【数据挖掘】HeartbeatSignalsClassification 学习笔记(四)——模型融合

模型融合

分类

模型融合大致有以下几种方式:

  1. 简单加权融合:
    • 回归(分类概率):算术平均融合(Arithmetic mean),几何平均融合
    • 分类:投票(Voting)
    • 综合:排序融合(Rank averaging),log融合
  2. stacking/blending:
    • 构建多层模型,并利用预测结果再拟合预测
  3. boosing/bagging(在xgboost,Adaboost, GBDT中已经用到)
    • 多树的提升方法

Averaging

模型融合最粗暴的方法就是直接将多个模型的预测值取平均,好一点的方法就是根据模型的效果给模型赋权重。这两种方法虽然简单却是Bagging,Boosting等高级算法的思想基础

import numpy as np
import pandas as pd
from sklearn import metrics

## 生成一些简单的样本数据,test_prei 代表第i个模型的预测值
test_pre1 = [1.2, 3.2, 2.1, 6.2]
test_pre2 = [0.9, 3.1, 2.0, 5.9]
test_pre3 = [1.1, 2.9, 2.2, 6.0]

# y_test_true 代表第模型的真实值
y_test_true = [1, 3, 2, 6] 

## 定义结果的加权平均函数
def Weighted_method(test_pre1,test_pre2,test_pre3,w=[1/3,1/3,1/3]):
    Weighted_result = w[0]*pd.Series(test_pre1)+w[1]*pd.Series(test_pre2)+w[2]*pd.Series(test_pre3)
    return Weighted_result

# 各模型的预测结果计算MAE
print('Pred1 MAE:',metrics.mean_absolute_error(y_test_true, test_pre1))
print('Pred2 MAE:',metrics.mean_absolute_error(y_test_true, test_pre2))
print('Pred3 MAE:',metrics.mean_absolute_error(y_test_true, test_pre3))

## 根据加权计算MAE
w = [0.3,0.4,0.3] # 定义比重权值
Weighted_pre = Weighted_method(test_pre1,test_pre2,test_pre3,w)
print('Weighted_pre MAE:',metrics.mean_absolute_error(y_test_true, Weighted_pre))
Pred1 MAE: 0.1750000000000001
Pred2 MAE: 0.07499999999999993
Pred3 MAE: 0.10000000000000009
Weighted_pre MAE: 0.05750000000000027

可以发现加权结果相对于之前的结果是有提升的,这种我们称其为简单的加权平均。
还有一些特殊的形式,比如mean平均,median平均

## 定义结果的加权平均函数
def Mean_method(test_pre1,test_pre2,test_pre3):
    Mean_result = pd.concat([pd.Series(test_pre1),pd.Series(test_pre2),pd.Series(test_pre3)],axis=1).mean(axis=1)
    return Mean_result

Mean_pre = Mean_method(test_pre1,test_pre2,test_pre3)
print('Mean_pre MAE:',metrics.mean_absolute_error(y_test_true, Mean_pre))

## 定义结果的加权平均函数
def Median_method(test_pre1,test_pre2,test_pre3):
    Median_result = pd.concat([pd.Series(test_pre1),pd.Series(test_pre2),pd.Series(test_pre3)],axis=1).median(axis=1)
    return Median_result

Median_pre = Median_method(test_pre1,test_pre2,test_pre3)
print('Median_pre MAE:',metrics.mean_absolute_error(y_test_true, Median_pre))
Mean_pre MAE: 0.06666666666666693
Median_pre MAE: 0.07500000000000007

Voting

Voting即投票机制,分为**硬投票(hard voting)软投票(soft voting)**两种。

hard voting

硬投票对多个模型直接进行投票,不区分模型结果的相对重要度,最终投票数最多的类为最终被预测的类。假设有三个相互独立的模型,每个模型正确率均为70%,根据概率统计知识计算最终的正确率为:
0.7 ∗ 0.7 ∗ 0.7 + 0.7 ∗ 0.7 ∗ 0.3 ∗ 3 = 0.784 0.7*0.7*0.7+0.7*0.7*0.3*3=0.784 0.70.70.7+0.70.70.33=0.784
经过简单的投票,正确率提高了8%。这是一个简单的概率学问题——如果进行投票的模型越多(以一定正确率为前提),那么显然其结果将会更好。值得注意的是模型要相互独立,结果之间没有相关性。越相近的模型进行融合,融合效果也会越差。

比如对于一个正确输出全为1的测试,我们有三个很相近的的预测结果,分别为:

在这里插入图片描述

投票结果为:

在这里插入图片描述

而假如我们的各个预测结果之间有很大差异:

在这里插入图片描述

投票结果将为:

在这里插入图片描述

模型差异越大,融合的效果越好。注意这里所指模型之间的差异,并不是指正确率的差异,而是指模型之间相关性的差异。

'''
硬投票:对多个模型直接进行投票,不区分模型结果的相对重要度,最终投票数最多的类为最终被预测的类。
'''
iris = datasets.load_iris()

x=iris.data
y=iris.target
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3)

clf1 = lgb.LGBMClassifier(learning_rate=0.1, n_estimators=150, max_depth=3, min_child_weight=2, subsample=0.7,
                     colsample_bytree=0.6, objective='binary:logistic')
clf2 = RandomForestClassifier(n_estimators=200, max_depth=10, min_samples_split=10,
                              min_samples_leaf=63,oob_score=True)
clf3 = SVC(C=0.1)

# 硬投票
eclf = VotingClassifier(estimators=[('lgb', clf1), ('rf', clf2), ('svc', clf3)], voting='hard')
for clf, label in zip([clf1, clf2, clf3, eclf], ['LGB', 'Random Forest', 'SVM', 'Ensemble']):
    scores = cross_val_score(clf, x, y, cv=5, scoring='accuracy')
    print("Accuracy: %0.2f (+/- %0.2f) [%s]" % (scores.mean(), scores.std(), label))
Accuracy: 0.95 (+/- 0.05) [LGB]
Accuracy: 0.33 (+/- 0.00) [Random Forest]
Accuracy: 0.92 (+/- 0.03) [SVM]
Accuracy: 0.95 (+/- 0.05) [Ensemble]

soft voting

软投票也称加权平均概率投票,是使用输出类概率分类的投票法,其通过输入权重,得到每个类概率的加权平均值,并选择值较大的那一类。

软投票将类别标签返回为预测概率之和的 Argmax,其通过输出类概率实现;硬投票则是直接输出类标签实现。

对于回归问题,对各种模型的预测结果进行平均,所得到的结果通过能够减少过拟合,并使得边界更加平滑,单个模型的边界可能很粗糙。

在这里插入图片描述

参考:

  1. 模型融合方法总结
  2. 软投票 Soft voting

Stacking

介绍

以Kaggle的Titanic为例,使用5-折交叉验证训练两层模型,再用Stacking融合模型。先有如下数据:

  • train_data (890, 7+1)
  • test_data (418, 1)

先训练第一层的Model1,每次fold都会将train_data分为713行的kf_train_set和178行kf_vali_set,然后预测vali_set,得到长度为178的预测值,5-fold后就会得到178*5=890的预测值,与train_data长度一致(所有训练集数据都会被预测一遍)。同时,每次fold训练的模型都要预测全部的test_data,得到长度为418的预测值,5次之后就是5x418的预测矩阵,然后按行求平均值,最后得到1x418的平均预测值。

如果第一层还有其他模型,按上述方法同样训练一遍,若第一层共有n个模型(P1, P2,…, Pn),则训练完第一层模型最终得到:kf_vali_set的预测值矩阵(890, n),训练后的模型(P1, P2, … Pn)以及test_data的预测值矩阵(418, n)。

第二层的kf_train_data即为第一层的kf_vali_set预测值矩阵(890, n),test_data即为第一层的test_data预测值矩阵(418, n)。

在这里插入图片描述

代码如下:

# Out-of-Fold Prediction
ntrain = train.shape[0] # 891
ntest = test.shape[0]  # 418
kf = KFodl(n_splits=5, random_state=42)

def get_oof(clf, X_train, y_train, X_test):
    oof_train = np.zeros((ntrain,))  # (891,)
    oof_test = np.zeros((ntest,))   # (418,)
    oof_test_skf = np.empty((5, ntest))  # (5, 418)
    
    for i, (train_index, vali_index) in enumerate(kf.split(X_train)):
        kf_X_train = X_train[train_index] # kf_train_set, 712 instances for each fold
        kf_y_train = y_train[train_index] # kf_train_set label, 712 instances for each fold
        kf_X_vali = X_train[vali_index]   # kf_vali_set  178 instances for each fold
        
        clf.train(kf_X_train, kf_y_train)
        
        oof_train[vali_index] = clf.predict(kf_X_vali)  # predict kf_vali_set
        oof_test_skf[i, :] = clf.predict(X_test) # predict kf_test_data
    
    oof_test[:] = oof_test_skf.mean(axis=0) # (1, 418)
    return oof_train.reshape(-1,1), oof_test.reshape(-1, 1) # (891, 1)  (481, 1)

参考:Kaggle机器学习之模型融合(stacking)心得

回归

from sklearn import linear_model

def Stacking_method(train_reg1,train_reg2,train_reg3,y_train_true,test_pre1,test_pre2,test_pre3,model_L2= linear_model.LinearRegression()):
    model_L2.fit(pd.concat([pd.Series(train_reg1),pd.Series(train_reg2),pd.Series(train_reg3)],axis=1).values,y_train_true)
    Stacking_result = model_L2.predict(pd.concat([pd.Series(test_pre1),pd.Series(test_pre2),pd.Series(test_pre3)],axis=1).values)
    return Stacking_result

## 生成一些简单的样本数据,test_prei 代表第i个模型的预测值
train_reg1 = [3.2, 8.2, 9.1, 5.2]
train_reg2 = [2.9, 8.1, 9.0, 4.9]
train_reg3 = [3.1, 7.9, 9.2, 5.0]
# y_test_true 代表样本的真实值
y_train_true = [3, 8, 9, 5] 

test_pre1 = [1.2, 3.2, 2.1, 6.2]
test_pre2 = [0.9, 3.1, 2.0, 5.9]
test_pre3 = [1.1, 2.9, 2.2, 6.0]

# y_test_true 代表第模型的真实值
y_test_true = [1, 3, 2, 6] 

model_L2= linear_model.LinearRegression()
Stacking_pre = Stacking_method(train_reg1,train_reg2,train_reg3,y_train_true,
                               test_pre1,test_pre2,test_pre3,model_L2)
print('Stacking_pre MAE:',metrics.mean_absolute_error(y_test_true, Stacking_pre))
Stacking_pre MAE: 0.04213483146067404

值得注意的是,第二层Stacking的模型不宜过于复杂,因为这会导致模型在训练集上过拟合,从而在测试集上不能达到好的效果

分类

'''
5-Fold Stacking
'''
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier,GradientBoostingClassifier
import pandas as pd
#创建训练的数据集
data_0 = iris.data
data = data_0[:100,:]

target_0 = iris.target
target = target_0[:100]

#模型融合中使用到的各个单模型
clfs = [LogisticRegression(solver='lbfgs'),
        RandomForestClassifier(n_estimators=5, n_jobs=-1, criterion='gini'),
        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, X_predict, y, y_predict = train_test_split(data, target, test_size=0.3, random_state=2020)

dataset_blend_train = np.zeros((X.shape[0], len(clfs)))
dataset_blend_test = np.zeros((X_predict.shape[0], len(clfs)))

#5折stacking
n_splits = 5
skf = StratifiedKFold(n_splits)
skf = skf.split(X, y)

for j, clf in enumerate(clfs):
    #依次训练各个单模型
    dataset_blend_test_j = np.zeros((X_predict.shape[0], 5))
    for i, (train, test) in enumerate(skf):
        #5-Fold交叉训练,使用第i个部分作为预测,剩余的部分来训练模型,获得其预测的输出作为第i部分的新特征。
        X_train, y_train, X_test, y_test = X[train], y[train], X[test], y[test]
        clf.fit(X_train, y_train)
        y_submission = clf.predict_proba(X_test)[:, 1]
        dataset_blend_train[test, j] = y_submission
        dataset_blend_test_j[:, i] = clf.predict_proba(X_predict)[:, 1]
    #对于测试集,直接用这k个模型的预测值均值作为新的特征。
    dataset_blend_test[:, j] = dataset_blend_test_j.mean(1)
    print("val auc Score: %f" % roc_auc_score(y_predict, dataset_blend_test[:, j]))

clf = LogisticRegression(solver='lbfgs')
clf.fit(dataset_blend_train, y)
y_submission = clf.predict_proba(dataset_blend_test)[:, 1]

print("Val auc Score of Stacking: %f" % (roc_auc_score(y_predict, y_submission)))
val auc Score: 1.000000
val auc Score: 0.500000
val auc Score: 0.500000
val auc Score: 0.500000
val auc Score: 0.500000
Val auc Score of Stacking: 1.000000
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值