跳到主要内容

把物理写进损失:残差与软约束

1. 这一章讲什么

三件事: 怎么在没有任何标准答案的情况下给一个解打分(答案:把它代回方程); 这条路能走多远(一个八万参数的小网络、16 秒训完,而且能零样本泛化到没见过的障碍物——「零样本」的意思是训练时一个这样的例子都没见过); 以及它一条不能跨过的边界——它只是「尽量让方程成立」,不是「保证方程成立」。

它在全书链条里的位置:这是三档里的第二档,而且它治的病和第 05 章那个不是同一个。 第 05 章末尾那个分布漂移,这一章一点都治不了(要等第 09 章)。 它治的是另一件浪费。

2. 顶层全景

第一档(第 05 章) 第二档(这一章)

求解器 ──算出一堆题目和答案──▶ 数据集 求解器 ──不出场──

▼ 网络吐一个解 u
网络答 vs 答案 │
│ ▼
▼ 把 u 代回方程
「差多远」 左右两边的差 = 残差


「离零差多远」

✗ 必须先花几小时算答案 ✓ 一条标注数据都不用
✗ 会把多个都对的答案平均掉 ✗ 只能「尽量」满足,不保证

这张图在讲什么: 换掉的只是打分的方式,网络和训练循环一个字都没改。 但打分方式一换,「必须先有标准答案」这个前提就没了。

这一章的所有数字都是书里那次真实运行的输出。

3. 问题意识:上一章把物理当成了一个外部车间

书对第一档的不满只有一句,但它是这一整部分的起点:

第 05 章那个设置里,物理模型和数值方法只被当成一个「外部」工具, 用来生产大量数据——算完就扔1

书觉得这很可惜,理由也说得很直接: 我们人类对「怎么用数学描述物理过程」知道得很多, 这些知识本来可以用来引导训练过程,而不是只用来造一批题目1

注意这和第 05 章那个分布漂移是两件事。 那一条说的是「网络将来见到的输入不对」, 这一条说的是「造数据太贵、而且造完就浪费了」。 这一章治的是后者,前者要等第 09 章。

4. 残差:不用标准答案也能给一个解打分

结论先行:这一节是第二档的全部机制,后面所有内容都是它的推论。

先看现象。 你手上有一个解,想知道它对不对。第 01 章和第 05 章的做法是「和标准答案比」。 可如果根本没有标准答案呢?

还有一个办法:把这个解代回方程,看方程还成不成立。

具体一点。 一条随时间演化的 PDE 可以写成这个形状: 「这一点随时间的变化率」=「由它周围的空间变化算出来的某个东西」。 把网络给的解代进去,左边算一个数,右边算一个数,两边一减—— 这个差就叫残差(residual)2

正确的解让残差等于零。 所以「残差有多大」就是一个现成的分数, 而算这个分数完全不需要标准答案,只需要那条方程。

⚠️ 这两个字在这一行里的意思,和第 04 章那个「残差连接」不是一回事: 那边说的是网络的接线方式(把一层的输出加回它的输入),这里说的是方程左右两边的差。 本拆解从这里往后,单说「残差」一律指方程两边的差;接线的那个一律写全称「残差连接」。

这里要用到一条第 02 章记过的记号: 书用带 * 上标的符号表示 理想的、连续的、我们拿不到的那个量,不带 * 的表示离散的、计算机里真能算出来的3。 残差是在离散的那一边算的——因为要算它,你得先选好怎么切格子、怎么算导数。

5. 两项加权和:各自单独用会出什么事

实际训练时,损失通常是两项加起来:一项是老的监督项(和标准答案比), 一项是新的残差项,各配一个权重4

书把「只用其中一项会怎样」讲得很清楚,这一段是全章信息密度最高的地方5:

只用哪一项会出什么事
只用监督项网络能很好地贴合训练样本,但可能把多个都对的答案平均掉(第 01 章那件事),而且在采样点之间的区域表现很差
只用残差项网络可能在局部满足方程,却很难找到一个全局成立的解

第二行需要解释,因为它反直觉:方程处处都满足了,怎么还会不对?

原因是:同一条方程可以有一整族都满足它的解。 于是不同的局部采样点各自收敛到这一族里的不同成员, 拼在一起就成了一个非常糟糕的东西5

这一族解有个名字,但它在本章末尾那个真实例子里才会长出具体的样子,所以留到那儿再给。

书还给了监督项存在的正面理由: 大多数实用的 PDE 在没有指定初始条件和边界条件之前根本没有唯一解; 所以只看残差的话,可能得到一个带着随机偏移或者别的多余成分的解。 监督样本的作用是在某些位置上把解钉住4

6. 软约束:这一档不能跨过的那条线

结论先行:「把物理写进损失」不等于「物理被保证满足」。

书说得很硬:和监督样本一样,没有任何保证残差会在训练中真的到零。 非线性优化会尽量把监督项和残差项都压小,但没有保证——大的非零残差可能一直留着6

所以这样表述出来的物理约束只是软约束(soft constraint): 它是一个「请你尽量」,不是一个「必须」。

对比一下第 02 章那条散度约束就明白差别了。 那一条是硬的: 求解器每一步的最后都真的执行一次投影,执行完之后约束确实成立而这里,约束是被写成损失里的一项,由优化器去尽量满足——中间没有任何强制的动作。

那么在深度学习里,「硬」的长什么样? 要等到求解器真的走进训练过程之后才有答案(第 10 章)。 这里先记一笔账。

7. 分岔口:同一个想法的两种做法

「把残差写进损失」这个想法,有两种截然不同的实现,而书从这里分岔7:

变体一变体二
解怎么表示先选好离散化(网格 / 图 / 点集),网络在这些格子上产生解没有显式表示——网络接收一个空间坐标,吐出那一点的解值
导数怎么算在计算网格上离散地算(有限差分那一套),但必须用深度学习框架的算子写,才能反传直接对网络本身求导
适合的架构规则网格用卷积网络、三角网格用图网络、粒子用点卷积全连接网络(因为没有空间采样点可用)
一个关键优势能直接处理相当大的解空间——不局限于学单个解输入维度极低(三维加时间也只有 4 个数)

书自己说:变体一「基本上是可微物理训练的一个子集」8—— 这是一处伏笔,第 08 章会揭晓。

变体二有个很有名的名字,而书对它下了全书最重的一句判断。 那个名字、那个定义、那句判断,以及一整套实测数据,我们放在第 07 章整章处理。 这一章的下半场走变体一。

8. 变体一的样板:让网络接手求解器里最贵的那一步

任务的来历:第 03 章说过,压力投影是整条仿真流水线里最贵的一步。 那能不能训一个网络去干这一步?

先把那一步再拆细一点。 不可压的 Navier-Stokes 常见的解法是把一步拆成两段9:

  1. 自平流:先不管守恒,让速度场被自己搬一下。 这一步很便宜,代价对格子数是线性的;
  2. 压力投影:解一个泊松方程,得到一个压力场; 这个压力场的梯度恰好抓住了速度场里「有净流出」的那一部分; 把它减掉,剩下的就是每格净流出为零的场。

第 2 步背后是一条数学结论:任何一个向量场,都可以拆成两半—— 一半是无散的(每格净流出为零),一半是无旋的(它正好是某个标量场的梯度)。 这个拆分叫亥姆霍兹分解(Helmholtz-Hodge decomposition)9

训练怎么设计的,这是本章最漂亮的一处:

  • 网络吃散度、吐压力——把每一格的净流出交给它,让它给出一个压力场;
  • 修正后的速度 = 原速度 − 压力场的梯度;
  • 损失 = 修正后速度的散度的 L2,目标是 0。

注意这个损失里没有出现任何标准答案。 你不需要预先算出成千上万对 「速度场 → 正确的压力场」——要算的东西当场就能从网络的输出里算出来。

要让它跑起来,只差一样东西:一个可微的散度算子。 书说这通常很容易做——拿一个卷积层,把权重设成有限差分的那几个系数就行10 (这正是第 04 章那个「卷积核就是模板」)。

书还澄清了一个标签:这可以叫「无监督训练」,但这个标签有误导性, 因为所谓无监督的训练目标其实只是当场算出来的9

最后一个设计选择值得记:为什么喂给网络的是散度,而不是速度场本身? 因为已知散度就足以唯一确定压力场——把无关的信息挡在门外,推断任务就简单了9

9. 走查:32×32 的随机流场,16 秒训完

设置11:

网格32 × 32,周期边界
训练数据100 个随机的、有散度的流场(合成的,一批 10 个)
网络U-Net,4 级,periodic 模式,83,521 个参数
训练15 轮,学习率 5e-3,总共 16 秒

83,521 这个数要有参照物:第 05 章那个翼型网络有 585,027 个参数,这个小七倍; 训练时间从 5 分 40 秒缩到 16 秒,快了二十一倍。

现在走一遍:

主走查第 1 步(输入): 一个随机生成的、有净流出的 32×32 速度场。 书打印了它的散度大小:L2 为 195.28,单格最大 1.89712。 (书还提了一句:散度比速度本身的量级小,平均在 0.4 上下。)

主走查第 2 步(还没训练的网络): 把散度喂进去,拿到一个压力场,减掉它的梯度, 再算一次散度——损失 390.2213这个 390 和上一步那个 195 是同一个量,只差一个 2:代码里的损失写的是 「2 × 那个 L2」,而第 1 步打印的 195.28 是不带那个 2 的14。 折算回同一把尺子:390.22 ÷ 2 = 195.11,和输入的 195.28 几乎一模一样。 也就是说,一个随机初始化的网络等于什么都没做——它吐出来的压力场是随机的, 减掉它的梯度对净流出既没帮忙也没添乱。这才是训练开始前应该看到的样子。

主走查第 3 步(训练): 100 个流场 × 15 轮 = 1500 次前向反向,16 秒。 梯度从「散度的 L2」出发,穿过那个有限差分的散度算子,一路传回网络的权重10

主走查第 4 步(结果): 损失 0.67915。 从 390.22 到 0.679,降了约 570 倍——书自己的说法是「两个数量级以上」。

主走查第 5 步(分布外测试): 生成一批新的流场,但把噪声的强度翻倍, 于是网络会收到量级从没见过的散度输入16。 结果三行并排:原始 2059.14 → 网络处理后 11.51 → 传统求解器 0.00017

第 5 步是这一节最要紧的一步,它同时说了两件事: 网络确实学到了本事(把 2059 压到 11.5,降了 179 倍); 而它离真求解器还差得远(求解器是 0.000,也就是精确满足)。 这就是第 6 节那条软约束边界的样子——它不是一句抽象的告诫,它就是这 11.51。

10. 零空间:网络学到的压力和求解器的差十倍,而两个都对

这是书自己给的一处诚实观察,而且它解释了第 5 节留的那个悬念。

先看现象。 把网络学到的压力场和传统求解器算出来的压力场画在一起, 结构上有些相似,但明显不是同一个东西;而且量级常常差十倍—— 书说网络学到的值往往比求解器的大十倍18

一个自然的反应是:那网络不是学错了吗?

没有。因为这个泊松问题有一整族同样合法的解。

理由只有一句话:那一步只用到压力场的梯度。 给整个压力场加上同一个常数,梯度一点都不变—— 所以「所有加了常数偏移的版本」全都是完全合法的解18

这一整族解有个名字:零空间(null space)。 第 5 节说「同一条方程可以有一整族都满足它的解」,这就是它具体的样子。

那为什么求解器给的是特定的那一个?因为它被额外约束成零均值—— 传统求解器主动从这一族里挑了一个18而我们那个可微的损失对绝对值没有任何约束, 所以网络自由地用了随机初始化时碰巧给它的那个偏移

这件事的普适价值:当你训出来的东西和参考解「差一个常数」「差一个倍数」的时候, 先别急着找 bug——先问问你的损失有没有约束住那个自由度。

11. 零样本泛化:只见过随机噪声,却处理得了没见过的障碍物

这是全章最有说服力的一处,而且它是一个真正的压力测试。

回想训练数据:100 个合成的随机噪声流场。 那些东西和真实流体的运动很不一样, 而且里面一个障碍物都没有。

测试是这样的: 搭一个带浮力的烟羽仿真,在烟源正上方放一个方形障碍物; 然后跑两条,一条用传统求解器做压力投影,一条用这个网络做,各跑 50 步19

结果:最后一帧「非常相似」19

书特意讲了边界条件怎么处理,这一条很实用: 在调用网络之前和之后各强制一次边界条件。 前一次是让网络看见那个固体障碍; 后一次是保证即使网络在障碍物边缘犯了点小错,边界条件仍然成立20

书把这次成功归功于「训练时用的那个可微的离散算子」—— 因为损失是通过真正的散度算子算出来的,网络学到的是「怎么消掉散度」这件事本身, 而不是「怎么模仿某一批流场」19

书也没有把话说满,它自己指出了改进方向: 网络可以把障碍物的几何当成一个额外输入, 也可以在边界区域给更大的损失权重、让它多花点心思19

另起一处的走查(零样本泛化): 一个只见过合成噪声流场的 83,521 参数网络 → 接进带方形障碍物的浮力烟羽仿真 → 每步:加浮力 → 平流 → 强制边界 → 网络算压力 → 减梯度 → 再强制一次边界 → 跑 50 步(约 1 分 37 秒) → 和纯求解器的最后一帧非常相似19

12. 主走查合起来看

发生了什么具体的数
1输入一个有净流出的 32×32 流场散度 L2 = 195.28,单格最大 1.897
2未训练的网络处理一遍损失 390.22;损失定义里带一个 ×2,折回去是 195.11——和不处理时的 195.28 一样,等于什么都没做
3训练100 个流场 × 15 轮,16 秒
4训练后损失 0.679(降了约 570 倍)
5分布外测试(噪声翻倍)原始 2059.14 → 网络 11.51 → 求解器 0.000
6零样本泛化接进带障碍物的烟羽,跑 50 步,和纯求解器非常相似
7但压力场本身和求解器的差约十倍——两个都对(零空间)

每一个数都是书里的真实输出(逐条见脚注); 「390.22 折回去是 195.11」这个换算,和「降了 570 倍」「快二十一倍」「小七倍」这几个比值, 是我们算的,书里没有。

13. 作者的判断与证据

书里给了证据的:

说法证据
不用一条标注数据也能训出来整个训练循环里没有出现任何参考压力场,损失 390.22 → 0.6791315
它在分布外输入上仍然有效噪声翻倍后 2059.14 → 11.5117
但它达不到求解器的精度同一行:求解器 0.00017
压力场差十倍是零空间造成的给了完整的数学理由(梯度对常数偏移不敏感)18
零样本泛化到障碍物50 步对比图19

作者的判断、书没有给对照实验的:

说法为什么算判断
「成功要归功于可微的离散算子」没有消融实验——书没有换掉那个算子再跑一遍给我们看19
「变体一基本上是可微物理训练的一个子集」这是一句结构性的论断,证据在第 08 章8
「只用残差项会因为零空间而失败」没有跑这个失败案例,只是描述了机制5

判断(我们的,不是书里的): 这一章最容易被误读的地方是那个 11.51。 它看起来很小(比 2059 小 179 倍),但和求解器的 0.000 比,它是无穷大在需要「误差能被压到任意指定阈值以下」的场合,这个差距是定性的,不是定量的。 换句话说:软约束省下的是造数据的成本,付出的是「不保证」这个性质。 如果错,会错在: 如果你的应用只要求「散度小到看不出来」(比如做视觉效果), 那 11.51 完全够用,这个区别不重要。 判据是:你的下游会不会把这点残余误差累积放大? 会,就不能用软约束交差。

14. 边界与局限

  • 这个例子只学了求解器里的一步(压力投影),不是整个求解器;
  • 训练数据是合成的随机噪声流场,书自己也说那和真实流体的运动很不一样; 它能泛化过去是个好消息,但书没有测试更极端的情形;
  • 书没有给这个网络的速度对比——它比传统的泊松求解器快多少?这一章一个字都没说, 而这恰恰是「学一个压力求解器」最该问的问题;
  • 零空间那件事只在压力这个特定问题上被讨论过,书没有推广;
  • 变体二(那个更有名的做法)这一章只给了定义,实测在第 07 章;
  • 软约束的边界这一章只给了一个数(11.51),没有做「什么时候够、什么时候不够」的讨论。

15. 可带走的

  1. 残差 = 把解代回方程,左右两边的差。 正确的解让它等于零, 所以没有标准答案也能给分;
  2. 这是第二档的全部机制。 网络和训练循环都没变,变的只是打分的方式;
  3. 只用监督项会平均掉多个答案;只用残差项会局部满足、全局崩掉。 通常两项一起用;
  4. 软约束 = 优化尽力而为。 没有任何保证残差会到零,大的偏差可能一直留着;
  5. 判断一个方法是软是硬,看它有没有一个强制的动作。 优化器「尽量」压小的是软的;每步真的执行一次投影的是硬的;
  6. 要把残差写进损失,你需要一个可微的离散算子—— 通常就是一个权重设成有限差分系数的卷积层;
  7. 「无监督」这个标签有误导性。 目标不是没有,只是当场算出来的;
  8. 喂给网络的东西越精确越好。 这里喂散度而不是速度场,因为散度就足以定下压力;
  9. 训出来的东西和参考解差一个常数或一个倍数时,先查你的损失约束住那个自由度没有。 这一族同样合法的解叫零空间;
  10. 零样本泛化是有可能的:只见过合成噪声的网络,能处理没见过的障碍物—— 前提是你学的是「机制」(消掉散度),不是「样子」(模仿某批流场);
  11. 边界条件在网络前后各强制一次:前一次让网络看见障碍,后一次兜住网络的小错。

16. 原文地图

主题原书章原文位置
「物理只被当成外部工具」这句不满8 Physical Loss Termstext/05-p81-100.txt:96(搜「external」)
残差的定义8.1 Using physical modelstext/05-p81-100.txt:128(搜「should be equal to zero for a correct solution」)
两项加权和、监督项为什么需要8.1 同上text/05-p81-100.txt:135(搜「do not have unique solutions」)
只用一项会怎样、零空间8.1 同上text/05-p81-100.txt:146(搜「average multiple modes」) · text/05-p81-100.txt:148(搜「null spaces」)
软约束、没有保证8.1 同上text/05-p81-100.txt:100(搜「soft constraints」)
变体一:显式表示上的残差8.2 Variant 1text/05-p81-100.txt:165(搜「we choose the discretization」)
「变体一基本上是可微物理的子集」8.2 同上text/05-p81-100.txt:196(搜「basically a subset」)
有限差分算子写成卷积层8.2 同上text/05-p81-100.txt:192(搜「finite difference weights」)
变体二:从网络表示求导8.3 Variant 2text/05-p81-100.txt:202(搜「fully connected NNs to represent」)
算子分裂、压力投影、亥姆霍兹分解9.1 Solving Navier-Stokestext/05-p81-100.txt:261(搜「operator splitting」) · text/05-p81-100.txt:265(搜「Poisson problem」)
「无监督」这个标签有误导性9.1 同上text/05-p81-100.txt:271(搜「misleading」)
为什么喂散度而不是速度9.1 同上text/05-p81-100.txt:277(搜「uniquely determine the pressure field」)
32×32、100 个流场、批 109.2 Setting up the Discrete PDEtext/05-p81-100.txt:302(搜「RES_X = 32」) · text/05-p81-100.txt:305(搜「NUM_SAMPLES_TRAIN」)
初始散度 195.289.2 同上text/05-p81-100.txt:326(搜「Initial L2 of divergence」)
网络结构与 83,521 个参数9.3 Neural Network Trainingtext/05-p81-100.txt:363(搜「filters=24」) · text/05-p81-100.txt:499(搜「Total number of trainable parameters」)
未训练时的损失 390.229.3 同上text/05-p81-100.txt:562(搜「Loss for untrained network」)
损失定义里那个 ×29.3 同上text/05-p81-100.txt:534(搜「div_sum = 2」)
15 轮、16 秒、最终 0.6799.4 Trainingtext/05-p81-100.txt:575(搜「range(15)」) · text/05-p81-100.txt:583(搜「Final loss」)
分布外测试:噪声翻倍9.5 Testing with New Inputstext/05-p81-100.txt:597(搜「increasing the scale by a factor of 2」)
2059.14 / 11.51 / 0.0009.5 同上text/05-p81-100.txt:619(搜「NN, mean divergence」)
压力差十倍、零空间9.5 同上text/05-p81-100.txt:669(搜「null-space in the form of a constant offset」)
障碍物测试与边界条件调用两次9.6 Tougher Teststext/06-p101-120.txt:8(搜「before and after invoking」) · text/06-p101-120.txt:86(搜「remarkably similar」)

Footnotes

  1. 出处:第 8 章 Physical Loss Terms(p.75)第 96 段(text/05-p81-100.txt:96,搜「external」)。 原文:「it's unfortunate that we only use physical models and numerical methods as an "external" tool to produce lots of data」,紧接着是「We as humans have a lot of knowledge about how to describe physical processes mathematically」。 2

  2. 出处:第 8.1 节 Using physical models(p.75)第 128 段 (text/05-p81-100.txt:128,搜「should be equal to zero for a correct solution」)。 原文把 PDE 写成 u_t = ℱ(u_x, u_xx, …),残差定义为 R = u_t − ℱ(…) = 0, 并说「which naturally should be equal to zero for a correct solution」。

  3. 出处:第 1.1 节 Differentiable physics(p.5)第 484 段 (text/01-p1-20.txt:484,搜「idealized, and unknown functions」)。 这条记号第 02 章记过账,这里第一次真的要用它: 残差算在离散那一边, 因为算它必须先定下怎么切格子、怎么算导数。

  4. 出处:第 8.1 节(p.75)第 135 段(text/05-p81-100.txt:135,搜「do not have unique solutions」)。 原文:大多数实用的 PDE 在没有指定初始与边界条件之前没有唯一解; 只考虑残差可能得到带随机偏移或其他不良成分的解,监督样本因此帮助在某些位置钉住解。 训练目标写成 arg min_θ Σ_i [α₀(f(x_i;θ) − y*_i) + α₁ R(x_i)],两个 α 是权重超参数。 2

  5. 出处:第 8.1 节(p.76)第 146 段(text/05-p81-100.txt:146,搜「average multiple modes」) 与第 148 段(text/05-p81-100.txt:148,搜「null spaces」)。 原文:只优化残差项时,网络可能局部满足 PDE 却很难找到全局成立的解, 原因是解里有「null spaces」——不同的解都满足残差,于是不同的局部点收敛到不同的解。 2 3

  6. 出处:第 8.1 节(p.76)第 100 段(text/05-p81-100.txt:100,搜「soft constraints」)。 原文:「Large, non-zero residual contributions can remain … the physical constraints formulated this way only represent soft constraints, without guarantees of minimizing these constraints.」

  7. 出处:第 8.2 节 Variant 1(p.76)第 165 段(text/05-p81-100.txt:165,搜「we choose the discretization」) 与第 8.3 节 Variant 2(p.78)第 202 段(text/05-p81-100.txt:202,搜「fully connected NNs to represent」)。

  8. 出处:第 8.2 节(p.77)第 196 段(text/05-p81-100.txt:196,搜「basically a subset」)。 原文:「this variant 1 has a lot in common with differentiable physics training (it's basically a subset)」。 2

  9. 出处:第 9.1 节 Solving Navier-Stokes(p.81)第 261 段(text/05-p81-100.txt:261,搜「operator splitting」)、 第 265 段(text/05-p81-100.txt:265,搜「Poisson problem」)、 第 271 段(text/05-p81-100.txt:271,搜「misleading」) 与第 277 段(text/05-p81-100.txt:277,搜「uniquely determine the pressure field」)。 原文把自平流那一步的复杂度写成「linear in the number of discretization points」, 把压力投影那一步称为「an elliptic PDE that is non-trivial to solve in the general case」。 2 3 4

  10. 出处:第 8.2 节(p.77)第 192 段(text/05-p81-100.txt:192,搜「finite difference weights」)。 原文:实现这个残差只需要提供计算网格上的散度算子, 而这通常很容易通过一个装着有限差分权重的卷积层做到 2

  11. 出处:第 9.2 节 Setting up the Discrete PDE(p.82)第 302 段(text/05-p81-100.txt:302,搜「RES_X = 32」) 与第 305 段(text/05-p81-100.txt:305,搜「NUM_SAMPLES_TRAIN」); 网络设置见第 9.3 节第 363 段(text/05-p81-100.txt:363,搜「filters=24」), 是一个 levels=4periodic=Truefilters=24 的 U-Net。 书还说了一句:这个网络因为感受野是局部的,按理说不该做得这么好 (text/05-p81-100.txt:360,搜「local receptive field」)—— 这句话和第 04 章「椭圆型问题需要全局感受野」正好对上,书自己也觉得意外。

  12. 出处:第 9.2 节(p.83)第 326 段(text/05-p81-100.txt:326,搜「Initial L2 of divergence」)。 打印输出是「Initial L2 of divergence: 195.28241, max=1.8969224691390991」。

  13. 出处:第 9.3 节(p.87)第 562 段(text/05-p81-100.txt:562,搜「Loss for untrained network」)。 打印输出是 390.21857。书只说「loss 很大」,没有把它和第 1 步的 195.28 对起来看。 2

  14. 出处:损失的定义在第 9.3 节第 534 段(text/05-p81-100.txt:534,搜「div_sum = 2」), 写的是 div_sum = 2 * math.l2_loss(div);而第 1 步那句打印用的是 math.l2_loss(...),不带那个 2(text/05-p81-100.txt:323,搜「Initial L2 of divergence」)。 「390.22 ÷ 2 = 195.11、和 195.28 几乎一样」这个折算是我们做的,书里没有。 还有一处口径差要说明:第 1、2 步都是拿同一个样本 vel.batch[0] 算的, 而第 4 步那个 0.679 是对全部 100 个流场取的平均(代码里是 math.mean(loss, dim=batch))——两个数用的是同一把尺子(都带那个 2), 但一个是单样本、一个是全批平均。

  15. 出处:第 9.4 节 Training(p.87)第 583 段(text/05-p81-100.txt:583,搜「Final loss」)。 打印输出是 0.6785882711410522;训练循环是 range(15)、学习率 5e-3, 进度条显示「15/15 [00:16<00:00, 1.12s/it]」。 2

  16. 出处:第 9.5 节 Testing with New Inputs(p.88)第 597 段 (text/05-p81-100.txt:597,搜「increasing the scale by a factor of 2」)。 原文明说这些样本「effectively out of the distribution of the training inputs」。

  17. 出处:第 9.5 节(p.89)第 619 段(text/05-p81-100.txt:619,搜「NN, mean divergence」)。 三行输出是「Original, mean divergence=2059.143, div. max=4.946」/ 「NN, mean divergence=11.510, div. max=0.417」/「Solver, mean divergence=0.000, div. max=0.000」。 2 3

  18. 出处:第 9.5 节(p.89)第 669 段 (text/05-p81-100.txt:669,搜「null-space in the form of a constant offset」)。 原文:网络学到的值常常比求解器大十倍,而 PhiFlow 的压力求解器被约束成零均值; 这个泊松问题的零空间就是一个常数偏移,取导数之后偏移不起作用,所以所有偏移都是完全合法的解 2 3 4

  19. 出处:第 9.6 节 Tougher Tests(p.93)第 86 段(text/06-p101-120.txt:86,搜「remarkably similar」)。 原文:最后一帧「remarkably similar」;书把这归功于训练时用的那个可微离散算子, 并说网络「hasn't even seen any obstacles at training time」; 改进建议(把障碍几何当额外输入、在边界区域加大损失)在同一段末尾。 50 步的进度条显示「50/50 [01:37<00:00, 1.96s/it]」(text/06-p101-120.txt:84,搜「1.96s/it」)。 2 3 4 5 6 7

  20. 出处:第 9.6 节(p.91)第 8 段(text/06-p101-120.txt:8,搜「before and after invoking」)。 原文:最安全的做法是在调用基于网络的压力修正前后各设一次速度边界; 前者确保网络看见固体障碍,后者确保即使网络在障碍边界处有小错,边界条件仍被满足。