王境泽的机器学习技巧
什么是集成学习(Voting Classifier)?
同一数据,同时应用多种差异模型,将预测结果用某种方式投票选出最佳结果
例如:新出的电影好不好看?根据其他人评价自行判断
日常工作应用中,监督学习算法的选择:
- 如果为了模型的可解释性,如数据分析报告(准确率不重要),一般使用独立模型(线性回归和逻辑回归)
- 如果为了模型的性能
- 中小型数据(表格):集成学习
- 大型/海量数据(图片,音频、视频):深度学习
算法准确率:集成学习(随机森林,GBDT)是仅次于深度学习的第二大算法
- 深度学习需要海量数据支持,集成学习不需要大量数据,更简单,应用更广泛
- 非常常用
- 其他机器学习算法无论性能还是可解释性都不如上述三类算法,实际工作很少使用
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# 噪点参数0.3
X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=42)
plt.scatter(X[y==0,0], X[y==0,1], alpha=0.3)
plt.scatter(X[y==1,0], X[y==1,1], alpha=0.3)
<matplotlib.collections.PathCollection at 0x11906e80>
[外链图片转存失败(img-jRiZvp8T-1567001324539)(output_4_1.png)]
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(solver='lbfgs')
log_clf.fit(X_train, y_train)
log_clf.score(X_test, y_test)
0.864
手动集成三种算法学习结果
y_predict1 = knn_clf.predict(X_test)
y_predict1
y_predict2 = dt_clf.predict(X_test)
y_predict2
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)
# 000,001 011,111
y_predict1 + y_predict2 + y_predict3
# 0,1, 2, 3
# 0, 1 = 0
# 2, 3 = 1
y_predict = ((y_predict1 + y_predict2 + y_predict3) >= 2).astype(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=[
('df_clf', DecisionTreeClassifier(random_state=222)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression(solver='lbfgs')),
],
voting='hard'
)
voting_clf.fit(X_train, y_train)
VotingClassifier(estimators=[('df_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_leaf=0.0,
presort=False,
random_state=222,
splitter='best')),
('knn_clf',
KNeighborsClassifier(a...
n_jobs=None, n_neighbors=5,
p=2, weights='uniform')),
('log_clf',
LogisticRegression(C=1.0, class_weight=None,
dual=False, fit_intercept=True,
intercept_scaling=1,
l1_ratio=None, max_iter=100,
multi_class='warn',
n_jobs=None, penalty='l2',
random_state=None,
solver='lbfgs', tol=0.0001,
verbose=0,
warm_start=False))],
flatten_transform=True, n_jobs=None, voting='hard',
weights=None)
voting_clf.predict(X_test)
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, 0, 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)
0.928
参数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=[
('df_clf', DecisionTreeClassifier(random_state=666)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression(solver='lbfgs')),
],
voting='soft',
)
voting_clf.fit(X_train, y_train)
voting_clf.score(X_test, y_test)
0.888
更好的集成学习方式
现有的集成学习方式,能用于集成的模型太少,想要提高集成学习的准确率需要:
- 创建和集成更多子模型
- 子模型之间不能一致,要有差异性
子模型不需要太高的准确率,只要足够多,就可以极大提升最终模型准确率
例如:每个子模型有60%准确率
- 如果只有一个子模型,准确率60%
- 如果有3个子模型:$ 0.6{3}+c{2}_{3}\cdot 0.6^{2}\cdot 0.4 =0.648 $
- 如果有500个子模型:$ \sum {500}_{i=251}C{i}_{500}\cdot 0.6^{i}\cdot 0.4^{500-i} =0.9999 $
注意:子模型的准确率最好高于平均准确率
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: 2.82 s
BaggingClassifier(base_estimator=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_leaf=0.0,
presort=False,
random_state=None,
splitter='best'),
bootstrap=True, bootstrap_features=False, max_features=1.0,
max_samples=100, n_estimators=500, n_jobs=None,
oob_score=True, random_state=None, verbose=0,
warm_start=False)
%%time
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True,
n_jobs=-1) # 使用全部cpu内核
bagging_clf.fit(X, y)
更多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=None, 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
# help(RandomForestClassifier)
集成学习解决回归问题的子库
from sklearn.ensemble import BaggingRegressor
from sklearn.ensemble import RandomForestRegressor # 回归随机森林
上面的集成学习算法叫Bagging
使用Bagging方式集成学习
- Bagging:集成多个模型(并行)
- 数据集每次抽取若干数据,用于子模型
- 若干子模型同时进行训练,和预测
- 采用投票方式算出集成后的预测结果
- Boosting:每个模型都在尝试增强(Boosting)整体效果(串行)
- 同一时间只有一个模型,只使用一个数据集
- 每次训练预测完成后,增加错误数据权重,降低预测正确数据权重,然后重新训练
- 迭代循环,直到准确率达到一定标准
Ada Boosting
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=2), # 基础学习算法
n_estimators=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,(GBDT,更强)
- 训练模型m1,产生错误e1
- 针对e1训练模型m2,产生错误e2
- 针对e2训练模型m3, 产生错误e3…
- 最终预测结果是:m1+m2+m3…
from sklearn.ensemble import GradientBoostingClassifier
# Gradient Boosting就是以决策树为基础的,不需要写,设好集成子模型数量就行
gb_clf = GradientBoostingClassifier(max_depth=3, n_estimators=30)
gb_clf.fit(X_train, y_train)
gb_clf.score(X_test, y_test)
0.92
总结
scikit-learn自带的常用集成算法:
- Bagging:随机森林
- Boosting:GBDT
不过实际工作和比赛中,我们更常用的时专用集成学习库
其他常用Boosting算法实现,如:XGboost,LightGBM(这两个库都是Boosting算法中的GBDT第三方实现),广泛应用于各种数据科学机器学习竞赛中
- 运算速度更快
- 准确率更高