GBDT算法
import numpy as np
from collections import Counter
from sklearn.ensemble import GradientBoostingRegressor
import copy
# gbdt回归数例子
data = np.array([[5, 20, 1.1],
[7, 30, 1.3],
[21, 70, 1.7],
[30, 60, 1.8]
])
# 计算损失函数
def calc_mse(data):
if len(data) == 0:
return 0
label = data[:, -1]
return np.var(label) * len(label)
# 切分分类数据
def split_data(data, feat, val, data_type='classifier'):
if data_type == 'classifier':
arr1 = data[np.nonzero(data[:, feat] == val)]
arr2 = data[np.nonzero(data[:, feat] != val)]
else:
arr1 = data[np.nonzero(data[:, feat].astype(float) < val)]
arr2 = data[np.nonzero(data[:, feat].astype(float) >= val)]
return arr1, arr2
# 连续变量的切分点处理
def continuity_params_process(arr, feat):
c = arr[:, feat].astype(float)
c_sort = sorted(set(c))
new_c = []
for i in range(len(c_sort) - 1):
val = (c_sort[i] + c_sort[i + 1]) / 2
new_c.append(val)
return new_c
# 选择最好的切分点
# 满足基尼系数减少最快的方向
def select_split(data):
min_gini = np.inf
best_feat = None
best_val = None
left = None
right = None
data_type = 'continuity'
for i in range(data.shape[1] - 1):
c_set = set(data[:, i])
for val in c_set:
arr1, arr2 = split_data(data, i, val, data_type)
g1 = calc_mse(arr1)
g2 = calc_mse(arr2)
# g = len(arr1) / len(data) * g1 + len(arr2) / len(data) * g2
g = g1 + g2 # 获取剩下最小的均方误差
# print(i,val,g)
if min_gini > g:
min_gini = g
best_feat = i
best_val = val
left = arr1
right = arr2
return best_feat, best_val, left, right
# 构建递归树
def create_tree(data):
tree = {}
if len(set(data[:, -1])) <= 1:
return data[:, -1][0]
# 如果数据的特征一模一样,则无法进一步切分
# 返回
dd = data[:, :-1].tolist()
ddd = list(map(tuple, dd))
cc = Counter(ddd)
if len(cc) == 1:
return np.mean(data[:, -1])
best_feat, best_val, left, right = select_split(data)
tree[(best_feat, best_val, 'left')] = create_tree(left)
tree[(best_feat, best_val, 'right')] = create_tree(right)
return tree
# 构建gbdt回归树
def create_gbdt(dataset, n_estimators, lr):
'''
:param data: 输入数据
:param n_estimators: 弱分类器的个数
:param lr: 学习率,也叫残差学习率
:return:
'''
data = copy.copy(dataset)
tree_list = []
tree_list.append(np.mean(data[:, -1]))
y = dataset[:, -1]
data[:, -1] = np.mean(data[:, -1])
for i in range(n_estimators):
data[:, -1] = data[:, -1] + lr * (y - data[:, -1])
tree = create_tree(data)
return tree
# 预测单颗树
def predict(tree, X):
if type(tree) != dict:
return tree
for key in tree:
if X[key[0]] < key[1]:
r = tree[(key[0], key[1], 'left')]
else:
r = tree[(key[0], key[1], 'right')]
return predict(r, X)
n_estimators = 3 # 估计数
gbdt_tree = create_gbdt(data, n_estimators, 0.1)
print("create gbdt:", predict(gbdt_tree, data[0, :-1]))
gbdt = GradientBoostingRegressor(n_estimators=n_estimators, learning_rate=0.1, max_depth=2)
gbdt.fit(data[:, :-1], data[:, -1])
print("GBDT:", gbdt.predict([data[0, :-1]]))
1、关于GBDT回归树的原理参考这篇博文https://blog.csdn.net/zpalyq110/article/details/79527653,但想要深入理解算法的原理最好应该自己动手实现整个过程,本篇主要是用python简单实现了gbdt回归同时推导了下每次学习的残差与上一次迭代的关系
2、构建测试数据,做一个简单线性回归
def create_data():
X = []
for i in range(100):
x = 2 * i
y = 3 * i
z = i
l = x + y + z + np.random.rand() * 10
X.append([x,y,z,l])
return np.array(X)
data = create_data()
3、构建cart回归树,代码跟https://blog.csdn.net/Spirit_6275/article/details/89205571基本一样,只是会稍微改几个地方,一个损失函数改用均方误差,第二个是切分数据的时候以切分后的均方误差最小作为切分点
计算均方误差
def calc_mse(data):
if len(data) == 0:
return 0
label = data[:,-1]
return np.var(label) * len(label)
寻找最好的切分点
def select_split(data):
min_gini = np.inf
best_feat = None
best_val = None
left = None
right = None
data_type = 'continuity'
for i in range(data.shape[1]-1):
c_set = set(data[:, i])
for val in c_set:
arr1,arr2 = split_data(data,i,val,data_type)
g1 = calc_mse(arr1)
g2 = calc_mse(arr2)
# g = len(arr1) / len(data) * g1 + len(arr2) / len(data) * g2 #基尼用于分类
g = g1 + g2 # 获取剩下最小的均方误差
# print(i,val,g)
if min_gini > g:
min_gini = g
best_feat = i
best_val = val
left = arr1
right = arr2
return best_feat,best_val,left,right
4、构建gbdt回归树
def create_gbdt(dataset,n_estimators,lr):
'''
:param data: 输入数据
:param n_estimators: 弱分类器的个数
:param lr: 学习率,也叫残差学习率
:return:
'''
data = copy.copy(dataset)
tree_list = []
tree_list.append(np.mean(data[:, -1]))
data[:, -1] = data[:, -1] - np.mean(data[:, -1])
tree_list.append(create_tree(data))
for i in range(1,n_estimators):
data[:,-1] = (1 - lr) * data[:,-1] # 剩余残差
tree_list.append(create_tree(data))
return tree_list
XGBOOST算法
# ================基于XGBoost原生接口的分类=============
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score # 准确率
# 加载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565) # 数据集分割
# 算法参数
params = {
'booster': 'gbtree',
'objective': 'multi:softmax',
'num_class': 3,
'gamma': 0.1,
'max_depth': 6,
'lambda': 2,
'subsample': 0.7,
'colsample_bytree': 0.7,
'min_child_weight': 3,
'silent': 1,
'eta': 0.1,
'seed': 1000,
'nthread': 4,
}
plst = params.items()
dtrain = xgb.DMatrix(X_train, y_train) # 生成数据集格式
num_rounds = 500
model = xgb.train(plst, dtrain, num_rounds) # xgboost模型训练
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
# 计算准确率
accuracy = accuracy_score(y_test,y_pred)
print("accuarcy: %.2f%%" % (accuracy*100.0))
# 显示重要特征
plot_importance(model)
plt.show()
xgboost 基本方法和默认参数
XGBoost参数详解
在运行XGboost之前,必须设置三种类型成熟:general parameters,booster parameters 和 task parameters:
通用参数:该参数参数控制在提升(boosting)过程中使用哪种booster,常用的booster有树模型(tree)和线性模型(linear model)。
Booster参数:这取决于使用哪种booster。
学习目标参数:控制学习的场景,例如在回归问题中会使用不同的参数控制排序。
通用参数
booster [default=gbtree]:有两中模型可以选择gbtree和gblinear。gbtree使用基于树的模型进行提升计算,gblinear使用线性模型进行提升计算。缺省值为gbtree
silent [default=0]:取0时表示打印出运行时信息,取1时表示以缄默方式运行,不打印运行时信息。缺省值为0
nthread:XGBoost运行时的线程数。缺省值是当前系统可以获得的最大线程数
num_pbuffer:预测缓冲区大小,通常设置为训练实例的数目。缓冲用于保存最后一步提升的预测结果,无需人为设置。
num_feature:Boosting过程中用到的特征维数,设置为特征个数。XGBoost会自动设置,无需人为设置。
tree booster参数
eta [default=0.3] :为了防止过拟合,更新过程中用到的收缩步长。在每次提升计算之后,算法会直接获得新特征的权重。 eta通过缩减特征的权重使提升计算过程更加保守。缺省值为0.3
取值范围为:[0,1]。典型值为0.01-0.2。
gamma [default=0]:在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。
这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关,所以是需要调整的。
取值范围为:[0,inf]
max_depth [default=6] :数的最大深度。缺省值为6 。取值范围为:[1,∞]。需要使用CV函数来进行调优。典型值:3-10
min_child_weight [default=1] :孩子节点中最小的样本权重和。如果一个叶子节点的样本权重和小于min_child_weight则拆分过程结束。在现行回归模型中,这个参数是指建立每个模型所需要的最小样本数。这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。
但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。取值范围为:[0,∞]
max_delta_step [default=0] :我们允许每个树的权重被估计的值。如果它的值被设置为0,意味着没有约束;如果它被设置为一个正值,它能够使得更新的步骤更加保守。通常这个参数是没有必要的,但是如果在逻辑回归中类极其不平衡这时候他有可能会起到帮助作用。把它范围设置为1-10之间也许能控制更新。 取值范围为:[0,∞]
subsample [default=1]:用于训练模型的子样本占整个样本集合的比例。如果设置为0.5则意味着XGBoost将随机的从整个样本集合中随机的抽取出50%的子样本建立树模型,这能够防止过拟合。 取值范围为:(0,1]
colsample_bytree [default=1]:在建立树时对特征采样的比例。缺省值为1 。取值范围为:(0,1]
Linear Booster参数
lambda [default=0] :L2 正则的惩罚系数
alpha [default=0] :L1 正则的惩罚系数
lambda_bias :在偏置上的L2正则。缺省值为0(在L1上没有偏置项的正则,因为L1时偏置不重要)
学习目标参数
objective [ default=reg:linear ] :定义学习任务及相应的学习目标,可选的目标函数如下:
“reg:linear” —— 线性回归。
“reg:logistic”—— 逻辑回归。
“binary:logistic”—— 二分类的逻辑回归问题,输出为概率。
“binary:logitraw”—— 二分类的逻辑回归问题,输出的结果为wTx。
“count:poisson”—— 计数问题的poisson回归,输出结果为poisson分布。在poisson回归中,max_delta_step的缺省值为0.7。(used to safeguard optimization)
“multi:softmax” –让XGBoost采用softmax目标函数处理多分类问题,同时需要设置参数num_class(类别个数)
“multi:softprob” –和softmax一样,但是输出的是ndata * nclass的向量,可以将该向量reshape成ndata行nclass列的矩阵。没行数据表示样本所属于每个类别的概率。
“rank:pairwise” –set XGBoost to do ranking task by minimizing the pairwise loss
base_score [ default=0.5 ]
所有实例的初始化预测分数,全局偏置;
当有足够的迭代次数时,改变这个值将不会有太大的影响。
eval_metric [ default according to objective ]
校验数据所需要的评价指标,不同的目标函数将会有缺省的评价指标(rmse for regression, and error for classification, mean average precision for ranking)-
用户可以添加多种评价指标,对于Python用户要以list传递参数对给程序,而不是map参数list参数不会覆盖’eval_metric’
seed [ default=0 ]:随机数的种子。缺省值为0
xgboost 基本方法和默认参数
函数原型:
xgboost.train(params,dtrain,num_boost_round=10,evals=(),obj=None,feval=None,maximize=False,early_stopping_rounds=None,evals_result=None,verbose_eval=True,learning_rates=None,xgb_model=None)
params :这是一个字典,里面包含着训练中的参数关键字和对应的值,形式是params = {‘booster’:’gbtree’, ’eta’:0.1}
dtrain :训练的数据
num_boost_round :这是指提升迭代的个数
evals :这是一个列表,用于对训练过程中进行评估列表中的元素。形式是evals = [(dtrain,’train’), (dval,’val’)]或者是evals = [ (dtrain,’train’)], 对于第一种情况,它使得我们可以在训练过程中观察验证集的效果。
obj:自定义目的函数
feval:自定义评估函数
maximize: 是否对评估函数进行最大化
early_stopping_rounds: 早期停止次数 ,假设为100,验证集的误差迭代到一定程度在100次内不能再继续降低,就停止迭代。这要求evals 里至少有一个元素,如果有多个,按最后一个去执行。返回的是最后的迭代次数(不是最好的)。如果early_stopping_rounds 存在,则模型会生成三个属性,bst.best_score, bst.best_iteration, 和bst.best_ntree_limit
evals_result :字典,存储在watchlist 中的元素的评估结果。
verbose_eval(可以输入布尔型或数值型):也要求evals 里至少有 一个元素。如果为True, 则对evals中元素的评估结果会输出在结果中;如果输入数字,假设为5,则每隔5个迭代输出一次。
learning_rates :每一次提升的学习率的列表,
xgb_model:在训练之前用于加载的xgb model。