一 LightGBM网络组网步骤
LightGBM并行计算中,计算节点之间采用静态互连网络,即程序执行期间,节点与节点之间的连接保持不变。LightGBM网络的建立需要事先定义所有worker的ip以及port,组网的过程通过Network::Init()完成,主要步骤如下:
1.1 网络初始化入口
建立网络连接主要的逻辑在new Linkers(config)中。
1.2 LightGBM中网络初始化过程
初始化过程主要是建立Linkers对象:
网络初始化过程中,首先从配置中读取当前worker的端口号。然后解析machine list文件,获取所有worker的ip列表以及port列表。接下来需要对Bruck算法及Recursive Halving算法计算出当前worker需要连接到其他哪些worker。
从2.12可以看出BruckMap不要求workers数量为2的k次方,因此对于任意数量的workers,都可以按以上步骤计算当前节点需要在第k步通信时发送数据给哪个worker以及接收哪个worker发送过来的数据。
从2.6.1可知,Recursive Halving算法要求workers数量为2k,但当workers数量非2k时,可以通过2.6.2介绍的方法进行解决这一问题。以上construct步骤主要是计算哪些worker需要参于到Recursive Halving过程中,同时计算第k次通信配对的worker,以及第k次通信发送、接收的数据块大小及位置(此处的值只是一个基数,具体位置及大小在实际的执行过程中根据实际数据块的大小计算)
计算完需要连接的worker之后,Construct将依次连接到需要连接的worker上,并等待被连接的worker的连接。连接与监听过程分别在两个线程中执行。自此,LightGBM静态互连网络的建立已经完成。
二 LightGBM中Allreduce、ReduceScatter、Allgather实现解析
LightGBM的并行训练过程中使用了大量的数值归约操作,如随机种子的同步用到了Allreduce、对数据进行分桶时计算bin_mapper用到了Allgather、计算最佳分裂时用到了ReduceScatter等,以下分别对这三种归约操作的代码进行解析。
2.1 Allgather代码解析
LightGBM中Allgather的实现采用Bruck Algorithm算法:
以上的操作经过log(N)次的通信能够完成Allgather。每一次通信数据块的大小为*send_size。可以理解,这种方式适用于N不太大并且数据块大小不太大的情形。
2.2 ReduceScatter代码解析
以上仅介绍非2k个节点的情形,workers为时执行过程仅需要第二个for循环。
2.3 Allreduce代码解析
Allreduce根据数据块的大小的不同,分别采用两种方式进行操作。在需要归约的数据块较小时采用Allgather加上一个reducer操作完成。针对数据块较大的情形则采用另一种方式:ReduceScatter + Allgather操作。
2.4 一个调用Allreduce的例子
以下介绍一个使用Allreduce的例子,在训练之前以label的平均值作为初始值:
因为init_score的size小,因此先通过Allgather收集所有worker上的init_score,然后在本地进行reducer操作,此处的reducer是做sum。