5.1 集成学习算法简介
1 什么是集成学习
集成学习通过建立几个模型来解决单一预测问题。它的工作原理是生成多个分类器/模型,各自独立地学习和作出预测。这些预测最后结合成组合预测,因此优于任何一个单分类的做出预测。
2 复习:机器学习的两个核心任务
- 任务一:如何优化训练数据 —> 主要用于解决欠拟合问题
- 任务二:如何提升泛化性能 —> 主要用于解决过拟合问题
3 集成学习中boosting和Bagging
只要单分类器的表现不太差,集成学习的结果总是要好于单分类器的
4 小结
- 什么是集成学习【了解】
- 通过建立几个模型来解决单一预测问题
- 机器学习两个核心任务【知道】
- 1.解决欠拟合问题
- 弱弱组合变强
- boosting
- 2.解决过拟合问题
- 互相遏制变壮
- Bagging
5.2 Bagging和随机森林
1 Bagging集成原理
目标:把下面的圈和方块进行分类
实现过程:
1) 采样不同数据集
2)训练分类器
3)平权投票,获取最终结果
4)主要实现过程小结
2 随机森林构造过程
在机器学习中,随机森林是一个包含多个决策树的分类器,并且其输出的类别是由个别树输出的类别的众数而定。
随机森林 = Bagging + 决策树
例如, 如果你训练了5个树, 其中有4个树的结果是True, 1个树的结果是False, 那么最终投票结果就是True
随机森林够造过程中的关键步骤(M表示特征数目):
1)一次随机选出一个样本,有放回的抽样,重复N次(有可能出现重复的样本)
2) 随机去选出m个特征, m <<M,建立决策树
- 思考
- 1.为什么要随机抽样训练集?
- 如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的
- 2.为什么要有放回地抽样?
- 如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,都是绝对“片面的”(当然这样说可能不对),也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决。
3 包外估计 (Out-of-Bag Estimate)
在随机森林构造过程中,如果进行有放回的抽样,我们会发现,总是有一部分样本我们选不到。
- 这部分数据,占整体数据的比重有多大呢?
- 这部分数据有什么用呢?
3.1 包外估计的定义
如何理解无偏估计?无偏估计有什么用?
1.如何理解无偏估计
无偏估计:就是我认为所有样本出现的概率一样。
假如有N种样本我们认为所有样本出现概率都是1/N。然后根据这个来计算数学期望。此时的数学期望就是我们平常讲的平均值。
数学期望本质就是平均值
2.无偏估计为何叫做“无偏”?它要“估计”什么?
首先回答第一个问题:它要“估计”什么?
- 它要估计的是整体的数学期望(平均值)。
3.为何要用无偏估计?
因为现实生活中我不知道某个样本出现的概率啊,就像骰子,我不知道他是不是加过水银。
所以我们暂时按照每种情况出现概率一样来算。
3.2 包外估计的用途
- 当基学习器是决策树时,可使用包外样本来辅助剪枝 ,或用于估计决策树中各结点的后验概率以辅助对零训练样本结点的处理;
- 当基学习器是神经网络时,可使用包外样本来辅助早期停止以减小过拟合 。
3 随机森林api介绍
- sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion=’gini’, max_depth=None, bootstrap=True, random_state=None, min_samples_split=2)
- n_estimators:integer,optional(default = 10)森林里的树木数量120,200,300,500,800,1200
- 在利用最大投票数或平均值来预测之前,你想要建立子树的数量。
- Criterion:string,可选(default =“gini”)
- 分割特征的测量方法
- max_depth:integer或None,可选(默认=无)
- 树的最大深度 5,8,15,25,30
- 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:boolean,optional(default = True)
- 是否在构建树时使用放回抽样
- min_samples_split 内部节点再划分所需最小样本数
- 这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分,默认是2。
- 如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
- min_samples_leaf 叶子节点的最小样本数
- 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝, 默认是1。
- 叶是决策树的末端节点。 较小的叶子使模型更容易捕捉训练数据中的噪声。
一般来说,我更偏向于将最小叶子节点数目设置为大于50。
- min_impurity_split: 节点划分最小不纯度
- 这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。
一般不推荐改动默认值1e-7。
- 上面决策树参数中最重要的包括
- 最大特征数max_features,
- 最大深度max_depth,
- 内部节点再划分所需最小样本数min_samples_split
- 叶子节点最少样本数min_samples_leaf。
4 随机森林预测案例
- 实例化随机森林
# 随机森林去进行预测
rf = RandomForestClassifier()
- 定义超参数的选择列表
param = {"n_estimators": [120,200,300,500,800,1200], "max_depth": [5, 8, 15, 25, 30]}
- 使用GridSearchCV进行网格搜索
# 超参数调优
gc = GridSearchCV(rf, param_grid=param, cv=2)
gc.fit(x_train, y_train)
print("随机森林预测的准确率为:", gc.score(x_test, y_test))
注意
- 随机森林的建立过程
- 树的深度、树的个数等需要进行超参数调优
5 bagging集成优点
Bagging + 决策树/线性回归/逻辑回归/深度学习… = bagging集成学习方法
经过上面方式组成的集成学习方法:
- 均可在原有算法上提高约2%左右的泛化正确率
- 简单, 方便, 通用
6 小结
- bagging集成过程【知道】
- 1.采样 — 从所有样本里面,采样一部分
- 2.学习 — 训练弱学习器
- 3.集成 — 使用平权投票
- 随机森林介绍【知道】
- 随机森林定义
- 随机森林 = Bagging + 决策树
- 流程:
- 1.随机选取m条数据
- 2.随机选取k个特征
- 3.训练决策树
- 4.重复1-3
- 5.对上面的若决策树进行平权投票
- 注意:
- 1.随机选取样本,且是有放回的抽取
- 2.选取特征的时候吗,选择m<<M
- M是所有的特征数
- 包外估计
- 如果进行有放回的对数据集抽样,会发现,总是有一部分样本选不到;
- api
- sklearn.ensemble.RandomForestClassifier()
- Bagging + 决策树/线性回归/逻辑回归/深度学习… = bagging集成学习方法【了解】
- bagging的优点【了解】
- 1.均可在原有算法上提高约2%左右的泛化正确率
- 2.简单, 方便, 通用
5.3 otto案例介绍 -- Otto Group Product Classification Challenge
1.背景介绍
奥托集团是世界上最大的电子商务公司之一,在20多个国家设有子公司。该公司每天都在世界各地销售数百万种产品,所以对其产品根据性能合理的分类非常重要。
不过,在实际工作中,工作人员发现,许多相同的产品得到了不同的分类。本案例要求,你对奥拓集团的产品进行正确的分分类。尽可能的提供分类的准确性。
链接:https://www.kaggle.com/c/otto-group-product-classification-challenge/overview
2.数据集介绍
- 本案例中,数据集包含大约200,000种产品的93个特征。
- 其目的是建立一个能够区分otto公司主要产品类别的预测模型。
- 所有产品共被分成九个类别(例如时装,电子产品等)。
- id - 产品id
- feat_1, feat_2, ..., feat_93 - 产品的各个特征
- target - 产品被划分的类别
3.评分标准
本案例中,最后结果使用多分类对数损失进行评估。
具体公式:
4.实现过程
4.1 流程分析
- 获取数据
- 数据基本处理
- 数据量比较大,尝试是否可以进行数据分割
- 转换目标值表示方式
- 模型训练
- 模型基本训练
4.2 代码实现
In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
数据获取
In [2]:
data = pd.read_csv("./data/otto/train.csv")
In [3]:
data.head()
Out[3]:
id | feat_1 | feat_2 | feat_3 | feat_4 | feat_5 | feat_6 | feat_7 | feat_8 | feat_9 | ... | feat_85 | feat_86 | feat_87 | feat_88 | feat_89 | feat_90 | feat_91 | feat_92 | feat_93 | target | |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Class_1 |
1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Class_1 |
2 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Class_1 |
3 | 4 | 1 | 0 | 0 | 1 | 6 | 1 | 5 | 0 | 0 | ... | 0 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | Class_1 |
4 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | Class_1 |
5 rows × 95 columns
In [4]:
data.shape
Out[4]:
(61878, 95)
In [5]:
data.describe()
Out[5]:
id | feat_1 | feat_2 | feat_3 | feat_4 | feat_5 | feat_6 | feat_7 | feat_8 | feat_9 | ... | feat_84 | feat_85 | feat_86 | feat_87 | feat_88 | feat_89 | feat_90 | feat_91 | feat_92 | feat_93 | |
count | 61878.000000 | 61878.00000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | ... | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 | 61878.000000 |
mean | 30939.500000 | 0.38668 | 0.263066 | 0.901467 | 0.779081 | 0.071043 | 0.025696 | 0.193704 | 0.662433 | 1.011296 | ... | 0.070752 | 0.532306 | 1.128576 | 0.393549 | 0.874915 | 0.457772 | 0.812421 | 0.264941 | 0.380119 | 0.126135 |
std | 17862.784315 | 1.52533 | 1.252073 | 2.934818 | 2.788005 | 0.438902 | 0.215333 | 1.030102 | 2.255770 | 3.474822 | ... | 1.151460 | 1.900438 | 2.681554 | 1.575455 | 2.115466 | 1.527385 | 4.597804 | 2.045646 | 0.982385 | 1.201720 |
min | 1.000000 | 0.00000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 15470.250000 | 0.00000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
50% | 30939.500000 | 0.00000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
75% | 46408.750000 | 0.00000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
max | 61878.000000 | 61.00000 | 51.000000 | 64.000000 | 70.000000 | 19.000000 | 10.000000 | 38.000000 | 76.000000 | 43.000000 | ... | 76.000000 | 55.000000 | 65.000000 | 67.000000 | 30.000000 | 61.000000 | 130.000000 | 52.000000 | 19.000000 | 87.000000 |
8 rows × 94 columns
In [7]:
# 图形可视化,查看数据分布
import seaborn as sns
sns.countplot(data.target)
plt.show()
由上图可以看出,该数据类别不均衡,所以需要后期处理
数据基本处理
数据已经经过脱敏,不再需要特殊处理
截取部分数据
In [8]:
new1_data = data[:10000]
new1_data.shape
Out[8]:
(10000, 95)
In [9]:
# 图形可视化,查看数据分布
import seaborn as sns
sns.countplot(new1_data.target)
plt.show()
使用上面方式获取数据不可行,然后使用随机欠采样获取响应的数据
In [10]:
# 随机欠采样获取数据
# 首先需要确定特征值\标签值
y = data["target"]
x = data.drop(["id", "target"], axis=1)
In [11]:
x.head()
Out[11]:
feat_1 | feat_2 | feat_3 | feat_4 | feat_5 | feat_6 | feat_7 | feat_8 | feat_9 | feat_10 | ... | feat_84 | feat_85 | feat_86 | feat_87 | feat_88 | feat_89 | feat_90 | feat_91 | feat_92 | feat_93 | |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 1 | 0 | 0 | 1 | 6 | 1 | 5 | 0 | 0 | 1 | ... | 22 | 0 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
5 rows × 93 columns
In [12]:
y.head()
Out[12]:
0 Class_1
1 Class_1
2 Class_1
3 Class_1
4 Class_1
Name: target, dtype: object
In [14]:
# 欠采样获取数据
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
X_resampled, y_resampled = rus.fit_resample(x, y)
In [15]:
x.shape, y.shape
Out[15]:
((61878, 93), (61878,))
In [16]:
X_resampled.shape, y_resampled.shape
Out[16]:
((17361, 93), (17361,))
In [17]:
# 图形可视化,查看数据分布
import seaborn as sns
sns.countplot(y_resampled)
plt.show()
把标签值转换为数字
In [18]:
y_resampled.head()
Out[18]:
0 Class_1
1 Class_1
2 Class_1
3 Class_1
4 Class_1
Name: target, dtype: object
In [19]:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_resampled = le.fit_transform(y_resampled)
In [20]:
y_resampled
Out[20]:
array([0, 0, 0, ..., 8, 8, 8])
分割数据
In [21]:
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2)
In [23]:
x_train.shape, y_train.shape
Out[23]:
((13888, 93), (13888,))
In [24]:
x_test.shape, y_test.shape
Out[24]:
((3473, 93), (3473,))
模型训练
基本模型训练
In [26]:
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(oob_score=True)
rf.fit(x_train, y_train)
Out[26]:
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
criterion='gini', max_depth=None, max_features='auto',
max_leaf_nodes=None, max_samples=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100,
n_jobs=None, oob_score=True, random_state=None,
verbose=0, warm_start=False)
In [28]:
y_pre = rf.predict(x_test)
y_pre
Out[28]:
array([7, 7, 0, ..., 1, 1, 0])
In [29]:
rf.score(x_test, y_test)
Out[29]:
0.7782896631154621
In [30]:
rf.oob_score_
Out[30]:
0.7659850230414746
In [31]:
# 图形可视化,查看数据分布
import seaborn as sns
sns.countplot(y_pre)
plt.show()
In [33]:
# logloss模型评估
from sklearn.metrics import log_loss
#log_loss(y_test, y_pre, eps=1e-15, normalize=True)
In [35]:
y_test, y_pre
Out[35]:
(array([7, 7, 8, ..., 1, 1, 0]), array([7, 7, 0, ..., 1, 1, 0]))
上面报错原因:logloss使用过程中,必须要求将输出用one-hot表示,
需要将这个多类别问题的输出结果通过OneHotEncoder修改为如下:
In [37]:
from sklearn.preprocessing import OneHotEncoder
one_hot = OneHotEncoder(sparse=False)
y_test1 = one_hot.fit_transform(y_test.reshape(-1, 1))
y_pre1 = one_hot.fit_transform(y_pre.reshape(-1, 1))
In [38]:
y_test1
Out[38]:
array([[0., 0., 0., ..., 0., 1., 0.],
[0., 0., 0., ..., 0., 1., 0.],
[0., 0., 0., ..., 0., 0., 1.],
...,
[0., 1., 0., ..., 0., 0., 0.],
[0., 1., 0., ..., 0., 0., 0.],
[1., 0., 0., ..., 0., 0., 0.]])
In [39]:
y_pre1
Out[39]:
array([[0., 0., 0., ..., 0., 1., 0.],
[0., 0., 0., ..., 0., 1., 0.],
[1., 0., 0., ..., 0., 0., 0.],
...,
[0., 1., 0., ..., 0., 0., 0.],
[0., 1., 0., ..., 0., 0., 0.],
[1., 0., 0., ..., 0., 0., 0.]])
In [40]:
# logloss模型评估
log_loss(y_test1, y_pre1, eps=1e-15, normalize=True)
Out[40]:
7.65760375009538
In [41]:
# 改变预测值的输出模式,让输出结果为百分占比,降低logloss值
y_pre_proba = rf.predict_proba(x_test)
In [42]:
y_pre_proba
Out[42]:
array([[0.04, 0. , 0. , ..., 0.01, 0.92, 0.01],
[0.03, 0.02, 0.03, ..., 0.2 , 0.48, 0.08],
[0.41, 0.06, 0.06, ..., 0.05, 0.04, 0.31],
...,
[0. , 0.49, 0.28, ..., 0.02, 0. , 0.01],
[0.01, 0.54, 0.09, ..., 0. , 0. , 0.02],
[0.31, 0.03, 0.02, ..., 0.11, 0.13, 0.13]])
In [43]:
rf.oob_score_
Out[43]:
0.7659850230414746
In [44]:
# logloss模型评估
log_loss(y_test1, y_pre_proba, eps=1e-15, normalize=True)
Out[44]:
0.747188586960504
模型调优
n_estimators, max_feature, max_depth, min_samples_leaf
确定最优的n_estimators
In [45]:
# 确定n_estimators的取值范围
tuned_parameters = range(10, 200, 10)
# 创建添加accuracy的一个numpy
accuracy_t = np.zeros(len(tuned_parameters))
# 创建添加error的一个numpy
error_t = np.zeros(len(tuned_parameters))
# 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
rf2 = RandomForestClassifier(n_estimators=one_parameter,
max_depth=10,
max_features=10,
min_samples_leaf=10,
oob_score=True,
random_state=0,
n_jobs=-1)
rf2.fit(x_train, y_train)
# 输出accuracy
accuracy_t[j] = rf2.oob_score_
# 输出log_loss
y_pre = rf2.predict_proba(x_test)
error_t[j] = log_loss(y_test, y_pre, eps=1e-15, normalize=True)
print(error_t)
[1.1363637 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. ]
[1.1363637 1.12502792 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 0.
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
0. 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 0. 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 0. 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 1.11551087 0. 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 1.11551087 1.11454353 0. 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 1.11551087 1.11454353 1.11539224 0.
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 1.11551087 1.11454353 1.11539224 1.11527522
0. ]
[1.1363637 1.12502792 1.12888587 1.12663772 1.12185466 1.11919361
1.11933586 1.1209923 1.12231238 1.11887248 1.11682286 1.11668191
1.11607429 1.11658966 1.11551087 1.11454353 1.11539224 1.11527522
1.11545188]
In [50]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 4), dpi=100)
axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)
axes[0].set_xlabel("n_estimators")
axes[0].set_ylabel("error_t")
axes[1].set_xlabel("n_estimators")
axes[1].set_ylabel("accuracy_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
经过图像展示,最后确定n_estimators=175的时候,表现效果不错
确定最优的max_features
In [51]:
# 确定n_estimators的取值范围
tuned_parameters = range(5, 40, 5)
# 创建添加accuracy的一个numpy
accuracy_t = np.zeros(len(tuned_parameters))
# 创建添加error的一个numpy
error_t = np.zeros(len(tuned_parameters))
# 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
rf2 = RandomForestClassifier(n_estimators=175,
max_depth=10,
max_features=one_parameter,
min_samples_leaf=10,
oob_score=True,
random_state=0,
n_jobs=-1)
rf2.fit(x_train, y_train)
# 输出accuracy
accuracy_t[j] = rf2.oob_score_
# 输出log_loss
y_pre = rf2.predict_proba(x_test)
error_t[j] = log_loss(y_test, y_pre, eps=1e-15, normalize=True)
print(error_t)
[1.21048299 0. 0. 0. 0. 0.
0. ]
[1.21048299 1.11570883 0. 0. 0. 0.
0. ]
[1.21048299 1.11570883 1.08477789 0. 0. 0.
0. ]
[1.21048299 1.11570883 1.08477789 1.06988755 0. 0.
0. ]
[1.21048299 1.11570883 1.08477789 1.06988755 1.06604809 0.
0. ]
[1.21048299 1.11570883 1.08477789 1.06988755 1.06604809 1.05863125
0. ]
[1.21048299 1.11570883 1.08477789 1.06988755 1.06604809 1.05863125
1.06050897]
In [52]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 4), dpi=100)
axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)
axes[0].set_xlabel("max_features")
axes[0].set_ylabel("error_t")
axes[1].set_xlabel("max_features")
axes[1].set_ylabel("accuracy_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
经过图像展示,最后确定max_features=15的时候,表现效果不错
确定最优的max_depth
In [53]:
# 确定n_estimators的取值范围
tuned_parameters = range(10, 100, 10)
# 创建添加accuracy的一个numpy
accuracy_t = np.zeros(len(tuned_parameters))
# 创建添加error的一个numpy
error_t = np.zeros(len(tuned_parameters))
# 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
rf2 = RandomForestClassifier(n_estimators=175,
max_depth=one_parameter,
max_features=15,
min_samples_leaf=10,
oob_score=True,
random_state=0,
n_jobs=-1)
rf2.fit(x_train, y_train)
# 输出accuracy
accuracy_t[j] = rf2.oob_score_
# 输出log_loss
y_pre = rf2.predict_proba(x_test)
error_t[j] = log_loss(y_test, y_pre, eps=1e-15, normalize=True)
print(error_t)
[1.08477789 0. 0. 0. 0. 0.
0. 0. 0. ]
[1.08477789 0.85395849 0. 0. 0. 0.
0. 0. 0. ]
[1.08477789 0.85395849 0.82965202 0. 0. 0.
0. 0. 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0. 0.
0. 0. 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0.8302702 0.
0. 0. 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0.8302702 0.8302702
0. 0. 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0.8302702 0.8302702
0.8302702 0. 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0.8302702 0.8302702
0.8302702 0.8302702 0. ]
[1.08477789 0.85395849 0.82965202 0.83011988 0.8302702 0.8302702
0.8302702 0.8302702 0.8302702 ]
In [54]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 4), dpi=100)
axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)
axes[0].set_xlabel("max_depth")
axes[0].set_ylabel("error_t")
axes[1].set_xlabel("max_depth")
axes[1].set_ylabel("accuracy_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
经过图像展示,最后确定max_depth=30的时候,表现效果不错
确定最优的min_sample_leaf
In [56]:
# 确定n_estimators的取值范围
tuned_parameters = range(1, 10, 2)
# 创建添加accuracy的一个numpy
accuracy_t = np.zeros(len(tuned_parameters))
# 创建添加error的一个numpy
error_t = np.zeros(len(tuned_parameters))
# 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
rf2 = RandomForestClassifier(n_estimators=175,
max_depth=30,
max_features=15,
min_samples_leaf=one_parameter,
oob_score=True,
random_state=0,
n_jobs=-1)
rf2.fit(x_train, y_train)
# 输出accuracy
accuracy_t[j] = rf2.oob_score_
# 输出log_loss
y_pre = rf2.predict_proba(x_test)
error_t[j] = log_loss(y_test, y_pre, eps=1e-15, normalize=True)
print(error_t)
[0.71382973 0. 0. 0. 0. ]
[0.71382973 0.74611594 0. 0. 0. ]
[0.71382973 0.74611594 0.77748299 0. 0. ]
[0.71382973 0.74611594 0.77748299 0.80311457 0. ]
[0.71382973 0.74611594 0.77748299 0.80311457 0.82513751]
In [57]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 4), dpi=100)
axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)
axes[0].set_xlabel("min_sample_leaf")
axes[0].set_ylabel("error_t")
axes[1].set_xlabel("min_sample_leaf")
axes[1].set_ylabel("accuracy_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
经过图像展示,最后确定min_sample_leaf=1的时候,表现效果不错
确定最优模型
n_estimators=175,
max_depth=30,
max_features=15,
min_samples_leaf=1,
In [59]:
rf3 = RandomForestClassifier(n_estimators=175, max_depth=30, max_features=15, min_samples_leaf=1,
oob_score=True, random_state=40, n_jobs=-1)
rf3.fit(x_train, y_train)
rf3.fit(x_train, y_train)
Out[59]:
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
criterion='gini', max_depth=30, max_features=15,
max_leaf_nodes=None, max_samples=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=175,
n_jobs=-1, oob_score=True, random_state=40, verbose=0,
warm_start=False)
In [60]:
rf3.score(x_test, y_test)
Out[60]:
0.7837604376619637
In [61]:
rf3.oob_score_
Out[61]:
0.7746255760368663
In [62]:
y_pre_proba1 = rf3.predict_proba(x_test)
log_loss(y_test, y_pre_proba1)
Out[62]:
0.7113183040048942
生成提交数据¶
In [63]:
test_data = pd.read_csv("./data/otto/test.csv")
In [64]:
test_data.head()
Out[64]:
id | feat_1 | feat_2 | feat_3 | feat_4 | feat_5 | feat_6 | feat_7 | feat_8 | feat_9 | ... | feat_84 | feat_85 | feat_86 | feat_87 | feat_88 | feat_89 | feat_90 | feat_91 | feat_92 | feat_93 | |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 11 | 1 | 20 | 0 | 0 | 0 | 0 | 0 |
1 | 2 | 2 | 2 | 14 | 16 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 2 | 0 |
2 | 3 | 0 | 1 | 12 | 1 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 1 |
3 | 4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 3 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 5 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 2 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 0 | 0 |
5 rows × 94 columns
In [65]:
test_data_drop_id = test_data.drop(["id"], axis=1)
test_data_drop_id.head()
Out[65]:
feat_1 | feat_2 | feat_3 | feat_4 | feat_5 | feat_6 | feat_7 | feat_8 | feat_9 | feat_10 | ... | feat_84 | feat_85 | feat_86 | feat_87 | feat_88 | feat_89 | feat_90 | feat_91 | feat_92 | feat_93 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | ... | 0 | 0 | 11 | 1 | 20 | 0 | 0 | 0 | 0 | 0 |
1 | 2 | 2 | 14 | 16 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 2 | 0 |
2 | 0 | 1 | 12 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 1 |
3 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 3 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 2 | 0 | 3 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 0 | 0 |
5 rows × 93 columns
In [66]:
y_pre_test = rf3.predict_proba(test_data_drop_id)
In [67]:
y_pre_test
Out[67]:
array([[3.42857143e-02, 4.00000000e-02, 6.00000000e-02, ...,
4.01785714e-02, 5.71428571e-03, 2.28571429e-02],
[1.14285714e-01, 4.00000000e-02, 3.42857143e-02, ...,
2.85714286e-02, 2.17142857e-01, 2.85714286e-02],
[5.71428571e-03, 0.00000000e+00, 0.00000000e+00, ...,
0.00000000e+00, 5.71428571e-03, 0.00000000e+00],
...,
[1.72644377e-02, 2.48465399e-01, 4.16172412e-01, ...,
3.42857143e-02, 1.21580547e-04, 5.71428571e-03],
[1.72287863e-02, 3.06668815e-01, 1.07797351e-01, ...,
3.93841747e-04, 0.00000000e+00, 5.71428571e-03],
[1.92414742e-02, 2.14828481e-01, 3.61958916e-01, ...,
1.60309793e-01, 0.00000000e+00, 1.63868444e-03]])
In [68]:
result_data = pd.DataFrame(y_pre_test, columns=["Class_"+str(i) for i in range(1, 10)])
In [69]:
result_data.head()
Out[69]:
Class_1 | Class_2 | Class_3 | Class_4 | Class_5 | Class_6 | Class_7 | Class_8 | Class_9 | |
0 | 0.034286 | 0.040000 | 0.060000 | 0.774107 | 0.000000 | 0.022857 | 0.040179 | 0.005714 | 0.022857 |
1 | 0.114286 | 0.040000 | 0.034286 | 0.034286 | 0.017143 | 0.485714 | 0.028571 | 0.217143 | 0.028571 |
2 | 0.005714 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.988571 | 0.000000 | 0.005714 | 0.000000 |
3 | 0.027714 | 0.215714 | 0.300000 | 0.277143 | 0.000000 | 0.000000 | 0.017143 | 0.006286 | 0.156000 |
4 | 0.237060 | 0.000080 | 0.005834 | 0.000000 | 0.005954 | 0.023057 | 0.018190 | 0.199658 | 0.510166 |
In [70]:
result_data.insert(loc=0, column="id", value=test_data.id)
In [71]:
result_data.head()
Out[71]:
id | Class_1 | Class_2 | Class_3 | Class_4 | Class_5 | Class_6 | Class_7 | Class_8 | Class_9 | |
0 | 1 | 0.034286 | 0.040000 | 0.060000 | 0.774107 | 0.000000 | 0.022857 | 0.040179 | 0.005714 | 0.022857 |
1 | 2 | 0.114286 | 0.040000 | 0.034286 | 0.034286 | 0.017143 | 0.485714 | 0.028571 | 0.217143 | 0.028571 |
2 | 3 | 0.005714 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.988571 | 0.000000 | 0.005714 | 0.000000 |
3 | 4 | 0.027714 | 0.215714 | 0.300000 | 0.277143 | 0.000000 | 0.000000 | 0.017143 | 0.006286 | 0.156000 |
4 | 5 | 0.237060 | 0.000080 | 0.005834 | 0.000000 | 0.005954 | 0.023057 | 0.018190 | 0.199658 | 0.510166 |
In [72]:
result_data.to_csv("./data/otto/submission.csv", index=False)
5.4 Boosting
1 什么是boosting
随着学习的积累从弱到强
简而言之:每新加入一个弱学习器,整体能力就会得到提升
代表算法:Adaboost,GBDT,XGBoost,LightGBM
2 实现过程:
3 bagging集成与boosting集成的区别:
- 区别一:数据方面
- Bagging:对数据进行采样训练;
- Boosting:根据前一轮学习结果调整数据的重要性。
- 区别二:投票方面
- Bagging:所有学习器平权投票;
- Boosting:对学习器进行加权投票。
- 区别三:学习顺序
- Bagging的学习是并行的,每个学习器没有依赖关系;
- Boosting学习是串行,学习有先后顺序。
- 区别四:主要作用
- Bagging主要用于提高泛化性能(解决过拟合,也可以说降低方差)
- Boosting主要用于提高训练精度 (解决欠拟合,也可以说降低偏差)
4 AdaBoost介绍
4.1 构造过程细节
4.2 关键点剖析
如何确认投票权重?
如何调整数据分布?
4.3 案例:
给定下面这张训练数据表所示的数据,假设弱分类器由xv产生,其阈值v使该分类器在训练数据集上的分类误差率最低,试用Adaboost算法学习一个强分类器。
4.4 api介绍
- from sklearn.ensemble import AdaBoostClassifier
- api链接:https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html#sklearn.ensemble.AdaBoostClassifier
5 小结
- 什么是Boosting 【知道】
- 随着学习的积累从弱到强
- 代表算法:Adaboost,GBDT,XGBoost,LightGBM
- bagging和boosting的区别【知道】
- 区别一:数据方面
- Bagging:对数据进行采样训练;
- Boosting:根据前一轮学习结果调整数据的重要性。
- 区别二:投票方面
- Bagging:所有学习器平权投票;
- Boosting:对学习器进行加权投票。
- 区别三:学习顺序
- Bagging的学习是并行的,每个学习器没有依赖关系;
- Boosting学习是串行,学习有先后顺序。
- 区别四:主要作用
- Bagging主要用于提高泛化性能(解决过拟合,也可以说降低方差)
- Boosting主要用于提高训练精度 (解决欠拟合,也可以说降低偏差)
- AdaBoost构造过程【知道】
- 步骤一:初始化训练数据权重相等,训练第一个学习器;
- 步骤二:AdaBoost反复学习基本分类器;
- 步骤三:对m个学习器进行加权投票
5.5 GBDT介绍
GBDT 的全称是 Gradient Boosting Decision Tree,梯度提升树,在传统机器学习算法中,GBDT算的上TOP3的算法。想要理解GBDT的真正意义,那就必须理解GBDT中的Gradient Boosting 和Decision Tree分别是什么?
1 Decision Tree:CART回归树
首先,GBDT使用的决策树是CART回归树,无论是处理回归问题还是二分类以及多分类,GBDT使用的决策树通通都是都是CART回归树。
- 为什么不用CART分类树呢?
- 因为GBDT每次迭代要拟合的是梯度值,是连续值所以要用回归树。
对于回归树算法来说最重要的是寻找最佳的划分点,那么回归树中的可划分点包含了所有特征的所有可取的值。
在分类树中最佳划分点的判别标准是熵或者基尼系数,都是用纯度来衡量的,但是在回归树中的样本标签是连续数值,所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度。
1.1 回归树生成算法(复习)
2 Gradient Boosting: 拟合负梯度
梯度提升树(Grandient Boosting)是提升树(Boosting Tree)的一种改进算法,所以在讲梯度提升树之前先来说一下提升树。
先来个通俗理解:假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时我们用6岁去拟合剩下的损失,发现差距还有4岁,第三轮我们用3岁拟合剩下的差距,差距就只有一岁了。如果我们的迭代轮数还没有完,可以继续迭代下面,每一轮迭代,拟合的岁数误差都会减小。最后将每次拟合的岁数加起来便是模型输出的结果。
提升树算法:
当损失函数是平方损失和指数损失函数时,梯度提升树每一步优化是很简单的,但是对于一般损失函数而言,往往每一步优化起来不那么容易。
针对这一问题,Friedman提出了梯度提升树算法,这是利用最速下降的近似方法,其关键是利用损失函数的负梯度作为提升树算法中的残差的近似值。
那么负梯度长什么样呢?
此时我们发现GBDT的负梯度就是残差,所以说对于回归问题,我们要拟合的就是残差。
那么对于分类问题呢?
- 二分类和多分类的损失函数都是logloss。
本文以回归问题为例进行讲解。
3 GBDT算法原理
上面两节分别将Decision Tree和Gradient Boosting介绍完了,下面将这两部分组合在一起就是我们的GBDT了。
4 实例介绍
4.1 数据介绍
根据如下数据,预测最后一个样本的身高。
编号 | 年龄(岁) | 体重(kg) | 身高(m)(标签值) |
0 | 5 | 20 | 1.1 |
1 | 7 | 30 | 1.3 |
2 | 21 | 70 | 1.7 |
3 | 30 | 60 | 1.8 |
4(要预测的) | 25 | 65 | ? |
4.2 模型训练
4.2.1 设置参数:
- 学习率:learning_rate=0.1
- 迭代次数:n_trees=5
- 树的深度:max_depth=3
4.2.2 开始训练
1.年龄x=5 7为左节点SEl,年龄x=21 30为右节点SEr。
2.年龄x 左节点SEl:
负梯度(残差)作为标签值y,然后计算左节点的标签值y的平均值c1=(-0.375+(-0.175))/2=-0.275
求平方差(标签值y-c1)^2:SEl=[-0.375-(-0.275)]^2 + [-0.175-(-0.275)]^2 = 0.02
3.年龄x 右节点SEr:
负梯度(残差)作为标签值y,然后计算右节点的标签值y的平均值c2=(0.225+0.325)/2=0.275
求平方差(标签值y-c2)^2:SEr=[0.225-0.275]^2 + [0.325-0.275]^2 = 0.005