大数跨境

导师:“训练集AUC都0.99了,测试集怎么还这么差?”我:“先用Codex画张学习曲线看看!”导师:“别急着换模型,这一步才是关键!”

导师:“训练集AUC都0.99了,测试集怎么还这么差?”我:“先用Codex画张学习曲线看看!”导师:“别急着换模型,这一步才是关键!” 机器学习和人工智能AI
2026-10-09
2

哈喽,大家好~

最近看到一个问题:“训练集分数都快满了,为什么测试效果还是不行?”

那么,这个模型到底需要调参数,还是继续增加数据?

遇到这类问题,先别急着换模型。画一张学习曲线,通常就能看出模型究竟卡在哪里~

什么是学习曲线

学习曲线不是一个新的机器学习模型,而是一种模型诊断工具。

它会逐步增加训练样本数量,然后记录模型在训练集和验证集上的表现。横轴通常是训练样本数,纵轴可以是准确率、F1或AUC等指标。

你可以这样理解:让一个学生依次做100道、500道、1000道练习题,再观察他在练习题和考试题上的成绩变化。

如果练习题接近满分,考试却一直很差,说明他“背题”了,也就是模型发生了过拟合。

核心原理

假设训练样本数量为 ,经过 折交叉验证后,训练分数和验证分数分别为:

这里的 代表评价指标, 是交叉验证折数。两个分数之间的差距称为泛化差距:

一句话概括: 越大,模型对训练数据的依赖越强,过拟合越明显。

看学习曲线时,主要关注三种情况:

  • 两条曲线都低,而且靠得很近:模型欠拟合。
  • 训练分数高、验证分数低:模型过拟合。
  • 验证曲线仍在明显上升:继续增加数据有效。

如果验证曲线已经进入平台期,再堆数据的收益就很小,此时更应该调整特征和模型。

完整案例

我们使用make_classification生成3000条分类数据,再训练随机森林。

随机森林会训练多棵决策树,并通过投票完成分类。它能拟合复杂的非线性关系,但树太深、叶节点样本太少时,也容易记住训练数据。

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import (
    StratifiedKFold, learning_curve
)

# 1. 二分类数据
X, y = make_classification(
    n_samples=3000,
    n_features=20,
    n_informative=10,
    n_redundant=5,
    weights=[0.65, 0.35],
    class_sep=0.85,
    flip_y=0.08,
    random_state=42
)

# 2. 定义模型
model = RandomForestClassifier(
    n_estimators=180,
    max_depth=8,
    min_samples_leaf=3,
    random_state=42,
    n_jobs=-1
)

cv = StratifiedKFold(
    n_splits=5,
    shuffle=True,
    random_state=42
)

# 3. 计算学习曲线
sizes, train_scores, val_scores, fit_times, _ = learning_curve(
    model,
    X,
    y,
    train_sizes=np.linspace(0.1, 1.0, 8),
    cv=cv,
    scoring="roc_auc",
    n_jobs=-1,
    return_times=True
)

train_mean = train_scores.mean(axis=1)
train_std = train_scores.std(axis=1)
val_mean = val_scores.mean(axis=1)
val_std = val_scores.std(axis=1)
time_mean = fit_times.mean(axis=1)
gap = train_mean - val_mean

# 4. 可视化分析
fig, axes = plt.subplots(1, 2, figsize=(15, 5))

pink = "#FF2D95"
cyan = "#00E5FF"
yellow = "#FFE600"
purple = "#9D4EDD"

# 图1:性能学习曲线
ax = axes[0]
ax.plot(sizes, train_mean, "o-", lw=3,
        color=pink, label="Training AUC")
ax.plot(sizes, val_mean, "s-", lw=3,
        color=cyan, label="Validation AUC")

ax.fill_between(
    sizes,
    train_mean - train_std,
    train_mean + train_std,
    color=pink,
    alpha=0.18
)
ax.fill_between(
    sizes,
    val_mean - val_std,
    val_mean + val_std,
    color=cyan,
    alpha=0.18
)
ax.fill_between(
    sizes, val_mean, train_mean,
    color=purple, alpha=0.15,
    label="Generalization gap"
)

ax.set_title("Learning Curve with CV Uncertainty")
ax.set_xlabel("Training samples")
ax.set_ylabel("ROC-AUC")
ax.set_ylim(0.5, 1.02)
ax.grid(alpha=0.2)
ax.legend()

# 图2:泛化差距与训练成本
ax2 = axes[1]
ax2.plot(
    sizes, gap, "D-", lw=3,
    color=yellow, label="Generalization gap"
)
ax2.set_xlabel("Training samples")
ax2.set_ylabel("AUC gap", color=yellow)
ax2.tick_params(axis="y", colors=yellow)
ax2.grid(alpha=0.2)

time_ax = ax2.twinx()
time_ax.plot(
    sizes, time_mean, "o--", lw=3,
    color=cyan, label="Fit time"
)
time_ax.fill_between(
    sizes, 0, time_mean,
    color=cyan, alpha=0.12
)
time_ax.set_ylabel("Fit time (seconds)", color=cyan)
time_ax.tick_params(axis="y", colors=cyan)
ax2.set_title("Generalization Gap vs Training Cost")

lines1, labels1 = ax2.get_legend_handles_labels()
lines2, labels2 = time_ax.get_legend_handles_labels()
ax2.legend(lines1 + lines2, labels1 + labels2, loc="best")

plt.tight_layout()
plt.savefig("learning_curve_analysis.png",
            dpi=180, bbox_inches="tight")
plt.show()

print(f"最终训练AUC:{train_mean[-1]:.3f}")
print(f"最终验证AUC:{val_mean[-1]:.3f}")
print(f"最终泛化差距:{gap[-1]:.3f}")

左图同时展示训练AUC、验证AUC和交叉验证波动范围。

两条曲线之间的紫色区域就是泛化差距。

如果样本增加后,紫色区域逐渐缩小,说明更多数据正在缓解过拟合。阴影越窄,则不同交叉验证分组的结果越稳定。

右图把泛化差距和训练时间放在一起分析。

黄色曲线下降,代表泛化能力提升;蓝色曲线上升,代表增加数据带来了更高的计算成本。

这张图回答的是一个很实际的问题:继续增加数据,带来的性能收益是否值得计算成本。

参数调优

如果训练和验证AUC都低,可以提高模型复杂度,例如增大max_depth、增加有效特征。

如果泛化差距很大,可以限制树深、增大min_samples_leaf,或者继续增加训练数据。

我们这里使用ROC-AUC,是因为数据类别比例并不完全均衡。实际任务中,类别严重不均衡时,还可以把scoring改成f1或average_precision。

总结

学习曲线的重点,不是单独看某个分数,而是观察训练表现、验证表现和两者差距如何随数据量变化。

下一步大家可以对比不同max_depth下的学习曲线,也可以把随机森林换成逻辑回归、XGBoost或神经网络。

再进一步,还可以同时记录内存占用和推理时间,把模型效果与工程成本放在一起评估。

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 417
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读7.4k
粉丝0
内容417