1. 程式人生 > 其它 >機器學習實戰 | LightGBM建模應用詳解

機器學習實戰 | LightGBM建模應用詳解

作者:韓信子@ShowMeAI
教程地址http://www.showmeai.tech/tutorials/41
本文地址http://www.showmeai.tech/article-detail/205
宣告:版權所有,轉載請聯絡平臺與作者並註明出處
收藏ShowMeAI檢視更多精彩內容


引言

LightGBM是微軟開發的boosting整合模型,和XGBoost一樣是對GBDT的優化和高效實現,原理有一些相似之處,但它很多方面比XGBoost有著更為優秀的表現。

本篇內容ShowMeAI展開給大家講解LightGBM的工程應用方法,對於LightGBM原理知識感興趣的同學,歡迎參考ShowMeAI的另外一篇文章

圖解機器學習 | LightGBM模型詳解

1.LightGBM安裝

LightGBM作為常見的強大Python機器學習工具庫,安裝也比較簡單。

1.1 Python與IDE環境設定

python環境與IDE設定可以參考ShowMeAI文章 圖解python | 安裝與環境設定 進行設定。

1.2 工具庫安裝

(1) Linux/Mac等系統

這些系統下的XGBoost安裝,大家只要基於pip就可以輕鬆完成了,在命令列端輸入命令如下命令即可等待安裝完成。

pip install lightgbm

大家也可以選擇國內的pip源,以獲得更好的安裝速度:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple lightgbm

(2) Windows系統

對於windows系統而言,比較高效便捷的安裝方式是:在網址http://www.lfd.uci.edu/~gohlke/pythonlibs/ 中去下載對應版本的的LightGBM安裝包,再通過如下命令安裝。
pip install lightgbm‑3.3.2‑cp310‑cp310‑win_amd64.whl

2.LightGBM引數手冊

ShowMeAI的前一篇內容 XGBoost工具庫建模應用詳解 中,我們講解到了Xgboost的三類引數通用引數,學習目標引數,Booster引數。而LightGBM可調引數更加豐富,包含核心引數,學習控制引數,IO引數,目標引數,度量引數,網路引數,GPU引數,模型引數,這裡我常修改的便是核心引數,學習控制引數,度量引數等。下面我們對這些模型引數做展開講解,更多的細節可以參考

LightGBM中文文件

2.1 引數介紹

(1) 核心引數

  • config或者config_file:一個字串,給出了配置檔案的路徑。預設為空字串。

  • task:一個字串,給出了要執行的任務。可以為:

    • train或者training:表示是訓練任務。預設為train
    • predict或者prediction或者test:表示是預測任務。
    • convert_model:表示是模型轉換任務。將模型檔案轉換成if-else格式。
  • application或者objective或者app:一個字串,表示問題型別。可以為:

    • regressionregression_l2mean_squared_errormsel2_rootroot_mean_squred_errorrmse:表示迴歸任務,但是使用L2損失函式。預設為regression
    • regression_l1或者mae或者mean_absolute_error:表示迴歸任務,但是使用L1損失函式。
    • huber:表示迴歸任務,但是使用huber損失函式。
    • fair:表示迴歸任務,但是使用fair損失函式。
    • poisson:表示Poisson迴歸任務。
    • quantile:表示quantile迴歸任務。
    • quantile_l2:表示quantile迴歸任務,但是使用了L2損失函式。
    • mape或者mean_absolute_precentage_error:表示迴歸任務,但是使用MAPE損失函式
    • gamma:表示gamma迴歸任務。
    • tweedie:表示tweedie迴歸任務。
    • binary:表示二分類任務,使用對數損失函式作為目標函式。
    • multiclass:表示多分類任務,使用softmax函式作為目標函式。必須設定num_class引數
    • multiclassova或者multiclass_ova或者ova或者ovr:表示多分類任務,使用one-vs-all的二分類目標函式。必須設定num_class引數。
    • xentropy或者cross_entropy:目標函式為交叉熵(同時具有可選擇的線性權重)。要求標籤是[0,1]之間的數值。
    • xentlambda或者cross_entropy_lambda:替代了引數化的cross_entropy。要求標籤是[0,1]之間的數值。
    • lambdarank:表示排序任務。在lambdarank任務中,標籤應該為整數型別,數值越大表示相關性越高。label_gain引數可以用於設定整數標籤的增益(權重)。
  • boosting或者boost或者boosting_type:一個字串,給出了基學習器模型演算法。可以為:

    • gbdt:表示傳統的梯度提升決策樹。預設值為gbdt
    • rf:表示隨機森林。
    • dart:表示帶dropout的gbdt。
    • goss:表示Gradient-based One-Side Sampling 的gbdt。
  • data或者train或者train_data:一個字串,給出了訓練資料所在的檔案的檔名。預設為空字串。LightGBM將使用它來訓練模型。

  • valid或者test或者valid_data或者test_data:一個字串,表示驗證集所在的檔案的檔名。預設為空字串。LightGBM將輸出該資料集的度量。如果有多個驗證集,則用逗號分隔。

  • num_iterations或者num_iteration或者num_tree或者num_trees或者num_round或者num_rounds或者num_boost_round一個整數,給出了boosting的迭代次數。預設為100。

    • 對於Python/R包,該引數是被忽略的。對於Python,使用train()/cv()的輸入引數num_boost_round來代替。
    • 在內部,LightGBM對於multiclass問題設定了num_class*num_iterations棵樹。
  • learning_rate或者shrinkage_rate:個浮點數,給出了學習率。預設為1。在dart中,它還會影響dropped trees的歸一化權重。

  • num_leaves或者num_leaf:一個整數,給出了一棵樹上的葉子數。預設為31。

  • tree_learner或者tree:一個字串,給出了tree learner,主要用於並行學習。預設為serial。可以為:

    • serial:單臺機器的tree learner
    • feature:特徵並行的tree learner
    • data:資料並行的tree learner
    • voting:投票並行的tree learner
  • num_threads或者num_thread或者nthread:一個整數,給出了LightGBM的執行緒數。預設為OpenMP_default

    • 為了更快的速度,應該將它設定為真正的CPU核心數,而不是執行緒的數量(大多數CPU使用超執行緒來使每個CPU核心生成2個執行緒)。
    • 當資料集較小的時候,不要將它設定的過大。
    • 對於並行學習,不應該使用全部的CPU核心,因為這會使得網路效能不佳。
  • device:一個字串,指定計算裝置。預設為cpu。可以為gpucpu

    • 建議使用較小的max_bin來獲得更快的計算速度。
    • 為了加快學習速度,GPU預設使用32位浮點數來求和。你可以設定gpu_use_dp=True來啟動64位浮點數,但是它會使得訓練速度降低。

(2) 學習控制引數

  • max_depth:一個整數,限制了樹模型的最大深度,預設值為-1。如果小於0,則表示沒有限制。
  • min_data_in_leaf或者min_data_per_leaf或者min_data或者min_child_samples:一個整數,表示一個葉子節點上包含的最少樣本數量。預設值為20。
  • min_sum_hessian_in_leaf或者min_sum_hessian_per_leaf或者min_sum_hessian或者min_hessian或者min_child_weight:一個浮點數,表示一個葉子節點上的最小hessian之和。(也就是葉節點樣本權重之和的最小值)預設為1e-3。
  • feature_fraction或者sub_feature或者colsample_bytree:一個浮點數,取值範圍為[0.0,1.0],預設值為0。如果小於1.0,則LightGBM會在每次迭代中隨機選擇部分特徵。如0.8表示:在每棵樹訓練之前選擇80%的特徵來訓練。
  • feature_fraction_seed:一個整數,表示feature_fraction的隨機數種子,預設為2。
  • bagging_fraction或者sub_row或者subsample:一個浮點數,取值範圍為[0.0,1.0],預設值為0。如果小於1.0,則LightGBM會在每次迭代中隨機選擇部分樣本來訓練(非重複取樣)。如0.8表示:在每棵樹訓練之前選擇80%的樣本(非重複取樣)來訓練。
  • bagging_freq或者subsample_freq:一個整數,表示每bagging_freq次執行bagging。如果該引數為0,表示禁用bagging。
  • bagging_seed或者bagging_fraction_seed:一個整數,表示bagging的隨機數種子,預設為3。
  • early_stopping_round或者early_stopping_rounds或者early_stopping:一個整數,預設為0。如果一個驗證集的度量在early_stopping_round迴圈中沒有提升,則停止訓練。如果為0則表示不開啟早停。
  • lambda_l1或者reg_alpha:一個浮點數,表示L1正則化係數。預設為0。
  • lambda_l2或者reg_lambda:一個浮點數,表示L2正則化係數。預設為0。
  • min_split_gain或者min_gain_to_split:一個浮點數,表示執行切分的最小增益,預設為0。
  • drop_rate:一個浮點數,取值範圍為[0.0,1.0],表示dropout的比例,預設為1。該引數僅在dart中使用。
  • skip_drop:一個浮點數,取值範圍為[0.0,1.0],表示跳過dropout的概率,預設為5。該引數僅在dart中使用。
  • max_drop:一個整數,表示一次迭代中刪除樹的最大數量,預設為50。如果小於等於0,則表示沒有限制。該引數僅在dart中使用。
  • uniform_drop:一個布林值,表示是否想要均勻的刪除樹,預設值為False。該引數僅在dart中使用。
  • xgboost_dart_mode:一個布林值,表示是否使用xgboost dart模式,預設值為False。該引數僅在dart中使用。
  • drop_seed:一個整數,表示dropout的隨機數種子,預設值為4。該引數僅在dart中使用。
  • top_rate:一個浮點數,取值範圍為[0.0,1.0],表示在goss中,大梯度資料的保留比例,預設值為2。該引數僅在goss中使用。
  • other_rate:一個浮點數,取值範圍為[0.0,1.0],表示在goss中,小梯度資料的保留比例,預設值為1。該引數僅在goss中使用。
  • min_data_per_group:一個整數,表示每個分類組的最小資料量,預設值為100。用於排序任務
  • max_cat_threshold:一個整數,表示category特徵的取值集合的最大大小。預設為32。
  • cat_smooth:一個浮點數,用於category特徵的概率平滑。預設值為10。它可以降低噪聲在category特徵中的影響,尤其是對於資料很少的類。
  • cat_l2:一個浮點數,用於category切分中的L2正則化係數。預設為10。
  • top_k或者topk:一個整數,用於投票並行中。預設為20。將它設定為更大的值可以獲得更精確的結果,但是會降低訓練速度。

(3) IO引數

  • max_bin:一個整數,表示最大的桶的數量。預設值為255。LightGBM會根據它來自動壓縮記憶體。如max_bin=255時,則LightGBM將使用uint8來表示特徵的每一個值。
  • min_data_in_bin:一個整數,表示每個桶的最小樣本數。預設為3。該方法可以避免出現一個桶只有一個樣本的情況。
  • data_random_seed:一個整數,表示並行學習資料分隔中的隨機數種子。預設為1它不包括特徵並行。
  • output_model或者model_output或者model_out:一個字串,表示訓練中輸出的模型被儲存的檔案的檔名。預設txt。
  • input_model或者model_input或者model_in:一個字串,表示輸入模型的檔案的檔名。預設空字串。對於prediction任務,該模型將用於預測資料,對於train任務,訓練將從該模型繼續
  • output_result或者predict_result或者prediction_result:一個字串,給出了prediction結果存放的檔名。預設為txt。
  • pre_partition或者is_pre_partition:一個布林值,指示資料是否已經被劃分。預設值為False。如果為true,則不同的機器使用不同的partition來訓練。它用於並行學習(不包括特徵並行)
  • is_sparse或者is_enable_sparse或者enable_sparse:一個布林值,表示是否開啟稀疏優化,預設為True。如果為True則啟用稀疏優化。
  • two_round或者two_round_loading或者use_two_round_loading:一個布林值,指示是否啟動兩次載入。預設值為False,表示只需要進行一次載入。預設情況下,LightGBM會將資料檔案對映到記憶體,然後從記憶體載入特徵,這將提供更快的資料載入速度。但是當資料檔案很大時,記憶體可能會被耗盡。如果資料檔案太大,則將它設定為True
  • save_binary或者is_save_binary或者is_save_binary_file:一個布林值,表示是否將資料集(包括驗證集)儲存到二進位制檔案中。預設值為False。如果為True,則可以加快資料的載入速度。
  • verbosity或者verbose:一個整數,表示是否輸出中間資訊。預設值為1。如果小於0,則僅僅輸出critical資訊;如果等於0,則還會輸出error,warning資訊;如果大於0,則還會輸出info資訊。
  • header或者has_header:一個布林值,表示輸入資料是否有頭部。預設為False。
  • label或者label_column:一個字串,表示標籤列。預設為空字串。你也可以指定一個整數,如label=0表示第0列是標籤列。你也可以為列名新增字首,如label=prefix:label_name
  • weight或者weight_column:一個字串,表示樣本權重列。預設為空字串。你也可以指定一個整數,如weight=0表示第0列是權重列。注意:它是剔除了標籤列之後的索引。假如標籤列為0,權重列為1,則這裡weight=0。你也可以為列名新增字首,如weight=prefix:weight_name
  • query或者query_column或者gourp或者group_column:一個字串,query/groupID列。預設為空字串。你也可以指定一個整數,如query=0表示第0列是query列。注意:它是剔除了標籤列之後的索引。假如標籤列為0,query列為1,則這裡query=0。你也可以為列名新增字首,如query=prefix:query_name
  • ignore_column或者ignore_feature或者blacklist:一個字串,表示訓練中忽略的一些列,預設為空字串。可以用數字做索引,如ignore_column=0,1,2表示第0,1,2列將被忽略。注意:它是剔除了標籤列之後的索引。
  • 你也可以為列名新增字首,如ignore_column=prefix:ign_name1,ign_name2
  • categorical_feature或者categorical_column或者cat_feature或者cat_column:一個字串,指定category特徵的列。預設為空字串。可以用數字做索引,如categorical_feature=0,1,2表示第0,1,2列將作為category特徵。注意:它是剔除了標籤列之後的索引。你也可以為列名新增字首,如categorical_feature=prefix:cat_name1,cat_name2在categorycal特徵中,負的取值被視作缺失值。
  • predict_raw_score或者raw_score或者is_predict_raw_score:一個布林值,表示是否預測原始得分。預設為False。如果為True則僅預測原始得分。該引數只用於prediction任務。
  • predict_leaf_index或者leaf_index或者is_predict_leaf_index:一個布林值,表示是否預測每個樣本在每棵樹上的葉節點編號。預設為False。在預測時,每個樣本都會被分配到每棵樹的某個葉子節點上。該引數就是要輸出這些葉子節點的編號。該引數只用於prediction任務。
  • predict_contrib或者contrib或者is_predict_contrib:一個布林值,表示是否輸出每個特徵對於每個樣本的預測的貢獻。預設為False。輸出的結果形狀為[nsamples,nfeatures+1],之所以+1是考慮到bais的貢獻。所有的貢獻加起來就是該樣本的預測結果。該引數只用於prediction任務。
  • bin_construct_sample_cnt或者subsample_for_bin:一個整數,表示用來構建直方圖的樣本的數量。預設為200000。如果資料非常稀疏,則可以設定為一個更大的值,如果設定更大的值,則會提供更好的訓練效果,但是會增加資料載入時間。
  • num_iteration_predict:一個整數,表示在預測中使用多少棵子樹。預設為-1。小於等於0表示使用模型的所有子樹。該引數只用於prediction任務。
  • pred_early_stop:一個布林值,表示是否使用早停來加速預測。預設為False。如果為True,則可能影響精度。
  • pred_early_stop_freq:一個整數,表示檢查早停的頻率。預設為10
  • pred_early_stop_margin:一個浮點數,表示早停的邊際閾值。預設為0
  • use_missing:一個布林值,表示是否使用缺失值功能。預設為True如果為False則禁用缺失值功能。
  • zero_as_missing:一個布林值,表示是否將所有的零(包括在libsvm/sparse矩陣中未顯示的值)都視為缺失值。預設為False。如果為False,則將nan視作缺失值。如果為True,則np.nan和零都將視作缺失值。
  • init_score_file:一個字串,表示訓練時的初始化分數檔案的路徑。預設為空字串,表示train_data_file+”.init”(如果存在)
  • valid_init_score_file:一個字串,表示驗證時的初始化分數檔案的路徑。預設為空字串,表示valid_data_file+”.init”(如果存在)。如果有多個(對應於多個驗證集),則可以用逗號,來分隔。

(4) 目標引數

  • sigmoid:一個浮點數,用sigmoid函式的引數,預設為0。它用於二分類任務和lambdarank任務。
  • alpha:一個浮點數,用於Huber損失函式和Quantileregression,預設值為0。它用於huber迴歸任務和Quantile迴歸任務。
  • fair_c:一個浮點數,用於Fair損失函式,預設值為0。它用於fair迴歸任務。
  • gaussian_eta:一個浮點數,用於控制高斯函式的寬度,預設值為0。它用於regression_l1迴歸任務和huber迴歸任務。
  • posson_max_delta_step:一個浮點數,用於Poisson regression的引數,預設值為7。它用於poisson迴歸任務。
  • scale_pos_weight:一個浮點數,用於調整正樣本的權重,預設值為0它用於二分類任務。
  • boost_from_average:一個布林值,指示是否將初始得分調整為平均值(它可以使得收斂速度更快)。預設為True。它用於迴歸任務。
  • is_unbalance或者unbalanced_set:一個布林值,指示訓練資料是否均衡的。預設為True。它用於二分類任務。
  • max_position:一個整數,指示將在這個NDCG位置優化。預設為20。它用於lambdarank任務。
  • label_gain:一個浮點數序列,給出了每個標籤的增益。預設值為0,1,3,7,15,….它用於lambdarank任務。
  • num_class或者num_classes:一個整數,指示了多分類任務中的類別數量。預設為1它用於多分類任務。
  • reg_sqrt:一個布林值,預設為False。如果為True,則擬合的結果為:\sqrt{label}。同時預測的結果被自動轉換為:{pred}^2。它用於迴歸任務。

(5) 度量引數

  • metric:一個字串,指定了度量的指標,預設為:對於迴歸問題,使用l2;對於二分類問題,使用binary_logloss;對於lambdarank問題,使用ndcg。如果有多個度量指標,則用逗號,分隔。
    • l1或者mean_absolute_error或者mae或者regression_l1:表示絕對值損失。
    • l2或者mean_squared_error或者mse或者regression_l2或者regression:表示平方損失。
    • l2_root或者root_mean_squared_error或者rmse:表示開方損失。
    • quantile:表示Quantile迴歸中的損失。
    • mape或者mean_absolute_percentage_error:表示MAPE損失。
    • huber:表示huber損失。
    • fair:表示fair損失。
    • poisson:表示poisson迴歸的負對數似然。
    • gamma:表示gamma迴歸的負對數似然。
    • gamma_deviance:表示gamma迴歸的殘差的方差。
    • tweedie:表示Tweedie迴歸的負對數似然。
    • ndcg:表示NDCG。
    • map或者mean_average_precision:表示平均的精度。
    • auc:表示AUC。
    • binary_logloss或者binary:表示二類分類中的對數損失函式。
    • binary_error:表示二類分類中的分類錯誤率。
    • multi_logloss或者multiclass或者softmax或者‘multiclassova或者multiclass_ova,或者ova或者ovr`:表示多類分類中的對數損失函式。
    • multi_error:表示多分類中的分類錯誤率。
    • xentropy或者cross_entropy:表示交叉熵。
    • xentlambda或者cross_entropy_lambda:表示intensity加權的交叉熵。
    • kldiv或者kullback_leibler:表示KL散度。
  • metric_freq或者output_freq:一個正式,表示每隔多少次輸出一次度量結果。預設為1。
  • train_metric或者training_metric或者is_training_metric:一個布林值,預設為False。如果為True,則在訓練時就輸出度量結果。
  • ndcg_at或者ndcg_eval_at或者eval_at:一個整數列表,指定了NDCG評估點的位置。預設為1、2、3、4、5。

2.2 引數影響與調參建議

以下為總結的核心引數對模型的影響,及與之對應的調參建議。

(1) 對樹生長控制

  • num_leaves:葉節點的數目。它是控制樹模型複雜度的主要引數。
    • 如果是level-wise,則該引數為\(2^{depth}\),其中depth為樹的深度。但是當葉子數量相同時,leaf-wise的樹要遠遠深過level-wise樹,非常容易導致過擬合。因此應該讓num_leaves小於\(2^{depth}\)。在leaf-wise樹中,並不存在depth的概念。因為不存在一個從leaves到depth的合理對映。
  • min_data_in_leaf:每個葉節點的最少樣本數量。
    • 它是處理leaf-wise樹的過擬合的重要引數。將它設為較大的值,可以避免生成一個過深的樹。但是也可能導致欠擬合。
  • max_depth:樹的最大深度。該引數可以顯式的限制樹的深度。

(2) 更快的訓練速度

  • 通過設定bagging_fractionbagging_freq引數來使用bagging方法。
  • 通過設定feature_fraction引數來使用特徵的子抽樣。
  • 使用較小的max_bin
  • 使用save_binary在未來的學習過程對資料載入進行加速。

(3) 更好的模型效果

  • 使用較大的max_bin(學習速度可能變慢)。
  • 使用較小的learning_rate和較大的num_iterations
  • 使用較大的num_leaves(可能導致過擬合)。
  • 使用更大的訓練資料。
  • 嘗試dart

(4) 緩解過擬合問題

  • 使用較小的max_bin
  • 使用較小的num_leaves
  • 使用min_data_in_leafmin_sum_hessian_in_leaf
  • 通過設定bagging_fractionbagging_freq來使用bagging
  • 通過設定feature_fraction來使用特徵子抽樣。
  • 使用更大的訓練資料。
  • 使用lambda_l1lambda_l2min_gain_to_split來使用正則。
  • 嘗試max_depth來避免生成過深的樹。

3.LightGBM內建建模方式

3.1 內建建模方式

LightGBM內建了建模方式,有如下的資料格式與核心訓練方法:

  • 基於lightgbm.Dataset格式的資料。
  • 基於lightgbm.train介面訓練。

下面是官方的一個簡單示例,演示了讀取libsvm格式資料(成Dataset格式)並指定引數建模的過程。

# coding: utf-8
import json
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error


# 載入資料集合
print('載入資料...')
df_train = pd.read_csv('./data/regression.train.txt', header=None, sep='\t')
df_test = pd.read_csv('./data/regression.test.txt', header=None, sep='\t')

# 設定訓練集和測試集
y_train = df_train[0].values
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

# 構建lgb中的Dataset格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 敲定好一組引數
params = {
    'task': 'train',
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': {'l2', 'auc'},
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'verbose': 0
}

print('開始訓練...')
# 訓練
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=20,
                valid_sets=lgb_eval,
                early_stopping_rounds=5)

# 儲存模型
print('儲存模型...')
# 儲存模型到檔案中
gbm.save_model('model.txt')

print('開始預測...')
# 預測
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
# 評估
print('預估結果的rmse為:')
print(mean_squared_error(y_test, y_pred) ** 0.5)
載入資料...
開始訓練...
[1]  valid_0's l2: 0.24288   valid_0's auc: 0.764496
Training until validation scores don't improve for 5 rounds.
[2]  valid_0's l2: 0.239307  valid_0's auc: 0.766173
[3]  valid_0's l2: 0.235559  valid_0's auc: 0.785547
[4]  valid_0's l2: 0.230771  valid_0's auc: 0.797786
[5]  valid_0's l2: 0.226297  valid_0's auc: 0.805155
[6]  valid_0's l2: 0.223692  valid_0's auc: 0.800979
[7]  valid_0's l2: 0.220941  valid_0's auc: 0.806566
[8]  valid_0's l2: 0.217982  valid_0's auc: 0.808566
[9]  valid_0's l2: 0.215351  valid_0's auc: 0.809041
[10] valid_0's l2: 0.213064  valid_0's auc: 0.805953
[11] valid_0's l2: 0.211053  valid_0's auc: 0.804631
[12] valid_0's l2: 0.209336  valid_0's auc: 0.802922
[13] valid_0's l2: 0.207492  valid_0's auc: 0.802011
[14] valid_0's l2: 0.206016  valid_0's auc: 0.80193
Early stopping, best iteration is:
[9]  valid_0's l2: 0.215351  valid_0's auc: 0.809041
儲存模型...
開始預測...
預估結果的rmse為:
0.4640593794679212

3.2 設定樣本權重

LightGBM的建模非常靈活,它可以支援我們對於每個樣本設定不同的權重學習,設定的方式也非常簡單,我們需要提供給模型一組權重陣列資料,長度和樣本數一致。

如下是一個典型的例子,其中binary.trainbinary.test讀取後加載為lightgbm.Dataset格式的輸入,而在lightgbm.Dataset的構建引數中可以設定樣本權重(這個例子中是numpy array的形態)。再基於lightgbm.train介面使用內建建模方式訓練。

# coding: utf-8
import json
import lightgbm as lgb
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error
import warnings
warnings.filterwarnings("ignore")

# 載入資料集
print('載入資料...')
df_train = pd.read_csv('./data/binary.train', header=None, sep='\t')
df_test = pd.read_csv('./data/binary.test', header=None, sep='\t')
W_train = pd.read_csv('./data/binary.train.weight', header=None)[0]
W_test = pd.read_csv('./data/binary.test.weight', header=None)[0]

y_train = df_train[0].values
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

num_train, num_feature = X_train.shape

# 載入資料的同時載入權重
lgb_train = lgb.Dataset(X_train, y_train,
                        weight=W_train, free_raw_data=False)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train,
                       weight=W_test, free_raw_data=False)

# 設定引數
params = {
    'boosting_type': 'gbdt',
    'objective': 'binary',
    'metric': 'binary_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'verbose': 0
}

# 產出特徵名稱
feature_name = ['feature_' + str(col) for col in range(num_feature)]

print('開始訓練...')
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                valid_sets=lgb_train,  # 評估訓練集
                feature_name=feature_name,
                categorical_feature=[21])
載入資料...
開始訓練...
[1]  training's binary_logloss: 0.68205
[2]  training's binary_logloss: 0.673618
[3]  training's binary_logloss: 0.665891
[4]  training's binary_logloss: 0.656874
[5]  training's binary_logloss: 0.648523
[6]  training's binary_logloss: 0.641874
[7]  training's binary_logloss: 0.636029
[8]  training's binary_logloss: 0.629427
[9]  training's binary_logloss: 0.623354
[10] training's binary_logloss: 0.617593

3.3 模型儲存與載入

上述建模過程得到的模型物件,可以通過save_model成員函式進行儲存。儲存好的模型可以通過lgb.Booster載入回記憶體,並對測試集進行預測。

具體示例程式碼如下:

# 檢視特徵名稱
print('完成10輪訓練...')
print('第7個特徵為:')
print(repr(lgb_train.feature_name[6]))

# 儲存模型
gbm.save_model('./model/lgb_model.txt')

# 特徵名稱
print('特徵名稱:')
print(gbm.feature_name())

# 特徵重要度
print('特徵重要度:')
print(list(gbm.feature_importance()))

# 載入模型
print('載入模型用於預測')
bst = lgb.Booster(model_file='./model/lgb_model.txt')

# 預測
y_pred = bst.predict(X_test)

# 在測試集評估效果
print('在測試集上的rmse為:')
print(mean_squared_error(y_test, y_pred) ** 0.5)
完成10輪訓練...
第7個特徵為:
'feature_6'
特徵名稱:
['feature_0', 'feature_1', 'feature_2', 'feature_3', 'feature_4', 'feature_5', 'feature_6', 'feature_7', 'feature_8', 'feature_9', 'feature_10', 'feature_11', 'feature_12', 'feature_13', 'feature_14', 'feature_15', 'feature_16', 'feature_17', 'feature_18', 'feature_19', 'feature_20', 'feature_21', 'feature_22', 'feature_23', 'feature_24', 'feature_25', 'feature_26', 'feature_27']
特徵重要度:
[8, 5, 1, 19, 7, 33, 2, 0, 2, 10, 5, 2, 0, 9, 3, 3, 0, 2, 2, 5, 1, 0, 36, 3, 33, 45, 29, 35]
載入模型用於預測
在測試集上的rmse為:
0.4629245607636925

3.4 繼續訓練

LightGBM為boosting模型,每一輪訓練會增加新的基學習器,LightGBM還支援基於現有模型和引數繼續訓練,無需每次從頭訓練。

如下是典型的示例,我們載入已經訓練10輪(即10顆樹整合)的lgb模型,在此基礎上繼續訓練(在引數層面做了一些改變,調整了學習率,增加了一些bagging等緩解過擬合的處理方法)

# 繼續訓練
# 從./model/model.txt中載入模型初始化
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model='./model/lgb_model.txt',
                valid_sets=lgb_eval)

print('以舊模型為初始化,完成第 10-20 輪訓練...')

# 在訓練的過程中調整超引數
# 比如這裡調整的是學習率
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                learning_rates=lambda iter: 0.05 * (0.99 ** iter),
                valid_sets=lgb_eval)

print('逐步調整學習率完成第 20-30 輪訓練...')

# 調整其他超引數
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                valid_sets=lgb_eval,
                callbacks=[lgb.reset_parameter(bagging_fraction=[0.7] * 5 + [0.6] * 5)])

print('逐步調整bagging比率完成第 30-40 輪訓練...')
[11] valid_0's binary_logloss: 0.616177
[12] valid_0's binary_logloss: 0.611792
[13] valid_0's binary_logloss: 0.607043
[14] valid_0's binary_logloss: 0.602314
[15] valid_0's binary_logloss: 0.598433
[16] valid_0's binary_logloss: 0.595238
[17] valid_0's binary_logloss: 0.592047
[18] valid_0's binary_logloss: 0.588673
[19] valid_0's binary_logloss: 0.586084
[20] valid_0's binary_logloss: 0.584033
以舊模型為初始化,完成第 10-20 輪訓練...
[21] valid_0's binary_logloss: 0.616177
[22] valid_0's binary_logloss: 0.611834
[23] valid_0's binary_logloss: 0.607177
[24] valid_0's binary_logloss: 0.602577
[25] valid_0's binary_logloss: 0.59831
[26] valid_0's binary_logloss: 0.595259
[27] valid_0's binary_logloss: 0.592201
[28] valid_0's binary_logloss: 0.589017
[29] valid_0's binary_logloss: 0.586597
[30] valid_0's binary_logloss: 0.584454
逐步調整學習率完成第 20-30 輪訓練...
[31] valid_0's binary_logloss: 0.616053
[32] valid_0's binary_logloss: 0.612291
[33] valid_0's binary_logloss: 0.60856
[34] valid_0's binary_logloss: 0.605387
[35] valid_0's binary_logloss: 0.601744
[36] valid_0's binary_logloss: 0.598556
[37] valid_0's binary_logloss: 0.595585
[38] valid_0's binary_logloss: 0.593228
[39] valid_0's binary_logloss: 0.59018
[40] valid_0's binary_logloss: 0.588391
逐步調整bagging比率完成第 30-40 輪訓練...

3.5 自定義損失函式

LightGBM支援在訓練過程中,自定義損失函式和評估準則,其中損失函式的定義需要返回損失函式一階和二階導數的計算方法,評估準則部分需要對資料的label和預估值進行計算。其中損失函式用於訓練過程中的樹結構學習,而評估準則很多時候是用在驗證集上進行效果評估。

# 自定義損失函式需要提供損失函式的一階和二階導數形式
def loglikelood(preds, train_data):
    labels = train_data.get_label()
    preds = 1. / (1. + np.exp(-preds))
    grad = preds - labels
    hess = preds * (1. - preds)
    return grad, hess


# 自定義評估函式
def binary_error(preds, train_data):
    labels = train_data.get_label()
    return 'error', np.mean(labels != (preds > 0.5)), False


gbm = lgb.train(params,
                lgb_train,
                num_boost_round=10,
                init_model=gbm,
                fobj=loglikelood,
                feval=binary_error,
                valid_sets=lgb_eval)

print('用自定義的損失函式與評估標準完成第40-50輪...')
[41] valid_0's binary_logloss: 0.614429  valid_0's error: 0.268
[42] valid_0's binary_logloss: 0.610689  valid_0's error: 0.26
[43] valid_0's binary_logloss: 0.606267  valid_0's error: 0.264
[44] valid_0's binary_logloss: 0.601949  valid_0's error: 0.258
[45] valid_0's binary_logloss: 0.597271  valid_0's error: 0.266
[46] valid_0's binary_logloss: 0.593971  valid_0's error: 0.276
[47] valid_0's binary_logloss: 0.591427  valid_0's error: 0.278
[48] valid_0's binary_logloss: 0.588301  valid_0's error: 0.284
[49] valid_0's binary_logloss: 0.586562  valid_0's error: 0.288
[50] valid_0's binary_logloss: 0.584056  valid_0's error: 0.288
用自定義的損失函式與評估標準完成第40-50輪...

4.LightGBM預估器形態介面

4.1 SKLearn形態預估器介面

和XGBoost一樣,LightGBM也支援用SKLearn中統一的預估器形態介面進行建模,如下為典型的參考案例,對於讀取為Dataframe格式的訓練集和測試集,可以直接使用LightGBM初始化LGBMRegressor進行fit擬合訓練。使用方法與介面,和SKLearn中其他預估器一致。

# coding: utf-8
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV

# 載入資料
print('載入資料...')
df_train = pd.read_csv('./data/regression.train.txt', header=None, sep='\t')
df_test = pd.read_csv('./data/regression.test.txt', header=None, sep='\t')

# 取出特徵和標籤
y_train = df_train[0].values
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

print('開始訓練...')
# 初始化LGBMRegressor
gbm = lgb.LGBMRegressor(objective='regression',
                        num_leaves=31,
                        learning_rate=0.05,
                        n_estimators=20)

# 使用fit函式擬合
gbm.fit(X_train, y_train,
        eval_set=[(X_test, y_test)],
        eval_metric='l1',
        early_stopping_rounds=5)

# 預測
print('開始預測...')
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_)
# 評估預測結果
print('預測結果的rmse是:')
print(mean_squared_error(y_test, y_pred) ** 0.5)
載入資料...
開始訓練...
[1]  valid_0's l1: 0.491735
Training until validation scores don't improve for 5 rounds.
[2]  valid_0's l1: 0.486563
[3]  valid_0's l1: 0.481489
[4]  valid_0's l1: 0.476848
[5]  valid_0's l1: 0.47305
[6]  valid_0's l1: 0.469049
[7]  valid_0's l1: 0.465556
[8]  valid_0's l1: 0.462208
[9]  valid_0's l1: 0.458676
[10] valid_0's l1: 0.454998
[11] valid_0's l1: 0.452047
[12] valid_0's l1: 0.449158
[13] valid_0's l1: 0.44608
[14] valid_0's l1: 0.443554
[15] valid_0's l1: 0.440643
[16] valid_0's l1: 0.437687
[17] valid_0's l1: 0.435454
[18] valid_0's l1: 0.433288
[19] valid_0's l1: 0.431297
[20] valid_0's l1: 0.428946
Did not meet early stopping. Best iteration is:
[20] valid_0's l1: 0.428946
開始預測...
預測結果的rmse是:
0.4441153344254208

4.2 網格搜尋調參

上面提到LightGBM的預估器介面,整體使用方法和SKLearn中其他預估器一致,所以我們也可以使用SKLearn中的超引數調優方法來進行模型調優。

如下是一個典型的網格搜尋交法調優超引數的程式碼示例,我們會給出候選引數列表字典,通過GridSearchCV進行交叉驗證實驗評估,選出LightGBM在候選引數中最優的超引數。

# 配合scikit-learn的網格搜尋交叉驗證選擇最優超引數
estimator = lgb.LGBMRegressor(num_leaves=31)

param_grid = {
    'learning_rate': [0.01, 0.1, 1],
    'n_estimators': [20, 40]
}

gbm = GridSearchCV(estimator, param_grid)

gbm.fit(X_train, y_train)

print('用網格搜尋找到的最優超引數為:')
print(gbm.best_params_)
用網格搜尋找到的最優超引數為:
{'learning_rate': 0.1, 'n_estimators': 40}

4.3 繪圖解釋

LightGBM支援對模型訓練進行視覺化呈現與解釋,包括對於訓練過程中的損失函式取值與評估準則結果的視覺化、訓練完成後特徵重要度的排序與視覺化、基學習器(比如決策樹)的視覺化。

以下為參考程式碼:

# coding: utf-8
import lightgbm as lgb
import pandas as pd

try:
    import matplotlib.pyplot as plt
except ImportError:
    raise ImportError('You need to install matplotlib for plotting.')

# 載入資料集
print('載入資料...')
df_train = pd.read_csv('./data/regression.train.txt', header=None, sep='\t')
df_test = pd.read_csv('./data/regression.test.txt', header=None, sep='\t')

# 取出特徵和標籤
y_train = df_train[0].values
y_test = df_test[0].values
X_train = df_train.drop(0, axis=1).values
X_test = df_test.drop(0, axis=1).values

# 構建lgb中的Dataset資料格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_test = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 設定引數
params = {
    'num_leaves': 5,
    'metric': ('l1', 'l2'),
    'verbose': 0
}

evals_result = {}  # to record eval results for plotting

print('開始訓練...')
# 訓練
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=100,
                valid_sets=[lgb_train, lgb_test],
                feature_name=['f' + str(i + 1) for i in range(28)],
                categorical_feature=[21],
                evals_result=evals_result,
                verbose_eval=10)

print('在訓練過程中繪圖...')
ax = lgb.plot_metric(evals_result, metric='l1')
plt.show()

print('畫出特徵重要度...')
ax = lgb.plot_importance(gbm, max_num_features=10)
plt.show()

print('畫出第84顆樹...')
ax = lgb.plot_tree(gbm, tree_index=83, figsize=(20, 8), show_info=['split_gain'])
plt.show()

#print('用graphviz畫出第84顆樹...')
#graph = lgb.create_tree_digraph(gbm, tree_index=83, name='Tree84')
#graph.render(view=True)
載入資料...
開始訓練...
[10] training's l2: 0.217995 training's l1: 0.457448 valid_1's l2: 0.21641   valid_1's l1: 0.456464
[20] training's l2: 0.205099 training's l1: 0.436869 valid_1's l2: 0.201616  valid_1's l1: 0.434057
[30] training's l2: 0.197421 training's l1: 0.421302 valid_1's l2: 0.192514  valid_1's l1: 0.417019
[40] training's l2: 0.192856 training's l1: 0.411107 valid_1's l2: 0.187258  valid_1's l1: 0.406303
[50] training's l2: 0.189593 training's l1: 0.403695 valid_1's l2: 0.183688  valid_1's l1: 0.398997
[60] training's l2: 0.187043 training's l1: 0.398704 valid_1's l2: 0.181009  valid_1's l1: 0.393977
[70] training's l2: 0.184982 training's l1: 0.394876 valid_1's l2: 0.178803  valid_1's l1: 0.389805
[80] training's l2: 0.1828   training's l1: 0.391147 valid_1's l2: 0.176799  valid_1's l1: 0.386476
[90] training's l2: 0.180817 training's l1: 0.388101 valid_1's l2: 0.175775  valid_1's l1: 0.384404
[100]   training's l2: 0.179171 training's l1: 0.385174 valid_1's l2: 0.175321  valid_1's l1: 0.382929

參考資料

ShowMeAI系列教程推薦

相關文章推薦