# 通过提前停止(early stopping)避免Python中应用XGBoost的过拟合(overfitting) > 原文: [https://machinelearningmastery.com/avoid-overfitting-by-early-stopping-with-xgboost-in-python/](https://machinelearningmastery.com/avoid-overfitting-by-early-stopping-with-xgboost-in-python/) 过拟合是非线性学习算法(例如梯度提升)中常见的一个复杂问题。 在这篇文章中,您将了解如何通过提前停止(early stopping)来抑制在Python中应用XGBoost的过拟合现象。 阅读这篇文章后,您会知道: * 提前停止(early stopping)是减少训练数据过拟合的一种方法。 * 如何在训练期间监测XGBoost模型的表现并绘制学习曲线。 * 如何使用提前停止(early stopping)来适时及早终止训练处于最佳epoch中的XGBoost模型。 请在我的[新书](https://machinelearningmastery.com/xgboost-with-python/)中找到如何通过XGBoost配置、训练、调试和评估梯度提升模型,其中包括了15个手把手(Step-by-Step)的示例课程以及完整的Python代码。 让我们开始吧。 * **2017年1月更新**:更新为反映scikit-learn API版本0.18.1中的更改。 * **2018年3月更新**:为下载数据集添加了备用链接,旧链接已被移除。 ![Avoid Overfitting By Early Stopping With XGBoost In Python](img/3b5a137b9d5bd85033c44aac0f3068ff.jpg) 通过提前停止(early stopping)避免Python中应用XGBoost的过拟合(overfitting) 照片由[Michael Hamann](https://www.flickr.com/photos/michitux/7218180540/)拍摄,保留部分版权。 ## 通过提前停止(early stopping)避免过拟合 [提前停止(early stopping)](https://en.wikipedia.org/wiki/Early_stopping)是一种训练复杂机器学习模型时避免过拟合的方法。 它通过监测在单独的测试数据集上训练模型的表现,并且注意到一旦在固定数量的训练迭代之后测试数据集上的表现没有得到改善就停止训练过程。 通过尝试自动选出测试数据集上的表现开始降低而训练数据集上的表现继续提高这样过拟合发生迹象的拐点来避免过拟合。 表现度量可以是通过训练模型而进行优化的损失函数(例如对数损失函数(logarithmic loss)),或者通常情况下问题所关注的外部度量(例如分类精度)。 ## 在XGBoost中监测训练表现 XGBoost模型可以在训练期间评估和报告模型在测试集上的表现。 它通过在训练模型和获取verbose输出中调用 **model.fit()**的同时指定测试数据集以及评估度量来支持此功能。 例如,我们可以在训练 XGBoost模型时,在独立测试集( **eval_set** )上报告二值分类误差("error"),如下所示: ```py 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参数网页“[学习任务参数(Learning Task Parameters)](http://xgboost.readthedocs.io/en/latest//parameter.html)”。 例如,我们可以展示如何追踪XGBoost模型训练的表现,应用对象是[Pima印第安人糖尿病数据集(Pima Indians onset of diabetes dataset)](https://archive.ics.uci.edu/ml/datasets/Pima+Indians+Diabetes),它可从UCI机器学习库(UCI Machine Learning Repository)获取(更新:[从此处下载](https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv))。 完整示例如下: ```py # 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%的测试数据集上,于每一个训练epoch评估一次模型。 每一次迭代的结果都将会报告分类误差,而分类精度将会在最后给出。 下面给出了结果输出,为简洁明了,只截取末尾部分。我们可以看到每次训练迭代都会报告分类误差(在每个boosted tree被添加到模型之后)。 ```py ... [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数组。除了测试集,我们也可以一并提供训练数据集。它将说明模型在训练期间在训练集和测试集上分别表现的情况。 例如: ```py 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()**函数,在每个评估集上训练的模型都可以存储并再度可用。这将返回评估数据集和评分的dictionary,例如: ```py results = model.evals_result() print(results) ``` 这将print如下结果(为简洁明了,只截取部分作说明): ```py { '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** 参数提供数据集的顺序。 若需要访问特定的结果数组,例如针对第一个数据集和其误差度量,可以操作如下: ```py results['validation_0']['error'] ``` 此外,我们可以通过向**fit()**函数的eval_metric参数提供度量数组,来指定更多的评估度量指标用于评价和汇总。 我们可以使用这些汇总的表现度量来创建曲线图,并进一步解读模型在训练epochs过程中分别在训练数据集和测试数据集上的表现。 下面是完整的代码示例,显示了如何在曲线图上可视化汇总的结果。 ```py # 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() ``` 运行这段代码会显示每个epoch中训练数据集和测试数据集的分类误差。我们可以通过在 **fit()**函数的调用中设置 **verbose = False** (默认值)来关闭这个功能。 我们看到结果创建了两张图。第一张图显示了训练数据集和测试数据集中每个epoch中XGBoost模型的对数损失(logarithmic loss)。 ![XGBoost Learning Curve Log Loss](img/3dd164f486ba1862fa97f82eb6693360.jpg) XGBoost学习曲线(对数损失(log loss)) 第二张图显示了训练数据集和测试数据集中每个epoch中XGBoost模型的分类误差(classification error)。 ![XGBoost Learning Curve Classification Error](img/cdfec3000bac01a37daacb6f874ff978.jpg) XGBoost学习曲线(分类误差(classification error)) 通过回顾logloss的图像,我们像是有提前停止学习过程的机会,也许在epoch 20到epoch 40之间的某个阶段。 在分类误差的图像中,我们也观察到了类似的情况,误差似乎在epoch 40左右出现上升。 ## 在XGBoost中使用提前停止(early stopping) XGBoost支持在固定次数的迭代后提前停止(early stopping)。 除了为每个epoch指定用于评估的度量和测试数据集之外,还必须指定一个epoch窗长,它代表没有观察到任何改善的epoch数目。它可以在**early_stopping_rounds**参数中实现。 例如,我们可以在10个epoch中检查对数损失没有得到改善,代码如下: ```py 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)将使用列表中的最后一个。 下面展示提前停止(early stopping)的一个完整示例。 ```py # 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)) ``` 运行示例代码将给出如下输出(为简洁明了,只截取部分作说明): ```py ... [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** 作为训练epoch总数(在这种情况下为 10%)是个不错的主意,或者尝试找到对应的可能观察到的学习曲线拐点时期。 ## 总结 在这篇文章中,您实际了解了监测性能表现和提前停止(early stopping)。 所学到的是: * 提前停止(early stopping)技术能够在训练数据集上发生过拟合之前就停止模型训练。 * 如何在训练期间监测XGBoost模型的表现并绘制学习曲线。 * 如何在训练XGBoost模型中配置提前停止(early stopping)。 您对过拟合或这篇文章有任何疑问吗?请在评论中提出您的问题,我会尽力回答。