量化交易中文教材

第 12 章 反向传播的变体

对应原书第 12 章。第 11 章的反向传播是重大突破,但基本算法在实际问题上慢得令人难以接受,原书说可能要几天甚至几周机时。本章先用一个能看清误差曲面的小例子说明为什么慢,再介绍两类加速办法:一类是从观察中得来的启发式改进(动量、可变学习率),另一类是直接借用数值优化的成熟方法(共轭梯度、Levenberg–Marquardt)。

要强调的是:本章所有算法都用反向传播计算导数(从最后一层向第一层算),区别只在于拿到导数后怎样更新权值。为避免混淆,原书把第 11 章的基本算法称为最速下降反向传播(SDBP, steepest descent backpropagation)。Levenberg–Marquardt 对量化读者尤其重要:它是收益率曲线、波动率曲面等模型校准的主力算法。

学习目标

读完本章,你应当能够:

  1. 描述多层网络误差曲面的三个特征:曲率变化剧烈、存在多个局部极小、对称性使原点成为鞍点;据此说明初值应取小随机数。
  2. 写出动量反向传播(MOBP),从低通滤波的角度解释它,并用特征值分析证明:对二次函数,无论学习率多大,总有动量系数使其稳定。
  3. 执行可变学习率反向传播(VLBP)的三条规则,能手算若干步。
  4. 实现共轭梯度反向传播所需的线搜索:区间定位 + 黄金分割,并知道何时重置搜索方向。
  5. 从平方和函数的 Hessian 推导 Gauss–Newton 与 Levenberg–Marquardt 算法,解释 \(\mu\) 如何在牛顿法与最速下降之间切换。
  6. 用 Marquardt 敏感度反传计算网络误差的 Jacobian 矩阵,执行 LMBP 的四个步骤,并知道它的存储瓶颈。
  7. 用 LM 校准一条 Nelson–Siegel 收益率曲线,并用 \(\mathbf{J}^T\mathbf{J}\) 估计参数的标准误。

读前导读

这一章在解决什么问题。 第 11 章解决了"梯度怎么算",本章解决"拿到梯度后怎么走"。基本的反向传播每一步都沿负梯度走固定步长,就像一个只看当下坡度、步幅永远不变的登山者:在平地上挪不动,在窄谷里左右乱撞。本章的改进分两类。一类是经验规则:给步子加惯性(动量),或者走得顺就迈大步、走错就退回来迈小步(可变学习率)。另一类是数值优化的正规军:共轭梯度和 Levenberg–Marquardt(LM)。

LM 对你最有用。如果你做过收益率曲线拟合、期权波动率曲面校准,或者用过 Excel 的规划求解去拟合一个非线性模型,后台很可能就是 LM。它的核心思想你也熟悉:把非线性模型在当前点做一阶近似(像用久期近似债券价格),近似后就是一个普通的线性回归,解正规方程得到一步;再在新的点重新近似、重新回归,如此反复。这叫 Gauss–Newton。LM 再加一个"刹车"参数 \(\mu\):近似不靠谱时就把步子缩小、方向转向最速下降。加刹车的写法 \((\mathbf{J}^T\mathbf{J}+\mu\mathbf{I})\) 和岭回归的 \((X^TX+\lambda I)\) 是同一个形式。

需要先想起来的数学。

  • 二阶泰勒展开与牛顿法。 在 \(x_0\) 附近 \(F(x)\approx F(x_0)+F'(x_0)\Delta x+\tfrac12F''(x_0)\Delta x^2\),对 \(\Delta x\) 求最小得 \(\Delta x=-F'/F''\),这就是牛顿法。多元时 \(F''\) 换成 Hessian 矩阵 \(\nabla^2F\),除法换成乘逆矩阵。金融对应:用久期加凸性近似价格变动。见 第 00 册第 02 章 导数与泰勒展开 和 第 00 册第 05 章 多元微积分与优化。
  • Jacobian 矩阵。 一组函数 \(v_1,\dots,v_N\) 对一组参数 \(x_1,\dots,x_n\) 的全部一阶偏导排成 \(N\times n\) 的矩阵,第 \(i\) 行是第 \(i\) 个残差对各参数的敏感度。例:\(v_1=x_1+2x_2\),\(v_2=x_1x_2\),在 \((1,1)\) 处 \(\mathbf{J}=\begin{bmatrix}1&2\\1&1\end{bmatrix}\)。在线性回归里 Jacobian 就是(带负号的)设计矩阵 \(X\)。见第 05 章。
  • 特征值与线性递推的稳定性。 递推 \(\mathbf{x}_{k+1}=\mathbf{W}\mathbf{x}_k\) 收敛到 0,当且仅当 \(\mathbf{W}\) 所有特征值的模(复数的绝对值)都小于 1,这叫"谱半径小于 1"。一维就是 AR(1) 模型 \(x_{k+1}=\phi x_k\) 平稳要求 \(|\phi|<1\),你在 CFA 时间序列里见过。见 第 00 册第 06 章 线性代数速成。
  • 一元二次方程的根。 \(\lambda^2-b\lambda+c=0\) 的两根之和为 \(b\)、之积为 \(c\)(韦达定理);判别式 \(b^2-4c<0\) 时两根是共轭复数,模相等,都等于 \(\sqrt c\)。12.2.3 节的动量稳定性证明就靠这一条。
  • 矩阵加 \(\mu\mathbf{I}\) 的效果。 \(\mathbf{A}+\mu\mathbf{I}\) 与 \(\mathbf{A}\) 特征向量相同、每个特征值加 \(\mu\)。所以一个接近奇异(最小特征值接近 0)的矩阵加上 \(\mu\mathbf{I}\) 就变得可逆,条件数变小。见第 06 章。

怎么读这一章。 必读 12.1(为什么慢,看懂误差曲面的三个特征)、12.2(动量)、12.5.1–12.5.2(Gauss–Newton 和 LM),以及 12.7.3 的 Nelson–Siegel 校准,那是最直接的实务应用。12.2.3 的稳定性证明第一次可以只看结论(动量系数足够接近 1 总能稳定),有兴趣再回来读讲解框。12.3 可变学习率的手算例和 12.4 共轭梯度的线搜索细节第一次可以跳过。12.5.3 的 Marquardt 敏感度是第 11 章反向传播的直接变形,读懂第 11 章后会很顺。


12.1 反向传播为什么慢

LMS 在学习率不太大时保证收敛到均方误差最小解,因为单层线性网络的均方误差是二次函数:只有一个驻点,Hessian 处处相同,任何方向上的曲率不变,等高线是椭圆。SDBP 是 LMS 的推广(单层线性时两者相同),但多层网络的误差曲面完全不同。

12.1.1 性能曲面例

为了知道最优解在哪里,原书让一个 1-2-1 网络(两层都是 log-sigmoid)去逼近同结构网络在下列参数下的响应:

\[w^1_{1,1}=10,\ w^1_{2,1}=10,\ b^1_1=-5,\ b^1_2=5;\qquad w^2_{1,1}=1,\ w^2_{1,2}=1,\ b^2=-1 \tag{12.1–12.2}\]

响应是 \([-2,2]\) 上一条从 0 升到 1 的双台阶曲线。在 \(p=-2,-1.9,\dots,2\)(41 个点)采样,性能指标取 41 点的误差平方和。每次只让两个参数变化、其余固定在最优值,画出误差曲面:

  • \(w^1_{1,1}\) 与 \(w^2_{1,1}\)(图 12.3):曲面显然不是二次的,曲率变化剧烈——平坦区域适合大学习率,高曲率区域需要小学习率,选不出一个对全局都合适的学习率。平坦区在意料之中:sigmoid 输入很大时输出几乎不变。曲面有多个局部极小:全局极小在 \(w^1_{1,1}=10,\ w^2_{1,1}=1\),位于一条平行于 \(w^1_{1,1}\) 轴的山谷中;另一个局部极小约在 \(w^1_{1,1}=0.88,\ w^2_{1,1}=38.6\),位于平行于 \(w^2_{1,1}\) 轴的山谷中。
  • \(w^1_{1,1}\) 与 \(b^1_1\)(图 12.4):极小在 \((10,-5)\);曲面扭曲,有的地方很陡,有的地方极平。从 \(w^1_{1,1}=0,\ b^1_1=-10\) 出发,梯度几乎为零,最速下降实际上停住不动,尽管离极小点很远。
  • \(b^1_1\) 与 \(b^1_2\)(图 12.5):极小在 \((-5,5)\)。这里能看到多层网络的对称性:存在两个误差相同的极小,第二个对应把第一层两个神经元交换(网络"上下翻转")。正因为这种对称性,原点往往是误差曲面的鞍点。

白话解释:鞍点是梯度为零、但既不是极小也不是极大的点——沿某些方向是谷底,沿另一些方向是山脊,形状像马鞍。为什么对称性让原点成为鞍点?两个隐层神经元互换位置,网络函数不变,所以误差曲面关于"互换"是对称的,两个等价的极小点分处对称面两侧。如果全部权值都是 0,两个神经元完全相同,处于这面"镜子"的正中间。更糟的是,此时两个神经元收到的梯度也完全相同(反向传播里它们的 \(s\) 一样),它们会永远保持相同、无法分化,等于只有一个神经元。小随机初值的作用就是"打破对称"。

初值的启示:不要设为零(原点倾向于是鞍点);不要设得很大(远离最优点处曲面非常平坦,sigmoid 饱和、梯度消失)。通常取小随机数,既避开原点又不进入平坦区;Nguyen–Widrow 初始化 [NgWi90] 根据 sigmoid 形状和输入范围确定权值大小,并用偏置把各 sigmoid 的中心散布在输入区间内。另外还应尝试多个初值。

12.1.2 收敛例

下面都用批处理(batching):整个训练集都呈现之后才更新一次,梯度是各样本梯度的平均,比单样本估计准确得多;训练集完备时就是精确梯度。

只调 \(w^1_{1,1}\) 与 \(w^2_{1,1}\) 的两条 SDBP 轨迹(图 12.6):

  • 轨迹 a 最终收敛到最优解,但非常慢:途中先是中等坡度,然后经过一片很平的区域,最后落入坡度极缓的山谷。增大学习率能加快通过平坦区,但进入山谷后会变得不稳定。
  • 轨迹 b 被困在另一条山谷中,收敛到局部极小 \(w^1_{1,1}=0.88,\ w^2_{1,1}=38.6\)。

误差随迭代的曲线(图 12.7,对数横轴)是 SDBP 的典型形态:长时间几乎没有进展,然后在很短时间内骤降。图 12.8 用更大的学习率:开始快得多,但进入含极小点的狭窄山谷后开始发散,在山谷两壁之间来回振荡。

由此得到两个改进思路:(1) 可变学习率——平坦处增大、坡度变陡时减小(问题是算法怎么知道自己在哪儿);(2) 平滑轨迹——对更新量做平均,滤掉振荡。


12.2 启发式改进之一:动量

12.2.1 低通滤波器

先看一阶滤波器

\[y(k)=\gamma y(k-1)+(1-\gamma)w(k),\qquad 0\le\gamma<1 \tag{12.4–12.5}\]

\(w(k)\) 是输入,\(y(k)\) 是输出,\(\gamma\) 称为动量系数(momentum coefficient)。原书用 \(w(k)=1+\sin(2\pi k/16)\) 演示:\(\gamma=0.9\) 时输出的振荡明显小于输入,\(\gamma=0.98\) 时更小;输出的均值始终等于输入的均值,但 \(\gamma\) 越大响应越慢。一句话:减少振荡,同时跟踪平均值。量化读者会认出这就是指数加权移动平均(EWMA)。

12.2.2 动量反向传播(MOBP)

把参数更新量 \(\Delta\mathbf{W}^m(k)=-\alpha\mathbf{s}^m(\mathbf{a}^{m-1})^T\) 当作滤波器的输入:

\[\boxed{\Delta\mathbf{W}^m(k)=\gamma\Delta\mathbf{W}^m(k-1)-(1-\gamma)\alpha\mathbf{s}^m(\mathbf{a}^{m-1})^T,\qquad \Delta\mathbf{b}^m(k)=\gamma\Delta\mathbf{b}^m(k-1)-(1-\gamma)\alpha\mathbf{s}^m} \tag{12.9–12.10}\]

原书图 12.10:初值和学习率与图 12.8(原本发散)相同,加上 \(\gamma=0.8\) 的动量后算法变得稳定。动量允许使用更大的学习率而保持稳定;当轨迹连续沿同一方向移动时,更新量不断累积,还会加速收敛。名字由来:它让轨迹倾向于沿原方向继续走,\(\gamma\) 越大"惯性"越大。在狭窄山谷中,横向的来回振荡被平均掉,纵向(沿谷底)的一致分量被保留并累积。

推导拆解:把式 12.9 的递推一路展开(记 \(\mathbf{G}(k)=\mathbf{s}^m(\mathbf{a}^{m-1})^T\) 为第 \(k\) 步的梯度):

\[\Delta\mathbf{W}(k)=-(1-\gamma)\alpha\big[\mathbf{G}(k)+\gamma\mathbf{G}(k-1)+\gamma^2\mathbf{G}(k-2)+\cdots\big]\]
方括号乘 \((1-\gamma)\) 正是梯度的指数加权移动平均,权重 \((1-\gamma)\gamma^j\) 之和为 1。所以动量的每一步 = 学习率 × 近期梯度的 EWMA。在山谷里,横向梯度一正一负交替,加权平均后相互抵消;沿谷底的梯度符号一致,平均后保留。金融上这和用 EWMA 平滑一个噪声很大的交易信号、只在信号持续同向时才大幅调仓是同一思路。

12.2.3 动量总能使最速下降稳定(P12.2)

这是一个漂亮的结果。对二次函数 \(F=\tfrac12\mathbf{x}^T\mathbf{A}\mathbf{x}+\mathbf{d}^T\mathbf{x}+c\),带动量的最速下降为 \(\Delta\mathbf{x}_k=\gamma\Delta\mathbf{x}_{k-1}-(1-\gamma)\alpha\mathbf{g}_k\),\(\mathbf{g}_k=\mathbf{A}\mathbf{x}_k+\mathbf{d}\)。代入 \(\Delta\mathbf{x}_k=\mathbf{x}_{k+1}-\mathbf{x}_k\):

\[\mathbf{x}_{k+1}=[(1+\gamma)\mathbf{I}-(1-\gamma)\alpha\mathbf{A}]\mathbf{x}_k-\gamma\mathbf{x}_{k-1}-(1-\gamma)\alpha\mathbf{d}\]

这是二阶差分方程。令 \(\tilde{\mathbf{x}}_k=[\mathbf{x}_{k-1};\mathbf{x}_k]\),化为一阶系统 \(\tilde{\mathbf{x}}_{k+1}=\mathbf{W}\tilde{\mathbf{x}}_k+\mathbf{v}\):

\[\mathbf{W}=\begin{bmatrix}\mathbf{0}&\mathbf{I}\\-\gamma\mathbf{I}&\mathbf{T}\end{bmatrix},\qquad \mathbf{T}=(1+\gamma)\mathbf{I}-(1-\gamma)\alpha\mathbf{A}\]

稳定当且仅当 \(\mathbf{W}\) 的特征值模都小于 1。设 \(\mathbf{W}\mathbf{z}=\lambda^w\mathbf{z}\),\(\mathbf{z}=[\mathbf{z}_1;\mathbf{z}_2]\),第一块给出 \(\mathbf{z}_2=\lambda^w\mathbf{z}_1\),第二块给出 \(-\gamma\mathbf{z}_1+\mathbf{T}\mathbf{z}_2=\lambda^w\mathbf{z}_2\)。取 \(\mathbf{z}_2\) 为 \(\mathbf{T}\) 的特征向量(特征值 \(\lambda^t\)),得 \([(\lambda^w)^2-\lambda^t\lambda^w+\gamma]\mathbf{z}_2=\mathbf{0}\),所以 \(\mathbf{T}\) 的每个特征值对应 \(\mathbf{W}\) 的两个特征值

\[\lambda^w=\frac{\lambda^t\pm\sqrt{(\lambda^t)^2-4\gamma}}{2}\]

当 \((\lambda^t)^2<4\gamma\) 时它们是共轭复数,模为 \(|\lambda^w|=\sqrt{(\lambda^t)^2/4+(4\gamma-(\lambda^t)^2)/4}=\sqrt\gamma<1\)——稳定。

推导拆解:这段证明分三步,每步一个技巧。

  1. 二阶递推化为一阶。 \(\mathbf{x}_{k+1}\) 依赖 \(\mathbf{x}_k\) 和 \(\mathbf{x}_{k-1}\),像 AR(2)。把相邻两期堆成一个长向量 \(\tilde{\mathbf{x}}_k\),就变成"状态向量的一阶递推",矩阵 \(\mathbf{W}\) 的第一块行只是把 \(\mathbf{x}_k\) 原样搬到上半部分。这和把 AR(2) 写成伴随矩阵(companion matrix)形式完全相同。
  2. 把大矩阵的特征值化为小方程。 因为 \(\mathbf{T}\) 和 \(\mathbf{A}\) 只差单位矩阵的倍数,二者特征向量相同;沿每个特征向量方向,问题退化为一维的二次方程 \((\lambda^w)^2-\lambda^t\lambda^w+\gamma=0\)。这一步相当于把耦合的多维问题按"主轴"拆成互不干扰的标量问题。
  3. 复根的模。 由韦达定理,两根之积等于常数项 \(\gamma\);共轭复根模相等,所以每个根的模都是 \(\sqrt\gamma\)。这就是为什么只要进入复根区域,收敛速度就完全由 \(\gamma\) 决定、与学习率无关。

白话解释:复根意味着轨迹以螺旋的方式靠近最优点(有振荡),振幅每步乘以 \(\sqrt\gamma\)。\(\gamma=0.81\) 时每步缩为 0.9 倍,约 22 步缩小到十分之一。\(\mathbf{T}\) 与 \(\mathbf{A}\) 特征向量相同,特征值 \(\lambda^t_i=(1+\gamma)-(1-\gamma)\alpha\lambda_i\),所以只需

\[|(1+\gamma)-(1-\gamma)\alpha\lambda_i|<2\sqrt\gamma\]

在 \(\gamma=1\) 处两边都等于 2。对 \(\gamma\) 求导:右边斜率为 1,左边斜率为 \(1+\alpha\lambda_i>1\)(强极小时 \(\lambda_i>0\))。所以当 \(\gamma\) 从 1 稍稍减小时,左边下降得比右边快,不等式成立。结论:对二次函数,无论学习率多大,只要动量系数足够接近 1,带动量的最速下降就稳定,且此时特征值的模为 \(\sqrt\gamma\)。 代价是 \(\sqrt\gamma\) 接近 1 时收敛变慢。原书示例:\(F=x_1^2+25x_2^2\),\(\alpha=0.041\) 时无动量发散(第 09 章),加 \(\gamma=0.2\) 后轨迹稳定。


12.3 启发式改进之二:可变学习率

单层线性网络的误差曲面是二次的,最大稳定学习率是固定的 \(2/\lambda_{\max}\)。多层网络的曲面形状随区域变化,可以在训练中调整学习率,难点是何时调、调多少。原书介绍一种简单的批量方法 [VoMa88]——可变学习率反向传播(VLBP):

  1. 若一次更新后(整个训练集上的)平方误差增加超过设定比例 \(\zeta\)(通常 1%–5%),则丢弃这次更新,学习率乘以 \(\rho\in(0,1)\),动量系数(若使用)置零;
  2. 若平方误差下降,接受更新,学习率乘以 \(\eta>1\);若 \(\gamma\) 之前被置零则恢复;
  3. 若平方误差增加但不超过 \(\zeta\),接受更新,学习率不变;若 \(\gamma\) 之前被置零则恢复。

原书用 \(\eta=1.05,\ \rho=0.7,\ \zeta=4\%\) 重做前面的例子(图 12.11、12.12):轨迹沿直线前进、误差持续下降时学习率不断增大;到达狭窄山谷时学习率迅速减小——每当一步会使误差增加超过 4%,就降学习率并去掉动量,使轨迹能急转弯沿山谷走;之后学习率再次增大;接近收敛越过极小点时再次降低。

例(P12.3)——手算三步。 \(F=x_1^2+25x_2^2\),\(\mathbf{x}_0=[0.5,0.5]^T\),\(\alpha=0.05,\ \gamma=0.2,\ \eta=1.5,\ \rho=0.5,\ \zeta=5\%\)。\(F(\mathbf{x}_0)=6.5\),\(\mathbf{g}_0=[1,25]^T\)。

  • 第 1 步:\(\Delta\mathbf{x}_0=-0.8(0.05)[1,25]^T=[-0.04,-1]^T\),试探点 \([0.46,-0.5]^T\),\(F=6.4616<6.5\),接受,\(\alpha\to0.075\)。
  • 第 2 步:\(\mathbf{g}_1=[0.92,-25]^T\),\(\Delta\mathbf{x}_1=0.2[-0.04,-1]^T-0.8(0.075)[0.92,-25]^T=[-0.0632,1.3]^T\),试探点 \([0.3968,0.8]^T\),\(F=16.157\),增幅远超 5%,拒绝;\(\alpha\to0.0375\),\(\gamma\to0\)。
  • 第 3 步:\(\Delta\mathbf{x}_2=-0.0375[0.92,-25]^T=[-0.0345,0.9375]^T\),试探点 \([0.4255,0.4375]^T\),\(F=4.966<6.4616\),接受;\(\gamma\) 恢复为 0.2,\(\alpha\to0.05625\)。

相关变体:Jacobs 的 delta-bar-delta [Jaco88] 给每个参数一个独立的学习率,参数变化方向连续几次相同就增大、方向交替就减小;Tollenaere 的 SuperSAB [Toll90] 类似但规则更复杂;Fahlman 的 Quickprop [Fahl88] 假设误差曲面在极小点附近是开口向上的抛物线,且各权值的影响可以分开考虑。今天深度学习常用的 RMSProp、Adam 等"逐参数自适应学习率 + 动量"的优化器,在思想上正是这条路线的延续。

启发式方法的两大缺点:(1) 需要设定好几个参数(\(\zeta,\rho,\eta\) 等),SDBP 只需一个学习率;复杂的方法可能有五六个参数,性能常对它们敏感且依问题而定;(2) 有时在 SDBP 最终能解决的问题上反而不收敛。算法越复杂,这两点越常见。


12.4 数值优化技术之一:共轭梯度反向传播

第 09 章的共轭梯度不需要二阶导数,又有二次终止性。用于多层网络称为 CGBP。算法回顾:\(\mathbf{p}_0=-\mathbf{g}_0\);\(\mathbf{x}_{k+1}=\mathbf{x}_k+\alpha_k\mathbf{p}_k\),\(\alpha_k\) 沿方向极小化;\(\mathbf{p}_k=-\mathbf{g}_k+\beta_k\mathbf{p}_{k-1}\),\(\beta_k\) 取 HS / FR / PR 之一;未收敛则重复。梯度由反向传播(批量)计算。

但它不能照搬,因为性能指标不是二次的:(1) 不能用式 9.31 解析地求步长,需要线搜索;(2) 一般不会在 \(n\) 步内精确收敛,需要重置。

12.4.1 区间定位

先找一个包含极小点的区间。函数比较法 [Scal85]:先算 \(F(\mathbf{x}_0)\),再算 \(F(\mathbf{x}_0+\varepsilon\mathbf{p}_0)\),然后依次在 \(2\varepsilon,4\varepsilon,8\varepsilon,\dots\) 处求值(距离每次加倍),直到函数值上升为止。此时极小点被夹在最后三个求值点中首尾两点构成的区间里(倒数第三个点到最后一个点)。仅凭这些求值无法再缩小区间:极小点可能在区间前半段,也可能在后半段。

12.4.2 区间缩减:黄金分割

至少要在区间内求两个内点 \(c<d\) 的值才能缩小区间(一个内点不提供方向信息):若 \(F(c)>F(d)\),极小必在 \([c,b]\);若 \(F(c)<F(d)\),极小必在 \([a,d]\)(假设区间内只有一个极小)。黄金分割搜索(Golden Section search)巧妙地安排内点,使每次迭代只需一次新的函数求值。设 \(\tau=0.618\):

\(c_1=a_1+(1-\tau)(b_1-a_1)\),\(d_1=b_1-(1-\tau)(b_1-a_1)\)。对 \(k=1,2,\dots\):

  • 若 \(F_c<F_d\):\(a_{k+1}=a_k\),\(b_{k+1}=d_k\),\(d_{k+1}=c_k\),\(c_{k+1}=a_{k+1}+(1-\tau)(b_{k+1}-a_{k+1})\);\(F_d\leftarrow F_c\),\(F_c\leftarrow F(c_{k+1})\);
  • 否则:\(a_{k+1}=c_k\),\(b_{k+1}=b_k\),\(c_{k+1}=d_k\),\(d_{k+1}=b_{k+1}-(1-\tau)(b_{k+1}-a_{k+1})\);\(F_c\leftarrow F_d\),\(F_d\leftarrow F(d_{k+1})\);

直到 \(b_{k+1}-a_{k+1}<tol\)。每次区间缩为原来的 0.618 倍。妙处在于旧的内点恰好成为新区间的内点之一,这正是黄金比例 \(\tau^2=1-\tau\) 的性质。

推导拆解:把区间长度归一为 1,内点放在 \(1-\tau\) 和 \(\tau\) 处。若保留左段 \([0,\tau]\),旧的左内点 \(1-\tau\) 要能直接充当新区间的右内点,即它应在新区间长度的 \(\tau\) 倍处:\(1-\tau=\tau\cdot\tau\)。解 \(\tau^2+\tau-1=0\) 得 \(\tau=(\sqrt5-1)/2\approx0.618\)。这样每轮只有一个新内点需要求值。对神经网络来说,每次"求值"都要对全部样本做一次前向传播,省下一次求值就省下一次全样本计算。

例(P12.4)。 \(F=\tfrac12\mathbf{x}^T\begin{bmatrix}2&1\\1&2\end{bmatrix}\mathbf{x}\),\(\mathbf{x}_0=[0.8,-0.25]^T\),沿 \(\mathbf{p}_0=[-1.35,-0.3]^T\) 搜索。区间定位(\(\varepsilon=0.075\)):\(F(0)=0.5025\),\(F(0.075)=0.3721\),\(F(0.15)=0.2678\),\(F(0.3)=0.1373\),\(F(0.6)=0.1893\)——上升了,极小在 \([0.15,0.6]\)。黄金分割:\(c_1=0.15+0.382(0.45)=0.3219\),\(d_1=0.6-0.382(0.45)=0.4281\),\(F_c=0.1270\),\(F_d=0.1085\);\(F_c>F_d\),新区间 \([0.3219,0.6]\),\(c_2=0.4281\),\(d_2=0.4938\),\(F_d=0.1232\);此时 \(F_c<F_d\),新区间 \([0.3219,0.4938]\),\(d_3=0.4281\),\(c_3=0.3876\),\(F_c=0.1094\)……继续下去收敛到第 09 章解析得到的 \(\alpha_0=0.413\)。

12.4.3 重置与表现

二次函数至多 \(n\) 次迭代收敛(\(n\) 为参数个数);非二次函数一般不会,而理论也没说一个 \(n\) 步周期之后该用什么方向。最简单的做法:每 \(n\) 次迭代把搜索方向重置为负梯度 [Scal85]。

原书图 12.16 显示 CGBP 的迭代次数远少于前述算法。但这有些误导:每次迭代包含区间定位和黄金分割的多次函数求值,单次迭代的计算量大得多。尽管如此,CGBP 已被证明是多层网络最快的批量训练算法之一 [Char92]。


12.5 数值优化技术之二:Levenberg–Marquardt 算法

LM 算法是牛顿法的变体,专为平方和形式的函数设计——这正是均方误差的形式,所以非常适合网络训练,也是一切非线性最小二乘校准问题的标准工具。更系统的理论(信赖域解释、大残差问题)见第 04 册第 10 章。

12.5.1 从牛顿法到 Gauss–Newton

设 \(F\) 是 \(N\) 个函数的平方和:

\[F(\mathbf{x})=\sum_{i=1}^Nv_i^2(\mathbf{x})=\mathbf{v}^T(\mathbf{x})\mathbf{v}(\mathbf{x}) \tag{12.20}\]

梯度第 \(j\) 元 \(\partial F/\partial x_j=2\sum_iv_i\,\partial v_i/\partial x_j\),矩阵形式

\[\nabla F(\mathbf{x})=2\mathbf{J}^T(\mathbf{x})\mathbf{v}(\mathbf{x}),\qquad \mathbf{J}(\mathbf{x})=\Big[\frac{\partial v_i}{\partial x_j}\Big]_{N\times n} \tag{12.22–12.23}\]

\(\mathbf{J}\) 是 Jacobian 矩阵。Hessian 的 \((k,j)\) 元

\[[\nabla^2F]_{k,j}=2\sum_{i=1}^N\Big\{\frac{\partial v_i}{\partial x_k}\frac{\partial v_i}{\partial x_j}+v_i\frac{\partial^2v_i}{\partial x_k\partial x_j}\Big\}\quad\Rightarrow\quad \nabla^2F(\mathbf{x})=2\mathbf{J}^T\mathbf{J}+2\mathbf{S}(\mathbf{x}),\quad \mathbf{S}=\sum_iv_i\nabla^2v_i \tag{12.24–12.26}\]

若 \(\mathbf{S}\) 很小(残差小,或模型接近线性),\(\nabla^2F\approx2\mathbf{J}^T\mathbf{J}\),代入牛顿法 \(\mathbf{x}_{k+1}=\mathbf{x}_k-\mathbf{A}_k^{-1}\mathbf{g}_k\) 得 Gauss–Newton 法:

\[\mathbf{x}_{k+1}=\mathbf{x}_k-[2\mathbf{J}^T\mathbf{J}]^{-1}2\mathbf{J}^T\mathbf{v}=\mathbf{x}_k-[\mathbf{J}^T\mathbf{J}]^{-1}\mathbf{J}^T\mathbf{v} \tag{12.28}\]

优点是不需要二阶导数。对线性最小二乘(\(\mathbf{v}=\mathbf{t}-\mathbf{G}\mathbf{x}\)),\(\mathbf{S}=\mathbf{0}\),Gauss–Newton 一步就得到正规方程的解(原书 E12.15)。

推导拆解:梯度与 Hessian 的两行公式各用了一个求导法则。

  • 梯度(链式法则):\(v_i^2\) 对 \(x_j\) 求导,外层 \((\cdot)^2\) 给出 \(2v_i\),内层给出 \(\partial v_i/\partial x_j\),再对 \(i\) 求和。"对 \(i\) 求和 \(J_{ij}v_i\)"正是 \(\mathbf{J}^T\mathbf{v}\) 的第 \(j\) 个分量。
  • Hessian(乘积法则):再对 \(x_k\) 求导 \(2v_i\,\partial v_i/\partial x_j\),这是两个因子的乘积,\((uv)'=u'v+uv'\) 给出两项:一项是两个一阶导相乘(组成 \(\mathbf{J}^T\mathbf{J}\)),另一项是残差 \(v_i\) 乘二阶导(组成 \(\mathbf{S}\))。

白话解释:Gauss–Newton 还有一个更直观的推法,不用 Hessian。把残差在当前点线性化:\(\mathbf{v}(\mathbf{x}+\Delta\mathbf{x})\approx\mathbf{v}+\mathbf{J}\Delta\mathbf{x}\)。最小化 \(\|\mathbf{v}+\mathbf{J}\Delta\mathbf{x}\|^2\) 是一个普通线性回归:把 \(\mathbf{J}\) 当设计矩阵、\(-\mathbf{v}\) 当因变量,正规方程的解就是式 12.28。所以 Gauss–Newton = "线性化 → 跑一次 OLS → 移到新点 → 再线性化"。被丢掉的 \(\mathbf{S}\) 正是线性化忽略的弯曲部分,残差小或模型近似线性时它不重要。

金融直觉:这和用修正久期近似债券价格、再迭代求到期收益率是同一个逻辑。用 Newton 法从价格反解 YTM 时,每一步都用"价格对收益率的一阶导"做线性外推;价格–收益率曲线弯得越厉害(凸性越大)、初始猜测离答案越远,单步近似就越差,需要更多迭代。

12.5.2 Levenberg–Marquardt

Gauss–Newton 的问题是 \(\mathbf{H}=\mathbf{J}^T\mathbf{J}\) 可能奇异或接近奇异。修正为

\[\mathbf{G}=\mathbf{H}+\mu\mathbf{I} \tag{12.29}\]

若 \(\mathbf{H}\) 的特征值、特征向量为 \(\lambda_i,\mathbf{z}_i\),则 \(\mathbf{G}\mathbf{z}_i=(\lambda_i+\mu)\mathbf{z}_i\):特征向量不变,特征值都加 \(\mu\)。\(\mu\) 足够大时 \(\mathbf{G}\) 正定可逆。由此得 Levenberg–Marquardt 算法:

\[\boxed{\Delta\mathbf{x}_k=-[\mathbf{J}^T(\mathbf{x}_k)\mathbf{J}(\mathbf{x}_k)+\mu_k\mathbf{I}]^{-1}\mathbf{J}^T(\mathbf{x}_k)\mathbf{v}(\mathbf{x}_k)} \tag{12.32}\]

关键性质:\(\mu_k\) 很大时,

\[\mathbf{x}_{k+1}\cong\mathbf{x}_k-\frac{1}{\mu_k}\mathbf{J}^T\mathbf{v}=\mathbf{x}_k-\frac{1}{2\mu_k}\nabla F(\mathbf{x}) \tag{12.33}\]

即学习率为 \(1/(2\mu_k)\) 的最速下降;\(\mu_k\to0\) 时就是 Gauss–Newton。

金融直觉:式 12.32 与岭回归的系数公式 \((X^TX+\lambda I)^{-1}X^Ty\) 形式完全相同:LM 的每一步,就是对线性化后的问题跑一次岭回归。岭惩罚的效果是把系数往 0 收缩,在这里就是"把这一步 \(\Delta\mathbf{x}\) 往 0 收缩",即迈小步。\(\mu\) 越大,越不相信线性近似、步子越小越保守。好比风险经理对一个只在小幅变动内可靠的 delta 近似设了头寸限额:近似屡次失败(误差不降反升)就收紧限额(增大 \(\mu\)),连续成功就放宽。

\(\mu\) 的调整:初始取小值(如 \(\mu_0=0.01\));若一步不能减小 \(F\),则 \(\mu\) 乘以 \(\vartheta>1\)(如 10)后重做这一步——\(F\) 最终一定会下降,因为这时走的是沿最速下降方向的小步;若一步使 \(F\) 减小,下一步 \(\mu\) 除以 \(\vartheta\),向 Gauss–Newton 靠拢以加快收敛。这样 LM 兼得牛顿法的速度和最速下降的保证收敛,也就是第 09 章预告的"出现发散时退化为最速下降"的牛顿法变体。

12.5.3 用于多层网络:Jacobian 的计算

若各样本等概率,均方误差正比于误差平方和

\[F(\mathbf{x})=\sum_{q=1}^Q(\mathbf{t}_q-\mathbf{a}_q)^T(\mathbf{t}_q-\mathbf{a}_q)=\sum_{q=1}^Q\sum_{j=1}^{S^M}(e_{j,q})^2=\sum_{i=1}^Nv_i^2 \tag{12.34}\]

误差向量与参数向量为

\[\mathbf{v}^T=[e_{1,1}\ e_{2,1}\ \cdots\ e_{S^M,1}\ e_{1,2}\ \cdots\ e_{S^M,Q}],\qquad \mathbf{x}^T=[w^1_{1,1}\ w^1_{1,2}\ \cdots\ w^1_{S^1,R}\ b^1_1\ \cdots\ b^1_{S^1}\ w^2_{1,1}\ \cdots\ b^M_{S^M}] \tag{12.35–12.36}\]

\(N=Q\times S^M\),\(n=S^1(R+1)+S^2(S^1+1)+\cdots+S^M(S^{M-1}+1)\)。Jacobian 的每一行对应某个样本的某个输出误差,每一列对应一个权值或偏置。

标准反向传播算的是平方误差对参数的导数 \(\partial(\mathbf{e}_q^T\mathbf{e}_q)/\partial x_l\);LM 需要的是误差本身的导数 \(\partial e_{k,q}/\partial x_l\)。仿照敏感度,定义 Marquardt 敏感度:

\[\tilde s^m_{i,h}\equiv\frac{\partial v_h}{\partial n^m_{i,q}}=\frac{\partial e_{k,q}}{\partial n^m_{i,q}},\qquad h=(q-1)S^M+k \tag{12.42}\]

则 Jacobian 元素为

\[[\mathbf{J}]_{h,l}=\frac{\partial e_{k,q}}{\partial w^m_{i,j}}=\tilde s^m_{i,h}\,a^{m-1}_{j,q}\ \ \text{(权值)},\qquad [\mathbf{J}]_{h,l}=\frac{\partial e_{k,q}}{\partial b^m_i}=\tilde s^m_{i,h}\ \ \text{(偏置)} \tag{12.43–12.44}\]

Marquardt 敏感度的反传递推与标准敏感度(式 11.35)完全相同,只有起点不同:

\[\tilde s^M_{i,h}=\frac{\partial(t_{k,q}-a^M_{k,q})}{\partial n^M_{i,q}}=\begin{cases}-\dot f^M(n^M_{i,q})&i=k\\0&i\ne k\end{cases}\quad\Rightarrow\quad \tilde{\mathbf{S}}^M_q=-\dot{\mathbf{F}}^M(\mathbf{n}^M_q) \tag{12.45–12.46}\]

这是一个 \(S^M\times S^M\) 的对角阵——没有标准反传中的因子 2,也没有误差。然后

推导拆解:Marquardt 敏感度与第 11 章敏感度的关系,只差链式法则的最外一层。标准反传求的是 \(\partial(e^2)/\partial n\);按一元链式法则 \(\dfrac{\partial(e^2)}{\partial n}=2e\cdot\dfrac{\partial e}{\partial n}\)。LM 需要的是去掉外层的 \(\dfrac{\partial e}{\partial n}\),所以起点去掉了"\(2e\)"这个因子,只剩 \(\partial e/\partial n^M=-\dot f^M(n^M)\)(\(e=t-a\),对 \(a\) 的导数是 \(-1\))。之后层与层之间的传递与误差无关,所以递推公式不变。对单输出网络,有 \(\mathbf{s}^m=2e\,\tilde{\mathbf{s}}^m\):标准敏感度就是 Marquardt 敏感度乘以 \(2e\)。为什么要分开存?因为 LM 需要 Jacobian 的每一行(每个误差各自的导数)来组装 \(\mathbf{J}^T\mathbf{J}\),而标准反传只给出它们加权求和后的梯度 \(2\mathbf{J}^T\mathbf{v}\),信息已经被压缩掉了。

\[\tilde{\mathbf{S}}^m_q=\dot{\mathbf{F}}^m(\mathbf{n}^m_q)(\mathbf{W}^{m+1})^T\tilde{\mathbf{S}}^{m+1}_q,\qquad \tilde{\mathbf{S}}^m=[\tilde{\mathbf{S}}^m_1\,|\,\tilde{\mathbf{S}}^m_2\,|\,\cdots\,|\,\tilde{\mathbf{S}}^m_Q] \tag{12.47–12.48}\]

注意每个样本要反传 \(S^M\) 个敏感度向量(矩阵的每一列),因为每个样本有 \(S^M\) 个误差,每个误差对应 Jacobian 的一行。

例(P12.5)。 1-1-1 网络,\(f^1(n)=n^2\),\(f^2(n)=n\),训练集 \(\{p_1=1,t_1=1\}\)、\(\{p_2=2,t_2=2\}\),初值 \(W^1=1,b^1=0,W^2=2,b^2=1\)。

  • 前向:\(q=1\):\(n^1=1,a^1=1,a^2=3,e_1=-2\);\(q=2\):\(n^1=2,a^1=4,a^2=9,e_2=-7\)。
  • Marquardt 敏感度:\(\tilde S^2_1=-1\),\(\tilde S^1_1=\dot f^1(n^1_1)W^2\tilde S^2_1=2(1)(2)(-1)=-4\);\(\tilde S^2_2=-1\),\(\tilde S^1_2=2(2)(2)(-1)=-8\)。
  • Jacobian(参数顺序 \(w^1,b^1,w^2,b^2\)):
\[\mathbf{J}=\begin{bmatrix}(-4)(1)&-4&(-1)(1)&-1\\(-8)(2)&-8&(-1)(4)&-1\end{bmatrix}=\begin{bmatrix}-4&-4&-1&-1\\-16&-8&-4&-1\end{bmatrix}\]

12.5.4 LMBP 的四个步骤

  1. 把所有输入送入网络,计算输出和误差 \(\mathbf{e}_q=\mathbf{t}_q-\mathbf{a}^M_q\),按式 12.34 求误差平方和 \(F(\mathbf{x})\)。
  2. 计算 Jacobian:用式 12.46 初始化、式 12.47 反传、式 12.48 拼接,再由式 12.43、12.44 得到各元素。
  3. 解式 12.32 得 \(\Delta\mathbf{x}_k\)。
  4. 用 \(\mathbf{x}_k+\Delta\mathbf{x}_k\) 重算误差平方和。若比第 1 步小,则 \(\mu\leftarrow\mu/\vartheta\),\(\mathbf{x}_{k+1}=\mathbf{x}_k+\Delta\mathbf{x}_k\),回到第 1 步;否则 \(\mu\leftarrow\mu\vartheta\),回到第 3 步。

收敛判据:梯度 \(2\mathbf{J}^T\mathbf{v}\) 的范数小于预定值,或误差平方和降到目标值。

原书图 12.17 画出了第一次迭代所有可能的 LM 步:小 \(\mu\) 时是 Gauss–Newton 方向,大 \(\mu\) 时是最速下降方向,中间值连成一条曲线;\(\mu\) 增大时步子缩短并转向最速下降方向,因此每次迭代总能减小误差。图 12.18(\(\mu_0=0.01\),\(\vartheta=5\))的迭代次数比之前所有方法都少。每次迭代的计算量最大(要解 \(n\times n\) 方程),但对中等规模的网络,LMBP 似乎是最快的训练算法 [HaMe94]。

主要缺点:存储。 要存储并求解 \(n\times n\) 的 \(\mathbf{J}^T\mathbf{J}\),而其他方法只需存 \(n\) 维梯度。参数很多(原书说通常是几千个以上,视内存而定)时不实用。这也是今天的深度网络(参数以百万、十亿计)不用 LM 的原因。


12.6 五种算法的比较

算法 每步要做的事 批量 / 增量 需选的参数 特点
SDBP 梯度 × 固定学习率 都可以 \(\alpha\) 最简单,最慢,平坦区停滞、山谷振荡
MOBP 梯度的指数平滑 都可以 \(\alpha,\gamma\) 实现简单,明显快于 SDBP,对 \(\gamma\) 不太敏感
VLBP 试探 + 按误差变化调学习率 只能批量 \(\alpha,\gamma,\eta,\rho,\zeta\) 比 MOBP 快,参数多且影响速度
CGBP 共轭方向 + 线搜索 只能批量 线搜索容差等 一般比 VLBP 快,每步多次函数求值
LMBP 解 \((\mathbf{J}^T\mathbf{J}+\mu\mathbf{I})\Delta\mathbf{x}=-\mathbf{J}^T\mathbf{v}\) 只能批量 \(\mu_0,\vartheta\)(不敏感) 中等规模最快,存储 \(O(n^2)\)

原书此处写"更多变体见第 19 章",系沿用第一版章号(第二版第 19 章为 ART,与此无关);训练算法的实际选择见本册第 22 章。


12.7 量化实战

12.7.1 本章在量化中的位置

  • LM 是非线性最小二乘校准的主力:收益率曲线拟合(Nelson–Siegel、Svensson)、期权隐含波动率曲面拟合(SVI、SABR)、Heston 等随机波动率模型的参数校准,本质都是"残差平方和"最小化。Jacobian 用解析导数、有限差分或自动微分得到(Marquardt 敏感度就是对网络的解析 Jacobian),\(\mu\) 的自适应保证从粗糙初值出发也能稳健收敛。
  • Gauss–Newton 的适用条件:\(\mathbf{S}(\mathbf{x})\) 小,即残差小或模型近似线性时,\(\mathbf{J}^T\mathbf{J}\) 才是好的 Hessian 近似。市场数据噪声大或模型设定不对时残差大,GN 方向可能很差,LM 会自动增大 \(\mu\)。
  • \(\mathbf{J}^T\mathbf{J}\) 给出参数的不确定性:在噪声独立同分布的假设下,参数估计的近似协方差为 \(\hat\sigma^2(\mathbf{J}^T\mathbf{J})^{-1}\),\(\hat\sigma^2=F/(N-n)\)。它可以用来判断校准参数是否可识别——标准误巨大、\(\mathbf{J}^T\mathbf{J}\) 条件数极高,说明数据无法区分某些参数组合,校准结果每天跳来跳去也就不奇怪了。

白话解释:这个公式就是 OLS 系数协方差 \(\hat\sigma^2(X^TX)^{-1}\) 的非线性版本:在最优点附近把模型线性化,Jacobian \(\mathbf{J}\) 扮演设计矩阵 \(X\) 的角色,\(N-n\) 是自由度(样本数减参数数,和 CFA 里回归的 \(n-k-1\) 同一意思)。条件数高相当于回归中的多重共线性:两个参数对残差的影响几乎成比例(Jacobian 的两列几乎平行),数据只能确定它们的某个组合,确定不了各自的值。

  • 动量与自适应学习率:训练收益预测网络时用的 SGD+momentum、Adam 等优化器是 MOBP、delta-bar-delta 的后代;"动量 = 低通滤波"的直觉也与交易信号的指数平滑同构。
  • 黄金分割线搜索可用于一维参数的高效搜索,但回测目标函数通常噪声大、不是单峰的,盲目优化单一超参数(止损阈值、持仓期)很容易过拟合(第 03 册第 10b 章)。
  • 局部极小与对称性:网络训练结果依赖初值,回测中应多次随机初始化取平均或集成,并检验结果的稳定性。

12.7.2 代码一:五种训练算法比较

在原书 12.1 节的问题上(1-2-1、两层 logsig、拟合 12.1–12.2 参数下的网络响应,41 个点)比较 SDBP、MOBP、VLBP、LMBP。全部 7 个参数都参与训练,从 10 组不同的小随机初值出发。梯度用批量反向传播,Jacobian 用 Marquardt 敏感度。

import numpy as np, time
logsig = lambda n: 1 / (1 + np.exp(-n))
S1 = 2
# 参数向量 x 的排列(同 12.36):[W1(S1), b1(S1), W2(S1), b2]
def unpack(x):
    return x[:S1, None], x[S1:2*S1, None], x[2*S1:3*S1][None, :], x[3*S1]

def forward(x, p):
    W1, b1, W2, b2 = unpack(x)
    a1 = logsig(W1 @ p[None, :] + b1)
    a2 = logsig(W2 @ a1 + b2)                       # 两层都是 logsig(图 12.1)
    return a1, a2.ravel()

def sse_grad(x, p, t):
    """误差平方和及其梯度(批量反向传播)"""
    W1, b1, W2, b2 = unpack(x)
    a1, a2 = forward(x, p); e = t - a2
    s2 = -2 * a2 * (1 - a2) * e                     # (1, Q)
    s1 = a1 * (1 - a1) * (W2.T @ s2[None, :])        # (S1, Q)
    g = np.concatenate([s1 @ p, s1.sum(1), a1 @ s2, [s2.sum()]])
    return e @ e, g

def jacobian(x, p):
    """Marquardt 敏感度 (12.46)-(12.48) 求 J = de/dx,每行对应一个样本"""
    W1, b1, W2, b2 = unpack(x)
    a1, a2 = forward(x, p)
    S2t = -(a2 * (1 - a2))[None, :]                 # 初值 -F'(n^M),无因子 2 和误差
    S1t = a1 * (1 - a1) * (W2.T @ S2t)
    return np.column_stack([(S1t * p).T, S1t.T, (S2t * a1).T, S2t.T])

# 目标:同结构网络在 (12.1)(12.2) 参数下的响应,41 个点
p = np.linspace(-2, 2, 41)
x_true = np.array([10, 10, -5, 5, 1, 1, -1.0])
t = forward(x_true, p)[1]
x0 = np.zeros(7)

def run(method, iters=5000, alpha=1.0, gamma=0.9):
    x = x0.copy(); F, g = sse_grad(x, p, t); dx = np.zeros_like(x)
    lr, mu = alpha, 0.01
    hist = [F]
    for k in range(iters):
        if method == "SDBP":
            x = x - lr * g
        elif method == "MOBP":
            dx = gamma * dx - (1 - gamma) * lr * g; x = x + dx
        elif method == "VLBP":                      # eta=1.05, rho=0.7, zeta=4%
            dx_try = gamma * dx - (1 - gamma) * lr * g
            F_try, _ = sse_grad(x + dx_try, p, t)
            if F_try > F * 1.04:
                lr *= 0.7; dx = np.zeros_like(x); hist.append(F); continue
            x = x + dx_try; dx = dx_try
            if F_try < F: lr *= 1.05
        elif method == "LMBP":                      # mu0=0.01, theta=10
            e = t - forward(x, p)[1]; J = jacobian(x, p)
            while mu < 1e10:
                step = -np.linalg.solve(J.T @ J + mu * np.eye(7), J.T @ e)
                F_try, _ = sse_grad(x + step, p, t)
                if F_try < F: x = x + step; mu /= 10; break
                mu *= 10
        F, g = sse_grad(x, p, t); hist.append(F)
    return np.array(hist), x

print("方法   学习率  到达SSE<0.05的迭代数(中位)  到达全局极小的初值数/10  10个初值总耗时")
for m, a, iters in [("SDBP", 1, 20000), ("SDBP", 5, 20000), ("MOBP", 5, 20000),
                    ("VLBP", 5, 20000), ("LMBP", None, 300)]:
    ks, n_glob, t0 = [], 0, time.time()
    for seed in range(10):                          # 多初值:原书反复强调
        x0[:] = np.random.default_rng(seed).uniform(-0.5, 0.5, 7)
        h, x = run(m, iters, alpha=a or 1.0)
        if (h < 0.05).any(): ks.append(np.argmax(h < 0.05))
        n_glob += h[-1] < 1e-3
    med = f"{int(np.median(ks))}({len(ks)}个)" if ks else "未到达"
    print(f"{m}  {str(a):>4}  {med:>12}  {n_glob:>14d}  {time.time()-t0:8.1f}s")

# 看 LMBP 从两个初值出发停在哪里:参数顺序 [w1_11, w1_21, b1_1, b1_2, w2_11, w2_12, b2]
for seed in [1, 2]:
    x0[:] = np.random.default_rng(seed).uniform(-0.5, 0.5, 7)
    h, x = run("LMBP", 300)
    print(f"seed {seed}: SSE={h[-1]:.4f}, x={np.round(x, 2)}")

# P12.5:f1=n^2, f2=n 的 1-1-1 网络 Jacobian
W1, b1, W2, b2 = 1.0, 0.0, 2.0, 1.0
J = []
for pq in [1.0, 2.0]:
    n1 = W1 * pq + b1; a1 = n1**2
    S2 = -1.0; S1 = 2 * n1 * W2 * S2
    J.append([S1 * pq, S1, S2 * a1, S2])
print("P12.5 Jacobian:\n", np.array(J))

输出:

方法   学习率  到达SSE<0.05的迭代数(中位)  到达全局极小的初值数/10  10个初值总耗时
SDBP     1       399(8个)               0       2.0s
SDBP     5           未到达               0       2.0s
MOBP     5       43(10个)               1       2.1s
VLBP     5       15(10个)               2       4.1s
LMBP  None        3(10个)               1       0.2s
seed 1: SSE=0.0227, x=[ -0.88  -0.69   0.     0.   -39.05  42.65  -1.8 ]
seed 2: SSE=0.0000, x=[-10.  10.   5.   5.  -1.   1.  -0.]
P12.5 Jacobian:
 [[ -4.  -4.  -1.  -1.]
 [-16.  -8.  -4.  -1.]]

解读。

  1. 速度。 走出初始平台(SSE 从约 1.6 降到 0.05 以下)所需的迭代数:SDBP 约 400 步,且有 2 个初值 2 万步都没走出来;MOBP 43 步;VLBP 15 步;LMBP 只要 3 步,总耗时也最少(0.2 秒,对比其他方法 2–4 秒;具体耗时因机器而异)。与原书结论一致。
  2. 动量的稳定作用。 学习率 5 时 SDBP 失败(sigmoid 被推入饱和区,梯度消失,停在 SSE 约 4.6 或 11.8 的平台上),而同样学习率的 MOBP 稳定且快——正是 12.2.3 节的理论结论在非二次曲面上的体现。
  3. 局部极小是常态。 10 个初值中,最好的算法也只有 1–2 个到达全局极小,其余停在 SSE≈0.023 一类的局部极小。看 seed 1 的终点:第一层权值很小(约 \(\pm0.8\))、第二层权值很大(约 \(\pm40\)),与原书描述的局部极小(\(w^1_{1,1}=0.88,\ w^2_{1,1}=38.6\))属于同一类型——用两个几乎线性的隐层神经元的大幅差值去凑台阶。再快的算法也只是更快地掉进最近的坑,多初值是必需的。
  4. 对称性。 seed 2 到达的全局极小是 \(\mathbf{W}^1=[-10,10]^T\)、\(\mathbf{b}^1=[5,5]^T\)、\(\mathbf{W}^2=[-1,1]\)、\(b^2=0\),与真参数 \([10,10,-5,5,1,1,-1]\) 不同,但网络函数完全相同:因为 \(\mathrm{logsig}(-n)=1-\mathrm{logsig}(n)\),第一个神经元的符号翻转可以被输出权值和偏置补偿。所以比较不同初值的结果应比较误差,而不是比较参数。
  5. P12.5 的 Jacobian 与手算一致。

12.7.3 代码二:用 LM 校准 Nelson–Siegel 收益率曲线

Nelson–Siegel 模型 \(y(\tau)=\beta_0+\beta_1\dfrac{1-e^{-\tau/\lambda}}{\tau/\lambda}+\beta_2\Big(\dfrac{1-e^{-\tau/\lambda}}{\tau/\lambda}-e^{-\tau/\lambda}\Big)\) 对 \(\beta\) 线性、对形状参数 \(\lambda\) 非线性,是典型的非线性最小二乘问题。下面用 10 个期限的模拟报价(噪声 3bp)从粗糙初值校准,与 scipy 的 MINPACK 实现对照,并给出参数标准误。

import numpy as np
from scipy.optimize import least_squares
rng = np.random.default_rng(1)

# Nelson-Siegel 收益率曲线:y(tau) = b0 + b1*L1 + b2*L2,tau 为期限(年),lam 为形状参数
def ns(x, tau):
    b0, b1, b2, lam = x
    u = tau / lam; L1 = (1 - np.exp(-u)) / u; L2 = L1 - np.exp(-u)
    return b0 + b1 * L1 + b2 * L2

tau = np.array([0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30.0])
x_true = np.array([4.0, -2.0, 1.5, 1.8])                     # 单位:%
y = ns(x_true, tau) + rng.normal(0, 0.03, tau.size)        # 报价噪声 3bp

def resid(x): return y - ns(x, tau)                          # v(x) = 误差向量
def jac(x, h=1e-7):                                          # 数值 Jacobian dv/dx
    return np.column_stack([(resid(x + h*np.eye(4)[j]) - resid(x - h*np.eye(4)[j])) / (2*h)
                            for j in range(4)])

def levenberg_marquardt(x, mu=0.01, theta=10, tol=1e-8, maxit=200):
    F = resid(x) @ resid(x)
    for k in range(maxit):
        J, v = jac(x), resid(x)
        g = 2 * J.T @ v                                      # (12.22)
        if np.linalg.norm(g) < tol: break                    # 收敛判据:梯度范数
        while mu < 1e10:
            dx = -np.linalg.solve(J.T @ J + mu * np.eye(4), J.T @ v)   # (12.32)
            F_new = resid(x + dx) @ resid(x + dx)
            if F_new < F: x, F, mu = x + dx, F_new, mu / theta; break
            mu *= theta                                      # 失败则向最速下降靠拢
        else:
            break                                            # 已无法再下降(数值极限)
    return x, F, k

x0 = np.array([3.0, -1.0, 0.0, 1.0])                         # 粗糙初值
x_lm, F_lm, k = levenberg_marquardt(x0)
ref = least_squares(resid, x0, method="lm")
print(f"自写 LM: {k} 次迭代, x = {np.round(x_lm, 4)}, SSE = {F_lm:.5f}")
print(f"scipy  : x = {np.round(ref.x, 4)}, SSE = {2*ref.cost:.5f}")
J = jac(x_lm); s2 = F_lm / (tau.size - 4)
se = np.sqrt(np.diag(s2 * np.linalg.inv(J.T @ J)))           # 近似标准误
print("参数近似标准误:", np.round(se, 3))
print("J'J 条件数: %.1e" % np.linalg.cond(J.T @ J))

输出:

自写 LM: 8 次迭代, x = [ 4.0083 -1.9811  1.5154  1.8724], SSE = 0.00316
scipy  : x = [ 4.0083 -1.9811  1.5154  1.8724], SSE = 0.00316
参数近似标准误: [0.028 0.044 0.193 0.166]
J'J 条件数: 1.6e+03

解读。 30 行的 LM 实现 8 次迭代就收敛,结果与 scipy 完全一致。标准误显示:长端水平 \(\beta_0\) 和斜率 \(\beta_1\) 估得很准(标准误 3–4bp),而曲率 \(\beta_2\) 和形状参数 \(\lambda\) 的标准误约 0.2——它们在 10 个期限上的作用相互替代(\(\lambda\) 变大时曲率因子的驼峰右移,可以部分由 \(\beta_2\) 补偿),\(\mathbf{J}^T\mathbf{J}\) 的条件数 1600 反映了这一点。实务中每天重新校准时 \(\beta_2,\lambda\) 跳动大、\(\beta_0,\beta_1\) 稳定,原因就在这里;常见做法是固定 \(\lambda\)(问题变成线性回归)或对其加正则化(第 13a 章)。


本章小结

多层网络的误差曲面不是二次的:曲率变化剧烈,平坦区与狭窄山谷并存,有多个局部极小,对称性使原点成为鞍点,所以初值应取小随机数并多试几组;单一学习率无法同时适应平坦区和山谷,SDBP 的误差曲线呈"长期停滞 + 短时骤降"。启发式改进中,动量是对更新量的一阶低通滤波,平滑振荡、允许更大的学习率,对二次函数可以证明总有动量系数使其稳定;可变学习率按误差变化自适应调整步长并暂停动量,但参数多且敏感。数值优化技术中,CGBP 用区间定位加黄金分割做线搜索、每 \(n\) 步重置;LMBP 用 \(\mathbf{J}^T\mathbf{J}\) 近似 Hessian、用 \(\mu\) 在 Gauss–Newton 与最速下降之间自适应切换,Jacobian 由 Marquardt 敏感度反传得到,对中等规模问题最快,但存储 \(O(n^2)\)。在量化中,LM 是一切非线性最小二乘校准的标准工具,\(\mathbf{J}^T\mathbf{J}\) 还给出参数的可识别性诊断。

概念 公式 / 要点
MOBP \(\Delta\mathbf{W}^m(k)=\gamma\Delta\mathbf{W}^m(k-1)-(1-\gamma)\alpha\mathbf{s}^m(\mathbf{a}^{m-1})^T\)
动量稳定性 \(\lambda^w=\tfrac12\big(\lambda^t\pm\sqrt{(\lambda^t)^2-4\gamma}\big)\),复根时 \(\vert \lambda^w\vert =\sqrt\gamma\)
VLBP 误差增幅 \(>\zeta\):拒绝,\(\alpha\leftarrow\rho\alpha\),\(\gamma\leftarrow0\);下降:接受,\(\alpha\leftarrow\eta\alpha\);增幅 \(\le\zeta\):接受,不变
黄金分割 每步区间缩为 \(\tau=0.618\) 倍,只需一次新求值
平方和梯度 / Hessian \(\nabla F=2\mathbf{J}^T\mathbf{v}\),\(\nabla^2F=2\mathbf{J}^T\mathbf{J}+2\mathbf{S}\)
Gauss–Newton \(\Delta\mathbf{x}=-(\mathbf{J}^T\mathbf{J})^{-1}\mathbf{J}^T\mathbf{v}\)
Levenberg–Marquardt \(\Delta\mathbf{x}=-(\mathbf{J}^T\mathbf{J}+\mu\mathbf{I})^{-1}\mathbf{J}^T\mathbf{v}\);大 \(\mu\) 时 \(\approx-\frac{1}{2\mu}\nabla F\)
Marquardt 敏感度 \(\tilde{\mathbf{S}}^M_q=-\dot{\mathbf{F}}^M(\mathbf{n}^M_q)\),\(\tilde{\mathbf{S}}^m_q=\dot{\mathbf{F}}^m(\mathbf{W}^{m+1})^T\tilde{\mathbf{S}}^{m+1}_q\)
Jacobian 元素 权值 \(\tilde s^m_{i,h}a^{m-1}_{j,q}\),偏置 \(\tilde s^m_{i,h}\)
参数协方差(校准) \(\hat\sigma^2(\mathbf{J}^T\mathbf{J})^{-1}\),\(\hat\sigma^2=F/(N-n)\)

练习

基础

  1. 单个 logsig 神经元,训练集 \(\{p_1=-3,t_1=0.5\}\)、\(\{p_2=2,t_2=1\}\),初值 \(w=0.4,\ b=0.15\)(原书 P12.1)。分别求只用第一个样本和用两个样本批处理时的初始负梯度方向。 提示: 第一个样本:\(a=0.2592\),\(e=0.2408\),\(s=-0.0925\),负梯度 \([-sp,-s]=[-0.2774,0.0925]\);第二个样本:\(a=0.7211\),\(s=-0.1122\),负梯度 \([0.2243,0.1122]\);平均 \([-0.0265,0.1023]\),与单样本方向差别很大。
  2. 证明:对线性网络(\(\mathbf{v}=\mathbf{t}-\mathbf{G}\mathbf{x}\)),\(\mu=0\) 时 LMBP 一步收敛到最小二乘解(原书 E12.15)。 提示: \(\mathbf{J}=-\mathbf{G}\),\(\Delta\mathbf{x}=(\mathbf{G}^T\mathbf{G})^{-1}\mathbf{G}^T(\mathbf{t}-\mathbf{G}\mathbf{x}_0)\),故 \(\mathbf{x}_1=(\mathbf{G}^T\mathbf{G})^{-1}\mathbf{G}^T\mathbf{t}\),与 \(\mathbf{x}_0\) 无关。
  3. 继续 P12.4 的黄金分割,再做两次迭代,写出 \(a_k,b_k,c_k,d_k\)。 提示: 当前 \([0.3219,0.4938]\),\(c=0.3876\ (F=0.1094)\),\(d=0.4281\ (F=0.1085)\);\(F_c>F_d\),取 \([0.3876,0.4938]\),新 \(d=0.4938-0.382(0.1062)=0.4532\)……区间向 0.413 收缩。
  4. 对 \(F=x_1^2+25x_2^2\)、\(\alpha=0.05\)(\(>0.04\),无动量会发散),用 P12.2 的条件求使带动量最速下降稳定的 \(\gamma\) 范围。 提示: 二次方程 \((\lambda^w)^2-\lambda^t\lambda^w+\gamma=0\) 两根都在单位圆内的充要条件是 \(|\lambda^t|<1+\gamma\)(\(0\le\gamma<1\)),比正文用的复根条件更宽。代入 \(\lambda^t=(1+\gamma)-(1-\gamma)\alpha\lambda\) 得 \(\gamma>\dfrac{\alpha\lambda_{\max}-2}{\alpha\lambda_{\max}+2}\)。这里 \(\alpha\lambda_{\max}=2.5\),\(\gamma>1/9\approx0.111\)(直接计算 \(\mathbf{W}\) 的谱半径可验证)。

进阶

  1. 用 P12.2 的方法分析:对 P9.1 的函数(Hessian 特征值 4 和 16),学习率 \(\alpha=0.2\) 与 \(\alpha=20\) 时分别需要多大的 \(\gamma\) 才稳定(原书 E12.3),并编程画轨迹验证。 提示: 用上题的公式:\(\alpha=0.2\) 时 \(\alpha\lambda_{\max}=3.2\),\(\gamma>0.231\);\(\alpha=20\) 时 \(\alpha\lambda_{\max}=320\),\(\gamma>0.9876\),此时谱半径接近 1,收敛很慢。
  2. 在 12.7.2 节的代码中加入 CGBP(Polak–Ribière + 区间定位 + 黄金分割 + 每 7 步重置),与其他算法比较迭代次数和函数求值次数。 提示: 迭代次数接近 LMBP,但每次迭代的函数求值次数在 10 次以上。
  3. 对 1-2-1 网络(logsig 隐层 + 线性输出,初值同第 11 章 11.4.1 节),以 \(p=1\) 和 \(p=0\) 两个样本、目标 \(1+\sin(\pi p/4)\),手算 LMBP 第一步所需的 Jacobian(原书 E12.14)。 提示: 每个样本一行,7 列;\(\tilde S^2=-1\),\(\tilde{\mathbf{S}}^1=\mathrm{diag}(a^1(1-a^1))(\mathbf{W}^2)^T(-1)\);权值列乘以对应输入。
  4. 在 12.7.3 节中,把期限点减少为 \(\{1,2,5,10\}\) 年,或把 \(\lambda\) 固定为 1.8 后只估计三个 \(\beta\),观察标准误和 \(\mathbf{J}^T\mathbf{J}\) 条件数如何变化,并解释。 提示: 期限减少时可识别性变差,\(\beta_2,\lambda\) 的标准误急剧上升;固定 \(\lambda\) 后问题变成线性回归,条件数大幅下降。

原书推荐习题: P12.2、E12.3–E12.6(动量稳定性的特征值分析,理解动量的数学本质);P12.3、E12.7、E12.8(手算 VLBP);P12.4、E12.10–E12.13(区间定位与黄金分割);P12.5、E12.14、E12.15(Jacobian 与 LM 步,线性情形一步收敛);E12.16(五种算法编程比较,最有实践价值)。

原书对照

本章小节 原书章节 PDF 页码
引言与分类 12 Objectives / Theory and Examples p.413–414
12.1 SDBP 的缺点(性能曲面、收敛例) 12 Drawbacks of Backpropagation p.415–421
12.2 动量 12 Momentum p.421–423
12.3 可变学习率 12 Variable Learning Rate p.424–426
12.4 共轭梯度反向传播 12 Conjugate Gradient p.426–431
12.5 Levenberg–Marquardt 12 Levenberg-Marquardt Algorithm p.431–439
小结 12 Summary of Results p.440–443
例题 P12.1–P12.5 12 Solved Problems p.444–457
12.6 比较、结语与延伸阅读 12 Epilogue / Further Reading p.458–461
习题 E12.1–E12.16 12 Exercises p.462–467