天天看點

Python降低XGBoost 過度拟合的多種方法

之前已經講過機器學習的 XGBoost 算法簡介

XGBoost feature importance特征重要性-實戰印第安人糖尿病資料集

用Python 開發您的第一個 XGBoost 模型(附腳本和實戰資料)

今天來講解xgboost過度拟合問題。

過度拟合是機器學習模組化經常遇到的問題,也是棘手問題,甚至資料科學崗位面試時候經常會遇到這類難題。大家不要怕,接下來我會詳細講述python xgboost如何降低過度拟合方法和其它更好方法。

xgboost模組化中,如果變量數量太多,模型容易過度拟合,英文術語為overfitting,如下圖右。

如果變量太少,容易欠拟合,英文術語為underfitting,如下圖左。

如果變量剛好合适,模型拟合就會比較好,如下圖中。

Python降低XGBoost 過度拟合的多種方法

我們用xgboost模組化時應當如何避免過度拟合呢?

1.我們應首先觀察模型變量是否太多,是否包含大量無效變量(噪音變量)?如果模型噪音變量太多,就先變量篩選,踢除無效變量,或用降維手段測試效果。這一條是至關重要的前提,如果做不好變量篩選和降維,後續方法效果會大打折扣。

2.我們可以設定xgboost中eval_set,eval_metric,early_stopping_rounds參數,來減輕過度拟合。如果因資料原因,完全消除過度拟合是不可能的。

3.采用其他內建樹算法進一步減輕過度拟合,例如對稱樹算法catboost。對稱樹算法catboost有天然對抗過度拟合優點,之後我會安排時間講解catboost算法。

Python降低XGBoost 過度拟合的多種方法

接下來我将描述如何在 Python 中使用提前停止來限制 XGBoost 的過度拟合。

通過這篇文章,你會了解:

  • 關于提前停止作為減少訓練資料過度拟合的一種方法。
  • 如何在訓練期間監控 XGBoost 模型的性能并繪制學習曲線。
  • 如何使用提前停止在最佳時期提前停止 XGBoost 模型的訓練。

提前停止以避免過度拟合

提前停止是一種訓練複雜機器學習模型以避免過度拟合的方法。

它的工作原理是監控在單獨的測試資料集上訓練的模型的性能,并在經過固定次數的訓練疊代後測試資料集的性能沒有提高時停止訓練過程。

它通過嘗試自動選擇測試資料集性能開始下降的拐點來避免過度拟合,而随着模型開始過度拟合,訓練資料集的性能繼續提高。

性能度量可能是正在優化以訓練模型的損失函數(例如對數損失),或通常對問題感興趣的外部度量(例如分類準确度)。

使用 XGBoost 監控訓練表現

XGBoost 模型可以在訓練期間評估和報告模型在測試集上的性能。

它通過在訓練模型和指定詳細輸出時在調用model.fit()時指定測試資料集和評估名額來支援此功能。

例如,我們可以在訓練 XGBoost 模型時報告獨立測試集 ( eval_set )上的二進制分類錯誤率 (“ error ”) ,如下所示:

eval_set = [(X_test, y_test)]
model.fit(X_train, y_train, eval_metric="error", eval_set=eval_set, verbose=True)           

XGBoost 支援一套評估名額,不僅限于:

  • “ rmse ”表示均方根誤差。
  • “ mae ”表示平均絕對誤差。
  • “ logloss ”用于二進制對數損失,“ mlogloss ”用于多類對數損失(交叉熵)。
  • “ error ”表示分類錯誤。
  • “ auc ”表示ROC曲線下的面積。

XGBoost 參數網頁的“學習任務參數”部分提供了完整清單。

例如,我們可以示範如何在皮馬印第安人糖尿病資料集上跟蹤 XGBoost 模型的訓練性能。

下載下傳資料集檔案并将其放在您目前的工作目錄中。

  • 資料集下載下傳連結:

    https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv

  • 資料集詳細資訊如下:
1. Title: Pima Indians Diabetes Database

2. Sources:
   (a) Original owners: National Institute of Diabetes and Digestive and
                        Kidney Diseases
   (b) Donor of database: Vincent Sigillito ([email protected])
                          Research Center, RMI Group Leader
                          Applied Physics Laboratory
                          The Johns Hopkins University
                          Johns Hopkins Road
                          Laurel, MD 20707
                          (301) 953-6231
   (c) Date received: 9 May 1990

3. Past Usage:
    1. Smith,~J.~W., Everhart,~J.~E., Dickson,~W.~C., Knowler,~W.~C., \&
       Johannes,~R.~S. (1988). Using the ADAP learning algorithm to forecast
       the onset of diabetes mellitus.  In {\it Proceedings of the Symposium
       on Computer Applications and Medical Care} (pp. 261--265).  IEEE
       Computer Society Press.

       The diagnostic, binary-valued variable investigated is whether the
       patient shows signs of diabetes according to World Health Organization
       criteria (i.e., if the 2 hour post-load plasma glucose was at least 
       200 mg/dl at any survey  examination or if found during routine medical
       care).   The population lives near Phoenix, Arizona, USA.

       Results: Their ADAP algorithm makes a real-valued prediction between
       0 and 1.  This was transformed into a binary decision using a cutoff of 
       0.448.  Using 576 training instances, the sensitivity and specificity
       of their algorithm was 76% on the remaining 192 instances.

4. Relevant Information:
      Several constraints were placed on the selection of these instances from
      a larger database.  In particular, all patients here are females at
      least 21 years old of Pima Indian heritage.  ADAP is an adaptive learning
      routine that generates and executes digital analogs of perceptron-like
      devices.  It is a unique algorithm; see the paper for details.

5. Number of Instances: 768

6. Number of Attributes: 8 plus class 

7. For Each Attribute: (all numeric-valued)
   1. Number of times pregnant
   2. Plasma glucose concentration a 2 hours in an oral glucose tolerance test
   3. Diastolic blood pressure (mm Hg)
   4. Triceps skin fold thickness (mm)
   5. 2-Hour serum insulin (mu U/ml)
   6. Body mass index (weight in kg/(height in m)^2)
   7. Diabetes pedigree function
   8. Age (years)
   9. Class variable (0 or 1)

8. Missing Attribute Values: Yes

9. Class Distribution: (class value 1 is interpreted as "tested positive for
   diabetes")

   Class Value  Number of instances
   0            500
   1            268

10. Brief statistical analysis:

    Attribute number:    Mean:   Standard Deviation:
    1.                     3.8     3.4
    2.                   120.9    32.0
    3.                    69.1    19.4
    4.                    20.5    16.0
    5.                    79.8   115.2
    6.                    32.0     7.9
    7.                     0.5     0.3
    8.                    33.2    11.8           

完整示例如下:

# monitor training performance
from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# load data
dataset = loadtxt('pima-indians-diabetes.csv', delimiter=",")
# split data into X and y
X = dataset[:,0:8]
Y = dataset[:,8]
# split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.33, random_state=7)
# fit model no training data
model = XGBClassifier()
eval_set = [(X_test, y_test)]
model.fit(X_train, y_train, eval_metric="error", eval_set=eval_set, verbose=True)
# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))           

運作此示例在 67% 的資料上訓練模型,并在 33% 的測試資料集上評估每個訓練時期的模型。

每次疊代都會報告分類錯誤,最後在最後報告分類精度。

注意:您的結果可能會因算法或評估程式的随機性或數值精度的差異而有所不同。考慮多次運作該示例并比較平均結果。

下面提供了輸出,為簡潔起見被截斷。我們可以看到,每次訓練疊代都會報告分類錯誤(在将每個提升樹添加到模型之後)。

...
[89] validation_0-error:0.204724
[90] validation_0-error:0.208661
[91] validation_0-error:0.208661
[92] validation_0-error:0.208661
[93] validation_0-error:0.208661
[94] validation_0-error:0.208661
[95] validation_0-error:0.212598
[96] validation_0-error:0.204724
[97] validation_0-error:0.212598
[98] validation_0-error:0.216535
[99] validation_0-error:0.220472
Accuracy: 77.95%           

檢視所有輸出,我們可以看到測試集上的模型性能持平,甚至在訓練結束時變得更糟。

使用學習曲線評估 XGBoost 模型

我們可以在評估資料集上檢索模型的性能并繪制它以深入了解訓練過程中學習的展開方式。

在拟合 XGBoost 模型時,我們為eval_metric參數提供了一組 X 和 y 對。除了測試集,我們還可以提供訓練資料集。這将提供關于模型在訓練期間在訓練集和測試集上的表現如何的報告。

例如:

eval_set = [(X_train, y_train), (X_test, y_test)]
model.fit(X_train, y_train, eval_metric="error", eval_set=eval_set, verbose=True)           

此外,模型在每個評估集上的性能通過調用model.evals_result()函數在訓練後存儲并由模型提供。這将傳回評估資料集和分數的字典,例如:

results = model.evals_result()
print(results)           

這将列印如下結果(為簡潔起見被截斷):

{
'validation_0': {'error': [0.259843, 0.26378, 0.26378, ...]},
'validation_1': {'error': [0.22179, 0.202335, 0.196498, ...]}
}           

“ validation_0 ”和“ validation_1 ”中的每一個都對應于在調用fit() 時将資料集提供給eval_set參數的順序。

可以按如下方式通路特定的結果數組,例如第一個資料集和錯誤度量:

results['validation_0']['error']           

此外,我們可以通過向fit()函數的 eval_metric 參數提供一組名額來指定更多的評估名額來評估和收集。

然後,我們可以使用這些收集到的性能名額來建立線圖,并進一步了解模型在訓練時期在訓練和測試資料集上的表現。

下面是完整的代碼示例,顯示了如何在折線圖上可視化收集的結果。

#原創公衆号:python風控模型
# plot learning curve
from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from matplotlib import pyplot
# load data
dataset = loadtxt('pima-indians-diabetes.csv', delimiter=",")
# split data into X and y
X = dataset[:,0:8]
Y = dataset[:,8]
# split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.33, random_state=7)
# fit model no training data
model = XGBClassifier()
eval_set = [(X_train, y_train), (X_test, y_test)]
model.fit(X_train, y_train, eval_metric=["error", "logloss"], eval_set=eval_set, verbose=True)
# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))
# retrieve performance metrics
results = model.evals_result()
epochs = len(results['validation_0']['error'])
x_axis = range(0, epochs)
# plot log loss
fig, ax = pyplot.subplots()
ax.plot(x_axis, results['validation_0']['logloss'], label='Train')
ax.plot(x_axis, results['validation_1']['logloss'], label='Test')
ax.legend()
pyplot.ylabel('Log Loss')
pyplot.title('XGBoost Log Loss')
pyplot.show()
# plot classification error
fig, ax = pyplot.subplots()
ax.plot(x_axis, results['validation_0']['error'], label='Train')
ax.plot(x_axis, results['validation_1']['error'], label='Test')
ax.legend()
pyplot.ylabel('Classification Error')
pyplot.title('XGBoost Classification Error')
pyplot.show()           

運作此代碼會報告每個時期訓練和測試資料集上的分類錯誤。我們可以通過在調用fit()函數時設定verbose=False(預設值)來關閉它。

建立了兩個圖。第一個顯示了 XGBoost 模型在訓練和測試資料集上每個時期的對數損失。

Python降低XGBoost 過度拟合的多種方法

XGBoost 學習曲線對數損失

第二個圖顯示了 XGBoost 模型在訓練和測試資料集上每個 epoch 的分類誤差。

Python降低XGBoost 過度拟合的多種方法

XGBoost 學習曲線分類錯誤

從檢視 logloss 圖來看,似乎有機會提前停止學習,可能在 20 到 40 紀元左右。

我們看到了類似的分類錯誤故事,其中錯誤似乎在第 40 輪左右回升。

使用 XGBoost 提前停止

XGBoost 支援在固定次數的疊代後提前停止。

除了指定用于評估每個 epoch 的度量和測試資料集之外,您還必須指定一個視窗,其中包含沒有觀察到改進的 epoch 數。這是在early_stopping_rounds 參數中指定的。

例如,我們可以檢查 10 個時期的對數損失沒有改善,如下所示:

eval_set = [(X_test, y_test)]
model.fit(X_train, y_train, early_stopping_rounds=10, eval_metric="logloss", eval_set=eval_set, verbose=True)           

如果提供了多個評估資料集或多個評估名額,則提前停止将使用清單中的最後一個。

下面提供了一個完整的例子,說明提前停止的完整性。

# early stopping
from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# load data
dataset = loadtxt('pima-indians-diabetes.csv', delimiter=",")
# split data into X and y
X = dataset[:,0:8]
Y = dataset[:,8]
# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size, random_state=seed)
# fit model no training data
model = XGBClassifier()
eval_set = [(X_test, y_test)]
model.fit(X_train, y_train, early_stopping_rounds=10, eval_metric="logloss", eval_set=eval_set, verbose=True)
# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))           

運作該示例提供以下輸出,為簡潔起見被截斷。

...
[35] validation_0-logloss:0.487962
[36] validation_0-logloss:0.488218
[37] validation_0-logloss:0.489582
[38] validation_0-logloss:0.489334
[39] validation_0-logloss:0.490969
[40] validation_0-logloss:0.48978
[41] validation_0-logloss:0.490704
[42] validation_0-logloss:0.492369
Stopping. Best iteration:
[32] validation_0-logloss:0.487297           

我們可以看到模型在 epoch 42 停止訓練(接近我們手動判斷學習曲線的預期),并且在 epoch 32 觀察到損失最好的模型。

通常選擇early_stopping_rounds作為訓練時期總數的合理函數(在這種情況下為 10%)或嘗試對應于可能在學習曲線圖上觀察到的拐點周期是一個好主意。

總結

在這篇文章中,我們了解到了:

  • 關于在模型過度拟合訓練資料之前停止模型訓練的提前停止技術。
  • 如何在訓練 XGBoost 模型時配置提前停止。

xgboost過度拟合知識就為大家介紹到這裡了,歡迎各位同學報名<python風控模組化實戰lendingclub>,學習更多內建樹算法相關知識

https://edu.51cto.com/sd/7c7d7

Python降低XGBoost 過度拟合的多種方法