1.Boosting
Boosting 是一族可将弱学习器提升为强学习器的算法。这族算法的工作机制类似:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器;如此重复进行,直至基学习器数目达到事先指定的值 T , 最终将这 T 个基学习器进行加权结合。Boosting 算法工作机制如下图所示:
样本加权如下图所示,m 代表分类器序号,在第1个分类器中,样本初始化权重相等;经过第1个分类器训练,将被第1个分类器错分的样本权重增加,即下图中样本点被放大;随着不断的训练,被错分的样本权重不断增大,使得被错分的样本受到分类器更多的重视。
1.1 AdaBoost
优 点 :泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。
缺 点 :对离群点敏感。
适用数据类型:数值型和标称型数据。
——《机器学习实战》 P116 P 116
AdaBoost是adaptive boosting(自适应boosting )的缩写,是模型为加法模型、损失函数为指数函数、学习算法为前向分布算法的二分类学习方法。为了从所有弱分类器中得到最终的分类结果 ,AdaBoost为每个分类器都分配了一个权重值
α
α
,这些
α
α
值是基于每个弱分类器的误差率进行计算的。其中,加权误差率 e 的定义为:
其中 Gm(xi) G m ( x i ) 为基本分类器。其系数为:
样本的权值分布为:
其中 yi y i 为样本标记值,对于二分类, yi∈{−1,1} y i ∈ { − 1 , 1 } ; Zm Z m 为规范化因子,使得ω_{m+1,i}相加为1。
最终分类器为:
由 ωm+1,i ω m + 1 , i 可知被基本分类器 Gm(x) G m ( x ) 误分类样本的权值得以扩大,而被正确分类样本的权值得以缩小。因此,误分类样本将在下一轮学中起更大的作用。训练数据没有改变,而不断改变训练数据权值的分布,使得训练数据在基本分类器的学习中起不同的作用。
由 αm α m 可知其随着 em e m 的减小而增大,所以分类误差率越小的基本分类器在最终分类器中的作用越大。
1.2 GBDT
1.2.1 提升树
提升树是以分类树或回归树为基本分类器的提升方法。提升树被认为是统计学习中性能最好的方法之一。
以决策树为其函数的提升方法称为提升树(boosting tree)。提升树模型可表示为决策树的加法模型:
其中, T(x;Θm) T ( x ; Θ m ) 表示决策树; Θm Θ m 为决策树的参数;M为树的个数。
提升树采用前向分布算法,第m步的模型为:
fm−1(x) f m − 1 ( x ) 为当前模型,通过经验风险极小化确定下一棵决策树的参数 Θm Θ m :
L L 为损失函数,当采用平方误差损失函数时,即:
即提升树损失为:
r=y−fm−1(x) r = y − f m − 1 ( x ) 为当前模型的残差(residual)。对回归问题的提升树算法,只需简单地拟合当前模型的残差。
1.2.2 梯度提升
GBDT(gradient boosting decission tree,梯度提升树),由Freidman提出,利用最速下降法的近似方法,其关键是利用损失函数的负梯度在当前模型的值:
作为回归问题提升树算法中的残差的近似值,拟合一个回归树。
1.3 XGBoost
相对于传统的GBDT,XGBoost使用了二阶信息,可以更快的在训练集上收敛。
由于“随机森林族”本身具备过拟合的优势,因此XGBoost仍然一定程度的具有该特性。
XGBoost的实现中使用了并行/多核计算,因此训练速度快;同时他的原生语言为C/C++,这是它速度快的原因。
2.Boosting算法实现
2.1 AdaBoost
实现代码如下:
"""
单层决策树分类
Args:
dataMatrix - 数据矩阵
dimen - 特征列
threshVal - 阈值
threshIneq - 标志位
Returns: retArray - 分类结果
Author: dkjkls
Blog: https://blog.csdn.net/dkjkls
Modify: 2018/4/4 1:11
"""
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
retArray = np.ones((np.shape(dataMatrix)[0],1)) # 初始化retArray为1
if threshIneq == 'lt':
retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 # 若小于等于阈值,赋值为-1
else:
retArray[dataMatrix[:,dimen] > threshVal] = -1.0 # 若大于阈值,赋值为-1
return retArray
"""
基于权重向量生成单层决策树
Args:
dataArr - 数据矩阵
classLabels - 类别标签
D - 权重向量
Returns:
bestStump - 最佳单层决策树信息
minError - 最小误差
bestClassEst - 最优分类结果
Author: dkjkls
Blog: https://blog.csdn.net/dkjkls
Modify: 2018/4/8 23:07
"""
def buildStump(dataArr, classLabels, D):
dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T # 转换数据及数据标签为numpy矩阵
m, n = np.shape(dataMatrix) # 数据集的行数和列数
numSteps = 10.0 # 遍历的步数
bestStump = {} # 最佳单层决策树信息
bestClassEst = np.mat(np.zeros((m, 1))) # 初始化最佳预测值
minError = np.inf # 初始化最小误差为正无穷大
for i in range(n): # 遍历特征
rangeMin = dataMatrix[:, i].min() # 特征中的最小值
rangeMax = dataMatrix[:, i].max() # 特征中的最大值
stepSize = (rangeMax - rangeMin) / numSteps # 计算步长
for j in range(-1, int(numSteps) + 1): # 遍历步长
for inequal in ['lt', 'gt']: # 遍历大于、小于
threshVal = (rangeMin + float(j) * stepSize) # 计算阈值
predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) # 计算分类结果
errArr = np.mat(np.ones((m, 1))) # 初始化误差矩阵
errArr[predictedVals == labelMat] = 0 # 分类正确的赋值为0
weightedError = D.T * errArr # 计算加权错误率
if weightedError < minError: # 找到误差最小的分类方式,并赋值
minError = weightedError
bestClassEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClassEst
"""
基于单层决策树的AdaBoost训练
Args:
dataArr - 数据矩阵
classLabels - 类别标签
numIt - 迭代次数
Returns:
weakClassArr - 分类器数组
aggClassEst - 类别估计累计值
Author: dkjkls
Blog: https://blog.csdn.net/dkjkls
Modify: 2018/4/8 23:55
"""
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
weakClassArr = [] # 分类器数组
m = np.shape(dataArr)[0] # 数据集的特征值个数
D = np.mat(np.ones((m,1))/m) # 初始化权重向量
aggClassEst = np.mat(np.zeros((m,1))) # 初始化类别估计累计值
for i in range(numIt): # 根据迭代次数遍历
bestStump,error,classEst = buildStump(dataArr,classLabels,D) # 基于权重向量生成单层决策树
alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) # 计算权重alpha,防止发生除零溢出
bestStump['alpha'] = alpha # 记录权重值
weakClassArr.append(bestStump) # 存储最好的单层决策树
expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # 计算e的指数项
D = np.multiply(D, np.exp(expon)) # 计算新的权重向量
D = D / D.sum() # 权重向量归一化
aggClassEst += alpha * classEst # 计算类别估计累计值
aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1))) # 计算误差
errorRate = aggErrors.sum() / m
print("total error: ", errorRate)
if errorRate == 0.0: break # 误差为0则退出循环
return weakClassArr, aggClassEst
"""
AdaBoost分类函数
Args:
datToClass - 待分类样例
classifierArr - 训练好的弱分类器
Returns: 分类结果
Author: dkjkls
Blog: https://blog.csdn.net/dkjkls
Modify: 2018/4/9 1:17
"""
def adaClassify(datToClass,classifierArr):
dataMatrix = np.mat(datToClass) # 转换待分类样例为numpy矩阵
m = np.shape(dataMatrix)[0] # 待分类样例个数
aggClassEst = np.mat(np.zeros((m,1))) # 初始化类别估计累计值
for i in range(len(classifierArr)): # 遍历所有弱分类器
classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],
classifierArr[i]['thresh'],
classifierArr[i]['ineq']) # 计算类别估计值
aggClassEst += classifierArr[i]['alpha']*classEst # 根据权重累加得到类别估计累计值
print(aggClassEst)
return np.sign(aggClassEst)
if __name__ == '__main__':
dataArr,classLabels = loadSimpData()
weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels)
print(adaClassify([[0,0],[5,5]], weakClassArr))
使用sklearn.ensemble 实现 AdaBoost 代码如下:
from sklearn.ensemble import AdaBoostClassifier
# 决策树
base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)
# AdaBoost学习器,10棵树,学习率为0.1
model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)
# 训练AdaBoost学习器
model.fit(x_train, y_train)
# AdaBoost学习器预测训练集
y_train_pred = model.predict(x_train)
2.2 GBDT
使用sklearn.ensemble 实现 GBDT 代码如下:
from sklearn.ensemble import GradientBoostingClassifier
# GBDT,100棵树,学习率0.1,最大深度2
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=2)
# GBDT训练
gb.fit(x_train, y_train.ravel())
# GBDT预测训练集
y_train_pred = gb.predict(x_train)
2.3 XGBoost
使用xgboost实现代码如下:
import xgboost as xgb
# 设置参数
# max_depth - 树的深度为3
# eta - 学习率,为1时是原始模型,过小的学习率会造成计算次数增多,可防止过拟合,通过减少每一步的权重,可以提高模型的鲁棒性。典型值0.01-0.2
# silent-是否输出中间结果
# objective-定义需要被最小化的损失函数(分类问题的逻辑回归)
param = {'max_depth': 3, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}
watchlist = [(data_test, 'eval'), (data_train, 'train')] # 输出过程中的错误率
n_round = 7 # 7颗决策树
# 训练XGBoost
bst = xgb.train(param, data_train, num_boost_round=n_round, evals=watchlist)
# XGBoost预测数据集
y_hat = bst.predict(data_test)
3 Boosting实战
3.1 AdaBoost
使用Adaboost对鸢尾花数据两特征组合进行分类,实现代码如下:
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
if __name__ == "__main__":
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
iris_feature = '花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度'
path = 'iris.data' # 数据文件路径
data = pd.read_csv(path, header=None)
x_prime = data[list(range(4))]
y = pd.Categorical(data[4]).codes
x_prime_train, x_prime_test, y_train, y_test = train_test_split(x_prime, y, train_size=0.7, random_state=0)
feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
plt.figure(figsize=(11, 8), facecolor='#FFFFFF')
for i, pair in enumerate(feature_pairs):
# 准备数据
x_train = x_prime_train[pair]
x_test = x_prime_test[pair]
# 决策树
base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)
# AdaBoost学习器,10棵树,学习率为0.1
model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)
model.fit(x_train, y_train)
# 画图
N, M = 500, 500 # 横纵各采样多少个值
x1_min, x2_min = x_train.min()
x1_max, x2_max = x_train.max()
t1 = np.linspace(x1_min, x1_max, N)
t2 = np.linspace(x2_min, x2_max, M)
x1, x2 = np.meshgrid(t1, t2) # 生成网格采样点
x_show = np.stack((x1.flat, x2.flat), axis=1) # 测试点
# 训练集上的预测结果
y_train_pred = model.predict(x_train)
acc_train = accuracy_score(y_train, y_train_pred)
y_test_pred = model.predict(x_test)
acc_test = accuracy_score(y_test, y_test_pred)
print('特征:', iris_feature[pair[0]], ' + ', iris_feature[pair[1]])
print('\t训练集准确率: %.4f%%' % (100*acc_train))
print('\t测试集准确率: %.4f%%\n' % (100*acc_test))
cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
y_hat = model.predict(x_show)
y_hat = y_hat.reshape(x1.shape)
plt.subplot(2, 3, i+1)
plt.contour(x1, x2, y_hat, colors='k', levels=[0, 1], antialiased=True, linestyles='--', linewidths=1.5)
plt.pcolormesh(x1, x2, y_hat, cmap=cm_light) # 预测值
plt.scatter(x_train[pair[0]], x_train[pair[1]], c=y_train, s=20, edgecolors='k', cmap=cm_dark)
plt.scatter(x_test[pair[0]], x_test[pair[1]], c=y_test, s=100, marker='*', edgecolors='k', cmap=cm_dark)
plt.xlabel(iris_feature[pair[0]], fontsize=14)
plt.ylabel(iris_feature[pair[1]], fontsize=14)
plt.xlim(x1_min, x1_max)
plt.ylim(x2_min, x2_max)
plt.grid(b=True)
plt.suptitle('Adaboost对鸢尾花数据两特征组合的分类结果', fontsize=18)
plt.tight_layout(1, rect=(0, 0, 1, 0.95)) # (left, bottom, right, top)
plt.show()
输出结果如下:
特征: 花萼长度 + 花萼宽度
训练集准确率: 87.6190%
测试集准确率: 75.5556%
特征: 花萼长度 + 花瓣长度
训练集准确率: 99.0476%
测试集准确率: 88.8889%
特征: 花萼长度 + 花瓣宽度
训练集准确率: 98.0952%
测试集准确率: 88.8889%
特征: 花萼宽度 + 花瓣长度
训练集准确率: 100.0000%
测试集准确率: 95.5556%
特征: 花萼宽度 + 花瓣宽度
训练集准确率: 98.0952%
测试集准确率: 93.3333%
特征: 花瓣长度 + 花瓣宽度
训练集准确率: 99.0476%
测试集准确率: 97.7778%
输出图像如下:
3.2 GBDT & XGBoost
分别使用XGBoost、GBDT、随机森林、逻辑回归对鸢尾花数据进行分类,实现代码如下:
import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_score
if __name__ == "__main__":
path = 'iris.data' # 数据文件路径
data = pd.read_csv(path, header=None) # 读取数据
x, y = data[list(range(4))], data[4] # 取特征值
y = pd.Categorical(y).codes # 取标签值
# 拆分数据为训练集(50%)和测试集(50%)
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, test_size=50)
# 转换为xgboost中的矩阵格式
data_train = xgb.DMatrix(x_train, label=y_train)
data_test = xgb.DMatrix(x_test, label=y_test)
watch_list = [(data_test, 'eval'), (data_train, 'train')] # 输出过程中的错误率
# 3分类的softmax训练模型
param = {'max_depth': 8, 'eta': 0.8, 'silent': 1, 'objective': 'multi:softmax', 'num_class': 3}
# 训练XGBoost
bst = xgb.train(param, data_train, num_boost_round=6, evals=watch_list)
# XGBoost预测数据集
y_hat = bst.predict(data_test)
result = y_test == y_hat
print('测试集正确率:\t', float(np.sum(result)) / len(y_hat))
print('END.....\n')
# 分别用GBDT、随机森林和逻辑回归训练
models = [
# GBDT 30棵决策树,学习率为0.1,树的最大深度为5
('GBDT', GradientBoostingClassifier(n_estimators=30, learning_rate=0.1, max_depth=5)),
# 随机森林 30棵决策树,特征选择用gini系数,树的最大深度为5
('RandomForest', RandomForestClassifier(n_estimators=30, criterion='gini', max_depth=5)),
('LogisticRegression', LogisticRegressionCV(Cs=np.logspace(1, 100, 100), cv=3))
]
for name, model in models:
model.fit(x_train, y_train)
print(name, '训练集正确率:', accuracy_score(y_train, model.predict(x_train)))
print(name, '测试集正确率:', accuracy_score(y_test, model.predict(x_test)))
输出结果如下:
[0] eval-merror:0.02 train-merror:0.02
[1] eval-merror:0.02 train-merror:0.02
[2] eval-merror:0.02 train-merror:0.02
[3] eval-merror:0.04 train-merror:0.01
[4] eval-merror:0.04 train-merror:0.01
[5] eval-merror:0.04 train-merror:0.01
测试集正确率: 0.96
END.....
GBDT 训练集正确率: 1.0
GBDT 测试集正确率: 0.96
RandomForest 训练集正确率: 1.0
RandomForest 测试集正确率: 0.96
LogisticRegression 训练集正确率: 0.97
LogisticRegression 测试集正确率: 0.92
4.参考
- 机器学习升级版视频 - 邹博
- 《机器学习实战》第7章 利用AdaBoost元算法提高分类性能
- 《机器学习 - 周志华》第8章 集成学习
- 《统计学习方法》第8章 提升方法
===============文档信息================
学习笔记由博主整理编辑,供非商用学习交流用
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处
署名(BY) :dkjkls(dkj卡洛斯)
文章出处:http://blog.csdn.net/dkjkls