量化交易中文教材

第 14 章 动态网络与随时间反向传播

本章对应原书第 14 章(Dynamic Networks)。前面几章的多层网络都是静态的:输出只由当前输入决定。本章讨论带延迟和反馈的动态网络,它们逼近的是"序列到序列"的动态系统,是 RNN、LSTM 等现代序列模型的直接前身。训练用的优化算法(最速下降、共轭梯度、Levenberg–Marquardt)与第 9–12 章完全相同,唯一的新问题是梯度怎么算。本章给出两种精确算法:实时递归学习(RTRL)和随时间反向传播(BPTT)。这是本册与金融时间序列建模联系最紧密的一章,请完整掌握。

学习目标

读完本章,你应当能够:

  1. 区分静态网络与动态网络,说清 FIR(只有输入延迟)与 IIR(有输出反馈)两类动态网络的记忆长度差异。
  2. 用 LDDN 记号(\(\mathbf{IW}^{m,l}(d)\)、\(\mathbf{LW}^{m,l}(d)\)、\(DL_{m,l}\)、\(DI_{m,l}\)、输入层集合 \(X\)、输出层集合 \(U\))描述任意带延迟和反馈的网络,并确定仿真顺序。
  3. 解释权值对输出的"直接作用"和"间接作用",手推单个递归神经元的导数递推式。
  4. 写出 RTRL(向前传播 \(\partial\mathbf{a}(t)/\partial\mathbf{x}^T\))和 BPTT(向后传播 \(\partial F/\partial\mathbf{a}(t)\))的核心递推,知道二者给出同一梯度,并能比较其计算量、存储量和适用场景。
  5. 说出递归网络难训练的三个原因(梯度消失/爆炸、伪谷、训练序列代表性),并理解 LSTM/GRU 为何能缓解梯度消失。
  6. 认出 GARCH(1,1) 的方差递推正是一个线性 IIR 递归神经元,并能用 BPTT 梯度估计它。

读前导读

这一章在解决什么问题。 前几章的网络都是"无记忆"的:今天的输出只看今天的输入。金融里大多数有用的模型都有记忆:EWMA 波动率、GARCH、AR 模型,今天的值依赖昨天的值。本章讨论带反馈的"动态网络",它和这些时间序列模型是同一类东西。实际上 GARCH(1,1) 的方差递推 \(\sigma^2_t=\omega+\alpha r^2_{t-1}+\beta\sigma^2_{t-1}\) 就是本章最简单的递归神经元,14.10.2 节会把这一点做实。

训练这类网络的难点仍然是求梯度,而且是链式法则的又一次扩展。在第 11 章,一个权值通过"多条网络路径"影响输出;在本章,一个权值还通过"时间"影响输出:它改变了昨天的输出,昨天的输出又进入今天的计算。估计过 GARCH 的人可能见过这个结构:\(\partial\sigma^2_t/\partial\beta\) 里除了 \(\sigma^2_{t-1}\),还有 \(\beta\cdot\partial\sigma^2_{t-1}/\partial\beta\) 这一项。本章给出两种系统的算法:RTRL 沿时间向前推导数,BPTT 先跑完整段序列再沿时间向后推误差。二者给出同一个梯度,区别只在计算顺序和存储,和第 11 章"从前往后乘 vs 从后往前乘"是同一个道理。

需要先想起来的数学。

  • 链式法则的"多路径"形式。 若 \(w\) 既直接影响 \(y\),又通过中间量 \(u(w)\) 影响 \(y=f(w,u(w))\),则 \(\dfrac{dy}{dw}=\dfrac{\partial f}{\partial w}+\dfrac{\partial f}{\partial u}\dfrac{du}{dw}\)。第一项是直接作用,第二项是间接作用。例:\(y=w\cdot u\),\(u=3w\),在 \(w=1\) 处直接项 \(u=3\),间接项 \(w\cdot3=3\),合计 6(检验:\(y=3w^2\),\(y'=6w=6\))。见 第 00 册第 02 章 导数与泰勒展开 和 第 00 册第 05 章 多元微积分与优化,以及第 11 章 11.3.2 节的讲解框。
  • 几何级数。 \(\sum_{k=0}^{\infty}r^k=1/(1-r)\),\(|r|<1\) 时收敛,否则发散。IIR 网络的输出、EWMA 和 GARCH 的无限滞后展开都是这种形式,稳定条件 \(|lw|<1\) 就是级数收敛条件。见 第 00 册第 04 章 级数与收敛。
  • 谱半径。 方阵所有特征值的模中最大的那个,记作 \(\rho\)。矩阵连乘 \(\mathbf{W}^k\) 大致按 \(\rho^k\) 增长或衰减,这是一维 \(lw^k\) 的推广。见 第 00 册第 06 章 线性代数速成。
  • 外积、vec 与 Kronecker 积。 外积 \(\mathbf{d}\mathbf{a}^T\) 是第 \((i,j)\) 元为 \(d_ia_j\) 的矩阵(第 11 章已用过)。\(\mathrm{vec}(\mathbf{W})\) 把矩阵按列首尾相接排成一个长向量;Kronecker 积 \(\mathbf{A}\otimes\mathbf{B}\) 是把 \(\mathbf{A}\) 的每个元素 \(a_{ij}\) 替换成块 \(a_{ij}\mathbf{B}\) 得到的大矩阵。它们只用于 14.5.3 节把导数排成矩阵,逐元素看仍是普通链式法则。见第 06 章。

怎么读这一章。 必读 14.1、14.3(FIR 与 IIR)、14.4(直接作用与间接作用,全章的核心思想)、14.5.5 与 14.6.4 两个小例子、14.8(为什么难训练),以及 14.10 量化实战。14.2 的 LDDN 记号和 14.5.1–14.5.4、14.6.1–14.6.3 的一般公式记号繁重,第一次可以只读每个公式后面的"读法",把它们当作"单神经元例子的多层、多延迟版本";14.6.5 的大网络例子可以跳过。14.9 是现代 RNN/LSTM 的补充,了解即可。


14.1 从静态网络到动态网络

14.1.1 动态网络有记忆

静态网络(第 11、12、13a、13b 章的多层感知机)计算的是一个函数:给定输入 \(\mathbf{p}\),输出 \(\mathbf{a}=g(\mathbf{p})\),与此前看到过什么无关。动态网络(dynamic network)的输出还依赖过去的输入、过去的输出或内部状态。实现方式是在网络里放进延迟(delay,离散时间)或积分器(连续时间)。你在前面已经见过两种动态网络:

  • 第 10 章的自适应滤波器:输入端接一条抽头延迟线(tapped delay line, TDL),把 \(p(t),p(t-1),\dots,p(t-D)\) 同时送进神经元。它只有前馈连接。
  • 第 3 章的 Hopfield 网络:输出经延迟回送到输入,形成递归(recurrent)连接。

动态网络处理的是序列,输入的先后顺序有意义。由于响应依赖输入的历史,它能学习时序模式。一句话对比:静态多层感知机逼近函数,动态网络逼近动态系统。原书列出的应用包括动态系统控制、金融市场预测、通信信道均衡、电力系统相位检测、故障检测、语音识别、文法学习、蛋白质结构预测。

14.1.2 标准反向传播为什么不够

第 11 章的反向传播假设:网络输出只是权值和当前输入的函数。动态网络里,某层的输入可能是该层或其他层在过去时刻的输出,而这些过去的输出本身也是权值的函数。所以权值对输出有两种作用:

  • 直接作用(direct effect):把过去的输出当作常数时,权值对当前输出的影响。可以用标准反传计算。
  • 间接作用(indirect effect):权值通过改变过去的输出,再经反馈环影响当前输出。

只算直接作用得到的梯度是错的。动态反向传播(dynamic backpropagation)就是把间接作用也算进来,有两种组织方式:

  • BPTT(backpropagation-through-time,随时间反向传播,Werbos 1990):先算完所有时刻的网络响应,再从最后时刻往回算梯度。算梯度效率高,但需要整段序列,难以在线实现。
  • RTRL(real-time recurrent learning,实时递归学习,Williams & Zipser 1989):从第一时刻开始,梯度与网络响应一起向前推进。梯度的计算量更大,但每步都能得到导数,便于在线实现;计算 Levenberg–Marquardt 所需的 Jacobian 时通常比 BPTT 更高效。

14.2 分层数字动态网络(LDDN)

为了统一表示任意多条反馈和延迟线,原书把第 11 章的多层网络记号扩展为分层数字动态网络(Layered Digital Dynamic Network, LDDN)。

14.2.1 净输入与层输出

第 \(m\) 层的净输入是所有流入它的连接之和:

\[ \mathbf{n}^m(t)=\sum_{l\in L^f_m}\sum_{d\in DL_{m,l}}\mathbf{LW}^{m,l}(d)\,\mathbf{a}^l(t-d)+\sum_{l\in I_m}\sum_{d\in DI_{m,l}}\mathbf{IW}^{m,l}(d)\,\mathbf{p}^l(t-d)+\mathbf{b}^m \tag{14.1} \]
\[ \mathbf{a}^m(t)=\mathbf{f}^m\big(\mathbf{n}^m(t)\big) \tag{14.2} \]

各符号含义如下。

符号 含义
\(\mathbf{p}^l(t)\) 第 \(l\) 个外部输入向量
\(\mathbf{IW}^{m,l}(d)\) 输入权值(input weight):输入 \(l\) 延迟 \(d\) 步后连到层 \(m\)
\(\mathbf{LW}^{m,l}(d)\) 层权值(layer weight):层 \(l\) 的输出延迟 \(d\) 步后连到层 \(m\)
\(DI_{m,l}\)、\(DL_{m,l}\) 对应 TDL 上的延迟集合,例如 \(\{0,1,2\}\)
\(I_m\) 连到层 \(m\) 的输入下标集合
\(L^f_m\) 直接前向连到层 \(m\) 的层下标集合
\(L^b_m\) 层 \(m\) 以零延迟连过去的层的下标集合(反传时用)

与静态网络相比,LDDN 允许:多个层连到同一层;任何层连到任何层,包括自己;连接上带 TDL 形成递归;多个输入向量,且可以接到任意层。

每一层仍是基本单元,由五部分组成:流入的权值矩阵、权值前的 TDL、偏置、求和点、传递函数。

14.2.2 仿真顺序与反传顺序

要算出正确的输出,必须按某种顺序逐层计算,叫仿真顺序(simulation order),不一定唯一;它的逆序叫反传顺序(backpropagation order)。例如原书图 14.1 的三层网络(第 1 层有自反馈 \(\mathbf{LW}^{1,1}\),第 3 层反馈到第 1 层 \(\mathbf{LW}^{1,3}\),另有 \(\mathbf{LW}^{2,1}\)、\(\mathbf{LW}^{2,3}\)、\(\mathbf{LW}^{3,2}\)),仿真顺序 1-2-3,反传顺序 3-2-1。

注意:一个零延迟的回路(代数环)无法仿真,因为计算某层时需要它自己的当前输出。原书习题 E14.7 专门考这一点。凡是回路,至少要有一个非零延迟。

14.2.3 输入层与输出层

为推导梯度,再定义两类层:

  • 输入层(input layer):有输入权值,或者它的某个权值矩阵带延迟。集合记为 \(X\)。
  • 输出层(output layer):训练时与目标比较,或者经带延迟的矩阵连到某个输入层。集合记为 \(U\)。

图 14.1 中 \(U=\{1,3\}\),\(X=\{1,2\}\)。直观地说,输出层是"会被反馈回去或被打分的层",输入层是"接收延迟信号的层"。间接作用正是沿"输出层 → 延迟 → 输入层"这条路径传播的。


14.3 两个最小例子:FIR 与 IIR

14.3.1 前馈动态网络:FIR 滤波器

单输入、单线性神经元,输入端 TDL,\(DI_{1,1}=\{0,1,2\}\):

\[ a(t)=iw_{1,1}(0)\,p(t)+iw_{1,1}(1)\,p(t-1)+iw_{1,1}(2)\,p(t-2) \]

取三个权值都为 \(1/3\),它就是三期移动平均。对方波输入,每个时刻的输出只依赖最近三个输入,输入恒定三步后输出也恒定。这种网络叫有限冲激响应(Finite Impulse Response, FIR)滤波器:没有反馈,只有有限长的历史影响响应。

14.3.2 递归线性神经元:IIR 滤波器

\[ a(t)=lw_{1,1}(1)\,a(t-1)+iw_{1,1}\,p(t) \tag{14.5} \]

取 \(lw_{1,1}(1)=iw_{1,1}=1/2\)。对方波输入呈指数式响应。把递推展开:

\[ a(t)=iw\sum_{k=0}^{t-1}lw^{k}\,p(t-k)+lw^{t}\,a(0) \]

任一时刻的输出是全部输入历史的加权和,权重按 \(lw^k\) 衰减,所以叫无限冲激响应(Infinite Impulse Response, IIR)。\(|lw|<1\) 时系统稳定;\(|lw|>1\) 时发散。

推导拆解:展开式就是反复代入。\(a(t)=lw\,a(t-1)+iw\,p(t)\),把 \(a(t-1)=lw\,a(t-2)+iw\,p(t-1)\) 代进去得 \(a(t)=lw^2a(t-2)+iw[p(t)+lw\,p(t-1)]\);再代一次 \(a(t-2)\),方括号里多出 \(lw^2p(t-2)\)……一直代到 \(a(0)\) 为止。输入恒为 1 时,稳态输出 \(=iw\sum_klw^k=iw/(1-lw)\),\(lw=iw=0.5\) 时为 1,这是一个几何级数,只有 \(|lw|<1\) 时才收敛。

金融直觉:\(lw=\lambda\)、\(iw=1-\lambda\) 时这就是 EWMA:\(\hat\sigma^2_t=\lambda\hat\sigma^2_{t-1}+(1-\lambda)r^2_t\),RiskMetrics 的 \(\lambda=0.94\)。权重 \((1-\lambda)\lambda^k\) 加起来为 1,过去任何一天的冲击都永远留有一点影响,只是越来越小。

量化读者应立即认出:FIR 就是移动平均或 ARCH 型的有限滞后结构,IIR 就是 EMA、GARCH 型的递归结构。14.10 节会把这个联系做实。


14.4 动态学习原理:直接作用与间接作用

14.4.1 单个递归神经元

对式 (14.5) 的网络,性能函数取误差平方和:

\[ F(\mathbf{x})=\sum_{t=1}^{Q}e^2(t)=\sum_{t=1}^{Q}\big(t(t)-a(t)\big)^2 \tag{14.7} \]

梯度为

\[ \frac{\partial F}{\partial lw_{1,1}(1)}=\sum_{t=1}^{Q}-2e(t)\frac{\partial a(t)}{\partial lw_{1,1}(1)},\qquad \frac{\partial F}{\partial iw_{1,1}}=\sum_{t=1}^{Q}-2e(t)\frac{\partial a(t)}{\partial iw_{1,1}} \]

关键是输出对权值的导数。如果是静态网络,它们就是 \(a(t-1)\) 和 \(p(t)\)。但这里 \(a(t-1)\) 本身也依赖权值,对式 (14.5) 两边求导:

\[ \frac{\partial a(t)}{\partial lw_{1,1}(1)}=\underbrace{a(t-1)}_{\text{直接作用}}+\underbrace{lw_{1,1}(1)\frac{\partial a(t-1)}{\partial lw_{1,1}(1)}}_{\text{间接作用}} \tag{14.12} \]
\[ \frac{\partial a(t)}{\partial iw_{1,1}}=p(t)+lw_{1,1}(1)\frac{\partial a(t-1)}{\partial iw_{1,1}} \tag{14.13} \]

推导拆解:这是第 11 章 11.3.2 节讲解框四步之后的又一步——"沿时间的多路径"。\(lw\) 通过两条路径影响 \(a(t)\):

  • 直接路径:\(a(t)=lw\cdot a(t-1)+\cdots\) 中,把 \(a(t-1)\) 当常数,对 \(lw\) 求导得 \(a(t-1)\)。这就是乘积求导法则 \((uv)'=u'v+uv'\) 中的 \(u'v\) 项。
  • 间接路径:\(a(t-1)\) 本身也是 \(lw\) 的函数,它变动 \(\partial a(t-1)/\partial lw\),再乘以它在式中的系数 \(lw\) 传到 \(a(t)\)。这就是 \(uv'\) 项。

两条路径相加就是式 14.12。这个式子本身又是一个 IIR 递推:导数序列 \(\partial a(t)/\partial lw\) 和 \(a(t)\) 满足同样形式的方程,只是"输入"从 \(p(t)\) 换成了 \(a(t-1)\)。

数值例:\(lw=iw=0.5\),\(a(0)=0\),\(p=\{1,1,1\}\)。前向 \(a(1)=0.5\),\(a(2)=0.75\),\(a(3)=0.875\)。导数 \(\partial a/\partial lw\):\(t=1\) 为 \(a(0)=0\);\(t=2\) 为 \(a(1)+0.5\times0=0.5\);\(t=3\) 为 \(a(2)+0.5\times0.5=1.0\)。若只算直接作用,\(t=3\) 的导数是 \(0.75\),少算了四分之一。检验:\(a(3)=iw(1+lw+lw^2)\),对 \(lw\) 求导得 \(iw(1+2lw)=0.5\times2=1.0\),与递推一致。

两条要点:

  1. 导数由静态部分和动态部分组成;
  2. 动态部分依赖其他时刻的导数。RTRL 中依赖之前时刻,BPTT 中依赖之后时刻。

原书图 14.6 对比了 \(\partial a(t)/\partial iw\) 的总导数与静态部分:只看静态部分会低估权值变化对响应的影响(线性、正权值的情形)。把 \(iw\) 从 0.5 改到 0.6 再仿真,响应的变化与总导数一致。

14.4.2 一般化:带一个反馈环的网络

设 \(\mathbf{a}(t)=\mathrm{NN}\big(\mathbf{p}(t),\mathbf{a}(t-1),\mathbf{x}\big)\),\(\mathbf{x}\) 为全部权值和偏置。用上标 \(e\) 表示显式导数(explicit derivative):只计当前时刻的直接作用,可用第 11 章标准反传求出。链式法则有两种用法:

\[ \frac{\partial F}{\partial \mathbf{x}}=\sum_{t=1}^{Q}\left[\frac{\partial \mathbf{a}(t)}{\partial \mathbf{x}^T}\right]^T\frac{\partial^e F}{\partial \mathbf{a}(t)} \tag{14.14} \]
\[ \frac{\partial F}{\partial \mathbf{x}}=\sum_{t=1}^{Q}\left[\frac{\partial^e \mathbf{a}(t)}{\partial \mathbf{x}^T}\right]^T\frac{\partial F}{\partial \mathbf{a}(t)} \tag{14.15} \]

各自配一个随时间传播的递推:

\[ \frac{\partial \mathbf{a}(t)}{\partial \mathbf{x}^T}=\frac{\partial^e \mathbf{a}(t)}{\partial \mathbf{x}^T}+\frac{\partial^e \mathbf{a}(t)}{\partial \mathbf{a}^T(t-1)}\,\frac{\partial \mathbf{a}(t-1)}{\partial \mathbf{x}^T} \tag{14.16} \]
\[ \frac{\partial F}{\partial \mathbf{a}(t)}=\frac{\partial^e F}{\partial \mathbf{a}(t)}+\left[\frac{\partial^e \mathbf{a}(t+1)}{\partial \mathbf{a}^T(t)}\right]^T\frac{\partial F}{\partial \mathbf{a}(t+1)} \tag{14.17} \]
  • (14.14)+(14.16) 是 RTRL:关键量 \(\partial\mathbf{a}(t)/\partial\mathbf{x}^T\)("输出对参数的敏感度")向前传播。
  • (14.15)+(14.17) 是 BPTT:关键量 \(\partial F/\partial\mathbf{a}(t)\)("损失对状态的敏感度")向后传播。

白话解释:符号 \(\partial^e\)(显式导数)与 \(\partial\)(总导数)的区别:\(\partial^e\) 把过去的输出 \(\mathbf{a}(t-1)\) 当成固定的数据,只算"这一步之内"的影响;\(\partial\) 把一切经由过去输出绕回来的影响也算上。\(\partial\mathbf{a}(t)/\partial\mathbf{x}^T\) 中分母写成行向量 \(\mathbf{x}^T\),表示结果是一个矩阵:行对应 \(\mathbf{a}\) 的分量,列对应各个参数,即 Jacobian。

推导拆解:把四个式子还原成单神经元、标量版本,就能看清它们只是同一个链式法则的两种括号加法。设 \(a(t)=f(a(t-1),x)\),\(F=\sum_t\ell(a(t))\)。

  • RTRL:先问"\(x\) 动一点,每个 \(a(t)\) 动多少?"记 \(r(t)=\partial a(t)/\partial x\),则 \(r(t)=\dfrac{\partial^ef}{\partial x}+\dfrac{\partial^ef}{\partial a(t-1)}r(t-1)\)(式 14.16),从 \(t=1\) 往后推;再汇总 \(\dfrac{\partial F}{\partial x}=\sum_t\ell'(a(t))\,r(t)\)(式 14.14)。
  • BPTT:先问"\(a(t)\) 动一点,总损失动多少?"记 \(\delta(t)=\partial F/\partial a(t)\)。\(a(t)\) 既直接进入损失 \(\ell(a(t))\),又通过 \(a(t+1)\) 影响后面所有损失,所以 \(\delta(t)=\ell'(a(t))+\dfrac{\partial^ef}{\partial a(t)}\big|_{t+1}\delta(t+1)\)(式 14.17),从 \(t=Q\) 往前推;再汇总 \(\dfrac{\partial F}{\partial x}=\sum_t\dfrac{\partial^ef}{\partial x}\big|_t\,\delta(t)\)(式 14.15)。

两种做法都在计算"\(x\) 到 \(F\) 的所有路径上局部导数之积的总和",只是 RTRL 从路径起点开始累乘,BPTT 从路径终点开始累乘。

金融直觉:在蒙特卡洛定价里,RTRL 对应"切线模式"(tangent mode)的 pathwise Greeks:沿着每条路径向前同时传播价格和它对参数的导数,有几个参数就要传几份导数;BPTT 对应"伴随模式"(adjoint,即 AAD):先向前模拟、存下路径,再向后传播一次伴随量,就得到对所有参数的导数。参数多时 AAD 更省,这正是下文 14.7 节复杂度比较的结论。

理解这两种组织方式的最好办法是看它们各自要存什么。RTRL 在每一时刻维护一个"状态维数 × 参数个数"的矩阵,不需要回看历史;BPTT 每个时刻只维护一个"状态维数"的向量,但必须保存整段前向轨迹。这正是两者计算量与存储量差异的根源。


14.5 RTRL 的一般形式

14.5.1 总导数的递推

把 (14.14) 推广到多个输出层:

\[ \frac{\partial F}{\partial \mathbf{x}^T}=\sum_{t=1}^{Q}\sum_{u\in U}\left[\frac{\partial \mathbf{a}^u(t)}{\partial \mathbf{x}^T}\right]^T\frac{\partial^e F}{\partial \mathbf{a}^u(t)} \tag{14.20} \]

若 \(F\) 不显式依赖某个输出层(它只是被反馈回去、不和目标比较),对应显式导数为零。

把 (14.16) 推广时,间接作用要对三样东西求和:哪个输出层 \(u'\) 被反馈、反馈到哪个输入层 \(x\)、延迟几步 \(d\)。由净输入的分量形式可得 \(\partial^e n^x_k(t)/\partial a^{u'}_j(t-d)=lw^{x,u'}_{k,j}(d)\),即 \(\partial^e\mathbf{n}^x(t)/\partial\mathbf{a}^{u'}(t-d)^T=\mathbf{LW}^{x,u'}(d)\)。

14.5.2 敏感度

定义敏感度(sensitivity):

\[ s^{u,m}_{k,i}(t)\equiv\frac{\partial^e a^u_k(t)}{\partial n^m_i(t)},\qquad \mathbf{S}^{u,m}(t)=\frac{\partial^e \mathbf{a}^u(t)}{\partial \mathbf{n}^m(t)^T}\quad(S^u\times S^m) \tag{14.27} \]

它衡量第 \(m\) 层净输入的变化经零延迟连接对输出层 \(u\) 当前输出的影响。于是 \(\frac{\partial^e \mathbf{a}^u(t)}{\partial \mathbf{n}^x(t)^T}\frac{\partial^e \mathbf{n}^x(t)}{\partial \mathbf{a}^{u'}(t-d)^T}=\mathbf{S}^{u,x}(t)\,\mathbf{LW}^{x,u'}(d)\)。

为了只对非零项求和,原书引入指示集(indicator sets):

  • \(E^U_{LW}(x)=\{u\in U:\ \exists\,\mathbf{LW}^{x,u}(d)\neq0,\ d\neq0\}\):以非零延迟连到输入层 \(x\) 的输出层;
  • \(E^X_S(u)=\{x\in X:\ \mathbf{S}^{u,x}\neq0\}\):与输出层 \(u\) 有静态(零延迟)通路的输入层;
  • \(E_S(u)=\{x:\ \mathbf{S}^{u,x}\neq0\}\):与 \(u\) 有非零敏感度的所有层。

RTRL 核心递推:

\[ \boxed{\frac{\partial \mathbf{a}^u(t)}{\partial \mathbf{x}^T}=\frac{\partial^e \mathbf{a}^u(t)}{\partial \mathbf{x}^T}+\sum_{x\in E^X_S(u)}\mathbf{S}^{u,x}(t)\sum_{u'\in E^U_{LW}(x)}\ \sum_{d\in DL_{x,u'}}\mathbf{LW}^{x,u'}(d)\,\frac{\partial \mathbf{a}^{u'}(t-d)}{\partial \mathbf{x}^T}} \tag{14.34} \]

读法:输出 \(u\) 对参数的总导数 = 显式部分 + 所有"过去输出 \(u'\) 经延迟 \(d\) 进入输入层 \(x\)、再经静态通路到达 \(u\)"的路径贡献。初始条件:\(t\le0\) 时 \(\partial\mathbf{a}^u(t)/\partial\mathbf{x}^T=0\)。

敏感度本身用静态反传计算。在输出层:

\[ \mathbf{S}^{u,u}(t)=\dot{\mathbf{F}}^u\big(\mathbf{n}^u(t)\big)=\mathrm{diag}\big(\dot f^u(n^u_1),\dots,\dot f^u(n^u_{S^u})\big) \]

再沿反传顺序往回传(只走零延迟连接):

\[ \mathbf{S}^{u,m}(t)=\Big[\sum_{l\in E_S(u)\cap L^b_m}\mathbf{S}^{u,l}(t)\,\mathbf{LW}^{l,m}(0)\Big]\dot{\mathbf{F}}^m\big(\mathbf{n}^m(t)\big) \tag{14.38} \]

这就是第 11 章式 (11.45) 推广到任意层间连接。

14.5.3 显式导数

用 vec 算子(矩阵按列堆成向量)和 Kronecker 积 \(\otimes\):

\[ \frac{\partial^e \mathbf{a}^u(t)}{\partial\,\mathrm{vec}(\mathbf{IW}^{m,l}(d))^T}=[\mathbf{p}^l(t-d)]^T\otimes\mathbf{S}^{u,m}(t),\qquad \frac{\partial^e \mathbf{a}^u(t)}{\partial\,\mathrm{vec}(\mathbf{LW}^{m,l}(d))^T}=[\mathbf{a}^l(t-d)]^T\otimes\mathbf{S}^{u,m}(t), \]
\[ \frac{\partial^e \mathbf{a}^u(t)}{\partial(\mathbf{b}^m)^T}=\mathbf{S}^{u,m}(t) \]

白话解释:为什么要 vec 和 Kronecker 积?导数 \(\partial\mathbf{a}/\partial\mathbf{W}\) 是"向量对矩阵"求导,天然是一个三维数组(输出分量 × 行 × 列),写不成普通矩阵。把 \(\mathbf{W}\) 拉直成长向量 \(\mathrm{vec}(\mathbf{W})\) 后,结果就是一个普通的二维 Jacobian。小例子:\(\mathbf{p}=[p_1,p_2]^T\),\(\mathbf{S}=[s_1,s_2]\)(单输出、两个神经元,\(1\times2\) 行向量),则 \(\mathbf{p}^T\otimes\mathbf{S}=[p_1s_1,\ p_1s_2,\ p_2s_1,\ p_2s_2]\),依次对应 \(\mathrm{vec}(\mathbf{W})\) 中的 \(w_{1,1},w_{2,1},w_{1,2},w_{2,2}\)(按列排)。每个元素仍是"敏感度 × 输入"。

逐元素看就是熟悉的链式法则 \(\partial^e a^u_k/\partial iw^{m,l}_{i,j}(d)=s^{u,m}_{k,i}(t)\,p^l_j(t-d)\)。Kronecker 积只是把它排成矩阵的紧凑写法(原书例题 P14.4 展开了一个 \(2\times3\) 权值矩阵的情形:\(\mathbf{S}^{2,1}\) 为 \(3\times2\),结果是 \(3\times6\) 矩阵)。

14.5.4 算法

  1. 初始化:对所有 \(u\in U\),\(t\le0\) 时 \(\partial\mathbf{a}^u(t)/\partial\mathbf{x}^T=0\)。
  2. 对 \(t=1,\dots,Q\):
    • 沿反传顺序逐层:在输出层置 \(\mathbf{S}^{u,u}(t)=\dot{\mathbf{F}}^u\),在其他层按 (14.38) 算 \(\mathbf{S}^{u,m}(t)\),同时登记指示集 \(E_S(u)\)、\(E^X_S(u)\);
    • 沿仿真顺序对每个输出层:算显式导数,再按 (14.34) 算总导数。
  3. 按 (14.20) 累加梯度。

14.5.5 例:FIR 与 IIR

FIR 网络(\(DI_{1,1}=\{0,1,2\}\),无反馈,\(E^U_{LW}(1)=\varnothing\))。需要初始条件 \(p(0),p(-1)\)。线性传递函数使 \(\mathbf{S}^{1,1}=1\)。因为没有反馈,总导数等于显式导数:\(\partial a(t)/\partial iw(d)=p(t-d)\)。取 \(Q=3\):

\[ \frac{\partial F}{\partial iw(0)}=-2e(1)p(1)-2e(2)p(2)-2e(3)p(3) \]

\(\partial F/\partial iw(1)\)、\(\partial F/\partial iw(2)\) 把 \(p\) 的时间下标依次减 1、减 2。用最速下降,这正是第 10 章 LMS 算法的批量形式。

IIR 网络(\(DI_{1,1}=\{0\}\),\(DL_{1,1}=\{1\}\),\(E^U_{LW}(1)=\{1\}\))。需要 \(a(0)\) 和初始导数(取 0)。递推为

\[ \frac{\partial a(t)}{\partial\mathbf{x}^T}=\frac{\partial^e a(t)}{\partial\mathbf{x}^T}+\mathbf{S}^{1,1}(t)\,\mathbf{LW}^{1,1}(1)\,\frac{\partial a(t-1)}{\partial\mathbf{x}^T} \tag{14.45} \]

逐步展开(线性,\(S=1\)):

\[ \frac{\partial a(1)}{\partial iw}=p(1),\quad \frac{\partial a(2)}{\partial iw}=p(2)+lw\,p(1),\quad \frac{\partial a(3)}{\partial iw}=p(3)+lw\,p(2)+lw^2p(1) \]
\[ \frac{\partial a(1)}{\partial lw}=a(0),\quad \frac{\partial a(2)}{\partial lw}=a(1)+lw\,a(0),\quad \frac{\partial a(3)}{\partial lw}=a(2)+lw\,a(1)+lw^2a(0) \]

于是

\[ \frac{\partial F}{\partial iw}=-2e(1)p(1)-2e(2)\big[p(2)+lw\,p(1)\big]-2e(3)\big[p(3)+lw\,p(2)+lw^2p(1)\big] \]

实际计算中这些都是数,原书写成符号只为与 BPTT 对照。


14.6 BPTT 的一般形式

14.6.1 梯度公式

把 (14.15) 推广,对层权值有

\[ \frac{\partial F}{\partial lw^{m,l}_{i,j}(d)}=\sum_{t=1}^{Q}\left[\sum_{u\in U}\sum_{k=1}^{S^u}\frac{\partial F}{\partial a^u_k(t)}\,s^{u,m}_{k,i}(t)\right]a^l_j(t-d) \]

方括号里的量记为 \(d^m_i(t)\),矩阵形式

\[ \mathbf{d}^m(t)=\sum_{u\in U}\big[\mathbf{S}^{u,m}(t)\big]^T\frac{\partial F}{\partial \mathbf{a}^u(t)} \tag{14.52} \]

梯度就是外积在时间上求和:

\[ \frac{\partial F}{\partial \mathbf{LW}^{m,l}(d)}=\sum_{t=1}^{Q}\mathbf{d}^m(t)\,[\mathbf{a}^l(t-d)]^T,\quad \frac{\partial F}{\partial \mathbf{IW}^{m,l}(d)}=\sum_{t=1}^{Q}\mathbf{d}^m(t)\,[\mathbf{p}^l(t-d)]^T,\quad \frac{\partial F}{\partial \mathbf{b}^m}=\sum_{t=1}^{Q}\mathbf{d}^m(t) \tag{14.54–14.56} \]

形式与第 11 章完全一样,只是多了对时间的求和,而 \(\partial F/\partial\mathbf{a}^u(t)\) 要包含未来时刻经反馈传回来的影响。

14.6.2 核心递推

再引入两个指示集:\(E^X_{LW}(u)\) 为输出层 \(u\) 以非零延迟连到的输入层;\(E^U_S(x)\) 为与输入层 \(x\) 有静态通路的输出层。

\[ \boxed{\frac{\partial F}{\partial \mathbf{a}^u(t)}=\frac{\partial^e F}{\partial \mathbf{a}^u(t)}+\sum_{x\in E^X_{LW}(u)}\sum_{d\in DL_{x,u}}[\mathbf{LW}^{x,u}(d)]^T\sum_{u'\in E^U_S(x)}[\mathbf{S}^{u',x}(t+d)]^T\frac{\partial F}{\partial \mathbf{a}^{u'}(t+d)}} \tag{14.63} \]

读法:损失对 \(\mathbf{a}^u(t)\) 的总敏感度 = 当前时刻直接产生的误差 + 所有"\(\mathbf{a}^u(t)\) 经延迟 \(d\) 进入输入层 \(x\)、在 \(t+d\) 时刻影响输出 \(u'\)"的路径把未来的敏感度折回来。终止条件:\(t>Q\) 时 \(\partial F/\partial\mathbf{a}^u(t)=0\)。

14.6.3 算法

  1. 先按仿真顺序算出 \(t=1,\dots,Q\) 的全部响应并保存。
  2. 对 \(t=Q\) 递减到 1:
    • 沿反传顺序算各层敏感度 \(\mathbf{S}^{u,m}(t)\),登记 \(E_S(u)\)、\(E^U_S(m)\);
    • 沿反传顺序对每个输出层按 (14.63) 算 \(\partial F/\partial\mathbf{a}^u(t)\);
    • 对每层按 (14.52) 算 \(\mathbf{d}^m(t)\)。
  3. 按 (14.54)–(14.56) 求梯度。

14.6.4 例:IIR 网络的 BPTT

先算出 \(a(1),a(2),a(3)\)。终止条件 \(\partial F/\partial a(4)=0\)。线性神经元 \(S=1\),所以 \(d(t)=\partial F/\partial a(t)\):

\[ \begin{aligned} d(3)&=-2e(3)\\ d(2)&=-2e(2)+lw\,(-2e(3))\\ d(1)&=-2e(1)+lw\,(-2e(2))+lw^2(-2e(3)) \end{aligned} \]
\[ \frac{\partial F}{\partial lw}=\sum_{t=1}^{3}d(t)\,a(t-1)=a(0)\big[-2e(1)-2lw\,e(2)-2lw^2e(3)\big]+a(1)\big[-2e(2)-2lw\,e(3)\big]+a(2)\big[-2e(3)\big] \]

推导拆解:"按 \(e(t)\) 重新整理"具体是:把上式中所有含 \(e(3)\) 的项收集起来,系数是 \(-2[a(2)+lw\,a(1)+lw^2a(0)]\),方括号正是 RTRL 算出的 \(\partial a(3)/\partial lw\);含 \(e(2)\) 的项系数是 \(-2[a(1)+lw\,a(0)]=-2\,\partial a(2)/\partial lw\);含 \(e(1)\) 的项系数是 \(-2a(0)=-2\,\partial a(1)/\partial lw\)。同一个双重求和 \(\sum_t\sum_{k}(\cdots)\),RTRL 先对"过去"求和,BPTT 先对"未来"求和,交换求和次序而已。

按 \(e(t)\) 重新整理,正好得到 14.5.5 节 RTRL 的结果。RTRL 与 BPTT 总给出同一个精确梯度,区别只在计算的组织方式。 直观地说:RTRL 把"过去的导数"沿反馈环乘以 \(lw\) 推向未来;BPTT 把"未来的误差"沿反馈环乘以 \(lw\) 折回过去。对 FIR 网络,BPTT 得到 \(d(t)=-2e(t)\),梯度与 RTRL 也相同。

14.6.5 一个较大网络的例子(原书 P14.1–P14.3 概要)

原书用一个 10 层网络练习全部记号:\(X=\{1,2,4,6,9\}\),假定只有第 10 层与目标比较,则 \(U=\{2,3,4,5,8,10\}\)(第 2、3、4、5、8 层因为有带延迟的输出连接而成为输出层);仿真顺序 \(\{1,2,3,7,4,5,6,8,9,10\}\)。BPTT 中多条零延迟路径汇合时敏感度相加,如 \(\mathbf{S}^{10,2}=\big[\mathbf{S}^{10,6}\mathbf{LW}^{6,2}(0)+\mathbf{S}^{10,7}\mathbf{LW}^{7,2}(0)\big]\dot{\mathbf{F}}^2\);经延迟回传的项如

\[ \frac{\partial F}{\partial\mathbf{a}^8(t)}=\frac{\partial^eF}{\partial\mathbf{a}^8(t)}+\mathbf{LW}^{9,8}(1)^T\,\mathbf{S}^{10,9}(t+1)^T\,\frac{\partial F}{\partial\mathbf{a}^{10}(t+1)} \]

这类记账工作在现代深度学习框架里由自动微分完成,但读懂它能帮你判断一个网络结构能否训练、梯度要经过多少条路径。


14.7 计算量与存储比较

原书 P14.5 用一个简单网络比较两者:单输入,第 1 层 \(S^1\) 个神经元、带 \(D\) 个延迟的自反馈 \(\mathbf{LW}^{1,1}(d)\),单个输出神经元,序列长 \(Q\)。

  • BPTT:主导项是 \(\sum_t\mathbf{d}^1(t)[\mathbf{a}^1(t-d)]^T\),每个外积 \(O((S^1)^2)\),乘 \(Q\) 个时刻和 \(D\) 个延迟,得 \(O\big((S^1)^2DQ\big)\)。
  • RTRL:主导项是 (14.34) 中 \(\mathbf{LW}^{1,1}(d)\)(\(S^1\times S^1\))乘以 \(\partial\mathbf{a}^1(t-d)/\partial\mathbf{x}^T\)(\(S^1\times\) 参数个数,参数个数约 \(D(S^1)^2\)),为 \(O((S^1)^4D)\),再对每个 \(d\)、每个 \(t\) 累计,得 \(O\big((S^1)^4D^2Q\big)\)。
RTRL BPTT
传播方向 向前(与仿真同步) 向后(仿真完成后)
传播的量 \(\partial\mathbf{a}(t)/\partial\mathbf{x}^T\)(矩阵) \(\partial F/\partial\mathbf{a}(t)\)(向量)
梯度计算量 \(O(S^4D^2Q)\),大 \(O(S^2DQ)\),小
存储 不随 \(Q\) 增长 需存整段轨迹,随 \(Q\) 增长
在线实现 自然 困难
计算 Jacobian(LM 用) 通常更高效 每个输出要反传一次

现代 RNN 训练几乎都用 BPTT(自动微分的"反向模式"),因为参数多时 RTRL 的 \(S^4\) 不可承受;RTRL 的思想则保留在在线学习、以及 GARCH 等低维递归模型的解析梯度中。


14.8 为什么动态网络难训练

两种算法都给出精确梯度,但递归网络依然比前馈网络难训练。原书给出三个原因。

1. 按时间展开后是一个很深的网络。 把图 14.4 的网络训练 5 个时刻,可以展开成 5 层前馈网络,每层共享同一组权值。梯度从 \(t\) 传到 \(t-k\) 要连乘 \(k\) 次 \(\mathbf{S}(t)\mathbf{LW}\)。若用 sigmoid 且某时刻接近饱和,\(\dot f\approx0\),梯度就几乎消失。一般地,\(\partial\mathbf{a}(t)/\partial\mathbf{a}(t-k)=\prod_{j=0}^{k-1}\dot{\mathbf{F}}(t-j)\,\mathbf{LW}\),它的范数大致按 \((\rho\cdot\overline{|\dot f|})^k\) 变化(\(\rho\) 为 \(\mathbf{LW}\) 的谱半径),小于 1 则指数消失,大于 1 则可能指数爆炸。

推导拆解:\(\partial\mathbf{a}(t)/\partial\mathbf{a}(t-k)\) 为什么是连乘?每一步 \(\mathbf{a}(j)=\mathbf{f}(\mathbf{LW}\,\mathbf{a}(j-1)+\cdots)\) 的局部 Jacobian 是 \(\dot{\mathbf{F}}(j)\mathbf{LW}\)(与第 11 章式 11.33 完全相同,只是"层"换成了"时刻")。从 \(t-k\) 到 \(t\) 要经过 \(k\) 步,按链式法则把 \(k\) 个局部 Jacobian 相乘。标量情形就是 \((\dot f\cdot lw)^k\):\(\dot f\cdot lw=0.8\) 时 30 步后剩 \(0.8^{30}\approx0.001\);\(=1.2\) 时变成约 237。

金融直觉:这和 GARCH 的持续性参数完全对应。冲击对 \(k\) 天后条件方差的影响按 \((\alpha+\beta)^k\) 衰减:\(\alpha+\beta=0.98\) 时半衰期约 34 天,\(0.9\) 时只有 6.6 天。梯度消失就是"网络对 \(k\) 步以前的事件的敏感度"衰减得太快,导致训练时无法把远期误差归因到早期输入上,自然学不到长期依赖。

2. 误差曲面有伪谷。 递归网络可能不稳定,例如图 14.4 的网络在 \(|lw|>1\) 时发散。但对特定输入序列,某些 \(|lw|>1\) 的值或 \(lw\)、\(iw\) 的某些组合恰好让输出很小,在误差曲面上形成与被逼近系统无关的伪谷(spurious valleys,Phan & Hagan 2013)。原书习题 E14.18 用一个漂亮的论证解释它:\(iw=1\)、\(a(0)=0\) 时 \(a(t)\) 是 \(lw\) 的多项式,多项式的根附近输出接近零,于是出现窄谷。

原书的演示:把图 14.4 改成 tansig 神经元 \(a(t)=\mathrm{tansig}\big(lw\,a(t-1)+iw\,p(t)\big)\),用同一网络在 \(lw=iw=0.5\) 下生成目标,输入是天际线函数(skyline function,一串高度和宽度各异的脉冲)。误差曲面在 \(|lw|>1\) 处陡增,训练序列越长越明显;但在 \(lw>1\) 一侧仍能看到窄谷,最速下降轨迹一开始就被底部的窄谷误导。

3. 训练数据难以充分。 接在 TDL 后的输入分量来自同一条时间相关的序列,不能独立选取。训练数据不仅要覆盖每个输入的取值范围,还要覆盖输入随时间变化的方式。对实际网络这往往很难做到。


14.9 从本书到现代循环网络(补充)

本节是编者补充,原书第 14 章没有讨论 LSTM 等模型。目的是让你知道本章的 RTRL/BPTT 在现代序列建模中的位置。

Elman 网络与 NARX。 原书的 LDDN 框架涵盖了两类经典结构:Elman(1990)的"简单循环网络"——隐层输出经一步延迟反馈到自身,即 \(\mathbf{h}(t)=\tanh(\mathbf{W}\mathbf{h}(t-1)+\mathbf{U}\mathbf{p}(t)+\mathbf{b})\);以及 NARX(带外生输入的非线性自回归)网络——输入 TDL 加输出 TDL 反馈。原书第 27 章有用动态网络做预测的案例。

梯度消失问题。 Hochreiter(1991)和 Bengio、Simard、Frasconi(1994)系统分析了上节第 1 点:标准 RNN 很难学到相隔几十步以上的依赖。下面的代码对一个 20 维 tanh 循环层,计算 \(\|\partial\mathbf{a}(k)/\partial\mathbf{a}(0)\|_2\) 随 \(k\) 的变化:

import numpy as np

rng = np.random.default_rng(1)
S, T = 20, 60
p = rng.standard_normal((T, 1))

def jacobian_norms(rho):
    W = rng.standard_normal((S, S))
    W *= rho / max(abs(np.linalg.eigvals(W)))        # 把谱半径缩放到 rho
    U = rng.standard_normal((S, 1))
    a = np.zeros(S); J = np.eye(S); norms = []
    for t in range(T):
        a = np.tanh(W @ a + U @ p[t])
        J = np.diag(1 - a ** 2) @ W @ J               # da(t)/da(0) = Π F'(n) W
        norms.append(np.linalg.norm(J, 2))
    return np.array(norms)

for rho in (0.5, 0.9, 1.5, 3.0):
    n = jacobian_norms(rho)
    print(f"谱半径 {rho}: ||da(k)/da(0)|| 在 k=1,10,30,60 时 =",
          np.array2string(n[[0, 9, 29, 59]], precision=2, formatter={'float_kind': lambda v: f'{v:.1e}'}))

输出:

谱半径 0.5: ||da(k)/da(0)|| 在 k=1,10,30,60 时 = [6.7e-01 1.1e-04 9.0e-13 4.7e-26]
谱半径 0.9: ||da(k)/da(0)|| 在 k=1,10,30,60 时 = [1.2e+00 7.0e-03 1.5e-07 1.1e-16]
谱半径 1.5: ||da(k)/da(0)|| 在 k=1,10,30,60 时 = [2.4e+00 3.0e+00 2.4e-01 2.2e-03]
谱半径 3.0: ||da(k)/da(0)|| 在 k=1,10,30,60 时 = [5.0e+00 3.3e+02 2.6e+04 3.7e+05]

谱半径小于 1 时,30 步以前的信息对梯度几乎没有贡献;谱半径大时则可能爆炸。tanh 的饱和(\(\dot f<1\))会把有效增益压低,所以谱半径 1.5 时仍然在缓慢衰减。

LSTM 与 GRU。 Hochreiter 与 Schmidhuber(1997)提出长短期记忆网络(Long Short-Term Memory, LSTM),Gers 等(2000)加入遗忘门。其核心是一个以加法更新的细胞状态:

\[ \mathbf{c}(t)=\mathbf{f}(t)\odot\mathbf{c}(t-1)+\mathbf{i}(t)\odot\tilde{\mathbf{c}}(t),\qquad \mathbf{h}(t)=\mathbf{o}(t)\odot\tanh\big(\mathbf{c}(t)\big) \]

其中遗忘门 \(\mathbf{f}\)、输入门 \(\mathbf{i}\)、输出门 \(\mathbf{o}\) 都是 \(\sigma(\cdot)\) 形式的门控,\(\tilde{\mathbf{c}}\) 是候选值,\(\odot\) 为逐元素乘。只看细胞状态这条路径,\(\partial\mathbf{c}(t)/\partial\mathbf{c}(t-1)\approx\mathrm{diag}(\mathbf{f}(t))\),遗忘门接近 1 时梯度可以几乎无损地穿过很多步。对比式 (14.45):普通 RNN 的间接作用每步乘 \(\mathbf{S}\mathbf{LW}\),LSTM 把这个乘子换成了一个可学习、可接近 1 的门。GRU(Cho 等 2014)用更新门和重置门实现类似效果,参数更少。门控的思想你会在第 18 章再次见到:Grossberg 的学习律用神经元活动"门控"权值变化。

实用训练技巧。 现代实践几乎都用截断 BPTT(truncated BPTT):只把梯度回传固定的 \(k\) 步,用有限的存储换取近似梯度;再配合梯度裁剪(gradient clipping,Pascanu 等 2013)防止爆炸。2017 年后,基于注意力的 Transformer 在许多序列任务上取代了 RNN,它不再沿时间递归,而是直接对全部历史位置加权。

在量化中的位置。 LSTM/GRU 被广泛用于收益、波动率、成交量和订单流的预测研究。但金融序列信噪比极低,参数多的递归模型很容易拟合噪声。经验上,先用本章的低维递归结构(如下一节的 GARCH 型神经元)或简单的 TDL 输入网络建立基准,再考虑更大的模型,并始终用按时间切分的样本外检验来判断。


14.10 量化实战

14.10.1 用 RTRL 与 BPTT 算同一个梯度

先把本章的两个算法写出来,并用有限差分验证。网络是原书 14.6 节的 tansig 递归神经元,目标由 \(lw=iw=0.5\) 生成,输入为天际线函数。同时计算"只算静态部分"的错误梯度作对比。

import numpy as np

rng = np.random.default_rng(0)

# 天际线输入:高度、宽度随机的一串脉冲
def skyline(Q, rng):
    p = np.empty(Q); t = 0
    while t < Q:
        w = rng.integers(2, 8); h = rng.uniform(-2, 2)
        p[t:t + w] = h; t += w
    return p

def simulate(lw, iw, p, a0=0.0):
    a = np.empty(len(p)); prev = a0
    for t in range(len(p)):
        prev = np.tanh(lw * prev + iw * p[t]); a[t] = prev
    return a

def loss(x, p, target):
    a = simulate(x[0], x[1], p)
    return np.sum((target - a) ** 2)

def grad_rtrl(x, p, target, static_only=False):
    """RTRL:向前传播 da(t)/dx,x=[lw, iw]"""
    lw, iw = x
    a_prev, da_prev = 0.0, np.zeros(2)          # a(0)=0, da(0)/dx=0
    g = np.zeros(2)
    for t in range(len(p)):
        n = lw * a_prev + iw * p[t]
        a = np.tanh(n); s = 1 - a ** 2           # 敏感度 S(t)=f'(n)
        explicit = s * np.array([a_prev, p[t]])  # 显式导数
        da = explicit if static_only else explicit + s * lw * da_prev   # 式 (14.34)
        g += -2 * (target[t] - a) * da          # 式 (14.20)
        a_prev, da_prev = a, da
    return g

def grad_bptt(x, p, target):
    """BPTT:先前向算完,再向后传播 dF/da(t)"""
    lw, iw = x
    Q = len(p)
    a = simulate(lw, iw, p)
    a_lag = np.concatenate([[0.0], a[:-1]])
    s = 1 - a ** 2
    g = np.zeros(2); dFda_next, s_next = 0.0, 0.0   # 终止条件 dF/da(Q+1)=0
    for t in range(Q - 1, -1, -1):
        dFda = -2 * (target[t] - a[t]) + lw * s_next * dFda_next   # 式 (14.63)
        d = s[t] * dFda                                             # 式 (14.52)
        g += d * np.array([a_lag[t], p[t]])                          # 式 (14.54)(14.55)
        dFda_next, s_next = dFda, s[t]
    return g

Q = 60
p = skyline(Q, rng)
target = simulate(0.5, 0.5, p)          # 目标由 lw=iw=0.5 的同一网络生成(式 14.64)
x = np.array([0.2, 0.9])                # 一个偏离最优解的点

g_rtrl = grad_rtrl(x, p, target)
g_bptt = grad_bptt(x, p, target)
g_static = grad_rtrl(x, p, target, static_only=True)
eps = 1e-6
g_fd = np.array([(loss(x + eps * e, p, target) - loss(x - eps * e, p, target)) / (2 * eps)
                 for e in np.eye(2)])
np.set_printoptions(precision=6, suppress=True)
print("RTRL     :", g_rtrl)
print("BPTT     :", g_bptt)
print("有限差分 :", g_fd)
print("只算静态 :", g_static)

# 用 BPTT 梯度做最速下降
x = np.array([0.2, 0.9]); lr = 0.01
for k in range(2000):
    x -= lr * grad_bptt(x, p, target)
print("训练后 [lw, iw] =", x, " F =", round(loss(x, p, target), 8))

输出:

RTRL     : [0.102445 4.861552]
BPTT     : [0.102445 4.861552]
有限差分 : [0.102445 4.861552]
只算静态 : [0.16396  4.752038]
训练后 [lw, iw] = [0.5 0.5]  F = 0.0

RTRL、BPTT 与有限差分三者一致,"只算静态"的梯度在 \(lw\) 方向上偏差超过 50%。注意 BPTT 中 \(\partial F/\partial a(t)\) 的递推要乘的是下一时刻的敏感度 \(s(t+1)\):因为 \(a(t)\) 是经过 \(n(t+1)=lw\,a(t)+\dots\) 才影响 \(a(t+1)\) 的。这是自己实现 BPTT 时最常写错的地方。

14.10.2 GARCH(1,1) 就是一个线性 IIR 递归神经元

GARCH(1,1) 的条件方差递推(详见第 06 册第 03a 章)是

\[ \sigma^2_t=\omega+\alpha\,r^2_{t-1}+\beta\,\sigma^2_{t-1} \]

把它和式 (14.5) 加上偏置后的 \(a(t)=lw\,a(t-1)+iw\,p(t)+b\) 对照:输入 \(p(t)=r^2_{t-1}\),状态 \(a(t)=\sigma^2_t\),\(lw=\beta\),\(iw=\alpha\),\(b=\omega\)。GARCH 正是一个单神经元、线性传递函数、带一步输出反馈的 LDDN;ARCH(\(q\)) 则是输入端 TDL 的 FIR 网络。平稳性条件 \(\alpha+\beta<1\) 包含了本章说的反馈稳定条件 \(|lw|<1\)。

白话解释:为什么 GARCH 的平稳性要求 \(\alpha+\beta<1\),比网络视角的 \(|lw|=\beta<1\) 更严?在网络视角里,输入 \(p(t)=r^2_{t-1}\) 被当作外部给定的数据,只要 \(\beta<1\),这个滤波器对有界输入就给出有界输出。但在 GARCH 作为数据生成过程时,输入并不外生:\(E[r^2_{t-1}]=\sigma^2_{t-1}\),即"输入"本身又由"输出"决定,相当于多了一条反馈回路。取期望后 \(E[\sigma^2_t]=\omega+(\alpha+\beta)E[\sigma^2_{t-1}]\),有效反馈系数是 \(\alpha+\beta\)。所以 \(\beta<1\) 只保证滤波器稳定,\(\alpha+\beta<1\) 才保证整个过程的方差有限。

更进一步,计量软件对 GARCH 似然求解析梯度时用的递推

\[ \frac{\partial\sigma^2_t}{\partial\beta}=\sigma^2_{t-1}+\beta\,\frac{\partial\sigma^2_{t-1}}{\partial\beta} \]

与式 (14.12) 一字不差,也就是 RTRL。

下面的代码模拟 GARCH 收益,把 \(r_t^2\) 当作条件方差的含噪目标,用 BPTT 梯度加 L-BFGS 训练 IIR 神经元,再与 FIR(5 个滞后的 ARCH,线性最小二乘)对比样本外表现。

import numpy as np
from scipy.optimize import minimize

rng = np.random.default_rng(42)

# 1) 模拟 GARCH(1,1) 日收益:sigma2_t = omega + alpha*r_{t-1}^2 + beta*sigma2_{t-1}
T = 6000
omega, alpha, beta = 0.02, 0.08, 0.90
r = np.empty(T); sig2 = np.empty(T)
sig2[0] = omega / (1 - alpha - beta)
r[0] = np.sqrt(sig2[0]) * rng.standard_normal()
for t in range(1, T):
    sig2[t] = omega + alpha * r[t - 1] ** 2 + beta * sig2[t - 1]
    r[t] = np.sqrt(sig2[t]) * rng.standard_normal()

p = r[:-1] ** 2              # 输入 p(t) = r_{t-1}^2
y = r[1:] ** 2               # 目标 t(t) = r_t^2(条件方差的含噪代理)
true_s2 = sig2[1:]
split = 4000

# 2) IIR 线性递归神经元:a(t) = lw*a(t-1) + iw*p(t) + b,就是 GARCH(1,1) 的方差递推
def forward(x, p, a0):
    lw, iw, b = x
    a = np.empty(len(p)); prev = a0
    for t in range(len(p)):
        prev = lw * prev + iw * p[t] + b; a[t] = prev
    return a

def loss_and_grad_bptt(x, p, y, a0):
    lw, iw, b = x
    a = forward(x, p, a0)
    e = y - a
    a_lag = np.concatenate([[a0], a[:-1]])
    g = np.zeros(3); dFda_next = 0.0
    for t in range(len(p) - 1, -1, -1):       # 线性神经元:S=1,d(t)=dF/da(t)
        dFda = -2 * e[t] + lw * dFda_next
        g += dFda * np.array([a_lag[t], p[t], 1.0])
        dFda_next = dFda
    Q = len(p)
    return np.sum(e ** 2) / Q, g / Q

a0 = y[:split].mean()
res = minimize(loss_and_grad_bptt, x0=np.array([0.5, 0.2, 0.1]), jac=True,
               args=(p[:split], y[:split], a0), method="L-BFGS-B",
               bounds=[(0, 0.999), (0, 1), (1e-6, None)])
lw, iw, b = res.x
print(f"IIR(=GARCH) 估计: lw(beta)={lw:.3f}, iw(alpha)={iw:.3f}, b(omega)={b:.4f}")

# 3) FIR 对照:输入端 TDL 取 D=5 个滞后(=ARCH(5)),线性最小二乘
D = 5
def tdl(p, D):
    return np.column_stack([np.ones(len(p) - D + 1)] +
                           [p[D - 1 - d: len(p) - d] for d in range(D)])
X = tdl(p, D); yy = y[D - 1:]; s2 = true_s2[D - 1:]
cut = split - (D - 1)
coef, *_ = np.linalg.lstsq(X[:cut], yy[:cut], rcond=None)
print("FIR(=ARCH(5)) 系数:", np.round(coef, 3))

# 4) 样本外比较:对真实条件方差的 MSE(模拟数据才有这个"上帝视角")
a_all = forward(res.x, p, a0)
fir_all = X @ coef
oos_iir = np.mean((a_all[split:] - true_s2[split:]) ** 2)
oos_fir = np.mean((fir_all[cut:] - s2[cut:]) ** 2)
oos_naive = np.mean((y[:split].mean() - true_s2[split:]) ** 2)
print(f"样本外 MSE(对真实 sigma^2): IIR={oos_iir:.4f}, FIR={oos_fir:.4f}, 常数={oos_naive:.4f}")

输出:

IIR(=GARCH) 估计: lw(beta)=0.908, iw(alpha)=0.065, b(omega)=0.0251
FIR(=ARCH(5)) 系数: [0.537 0.057 0.099 0.093 0.117 0.059]
样本外 MSE(对真实 sigma^2): IIR=0.0073, FIR=0.1081, 常数=0.3646

几点解读:

  • IIR 神经元恢复出接近真值的 \(\beta=0.90\)、\(\alpha=0.08\)。偏差来自目标 \(r_t^2\) 噪声极大(它的条件方差是 \(2\sigma^4_t\)),用平方误差而非高斯似然做损失也不是最有效的估计。实务中 GARCH 用极大似然,但梯度递推完全相同。
  • FIR 只能看 5 天历史,而真实过程的记忆以 \(\beta^k\) 衰减,半衰期约 \(\ln0.5/\ln0.9\approx6.6\) 天、影响远超 5 天,所以样本外误差高出一个数量级。这就是"有限记忆 vs 无限记忆"在波动率建模中的具体含义。用 FIR 逼近 IIR 需要很多滞后,参数随之膨胀。
  • 这给了一个可操作的建模路线:要做非线性波动率模型时,把线性 IIR 神经元换成 tansig 递归层(或 LSTM),以 GARCH 为基准,只有样本外显著胜出才采用。

14.10.3 实务要点

  • 样本切分:TDL 输入和递归状态使样本在时间上相关,不能随机打乱做交叉验证。用滚动或扩张窗口的"前推"(walk-forward)验证,并在训练段与测试段之间留出隔离期。
  • 离线与在线:离线回测训练用 BPTT(或截断 BPTT)效率最高。实盘中每根 bar 更新一次的在线模型可以用 RTRL 思路:维护 \(\partial\mathbf{a}(t)/\partial\mathbf{x}^T\) 并逐步更新权值。原书习题 E14.12 指出,权值每步变化后,沿用的旧导数已不再是精确梯度;学习率要小,这个近似才可接受。
  • 稳定性检查:伪谷和发散提示递归模型对初值敏感。训练时多次随机初始化,检查反馈权值的谱半径,或像上面的代码那样用约束把 \(lw\) 限制在 \([0,1)\)。
  • 代表性:原书说"训练序列必须代表所有可能的输入序列",放到市场里就是训练数据要覆盖牛熊、高低波动、流动性危机等不同状态,以及这些状态之间的切换方式。
  • TDL 长度 = 滞后阶数:\(DI_{m,l}\) 的选择与 ARMA 定阶、因子滞后结构选择是同一个问题,可借用信息准则或样本外误差来定。

本章小结

动态网络在静态网络上加入延迟和反馈,因而有记忆,逼近的是序列到序列的动态系统:只有输入 TDL 的是 FIR(有限记忆),有输出反馈的是 IIR(无限记忆)。LDDN 记号把任意连接统一写成 \(\mathbf{n}^m(t)=\sum\mathbf{LW}^{m,l}(d)\mathbf{a}^l(t-d)+\sum\mathbf{IW}^{m,l}(d)\mathbf{p}^l(t-d)+\mathbf{b}^m\)。训练动态网络与静态网络的唯一区别在于梯度:权值除了直接作用,还通过过去输出产生间接作用。RTRL 向前传播输出对参数的导数,适合在线、适合算 Jacobian,但计算量高;BPTT 向后传播损失对输出的导数,计算量低但要存整段轨迹;二者结果完全相同。递归网络难训练,原因是展开后很深(梯度消失/爆炸)、误差曲面有伪谷、训练序列难以有代表性;LSTM/GRU 用门控的加法状态缓解了梯度消失。对量化而言,GARCH(1,1) 本身就是一个线性 IIR 神经元,ARCH 是 FIR 网络,本章的梯度递推就是 GARCH 解析梯度的来源。

概念 公式 / 要点
LDDN 净输入 \(\mathbf{n}^m(t)=\sum_{l,d}\mathbf{LW}^{m,l}(d)\mathbf{a}^l(t-d)+\sum_{l,d}\mathbf{IW}^{m,l}(d)\mathbf{p}^l(t-d)+\mathbf{b}^m\)
单神经元导数递推 \(\partial a(t)/\partial lw=a(t-1)+lw\,\partial a(t-1)/\partial lw\)
敏感度 \(\mathbf{S}^{u,m}(t)=\partial^e\mathbf{a}^u(t)/\partial\mathbf{n}^m(t)^T\),只沿零延迟连接传播
RTRL \(\dfrac{\partial\mathbf{a}^u(t)}{\partial\mathbf{x}^T}=\dfrac{\partial^e\mathbf{a}^u(t)}{\partial\mathbf{x}^T}+\sum\mathbf{S}^{u,x}(t)\mathbf{LW}^{x,u'}(d)\dfrac{\partial\mathbf{a}^{u'}(t-d)}{\partial\mathbf{x}^T}\)
BPTT \(\dfrac{\partial F}{\partial\mathbf{a}^u(t)}=\dfrac{\partial^e F}{\partial\mathbf{a}^u(t)}+\sum[\mathbf{LW}^{x,u}(d)]^T[\mathbf{S}^{u',x}(t+d)]^T\dfrac{\partial F}{\partial\mathbf{a}^{u'}(t+d)}\)
BPTT 梯度 \(\partial F/\partial\mathbf{LW}^{m,l}(d)=\sum_t\mathbf{d}^m(t)[\mathbf{a}^l(t-d)]^T\),\(\mathbf{d}^m=\sum_u[\mathbf{S}^{u,m}]^T\partial F/\partial\mathbf{a}^u\)
复杂度 RTRL \(O(S^4D^2Q)\);BPTT \(O(S^2DQ)\),存储随 \(Q\) 增长
梯度消失 \(\partial\mathbf{a}(t)/\partial\mathbf{a}(t-k)=\prod\dot{\mathbf{F}}\,\mathbf{LW}\),按 \((\rho\,\overline{\vert \dot f\vert })^k\) 变化
LSTM 细胞 \(\mathbf{c}(t)=\mathbf{f}\odot\mathbf{c}(t-1)+\mathbf{i}\odot\tilde{\mathbf{c}}\),\(\partial\mathbf{c}(t)/\partial\mathbf{c}(t-1)\approx\mathrm{diag}(\mathbf{f})\)
GARCH ↔ IIR \(\sigma^2_t=\omega+\alpha r^2_{t-1}+\beta\sigma^2_{t-1}\) ↔ \(a(t)=lw\,a(t-1)+iw\,p(t)+b\)

练习

基础

  1. 图 14.4 的线性 IIR 网络,\(iw=2\),\(lw(1)=0.5\),\(a(0)=4\),输入 \(p=\{2,3,2\}\),求 \(a(1),a(2),a(3)\)。(原书 E14.2) 答案要点:\(a(1)=0.5\cdot4+2\cdot2=6\);\(a(2)=3+6=9\);\(a(3)=4.5+4=8.5\)。

  2. 对第 1 题的网络,取 \(Q=2\),\(F=e^2(1)+e^2(2)\)。直接把 \(F\) 写成 \(lw\)、\(iw\) 的显式函数并求导,验证与 RTRL、BPTT 的结果一致。(原书 E14.13) 提示:\(a(2)=lw^2a(0)+lw\,iw\,p(1)+iw\,p(2)\),对 \(lw\) 求导得 \(2lw\,a(0)+iw\,p(1)=a(1)+lw\,a(0)\),正是 RTRL 的 \(\partial a(2)/\partial lw\)。

  3. 一个网络中第 1 层到第 2 层有零延迟连接,第 2 层到第 1 层也有零延迟连接,没有其他延迟。这个网络能仿真吗?如何检查一个 LDDN 是否可仿真?(原书 E14.7) 答案要点:不能,存在代数环。检查方法:只保留零延迟连接构成的有向图,它必须无环(存在拓扑序,即仿真顺序)。

  4. 说明为什么 FIR 网络的 RTRL 梯度与 BPTT 梯度都退化为 LMS 的批量形式。 提示:\(E^U_{LW}=\varnothing\),间接项为零,\(d(t)=-2e(t)\)。

进阶

  1. 对 P14.5 的网络(\(S^1\) 个神经元、\(D\) 个延迟的自反馈),若 \(S^1=20\),\(D=3\),\(Q=500\),估算 RTRL 与 BPTT 梯度计算量的比值。在线交易系统每根 bar 只做一次更新时,应选哪个? 答案要点:比值约 \(S^2D=1200\)。离线训练用 BPTT;若必须每步在线更新且不能保存长历史,用 RTRL 或截断 BPTT。

  2. \(iw=1\)、\(a(0)=0\) 的线性 IIR 网络,把 \(a(t)\) 写成 \(lw\) 的多项式。说明为什么在 \(|lw|>1\) 的区域,某些输入序列会让 \(a(8)\) 很小,并据此解释误差曲面的伪谷。(原书 E14.18) 提示:\(a(8)=\sum_{k=0}^{7}lw^k p(8-k)\) 是 7 次多项式,在其实根附近值接近 0,形成窄谷。

  3. 把净输入改为距离形式 \(n_i=-\|{}_i\mathbf{w}-\mathbf{a}\|\)(RBF 型层),RTRL 与 BPTT 中哪些公式需要修改?(原书 E14.16) 提示:只有 \(\partial^e\mathbf{n}/\partial\mathbf{w}\) 和 \(\partial^e\mathbf{n}/\partial\mathbf{a}\) 变化,前者参考原书第 17 章式 (17.82)(本册第 17 章 RBF 的梯度推导);敏感度的递推结构不变。

  4. 修改 14.10.1 的代码,实现在线 RTRL:每个时刻用当前导数更新一次权值(学习率 0.1),与"整段算完再更新"的梯度比较。为什么在线版本的梯度不再精确?(原书 E14.12) 答案要点:\(\partial a(t-1)/\partial\mathbf{x}\) 是在旧权值下累积的,权值变了它就过时了;学习率越小越接近精确值。

  5. 在 14.10.2 的代码中,把损失换成高斯负对数似然 \(\sum_t\big(\ln a(t)+y(t)/a(t)\big)\),推导 BPTT 中 \(\partial^eF/\partial a(t)\) 的新形式并实现,比较参数估计精度。 提示:\(\partial^eF/\partial a(t)=1/a(t)-y(t)/a(t)^2\),其余递推不变。

  6. 证明对单个线性递归神经元,若 \(|lw|<1\),\(\partial a(t)/\partial iw\) 在输入有界时有界;若 \(|lw|>1\) 则可能无界。这与梯度爆炸有什么关系?

原书推荐习题:E14.2、E14.3(手算响应);E14.4、E14.10(LDDN 集合与仿真顺序);E14.13(直接求导验证 RTRL/BPTT);E14.12(在线 RTRL);E14.17 与 P14.5(复杂度);E14.18(伪谷)。


原书对照

本章小节 原书内容 PDF 页码
14.1 14 目标、理论概述 p.520–521
14.2 Layered Digital Dynamic Networks p.522–524
14.3 Example Dynamic Networks(FIR、IIR) p.524–527
14.4 Principles of Dynamic Learning p.527–530
14.5 Dynamic Backpropagation:Preliminary Definitions、RTRL 及例子 p.531–541
14.6 BPTT 及例子 p.541–549
14.7、14.8 Summary and Comments on Dynamic Training;P14.5 p.549–552、p.556–564
小结 Summary of Results p.553–555
例题 Solved Problems P14.1–P14.5 p.556–564
延伸阅读 Epilogue、Further Reading(DeHa07、MaNe99、PhHa13、Werb90、WiZi89) p.565–566
习题 Exercises E14.1–E14.18 p.567–573

14.9 节(Elman、LSTM、GRU、截断 BPTT 等)与 14.10 节为编者补充,不在原书第 14 章中。