

© 2026 async. fork自:GitHub
介绍了循环神经网络与长短期记忆网络。
而接下来要处理的序列模型,会在这一套方法的每一个层面上都带来新的挑战。
之前讨论的模型,其输入都可以被表示为一个固定维度的向量或张量,维度在模型设计阶段是已知且固定的,模型的输出同样通常被设计为一个固定维度的向量,例如类型分数或回归值。
但是在现实世界中,存在大量任务,其本质并不是对一个整体对象做一次性判断,而是对一个随时间或顺序展开的过程进行建模。在这类问题中,输入的长度、结构乃至输出的形式,往往都无法事先固定。
例如在图像分类中,一张图被视为一个整体,模型的任务是从中提取判别性特征并输出一个类别;但是在图像描述中,输入仍然是一张图像,输出却不再是一个单一的标签,而是一个词序列,其长度依赖于语义内容本身。类似地,在视频理解任务中,输入不再是一个二维空间结构,而是一系列按照时间顺序排列的帧。模型不仅仅需要理解单帧的空间语义,还必须建模帧与帧之间的时间依赖关系;在语言建模、语音识别、金融时间序列预测等问题中,这种“输入即过程”的特征更加明显。这一类问题的共同点是输入本身携带顺序信息,而这个顺序对输出具有决定性影响。简单地将所有输入拼接成一个超长向量,不仅在计算上不可行,还会破坏这种顺序结构,因此需要引入序列建模来解决这一问题。
输入 经过 RNN 得到输出 ,这看似之前的前馈网络没有什么本质区别,但是实际上 RNN 并不是一次性处理整个输入,而是拥有一个随着序列而不断更新的内部状态,这一内部状态并不是某种额外的存储结构,而是网络计算过程本身的一部分,在每个时间步都会被重新计算。
通过将 RNN 沿时间轴展开,可以将其理解为一个在结构上类似于深层前馈网络的计算图。
其中每一个“层”对应序列中的一个时间步,层与层之间通过内部状态相连接。但是与普通深层网络不同的是,这些时间步对应的计算单元并不拥有各自独立的参数,而是严格共享同一组权重,正是这种参数共享,使得 RNN 能够以固定规模的参数来处理任意长度的序列。
而这种隐藏状态的更新,在每一时间步内,新的隐状态是由“上一时刻的隐状态”和“当前输入”共同决定的,并且这一更新由一个带参数的函数来实现,通过这种方式,模型被迫在每一步将历史信息与新信息进行融合,而不是丢弃过去或忽略当前:
而新的输出则是通过另一个带权重的映射函数从隐状态中产生,这也说明隐藏状态可以被视为一种内部表示,其维度和语义并不必然与输出空间相一致,输出只是这一内部状表示在特定任务下的一种特殊的输出:
在 Vanilla RNN 中,通用的更新公式 被具体化为线性变化后接非线性激活函数的形式。
其中 为循环权重矩阵,负责捕捉时间序列上的长期依赖, 为输入权重矩阵,负责将当前输入映射到隐空间。而 为双曲正切函数,将隐状态的值域约束在 之间,这一选择是为了防止在多层递归计算中数值发散,但是从其图像中也可以预见其在梯度回传时的潜在问题。 为输出权重矩阵。
课程通过举一个具体的手工构造 RNN案例来进行说明:定义一个任务,其输入序列为一个二值序列,例如 ,目标任务是检测序列中是否出现了连续的两个 1,为了实现这个逻辑,隐藏状态 被人为赋予了明确的物理含义,将 设定为一个 3 维向量,这三个维度分别充当了不同的“寄存器”角色:
第 0 维存储当前时刻的输入 ,第 1 维存储上一时刻的输入 ,第 2 维存储常数 1,作为一个偏置项用于后续的阈值计算。
其中 被初始化为 ,这是为了将当前输入 写入到隐藏状态的第 0 维,确保输入信号只改变 的第一个分量,而不直接干扰 和 。
被设计为以下矩阵:
第 0 行是为了让上一时刻的信息不保留在当前时刻的第 0 维中,因为第零维度是留给新输入 的。第 1 行将上一时刻的第 0 维复制到当前时刻的第 1 维,实现记忆功能。第三行则是恒等映射,保证第 2维永远保持为 1,充当一个稳定的偏置。
最终得到的完整的状态更新公式实际上执行了以下操作:
这个系统就像是一个移位寄存器,新的 进来填补 ,旧的 移位变成 ,常数 1 保持在 。
最后的输出权重 用于生成输出:
但是在复杂问题中,依靠手工设定是不可能的,因此目前的问题就变成了,应该怎么去找到 。
通过引入计算图视角,可以看到每一个时间步都是一个独立的节点实例,但是它们在参数层面是完全共享的,这种节点独立、参数共享可以让 RNN 在训练时既能表现出深层网络的特征,又具有与普通深层网络截然不同的优化行为。
在 many-to-many 的情形下,每个时间步都会产生一个输出,并且通常会在每一个时间步定义一个对应的损失项,这意味着整个序列的总损失可以被看作是所有时间步损失的累加,而梯度信号也会在时间维度上被多次注入到隐状态更新的过程中。从训练的角度来看,这种结构相对友好,模型不会完全依赖于最后一个时间步来获得监督信号。
在 many-to-one 的计算图中,隐状态仍然会在每一个时间步被更新,但输出和损失只在序列的最后时刻出现。这意味着所有关于早期输入的梯度信息,都必须通过一条横跨整个时间轴的反向传播路径,才能对早期的状态更新参数产生影响。
在 one-to-many 的计算图中,模型在初始时刻接收一次外部输入,随后完全依赖隐状态递推来生成输出序列。在这一过程中,隐状态不仅承担了“记忆输入”的职责,还成为输出生成的唯一信息来源。因此,任何关于初始输入的误差反馈,都必须通过输出序列的多个时间步反向传播回初始状态。这种结构在生成任务中非常常见,但它也意味着模型在训练时对初始状态表示的质量极其敏感。
对以上的时间展开计算图进行反向传播的过程,就是所谓的Backpropagation Through Time。在标准的时间反向传播中,模型会首先沿时间维度完整地执行一次前向传播,从初始的隐状态开始,依次计算出每一个时间步的隐状态与输出,并且最终得到一个关于整个序列的标量损失。
但是这样会导致一个问题,当序列很长的时候会导致显存不足,且训练数值不稳定,时间反向传播不是一个纯理论动作,落到具体的训练流程上会付出非常直接的工程代价。
具体的解决方案是可以通过人为设定一个窗口长度,在训练时假设模型目前只看到了这一段,在一段窗口中从 开始,按照时间步读入输入、更新隐藏状态、生成输出并计算损失函数,然后在这个窗口内做反向传播。
这个方法实际上是工程上的妥协,而不是一个理论上的完美解决方案。当时间轴变得很长,而梯度又被截断时,模型对远距离依赖的学习能力在机制层面上已经被削弱了。
隐藏状态的数值传递决定模型在推理阶段“记得住什么”,而梯度的反向传递,决定的是模型在训练阶段“能学会记住什么”,这种截断导致模型在前向时似乎拥有长期记忆,但是在训练时则缺乏塑造这种记忆的有效信号。
接着 Lecture 7 举了一个案例:Character-level Language Model。目的是训练一个 RNN,使其在看到一串字符之后,学会预测下一个字符。
这是一个标准的 many-to-many 序列任务,由于字符本身是离散符号,模型需要先就将每个字符编码为一个 one-hot 向量,one-hot 向量乘以权重矩阵,本质上等价于一次嵌入查询,在每一个时间步,embedding 向量会与上一个时刻的隐藏状态一起,经过同一个状态更新函数得到新的隐藏状态,而后 RNN 的输出会经过一个线性层和 softmax,得到所有可能字符的概率分布,而训练的目标就是让这个分布在真实“下一个字符”处的概率尽可能大。
这部分内容主要是为了探究 RNN 内部到底学到了什么而进行可视化分析。如果 RNN 真的学会了语言,那么其隐藏向量 中的某些维度应该对应具体的语言学特征,随机选取隐藏向量 中的某一个维度,将其激活值用颜色映射到对应的文本上,观察其数值随时间序列的变化规律。
" 时其激活值瞬间飙升(变红/正值),并在引号内的所有字符上保持高激活状态,直到遇到闭引号 " 时瞬间关闭(变蓝/复位)。这说明模型学会了长距离依赖和状态保持,模型知道自身处于一个引用字符串的内部,这个状态必须保持直到特定的结束符号出现。
\n 出现后瞬间重置为零。这证明模型学会了计数或边界检测,在没有任何显式的计数指令的情况下学会了在何时应该换行。
if 语句或代码块的嵌套深度,在进入 if 块时激活值增加,随着嵌套层级加深而变化,在退出代码块时恢复。这证明模型学会了层级结构,模型通过隐状态捕获了高度结构化的编程语言语法树。
这部分的讨论导出了关键的结论,实际上大部分的单元都是无法解释的,无法对应到具体的规则,这也符合分布式表示,即大部分概念并不是由单一神经元编码的,而是由多个神经元的组合模式共同编码的。
RNN 的优势在于:
于此同时也存在其缺点:
Lecture 7 使用图像描述展示了多模态和变长输出的综合案例,将计算机视觉组件与自然语言处理的组件嫁接在一起。
可以看到去除掉了原本图像分类任务中的检测头,提取出固定维度的特征向量输入到 RNN 中,RNN 负责生成相应的文本序列。CNN 输出的向量 经过一个线性变换 赋值给 RNN 的隐藏状态,这也意味着在生成每一个单词的过程中, 都会对隐藏层的更新产生影响,这样能够增强图片信息对生成过程的影响,防止 RNN 在生成长句子时“忘掉”图片中到底有什么。
为了说明为什么要提出 LSTM,对 Vanilla RNN 进行了梯度流分析。当进行时间反向传播时,目标是计算损失函数 关于初始状态或者某个早期参数的梯度。考虑时间步 反向传播到时间步 (其中 ),根据链式法则,需要计算隐状态 关于 的偏导数:
此处的关键项是 这一项展开是一系列雅可比矩阵的连乘:
将 根据 Vanilla RNN 的前向公式 展开,得到
将单步结果带入连乘公式,发现梯度在回传每一步时,都会被乘以该因子:
如果序列很长,这意味着将同一个矩阵 连乘了很多次,结果只有两种极端:
与 Vanilla RNN 仅维护一个单一的隐藏状态 不同,LSTM 在每个时间步维护两个状态向量:
此外还有四个门控信号:在时间步 ,LSTM 首先接收当前输入 和上一时刻的隐藏状态 ,通过一个大的权重矩阵 进行线性变换,然后分割成四个向量,分别通过激活函数计算出四个控制信号:
四个向量的具体物理含义与数学定义如下:
一旦计算出四个控制信号,LSTM 会执行以下两步更新操作:
其中 说明历史信息 被遗忘门 逐元素地衰减, 说明新信息 被输入门 逐元素地加权。最终通过加法运算将新旧信息进行融合。
其中 将细胞状态的值域规范化到 , 根据输出门 的指示,选择性地将细胞内部的状态暴露给外界,形成 。
根据加法更新公式 ,对 求偏导,得到:
这说明在梯度流回传的时候,梯度不再乘以一个可能导致爆炸或者消失的稠密矩阵 而是乘以遗忘门向量 , 的每个元素都在 之间,如果 ,梯度几乎就能够无损通过,如果 ,则梯度被截断。梯度的消失与否完全由网络自己控制。如果网络认为这部分历史信息对未来很重要,它就会把遗忘门设为 1,从而保护梯度不消失;而在 Vanilla RNN 中,梯度消失是结构性的缺陷,网络无法控制。
从另一个角度看,根据微积分的加法法则,梯度在通过加法节点时会原样传递,不会发生改变,因此梯度可以长时间保留并且传递到过去。
现在序列建模的新趋势是去 Transformer 化 (Beyond Transformers)。通过引入状态空间(State Space)的数学工具,Mamba 和 RWKV 等模型证明了不需要为了高性能而忍受 的计算代价,RNN 的“线性推理”特性在 LLM 时代重新焕发了光彩。
Lecture 7 的内容标志着深度学习从处理静态固定输入转向对动态序列过程的建模。首先确立了 RNN 通过参数共享和隐状态递归来处理任意长度序列的通用范式,但深入分析揭示了 Vanilla RNN 在反向传播中因矩阵连乘导致的梯度消失与爆炸问题。为此,重点引入了 LSTM 架构,通过设计精巧的门控机制与加法更新的细胞状态(Cell State),构建了无阻断的梯度流通道,从而在数学和工程上有效解决了长距离依赖的学习难题,为图像描述等复杂时序任务奠定了基础。
参考如下:
https://cs231n.stanford.edu/index.html