模型融合與超參數優化

模型融合

一般來說,通過融合多個不同的模型,可能提升機器學習的性能,這一方法在各種機器學習比賽中廣泛應用, 常見的集成學習&模型融合方法包括:簡單的Voting/Averaging(分別對於分類和迴歸問題)、Stacking、Boosting和Bagging。

1 Voting

模型融合其實也沒有想象的那麼高大上,從最簡單的Voting說起,這也可以說是一種模型融合。假設對於一個二分類問題,有3個基礎模型,那麼就採取投票制的方法,投票多者確定爲最終的分類。

2 Averaging

對於迴歸問題,一個簡單直接的思路是取平均。稍稍改進的方法是進行加權平均。
權值可以用排序的方法確定,舉個例子,比如A、B、C三種基本模型,模型效果進行排名,假設排名分別是1,2,3,那麼給這三個模型賦予的權值分別是3/6、2/6、1/6。

注意兩個問題

  • 如果進行投票的模型越多,那麼顯然其結果將會更好。但是其前提條件是模型之間相互獨立,結果之間沒有相關性。越相近的模型進行融合,融合效果也會越差。 模型之間差異越大,融合所得的結果將會更好。這種特性不會受融合方式的影響。注意這裏所指模型之間的差異,並不是指正確率的差異,而是指模型之間相關性的差異。對於迴歸問題,對各種模型的預測結果進行平均,所得到的結果通過能夠減少過擬合,並使得邊界更加平滑,單個模型的邊界可能很粗糙。
  • 在上述融合方法的基礎上,一個進行改良的方式是對各個投票者/平均者分配不同的權重以改變其對最終結果影響的大小。對於正確率低的模型給予更低的權重,而正確率更高的模型給予更高的權重。

3 Bagging

Bagging就是採用有放回的方式進行抽樣,用抽樣的樣本建立子模型,對子模型進行訓練,這個過程重複多次,最後進行融合。大概分爲這樣兩步:

  1. 重複K次
    有放回地重複抽樣建模
    訓練子模型

  2. 模型融合
    分類問題:voting
    迴歸問題:average

Bagging算法不用我們自己實現,隨機森林就是基於Bagging算法的一個典型例子,採用的基分類器是決策樹。

4 Boosting

Bagging算法可以並行處理,而Boosting的思想是一種迭代的方法,每一次訓練的時候都更加關心分類錯誤的樣例,給這些分類錯誤的樣例增加更大的權重,下一次迭代的目標就是能夠更容易辨別出上一輪分類錯誤的樣例。最終將這些弱分類器進行加權相加。

其基本工作機制如下:

    1、從初始樣本集中訓練出一個基學習器;

    2、根據基學習器的表現對樣本集分佈進行調整,使得做錯的樣本能在之後的過程中受到更多的關注;

    3、用調整後的樣本集訓練下一個基學習器;

    4、重複上述步驟,直到滿足一定條件。

注意,一般只有弱分類器都是同一種分類器(即同質集成)的時候,纔將弱分類器稱爲基學習器,如果是異質集成,則稱之爲個體學習器。由於不是本文重點,所以此處不作區分。特此說明。

最終將這些弱分類器進行加權相加。


常見的Boosting方法有Adaboost、GBDT、XGBOOST等

5 Stacking 算法

1、首先我們將訓練集分爲五份。

2、對於每一個基模型來說,我們用其中的四份來訓練,然後對未用來的訓練的一份訓練集和測試集進行預測。然後改變所選的用來訓練的訓練集和用來驗證的訓練集,重複此步驟,直到獲得完整的訓練集的預測結果。

3、對五個模型,分別進行步驟2,我們將獲得5個模型,以及五個模型分別通過交叉驗證獲得的訓練集預測結果。即P1、P2、P3、P4、P5。

4、用五個模型分別對測試集進行預測,得到測試集的預測結果:T1、T2、T3、T4、T5。

5、將P15、T15作爲下一層的訓練集和測試集。在圖中分別作爲了模型6的訓練集和測試集。

代碼實例如下:

""" 
@author: quincy qiang 
@license: Apache Licence 
@file: 04_stakcing_template.py 
@time: 2019/12/12
@software: PyCharm 
"""

import numpy as np
import pandas as pd
import lightgbm as lgb
import xgboost as xgb
from sklearn.linear_model import BayesianRidge
from sklearn.model_selection import KFold, RepeatedKFold
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from scipy import sparse
import warnings
import time
import sys
import os
import re
import datetime
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.offline as py

py.init_notebook_mode(connected=True)
import plotly.graph_objs as go
import plotly.tools as tls
from sklearn.metrics import mean_squared_error
from sklearn.metrics import log_loss
from gen_feas import load_data

train, test, no_features, features = load_data()

X_train = train[features].values
y_train = train['target']
target = y_train
X_test = test[features].values

## lgb
param = {
    'boosting_type': 'gbdt',
    'objective': 'binary',
    'metric': {'auc'},
    'max_depth': 4,
    'min_child_weight': 6,
    'num_leaves': 16,
    'learning_rate': 0.02,  # 0.05
    'feature_fraction': 0.7,
    'bagging_fraction': 0.7,
    'bagging_freq': 5,
    'verbose': -1
}

folds = KFold(n_splits=5, shuffle=True, random_state=2018)
oof_lgb = np.zeros(len(train))
predictions_lgb = np.zeros(len(test))

for fold_, (trn_idx, val_idx) in enumerate(folds.split(X_train, y_train)):
    print("fold n°{}".format(fold_ + 1))
    trn_data = lgb.Dataset(X_train[trn_idx], y_train[trn_idx])
    val_data = lgb.Dataset(X_train[val_idx], y_train[val_idx])

    num_round = 10000
    clf = lgb.train(param, trn_data, num_round, valid_sets=[trn_data, val_data], verbose_eval=200,
                    early_stopping_rounds=100)
    oof_lgb[val_idx] = clf.predict(X_train[val_idx], num_iteration=clf.best_iteration)

    predictions_lgb += clf.predict(X_test, num_iteration=clf.best_iteration) / folds.n_splits

print("CV score: {:<8.8f}".format(mean_squared_error(oof_lgb, target)))

#### xgb
xgb_params = {'booster': 'gbtree', 'objective': 'binary:logistic', 'eta': 0.02, 'max_depth': 4, 'min_child_weight': 6,
              'colsample_bytree': 0.7, 'subsample': 0.7, 'silent': 1, 'eval_metric': ['auc']}

folds = KFold(n_splits=5, shuffle=True, random_state=2018)
oof_xgb = np.zeros(len(train))
predictions_xgb = np.zeros(len(test))

for fold_, (trn_idx, val_idx) in enumerate(folds.split(X_train, y_train)):
    print("fold n°{}".format(fold_ + 1))
    trn_data = xgb.DMatrix(X_train[trn_idx], y_train[trn_idx])
    val_data = xgb.DMatrix(X_train[val_idx], y_train[val_idx])

    watchlist = [(trn_data, 'train'), (val_data, 'valid_data')]
    clf = xgb.train(dtrain=trn_data, num_boost_round=20000, evals=watchlist, early_stopping_rounds=200,
                    verbose_eval=100, params=xgb_params)
    oof_xgb[val_idx] = clf.predict(xgb.DMatrix(X_train[val_idx]), ntree_limit=clf.best_ntree_limit)
    predictions_xgb += clf.predict(xgb.DMatrix(X_test), ntree_limit=clf.best_ntree_limit) / folds.n_splits

print("CV score: {:<8.8f}".format(mean_squared_error(oof_xgb, target)))

# 將lgb和xgb的結果進行stacking
train_stack = np.vstack([oof_lgb, oof_xgb]).transpose()
test_stack = np.vstack([predictions_lgb, predictions_xgb]).transpose()

folds_stack = RepeatedKFold(n_splits=5, n_repeats=2, random_state=4590)
oof_stack = np.zeros(train_stack.shape[0])
predictions = np.zeros(test_stack.shape[0])

for fold_, (trn_idx, val_idx) in enumerate(folds_stack.split(train_stack, target)):
    print("fold {}".format(fold_))
    trn_data, trn_y = train_stack[trn_idx], target.iloc[trn_idx].values
    val_data, val_y = train_stack[val_idx], target.iloc[val_idx].values

    clf_3 = BayesianRidge()
    clf_3.fit(trn_data, trn_y)

    oof_stack[val_idx] = clf_3.predict(val_data)
    predictions += clf_3.predict(test_stack) / 10

mean_squared_error(target.values, oof_stack)

from pandas import DataFrame

result = DataFrame()
result['id'] = test['id']
result['target'] = predictions
result.to_csv('result/stacking.csv', index=False, sep=",", float_format='%.6f')

6 Blending

第一步:將原始訓練數據劃分爲訓練集和驗證集。
第二步:使用訓練集對訓練T個不同的模型。
第三步:使用T個基模型,對驗證集進行預測,結果作爲新的訓練數據。
第四步:使用新的訓練數據,訓練一個元模型。
第五步:使用T個基模型,對測試數據進行預測,結果作爲新的測試數據。
第六步:使用元模型對新的測試數據進行預測,得到最終結果。


超參數優化

推薦兩個工具:Optuna和BayesianOptimization

推薦1:Optuna

import numpy as np
import optuna

import lightgbm as lgb
import sklearn.datasets
import sklearn.metrics
from sklearn.model_selection import train_test_split


# FYI: Objective functions can take additional arguments
# (https://optuna.readthedocs.io/en/stable/faq.html#objective-func-additional-args).
def objective(trial):
    data, target = sklearn.datasets.load_breast_cancer(return_X_y=True)
    train_x, valid_x, train_y, valid_y = train_test_split(data, target, test_size=0.25)
    dtrain = lgb.Dataset(train_x, label=train_y)

    param = {
        "objective": "binary",
        "metric": "binary_logloss",
        "verbosity": -1,
        "boosting_type": "gbdt",
        "lambda_l1": trial.suggest_float("lambda_l1", 1e-8, 10.0, log=True),
        "lambda_l2": trial.suggest_float("lambda_l2", 1e-8, 10.0, log=True),
        "num_leaves": trial.suggest_int("num_leaves", 2, 256),
        "feature_fraction": trial.suggest_float("feature_fraction", 0.4, 1.0),
        "bagging_fraction": trial.suggest_float("bagging_fraction", 0.4, 1.0),
        "bagging_freq": trial.suggest_int("bagging_freq", 1, 7),
        "min_child_samples": trial.suggest_int("min_child_samples", 5, 100),
    }

    gbm = lgb.train(param, dtrain)
    preds = gbm.predict(valid_x)
    pred_labels = np.rint(preds)
    accuracy = sklearn.metrics.accuracy_score(valid_y, pred_labels)
    return accuracy


if __name__ == "__main__":
    study = optuna.create_study(direction="maximize")
    study.optimize(objective, n_trials=100)

    print("Number of finished trials: {}".format(len(study.trials)))

    print("Best trial:")
    trial = study.best_trial

    print("  Value: {}".format(trial.value))

    print("  Params: ")
    for key, value in trial.params.items():
        print("    {}: {}".format(key, value))

推薦2 BayesianOptimization

數據來源:https://www.kaggle.com/c/home-credit-default-risk

import pandas as pd
import numpy as np
import warnings
import time
warnings.filterwarnings("ignore")
import lightgbm as lgb
from bayes_opt import BayesianOptimization
from sklearn.metrics import roc_auc_score



application_train = pd.read_csv('../input/application_train.csv')
from sklearn.preprocessing import LabelEncoder
def label_encoder(input_df, encoder_dict=None):
    """ Process a dataframe into a form useable by LightGBM """
    # Label encode categoricals
    categorical_feats = input_df.columns[input_df.dtypes == 'object']
    for feat in categorical_feats:
        encoder = LabelEncoder()
        input_df[feat] = encoder.fit_transform(input_df[feat].fillna('NULL'))
    return input_df, categorical_feats.tolist(), encoder_dict
application_train, categorical_feats, encoder_dict = label_encoder(application_train)
X = application_train.drop('TARGET', axis=1)
y = application_train.TARGET

# 第一步:設置需要優化的參數
def lgb_eval(num_leaves, feature_fraction, bagging_fraction, max_depth, lambda_l1, lambda_l2, min_split_gain, min_child_weight):
    params = {'application':'binary','num_iterations':4000, 'learning_rate':0.05, 'early_stopping_round':100, 'metric':'auc'}
    params["num_leaves"] = round(num_leaves)
    params['feature_fraction'] = max(min(feature_fraction, 1), 0)
    params['bagging_fraction'] = max(min(bagging_fraction, 1), 0)
    params['max_depth'] = round(max_depth)
    params['lambda_l1'] = max(lambda_l1, 0)
    params['lambda_l2'] = max(lambda_l2, 0)
    params['min_split_gain'] = min_split_gain
    params['min_child_weight'] = min_child_weight
    cv_result = lgb.cv(params, train_data, nfold=n_folds, seed=random_seed, stratified=True, verbose_eval =200, metrics=['auc'])
    return max(cv_result['auc-mean'])


# 第二步:設置超參數搜索範圍

lgbBO = BayesianOptimization(lgb_eval, {'num_leaves': (24, 45),
                                        'feature_fraction': (0.1, 0.9),
                                        'bagging_fraction': (0.8, 1),
                                        'max_depth': (5, 8.99),
                                        'lambda_l1': (0, 5),
                                        'lambda_l2': (0, 3),
                                        'min_split_gain': (0.001, 0.1),
                                        'min_child_weight': (5, 50)}, random_state=0)


# 第三步:設置優化目標

# lgbBO.maximize(init_points=init_round, n_iter=opt_round)


# 第四步:獲取最優參數
# lgbBO.res['max']['max_params']

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章