最简单的深度网络称为多层感知机。多层感知机由多层神经元组成,每一层与它的上一层相连,从中接收输入;同时每一层也与它的下一层相连,影响当前层的神经元
4.1. 多层感知机
4.1.1. 隐藏层
4.1.1.1. 核心概念
多层感知机(multilayer perceptron)通过引入一个或多个隐藏层克服线性模型的局限性,可处理更一般的函数关系类型
架构:堆叠多个全连接层,前 L-1 层作为特征表示,最后一层作为线性预测器
隐藏层输出称为隐藏变量(hidden variable)
4.1.1.2. 公式
含隐藏层的 MLP 计算(无激活函数):
\[\begin{split}\begin{aligned} \mathbf{H} & = \mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}\\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)} \end{aligned}\end{split}\]- 此形式等价于线性模型,无实际增益
含激活函数的 MLP 计算:
\[\begin{split}\begin{aligned} \mathbf{H} & = \sigma(\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)})\\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)} \end{aligned}\end{split}\]- $\sigma$ 为非线性激活函数,使模型摆脱线性限制
4.1.2. 激活函数
4.1.2.1. ReLU 函数
定义:$\operatorname{ReLU}(x) = \max(x, 0)$
导数:输入为负时 0,输入为正时 1,0 处取左导数 0
实现:torch.relu(x)
4.1.2.2. sigmoid 函数
定义:$\operatorname{sigmoid}(x) = \frac{1}{1 + \exp(-x)}$
导数:$\operatorname{sigmoid}(x)\left(1-\operatorname{sigmoid}(x)\right)$
实现:torch.sigmoid(x)
4.1.2.3. tanh 函数
定义:$\operatorname{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}$
导数:$1 - \operatorname{tanh}^2(x)$
实现:torch.tanh(x)
4.2. 多层感知机的从零开始实现
多层感知机包含输入层、隐藏层、输出层,此处实现含 1 个隐藏层
输入:Fashion-MNIST 数据集(28×28 灰度图,展平为 784 维特征)
输出:10 个类别(对应 10 种服饰)
4.2.1. 初始化模型参数
输入维度 num_inputs=784,输出维度 num_outputs=10,隐藏层维度 num_hiddens=256
权重参数:W1(784×256)、W2(256×10),随机初始化(缩放 0.01)
偏置参数:b1(256 维)、b2(10 维),初始化为 0
所有参数需计算梯度(requires_grad=True)
4.2.2. 激活函数
实现 ReLU 函数:relu(X) = max(X, torch.zeros_like(X))
4.2.3. 模型
输入展平:X = X.reshape((-1, num_inputs))
计算:隐藏层 H = relu(X@W1 + b1),输出层 output = H@W2 + b2
4.2.4. 损失函数
使用 nn.CrossEntropyLoss()
4.2.5. 训练
优化器:随机梯度下降(SGD),学习率 lr=0.1
训练轮次 num_epochs=10
调用 d2l.train_ch3 进行训练
调用 d2l.predict_ch3 在测试集上预测
4.3. 多层感知机的简洁实现
4.3.1. 模型
网络结构:
net = nn.Sequential(nn.Flatten(), # 展平输入
nn.Linear(784, 256), # 隐藏层:784→256
nn.ReLU(), # ReLU激活函数
nn.Linear(256, 10)) # 输出层:256→10
权重初始化:
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)
训练配置:
-
超参数:批量大小 256,学习率 0.1,训练轮次 10
-
损失函数:
nn.CrossEntropyLoss() -
优化器:
torch.optim.SGD(net.parameters(), lr=lr)
训练过程:
-
数据加载:
d2l.load_data_fashion_mnist(batch_size) -
训练函数:
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
4.4. 模型选择、欠拟合和过拟合
核心目标:发现具有泛化能力的模式
4.4.1. 训练误差和泛化误差
训练误差(training error):模型在训练数据集上的误差
泛化误差(generalization error):模型在来自相同分布的无限新数据上的期望误差(无法精确计算,需用测试集估计)
4.4.1.1. 统计学习理论
i.i.d. 假设:训练数据和测试数据独立同分布
训练误差会收敛到泛化误差(Glivenko-Cantelli 定理)
4.4.1.2. 模型复杂性
影响模型复杂性(模型泛化)的因素:
- 可调整参数数量(自由度)
- 参数取值范围
- 训练迭代次数
- 训练样本数量
4.4.2. 模型选择
验证集(validation set):用于模型选择,避免使用测试集进行选择(防止过拟合测试集)
K 折交叉验证:训练数据稀缺时使用,将数据分为 K 个子集,轮流用 K-1 个子集训练,1 个子集验证,结果取平均
4.4.3. 欠拟合还是过拟合?
欠拟合(underfitting):模型过于简单,无法捕捉数据模式,训练误差和泛化误差都较高且差距小
过拟合(overfitting):模型过度拟合训练数据,训练误差远低于泛化误差
正则化(regularization):对抗过拟合的技术
4.4.4. 多项式回归
生成数据:基于三次多项式 $y = 5 + 1.2x - 3.4\frac{x^2}{2!} + 5.6 \frac{x^3}{3!} + \epsilon$
关键函数:
evaluate_loss(net, data_iter, loss):评估模型在数据集上的损失
train(...):训练模型,使用 MSELoss 和 SGD 优化器
4.4.4.1. 三种拟合情况
正常拟合(3 阶多项式):训练和测试损失均低,参数接近真实值
欠拟合(线性函数):训练损失难降低,无法拟合非线性模式
过拟合(高阶多项式):训练损失低但测试损失高,受噪声影响大
4.5. 权重衰减
权重衰减(weight decay):常用的参数化机器学习模型正则化技术
核心思想:通过在损失函数中添加权重向量的 $L_2$ 范数作为惩罚项,限制权重大小,降低模型复杂度,缓解过拟合
原始损失函数(线性回归示例):$L(\mathbf{w}, b) = \frac{1}{n}\sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2$
带 $L_2$ 正则化的损失函数:$L(\mathbf{w}, b) + \frac{\lambda}{2} |\mathbf{w}|^2$,其中 $\lambda$ 为非负正则化常数,控制惩罚强度
小批量随机梯度下降更新公式:
\[\begin{aligned} \mathbf{w} & \leftarrow \left(1- \eta\lambda \right) \mathbf{w} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \mathbf{x}^{(i)} \left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right) \end{aligned}\]- 权重在更新时被向零方向衰减
4.5.1. 从零开始实现
def l2_penalty(w):
return torch.sum(w.pow(2)) / 2
# 训练时将惩罚项加入损失
l = loss(net(X), y) + lambd * l2_penalty(w)
4.5.2. 简洁实现
trainer = torch.optim.SGD([
{"params": net[0].weight, 'weight_decay': wd}, # 对权重应用衰减
{"params": net[0].bias} # 不对偏置应用衰减
], lr=lr)
4.6. 暂退法(Dropout)
4.6.1. 基本概念
一种用于改善深度神经网络泛化能力的正则化技术
训练时在每层前向传播中随机“丢弃”部分神经元(置零)
通过打破神经元间的共适应减少过拟合
4.6.2. 数学原理
给定丢弃概率 $p$,每个激活值 $h$ 变为随机变量 $h’$:
\[\begin{split}\begin{aligned} h' = \begin{cases} 0 & \text{ 概率为 } p \\ \frac{h}{1-p} & \text{ 概率为 } 1 - p \end{cases} \end{aligned}\end{split}\]设计保证 $E[h’] = h$(期望不变)
4.6.3. 实践要点
仅在训练时启用,测试时禁用
不同层可设置不同丢弃概率,通常输入层概率较低
测试时无需规范化处理
4.6.4. 实现
-
手动实现 dropout 层:
def dropout_layer(X, dropout): assert 0 <= dropout <= 1 if dropout == 1: return torch.zeros_like(X) if dropout == 0: return X mask = (torch.rand(X.shape) > dropout).float() return mask * X / (1.0 - dropout) -
含 dropout 的网络定义:
class Net(nn.Module): def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2): super(Net, self).__init__() self.lin1 = nn.Linear(num_inputs, num_hiddens1) self.lin2 = nn.Linear(num_hiddens1, num_hiddens2) self.lin3 = nn.Linear(num_hiddens2, num_outputs) self.relu = nn.ReLU() def forward(self, X): H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs)))) if self.training: # 仅训练时应用 H1 = dropout_layer(H1, dropout1) H2 = self.relu(self.lin2(H1)) if self.training: H2 = dropout_layer(H2, dropout2) return self.lin3(H2) -
简洁实现(使用内置层):
net = nn.Sequential( nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), nn.Dropout(dropout1), # 内置dropout层 nn.Linear(256, 256), nn.ReLU(), nn.Dropout(dropout2), nn.Linear(256, 10) )
4.7. 前向传播、反向传播和计算图
前向传播(forward propagation):按从输入层到输出层的顺序计算并存储神经网络的中间变量(含输出)
反向传播(backward propagation):按从输出到输入层的反向顺序,根据微积分链式法则计算神经网络参数的梯度,存储所需中间变量(偏导数)
计算图:可视化计算中算子与变量的依赖关系,方块表示变量,圆圈表示算子
4.7.1. 前向传播关键公式
隐藏层中间变量:$\mathbf{z}= \mathbf{W}^{(1)} \mathbf{x}$
隐藏层激活向量:$\mathbf{h}= \phi (\mathbf{z})$($\phi$ 为激活函数)
输出层变量:$\mathbf{o}= \mathbf{W}^{(2)} \mathbf{h}$
单个样本损失项:$L = l(\mathbf{o}, y)$($l$ 为损失函数,$y$ 为标签)
$L_2$ 正则化项:$s = \frac{\lambda}{2} \left(|\mathbf{W}^{(1)}|_F^2 + |\mathbf{W}^{(2)}|_F^2\right)$
正则化损失(目标函数):$J = L + s$
4.7.2. 反向传播梯度计算
目标函数对损失项和正则项梯度:$\frac{\partial J}{\partial L} = 1,\;\frac{\partial J}{\partial s} = 1$
目标函数对输出层变量梯度:$\frac{\partial J}{\partial \mathbf{o}} = \frac{\partial L}{\partial \mathbf{o}}$
正则项对参数梯度:$\frac{\partial s}{\partial \mathbf{W}^{(1)}} = \lambda \mathbf{W}^{(1)},\;\frac{\partial s}{\partial \mathbf{W}^{(2)}} = \lambda \mathbf{W}^{(2)}$
目标函数对输出层权重梯度:$\frac{\partial J}{\partial \mathbf{W}^{(2)}} = \frac{\partial J}{\partial \mathbf{o}} \mathbf{h}^\top + \lambda \mathbf{W}^{(2)}$
目标函数对隐藏层输出梯度:$\frac{\partial J}{\partial \mathbf{h}} = {\mathbf{W}^{(2)}}^\top \frac{\partial J}{\partial \mathbf{o}}$
目标函数对隐藏层中间变量梯度:$\frac{\partial J}{\partial \mathbf{z}} = \frac{\partial J}{\partial \mathbf{h}} \odot \phi’\left(\mathbf{z}\right)$($\odot$ 为元素乘法)
目标函数对输入层权重梯度:$\frac{\partial J}{\partial \mathbf{W}^{(1)}} = \frac{\partial J}{\partial \mathbf{z}} \mathbf{x}^\top + \lambda \mathbf{W}^{(1)}$
4.7.3. 神经网络训练特点
前向传播与反向传播相互依赖,训练时交替进行
反向传播复用前向传播存储的中间值,减少重复计算
训练比预测需要更多内存(需保留中间值直至反向传播完成)
中间值大小与网络层数和批量大小大致成正比,深层网络 + 大批量易导致内存不足
4.8. 数值稳定性和模型初始化
4.8.1. 梯度消失和梯度爆炸
深层网络中,梯度是多层矩阵乘积与梯度向量的结果,易因矩阵特征值乘积过大/过小导致梯度爆炸/消失,影响优化稳定性
对称性问题:神经网络参数化存在对称性(如 MLP 隐藏单元的排列对称),若初始化不当(如参数全为相同值),会导致隐藏单元功能等价,无法发挥网络表达能力
激活函数影响:
-
Sigmoid 函数梯度在输入过大或过小时会消失,深层网络中易导致梯度传递中断
-
ReLU 能缓解梯度消失问题,加速收敛,成为实践中的默认选择
4.8.2. 参数初始化
-
默认初始化:框架采用默认随机初始化(如正态分布),适用于中等规模问题
-
Xavier 初始化:
目标:使各层输出方差不受输入数量影响,梯度方差不受输出数量影响
高斯分布:均值 0,方差 $\sigma^2 = \frac{2}{n_\mathrm{in} + n_\mathrm{out}}$($n_{in}$ 为输入数,$n_{out}$ 为输出数)
均匀分布:$U\left(-\sqrt{\frac{6}{n_\mathrm{in} + n_\mathrm{out}}}, \sqrt{\frac{6}{n_\mathrm{in} + n_\mathrm{out}}}\right)$
4.9. 环境和分布偏移
分布偏移:训练数据分布 $p_S(\mathbf{x},y)$与测试数据分布 $p_T(\mathbf{x},y)$ 不同,可能导致模型部署失败
真实风险:模型在真实数据分布上的期望损失 $E_{p(\mathbf{x}, y)} [l(f(\mathbf{x}), y)]$
经验风险:训练数据上的平均损失 $\frac{1}{n} \sum_{i=1}^n l(f(\mathbf{x}_i), y_i)$,用于近似真实风险
经验风险最小化:通过最小化经验风险训练模型的策略
4.9.1. 分布偏移的类型
-
协变量偏移(covariate shift)
-
输入分布变化($p_S(\mathbf{x}) \neq p_T(\mathbf{x})$)
-
条件分布不变($p_S(y \mid \mathbf{x}) = p_T(y \mid \mathbf{x})$)
-
适用于 $\mathbf{x}$ 导致 $y$ 的场景
-
-
标签偏移(label shift)
-
标签边缘分布变化($p_S(y) \neq p_T(y)$)
-
类条件分布不变($p_S(\mathbf{x} \mid y) = p_T(\mathbf{x} \mid y)$)
-
适用于 $y$ 导致 $\mathbf{x}$ 的场景
-
-
概念偏移(concept shift)
-
标签定义本身发生变化($p(y \mid \mathbf{x})$ 改变)
-
常随时间/地理等因素逐渐发生
-
4.9.2. 分布偏移纠正
-
协变量偏移纠正
-
计算权重 $\beta_i = \frac{p(\mathbf{x}_i)}{q(\mathbf{x}_i)}$(目标/源分布密度比)
-
通过逻辑回归训练二分类器区分源/目标分布估计 $h(\mathbf{x})$,得 $\beta_i = \exp(h(\mathbf{x}_i))$
-
加权经验风险最小化
-
-
标签偏移纠正
-
计算权重 $\beta_i = \frac{p(y_i)}{q(y_i)}$(目标/源标签分布比)
-
利用混淆矩阵 $\mathbf{C}$ 和测试集预测均值 $\mu(\hat{\mathbf{y}})$ 求解 $p(\mathbf{y}) = \mathbf{C}^{-1} \mu(\hat{\mathbf{y}})$
-
应用加权经验风险最小化
-
-
概念偏移纠正
- 多采用增量更新策略,利用新数据微调模型而非从头训练
4.9.3. 学习问题分类
批处理学习:用固定训练集训练模型后部署,不再更新
在线学习:数据逐次到达,模型持续更新
老虎机(bandits):有限动作集的在线学习特例
控制:环境会记忆并基于历史行为响应
强化学习:环境可能合作或对抗,存在复杂交互