AI Hero Club

扩散模型与 Flow Matching · 第 7 章

已自行标记完成0 / 8

仅保存在当前浏览器;完成标记不代表通过项目验收。

本章提纲 8 个小节
全课目录 8 章
完整课程计划 ↗
扩散模型与 Flow Matching / 课程计划 ↗

CHAPTER 07 / 8建议学习 170 分钟

训练二维速度网络与数值梯度验收

回归随机端点差时,怎样知道所有网络参数都在学习,并解释损失不会降到零?

本章要解决的 3 个问题
  • 核算网络输入、参数与MSE梯度
  • 完成全参数训练并解释不可约条件噪声
  • 读取真实训练证据并区分保存与恢复能力
课程项目 ZIP ↓

任务:训练真正的神经速度场,而不是展示解析教师

本章把前六章接口接到完整项目 projects/diffusion-models。网络输入是二维当前位置和连续时间,输出是二维速度;训练标签直接由独立抽样端点相减得到。解析教师不参与反向传播,只在验证中衡量场误差。这个区别决定我们是否真的做了机器学习:如果采样器实际调用的仍是人工解析公式,就不能把结果称为神经网络训练后的生成。本项目同时保存两种场的输出,但记录明确区分 learned 与 analytic_oracle。

运行环境固定为Python至少三点十、PyTorch二点六零、CPU单线程。训练使用Adam更新所有可训练参数,没有预训练权重,也没有冻结隐藏层只拟合最后输出层。由于任务仅有二维坐标和合成分布,标准库与PyTorch已足够,不需要下载图片、图像编码器或远程服务。后面的表现结论都限制在这份合成任务,不能推广为图像质量或真实数据的生成能力。

一、张量与参数账本

设批大小为 BB。初始噪声与目标端点形状均为 B×2B\times2,时间形状为 B×1B\times1。项目将两个空间坐标、时间本身、时间的正弦和余弦拼成五维特征;正弦与余弦使用角度 πt\pi t。它们提供非线性的时间坐标,但不是学习出的语义特征。输入随后经过宽度为六十四的线性层、SiLU激活、第二个同宽线性层、SiLU,再经线性层输出两个坐标;三层线性都带偏置。

SiLU⁡(z)=z 11+e−z,5⋅64+64+64⋅64+64+64⋅2+2=4674\operatorname{SiLU}(z)=z\,\frac{1}{1+e^{-z}},\qquad 5\cdot64+64+64\cdot64+64+64\cdot2+2=4674
参数数目包括每层偏置;激活函数与固定时间特征没有参数。

第一层权重是六十四乘五,第二层六十四乘六十四,输出层二乘六十四;偏置分别六十四、六十四、二。激活张量的数量随批大小变化,参数数量不随批大小变化。把两者混淆会错误估算模型规模。项目单元测试逐个参数张量检查梯度存在、有限且非零,再用有限差分核对一个权重导数,防止仅凭optimizer.step被调用就宣布完成全参数训练。

二、一次训练更新的完整信息流

训练器先用独立的本地随机数生成器抽 X0,X1,tX_0,X_1,t,根据实际线性桥构造当前位置与差值标签。将网络预测与标签的逐元素平方差在样本和坐标上一起取平均,得到标量损失。清除上一次梯度后反向传播,再执行Adam更新。验证对使用固定且不同的种子生成,不参与这些参数更新;这不是文档切分问题,而是独立合成随机样本和随机端点对的隔离。

L^=12B∑i=1B∑d=12(vθ(xi,ti)d−ui,d)2,∂L^∂vi,d=1B(vi,d−ui,d)\widehat L=\frac{1}{2B}\sum_{i=1}^B\sum_{d=1}^{2}\left(v_\theta(x_i,t_i)_d-u_{i,d}\right)^2,\qquad \frac{\partial\widehat L}{\partial v_{i,d}}=\frac1B(v_{i,d}-u_{i,d})
本式的分母二B来自二维坐标平均;不要再在训练器外重复除批大小。

算例一:损失和输出梯度的手算

取两条样本,目标速度分别为一负一和零二,预测分别为零零和一一。四个坐标误差依次为负一、正一、正一、负一,平方和为四,除以二乘批大小二,损失为一。输出梯度依次为负零点五、正零点五、正零点五、负零点五。若实现按坐标取和而非平均,会使损失与梯度都放大两倍;它仍可能下降,所以“训练能跑”不能独立证明归一化符合协议。

若最后一层为 v=Wh+bv=Wh+b,其中隐藏向量 hh 由之前层产生,单样本的权重梯度是输出梯度与隐藏向量的外积,偏置梯度就是输出梯度;隐藏梯度为权重转置乘输出梯度,再通过激活和前一层继续传播。停止隐藏梯度、把隐藏向量预先转成普通数值,或把大段前向放入no_grad,都会破坏全参数学习。有限差分应针对真正参与损失的参数,并用双精度小扰动避免舍入误差主导。

三、为什么正确训练的条件MSE不会归零

第五章的投影分解告诉我们,隐藏端点带来的速度条件方差构成不可约部分。训练器看到的是一次抽到的端点差,网络却只能读当前位置和时间。即使精确知道边缘场,也不可能在每个重复输入上猜中所有不同标签。因此需要同时记录条件标签MSE和相对解析边缘教师的场MSE;前者包含固有随机性,后者直接衡量本任务的场逼近程度,两者回答不同问题。

算例二:一个正的理论损失地板

考虑一维初始端点标准正态,目标端点均值二、方差一,端点独立。中间时刻为一半时,速度差的均值为二、方差为二;它与当前位置的协方差为零。因为该单高斯例子联合正态,零协方差意味着这两个量独立,所以给定当前位置后,速度条件方差仍为二。最佳预测处处为二,最小每坐标MSE为二;错误地期待它为零,会把正确场当作训练失败。

这个单高斯例子不能直接把数值二当作项目四簇混合的精确总体地板。项目在四千零九十六个固定验证端点对上,用解析场计算的标签MSE约为二点四一六一四;这是有限验证样本的测量。最终网络标签MSE约二点四六四零零,相对教师的场MSE约零点零五九三二。有限样本里投影交叉项不一定精确为零,所以不能硬要求“网络标签MSE减教师标签MSE”逐小数等于场MSE。总体等式与经验估计要分开。

四、真实运行记录与检查点

冻结的示例训练使用一千五百次更新,批大小二百五十六,隐藏宽度六十四,学习率零点零零二,模型初始化种子七、训练端点对生成器种子八。固定验证端点对种子为一万零七。网络标签MSE从五点零四五二八五降到二点四六四零零;相对解析教师的场MSE从二点六二四三六七降到零点零五九三二。参数总数与可训练数都是四千六百七十四。这些是项目JSON中的实测,不是示意期待值。

检查点保存模型、Adam状态、更新步数、模型配置、全局torch随机状态、本地训练对生成器状态及训练报告。保存这些信息有助于复查运行,但当前CLI没有提供精确恢复训练命令,因此我们不宣称已经实现和检验恢复接口。评价命令只读检查点,并核对前后文件指纹不变。换隐藏宽度、路径、坐标尺度或噪声公式都应视作新实验,不能沿用旧报告中的数值作为新版本成绩。

训练报告的验证损失来自未参与更新的合成端点对。它并不证明未见真实世界数据上的泛化,也不等同于样本分布已经正确。采样还会遭遇求解器误差;局部场可能在验证分布少见区域偏差更大;单个训练种子也不能显示不同初始化的波动。第八章将锁定此模型,再用多个采样种子与失败对照评估终端分布,而不继续挑选训练版本来迎合指标。

有限差分检验必须冻结同一批端点与时间。若参数加小扰动后重新抽数据,再减扰动时又抽另一批,两个损失之差主要反映随机样本,而不是导数。项目先固定双精度样本,计算反向传播导数,再将一个权重分别加减十的负五次方,使用同一标量损失做中心差分。最后把权重恢复原值。测试通过只验证被检查的导数和全参数梯度路径,不等于证明整个训练系统没有其他错误。

对SiLU激活,可以直接由乘积法则核对局部梯度:令sigmoid为一除以一加指数负输入,导数等于sigmoid加输入乘sigmoid乘一减sigmoid。这个激活在负区间也不完全切断梯度,但深层权重组合仍可能让梯度很小。应检查实际数值有限与参数确实更新,不能因为选择了一个平滑激活就省掉梯度验收。CPU单线程和固定随机种子帮助复跑,本课并未承诺跨硬件版本逐位一致。

在课程项目目录运行在配套项目根目录执行;这是独立可运行的小批更新,不含未实现函数。
import torch
from flow2d.core import Config,VelocityNet,generator,pairs,interpolate
torch.set_num_threads(1);torch.manual_seed(7)
model=VelocityNet(Config(16))
optimizer=torch.optim.Adam(model.parameters(),lr=0.002)
rng=generator(8)
for step in range(5):
    x0,x1,t=pairs(32,rng);x,target=interpolate(x0,x1,t)
    loss=(model(x,t)-target).square().mean()
    optimizer.zero_grad(set_to_none=True);loss.backward()
    assert all(p.grad is not None for p in model.parameters())
    optimizer.step()
print("updates",5,"loss",float(loss.detach()))

练习与完整解答

TRY IT YOURSELF

练习一|改变隐藏宽度

若隐藏宽度改成32,仍使用五维输入、两层同宽隐藏层和二维输出,总参数是多少?批大小加倍会改变它吗?

提示 1
逐层按输入数×输出数加bias计参数,隐藏层之间是方阵。
提示 2
区分共享权重与随batch增长的激活;若统计随batch变化,检查是否误数了激活或逐样本建网。
对照参考解答
第一层参数五乘三十二加三十二,得到一百九十二;第二层三十二乘三十二加三十二,得到一千零五十六;输出层三十二乘二加二,得到六十六;相加为一千三百一十四。批大小只改变同时计算的激活与随机样本,不改变这些共享权重的数量。若参数统计随批大小变化,应检查是否错误地为每个样本创建了独立网络或把激活数量当成参数数量。

TRY IT YOURSELF

练习二|噪声地板和学会的场

假设一个任务的解析教师标签MSE为2.5,网络标签MSE为2.6,而场MSE为0.08。能否因为2.6没有接近零就判断网络无效?三者不精确相减是否必然是bug?

提示 1
条件标签含隐藏端点噪声,解析边缘教师的标签损失为何也可为正?
提示 2
总体平方投影分解的交叉项期望为零,有限样本不必恰零;先核同样本、单位与时间权重。
对照参考解答
不能。条件标签中含有输入未确定的随机端点信息,正确边缘场也有正的标签损失;应看网络与教师及未训练基线的差异。总体投影分解中的交叉项期望为零,但有限验证样本的经验交叉项不必恰好为零,所以二点六减二点五不精确等于零点零八,不自动证明实现有bug。仍应核对相同样本、相同坐标归一化、相同时间权重,再用更大独立验证集估计误差,而非强行改数字满足恒等式。

TRY IT YOURSELF

练习三|防止假训练证据

某代码保存训练后模型,却在evaluate里总调用oracle_velocity。列出最小修改和独立验证,确保报告真的是网络采样。

提示 1
先追踪evaluate的learned分支究竟调用网络还是解析教师。
提示 2
在副本中换随机网络权重,learned结果应响应而oracle不变;同时记录实际加载的检查点身份。
对照参考解答
评价应从检查点重建VelocityNet并加载参数,learned分支直接调用这个网络;解析教师保留为单独analytic_oracle分支。记录各分支名称、检查点指纹和调用函数;把检查点权重临时替换为随机初始化副本,应使learned输出变化,而不影响oracle输出。还需检查全部网络参数有梯度、训练前后字段误差实际下降。即使两种输出偶然接近,也不能把教师程序当作网络实现;证据需要接口与实际调用链支持。

CHECK YOUR UNDERSTANDING

检验理解

先作答,再决定是否展开解析。选择题用于发现理解缺口;独立实现与迁移任务仍需另行检验。

1. 本项目网络输入维度是多少?

查看解析两个坐标加时间、sin(πt)、cos(πt)。
2. 隐藏64网络参数数目是多少?

查看解析三层权重与偏置相加得到4674。
3. 标签MSE的正地板说明什么?

查看解析边缘预测不能逐例恢复所有隐藏端点差。
4. 本项目当前CLI检验了精确恢复吗?

查看解析应区分已保存信息与已实现验收的功能。

自行标记用于记录学习进度,不代表通过项目验收。