简介:
GBDT是Gradient Boosting Decision Tree,梯度提升决策树,首先gbdt 是通过采用加法模型(即基函数的线性组合),以及不断减小训练过程产生的残差来达到将数据分类或者回归的算法。
- gbdt的训练过程
我们通过一张图片说明gbdt的训练过程:
解释:gbdt要经过多轮迭代,首先弱分类器1进行训练,分类器2在分类器1的残差的基础上进行训练,然后分类器3在分类器2的残差的基础上进行训练,继续迭代训练,直至达到迭代停止条件(达到设置的参数,精度要求等)。
对弱分类器的要求:足够简单,并且是低偏差高方差的。因为boosting的过程是一个不断降低偏差来提高最终分类器的精度的。
弱分类器一般选择为CART,根据上述对分类器的要求,CART的深度不会很深。最终的强分类器要将整个迭代过程产生的弱分类器进行加权求和。强分类器模型可以描述为:
模型一共训练m轮,生成m个弱分类器T(x; )。每个弱分类器的损失函数:
为当前的模型,gbdt通过经验风险极小化来确定下一个弱分类器的参数。
在传统机器学习算法中, GBDT算的上top3的算法。想要真正理解GBDT的真正意义,就必须理解GBDT中的Gradient Boosting和Decison Tree分别是什么!
一. Decision Tree:CART回归树
GBDT使用的决策树是CART回归树,无论是处理回归问题还是处理二分类,多分类问题,GBDT使用的决策树都是CART回归树。为啥不用CART分类树?因为CART分类树一般处理标称型数据,划分节点的最佳判别标准是某一特征对应的信息增益最大,但在回归树中的样本一般是连续数值,所以用信息增益作为判别标准已经不合适了,通常用最小误差平方和所对应的特征值作为节点的划分依据。下面通过具体代码详解CART回归树
CART回归树算法:
输入:数据集,叶节点中值的类型,划分节点时误差评判方法,允许的误差下降值,划分节点的最小样本数
输出:回归树
整体算法:
def createTree(dataSet, leaf_type=regLeaf, err_type=regErr, ops=(1, 4)):
#选择最佳划分特征及对应的特征值bestFeature, bestFeatureSplitValue
bestFeature, bestFeatureSplitValue = chooseBestFeatureSplit(dataSet, leaf_type, err_type, ops)
#根据最佳划分特征及对应的特征值将数据集分成两部分lset, rset
lset, rset = binSplitDataset(dataSet, bestFeature, bestFeatureSplitValue)
#将前面得到的两部分数据作为递归的输入,开始两次递归,创建整个决策树
ltree = createTree(lset, leaf_type, err_type, ops)
rtree = createTree(rset, leaf_type, err_type, ops)
#记录左右子树的信息
regTree={}
regTree['splitFIdx'] = bestFeature
regTree['splitFVal'] = bestFeatureSplitValue
regTree['left'] = ltree
regTree['right'] = rtree
# 返回决策树
return regTree
各个击破:
#按照特征及对应的特征值把整个数据集划分为两部分:一部分大于该特征值,一部分小于等于该特征值
def binSplitDataset(dataset, feature, feature_value):
mat0 = dataset[nonzero(dataset[:, feature] > feature_value)[0], :]
mat1 = dataset[nonzero(dataset[:, feature] <= feature_value)[0], :]
return mat0, mat1
def regErr(dataSet): #计算目标的总误差,
return var(dataSet[:, -1])*shape(dataSet)[0]
def regLeaf(dataSet): #生成叶节点,在回归树中是目标变量特征的均值
return mean(dataSet[:, -1])
#选择最佳划分特征及特征值
def chooseBestFeatureSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
print('hello, begin to choose best feature split')
tolS = ops[0] #由用户指定的参数,用于控制函数的停止,实际允许的误差下降值
tolN = ops[1] #切分的最小样本数
if len(set(dataSet[:, -1].T.tolist()[0])) == 1: #当输入数据集所有标签都相同时,说明节点纯度100%,函数返回
return None, leafType(dataSet)
minErr = 1e6
bestFeature = 0 #最佳划分特征对应的Id
bestFeatureSplitValue = 0.0 #最佳划分特征值
origin_error = errType(dataSet) #求未划分之前初始数据的总方差
num_sample, n = shape(dataSet) #获取样本数量及维度
for feature_idx in range(n - 1): #数据最后一列对应的目标变量不在遍历范围内
for split_value in dataSet[:, feature_idx]: #遍历特征Id为feature_idx的所有特征值
#feature_value作为拆分的阈值
l_data, r_data = binSplitDataset(dataSet, feature_idx, split_value)
if shape(l_data)[0] < tolN or shape(r_data)[0] < tolN: #划分出的两数据集大小不满足要求, 继续下一次划分
continue
splitErr = errType(l_data) + errType(r_data) #计算节点划分过程产生的误差
if minErr > splitErr: #寻找最小划分误差及对应的特征id,特征值
minErr = splitErr
bestFeature = feature_idx
bestFeatureSplitValue = split_value
if origin_error - minErr < tolS: #切分数据集后效果提升不够大, 则不应该进行切分工作而直接将该节点作为叶节点
return None, leafType(dataSet)
l_data, r_data = binSplitDataset(dataSet, bestFeature, bestFeatureSplitValue) #使用最佳特征值进行划分
# plt.subplot(121)
# plt.plot(l_data[:, 0], l_data[:, 1], 'ro')
# plt.plot(r_data[:, 0], r_data[:, 1], 'go')
# plt.show()
if shape(l_data)[0] < tolN or shape(r_data)[0] < tolN: #最佳特征值划分得到的结果不满足用户要求,则返回输