三个臭皮匠,顶个诸葛亮!
一、简介
1. 什么是集成学习
集成学习通过建立几个模型来解决单一预测问题。它的工作原理是生成多个分类器/模型,各自独立地学习和作出预测。这些预测最后结合成组合预测,因此优于任何一个单分类的做出预测。
2. 机器学习的两个核心任务
- 任务一:如何优化训练数据
- 主要用于解决欠拟合问题
- 任务二:如何提升泛化性能
- 主要用于解决过拟合问题
3. boosting和Bagging
二、Bagging和随机森林
1. Bagging
- bagging是Boostrap Aggregating的缩写,意为自助法。
- 它使用到了Boostrap sampling(随机采样法)的思想,
- 每个基学习器使用的训练数据是差异化的,但是这些训练数据又都来自于同一个整体,最终综合所有基学习器的结果来决定最后集成算法的结果输出。
- 例如随机森林(RandomForest)就是采用了bagging思想的集成学习算法,它使用的基学习器是决策树。
- 实现过程:
- 采样不同数据集
- 训练分类器
- 平权投票,获取最终结果
2. 随机森林
2.1 构造过程
2.2 要点
- 1.为什么要随机抽样训练集?
- 如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的
- 2.为什么要有放回地抽样?
- 如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,都是绝对“片面的”(当然这样说可能不对),也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决。
3. 包外估计
3.1 包外估计的用途
- 当基学习器是决策树时,可使用包外样本来辅助剪枝 ,或用于估计决策树中各结点的后验概率以辅助对零训练样本结点的处理;
- 当基学习器是神经网络时,可使用包外样本来辅助早期停止以减小过拟合 。
4. 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。
- n_estimators:integer,optional(default = 10)森林里的树木数量120,200,300,500,800,1200
- 上面决策树参数中最重要的包括:
- 最大特征数max_features,
- 最大深度max_depth,
- 内部节点再划分所需最小样本数min_samples_split
- 叶子节点最少样本数min_samples_leaf。
5. 随机森林预测案例
案例使用了 泰坦尼克生存预测的数据
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
from sklearn.feature_extraction import DictVectorizer
from sklearn.model_selection import train_test_split,GridSearchCV
import numpy as np
data = pd.read_csv('../decision tree/data/titanic.txt')
data['age'].fillna(value=data['age'].mean(), inplace= True)
x = data[['pclass','age', 'sex']]
y = data['survived']
x_train, x_test, y_train, y_test = train_test_split(x,y)
# 字典特征提取
transfer = DictVectorizer(sparse=False)
x_train = transfer.fit_transform(x_train.to_dict(orient="records"))
x_test = transfer.fit_transform(x_test.to_dict(orient="records"))
rf = RandomForestClassifier()
# 定义超参数列表
param = {"n_estimators": [120,200,300,500,800,1200], "max_depth": [5, 8, 15, 25, 30]}
gc = GridSearchCV(rf, param_grid=param, cv=2)
gc.fit(x_train, y_train)
print("随机森林预测的准确率为:", gc.score(x_test, y_test))
print("最优参数组合:",gc.best_estimator_)
6. bagging集成的优点
- Bagging + 决策树/线性回归/逻辑回归/深度学习… = bagging集成学习方法
- 经过上面方式组成的集成学习方法:
- 均可在原有算法上提高约2%左右的泛化正确率
- 简单, 方便, 通用
7. otto案例介绍
此案例来自kaggle, 参考:https://www.kaggle.com/c/otto-group-product-classification-challenge/overview
#!/usr/bin/env python
# coding: utf-8
# In[7]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from collections import Counter
# # 获取数据
# In[2]:
data = pd.read_csv('./data/otto/train.csv')
data.head()
# In[4]:
data.describe()
# In[6]:
data.shape
# In[8]:
Counter(data['target'])
# In[13]:
# 图形可视化,查看数据分布
import seaborn as sns
plt.figure(figsize=(10,6))
sns.countplot(data.target)
plt.show()
# 由上图可看出,该数据类别严重不均衡,所以需要后期处理
# # 数据基本处理
# ## 截取部分数据
# In[15]:
new_data = data[:10000]
# In[16]:
new_data.shape
# In[17]:
# 图形可视化,查看数据分布
import seaborn as sns
plt.figure(figsize=(10,6))
sns.countplot(new_data.target)
plt.show()
# 以上方式截取数据不可取,因为大部分目标值都不在该区间内
# In[20]:
# 随机欠采样获取数据
# 首先需要确定标签值和特征值
y = data.target
x = data.drop(['id','target'], axis=1)
x.head()
# In[22]:
# 欠采样获取数据
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
x_res,y_res = rus.fit_resample(x, y)
# In[23]:
x_res.shape
# In[24]:
# 图形可视化,查看数据分布
import seaborn as sns
plt.figure(figsize=(10,6))
sns.countplot(y_res)
plt.show()
# ## 把标签值转换为数字
# In[26]:
y_res.head()
# In[27]:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_res = le.fit_transform(y_res)
y_res
# ## 分割数据
# In[28]:
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_res, y_res, test_size=0.2)
# In[29]:
x_train.shape
# In[30]:
x_test.shape
# # 模型训练
# ## 模型基本训练
# In[31]:
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(oob_score=True)
rf.fit(x_train,y_train)
y_pre = rf.predict(x_test)
score = rf.score(x_test, y_test)
print('得分为:', score)
# In[32]:
rf.oob_score_
# In[33]:
# 图形可视化,查看数据分布
import seaborn as sns
sns.countplot(y_pre)
plt.show()
# In[36]:
# logloss模型评估损失
from sklearn.metrics import log_loss
log_loss(y_test, y_pre, eps=1e-15,normalize=True)
# 上面报错原因:logloss使用过程中,必须要求将输出用one-hot表示,
# 需要将这个多类别问题的输出结果通过OneHotEncoder修改为如下:
# In[38]:
from sklearn.preprocessing import OneHotEncoder
one_hot = OneHotEncoder(sparse=False)
y_pre1 = one_hot.fit_transform(y_pre.reshape(-1,1))
y_test1 = one_hot.fit_transform(y_test.reshape(-1,1))
# In[39]:
y_pre1
# In[40]:
y_test1
# In[41]:
# logloss模型评估
log_loss(y_test1, y_pre1, eps=1e-15,normalize=True)
# In[42]:
# 改变预测值的输出模式,让输出结果为百分占比,降低logloss值
y_pre_prob = rf.predict_proba(x_test)
# In[43]:
y_pre_prob
# In[44]:
log_loss(y_test1, y_pre_prob, eps=1e-15, normalize=True)
# ## 模型调优
# n_estimators, max_feature, max_depth, min_samples_leaf
#
# 这里可以使用网格搜索得到全局最优参数,但是时间复杂度较高
#
# 这里采用控制单一变量法,随机搜索得到相对较优的参数
# ### 确定最优的n_estimators
# In[46]:
# 确定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_
# 输出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)
# In[50]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20,4), dpi=100)
axes[0].plot(tuned_parameters, accuracy_t)
axes[1].plot(tuned_parameters, error_t)
axes[0].set_xlabel("n_estimators")
axes[0].set_ylabel("accuracy_t")
axes[1].set_xlabel("n_estimators")
axes[1].set_ylabel("error_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
# 经过图像展示,最后确定n_estimators=175的时候,表现效果不错
# ### 确定最优的max_features
# In[51]:
# 确定max_features的取值范围
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_
# 输出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)
# In[52]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20,4), dpi=100)
axes[0].plot(tuned_parameters, accuracy_t)
axes[1].plot(tuned_parameters, error_t)
axes[0].set_xlabel("ax_features")
axes[0].set_ylabel("accuracy_t")
axes[1].set_xlabel("ax_features")
axes[1].set_ylabel("error_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
# 经过图像展示,最后确定max_features=15的时候,表现效果不错
# ### 确定最优的max_depth
# In[54]:
# 确定max_depth的取值范围
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_
# 输出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)
# In[55]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20,4), dpi=100)
axes[0].plot(tuned_parameters, accuracy_t)
axes[1].plot(tuned_parameters, error_t)
axes[0].set_xlabel("max_depth")
axes[0].set_ylabel("accuracy_t")
axes[1].set_xlabel("max_depth")
axes[1].set_ylabel("error_t")
axes[0].grid(True)
axes[1].grid(True)
plt.show()
# 经过图像展示,最后确定max_depth=30的时候,表现效果不错
# ### 确定最优的min_sample_leaf
# In[56]:
# 确定min_sample_leaf的取值范围
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_
# 输出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)
# In[57]:
# 优化结果过程可视化
fig,axes = plt.subplots(nrows=1, ncols=2, figsize=(20,4), dpi=100)
axes[0].plot(tuned_parameters, accuracy_t)
axes[1].plot(tuned_parameters, error_t)
axes[0].set_xlabel("min_sample_leaf")
axes[0].set_ylabel("accuracy_t")
axes[1].set_xlabel("min_sample_leaf")
axes[1].set_ylabel("error_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[58]:
rf3 = RandomForestClassifier(n_estimators=175, max_depth=30,max_features=15,min_samples_leaf=1,
oob_score=True,random_state=0,n_jobs=-175)
rf3.fit(x_train,y_train)
rf3.oob_score_
# In[59]:
rf3.score(x_test, y_test)
# In[61]:
y_pre_prob = rf3.predict_proba(x_test)
log_loss(y_test, y_pre_prob)
# # 生成提交数据
# In[63]:
test_data = pd.read_csv('./data/otto/test.csv')
test_data.head()
# In[64]:
test_data_1 = test_data.drop(['id'], axis=1)
test_data_1.head()
# In[65]:
y_pre_test = rf3.predict_proba(test_data_1)
y_pre_test
# In[66]:
result_data = pd.DataFrame(y_pre_test, columns=['Class_'+str(i) for i in range(1,10)])
# In[67]:
result_data.head()
# In[68]:
result_data.insert(loc=0, column='id', value=test_data.id)
# In[69]:
result_data.head()
# In[70]:
result_data.to_csv('./data/otto/submission.csv', index=False)
# In[ ]: