并行与分布式系统中的并行随机森林算法:基于特征子空间的并行化方法
一、问题描述
随机森林是一种经典的集成学习算法,它通过构建多棵决策树并进行投票(分类)或平均(回归)来提高预测的准确性和鲁棒性。然而,在大规模数据集上,训练随机森林的计算成本可能非常高,因为需要构建成百上千棵决策树,并且每棵树在构建过程中需要搜索最优特征划分点。这使其成为并行与分布式计算的理想应用对象。基于特征子空间的并行化方法,主要将并行性引入到构建单棵决策树时对特征(即输入变量)的搜索过程中,以加速每棵树的训练。问题可以形式化为:如何设计一个高效的并行算法,使得在多处理器或多计算节点环境中,构建随机森林时对特征空间的搜索能够被有效地分而治之,从而降低整体训练时间。
二、核心思想与挑战
核心思想
- 总体并行策略:随机森林训练天然存在两个层面的并行性。
- 树间并行:不同决策树的构建过程彼此独立,可以完全并行地分配给不同的处理器或计算节点。
- 树内并行:构建单棵决策树时,主要耗时在于为当前待划分的节点寻找最优特征和划分点。这个过程需要对一个特征子集进行评估,计算其“不纯度”指标(如基尼指数、信息增益等)。基于特征子空间的并行化,就是将一个节点上需要评估的多个特征分配给不同的处理单元并行计算,最后汇总选出最优划分。
- 基于特征子空间的并行:这是本方法的核心。当为决策树的一个节点寻找最佳划分时,算法通常从全部特征中随机采样一个子集(例如
sqrt(n_features)个特征)。然后,对该子集中的每个特征,遍历其所有可能的分裂点(或通过预排序/分箱技术减少候选点),并计算其分裂收益。这个过程是“Embarrassingly Parallel”的:每个特征的分裂收益计算与其他特征的计算相互独立。
主要挑战
- 负载均衡:不同特征的数据类型(连续值、离散值)、取值数量、处理复杂度可能不同。简单地按特征数量均分可能导致部分处理器提前完成而其他处理器仍在工作。
- 数据局部性与通信开销:在多机分布式环境中,特征数据可能分布在不同节点上。为了并行计算,可能需要将部分数据(如特定特征的列)在节点间广播或重分发,这会产生网络通信开销。
- 聚合与同步:并行计算完所有候选特征的分裂质量后,需要一个全局归约操作(如最大值查找)来确定最优特征和最优划分点。这需要处理器间进行同步和通信。
- 随机性与可重复性:随机森林依赖于随机性(特征子集采样、自助采样)。并行实现需要确保随机数生成的独立性,同时保持结果的可重复性(如设置种子)。
三、解题步骤与并行化方法详述
假设我们有一个具有 p 个处理器的共享内存或分布式内存系统。数据集 D 包含 n 个样本,每个样本有 d 个特征。
步骤 1:数据准备与分配(在分布式环境中尤为重要)
- 数据分区:
- 常见的做法是按行分区,即将样本划分到不同的处理器上。这种“数据并行”模式有利于进行自助采样(Bootstrap Sampling)和后续的预测。
- 每个处理器
P_i存储其分配到的样本行的全部d个特征值。为了支持对特定特征的快速访问,通常采用列优先或压缩列存储(CSC)格式来存储特征矩阵,但这在按行分区时需要额外的数据结构支持。
步骤 2:并行构建单棵决策树(聚焦于节点分裂)
对于每一棵决策树:
- 创建根节点:将当前树对应的自助采样数据集(Bootstrap Sample)分配给该树的构建任务。由于按行分区,这个数据集可能物理上分布在多个处理器上。
- 递归分裂节点:
- 对当前节点
N,确定分裂候选集:从全部d个特征中随机选择m个特征(m ≤ d,通常m = sqrt(d)或log2(d))。这个随机选择过程需要在主处理器上完成,或者通过一个分布式的伪随机数生成器(使用相同的种子)在各个处理器上独立但同步地完成,以确保所有处理器知道相同的m个特征子集F = {f_1, f_2, ..., f_m}。 - 并行评估特征:将
m个特征分配给p个处理器。分配策略可以是:- 循环分配:特征
f_k分配给处理器P_{k mod p}。 - 块分配:将特征划分为
p个连续的块,每个块分配给一个处理器。
- 循环分配:特征
- 处理器
P_j的本地计算:对于分配到的每个特征f:- 收集(或局部访问)当前节点
N中所有样本在特征f上的值以及其类别标签。 - 对该特征的取值进行排序(如果连续)或统计类别分布(如果离散)。
- 遍历所有可能的分裂点
s(例如,对于连续特征,可以取相邻值的中间值;对于分类特征,可以取所有可能的子集划分),计算分裂点s将节点数据分为左子集D_L和右子集D_R后,计算不纯度的减少量(如信息增益ΔI)。保留该特征上找到的最佳分裂点s_f及其对应的ΔI_f。 - 本地输出:对于每个分配到的特征,输出一个三元组
(f, s_f, ΔI_f)。
- 收集(或局部访问)当前节点
- 全局归约与确定最优分裂:所有处理器完成本地计算后,需要进行一次全局归约操作,目标是找到具有最大
ΔI的特征和分裂点。- 每个处理器
P_j将其本地找到的m_j个最优特征(每个特征一个最佳分裂点) 发送给一个指定的根处理器(或通过全归约操作All-Reduce)。 - 根处理器从所有接收到的候选
(f, s_f, ΔI_f)中,选择ΔI最大的那个,确定为节点N的最优分裂特征f*和分裂点s*。 - 这个过程等价于在所有候选特征上进行一次“最大值查找”的归约。
- 每个处理器
- 执行分裂:根处理器将决策
(f*, s*)广播给所有处理器。每个处理器根据这个规则,将自己本地存储的、属于节点N的样本划分到左子节点或右子节点(即根据样本在特征f*上的取值与s*的比较结果进行判断)。由于数据是行分区的,每个处理器仅对自己的样本进行划分,无需大规模数据移动。 - 递归处理:对生成的两个子节点重复上述过程,直到满足停止条件(如节点深度、样本数、不纯度等)。
- 对当前节点
步骤 3:集成多棵树(树间并行)
这是最简单直接的并行。将构建 T 棵决策树的任务均匀分配到 p 个处理器上。由于树的构建是独立的,几乎没有通信开销。例如,在一个有 p 个处理器的系统中,每个处理器负责构建约 T/p 棵树。所有树构建完成后,可以简单地将所有树的模型(结构)收集到一个主节点,或者就分布式存储,用于后续的预测。
四、算法优化与变体
- 混合并行:同时利用树间并行和基于特征子空间的树内并行。例如,如果总共有
p个处理器,可以分成g个组。每组p/g个处理器协作构建一棵树(使用特征子空间并行加速单棵树),而不同的组并行构建不同的树。 - 负载均衡优化:在分配特征给处理器时,可以基于特征的预估计算量(如连续特征需要排序,离散特征只需统计)进行更智能的动态调度。
- 避免特征数据传输:在分布式场景下,如果数据是按行分区的,那么每个处理器都拥有所有样本的部分行,但拥有全部特征。在进行节点分裂的并行特征评估时,处理器只需要本地数据即可计算,这大大减少了网络通信。这是这种并行方法的主要优势。
- 使用直方图近似:许多高效的实现(如 XGBoost、LightGBM 的直方图算法)会将连续特征的值离散化到多个箱子(bin)中。这样,寻找最佳分裂点就变成了遍历箱子边界,计算速度更快。并行化时,每个处理器先为分配到的特征构建局部直方图(基于其本地样本),然后将局部直方图通过通信合并成全局直方图(
Reduce操作),最后在全局直方图上寻找最优分裂点。这进一步减少了通信量(只需传递直方图,而不是原始样本)。 - 异步更新:为了减少同步等待时间,一些系统会探索异步并行策略。处理器在完成其分配到的特征评估后,不必等待所有处理器,而是立即将结果贡献给一个全局的“最佳分裂”缓存。其他处理器可以读取当前最佳结果,并判断自己是否需要继续深入搜索。这种策略更复杂,但可能提高资源利用率。
五、总结
并行随机森林的基于特征子空间的并行化方法,核心在于将构建单棵决策树时最耗时的“特征评估”步骤进行并行化。通过将候选特征集分配给多个处理器并行计算其最优分裂点,再通过一个全局归约操作确定全局最优分裂,可以有效加速单棵树的构建。结合最外层的“树间并行”,构成了一个两层的并行架构,能够充分利用大规模并行计算资源。这种方法平衡了计算与通信,尤其适合按行分区的分布式数据场景,是实际系统中(如 Spark MLlib、H₂O)实现大规模随机森林训练的常用策略。其设计关键在于高效的特征分配、本地收益计算、全局归约以及数据划分策略的协调。