如何有效地并行化brms :: brm?

时间:2019-01-04 14:48:52

标签: r parallel-processing rstan

问题摘要

我正在将brms::brm_multiple()模型拟合到一个大型数据集,在该数据集中已经使用mice包来估算缺少的数据。数据集的大小非常需要使用并行处理。但是,我不清楚如何最好地使用计算资源,因为我不清楚brms如何将插补数据集上的采样在内核之间进行分配。

如何选择以下选项以最大程度地有效利用计算资源?

  • 估算数(m
  • 链数(chains
  • 核心数(cores

概念示例

让我们说我天真(或出于示例目的故意愚蠢地)选择m = 5chains = 10cores = 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条链。在这种情况下,我希望速度提高(相对于串行处理)如下*:

  • 场景1:60 /(6条链x天花板(10 m / 4芯))= 3.3x
  • 场景2:60 /(天花板(6条链/ 4芯)x 10 m)= 3.0x
  • 场景3:60 /天花板((6条链x 10 m)/ 4芯)= 4.0x

*(之所以使用上限/四舍五入,是因为不能在核心之间细分链)

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

很明显,我缺少了一些东西。用这种方法我无法区分这两种情况。

0 个答案:

没有答案