本文翻译自 Avoid Overfitting By Early Stopping With XGBoost In Python, 讲述如何在使用 XGBoost 建模时通过 Early Stop 手段来避免过拟合. 全文系作者原创, 仅供学习参考使用, 转载授权请私信联系, 否则将视为侵权行为. 码字不易, 感谢支持. 以下为全文内容:
过拟合问题是在使用复杂的非线性学习算法时会经常碰到的, 比如 gradient boosting 算法.
在这篇博客中你将发现如何通过 Early Stop 方法使得我们在使用 Python 中的 XGBoost 模型时可以尽可能地避免过拟合问题:
读完这篇博客后, 你将学到:
Early Stop 可以减少训练集上的过拟合
在使用 XGBoost 模型时如何监控训练过程中模型的表现, 如何绘制学习曲线
如何使用 Early Stop 方法在模型表现最好的时候停止训练
让我们开始吧.
使用 Early Stop 避免过拟合
Early Stop 是训练复杂机器学习模型以避免其过拟合的一种方法.
它通过监控模型在一个额外的测试集上的表现来工作, 当模型在测试集上的表现在连续的若干次 (提前指定好的) 迭代中都不再提升时它将终止训练过程.
它通过尝试自动选择拐点来避免过拟合, 在拐点处, 测试数据集的性能开始下降, 而训练数据集的性能随着模型开始过拟合而继续改善.
性能的度量可以是训练模型时正在使用的损失函数(例如对数损失), 或通常意义上用户感兴趣的外部度量(例如分类精度).
在 XGBoost 中监控模型的表现
XGBoost 模型在训练时可以计算并输入在某个指定的测试数据集的性能表现.
在调用 model.fit()函数时, 可以指定测试数据集和评价指标, 同时设置 verbose 参数为 True, 这样就可以在训练过程中输出模型在测试集的表现.
例如, 我们可以通过下面的方法在使用 XGBoost 训练二分类任务时输出分类错误率(通过 "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" 代表 m - 元对数损失
"error" 代表分类错误率
"auc" 代表 ROC 曲线下面积
完整的列表见 XGBoost 文档中的 "Learning Task Parameters"" 章节.
例如, 我们可以演示如何监控使用 UCI 机器学习存储库 (更新: 从这里下载) 的关于 Pima 糖尿病发病数据集的 XGBoost 模型在训练过程中的性能指标.
完整代码清单如下:
- # 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 模型时我们提供了一个数组, 数组的每一项是一个 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()函数获取, 这个函数返回一个 dict 包含了评估数据集的代码和对应的分数列表, 例如:
- 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']
另外我们可以指定更多的评价指标, 从而同时获取多种评价指标的变化情况.
接着我们可以使用收集到的数据绘制曲线, 从而更直观地了解在整个训练过程中模型在训练集和测试集上的表现究竟如何.
下面是一段完整的代码, 展示了如何将收集到的数据绘制成学习曲线:
- # 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()
运行这段代码将会在每一次训练迭代中输出模型在训练集和测试集上的分类错误率. 我们可以通过设置 verbose=False 来关闭输出.
我们绘制了两张图, 第一张图表示的是模型在每一轮迭代中在两个数据集上的对数损失:
XGBoost Learning Curve Log Loss
第二张图表示分类错误率:
XGBoost Learning Curve Classification Error
从第一张图来看, 似乎有机会可以进行 Early Stop, 大约在 20 到 40 轮迭代时比较合适.
从第二张图可以得到相似的结果, 大概在 40 轮迭代时效果比较理想.
在 XGBoost 中进行 Early Stop
XGBoost 提供了在指定轮数完成后提前停止训练的功能.
除了提供用于评估每轮迭代中的评价指标和数据集之外, 还需要指定一个窗口大小, 意味着连续这么多轮迭代中模型的效果没有提升. 这是通过 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_rounds 将会使用数组中的最后一个作为依据.
下面提供了一个使用 early_stopping_rounds 的详细例子:
- # 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
我们可以看到模型在迭代到 42 轮时停止了训练, 在 32 轮迭代后观察到了最好的效果.
通常将 early_stopping_rounds 设置为一个与总训练轮数相关的函数(本例中是 10%), 或者通过观察学习曲线来设置使得训练过程包含拐点, 这两种方法都是不错的选择.
总结
在这篇博客中你发现了如何监控模型的表现以及怎么做 Early Stop.
你学会了:
使用 Early Stop 手段在模型过拟合之前停止训练
在使用 XGBoost 模型时如何监控模型的表现并绘制出模型的学习曲线
在训练 XGBoost 模型时如何设置 Early Stop 参数
关于 Early Stop 或者这篇博客你还有什么想问的问题吗? 欢迎在下方的评论区留言, 我将尽我最大的努力来解答.
来源: http://www.jianshu.com/p/f0ffa8a93327