❝今天有朋友询问如何在R中进行并行运算,那本节就来简单介绍下,并行运算的方式有很多,在此主要介绍「foreach & doParallel」。下面小编通过几个小例子及对penguins数据集进行随机森林分析的具体例子来进行介绍,过程仅供参考。 ❞
install.packages("foreach")
install.packages("doParallel")
install.packages("randomForest")
library(foreach)
library(doParallel)
library(randomForest)
library(palmerpenguins)
library(tidyverse)
library(ranger)
x <- vector()
for(i in 1:10){
x[i] <- sqrt(i)}
x
x <- foreach(
i = 1:10000,
.combine = 'c') %do% {sqrt(i)}
x
❝foreach使用运算符%do%,会按顺序处理任务。要并行运行任务可以使用运算符%dopar%,该运算符必须得到并行后端的支持。不然会产生警告信息 ❞
x <- foreach(
i = 1:10000,
.combine = 'c') %dopar% {
sqrt(i)
}
Warning message:
executing %dopar% sequentially: no parallel backend registered
parallel::detectCores() # 检测可用的CPU核心数量
# 设置核心数量为总核心数减一
n.cores <- parallel::detectCores() - 1
# 创建一个集群
my.cluster <- parallel::makeCluster(n.cores, type = "PSOCK")
# 使用doParallel包注册这个集群方便后续使用
doParallel::registerDoParallel(cl = my.cluster)
foreach::getDoParRegistered() # 检查并行设置
foreach::getDoParWorkers() # 返回线程数
经过上面的设置在执行并行计算就不会出现警告信息
x <- foreach(
i = 1:10000,
.combine = 'c') %dopar% {
sqrt(i)
}
penguins <- penguins %>%
select(species, bill_length_mm, bill_depth_mm,
flipper_length_mm, body_mass_g) %>%
drop_na()
m <- ranger::ranger(
data = penguins, # 使用penguins数据
dependent.variable.name = "species", # 设置因变量为species
importance = "permutation" # 设置importance参数为permutation
)
# 打印模型的特征重要性
m$variable.importance
sensitivity.df <- expand.grid(
num.trees = c(500, 1000, 1500), # 考虑三种不同的num.trees值
mtry = 2:4, # 考虑三种mtry值
min.node.size = c(1, 10, 20) # 考虑三种min.node.size值
)
prediction.error <- foreach(
num.trees = sensitivity.df$num.trees, # 设置num.trees的值
mtry = sensitivity.df$mtry, # 设置mtry的值
min.node.size = sensitivity.df$min.node.size, # 设置min.node.size的值
.combine = 'c', # 结果合并方式为连接
.packages = "ranger" # 加载ranger包
) %dopar% {
# 使用ranger函数拟合随机森林模型
m.i <- ranger::ranger(
data = penguins, # 使用penguins数据
dependent.variable.name = "species", # 设置因变量为species
num.trees = num.trees, # 设置num.trees的值
mtry = mtry, # 设置mtry的值
min.node.size = min.node.size # 设置min.node.size的值
)
# 返回模型的预测误差(百分比)
return(m.i$prediction.error * 100)
}
sensitivity.df$prediction.error <- prediction.error
best.hyperparameters <- sensitivity.df %>%
dplyr::arrange(prediction.error) %>%
dplyr::slice(1)
# 定义一个函数将模型的特征重要性转换为数据框
importance_to_df <- function(model){
x <- as.data.frame(model$variable.importance) # 将特征重要性转换为数据框
x$variable <- rownames(x) # 添加变量名列
colnames(x)[1] <- "importance" # 重命名第一列为"importance"
rownames(x) <- NULL # 删除行名
return(x) # 返回数据框
}
system.time(
importance.scores <- foreach(i = 1:1000, # 进行1000次迭代
.combine = 'rbind',
.packages = "ranger" # 加载ranger包
) %dopar% {
# 使用ranger函数拟合随机森林模型
m.i <- ranger::ranger(
data = penguins, # 使用penguins数据
dependent.variable.name = "species", # 设置因变量为species
importance = "permutation", # 设置importance参数为permutation
mtry = best.hyperparameters$mtry, # 使用最佳的mtry值
num.trees = best.hyperparameters$num.trees, # 使用最佳的num.trees值
min.node.size = best.hyperparameters$min.node.size # 使用最佳的min.node.size值
)
# 使用importance_to_df函数转换特征重要性为数据框
m.importance.i <- importance_to_df(model = m.i)
return(m.importance.i)
}
)
# user system elapsed
# 0.151 0.026 3.805
importance.scores.list <- list()
system.time(
for(i in 1:1000){
m.i <- ranger::ranger(
data = penguins, # 使用penguins数据
dependent.variable.name = "species", # 设置因变量为species
importance = "permutation", # 设置importance参数为permutation
seed = i, # 设置随机种子以确保每次迭代的结果是可重复的
num.threads = parallel::detectCores() - 1 # 使用所有可用的核心进行计算,除了一个
)
importance.scores.list[[i]] <- importance_to_df(model = m.i)
}
)
## user system elapsed
## 43.663 2.815 12.948
parallel::stopCluster(cl = my.cluster)
可以看到并行计算香对于多核串行计算在提高效率方面说有大的提高,本节内容介绍到此结束过程仅供参考;