智慧海洋建设-Task5 模型融合

Datawhale 智慧海洋建设-Task5 模型融合

该部分学习如何进行模型的融合

内容介绍

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

简单加权融合

平均法-Averaging

1.对于回归问题,一个简单直接的思路是取平均。将多个模型的回归结果取平均值作为最终预测结果,进而把多个弱分类器荣和城强分类器。

2.稍稍改进的方法是进行加权平均,权值可以用排序的方法确定,举个例子,比如A、B、C三种基本模型,模型效果进行排名,假设排名分别是1,2,3,那么给这三个模型赋予的权值分别是3/6、2/6、1/6。

3.平均法或加权平均法看似简单,其实后面的高级算法也可以说是基于此而产生的,Bagging或者Boosting都是一种把许多弱分类器这样融合成强分类器的思想。

4.Averaging也可以用于对分类问题的概率进行平均。

投票法-voting

1.对于一个二分类问题,有3个基础模型,现在我们可以在这些基学习器的基础上得到一个投票的分类器,把票数最多的类作为我们要预测的类别。

2.投票法有硬投票(hard voting)和软投票(soft voting)

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

4.软投票:增加了设置权重的功能,可以为不同模型设置不同权重,进而区别模型不同的重要度。

stacking/blending

堆叠法-stacking

用初始训练数据学习出若干个基学习器后,将这几个学习器的预测结果作为新的训练集(第一层),来学习一个新的学习器(第二层)。

例子:

1.训练集数据大小为10000100,测试集大小为3000100。即训练集有10000条数据、100个特征;测试集有3000条数据、100个特征。该数据对应回归问题。

2.第一层使用三种算法-XGB、LGB、NN。第二层使用GBDT。

算法:

1.stacking 第一层
XGB算法

输入:使用训练集进行5-fold处理
处理:具体处理细节如下
1.使用1、2、3、4折作为训练集,训练一个XGB模型并预测第5折和测试集,将预测结果分别称为XGB-pred-tran5(shape 20001)和XGB-pred-test1(shape 30001).
2.使用1、2、3、5折作为训练集,训练一个XGB模型并预测第4折和测试集,将预测结果分别称为XGB-pred-tran4(shape 20001)和XGB-pred-test2(shape 30001).
3.使用1、2、4、5折作为训练集,训练一个XGB模型并预测第3折和测试集,将预测结果分别称为XGB-pred-tran3(shape 20001)和XGB-pred-test3(shape 30001).
4.使用1、3、4、5折作为训练集,训练一个XGB模型并预测第2折和测试集,将预测结果分别称为XGB-pred-tran2(shape 20001)和XGB-pred-test4(shape 30001).
5.使用2、3、4、5折作为训练集,训练一个XGB模型并预测第1折和测试集,将预测结果分别称为XGB-pred-tran1(shape 20001)和XGB-pred-test5(shape 30001).
输出:
1.将XGB分别对1、2、3、4、5折进行预测的结果合并,得到XGB-pred-tran(shape 100001)。并且根据5-fold的原理可以知道,与原数据可以形成对应关系。
2.将XGB-pred-test1 - 5 的结果使用Averaging的方法求平均值,最终得到XGB-pred-test(shape 3000
1)。

LGB算法

输入:与XGB算法一致
处理:与XGB算法一致。只需更改预测结果的命名即可,如LGB-pred-tran5和LGB-pred-test1
输出:
1.将LGB分别对1、2、3、4、5折进行预测的结果合并,得到LGB-pred-tran(shape 100001)。
2.将LGB-pred-test1 - 5 的结果使用Averaging的方法求平均值,最终得到LGB-pred-test(shape 3000
1)。

NN算法

输入:与XGB算法一致
处理:与XGB算法一致。只需更改预测结果的命名即可,如NN-pred-tran5和NN-pred-test1
输出:
1.将NN分别对1、2、3、4、5折进行预测的结果合并,得到NN-pred-tran(shape 100001)。
2.将NN-pred-test1 - 5 的结果使用Averaging的方法求平均值,最终得到NN-pred-test(shape 3000
1)。
2.stacking 第二层
1.训练集:将三个新特征 XGB-pred-tran、LGB-pred-tran、NN-pred-tran合并得到新的训练集(shape 100003)
2.测试集:将三个新测试集XGB-pred-test、LGB-pred-test、NN-pred-test合并得到新的测试集(shape 30000
3)
用新训练集和测试集构造第二层的预测器,即GBDT模型
混合法 - blending
Blending与Stacking大致相同,只是Blending的主要区别在于训练集不是通过K-Fold的CV策略来获得预测值从而生成第二阶段模型的特征,而是建立一个Holdout集。简单来说,Blending直接用不相交的数据集用于不同层的训练。

同样以上述数据集为例,构造一个两层的Blending模型。

首先将训练集划分为两部分(d1,d2),例如d1为4000条数据用于blending的第一层,d2是6000条数据用于blending的第二层。

第一层:用d1训练多个模型,将其对d2和test的预测结果作为第二层的New Features。例如同样适用上述三个模型,对d2生成60003的新特征数据;对test生成30003的新特征矩阵。

第二层:用d2的New Features和标签训练新的分类器,然后把test的New Features输入作为最终的测试集,对test预测出的结果就是最终的模型融合的值。
优缺点对比

Blending的优点在于:

比stacking简单(因为不用进行k次的交叉验证来获得stacker feature)

避开了一个信息泄露问题:generlizers和stacker使用了不一样的数据集

在团队建模过程中,不需要给队友分享自己的随机种子

缺点:

使用了很少的数据(是划分hold-out作为测试集,并非cv)

blender可能会过拟合(其实大概率是第一点导致的)

stacking使用多次的CV会比较稳健

代码

import pandas as pd
import numpy as np
from sklearn.metrics import classification_report, f1_score
from sklearn.model_selection import StratifiedKFold, KFold,train_test_split
def reduce_mem_usage(df):
    start_mem = df.memory_usage().sum() / 1024**2 
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
    
    for col in df.columns:
        col_type = df[col].dtype
        
        if col_type != object:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
        else:
            df[col] = df[col].astype('category')

    end_mem = df.memory_usage().sum() / 1024**2 
    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
    
    return df
all_df = pd.read_csv(r'E:\百度云下载\智慧海洋组队文件/group_df.csv',index_col=0)
all_df = reduce_mem_usage(all_df)
all_df = all_df.fillna(99)
all_df.head()
Memory usage of dataframe is 30.28 MB
Memory usage after optimization is: 7.59 MB
Decreased by 74.9%
IDlabelcntanchor_cntanchor_ratiolat_minlat_maxlat_meanlat_medianlat_nunique...w2v_20_meanw2v_21_meanw2v_22_meanw2v_23_meanw2v_24_meanw2v_25_meanw2v_26_meanw2v_27_meanw2v_28_meanw2v_29_mean
0024143720.89843840.0625040.2812540.0625040.0625023...5.1835944.207031-2.089844-2.855469-1.6865231.7011721.500000-1.8974611.8759771.623047
1123851870.48559640.0625040.2187540.0937540.06250138...1.4130862.259766-0.436279-1.192383-0.6826171.1279301.210938-0.2233891.6562501.078125
2102397600.15112340.2187540.5937540.2812540.2187597...4.0507812.544922-0.881836-0.593750-0.0806882.0976560.287598-0.0388181.7861331.169922
31002411670.16296440.0625040.5312540.3125040.34375337...0.8413090.671875-0.587402-0.842773-0.3493650.1708980.4201660.1278080.5141600.275635
410000377360.09552041.1250041.9062541.7500041.84375257...0.2919920.578613-0.998047-0.379395-0.0047110.5180660.3178710.0600280.4633790.733887

5 rows × 440 columns


all_df.shape
(9000, 440)
all_df['label'].value_counts()
 2    4361
-1    2000
 0    1621
 1    1018
Name: label, dtype: int64

all_df中label为0/1/2的为训练集,一共有7000条;label为-1的为测试集,一共有2000条。

label为-1的测试集没有label,这部分数据用于模拟真实比赛提交数据。

train数据均有标签,我们将从中分出30%作为验证集,其余作为训练集。在验证集上比较模型性能优劣,模型性能均使用f1作为评分。

train = all_df[all_df['label'] != -1]
test =  all_df[all_df['label'] == -1]
feats = [c for c in train.columns if c not in ['ID', 'label']]
X_train,X_val,y_train,y_val= train_test_split(train[feats],train['label'],test_size=0.2,random_state=0)

单模及加权融合

分别是用了一个三种不同的RF/LGB/LGB模型。事实上模型融合需要基础分类器之间存在差异,一般不会选用相同的分类器模型。

# 单模函数
def build_model_rf(X_train,y_train):
    model = RandomForestClassifier(n_estimators = 100)
    model.fit(X_train, y_train)
    return model


def build_model_lgb(X_train,y_train):
    model = lgb.LGBMClassifier(num_leaves=127,learning_rate = 0.1,n_estimators = 200)
    model.fit(X_train, y_train)
    return model


def build_model_lgb2(X_train,y_train):
    model = lgb.LGBMClassifier(num_leaves=63,learning_rate = 0.05,n_estimators = 400)
    model.fit(X_train, y_train)
    return model

# 这里针对三个单模进行训练,其中subA_rf/lgb/nn都是可以提交的模型
# 单模没有进行调参,因此是弱分类器,效果可能不是很好。

print('predict rf ...')
model_rf = build_model_rf(X_train,y_train)
val_rf = model_rf.predict(X_val)
subA_rf = model_rf.predict(test[feats])
rf_f1_score = f1_score(y_val,val_rf,average='macro')
print(rf_f1_score)

print('predict lgb...')
model_lgb = build_model_lgb(X_train,y_train)
val_lgb = model_lgb.predict(X_val)
subA_lgb = model_lgb.predict(test[feats])
lgb_f1_score = f1_score(y_val,val_lgb,average='macro')
print(lgb_f1_score)


print('predict lgb 2...')
model_lgb2 = build_model_lgb2(X_train,y_train)
val_lgb2 = model_lgb2.predict(X_val)
subA_lgb2 = model_lgb2.predict(test[feats])
lgb2_f1_score = f1_score(y_val,val_lgb2,average='macro')
print(lgb2_f1_score)
predict rf ...
0.9094505093296462
predict lgb...
0.9277712865703224
predict lgb 2...
0.9296871289926031

voting_clf = VotingClassifier(estimators=[('rf',model_rf ),
                                          ('lgb',model_lgb),
                                          ('lgb2',model_lgb2 )],voting='hard')

voting_clf.fit(X_train,y_train)
val_voting = voting_clf.predict(X_val)
subA_voting = voting_clf.predict(test[feats])
voting_f1_score = f1_score(y_val,val_voting,average='macro')
print(voting_f1_score)
0.9285687669543128

Stacking融合

import warnings
warnings.filterwarnings('ignore')
_N_FOLDS = 5  # 采用5折交叉验证
kf = KFold(n_splits=_N_FOLDS, random_state=42,shuffle=True)  # sklearn的交叉验证模块,用于划分数据


def get_oof(clf, X_train, y_train, X_test):
    oof_train = np.zeros((X_train.shape[0], 1)) 
    oof_test_skf = np.empty((_N_FOLDS, X_test.shape[0], 1))  
    
    for i, (train_index, test_index) in enumerate(kf.split(X_train)): # 交叉验证划分此时的训练集和验证集
        kf_X_train = X_train.iloc[train_index,]
        kf_y_train = y_train.iloc[train_index,]
        kf_X_val = X_train.iloc[test_index,]
        
        clf.fit(kf_X_train, kf_y_train)
 
        oof_train[test_index] = clf.predict(kf_X_val).reshape(-1, 1) 
        oof_test_skf[i, :] = clf.predict(X_test).reshape(-1, 1)  
 
    oof_test = oof_test_skf.mean(axis=0)  # 对每一则交叉验证的结果取平均
    return oof_train, oof_test  # 返回当前分类器对训练集和测试集的预测结果

# 将你的每个分类器都调用get_oof函数,并把它们的结果合并,就得到了新的训练和测试数据new_train,new_test
new_train, new_test = [], []


model1 = RandomForestClassifier(n_estimators = 100)
model2 = lgb.LGBMClassifier(num_leaves=127,learning_rate = 0.1,n_estimators = 200)
model3 = lgb.LGBMClassifier(num_leaves=63,learning_rate = 0.05,n_estimators = 400)

for clf in [model1, model2, model3]:
    oof_train, oof_test = get_oof(clf, X_train, y_train, X_val)
    new_train.append(oof_train)
    new_test.append(oof_test)
    
new_train = np.concatenate(new_train, axis=1)
new_test = np.concatenate(new_test, axis=1)

# 用新的训练数据new_train作为新的模型的输入,stacking第二层
# 使用LogisticRegression作为第二层是为了防止模型过拟合
# 这里使用的模型还有待优化,因此模型融合效果并不是很好
clf = LogisticRegression()
clf.fit(new_train, y_train)
result = clf.predict(new_test)

stacking_f1_score = f1_score(y_val,result,average='macro')
print(stacking_f1_score)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值