通常模型调参可通过网格搜索的方式进行搜索实验,但是另一种方法是在训练期间评估模型在每次迭代的模型性能,并将结果绘制成图,则该图即为学习曲线。
学习曲线提供了一种模型诊断工具,可以解释并建议对模型超参数进行特定更改,从而可能会提高预测性能,通常学习曲线是在 x 轴上显示时间或经验,在 y 轴上显示学习或改进的评估指标值,从而更加了解模型 “学习” 的程度。
在对训练数集合验证集进行训练期间,通常会为机器学习模型创建双学习曲线,通多曲线的形状和动态变化可以诊断机器学习模型的表现,反过来,可以建议修改模型的配置参数从而改进模型学习曲线的表现,通常学习曲线有以下三种常见动态:
最常见的是学习曲线用于诊断模型的过度拟合行为,可以通过调整模型的超参数来解决,也是文章开头所述使用学习曲线调整模型超参数,另外学习曲线也可以诊断训练集或验证集是否能代表问题域,对模型选择是没有帮助的。
本节主要讲解如何绘制模型的学习曲线,并以XGBoost为例进行代码示例。具体代码如下:
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from xgboost import XGBClassifier
import xgboost as xgb
import matplotlib.pyplot as plt
from copy import deepcopy
import sklearn.metrics as metrics
import numpy as np
from typing import Tuple
from sklearn.model_selection import learning_curve, validation_curve, GridSearchCV, RandomizedSearchCV
# 模拟生成样本数据集
X, y = make_classification(n_samples=10000, n_features=50, n_informative=50, n_redundant=0, random_state=1)
# 划分训练集、测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1)
# 定义模型的eval_set,也是后期绘制模型学习曲线的必须设置的参数
evalset = [(X_train, y_train), (X_test, y_test)]
# 定义模型
model = XGBClassifier(n_estimators=100, eta=0.05)
# 训练模型
model.fit(X_train, y_train, eval_set=evalset, verbose=10)
yhat = model.predict(X_test)
score = accuracy_score(y_test, yhat)
print('Accuracy: %.3f' % score)
需注意的几点:
results = model.evals_result()
plt.plot(results['validation_0']['logloss'], label='train')
plt.plot(results['validation_1']['logloss'], label='test')
#
plt.legend()
plt.show()
说明:
- 横坐标显示模型的迭代次数或树的个数的变化
- 纵坐标显示 logloss 值的变化
- 该种方式可以验证模型参数对模型表现的影响
2. 模型参数不包含类似 eval_set 参数
对于模型参数中不包含类似 eval_set 的参数,可以通过简单绘图的方式进行绘制,下面以不同样本量下绘制模型的学习曲线:
2.1 纯手工画图
x_train, x_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
train_accuracy_score, val_accuracy_score = [], []
for m in range(500, len(x_train), 500):
clf = deepcopy(model)
clf.fit(x_train[:m], y_train[:m])
y_train_predict = clf.predict(x_train)
y_val_predict = clf.predict(x_val)
train_accuracy_score.append(metrics.accuracy_score(y_train, y_train_predict))
val_accuracy_score.append(metrics.accuracy_score(y_val, y_val_predict))
#
fig = plt.figure(figsize=(6, 5))
ax = fig.add_subplot(111)
#
train_set_size = range(500, len(x_train), 500)
line1, = ax.plot(train_set_size, train_accuracy_score, "r-+", linewidth=2, label="train")
line2, = ax.plot(train_set_size, val_accuracy_score, "b-", linewidth=3, label="test")
#
ax.grid()
plt.xlabel("train set size")
plt.ylabel("accuracy_score")
plt.legend(handles=[line1, line2])
plt.title("model learning curve", fontsize=15)
plt.tight_layout()
plt.show()
说明:
- 横坐标显示样本量的变化
- 纵坐标显示 accuracy_score 值的变化
- 该种方式可以验证不同样本量下模型的表现看,从而判断当前样本量是否可以满足模型训练(个人经验:如果学习曲线随着模型未达到稳定状态,则说明当前样本量可能不足,需要增加模型训练样本)
2.2 借助learn_curve 绘制
借助 sklearn.model_select中的learn_curve进行绘制,逻辑与 2.1 类似
train_sizes, train_scores, test_scores = learning_curve(model, X, y, cv=5, n_jobs=-1, train_sizes=np.linspace(0.1, 1.0, 10), random_state=42, scoring="accuracy") # scoring可以通过 metrics.get_scorer_names() 确定
#
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
#
plt.figure()
plt.title("learnding Curve")
plt.xlabel("training examples")
plt.ylabel("accuracy_score")
plt.grid()
plt.plot(train_sizes, train_scores_mean, "o-", color="r", label="training Score")
plt.plot(train_sizes, test_scores_mean, "o-", color="g", label="cross-validation score")
plt.legend(loc="best")
plt.show()
本节主要介绍如何使用学习曲线调整模型超参数,结合上一节的2.1节中绘制的学习曲线具体介绍,从2.1中可以观察学习曲线随着迭代次数的增加并未趋于平稳,因此尝试增加迭代次数:将n_estimators调整为1000,绘制学习曲线如下图
从学习曲线中可以看出,模型随着迭代次数的增加最后在约500次迭代后稳定,因此可以定义模型的n_estimators暂定位500(为啥是暂定,因为后续还需要调整learning_rate等参数,该值可能会发生调整,具体情况具体分析)
其他模型参数调整逻辑类似。
学习曲线在训练期间可用于诊断模型的学习问题,例如欠拟合、过拟合等问题,以及训练集合验证集是否具有代表性。本节中将介绍学习曲线如何诊断模型的学习和泛化行为,并通过示例图显示常见的学习问题。
在机器学习模型的训练过程中,学习曲线可以在训练集上进行评估以了解模型的"学习能力",也可以在验证集上进行评估以了解模型的"泛化能力"
绘制模型训练集、验证集的学习曲线后,对于良好拟合的模型通常具有以下特征:
不具有代表性的训练集意味着训练集没有提供足够的信息进行模型训练,导致模型的学习曲线在训练集或验证集上表现出波动性或者不收敛或者过拟合问题。
如果与验证数据集相比训练数据集的示例太少,则会发生如下图情况,这种情况可以通过显示改进的训练损失学习曲线和类似的显示改进的验证损失学习曲线来识别,但两条曲线之间仍然存在很大差距。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如果与训练数据集相比验证数据集的示例太少,则会发生如下图情况,这种情况可以通过看起来很适合(或其他适合)的训练损失学习曲线和显示训练损失周围嘈杂运动的验证损失学习曲线来识别。这种情况下,它表明验证数据集可能比训练数据集更容易让模型预测。
而下图则显示比训练数据集更容易预测的验证数据集,具体如下图所示:
1、模型在训练集上准确率较高,比如83%,而在验证集上准去率较低(50%),具体原因?
答:可能是验证集太小导致不能代表整体数据集,尝试更多的数据拆分比例或者获取更多的数据,例如50%的数据拆分
另外从偏差和方差的角度可以得出:模型存在高方差、低偏差的问题,过度拟合了训练数据,添加更多的训练数据、增加算法的正则化、减少训练数据中特征数量等措施可能会在当前算法下产生更好的模型
2、如果训练集误差较小,验证集误差较大,能否通过调参的方式增加训练集的误差使得训练集与验证集的误差相对较小,从而表示模型拟合良好这种情况?
参考:Python机器学习的学习曲线
答:根据经验,在实践中不会发生,因为测试集或验证集通常比训练集更小且更具有代表性。
3、如果验证集比训练集获得更好的性能时,如何解释这种情况?这是良好泛化的迹象吗?
答:这通常表明验证数据集太小,不能代表问题(这种问题很常见)
验证损失可能低于训练损失的三个主要原因:
在训练模型时,最大的担忧几乎总是过度拟合,因此我们引入了正则化技术,从而导致模型更加保守,但有时会过度规范我们的模型而引起验证损失低于训练损失情况的出现。因此需要:降低L2权重衰减强度,尝试更大的学习率、减少dropout比例、复杂化模型等
为什么我的验证损失低于训练损失
Sometimes validation loss < training loss. Ever wondered why?
4、损失增加然后减小然后增加然后减小这种波动,意味着什么?
答:这可能意味着数据嘈杂/不具有代表性,或者模型不稳定(例如输入数据的批量大小或缩放比例)
5、验证损失是否必须低于训练损失值?
对于拟合良好的模型,验证和训练损失应该非常相似
6、施了一个 RNN,验证损失在 2 个时期后开始增加,表明该模型可能过度拟合。然而,我比较了 Precision 和 Recall 的评估结果,在 2 个 epoch 和 10 个 epoch 上运行只给我几乎相似的结果。
我该如何解释?这是否意味着模型在 2 个 epoch 内收敛并且不需要更多训练?我是否可以争辩说,即使验证损失在 2 个时期后增加并表明过度拟合,在 2 个时期后停止是最好的点?
推理似乎不错。也许尝试较小的学习率来减慢学习速度?
7、确定模型和配置后,可以使用所有的数据拟合最终的模型。
8、型在训练集上的准确率为84%,在测试集上为72%,但是观察学习曲线发现训练损失在减少,而测试损失没有较少
模型出现过拟合线性
如何识别sklearn中过度拟合的机器学习模型