随机森林算法的加速:OpenMP方法

项目主页:randomforest github, gitee
C++ implementation of random forests classification, regression, proximity and variable importance.
推荐阅读:

  1. Random Forests C++实现:细节,使用与实验
  2. 随机森林特征重要性(Variable importance)评估方法
  3. 随机森林Proximity实现及应用

前言

  OpenMP用于编写多线程应用程序的 API。它是一组编译器指令和库例程,使并行编程比使用显式线程更容易它实际上大大简化了用 Fortran 、C/ C++ 编写多线程编程程序。主流编译器(gcc/g++,Visual C++)都支持OpenMP,在Windows和Linux操作系统上都比较容易上手,对原始代码的侵入较少。
  随机森林由N棵随机树(Randomized Trees)组成,在训练与其他衍生算法中,很多情形下每棵树内的运算是独立的,且与树的顺序无关。因此,可以采用OpenMP进行加速,将对随机树的运算分配到多个线程中,然后由CPU的多核进行并行处理。主要采用OpenMP对for循环加速功能,将顺序执行的循环并行化,大大提升了算法整体速度。下面的章节介绍了OpenMP在随机森林加速中的应用,也对OpenMP的使用方法进行简单解释。主要分为对随机森林训练算法的加速、对预测算法的加速、对Proximity的加速以及对特征重要性算法的加速给出了具体的实验结果。

使用OpenMP

对RF训练的加速

代码

随机森林训练其中每棵随机数是独立的过程,所以很容易采用OpenMP对训练每棵随机树的循环进行并行化。

#include "omp.h"
... ...
int TrainRandomForestClassifierOMP(float **data, int *label, RandomCForests_info RFinfo, LoquatCForest *&loquatForest, int jobs, int trace)
{
    ... ...
    jobs = jobs>omp_get_max_threads() ? omp_get_max_threads() : jobs;
	omp_set_num_threads(jobs);
	int growed_num = 0;
	... ...
#pragma omp parallel for 
	for (int i = 0; i < Ntrees; i++)
	{
		GrowRandomizedCLoquatTreeRecursively(data, label, RFinfo, loquatForest->loquatTrees[i]);
		#pragma omp critical  
		{
			growed_num++;
			if (trace > 0 && growed_num % trace == 0)
			{
				cout << "Tree: " << growed_num << endl;
			}
		}
	}
	... ...
	return 0;
}

omp_set_num_threads 设置了线程数量,如果线程数<=CPU核心数,那么每个线程可以又一个独立核心处理,所以这个线程数应该设置为CPU核心数或更少;
#pragma omp parallel for 对紧跟其后的for循环进行多核并行加速;
#pragma omp critical 指定后面大括号里的代码块同一时间只能被一条线程执行的代码区域,为了是统计到目前为止有多少颗随机树完成训练。

加速效果

实验平台为:Intel i5-13400F(10核), win11操作系统
对不同数据集进行训练,记录原始算法实现(without OpenMP)、4线程 (With OpenMP T=4)与8线程 (With OpenMP T=8) 加速,三种情况下的训练耗时。

数据集 (样本数)参数Without OpenMP (秒)With OpenMP T=4 (秒)With OpenMP T=8 (秒)
mnist (60000)[200, 27*, 40, 2]97.327.616.843
pendigits (7494)[200,4*, 40, 2]1.8690.5270.309
MAGIC_Gamma_Telescope (19020)[200,3*, 40, 2]9.0662.5541.464
Sensorless_drive_diagnosis (58509)[200,6*, 40, 2]50.0213.897.91

理想情况下,4线程和8线程应该分别为原始算法耗时的 1 4 \frac {1}{4} 41 1 8 \frac {1}{8} 81。从实验结果来看,加速倍数与理论接近,考虑到线程调度等也需要耗时,所以实际并没有达到理论倍数是正常的。

对特征重要性评估的加速

特征重要性的实现请见:随机森林特征重要性(Variable importance)评估方法
完整代码:项目主页–randomforest github, gitee

代码

#pragma omp parallel for 
for (tr = 0; tr < Ntrees; tr++)
{
	struct LoquatCTreeStruct* pTree = loquatForest->loquatTrees[tr];
	int *pIndex = pTree->outofbag_samples_index;
	int oobnum = pTree->outofbag_samples_num;

	if (pIndex == NULL || oobnum == 0)
		continue;
	oobFound = true;
	oobOfTrees[tr] = oobnum;
	int* permuted_order = new int[oobnum];

	int index, predicted_class_index;
	float confidence = 0.f;
	int correct_num = 0, correct_num_premute = 0;
	int i = 0;
	for (; i < oobnum; i++)
	{
		index = pIndex[i];
		PredictAnTestSampleOnOneTree(data[index], variables_num, pTree, predicted_class_index, confidence);
		if (predicted_class_index == label[index])
			correct_num++;
	}

	float* tmp_data = new float[variables_num];
	for (int var = 0; var < variables_num; var++)
	{
		// permuting (var)th variables
		// permute [0, oobnum-1]
		permute(oobnum, permuted_order);

		for (correct_num_premute = 0, i = 0; i < oobnum; i++)
		{
			index = pIndex[permuted_order[i]];
			memcpy(tmp_data, data[pIndex[i]], variables_num * sizeof(float));
			tmp_data[var] = data[index][var];
			PredictAnTestSampleOnOneTree(tmp_data, variables_num, pTree, predicted_class_index, confidence);
			if (predicted_class_index == label[pIndex[i]])
				correct_num_premute++;
		}

		DeltMatrix[tr][var] = correct_num - correct_num_premute;
	}

	delete [] permuted_order;
	delete [] tmp_data;
}

for (int v=0; v<variables_num; v++)
		for (int t = 0; t < Ntrees; t++)
		{
			mean_var[v] += DeltMatrix[t][v] / (float)oobOfTrees[t];
		}
// raw_score <- mean_var
... ...

与训练加速的方法相同,也是通过for循环的并行化进行加速。与原始代码相比,OpenMP版本的代码将循环内要用到的变量都在循环内部定义,这样每个循环就有一份独立的变量,并行时不会造成冲突。

加速效果

数据集 (样本数,特征数)参数Without OpenMP (秒)With OpenMP T=4 (秒)With OpenMP T=8 (秒)
mnist (60000,720)[200, 27*, 40, 2]958947201681
pendigits (7494,16)[200,4*, 40, 2]1.3640.4300.323
MAGIC_Gamma_Telescope (19020,10)[200,3*, 40, 2]17.064.8062.965
Sensorless_drive_diagnosis (58509,48)[200,6*, 40, 2]915.83261.02156.57

附录

Windows中使用OpenMP

Visual studio中开启openmp的方法见下图
在这里插入图片描述
开启OpenMP后多核处理的截图如下
在这里插入图片描述

Linux中使用OpenMP

编译时增加命令 -fopenmp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值