集成学习和随机森林
机器学习技巧:真香
什么是集成学习(Voting Classifier)?
同一数据,同时应用多种机器学习算法,将预测结果用某种方式投票选出最佳结果
例如:新出的电影好不好看?根据其他人评价自行判断
日常应用中,算法准确率:集成学习(随机森林)是仅次于深度学习的第二大算法
- 深度学习需要海量数据支持,集成学习不需要大量数据,更简单,应用更广泛
- 非常常用
import numpy as np
import matplotlib.pyplot as plt
# 生成非线性数据
from sklearn import datasets
X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=42)
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
<matplotlib.collections.PathCollection at 0x9c77048>
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
from sklearn.neighbors import KNeighborsClassifier # KNN
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
knn_clf.score(X_test, y_test)
0.912
from sklearn.tree import DecisionTreeClassifier # 决策树
dt_clf = DecisionTreeClassifier(random_state=666)
dt_clf.fit(X_train, y_train)
dt_clf.score(X_test, y_test)
0.864
from sklearn.linear_model import LogisticRegression # 逻辑回归
log_clf = LogisticRegression()
log_clf.fit(X_train, y_train)
log_clf.score(X_test, y_test)
0.864
手动集成三种算法学习结果
y_predict1 = knn_clf.predict(X_test)
y_predict2 = dt_clf.predict(X_test)
y_predict3 = log_clf.predict(X_test)
y_predict3
array([1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
# 三种算法预测结果,相同的预测结果大于等于2个,返回True,否则False,结果转为整型1/0
# 三种算法结果:0,0,0返回0;0,0,1返回0;0,1,1返回1;1,1,1返回1
# 相加,四种可能
# 0,1,2,3,前两种返回0,后两种返回1
y_predict1 + y_predict2 + y_predict3
y_predict = np.array((y_predict1 + y_predict2 + y_predict3) >= 2, dtype=np.int)
y_predict
array([1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0])
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_predict)
0.92
使用Voting Classifier自动集成学习
建议先分别执行各个算法,调好参数后再统一集成到一起
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(
estimators=[
('dt_clf', DecisionTreeClassifier(random_state=666)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression())
],
voting='hard'
)
voting_clf.fit(X_train, y_train) # 训练
VotingClassifier(estimators=[('dt_clf', DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_...ty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False))],
flatten_transform=None, n_jobs=1, voting='hard', weights=None)
voting_clf.predict(X_test) # 预测
e:\Anaconda3\lib\site-packages\sklearn\preprocessing\label.py:151: DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use `array.size > 0` to check that an array is not empty.
if diff:
array([1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
voting_clf.score(X_test, y_test) # 准确率
e:\Anaconda3\lib\site-packages\sklearn\preprocessing\label.py:151: DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use `array.size > 0` to check that an array is not empty.
if diff:
0.92
参数voting值
- hard
- soft
例:某数据有2种分类,使用五种模型训练并预测,得到结果为:
模型1:A 99%;B 1%
模型2:A 49%;B 51%
模型3:A 40%;B 60%
模型4:A 90%;B 10%
模型5:A 30%;B 70%
- hard voting模式:考虑投票数。
- A:2票
- B:3票
- 最终结果为B
- soft voting模式:考虑投票权重
- A = (0.99+0.49+0.4+0.9+0.3)/5 = 0.616
- B = (0.01+0.51+0.6+0.1+0.7)/5 = 0.384
- 最终结果为A
soft voting要求集成的每个模型都能估计概率,否则无法运算(kNN、决策树、逻辑回归都可以)
voting_clf = VotingClassifier(
estimators=[
('dt_clf', DecisionTreeClassifier(random_state=666)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression())
],
voting='soft'
)
voting_clf.fit(X_train, y_train)
voting_clf.score(X_test, y_test)
e:\Anaconda3\lib\site-packages\sklearn\preprocessing\label.py:151: DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use `array.size > 0` to check that an array is not empty.
if diff:
0.888
更好的集成学习方式
现有的集成学习方式,能用于集成的模型太少,想要提高集成学习的准确率需要:
- 创建和集成更多子模型
- 子模型之间不能一致,要有差异性
子模型不需要太高的准确率,只要足够多,就可以极大提升最终模型准确率
例如:每个子模型有60%准确率
- 如果只有一个子模型,准确率60%
- 如果有3个子模型:
- 如果有500个子模型:
注意:子模型的准确率最好高于平均准确率
0.6 ** 3 + (2*3/2*1) * 0.6**2 * 0.4
0.648
如何产生大量有差异的子模型?
- 机器学习算法就那么点,无法生成大量子模型
- 随机抽样方式,每个子模型只抽取一部分样本进行训练
- 例如:所有子模型都用一个算法,训练集500条数据,每个子模型随机抽取100条进行训练,可以生成任意多个子模型
- 子模型算法基本只使用决策树算法,因为决策树大量的剪枝方式可以生成差异更大的模型,优于其他模型
两种采样方式:
Bagging:放回采样(bootstrap),更常用,能制造更多子模型
Pasting:不放回采样
使用Bagging方式集成学习
这里Bagging方式集成学习只使用决策树模型,
通过改变训练数据生成子模型,决策树大量的剪枝方式可以生成差异更大的模型,优于其他模型
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(), # 集成的模型
n_estimators=500, # 集成多少个子模型
max_samples=100, # 每个子模型需要多少训练数据
bootstrap=True) # 放回采样
bagging_clf.fit(X_train, y_train)
bagging_clf.score(X_test, y_test)
0.92
子模型越多,准确率越高
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(), # 集成的模型
n_estimators=5000, # 集成多少个子模型
max_samples=100, # 每个子模型需要多少训练数据
bootstrap=True) # 放回采样
bagging_clf.fit(X_train, y_train)
bagging_clf.score(X_test, y_test)
0.912
没有99%那么多,因为一些子模型准确率会低于均值
OOB:out of bag
放回采样导致一部分样本始终没有被用于训练
平均约有37%的样本没有被用到
所以不需要分离测试集,直接使用没有被用过的样本数据做测试集即可
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True) # OOB为True
bagging_clf.fit(X, y) # 训练数据采用全部样本数据,不需要测试集
bagging_clf.oob_score_ # 调用OOB方法计算准确率
0.916
n_jobs 并行化处理
Bagging非常易于并行化处理
每个子模型可以独立训练,使用独立cpu内核,加快速度
注意:如果训练时间非常久,同时使用所有内核 n_jobs=-1, 非常容易卡死,训练完成后才能恢复运算
%%time
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True)
bagging_clf.fit(X, y)
Wall time: 999 ms
%%time
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True,
n_jobs=3) # 使用3个cpu内核
bagging_clf.fit(X, y)
Wall time: 3.59 s
更多Bagging方式
除了针对样本数据进行随机采样,还有更多方式
- 针对特征进行随机采样,Random Subspaces
- 既针对样本、又针对特征进行随机采样,Random Patches
bootstrap_features 针对特征随机取样
random_subspaces_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=500, # 关闭样本随机采样,因为子模型需要数据,设为全部500条,不再选取部分数据
bootstrap=True,
oob_score=True,
max_features=1, # 随机取1列特征(因为数据一共就2列特征)
bootstrap_features=True) # 对特征随机采样,放回采样
random_subspaces_clf.fit(X, y)
random_subspaces_clf.oob_score_
0.834
既对样本、又对特征进行随机采样
random_patches_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100, # 打开样本随机采样
bootstrap=True,
oob_score=True,
max_features=1, # 随机取1列特征(因为数据一共就2列特征)
bootstrap_features=True) # 对特征随机采样,放回采样
random_patches_clf.fit(X, y)
random_patches_clf.oob_score_
0.856
随机森林
上面使用决策树、以Bagging集成学习的方式,就是随机森林
除了手动集成学习外,sklearn自带一个随机森林类,可以方便的创建随机森林模型
随机森林模型集成了决策树和Bagging分类器,所以拥有决策树和BaggingClassifier的所有参数
from sklearn.ensemble import RandomForestClassifier
rf_clf = RandomForestClassifier(
n_estimators=500, # 500棵树
oob_score=True, # 使用未被抽取的数据做测试集
random_state=666, # 随机数种子固定
n_jobs=-1) # 所有cpu内核并行运算
rf_clf.fit(X, y)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=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=500, n_jobs=-1,
oob_score=True, random_state=666, verbose=0, warm_start=False)
rf_clf.oob_score_
0.896
调节参数,提升准确率
rf_clf2 = RandomForestClassifier(
n_estimators=500,
max_leaf_nodes=16, # 每个决策树最多有几个叶子节点
oob_score=True,
random_state=666,
n_jobs=-1)
rf_clf2.fit(X, y)
rf_clf2.oob_score_
0.92
集成学习解决回归问题的子库
from sklearn.ensemble import BaggingRegressor
from sklearn.ensemble import RandomForestRegressor
Boosting
- Bagging:集成多个模型
- Boosting:每个模型都在尝试增强(Boosting)整体效果
Ada Boosting
Ada Boosting
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=2), # 基础学习算法
n_estimators=500) # 集成500个子分类器
ada_clf.fit(X_train, y_train)
AdaBoostClassifier(algorithm='SAMME.R',
base_estimator=DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=2,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best'),
learning_rate=1.0, n_estimators=500, random_state=None)
ada_clf.score(X_test, y_test)
0.856
Gradient Boosting
- 训练模型m1,产生错误e1
- 针对e1训练模型m2,产生错误e2
- 针对e2训练模型m3, 产生错误e3......
- 最终预测结果是:m1+m2+m3...
from sklearn.ensemble import GradientBoostingClassifier
# Gradient Boosting就是以决策树为基础的,不需要写,设好集成子模型数量就行
gb_clf = GradientBoostingClassifier(max_depth=2, n_estimators=30)
gb_clf.fit(X_train, y_train)
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=2,
max_features=None, max_leaf_nodes=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=30,
presort='auto', random_state=None, subsample=1.0, verbose=0,
warm_start=False)
gb_clf.score(X_test, y_test)
0.912
其他常用Boosting算法实现:XGboost,GBDT;
sklearn不带,需要另行安装
Boosting 解决回归问题子库
from sklearn.ensemble import AdaBoostRegressor
from sklearn.ensemble import GradientBoostingRegressor