机器学习之集成学习
一、集成学习思想
1、定义概念
集成学习是机器学习中的一种思想,它通过多个模型的组合形成一个精度更高的模型,参与组合的模型成为弱学习器(弱学习器)。训练时,使用训练集依次训练出这些弱学习器,对未知的样本进行预测时,使用这些弱学习器联合进行预测(不易出现过拟合)。

2、分类
(1)Bagging:随机森林
(2)Boosting:Adaboost、GBDT、XGBoost、LightGBM
3、Bagging思想
Bagging思想图

- 有放回的抽样(bootstrap抽样)产生不同的训练集,从而训练不同的学习器
- 通过平权投票、多数表决的方式决定预测结果
- 弱学习器可以并行训练
4、Boosting思想(重点)
Boosting思想图

- 每一个训练器重点关注前一个训练器不足的地方进行训练
- 通过加权投票的方式,得出预测结果
- 串行的训练方式
5、 Bagging&Boosting对比
| Bagging | Boosting | |
| 数据采样 | 对数据进行有放回的采样训练 |
全部样本,根据前一轮学习结果调整数据的重要性 |
| 投票方式 | 所有学习期平权投票 | 对学习器进行加权投票 |
| 学习顺序 | 并行的,每个学习期没有依赖关系 | 串行,学习有先后顺序 |
二、随机森林算法
1、随机森林的构建方法
随机森林是基于 Bagging 思想实现的一种集成学习算法,采用决策树模型作为每一个弱学习器。
(1)在数据样本中随机选取m条数据
(2)随机选取k个数据特征
(3)训练决策树(默认CART树)
(4)重复1-3步构造n个弱决策树(弱学习器)
(5)平权投票集成n个弱决策树
2、相关概念
(1)为什么要随机抽样训练集?
如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样。
(2)为什么要有放回地抽样?
如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决。
综上:弱学习器的训练样本既有交集也有差异数据,更容易发挥投票表决效果
3、随机森林算法的API
sklearn.ensemble.RandomForestClassifier()
"""
n_estimators:决策树数量,(default =10)
Criterion:entropy、或者gini,(default=gini)
max_depth:指定树的最大深度,(default =None表示树会尽可能的生长)
max_features="auto",决策树构建时使用的最大特征数量
If "auto",then max_features=sqrt(n_features).
If "sqrt",then max_features=sqrt(n_features)(same as "auto").
If "log2",then max_features=log2(n_features).
If None,then max_features=n_features.
bootstrap:是否采用有放回抽样,如果为False将会使用全部训练样本,(default=True)
min_samples_split:结点分裂所需最小样本数,(default =2)
如果节点样本数少于min_samples_split,则不会再进行划分.
如果样本量不大,不需要设置这个值.
如果样本量数量级非常大,则推荐增大这个值.
min_samples_leaf:叶子节点的最小样本数,(default =1)
如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝.
较小的叶子结点样本数量使模型更容易捕捉训练数据中的噪声.
min_impurity_split:节点划分最小不纯度
如果某节点的不纯度(基尼系数,均方差)小于这个阈值,则该节点不再生成子节点,并变为叶子节点.
一般不推荐改动默认值1e-7。
"""
4、代码案例
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier # 随机森林算法(分类器)
from sklearn.model_selection import GridSearchCV # 网格搜索
# 1、读取数据
data = pd.read_csv('train.csv')
data.info()
# 2、数据预处理
# 2.1 抽取特征 和 标签
x = data[['Pclass', 'Age', 'Sex', 'Fare', 'SibSp']].copy()
y = data['Survived']
# 2.2 空值处理
x.fillna({'Age':x['Age'].mean()}, inplace=True)
x = pd.get_dummies(x)
x.drop('Sex_male', axis=1, inplace=True)
# 2.4 划分数据集 训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666)
# 3、特征工程 略
# 4、模型训练 预测 评估
# 场景1: 单一决策树
# 4.1 创建决策树对象 演示:单一决策树
es1 = DecisionTreeClassifier()
# 4.2 训练模型
es1.fit(x_train, y_train)
# 4.3 模型预测
y_predict1 = es1.predict(x_test)
# 4.4 评估模型
print('单一决策树准确率:', es1.score(x_test, y_test))
print('-'*50)
# 场景2: 随机森林
# 4.1 创建随机森林对象 默认 多个决策树(baggding思想)
# 弱学习器个数 有 !! 使用默认的!!! 100个 深度
es2 = RandomForestClassifier(n_estimators=100, max_depth=9, random_state=666)
# 4.2 训练模型
es2.fit(x_train, y_train)
# 4.3 模型预测
y_predict2 = es2.predict(x_test)
# 4.4 评估模型
print('随机森林准确率:', es2.score(x_test, y_test))
print('-'*50)
# 场景3: 随机森林算法 -> 采用网格搜索
# 4.1 创建随机森林对象
es3 = RandomForestClassifier()
# 4.2 参数准备,构建网格搜索
params = {
'n_estimators': [30, 70, 90, 120, 150, 180],
'max_depth': [2, 3, 5, 7, 9, 11, 13],
}
# 4.3 模型训练
es3 = GridSearchCV(es3, params, cv=5)
es3.fit(x_train, y_train)
# 4.4 模型预测
y_predict3 = es3.predict(x_test)
# 4.5 模型评估
print('网格搜索准确率:', es3.score(x_test, y_test))
print(f"最佳参数:{es3.best_params_}")
三、Adaboost算法
1、概念及算法推导
Adaptive Boosting(自适应提升)基于 Boosting思想实现的一种集成学习算法核心思想是通过逐步提高那些被前一步分类错误的样本的权重来训练一个强分类器。
(1)初始化训练数据权重相等,训练第1个学习器
—— 如果有100个样本,则每个样本的初始化权重为:1/100
—— 根据预测结果找一个错误率最小的分裂点,计算、更新:样本权重、模型权重
(2)根据新权重的样本集 训练第2个学习器
—— 根据预测结果找一个错误率最小的分裂点计算、更新:样本权重、模型权重
(3)迭代训练在前一个学习器的基础上,根据新的样本权重训练当前学习器
—— 直到训练处 m 个弱学习器
(4)m 个弱学习器集成预测公式:
—— 为模型的权重,输出结果大于0则归为正类,小于0则归为负类
(5)模型权重计算公式:
—— 为模型权重,
表示第 i 个弱学习器的错误率
(6)样本权重计算公式:
—— 其中 为归一化值(所有样本权重总和),
为样本权重,
为模型权重
—— f(x) 为预测值,y 为真实值
—— 预测值=真实值:预测对了,权重降低;预测值 != 真实值:预测错了,权重升高
2、Adaboost算法的构建过程
1、初始化数据权重,来训练第1个弱学习器。找最小的错误率计算模型权重,再更新模数据权重。
2、根据更新的数据集权重,来训练第2个弱学习器,再找最小的错误率计算模型权重,再更新模数据权重。
3、依次重复第2步,训练n个弱学习器。组合起来进行预测。结果大于0为正类、结果小于0为负类
已知训练数据见下面表格,假设弱分类器由 x 产生,预测结果使该分类器在训练数据集上的分类误差率最低,试用 Adaboost 算法学习一个强分类器。
| 序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
(1)初始化工作:初始化10个样本的权重,每个样本权重为0.1,构建第1个弱学习器

1. 寻找最优分裂点
a、对特征值x进行排序,确定分裂点为0.5、1.5、2.5、3.5、4.5、5.5、6.5、7.5、8.5
b、当以0.5位分裂点时,有5个样本分类错误(绿色为预测正确,红色预测错误,以下分类同此)

c、当以1.5位分裂点时,有4个样本分类错误

d、当以2.5位分裂点时,有3个样本分类错误
e、当以3.5位分裂点时,有4个样本分类错误
f、当以4.5位分裂点时,有5个样本分类错误
g、当以5.5位分裂点时,有4个样本分类错误
h、当以6.5位分裂点时,有5个样本分类错误
i、当以7.5位分裂点时,有4个样本分类错误
j、当以8.5位分裂点时,有3个样本分类错误

k、最终,选择以2.5作为分裂点,计算得出第一个弱学习器的错误率为:3/10=0.3
2. 计算模型权重
3. 更新样本权重:
a、分类正确样本序号为:1、2、3、4、5、6、10共7个,其计算公式为: ,则正确样本权重变化系数为
b、分类错误样本序号为:7、8、9共3个,其计算公式为: ,则正确样本权重变化系数为
c、更新新的权重值:新的权重 = 旧的权重 * 权重变化系数
样本序号1、2、3、4、5、6、10权重值为:0.06547
样本序号7、8、9权重值为:0.15275
d、归一化 值为:0.06547 * 7 + 0.15275 * 3 = 0.9165
e、计算最终权重值:最终权重 = 正确样本权重(或者错误样本权重)/ 归一化值
正确样本序号1、2、3、4、5、6、10的最终权重值为:0.07143
错误样本序号7、8、9的最终权重值为:0.16667
f、得到最新的样本数据,并作为源数据输入到第二个弱学习器中

(2)构建第2个弱学习器

1. 寻找最优分裂点
a、对特征值x进行排序,确定分裂点为0.5、1.5、2.5、3.5、4.5、5.5、6.5、7.5、8.5
b、当以0.5位分裂点时,有5个样本分类错误,错误率为:0.07143*2 + 0.16667*3=0.6428

c、当以1.5位分裂点时,有4个样本分类错误,错误率为:0.07143*1 + 0.16667*3=0.57144
d、当以2.5位分裂点时,有5个样本分类错误,错误率为:0.16667*3=0.50001
。。。。。。
j、当以8.5位分裂点时,有3个样本分类错误,错误率为:0.07143*3=0.21429
k、最终经比较,当以8.5为分裂点时,错误率最小,故选择以8.5为分裂点,此时学习器错误率为0.21429
2. 计算模型权重
3. 更新样本权重:
a、分类正确样本序号为:1、2、3、4、5、6、10共7个,其计算公式为: ,则正确样本权重变化系数为
b、分类错误样本序号为:7、8、9共3个,其计算公式为: ,则正确样本权重变化系数为
c、更新新的权重值:新的权重 = 旧的权重 * 权重变化系数
样本序号1、2、3、10权重值为:0.07143 * 0.5222 = 0.0373
样本序号7、8、9权重值为:0.16667 * 0.5222 = 0.087
样本序号4、5、6权重值为:0.07143 * 1.9148 = 0.1368
d、归一化 值为:0.0373 * 4 +0.087 * 3 + 0.1368 * 3 = 0.8206
e、计算最终权重值:最终权重 = 正确样本权重(或者错误样本权重)/ 归一化值
正确样本序号1、2、3、10的最终权重值为:0.0455
正确样本序号7、8、9的最终权重值为:0.1060
错误样本序号4、5、6的最终权重值为:0.1667
f、得到最新的样本数据,并作为源数据输入到第三个弱学习器中

(3)构建第三个弱学习器,步骤略
(4)最终三个弱学习器构成一个强学习器

若H(x) 的值大于0则归于正类,若小于0则归于负类
例:X = 3 带入公式为 :0.4236*(-1) + 0.64963*(1) + 0.7514*(-1) = -0.52537 < 0 负类
3、代码案例
已知葡萄酒数据,根据数据进行葡萄酒分类
import pandas as pd
from sklearn.preprocessing import LabelEncoder # 标签编码器
from sklearn.model_selection import train_test_split # 训练集、测试集分割
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.ensemble import AdaBoostClassifier # AdaBoost分类器 -> 集成学习Boosting思想
from sklearn.metrics import accuracy_score # 模型评估 -> 正确率
# 1 读数据到内存
# 2 特征处理
# 2-1 修改列名
# 2-2 Adaboost一般做二分类 去掉一类(1,2,3)
# 2-4 类别转化 (2,3)=>(0,1)
# 2-5 划分数据
# 3 实例化单决策树 实例化Adaboost-由500颗树组成
# 4 单决策树训练和评估
# 5 AdaBoost训练和评估
# 1、读取数据
data = pd.read_csv('wine0501.csv')
data.info()
# 2、数据预处理
# print(data['Class label'].unique()) # [1 2 3] 葡萄酒类别有3种,但是决策树CART树只能识别 二分类
# df_wine[df_wine['Class label']!=1]
#2.1 从标签列中 过滤标签1
data = data[data['Class label'] != 1] # 过滤 1 类别 剩下 2、3 类别
# 2.2 获取特征列 和 标签列
x = data[['Alcohol', 'Hue']] # 酒精 和 色泽
y = data['Class label'] # 标签列
# 2.3 打印一下
print(x[:5])
print(y[:5])
# 2.4 通过标签编码器 2、3 标签编码器 0、1 [2,3] -> [0,1]
le = LabelEncoder()
y = le.fit_transform(y)
# y = y.map({2:0, 3:1})
# print(y[:5])
# 2.5 训练集和测试集划分
x_train, x_test, y_train, y_test = train_test_split(x, y, \
test_size=0.2, random_state=666, stratify=y)
# 3、特征工程 略
# 4、模型训练 预测 、 评估
# 场景1: 单一决策树
# 4.1 创建模型对象
es1 = DecisionTreeClassifier(max_depth=3)
# 4.2 训练模型
es1.fit(x_train, y_train)
# 4.3 模型预测
y_pre1 = es1.predict(x_test)
print(f"预测结果:{y_pre1}")
# 4.4 模型评估
print(f"单一模型预测正确率:{accuracy_score(y_test, y_pre1)}")
print('-'*50)
# 场景2: AdaBoost -> 集成学习 CART树 300颗
# 4.1 创建模型对象
es2 = AdaBoostClassifier(estimator=es1, n_estimators=300, \
learning_rate=0.01, algorithm='SAMME')
# 4.2 训练模型
es2.fit(x_train, y_train)
# 4.3 模型预测
y_pre2 = es2.predict(x_test)
print(f"预测结果:{y_pre2}")
# 4.4 模型评估
print(f"Adaboost集成模型预测正确率:{accuracy_score(y_test, y_pre2)}")
四、GBDT
1、提升树
(1)思想:通过拟合残差的思想来进行提升
残差:真实值 - 预测值
(2)生活中的例子
某试卷一共有20题
第1次考试:只写对了14题;20 – 14 = 6(残差)
第2次考试:上一轮没有写对的6题作为新的题目,复习后写对了3题;6 – 3 = 3 (残差)
第3次考试:上一轮没有写对的3题作为新的题目,复习后写对了2题;3 – 2 = 1 (残差)
若三次考试的结果串联起来: 14 + 3 + 2 = 19
通过拟合残差可将多个弱学习器组成一个强学习器,这就是提升树的最朴素思想
2、梯度提升树
梯度提升树不再拟合残差,而是利用梯度下降的近似方法,利用损失函数的负梯度作为提升树算法中的残差近似值。
(1)前一轮迭代得到的强学习器是:
(2)损失函数为平方损失是:
(3)本轮迭代的目标是找到一个弱学习器:
(4)让本轮的损失最小化:
(5)则要拟合的负梯度为:
GBDT 拟合的负梯度就是残差。如果我们的 GBDT 进行的是分类问题,则损失函数变为 logloss,此时拟合的目标值就是该损失函数的负梯度值
BDT:残差提升树,通过拟合残差不断逼近真实值 残差 = 真实值-预测值
GBDT:梯度提升树:通过负梯度(值等于残差)不断逼近真实值,负梯度 = 真实值-预测值
3、梯度提升树的构建流程
1、初始化弱学习器(目标值的均值作为预测值)
2、迭代构建学习器,每一个学习器拟合上一个学习器的负梯度
3、直到达到指定的学习器个数
4、当输入未知样本时,将所有弱学习器的输出结果组合起来作为强学习器的输出

构建第1个弱学习器,根据负梯度的计算方法得到下表:

当 6.5 作为切分点时,平方损失最小,此时得到第1棵决策树




4、代码案例
import pandas as pd
from sklearn.model_selection import train_test_split # 切割训练集 和 测试集
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.ensemble import GradientBoostingClassifier # 梯度提升树分类器
from sklearn.metrics import classification_report, accuracy_score # 模型评估
from sklearn.model_selection import GridSearchCV # 网格搜索
# 1、读取数据
df = pd.read_csv("../data/train.csv")
df.info()
# 2、数据预处理
# 2.1提取特征和标签
x = df[['Pclass', 'Sex', 'Age']].copy()
y = df['Survived'].copy()
# 2.2处理age
x['Age'] = x['Age'].fillna(x['Age'].mean())
# 2.3热编码
x = pd.get_dummies(x)
# 2.4 训练集 和 测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=18)
# 3.特征工程
# 4. 模型训练 预测 评估
# 场景1: 单个决策树 (CART)
# 4.1创建模型对象
es = DecisionTreeClassifier()
# 4.2模型训练
es.fit(x_train, y_train)
# 4.3模型预测
y_pre = es.predict(x_test)
print(f"单个决策树对象预测结果:{y_pre}")
# 4.4模型评估
print(f"单个决策树对象准确率:{accuracy_score(y_test, y_pre)}")
# 场景2:梯度提升树对象(GBDT)
# 4.1创建模型对象
es2 = GradientBoostingClassifier()
# 4.2模型训练
es2.fit(x_train, y_train)
# 4.3模型预测
y_pre2 = es2.predict(x_test)
print(f"梯度提升树对象预测结果:{y_pre2}")
# 4.4模型评估
print(f"梯度提升树对象准确率:{accuracy_score(y_test, y_pre2)}")
# 场景3:针对GBDT模型,进行参数调优
param_dict = {
'learning_rate': [0.3, 0.5, 0.8],
'n_estimators': [50, 80, 120, 200],
'max_depth': [3, 5, 7]
}
es3 = GradientBoostingClassifier()
es4=GridSearchCV(es3, param_dict,cv=2)
es4.fit(x_train,y_train)
print(f"网络搜索的模型准确率:{es4.best_score_}")
print(f"网格搜索和交叉验证后的模型参数:{es4.best_params_}")
print(f"网格搜索后的模型:{es4.best_estimator_}")
五、XGBoost
1、XGBoost算法的思想
极端梯度提升树,集成学习方法的王牌,在数据挖掘比赛中,大部分获胜者用了XGBoost。
XGB的构建思想:
a、构建模型的方法是最小化训练数据的损失函数:
训练的模型复杂度较高,易过拟合。
b、在损失函数中加入正则化项,
提高对位置的测试数据的泛化性能。
XGBoost(Extreme Gradient Boosting)是对GBDT的改进,并且在损失函数中加入了正则化项
正则化项用来降低模型的复杂度
中的 T 表示一棵树的叶子节点数量。
中的
表示叶子节点输出值组成的向量,
表示向量的模;
表示对该项的调节系数
例:有一颗树有3三叶子节点,第一个叶子节点输出值为2,第二个叶子节点输出值为1,第三个叶子节点输出值为-1,那么这颗树的复杂度为
XGBoost算法的思想实例
第一轮复习:做历年真题(建立第一棵弱学习器)
- 他先做了一套历年真题,然后对照答案批改。这相当于计算损失函数(Loss Function),即当前成绩(预测值)和满分(真实值)之间的差距。
- 他发现自己在“三角函数”和“概率统计”这两个章节错得最多。于是,他决定下一轮复习要重点攻克这两个薄弱环节。
这就好比XGBoost建立了第一棵树后,计算残差,并让下一棵树去重点拟合这些残差(错误)。
第二轮至第N轮复习:针对性强化训练(迭代增加弱学习器)
- 他针对“三角函数”做了大量专项练习,成绩提升了,但发现“立体几何”又成了新的薄弱点。
- 于是他又针对“立体几何”进行强化...
- 他不断重复这个过程:“做题 -> 发现最薄弱环节 -> 针对它进行强化训练”
这就是Boosting的思想:通过多轮迭代,每一轮都让新模型(新一轮复习)去学习上一轮剩下的最难的题目(残差),从而组合成一个强大的模型(完备的知识体系)。
关键部分:XGBoost的精髓——“正则化”
现在来理解最核心的概念:正则化(Ω(f))。
如果这个学生只追求最小化训练误差(即
min L(yi, ŷi)),他会怎么做?
- 他会没日没夜地刷题,甚至去死记硬背每一道题目的答案和步骤。
- 结果可能是:他对做过的题目了如指掌,但一旦考试中出现一道全新的、没见过的题型,他就完全不会做了。
这就是过拟合(Overfitting):模型在训练数据上表现极好,但在未知数据上表现很差。
XGBoost如何避免这种情况?它引入了“正则化”这个“学习教练”!
这个“教练”会给学生制定两条学习规则(正则化项),防止他过度学习:
- 控制复杂度(
):
T是叶子节点数,类比为“复习的章节数量”。教练会要求:“你的复习计划不能太复杂,不能一天换10个章节,要专注几个重点(比如最多3个)。” 这避免了模型变得过于复杂。- 平滑输出(½λ||ω||²):
ω是叶子节点输出值,类比为“刷题强度”。教练会要求:“你刷题要讲究方法,不能一味追求刷题量(ω值不能过大),要注重理解和归纳(让输出平滑)。” 这确保了模型的稳定性。所以,XGBoost的最终目标不是一味地追求刷题高分,而是:
最终成绩 = 刷题得分 - 过度复杂的惩罚
(Obj(θ) = Loss + Ω(f))
这个“教练”的规则,保证了学生通过最高效、最科学的复习方式,最终真正掌握知识的核心原理,从而在未知的新考题(测试集) 上也能取得好成绩。这就是泛化能力(Generalization Ability)。
2、XGBoost目标函数

详细推导过程见资源包
3、XGBoost的算法API
(1)XGB的安装和使用
在sklean机器学习库中没有集成xgb。想要使用xgb,需要手工安装:
pip3 install xgboost -i https://pypi.tuna.tsinghua.edu.cn/simple
(2)XGB的编码风格
a、原生 API (Native API):支持非sklearn方式,也即是自己的风格
核心对象/类:
xgb.DMatrix,xgb.train,xgb.cv数据格式:必须使用 XGBoost 自定义的
DMatrix 对象常用方法:
xgb.train() 模型训练
xgb.cv() 交叉验证
predict() 预测
b、Scikit-learn 风格 API (Scikit-learn API):支持sklearn方式,调用方式保持sklearn的形式
核心对象/类:
XGBClassifier,XGBRegressor,XGBRanker等数据格式:支持 NumPy arrays, Pandas DataFrames, SciPy matrices 等
常用方法:
fit(),predict(),predict_proba(),score()
(3)重要参数对照说明
| 作用 | 原生 API 参数名 | Scikit-learn API 参数名 | 常见取值/说明 |
| 学习率 | eta | learning_rate | [0, 1],常设为 0.01-0.3 |
| 树的数量 | num_boost_round | n_estimators | 正整数 |
| 树的最大深度 | max_depth | max_depth | 正整数 |
| 目标函数 | objective | objective | 如 binary:logistic, multi:softmax |
| 随机种子 | seed | random_state | 整数 |
4、代码案例(重点)(作业)
案例练习:红酒品质分类
已知:数据集共包含 11 个特征,共计 3269 条数据. 我们通过训练模型来预测红酒的品质, 品质共有 6个类别, 分别使用数字:0、 1、2、3、4、5 来表示(数据源中类别不是[0,5],需导入数据后进行数据预处理)
需求:对红酒品质进行多分类
1、 拆分训练集和测试集,拆分后存储到CSV文件
2、训练模型并导出保存模型
3、测试模型(网格搜索+交叉验证)
注意:目标是多分类,且数据存在样本不均衡问题
代码解答于下一篇学习记录资源包中附带。
更多推荐



所有评论(0)