Skip to main content

5.4.4 偏差-方差权衡

偏差方差权衡三联图

本节概览

偏差和方差不是只存在于理论里的词。它们是一种诊断方式:模型是太简单、太不稳定,还是受限于数据质量。

你会做出什么

本节用决策树演示:

  • 模型复杂度如何改变训练分数和测试分数;
  • 如何通过 train-test gap 判断欠拟合和过拟合;
  • 学习曲线如何说明更多数据是否可能有帮助;
  • 高偏差和高方差分别应该采取什么行动。

偏差方差行动诊断图

环境准备

python -m pip install -U scikit-learn numpy

运行完整实验

新建 bias_variance_lab.py

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score
from sklearn.model_selection import learning_curve, train_test_split
from sklearn.tree import DecisionTreeClassifier


X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)

print("complexity_lab")
for depth in [1, 3, 5, None]:
model = DecisionTreeClassifier(max_depth=depth, random_state=42)
model.fit(X_train, y_train)
train_acc = accuracy_score(y_train, model.predict(X_train))
test_acc = accuracy_score(y_test, model.predict(X_test))
gap = train_acc - test_acc
print(
f"max_depth={str(depth):<4} "
f"train={train_acc:.3f} test={test_acc:.3f} gap={gap:.3f} "
f"leaves={model.get_n_leaves()}"
)

print("learning_curve_lab")
model = DecisionTreeClassifier(max_depth=3, random_state=42)
train_sizes, train_scores, val_scores = learning_curve(
model,
X,
y,
cv=5,
train_sizes=[0.2, 0.4, 0.6, 0.8, 1.0],
scoring="accuracy",
)
for size, train_mean, val_mean in zip(train_sizes, train_scores.mean(axis=1), val_scores.mean(axis=1)):
print(f"train_size={size:<3} train={train_mean:.3f} cv={val_mean:.3f} gap={train_mean - val_mean:.3f}")

运行:

python bias_variance_lab.py

预期输出:

complexity_lab
max_depth=1 train=0.923 test=0.923 gap=-0.001 leaves=2
max_depth=3 train=0.977 test=0.944 gap=0.032 leaves=7
max_depth=5 train=0.995 test=0.937 gap=0.058 leaves=15
max_depth=None train=1.000 test=0.923 gap=0.077 leaves=18
learning_curve_lab
train_size=91 train=0.989 cv=0.847 gap=0.142
train_size=182 train=0.986 cv=0.870 gap=0.116
train_size=273 train=0.978 cv=0.903 gap=0.075
train_size=364 train=0.975 cv=0.917 gap=0.057
train_size=455 train=0.974 cv=0.919 gap=0.055

偏差方差实验结果图

读懂复杂度实验

max_depth 越大,树越复杂:

max_depth=1    train=0.923 test=0.923 gap=-0.001 leaves=2
max_depth=None train=1.000 test=0.923 gap=0.077 leaves=18

max_depth=1 很简单。训练和测试很接近,但分数不是最好。这可能是高偏差:模型太简单。

max_depth=None 把训练集记得很完美,但测试准确率下降。这是高方差:模型学到了训练细节,却不能泛化。

实用上最好的模型往往在中间:

max_depth=3 train=0.977 test=0.944 gap=0.032

它没有在训练集上满分,但泛化更好。

学习曲线

学习曲线诊断图

学习曲线展示训练数据增加时会发生什么:

train_size=91  train=0.989 cv=0.847 gap=0.142
train_size=455 train=0.974 cv=0.919 gap=0.055

数据增多后,验证分数上升,gap 变小。这说明更多数据可能有帮助,但模型仍然可以通过更好的特征或调参继续改进。

诊断规则

模式可能问题尝试
train 低,validation 低高偏差 / 欠拟合更强模型、更好特征、减弱正则化
train 高,validation 低高方差 / 过拟合简化模型、加强正则化、增加数据
train 高,validation 高拟合良好在最终 holdout 上测试并监控漂移
validation 按 fold 波动大不稳定或存在数据分群检查 fold,增加数据,使用更稳模型

不要只靠一个指标诊断。要同时看训练分数、验证分数、gap,以及错误是否集中在某个分群。

实用修复

高偏差时:

  • 增加有用特征;
  • 使用表达能力更强的模型;
  • 减弱过强正则化;
  • 如果是迭代模型,训练更久。

高方差时:

  • 降低模型复杂度;
  • 加强正则化;
  • 收集更多样且有代表性的数据;
  • 使用交叉验证和最终 holdout;
  • 考虑能降低方差的集成模型。

常见排查清单

现象可能原因修复方式
train 和 validation 都差模型表达不了模式改进特征或模型类别
train 满分,validation 较差过拟合限制深度、剪枝、正则化
更多数据提升 validation方差或数据不足收集更有代表性的数据
更多数据没帮助高偏差或标签噪声改进特征、标签或模型
validation 按 fold 跳动数据不均匀检查分群分布

练习

  1. 给树加入 min_samples_leaf=5。gap 怎么变?
  2. 尝试 max_depth=2, 4, 6, 8。测试准确率在哪里最高?
  3. 把树换成逻辑回归。问题更像偏差还是方差?
  4. 在复杂度实验中改用 5 折交叉验证,而不是一次测试切分。
  5. 查看最佳树的错误样本。错误是否集中在某个类别?

过关检查

你能解释下面几点,就完成本节:

  • 高偏差表示模型太简单或缺少信号;
  • 高方差表示模型对训练细节太敏感;
  • train-validation gap 是实用诊断工具;
  • 学习曲线能说明更多数据是否可能有帮助;
  • 修复方式取决于现象,不取决于术语本身。