【问题标题】:R: using foreach() with sample() procedures in randomForest() callR:在 randomForest() 调用中使用 foreach() 和 sample() 过程
【发布时间】:2015-02-28 04:00:42
【问题描述】:

我有一个大数据帧(~700 nx 36000 p)并计划在 R 中进行 randomForest 分析。由于将完整帧发送到 randomForest 的运行时负担(即使使用并行计算和 512 GB RAM),我想在许多独立运行 (Nruns) 中将数据帧的不同随机子样本 (~5% p) 发送到 randomForest。对于较小的数据帧,我创建了一个 foreach 循环将整个数据帧发送到 randomForest 并返回一个重要结果矩阵,该矩阵是 dim(p,Nruns) 加上 3 个附加行,其中包含每个 Nrun 中生成的一些附加信息。但是,我在构建脚本的 foreach() 组件以在每次运行时将数据帧的不同子样本发送到 randomForest 时遇到问题。 (子采样包括两个步骤:首先通过对行进行采样(这部分有效)创建平衡数据集(在结果类上),然后选择列的子集。)所需的结果仍然是 dim(p+3, Nruns),但每列将仅包含在该列表示的运行中随机选择的变量的结果(即,未为该运行选择的变量将缺少值)。当我提交下面的代码(使用下面创建的假数据)时,我收到以下错误: “调用组合函数时出错: " 请注意,如代码中所示,如果我排除选择随机列的步骤,但保留完成平衡的步骤,我不会收到错误并且输出符合预期(使用 dim(p+3,Nruns ) 并且所有单元格都有非零值。) 所以,问题出在完成列采样的代码部分。 我想知道是否有人可以建议对以下代码的补救措施,该补救措施将为 1:Nruns 中的每一个进行新的列(和行)随机子采样。

感谢您的任何建议。

##########################################################################
# CREATE FAKE DATA
##########################################################################
FAKEinput <- 
data.frame(A=sample(25:75,20, replace=T), B=sample(1:2,20,replace=T), C=as.factor(sample(0:1,20,replace=T,prob=c(0.3,0.7))),
    D=sample(200:350,20,replace=T), E=sample(2300:2500,20,replace=T), F=sample(92000:105000,20,replace=T),
    G=sample(280:475,20,replace=T),H=sample(470:550,20,replace=T),I=sample(2537:2723,20,replace=T),
    J=sample(2984:4199,20,replace=T),K=sample(222:301,20,replace=T),L=sample(28:53,20,replace=T),
    M=sample(3:9,20,replace=T),N=sample(0:2,20,replace=T),O=sample(0:5,20,replace=T),P=sample(0:2,20,replace=T),
    Q=sample(0:2,20,replace=T), R=sample(0:2,20,replace=T), S=sample(0:7,20,replace=T))

##########################################################################
# set FOREST DATASET
##########################################################################
forestData <- FAKEinput

##########################################################################
# set Outcome 
##########################################################################
Outcome <- "C"

##########################################################################
#  set DV
#########################################################################
forestDV <- forestData$C
str(forestDV) #factor

##########################################################################
#set up number of runs:
##########################################################################
Nruns<-5

##########################################################################
#set up ntree
##########################################################################
ntree=100

###########################################################################
#set up mtry:
###########################################################################
mtry=round(sqrt(ncol(forestData)))  #4

###########################################################################
## CREATE DATASET WITH ONLY THE PREDICTORS (I.E., OMIT OUTCOME).
###########################################################################
dropVars <- names(forestData) %in% c(Outcome)
forestPREDICTORS <- forestData[!dropVars] 

###########################################################################
#set seed first to replicate the random draw of seeds
###########################################################################
set.seed(3456)

###########################################################################
# GENERATE Nruns RANDOMSEEDS
###########################################################################
randomseed<- sample(1:(length(forestData[,1])),Nruns, replace=TRUE) #16 16 18 8 11

##########################################
#Load necessary packages into R's memory
##########################################
require(iterators)
require(foreach)
require(parallel)
require(doParallel)
require(randomForest)

###########################################
# Get the number of available logical cores
###########################################
cores <- detectCores()
cores

###########################################
# Print info on computer, OS, cores
###########################################
print(paste('Processor: ', Sys.getenv('PROCESSOR_IDENTIFIER')), sep='')
print(paste('OS: ', Sys.getenv('OS')), sep='')
print(paste('Cores: ', cores, sep=''))

##################################################################################################
#  Set up new function, called ’ImpOOBerr':
# 1 )write in the set random seed part that uses the same ‘i’ from the ‘foreach’ loops 
# 2) save the importance and summary measures output from the random forest run
# 3) combine all of the importance scores and OOB error summary results (as columns) into single matrix
# * other options tried to correct error commented out.
###################################################################################################
ImpOOBerr<-function(y,d) { 
set.seed(randomseed[i])
out.model<-randomForest(y ~ ., 
    data=d, 
    ntree=ntree,
    mtry=mtry,
    nodesize=0.1*nrow(forestData),
    importance=TRUE,
    proximity=FALSE)
# create the frame before filling with values?
#out<-data.frame(matrix(nrow=ncol(forestPREDICTORS)+3, ncol=Nruns))
out<-rbind(importance(out.model, type=1, scale=FALSE),
    mean(out.model$err.rate[,1]),
    rbind(t(t(quantile(out.model$err.rate[,1], probs=c(0.025, 0.975))))))
#rownames(out) <- c(names(forestPREDICTORS),'meanOOB','oobL95CI', 'oobU95CI') # name all the rows
# OR name only newly-added rows since randomForest importance output preserves the variable names
rownames(out)[(nrow(out)-2):nrow(out)]<-c('meanOOB','oobL95CI', 'oobU95CI') 
return(out)
}

###########################################################################
# SET UP THE CLUSTER
###########################################################################
#Setup clusters via parallel/DoParallel
cl.spec <- rep("localhost", 10)
cl <- makeCluster(cl.spec, type="SOCK")
registerDoParallel(cl, cores=10)

###########################################################################
# Employ foreach to carry out randomForest in parallel
##########################################################################
system.time(fakeRF <- foreach(i=1:Nruns, .combine='cbind', .packages='randomForest') 
    %dopar% {    #<<change to %do% to see speed difference

######################################################################################################
# FIRST, BALANCE THE DATASET ON OUTCOME CLASS FOR INPUT TO randomForest CLASSIFICATION
######################################################################################################
dat1<-forestData[forestData$C==1,]
dat0<-forestData[forestData$C==0,]

####################################################
# RESET the seed to make sure it is updating and 
# giving different samples for each run
####################################################
set.seed(randomseed[i])

####################################################
# OVERSAMPLE FROM SMALLER GROUP TO BALANCE DATASET
####################################################
rands=sample(1:dim(dat0)[1],dim(dat1)[1], replace=TRUE) 
balancedCLASS<-rbind(dat0[rands,],dat1) 

######################################################################################################
# NOW DO RANDOM SAMPLES OF THE COLUMNS (VARIABLES) TO CREATE NEW DATA SUBSETS TO SEND TO randomForest
# AT EACH RUN
# NOTE: TO TEST SCRIPT WITHOUT COLUMN SAMPLING, COMMENT OUT ALL SCRIPT BETWEEN TWO "#xxxxxxxxx.." ROWS
# AND UNCOMMENT THE NEXT THREE LINES
######################################################################################################
#forestData<-balancedCLASS
#forestDV<-balancedCLASS$C
#forestPREDICTORS <- balancedCLASS[!names(balancedCLASS) %in% c('C')]

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
####################################################
# PULL OUT PREDICTORS (i.e., exclue the outcome) 
# before sampling the columns
####################################################
PREDICTORS <- balancedCLASS[!names(balancedCLASS) %in% c('C')]

####################################################
# from the row-balanced set created above, 
# draw a 5-column subset for each run
####################################################
randsCOL= sample(1:dim(PREDICTORS)[2], 5, replace=FALSE) 

####################################################
# BIND OUTCOME VAR BACK ONTO RANDOM COL SET
####################################################
Set_BALrandsCOL <- cbind(balancedCLASS$C, balancedCLASS[,randsCOL]) 

####################################################
# FIX OUTCOME NAME (was retained as "balancedCLASS$C")
####################################################
names(Set_BALrandsCOL)[names(Set_BALrandsCOL)=="balancedCLASS$C"] <- "C"

####################################################
# ASSIGN THE OUTCOME OF SAMPLING BACK TO 
# forestData, forestDV and forestPREDICTORS for RF runs
####################################################
forestData<-Set_BALrandsCOL
forestDV<-Set_BALrandsCOL$C
forestPREDICTORS <- Set_BALrandsCOL[!names(Set_BALrandsCOL) %in% c('C')]
##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

#############################################################################################
# CALL FUNCTION THAT WILL RUN randomForest AND COMBINE THE OUTPUT FROM EACH RUN
#############################################################################################
ImpOOBerr(forestDV, forestPREDICTORS)
})

##########################
# stop the cluster
##########################
stopCluster(cl)

#############################################################################################
# SAVE THE OUTPUT TO FILE
#############################################################################################
save(fakeRF, file="D:/RF/WORKING/fakeRF.rda")

【问题讨论】:

  • 只需使用i=1 手动尝试此操作,我得到一个 dim(8,1) 矩阵,而 i=2 给出了 dim(7,1)。如果这是ImpOOBerr 输出的已知变化,那么您需要一个不同的组合器,因为cbind 将需要相同数量的行。
  • 感谢您的回复。
  • 感谢您的回复。 ImpOOBerr() 应该产生相同大小的矩阵,并且如果我排除了随机抽取 cols 样本的脚本部分(即“#xxxx”行之间的部分)。我在随机列选择中的意图是在每次独立运行中选择相同数量的列。由于#col selected 指示输出矩阵中的#rows,因此每次运行都应生成相同大小的矩阵。但是,由于它是随机抽样,因此选择的特定列在运行中会有所不同。错误是因为 ImpOOBerr 组合的矩阵的 row.names 每次运行都不同吗?有什么建议吗?
  • 随机 col 子集脚本现在已修复,因此每次运行都会在输出矩阵中产生相同的 #rows,并且完整的代码运行时不会出现错误。但是,结果不正确,因为输出矩阵有 5 行(每次运行绘制 #var)而不是预期的 >5 行。例如,对于 3 次运行,随机抽取如下:运行 1:A、B、C、D、E;运行2:B,C,E,F,G; run3:E、A、C、H、K,输出矩阵为 12 行(A、B、C、D、E、F、G、H、K + 每次运行产生的 3 行汇总值)X 3 (#运行)。由于每次运行产生的矩阵的 row.names 不同,我是否需要 'cbind' 以外的函数?
  • 我建议您编辑您的问题以包含更正的代码。但是,当在具有行名的矩阵上使用 cbind 时,我相信第二个(以及后续)矩阵的行名会被忽略/丢弃。

标签: r foreach parallel-processing random-forest


【解决方案1】:

我已经解决了上述问题。 r2evans 的第一条评论让我修复了 随机列子集部分。然后,我对 ImpOOBerr 函数进行了一些修改,以强制函数的输出在每次运行中具有相同数量的观察值(以及相同的 row.names)。这允许 cbind 在 %dopar% 语句中工作。 感谢您的反馈和建议。

##########################################################################
# CREATE FAKE DATA
##########################################################################
FAKEinput <- 
data.frame(A=sample(25:75,20, replace=T), B=sample(1:2,20,replace=T), C=as.factor(sample(0:1,20,replace=T,prob=c(0.3,0.7))),
    D=sample(200:350,20,replace=T), E=sample(2300:2500,20,replace=T), F=sample(92000:105000,20,replace=T),
    G=sample(280:475,20,replace=T),H=sample(470:550,20,replace=T),I=sample(2537:2723,20,replace=T),
    J=sample(2984:4199,20,replace=T),K=sample(222:301,20,replace=T),L=sample(28:53,20,replace=T),
    M=sample(3:9,20,replace=T),N=sample(0:2,20,replace=T),O=sample(0:5,20,replace=T),P=sample(0:2,20,replace=T),
    Q=sample(0:2,20,replace=T), R=sample(0:2,20,replace=T), S=sample(0:7,20,replace=T))

##########################################################################
# set FOREST DATASET
##########################################################################
forestData0 <- FAKEinput

##########################################################################
# set Outcome 
##########################################################################
Outcome <- "C"

##########################################################################
#  set DV
#########################################################################
forestDV0 <- forestData0$C

##########################################################################
#set up number of runs:
##########################################################################
Nruns<-5

##########################################################################
#set up ntree
##########################################################################
ntree=100

###########################################################################
## CREATE DATASET WITH ONLY THE PREDICTORS (I.E., OMIT OUTCOME).
###########################################################################
dropVars <- names(forestData0) %in% c(Outcome)
forestPREDICTORS0 <- forestData0[!dropVars] 

###########################################################################
# CREATE single-column dataframe, whichi will be used to send the
# FULL SET OF PREDICTORS TO ImpOOBerr() OUTPUT MATRIX
# Automatically-generated column name is unwieldy; change that to Predictor.
###########################################################################
VARS <-data.frame(c(names(forestPREDICTORS0),'ZZZmeanOOB','ZZZoobL95CI', 'ZZZoobU95CI'))
VARS <- rename(VARS, c(c.names.forestPREDICTORS0....ZZZmeanOOB....ZZZoobL95CI....ZZZoobU95CI..="Predictor"))
row.names(VARS) <- VARS$Predictor

###########################################################################
#set seed first to replicate the random draw of seeds
###########################################################################
set.seed(3456)

###########################################################################
# GENERATE Nruns RANDOMSEEDS
###########################################################################
randomseed<- sample(1:Nruns,Nruns, replace=FALSE) 

##########################################
#Load necessary packages into R's memory
##########################################
require(iterators)
require(foreach)
require(parallel)
require(doParallel)
require(randomForest)

###########################################
# Get the number of available logical cores
###########################################
cores <- detectCores()
cores

###########################################
# Print info on computer, OS, cores
###########################################
print(paste('Processor: ', Sys.getenv('PROCESSOR_IDENTIFIER')), sep='')
print(paste('OS: ', Sys.getenv('OS')), sep='')
print(paste('Cores: ', cores, sep=''))

##################################################################################################
# SET UP NEW FUNCTION, called ’ImpOOBerr':
# 1) write in the set random seed part that uses the same ‘i’ from the ‘foreach’ loops 
# 2) save the importance and summary measures output from the random forest run
# 3) combine all of the importance scores and OOB error summary results (as columns) into single matrix
# 4) merge the ANNOTS dataset with each 'out' file so that cbind function will work 
# (requires same # of rows and same row.names)
###################################################################################################
ImpOOBerr<-function(y,d) { 
set.seed(randomseed[i])
out.model<-randomForest(y ~ ., 
    data=d, 
    ntree=ntree,
    mtry=mtry,
    nodesize=0.1*nrow(forestData),
    importance=TRUE,
    proximity=FALSE)
out<-rbind(importance(out.model, type=1, scale=FALSE),
    mean(out.model$err.rate[,1]),
    rbind(t(t(quantile(out.model$err.rate[,1], probs=c(0.025, 0.975))))))
rownames(out)[(nrow(out)-2):nrow(out)]<-c('ZZZmeanOOB','ZZZoobL95CI', 'ZZZoobU95CI') 
out2<- merge(ANNOTS, out, by="row.names", all.x=TRUE)
row.names(out2) <- out2$Row.names
out2 <- out2[,-1]
out2 <- out2[order(row.names(out2)),]
out3 <- data.frame(out2[,-1,drop=FALSE]) # !!!! THIS WORKS !!!
return(out3)
}

###########################################################################
# SET UP THE CLUSTER
###########################################################################
#Setup clusters via parallel/DoParallel
cl.spec <- rep("localhost", 30)
cl <- makeCluster(cl.spec, type="SOCK")
registerDoParallel(cl, cores=30)

###########################################################################
# Employ foreach to carry out randomForest in parallel
##########################################################################
system.time(fakeRF <- foreach(i=1:Nruns, .combine='cbind', .packages='randomForest') 
    %dopar% {    #<<change to %do% to see speed difference

######################################################################################################
# FIRST, BALANCE THE DATASET ON OUTCOME CLASS FOR INPUT TO randomForest CLASSIFICATION
######################################################################################################
dat1<-forestData[forestData$C==1,]
dat0<-forestData[forestData$C==0,]

####################################################
# RESET the seed to make sure it is updating and 
# giving different samples for each run
####################################################
set.seed(randomseed[i])

####################################################
# OVERSAMPLE FROM SMALLER GROUP TO BALANCE DATASET
####################################################
rands=sample(1:dim(dat0)[1],dim(dat1)[1], replace=TRUE) 
balancedCLASS<-rbind(dat0[rands,],dat1) 

######################################################################################################
# SELECT RANDOM SAMPLES OF THE COLUMNS (VARIABLES) TO CREATE NEW DATA SUBSETS TO SEND TO randomForest
# AT EACH RUN
######################################################################################################

#################################################################
# FROM ROW-BALANCED SET CREATED ABOVE (balancedCLASS),
# DRAW A 5% COL (5% OF 35365=1768) SUBSET FOR EACH RUN
# OMIT THE OUTCOME COLUMN (3) FROM THE RANDOM SELECTION
#################################################################
randsCOLs= sample(balancedCLASS[,-c(3)], 5, replace=FALSE) 

####################################################
# BIND OUTCOME VAR BACK ONTO RANDOM COL SET
####################################################
Set_BALrandsCOL <- cbind(balancedCLASS$C, randsCOLs) 

####################################################
# FIX OUTCOME NAME (was retained as "balancedCLASS$C")
####################################################
names(Set_BALrandsCOL)[names(Set_BALrandsCOL)=="balancedCLASS$C"] <- "C"

####################################################
# ASSIGN THE OUTCOME OF SAMPLING BACK TO 
# forestData, forestDV and forestPREDICTORS for RF runs
####################################################
forestData<-Set_BALrandsCOL
forestDV<-Set_BALrandsCOL$C
forestPREDICTORS <- Set_BALrandsCOL[!names(Set_BALrandsCOL) %in% c('C')]

#############################################################################################
# CALL FUNCTION THAT WILL RUN randomForest AND COMBINE THE OUTPUT FROM EACH RUN
#############################################################################################
ImpOOBerr(forestDV, forestPREDICTORS)
})

##########################
# stop the cluster
##########################
stopCluster(cl)

#############################################################################################
# SAVE THE OUTPUT TO FILE
#############################################################################################
save(fakeRF, file="D:/LearningMachines/RF/Knight_ADNI/WORKING/fakeRF.rda")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-26
    • 1970-01-01
    • 2013-10-18
    • 2013-12-13
    • 2020-09-17
    • 2017-01-31
    • 1970-01-01
    • 2014-04-22
    相关资源
    最近更新 更多