集成学习需要很好地解决如下两个问题:
①如何通过有效地训练,获得若干个基学习器?
②如何选择一个组合策略,将这些基学习器集合成为一个强学习器?
同质个体学习器按照个体学习器之间是否存在依赖关系又可以分为两类:
根据集成学习的用途不同,结论合成的方法也各不相同:
1)抽样产生每棵决策树的训练数据集。随机森林从原始训练数据集中产生n个训练子集(假设要随机生成n棵决策树)。训练子集中的样本存在一定的重复,主要是为了在训练模型时,每一棵树的输入样本都不是全部的样本,使森林中的决策树不至于产生局部最优解。
2)构建n棵决策树(基学习器)。每一个训练子集生成一棵决策树,从而产生n棵决策树组成的森林,每棵决策树不需要剪枝处理。由于随机森林在进行结点分裂时,随机地选择m个特征(一般取m= log 2 M \log{2}^{M} log2M,m≪M,其中M是数据集特征总数)参与比较,而不是像决策树将所有特征都参与特征指标的计算。这样减少了决策树之间的相关性,提升了决策树的分类精度,从而达到结点的随机性。
3)生成随机森林。使用第(2)步n棵决策树对测试样本进行分类,随机森林将每棵子树的结果汇总,以简单多数的原则决定该样本的类别。
注:由于从原始训练集中随机产生n个训练子集用于随机生成n棵决策树,且在构建具体的决策树过程中随机地选择m个属性,随机森林的这两个随机性设置可以很大程度上降低过拟合出现的概率。虽然随机森林中的每一棵树分类的能力都很弱,但是多棵树组合起来就变得十分强大。
sklearn.ensemble.RandomForestClassifier(n_estimators=10,criterion='gini',max_depth=None,min_samples_split=2,min_samples_leaf=1,min_weight_fraction_leaf=0.0,max_features='auto',max_leaf_nodes=None,bootstrap=True,oob_score=False,n_jobs=1,random_state=None,verbose=0,warm_start=False)
1)n_estimators:随机森林中树的数量。数据类型为整型,若不指定该参数值,则自动使用默认参数值10。
2)criterion:特征选择标准。数据类型为字符串,若不指定该参数值,则自动使用默认参数值gini。其可选值如:gini:表示切分时的评价准则时gini系数;entropy:表示切分的评价准则时信息熵。
3)max_depth:决策树的最大深度。数据类型为整型或None,与决策树中一致。
4)min_samples_split:子数据集再切分需要的最小样本数。数据类型为整型或浮点型,与决策树中一致。
5)min_samples_leaf:叶子结点上的最小样本数。数据类型为整型或浮点型,与决策树中一致。
6)min_weight_fraction_leaf:叶子结点最小的样本权重和。数据类型为浮点型,与决策树中一致。
7)max_features:划分结点以寻找最优划分特征时,设置允许搜索的最大特征个数。数据类型为整型、浮点型、字符串型或None,与决策树中一致。
8)max_leaf_nodes:最大叶子结点数。数据类型为整型或None,与决策树中一致。限制最大叶子结点数,可以防止过拟合。
9)bootstrap:建立决策树时,是否使用有放回抽样。数据类型为布尔型,若不指定该参数值,则自动使用默认参数值True,即放回抽样。
10)oob_score:估计泛化误差时是否使用袋外样本(out-of-bag samples)。数据类型为布尔型,若不指定该参数值,则自动使用默认参数值False,即不使用袋外样本。
11)n_jobs:用于拟合和预测的并行作业数量。数据类型为整型,若不指定该参数值,则自动使用默认参数值1。如果值为-1,则并行工作的数量被设置为CPU核的数量。
12)random_state:随机种子的设置。数据类型为整型,若不指定该参数值,则自动使用默认参数值None,即使用当前系统时间作为种子,每次随机结果不同。
13)verbose:控制决策树建立过程的冗余度。数据类型为整型,若不指定该参数值,则自动使用默认参数值0。
14)warm_start:数据类型为布尔型。若不指定该参数值,则自动使用默认参数值False。当被设置为True时,调用之前的模型,用来拟合完整数据集或添加更多的基学习器,反之则创建一个全新的随机森林。
1)estimators_:决策树实例的数组,存放所有训练过的决策树。
2)classes_:类别标签。
3)n_classes_:类别的数量。
4)n_features_:训练时使用的特征数量。
5)n_outputs_:训练时输出的数量。
6)features_importances:特征的重要程度。该值越高,则该特征越重要。
7)oob_score_:训练数据使用包外估计时的得分。
1)fit(X, y[, sample_weight]):训练模型。
2)predict(X):用模型进行预测,返回预测值。
3)predict_log_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率的对数值。
4)predict_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率值。
5)n_outputs_:训练时输出的数量。
6)score(X, y[, sample_weight]):返回在(X, y)上预测的准确率。
代码实现
# 导入内置数据集模块
from sklearn.datasets import load_breast_cancer
# 导入sklearn模块中的决策树分类器类和随机森林分类器类
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
# 导入sklearn模块中的模型验证类
from sklearn.model_selection import train_test_split,cross_val_score
import matplotlib.pyplot as plt
# 导入乳腺癌数据集
cancer = load_breast_cancer()
# 使用train_test_split函数自动分割训练集与测试集,其中test_size为测试集所占比例
x_train, x_test, y_train, y_test = train_test_split(cancer.data,cancer.target,test_size=0.3)
# 定义一个决策树分类器对象用于做比较
dt = DecisionTreeClassifier(random_state=0)
# 定义一个随机森林分类器对象
rf = RandomForestClassifier(random_state=0)
dt.fit(x_train,y_train)
rf.fit(x_train,y_train)
score_dt = dt.score(x_test,y_test)
score_rf = rf.score(x_test,y_test)
# 输出准确率
print('Single Tree : ', score_dt)
print('Random Forest : ', score_rf)
dt_scores = []
rf_scores = []
# 使用cross_val_score进行交叉验证,其中:
# cv为份数,即将数据集划分为n分,依次取每一份做测试集,其他n-1份做训练集,
# 返回每次测试准确率评分的列表
for i in range(10):
rf_score = cross_val_score(RandomForestClassifier(n_estimators=25),cancer.data, cancer.target,cv=10).mean()
rf_scores.append(rf_score)
dt_score = cross_val_score(DecisionTreeClassifier(),cancer.data, cancer.target, cv=10).mean()
dt_scores.append(dt_score)
# 绘制评分对比曲线
plt.figure()
plt.title('Random Forest VS Decision Tree')
plt.xlabel('Index')
plt.ylabel('Accuracy')
plt.plot(range(10),rf_scores,label = 'Random Forest')
plt.plot(range(10),dt_scores,label = 'Decision Tree')
plt.legend()
plt.show()
# 观察弱分类器数量对分类准确度的影响
rf_scores = []
for i in range(1,50):
rf = RandomForestClassifier(n_estimators=i)
rf_score = cross_val_score(rf, cancer.data, cancer.target,cv=10).mean()
rf_scores.append(rf_score)
plt.figure()
plt.title('Random Forest')
plt.xlabel('n_estimators')
plt.ylabel('Accuracy')
plt.plot(range(1,50),rf_scores)
plt.show()
实验效果
Boosting算法分为以下两个阶段:
首先,训练集用初始权重训练出一个弱学习器1,根据弱学习的学习误差率表现来更新训练样本的权重,使得之前弱学习器1学习误差率高的训练样本点的权重变高,使得这些误差率高的点在后面的弱学习器2中得到更多的重视。
然后,基于调整权重后的训练集来训练弱学习器2,如此重复进行,直到训练到指定的弱学习器数量。
最后,将这些弱学习器通过集合策略进行整合,得到最终的强学习器。
sklearn.ensemble.AdaBoostClassifier(base_estimator=None,n_estimators=50,learning_rate=1.0,algorithm='SAMME.R', random_state=None)
1)base_estimator:基分类器,在该分类器基础上进行boosting。默认为决策树,理论上可以是任意一个分类器,但是如果使用其他分类器时需要指明样本权重。
2)n_estimators:基分类器提升(循环)次数。数据类型为整型,若不指定该参数值,则自动使用默认参数值50。这个值过大,模型容易过拟合,值过小,模型容易欠拟合。。
3)learning_rate:学习率,表示梯度收敛速度。数据类型为整型,若不指定该参数值,则自动使用默认参数值1。如果过大,容易错过最优值,如果过小,则收敛速度会很慢。该值需要和n_estimators进行一个权衡,当分类器迭代次数较少时,学习率可以小一些,当迭代次数较多时,学习率可以适当放大。
4)algorithm:模型提升准则。数据类型为字符串型,若不指定该参数值,则自动使用默认参数值SAMME.R。可选值如: SAMME:对样本集预测错误的概率进行划分;SAMME.R:对样本集的预测错误的比例,即错分率进行划分。
5)random_state:设置随机种子。数据类型为整型,若不指定该参数值,则自动使用默认参数值None。
1)estimators_:所有训练过的基础分类器。
2)classes_:类别标签。
3)n_classes_:类别的数量。
4)estimator_weights_:每个基础分类器的权重。
5)feature_importances:每个特征的重要性。
6)estimator_errors_:每个基础分类器的分类误差。
1)fit(X, y[, sample_weight]):训练模型。
2)predict(X):用模型进行预测,返回预测值。
3)predict_log_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率的对数值。
4)predict_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率值。
5)score(X, y[, sample_weight]):返回在(X, y)上预测的准确率。
6)staged_predict_proba(X):返回一个二维数组,数组的元素依次是每一轮迭代结束时尚未完成的集成分类器预测X为各个类别的概率值。
7)staged_predict(X):返回一个数组,数组的元素依次是每一轮迭代结束时尚未完成的集成分类器的预测值。
8)staged_score(X, y[, sample_weight]):返回一个数组,数组的元素依次是每一轮迭代结束时尚未完成的集成分类器的预测准确率。
代码实现
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import train_test_split,cross_val_score
import matplotlib.pyplot as plt
cancer = load_breast_cancer()
x_train, x_test, y_train, y_test = train_test_split(cancer.data,cancer.target, test_size=0.3, random_state=1)
abc = AdaBoostClassifier(DecisionTreeClassifier(), algorithm='SAMME', n_estimators=50, learning_rate=0.1)
dt = DecisionTreeClassifier()
abc.fit(x_train,y_train)
dt.fit(x_train,y_train)
score_abc = abc.score(x_test,y_test)
score_dt = dt.score(x_test,y_test)
# 输出准确率
print('Ada Boost : ', score_abc)
print('Decision Tree : ', score_dt)
# 测试n_estimators参数对分类效果的影响
abc_scores = []
for i in range(1,50):
abc.estimators_ = i
abc.fit(x_train,y_train)
abc_score = abc.score(x_test,y_test)
abc_scores.append(abc_score)
# 绘制结果
plt.figure()
plt.title('AdaBoost')
plt.xlabel('n_estimators')
plt.ylabel('Accuracy')
plt.plot(range(1,50),abc_scores)
plt.show()
实验结果
梯度提升(Gradient Boosting)是一种用于回归和分类问题的集成学习方法,生成一个由弱学习器(通常是决策树)组成的强学习器。梯度提升的思想源于Leo Breiman的一次观察,提升可以被解释为基于一个合适的代价函数的优化算法。随后,Jerome H. Friedman开发了一个显式回归梯度提升算法,通过迭代选择一个指向负梯度方向上的函数(弱假设),优化函数空间上的成本函数,拟合一棵决策树。在回归问题中,称为梯度提升回归树(Gradient Boosting Rgression Tree,GBRT)。在分类问题中,又被称为提升决策树(Gradient Boosting Decision Tree,GBDT)。
提升决策树的弱学习器只使用CART回归树模型,迭代方法也与Adaboost有所不同。在GBDT的迭代中,假设前一轮迭代得到的强学习器是 f m − 1 ( x ) f_{m−1}(x) fm−1(x),损失函数是 L ( y , f m − 1 ( x ) ) L(y,f_{m−1}(x)) L(y,fm−1(x)),本轮迭代的目标是找到一棵CART回归树模型的弱学习器 h m ( x ) ℎ_m(x) hm(x),让本轮的损失 L ( y , f m ( x ) = L ( y , f m − 1 ( x ) ) + h m ( x ) ) L(y,f_m(x)=L(y,f_{m−1}(x))+ℎ_m(x)) L(y,fm(x)=L(y,fm−1(x))+hm(x))最小,即本轮迭代找到的决策树,要使样本的损失尽量变得更小。
sklearn.ensemble.GradientBoostingClassifier(learning_rate=0.1,n_estimators=100,subsample=1.0, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0,max_depth=3, random_state=None, max_features=None, verbose=0,max_leaf_nodes=None, presort=‘auto’)
1)min_samples_split:子数据集再切分需要的最小样本数。数据类型为整型或浮点型,与决策树中一致。
2)min_samples_leaf:叶子结点上的最小样本数。数据类型为整型或浮点型,与决策树中一致。
3)min_weight_fraction_leaf:叶子结点最小的样本权重和。数据类型为浮点型,与决策树中一致。
4)max_depth:决策树的最大深度。数据类型为整型或None,与决策树中一致。
5)max_leaf_nodes:最大叶子结点数。数据类型为整型或None,与决策树中一致。
6)max_features:随机森林允许单个决策树使用特征的最大数量。数据类型为整型、浮点型、字符串型或None,与决策树中一致。
7)learning_rate:每个弱学习器的权重缩减系数。数据类型为浮点型,若不指定该参数值,则自动使用默认参数值1.0。
8)n_estimators:弱学习器的最大个数。数据类型为整型,若不指定该参数值,则自动使用默认参数值100。
9)subsample:放回抽样比例。数据类型为浮点型,取值范围为[0,1],若不指定该参数值,则自动使用默认参数值1。如果取值为1,则全部样本都使用,等于没有使用子采样。如果取值小于1,则只有一部分样本会去做GBDT的决策树拟合。选择小于1的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低,推荐在[0.5, 0.8]之间。
10)random_state:随机种子的设置。数据类型为整型,与决策树中一致。
11)verbose :控制决策树建立过程的冗余度。数据类型为整型,与决策树中一致。
12)persort:是否进行预排序。数据类型为布尔型,与决策树中一致。
1)estimators_:每棵基础决策树。
2)init:初始预测使用的分类器。
3)oob_improvement_:输出一个数组,给出了每增加一棵基础决策树,在包外估计的损失函数的改善情况。
4)train_score_:输出一个数组,给出了每增加一棵基础决策树,在训练集上的损失函数的值。
5)feature_importances:每个特征的重要性。
1)fit(X, y[, sample_weight, monitor]):训练模型。其中monitor是一个可调用对象,它在当前迭代过程结束时调用。如果返回True,则训练过程提前终止。
2)predict(X):用模型进行预测,返回预测值。
3)predict_log_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率的对数值。
4)predict_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率值。
5)score(X, y[, sample_weight]):返回在(X, y)上预测的准确率。
6)staged_predict_proba(X):返回一个二维数组,数组的元素依次是每一轮迭代结束时集成分类器预测X为各个类别的概率值。
7)staged_predict(X):返回一个数组,数组的元素依次是每一轮迭代结束时集成分类器的预测值。
代码实现
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split,cross_val_score
import matplotlib.pyplot as plt
import numpy as np
cancer = load_breast_cancer()
x_train, x_test, y_train, y_test = train_test_split(cancer.data,cancer.target,test_size=0.3, random_state=1)
gbc = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1)
dt = DecisionTreeClassifier()
gbc.fit(x_train,y_train)
dt.fit(x_train,y_train)
score_gbc = gbc.score(x_test,y_test)
score_dt = dt.score(x_test,y_test)
# 输出准确率
print('Gradient Boost : ', score_gbc)
print('Decision Tree : ', score_dt)
# 测试learning_rate参数对分类效果的影响
gbc_scores = []
for i in np.arange(0.1,1,0.05):
gbc.learning_rate = i
gbc.fit(x_train,y_train)
gbc_score = gbc.score(x_test,y_test)
gbc_scores.append(gbc_score)
# 绘制测试结果
plt.figure()
plt.title('Gradient Boost')
plt.xlabel('learning_rate')
plt.ylabel('Accuracy')
plt.plot(range(len(gbc_scores)),gbc_scores)
plt.show()
gbc_scores = []
dt_scores = []
# 使用cross_val_score进行交叉验证
for i in range(20):
gbc_score = cross_val_score(gbc, cancer.data, cancer.target,cv=10).mean()
gbc_scores.append(gbc_score)
dt_score = cross_val_score(dt, cancer.data, cancer.target,cv=10).mean()
dt_scores.append(dt_score)
# 绘制评分对比曲线
plt.figure()
plt.title('Gradient Boost VS Decision Tree')
plt.xlabel('Index')
plt.ylabel('Accuracy')
plt.plot(range(20),dt_scores,label = 'Decision Tree')
plt.plot(range(20),gbc_scores,label = 'Gradient Boost')
plt.legend()
plt.show()
plt.show()
实验效果
偏差指预测结果与真实值之间的差异,排除噪声的影响,偏差更多的是针对某个模型输出的样本误差,偏差是模型无法准确表达数据关系导致,比如模型过于简单,非线性的数据关系采用线性模型建模,偏差较大的模型是错的模型。
模型方差不是针对某一个模型输出样本进行判定,而是指多个(次)模型输出的结果之间的离散差异,注意这里写的是多个模型或者多次模型,即不同模型或同一模型不同时间的输出结果方差较大,方差是由训练集的数据不够导致,一方面量 (数据量) 不够,有限的数据集过度训练导致模型复杂,另一方面样本质量不行,测试集中的数据分布未在训练集中,导致每次抽样训练模型时,每次模型参数不同,输出的结果都无法准确的预测出正确结果。
如图,红色的圆心代表理想的优化目标,黑色的点代表在不同的采样集合上训练模型的优化结果。可以看到左边一列低方差的优化结果要比右边一列高方差的优化结果更为集中,上边一行低偏差的优化结果要比下边一行高偏差的优化结果更靠近中心。
- Bias的对象是单个模型,是期望输出与真实标记的差别。它描述了模型对本训练集的拟合程度。
- Variance的对象是多个模型,是相同分布的不同数据集训练出模型的输出值之间的差异。它刻画的是数据扰动对模型的影响。
对于一个回归问题,假设样本( x → \overrightarrow{x} x ,y)服从的真实分布为 P ( x → , y ) P(\overrightarrow{x},y) P(x,y) 。设D表示含噪采样点集, ( x → \overrightarrow{x} x, y D y_D yD)服从分布D, y D y_D yD为输入 x → \overrightarrow{x} x在实际分布D中的标记,而噪声一般服从高斯分布,则模型随机变量 f D ( x → ) f_D(\overrightarrow{x}) fD(x)在所有可能的样本集合分布D上的期望为: E D [ f D ( x → ) ] E_D[f_D(\overrightarrow{x})] ED[fD(x)],则有:
偏差:期望输出与真实标记的差别,度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力。
b i a s ( x → ) = ( E D [ f D ( x → ) − y ] ) 2 bias(\overrightarrow{x})=(E_D[f_D(\overrightarrow{x})-y])^2 bias(x)=(ED[fD(x)−y])2 b i a s D ( x → ) = E D [ ( E D [ f D ( x → ) − y ] ) 2 ] bias_D(\overrightarrow{x})=E_D[(E_D[f_D(\overrightarrow{x})-y])^2] biasD(x)=ED[(ED[fD(x)−y])2]
方差:使用样本数相同的不同训练集产生的方差,度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动所造成的影响。
v a r ( x → ) = E D [ ( f D ( x → ) − E D [ f D ( x → ) ] ) 2 ] var(\overrightarrow{x})=E_D[(f_D(\overrightarrow{x})-E_D[f_D(\overrightarrow{x})])^2] var(x)=ED[(fD(x)−ED[fD(x)])2]
噪声的含义:噪声则表达了在当前任务上任何学习算法所能达到的期望泛化误差的下界,即刻画了学习问题本身的难度。
ε 2 = E D [ ( y D − y ) 2 ] \varepsilon^2=E_D[(y_D-y)^2] ε2=ED[(yD−y)2]
也就是说,泛化误差可以通过一系列公式分解运算证明:泛化误差为偏差、方差与噪声之和。
泛化误差 = 错误率 ( e r r o r ) = b i a s 2 ( x ) + v a r ( x ) + ε 2 泛化误差=错误率(error)=bias^2(x)+var(x)+\varepsilon^2 泛化误差=错误率(error)=bias2(x)+var(x)+ε2
注:泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。给定学习任务,为了取得好的泛化性能,则需使偏差较小,即能够充分拟合数据,并且使方差较小,即使得数据扰动产生的影响小。
偏差-方差窘境
实际在选择模型时,随着模型复杂度的增加,模型的偏差越来越小,而方差会越来越大。
如图所示,存在某一时刻,模型的方差和偏差之和最小,此时模型性能在误差及泛化能力方面达到最优。
一般来说,简单的模型会有一个较大的偏差和较小的方差,复杂的模型偏差较小方差较大。
整体思路
首先,要知道偏差和方差是无法完全避免的,只能尽量减少其影响。
(1)在避免偏差时,需尽量选择正确的模型,一个非线性问题而我们一直用线性模型去解决,那无论如何,高偏差是无法避免的。
(2)有了正确的模型,我们还要慎重选择数据集的大小,通常数据集越大越好,但大到数据集已经对整体所有数据有了一定的代表性后,再多的数据已经不能提升模型了,反而会带来计算量的增加。而训练数据太小一定是不好的,这会带来过拟合,模型复杂度太高,方差很大,不同数据集训练出来的模型变化非常大。
(3)最后,要选择合适的模型复杂度,复杂度高的模型通常对训练数据有很好的拟合能力。
针对偏差和方差的思路
偏差:实际上也可以称为避免欠拟合。
1、寻找更好的特征 – 具有代表性。
2、用更多的特征 – 增大输入向量的维度,增加模型复杂度。
方差:避免过拟合 。
1、增大数据集合 – 使用更多的数据,减少数据扰动所造成的影响
2、减少数据特征 – 减少数据维度,减少模型复杂度
3、正则化方法
4、交叉验证法
代码实现
from sklearn.datasets import load_boston
from sklearn.ensemble import GradientBoostingRegressor as GBDT
from sklearn.model_selection import train_test_split
boston=load_boston()
x_train,x_test,y_train,y_test=train_test_split(boston.data,boston.target)
model=GBDT(n_estimators=50)
model.fit(x_train,y_train)
train_score=model.score(x_train,y_train)
test_score=model.score(x_test,y_test)
print(train_score,test_score)
from sklearn.datasets import load_boston
from sklearn.ensemble import GradientBoostingRegressor as GBDT
from sklearn.model_selection import validation_curve
import matplotlib.pyplot as plt
boston=load_boston()
param_range=range(20,150,5)
train_scores,val_scores=validation_curve(
GBDT(max_depth=3),boston.data,boston.target,
param_name='n_estimators',
param_range=param_range,
cv=5,
)
train_mean=train_scores.mean(axis=-1)
train_std=train_scores.std(axis=-1)
val_mean=val_scores.mean(axis=-1)
val_std=val_scores.std(axis=-1)
_,ax=plt.subplots(1,2)
ax[0].plot(param_range,train_mean)
ax[1].plot(param_range,val_mean)
ax[0].fill_between(param_range,train_mean-train_std,train_mean+train_std,alpha=0.2)
ax[1].fill_between(param_range,val_mean-val_std,val_mean+val_std,alpha=0.2)
plt.show()