Chapter 11

偏差、方差与模型优化

诊断模型问题,掌握正则化与调优——让模型从"能用"到"好用"

01

核心概念

构建机器学习模型时,训练数据上的优秀表现并不等同于真实场景中的可靠性。理解偏差(Bias)、方差(Variance)以及过拟合/欠拟合的成因,是将模型从"能用"提升到"好用"的关键。

欠拟合与过拟合

欠拟合(Underfitting)指模型过于简单,无法捕捉数据中的规律,在训练集和测试集上表现都差。过拟合(Overfitting)指模型过于复杂,记住了训练数据中的噪声和特例,在训练集上表现极好但测试集上表现差。

诊断 checklist
  • 训练误差高、测试误差也高 → 欠拟合(模型太简单或特征不足)。
  • 训练误差很低、测试误差显著更高 → 过拟合(模型太复杂或数据太少)。
  • 训练误差和测试误差都低且接近 → 拟合适度(理想状态)。

偏差-方差权衡

模型的泛化误差可以分解为三个部分:

\mathbb{E}[(y - \hat{f}(x))^2] = \text{Bias}^2[\hat{f}(x)] + \text{Var}[\hat{f}(x)] + \sigma^2

偏差与方差通常此消彼长:增加模型复杂度会降低偏差但增加方差;降低复杂度则相反。优化的目标是在两者之间找到最佳平衡点。

学习曲线

学习曲线(Learning Curve)展示模型在训练集和验证集上的性能随训练样本量(或迭代次数)的变化。它是诊断过拟合/欠拟合的有力工具:

正则化

正则化通过在损失函数中加入惩罚项,限制模型参数的大小,从而抑制过拟合:

L_{\text{reg}} = L_{\text{original}} + \lambda \sum_{i} w_i^2

在神经网络中,L2 正则化也常被称为"权重衰减"(Weight Decay)。正则化强度 \lambda 越大,模型越简单。

Dropout 与早停法

Dropout 在训练过程中以概率 p 随机丢弃隐藏层神经元,迫使网络不依赖任何单个神经元,显著降低共适应(co-adaptation)风险。测试时所有神经元参与,但输出按 p 缩放。

早停法(Early Stopping) 监控验证集性能,当验证误差连续多轮不再下降时停止训练,避免在训练集上过度优化。它简单有效且几乎无额外计算开销。

超参数调优

超参数是在训练前设定的参数,不能从数据中学习。主要调优策略:

模型集成策略

集成学习通过组合多个模型降低方差或偏差:

02

算法原理与计算方法

偏差与方差分解示例

假设真实关系为 y = f(x) + \epsilon,其中 \epsilon \sim N(0, 4)。用 100 组不同的训练集训练同一个复杂模型,在某测试点 x_0 上得到预测值分布:均值 \bar{\hat{y}} = 8.2,方差 Var(\hat{y}) = 3.5,真实值 f(x_0) = 10。

计算

偏差 = \bar{\hat{y}} - f(x_0) = 8.2 - 10 = -1.8,偏差² = 3.24

方差 = 3.5

不可约误差 = \sigma^2 = 4

总期望误差 ≈ 3.24 + 3.5 + 4 = 10.74

此例中误差主要由不可约误差和方差贡献。若换用更简单的模型后方差降至 0.8 但偏差²升至 6.0,则总误差 ≈ 10.8,差异不大;但如果能将偏差²控制在 1.0 同时方差为 1.5,总误差可降至 6.5。

学习曲线分析

对于高偏差(欠拟合)模型,增加训练数据对改善验证性能帮助有限,因为模型根本没有足够容量学习规律。此时应尝试更复杂的模型或更好的特征。

对于高方差(过拟合)模型,增加训练数据通常能有效缩小训练与验证误差之间的差距。同时正则化也是直接的应对手段。

工程应用场景
  • 模型选择:通过学习曲线和交叉验证指标,在候选模型中选择偏差-方差权衡最优者。
  • 超参优化自动化:使用 Optuna、Ray Tune 等框架自动搜索超参数空间,减少人工试错。
  • 模型压缩:对过参数化的大模型进行剪枝、量化、知识蒸馏,在保持性能的同时降低部署成本。
  • 迁移学习:利用预训练模型的低偏差初始化,配合少量数据微调,快速适配新任务。
03

Python 代码实践

绘制学习曲线

Python
import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import learning_curve from sklearn.datasets import load_iris from sklearn.tree import DecisionTreeClassifier X, y = load_iris(return_X_y=True) # 使用不同复杂度模型观察学习曲线差异 model_high = DecisionTreeClassifier(max_depth=None, random_state=42) # 高方差 model_low = DecisionTreeClassifier(max_depth=2, random_state=42) # 高偏差 for model, title in [(model_high, 'Overfitting (deep tree)'), (model_low, 'Underfitting (shallow tree)')]: train_sizes, train_scores, val_scores = learning_curve( model, X, y, cv=5, train_sizes=np.linspace(0.1, 1.0, 10), scoring='accuracy', random_state=42) plt.plot(train_sizes, train_scores.mean(axis=1), 'o-', label='Train') plt.plot(train_sizes, val_scores.mean(axis=1), 's-', label='Validation') plt.title(title) plt.xlabel('Training Set Size') plt.ylabel('Accuracy') plt.legend() plt.grid(True, alpha=0.3) plt.show()

GridSearchCV 网格搜索

Python
from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC # 定义参数网格 param_grid = { 'C': [0.1, 1, 10, 100], 'kernel': ['rbf', 'linear'], 'gamma': ['scale', 'auto', 0.001, 0.01] } # 5折交叉验证网格搜索 grid = GridSearchCV( SVC(random_state=42), param_grid, cv=5, scoring='accuracy', n_jobs=-1 ) grid.fit(X, y) print(f"Best params: {grid.best_params_}") print(f"Best CV score: {grid.best_score_:.3f}")

RandomizedSearchCV 随机搜索

Python
from sklearn.model_selection import RandomizedSearchCV from scipy.stats import loguniform # 定义参数分布 param_distributions = { 'C': loguniform(1e-3, 1e3), 'gamma': loguniform(1e-4, 1e0), 'kernel': ['rbf', 'poly', 'linear'] } random_search = RandomizedSearchCV( SVC(random_state=42), param_distributions, n_iter=20, # 只评估20种随机组合 cv=5, scoring='accuracy', random_state=42, n_jobs=-1 ) random_search.fit(X, y) print(f"Best params: {random_search.best_params_}") print(f"Best CV score: {random_search.best_score_:.3f}")

验证曲线分析正则化效果

Python
from sklearn.model_selection import validation_curve from sklearn.linear_model import Ridge # 分析不同 alpha 对 Ridge 回归的影响 param_range = np.logspace(-3, 3, 10) train_scores, val_scores = validation_curve( Ridge(), X, y, param_name='alpha', param_range=param_range, cv=5, scoring='neg_mean_squared_error' ) plt.semilogx(param_range, -train_scores.mean(axis=1), 'o-', label='Train') plt.semilogx(param_range, -val_scores.mean(axis=1), 's-', label='Validation') plt.xlabel('Alpha (regularization strength)') plt.ylabel('MSE') plt.title('Validation Curve for Ridge Regression') plt.legend() plt.grid(True, alpha=0.3) plt.show()
04

例题与解析

例题 1:识别过拟合与欠拟合

某模型的训练准确率为 65%,验证准确率为 63%。另一模型的训练准确率为 98%,验证准确率为 68%。请分别判断两者的拟合状况并给出改进建议。

解析

模型一:训练与验证准确率都低且接近(65% vs 63%),属于欠拟合(高偏差)。模型容量不足,无法捕捉数据规律。

改进建议:增加模型复杂度(更多特征、更深的网络、更少的正则化),或检查特征工程是否充分。

模型二:训练准确率远高于验证准确率(98% vs 68%),差距达 30%,属于过拟合(高方差)。

改进建议:增加训练数据、添加正则化(L2、Dropout)、使用早停法、降低模型复杂度。

答案:模型一欠拟合,应增强模型能力;模型二过拟合,应添加正则化或增加数据。
例题 2:正则化调参

在线性回归中,L2 正则化系数 \lambda 分别取 0、0.01、1、100 时,观察到以下验证 MSE:0→120,0.01→85,1→78,100→150。应选择哪个 \lambda?

解析

\lambda = 0 时无正则化,验证误差大,说明过拟合。

\lambda = 0.01 时正则化较弱,验证误差有所下降。

\lambda = 1 时验证 MSE 最小(78),偏差-方差权衡最佳。

\lambda = 100 时正则化过强,模型过于简单,验证误差回升至 150,出现欠拟合。

答案:\lambda = 1 最优。正则化强度需要适中,过弱无法抑制过拟合,过强则导致欠拟合。
例题 3:网格搜索计算量

某网格搜索需要调优 3 个超参数,候选值数量分别为 4、3、5 个,使用 5 折交叉验证。总共需要训练多少个模型?若改用随机搜索,设置 n_iter=30,又需要训练多少个模型?

解析

网格搜索组合数 = 4 × 3 × 5 = 60。每组参数训练 5 个模型(5折交叉验证)。

总训练次数 = 60 × 5 = 300

随机搜索只采样 30 组参数,每组同样 5 折验证。

总训练次数 = 30 × 5 = 150,计算量减半。

答案:网格搜索训练 300 个模型,随机搜索训练 150 个模型。当参数空间较大时,随机搜索效率优势明显。
例题 4:早停法判断

某神经网络训练过程中,验证损失变化如下(每 10 轮记录):轮 10→0.45,轮 20→0.38,轮 30→0.34,轮 40→0.33,轮 50→0.35,轮 60→0.40,轮 70→0.48。若早停耐心值(patience)设为 20 轮(即连续 20 轮验证损失不下降则停止),应在哪一轮停止?

解析

验证损失在轮 40 达到最低值 0.33。

轮 50:0.35 > 0.33,未下降(第 1 次)。

轮 60:0.40 > 0.33,未下降(第 2 次)。

轮 70:0.48 > 0.33,未下降(第 3 次)。

注意 patience=20 轮通常指连续不改善的 epoch 数。若以每 10 轮为一个检查点,则在轮 60 时已累计 20 轮未改善,应触发早停。

答案:应在轮 60(或第 60 轮附近)停止,恢复轮 40 的模型权重作为最终模型。
← 上一章:神经网络与深度学习 下一章:机器学习项目实战 →