本周学习《动手学深度学习》第4章4.1至4.6。内容从多层感知机和激活函数开始,接着用多项式实验观察欠拟合与过拟合,最后复现权重衰减和Dropout。代码仍然分成独立案例,并在RTX 4060上完成四组Fashion-MNIST训练。

这篇复盘主要记录

  • 为什么只堆叠线性层仍然只是线性模型
  • ReLU怎样给网络加入非线性表达能力
  • 欠拟合、过拟合与模型容量之间的关系
  • 权重衰减和Dropout如何限制模型过度记忆训练集

一、为什么要增加隐藏层

上周的softmax回归把784个像素直接映射到10个类别。它只能学习一层线性关系,而图片中一个像素是否重要,往往取决于周围像素。多层感知机先让隐藏层学习一组中间表示,再由输出层完成分类。

H = ReLU(XW₁ + b₁),O = HW₂ + b₂

可以把隐藏层理解为一组“中间判断”:它不直接宣布图片属于哪一类,而是先学习轮廓、亮暗组合等更适合分类的表示。

二、只堆线性层为什么没有用

如果隐藏层后面没有激活函数,两层线性变换可以合并为一层:

H = XW1 + b1
O = HW2 + b2
  = X(W1W2) + (b1W2 + b2)

无论堆多少层,最后仍然是一条线性公式。ReLU等非线性激活函数让这种合并不再成立,网络才有能力表示弯曲、分段和更复杂的关系。

三、三种激活函数

ReLU、Sigmoid、Tanh及其导数曲线
图1:三种常见激活函数及其导数。输入远离0时,sigmoid和tanh的导数逐渐接近0。
激活函数输出范围直观理解
ReLU[0,+∞)负数归零,正数原样通过
sigmoid(0,1)把任意数字压缩成类似概率的值
tanh(-1,1)以0为中心压缩输入

本章MLP采用ReLU。它计算简单,正区间梯度为1,比隐藏层中的sigmoid更不容易让梯度快速变小。

四、MLP从零实现

Fashion-MNIST图片展平后有784个输入。书中使用256个隐藏单元和10个输出类别:

def forward(images):
    X = images.reshape((-1, 784))
    hidden = relu(X @ W1 + b1)
    return hidden @ W2 + b2

从零版要手动保存W1、b1、W2、b2;简洁版则用Flatten、Linear和ReLU组合。两种写法不同,但前向计算结构一致。

五、四组Fashion-MNIST训练

普通MLP与Dropout MLP四种实现的测试准确率曲线
图2:四组模型的测试准确率随训练轮数总体提高,但单轮结果存在明显波动。
模型第10轮训练准确率第10轮测试准确率
MLP从零版86.4%85.0%
MLP简洁版86.5%83.4%
Dropout从零版87.4%86.4%
Dropout简洁版87.3%86.3%

不能根据这一次实验就断言“从零版一定比简洁版好”或“Dropout一定提升准确率”。随机初始化、样本顺序和训练轮数都会让测试准确率波动。更可靠的比较需要多次重复、控制变量,并观察整段曲线。

六、欠拟合和过拟合

欠拟合是模型连训练数据的规律都没有学好;过拟合是训练数据学得很好,但把噪声和偶然细节也记住了,换到测试数据表现下降。

1阶、4阶和20阶多项式模型的训练与测试损失曲线
图3:1阶模型容量不足;4阶模型接近真实生成公式;20阶模型的训练损失低于测试损失,泛化差距更明显。
1阶:训练损失=25.3620,测试损失=15.8694
4阶:训练损失=0.0081,测试损失=0.0112
20阶:训练损失=0.0326,测试损失=0.0625

这里20阶模型的训练损失没有低于4阶,是因为有限轮数内高阶参数更难优化;但它的训练与测试差距更大,已经能看到高容量模型更容易出现泛化问题。过拟合程度还会受到数据量、噪声和训练时间影响。

七、权重衰减

权重衰减在原损失后加入权重大小的惩罚项。模型若想使用非常大的权重去贴合少量训练样本,就要付出额外代价:

总损失 = 原损失 + λ × ||w||² / 2

lambda=0:测试损失=185.0453,权重范数=13.3392
lambda=3:测试损失=30.4501,权重范数=5.4014

这个小样本高维实验里,加入权重衰减后权重整体变小,测试损失明显下降。λ越大并不一定越好,过强的惩罚也可能让模型学不动。

八、Dropout

Dropout训练时随机把一部分隐藏单元设为0,并放大留下的值以保持期望不变。可以类比小组练习时随机让一部分同学暂时不回答,迫使其他成员也学会解决问题,避免整个小组只依赖固定几个人。

mask = (torch.rand(X.shape, device=X.device) > probability).float()
return mask * X / (1.0 - probability)

关键点是:Dropout只在训练阶段开启。测试时调用model.eval(),使用完整网络预测,不能继续随机丢弃隐藏单元。

九、与PDF代码不同的地方

十、目前还不熟的地方

  1. 超参数选择。隐藏单元数、学习率、权重衰减系数和Dropout概率目前主要沿用书中设置。
  2. 泛化判断。知道不能只看训练损失,但还需要练习使用验证集和重复实验选择模型。
  3. 高阶模型优化。20阶模型的表现同时受模型容量和优化过程影响,不能把一次结果简单等同于所有过拟合现象。

十一、下周计划

  1. 01
    计算图

    继续4.7,整理正向传播、反向传播和计算图。

  2. 02
    数值稳定性

    学习梯度消失、梯度爆炸和参数初始化。

  3. 03
    分布偏移

    区分协变量偏移、标签偏移和概念偏移。

  4. 04
    案例准备

    开始整理房价预测需要的数据处理流程。

十二、本周最值得保留的认识

隐藏层非线性容量泛化正则化

隐藏层配合激活函数带来非线性表达能力;模型容量增加后更容易学会训练数据,也更需要关注测试数据上的泛化;权重衰减和Dropout等正则化方法用于限制模型过度记忆。