我正在将brms::brm_multiple()
模型拟合到一个大型数据集,在该数据集中已经使用mice
包来估算缺少的数据。数据集的大小非常需要使用并行处理。但是,我不清楚如何最好地使用计算资源,因为我不清楚brms
如何将插补数据集上的采样在内核之间进行分配。
如何选择以下选项以最大程度地有效利用计算资源?
m
)chains
)cores
)让我们说我天真(或出于示例目的故意愚蠢地)选择m = 5
,chains = 10
,cores = 24
。因此,在HPC上保留的24个内核之间分配了5 x 10 = 50个链。如果没有并行处理,这将需要约50个时间单位(不包括编译时间)。
我可以想象brms_multiple()
的三种并行化策略,但是可能还有其他几种:
方案1:并行估算的数据集,串行关联的链
在此,将5个归因中的每个归因于分配给它自己的处理器,该处理器通过10条串行链运行。处理时间为10个单位(与非并行处理相比,速度提高了5倍),但是糟糕的计划浪费了19个核心x 10个时间单位= 190个核心时间单位(ctu; =保留的计算资源的80%)。有效的解决方案是设置cores
= m
。
方案2:串行,关联链中的虚拟数据集并行
在这里,采样首先从获取第一个估算数据集开始,然后在10个不同核中的每个核上运行该数据集的链之一。然后针对其余四个估算数据集重复此过程。处理需要5个时间单位(与串行处理相比,速度提高了10倍,与方案1相比,速度提高了2倍)。但是,这也会浪费计算资源:14个内核x 5个时间单位= 70 ctu。有效的解决方案是设置cores
= chains
方案3:全部免费,其中每个核心在可用之前将进行挂起的插补/链组合,直到所有插补处理完毕。
在这里,采样首先分配所有24个核心,每个核心分配给50个挂起的链之一。它们完成迭代后,将处理第二批24条链,使处理的链总数达到48条。但是现在只有两条链待处理,22个核闲置了1个时间单位。总处理时间为3个时间单位,浪费的计算资源为22 ctu。有效的解决方案是将cores
设置为m
x chains
的倍数。
此代码使用从brms vignette修改的示例比较计算时间。在这里,我们将设置m
= 10,chains
= 6和cores
=4。这样一来,总共要处理60条链。在这种情况下,我希望速度提高(相对于串行处理)如下*:
*(之所以使用上限/四舍五入,是因为不能在核心之间细分链)
library(brms)
library(mice)
library(tictoc) # convenience functions for timing
# Load data
data("nhanes", package = "mice")
# There are 10 imputations x 6 chains = 60 total chains to be processed
imp <- mice(nhanes, m = 10, print = FALSE, seed = 234023)
# Fit the model first to get compilation out of the way
fit_base <- brm_multiple(bmi ~ age*chl, data = imp, chains = 6,
iter = 10000, warmup = 2000)
# Use update() function to avoid re-compiling time
# Serial processing (127 sec on my machine)
tic() # start timing
fit_serial <- update(fit_base, .~., cores = 1L)
t_serial <- toc() # stop timing
t_serial <- diff(unlist(t_serial)[1:2]) # calculate seconds elapsed
# Parallel processing with 3 cores (82 sec)
tic()
fit_parallel <- update(fit_base, .~., cores = 4L)
t_parallel <- toc()
t_parallel <- diff(unlist(t_parallel)[1:2]) # calculate seconds elapsed
# Calculate speed up ratio
t_serial/t_parallel # 1.5x
很明显,我缺少了一些东西。用这种方法我无法区分这两种情况。