在 R 上自动使用多个核心

数据挖掘 机器学习 r 元数据 元学习
2022-02-10 03:45:08

我正在使用一个名为 MFE 的库来生成元特征。但是,我现在正在处理几个文件,我注意到我只使用了机器的 1 个核心并且花费了太多时间。

正如我在另一个问题中看到的那样,我一直在尝试实现一些库: library(iterators) library(foreach) library(doParallel)

这个,但我很笨无法实现它='(。

我只想让这个片段在我所有的核心中运行,这样我就可以让它更快:

## Extract general, statistical and model based measures
metafeatures(Species ~ ., iris, groups=c("general", "statistical", "model.based"))

那么我该怎么做呢?MFE 库

[编辑]

我现在在做什么:

library(iterators)
library(foreach)
library(doParallel)
library(mfe)

# foreach
split = detectCores()
eachStart = 25
# set up iterators
iters = iter(rep(eachStart, split))
# set up combine function
cl = makeCluster(split)
registerDoParallel(cl)
result = foreach(nstart=iters) %do%
  metafeatures(Species ~ ., iris, groups=c("general", "statistical", "model.based"))
stopCluster(cl)

result

预期结果样本 我所期待的是以下输出并使用我 CPU 的所有内核。 预期结果

1个回答

首先,你不傻。我们都在这里学习。

其次,我查看了您的代码,现在可以更仔细地看到问题。

下面的代码有效,但它没有提供任何有用的东西(也不会导致任何加速)。我将尝试解释原因。

library(iterators)
library(foreach)
library(doParallel)
library(mfe)

data = iris

# foreach
split = detectCores()
eachStart = 25

# set up iterators
iters = iter(rep(eachStart, split))

# set up combine function
cl = makeCluster(split)
registerDoParallel(cl)

result = foreach(nstart=iters, .packages = c("iterators", "mfe")) %dopar% {

  metafeatures(Species ~ ., iris, groups=c("general", "statistical", "model.based"))

}

stopCluster(cl)

这将返回一个长度等于您机器上的核心数的列表,但列表中的所有元素都将完全相同。你没有得到加速(事实上,如果你使用它,你的代码会运行得更慢),因为你所做的只是运行完全相同的一段代码 n =(你机器上的核心数)次数,即:

metafeatures(Species ~ ., iris, groups=c("general", "statistical", "model.based"))

您不会遍历 foreach 循环中的任何内容来为并行运行带来任何好处。并行运行的总体想法(无论如何,在这种情况下)是在每个工作节点上运行完全相同的代码(即函数),但在每次调用中函数参数的输入不同。在 for 循环的上下文中,您可以将其视为 for 循环的单个迭代被发送到计算机上的单个工作程序(拆分,如您在代码中定义的那样)。当然的好处是,如果您的计算机上有许多内核,则可以同时计算 for 循环的不同迭代(与在单核机器上相比,您必须处理 for 的单个迭代一次循环一个)。

这是我能想到的一个可以考虑并行运行的示例:良好的旧 k 折交叉验证。每个生成的训练集都接受完全相同的预处理,但您所改变的只是构成训练集和测试集的折叠。

这可以这样写:

library(foreach)
library(doParallel)
library(caret)
library(e1071)
library(tidyverse)

data = iris

cl = makeCluster(detectCores())

#Generate three folds from our dataset

folds = createFolds(y = data$Species, k = 3)

#Train a support vector machine in parallel using foreach. Returns the predictions from the fitted model.

results = foreach(i = 1:length(folds), .packages = c("caret", "e1071", "tidyverse")) %dopar% {

  training = data[-folds[[i]], ]
  test = data[folds[[i]],]

  #Pre processing, if desired


  #Train the support vector machine. Parameters are just for example, ripped from a webpage that just so happened to be using the exact same dataset.
  model = svm(Species~., data=training, 
          method="C-classification", kernal="radial", 
          gamma=0.1, cost=10)

  tibble(Row = folds[[i]], Predicted.Class = predict(model, newdata = test))

}

#Bind all the data frames in the list "results"

processedResults = bind_rows(results) %>%
  arrange(-Row)

请注意我们如何迭代不同的训练/测试拆分(使用这些行):

training = data[-folds[[i]], ]
test = data[folds[[i]],]

但功能本身保持不变。每个核心将在 for 循环的每次迭代中接收不同的训练/测试集。在您的代码中,您没有更改函数的参数,因此并行运行代码没有意义。

现在,如果我正确理解 mfe,它会从您的数据集中生成一些汇总统计信息。这可能会并行运行(也许一些统计数据可以并行计算?我怀疑这里有什么速度可以提高),但我非常怀疑它会更快,因为并行运行的东西相关的开销(因此,为什么包创建者没有选择实现这样的功能)。

您提到了随机森林以及如何轻松并行运行它。这是因为随机森林很容易并行(它是“令人尴尬的并行”)。每棵树都可以适合单个工人(因为在引导训练集上,每棵树都独立于森林中的所有其他树)。树适合的顺序也无关紧要,因为我们只是对所有树的预测进行平均以获得我们的最终预测。

我的两分钱:并行运行只有在算法本身可以并行运行时才有用。如果算法本身不能被“划分”然后“组合”,你就不能利用你所有的核心。即便如此,如果算法在单个线程上运行很简单,那么与并行运行相关的计算开销在这些情况下通常会变得更糟。