机器学习 GBDT+xgboost 决策树提升
xgboost
xgboost是一个监督模型,它对应的模型就是一堆CART树,即由CART树组成的随机森林。预测的最终结果是由随机森林中的所有CART树的预测分数相加。
总而言之xgboost的想要解决的问题是通过前t-1棵的预测值加和我们是否能算出第t棵树的最优预测值?
CART(Classify and Regression Tree)
此处不多赘述,决策树一章会有较多描述。简而言之,CART就是既可以做分类又可以做回归的树。无论是做分类还是做回归他的叶节点(预测结果)都会有一个权重值(分数),而不是一个确定的类别。不清楚CART树不影响你理解xgboost。
GBDT(Gradient Boosting Desicion Tree)
GB思想(Gradient Boosting)
GB通过迭代多棵树共同决策,并且每一棵树学的是之前所有树的预测值的总和与真实值之间的残差,残差值=真实值-之前所有树的结论和
。例如nick的年龄是25岁,第一棵树预测年龄是12岁,相差13岁,即残差值为13;那么第二棵树我们把nick的年龄设置为13岁去学习,如果第二棵树真的能把nick分到13岁的叶子节点,那累加两棵树的预测值加和就是nick的真实年龄25岁;如果第二棵树的结论是10岁,仍存在3岁的残差,第三棵树nick的年龄就设置为3岁去学习,继续重复上述过程学习,不断逼近真实值……
DT树(Desicion Tree)
DT树称为回归树,它不是分类树,类似CART树
横空出世的前向分步算法
前向分步算法:每一颗树的预测值都和上一棵树有关系
\(T(x,\theta_m)\)表示第m棵决策树预测的值;\(\theta_m\)为第m棵决策树的参数;\(M\)表示决策树的总个数;\(f_m(x)\)是第m棵决策树的预测值;\(f_M(x)\)即所有预测数的预测值总和,即最终预测结果
\(f_0(x)=0\)
\(f_1(x)=f_0(x)+T(x;\theta_1)\)
\(\cdots\)
\(f_m(x)=f_{m-1}(x)+T(x,\theta_m),m=1,2,\cdots,M\)
\(f_M(x)=\sum_{m=1}^MT(x;\theta_m)\)
GB再解释
\(L\)是关于\(\theta_m\)的自定义代价函数,类似于线性回归的代价函数,即在已知真实值和预测值的情况下求最优回归系数\(\theta_m\);\(\hat{y_m}\)是第m棵决策树的预测值
\(J(\theta_m)=\sum_{m=1}^NL(y_m,\hat{y_m})\)
代入前向分步算法的结论\(f_m(x)=f_{m-1}(x)+T(x,\theta_m),m=1,2,\cdots,M\)
\(J(\theta_m)=\sum_{i=1}^NL(y_m,f_{m-1}(x_i)+T(x_m;\theta_m))\)
由上述代价函数可以求出最优解\(\theta_M\),即第m颗数的参数
我们假设\(L\)代价函数使用的是MSE(均方误差):\(L(y,\hat{y})=(y-\hat{y})^2\),既得
\(L(y_m,f_{m-1}(x)+T(x;\theta_m))\)
\(=(y-f_{m-1}(x)-T(x;\theta_m))^2\)
代入\(r=y-f_{m-1}(x)\)(第m颗数和m-1棵树的残差值)得
\((y-f_{m-1}(x)-T(x;\theta_m))^2=(r-T(x;\theta_m))^2\)
GBDT
后记:暂时没时间解释GBDT,GBDT是一阶泰勒展开,xgboost是二阶泰勒展开,其中细节我下次会写一篇专门来介绍GBDT,但是上述的介绍需要看,因为GB和DT是GBDT的基础也是xgboost的基础。
# TODO 哈哈哈!
大BOSS——xgboost
训练xgboost
假设我们获取了xgboost的模型和它的目标函数,现在我们的任务就是最小化目标函数\(J(\theta)\)找到最佳的\(\theta\),但是这个参数是什么呢?xgboost由一堆CART树组成,因此这个参数很明显存在于每颗CART树中。但是CART树的参数又是什么呢?CART树如果被确定之后,子节点是可以丢掉的,剩下的只有每一个叶子节点以及每一个叶子节点上的分数,这就是CART树的参数即xgboost的参数,还是不清楚继续往下看。
xgboost模型
我们借鉴前向分步算法和GB的思想优化我们的M棵树,其中\(\hat{y}_i^{(t)}\)表示第t棵树的预测值;\(f_m(x_i)\)表示第m棵CART树在\(x=x_i\)的值,即这棵树对应的叶节点的值
\(\hat{y}_i^{(0)}=0\)
\(\hat{y}_i^{(1)}=0+f_1(x_i)=\hat{y}_i^{(0)}+f_1(x_i)\)
\(\hat{y}_i^{(2)}=0+f_1(x_i)+f_2(x_i)=\hat{y}_i^{(1)}+f_2(x_i)\)
\(\cdots\)
\(\hat{y}_i^{(t)}=\sum_{m=1}^tf_m(x_i)=\hat{y}_i^{(t-1)}+f_t(x_i)\)
假设我们有M棵树,由此我们可以得到一个关于xgboost关于某个\(y_i\)的预测值\(\hat{y_i}\)的模型,即
\(\hat{y_i}=\sum_{m=1}^Mf_m(x_i)\)
目标函数
通过真实值和预测值以及xboost模型我们能得到一个目标函数,该目标函数假设存在一个\(L\)代价函数和一个正则项\(\sum_{i=1}^t\Omega(f_k)\)(类似于线性回归的L1、L2正则化,之后会详细解释,此处是t棵树的正则化项加和),现在假设我们有t棵树,n个训练样本,既得一个目标函数
\(J(\theta)=\sum_{i=1}^nL(y_i^t,\hat{y}_i^{(t)}))+\sum_{i=1}^t\Omega(f_i)\)
如果我们假设\(C\)是t-1棵树的正则化项加和,并且代入xgboost的模型,得
\(J(\theta)=\sum_{i=1}^nL(y_i^t,\hat{y}_i^{(t-1)}+f_t(x_i))+\Omega(f_t)+C\)
泰勒展开式:\(f(x+\Delta{x})\approx{f(x)}+f'(x)\Delta{x}+{\frac{1}{2}}f''(x)\Delta{x^2}\)
假设\(f(x)=\hat{y}_i^{(t-1)}\)
假设\(\Delta=f_t(x_i)\)
假设\(gi=\partial_{\hat{y}_i^{(t-1)}}L(y_i^t,\hat{y}_i^{(t-1)})\)
假设\(hi=\partial_{\hat{y}_i^{(t-1)}}^2L(y_i^t,\hat{y}_i^{(t-1)})\)
在这些假设的基础上,我们假设存在一个代价函数\(L\),我们可以把\(J(\theta)\)泰勒二阶展开:
\(J(\theta)=\sum_{i=1}^nL(y_i^t,\hat{y}_i^{(t)}))+\sum_{i=1}^t\Omega(f_i)\)
\(=\sum_{i=1}^nL(y_i^t,\hat{y}_i^{(t-1)}+f_t(x_i))+\Omega(f_t)+C\)
\(=\sum_{i=1}^n[L(y_i^t,\hat{y}_i^{(t-1)})+g_if_t(x_i)+{\frac{1}{2}}h_if_t^2(x_i)]+\Omega(f_t)+C\)
\(y_i^t\)和\(\hat{y}_i^{(t-1)}\)已知,即\(L(y_i^t,\hat{y}_i^{(t-1)})\)是一个常数项(因为我们假设了这个代价函数\(L\)是已知的一个代价函数,可以是MSE,可以是MSA,可以是任何一个已知的代价函数);\(C\)是前t-1棵树的正则化项加和,也是一个常数,这两个常数项对目标函数求最小值无意义,因此我们可以去掉,既得
\(J(\theta)=\sum_{i=1}^n[g_if_t(x_i)+{\frac{1}{2}}h_if_t^2(x_i)]+\Omega(f_t)\)
现在如果我们假设损失函数\(L\)使用的是MSE,那么上述式子会变成:
\(J(\theta)=\sum_{i=1}^n(y_i^t-(\hat{y}_i^{(t-1)}+f_t(x_i)))^2+\Omega(f_t)+C\)
\(=\sum_{i=1}^n((y_i^t-\hat{y}_i^{(t-1)})-f_t(x_i))^2+\Omega(f_t)+C\)
\(=\sum_{i=1}^n[(y_i^t-\hat{y}_i^{(t-1)})^2-2(y_i^t-\hat{y}_i^{(t-1)})f_t(x_i)+f_t(x_i)^2]+\Omega(f_t)+C\)
去掉常数项可以得到
\(J(\theta)=\sum_{i=1}^n[-2(y_i^t-\hat{y}_i^{(t-1)})f_t(x_i)+f_t(x_i)^2]+\Omega(f_t)\)
如果你代入验证很明显可以发现我们使用泰勒展开式得到的式子是没有问题的
其实走到这里我们的xgboost已经算是完结了,是不是忘了我们在做什么,哈哈!我们在做的是通过前t-1棵的预测值加和我们是否能算出第t棵树的最优预测值
正则化项处理
如线性回归的正则化项一样,你可以使用L1正则化,你也可以使用L2正则化……你高兴就行。这里我就讲讲我对xgboost使用的正则化项。
正则化前我们先对CART树做处理
假设一棵树有T个叶子节点,这T个叶子节点组成了一个T维向量\(w\),而\(q(x)\)是一个映射,用来将样本映射成1到T的某个值,即\(q(x)\)表示了CART树的结构,\(w_q(x)\)表示了这棵树对样本x的预测值
\(f_t(x)=w_{q(x)},w\in{R^T},a:R^d\rightarrow\{1,2,\cdots,T\}\)
由此我们可以假设xgboost的正则化项
\(\Omega(f_t)=\gamma{T}+{\frac{1}{2}}\lambda\sum_{j=1}^T{w_j^2}\)
\(\gamma\)和\(\lambda\)是我们自定义的一个数(类似线性回归的学习率),如果\(\gamma\)越大,表示希望获得结构简单的树,因为\(\gamma\)越大对叶子节点多的树惩罚更大;\(\lambda\)越大也是如此。
至于选择这个正则化项的原因,自己百度"奥卡姆剃刀",哈哈哈~
理论终章
这个时候我们有了泰勒二阶展开的目标函数,有了自定义的正则化项,我们可以把自定义的正则项代入目标函数中
\(J(\theta)=\sum_{i=1}^n[g_if_t(x_i)+{\frac{1}{2}}h_if_t^2(x_i)]+\gamma{T}+{\frac{1}{2}}\lambda\sum_{j=1}^T{w_j^2}\)
代入\(f_t(x)=w_{q(x)}\),得
\(J(\theta)=\sum_{i=1}^n[g_iw_{q(x_i)}+{\frac{1}{2}}h_i{w_{q(x_i)}^2}]+\gamma{T}+{\frac{1}{2}}\lambda\sum_{j=1}^T{w_j^2}\)
这个时候我们需要考虑,如果一个叶子节点上难道只会对应一个样本吗?很明显如果样本很多,一个叶子可能会对应多个样本。因此我们用\(I_j\)表示一个叶子节点上的所有样本,即\(\sum_{i\in{I_j}}\)对应一个叶子节点上所有样本的对应值的加和,我们需要计算的就是T个叶子节点上的样本预测值的加和,这也是为什么用\(\sum_{j=1}^T\)开头的原因
\(J(\theta)=\sum_{j=1}^T{[(\sum_{i\in{I_j}}g_i)w_j+{\frac{1}{2}}(\sum_{i\in{I_j}}hi)w_j^2]+\gamma{T}+{\frac{1}{2}}\lambda\sum_{j=1}^T{w_j^2}}\)
\(=\sum_{j=1}^T{[(\sum_{i\in{I_j}}g_i)w_j+{\frac{1}{2}}(\sum_{i\in{I_j}}hi+\lambda)w_j^2]+\gamma{T}}\)
假设\(G_j=\sum_{i\in{I_j}}g_i\)
假设\(H_j=\sum_{i\in{I_j}}h_i\)
\(J(\theta)=\sum_{j=1}^T[G_jw_j+{\frac{1}{2}}(H_j+\lambda)w_j^2]+\gamma{T}\)
通过上式我们可以对目标函数对\(w\)求偏导找到最优\(w^{*}\)
\({\frac{\partial{J(f_t)}}{\partial{w_J}}}=G_j+(H_j+\lambda)w_j==0\Rightarrow{w_j^*}=-{\frac{G_j}{H_j+\lambda}}\)
回代最优\(w^{*}\)得
\(J(\theta)^*=-{\frac{1}{2}}\sum_{j=1}^T{\frac{G_j^2}{H_j+\lambda}}+\gamma{T}\)
因为\(J(\theta)^*\)的推导过程中只和\(G_j\)和\(H_j\)和有关,而它们又只和树的结构\(q(x)\)有关,这表示\(J(\theta)^*\)代表了这颗树的结构有多好,值越小,代表这样的结构越好。
最终章-拨开云雾见月明
现在我们假设我们有一家五口的数据,见下表
儿子 | 妈妈 | 爸爸 | 奶奶 | 爷爷 | |
---|---|---|---|---|---|
g1,h1 | g2,h2 | g3,h3 | g4,h4 | g5,h5 |
儿子+妈妈\(G_L=g_1+g_2\)
爸爸+奶奶+爷爷\(G_R=g_3+g_4+g_5\)
\(J(\theta)^*=-{\frac{1}{2}}\sum_{j=1}^T{\frac{G_j^2}{H_j+\lambda}}+\gamma{T}\)
如果我们不对这5个样本分开,把数据代入\(J(\theta)\),他们的目标值是\({\frac{1}{2}}{\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}}\)
如果我们把他们五个人按照年龄排列并从空格列分开,即该决策树会有两个叶子,一个叶子会有儿子+妈妈的分数;另一个叶子会有爸爸+奶奶+爷爷的分数
把数据代入\(J(\theta)\)目标值是\({\frac{1}{2}}[{\frac{G_L^2}{H_L+\lambda}}+{\frac{G_R^2}{H_R+\lambda}}]\)
由此可以计算Gain值
\(Gain={\frac{1}{2}}[{\frac{G_L^2}{H_L+\lambda}}+{\frac{G_R^2}{H_R+\lambda}}-{\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}}]+\gamma\)
总结:该Gain值是单节点的目标值减去切分后的所有节点的目标值,Gain值如果是正的,并且Gain值越大,就越值得切分,然后不断重复上述过程;如果Gain值是负的,表明切分后目标值变大了。而\(\gamma\)在这里控制目标值的下降幅度。(类似于信息增益,并且相比较传统的GBDT,xgboost使用了二阶泰勒展开,可以更快的在训练集上收敛),虽然xgboost需要计算每个样本的g和h值,但是xgboost使用了并行/多核运算,这都不是问题。
多说一嘴
其实聪明的同学已经发现了我们的\(\theta\)这个参数完全可以看成\(f_t\),它表示的是第t颗树的结构,也就可以看成我们的\(\theta\)呀?不是吗?嘻嘻,你仔细思考下。当然\(f_t\)也是我们自己定义的。哈哈。
最后多说一嘴,xgboost成型库已经很牛逼了,你只需要会调参就行了,看到这里也不会让你白看,你真的能看懂并理解,他人调参需要1000次,你可能10次就解决了。
不多说,此处需要掌声!!!