前言
最近研究模型融合,看到很多关于介绍Stacking的文章,大多数文章都有这张图。如果你能一眼看懂,OK,那你就不用继续读下去了。如果一下子看不懂,我会结合代码具体介绍Stacking是如何工作的。
一、Stacking是什么?
Stacking 是集成学习的一种方法。集成学习可以融合不同模型学习到的特征以提升最终的预测效果。一般来说,集成学习的要求基学习器好而不同。即每个基学习器性能不能太差,同时不同的基学习器学习的特征不同。
二、代码实现
本示例使用Sklearn的威斯康辛州乳腺癌数据集
1.引入库并划分数据集
from sklearn.model_selection import StratifiedKFold,StratifiedShuffleSplit
from sklearn.metrics import roc_auc_score, f1_score,accuracy_score
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
import lightgbm as lgb
import pandas as pd
import numpy as np
random_ = 2022
data_X, data_y = load_breast_cancer(return_X_y=True, as_frame=True)
SSS = StratifiedShuffleSplit(n_splits=1,random_state=2022,test_size=0.2)
#划分数据集
for train_index, test_index in SSS.split(data_X, data_y):
train_X, train_y = data_X.iloc[train_index], data_y[train_index]
test_X, test_y = data_X.iloc[test_index], data_y[test_index]
print(f'train_X shape:{train_X.shape}, train_y shape:{train_y.shape}')
print(f'test_X shape:{test_X.shape}, test_y shape:{test_y.shape}')
"""
train_X shape:(455, 30), train_y shape:(455,)
test_X shape:(114, 30), test_y shape:(114,)
"""
2.定义模型
为了方便理解上图,我将每一个基学习器单独定义为一个模型,本例中只使用了GBDT和RF两个基学习器。
def model_gbdt(train_X, train_y, test_X, test_y, K_fold, random_):
Skf = StratifiedKFold(n_splits=K_fold,shuffle=True,random_state=random_)
train_predict = np.zeros(len(train_X))
test_predict = np.zeros(len(test_y))
for tra_idx, val_idx in Skf.split(train_X,train_y):
tra_X, tra_y = train_X.iloc[tra_idx], train_y.iloc[tra_idx]
val_X, val_y = train_X.iloc[val_idx], train_y.iloc[val_idx]
model = GradientBoostingClassifier(learning_rate=0.1,
max_depth=2,
n_estimators=3)
model.fit(tra_X, tra_y)
val_predict = model.predict(val_X)
train_predict[val_idx] = val_predict
test_predict += model.predict(test_X)/K_fold
test_pre = test_predict.copy()
test_predict[test_predict > 0.5], test_predict[test_predict < 0.5] = 1, 0
print('model_gbdt auc:',roc_auc_score(test_y, test_pre))
return train_predict, test_pre, test_predict
train_gbdt, test_gbdt, test_gbdt_ = model_gbdt(train_X, train_y,
test_X, test_y, 5, random_)
"""
输入结果:
model_gbdt auc: 0.998015873015873
"""
对于model_gbdt(相当于上图中的model1)其实我们训练了五个模型(这里的模型指的是estimator)。这里有人会想怎么是五个模型,熟悉K折交叉验证的话便很快就明白。
其实model_gbdt是将数据集划分为5份,每一次使用其中四份数据(即上图的蓝色learn部分)学习一个estimator,然后在剩下的一份数据(上图的橙色Predict部分)上验证,重复学习五个estimator。图中的五个model1,其实就是五个在不同数据集下(不同的蓝色learn部分)训练得到的estimator。最后可以得到model_gbdt对所有训练集的预测结果,同时每一个estimator可以预测得到一个测试集的结果(五个estimator即有五个结果,相当于上图中五个连续的绿色Predict),最后将这五个结果求平均则为model_gbdt对测试集最终的预测结果。
def model_rf(train_X, train_y, test_X, test_y,K_fold, random_):
Skf = StratifiedKFold(n_splits=K_fold,shuffle=True,random_state=random_)
train_predict = np.zeros(len(train_X))
test_predict = np.zeros(len(test_y))
for tra_idx, val_idx in Skf.split(train_X,train_y):
tra_X, tra_y = train_X.iloc[tra_idx], train_y.iloc[tra_idx]
val_X, val_y = train_X.iloc[val_idx], train_y.iloc[val_idx]
model = RandomForestClassifier(n_estimators=1,
max_depth=1,
bootstrap=False,
random_state=2)
model.fit(tra_X, tra_y)
val_predict = model.predict(val_X)
train_predict[val_idx] = val_predict
test_predict += model.predict(test_X)/K_fold
test_pre = test_predict.copy()
test_predict[test_predict > 0.5], test_predict[test_predict < 0.5] = 1, 0
print('model_rf accuracy_score:',roc_auc_score(test_y, test_pre))
return train_predict, test_pre, test_predict
train_rf, test_rf, test_rf_ = model_rf(train_X, train_y,
test_X, test_y, 5, random_)
"""
输入结果:
model_rf auc: 0.9836309523809524
"""
model_rf相当于model2,当然也可以有model3,model4…,基于篇幅这里就简单介绍两个基学习器的Stacking。Stacking第二层的模型也可以选择gbdt,lgb,xgb等,但通常基学习器的性能较好,Stacking的时候使用简单的lr就行了。
def stacking(data_train_X, train_y, data_test_X, test_y):
model = LogisticRegression()
model.fit(data_train_X, train_y)
test_stack = model.predict_proba(data_test_X)[:,1]
auc= roc_auc_score(test_y, test_stack)
print('Stacking auc:', auc)
return test_stack
stacking_train_X = pd.DataFrame({'gbdt':train_gbdt,'rf':train_rf})
stacking_test_X = pd.DataFrame({'gbdt':test_gbdt,'rf':test_rf})
test_stack = stacking(stacking_train_X, train_y, stacking_test_X, test_y)
"""
输出结果
Stacking auc: 0.9966931216931217
"""
我们发现,相比于gbdt模型的 0.998和rf模型的0.983,Stacking的融合结果为0.996。这表明使用Stacking融合的结果在趋近性能最优的基模型。虽然Stacking融合的效果并没有gbdt模型好,但是这并不是说Stacking没有效果。由于本文数据集较少,单个基模型就能达到接近1的效果,使用Stacking效果并不明显。
造成Stacking效果不明显的原因可能包括:
1.数据集较少
2.基模型性能差
3.基模型数量少等。
Stacking可以有以下的理解:
1.第一层做的是特征变换,第二层做的做结果预测;
2.第一层是各个模型进行预测,第二层进行结果筛选;
3.第一层是模型预测,第二层是置信度的打分和加权平均。
具体的工程实现可以参考下文
https://github.com/kaz-Anova/StackNet#restacking-mode