目录
[8.1 个体与集成](#8.1 个体与集成)
[8.2 Boosting](#8.2 Boosting)
[8.3 Bagging与随机森林](#8.3 Bagging与随机森林)
[8.3.1 Bagging](#8.3.1 Bagging)
[8.3.2 随机森林](#8.3.2 随机森林)
[8.4 结合策略](#8.4 结合策略)
[8 .4 .1 平均法](#8 .4 .1 平均法)
[8.4.2 投票法](#8.4.2 投票法)
[8 .4 .3 学习法](#8 .4 .3 学习法)
[8.5 多样性](#8.5 多样性)
[8.5.1 误差-分歧分解](#8.5.1 误差-分歧分解)
[8.5.2 多样性度量](#8.5.2 多样性度量)
[8.5.3 多样性增强](#8.5.3 多样性增强)
8.1 个体与集成
集成学习通过构建并结合多个学习器来完成学习任务,有时也被称为多分类器系统、基于委员会的学习等。
下面是集成学习的一般结构:先产生一组"个体学习器",再用某种策略将它们结合起来。如果集成中只包含同种类型的个体学习器,则这种集成称为同质集成,里面的个体学习器也叫做"基学习器";如果包含的不同类型的个体学习器,则这种集成称为异质集成,里面的个体学习器也叫做"组件学习器"。
在一般经验中,如果把好坏不等的个体学习器掺到一起,那么通常结果会是比最坏的要好一些,比最好的要坏一些,要获得好的集成,个体学习器应"好而不同",即个体学习器要有一定的"准确性",并且要有"多样性",也就意味着学习器间具有差异,如下所示。
根据个体学习器的生成方式,目前的集成学习方法大致可分为两大类,即个体学习器间存在强依赖关系、必须串行生成的序列化方法,以及个体学习器间不存在强依赖关系、可同时生成的并行化方法;前者的代表是Boosting,后者的代表是Bagging和"随机森林"。
8.2 Boosting
Boosting是一种可将弱学习器提升为强学习器的算法:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本的分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器;如此重复进行,直至基学习器数目达到事先指定的值,最终将这个基学习器进行加权组合。
Boosting算法最著名的代表是AdaBoost,如下图所示:
Boosting算法要求基学习器能对特定的数据分布进行学习,这可通过"重赋权法"来实现,即在训练过程的每一轮中,根据样本分布为每个训练样本重新赋予一个权重。对无法接受带权样本的学习算法,则可通过"重采样法"来实现,即在每一轮学习中,根据样本分布对训练集重新进行采样,再用重采样而得到的样本集对基学习器进行训练。需要注意的是,Boosting算法在训练的每一轮都要检查当前生成的基学习器是否满足基本条件。
下面是西瓜数据集上的一个AdaBoost算法的例子,图中黑色粗线为集成的分类边界,黑色细线为基学习器的分类边界。
import itertools
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import cross_val_score,train_test_split
from mlxtend.plotting import plot_learning_curves
from mlxtend.plotting import plot_decision_regions
iris = datasets.load_iris()
X,y = iris.data[:,0:2],iris.target
clf = DecisionTreeClassifier(criterion='entropy',max_depth=1)
num_est = [1,2,3,10]
label = ['AdaBoost(n_est=1)','AdaBoost(n_est=2)','AdaBoost(n_est=3)','AdaBoost(n_est=10)']
fig = plt.figure(figsize=(10,8))
gs = gridspec.GridSpec(2,2)
grid = itertools.product([0,1],repeat=2)
for n_est,label,grd in zip(num_est,label,grid):
boosting = AdaBoostClassifier(base_estimator=clf,n_estimators=n_est)
#n_estimator为树的数量,默认为10个树
boosting.fit(X,y)
ax = plt.subplot(gs[grd[0],grd[1]])
fig = plot_decision_regions(X=X,y=y,clf=boosting,legend=2)
plt.title(label)
# 每个基本学习器由一个深度为1的决策树组成,
# 从而根据一个特征阈值对数据进行分类,
# 该特征阈值将空间划分为两个区域,
# 两个区域之间由一个平行于其中一个轴的线性决策面分隔。
plt.show()
#plot learning curves
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=0)
boosting = AdaBoostClassifier(base_estimator=clf,n_estimators=10)
plt.figure()
plot_learning_curves(X_train,y_train,X_test,y_test,boosting,print_model=False,style='ggplot')
plt.show()
8.3 Bagging与随机森林
如果想得到泛化能力强的集成,集成中的个体学习器应尽可能相互独立,虽然"独立"在现实任务中无法做到,但可以设法使基学习器尽可能具有较大的差异,一种可能的做法是对训练样本进行采样,产生出若干个不同的子集,再从每个数据子集中训练出一个基学习器。
8.3.1 Bagging
Bagging是基于自助采样法,对于给定的包含m个样本的数据集,先随机取出一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中,这样经过m次随机采样操作,就可以得到含m个样本的采样集,最后也可以采样出T个含m个训练样本的采样集,然后基于每个采样集训练出一个基学习器,再将这些基学习器进行结合。在对预测输出进行结合时,Bagging通常对分类任务使用简单投票法,对回归任务使用简单平均法。下面是在西瓜数据集上运行Bagging算法的一个例子。
8.3.2 随机森林
随机森林(Random Forest)是Bagging的一个扩展变体,在以决策树为基学习器构建Bagging集成的基础上,进一步在决策树的训练过程中引入了随机属性选择。具体来说,传统决策树在选择划分属性时是在当前结点的属性集合(假定有d个属性)中选择一个最优属性;而在随机森林中,对基决策树的每个结点,先从该结点的属性集合中随机选择一个包含k个属性的子集,然后再从这个子集中选择一个最优属性用于划分,一般情况下,推荐值。
- 随机森林中的基学习器的多样性不仅来自样本扰动,还来自属性扰动,这就使得最终集成的泛化性能可通过个体学习器之间差异度的增加进一步提升。
- 随机森林的训练效率优于Bagging,因为在个体决策树的构建过程中,Bagging使用的是"确定型"的决策树,在选择属性划分时要对节点的所有属性进行考察,而随机森林使用的"随机型"的决策树则只需考察一个子集。
以下是基于sklearn封装的方法进行随机森林代处理分类问题的码实现:
# 随机森林
def RandomForestClf():
X_train, X_test, y_train, y_test, X, y = dataInit(type="moons")
# 随机森林: 使用多个决策树进行集成学习,决策树在节点划分上,在随机的特征子集上寻找最优划分特征
rf_clf = RandomForestClassifier(n_estimators=500, oob_score=True, random_state=666, n_jobs=-1)
rf_clf.fit(X, y)
test_score = rf_clf.oob_score_
print("Random Forest Classifier test_score = {}.".format(test_score))
# 极其随机森林 Extra-Trees:决策树在节点划分上,使用随机的特征和随机的阈值,提供额外的随机性,抑制过拟合,但增大了bias偏差
et_clf = ExtraTreesClassifier(n_estimators=500, bootstrap=True, oob_score=True, random_state=666, n_jobs=-1)
et_clf.fit(X, y)
test_score = et_clf.oob_score_
print("Extra Trees Classifier test_score = {}.".format(test_score))
以下是基于sklearn封装的方法进行随机森林代处理回归问题的码实现:
def RandomForestReg():
X_train, X_test, y_train, y_test, X, y = dataInit(type="boston")
# 随机森林: 使用多个决策树进行集成学习,决策树在节点划分上,在随机的特征子集上寻找最优划分特征
rf_reg = RandomForestRegressor(n_estimators=500, oob_score=True, random_state=666, n_jobs=-1)
rf_reg.fit(X, y)
test_score = rf_reg.oob_score_
print("Random Forest Regressor test_score = {}.".format(test_score))
# 极其随机森林 Extra-Trees:决策树在节点划分上,使用随机的特征和随机的阈值,提供额外的随机性,抑制过拟合,但增大了bias偏差
et_reg = ExtraTreesRegressor(n_estimators=500, bootstrap=True, oob_score=True, random_state=666, n_jobs=-1)
et_reg.fit(X, y)
test_score = et_reg.oob_score_
print("Extra Trees Regressor test_score = {}.".format(test_score))
8.4 结合策略
学习器结合可能会从三个方面带来好处:
- 从统计的方面来看,由于学习任务的假设空间往往很大,可能有多个假设在训练集上达到同等性能,此时若使用单学习器可能因误选而导致泛化性能不佳,结合多个学习器则会减小这一风险。
- 从计算的方面来看,学习算法往往会陷入局部极小,有的局部极小点所对应的泛化性能可能会很糟糕,而通过多次运行之后进行结合,可降低陷入糟糕局部极小点的风险。
- 从表示的方面来看,某些学习任务的真实假设可能不在当前学习算法所考虑的假设空间中,此时若使用单学习器则肯定无效,而通过结合多个学习器,由于相应的假设空间有所扩大,有可能学得更好的近似。
8.4.1 平均法
对数值型输出, 最常见的结合策略是使用平均法(averaging)。
- 简单平均法(simple averaging)
- 加权平均法(weighted averaging)
其中叫是个体学习器的权重,通常要求
8.4.2 投票法
将在样本上的预测输出表示为一个N维向量, 其中
是在类别标记上的输出。
- 绝对多数投票法:若某标记得票过半数,则预测为该标记,否则拒绝预测。
- 相对多数投票法:预测为得票最多的标记,若同时有多个标记获最高票,则从中随机选取一个。
- 加权投票法:与加权平均法类似,为每个投票方设置权重。
与加权平均法类似,是的权重,通常
8.4.3 学习法
当训练数据很多时,一种更为强大的结合策略是使用"学习法",即通过另一个学习器来进行结合,比如Stacking算法,这里我们把个体学习器称为初级学习器,用于结合的学习器称为次级学习器或元学习器。
Stacking算法是一种集成学习方法,它使用多个不同的基学习器对数据进行预测,然后将预测结果作为新的特征输入到一个元学习器中,从而得到最终的分类或回归结果。Stacking算法可以利用不同的基学习器的优势,提高泛化性能,也可以使用交叉验证或留一法来防止过拟合。
8.5 多样性
8.5.1 误差-分歧分解
误差-分歧分解是一种将集成学习的泛化误差分解为个体学习器的误差和分歧的方法,误差表示个体学习器的平均泛化误差,分歧表示个体学习器之间的不一致性或多样性。误差-分歧分解的公式如下:
其中,E是集成的泛化误差,是个体学习器的平均泛化误差,是个体学习器的平均分歧。这个公式表明,个体学习器的准确性越高、多样性越大,则集成越好。误差-分歧分解只适用于回归问题,对于分类问题则难以推广。误差-分歧分解是基于平方误差的推导,而平方误差是回归问题的常用损失函数。对于分类问题,平方误差不一定是一个合适的损失函数,因为它不能很好地反映分类错误的程度。因此,误差-分歧分解难以直接推广到分类学习任务上去。
8.5.2 多样性度量
- 不合度量(disagreement measure)
的值域为[0,1],值越大则多样性越大。
- 相关系数(correlation coefficient)
的值域为[-1 ,1]..若与无关,则值为0; 若与正相关则值
为正,否则为负.
- Q-统计量(Q-statistic )
与相关系数的符号相同,且.
- -统计量(κ-statistic)
其中且是两个分类器取得一致的概率;归是两个分类器偶然达成一致
的概率,它们可由数据集D估算:
若分类器与在D上完全一致,则= 1; 若它们仅是偶然达成一致,则= o。 通常为非负值,仅在与达成一致的概率甚主低于偶然性的情况下取负值.
8.5.3 多样性增强
- 数据样本扰动:给定初始数据集,可从中产生出不同的数据子集,再利用不同的数据子集训练出不同的个体学习器。
- 输入属性扰动:从初始属性集中抽取出若干个属性子集,再基于每个属性子集训练一个基学习器。
- 输出表示扰动:对训练样本的类标记稍作变动,或者对输出表示进行转化,又或将原任务拆解为多个可同时求解的子任务。
- 生出不同的数据子集,再利用不同的数据子集训练出不同的个体学习器。
- 算法参数扰动:通过随机设置不同的参数,产生差别较大的个体学习器。
8.6 总结
本文讲解了集成学习的原理,分类以及常见的集成学习模型。并基于sklearn使用常见集成学习方法处理回归和分类问题,在文章的最后,对易于混淆的集成算法进行了区分对比。
AdaBoost:
import pandas as pd
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, recall_score, f1_score, precision_score
import seaborn as sns
import matplotlib.pyplot as plt
# 创建一些模拟的多类别数据集
X, y = make_classification(n_samples=1000, n_features=20, n_informative=2, n_redundant=0,
random_state=42, n_classes=3, n_clusters_per_class=1)
# 将数据集分割成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建AdaBoost模型实例
# 使用决策树桩作为基本分类器
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=1), n_estimators=10,
algorithm="SAMME", learning_rate=0.5, random_state=42)
# 训练AdaBoost模型
ada_clf.fit(X_train, y_train)
print("建立的弱分类器:", ada_clf.estimators_)
print("分类误差:", ada_clf.estimator_errors_)
print("分类器权重:", ada_clf.estimator_weights_)
print("迭代速率:", ada_clf.learning_rate)
# 进行预测
y_pred = ada_clf.predict(X_test)
print("\n---------- 模型评价 ----------")
cm = confusion_matrix(y_test, y_pred) # 混淆矩阵
df_cm = pd.DataFrame(cm) # 构建DataFrame
print("ConfusionMatrix", df_cm)
print('Accuracy score:', accuracy_score(y_test, y_pred)) # 准确率
print('Recall:', recall_score(y_test, y_pred, average='weighted')) # 召回率
print('F1-score:', f1_score(y_test, y_pred, average='weighted')) # F1分数
print('Precision score:', precision_score(y_test, y_pred, average='weighted')) # 精确度
# 绘制混淆矩阵的热图
plt.figure(figsize=(8, 6))
sns.heatmap(df_cm, annot=True, fmt="d", cmap="Blues")
plt.title("Confusion Matrix")
plt.xlabel("Predicted label")
plt.ylabel("True label")
plt.show()
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"Model accuracy: {accuracy:.2f}")