阿里雲的金融風控-貸款違約預測_建模和調參


建模與調參

4.1 學習目標

  • 學習在金融分控領域常用的機器學習模型
  • 學習機器學習模型的建模過程與調參流程

4.2 內容介紹

  • 邏輯回歸模型:

    • 理解邏輯回歸模型;
    • 邏輯回歸模型的應用;
    • 邏輯回歸的優缺點;
  • 樹模型:

    • 理解樹模型;
    • 樹模型的應用;
    • 樹模型的優缺點;
  • 集成模型

    • 基於bagging思想的集成模型
      • 隨機森林模型
    • 基於boosting思想的集成模型
      • XGBoost模型
      • LightGBM模型
      • CatBoost模型
  • 模型對比與性能評估:

    • 回歸模型/樹模型/集成模型;
    • 模型評估方法;
    • 模型評價結果;
  • 模型調參:

    • 貪心調參方法;

    • 網格調參方法;

    • 貝葉斯調參方法;

4.3 模型相關原理介紹

由於相關算法原理篇幅較長,本文推薦了一些博客與教材供初學者們進行學習。

4.3.1 邏輯回歸模型

https://blog.csdn.net/han_xiaoyang/article/details/49123419

4.3.2 決策樹模型

https://blog.csdn.net/c406495762/article/details/76262487

4.3.3 GBDT模型

https://zhuanlan.zhihu.com/p/45145899

4.3.4 XGBoost模型

https://blog.csdn.net/wuzhongqiang/article/details/104854890

4.3.5 LightGBM模型

https://blog.csdn.net/wuzhongqiang/article/details/105350579

4.3.6 Catboost模型

https://mp.weixin.qq.com/s/xloTLr5NJBgBspMQtxPoFA

4.3.7 時間序列模型(選學)

RNN:https://zhuanlan.zhihu.com/p/45289691

LSTM:https://zhuanlan.zhihu.com/p/83496936

4.3.8 推薦教材:

《機器學習》 https://book.douban.com/subject/26708119/

《統計學習方法》 https://book.douban.com/subject/10590856/

《面向機器學習的特征工程》 https://book.douban.com/subject/26826639/

《信用評分模型技術與應用》https://book.douban.com/subject/1488075/

《數據化風控》https://book.douban.com/subject/30282558/

4.4 模型對比與性能評估

4.4.1 邏輯回歸

  • 優點

    • 訓練速度較快,分類的時候,計算量僅僅只和特征的數目相關;
    • 簡單易理解,模型的可解釋性非常好,從特征的權重可以看到不同的特征對最后結果的影響;
    • 適合二分類問題,不需要縮放輸入特征;
    • 內存資源占用小,只需要存儲各個維度的特征值;
  • 缺點

    • 邏輯回歸需要預先處理缺失值和異常值【可參考task3特征工程】;

    • 不能用Logistic回歸去解決非線性問題,因為Logistic的決策面是線性的;

    • 對多重共線性數據較為敏感,且很難處理數據不平衡的問題

    • 准確率並不是很高,因為形式非常簡單,很難去擬合數據的真實分布;

4.4.2 決策樹模型

  • 優點
    • 簡單直觀,生成的決策樹可以可視化展示
    • 數據不需要預處理,不需要歸一化,不需要處理缺失數據
    • 既可以處理離散值,也可以處理連續值
  • 缺點
    • 決策樹算法非常容易過擬合,導致泛化能力不強(可進行適當的剪枝)
    • 采用的是貪心算法,容易得到局部最優解

4.4.3 集成模型集成方法(ensemble method)

通過組合多個學習器來完成學習任務,通過集成方法,可以將多個弱學習器組合成一個強分類器,因此集成學習的泛化能力一般比單一分類器要好。

集成方法主要包括Bagging和Boosting,Bagging和Boosting都是將已有的分類或回歸算法通過一定方式組合起來,形成一個更加強大的分類。兩種方法都是把若干個分類器整合為一個分類器的方法,只是整合的方式不一樣,最終得到不一樣的效果。常見的基於Baggin思想的集成模型有:隨機森林、基於Boosting思想的集成模型有:Adaboost、GBDT、XgBoost、LightGBM等。

Baggin和Boosting的區別總結如下:

  • 樣本選擇上 Bagging方法的訓練集是從原始集中有放回的選取,所以從原始集中選出的各輪訓練集之間是獨立的;而Boosting方法需要每一輪的訓練集不變,只是訓練集中每個樣本在分類器中的權重發生變化。而權值是根據上一輪的分類結果進行調整
  • 樣例權重上: Bagging方法使用均勻取樣,所以每個樣本的權重相等;而Boosting方法根據錯誤率不斷調整樣本的權值,錯誤率越大則權重越大
  • 預測函數上: Bagging方法中所有預測函數的權重相等;而Boosting方法中每個弱分類器都有相應的權重,對於分類誤差小的分類器會有更大的權重
  • 並行計算上 Bagging方法中各個預測函數可以並行生成;而Boosting方法各個預測函數只能順序生成,因為后一個模型參數需要前一輪模型的結果

4.4.4 模型評估方法

對於模型來說,其在訓練集上面的誤差我們稱之為訓練誤差或者經驗誤差,而在測試集上的誤差稱之為測試誤差

對於我們來說,我們更關心的是模型對於新樣本的學習能力,即我們希望通過對已有樣本的學習,盡可能的將所有潛在樣本的普遍規律學到手,而如果模型對訓練樣本學的太好,則有可能把訓練樣本自身所具有的一些特點當做所有潛在樣本的普遍特點,這時候我們就會出現過擬合的問題。

因此我們通常將已有的數據集划分為訓練集和測試集兩部分,其中訓練集用來訓練模型,而測試集則是用來評估模型對於新樣本的判別能力。

對於數據集的划分,我們通常要保證滿足以下兩個條件:

  • 訓練集和測試集的分布要與樣本真實分布一致,即訓練集和測試集都要保證是從樣本真實分布中獨立同分布采樣而得;
  • 訓練集和測試集要互斥

對於數據集的划分有三種方法:留出法,交叉驗證法和自助法,下面挨個介紹:

  • ①留出法

    留出法是直接將數據集D划分為兩個互斥的集合,其中一個集合作為訓練集S,另一個作為測試集T。需要注意的是在划分的時候要盡可能保證數據分布的一致性,即避免因數據划分過程引入額外的偏差而對最終結果產生影響。為了保證數據分布的一致性,通常我們采用分層采樣的方式來對數據進行采樣。

    Tips: 通常,會將數據集D中大約2/3~4/5的樣本作為訓練集,其余的作為測試集。

  • ②交叉驗證法

    k折交叉驗證通常將數據集D分為k份,其中k-1份作為訓練集,剩余的一份作為測試集,這樣就可以獲得k組訓練/測試集,可以進行k次訓練與測試,最終返回的是k個測試結果的均值。交叉驗證中數據集的划分依然是依據分層采樣的方式來進行。

    對於交叉驗證法,其k值的選取往往決定了評估結果的穩定性和保真性,通常k值選取10。

    當k=1的時候,我們稱之為留一法

  • ③自助法

    我們每次從數據集D中取一個樣本作為訓練集中的元素,然后把該樣本放回,重復該行為m次,這樣我們就可以得到大小為m的訓練集,在這里面有的樣本重復出現,有的樣本則沒有出現過,我們把那些沒有出現過的樣本作為測試集。

    進行這樣采樣的原因是因為在D中約有36.8%的數據沒有在訓練集中出現過。留出法與交叉驗證法都是使用分層采樣的方式進行數據采樣與划分,而自助法則是使用有放回重復采樣的方式進行數據采樣

數據集划分總結

  • 對於數據量充足的時候,通常采用留出法或者k折交叉驗證法來進行訓練/測試集的划分
  • 對於數據集小且難以有效划分訓練/測試集時使用自助法;
  • 對於數據集小且可有效划分的時候最好使用留一法來進行划分,因為這種方法最為准確

4.4.5 模型評價標准

對於本次比賽,我們選用auc作為模型評價標准,類似的評價標准還有ks、f1-score等,具體介紹與實現大家可以回顧下task1中的內容。

一起來看一下auc到底是什么?

在邏輯回歸里面,對於正負例的界定,通常會設一個閾值,大於閾值的為正類,小於閾值為負類。如果我們減小這個閥值,更多的樣本會被識別為正類,提高正類的識別率,但同時也會使得更多的負類被錯誤識別為正類。為了直觀表示這一現象,引入ROC。

根據分類結果計算得到ROC空間中相應的點,連接這些點就形成ROC curve,橫坐標為False Positive Rate(FPR:假正率),縱坐標為True Positive Rate(TPR:真正率)。 一般情況下,這個曲線都應該處於(0,0)和(1,1)連線的上方,如圖:

ROC&AUC.png

ROC曲線中的四個點:

  • 點(0,1):即FPR=0, TPR=1,意味着FN=0且FP=0,將所有的樣本都正確分類
  • 點(1,0):即FPR=1,TPR=0,最差分類器,避開了所有正確答案
  • 點(0,0):即FPR=TPR=0,FP=TP=0,分類器把每個實例都預測為負類
  • 點(1,1):分類器把每個實例都預測為正類

總之:ROC曲線越接近左上角,該分類器的性能越好,其泛化性能就越好。而且一般來說,如果ROC是光滑的,那么基本可以判斷沒有太大的overfitting

但是對於兩個模型,我們如何判斷哪個模型的泛化性能更優呢?這里我們有主要以下兩種方法:

如果模型A的ROC曲線完全包住了模型B的ROC曲線,那么我們就認為模型A要優於模型B;

如果兩條曲線有交叉的話,我們就通過比較ROC與X,Y軸所圍得曲線的面積來判斷,面積越大,模型的性能就越優,這個面積我們稱之為AUC(area under ROC curve)

4.5 代碼示例

4.5.1 導入相關關和相關設置

import pandas as pd
import numpy as np
import warnings
import os
import seaborn as sns
import matplotlib.pyplot as plt
"""
sns 相關設置
@return:
"""
# 聲明使用 Seaborn 樣式
sns.set()
# 有五種seaborn的繪圖風格,它們分別是:darkgrid, whitegrid, dark, white, ticks。默認的主題是darkgrid。
sns.set_style("whitegrid")
# 有四個預置的環境,按大小從小到大排列分別為:paper, notebook, talk, poster。其中,notebook是默認的。
sns.set_context('talk')
# 中文字體設置-黑體
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解決保存圖像是負號'-'顯示為方塊的問題
plt.rcParams['axes.unicode_minus'] = False
# 解決Seaborn中文顯示問題並調整字體大小
sns.set(font='SimHei')

4.5.2 讀取數據

reduce_mem_usage 函數通過調整數據類型,幫助我們減少數據在內存中占用的空間

def reduce_mem_usage(df):
    start_mem = df.memory_usage().sum() # memory_usage()返回的是每列內存的使用情況
    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() 
    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

# 讀取數據
data = pd.read_csv('dataset/data_for_model.csv')
data = reduce_mem_usage(data)

4.5.3 簡單建模

Tips1:金融風控的實際項目多涉及到信用評分,因此需要模型特征具有較好的可解釋性,所以目前在實際項目中多還是以邏輯回歸作為基礎模型。但是在比賽中以得分高低為准,不需要嚴謹的可解釋性,所以大多基於集成算法進行建模。

Tips2:因為邏輯回歸的算法特性,需要提前對異常值、缺失值數據進行處理【參考task3部分】

Tips3:基於樹模型的算法特性,異常值、缺失值處理可以跳過,但是對於業務較為了解的同學也可以自己對缺失異常值進行處理,效果可能會更優於模型處理的結果。

注:以下建模的源數據參考baseline進行了相應的特征工程,對於異常缺失值未進行相應的處理操作。

建模之前的預操作

from sklearn.model_selection import KFold
# 分離數據集,方便進行交叉驗證
X_train = data.loc[data['sample']=='train', :].drop(['id','issueDate','isDefault', 'sample'], axis=1)
X_test = data.loc[data['sample']=='test', :].drop(['id','issueDate','isDefault', 'sample'], axis=1)
y_train = data.loc[data['sample']=='train', 'isDefault']

# 5折交叉驗證
folds = 5
seed = 2020
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)

使用Lightgbm進行建模

"""對訓練集數據進行划分,分成訓練集和驗證集,並進行相應的操作"""
from sklearn.model_selection import train_test_split
import lightgbm as lgb
# 數據集划分
X_train_split, X_val, y_train_split, y_val = train_test_split(X_train, y_train, test_size=0.2)
train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
valid_matrix = lgb.Dataset(X_val, label=y_val)

params = {
            'boosting_type': 'gbdt',
            'objective': 'binary',
            'learning_rate': 0.1,
            'metric': 'auc',
            'min_child_weight': 1e-3,
            'num_leaves': 31,
            'max_depth': -1,
            'reg_lambda': 0,
            'reg_alpha': 0,
            'feature_fraction': 1,
            'bagging_fraction': 1,
            'bagging_freq': 0,
            'seed': 2020,
            'nthread': 8,
            'silent': True,
            'verbose': -1,
}

"""使用訓練集數據進行模型訓練"""
model = lgb.train(params, train_set=train_matrix, valid_sets=valid_matrix, num_boost_round=20000, verbose_eval=1000, early_stopping_rounds=200)
Training until validation scores don't improve for 200 rounds
Early stopping, best iteration is:
[427]	valid_0's auc: 0.724947

對驗證集進行預測

from sklearn import metrics
from sklearn.metrics import roc_auc_score

"""預測並計算roc的相關指標"""
val_pre_lgb = model.predict(X_val, num_iteration=model.best_iteration)
fpr, tpr, threshold = metrics.roc_curve(y_val, val_pre_lgb)
roc_auc = metrics.auc(fpr, tpr)
print('未調參前lightgbm單模型在驗證集上的AUC:{}'.format(roc_auc))
"""畫出roc曲線圖"""
plt.figure(figsize=(8, 8))
plt.title('Validation ROC')
plt.plot(fpr, tpr, 'b', label = 'Val AUC = %0.4f' % roc_auc)
plt.ylim(0,1)
plt.xlim(0,1)
plt.legend(loc='best')
plt.title('ROC')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
# 畫出對角線
plt.plot([0,1],[0,1],'r--')
plt.show()
未調參前lightgbm單模型在驗證集上的AUC:0.7249469360631181

更進一步的,使用5折交叉驗證進行模型性能評估

import lightgbm as lgb
"""使用lightgbm 5折交叉驗證進行建模預測"""
cv_scores = []
for i, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)):
    print('************************************ {} ************************************'.format(str(i+1)))
    X_train_split, y_train_split, X_val, y_val = X_train.iloc[train_index], y_train[train_index], X_train.iloc[valid_index], y_train[valid_index]
    
    train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
    valid_matrix = lgb.Dataset(X_val, label=y_val)

    params = {
                'boosting_type': 'gbdt',
                'objective': 'binary',
                'learning_rate': 0.1,
                'metric': 'auc',
        
                'min_child_weight': 1e-3,
                'num_leaves': 31,
                'max_depth': -1,
                'reg_lambda': 0,
                'reg_alpha': 0,
                'feature_fraction': 1,
                'bagging_fraction': 1,
                'bagging_freq': 0,
                'seed': 2020,
                'nthread': 8,
                'silent': True,
                'verbose': -1,
    }
    
    model = lgb.train(params, train_set=train_matrix, num_boost_round=20000, valid_sets=valid_matrix, verbose_eval=1000, early_stopping_rounds=200)
    val_pred = model.predict(X_val, num_iteration=model.best_iteration)
    
    cv_scores.append(roc_auc_score(y_val, val_pred))
    print(cv_scores)

print("lgb_scotrainre_list:{}".format(cv_scores))
print("lgb_score_mean:{}".format(np.mean(cv_scores)))
print("lgb_score_std:{}".format(np.std(cv_scores)))
...
lgb_scotrainre_list:[0.7303837315833632, 0.7258692125145638, 0.7305149209921737, 0.7296117869375041, 0.7294438695369077]
lgb_score_mean:0.7291647043129024
lgb_score_std:0.0016998349834934656

4.5.4 模型調參

  • 1. 貪心調參

    先使用當前對模型影響最大的參數進行調優,達到當前參數下的模型最優化,再使用對模型影響次之的參數進行調優,如此下去,直到所有的參數調整完畢。

    這個方法的缺點就是可能會調到局部最優而不是全局最優,但是只需要一步一步的進行參數最優化調試即可,容易理解。

    需要注意的是在樹模型中參數調整的順序,也就是各個參數對模型的影響程度,這里列舉一下日常調參過程中常用的參數和調參順序:

    • ①:max_depth、num_leaves
    • ②:min_data_in_leaf、min_child_weight
    • ③:bagging_fraction、 feature_fraction、bagging_freq
    • ④:reg_lambda、reg_alpha
    • ⑤:min_split_gain
from sklearn.model_selection import cross_val_score

# 調objective
best_obj = dict()
for obj in objective:
    model = LGBMRegressor(objective=obj)
    """預測並計算roc的相關指標"""
    score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
    best_obj[obj] = score
    
# num_leaves
best_leaves = dict()
for leaves in num_leaves:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0], num_leaves=leaves)
    """預測並計算roc的相關指標"""
    score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
    best_leaves[leaves] = score
    
# max_depth
best_depth = dict()
for depth in max_depth:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0],
                          num_leaves=min(best_leaves.items(), key=lambda x:x[1])[0],
                          max_depth=depth)
    """預測並計算roc的相關指標"""
    score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
    best_depth[depth] = score

"""
可依次將模型的參數通過上面的方式進行調整優化,並且通過可視化觀察在每一個最優參數下模型的得分情況
"""

可依次將模型的參數通過上面的方式進行調整優化,並且通過可視化觀察在每一個最優參數下模型的得分情況

  • 2. 網格搜索

sklearn 提供GridSearchCV用於進行網格搜索,只需要把模型的參數輸進去,就能給出最優化的結果和參數。相比起貪心調參,網格搜索的結果會更優,但是網格搜索只適合於小數據集,一旦數據的量級上去了,很難得出結果

同樣以Lightgbm算法為例,進行網格搜索調參:

"""通過網格搜索確定最優參數"""
from sklearn.model_selection import GridSearchCV

def get_best_cv_params(learning_rate=0.1, n_estimators=581, num_leaves=31, max_depth=-1, bagging_fraction=1.0, 
                       feature_fraction=1.0, bagging_freq=0, min_data_in_leaf=20, min_child_weight=0.001, 
                       min_split_gain=0, reg_lambda=0, reg_alpha=0, param_grid=None):
    # 設置5折交叉驗證
    cv_fold = StratifiedKFold(n_splits=5, random_state=0, shuffle=True, )
    
    model_lgb = lgb.LGBMClassifier(learning_rate=learning_rate,
                                   n_estimators=n_estimators,
                                   num_leaves=num_leaves,
                                   max_depth=max_depth,
                                   bagging_fraction=bagging_fraction,
                                   feature_fraction=feature_fraction,
                                   bagging_freq=bagging_freq,
                                   min_data_in_leaf=min_data_in_leaf,
                                   min_child_weight=min_child_weight,
                                   min_split_gain=min_split_gain,
                                   reg_lambda=reg_lambda,
                                   reg_alpha=reg_alpha,
                                   n_jobs= 8
                                  )
    grid_search = GridSearchCV(estimator=model_lgb, 
                               cv=cv_fold,
                               param_grid=param_grid,
                               scoring='roc_auc'
                              )
    grid_search.fit(X_train, y_train)

    print('模型當前最優參數為:{}'.format(grid_search.best_params_))
    print('模型當前最優得分為:{}'.format(grid_search.best_score_))
"""以下代碼未運行,耗時較長,請謹慎運行,且每一步的最優參數需要在下一步進行手動更新,請注意"""

"""
需要注意一下的是,除了獲取上面的獲取num_boost_round時候用的是原生的lightgbm(因為要用自帶的cv)
下面配合GridSearchCV時必須使用sklearn接口的lightgbm。
"""
"""設置n_estimators 為581,調整num_leaves和max_depth,這里選擇先粗調再細調"""
lgb_params = {'num_leaves': range(10, 80, 5), 'max_depth': range(3,10,2)}
get_best_cv_params(learning_rate=0.1, n_estimators=581, num_leaves=None, max_depth=None, min_data_in_leaf=20, 
                   min_child_weight=0.001,bagging_fraction=1.0, feature_fraction=1.0, bagging_freq=0, 
                   min_split_gain=0, reg_lambda=0, reg_alpha=0, param_grid=lgb_params)

"""num_leaves為30,max_depth為7,進一步細調num_leaves和max_depth"""
lgb_params = {'num_leaves': range(25, 35, 1), 'max_depth': range(5,9,1)}
get_best_cv_params(learning_rate=0.1, n_estimators=85, num_leaves=None, max_depth=None, min_data_in_leaf=20, 
                   min_child_weight=0.001,bagging_fraction=1.0, feature_fraction=1.0, bagging_freq=0, 
                   min_split_gain=0, reg_lambda=0, reg_alpha=0, param_grid=lgb_params)

"""
確定min_data_in_leaf為45,min_child_weight為0.001 ,下面進行bagging_fraction、feature_fraction和bagging_freq的調參
"""
lgb_params = {'bagging_fraction': [i/10 for i in range(5,10,1)], 
              'feature_fraction': [i/10 for i in range(5,10,1)],
              'bagging_freq': range(0,81,10)
             }
get_best_cv_params(learning_rate=0.1, n_estimators=85, num_leaves=29, max_depth=7, min_data_in_leaf=45, 
                   min_child_weight=0.001,bagging_fraction=None, feature_fraction=None, bagging_freq=None, 
                   min_split_gain=0, reg_lambda=0, reg_alpha=0, param_grid=lgb_params)

"""
確定bagging_fraction為0.4、feature_fraction為0.6、bagging_freq為 ,下面進行reg_lambda、reg_alpha的調參
"""
lgb_params = {'reg_lambda': [0,0.001,0.01,0.03,0.08,0.3,0.5], 'reg_alpha': [0,0.001,0.01,0.03,0.08,0.3,0.5]}
get_best_cv_params(learning_rate=0.1, n_estimators=85, num_leaves=29, max_depth=7, min_data_in_leaf=45, 
                   min_child_weight=0.001,bagging_fraction=0.9, feature_fraction=0.9, bagging_freq=40, 
                   min_split_gain=0, reg_lambda=None, reg_alpha=None, param_grid=lgb_params)

"""
確定reg_lambda、reg_alpha都為0,下面進行min_split_gain的調參
"""
lgb_params = {'min_split_gain': [i/10 for i in range(0,11,1)]}
get_best_cv_params(learning_rate=0.1, n_estimators=85, num_leaves=29, max_depth=7, min_data_in_leaf=45, 
                   min_child_weight=0.001,bagging_fraction=0.9, feature_fraction=0.9, bagging_freq=40, 
                   min_split_gain=None, reg_lambda=0, reg_alpha=0, param_grid=lgb_params)

 

"""
參數確定好了以后,我們設置一個比較小的learning_rate 0.005,來確定最終的num_boost_round
"""
# 設置5折交叉驗證
# cv_fold = StratifiedKFold(n_splits=5, random_state=0, shuffle=True, )
final_params = {
                'boosting_type': 'gbdt',
                'learning_rate': 0.01,
                'num_leaves': 29,
                'max_depth': 7,
                'min_data_in_leaf':45,
                'min_child_weight':0.001,
                'bagging_fraction': 0.9,
                'feature_fraction': 0.9,
                'bagging_freq': 40,
                'min_split_gain': 0,
                'reg_lambda':0,
                'reg_alpha':0,
                'nthread': 6
               }

cv_result = lgb.cv(train_set=lgb_train,
                   early_stopping_rounds=20,
                   num_boost_round=5000,
                   nfold=5,
                   stratified=True,
                   shuffle=True,
                   params=final_params,
                   metrics='auc',
                   seed=0,
                  )

print('迭代次數{}'.format(len(cv_result['auc-mean'])))
print('交叉驗證的AUC為{}'.format(max(cv_result['auc-mean'])))

在實際調整過程中,可先設置一個較大的學習率(上面的例子中0.1),通過Lgb原生的cv函數進行樹個數的確定,之后再通過上面的實例代碼進行參數的調整優化。

最后針對最優的參數設置一個較小的學習率(例如0.05),同樣通過cv函數確定樹的個數,確定最終的參數。

需要注意的是,針對大數據集,上面每一層參數的調整都需要耗費較長時間,

  • 貝葉斯調參

    在使用之前需要先安裝包bayesian-optimization,運行如下命令即可:

pip install bayesian-optimization

貝葉斯調參的主要思想是:給定優化的目標函數(廣義的函數,只需指定輸入和輸出即可,無需知道內部結構以及數學性質),通過不斷地添加樣本點來更新目標函數的后驗分布(高斯過程,直到后驗分布基本貼合於真實分布)。簡單的說,就是考慮了上一次參數的信息,從而更好的調整當前的參數。

貝葉斯調參的步驟如下:

  • 定義優化函數(rf_cv)
  • 建立模型
  • 定義待優化的參數
  • 得到優化結果,並返回要優化的分數指標
from sklearn.model_selection import cross_val_score

"""定義優化函數"""
def rf_cv_lgb(num_leaves, max_depth, bagging_fraction, feature_fraction, bagging_freq, min_data_in_leaf, 
              min_child_weight, min_split_gain, reg_lambda, reg_alpha):
    # 建立模型
    model_lgb = lgb.LGBMClassifier(boosting_type='gbdt', bjective='binary', metric='auc',
                                   learning_rate=0.1, n_estimators=5000,
                                   num_leaves=int(num_leaves), max_depth=int(max_depth), 
                                   bagging_fraction=round(bagging_fraction, 2), feature_fraction=round(feature_fraction, 2),
                                   bagging_freq=int(bagging_freq), min_data_in_leaf=int(min_data_in_leaf),
                                   min_child_weight=min_child_weight, min_split_gain=min_split_gain,
                                   reg_lambda=reg_lambda, reg_alpha=reg_alpha,
                                   n_jobs= 8
                                  )
    
    val = cross_val_score(model_lgb, X_train_split, y_train_split, cv=5, scoring='roc_auc').mean()
    
    return val
from bayes_opt import BayesianOptimization
"""定義優化參數"""
bayes_lgb = BayesianOptimization(
    rf_cv_lgb, 
    {
        'num_leaves':(10, 200),
        'max_depth':(3, 20),
        'bagging_fraction':(0.5, 1.0),
        'feature_fraction':(0.5, 1.0),
        'bagging_freq':(0, 100),
        'min_data_in_leaf':(10,100),
        'min_child_weight':(0, 10),
        'min_split_gain':(0.0, 1.0),
        'reg_alpha':(0.0, 10),
        'reg_lambda':(0.0, 10),
    }
)

"""開始優化"""
bayes_lgb.maximize(n_iter=10)
"""顯示優化結果"""
bayes_lgb.max
{'target': 0.7282530196283977,
 'params': {'bagging_fraction': 0.9815471914843896,
  'bagging_freq': 96.14757648686668,
  'feature_fraction': 0.6961281791730929,
  'max_depth': 19.45450235568963,
  'min_child_weight': 1.6266132496156782,
  'min_data_in_leaf': 37.697878831472295,
  'min_split_gain': 0.4184947943942168,
  'num_leaves': 14.221122487200399,
  'reg_alpha': 7.056502173310882,
  'reg_lambda': 9.924023764203156}}

參數優化完成后,我們可以根據優化后的參數建立新的模型,降低學習率並尋找最優模型迭代次數

"""調整一個較小的學習率,並通過cv函數確定當前最優的迭代次數"""
base_params_lgb = {
                    'boosting_type': 'gbdt',
                    'objective': 'binary',
                    'metric': 'auc',
                    'learning_rate': 0.01,
                    'num_leaves': 14,
                    'max_depth': 19,
                    'min_data_in_leaf': 37,
                    'min_child_weight':1.6,
                    'bagging_fraction': 0.98,
                    'feature_fraction': 0.69,
                    'bagging_freq': 96,
                    'reg_lambda': 9,
                    'reg_alpha': 7,
                    'min_split_gain': 0.4,
                    'nthread': 8,
                    'seed': 2020,
                    'silent': True,
                    'verbose': -1,
}

cv_result_lgb = lgb.cv(
    train_set=train_matrix,
    early_stopping_rounds=1000, 
    num_boost_round=20000,
    nfold=5,
    stratified=True,
    shuffle=True,
    params=base_params_lgb,
    metrics='auc',
    seed=0
)

print('迭代次數{}'.format(len(cv_result_lgb['auc-mean'])))
print('最終模型的AUC為{}'.format(max(cv_result_lgb['auc-mean'])))

 

迭代次數14269
最終模型的AUC為0.7315032037635779

模型參數已經確定,建立最終模型並對驗證集進行驗證

import lightgbm as lgb
"""使用lightgbm 5折交叉驗證進行建模預測"""
cv_scores = []
for i, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)):
    print('************************************ {} ************************************'.format(str(i+1)))
    X_train_split, y_train_split, X_val, y_val = X_train.iloc[train_index], y_train[train_index], X_train.iloc[valid_index], y_train[valid_index]
    
    train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
    valid_matrix = lgb.Dataset(X_val, label=y_val)

    params = {
                'boosting_type': 'gbdt',
                'objective': 'binary',
                'metric': 'auc',
                'learning_rate': 0.01,
                'num_leaves': 14,
                'max_depth': 19,
                'min_data_in_leaf': 37,
                'min_child_weight':1.6,
                'bagging_fraction': 0.98,
                'feature_fraction': 0.69,
                'bagging_freq': 96,
                'reg_lambda': 9,
                'reg_alpha': 7,
                'min_split_gain': 0.4,
                'nthread': 8,
                'seed': 2020,
                'silent': True,
    }
    
    model = lgb.train(params, train_set=train_matrix, num_boost_round=14269, valid_sets=valid_matrix, verbose_eval=1000, early_stopping_rounds=200)
    val_pred = model.predict(X_val, num_iteration=model.best_iteration)
    
    cv_scores.append(roc_auc_score(y_val, val_pred))
    print(cv_scores)

print("lgb_scotrainre_list:{}".format(cv_scores))
print("lgb_score_mean:{}".format(np.mean(cv_scores)))
print("lgb_score_std:{}".format(np.std(cv_scores)))

 

...
lgb_scotrainre_list:[0.7329726464187137, 0.7294292852806246, 0.7341505801564857, 0.7328331383185244, 0.7317405262608612]
lgb_score_mean:0.732225235287042
lgb_score_std:0.0015929470575114753

通過5折交叉驗證可以發現,模型迭代次數在13000次的時候會停之,那么我們在建立新模型時直接設置最大迭代次數,並使用驗證集進行模型預測

""""""
base_params_lgb = {
                    'boosting_type': 'gbdt',
                    'objective': 'binary',
                    'metric': 'auc',
                    'learning_rate': 0.01,
                    'num_leaves': 14,
                    'max_depth': 19,
                    'min_data_in_leaf': 37,
                    'min_child_weight':1.6,
                    'bagging_fraction': 0.98,
                    'feature_fraction': 0.69,
                    'bagging_freq': 96,
                    'reg_lambda': 9,
                    'reg_alpha': 7,
                    'min_split_gain': 0.4,
                    'nthread': 8,
                    'seed': 2020,
                    'silent': True,
}

"""使用訓練集數據進行模型訓練"""
final_model_lgb = lgb.train(base_params_lgb, train_set=train_matrix, valid_sets=valid_matrix, num_boost_round=13000, verbose_eval=1000, early_stopping_rounds=200)

"""預測並計算roc的相關指標"""
val_pre_lgb = final_model_lgb.predict(X_val)
fpr, tpr, threshold = metrics.roc_curve(y_val, val_pre_lgb)
roc_auc = metrics.auc(fpr, tpr)
print('調參后lightgbm單模型在驗證集上的AUC:{}'.format(roc_auc))
"""畫出roc曲線圖"""
plt.figure(figsize=(8, 8))
plt.title('Validation ROC')
plt.plot(fpr, tpr, 'b', label = 'Val AUC = %0.4f' % roc_auc)
plt.ylim(0,1)
plt.xlim(0,1)
plt.legend(loc='best')
plt.title('ROC')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
# 畫出對角線
plt.plot([0,1],[0,1],'r--')
plt.show()
Training until validation scores don't improve for 200 rounds
[1000]	valid_0's auc: 0.723676
[2000]	valid_0's auc: 0.727282
[3000]	valid_0's auc: 0.728593
[4000]	valid_0's auc: 0.729493
[5000]	valid_0's auc: 0.730087
[6000]	valid_0's auc: 0.730515
[7000]	valid_0's auc: 0.730872
[8000]	valid_0's auc: 0.731121
[9000]	valid_0's auc: 0.731351
[10000]	valid_0's auc: 0.731502
[11000]	valid_0's auc: 0.731707
Early stopping, best iteration is:
[11192]	valid_0's auc: 0.731741
調參后lightgbm單模型在驗證集上的AUC:0.7317405262608612

可以看到相比最早的原始參數,模型的性能還是有提升的

"""保存模型到本地"""
# 保存模型
import pickle
pickle.dump(final_model_lgb, open('dataset/model_lgb_best.pkl', 'wb'))

模型調參小總結

  • 集成模型內置的cv函數可以較快的進行單一參數的調節,一般可以用來優先確定樹模型的迭代次數

  • 數據量較大的時候(例如本次項目的數據),網格搜索調參會特別特別慢,不建議嘗試

  • 集成模型中原生庫和sklearn下的庫部分參數不一致,需要注意,具體可以參考xgb和lgb的官方API

    xgb原生庫APIsklearn庫下xgbAPI

    lgb原生庫API, sklearn庫下lgbAPI

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM