MPI 是“Message Passing Interface”的缩写,通常用来做单机多线程的并发编程。
1. GibbsLDA++中训练框架大致如下:
循环:训练过程迭代N次 { 循环:遍历每一个训练样本(指doc) { 循环:遍历训练样本中的每一个word { 循环:gibbs采样过程,遍历每一个topic { 会更新: doc-topic矩阵、 word-topic矩阵、 doc-topic的边缘分布向量、 word-topic的边缘分布向量 当前word在topic上的分布向量 } } } }
LDA的训练过程主要通过gibbs采样过程来完成。在内存中会存储:(1)所有训练样本;(2)doc-topic矩阵、word-topic矩阵、几个topic相关的分布向量等。整个训练过程由上面嵌套的四重循环组成。当训词典不大且练样本集合不大的时候,gibbs采样是一个cpu消耗集中的项目,而不是内存占用的项目。也就是说cpu是整个训练的瓶颈。
2. 尝试用openmp来实现训练过程的单机并行化
主要是利用openmp利用单机cpu上的多个核心,将循环依照核心的数目来进行划分,例如:将一重循环划分为较小的两重循环,分别放在cpu的两个核心上进行运算,再将得到的结果进行合并。这样使用需要有个要求:数据独立性。即划分为两个循环之后,这两个循环内部不会对同一个变量或者内存区域进行修改,如果有的话,就会产生“竞争”关系,就会因为写内存的时机不同、程序运行出不同的结果。
2.1 在最内层循环用openmp
主要是计算“当前word在topic上的分布向量”的时候,遍历topic,这时候word在topic上的分布向量可以依据topic的不同拆分成不同的区域,相互并不干扰。代码如下:
#pragma omp parallel for num_threads(2) for (int k = 0; k < K; k++) { p[k] = (nw[w][k] + beta) / (nwsum[k] + Vbeta) * (nd[m][k] + alpha) / (ndsum[m] + Kalpha); }
上面代码用“#pragma omp parallel for num_threads(2)”指定该循环在两个核心上运算。
用GibbsLDA++自带的训练样本为例,实验结果如下:
没用MPI的,gibbs采样时间消耗:62.657s
用MPI的,gibbs采样时间消耗:94.626s
用MPI的,效率反而更低了!
现在能想到的原因,大概是针对K(topic个数)的循环太小了(实验中K的取值是100),且处于最内层循环,被调用的次数最多。这会引起大量的、拆分K循环给2个cpu的操作,这些操作也有性能损耗,且损耗大于了拆分后独立运行的收益,造成性能下降。
2.2 在第二层循环(遍历训练样本)中用openmp
第二层循环是遍历训练样本,在这一层循环用openmp实际上是对训练样本进行划分,并分别训练。代码如下:
#pragma omp parallel for num_threads(2) for (int m = 0; m < M; m++) { for (int n = 0; n < ptrndata->docs[m]->length; n++) { int topic = sampling(m, n); z[m][n] = topic; } }
这样修改,gibbs采样的运行时间就降低很多了,结果如下:
没用MPI的,gibbs采样时间消耗:62.657s
用MPI的,gibbs采样时间消耗:40.068s
降低了1/3的训练时间。
不过这样做会有数据依赖的问题:将第二层循环切分为两个并行的循环的话,这两个并行的小循环,最内层循环都会对word做gibbs采样,即都会读取和更新doc-topic和word-topic矩阵、以及那些边缘分布。从这个角度讲,算法运行出来的结果应该是不正确的结果。不过,换另外的角度想:
(1)对数据进行并行化,也就是将doc集合进行划分,那么doc-topic矩阵和doc边缘分布相对也划分成多份,互不干扰。不过word-topic矩阵以及当前word在每个topic上的分布的向量,会有相互之间的干扰。
(2)word-topic矩阵的内容,每个word不同,更新的区域不同,当并行循环计算的word不同的时候,也不会受到影响;当并行循环计算的word相同的时候,刚好需要累加计算结果,也OK
(3)word在每个topic上的分布的向量,这个结果会收到影响。例如:如果将训练数据划分为两份,这两份中分别找到两个word(通常是不同的),用来更新上面的向量,而这个向量的写操作如果不受保护的话,会写“混”了。不过,如果全局都是这种写混的状态的话....结果也说不准
(4)Gibbs采样是概率算法,已经验证过,即便是参数相同,每次运行的结果也不同。
完。
转载请注明出处:http://blog.csdn.net/xceman1997/article/details/46582637