1.概述
CART, Classification And Regression Tree, 分类与回归树. 该模型由Breiman等人在1984年提出.
该模型中决策树是二叉树, 每个内部节点对应一个二元判断, 判断为 ‘真’, 走左子树, 否则走右子树.
1.2 GBDT
GBDT, Gradient Boosting Decision tree. 梯度提升决策树.
使用提升树的集成学习boosting思想, 使用梯度下降的优化方法, 以CART树做模型.
见参考[1].
2. 思想
类似于分类决策树中, 通过信息熵
度量分布的随机情况. 回归树中, 用方差总和
(即方差*该集合中样本数) 来度量.
-
公式
arg min j , s [ ∑ x i ∈ R 1 ( j , s ) ( y i − y ˉ ) 2 + ∑ x i ∈ R 2 ( j , s ) ( y i − y ˉ ) 2 ] (1) \arg \min_{j,s}[\sum_{x_i\in R_1(j,s)}(y_i-\bar y)^2+\sum_{x_i\in R_2(j,s)}(y_i-\bar y)^2] \tag 1 argj,smin[xi∈R1(j,s)∑(yi−yˉ)2+xi∈R2(j,s)∑(yi−yˉ)2](1)
其中 s ∈ 特 征 集 合 , j ∈ s 特 征 下 的 取 值 集 合 , R 1 , R 2 为 切 分 特 征 s 与 切 分 值 j 切 分 后 的 两 个 子 样 本 集 s \in 特征集合, j \in s特征下的取值集合, R_1, R_2为切分特征s与切分值j切分后的两个子样本集 s∈特征集合,j∈s特征下的取值集合,R1,R2为切分特征s与切分值j切分后的两个子样本集. -
伪代码
for 特征i in 特征集合:
for 特征取值j in 特征i的取值集合:
在 特征i 维度依据 取值j 将集合划分成两部分A与B
计算切分后的误差x
if( 该轮误差 <上轮误差):
split_feature=特征i
split_value=取值j
依据split_feature,split_value划分集合得到A B,得到左右子树,再递归地使用上述步骤建立完整的树.
3.代码
"""
回归决策树
主要方法为chooseBestSplit(), 依靠它递归地创建二叉树.
该代码体现了大致思想, 实际中还需要
tolN=切分的最小样本数, tolS=允许的最小误差下降值
来决定该次划分是产生叶子节点还是递归划分下去.
"""
import numpy as np
import pandas as pd
df=pd.read_csv('D:/code-study/py/diy/regression_problem_training_set.csv')
X = df.loc[:, ['feature1','feature2']]
y= np.ravel(df.loc[:, ['label']])
def chooseBestSplit(X,y):
'''
选择最佳split_feature和split_value
:param X:
:param y:
:return: split_feature,split_value
'''
min_total_error=float('inf')
# 双层for循环
for feature in X.columns:
series_of_feature=X[feature]
for value in set(series_of_feature):
X1,y1,X2,y2=split_by_feature_and_value(X,y,feature,value)
total_error=calc_error(y1)+calc_error(y2)
if(total_error<min_total_error):
min_total_error=total_error
split_feature=feature
split_value=value
return split_feature,split_value
def split_by_feature_and_value(X,y,split_feature,split_value):
'''
依照split_feature,split_value, 切分数据集
:param X:
:param y:
:return: 切分后的 X1,y1,X2,y2
'''
tmp=X.copy()
tmp['label']=y
tmp1=tmp[tmp[split_feature]>split_value]
tmp2=tmp[tmp[split_feature]<=split_value]
X1=tmp1.drop('label',1) # 在axis=1的维度上扔掉'label'列
y1=tmp1['label']
X2=tmp2.drop('label',1)
y2=tmp2['label']
return X1,y1,X2,y2
def calc_error(y):
'''
:param y:
:return: 方差*元素个数
'''
return np.var(y)*len(y);
print(chooseBestSplit(X,y))
改代码参考自 <<机器学习实战>>, [美] Peter Harrington, P164.
参考
- my blog, 集成学习-GBDT