Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.1 为什么需要序列模型

第 1 讲我们一直在和定长向量打交道:一张 28×2828\times28 的图摊平成 784784 维,一组特征写成 nn 维向量,然后交给 MLP。这个设定有一个隐含前提——输入的形状是固定的,所以我们可以为每个输入维度配一个权重,把网络「焊」在形状上。

序列模型的第一句话就是:这个前提不成立。一句话可以是 3 个词,也可以是 300 个词;一段音频可以是 2 秒,也可以是 2 分钟;一只机械臂的传感器读数会一直流下去,没有终点。更麻烦的是,顺序本身携带信息:「狗咬人」和「人咬狗」用的是同一堆词,含义却相反;把时间序列的采样点打乱,物理意义直接消失。

于是问题变成:给定一个长度可变的、顺序有意义的符号序列,怎么设计一个参数数量不随长度变化的函数? 这一节要回答的就是这个问题——先把它写成精确的定义,再给出任务的分类学,然后从历史脉络(n-gram → 神经语言模型)说明为什么必须引入「表示学习」,最后画出本讲的三条技术主线:递推、卷积、注意力。后面七节都是在这三条主线上展开。

定义 2.1.1 · 序列建模问题

设序列 x1:T=(x1,…,xT)\boldsymbol x_{1:T}=(\boldsymbol x_1,\dots,\boldsymbol x_T),其中 xt∈Rdx\boldsymbol x_t\in\mathbb R^{d_x},长度 T≥1T\ge1 可变。序列建模(sequence modeling)是学习一个由参数 θ\theta 指定的映射 fθf_\theta,使它对任意 TT 都有定义,且

  1. 参数不变性:θ\theta 的规模与 TT 无关(∣θ∣≠O(T)\lvert\theta\rvert\neq\mathcal O(T),更不允许 O(T2)\mathcal O(T^2));
  2. 顺序敏感:fθ(x1:T)≠fθ(xπ(1:T))f_\theta(\boldsymbol x_{1:T})\neq f_\theta(\boldsymbol x_{\pi(1:T)}) 对非平凡的置换 π\pi 一般成立;
  3. 一致性:同一组 θ\theta 能作用于训练时未出现过的长度 T′T'。

按输出形态,序列任务分成四类(见 定义 2.1.2)。

这三条要求,每一条都在排除一种「偷懒方案」

把三条要求翻译成工程语言:参数不变性排除「给每个位置单独一套权重」——那正是朴素 MLP 的做法,TT 一长就崩;顺序敏感排除「把序列当成一个无序集合(如对所有词向量做平均或求和)」——词袋模型就是这么干的,它对「狗咬人 / 人咬狗」没有任何区分能力;一致性排除「pad 到训练时的最大长度再硬接 MLP」——不是不能跑,而是永远无法处理更长的输入,参数量还随最长长度线性膨胀。三条要求合起来,只剩一条路:让同一套函数在不同位置反复作用,也就是「沿序列方向的权值共享」。这正是 RNN、CNN 与注意力三种设计共同遵守的公约。

2.1.1 序列数据的三个性质与它们的代价

把上面三条要求具体化成数据侧的三个性质,并各自标注「如果不管它会付出什么」。

性质含义如果不管它对应方案
变长TT 不固定,且测试时可能比训练时更长权重形状固定 ⇒ 只能截断或 pad;截断丢信息,pad 引入无效计算与错误统计量权值共享 + 掩码 / 动态图长度
保序位置 tt 的语义依赖前文上下文词袋式平均 ⇒ 无法区分语序与搭配递推状态、位置编码、因果掩码
参数共享同一模式出现在任意位置都该由同一组参数处理每个位置一套权重 ⇒ 参数量 O(T)\mathcal O(T)、样本效率崩塌、无法外推循环权重矩阵、卷积核、注意力算子

值得强调的是第三条既是要求也是红利:权值共享让一个在位置 1 学到「主谓一致」的检测器自动适用于位置 50,这是参数效率与泛化能力的共同来源。RNN 的 WhhW_{hh}、卷积核的 ww、注意力的 WQ/WK/WVW^Q/W^K/W^V,本质上都是同一份「跨位置复用的函数」。

还有一处容易被忽略的代价:变长带来的「无效位置」必须显式剔除。把一批长度不同的序列凑成一个张量,只能 pad 到批内最长,而 pad 位置既不是数据、也不该产生梯度。如果只在模型内部跳过 pad、却忘了在损失端屏蔽它们,模型就会花一部分容量去学「预测 pad」,同时把损失均值拉向「容易的位置」——这也是序列任务里最常见的隐性偏差来源(2.1.6 给出屏蔽写法)。

2.1.2 任务的分类学:序列 → 什么

定义 2.1.2 · 序列任务的四种形态

按「输入是序列 / 输出是序列」以及「对齐方式」,序列任务分为四类:

形态 目标函数 输出与输入的长度关系 典型例子
序列 → 标签 y^=fθ(x1:T)\hat y=f_\theta(\boldsymbol x_{1:T}) 输出是单个标签,与 TT 无关 情感分类、音频事件识别、心电图异常判别
同步对齐(序列 → 等长序列) y^t=gθ(ht)\hat y_t=g_\theta(\boldsymbol h_t),逐时刻输出 Tout=TinT_{\text{out}}=T_{\text{in}} 词性标注、命名实体识别、逐帧语音识别、逐时刻动作分类
序列 → 变长序列(异步) y^1:S=fθ(x1:T)\hat{\boldsymbol y}_{1:S}=f_\theta(\boldsymbol x_{1:T}),S≠TS\neq T 一般成立 长度由任务决定 机器翻译、语音转写、摘要
自回归生成 建模 p(xt∣x<t)p(\boldsymbol x_t\mid \boldsymbol x_{&lt;t}),把上一步的输出接回输入 逐步生成,长度自定 语言模型、音乐生成、代码补全

四类的差别直接决定结构的选择:同步对齐只需要「看一眼过去」,可以用因果(单向)结构;序列 → 标签常常允许看完整句子,所以编码器可以双向;序列 → 变长序列需要一个「编码器 + 解码器」的组合(2.4 的动机);自回归生成则强制因果——第 tt 步只能看 ≤t\le t 的位置,任何「偷看未来」的结构在推理时都不成立。

分类学为什么重要:它决定了哪些信息「允许被看到」

这四类任务最实用的区别不是输出形状,而是因果约束。同步对齐与自回归生成是因果的:位置 tt 的输出不许依赖 t+1t+1 的输入;序列 → 标签(句级分类)与编码器部分则是非因果的:整句话在手上,双向看没问题。搞混这一条会出现两类经典事故:给语言模型用了双向编码器(训练指标极好、生成时无从下手),或者反过来给「实时逐帧预测」的任务用了双向结构(离线评测漂亮,上线必然失败——因为在线时未来还没发生)。本书后面凡是提到「双向」,都会同时点明它是否可用。

2.1.3 与 MLP / CNN 的根本差别

三种结构处理「空间(位置)方向」的方式完全不同,把它们并排看一次,后面的取舍就都能解释了。

结构跨位置的信息通路感受野参数量随 TT 的变化能否并行(沿位置)长程依赖能力
MLP(把序列摊平)无共享,位置各配权重全部位置(但不是共享的)O(T)\mathcal O(T)(甚至 O(T2)\mathcal O(T^2))能理论上有,实际不可训练 / 不可外推
因果膨胀卷积局部核 + 空洞(dilation)堆叠随层数指数扩张O(1)\mathcal O(1)(核大小固定,与 TT 无关)能需要足够多堆叠层;路径长度 O(log⁡T)\mathcal O(\log T) 级
递推网络(RNN 族)状态沿时间串行传递无界(原则上)O(1)\mathcal O(1)不能(依赖前一步)形而上无界,实际被梯度消失截断在几十步
注意力任意两位置直接相连全局O(1)\mathcal O(1)(与 TT 无关)能路径长度 O(1)O(1),但与 T2T^2 的算力换

读这张表要抓住两处对比。第一处是「并行」这一列:MLP 与卷积天然并行,因为它们对每个位置的处理互不依赖;RNN 不能并行,因为 ht\boldsymbol h_t 依赖 ht−1\boldsymbol h_{t-1}——这是它的效率原罪,门控(2.3)改善的是记忆而不是并行。第二处是「路径长度」:在递推结构里,位置 1 的信息要经过 T−1T-1 次状态更新才能影响位置 TT,梯度同样要走 T−1T-1 步(2.2 的主题);而注意力里任意两点之间只有一步。

把序列当图像处理行不行?

既然卷积「并行 + 参数与长度无关」,为什么不直接把一维序列当成 1×T1\times T 的图像,用卷积一路堆下去?答案在因果性与感受野的性价比上。因果卷积必须只用左边的点(否则就是偷看未来),于是要么把核开得很大(参数量与计算量随之上涨),要么用空洞卷积堆很多层(层数 O(log⁡T)\mathcal O(\log T),深了以后训练与前向延迟都变贵)。更要紧的是,逐帧在线预测要求「每来一个新采样点就能立刻给出输出」,卷积需要每步重新算一个窗口,递推只需要更新一次状态——这正是 RNN 在流式场景里至今仍有位置的原因(见 2.2 的陷阱框)。本章不把卷积当作序列建模的第三条主线展开,但请记住:「用局部窗口 + 膨胀」与「用状态递推」是同一种取舍的两种写法。

例 2.1.2 · 膨胀卷积的感受野与路径长度

考虑因果膨胀卷积(causal dilated convolution):核宽 k=2k=2、第 ii 层空洞率 di=2 i−1d_i=2^{\,i-1}(i=1,2,…i=1,2,\dots)。

(1) 推导堆 LL 层后的感受野(能回看多远)RLR_L。 (2) 设目标上下文长度 T=1024T=1024 与 T=106T=10^{6},分别需要多少层? (3) 位置 1 的信息传到位置 TT 的路径长度是多少?与 RNN 相比如何?

解

(1) 第 ii 层的核跨越 did_i 个间隔、k=2k=2 个点,因此把感受野扩大 (k−1)di=di(k-1)d_i=d_i:

RL=1+∑i=1L(k−1)di=1+∑i=1L2 i−1=2L.R_L=1+\sum_{i=1}^{L}(k-1)d_i=1+\sum_{i=1}^{L}2^{\,i-1}=2^{L}.

(2) 由 2L≥T2^{L}\ge T 得 L≥log⁡2TL\ge\log_2T:T=1024T=1024 需 L=10L=10 层;T=106T=10^{6} 需 L≈20L\approx20 层。也就是说层数只需随 TT 对数增长——这是卷积式方案的核心优势(RNN 要覆盖 TT 步需要 TT 步串行,注意力要 O(T2)O(T^2) 次比较)。

(3) 因果卷积里位置 1 与位置 TT 之间的信息路径长度为 L=log⁡2TL=\log_2T(每一层跨一个更大的间隔)。对比:RNN 是 O(T)\mathcal O(T) 的串行依赖链,注意力是 O(1)O(1) 但每对位置都要算一次。三者的「路径长度—每步成本」组合正好构成 method 2.1.1 那张表的三行。

一个必须补的限定:感受野大不等于信息能无损传过去。卷积层数一多,梯度同样要穿过 LL 个非线性层,而且卷积核本身可视为一种稀疏矩阵——卷积式方案的长程能力同样依赖残差连接与归一化来维持。「参数量与长度无关」只是入场券,不是长程建模能力的保证。 ■\blacksquare

∎

2.1.4 历史脉络:从 n-gram 到神经语言模型

在神经网络之前,序列建模有一套成熟的统计做法:n-gram 语言模型。它清晰、可解释、可枚举,也正好暴露了「不做表示学习」的代价。

定义 2.1.3 · n-gram 语言模型与马尔可夫假设

给定词表大小 VV 与序列 x1:T\boldsymbol x_{1:T},nn-gram 模型用马尔可夫假设截断历史:

p(x1:T)=∏t=1Tp(xt∣xt−n+1:t−1),p^(xt∣xt−n+1:t−1)=count(xt−n+1:t)count(xt−n+1:t−1).p(\boldsymbol x_{1:T})=\prod_{t=1}^{T}p(x_t\mid x_{t-n+1:t-1}),\qquad \hat p(x_t\mid x_{t-n+1:t-1})=\frac{\mathrm{count}(x_{t-n+1:t})}{\mathrm{count}(x_{t-n+1:t-1})}.

n=1n=1 退化为按词频采样(完全丢掉顺序),n=2n=2 只看前一个词(bigram),n=3n=3 看前两个词(trigram)。参数表就是所有 nn-gram 的条件概率,规模约 VnV^{n}。

n-gram 的两个死角:参数爆炸与数据稀疏

V=5×104V=5\times10^4 是中等词表的量级。于是:bigram 表约 2.5×1092.5\times10^9 个计数单元;trigram 约 1.25×10141.25\times10^{14};5-gram 约 3×10233\times10^{23}——后者已经超过地球上可用的存储能力好几个数量级。更糟的是即使表能建起来也填不满:一个 101010^{10} 词级别的语料里,绝大多数 5-gram 组合从未出现,概率会被估成 0(于是整句概率为 0)。经典的做法是各种平滑(把一部分概率质量分给未见组合,如 Kneser–Ney 类方法),但平滑只能在「已经离散化」的计数上做修补,它无法回答一个更本质的问题:「猫」和「狗」没见过同一堆上下文,但它们本该相似——因为它们作为词本身是相似的。要让模型利用这种相似性,必须先把词变成向量,而这就是表示学习的入口(第 3 讲与第 6 讲会正式展开词嵌入与大模型训练)。

例 2.1.1 · n-gram 的参数量与它为什么救不了长依赖

设词表 V=5×104V=5\times10^4,语料规模 N=1010N=10^{10} 个 token。

(1) 分别估算 n=1,2,3,5n=1,2,3,5 时参数表(计数单元)的规模,并与 NN 比较。 (2) 假设我们要建模「相隔 100 个词的主谓一致」,nn 需要取多大?给出参数表规模。 (3) 用上面的数字说明:为什么 n-gram 与神经网络必须解决的是两个不同的问题。

解

(1) 表规模约 VnV^n(更精确地说是 VnV^n 个可能的 nn-gram 组合):

nn 组合数 VnV^n 与语料 N=1010N=10^{10} 之比
1 5×1045\times10^{4} 5×10−65\times10^{-6}(远小于语料,可估准)
2 2.5×1092.5\times10^{9} 0.250.25(每个二元组平均只见到 4 次)
3 1.25×10141.25\times10^{14} 1.25×1041.25\times10^{4}(绝大多数单元没数据)
5 3.1×10233.1\times10^{23} 3.1×10133.1\times10^{13}(完全不可行)

(2) 马尔可夫假设的阶数 nn 决定了「看多远」:要覆盖 100 步之前的依赖,至少需要 n≈101n\approx101,则表规模约 V101V^{101},任何存储与估计算法都无意义。这说明 n-gram 的长程依赖不是「算得慢」,而是「结构上就不支持」。

(3) 两个问题的分工是:n-gram 的问题在如何用有限数据估计一个巨大的离散概率表(统计估计、平滑、回退);神经网络的问题在如何用一个参数量可控的连续函数压缩历史(表示学习、梯度优化)。前者无论怎么改进都受限于 VnV^n,后者把参数从「所有组合」变成「一个共享的函数」——这正是 定义 2.1.1 第 1 条「参数量与 TT 无关」的意义。■\blacksquare

∎

2.1.5 三条主线:递推 / 卷积 / 注意力

把历史与结构合起来看,处理序列的现代方案只有三类:把历史压进状态(递推),用局部窗口扫(卷积),或者直接对所有位置加权求和(注意力)。三者可以互相嵌套——今天的主流模型往往是「注意力为主干 + 卷积做局部 + 递推做流式」的混合体(见 2.7)。

三代方案的对照
递推(RNN / LSTM / GRU / SSM) 卷积(因果膨胀卷积) 注意力(Transformer / 线性注意力)
状态 / 记忆 固定维度向量 ht\boldsymbol h_t(有损摘要) 无显式状态,靠层叠感受野 全部位置的键值对(无损但昂贵)
每步计算量 O(d2)\mathcal O(d^2)(单步) O(kd2)\mathcal O(kd^2)(窗口 kk) 训练 O(T2d)\mathcal O(T^2d);增量解码 O(Td)\mathcal O(Td)
沿位置并行 否(必须串行) 是 是
任意两点的路径长度 O(T)\mathcal O(T) O(log⁡T)\mathcal O(\log T) 级(多层膨胀) O(1)\mathcal O(1)
推理时的内存 O(1)\mathcal O(1)(只留状态) 需要窗口缓冲 通常 O(T)\mathcal O(T)(KV cache)
最适合 流式 / 超长序列 / 小数据 局部结构强的信号(音频波形、传感器) 需要长程精确检索的大规模训练
代表节点 2.2 RNN、2.3 门控、2.7 SSM 因果卷积体系(本讲不展开) 2.4–2.5 Transformer

一句话记住这张表:递推省内存、卷积省结构、注意力省路径——三者的代价分别是「不能并行」「感受野要堆」「平方开销」。

选型清单:先问四个问题

真正的结构选择不是「哪个模型最强」,而是按约束逐层排除:

  1. 因果约束:输出必须在读到当前输入后立刻给出吗?是 ⇒ 只能用因果结构(递推 / 因果卷积 / 带掩码的注意力);否 ⇒ 可以先做双向编码(2.1.2)。
  2. 序列有多长:几百以内 ⇒ 注意力最省心;上万 ⇒ 必须处理 T2T^2(滑窗 / 稀疏 / 状态空间 / 分块,见 2.6)。
  3. 数据有多少:几千条以内 ⇒ 带强归纳偏置的递推或卷积通常更稳;数据足够大 ⇒ 注意力类结构的收益才会显现(这条是工程经验,不是严格定理)。
  4. 部署形态:流式 / 端侧 ⇒ 递推与状态空间(每步 O(1)O(1) 内存);离线批处理 ⇒ 注意力与卷积(并行度高)。

四条的顺序有意义:因果约束先于长度,长度先于数据量,数据量先于部署形态——越靠前的条件越不可协商,先按它砍掉一半选项,再比较剩下的。

这三条主线不是互斥的选项,而是三种可组合的机制

工程上真正被采用的方案几乎都是混合体:注意力负责全局检索(「我需要哪个位置的信息」),卷积负责局部模式(相邻 token 的搭配、音素的短时结构),递推或状态空间负责流式与超长上下文(每步 O(1)O(1) 的内存)。2.7 会把这类混合架构(如按比例交错注意力层与状态空间层)单列一节。理解本讲的方式不要是「记住三个名字」,而是「记住三笔账」——每引入一种机制,就问它把哪一笔账变便宜了、又把哪一笔变贵了。

2.1.6 代码:变长序列怎么进网络(批处理、掩码与形状)

序列代码里最容易出错的地方不是模型,而是批处理:同一个 batch 里的序列长度不同,必须 pad 到等长,而 pad 出来的位置不是数据。下面这段代码把三件事一次说清:形状约定、打包(packing)、以及掩码该加在哪里。

import torch, torch.nn as nn

B, T_max, d_x, d_h = 4, 6, 8, 16
x = torch.randn(B, T_max, d_x)          # 批优先(batch-first):(B, T, d)
lengths = torch.tensor([6, 4, 2, 3])    # 每个样本的真实长度(注意 T_max=6)

# ① 掩码:True 表示「是 pad,不算」
idx = torch.arange(T_max).unsqueeze(0)              # (1, T)
mask = idx >= lengths.unsqueeze(1)                  # (B, T) bool

# ② 让循环网络跳过 pad:入参必须是长度降序(或 enforce_sorted=False)
packed = nn.utils.rnn.pack_padded_sequence(
    x, lengths, batch_first=True, enforce_sorted=False)
gru = nn.GRU(d_x, d_h, batch_first=True)
out_packed, h_n = gru(packed)
out, _ = nn.utils.rnn.pad_packed_sequence(out_packed, batch_first=True)
print(out.shape)    # (4, ?, 16):packed 后按各样本真实长度还原,T 维随样本而变
print(h_n.shape)    # (1, 4, 16):num_layers*num_directions = 1, 批, 隐维

# ③ 损失端:pad 位置的 logits 必须被屏蔽(否则模型在学「预测 pad」)
logits = torch.randn(B, T_max, 10)                  # 例如 10 类 token
target = torch.randint(0, 10, (B, T_max))
loss = nn.functional.cross_entropy(
    logits.reshape(-1, 10), target.reshape(-1), reduction="none")
loss = loss.view(B, T_max)
loss = (loss * (~mask)).sum() / (~mask).sum()        # 只对真实位置求均值
print(loss.item())

三个必须记住的点:

  1. 形状约定是批优先:(B,T,d)(B,T,d) 中第 0 维永远是批、第 1 维是时间。这与第 1 讲的「批维不变」一脉相承,所有序列算子(RNN、注意力、卷积)都遵守它。
  2. h_n 不是「每个样本最后一个真实时刻的状态」——如果不做 packing,循环网络会把 pad 也走一遍,返回的第 Tmax⁡T_{\max} 步状态对应的是 pad 之后的状态。用 pack_padded_sequence 才能拿到「真实末尾」的状态;这也是「分类任务用最后一个隐状态」这类写法最常见的翻车点。
  3. 掩码要出现在损失里:只在模型里跳过 pad 是不够的,输出端的交叉熵同样要被屏蔽,否则 pad 位置会持续贡献梯度。
量形状在代码里是谁说明
输入(已 pad)B×Tmax⁡×dxB\times T_{\max}\times d_xxTmax⁡T_{\max} 是批内最长长度,不是全局最长
真实长度BBlengths每个样本的有效时间步数
掩码B×Tmax⁡B\times T_{\max}maskTrue 表示 pad;损失端用它按元素清零
循环网络输出B×Tmax⁡×dhB\times T_{\max}\times d_hout每个时间步的隐状态(pad 位置无意义)
末状态(层数×方向)×B×dh(\text{层数}\times\text{方向})\times B\times d_hh_n不打包时它对应 pad 之后的状态,这是最常见的隐性 bug
logitsB×Tmax⁡×VB\times T_{\max}\times VlogitsVV 为词表 / 类别数;拼接成 (BTmax⁡,V)(BT_{\max},V) 后交给交叉熵
本节四个常见误解
  1. 「序列任务就是把序列 pad 到定长再用 MLP」:能跑,但违反 定义 2.1.1 的两条要求——参数量随最长长度增长,且测试时更长的序列无从处理。pad + MLP 只适合样本长度高度集中(如固定窗口振动信号)的窄场景。
  2. 「词袋 + MLP 与 RNN 差不多,反正最后都做分类」:词袋丢弃了顺序(定义 2.1.1 第 2 条)。对「否定词、语序、修饰关系」敏感的任务(情感分类里的「不」、命名实体里的搭配)会系统性变差,且差距随句子变长扩大。
  3. 「序列 → 标签任务里,用双向结构总比单向好」:只有在「整段输入在推理时已经全部可获取」时才成立。逐帧在线任务(实时语音、在线动作识别)不能双向,因为未来数据还没产生——这是任务约束而不是模型偏好问题。
  4. 「n-gram 完全没有价值,只是历史」:n-gram 至今仍在工程里充当基线、语音识别系统的浅融合特征与拼写纠错的一环;它的价值在于快且可解释。本书强调它的不足,是为了说清「神经网络换了什么问题来解」,而不是宣告它无用。
即时自测 · 2.1

Q1 用一句话写出序列建模的三条要求,并各举一个「违反它」的做法。

Q2 词表 V=104V=10^4、语料 N=109N=10^9:trigram 的条件概率表有多少个单元?平均每个单元能分到多少计数?这会导致什么问题?

Q3 判断以下两个任务是否允许使用双向编码器,并说明理由:① 句级情感分类;② 在线实时手语逐帧翻译。

Q4 因果膨胀卷积与 RNN 都能让「参数量与 TT 无关」,它们在在线流式推理上的关键差别是什么?

参考要点

Q1 ① 参数不变性——违反者:给每个位置配一套权重的摊平 MLP;② 顺序敏感——违反者:对所有词向量取平均的词袋模型;③ 一致性(可外推到未见长度)——违反者:把序列截断 / pad 到固定长度再交给固定形状的网络。

Q2 V3=1012V^3=10^{12} 个单元,而语料只有 10910^9 个 token,平均每个单元 10−310^{-3} 次——也就是说绝大多数 trigram 的计数为 0,直接做最大似然估计会得到大量零概率。需要平滑 / 回退把概率质量分给未观测组合,但平滑无法解决「没见过同一上下文的相似词应该共享统计强度」这件事。

Q3 ① 允许:整句话在推理时一次给全,双向可以同时利用左右上下文,通常更准;② 不允许(至少不能直接用双向):第 tt 帧的输出在推理时必须立刻给出,此时 t+1t+1 之后的数据尚不存在,双向结构会造成训练与部署的不一致(离线指标虚高);若一定要用双向模型,需要引入固定的延迟窗口并明确标注它是「带延迟」的在线系统。

Q4 关键差别在每个新采样点到达时的增量计算量:RNN 只需更新一次状态(O(d2)\mathcal O(d^2)),卷积需要重新算一个窗口的卷积(O(kd2)\mathcal O(kd^2),kk 为窗口大小),且需要维护窗口缓冲。因此超低延迟的流式场景更偏向递推类结构(含 2.7 的状态空间模型),而离线或允许批处理的场景更偏向并行度高的卷积与注意力。

延伸阅读
  • MIT 6.S191《Introduction to Deep Learning》第 2 讲 Deep Sequence Modeling(官网 introtodeeplearning.com)——母本对「为什么需要序列模型」的原始讲法,配合它的音乐生成实验一起看。
  • 本书 Lab 1 · 深度学习入门与音乐生成——把本节的三条要求落到「旋律 = token 序列」这个具体任务上。
  • 经典统计语言模型与平滑(n-gram、Kneser–Ney 类方法)——任何一本统计自然语言处理教材的相关章节;本书不引具体编号,只要求你理解「VnV^n 表 + 平滑」和「共享函数 + 梯度」是两种不同的世界观。
  • 关于「长依赖为什么难学」的经典分析,见 2.2 的延伸阅读(Bengio 等 1994;Pascanu 等 2013)。

本节小结:序列建模的三条要求(变长 / 保序 / 参数不变)把「给每个位置配一套权重」和「把序列当无序集合」两条捷径都堵死了,只剩「沿序列方向共享同一套函数」这条路。任务的四种形态(序列→标签、同步对齐、异步序列→序列、自回归生成)之间最重要的差别是因果约束,它决定了结构可选单向还是双向。n-gram 的历史告诉我们:不做表示学习,参数会按 VnV^n 爆炸且数据永远稀疏。下一节从最朴素的那条共享之路开始——让网络自带状态,读一步更新一步,并看看梯度沿时间传回去时会发生什么。