核心问题:
为什么要深度学习
为什么要用神经网络实现深度学习
引入
浅层与深层网络
SVM 通常只有 1-2 层非线性映射,属于浅层网络;而有些应用需要多层的特征映射,其优势在于特征重用,低层特征通过组合形成高层特征,这种层级结构(类似于投票机制)可以指数级地增加表达能力。
也正因如此,只有一个隐藏层(但神经元很多)的MLP,并不等于有很多层的神经网络。尽管通用近似定理告诉我们,只有一个隐藏层且神经元足够多的网络,可以模拟任何函数;但是,如果要模拟一个复杂的逻辑,单隐藏层的网络可能需要指数级数量的神经元。而深层网络通过层级结构,可以用极少数量的神经元实现同样的复杂度。

特征
人脸识别、语音识别、OCR、自然语言处理等任务的共同难点在于,原始数据(像素、波形)很难直接处理,必须转化为有效的特征。

在手写数字识别中,模型学到了每个数字的笔画模式。

在人脸识别任务中,低层只学到了边缘轮廓;中层学到了眼睛、鼻子、耳朵等器官;高层学到了整张人脸。
何以深度
深度信念网络(2006,Hinton)

受限玻尔兹曼机(RBM)
RBM 有两层神经元:
- 可见层用来接收我们输入的原始数据。比如输入一张猫的图片,这层的节点就代表图片的像素。
- 隐藏层用来提取数据的潜在特征(latent variables)。比如这一层某个节点亮起,可能代表图片里有“猫耳朵”或“胡须”的特征。
所谓“受限”,是指层内无连接,层间全连接(即二分图)。
重构
RBM 的训练是无监督的,它基于一种叫做“重构”的机制,下面定义一些符号:
- \(v\) (Visible): 可见层向量(即输入数据,比如图片的像素值)。
- \(h\) (Hidden): 隐藏层向量(即提取的特征)。
- \(W\) (Weight): 连接可见层和隐藏层的权重矩阵。\(W_{ij}\) 表示可见层节点 \(i\) 和隐藏层节点 \(j\) 之间的权重。
- \(a\) 和 \(b\): 偏置项(Bias)。\(a_i\) 是可见层的偏置,\(b_j\) 是隐藏层的偏置。
- \(\sigma(x)\): 激活函数(通常是 Sigmoid 函数 \(\sigma(x) = \frac{1}{1 + e^{-x}}\)),它的作用是把计算结果压缩到 0 到 1 之间,当作概率来看待。
- 正向传播
给定输入数据 \(v\),隐藏层某个神经元 \(h_j\) 被激活(即取值为 1)的概率是:
\[ P(h_j = 1 | v) = \sigma \left( b_j + \sum_i v_i W_{ij} \right) \]
- 反向传播(解码 / 重构数据)
现在我们有了隐藏层的特征 \(h\)。假设我们只看这些特征,反推可见层某个像素 \(v_i\) 亮起(即取值为 1)的概率是多少?这就是重构。公式极其对称:
\[ P(v_i = 1 | h) = \sigma \left( a_i + \sum_j h_j W_{ij} \right) \] 这里的损失函数可以定义为重构误差,即 \(v-v'\)。RBM 训练的目的,就是通过不断修改权重 \(W\),让这个误差越来越小,最终使得 \(v'\) 无限接近 \(v\)。
“重构”有什么用?神经网络通过这种“自己尝试恢复自己”的过程,自动学会了识别什么是重要的特征。
此外有一个有趣的事实:训练过程理论上有无数多训练样本。因为模型不需要知道输入的图片长什么样,只需要尽可能让反向生成的图片和原始图片长得差不多就可以了,这样便能学习到有用的特征。
Autoencoder(2006,Bengio)
与RBM类似,autoencoder的主要目的也是为了学习数据的有效特征表示。它的隐藏层节点数量通常远远小于输入层,强行把高维度的数据塞进一个低维度的空间里。这个过程被称为降维。

逐层训练
在2006年之前,整个AI界对“多层神经网络”是绝望的。只要层数一深,反向传播算法就会失效(梯度消失),网络根本学不到东西。SVM是当时的主流。而 RBM 和 Autoencoder 指出,既然直接一起训练深层网络行不通,那我们就一层一层地“预训练”(贪婪逐层无监督预训练)。它向学术界证明了:深层神经网络是可以被有效训练的,而且深层提取的特征确实比浅层更好。
RBM 和 Autoencoder 解决了“深层网络怎么训练”的问题,让人们树立了 “Deeper is Better” 的信仰。一旦这个信仰被确立,同时配合了GPU和大数据(例如imagenet),网络的架构设计迅速从简单的线性堆叠,演变成了 GoogLeNet 这种极其宏大、复杂、精密的网络。
深度学习三要素
- 目标函数/损失函数
- 网络架构
- 学习规则
损失函数
- 回归问题一般使用均方误差 \(Error = \frac{1}{2} \sum (t_i - y(x_i))^2\)
- 分类问题一般使用交叉熵误差 \(Error = -\sum t_i \ln y(x_i)\)
分类问题
交叉熵误差的数学解释
以二分类问题为例,解释一下为什么要用交叉熵。这里要用到极大似然估计。
假设有一个数据集 \(\{\mathbf{x}_i, t_i\}_{i=1}^n\),其中 \(t_i \in \{0,1\}\)。现在观测到样本 \(t=\{t_1, t_2, \dots, t_n\}\)。似然函数可以写为 \[ \prod \limits_{i=1}^n y(\mathbf{x}_i)^{t_i} (1 - y(\mathbf{x}_i))^{(1 - t_i)} \] 这个函数不便于求导,考虑加个负对数: \[ -\sum \limits_{i=1}^n t_i \ln y(\mathbf{x}_i)+(1-t_i) \ln (1-y(\mathbf{x}_i)) \] 然后把后一项去掉,简化为“模型预测为类别1的概率”: \[ -\sum \limits_{i=1}^n t_i \ln y(\mathbf{x}_i) \]
Logistic 分类
那么分类问题的模型本身长什么样呢? \[ y(\mathbf{x}_i) = \frac{1}{1 + \exp(-f_\boldsymbol{\theta}(\mathbf{x}_i))} \]
这就是sigmoid函数(也成为逻辑斯蒂函数,logistic)。其中 \(f_{\theta}\) 是一个线性函数 \(f_\boldsymbol{\theta}(\mathbf{x}) = \theta_0 + \theta_1x_1 + ... + \theta_dx_d\)。
设logistics函数的反函数为 \(g\),易得 \(g(z)=\ln \frac{z}{1-z}\)。易得 \(g(y(\mathbf{x}_i))=f_{\theta}(\mathbf{x}_i)\)。Actually, we model the logit by the linear function.
一般的分类问题
见笔记
正则化与权重衰减
区别
(维基百科)在数学与计算机科学中,尤其是在机器学习和逆问题领域中,正则化是指为解决适定性问题或过拟合而加入额外信息的过程。在机器学习和逆问题的优化过程中,正则项往往被加在目标函数当中,例如 L1 惩罚项、L2 惩罚项。
权重衰减 是一个具体操作,它是指在每一次更新参数时,直接让权重乘以一个略小于 1 的系数(比如 0.99),强制让权重变得更小。
但在现实中,这两个概念经常被混用,因为在标准的随机梯度下降中,L2 正则化和权重衰减在数学上是完全等价的。
我们在原来的损失函数 \(L_{data}\) 后面加上 L2 惩罚项:
\[ Loss = L_{data} + \frac{1}{2} \lambda w^2 \]
求导后的梯度为: \[ \nabla Loss = \nabla L_{data} + \lambda w \] 参数更新公式为: \[ w_{new} = w_{old} - \alpha (\nabla L_{data} + \lambda w_{old}) \]
提取公因式变形一下: \[ w_{new} = (1 - \alpha \lambda) w_{old} - \alpha \nabla L_{data} \]
这个公式的第一项 \((1 - \alpha \lambda) w_{old}\) 正是权重衰减的定义。每次更新时,权重 \(w\) 自己先缩小了一点点(因为 \(\alpha \lambda\) 是个很小的正数),然后再减去原本的梯度。
因为这个等价关系,很多深度学习框架(比如早期的 PyTorch
甚至现在的一些文档)在 API 里把设置 L2 正则化系数的参数直接命名为
weight_decay。
为什么需要正则化
正则化本质上是为了防止过拟合。我们通过一个拟合正弦函数的例子说明这一点。
我们要通过 \(M\) 阶多项式拟合一个正弦函数,损失函数选择均方误差,即 \[ y(x, \mathbf{w}) = w_0 + w_1 x + w_2 x^2 + \cdots + w_M x^M = \sum_{j=0}^M w_j x^j \\ E(\mathbf{w}) = \frac{1}{2} \sum_{n=1}^N (y(x_n, \mathbf{w}) - t_n )^2 \] 以下是不同的 \(M\) 对应的拟合情况以及参数 \(\mathbf{w}\) 的取值:


可以看到 \(M=0,1\) 欠拟合,\(M=3\) 正好,\(M=9\) 过拟合。通过右表可以看到过拟合(\(M=9\))时参数的 scale 很大。因此,要控制过拟合,一个有效的方法是减小参数的 scale,而我们可以通过加入惩罚项减小参数的 scale。

参数 \(\lambda\) 表示惩罚的力度。如果 \(\lambda\) 太小,约束过松,还是会过拟合;如果 \(\lambda\) 太大,约束过强,也会欠拟合。
正则化的数学解释
先定义一些符号:
- \(N\): 训练样本的数量。
- \(\mathbf{x}_n\): 第 \(n\) 个输入特征向量(样本)。\(\mathbf{X}\) 代表所有输入的集合。
- \(t_n\): 第 \(n\) 个样本对应的真实目标值。\(\mathbf{t}\) 代表所有真实值的向量集合 \([t_1, t_2, ..., t_N]^T\)。
- \(\mathbf{w}\): 模型的权重参数向量 \([w_0, w_1, ..., w_M]^T\)。假设共有 \(M+1\) 个参数,包含偏置项。
- \(y(\mathbf{x}_n, \mathbf{w})\): 模型给出的预测值。
- \(\mathcal{N}(\mu, \sigma^2)\): 均值为 \(\mu\),方差为 \(\sigma^2\) 的高斯(正态)分布。
- \(\beta\): 数据噪声的精度 (Precision)。定义为方差的倒数,即 \(\beta = \frac{1}{\sigma^2}\)。\(\beta\) 越大,数据噪声越小(第二页图中的蓝线越瘦高)。
- \(\alpha\): 参数先验分布的精度。控制我们允许参数波动的范围。
假设我们观测到的真实值 \(t_n\),是模型预测值 \(y(\mathbf{x}_n, \mathbf{w})\) 加上一个高斯噪声 \(\epsilon\) 产生的。即 \(t_n = y(x_n, \mathbf{w}) + \epsilon\),其中 \(\epsilon \sim \mathcal{N}(0, \beta^{-1})\)。因此,给定输入 \(\mathbf{x}_n\) 和参数 \(\mathbf{w}\),观测到单一样本 \(t_n\) 的概率为: \[ p(t_n|x_n, \mathbf{w}, \beta) = \mathcal{N}(t_n | y(x_n, \mathbf{w}), \beta^{-1}) \]
假设所有 \(N\) 个样本是独立同分布的 (i.i.d),那么整个数据集 \(\mathbf{t}\) 的似然函数就是所有单个概率的乘积: \[ p(\mathbf{t}|\mathbf{X}, \mathbf{w}, \beta) = \prod_{n=1}^{N} \mathcal{N}(t_n|y(x_n, \mathbf{w}), \beta^{-1}) \] 如果只最大化上面的似然函数,模型会拼命拟合每一个噪声点,导致参数 \(\mathbf{w}\) 变得极大,发生过拟合。为了限制参数的 scale 太大,贝叶斯学派引入了先验概率。
在没看任何数据之前,我们假设好的模型应该尽量简单,即权重参数 \(\mathbf{w}\) 应该在 \(0\) 附近徘徊。我们假设参数 \(\mathbf{w}\) 服从一个均值为 \(\mathbf{0}\),协方差矩阵为 \(\alpha^{-1}\mathbf{I}\) 的多维高斯分布(\(\mathbf{I}\) 是单位矩阵):
\[ p(\mathbf{w}|\alpha) = \mathcal{N}(\mathbf{w}|\mathbf{0}, \alpha^{-1}\mathbf{I}) = \left(\frac{\alpha}{2\pi}\right)^{(M+1)/2} \exp(-\frac{\alpha}{2}\mathbf{w}^T\mathbf{w}) \]
根据贝叶斯定理:后验概率 \(\propto\) 似然 \(\times\) 先验,参数 \(\mathbf{w}\) 的后验概率可以写为:
\[ p(\mathbf{w}|\mathbf{X}, \mathbf{t}, \alpha, \beta) \propto p(\mathbf{t}|\mathbf{X}, \mathbf{w}, \beta) \cdot p(\mathbf{w}|\alpha) \]
我们的目标是找到一组参数 \(\mathbf{w}\),使得这个后验概率最大化,即 \[ \max p(\mathbf{w}|\mathbf{X}, \mathbf{t}, \alpha, \beta) \] 为了方便求导和计算,我们对后验概率取 \(-\ln\),此时最大化后验概率,等价于最小化其负对数。
$$ () = -
= -p(|, , ) -p(|) $$
我们分别把步骤 1 和步骤 2 的公式代入展开: \[ \text{Loss}(\mathbf{w}) = \frac{\beta}{2}\sum_{n=1}^{N} \{y(\mathbf{x}_n, \mathbf{w}) - t_n\}^2 + \frac{\alpha}{2}\mathbf{w}^T\mathbf{w} \]
为了形式上的统一,我们将整个目标函数乘以一个常数 \(\frac{1}{\beta}\): \[ \widetilde{E}(\mathbf{w}) = \frac{1}{2}\sum_{n=1}^{N} \{y(x_n, \mathbf{w}) - t_n\}^2 + \frac{\alpha}{2\beta}\mathbf{w}^T\mathbf{w} \]
令正则化系数 \(\lambda = \frac{\alpha}{\beta}\),并且由于 \(\mathbf{w}^T\mathbf{w}\) 就是向量的 L2 范数平方 \(\|\mathbf{w}\|^2\),我们得到了最终经典的损失函数公式:
\[ \widetilde{E}(\mathbf{w}) = \underbrace{\frac{1}{2}\sum_{n=1}^{N} \{y(x_n, \mathbf{w}) - t_n\}^2}_{\text{误差项}} + \underbrace{\frac{\lambda}{2}\|\mathbf{w}\|^2}_{\text{L2 正则化项 }} \]
下面总结一下: 1. 在机器学习中加上 L2 正则化(\(\frac{\lambda}{2}\|\mathbf{w}\|^2\))来防止过拟合,在数学本质上,等同于假设模型的权重参数服从均值为 0 的高斯分布。 2. \(\lambda\) 越大,意味着我们更看重先验(\(\alpha\) 大)而不太信任数据(\(\beta\) 小),这就导致模型受到的惩罚更重,参数会被压得更小,红线更平滑。
卷积
MLP在图像数据上的局限性
- MLP 处理图像数据时,通常把图像拉平,变成一个向量,作为神经网络的输入。但是,这丢弃了像素之间的邻接关系。无论是用原始图像进行训练,还是把原始图像的像素重新随机排列,然后送入神经网络进行训练,我们都会得到相同的结果,因为神经网络具有对称性。
- 假设图像有 \(n\) 个像素输入且第一个隐藏层有 \(n\) 个单元。如果输入层和第一个隐藏层完全连接,则意味着网络有 \(n^2\) 个权重;对于一个典型的百万像素 RGB 图像,它的权重将有 9 万亿个。如此庞大的参数空间意味着需要大量的训练图像和庞大的计算预算来运行训练算法。
一维卷积
假设向量 \(\mathbf{k} \in \mathbb{R}^l\) 为卷积核,向量 \(\mathbf{x}\) 为输入的向量,那么卷积操作可以表示为: \[ z_i=\sum \limits_{j=1}^l k_jx_{j+i-(l+1)/2} \] 用人话说,就是第 \(i\) 个位置的输出为核 \(\mathbf{k}\) 与 \(\mathbf{x}\) 中以 \(x_i\) 为中心、宽度为 \(l\) 的片段的内积。
例如,向量 \(\mathbf{x}=(5,6,6,2,5,6,5)^T\),卷积核 \(\mathbf{k}=(1,-1,1)\),那么卷积的结果如下:

该过程也可以用矩阵乘法表示: \[ \begin{pmatrix} +1 & -1 & +1 & 0 & 0 & 0 & 0 \\ 0 & 0 & +1 & -1 & +1 & 0 & 0 \\ 0 & 0 & 0 & 0 & +1 & -1 & +1 \end{pmatrix} \begin{pmatrix} 5 \\ 6 \\ 6 \\ 2 \\ 5 \\ 6 \\ 5 \end{pmatrix} = \begin{pmatrix} 5 \\ 9 \\ 4 \end{pmatrix} \]
二维卷积
基本操作

假设输入大小为 \(n_h \times n_w\),卷积核大小为 \(k_w \times k_w\),那么输出大小为 \((n_h + k_w - 1) \times (n_w + k_w - 1)\)。
感受野
神经元的感受野是指感觉输入中能够影响神经元激活状态的部分。

例如这个网络,第二层红色神经元的感受野是5,因为它接收了输入层五个神经元的信息。
填充
为什么需要 Padding?
- 保持输出尺寸:不加 padding 时,每次卷积输出尺寸都会缩小(输出 = 输入 - 卷积核 + 1)。在深层网络中,经过多次卷积后特征图会越来越小,甚至完全消失。padding 可以让输出保持与输入相同的尺寸。
- 保留边缘信息:没有 padding 时,边缘像素被卷积核扫过的次数比中心像素少得多,导致边缘信息容易丢失。padding 让边缘像素也能被充分处理。
- 设计灵活性:保持输入输出尺寸相同(称为 same padding)可以简化网络架构设计,更容易堆叠多层卷积。
什么是 Padding?
卷积层常常丢失边缘像素。 解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素是0)。 例如,我们将3*3输入填充到5*5,那么它的输出就增加为4*4。

通常,如果我们添加 \(p_h\) 行填充(大约一半在顶部,一半在底部)和 \(p_w\) 列填充(左侧大约一半,右侧一半),则输出形状将为
\[ (n_h - k_h + p_h + 1) \times (n_w - k_w + p_w + 1). \]
这意味着输出的高度和宽度将分别增加 \(p_h\) 和 \(p_w\)。
在许多情况下,我们需要设置 \(p_h = k_h - 1\) 和 \(p_w = k_w - 1\),使输入和输出具有相同的高度和宽度。这样可以在构建网络时更容易地预测每个图层的输出形状。假设 \(k_h\) 是奇数,我们将在高度的两侧填充 \(p_h / 2\) 行。如果 \(k_h\) 是偶数,则一种可能性是在输入顶部填充 \(p_h / 2\) 行,在底部填充 \(p_h / 2\) 行。同理,我们填充宽度的两侧。
卷积神经网络中卷积核的高度和宽度通常为奇数。这样做的的好处是,保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。
步幅
为什么需要 Stride?
- 下采样/降维:增大步幅可以减少输出特征图的尺寸,从而减少计算量和参数量。例如 stride=2 可以将特征图尺寸减半。
- 增大感受野:更大的步幅让后续卷积层能”看到”输入图像更大范围的区域,有助于捕获更宏观的特征。
- 替代池化层:现代网络架构(如 ResNet、VGG)常用 stride=2 的卷积直接进行下采样,替代传统的池化层,效果往往更好。
什么是 Stride?

默认情况下,卷积核每次移动一个像素(stride=1)。当 stride > 1 时,卷积核每次跳过多个像素,从而产生更小的输出。
通常,当垂直步幅为 \(s_h\)、水平步幅为 \(s_w\) 时,输出形状为
\[ \left[ (n_h - k_h + p_h + s_h) / s_h \right] \times \left[ (n_w - k_w + p_w + s_w) / s_w \right]. \]
如果我们设置了 \(p_h = k_h - 1\) 和 \(p_w = k_w - 1\),则输出形状将简化为
\[ \left[ (n_h + s_h - 1) / s_h \right] \times \left[ (n_w + s_w - 1) / s_w \right]. \]
更进一步,如果输入的高度和宽度可以被垂直和水平步幅整除,则输出形状将为 \((n_h / s_h) \times (n_w / s_w)\)。
多输入多输出通道
多输入通道:

多输出通道:

用 \(c_i\) 和 \(c_o\) 分别表示输入和输出通道的数目,并让 \(k_h\) 和 \(k_w\) 为卷积核的高度和宽度。为了获得多个通道的输出,我们可以为每个输出通道创建一个形状为 \(c_i \times k_h \times k_w\) 的卷积核张量,这样卷积核的形状是 \[ c_o \times c_i \times k_h \times k_w \] 在互相关运算中,每个输出通道先获取所有输入通道,再以对应该输出通道的卷积核计算出结果。
池化层
为什么需要池化层?
- 降维/减少计算量:池化层通过下采样减少特征图的空间尺寸,从而减少后续层的计算量和参数量。
- 平移不变性:池化操作对输入的微小位移具有一定的容忍度。例如,图像中的物体稍微移动几个像素,池化后的特征仍然相似,有助于模型识别不同位置的同一物体。
- 防止过拟合:通过减少参数量和特征图尺寸,池化层可以帮助防止模型过拟合。
- 增大感受野:池化后每个神经元对应的原始输入区域变大,有助于捕获更全局的特征。
常见池化类型:
- 最大池化(Max Pooling):取池化窗口内的最大值。能保留最显著的特征,对噪声有一定抑制作用。
- 平均池化(Average Pooling):取池化窗口内的平均值。保留整体特征信息,但可能模糊重要特征。
- 全局池化(Global Pooling):对整个特征图进行池化,输出一个标量。常用于网络末端替代全连接层。
为什么最大池化通常优于平均池化?
- 保留显著特征:最大池化只保留窗口内最强的激活值(即最显著的特征),而平均池化会将强激活值与弱激活值”稀释”在一起。
- 对噪声更鲁棒:窗口内如果有一个重要特征和几个噪声,最大池化只保留重要特征;平均池化会让噪声拉低整体输出。
- 稀疏性保持:ReLU 激活后很多值是 0,最大池化能保留非零的有用特征;平均池化把 0 也算进去,进一步稀释有效信息。
- 实验验证:在 ImageNet 等图像分类任务中,最大池化普遍表现更好。LeNet、AlexNet、VGG 等经典网络都主要使用最大池化。
平均池化的适用场景: 全局平均池化(GAP)在网络末端很常用,用于替代全连接层;某些需要保留整体信息的场景(如纹理分类)也适合使用平均池化。
池化 vs Stride 卷积:
现代网络(如 ResNet)越来越多地使用 stride > 1 的卷积来替代池化层,因为: - 带步幅的卷积是可学习的,而池化是固定操作 - 减少了信息损失 - 但池化的计算成本更低,且提供了更强的平移不变性

实例:LeNet
S2->C3的卷积操作:
LeCun 规定了如下的连接规则表(横轴是 C3 的 16 个输出通道,纵轴是 S2 的 6 个输入通道,“X”表示相连):
| S2通道 C3通道 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | X | X | X | X | X | X | X | X | X | X | ||||||
| 1 | X | X | X | X | X | X | X | X | X | X | ||||||
| 2 | X | X | X | X | X | X | X | X | X | X | ||||||
| 3 | X | X | X | X | X | X | X | X | X | X | ||||||
| 4 | X | X | X | X | X | X | X | X | X | X | ||||||
| 5 | X | X | X | X | X | X | X | X | X | X |
规律解析: C3 层的 16 个输出特征图被分成了 4 组不同的组合方式: 1. 前 6 个特征图(0~5): 每个只与 S2 中相邻的 3 个特征图相连。 2. 接下来的 6 个特征图(6~11): 每个与 S2 中相邻的 4 个特征图相连。 3. 接下来的 3 个特征图(12~14): 每个与 S2 中不相邻的 4 个特征图相连。 4. 最后 1 个特征图(15): 与 S2 中所有的 6 个特征图相连。
它是如何计算的?(以 C3 的第 0 个通道为例)
根据表格,C3 的通道 0 只与 S2 的通道
0, 1, 2 相连。 1. 系统会准备 3 个不同的
\(5 \times 5\) 卷积核。 2. 分别对 S2
的通道 0, 1, 2 进行卷积计算,得到 3 个中间结果。 3. 将这 3
个中间结果按位置相加。 4.
最后加上一个偏置项(Bias),并通过激活函数,就得到了 C3 的第 0
个特征图。
三维卷积
搞一个三维的卷积核
隐马尔可夫模型
模型本身见笔记本,这里只写HMM的经典应用:语音识别。
数据预处理
HMM 只能处理离散的观测序列(\(O = \{O_1, O_2, ..., O_T\}\))。但是人类说话的声音是连续的机械波,HMM 根本不认识,因此需要通过预处理,把音频转化成HMM看得懂的观测序列。
沿着时间轴,每隔极短的时间(比如 10 毫秒)切一刀(FFT),对这一小段声音做傅里叶变换。
傅里叶变换的结果是一个向量,也就是说,每个\(O_t\)都是一个向量。比如向量的第一个数字代表20Hz的能量有多大,第二个数字代表40Hz的能量有多大,以此类推。下图中格子的颜色深浅,代表某个频率对应的能量大小。
这一个个按时间顺序排列的向量,就是 HMM 的观测序列 \(O = \{O_1, O_2, ..., O_T\}\)

用 HMM 搭建语音识别网络
这里 \(p_{ij}\) 就是状态转移概率,\(d_{jt}\) 就是发射概率。当然这里的发射概率和之前的定义不太一样,之前的发射取决于当前状态和下一个状态,这里的发射只取决于当前状态。
那么这里的隐藏状态是什么呢?ppt下方写着:“Unit: phoneme, syllable, word”
选择 1:以“词”(Word)为基础单位(大号积木) * 怎么做: 我直接建一个包含 10 个状态的 HMM,这整个 HMM 就代表 “apple” 这个词。再建一个 HMM 代表 “banana”。 * 适用场景: 早期的声控电话,或者银行的自动语音服务(“按1请说查账,按2请说转账”)。词汇量极小(比如只有 10 个数字和几个命令),直接拿一整个词当一个状态模型,简单粗暴。
选择 2:以“音节”(Syllable)为基础单位(中号积木) * 怎么做: 中文有几百个基本音节(比如 ba, bo, bi, bu)。我给每个音节建一个 HMM。当我要识别“爸爸”(ba-ba)时,我就把两个 “ba” 的 HMM 连起来。 * 适用场景: 中文等以音节为鲜明特征的语言。
选择 3:以“音素”(Phoneme)为基础单位(最小的微型积木) —— 这是现代语音识别的绝对主流! * 怎么做: 英语有 44 个音素,我就只训练 44 个基础 HMM(就像图 3 画的那样,每个音素用 3 个状态表示)。 * 怎么拼成词: 当我要识别 “cat” 时,我不需要专门去训练一个叫 “cat” 的模型。我只需要去词典里查到 “cat” 的发音是 [k]-[æ]-[t],然后把 [k] 的 HMM、[æ] 的 HMM、[t] 的 HMM 像拼积木一样串联起来,就临时组装成了一个 “cat” 的巨型 HMM!

这个图确实比较抽象,因为它是以计算机数据结构(链表和指针)的形式来展示的。
不用担心,我们可以把它想象成一个“探险家在迷宫里留下路标”的故事。
图里的核心是在讲:当语音结束时,我们如何通过“倒推”来知道刚才到底识别出了哪几个词。
我们分两步来拆解你的疑惑:
1. 中间两个带
s的格子是啥?
s代表 Token(令牌/探险家):你可以把s想象成一个在各个单词模型(one, two, three)里穿梭的探险家。- 这两个格子的动作代表“跨越词边界并打卡”:
- 上面的
s格子:探险家刚刚走完了一个词(比如走完了 “one”),他站在终点线上。此时,他手里拿着一根线,这根线连着他以前走过的所有路标。- 下面的
s格子:探险家准备进入下一个新词了。在跨出这一步之前,他必须做一件事:原地放下一个新的路标(也就是下面那一排方块,WLR)。- 旁边那段英文 (
path id changed to...) 的意思就是:探险家把这个新路标和以前的旧路标连起来,然后把自己的“导盲犬(指针)”指向这个最新放下的路标。- 总结:这两个
s格子展示的是一个瞬间动作——Token 在离开上一个词、进入下一个词的间隙,生成了一条词链接记录(Word Link Record, WLR)。
2. 下方的链表到底谁在前谁在后?
简单来说:链表的生成顺序是从左到右的,但我们读取它(解码)的时候是从右向左“倒推”的。
我们来看看链表里的一个小方块(路标)里都有什么(看最右边大括号的解释): *
score: 走到这里的得分 *path id: 一根指向“上一个路标”的线(图中的虚线箭头) *time: 打卡的时间(比如 \(t, t-1, t-2\),\(t\) 是最新时间,\(t-3\) 是最老时间) *model id: 刚走完的单词是什么现在,让我们顺着图中的虚线箭头,玩一次“案件重演(回溯 Backtracking)”:
假设现在录音结束了(当前时间是 \(t\)),我们找到了得分最高的那个探险家(右下角的那个虚线箭头指向的源头)。
- 第一步(看最右边的格子):
- 我们看他最新放下的路标:时间是 \(t\),刚走完的词是 “one”。
- 这个路标上的
path id(虚线箭头)指向了左边一个格子。- 第二步(向左顺藤摸瓜):
- 顺着箭头来到前一个路标:时间是 \(t-1\),刚走完的词是 “one”。
- 它的箭头又指向了更左边。
- 第三步(继续向左):
- 来到时间 \(t-2\),刚走完的词是 “three”。
- 第四步(继续向左):
- 来到时间 \(t-3\),刚走完的词是 “two”。
结论出来啦! 按照我们倒推的顺序,最后走完的是 “one”,前面是 “one”,再前面是 “three”,最前面是 “two”。 把它们反转过来变成正常的时间顺序,这句话说的就是:“two \(\rightarrow\) three \(\rightarrow\) one \(\rightarrow\) one”。
💡 一个隐藏的小细节(进阶理解)
你有没有注意到,指向
t-2(three)和t-3(two)这两个格子的虚线箭头不止一根? 这说明在识别的过程中,有很多个不同的探险家(Token)在前期都走过 “two \(\rightarrow\) three” 这条路。只不过随着时间推移到 \(t\) 时刻,只有我们追踪的这一个探险家(走出了 one \(\rightarrow\) one)获得了最高分,赢得了比赛。这种结构在计算机里叫“树(Tree)”,它可以极大地节省内存。

局限性
假设你要识别单词 “rib” 还是 “rob”。
状态1 只有唯一一条出路(去状态2)。
状态4 也只有唯一一条出路(去状态5)。
问题所在(局部归一化):在传统的马尔可夫模型中,要求每个节点的所有出边概率相加必须等于 1。因为状态1只有一条出路,不管输入什么特征,它去状态2的概率被迫变成了 1.0。
这就导致了“偏置(Bias)”:模型会盲目地偏好那些“出路少”的状态(因为它们的转移概率被集中了,很容易接近1.0),而惩罚那些“出路多”的状态(出路多,概率就被平摊了,每条路分数都很低)。


上面是HMM,下面是CRF,CRF做对了
CRF(条件随机场,Conditional Random Fields)
基本要素
BEIS标注体系
B (Begin): 词的开头
E (End): 词的结尾
I (Inside): 词的中间
S (Single): 单字成词
特征模板:机器的观察视角
- \(C_0\):意思是“只看当前这个字是什么”。(\(C\)代表字,0代表当前位置)
- \(S_{-1}S_0\)(这张图新增的):意思是“不仅看当前,还要看前一个字的状态(\(S_{-1}\))和当前字的状态(\(S_0\))之间的搭配关系”。这叫做状态转移特征。
Features (特征/规则):这是机器通过看左边的语料库(Corpus)学到的“加分规则”。等号后面的数字就是“加多少分”。
- \(f(S_0 = S, C_0 = 他) = 1\):如果当前字是“他”,并且你猜它是状态 S,加 1 分。
- \(f(S_{-1} = S, S_0 = S) = 2\):如果前一个字的状态是 S,当前字的状态也是 S(即连续两个S),加 2 分。(因为语料库里“S S S”连续出现了,模型认为这种搭配很靠谱,给的分很高)。
- \(f(S_{-1} = NIL, S_0 = S) = 1\):如果这是句子的开头(前面没有状态,叫 NIL),并且当前状态是 S,加 1 分。
例子

右下角矩阵的每个值,表示某个词属于某个标注时,从句子开头到这个词能得到的最大总分。
“他”
- 如果是B/E/I,则不符合任意一条特征,得分为0
- 如果是S,符合特征 \(f(S_0=S, C_0=他)\),得1分
“爱”
- 这个词没有出现在corpus中,不触发任何规则。其得分均为1,回溯指针都指向S
“她”
- 如果是B/E/I,则不符合任意一条特征,新增得分为0,总得分仍为1。回溯指针选择第一个,B。
- 如果是S,则触发 \(f(S_0=S, C_0=她)\),得一分,变成2/B
最终回溯判断的时候取最高,比如“她”属于S的时候得分最高;回溯到B,然后再回溯到S,得到最终序列SBS。
但显然是错误的。
如果加一个特征模板 \(S_{-1}S_0\),就会变得好一点:

“他”
- 如果是 B/E/I,不触发任何规则,得分为0
- 如果是S,触发第1,5条规则,得两分
“爱”
- 如果是B/E/I,不触发任何规则,继承“他=S”的2分
- 如果是S,触发新规则(第四条),加2分,总共4分
“她”
- 如果是B/E/I,不触发任何规则,得4分
- 如果是S,触发新规则(第四条+第三条),加3分,总共7分
于是虽然没有在corpus中见过”爱“这个词,CRF还是能够正确地分词。
更长的例子
假设当前看的词为“就”,可以有以下特征:

训练

但CRF的问题在于特征模板太多了(zxq上课翻出来一个神秘文档,其中展示的特征模板有五六页)
RNN
\(h_t=\sigma (V h_{t-1} + U x_t + b)\)
$ y_t=W h_t$
\(L_t=(y_t-o_t)^2\)
\(L=\sum \limits_{t=1}^T L_t\)
反向传播:\(L\)对\(h_t\)求偏导,就要先对\(h_{t-1}\)求偏导;要对\(h_{t-1}\)求偏导,就要先对\(h_{t-2}\)求偏导…
BPTT会面临梯度爆炸/梯度消失:
- 梯度消失 (Vanishing Gradient): 如果权重矩阵 \(V\) 的特征值小于 1,且激活函数的导数 \(f'(z)\) (如 tanh 导数最大为 1,sigmoid 为 0.25)也较小,连乘结果会呈指数级衰减,趋近于 0。这意味着网络无法学习到长距离的依赖关系。
- 梯度爆炸 (Blow up / Exploding Gradient): 如果权重矩阵 \(V\) 的特征值大于 1,连乘结果会呈指数级增长,导致数值溢出,训练崩溃。
| 特性 | BPTT (随时间反向传播) | RTRL (实时循环学习) |
|---|---|---|
| 计算方向 | 先从前向后算完,再从后向前算梯度 | 从前向后算状态的同时,顺便算出梯度 |
| 权重更新时机 | 序列结束(或截断点)后批量更新 | 每个时间步都可以实时更新 |
| 空间复杂度 (内存) | O(T⋅N)O(T⋅N),随序列长度 TT 线性增长 |
O(N3)O(N3),与序列长度无关,但随网络大小立方级增长 |
| 时间复杂度 (算力) | 每个时间步 O(N2)O(N2),相对较快 |
每个时间步 O(N4)O(N4),极度缓慢 |
| 适用场景 | 大多数现代 NLP、时间序列任务 | 无限长序列、需要极度实时的场景 (现在多用近似算法) |