Stacking
- 参数含义
- 1. 工具库 & 数据
- 2. 定义交叉验证函数
- 2.1 对融合模型
- 2.2 对单个评估器
- 3. 定义个体学习器和元学习器
- 3.1 个体学习器
- 3.2 元学习器
- 4. 评估调整模型
- 5. 元学习器的特征矩阵
- 5.1 特征矩阵两个问题 & Stacking
- 5.2 StackingClassfier\Regressor参数cv - 解决样本量少
- 5.3 StackingClassfier参数stack_method - 解决特征少
- 5.3 StackingRegressor参数passthrough - 解决特征少
- 6. Blending
- 6.1 改进Stacking
- 6.2 代码实现
参数含义
'''
class sklearn.ensemble.StackingClassfier(estimators, final_estimator=None, *, stack_method='auto', cv=None, n_jobs=None, passthrough=False, verbose=0)
class sklearn.ensemble.StackingRegressor(estimators, final_estimator=None, *, cv=None, n_jobs=None, passthrough=False, verbose=0)
estimators 个体评估器列表
final_estimator 元学习器,只能一个. 分类任务必须为分类算法,回归任务必须为回归算法.
cv 指定交叉验证具体类型、折数, 或简单K折交叉验证
stack_method StackingClassfier独有参数,个体学习器输出的具体测试结果
passthrough 训练元学习器时,是否加入原始数据作为特征矩阵
n_jobs,verbose 线程数与监控参数
'''
1. 工具库 & 数据
# 常用工具库
import re
import numpy as np
import pandas as pd
import matplotlib as mlp
import matplotlib.pyplot as plt
import time
# 算法辅助 & 数据
import sklearn
from sklearn.model_selection import KFold, cross_validate
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_digits # 分类-手写数字数据集
from sklearn.datasets import load_iris
from sklearn.datasets import load_boston
# 算法(单一学习器)
from sklearn.neighbors import KNeighborsClassifier as KNNC
from sklearn.neighbors import KNeighborsRegressor as KNNR
from sklearn.tree import DecisionTreeClassifier as DTC
from sklearn.tree import DecisionTreeRegressor as DTR
from sklearn.linear_model import LinearRegression as LR
from sklearn.linear_model import LogisticRegression as LogiR
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.ensemble import GradientBoostingClassifier as GBC
from sklearn.ensemble import GradientBoostingRegressor as GBR
from sklearn.naive_bayes import GaussianNB
import xgboost as xgb
# 融合模型
from sklearn.ensemble import StackingClassifier
from sklearn.ensemble import StackingRegressor
data = load_digits()
X = data.data
y = data.target
print('X.shape =', X.shape)
print('类别:', np.unique(y)) # 10分类
'''
X.shape = (1797, 64)
类别: [0 1 2 3 4 5 6 7 8 9]
'''
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.2, random_state=1107)
print(Xtrain.shape, Xtest.shape, Ytrain.shape, Ytest.shape)
'''
(1437, 64) (360, 64) (1437,) (360,)
'''
2. 定义交叉验证函数
2.1 对融合模型
def fusion_estimators(clf):
'''
对融合模型做交叉验证,对融合模型的表现进行评估
模型融合很容易过拟合,需要保证交叉验证和测试集分数都漂亮.
'''
cv = KFold(n_splits=5, shuffle=True, random_state=1107)
results = cross_validate(clf
,Xtrain, Ytrain
,cv=cv
,scoring='accuracy'
,n_jobs=-1
,return_train_score=True
,verbose=False)
test = clf.fit(Xtrain, Ytrain).score(Xtest, Ytest)
print('train_score:{}'.format(results['train_score'].mean())
,'\n cv_mean:{}'.format(results['test_score'].mean())
,'\n test_score:{}'.format(test)
)
2.2 对单个评估器
def individual_estimators(estimators):
'''
对融合模型中的每个评估器做交叉验证,对单一评估器的表现进行评估
'''
for estimator in estimators:
cv = KFold(n_splits=5, shuffle=True, random_state=1107)
results = cross_validate(estimator[1]
,Xtrain, Ytrain
,cv=cv
,scoring='accuracy'
,n_jobs=-1
,return_train_score=True
,verbose=False
)
test = estimator[1].fit(Xtrain, Ytrain).score(Xtest, Ytest)
print('train_score:{}'.format(results['train_score'].mean())
,'\n cv_mean:{}'.format(results['test_score'].mean())
,'\n test_score:{}'.format(test)
)
3. 定义个体学习器和元学习器
3.1 个体学习器
'''
沿用Voting投票法(模型融合_投票法)选出的七个模型
'''
# 逻辑回归没有增加多样性选项
clf1 = LogiR(max_iter=3000, C=0.1, random_state=1107, n_jobs=8)
# 增加特征多样性和样本多样性
clf2 = RFC(n_estimators=100, max_features='sqrt', max_samples=0.9, random_state=1107, n_jobs=8)
# 增加特征多样性, 稍微上调特征数量
clf3 = GBC(n_estimators=100, max_features=16, random_state=1107)
# 增加算法多样性,新增决策树与KNN
clf4 = DTC(max_depth=8, random_state=1107)
clf5 = KNNC(n_neighbors=8, n_jobs=8)
clf6 = GaussianNB()
# 新增随即多样性,相同的算法更换随机数种子
clf7 = RFC(n_estimators=100, max_features='sqrt', max_samples=0.9, random_state=1998, n_jobs=-1)
clf8 = GBC(n_estimators=100, max_features=16, random_state=1998)
estimators = [('Logistic Regression', clf1)
,('RandomForest', clf2)
,('GBDT', clf3)
,('Decision Tree', clf4)
,('KNN', clf5)
# ,('Bayes', clf6) # 拖后腿不要了
,('RandomForest2', clf7)
,('GBDT2', clf8)
]
3.2 元学习器
'''
在此选择单个评估器中分数最高的随机森林作为元学习器
分类任务可以选择稍复杂的算法
回归任务需要选择简单的算法
'''
final_estimator = RFC(n_estimators=100
# 如果此分裂导致杂质减少大于或等于该值,则该节点将被分裂
# ,min_impurity_decrease=0.0025
,random_state=1107
,n_jobs=8
)
clf = StackingClassifier(estimators=estimators # Level0的个体学习器
,final_estimator=final_estimator # Level1的元学习器
,n_jobs=8
)
4. 评估调整模型
'''
没有过拟合限制,不如投票法
相对于投票法,交叉验证分数降低,测试集分数持平
'''
fusion_estimators(clf)
'''
train_score:1.0
cv_mean:0.9825929152148664
test_score:0.9833333333333333
'''
'''
精调过拟合
min_impurity_decrease从0.1往后试
'''
fusion_estimators(clf)
'''
train_score:1.0
cv_mean:0.9825929152148664
test_score:0.9833333333333333
'''
5. 元学习器的特征矩阵
5.1 特征矩阵两个问题 & Stacking
个体学习器在原始数据上训练预测,再把预测结果排布成新特征矩阵,供元学习器学习。
两个问题
- 元学习器特征矩阵的特征一定很少
个体学习器数量不多
- 元学习器特征矩阵的样本量不多
训练Stacking模型时,分为训练集、验证集和测试集。
测试集训练过程中不能用;
训练集用于训练个体学习器,已透露给个体学习器,再在上面预测则偏高;
只能用验证集,一般只占数据集的30%~40%。
个体学习器预测结果,即元学习器需要训练的矩阵如下排布:
Stacking堆叠法解决样本量少:
-
参数 cv
-
五折交叉验证训练了五个不同参数的模型,每个模型取五份数据中的一份作为预测集,将五次预测结果堆叠即得到一个算法在整个数据集上的预测结果。
-
如此,任意个体学习器输出的预测值数量 = 样本量;
特征矩阵的行数 = 原始数据样本量。
5.2 StackingClassfier\Regressor参数cv - 解决样本量少
'''
cv
None 默认5折交叉验证
交叉验证对象 如cv = KFold(n_splits=5, shuffle=True, random_state=1107)
任意整数
表示Stratified K折验证中的折数
Stratified K折验证考虑标签类别的占比,保证原始标签中类别占比=训练标签中类别占比=验证标签中类别占比
防止有的标签类别过少导致某一折该类标签不存在
'''
'''
Stacking内部交叉验证不验证泛化能力,只生产数据实现堆叠;
折数很小时,模型容易过拟合;
折数较大时,模型抗过拟合能力会上升,学习能力会略有下降;
数据量足够大时,折数过多不会带来好处,反而降低训练时间.
'''
5.3 StackingClassfier参数stack_method - 解决特征少
'''
特征量即个体学习器数量
一个个体学习器输出的是类别(如0,1,2)只能有一个特征,输出类型若是概率值、置信度等则可拓展多列.
StackingClassfier独有参数stack_method控制个体分类器的输出.
stack_method四种输入字符串:
'auto' 按后三种顺序尝试,若个体学习器可以使用某一种就选择该种输出
'predict_proba' 输出概率probability
对于二分类,输出一列标签为1的概率
对于n分类,输出n列标签为[0,1,2...,n]的概率
'decision_function' 输出置信度
对于二分类,输出一列标签为1的置信度
对于n分类,输出n列标签为[0,1,2...,n]的置信度
'predict' 输出预测结果
输出一列预测标签
后三种常在sklearn中使用
'''
LR = LogiR(max_iter=3000, random_state=1).fit(Xtrain, Ytrain)
LR.predict_proba(Xtest[:2])
'''
array([[6.69189045e-15, 9.99986537e-01, 5.91250275e-13, 1.36647983e-11,
1.33534349e-05, 5.70544031e-13, 1.47472860e-10, 3.56315884e-09,
1.05798134e-07, 3.58877485e-10],
[1.98852180e-07, 6.91956491e-07, 1.39758067e-11, 1.16336439e-09,
3.50962095e-07, 9.99988643e-01, 8.28053059e-06, 2.56757567e-07,
1.02535554e-06, 5.51767953e-07]])
'''
LR.decision_function(Xtest[:2])
'''
array([[-12.125248 , 20.51261851, -7.64390501, -4.50356608,
9.28889507, -7.67955407, -2.12474498, 1.06001361,
4.45089903, -1.23540807],
[ -0.91870057, 0.32826077, -10.48168984, -6.05994616,
-0.35058408, 14.51199217, 2.81040002, -0.66312999,
0.7215324 , 0.10186528]])
'''
LR.predict(Xtest[:2])
'''
array([1, 5])
'''
使用stack_method
# 使用stack_method
final_estimator = RFC(n_estimators=100
# 如果此分裂导致杂质减少大于或等于该值,则该节点将被分裂
# ,min_impurity_decrease=0.0025
,random_state=1107
,n_jobs=8
)
clf = StackingClassifier(estimators=estimators # Level0的个体学习器
,final_estimator=final_estimator # Level1的元学习器
,stack_method='auto'
,n_jobs=8
)
clf = clf.fit(Xtrain, Ytrain)
'''
transform接口查看元学习器所使用的训练特征矩阵
特征矩阵样本数 = 原数据样本数
7个个体分类器,10分类数据,每个个体分类器输出10个类别对应的概率,共0个特征.
'''
clf.transform(Xtrain).shape , Xtrain.shape
'''
((1437, 70), (1437, 64))
'''
5.3 StackingRegressor参数passthrough - 解决特征少
'''
对于回归算法
passthrough
True 将原始特征矩阵加入个体学习器的预测值,构成新特征矩阵.较高过拟合风险.
False 不加入
'''
6. Blending
6.1 改进Stacking
数据量大、Stacking过拟合严重时使用Blending
- Stacking
· 分训练集和测试集.
· 训练集在个体学习器上交叉验证, 验证结果进行堆叠形成元学习器的特征矩阵.
· 隐藏步骤: 之后使用全部训练数据对所有个体学习器训练, 为测试做准备.
- Blending 学习浅,计算少,不易过拟合
· 分训练集、验证集和测试集.
· 所有个体学习器在训练集上训练, 在验证集上验证, 所有个体学习器验证结果横向拼接形成特征矩阵.
6.2 代码实现
import pandas as pd
from sklearn.model_selection import train_test_split
def BlendingClassifier(X, y, estimators, final_estimator, test_size=0.2, vali_size=0.4):
'''
该函数实现Blending分类融合
X,y: 整体数据集, 会被分割为训练集、验证集和测试集三部分
estimators: level0的个体学习器, 输入格式形如sklearn中要求的[('名字', 算法),('名字', 算法)...]
final_estimator: 元学习器
test_size: 测试集占全数据集的比例
vali_size: 验证集占全数据集的比例
'''
# 分割数据集
# 1. 分测试集
# 2. 分训练集和验证集, 验证集占完整数据集的0.4, 因此占排除测试集后的0.4/0.8
X_, Xtest, y_, Ytest = train_test_split(X,y,test_size=test_size,random_state=1107)
Xtrain, Xvali, Ytrain, Yvali = train_test_split(X_,y_,test_size=vali_size/(1-test_size),random_state=1107)
# 训练
# 建立空dataframe用于保存个体学习器上的验证结果, 即用于生成特征矩阵
# 新建空列表用于保存训练完毕的个体学习器, 以便在测试中使用、
NewX_vali = pd.DataFrame()
trained_estimators = []
# 循环训练每个个体学习器, 并收集个体学习器在验证集上输出的概率
for clf_id, clf in estimators:
clf = clf.fit(Xtrain, Ytrain)
val_predictions = pd.DataFrame(clf.predict_proba(Xvali))
# 保存结果, 在循环中逐渐构建特征矩阵
NewX_vali = pd.concat([NewX_vali, val_predictions], axis=1)
trained_estimators.append((clf_id, clf))
# 元学习器在特征矩阵上训练, 并输出训练分数
final_estimator = final_estimator.fit(NewX_vali, Yvali)
train_score = final_estimator.score(NewX_vali, Yvali)
# 测试
# 建立空dataframe用于保存个体学习器的预测结果,即用于生成特征矩阵
NewX_test = pd.DataFrame()
# 循环, 在每个训练完的个体学习器上进行预测, 并收集每个个体学习器上输出的概率
for clf_id, clf in trained_estimators:
test_prediction = pd.DataFrame(clf.predict_proba(Xtest))
# 保存结果, 在循环中逐渐构建特征矩阵
NewX_test = pd.concat([NewX_test, test_prediction], axis=1)
# 元学习器在新特征上测试, 并输出测试分数
test_score = final_estimator.score(NewX_test, Ytest)
# 打印训练分数与验证分数
print('train_score=', train_score)
print('test_score=', test_score)
'''
沿用Voting投票法(模型融合_投票法)选出的七个模型
'''
# 逻辑回归没有增加多样性选项
clf1 = LogiR(max_iter=3000, C=0.1, random_state=1107, n_jobs=8)
# 增加特征多样性和样本多样性
clf2 = RFC(n_estimators=100, max_features='sqrt', max_samples=0.9, random_state=1107, n_jobs=8)
# 增加特征多样性, 稍微上调特征数量
clf3 = GBC(n_estimators=100, max_features=16, random_state=1107)
# 增加算法多样性,新增决策树与KNN
clf4 = DTC(max_depth=8, random_state=1107)
clf5 = KNNC(n_neighbors=8, n_jobs=8)
clf6 = GaussianNB()
# 新增随即多样性,相同的算法更换随机数种子
clf7 = RFC(n_estimators=100, max_features='sqrt', max_samples=0.9, random_state=1998, n_jobs=-1)
clf8 = GBC(n_estimators=100, max_features=16, random_state=1998)
estimators = [('Logistic Regression', clf1)
,('RandomForest', clf2)
,('GBDT', clf3)
,('Decision Tree', clf4)
,('KNN', clf5)
# ,('Bayes', clf6) # 拖后腿不要了
,('RandomForest2', clf7)
,('GBDT2', clf8)
]
final_estimator = RFC(n_estimators=100
# ,max_depth=8
,min_impurity_decrease=0.0025
,random_state=1107
,n_jobs=-1
)
from sklearn.datasets import load_digits
data = load_digits()
X = data.data
y = data.target
BlendingClassifier(X, y, estimators, final_estimator)
'''
train_score= 0.9972183588317107
test_score= 0.9777777777777777
'''