Home Gradient Boosting Decision Tree 相关笔记整理
Post
Cancel

Gradient Boosting Decision Tree 相关笔记整理

首先论文

Greedy Function Approximation: A Gradient Boosting Machine.

一些相关的开源实现

先贴几套开源实现代码的地址,这里主要研究的2,3,其中2是c++版的残差版本,3中的MART也是残差版本实现,最近在做ReRank相关的事情刚好要用到LambdaMART

  1. XgboostXgboost源码-github Xgboost文档
  2. C++版 gbdt源码下载地址-CSDN 10积分 github修改版地址
  3. Ranklibsourceforge地址
  4. simple-gbdtgoogle code地址 依赖tbb库github 某同学fork版本
  5. elf项目sourceforge地址
  6. Spark中的GradientBoostedTreesgithub地址

暂且简单描述下

  • 1中Xgboost支持力度很大,支持python,R,Java.etc 甚至spark
  • 2中所指gbdt网上有几篇分析的文章都是用的这个版本,这个版本训练是没啥问题,不过predict的时候不友好,感觉简化了。修改了下
  • 3中Ranklib支持的算法也很多,基本可以开包即用了.
  • 4中simple-gbdt,依赖tbb库.
  • Spark中的实现

讲到GBDT的时候首先应该指出是残差版本还是Gradient版本,因为在原理,求解,实现上存在一些差异(这个差异在理解上可能会导致犯迷糊,反正自己绕不少弯路),这里主要讨论残差版本。xgboost目前也在使用,还没深入研究,这里主要研究2和3中的版本。2,3也有讲解源代码的文章了。Ranklib的实现比较好理解。

Future选取问题

可以随机选择rand_fea_num个特征进行分裂,确定最优的分裂特性 2中C++代码实现

1
2
3
4
for (int i = 0; i < gbdt_inf.rand_fea_num; ++i) 
 {
  int select = rand() % (last+1);//随机选择一个特征
 }

分裂问题,

分裂:分裂后的均方误差最小

Best split标准计算依据

计算公式:

游园惊梦-GBDT原理实例演示 2

2中C++代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for (int j=ninf.index_b; j< ninf.index_e; j++)
{
// d = y_result_score[data_set->order_i[j]];
d = data_set->y_list[data_set->order_i[j]];
  left_sum += d;
  right_sum -= d;
  left_num++;
  right_num--;
if (data_set->fv[j] < data_set->fv[j+1])
  {/** 均方误差Mean Squared Error, MSE)最小? */
	crit = (left_sum * left_sum / left_num) + (right_sum * right_sum / right_num) - ninf.critparent;
  	if (crit > critvar) 
	{
		tmpsplit = (data_set->fv[j] + data_set->fv[j+1]) / 2.0; // 实际分割用的feature value
		critvar = crit;
	}
  }
}

if (critvar > critmax) // 如果这个feature最终的critvar > cirtmax, 保存信息
{
spinf->bestsplit = tmpsplit; // split feature vale
  spinf->bestid = fid; // split feature id
  critmax = critvar; // split crit vaule
}

节点的输出值

节点的输出值为该节点上所有sample的label的均值 Ranklib中MART的实现,在GBDT中pseudoResponses就是残差,也就是label

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void updateTreeOutput(RegressionTree rt)
{
	List<Split> leaves = rt.leaves();
	for(int i=0;i<leaves.size();i++)
	{
		float s1 = 0.0F;
		Split s = leaves.get(i);
		int[] idx = s.getSamples();
		
		System.out.println("leaves(i)" + i +" idx.length: "+idx.length);
		for(int j=0;j<idx.length;j++)
		{
			int k = idx[j];
			s1 += pseudoResponses[k];
		}
		s.setOutput(s1/idx.length);
	}
}

2中的代码gbdt_test.cpp 中跑test的时候尽然是一行输入一行输出? 修改了几行代码

1
2
3
char* test_file_name = argv[2];
std::ifstream fin(test_file_name,std::ios::in)
while(getline(fin,line))

进入gbdt的目录

1
2
3
4
5
cd lib_gbdt
make all
cd output/test
./gbdt-train -r 0.8 -t 100 -s 0.03 -n 30 -d 5 -m test.model -f ../../train
./gbdt-test ./test.model ../../train

GDB调试代码

1
gdb  ./gbdt-train  

可以设置参数,断点,执行

1
2
(gdb) set args -r 0.8 -t 100 -s 0.03 -n 30 -d 5 -m test.model -f ../../train 
(gdb) show args

参考链接

This post is licensed under CC BY 4.0 by the author.