
1. 从优化到哈密顿一个被忽略的视角切换这些年“AI for Science”这个词被反复提起但真正让人兴奋的不是把神经网络当成黑盒去拟合数据而是把物理世界的结构“搬进”机器学习算法里。我最近一段时间在折腾的一个方向——辛算法在神经网络优化中的应用——就是这种思路里非常典型的一支。它的想法用三句话就能讲清楚把神经网络训练看成一条动力学轨迹把损失函数看成某类“势能”把随机梯度下降加动量看成带惯性的物体在势能场中运动。如果你愿意沿着这个视角走下去就会发现很多传统优化器里“拍脑袋”的算法细节其实是可以从几何原理上一层层推导出来的。这篇内容没有门槛限制。哪怕你只写过几行机器学习代码、还没认真碰过分析力学也能跟着把思路盘一遍。我会把理论部分讲得浅一点把代码和参数选择讲得细一点毕竟算法再漂亮最后还是要能跑起来才算数。我最早接触这个方向是在做某个模拟项目X的收敛稳定性优化时当时被Adam的震荡折磨得够呛抱着“换个物理背景试试”的心态把蛙跳积分器塞进了训练循环结果效果出乎意料地稳。这之后我才开始把辛算法的文献和代码翻了个遍整个理解体系也就慢慢搭起来了。有一点先说明神经网络优化里的辛算法跟很多人理解的“模拟物理系统”不太一样。它不是预测行星轨道、也不是做分子动力学模拟而是把优化过程本身当作一个哈密顿系统来设计数值积分器。理解这一点后面所有的推导都会顺很多。1.1 每次训练都是一段动力学过程我们从最朴素的角度看。训练神经网络就是一个不断修正权重 (w) 的过程修正的依据是损失函数 (L(w))。如果只看最原始的梯度下降那更新规则就是[ w_{t1} w_t - \eta \nabla L(w_t) ]这像什么呢想象一个小球在高低不平的曲面上滚动每一步都沿着最陡的下坡方向挪一小步。小球的位置就是权重曲面高度就是损失值而 (\nabla L(w_t)) 决定了倾斜的方向。(-\eta\nabla L(w_t)) 就是小球受到的“推动力”。这个类比在物理上其实很成立因为损失函数天然满足“势能”的定义它是一个标量场对权重求梯度之后能给出空间中的“力”。更妙的是物理里的势能曲面怎么走取决于物体的初始动能和势能分布而神经网络怎么收敛取决于动量的初始值和梯度场。二者在数学结构上是完全同构的。所以一个带动量的优化器可以改写成这样[ v_{t1} \mu v_t - \eta \nabla L(w_t) ][ w_{t1} w_t v_{t1} ]这里的 (v_t) 就是“速度”。比起原始梯度下降它多了一项惯性。现实中没哪个球会在光滑曲面上立即停住它一定会冲过头再滑回来动量项做的事情正是让你在优化轨迹上保留这种“冲劲”。很多人在调参时都直觉到动量系数设为0.9附近比较稳设为0.99就容易震荡甚至发散。从动力学角度看这本质上是系统阻尼和动能分配的问题。1.2 损失函数天然就是势能梯度负方向就是力写成哈密顿力学语言会更清晰。我们定义一个哈密顿量[ H(w, v) L(w) \frac{1}{2} v^T v ]这里 (L(w)) 是势能(\frac{1}{2}v^T v) 是动能。那么系统的运动方程是[ \frac{d w}{dt} \frac{\partial H}{\partial v} v ][ \frac{d v}{dt} -\frac{\partial H}{\partial w} -\nabla L(w) ]看到没有这就直接还原了带“标准动量”的连续时间动力学。负梯度 (- \nabla L(w)) 就是力速度 (v) 的导数受这个力驱动。此时系统沿着一条由哈密顿量决定的轨迹运动只要没有外力耗散总能量 (H) 在连续时间里是严格守恒的。让我用生活里的例子解释一下你把一个弹簧挂在天花板上下面吊一个铁块它上下震动如果忽略空气阻力弹簧的弹性势能和铁块的动能来回转化总和保持不变。训练网络也一样(L(w)) 就像那个弹簧提供的势能带动权重不停运动理想情况下应该有一个“总机械能守恒”的画面。但问题来了计算机里做训练用的不是连续微分方程而是离散的迭代公式。步长一进来能量就未必守恒了。如果你的离散方式不够讲究可能出现一种很尴尬的情况明明损失函数是平滑的实际训练中却一路漂移、震荡甚至发散射出NaN。这很像一个人在旁边不停推秋千每次推的时机不对秋千越晃越夸张。1.3 数值离散真的会“毁掉”优化轨迹吗数值观念上的错误通常不是“算错了一步”而是“每步都有一点点累积误差几千步之后彻底偏离预期”。用最普通的欧拉法去积分哈密顿系统每一步都会产生一个能量误差。这个误差不是永远可逆的而是逐渐“灌进”系统里让总能量单调增长。优化场景下的表现就是训练曲线前两千步还不错两千步之后损失不降反升甚至突然爆掉。我第一次踩到这个坑时完全摸不着头脑。后来换成了一种保持“辛结构”的离散格式才发现原来问题出在积分器上而不是模型、数据或学习率。这就是为什么我后面要专门讲讲辛结构它是一个能保证长期轨迹不越飘越离谱的数学约束。2. 辛结构到底在保护什么2.1 相空间与哈密顿正则方程物理里为了描述系统状态常把位置 (q) 和动量 (p) 放在一起构成相空间。每个时刻系统的状态就是相空间里一个点所有可能的状态就是一张巨大的“地图”。哈密顿正则方程告诉你这个点怎么移动[ \dot{q} \frac{\partial H}{\partial p} ][ \dot{p} -\frac{\partial H}{\partial q} ]求这几条偏导数不是某个库函数给你算出来的而是数学本身保证的“流”。这个流有一个极其关键的性质它保持相空间中的面积体积不变。打个比方把一群点放在相空间的一个小方块里让它们随时间演化这个小方块的形状可以被拉长、扭曲但它的面积不会改变。这就是所谓的“辛结构”。对一个神经网络优化器来说相空间坐标是什么很简单(q \leftrightarrow w)(p \leftrightarrow v)。所以每一个训练epoch其实就是相空间里的一次演化。如果演化过程能保持辛结构优化轨迹在几何意义上就是“不丢信息”的。2.2 辛几何的“按近似守恒”为何重要当我们把连续演化变成离散迭代时不同数值方法的关键差别就出现了。普通欧拉法每次会引入的误差会破坏面积/体积不变性而辛积分器采用特殊的交错更新策略可以让每一步都是某种“近似辛映射”。它不是让能量完全不变而是让能量误差始终保持在一个有界范围内震荡不会无限累积。我在实际对比中发现这个问题对应到训练上有三个非常直观的体现普通梯度下降像“溜冰”方向和大小都是瞬时决定容易在一个下坡方向上来回抖普通动量像“踢球”虽然有了惯性但积分不当会让能量慢慢变大训练后期出现奇怪的爆发辛动量像“摆钟”能量有界地来回转化轨迹更“规矩”不会出现离谱的发散你可以把辛积分器理解成一个优秀的“节拍器”它不是每一下都绝对精确但宏观上永远守得住节奏。2.3 辛算法不是能量守恒器一个常被传错的观点这里我要特别泼一盆冷水辛算法并不保证哈密顿量在离散意义下严格守恒。它保证的是对于一个足够小步长 (h)存在一个“修正后的守恒量”始终维持在极小的误差范围内。可以想象成系统实际跑在一条看不见的“影子哈密顿量”上影子能量和真实能量差一个微小量。这个差值第二步长有关但不会随着步数增长而失控。也是因为这样辛算法才不怕“长期积分”。这正是它和普通欧拉法最本质的区别。普通方法适合你只关心“前几百步稳态”辛方法适合你关心“几千上万步之后轨迹是否还在合理区间内”。神经网络训练往往就是那种要跨过几十万步的长程演化所以这一优势相当契合。3. 把辛算法接进神经网络优化三条实操路线把理论落到代码之前我们得先把路线看清楚。我总结下来辛算法和神经网络优化结合主要有三种完全不同的姿势。3.1 路线一把蛙跳变成动量更新规则这大概是最直接、也是更多研究者第一篇论文选的路线。思想是把连续时间里的哈密顿运动方程用“蛙跳”也称Störmer–Verlet这种经典辛积分器离散化然后把离散后的每一步变成一个优化器更新规则。先来看连续系统的标准形式[ \frac{dw}{dt} v ][ \frac{dv}{dt} -\nabla L(w) ]蛙跳的做法是把“半步步长”拆开让速度和位置互不拖累先让速度走半拍 (v_{t1/2} v_t - \frac{h}{2}\nabla L(w_t))再让位置走一整拍用刚才的半拍速度 (w_{t1} w_t h v_{t1/2})最后用新位置计算梯度把速度再走半拍 (v_{t1} v_{t1/2} - \frac{h}{2}\nabla L(w_{t1}))你仔细看这不就是动量更新的“解剖版”吗普通动量更新是“一个公式合并完成”蛙跳则把更新拆成了“半步速度→位置→半步速度”。这样做的好处是两次半步更新配合中间一次全量位置更新整体保持了辛映射性质能量漂移被严格控制住了。我在实际写这个优化器的时候第一版很生硬就是照公式搬。后来才发现一个细节如果想严格保持辛结构在更新完位置后还需要再算一次梯度。也就是说每一步训练至少需要两次梯度计算。这在神经网络上是开销翻倍的。所以工程里常常需要做一些近似比如利用“上一次的梯度缓存”或者每隔几步再重新计算效果依然比普通动量稳定但已经不再是严格的辛算法。严格和非严格之间的取舍完全取决于你有多看重长期几何性质。3.2 路线二把损失面几何交给结构第二种路线更激进不是把辛算法当作优化器而是把网络结构本身设计成“辛映射”。这让学习过程天然遵守哈密顿动力学的规则。比如设计残差网络时让每一层的变换对应一个辛欧拉步或者利用可逆结构实现类似“蛙跳”的层间传播。这类方法在物理模拟类任务里特别吃香因为最终目标不是最小化一个损失而是让网络学到的变换能长期保持某个物理系统的能量、动量或相空间体积。我之前帮人调过一个小型物理神经网络预测一个弹簧振子的长期运动普通前向网络预测到后期轨迹越来越偏换成辛层结构之后即使预测步数翻倍误差累积也远小于对照组。这就是“结构先验”的威力。但要提醒的是路线二对网络层设计的要求比较高不是随便套一下就能上的。它更适合你对底层物理规律有明确认识、并且愿意为网络结构做一些定制化修改的场景。如果你只是想改进一个常规分类任务的收敛速度路线一和路线三会更省钱。3.3 路线三神经算子与辛积分器混合第三种路线是把传统数值积分器直接作为计算图的一部分参与端到端训练。你现在用的自动微分框架里完全可以把“蛙跳”当作一个算子让它在前向传播里做若干步积分然后反向传播获得梯度。这个思路的好处是模型既可以保留辛积分器长期稳定的优点又可以通过梯度下降调整其他模块。比如你想用一个可学习的能量函数去描述一个未知系统同时要保证它按某种物理规则演化那就可以让网络作为“力场预测器”辛积分器作为“更新器”两者拼成一个完整的模拟闭环。我在实验里最喜欢用这条路线做“可学习模拟器”。代码结构也不复杂无非是“算力→半步步→更新→算力→半步步”。整个模块就是一个可微block丢进训练里不会有任何违和感。4. 最小可跑通搭建一个辛动量优化器前面讲了不少理论与路线接下来进入我最喜欢的部分动手写代码。我用的是极简伪代码风格思路在任何自动微分框架里都能直接平移。4.1 一次更新的完整分解先记住这个模块输入当前权重 (w)、当前动量 (v)、步长 (h)、梯度函数 (\text{grad}(w))执行流程用当前权重计算梯度 (g_1 \text{grad}(w))更新一半动量 (v_{1/2} v - \frac{h}{2} g_1)由半动量更新权重 (w w h v_{1/2})用新权重再计算梯度 (g_2 \text{grad}(w))更新另一半动量 (v v_{1/2} - \frac{h}{2} g_2)返回 ((w, v))很多人会问这样不就要算两遍梯度吗对为了保持严格的辛性质第二遍梯度避免不了。不过你可以把它理解成“两步合并为一个大步”。举个例子如果普通训练跑1000步这里相当于跑500个“辛大步”总开销并不会翻倍因为每个大步里权重更新的幅度也可以适当放大。实际对比中我发现在某些鞍点密集的损失面上这种大步长反而能更快穿过平台期。4.2 一个可跑的代码骨架下面代码我特意省去了某个特定框架的API只展示数据结构与更新逻辑你把它搬到自己的代码里就是现成优化器。class SymplecticMomentum: def __init__(self, init_w, init_vNone, h0.01): self.w init_w self.v np.zeros_like(init_w) if init_v is None else init_v self.h h def step(self, loss_fn, grad_fn): # 第一步半动量更新 g1 grad_fn(self.w) v_half self.v - 0.5 * self.h * np.ravel(g1) # 第二步位置全量更新 w_new self.w self.h * v_half # 第三步用新位置算梯度完成另一半动量更新 g2 grad_fn(w_new) v_new v_half - 0.5 * self.h * np.ravel(g2) self.w, self.v w_new, v_new return loss_fn(w_new)注意grad_fn必须是“给定参数、返回梯度”的可调用函数而不是一个固定常量。许多深度框架中这对应到你在训练循环里手动调用loss.backward()之后取.grad。区别只是风格本质完全一样。如果你想要工程性能更高一点可以加入“梯度缓存”大步第二次算的g2直接作为下一步g1的缓存这样每个大步只额外算“一次”新位置梯度广义成本仍然可控4.3 一个非常小的数值对照实验我在本地做实验时用了一个经典的非凸函数Himmelblau函数。它形状像一座“四谷之山”没有任何凸性特别适合测试优化器的“地形通过能力”。对比组分别是普通SGD动量、Adam实现和严格辛动量。我记录了两个关键指标到达局部最小值区域所需的迭代次数、以及训练过程中损失路径是否出现异常震荡。我得到的主观结论是Adam前期非常猛能很快冲入深谷那个状态下积分虽不辛但鉴于每一步都是自适应的漂移不大而严格辛动量在迭代中等步数时表现更平滑尤其是当我把步长调大到普通优化器已经要发散时它还能稳定运行。对这正是辛算法“长期稳定”的体现。5. 参数调节、避坑指南我在实操里踩过哪些雷5.1 步长h、动量和批噪声之间的关系一来就要说个坑步长 (h) 跟深度学习里的“学习率”并不能简单画等号。在普通SGD里学习率 (\eta) 直接决定了权重更新幅度而在辛优化器里权重更新靠的是速度与步长的乘积 (h v)。如果直接把原来的学习率 (\eta) 赋值给 (h)第一步通常没问题但连续几步后动量会越积越大造成更新幅度失控。我的经验做法是先“预热缩小”把 (h) 设成身学习率的 (0.1\text{}0.5) 倍然后观察训练前三百步的损失变化。如果损失下降太慢再把 (h) 慢慢提上去。你可以把这个当做一个非常土但好用的调试策略。第二步需要处理批大小噪声。小批量梯度天然带有噪声放在哈密顿系统里就相当于给系统加了一个随机力。随机力太大会破坏长期稳定性。所以如果你是跑小批量训练我建议在动量更新里加入一个轻微衰减系数比如0.999等价于给系统加一点点绝对摩擦防止噪声慢慢累积成“能量爆炸”。这也是我在跑某个图像分类Demo时发现的方法。当时辛优化器连续训练三十轮后loss曲线一直在极小值附近轻微晃动降不下来。后来加入0.999的动量衰减一下子就把噪声磨平了。原理很简单辛结构保证无耗散稳定但真实训练并不需要完全无耗散引入可控摩擦反而能收敛到更尖锐的极小值。5.2 常见问题速查表我把自己这几年踩过的坑整理成表格你们可以直接照着排查。现象可能原因解决办法损失曲线剧烈震荡步长 (h) 太大能量注入过多把 (h) 缩小到刚才的1/5甚至1/10训练后期损失突然暴涨批噪声累积能量“爆表”加动量衰减或在更新末尾做“能量削峰”前几步下降极快之后原地不动初始动能太大冲过了平坦谷底把初始 (v) 设为小值或减小 (h)需要两次梯度训练明显变慢没有做梯度缓存优化在每个大步中缓存第二步梯度作为下一步的第一步梯度损失一直不在关键区域收敛缺少必要摩擦在动量平方上再加轻微衰减相当于L2正则5.3 和普通优化器怎么选表格做出来并不难难的是知道什么时候该用什么。如果任务是常规监督学习训练量不大收敛速度优先那我建议你还是用更成熟的优化器辛算法吃得亏是每次多算一遍梯度如果任务是需要长程外推的物理建模比如预测机器人关节轨迹、模拟流体运动、生成长时间稳定的物理动画那么辛算法的长期稳定性优势会被彻底放大如果任务是优化过程本身出现了严重的发散或震荡就算不是物理模型你也可以把辛动量当作一种“慢但稳”的备选方案纯粹从损失最终值来看辛算法未必能秒掉Adam它的价值在于“长时间演化不失控”这个工程特性这一点恰恰是普通优化器最容易在步数增加后翻车的地方。6. 面向更远的方向优化之外的辛收益辛算法进入神经网络远不止“造一个新的梯度下降变体”这么简单。它是“AI for Science”里非常具代表性的一种思路用一个离散格式去逼近真实世界的动力学过程再把这个动力学过程装进可学习的计算图里。如果你做的是物理信息学习方法你会发现辛积分器几乎是标配。它带来的收益有两个层次第一层是“不崩”。训练完成后网络外推到未来100个时间步普通RNN早就抖到不可见而辛结构还在维持大致准确的轨迹形状。第二层是“能解释”。因为中间过程是明确的物理积分你可以随时抽出来查看系统当前处于什么能量状态、动量是多少、相空间体积有没有异常变化。这些指标在纯黑盒网络里根本拿不到。我后来把辛优化器和网络结构混搭使用逐渐意识到一件更本质的事在科学计算场景里算法的几何正确性往往比传统意义上的“收敛速度”更重要。你可以花两倍时间收敛但绝不能允许在长程预测中突然跑飞。前者是性能问题后者是可靠性问题。可靠性是科学问题里不能妥协的底线。如果你最近也在做预测类任务尤其是和“时间演化”相关的建议可以试试这个组合用一个普通的神经网络去拟合未知力场外面套一层蛙跳积分器。这个结构几乎不会毁掉你现有的训练流程却可能带来完全不同的稳定性表现。7. 我的几点私房经验代码写到这里理论上该收尾了。但自媒体式的总结没有意义我讲一讲自己更愿意分享的几个细节吧。第一别迷信“严格辛”。严格辛在理论上最美但真实训练中几百万参数、有限精度浮点数、小批量梯度噪声都会让“严格”变成一纸空文。你需要的是“保持稳定”而不是“保持绝对守恒”所以那些带轻微衰减的近似辛方案往往才是工业级首选。第二调辛优化器时不要一上来就追求“一步到位”。我现在的习惯是先跑300步观察loss曲线如果震荡幅度小于0.02就说明步长和动量衰减的搭配已经相当合理如果震荡明显优先降 (h)而不是动动量参数。动量参数影响的是长期记忆乱动它会让你的调试变得很难定位。第三如果你打算做相关实验强烈建议先在二维或三维损失函数上把几何结构可视化清楚。我自己就常把一个损失面的等高线图连同优化轨迹一起画出来多试几组步长很快就能建立直觉。没有这个直觉直接上复杂模型会非常痛苦。第四辛算法加神经网络优化终究是一种“吃坐标系和守恒性质”的技术路线。你不需要先成为一个分析力学专家才敢用但至少要在动手前理解清楚你是想让优化过程更稳定还是让网络学到一个物理系统前者用优化器就够了后者则需要把辛结构装进网络结构里。最让我感慨的还是第一段说的那句话损失函数真的是“势能”梯度负方向真的是“力”神经网络权重真的是在相空间里流动。很多机器学习的调参直觉放到物理图景里都能找到对应的解释而很多物理上的精细计算反过来又能指导你把优化调得更有章法。辛算法只是这扇门的一个入口门后面还有更广阔的可解释计算世界等着慢慢探索。