机器学习系列(三十七)——集成学习与随机森林
集成学习的原理
如果你随机向多个人询问一个复杂的决策问题 ,然后综合他们的决策,通过投票的办法选出最终决策,在许多情况下 ,你会发现 ,这个汇总的决策相当的好,这被称为群体智慧。同样,如果你聚合一组预测器(分类器或回归器)的预测,得到的预测结果也往往比最好的单个预测器要好。这样的一组预测器,我们称为集成,所以这种技术,也被称为集成学习。
在生活中随处可见集成学习的例子,比如你知道一部新电影,但不知道到底值不值得看,于是你去问身边的几个朋友,如果他们大多数都觉得好看,你最终也决定去看,这就是一种“集成学习”。在机器学习竞赛中获胜的解决方案通常都涉及多种集成方法。
现在假设有一个分类任务,我们已经训练好了一些分类器,包括一个Logistic分类器、一个 SVM 分类器、一个RF分类器 、 一个 K- 近邻分类器等等。每个分类器的准确率约为70% 。
这时,要创建出一个更好的分类器,最简单的办单就是聚合每个分类器的预测,然后将得票最多的结果作为预测类别。这种大多数投票分类器被称为硬投票分类器:
硬投票我们会发现,这个投票分类器的准确率通常比集成中最好的分类器还要高。
相应地,我们有软投票分类器,它是一种概率平均的决策分类器。(本篇后面会介绍)
事实上,即使每个分类器都是弱学习器(意味着它仅比随机猜测好一 点),通过集成依然可以实现一个强学习器(高准确率),只要有足够大数量数据且足够多种类的弱学习器就可以。
为什么会这样呢?来类比一个情况:假设你有一个略微偏倚的硬币,它有 51%的可能正面数字朝上,49%的可能背面花朝上。如果你掷这样的一枚硬币1000 次,你大致会得到差不多510次数字和490次花,所以正面是大多数。而如果你做数学计算,你会发现,“在1000次投掷后,大多数为正面朝上”这件事的概率为:
这个结果接近 75%,比单独投掷一次要大很多。事情的关键就在这个概率上。如果假设有一个决策,每个决策器有60%的概率给出正确决策,那么500个这样的决策器综合的结果就将有99.999%的概率给出正确决策!
这就是集成学习的威力,理论上只要集成分类器中每个分类器的分类准确率比随机猜测准确率高,则只要这个集成学习器中有足够多的这样的分类器,就一定会获得接近100%准确率的性能。下面以一个具体的例子来感受一下集成学习的威力,首先用make_moons生成二类别模拟数据:
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])
plt.show()
数据集图示
接下来训练三个分类器,Logistic,SVM,DecisionTree,并分别查看准确率:
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.linear_model import LogisticRegression
log_clf=LogisticRegression()
log_clf.fit(x_train,y_train)
log_clf.score(x_test,y_test)
log
from sklearn.svm import SVC
svm_clf = SVC()
svm_clf.fit(x_train,y_train)
svm_clf.score(x_test,y_test)
svm
from sklearn.tree import DecisionTreeClassifier
dt_clf = DecisionTreeClassifier()
dt_clf.fit(x_train,y_train)
dt_clf.score(x_test,y_test)
dt
接下来使用少数服从多数的投票方式重新对测试集进行分类预测,并查看准确率:
y_predict1 = log_clf.predict(x_test)
y_predict2 = svm_clf.predict(x_test)
y_predict3 = dt_clf.predict(x_test)
y_predict = np.array((y_predict1 + y_predict2 + y_predict3) >= 2,dtype='int')
'''综合3个算法给出预测'''
from sklearn.metrics import accuracy_score
accuracy_score(y_test,y_predict)
投票决策
由结果看出,之前的三个分类器最高的准确率都不到90%,而综合三者的决策,准确率达到了90.4%。可见相比于单独的分类,集成学习确实提高了预测准确率。
sklearn中的集成学习
上述过程在sklearn中可以用VotingClassifier封装(hard voting):
'''硬投票'''
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(estimators=[
('log_clf',LogisticRegression()),
('svm_clf',SVC()),
('dt_clf',DecisionTreeClassifier(random_state=666))
],voting = 'hard')
voting_clf.fit(x_train,y_train)
voting_clf.score(x_test,y_test)
准确率:
VotingClassifier这里只是演示操作,实际使用voting classifier时会先把使用的单个算法的超参调至最优,再使用voting classifier。
soft voting
对应于hard voting,soft voting是一种基于概率平均的投票,所以soft voting要求集成中每个模型都能计算分类概率。 逻辑回归本身就是基于概率的,knn可进行概率转化,决策树也可以,SVM转概率比较麻烦,不过也可以,具体转化方式这里不做具体介绍。为什么要有soft voting呢?
这是因为,在很多情况下一人一票的少数服从多数的决策结果是非常不好的,比如下面这种情况,对某个样本5个模型预测的分类结果:
- 模型1:A-0.99,B-0.1
模型2:A-0.49,B-0.51
模型3:A-0.45,B-0.55
模型4:A-0.90,B-0.10
模型5:A-0.40,B-0.60
如果按投票的规则,A-2,B-3,最终结果应为B,但是观察数据发现,模型1和4非常确信样本应当属于A类,而其它模型并没有这么确定,如模型2对A和B几乎是一半一半,所以分类为A好像更加合理。
soft voting就是基于概率的加权进行分类,按soft voting思想,属于各个类别的集成预测分类概率为各个模型预测概率的平均值,计算可得最终投票的结果为A类。
下面在sklearn中使用soft voting,其实就是把voting参数设置为soft,不过要注意的是SVC要使用soft voting必须将probability设置为true:
voting_clf2 = VotingClassifier(estimators=[
('log_clf',LogisticRegression()),
('svm_clf',SVC(probability=True)),
('dt_clf',DecisionTreeClassifier(random_state=666))
],voting = 'soft')
voting_clf2.fit(x_train,y_train)
voting_clf2.score(x_test,y_test)
准确率:
soft_voting可见soft voting比hard voting有更好的效果。
子模型进行集成学习
前面我们知道,一个集成分类器中,若其中每个分类器的分类正确的概率都大于随机猜测的概率,则理论上只要这个集成学习器中有足够多的分类器,一定会获得非常非常强的性能(准确率逼近1)。
但一个问题是,虽然有很多机器学习算法,可算法种类毕竟是有限的,从投票来看,这么少的算法还是不够,如何集成更多算法进行集成学习呢? 那就是创建更多子模型,集成更多子模型的意见,来获得更强的决策效果。而且子模型之间不能一致,要有差异性。即子模型最好是完全独立的,彼此的错误毫不相关,这样集成方法的效果最优。这种差异性是通过每个子模型只看样本数据的一部分实现的。
对于取数据,取样方式分为放回取样Bagging和不放回取样Pasting,更常用的是Bagging方式,统计学中叫bootstrap。
随机森林Random Forest
随机森林(下称RF)就是集成学习的一种,它的子模型是一棵一棵的决策树,决策树作为一种非参数学习算法,使用它创建子模型更能产生差异性。下面使用放回抽样方式构建一个含有500个决策树的RF分类器对本篇之前的数据做分类:
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
n_estimators = 500#集成500个
,max_samples=100
,bootstrap=True)
bagging_clf.fit(x,y)
bagging_clf.score_
RF_500
相应地我们可以增加子模型的个数为1000再看分类结果:
'''增加子模型数量'''
bagging_clf2 = BaggingClassifier(DecisionTreeClassifier(),
n_estimators = 1000
,max_samples=100
,bootstrap=True)
bagging_clf2.fit(x_train,y_train)
bagging_clf2.score(x_test,y_test)
RF_1000
可见创建更多子模型的RF效果确实是更好的,但是由于数据本身存在噪音或错误样本,我们的算法不会像理论推导那样可以有接近100%的准确率。不过使用集成学习还是会有很好的结果。另外可以调节其中max_samples参数、DecisionTreeClassifier构造参数等来进一步获得更好的结果。
更多关于集成学习将在下篇继续介绍。