决策树
- ID3 决策树:信息增益
- C4.5 决策树:信息增益率
- CRAT决策树:分类和回归树,基尼指数
- CRAT 回归树:平方均值
决策树是一套做决定的流程。
信息熵
熵 Entropy:信息论中代表随机变量不确定度的度量
- 熵越大,数据的不确定性度越高,信息就越多,数据越乱
- 熵越小,数据的不确定性越低,数据越规整
公式:(信息)熵 = ∑ -分类占比 * log2(分类占比)
举个例子: 数据 α:ABCDEFGH 数据 b:AAAABBCD
数据 α 包含了 8 种信息,数据 b 包含了 4 种信息,特征 α 的信息熵大于特征 b 的信息熵。
信息增益
特征a对训练数据集D的信息增益g(D,a),定义为集合D 的熵 H(D) 与特征 a 给定条件下D的熵 H(D|a) 之差。
数学公式:g(D,A) = H(D)-H(D|A)
信息增益 = 熵 - 条件熵
信息增益是越大越好
条件熵
ID3决策树构建流程
- 计算每个特征的信息增益
- 使用信息增益最大的特征将数据集 拆分为子集
- 使用该特征(信息增益最大的特征)作为决策树的一个节点
- 使用剩余特征对子集重复上述(1,2,3)过程
案例
经过上述比较,活跃度的信息增益大,因此采用活跃度,结论活跃度对于用户流失影响较大。因此活跃度作为根节点,以此类推
小结
- 决策树是什么?
决策树是一种树形结构,树中每个内部节点表示一个特征上的判断,每个分支代表一个判断结果的输出,每个叶子节点代表一种分类结果
- 信息熵?
在信息论中代表随机变量不确定度的度量
- 信息增益?
由于特征A而使得对数据D的分类不确定性减少的程度。
- ID3树的构建流程
- 计算每个特征的信息增益
- 使用信息增益最大的特征将数据集 拆分为子集
- 使用信息增益最大的特征作为决策树的一个节点
- 重复上述步骤
C4.5 决策树
信息增益率的作用
- 信息增益偏向于选择种类多的特征作为分裂依据
- 缓解ID3树中存在的不足
信息增益率
- 信息增益率 = 信息增益 /特征熵
- 相当于对信息增益进行修正,增加一个惩罚系数
CRAT 决策树
- Cart模型是一种决策树模型,它即可以用于分类,也可以用于回归。
- Cart回归树使用平方误差最小化策略,
- Cart分类生成树采用的基尼指数最小化策略。
CRAT决策树 线性数据
- CART决策树的作用?
分类和回归
- 基尼指数的作用?
特征筛选,基尼指数值越小,则说明优先选择该特征。
泰坦尼克号数据集案例
API 使用
案例背景
- 1912年4月15日,在她的处女航中,泰坦尼克号在与冰山相撞后沉没,在2224名乘客和机组人员中造成1502人死亡。
- 造成海难失事的原因之一是乘客和机组人员没有足够的救生艇。尽管幸存生存有一些运气因素,但有些人比其他人更容易生存,例如妇女,儿童和上流社会。
- 有了遇难和幸运数据,运用机器学习工具来预测哪些乘客可幸免于悲剧。
数据情况
- 数据集中的特征包括票的类别,是否存活,乘坐班次,年龄,登陆,home.dest,房间,船和性别等
- 乘坐班是指乘客班(1,2,3),是社会经济阶层的代表
- age数据存在缺失
结果
根据分析,泰坦尼克号生存预测,基于:船舱等级,性别,年龄做预测。是否LDied,Survivar
目的:演示决策树的分层
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
# TODO:1. 读取数据
titanic_df = pd.read_csv('./train.csv')
# print(titanic_df.info())
# TODO: 2. 数据预测处理
# 获取特征 和 标签
x = titanic_df[['Pclass', 'Sex', 'Age']].copy() # 船舱等级,性别和年龄
y = titanic_df['Survived']
# 用平均值填充空值
x['Age'] = x['Age'].fillna(x['Age'].mean())
# x.info()
# y.info()
# print(x.head(5))
# print(y.head(5))
# 对 Sex 列做热处理
x = pd.get_dummies(x)
# print(x.head(5))
# print(x.info())
# 拆分数据集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=22)
# TODO:3. 特征工程(标准化),这里不需要
# TODO: 4. 模型训练
estimator = DecisionTreeClassifier()
estimator.fit(x_train, y_train)
# TODO: 5 模型预测
y_predict = estimator.predict(x_test)
print('预测结果:', y_predict)
# TODO: 6 模型评估
print(f'准确率:{estimator.score(x_test, y_test)}')
print(f'分类评估报告: \n {classification_report(y_test, y_predict, target_names=["死亡", "存活"])}')
# TODO:7. 绘图
# 设置画布大小
plt.figure(figsize=(50, 50))
# 绘制决策树
# 参数一:决策树分类器,参数二 filled 填充颜色,参数三:最大深度
plot_tree(estimator, filled=True, max_depth=10)
# 保存图片
plt.savefig('/titanic.png')
plt.show()
CRAT 回归树
案例
回归类问题,即能用线性回归也可以用决策树回归,有限使用线性回归,因为决策树回归容易产生过拟合问题。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
# TODO: 1. 获取数据
x = np.array(list(range(1, 11))).reshape(-1, 1)
y = np.array([5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05])
# print(x)
# print(y)
# TODO: 2. 创建线性回归和决策树回归的模型
estimator1 = LinearRegression() # 线性回归
estimator2 = DecisionTreeRegressor(max_depth=1) # 决策树回归,层数1
estimator3 = DecisionTreeRegressor(max_depth=3) # 决策树回归,层数3
# TODO: 3. 训练模型
estimator1.fit(x, y)
estimator2.fit(x, y)
estimator3.fit(x, y)
# TODO: 4. 准备测试数据
x_test = np.arange(0.0, 10.0, 0.1).reshape(-1, 1)
# print(x_test)
# TODO: 5. 模型预测
y_predict1 = estimator1.predict(x_test)
y_predict2 = estimator2.predict(x_test)
y_predict3 = estimator3.predict(x_test)
# TODO: 6. 绘图
plt.figure(figsize=(10,5))
# 散点图(原始数据)
plt.scatter(x, y, color='gray', label='data')
# 线性回归预测结果
plt.plot(x_test, y_predict1, color='r', label='Liner')
# 决策树回归 层1 预测结果
plt.plot(x_test, y_predict2, color='b', label='DecisionTree(max_depth=1)')
# 决策树回归 层3 预测结果
plt.plot(x_test, y_predict3, color='g', label='DecisionTree(max_depth=3)')
plt.legend()
plt.xlabel('data')
plt.ylabel('target')
plt.title('Decision Tree Regression')
plt.show()
决策树剪枝
为什么要剪枝?
- 决策树剪枝是一种防止决策树过拟合的一种正则化方法;提高其泛化能力。
剪枝
- 把子树的节点全部删掉,使用叶子节点来替换
剪枝方法
- 预剪枝:指在决策树生成过程中,对每个节点在划分前先进行估计,若当前节点的划分不能带来决策树泛化性能提升,则停止划分并将当前节点标记为叶节点边生成边剪
- 后剪枝:是先从训练集生成一棵完整的决策树,然后自底向上地对非叶节点进行考察,若将该节点对应的子树替换为叶节点能带来决策树泛化性能提升,则将该子树替换为叶节点。
基于预剪枝策略从上表数据所生成的决策树如上图所示,其验证集精度为 71.4%.
后剪枝
集成学习
同时给多个模型训练,结果进行投票,票多数正确,每个模型抽取一定的样本数量,是会有交集的。
集成学习是机器学习中的一种思想,它通过多个模型的组合形成一个精度更高的模型,参与组合的模型称为弱学习器。训练时,使用训练集依次训练出这些弱学习器,对未知的样本进行预测时,使用这些弱学习器联合进行预测。
- Boosting 将预测失败的提升权重,转给下一个,不能并行。
- Bagging:随机森林
- Boosting:Adaboost、GBDT、XGBoost、LightGBM
Bagging思想
- 有放回的抽样(bootstrap抽样)产生不同的训练集,从而训练不同的学习器
- 通过平权投票、多数表决的方式决定预测结果
- 弱学习器可以并行训练
Boosting思想
- 每一个训练器重点关注前一个训练器不足的地方进行训练
- 通过加权投票的方式,得出预测结果
- 串行的训练方式
- 随着学习的积累从弱到强
- 每新加入一个弱学习器,整体能力就会得到提升
- 代表算法:Adaboost,GBDT,XGBoost,LightGBM
| bagging | boosting | |
|---|---|---|
| 数据采样 | 对数据进行有放回的采样训练 | 全部样本,根据前一轮学习结果调整数据的重要性 |
| 投票方式 | 所有学习器平权投票 | 对学习器进行加权投票 |
| 学习顺序 | 并行的,每个学习器没有依赖关系 | 串行,学习有先后顺序 |
随机森林
随机森林是基于 Bagging 思想实现的一种集成学习算法,采用决策树模型作为每一个弱学习器。
- 训练:
- 有放回的产生训练样本 随机挑选 n 个特征(n 小于总特征数量) 预测:平权投票,多数表决输出预测结果
API 介绍
随机森林案例
通过泰坦尼克号数据集,基于特征列:Pclass(船舱等级),Age(年龄),Sex(性别,需要热编码处理)来预测是否生存(Survivor)。
目的:Bagging suanfa ,使用集成学习算法。
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.tree import DecisionTreeClassifier
# TODO: 1.加载数据
data = pd.read_csv('./train.csv')
# TODO: 2.数据预处理
x = data[['Pclass', 'Sex', 'Age']].copy()
y = data['Survived']
x['Age'] = x['Age'].fillna(x['Age'].mean())
x = pd.get_dummies(x)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=22)
# TODO: 3.模型训练,单一决策树训练
estimator1 = DecisionTreeClassifier()
estimator1.fit(x_train, y_train)
print('单决策树准确率:', estimator1.score(x_test, y_test)) # 单决策树准确率: 0.7821229050279329
# TODO: 4.模型训练,随机森林训练
estimator2 = RandomForestClassifier()
estimator2.fit(x_train, y_train)
print('随机森林准确率:', estimator2.score(x_test, y_test)) # 随机森林准确率: 0.7877094972067039
# TODO: 5.模型训练,随机森林+网格搜索调优
estimator3 = RandomForestClassifier()
# 创建参数字典,定有模型可选的超参数
param_dict = {'n_estimators': [50, 60, 70, 80, 90, 200], 'max_depth': [2, 3, 4, 5, 6, 7, 8, 9, 10]}
# 网格搜索+交叉验证
gs_estimator = GridSearchCV(estimator3, param_grid=param_dict, cv=5) # 5 折交叉验证
gs_estimator.fit(x_train, y_train)
print('随机森林+网格搜索准确率:', gs_estimator.score(x_test, y_test))
# 查看最优的组合
print('随机森林+网格搜索准确率:', gs_estimator.best_params_) # 随机森林+网格搜索准确率: {'max_depth': 7, 'n_estimators': 60}
Adaboost算法
Adaptive Boosting(自适应提升)基于 Boosting思想实现的一种集成学习算法核心思想是通过逐步提高那些被前一步分类错误的样本的权重来训练一个强分类器。
- 自适应提升:如果上一步预算对了,就下降权重,算错了,就提升权重。
比如:每次切一刀,都会让正确的数据在正确处,把错误的交给下一个模型继续处理,注意正确的数据会进一步缩水,错误的数据进一步放大。
训练过程
算法推导
构建过程
已知训练数据见下面表格,假设弱分类器由 x 产生,预测结果使该分类器在训练数据集上的分类误差率最低,试用 Adaboost 算法学习一个强分类器。
推到过程
原则:预测对的,权重下降,预测错的,权重提升。
于是第一棵树就出来了,结果如下图:
第二棵树
第三个树
AdaBoost 葡萄酒数据案例
AdaBoost 算法原理:
- 它属于 Boosting 算法(提升算法),会训练 n 课决策树
- 数据集(全集)会全部交给 1 个模型(弱学习器)来训练,正确值 -> 权重下降,错误值 -> 权重上升。
- 把上个弱学习器的处理结果,交给第 2 个模型(弱学习器)来训练,正确值 -> 权重下降,错误值 -> 权重上升。
- 重复该操作,串行化执行,加权投票,直至获取最终结果。
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
# TODO: 1.导入数据
df_wine = pd.read_csv('./wine0501.csv')
# TODO: 2.数据预处理
# 删除类别为1的样本,从种过滤2,3类别,即:Adaboost 算法更适合做 二分法(二分类)
df_wine = df_wine[df_wine['Class label'] != 1]
x = df_wine[['Alcohol', 'Hue']].values
y = df_wine['Class label'].values
# 标签编码器,把标签改为 0 和 1
le = LabelEncoder()
y = le.fit_transform(y) # 将 [2,3] 转为 [0,1]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=22)
# TODO: 3.模型训练
ada = AdaBoostClassifier()
# 模型训练
ada.fit(x_train, y_train)
# 模型预测
y_pred_ada = ada.predict(x_test)
print('准确率:', accuracy_score(y_test, y_pred_ada)) # 准确率: 0.9583333333333334
# 手动创建,决策树对象,将其传入 AdaBoostClassifier 类,获取预测结果
# 创建决策树对象,参数一:熵,参数二:树的深度
# my_tree = DecisionTreeClassifier(criterion='entropy', max_depth=1)
my_tree = DecisionTreeClassifier(criterion='gini', max_depth=1)
# 创建AdaBoostClassifier类对象,参1:决策树对象,参2:迭代次数,参3:学习率,参4:随机种子
my_ada = AdaBoostClassifier(estimator=my_tree, n_estimators=500, learning_rate=0.1, random_state=22)
# 把上述的决策树对象,传入AdaBoostClassifier类对象,并获取预测结果
my_ada.fit(x_train, y_train)
y_pred_my_ada = my_ada.predict(x_test)
print('手写准确率:', accuracy_score(y_test, y_pred_my_ada)) # entropy 结果 0.9583333333333334 # gini 0.9583333333333334
- Adaboost概念
通过逐步提高被分类错误的样本的权重来训练一个强分类器。提升的思想 2. Adaboost构建过程
- 初始化数据权重,来训练第1个弱学习器。找最小的错误率计算模型权重,再更新模数据权重。
- 根据更新的数据集权重,来训练第2个弱学习器,再找最小的错误率计算模型权重,再更新模数据权重。
- 依次重复第2步,训练n个弱学习器。组合起来进行预测。结果大于0为正类、结果小于0为负类
BDT 提升树
思想
- 通过拟合残差的思想来进行提升
- 残差:真实值 - 预测值
生活中的例子
- 预测某人的年龄为100岁
- 第1次预测:对100岁预测,预测成80岁;100 – 80 = 20(残差)
- 第2次预测:上一轮残差20岁作为目标值,预测成16岁;20 – 16 = 4 (残差)
- 第3次预测:上一轮的残差4岁作为目标值,预测成3.2岁;4 – 3.2 = 0.8(残差)
- 若三次预测的结果串联起来: 80 + 16 + 3.2 = 99.2
- 通过拟合残差可将多个弱学习器组成一个强学习器,这就是提升树的最朴素思想
GBDT 梯度提升树
到了第二棵树,就要把上一个的结果作为第二棵树的目标值。
文档
简单理解:如下
梯度提升树的构建流程
- 初始化弱学习器(目标值的均值作为预测值)
- 迭代构建学习器,每一个学习器拟合上一个学习器的负梯度
- 直到达到指定的学习器个数
- 当输入未知样本时,将所有弱学习器的输出结果组合起来作为强学习器的输出
GBDT 梯度提升树泰坦尼克号案例
提升树,采用残差思路,不断的逼近正确值。
残差:真实值 - 预测值
流程
| 真实值 | 预测值 | 残差 |
|---|---|---|
| 100(岁) | 80(岁) | 20(岁) |
| 20(岁) | 16(岁) | 4(岁) |
| 4(岁) | 3.2(岁) | 0.8(岁) |
| 0.8(岁) | ... | ... |
梯度提升树,采用负梯度思路,来不断逼近正确值
负梯度:经过公式推导,负梯度 = 真实值 - 预测值,所以可以把负梯度当作类似于 BDT树的残差来用。
梯度提升树:采用类似于梯度下降算法,不断的比较正确值,把多个弱学习器组合成 1 个强学习器,属于 Boosting 思想,也是机器学习的一种。
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split, GridSearchCV
# TODO:1.读取数据
data = pd.read_csv('./train.csv')
# TODO:2.预数据处理
x = data[['Pclass', 'Sex', 'Age']].copy()
y = data['Survived']
x['Age'] = x['Age'].fillna(x['Age'].mean())
x = pd.get_dummies(x)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=22)
# TODO:3.构建模型
# 构建 GBDT 梯度提升树,采用默认值
estimator = GradientBoostingClassifier()
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
# 模型评估
print('准确率:', estimator.score(x_test, y_test)) # 准确率: 0.7541899441340782
print(f'梯度提升树,预测结果:{accuracy_score(y_test, y_predict)}') # 准确率: 0.7541899441340782
# TODO:4.模型调优
gbc = GradientBoostingClassifier()
# 参数调优
param_grid = {'n_estimators': [70, 80, 90, 100, 130, 150, 200], 'max_depth': [2, 3, 4, 5, 6], 'random_state': [22]}
# 构建 GridSearchCV,采用网格搜索+交叉验证提升模型效果
gbc_model = GridSearchCV(gbc, param_grid=param_grid, cv=5)
# 训练模型
gbc_model.fit(x_train, y_train)
# 模型预测
y_predict = gbc_model.predict(x_test)
# 模型评估
print('准确率:', gbc_model.score(x_test, y_test)) # 准确率: 0.7653631284916201
print(f'交叉验证梯度提升树,预测结果:{accuracy_score(y_test, y_predict)}') # 交叉验证梯度提升树,预测结果:0.7653631284916201
总结
- 提升树?
每一个弱学习器通过拟合残差来构建强学习器 2. 梯度提升树
每一个弱学习器通过拟合负梯度来构建强学习器
XGBoost
笔记
最后