前面两篇笔记Boosting之Adaboost原理和Boosting之Adaboost简单实现主要介绍了Adaboost在二分类中的理论和简单实现,本文主要关注Python中Sklearn相关函数的调用,有空的话写写R的相关内容,主要参考资料是Sklearn的文档。
Python训练Adaboost分类模型
原理部分已经解释过,Adaboost模型可以看成加法模型,那么Adaboost的参数可以分为两个:Boosting框架的参数和基分类器的参数。直接看代码:
sklearn.ensemble.AdaBoostClassifier(base_estimator=None, n_estimators=50, learning_rate=1.0, algorithm=’SAMME.R’, random_state=None)
下面参照文档对上述参数做说明,插一句,英文水平真的急需提高~
1. 参数介绍
1.1 Boosting框架参数
上面代码中体现出的参数均可以看成框架参数,具体解释如下:
1. base_estimator
参数含义:基分类器,要求基分类器支持样本权重,默认是CART决策树
格式:文档看不懂The collection of fitted sub-estimators,实际上是基分类器名称+其参数
2. n_estimators
参数含义:迭代次数,默认50次,最大迭代次数是有限的,算法会在效果较好时,自动停止迭代
3. learning_rate
参数含义:学习率,该参数控制每个基分类器的权重,从算法原理看,相当于对最后的
f(x)
又做了一次缩放。该参数范围
\[0,1\]
,值越小,迭代次数越多,所以需要和n_estimators一起调参
4. algorithm
参数含义:指实现Adaboost两种算法,默认为SAMME.R算法,另有SAMME可选。两者异同:SAMME.R要求base_estimator可以返回预测概率,且一般来说迭代速度更快,SAMME算法文档见此。
5. random_state
默认为None,似乎是和随机数发生器有关系
1.2 基分类器参数
不同基分类器对应不同的参数,这里只讨论默认的决策树做弱分类器的参数(Python中决策树参数我还不清楚,此处主要参考Pinard的博客)。
1. max_features
参数含义:划分时考虑的最大特征数,默认”None”,即划分时考虑所有的特征数;其他候选值如”log2”为考虑
log2N
个特征,”sqrt”或”auto”为考虑
N−−√
个特征,绝对数代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数,其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的”None”就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。(似乎没有确定的结论,不同的数据集还是要很耐心的调参)
2. max_depth
参数含义:决策树最大深度,默认不输入,则建立子树时不限制子树的深度。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间
3. min_samples_split
参数含义:内部节点再划分所需最小样本数,默认为2。如果某个节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。这个一般在处理过拟合问题的使用,相当于控制决策树的深度。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
4. min_samples_leaf
参数含义:叶子节点最少样本数。如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
5. min_weight_fraction_leaf
参数含义:叶子节点最小的样本权重。这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
6. max_leaf_nodes
参数含义:最大叶子节点数。通过限制最大叶子节点数,可以防止过拟合,默认是”None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
2. Adaboost在Sklearn中的应用
下面的例子是Sklearn文档中的例子,没有涉及调参,更多的是通过实验数据集,体现Adaboost较好的性能,不妨一看。
2.1 层决策树和9层决策树
基分类器选择决策树,决策树参数不同处为最大深度分别为1和9.数据选取make_hastie_10_2,Sklearn上有该数据的构造方法,可以参看。生成12000个样本,前2000个样本划分成训练集,其余测试集。首先训练两个决策树模型,计算误差率。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
## zero_one_loss(y_true, y_pred, normalize=True, sample_weight=None)
#### normalize = True返回错分率,Flase返回错分个数
from sklearn.metrics import zero_one_loss
from sklearn.ensemble import AdaBoostClassifier
n_estimators = 400 ## 迭代次数or基分类器个数
# A learning rate of 1. may not be optimal for both SAMME and SAMME.R
learning_rate = 1.
## 数据来源:
## 数据类型及格式:ndarray X12000*10,y12000*1
X, y = datasets.make_hastie_10_2(n_samples=12000, random_state=1)
## 划分测试集和训练集 divided into train set and test set
X_test, y_test = X[2000:], y[2000:]
X_train, y_train = X[:2000], y[:2000]
## 指定基分类器的参数
dt_stump = DecisionTreeClassifier(max_depth=1, min_samples_leaf=1)
## 训练决策树模型
dt_stump.fit(X_train, y_train)
## score返回测试集中预测的错误率
dt_stump_err = 1.0 - dt_stump.score(X_test, y_test)## 0.459
## 调整决策树的深度
dt = DecisionTreeClassifier(max_depth=9, min_samples_leaf=1)
dt.fit(X_train, y_train)
dt_err = 1.0 - dt.score(X_test, y_test)##0.312
############################-----------------------------------############################
## 原脚本没有这部分内容
## 观察决策树的结构
## 画出深度为1的决策树的结构
# from sklearn import tree
# import sys
# import os
# os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'
# import pydotplus
# dot_data = tree.export_graphviz(dt_stump, out_file=None)
# graph = pydotplus.graph_from_dot_data(dot_data)
# graph.write_pdf("dt_stump.pdf")
## 画出深度为1的决策树的结构
# from sklearn import tree
# import sys
# import os
# os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'
# import pydotplus
# dot_data = tree.export_graphviz(dt, out_file=None)
# graph = pydotplus.graph_from_dot_data(dot_data)
# graph.write_pdf("dt.pdf")
2.2 基分类器为单层决策树之SAMME VS SAMME.R
下面对SAMME和SAMME.R做了个对比,基分类器及其参数都是一致的:单层决策树。通过staged_predict,分别计算SAMME和SAMME.R的迭代训练误差,迭代测试误差,即每轮迭代之后都会用所有分类器给出一个预测结果。
## 基分类器为单层决策树之SAMME VS SAMME.R
## SAMME算法本身还是不知道实现的逻辑啊,唉
#### 参数设置:基分类器为决策树树桩,学习率和迭代次数保持默认
#### SAMME算法
ada_discrete = AdaBoostClassifier(
base_estimator=dt_stump,
learning_rate=learning_rate,
n_estimators=n_estimators,
algorithm="SAMME")
#### 利用训练样本训练模型
ada_discrete.fit(X_train, y_train)
#### SAMME.R算法
ada_real = AdaBoostClassifier(
base_estimator=dt_stump,
learning_rate=learning_rate,
n_estimators=n_estimators,
algorithm="SAMME.R")
ada_real.fit(X_train, y_train)
## SAMME
### 测试集误差
ada_discrete_err = np.zeros((n_estimators,))
#### enumerate取出对应索引和索引位置的value
## staged_predict 返回的是每一轮迭代的预测值,但是不知道是每个分类器还每轮迭代之后所有基分类器的加权和
#### 返回结果i为迭代顺次,y_pred是一个列表,该轮迭代后的预测值{-1,+1}
#### zero_one_loss计算每轮迭代之后的错误率
for i, y_pred in enumerate(ada_discrete.staged_predict(X_test)):
ada_discrete_err[i] = zero_one_loss(y_pred, y_test)
### 训练集误差
ada_discrete_err_train = np.zeros((n_estimators,))
for i, y_pred in enumerate(ada_discrete.staged_predict(X_train)):
ada_discrete_err_train[i] = zero_one_loss(y_pred, y_train)
## SAMME.R
### 测试集误差
ada_real_err = np.zeros((n_estimators,))
for i, y_pred in enumerate(ada_real.staged_predict(X_test)):
ada_real_err[i] = zero_one_loss(y_pred, y_test)
### 训练集误差
ada_real_err_train = np.zeros((n_estimators,))
for i, y_pred in enumerate(ada_real.staged_predict(X_train)):
ada_real_err_train[i] = zero_one_loss(y_pred, y_train)
## py的画图函数不熟悉,简单说一说
%matplotlib inline
## 画出单层决策树和9层决策树的误差图
fig = plt.figure()
ax = fig.add_subplot(111)
## 单层决策树0.459 样式:黑色实线
ax.plot([1, n_estimators], [dt_stump_err] * 2, 'k-',
label='Decision Stump Error')
## 9层决策树0.312 样式:黑色虚线
ax.plot([1, n_estimators], [dt_err] * 2, 'k--',
label='Decision Tree Error')
#### 上面显示决策树的深度较大,训练误差越小
## SAMME 测试集误差 颜色:红色
ax.plot(np.arange(n_estimators) + 1, ada_discrete_err,
label='Discrete AdaBoost Test Error',
color='red')
## SAMME 训练集误差 颜色:蓝色
ax.plot(np.arange(n_estimators) + 1, ada_discrete_err_train,
label='Discrete AdaBoost Train Error',
color='blue')
## SAMME.R 测试集误差 颜色:橙色
ax.plot(np.arange(n_estimators) + 1, ada_real_err,
label='Real AdaBoost Test Error',
color='orange')
## SAMME.R 训练集误差 颜色:绿色
ax.plot(np.arange(n_estimators) + 1, ada_real_err_train,
label='Real AdaBoost Train Error',
color='green')
## 坐标轴名称和范围
ax.set_ylim((0.0, 0.5))
ax.set_xlabel('n_estimators')
ax.set_ylabel('error rate')
## 图例位置:右上;有边框设置
leg = ax.legend(loc='upper right', fancybox=True)
leg.get_frame().set_alpha(0.7)
plt.show()
从上图可以看出Adaboost在训练集上的准确率远高于简单调参的决策树(深度为1or9)。不管是训练集还是测试集,SAMME.R(Real)算法迭代速度,预测准度都是比SAMME(Discrete)更好的。另外,对于同一种算法,测试误差都是略高于训练误差的,情理之中,不赘。Sklearn还有不同分类器,多元分类等等案例,可以深入的学习,地址见此。
3. 实际调参中的应用
上面的demo对比了SAMME.R和SAMME算法,但是没有对框架参数做完整的调参,也没有探究基分类器参数对整体预测能力的影响。看了一些网格调参的东西,大概的逻辑是根据参数对模型性能的重要性,顺序调整参数。具体的逻辑可以参看GBDT的调参逻辑,后面会写,毕竟Adaboost是损失函数为指数损失函数的GBDT算法。那么Adaboost算法暂告一段落,后面会写一个Boosting三个算法的一个总结,如果有机会的话~
4 Ref
[1] Sklearn Adaboost参数介绍文档
[2] Sklearn Adaboost原理介绍文档
[3] Pinard博客
2018-04-29 于杭州