模型融合的基本思想就是集成单个模型以提升整体性能,也就是所说的 Ensemble Model。
常见的模型融合方法有:Bagging、Boosting、Stacking、Blending。
Bagging:该方法通常考虑的是同质弱学习器,相互独立地并行学习这些弱学习器,并按照某种确定性的平均过程将它们组合起来。单个学习器之间独立学习,学习器之间无依赖,因此可以高效并行学习。模型代表有 随机森林(Random Forest)。
Boosting:该方法通常考虑的也是同质弱学习器。它以一种高度自适应的方法顺序地学习这些弱学习器(每个弱学习器都依赖于前面的学习器),并按照某种确定性的策略将它们组合起来。学习器之间相互依赖,只能在样本上并行,在学习器上无法并行。这类模型有:Adaboost、GBDT、XGBoost等。
Stacking:该方法通常考虑的是异质弱学习器,并行地学习它们,并通过训练一个「元模型」将它们组合起来,根据不同弱模型的预测结果输出一个最终的预测结果。
Blending:Blending与Stacking大致相同,主要区别在于训练集不是通过K-Fold的CV策略来获得预测值从而生成第二阶段模型的特征,而是建立一个Holdout集。简单来说,Blending直接用不相交的数据集用于不同层的训练。
以两层的Blending为例,训练集划分为两部分(d1,d2),测试集为test。
第一层:用d1训练多个模型,将其对d2和test的预测结果作为第二层的New Features。
第二层:用d2的New Features和标签训练新的分类器,然后把test的New Features输入作为最终的测试集,对test预测出的结果就是最终的模型融合的值。
缺点是,d1未参与第二层的模型训练,每一层的训练数据量较上一层都有所减少,后续的模型看到的训练数据越来越少。
下面结合模型架构和代码重点解释一下 Stacking和Blending方法。
Stacking
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedKFold
import numpy as np
from sklearn.metrics import roc_auc_score
from sklearn.datasets.samples_generator import make_blobs
# 创建训练的数据集
data, target = make_blobs(n_samples=50000, centers=2, random_state=0, cluster_std=0.60)
# 模型融合中使用到的各个单模型
clfs = [RandomForestClassifier(n_estimators=5, n_jobs=-1, criterion='gini'),
RandomForestClassifier(n_estimators=5, n_jobs=-1, criterion='entropy'),
ExtraTreesClassifier(n_estimators=5, n_jobs=-1, criterion='gini'),
ExtraTreesClassifier(n_estimators=5, n_jobs=-1, criterion='entropy'),
GradientBoostingClassifier(learning_rate=0.05, subsample=0.5, max_depth=6, n_estimators=5)]
# 切分一部分数据作为测试集
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.33, random_state=2017)
dataset_blend_train = np.zeros((X_train.shape[0], len(clfs)))
dataset_blend_test = np.zeros((X_test.shape[0], len(clfs)))
# 定义交叉验证折数
# 4折stacking
n_folds = 4
skf = StratifiedKFold(n_splits=n_folds)
train_val = list(skf.split(X_train, y_train))
# 第一层将原始X作为特征
for j, clf in enumerate(clfs):
dataset_blend_test_j = np.zeros((X_test.shape[0], n_folds))
for i, (train, val) in enumerate(train_val):
# 使用第i个部分作为预测,剩余的部分来训练模型,获得其预测的输出作为第i部分的新特征。
X_tra, y_tra, X_val, y_val = X_train[train], y_train[train], X_train[val], y_train[val]
clf.fit(X_tra, y_tra)
y_submission = clf.predict_proba(X_val)[:, 1]
dataset_blend_train[val, j] = y_submission
dataset_blend_test_j[:, i] = clf.predict_proba(X_test)[:, 1]
# 对于测试集,直接用这k个模型的预测值均值作为新的特征。
dataset_blend_test[:, j] = dataset_blend_test_j.mean(1)
print("Model Name: {0:<30}, Val auc score: {1:.5f}".format(clf.__class__.__name__, roc_auc_score(y_test, dataset_blend_test[:, j])))
打印结果:
Model Name: RandomForestClassifier , Val auc score: 0.99956
Model Name: RandomForestClassifier , Val auc score: 0.99963
Model Name: ExtraTreesClassifier , Val auc score: 0.99980
Model Name: ExtraTreesClassifier , Val auc score: 0.99975
Model Name: GradientBoostingClassifier , Val auc score: 0.99974
# 第二层将上一层预测的结果作为特征
clf = GradientBoostingClassifier(learning_rate=0.02, subsample=0.5, max_depth=6, n_estimators=30)
clf.fit(dataset_blend_train, y)
y_submission = clf.predict_proba(dataset_blend_test)[:, 1]
print("Linear stretch of predictions to [0,1]")
y_submission = (y_submission - y_submission.min()) / (y_submission.max() - y_submission.min())
print("stack result")
print("val auc Score: %f" % (roc_auc_score(y_predict, y_submission)))
Linear stretch of predictions to [0,1]
stack result
val auc Score: 0.999617
Blending
# 切分训练数据集为d1,d2两部分
X_d1, X_d2, y_d1, y_d2 = train_test_split(X_train, y_train, test_size=0.5, random_state=2017)
dataset_d1 = np.zeros((X_d2.shape[0], len(clfs)))
dataset_d2 = np.zeros((X_test.shape[0], len(clfs)))
# 第一层
for j, clf in enumerate(clfs):
'''依次训练各个单模型'''
# print(j, clf)
'''使用第1个部分作为预测,第2部分来训练模型,获得其预测的输出作为第2部分的新特征。'''
# X_train, y_train, X_test, y_test = X[train], y[train], X[test], y[test]
clf.fit(X_d1, y_d1)
y_submission = clf.predict_proba(X_d2)[:, 1]
dataset_d1[:, j] = y_submission
'''对于测试集,直接用这k个模型的预测值作为新的特征。'''
dataset_d2[:, j] = clf.predict_proba(X_test)[:, 1]
print("Model Name: {0:<30}, Val auc score: {1:.5f}".format(clf.__class__.__name__,
roc_auc_score(y_test, dataset_d2[:, j])))
# 第二层
'''融合使用的模型'''
# clf = LogisticRegression()
clf = GradientBoostingClassifier(learning_rate=0.02, subsample=0.5, max_depth=6, n_estimators=30)
clf.fit(dataset_d1, y_d2)
y_submission = clf.predict_proba(dataset_d2)[:, 1]
print('----------')
print("Linear stretch of predictions to [0,1]")
y_submission = (y_submission - y_submission.min()) / (y_submission.max() - y_submission.min())
print("blend result")
print("val auc Score: %f" % (roc_auc_score(y_predict, y_submission)))
打印结果:
Model Name: RandomForestClassifier , Val auc score: 0.99903
Model Name: RandomForestClassifier , Val auc score: 0.99927
Model Name: ExtraTreesClassifier , Val auc score: 0.99944
Model Name: ExtraTreesClassifier , Val auc score: 0.99938
Model Name: GradientBoostingClassifier , Val auc score: 0.99871
----------
Linear stretch of predictions to [0,1]
blend result
val auc Score: 0.999272