Part V · 第 17 章 网络架构 · 17.2

RNN 与状态空间模型

机器人的一帧传感器读数往往不够:速度要从几帧位置里推出来,负载要从关节怎样响应里看出来。循环神经网络(RNN)给网络一个随时间更新的隐藏状态,它就是 13.2 节的状态空间模型,只是矩阵从数据里学出来。本节在浏览器里现场训练 RNN 估计关节速度,看梯度为什么会随时间消失或爆炸、LSTM 与 GRU 的门怎样修好它,再看 S4、Mamba 这类线性状态空间层:它们能像卷积一样并行训练、像滤波器一样逐步推理,Mamba 还让步长随输入变化,从而学会“挑着记”。

约 45 分钟 前置:16.3 反向传播 · 13.2 状态空间与稳定性 · 4.8 卡尔曼滤波 · 17.1 MLP 与 CNN 6 个交互

    1为什么机器人需要它

    17.1 节的 MLP 和 CNN 都是“一次看一张”:同样的输入永远给出同样的输出。可机器人关心的很多量不在任何一帧读数里。关节编码器只给角度,速度要靠前后几帧之差;腿式机器人的本体感觉(proprioception,关节角、角速度、IMU)里没有“地面有多滑”这一项,只有当腿踩下去、关节怎样响应时才看得出来;夹爪闭合后物体被手挡住,相机这一帧看不到它,但上一秒看到过。这些问题的共同点是:答案藏在历史里,用 20.1 节的话说,观测只是部分可观测的。

    经典的答案是滤波:4.8 节的卡尔曼滤波维护一个状态估计,每来一个测量就更新一次,更新规则来自人写的运动模型和噪声模型。学习的答案是同一个结构,但让数据来写规则:给网络一个隐藏状态 \(\mathbf h_t\),每一步用当前输入和上一步的状态算出新的状态。这就是循环神经网络(RNN)。它有两个大家族:带非线性和“门”的 RNN(LSTM、GRU),以及把递推保持为线性的状态空间模型(S4、Mamba),后者正是 13.2 节的 \(\mathbf x_{k+1}=A_d\mathbf x_k+B_d\mathbf u_k\),矩阵由训练得到。

    为什么不干脆把最近 \(n\) 帧拼起来喂给 MLP,或者用 17.3 节的 Transformer 看整段历史?也可以,而且很多机器人系统就是这样做的(第 6 节)。循环模型的卖点是:每个控制周期只花固定的计算和固定的内存,历史再长也一样;这在几十到几百赫兹的控制回路、跑在机载计算机上的策略里很重要。本节从“状态 = 记忆”出发,现场训练一个 RNN,看清它训练时的主要困难(梯度随时间消失或爆炸)和各种解法。

    2直觉:状态是一段压缩的记忆,特征值决定它记多久

    RNN 的全部定义就是一个递推式:

    $$ \hl{h}{\mathbf h_t}=\phi\big(W_h\,\mathbf h_{t-1}+W_x\,\mathbf x_t+\mathbf b\big),\qquad \hat{\mathbf y}_t=W_y\,\mathbf h_t+\mathbf b_y,\qquad \mathbf h_0=\mathbf 0 . $$

    \(\mathbf x_t\) 是第 \(t\) 步的输入(比如关节角读数),\(\mathbf h_t\) 是隐藏状态,\(\hat{\mathbf y}_t\) 是输出,\(\phi\) 通常是 \(\tanh\)。所有时刻共用同一组参数 \(\theta=(W_h,W_x,\mathbf b,W_y,\mathbf b_y)\):这和 CNN 在空间上共用卷积核是同一个想法,只是换到了时间上。注意记号:13.2 节里 \(\mathbf x\) 是状态、\(\mathbf u\) 是输入;机器学习的习惯是 \(\mathbf x\) 为输入、\(\mathbf h\) 为状态,本节沿用后者。把 \(\phi\) 换成恒等映射,RNN 就是 13.2 节的离散线性系统,\(W_h\) 对应 \(A_d\),\(W_x\) 对应 \(B_d\)。

    线性的情形最好理解记忆。在第 5 步给一个脉冲 \(\mathbf x_5\),之后输入为零,则 \(\mathbf h_{5+k}=W_h^k W_x\mathbf x_5\):脉冲的痕迹每一步乘一次 \(W_h\)。把 \(W_h\) 分解到特征向量上,每个分量按特征值 \(\hl{lam}{\lambda}\) 的幂变化:\(|\lambda|<1\) 时按 \(|\lambda|^k\) 衰减,半衰期是 \(\ln\tfrac12/\ln|\lambda|\) 步;\(|\lambda|>1\) 时指数增长;复特征值 \(\lambda=\rho e^{\pm i\omega}\) 让状态每步转过 \(\omega\) 弧度,在平面上画出螺旋。这正是 13.2 节“离散时间稳定 \(\iff\) 特征值在单位圆内”。下图是一个 2 维隐藏状态,\(W_h=\rho R(\omega)\)(旋转乘缩放),它的特征值就是 \(\rho e^{\pm i\omega}\);拖动特征值,看同一个脉冲被记住多久。

    FIG 1记忆就是特征值:一个脉冲在 2 维隐藏状态里能留多久
    左:复平面。青色虚线是单位圆,琥珀色实心点是可拖动的特征值 \(\lambda=\rho e^{i\omega}\),空心圈是它的共轭 \(\bar\lambda\)(实矩阵的复特征值总是成对出现)。右:输入与状态随时间的变化,第 5 步有一个大小为 \(A\) 的青色脉冲(只进入 \(h_1\)),琥珀线是 \(h_1\),紫线是 \(h_2\),蓝色虚线是线性情形的包络 \(\pm A\rho^{k}\)。纵轴固定在 ±2.2,超出部分被裁掉,真实的最大值看读数。“tanh”模式把 \(\phi\) 换成 \(\tanh\),其余不变。
    试一试
    • 拖动特征值,让脉冲的半衰期至少 20 步,同时仍然稳定(\(|\lambda|<1\))。读数会告诉你需要 \(|\lambda|\ge0.966\):长记忆意味着特征值紧贴单位圆。
    • 在“线性”模式把 \(|\lambda|\) 拉到 1.05 以上,状态无界增长;再切到“tanh”:状态被限制在 ±1 之内,但它落到一个自激的圆周运动上,不再回到 0,而且换不同的脉冲大小 \(A\),最后转的圈几乎一样大:它已经不再记录输入了。
    核心洞见

    隐藏状态是网络为自己写的“历史摘要”,它记多久、记成什么形状,由递推矩阵的特征值决定:贴近单位圆才记得久,越过单位圆就失控。后面会看到,同一组特征值也决定了训练时梯度能沿时间传回多远(第 4 节),所以“记得住”和“学得会”是同一个问题的两面。

    3动手:现场训练一个 RNN 速度估计器

    任务:一个关节的角度传感器有噪声(标准差 \(\sigma\),单位 rad;比如廉价电位器或从图像估计的角度),以 50 Hz 采样(\(\Delta t=0.02\) s)。我们要从读数序列 \(z_1,z_2,\dots\) 实时估计关节角速度 \(\dot q_t\)。关节的运动是两个随机正弦之和(频率 0.3 到 1.5 Hz,幅值 0.15 到 0.6 rad),每段 80 步(1.6 s)。三种估计器同台比较:

    • 差分:\((z_t-z_{t-1})/\Delta t\)。几乎没有偏差(只滞后半步),但噪声被放大 \(\sqrt2/\Delta t\) 倍,\(\sigma=0.01\) rad 时就是约 0.7 rad/s 的抖动。
    • 卡尔曼滤波(4.8 节):常速度模型 \([q,\dot q]\),加速度当白噪声,过程噪声强度在另一批轨迹上网格搜索调到最好。
    • RNN:12 个 tanh 隐藏单元,输入 \(x_t=2z_t\),输出 \(\hat y_t\approx\dot q_t/3\)(缩放只为让数值在 1 附近)。每步训练抽 8 段新轨迹,损失是前 10 步(0.2 s 的热身)之后的均方误差,用 Adam(学习率 0.01 并逐渐减小,梯度范数裁剪到 1)更新。

    怎么求梯度?把递推按时间展开:80 步的 RNN 就是一个 80 层、每层权重相同的深网络,再用 16.3 节的反向传播从最后一步往回算。这就叫沿时间反向传播(BPTT)。因为每一层是同一个 \(W_h\),它的梯度是各步贡献之和。长序列上常用截断 BPTT:每 \(K\) 步把往回传的梯度切断(状态照样往前传,只是梯度不再往更早传),省内存,也让每次更新更快。下面的 \(K\) 就是它。

    FIG 2从带噪角度估计角速度:差分、卡尔曼滤波和一个现场训练的 RNN
    左上:一段测试轨迹的角速度(rad/s)。琥珀色是真值,青色细线是差分,紫色是卡尔曼滤波,绿色是 RNN;左侧的阴影是不计入误差的 0.2 s 热身。左下:RNN 的 12 个隐藏单元 \(h_t\) 随时间的取值,蓝为正、红为负,颜色越浓绝对值越大。右:32 段固定测试轨迹上的均方根误差(对数坐标)随训练步数的变化,虚线是两个基线。改变 \(\sigma\) 时网络保留,测试集和卡尔曼滤波按新的 \(\sigma\) 重建;改变 \(K\) 会从头训练。所有随机数都有固定种子,所以同样的操作给出同样的曲线。
    • 保持默认设置按“训练”:约 150 步后 RNN 超过差分,约 300 步后超过卡尔曼滤波(0.45 rad/s),1000 步左右稳定在约 0.27 rad/s。
    • 把截断长度设为 \(K=1\)(会从头训练),训练至少 500 步:误差在 0.6 到 0.8 rad/s 附近徘徊,始终比不上卡尔曼滤波。
    有意思的细节

    (1) RNN 为什么能赢卡尔曼滤波?不是因为卡尔曼滤波“错了”:在模型正确的线性高斯系统上它就是最优的。这里它用的是通用的常速度模型,把加速度当成白噪声,而这些轨迹其实是平滑的低频正弦。RNN 从数据里学到了“这类轨迹长什么样”的先验,于是能平滑得更狠而不滞后。反过来说,它也只懂训练数据里那类运动:遇到别的运动就是 16.2 节讲的分布偏移。 (2) \(K=1\) 为什么不行?它照样把状态往前传,也照样用上一步的状态,但梯度从不告诉它“现在存下什么,几步之后才有用”:它学会了利用状态,却学不会往状态里写什么。截断长度要覆盖任务需要的记忆跨度。 (3) 换噪声:在 \(\sigma=0.01\) 训练 2000 步之后把 \(\sigma\) 拉到 0.03,网络不变,误差立刻是 0.62 rad/s,已经好于按新噪声重新调好的卡尔曼滤波(0.76)和差分(2.09);继续训练 600 步降到约 0.48。

    4另外几个视角:梯度、门、线性状态空间层与选择性

    梯度沿时间消失或爆炸

    BPTT 里,第 \(T\) 步的误差要影响第 \(T-k\) 步的参数,必须穿过 \(k\) 个雅可比的乘积 \(\partial\mathbf h_T/\partial\mathbf h_{T-k}=J_T J_{T-1}\cdots J_{T-k+1}\),每个 \(J_t=\operatorname{diag}(\phi'(\mathbf a_t))\,W_h\)。16.3 节在深的前馈网络里已经见过这种乘积;时间上的版本更糟,因为每一层都是同一个 \(W_h\),乘积就像矩阵幂:典型的增益略小于 1,几十步后就小到没有意义(消失);略大于 1,就大到数值溢出(爆炸)。下图对随机初始化的网络直接把这个乘积算出来(隐藏维数 16,输入是随机噪声),画它的最大奇异值随 \(k\) 的变化。三种单元:普通 tanh RNN、GRU、LSTM(门的定义见下一小节和第 5 节)。“增益” \(g\) 是循环权重的尺度(谱半径约为 \(g\)),“门偏置” \(b\) 让门偏向“保留”:LSTM 的遗忘门偏置取 \(+b\),GRU 的更新门偏置取 \(-b\)。

    FIG 3梯度穿过 \(k\) 步之后还剩多少:\(\|\partial\mathbf h_T/\partial\mathbf h_{T-k}\|\)
    纵轴是对数刻度的雅可比乘积最大奇异值,横轴是回传的步数 \(k\)。红:tanh RNN;绿:GRU;蓝:LSTM(状态是 \([\mathbf c;\mathbf h]\),32 维)。青色带是 \(10^{-2}\) 到 \(10^{2}\),梯度在其中才算“有用”。每个雅可比用中心差分逐列算出,乘积的范数用幂迭代求,所以这是真实网络的真实数值,不是公式里的上界。低于 \(10^{-24}\) 的部分画在下边缘之外。
    • 把增益 \(g\) 推到 2.5 左右:RNN 的梯度不再衰减,50 步后反而放大了 10 倍以上(爆炸)。
    • 把增益放回 1 附近,把门偏置调到 3 或更高:100 步后 LSTM 和 GRU 的梯度都还在 \(10^{-1.5}\) 以上,而同样增益的 RNN 已低于 \(10^{-10}\)。

    爆炸好办:训练时把梯度范数裁剪到一个上限(FIG 2 就裁剪到 1),方向不变只缩短步长(Pascanu 等,2013)。消失难办得多:梯度小到不出错也不报警,网络只是学不会长程依赖。门控单元给出的解法是:给记忆开一条加法的通路。

    门:LSTM 与 GRU

    LSTM(Hochreiter 与 Schmidhuber,1997;遗忘门由 Gers 等在 2000 年加入)另设一个“细胞状态” \(\mathbf c_t\),更新方式是 \(\mathbf c_t=\mathbf f_t\odot\mathbf c_{t-1}+\mathbf i_t\odot\mathbf g_t\):遗忘门 \(\mathbf f_t\) 决定保留多少旧内容,输入门 \(\mathbf i_t\) 决定写入多少新内容,门都是 0 到 1 之间的 sigmoid,由当前输入和上一步输出算出。沿 \(\mathbf c\) 这条直通路径,\(\partial\mathbf c_t/\partial\mathbf c_{t-1}=\operatorname{diag}(\mathbf f_t)\):没有矩阵相乘、没有 tanh 的压缩,只要 \(\mathbf f_t\approx1\),梯度就几乎原样传回去。GRU(Cho 等,2014)更省:只有一个状态,用更新门 \(\hl{gate}{\mathbf z_t}\) 在“保留旧状态”和“换成候选值”之间插值,\(\mathbf h_t=(1-\mathbf z_t)\odot\mathbf h_{t-1}+\mathbf z_t\odot\tilde{\mathbf h}_t\),\(\mathbf z_t\approx0\) 就是原样保留。

    下面用一个需要长记忆的任务对比普通 RNN 和 GRU。机器人在 \(t=0\) 看到一个提示(比如“放进左边还是右边的箱子”,编码为 \(+1\) 或 \(-1\)),接下来 \(D\) 步同一个通道上只有干扰噪声(标准差 1,和提示一样大),最后一步来一个“执行”信号,网络要输出当初的提示。两个网络都是 8 个隐藏单元,用同样的种子、同样的数据、同样的优化器同时训练。GRU 的更新门偏置初始化为 \(-3\)(\(z\approx0.05\),每步保留 95%),这和给 LSTM 的遗忘门加正偏置是同一个技巧。

    FIG 4延迟回忆:普通 RNN 对 GRU
    左,从上到下:一条测试序列的输入通道(琥珀色竖条是 \(t=0\) 的提示,青色是干扰,最右端的标记是“执行”信号);RNN 的 8 个隐藏单元;GRU 的 8 个隐藏单元(蓝正 红负);GRU 的更新门 \(z\)(越接近琥珀色越接近 1,即“覆盖”;越接近底色越接近 0,即“保留”)。右:200 条测试序列上的正确率随训练步数的变化,红为 RNN,绿为 GRU,虚线是 50%(瞎猜)与 95%。训练最多 2000 步后自动停止。
    • 选延迟 \(D=10\) 并训练:两个网络都在几百步内达到 95% 以上,短记忆对普通 RNN 不成问题。
    • 选 \(D=60\)(更新门偏置保持 \(-3\))并训练:GRU 约 300 步就超过 95%,RNN 一直在瞎猜附近(低于 70%)。按“换种子”多试几次:在种子 1 到 10 里,RNN 碰巧学会了 3 次(一次在 300 步时,另两次要 1000 步以上),GRU 每次都在 300 到 500 步内学会。

    还可以把 GRU 的门偏置改成 0 再训练 \(D=30\) 或 \(D=60\):默认种子下 2000 步内 GRU 也学不会。门本身只提供了“可以保留”的通路,初始化得让网络一开始就倾向于保留,梯度才过得去:结构和初始化缺一不可。

    线性状态空间层:卷积着训练,递推着推理

    另一条路是反过来:把递推里的非线性去掉,非线性放到层与层之间。一层就是一个连续时间线性系统 \(\dot{\mathbf h}=A\mathbf h+B x,\ y=C\mathbf h\)(每个输入通道一份),按步长 \(\Delta\) 用 13.2 节的零阶保持离散化:\(\bar A=e^{\Delta A}\),\(\bar B=A^{-1}(e^{\Delta A}-I)B\),于是 \(\mathbf h_t=\bar A\mathbf h_{t-1}+\bar Bx_t\)。线性时不变带来一个大礼:把递推展开,

    $$ y_t=\sum_{k=0}^{t} C\bar A^{k}\bar B\,x_{t-k}=(\hl{kernel}{\bar{\mathbf K}}*x)_t,\qquad \bar{\mathbf K}=\big(C\bar B,\ C\bar A\bar B,\ C\bar A^2\bar B,\ \dots\big). $$

    整层就是输入与一个固定卷积核 \(\bar{\mathbf K}\) 的卷积。训练时整段序列已知,先算出卷积核,再用 FFT 一次卷完,\(O(L\log L)\) 且在时间上完全并行;推理时又可以退回递推,每步只做 \(O(N)\) 的运算、只存 \(N\) 维状态。S4(Gu、Goel 与 Ré,2022)用 HiPPO 理论设计 \(A\) 的结构与初值,让状态能记很长的历史;之后的 S4D 把 \(A\) 取成对角复矩阵,例如 S4D-Lin 的 \(A_n=-\tfrac12+i\pi n\),每个模态就是一个独立的复数标量递推。步长 \(\Delta\) 是可学习的“时间尺度”:卷积核的长度与 \(1/\Delta\) 成正比。

    递推本身也能并行。\(h_t=a_th_{t-1}+b_t\) 是仿射映射的复合,而复合满足结合律:\((a_2,b_2)\hl{scan}{\bullet}(a_1,b_1)=(a_2a_1,\ a_2b_1+b_2)\)。所以所有前缀 \(h_1,\dots,h_L\) 可以用并行前缀扫描(parallel scan)在 \(\lceil\log_2L\rceil\) 轮里算完,每一轮所有位置同时计算。扫描的另一个好处是:它不要求 \(a_t\) 与 \(t\) 无关;卷积则要求。这一点对下一小节至关重要。

    FIG 5一个对角状态空间层的三种算法:递推、卷积、并行扫描
    左上:卷积核 \(\bar K_k\)(蓝)。左下:输入(青色,一段 200 步的关节力矩指令:随机阶跃加噪声)和三种方法算出的输出:递推(琥珀色粗线)、卷积(紫色虚线)、并行扫描(绿点)。右:长度 16 的 Hillis–Steele 扫描。每一行是一轮,第 \(\ell\) 轮把位置 \(t\) 与位置 \(t-2^{\ell-1}\) 的结果用 \(\bullet\) 合并(琥珀色箭头);绿色实心点表示该位置的前缀已经完整(等于真正的 \(h_t\))。“Δ 随输入变化”时,每一步的步长是 \(\Delta_t=\Delta\,(0.2+1.8\,\sigma(3x_t))\),系统不再时不变,卷积只能用平均步长去近似。
    • 按“下一轮扫描”走完全部 4 轮:16 个前缀全部完整,读数显示它们与递推算出的状态之差在 \(10^{-12}\) 以下。递推要 16 个先后相继的步骤,扫描只要 4 轮。
    • 切到“Δ 随输入变化”:卷积的误差变得和输出本身一样大(读数里大于 0.1),扫描的误差仍是舍入误差级别。

    拖动 \(\Delta\) 看卷积核:S4D-Lin、8 个模态时,\(\Delta=0.05\) 的核在约 190 步后衰减到峰值的 1% 以下,\(\Delta=0.01\) 时约 960 步:\(\Delta\) 缩小 5 倍,记忆拉长 5 倍。直观地说,\(\Delta\) 决定了“一步”在连续时间里有多长,模态衰减率 \(\operatorname{Re}A_n=-\tfrac12\) 是按连续时间计的。

    选择性:让步长取决于输入(Mamba)

    线性时不变的层有个根本局限:它对每个输入一视同仁,同一个卷积核作用在所有位置上,没办法“这一帧重要就记住,那一帧是垃圾就跳过”。Mamba(Gu 与 Dao,2023)的做法是让 \(\Delta,B,C\) 都成为当前输入的函数,例如 \(\hl{delta}{\Delta_t}=\operatorname{softplus}(\mathbf w^\top\mathbf x_t+\beta)\)。取最简单的标量 \(A=-1\)、\(B=1\),零阶保持给出

    $$ h_t=e^{-\Delta_t}\,h_{t-1}+\big(1-e^{-\Delta_t}\big)\,x_t . $$

    \(\Delta_t\to0\) 时 \(h_t\approx h_{t-1}\):保留旧状态,忽略这一步的输入;\(\Delta_t\gg1\) 时 \(h_t\approx x_t\):用这一步的输入覆盖状态。这就是 GRU 的更新门 \(z_t=1-e^{-\Delta_t}\)!区别在于:门只由输入决定、不依赖 \(h_{t-1}\),状态更新仍然是线性的,所以依旧可以并行扫描(但不能再用卷积)。下面的任务需要这种能力:一串传感器读数 \(v_t\) 里只有带标记 \(m_t=1\) 的才有效(比如只有在足端着地时力传感器的读数才有意义,或者在夹爪闭合那一刻要锁存一个读数),目标是输出最近一次被标记的读数。一个是 4 个模态、所有参数都可学的线性时不变层;一个是只有 1 个模态、5 个参数的选择性层。

    FIG 6采样保持:时不变的 SSM 做不到,选择性 SSM 一学就会
    左上:一条 64 步的测试序列。青色点是读数 \(v_t\),琥珀色竖虚线标出被标记的步,琥珀色阶梯是目标,紫色是时不变层的输出,绿色是选择性层的输出。左下:选择性层每一步的“写入量” \(1-e^{-\Delta_t}\)(蓝色条,0 到 1)。右:64 条测试序列上的均方误差(对数坐标),虚线是“永远输出 0”的误差。“冻结 Δ”把训练好的选择性层的 \(\Delta_t\) 换成它在这条序列上的平均值:参数一个不变,只是去掉了对输入的依赖。
    • 按“训练”:时不变层很快卡在约 0.25(只比永远输出 0 好 8% 左右),选择性层约 400 步后误差低于它的 1%。
    • 训练好之后(选择性层误差低于 0.01)切到“冻结 Δ”:误差跳回 0.2 以上,和时不变层一样差。本领全在“步长随输入变化”这一点上。
    一个递推,几种看法

    卡尔曼滤波:人写模型的线性 RNN,稳态时 \(\hat{\mathbf x}_k=(I-KH)F\hat{\mathbf x}_{k-1}+K\mathbf z_k\)(第 5 节)。 RNN / LSTM / GRU:学出来的非线性递推,门让记忆有一条加法通路。 S4 类状态空间层:学出来的线性时不变递推,可以当卷积训练、当滤波器运行。 Mamba:线性递推加上只依赖输入的门,用扫描训练,相当于“不看自己状态的 GRU”。 Transformer(17.3 节):不压缩成状态,每一步直接回看整段历史,换来的是随历史增长的计算与内存。

    5再看数学

    提示:把鼠标悬停在公式中带底色的符号上,图中对应的元素会被高亮(FIG 1 的特征值与状态曲线、FIG 2 的隐藏状态、FIG 3 的梯度曲线、FIG 4 的更新门、FIG 5 的卷积核与扫描箭头、FIG 6 的写入量)。

    沿时间反向传播

    记预激活 \(\mathbf a_t=W_h\mathbf h_{t-1}+W_x\mathbf x_t+\mathbf b\),\(\mathbf h_t=\phi(\mathbf a_t)\),总损失 \(L=\sum_t\ell_t(\hat{\mathbf y}_t,\mathbf y_t)\)。令 \(\bar{\mathbf g}_t=\partial L/\partial\mathbf h_t\) 为对 \(\mathbf h_t\) 的全导数(既经过本步输出,也经过所有未来步)。链式法则给出从后往前的递推:

    $$ \bar{\mathbf g}_t=W_y^\top\frac{\partial\ell_t}{\partial\hat{\mathbf y}_t}+W_h^\top\boldsymbol\delta_{t+1},\qquad \boldsymbol\delta_t=\phi'(\mathbf a_t)\odot\bar{\mathbf g}_t,\qquad \frac{\partial L}{\partial W_h}=\sum_t\boldsymbol\delta_t\,\mathbf h_{t-1}^\top,\quad \frac{\partial L}{\partial W_x}=\sum_t\boldsymbol\delta_t\,\mathbf x_t^\top,\quad \frac{\partial L}{\partial\mathbf b}=\sum_t\boldsymbol\delta_t $$

    递推从 \(\boldsymbol\delta_{T+1}=\mathbf 0\) 开始。这就是 16.3 节反向传播在展开图上的样子:前向时要存下全部 \(\mathbf h_t\)(内存 \(O(TH)\)),反向的计算量是前向的一个小倍数。截断 BPTT 只改一处:若 \(t+1\) 是一个长度为 \(K\) 的块的起点,就把 \(W_h^\top\boldsymbol\delta_{t+1}\) 这一项置零。FIG 2 的训练代码就是这几行,页面里的实现用中心差分做过梯度检查(误差小于 \(10^{-8}\))。

    为什么会消失或爆炸

    把递推展开,第 \(T\) 步对第 \(t\) 步状态的敏感度是

    $$ \hl{grad}{\frac{\partial\mathbf h_T}{\partial\mathbf h_t}=\prod_{s=t+1}^{T}\operatorname{diag}\big(\phi'(\mathbf a_s)\big)\,W_h},\qquad \Big\|\frac{\partial\mathbf h_T}{\partial\mathbf h_t}\Big\|_2\le\big(\gamma\,\sigma_{\max}(W_h)\big)^{T-t},\quad \gamma=\sup|\phi'| . $$

    tanh 的 \(\gamma=1\)。所以 \(W_h\) 的最大奇异值小于 1 时梯度必然按指数消失;反过来,最大奇异值大于 \(1/\gamma\) 是爆炸的必要条件(Pascanu 等,2013)。实际网络还要乘上 \(\phi'(\mathbf a_s)\le1\):输入越大、tanh 越饱和,有效增益越小,所以 FIG 3 里 \(g=1\) 时 RNN 仍在消失,要到 \(g\approx1.8\) 左右才不再衰减,\(g\approx2\) 以上才开始爆炸。爆炸用梯度裁剪 \(\mathbf g\leftarrow\mathbf g\cdot\min(1,c/\|\mathbf g\|)\) 处理;消失要靠结构(门、线性递推)和初始化。

    LSTM 与 GRU 的方程

    LSTM(四个门与候选值都由 \(\mathbf x_t\) 和 \(\mathbf h_{t-1}\) 算出):

    $$ \begin{aligned} &\mathbf f_t=\sigma(W_f\mathbf x_t+U_f\mathbf h_{t-1}+\mathbf b_f),\quad \mathbf i_t=\sigma(W_i\mathbf x_t+U_i\mathbf h_{t-1}+\mathbf b_i),\quad \mathbf o_t=\sigma(W_o\mathbf x_t+U_o\mathbf h_{t-1}+\mathbf b_o),\\ &\mathbf g_t=\tanh(W_g\mathbf x_t+U_g\mathbf h_{t-1}+\mathbf b_g),\qquad \mathbf c_t=\mathbf f_t\odot\mathbf c_{t-1}+\mathbf i_t\odot\mathbf g_t,\qquad \mathbf h_t=\mathbf o_t\odot\tanh(\mathbf c_t). \end{aligned} $$

    GRU(更新门 \(\mathbf z_t\)、重置门 \(\mathbf r_t\)、候选值 \(\tilde{\mathbf h}_t\)):

    $$ \begin{aligned} &\hl{gate}{\mathbf z_t}=\sigma(W_z\mathbf x_t+U_z\mathbf h_{t-1}+\mathbf b_z),\quad \mathbf r_t=\sigma(W_r\mathbf x_t+U_r\mathbf h_{t-1}+\mathbf b_r),\\ &\tilde{\mathbf h}_t=\tanh\big(W_n\mathbf x_t+U_n(\mathbf r_t\odot\mathbf h_{t-1})+\mathbf b_n\big),\qquad \mathbf h_t=(1-\mathbf z_t)\odot\mathbf h_{t-1}+\mathbf z_t\odot\tilde{\mathbf h}_t . \end{aligned} $$

    LSTM 沿细胞状态的直通雅可比是 \(\partial\mathbf c_t/\partial\mathbf c_{t-1}=\operatorname{diag}(\mathbf f_t)\),GRU 的雅可比里有一项 \(\operatorname{diag}(1-\mathbf z_t)\),再加上经过门和候选值的其余项;门偏向“保留”时,这一项接近单位阵,乘很多次也不衰减。所以常把 LSTM 的 \(\mathbf b_f\) 初始化为 1 或更大(Jozefowicz 等,2015 推荐 1),GRU 的 \(\mathbf b_z\) 取负值(FIG 4 取 \(-3\))。两点约定上的差别要留意:PyTorch 的 GRU 写成 \(\mathbf h_t=(1-\mathbf z_t)\odot\tilde{\mathbf h}_t+\mathbf z_t\odot\mathbf h_{t-1}\),它的 \(\mathbf z\) 是这里的 \(1-\mathbf z\);它还把 \(\mathbf r_t\) 乘在 \(U_n\mathbf h_{t-1}\) 之后而不是之前,是一个小变体。

    卡尔曼滤波就是一个线性 RNN

    沿用 4.8 节的记号(那里 \(\mathbf x\) 是状态、\(\mathbf z\) 是测量):预测 \(\hat{\mathbf x}^-=F\hat{\mathbf x}_{k-1}\),更新 \(\hat{\mathbf x}_k=\hat{\mathbf x}^-+K_k(\mathbf z_k-H\hat{\mathbf x}^-)\)。代入得

    $$ \hat{\mathbf x}_k=\underbrace{(I-K_kH)F}_{W_h}\,\hat{\mathbf x}_{k-1}+\underbrace{K_k}_{W_x}\,\mathbf z_k . $$

    增益 \(K_k\) 很快收敛到稳态值,此后这就是一个 \(\phi\) 为恒等映射、参数固定的线性 RNN,状态维数 2(FIG 2 的卡尔曼基线就是它,输出取速度分量)。差别只在参数从哪里来:卡尔曼滤波从模型 \((F,H,Q,R)\) 推出来,RNN 从数据里学出来。这也解释了 FIG 2:当模型(常速度、白噪声加速度)与真实运动不符时,从数据学参数的那一方有机会赢。

    状态空间层:离散化、卷积核与稳定性

    对角化之后每个模态 \(n\) 独立:

    $$ \bar A_n=e^{\Delta A_n},\qquad \bar B_n=\frac{e^{\Delta A_n}-1}{A_n}\,B_n,\qquad h_{t,n}=\bar A_nh_{t-1,n}+\bar B_nx_t,\qquad y_t=\sum_n 2\operatorname{Re}\big(C_nh_{t,n}\big),\qquad \hl{kernel}{\bar K_k}=\sum_n2\operatorname{Re}\big(C_n\bar A_n^{\,k}\bar B_n\big). $$

    复模态成对出现(\(A_n\) 与 \(\bar A_n\) 共轭),只存一半再取 \(2\operatorname{Re}\) 就得到实数输出。只要 \(\operatorname{Re}A_n<0\),任何步长下都有 \(|\bar A_n|=e^{\Delta\operatorname{Re}A_n}<1\):零阶保持把左半平面映进单位圆(13.2 节),所以这样参数化的层在训练中不会变得不稳定,这正是 FIG 1 那种“越过单位圆就失控”的问题的结构性解法。实际实现里常把 \(\operatorname{Re}A_n\) 写成 \(-e^{p_n}\) 之类的形式,保证它恒为负(FIG 6 的时不变层用 \(\bar a_n=e^{-\operatorname{softplus}(p_n)}\in(0,1)\),同一个道理)。

    并行扫描为什么成立

    把第 \(t\) 步写成元素 \(e_t=(a_t,b_t)\),表示映射 \(h\mapsto a_th+b_t\)。两个映射先后作用的结果仍是这种形式:

    $$ e_2\hl{scan}{\bullet}e_1=(a_2a_1,\ a_2b_1+b_2),\qquad (e_3\bullet e_2)\bullet e_1=e_3\bullet(e_2\bullet e_1)=(a_3a_2a_1,\ a_3a_2b_1+a_3b_2+b_3),\qquad h_t=\big[e_t\bullet\cdots\bullet e_1\big]_b\ \ (h_0=0). $$

    有结合律就可以随意加括号,于是“所有前缀积”能分轮并行计算。FIG 5 用的 Hillis–Steele 扫描在第 \(\ell\) 轮让每个 \(t\ge2^{\ell-1}\) 同时做 \(e_t\leftarrow e_t\bullet e_{t-2^{\ell-1}}\),\(\lceil\log_2L\rceil\) 轮后完成,总工作量 \(O(L\log L)\);Blelloch(1990)的工作量最优版本只需 \(O(L)\) 次合并、约 \(2\log_2L\) 轮。注意整个推导只用到“\(a_t,b_t\) 已知”,从没用到它们与 \(t\) 无关,所以选择性的 \(a_t=e^{-\Delta_t}\) 照样适用;Mamba 正是用一个针对 GPU 内存层级优化过的扫描来训练的。

    选择性与时不变的极限

    Mamba 让 \(\hl{delta}{\Delta_t}=\operatorname{softplus}(\mathbf w^\top\mathbf x_t+\beta)\),\(B_t\)、\(C_t\) 也是 \(\mathbf x_t\) 的线性函数,\(A\) 本身不依赖输入;FIG 6 的选择性层是它最小的版本:\(\Delta_t=\operatorname{softplus}(w_vv_t+w_mm_t+\beta)\),\(A=-1\),\(B=1\),\(y_t=c\,h_t+d\)。默认种子训练 1500 步后 \(w_m\approx9.8\)、\(\beta\approx-5.9\):有标记的步 \(\Delta_t\approx3.9\)(写入 98%),没有标记的步 \(\Delta_t\approx0.003\)(每步保留 99.7%),读数里能看到这几个数随训练变化。

    为什么时不变的层做不到?读数 \(v_t\) 在 \([-1,1]\) 上独立均匀分布(方差 \(\tfrac13\)),标记以概率 \(p=0.12\) 独立出现。任何时不变线性层的输出都形如 \(\hat y_t=\sum_k\alpha_kv_{t-k}+\sum_k\beta_km_{t-k}+c_0\)(\(c_0\) 为常数)。目标 \(y_t=v_{t-\tau}\),\(\tau\) 是距上一个标记的步数,\(P(\tau=k)=p(1-p)^k\),所以 \(\operatorname{Cov}(y_t,v_{t-k})=\tfrac13p(1-p)^k\),而 \(y_t\) 与所有 \(m_s\) 不相关(\(v\) 均值为零且与标记独立)。输入在时间上互不相关,最优系数就是 \(\alpha_k=p(1-p)^k\)、\(\beta_k=0\),能解释的方差比例为

    $$ \frac{\sum_{k\ge0}\big(\tfrac13p(1-p)^k\big)^2/\tfrac13}{\tfrac13}=\sum_{k\ge0}p^2(1-p)^{2k}=\frac{p}{2-p}\approx0.064\quad(p=0.12). $$

    (这是长序列的极限,此时 \(\operatorname{Var}(y_t)\to\tfrac13\)。)最好的时不变层也只能比“输出 0”好约 6%;FIG 6 里训练出的时不变层停在约 8%(64 步的短序列开头有一段目标恒为 0,比长序列极限稍容易一点),也就在这附近;它学到的衰减率是什么都无所谓,问题在结构。选择性层把乘积 \(m_t\cdot v_t\) 这种输入之间的乘法交互放进了递推,线性时不变系统天生没有这种能力。

    代价对比

    推理时每步的计算需要带着的记忆在长度 \(L\) 的序列上训练
    RNN / LSTM / GRU\(O(H^2)\)\(H\) 个数(LSTM 为 \(2H\))\(L\) 个先后相继的步骤
    时不变 SSM(S4、S4D)每通道 \(O(N)\)每通道 \(N\) 个数卷积:FFT \(O(L\log L)\),并行
    选择性 SSM(Mamba)每通道 \(O(N)\)每通道 \(N\) 个数并行扫描:\(\lceil\log_2L\rceil\) 轮
    Transformer(17.3 节)第 \(t\) 步 \(O(t\,d)\)随 \(t\) 增长的 KV 缓存注意力 \(O(L^2)\),并行

    6机器人中的它:历史编码器与世界模型

    FIG 2 是一个缩小的典型用法:机器人测得到 \(q\),测不到 \(\dot q\),就从历史里推。放大之后,同样的思路用来推断那些根本没有传感器的量:地面摩擦和软硬、负载质量、电机实际输出的力矩、物体在手里的姿态。做法分两派:一派用固定长度的历史窗口(几十到几百步的本体感觉),交给 MLP、一维卷积(TCN)或 Transformer;另一派用循环状态,每个控制周期更新一次。前者训练简单、没有“状态带到哪里”的问题;后者每步代价固定、能记住窗口之外的东西。选哪个往往取决于控制频率、机载算力和任务需要的记忆长度。

    真实系统里

    灵巧手:OpenAI 的 Dactyl(Andrychowicz 等,IJRR 2020)在大量随机化的仿真里训练 LSTM 策略:有了记忆,策略可以从近期历史里推断当前这一局的物理参数。 腿式机器人:ANYmal 的野外行走(Lee 等,Science Robotics 2020)用一个时间卷积网络读一长段本体感觉历史;RMA(Kumar 等,2021)用一小段最近的状态和动作估计环境参数的隐变量,见 23.2 节;俄勒冈州立大学的 Cassie 双足机器人盲走楼梯(Siekmann 等,RSS 2021)用的是带记忆的循环策略,作者认为这项能力依赖于策略的记忆,因为不看地形走楼梯是部分可观测的。 世界模型:Dreamer 系列的 RSSM 用 GRU 做确定性的状态通路,再加随机隐变量,在想象中训练策略,见 24.3 节。 Transformer 与 SSM:人形机器人行走也有用因果 Transformer 读历史的工作(Radosavovic 等,Science Robotics 2024),原理见 17.3 节;Mamba 类模型在机器人上还比较新,已有模仿学习工作尝试用它替代 Transformer 作为策略主干,看重的正是“长上下文、每步固定代价”。 工具:PyTorch 的 nn.LSTM、nn.GRU(GPU 上调用 cuDNN 的融合实现);Mamba 的官方实现以 mamba-ssm 包发布,扫描是自定义的 CUDA 核。 递推策略的强化学习:从回放池里训练循环策略时,R2D2(Kapturowski 等,ICLR 2019)把隐藏状态一起存下,并先用一段“热身”序列把状态跑热再计算损失,FIG 2 里不计分的前 10 步是同一个思路。

    7常见误区

    误区 1:训练和部署时的状态不一致

    FIG 2 的网络只在“从 \(\mathbf h_0=\mathbf 0\) 开始的 1.6 s 片段”上训练过。部署时如果每个控制周期都把状态清零,它就退化成一个只看一帧的网络;如果开机后一直带着状态跑几个小时,它就进入了训练中从未见过的状态区域,输出可能慢慢漂走。规则是:部署时怎样处理状态(何时清零、带多久),训练时就要见过同样的情形;常见做法是在一局开始时清零、训练用长短不一的序列,或者像 R2D2 那样先热身再计分。

    误区 2:截断太短,却以为“状态反正会往前传”

    截断不限制状态能携带多远的信息,但限制了网络能学会携带多远:梯度到不了的地方,就没人教它“这里该写下什么”。FIG 2 的 \(K=1\) 就是这样失败的。截断长度要不短于任务需要的记忆跨度,否则就换一种能看到更长历史的训练方式。

    误区 3:只防爆炸,不管消失

    梯度裁剪能挡住爆炸(损失突然变成 NaN 的那种),但对消失毫无作用:消失的梯度不报错,网络只是安静地学不会长程依赖,损失卡在一个平台上(FIG 4 里 RNN 在 \(D=60\) 时的正确率曲线)。用门控单元或线性递推,并把门偏置初始化成“保留”;要验证模型确实用到了长历史,就像 FIG 4 那样专门构造一个需要长记忆的测试。

    误区 4:换了控制频率却照用原来的网络

    离散 RNN 学到的是“每一步”的动态:FIG 1 里一个脉冲每步乘 \(\lambda\)。把 50 Hz 训练的网络放到 100 Hz 的回路里,同样的 \(\lambda\) 意味着连续时间里的记忆短了一半,学到的速度估计(FIG 2 里隐含的 \(\Delta t=0.02\) s)也全错了。时间步不均匀(通信抖动、丢帧)也是同样的问题。状态空间层显式地有步长 \(\Delta\),原则上可以按新的采样率改 \(\Delta\)(S4 的论文演示过换采样率直接测试),但层间的非线性部分并不知道时间单位变了,最稳妥的仍是在部署频率上训练,或把 \(\Delta t\) 作为输入喂给网络。

    误区 5:把“线性 SSM”和“RNN”当成可以随意互换

    时不变的线性层能并行训练、不会失稳,但它不能根据内容决定记什么(FIG 6,第 5 节的界);要这种能力得加选择性,而选择性又让卷积失效,只能用扫描(FIG 5)。反过来,非线性 RNN 什么都能表示,却只能顺序训练、且梯度难传。还要注意稳定性要靠参数化来保证:直接把 \(\bar A\) 当自由参数学,训练中特征值一旦越过单位圆(FIG 1),状态就会爆掉;应像第 5 节那样让 \(\operatorname{Re}A<0\) 恒成立。

    8小练习

    1. 一个线性 RNN 的 \(W_h\) 有两个实特征值,绝对值分别是 0.9 和 0.5。在 \(t=0\) 给一个脉冲,20 步之后慢模态里还剩原来的多少?
    每一步乘一次特征值,20 步后是 \(0.9^{20}\approx0.12\);它的半衰期是 \(\ln\tfrac12/\ln0.9\approx6.6\) 步。选项 C 是快模态的 \(0.5^{20}\approx10^{-6}\)。长期来看状态几乎只剩慢模态:记忆长度由最接近单位圆的特征值决定(FIG 1)。
    2. FIG 2 里用截断长度 \(K=1\) 训练的 RNN 始终比不上卡尔曼滤波。根本原因是?
    截断只切断反向传播的梯度,状态照样往前传(所以选项 A 错)。可速度估计要求网络在第 \(t-1\) 步就把位置存进状态,以便第 \(t\) 步做差、并在多步上平滑;\(K=1\) 时第 \(t\) 步的误差永远传不到第 \(t-1\) 步的写入操作。同样的 12 个单元用完整 BPTT 训练能做到约 0.27 rad/s,可见容量不是问题(选项 D 错)。
    3. LSTM 沿细胞状态的直通路径上,\(\partial\mathbf c_t/\partial\mathbf c_{t-1}\) 等于?
    由 \(\mathbf c_t=\mathbf f_t\odot\mathbf c_{t-1}+\mathbf i_t\odot\mathbf g_t\),对 \(\mathbf c_{t-1}\) 直接求导得 \(\operatorname{diag}(\mathbf f_t)\):没有循环矩阵,也没有 tanh 的导数。只要遗忘门接近 1,乘很多步也不衰减,这就是 FIG 3 里遗忘门偏置大时 LSTM 曲线保持平坦的原因。选项 C 是普通 tanh RNN 的单步雅可比。
    4. 下面哪一种层,整段序列的输出可以写成输入与一个固定卷积核的卷积?
    卷积核 \(\bar K_k=C\bar A^k\bar B\) 要求每一步的 \(\bar A,\bar B,C\) 都相同,即线性且时不变。GRU 和 LSTM 是非线性的;选择性 SSM 是线性的,但 \(\bar A_t\) 随输入变化,没有单一的卷积核(FIG 5 切到“Δ 随输入变化”时卷积误差变大)。它仍然可以用并行扫描训练,因为扫描只需要结合律。
    5. 选择性层 \(h_t=e^{-\Delta_t}h_{t-1}+(1-e^{-\Delta_t})v_t\) 在 FIG 6 里训练好之后,在没有标记的步上 \(\Delta_t\) 是什么样?
    没有标记的读数是要忽略的,所以 \(e^{-\Delta_t}\approx1\),即 \(\Delta_t\approx0\):训练 1500 步后约为 0.003,每步保留 99.7%。有标记时 \(\Delta_t\approx3.9\),一步写入 98%。\(\Delta_t=\operatorname{softplus}(\cdot)\) 恒为正,所以选项 D 不可能。这正是 GRU 的更新门 \(z_t=1-e^{-\Delta_t}\):在该保留的地方关上。

    9延伸阅读

    • J. L. Elman, Finding Structure in Time, Cognitive Science, 1990 — 简单循环网络(Elman 网络)
    • P. J. Werbos, Backpropagation Through Time: What It Does and How to Do It, Proceedings of the IEEE, 1990 — BPTT 的经典讲解
    • Y. Bengio, P. Simard & P. Frasconi, Learning Long-Term Dependencies with Gradient Descent is Difficult, IEEE Transactions on Neural Networks, 1994 — 梯度消失问题
    • S. Hochreiter & J. Schmidhuber, Long Short-Term Memory, Neural Computation, 1997;F. A. Gers, J. Schmidhuber & F. Cummins, Learning to Forget: Continual Prediction with LSTM, Neural Computation, 2000 — LSTM 与遗忘门
    • K. Cho et al., Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation, EMNLP 2014 — GRU 的出处
    • R. Pascanu, T. Mikolov & Y. Bengio, On the Difficulty of Training Recurrent Neural Networks, ICML 2013 — 消失与爆炸的条件,梯度裁剪
    • R. Jozefowicz, W. Zaremba & I. Sutskever, An Empirical Exploration of Recurrent Network Architectures, ICML 2015 — 大规模比较门控结构,遗忘门偏置取 1
    • A. Gu, K. Goel & C. Ré, Efficiently Modeling Long Sequences with Structured State Spaces, ICLR 2022 — S4
    • A. Gu, A. Gupta, K. Goel & C. Ré, On the Parameterization and Initialization of Diagonal State Space Models, NeurIPS 2022 — S4D,FIG 5 的初始化
    • J. T. H. Smith, A. Warrington & S. W. Linderman, Simplified State Space Layers for Sequence Modeling, ICLR 2023 — S5:用并行扫描计算状态空间层
    • A. Gu & T. Dao, Mamba: Linear-Time Sequence Modeling with Selective State Spaces, 2023 (COLM 2024) — 选择性状态空间模型
    • G. E. Blelloch, Prefix Sums and Their Applications, Carnegie Mellon University technical report, 1990 — 并行前缀扫描
    • OpenAI: M. Andrychowicz et al., Learning Dexterous In-Hand Manipulation, International Journal of Robotics Research, 2020 — 随机化仿真中训练的 LSTM 灵巧手策略