引言:
试想一下,当你想买一辆新车时,你会直接走到第一家汽车商店,并根据经销商的建议购买一辆车吗?这显然不太可能。
你可能会浏览一些门户网站,在那里查看人们对于不同车型的比较和评论,了解它们的特点和价格。你也可能会向朋友和同事征求一下他们的意见。简而言之,你不会直接给出一个结论,而是会综合考虑其他人的意见再做出决定。
机器学习中的集成模型(Ensemble Models)采用了类似的思想。集成模型结合多个模型的决策,以提高整体性能。这可以通过多种方式来实现,本文将一一介绍。
本文的目的是介绍集成学习的概念,并理解使用这种技术的算法。为了加强您对不同算法的理解,我们将对实际问题的案例使用 Python 来解释这些高级算法。
注意:弄懂本文需要对机器学习算法有一个基本的了解,我建议你通过这篇文章来熟悉一些基本概念:
目录:
2.2 平均化
2.3 加权平均
3. 高级集成技术
3.1 Stacking
3.2 Blending
3.3 Bagging
3.4 Boosting
4. 基于 Bagging 和 Boosting 的算法
4.1 Bagging meta-estimator
4.2 Random Forest
4.3 AdaBoost
4.4 GBM
4.5 XGB
4.6 Light GBM
4.7 CatBoost
1. 集成学习简介
让我们通过一个例子来理解集成学习的概念。假设你是一个电影导演,你对一个非常重要和有趣的话题都创作了一部电影短片。现在,你想在公映之前对这部电影进行初步的反馈(打分)。该怎么做呢?
A: 你可以咨询一个好朋友来对电影进行打分
这种方法完全有可能会出现一种情况,就是电影很糟糕,但是你的好朋友不忍心伤你的心,因此不会打一星。
B: 你可以咨询 5 位同事来对电影进行打分
这种方法要好些,可以为你的电影提供较诚实的评价。但问题依然存在,这 5 个人可能不是该电影主题的专家,虽然他们可能知道摄影、镜头、音频等一些电影知识。
C: 你可以咨询 50 个人来对电影进行打分
其中一些可能是你的朋友,有些可能是你的同事,有些甚至可能是陌生人。
这种打分方法,反应会更加普遍和多样化,因为打分的人更多更多样了。事实证明,相比 A 和 B,C 更加科学。
通过这些例子,你可以推断出相比一个人的意见,综合多个人的意见可能做出更好的决定。这个例子对于多模型和单模型的比较也是成立的。机器学习中的这种多样化是通过一种称为集成学习的技术实现的。
现在,你已经掌握了集成学习的要点。接下来让我们看看集成学习中的各种技术及其实现方法。
2. 基本的集成技术
在本节中,我们将介绍一些简单但功能强大的技术:
2.1 最大化
最大化通常用于分类问题。这种技术,所有模型都对该实例进行预测,每个模型的预测都可以看成是一次投票。获得投票数最多的那一类别就是最终的预测结果。
例如刚才的例子,5 个同事给你的电影打分,其中 3 人打了 5 星,2 人打了 4 星。则根据最大化原则,打 5 星的人数更多,最终判断电影评分为 5 星。
示例代码:
model1 = tree.DecisionTreeClassifier()
model2 = KNeighborsClassifier()
model3= LogisticRegression()
model1.fit(x_train,y_train)
model2.fit(x_train,y_train)
model3.fit(x_train,y_train)
pred1=model1.predict(x_test)
pred2=model2.predict(x_test)
pred3=model3.predict(x_test)
final_pred = np.array([])
for i in range(0,len(x_test)):
final_pred = np.append(final_pred, mode([pred1[i], pred2[i], pred3[i]]))
或者,你也可以使用 sklearn 库中的 VotingClassifier:
from sklearn.ensemble import VotingClassifier
model1 = LogisticRegression(random_state=1)
model2 = tree.DecisionTreeClassifier(random_state=1)
model = VotingClassifier(estimators=[('lr', model1), ('dt', model2)], voting='hard')
model.fit(x_train,y_train)
model.score(x_test,y_test)
2.2 平均化
类似于最大化投票方法,平均化的做法就是对所有的预测结果求平均值,平均值作为最后的预测结果。平均化可以应用于回归问题的预测或者在分类问题中计算概率值。
还是刚才的例子,5 个同事给你的电影打分,其中 3 人打了 5 星,2 人打了 4 星。则根据平均化原则,计算最终打分为:
(5 + 5 + 5 + 4 + 4) / 5 = 4.6
示例代码:
model1 = tree.DecisionTreeClassifier()
model2 = KNeighborsClassifier()
model3= LogisticRegression()
model1.fit(x_train,y_train)
model2.fit(x_train,y_train)
model3.fit(x_train,y_train)
pred1=model1.predict_proba(x_test)
pred2=model2.predict_proba(x_test)
pred3=model3.predict_proba(x_test)
finalpred=(pred1+pred2+pred3)/3
2.3 加权平均
这是平均化方法的一个扩展,所有的模型被赋予不同的权重(定义为在预测中不同模型所占的重要性)。例如,如果你的两个同事在这方面比较专业,那么他们两人的意见所占的比重就更大一些。假设这两人占的比重均为 0.23,其他三人占的比重均为 0.18,则加权平均后的结果为:
5*0.23 + 4*0.23 + 5*0.18 + 4*0.18 + 4*0.18 = 4.41
示例代码:
model1 = tree.DecisionTreeClassifier()
model2 = KNeighborsClassifier()
model3= LogisticRegression()
model1.fit(x_train,y_train)
model2.fit(x_train,y_train)
model3.fit(x_train,y_train)
pred1=model1.predict_proba(x_test)
pred2=model2.predict_proba(x_test)
pred3=model3.predict_proba(x_test)
finalpred=(pred1*0.3+pred2*0.3+pred3*0.4)
3. 高级集成技术
上文我们已经了解了基本的集成技术,接下来将介绍一下高级的集成技术。
3.1 Stacking
Stacking 是使用多个模型(例如决策树、KNN、SVM)来构建新的模型的集成技术。该模型在测试集上进行预测。下面是一个简单的 Stacking 集成的详细步骤解释。
1)将训练集划分为 10 个子集。
2)在其中 9 个子集上训练一个基本模型(例如决策树模型),在第 10 个子集上进行测试。遍历每个子集,重复进行 10 次。得到的 DT 长度与 Train set 相同。
3)在整个训练集上使用该模型(决策树)进行建模。
4)使用建模的模型在测试集上进行测试。
5)使用另一种算法(例如 knn),重复步骤 2~4,作用在 Train set 和 Test set 上,得到另一组值。
6)使用得到的 DT 和 knn 组合作为新的特征 TRAIN PREDICTION SET,训练新的模型(例如逻辑回归)。
7)使用训练好的模型对 TEST PREDICTION SET 进行预测。
示例代码:
首先,我们需要定义一个函数对 n 折训练集和测试集进行预测,该函数返回每个模型对训练集和测试集的预测结果。
def Stacking(model,train,y,test,n_fold):
folds=StratifiedKFold(n_splits=n_fold,random_state=1)
test_pred=np.empty((test.shape[0],1),float)
train_pred=np.empty((0,1),float)
for train_indices,val_indices in folds.split(train,y.values):
x_train,x_val=train.iloc[train_indices],train.iloc[val_indices]
y_train,y_val=y.iloc[train_indices],y.iloc[val_indices]
model.fit(X=x_train,y=y_train)
train_pred=np.append(train_pred,model.predict(x_val))
test_pred=np.append(test_pred,model.predict(test))
return test_pred.reshape(-1,1),train_pred
然后,我们构建两个基本模型:决策树和 knn。
model1 = tree.DecisionTreeClassifier(random_state=1)
test_pred1 ,train_pred1=Stacking(model=model1,n_fold=10, train=x_train,test=x_test,y=y_train)
train_pred1=pd.DataFrame(train_pred1)
test_pred1=pd.DataFrame(test_pred1)
model2 = KNeighborsClassifier()
test_pred2 ,train_pred2=Stacking(model=model2,n_fold=10,train=x_train,test=x_test,y=y_train)
train_pred2=pd.DataFrame(train_pred2)
test_pred2=pd.DataFrame(test_pred2)
最后,使用逻辑回归,进行训练和预测。
df = pd.concat([train_pred1, train_pred2], axis=1)
df_test = pd.concat([test_pred1, test_pred2], axis=1)
model = LogisticRegression(random_state=1)
model.fit(df,y_train)
model.score(df_test, y_test)
为了将问题简单化,我们所创建的 Stacking 模型只有两层。第一层是建立决策树和 knn 模型,第二层是建立逻辑回归模型。实际应用中可以使用多个层次的复杂结构。
3.2 Blending
Blending 与 Stacking 类似,但是仅从训练集上划分一部分作为 holdout(验证集),没有使用 k 折验证。Holdout 集结果作为下一层的训练数据。下面是 Blending 的详细步骤解释。
1)将所有的训练数据划分为训练集和验证集。
2)在训练集上训练模型。
3)在验证集和整体测试集上进行模型测试。
4)验证集和测试结果作为元特征,进行第二层的模型训练。
5)使用该模型在整体测试集的元特征上进行模型验证。
示例代码:
首先,我们在训练集上训练两个模型:决策树和 knn,以便在验证集上作出预测。
model1 = tree.DecisionTreeClassifier()
model1.fit(x_train, y_train)
val_pred1=model1.predict(x_val)
test_pred1=model1.predict(x_test)
val_pred1=pd.DataFrame(val_pred1)
test_pred1=pd.DataFrame(test_pred1)
model2 = KNeighborsClassifier()
model2.fit(x_train,y_train)
val_pred2=model2.predict(x_val)
test_pred2=model2.predict(x_test)
val_pred2=pd.DataFrame(val_pred2)
test_pred2=pd.DataFrame(test_pred2)
然后,结合验证集的元特征,训练逻辑回归模型,在测试集上进行验证。
df_val=pd.concat([x_val, val_pred1,val_pred2],axis=1)
df_test=pd.concat([x_test, test_pred1,test_pred2],axis=1)
model = LogisticRegression()
model.fit(df_val,y_val)
model.score(df_test,y_test)
3.3 Bagging
Bagging 背后的思想就是将多个模型(例如决策树)的结果结合得到泛化的结果。这里有一个问题:在同样的数据集上训练得到不同的模型有用吗?有很大几率这些模型将给出同样的结果,因为它们的输入都是一致的。因此,如何解决这一问题呢?常用的方法就是 Bootstrapping。
Bootstrapping 是一种采样技术,从原始数据集中有放回地采样,创建观测子集。子集的大小与原始集合的大小相同。
Bagging(Bootstrap Aggregating)技术使用这些子集(bags)来得到一个相对公平的分布(完全集)。子集的大小也可能少于原始集。
1)从原始数据集中创建多个子集,有放回地进行采样。
2)在每个子集上训练一个基本模型(弱模型)。
3)这些模型相互平行且独立。
4)最后的预测结果由所有模型共同决定。
3.4 Boosting
在我们进一步讨论之前,还有一个问题:如果一个数据点被第一个模型预测错误,那么下一个模型(可能是所有的模型)组合预测会预测正确吗?这样的情况可以通过 boosting 来处理。
Boosting 是一个循序渐进的过程,其中每一个后续模型都试图纠正前一个模型的错误。后一个模型依赖于先前的模型。下面让我们来了解 Boosting 的工作方式。
1)从原始数据集中创建一个子集。
2)起始状态,所有数据点都赋予相同的权重。
3)在这个子集上训练一个基本模型。
4)使用该模型在整个数据集上进行预测。
5)根据实际值和预测值计算误差。
6)对预测错误的数据点给予更高的权重(如上图中预测错误的蓝色的“+”将赋予更大的权重)。
7)在此数据集上训练一个新的模型并预测(该模型试图纠正上一个模型中的错误点)。
8)类似地创建多个模型,每个模型校正前一个模型的错误。
9)最终的模型(强学习器)是所有模型(弱学习器)的加权平均。
因此,Boosting 算法结合了一些弱学习器,形成一个强大的学习器。单个模型在整个数据集上表现不好,但是它们在部分数据集上可能表现得很好。因此,每个模型实际上提升了集合的性能。
4. 基于 Bagging 和 Boosting 的算法
Bagging 和 Boosting 是机器学习中最常用的两种技术。在这一节中,我们将详细地研究它们。以下是我们将重点研究的算法:
Bagging 算法:
Boosting 算法:
或者在本节中讨论的所有算法,我们将遵循这个过程:
这篇文章中,我将讨论贷款预测问题。您可以从这里下载数据集:
https://datahack.analyticsvidhya.com/contest/practice-problem-loan-prediction-iii/
请注意,对于每个算法,一些代码(读取数据、分割训练集、测试集等)是相同的。为了避免重复,我已经在下面写下这些相同的代码,之后只会深入讨论算法的核心代码。
#importing important packages
import pandas as pd
import numpy as np
#reading the dataset
df=pd.read_csv("/home/user/Desktop/train.csv")
#filling missing values
df['Gender'].fillna('Male', inplace=True)
类似地,关于数据填充、缺失值、异常值的处理,本文不做具体介绍。可参阅这篇文章了解相关内容:
https://www.analyticsvidhya.com/blog/2015/04/comprehensive-guide-data-exploration-sas-using-python-numpy-scipy-matplotlib-pandas/
#split dataset into train and test
from sklearn.model_selection import train_test_split
train, test = train_test_split(df, test_size=0.3, random_state=0)
x_train=train.drop('Loan_Status',axis=1)
y_train=train['Loan_Status']
x_test=test.drop('Loan_Status',axis=1)
y_test=test['Loan_Status']
#create dummies
x_train=pd.get_dummies(x_train)
x_test=pd.get_dummies(x_test)
接下来,让我们开始 Bagging 和 Boosting 算法吧!
4.1 Bagging 元估计
Bagging 元估计是一种集成算法,可用于分类(BaggingClassifier)和回归(BaggingRegressor)问题。它遵循典型的 Bagging 技术进行预测。下面是 Bagging 元估计算法的步骤:
1)从原始数据集中创建随机子集(Bootstrapping)。
2)子集包含所有特征。
3)用户指定的基本估计器在这些子集上进行训练。
4)每个模型的预测结合形成最终的结果。
代码:
from sklearn.ensemble import BaggingClassifier
from sklearn import tree
model = BaggingClassifier(tree.DecisionTreeClassifier(random_state=1))
model.fit(x_train, y_train)
model.score(x_test,y_test)
0.75135135135135134
对于回归问题:
from sklearn.ensemble import BaggingRegressor
model = BaggingRegressor(tree.DecisionTreeRegressor(random_state=1))
model.fit(x_train, y_train)
model.score(x_test,y_test)
参数:
4.2 随机森林
随机森林遵循 Bagging 技术的另一种集成机器学习算法。它是 Bagging 估计算法的一个扩展。随机森林中的基本学习器是决策树。与 Bagging 元估计不同,随机森林随机选择一组特征,这些特征用于在决策树的每个节点处决定最佳分割。
随机森林算法的详细步骤如下:
1)从原始数据集中创建随机子集(Bootstrapping)。
2)在决策树中的每个节点,使用随机特征来决定最佳分割。
3)在每个子集上训练一个决策树模型。
4)最后的结果是对所有决策树的预测计算平均值。
注:随机森林中的决策树可以建立在数据和特征的子集上。特别地,sklearn 中的随机森林模型将所有特征用于决策树,并且随机选择特征子集用于在每个节点处分割。
总之,随机森林随机选择数据点和特征,并建立多个树木(森林)。
代码:
from sklearn.ensemble import RandomForestClassifier
model= RandomForestClassifier(random_state=1)
model.fit(x_train, y_train)
model.score(x_test,y_test)
0.77297297297297296
你可以使用 model.feature_importances_ 来查看特征的重要性。
for i, j in sorted(zip(x_train.columns, model.feature_importances_)):
print(i, j)
结果如下:
ApplicantIncome 0.180924483743 CoapplicantIncome 0.135979758733 Credit_History 0.186436670523 . . . Property_Area_Urban 0.0167025290557 Self_Employed_No 0.0165385567137 Self_Employed_Yes 0.0134763695267
回归问题的示例代码:
from sklearn.ensemble import RandomForestRegressor
model= RandomForestRegressor()
model.fit(x_train, y_train)
model.score(x_test,y_test)
参数:
4.3 AdaBoost
自适应 Boosting(AdaBoost)是最简单的 Boosting 算法之一。通常,决策树用于建模。顺序创建多个模型,每个模型校正前一个模型的错误。AdaBoost 为上一个模型中预测错误的数据点分配更大的权重,在此模型工作以便能够正确地预测。
下面是 AdaBoost 算法的步骤:
1)最初,对数据集中的所有数据点赋予相同的权重。
2)在数据子集上建立模型。
3)使用该模型,对整个数据集进行预测。
4)通过比较预测值和实际值来计算误差。
5)在创建下一个模型时,对预测不正确的数据点给出更高的权重。
6)可以使用误差值确定权重。例如,误差大的赋予更大的权重。
7)重复这个过程直到误差函数不改变,或者达到学习器的最大数量。
代码:
from sklearn.ensemble import AdaBoostClassifier
model = AdaBoostClassifier(random_state=1)
model.fit(x_train, y_train)
model.score(x_test,y_test)
0.81081081081081086
回归问题的示例代码:
from sklearn.ensemble import AdaBoostRegressor
model = AdaBoostRegressor()
model.fit(x_train, y_train)
model.score(x_test,y_test)
参数:
4.4 Gradient Boosting (GBM)
梯度提升(GBM)是另一种集成机器学习算法,它同时适用于回归和分类问题。GBM 使用提升技术,结合一些弱学习器形成一个强大的学习器。回归树作为基础学习器,每个后续的树是建立在由前一棵树计算的误差上的。
我们将使用一个简单的例子来理解 GBM 算法。使用以下数据来预测一组人的年龄:
1)平均年龄被假定为在数据集中的所有观察值的预测值。
2)使用该平均预测值和实际年龄值计算误差。
3)使用上面计算的误差作为目标变量创建树模型。我们的目标是找到最佳的分割,以尽量减少误差。
4)该模型的预测结果与 predicion1 相结合。
5)上面计算的值就是新的预测值。
6)利用该预测值和实际值计算新的误差。
7)重复步骤2到6直到达到最大迭代次数(或者误差函数不改变)。
代码:
from sklearn.ensemble import GradientBoostingClassifier
model= GradientBoostingClassifier(learning_rate=0.01,random_state=1)
model.fit(x_train, y_train)
model.score(x_test,y_test)
0.81621621621621621
回归问题的示例代码:
from sklearn.ensemble import GradientBoostingRegressor
model= GradientBoostingRegressor()
model.fit(x_train, y_train)
model.score(x_test,y_test)
参数:
4.5 XGBoost
XGBoost(extreme Gradient Boosting)是一种先进的梯度提升算法的实现。XGBoost 已被证明是一种高效的 ML 算法,广泛应用于机器学习竞赛中。XGBoost 具有较高的预测能力,比其他梯度提升技术速度快 10 倍。它还包括各种正则化技术减少过拟合来提高整体性能。因此,它也被称为“regularized boosting”技术。
让我们看看 XGBoost 是如何比其他技术更好的:
1)正则化
标准 GBM 没有像 XGBoost 那样的正则化。因此 XGBoost 有助于减少过拟合。
2)并行处理
XGBoost 实现并行处理,并且比 GBM 更快。XGBoost 还支持在 Hadoop 上实现。
3)高灵活性
XGBoost 允许用户自定义优化目标和评价标准,为模型添加一个新的维度。
4)处理缺失值
XGBoost 有一个内置功能来处理缺失值。
5)树修剪
XGBoost 进行分割到最大深度后,然后开始由下到上修剪树,移除没有正增益的分割。
6)内建交叉验证
XGBoost 允许用户在提升过程的每次迭代中运行交叉验证,因此很容易在一次运行中获得最佳提升迭代次数。
代码:
由于 XGBoost 可以处理缺失值,所以不必在预处理中对缺失值进行填充。你可以跳过上述代码中缺失值的填补步骤。按照其余的步骤进行:
import xgboost as xgb
model=xgb.XGBClassifier(random_state=1,learning_rate=0.01)
model.fit(x_train, y_train)
model.score(x_test,y_test)
0.82702702702702702
回归问题的示例代码:
import xgboost as xgb
model=xgb.XGBRegressor()
model.fit(x_train, y_train)
model.score(x_test,y_test)
参数:
4.6 LightGBM
在讨论 LightGBM 如何工作之前,让我们先了解为什么需要这个算法。我们已经有这么多 Boosting 算法了(如上面介绍的),当数据集非常大时,Light GBM 则优于其它所有的算法。与其他算法相比,Light GBM 在巨大的数据集上运行所消耗的时间更少。
LightGBM 是一个梯度增强框架,它使用基于树的算法,并遵循 leaf-wise 方式,而其他算法以 level-wise 方式工作。下面的图片将帮助你更好地理解差异。
level-wise 在数据量少的时候可能会导致过拟合,但是可以通过使用参数 max_depth 可以避免这种情况发生。你可以阅读下面的文章了解更多 Light GBM 及其与 XGB 的比较:
https://www.analyticsvidhya.com/blog/2017/06/which-algorithm-takes-the-crown-light-gbm-vs-xgboost/
代码:
import lightgbm as lgb
train_data=lgb.Dataset(x_train,label=y_train)
#define parameters
params = {'learning_rate':0.001}
model= lgb.train(params, train_data, 100)
y_pred=model.predict(x_test)
for i in range(0,185):
if y_pred[i]>=0.5:
y_pred[i]=1
else:
y_pred[i]=0
0.81621621621621621
回归问题的示例代码:
import lightgbm as lgb
train_data=lgb.Dataset(x_train,label=y_train)
params = {'learning_rate':0.001}
model= lgb.train(params, train_data, 100)
from sklearn.metrics import mean_squared_error
rmse=mean_squared_error(y_pred,y_test)**0.5
参数:
4.7 CatBoost
处理分类变量是一个乏味的过程,尤其有大量这样的变量。当分类变量的标签太多(例如高度基数)时,对它们进行独热编码会指数地增加维度,这让数据处理非常困难。
CatBoost 可以自动处理分类变量,并且不需要像其他机器学习算法那样进行额外的数据预处理。下面是一篇详细介绍 CatBoost 的文章:
https://www.analyticsvidhya.com/blog/2017/08/catboost-automated-categorical-data/
代码:
CatBoost 算法能有效地处理分类变量。因此,不需要对分类变量进行独热编码。只需加载文件,填入缺失值,就可以了。
from catboost import CatBoostClassifier
model=CatBoostClassifier()
categorical_features_indices = np.where(df.dtypes != np.float)[0]
model.fit(x_train,y_train,cat_features=([ 0, 1, 2, 3, 4, 10]),eval_set=(x_test, y_test))
model.score(x_test,y_test)
0.80540540540540539
回归问题的示例代码:
from catboost import CatBoostRegressor
model=CatBoostRegressor()
categorical_features_indices = np.where(df.dtypes != np.float)[0]
model.fit(x_train,y_train,cat_features=([ 0, 1, 2, 3, 4, 10]),eval_set=(x_test, y_test))
model.score(x_test,y_test)
参数:
结语
集合建模可以指数地提高模型的性能!在本文中,我们介绍了各种集成学习技术,并了解了这些技术在机器学习算法中的应用。此外,我们实现了我们的贷款预测数据集的算法。
这篇文章将使你对这个话题有一个全面的了解。此外,鼓励你自己动手实现这些算法并与我们分享你的结果!