首先是一些背景信息,这在 stats.stackexchange 上可能更有趣:
在我的数据分析中,我尝试比较不同机器学习方法对时间序列数据(回归,而不是分类)的性能。例如,我训练了一个 Boosting 训练模型,并将其与随机森林训练模型(R 包 randomForest)进行了比较。
我使用时间序列数据,其中解释变量是其他数据和因变量的滞后值。
出于某种原因,随机森林的表现严重不佳。我能想到的问题之一是随机森林对每棵树的训练数据执行一个采样步骤。如果它对时间序列数据执行此操作,则该序列的自回归性质将被完全消除。
为了测试这个想法,我想用所谓的按块引导步骤替换 randomForest() 函数中的(引导)采样步骤。这基本上意味着我将训练集切成 k 个部分,其中 k<<N
,其中每个第 k 部分都按原始顺序排列。如果我对这 k 个部分进行采样,我仍然可以从随机森林中的“随机性”中受益,但时间序列的性质基本保持不变。
现在我的问题是这样的:
为此,我通常会复制现有函数并编辑所需的步骤/行。
randomForest2 <- randomForest()
但是 randomForest() 函数似乎是另一个包装器的包装器,用于更深层次的底层功能。那么如何在 randomForest() 函数中编辑实际的引导步骤,并仍然定期运行该函数的其余部分?
最佳答案
所以对我来说,解决方案不是编辑现有的 randomForest 函数。相反,我使用 split2
自己编写了逐块 bootstrap 。 Soren H. Welling 给出的函数来创建块。一旦我的数据块级引导,我寻找一个包( rpart
),它只执行一个回归树并自己聚合(采取手段)。
我的实际数据的结果是在 RMSPE 方面比正常随机森林性能略有但持续改进的版本。
对于下面的代码,性能似乎是掷硬币。
以 Soren 的代码为例,它看起来有点像这样:
library(randomForest)
library(doParallel) #parallel package and mclapply is better for linux
library(rpart)
#parallel backend ftw
nCPU = detectCores()
cl = makeCluster(nCPU)
registerDoParallel(cl)
#simulated time series(y) with time roll and lag=1
timepoints=1000;var=6;noise.factor=.2
#past to present orientation
y = sin((1:timepoints)*pi/30) * 1000 +
sin((1:timepoints)*pi/40) * 1000 + 1:timepoints
y = y+rnorm(timepoints,sd=sd(y))*noise.factor
plot(y,type="l")
#convert to absolute change, with lag=1
dy = c(0,y[-1]-y[-length(y)]) # c(0,t2-t1,t3-t2,...)
#compute lag
dy = dy + rnorm(timepoints)*sd(dy)*noise.factor #add noise
dy = c(0,y[-1]-y[-length(y)]) #convert to absolute change, with lag=1
dX = sapply(1:40,function(i){
getTheseLags = (1:timepoints) - i
getTheseLags[getTheseLags<1] = NA #remove before start timePoints
dx.lag.i = dy[getTheseLags]
})
dX[is.na(dX)]=-100 #quick fix of when lag exceed timeseries
pairs(data.frame(dy,dX[,1:5]),cex=.2)#data structure
#make train- and test-set
train=1:600
dy.train = dy[ train]
dy.test = dy[-train]
dX.train = dX[ train,]
dX.test = dX[-train,]
#classic rf
rf = randomForest(dX.train,dy.train,ntree=500)
print(rf)
#like function split for a vector without mixing
split2 = function(aVector,splits=31) {
lVector = length(aVector)
mod = lVector %% splits
lBlocks = rep(floor(lVector/splits),splits)
if(mod!=0) lBlocks[1:mod] = lBlocks[1:mod] + 1
lapply(1:splits,function(i) {
Stop = sum(lBlocks[1:i])
Start = Stop - lBlocks[i] + 1
aVector[Start:Stop]
})
}
#create a list of block-wise bootstrapped samples
aBlock <- list()
numTrees <- 500
splits <- 40
for (ttt in 1:numTrees){
aBlock[[ttt]] <- unlist(
sample(
split2(1:nrow(dX.train),splits=splits),
splits,
replace=T
)
)
}
#put data into a dataframe so rpart understands it
df1 <- data.frame(dy.train, dX.train)
#perform regression trees for Blocks
rfBlocks = foreach(aBlock = aBlock,
.packages=("rpart")) %dopar% {
dBlock = df1[aBlock,]
rf = predict( rpart( dy.train ~., data = dBlock, method ="anova" ), newdata=data.frame(dX.test) )
}
#predict test, make results table
#use rowMeans to aggregate the block-wise predictions
results = data.frame(predBlock = rowMeans(do.call(cbind.data.frame, rfBlocks)),
true=dy.test,
predBootstrap = predict(rf,newdata=dX.test)
)
plot(results[,1:2],xlab="OOB-CV predicted change",
ylab="trueChange",
main="black bootstrap and blue block train")
points(results[,3:2],xlab="OOB-CV predicted change",
ylab="trueChange",
col="blue")
#prediction results
print(cor(results)^2)
stopCluster(cl)#close cluster
关于r - 如何替换包 randomForest r 中的引导步骤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32057569/