Lecture 04 · LAB 05 · 扩散的条件均值与流积分

LAB 05 · 扩散的条件均值与流积分

先阅读保存结果和解释,再按本册步骤选择是否运行。在 Deepnote 阅读与运行 · 下载 Notebook

LAB 05 · 扩散的条件均值、噪声预测与流积分

三个可计算问题:一对多为什么仍可使用噪声 MSE;噪声水平怎样改变后验;生成为什么要不断重新计算速度。这里使用已知玩具分布的解析最优预测器,不训练神经网络,也不把实验结果当作真实图像或 LLM 能力证据。

本轮入口:先看下面的保存图,再只改一个参数。 原有可手算代码与推导完整保留在后面。

先看问题与保存结果

本册先用可精确核对的解析条件均值和方向场,隔离数值更新产生的误差。LAB09 真正学习时间条件网络;两者噪声约定不同,不能直接把图的差异归为模型优劣。

本册先用可精确核对的解析条件均值和方向场,隔离数值更新产生的误差。LAB09 真正学习时间条件网络;两者噪声约定不同,不能直接把图的差异归为模型优劣。

连续概念解释。读完后留下一个结果:固定量、改变项、实际观察,以及它支持的机制。

先看保存结果:从带噪观察到条件预测,再沿同批起点积分

从带噪观察到条件预测,再沿同批起点积分:图1

从带噪观察到条件预测,再沿同批起点积分:图2

从带噪观察到条件预测,再沿同批起点积分:图3

前两图把两类似然、±1后验和条件均值分开:均值是当前观测下的预测,未必等于一个合法最终样本。噪声预测图用数值积分核对最优条件均值与偏置风险;加0.5偏置使期望MSE增加0.25。Flow图的蓝实线是Euler数值路径,金色点线是同起点的解析路径。Flow图使用另一明确高斯模型:时间1是噪声,0是数据;每一步重算场,再用负步长更新。所有步数共用同批起点,右图比较数值终点与同起点解析终点2+0.5z的误差,不将有限样本均值偏差混作积分误差。

结果身份: 2026-09-28由下方同源Python代码在本地CPU实际生成并核对。解析Bayes预测器与手写高斯流,未训练扩散网络,也没有图像生成。 图不是模型训练输出。

无需GPU的即时探索

保存数据、图片与代码随下载材料提供;静态图无需GPU。学生重算需要Python、NumPy、matplotlib,不发起网络请求、不打印密钥,只在当前目录写入本册输出文件夹。

只改一个变量:FLOW_STEPS

主对照只改变 FLOW_STEPS(1/4/16/64),固定保存的1600个高斯起点。另一个独立对照可只改 NOISE_A(0.1/0.4/0.64/0.9),保持带噪观测x_t=0.5;不要同时改两个参数后归因。

在下面参数区改值后运行本格;重算图会显示在输出中,并保存在 lab05-visual-output。接着用图中的具体变化解释,不从一次样本判断整个分布。

查看可执行代码
import math, json, platform
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt

# Primary intervention: FLOW_STEPS. Keep the exact same stored starting particles.
FLOW_STEPS=16
# A separate intervention: change NOISE_A while holding observed x_t=0.5 fixed.
NOISE_A=.64
FLOW_SEED=20260928
FLOW_N=1600
OUT=Path('lab05-visual-output');OUT.mkdir(exist_ok=True)
assert FLOW_STEPS>=1 and 0<NOISE_A<1

def vis_normal(x,mean,var):return np.exp(-(x-mean)**2/(2*var))/np.sqrt(2*np.pi*var)
def vis_post(xt,a):return np.tanh(np.sqrt(a)*np.asarray(xt)/(1-a))
def vis_eps(xt,a):return (np.asarray(xt)-np.sqrt(a)*vis_post(xt,a))/np.sqrt(1-a)
def vis_velocity(x,t):
    mean=2*(1-t);var=.25*(1-t)**2+t*t;cov=t-.25*(1-t)
    return -2+cov/var*(np.asarray(x)-mean)
def vis_starts(seed=FLOW_SEED,n=FLOW_N):
    # Same integer stream and Box-Muller transform in the browser.
    state=int(seed)&0xffffffff;out=[]
    for _ in range((n+1)//2):
        state=(1664525*state+1013904223)&0xffffffff;u1=(state+.5)/4294967296
        state=(1664525*state+1013904223)&0xffffffff;u2=(state+.5)/4294967296
        radius=math.sqrt(-2*math.log(u1));angle=2*math.pi*u2
        out.extend([radius*math.cos(angle),radius*math.sin(angle)])
    return np.asarray(out[:n])
def vis_flow(z,steps):
    x=np.asarray(z).copy();history=[x.copy()]
    for k in range(steps):
        t=1-k/steps;x=x-vis_velocity(x,t)/steps;history.append(x.copy())
    return np.stack(history)

grid=np.linspace(-2.8,2.8,500);a=NOISE_A
fig,axes=plt.subplots(1,3,figsize=(13,3.8))
axes[0].plot(grid,vis_normal(grid,-math.sqrt(a),1-a),label='p(x_t | X0=-1)',color='#b88526')
axes[0].plot(grid,vis_normal(grid,math.sqrt(a),1-a),label='p(x_t | X0=+1)',color='#003262')
axes[0].axvline(.5,color='#455b6d',ls='--');axes[0].set(xlabel='observed noisy x_t',ylabel='Density',title=f'Noise level: a={a:g}');axes[0].legend(fontsize=8)
prob=float((vis_post(.5,a)+1)/2);axes[1].bar(['-1','+1'],[1-prob,prob],color=['#b88526','#003262']);axes[1].set(ylim=(0,1),title='Posterior for the SAME x_t=0.5',ylabel='P(X0 | x_t)')
for aa in [.1,.4,.64,.9]:axes[2].plot(grid,vis_post(grid,aa),label=f'a={aa:g}')
axes[2].set(xlabel='x_t',ylabel='E[X0 | x_t]',title='Mean is a prediction, not a final sample');axes[2].legend(fontsize=8)
fig.tight_layout();fig.savefig(OUT/'lab05-posterior.png',dpi=160);plt.show();plt.close(fig)

fig,axes=plt.subplots(1,2,figsize=(11,3.7));axes[0].plot(grid,vis_eps(grid,a),color='#003262',label='Bayes noise predictor');axes[0].plot(grid,vis_eps(grid,a)+.5,color='#b88526',label='same predictor + 0.5');axes[0].set(xlabel='x_t',ylabel='Predicted epsilon');axes[0].legend(fontsize=9)
# Integrate the true joint density over x_t rather than simulate an unpaired noise sample.
integration=np.linspace(-8,8,12001);wminus=.5*vis_normal(integration,-np.sqrt(a),1-a);wplus=.5*vis_normal(integration,np.sqrt(a),1-a)
eminus=(integration+np.sqrt(a))/np.sqrt(1-a);eplus=(integration-np.sqrt(a))/np.sqrt(1-a)
losses=[]
for name,pred in [('zero',np.zeros_like(integration)),('Bayes',vis_eps(integration,a)),('Bayes + 0.5',vis_eps(integration,a)+.5)]:
    integrand=wminus*(eminus-pred)**2+wplus*(eplus-pred)**2; mse=float(np.sum((integrand[1:]+integrand[:-1])*.5*np.diff(integration)));losses.append({'predictor':name,'MSE':mse})
axes[1].bar([r['predictor'] for r in losses],[r['MSE'] for r in losses],color=['#9aabb8','#003262','#c29324']);axes[1].set(ylabel='Expected noise MSE',title='A +0.5 bias adds exactly 0.25 risk')
assert abs(losses[2]['MSE']-losses[1]['MSE']-.25)<1e-8
fig.tight_layout();fig.savefig(OUT/'lab05-noise-prediction.png',dpi=160);plt.show();plt.close(fig)

starts=vis_starts();np.save(OUT/'lab05-starts.npy',starts);exact=2+.5*starts;history=vis_flow(starts,FLOW_STEPS)
records=[]
for steps in [1,4,16,64]:
    end=vis_flow(starts,steps)[-1];records.append({'steps':steps,'mean':float(end.mean()),'variance':float(end.var()),'coupling_MSE':float(np.mean((end-exact)**2))})
fig,axes=plt.subplots(1,3,figsize=(13,4))
times=np.linspace(1,0,FLOW_STEPS+1);selected=np.argsort(starts)[np.linspace(0,FLOW_N-1,13,dtype=int)]
for index in selected:
    axes[0].plot(times,history[:,index],lw=1,color='#376782',alpha=.65)
    exact_path=2*(1-times)+np.sqrt(.25*(1-times)**2+times**2)*starts[index]
    axes[0].plot(times,exact_path,color='#b18421',ls=':',lw=.9,alpha=.5)
axes[0].set(xlabel='t: noise 1 -> data 0',ylabel='Same particle position',title=f'Recompute velocity after each step ({FLOW_STEPS})',xlim=(1,0))
bins=np.linspace(-1,5,40);axes[1].hist(history[-1],bins=bins,density=True,alpha=.6,color='#5d87a0',label=f'Euler {FLOW_STEPS}')
density_grid=np.linspace(-1,5,500);axes[1].plot(density_grid,vis_normal(density_grid,2,.25),color='#c18c0e',label='target N(2, 0.25)');axes[1].set(xlabel='Final sample',ylabel='Density');axes[1].legend(fontsize=8)
axes[2].loglog([r['steps'] for r in records],[r['coupling_MSE'] for r in records],'o-',color='#003262');axes[2].set(xlabel='Euler steps / field evaluations',ylabel='MSE vs exact same-start endpoint',title='Numerical error, not learned-model error')
fig.tight_layout();fig.savefig(OUT/'lab05-flow.png',dpi=160);plt.show();plt.close(fig)
payload={'kind':'analytic Bayes predictor and hand-specified Gaussian flow; no learned diffusion network','a':NOISE_A,'observation':.5,'posterior_plus':prob,'noise_MSE':losses,'seed':FLOW_SEED,'n':FLOW_N,'flow_steps':FLOW_STEPS,'comparisons':records,'changed':{'mean':float(history[-1].mean()),'variance':float(history[-1].var()),'coupling_MSE':float(np.mean((history[-1]-exact)**2))},'first_12_starts':starts[:12].tolist(),'python':platform.python_version()}
(OUT/'lab05-results.json').write_text(json.dumps(payload,indent=2));print(json.dumps(payload['changed'],indent=2))
LAB 05 · 扩散的条件均值与流积分的保存输出
已保存输出;运行地点及条件见本册说明。
LAB 05 · 扩散的条件均值与流积分的保存输出
已保存输出;运行地点及条件见本册说明。
LAB 05 · 扩散的条件均值与流积分的保存输出
已保存输出;运行地点及条件见本册说明。
查看保存的计算输出
{
  "mean": 2.0054384912218075,
  "variance": 0.21030638288437056,
  "coupling_MSE": 0.0019894204282116645
}

本轮运行与后续阅读

本地执行与云端复核均已完成。 2026-09-28,本册全部代码在本地CPU从空命名空间顺序执行;另通过Deepnote全本运行,状态为success,运行ID为 911df979-ad74-493b-88c0-949bfe261a15,完成时间 2026-09-28T15:31:30.280Z(UTC)。运行快照检查到本册图形输出且无失败代码块。后验、噪声预测与同批Flow轨迹图已输出;16步数值终点相对同起点解析终点MSE=0.0019894204,64步误差进一步降低。

云端运行使用Python 3.13数据科学环境。平台总用时包含启动、Notebook执行与输出保存,不作为算法速度基准;五本均为小规模CPU计算,不调用外部模型或付费API。上方静态图仍明确保留其本地生成来源,云端输出是另一次实际复核。学生修改参数后得到的是自己的新结果。

下面保留此前逐步计算与推导。历史本地数字保留原身份,可把图中一个关系追到对应公式。

1 · 先预测:观察带噪样本后,原值还是各占一半吗?

令 X₀ 等概率取 −1、+1,Xₜ=√a X₀+√(1−a)ε,ε∼N(0,1)。给定 a=0.64、xₜ=0.5,猜 P(X₀=+1|xₜ) 是否大于 0.5。注意条件从“什么也没看见”变成“看见当前带噪值”。

查看可执行代码
import math, random, statistics

def posterior_mean(xt, a):
    assert 0 < a < 1
    return math.tanh(math.sqrt(a)*xt/(1-a))

def epsilon_star(xt, a):
    return (xt-math.sqrt(a)*posterior_mean(xt,a))/math.sqrt(1-a)

for xt in [-1., -.5, 0., .5, 1.]:
    ex=posterior_mean(xt,.64)
    print('xt=',xt,'P(+1|xt)=',round((ex+1)/2,5),'E[X0|xt]=',round(ex,5),'E[eps|xt]=',round(epsilon_star(xt,.64),5))
assert abs(posterior_mean(0,.64))<1e-12
assert abs(posterior_mean(.5,.64)-math.tanh(10/9))<1e-12
查看保存的计算输出
xt= -1.0 P(+1|xt)= 0.01161 E[X0|xt]= -0.97679 E[eps|xt]= -0.36429
xt= -0.5 P(+1|xt)= 0.09777 E[X0|xt]= -0.80445 E[eps|xt]= 0.23927
xt= 0.0 P(+1|xt)= 0.5 E[X0|xt]= 0.0 E[eps|xt]= 0.0
xt= 0.5 P(+1|xt)= 0.90223 E[X0|xt]= 0.80445 E[eps|xt]= -0.23927
xt= 1.0 P(+1|xt)= 0.98839 E[X0|xt]= 0.97679 E[eps|xt]= 0.36429

从似然比得到公式

两个高斯条件似然的 log 比值为 2√a·xₜ/(1−a)。因此 E[X₀|xₜ]=tanh(√a·xₜ/(1−a))。再由加噪恒等式得到 E[ε|xₜ]=(xₜ−√a E[X₀|xₜ])/√(1−a)。这里的后验均值通常不是 ±1,但它是采样器中使用的局部预测量,并不被直接当作最终生成样本。

2 · 先预测:降低噪声时,相同 xₜ 会提供更多还是更少信息?

保持 xₜ=0.5,只改变 a。随后比较零预测、解析最优预测和加偏置后的噪声 MSE。样本、噪声、随机种子保持相同。

查看可执行代码
for a in [.1,.4,.64,.9]:
    print('a=',a,'P(+1|xt=.5)=',round((posterior_mean(.5,a)+1)/2,5))
rng=random.Random(20260926)
a=.64; cases=[]
for _ in range(50000):
    x0=rng.choice([-1.,1.]); eps=rng.gauss(0,1)
    xt=math.sqrt(a)*x0+math.sqrt(1-a)*eps
    cases.append((xt,eps))
loss0=statistics.mean(eps**2 for _,eps in cases)
loss_star=statistics.mean((eps-epsilon_star(xt,a))**2 for xt,eps in cases)
loss_bias=statistics.mean((eps-epsilon_star(xt,a)-.5)**2 for xt,eps in cases)
print('zero / Bayes / Bayes+0.5:',loss0,loss_star,loss_bias)
print('biased excess:',loss_bias-loss_star,'theory:',.25)
assert loss_star<loss0
assert abs((loss_bias-loss_star)-.25)<.02
查看保存的计算输出
a= 0.1 P(+1|xt=.5)= 0.58695
a= 0.4 P(+1|xt=.5)= 0.74156
a= 0.64 P(+1|xt=.5)= 0.90223
a= 0.9 P(+1|xt=.5)= 0.99992
zero / Bayes / Bayes+0.5: 1.0077005062969948 0.4827691823522884 0.7268944450964093
biased excess: 0.24412526274412094 theory: 0.25

解释

给定当前状态的条件期望最小化平方误差,加常数偏置 b 的期望超额风险为 b²。经验值有蒙特卡洛误差。不可由此推出“直接预测最终图像均值也等价于采样”:扩散把局部预测放进多步生成过程。我们的解析预测器与网络回归具有同一种统计目标,但不是训练得到的网络。

3 · Flow:回归的目标与采样的方向

数据 X∼N(2,0.25),独立噪声 Z∼N(0,1),Xₜ=(1−t)X+tZ,时间 t=0 为数据、t=1 为噪声。条件配对速度是 Z−X;真正采样用 v*(x,t)=E[Z−X|Xₜ=x]。先猜:生成积分应加还是减 Δt·v?

查看可执行代码
def velocity(x,t):
    mean=2*(1-t)
    var=.25*(1-t)**2+t*t
    covariance=t-.25*(1-t)
    return -2+covariance/var*(x-mean)

def sample_flow(z,steps):
    x=z; dt=1/steps
    for k in range(steps):
        t=1-k*dt
        x-=dt*velocity(x,t)
    return x

rng=random.Random(20261004)
z=[rng.gauss(0,1) for _ in range(5000)]
for steps in [1,4,16,64]:
    xs=[sample_flow(v,steps) for v in z]
    print('steps',steps,'mean',round(statistics.mean(xs),4),'variance',round(statistics.pvariance(xs),4))
exact=[2+.5*v for v in z]
for steps in [4,16,64]:
    error=statistics.mean((sample_flow(v,steps)-target)**2 for v,target in zip(z,exact))
    print('steps',steps,'numerical MSE against exact coupling',error)
assert statistics.mean((sample_flow(v,64)-target)**2 for v,target in zip(z,exact))<.001
查看保存的计算输出
steps 1 mean 2.0 variance 0.0
steps 4 mean 2.0046 variance 0.1153
steps 16 mean 2.0061 variance 0.2064
steps 64 mean 2.0065 variance 0.2373
steps 4 numerical MSE against exact coupling 0.025281674584902998
steps 16 numerical MSE against exact coupling 0.0019527387230765408
steps 64 numerical MSE against exact coupling 0.00013023103467744635

为什么不直接用一对样本的 Z−X?

采样时只有当前 x 和 t,没有对应的训练配对 X、Z。高斯联合分布让我们能够解析计算条件平均速度;一般数据中它由网络学习。生成从 1 到 0,dt 的符号因此是负方向。这个特定仿射场具有可计算解,更多步降低了本实验的离散误差;不能推出任何神经网络采样器增加步数都改善感知质量。

只改一个变量

  1. 将 ±1 的先验概率改为 0.8/0.2,先推导新的后验 logit,再改代码。
  2. 将最优噪声预测加 b=−0.5、0.2、1,检验超额风险 b²。
  3. 把 Euler 的减号改为加号,解释为什么走向错误的时间方向,再改回来。
  4. 将数据方差改为 1,重新推导 velocity;哪些时刻条件平均速度发生变化?
  5. 让 AI 检查你的推导和随机误差解释,不让它用“模型理解了图像”代替计算证据。

历史本地保存输出(2026-09-26;本轮执行记录见开篇)

xt= -1.0 P(+1|xt)= 0.01161 E[X0|xt]= -0.97679 E[eps|xt]= -0.36429
xt= -0.5 P(+1|xt)= 0.09777 E[X0|xt]= -0.80445 E[eps|xt]= 0.23927
xt= 0.0 P(+1|xt)= 0.5 E[X0|xt]= 0.0 E[eps|xt]= 0.0
xt= 0.5 P(+1|xt)= 0.90223 E[X0|xt]= 0.80445 E[eps|xt]= -0.23927
xt= 1.0 P(+1|xt)= 0.98839 E[X0|xt]= 0.97679 E[eps|xt]= 0.36429
a= 0.1 P(+1|xt=.5)= 0.58695
a= 0.4 P(+1|xt=.5)= 0.74156
a= 0.64 P(+1|xt=.5)= 0.90223
a= 0.9 P(+1|xt=.5)= 0.99992
zero / Bayes / Bayes+0.5: 1.0077005062969948 0.4827691823522884 0.7268944450964093
biased excess: 0.24412526274412094 theory: 0.25
steps 1 mean 2.0 variance 0.0
steps 4 mean 2.0046 variance 0.1153
steps 16 mean 2.0061 variance 0.2064
steps 64 mean 2.0065 variance 0.2373
steps 4 numerical MSE against exact coupling 0.025281674584902998
steps 16 numerical MSE against exact coupling 0.0019527387230765408
steps 64 numerical MSE against exact coupling 0.00013023103467744635

来源与返回课堂

MIT 6.S978 Lecture 5 物理页 16–23(前向)、25–57(目标)、59–65(训练与采样);MIT 6.S058 Diffusion Models 物理页 69–90(flow)。参数与解析双点/高斯算例由本课程构造。

  • https://mit-6s978.github.io/assets/pdfs/lec5_diffusion.pdf
  • https://introtocv.github.io/schedule.html
  • Lecture 04 课程导读

从课堂展开:推导、反例与变量实验

先完成上面的有限例子,再按自己尚未弄清的问题选择推导。已有代码只覆盖本册明确列出的计算;后面的研究练习是可继续提出的实验,不是已经运行的结果。

原有解释按连续主题拆到下方;本入口继续保留。

训练造题与采样更新:别把答案放进输入

按本课连续扩散记号 x_t=√a·x_0+√(1-a)·ε,a 是累计信号系数;越接近1通常保留越多信号。训练者知道 x_0、ε、a,预测器只能读取 x_t 与相应难度。给 x_0 多抽几组 ε/a 增加了题型,没有增加原始对象覆盖。不同时间各自独立抽噪得到正确边际快照,也不等于模拟了一条联合随机路径。

在 x_0 等概率取±1的实验中,条件均值 tanh(√a·x_t/(1-a)) 随状态与噪声等级变化。局部 MSE 的平均预测并不是最终样本;生成还要用匹配的采样规则更新状态,再重新查询预测器。模型输出、数值更新、可选的额外随机项承担不同角色,关闭随机项不能让任意更新式都自动等价。

Flow 实验采用 data 位于 t=0、noise 位于 t=1 的约定。配对路径 x_t=(1-t)x+tz 的监督速度 z−x,采样从1走到0,Δt为负。代码积分的是已知高斯族的解析场,步数改变可检验数值误差;它未训练图像扩散网络,也未实测 DDIM 或低步数研究模型的图像质量。少步求解器与改变训练目标是两个选择。

高斯噪声是随机变量,不是白色遮罩

为什么加噪图像看起来相似,却不能把噪声当成固定纹理?

写 ε∼N(0,I) 表示各维标准高斯随机变量,而不是每个像素都加同一个数。某一次抽样得到的 ε 是一张具体张量;重复抽样会得到另一张张量。高斯噪声可以为负,也可以超过常见图像显示范围,因此用于展示的裁剪与归一化不能偷偷成为训练分布的一部分。各向同性描述噪声协方差,并不声称真实图像各像素独立。给相关的图像加入独立噪声,得到的中间状态仍可能保留跨像素结构。课堂只需一维或二维例子就能先把分布、随机样本、显示三层拆开。

追问:噪声独立,为什么加完噪声的图像还看得出轮廓?

独立的是新增噪声各维之间的关系,原图的结构仍通过信号系数留在状态里。只要信号尚未被压得很小,邻近像素的相关性仍可从原图传递过来。看得见轮廓说明当前信噪比还有信息,不表示噪声抽样一定相关,也不表示模型已经参与了这个前向步骤。

返回当前页说明

相关阅读:Diffusion Models

一次可以手算的加噪

x0=2、ᾱ=.64、ε=−.5 时,网络到底看见什么?

本课采用一个人为构造的一维例子:干净值 x_0=2,累计系数 ᾱ_t=.64,实际抽到的噪声 ε=−.5。于是 √ᾱ_t=.8,√(1−ᾱ_t)=.6,输入为 x_t=.8×2+.6×(−.5)=1.3。网络在训练时看到 1.3 与对应噪声等级,目标噪声是 −.5。注意这个负噪声把状态往下拉,并不是噪声只能让数值增大。该例用于核查公式与程序,既不是训练得到的模型结果,也不能凭一个数据点估计真实图像生成能力。后续损失、重建与采样例都沿用这组量。学生应能在纸上复算,也能逐行检查程序是否使用了同样的数值。

追问:为什么不能说网络输入1.3,正确输出就一定是−.5?

对于这一次人为制造的训练对,标签确实是 −.5。但相同输入可能由不同干净值和噪声组合产生,网络并不知道我们选了哪一组。它在平方误差下学习的是这些可能标签的条件平均。单条监督记录有明确标签,并不意味着总体统计问题具有唯一的真实噪声答案。

返回当前页说明

相关阅读:Diffusion Models

边缘分布相同,不等于同一段随机轨迹

直接抽 xt 与逐步模拟前向链,什么时候能互相替代?

闭式加噪与逐步马尔可夫模拟在某个固定时刻给出相同的条件边缘分布 q(x_t|x_0),但若要同时观察多个时刻,随机数如何共享就很重要。分别为每个时刻独立抽 ε,会得到一组正确的单时刻样本,却通常不对应原来的马尔可夫链。所有时刻共用一份 ε 又会定义另一种耦合。训练噪声预测器通常只需抽取一个时刻,因此可安全使用闭式边缘;如果要研究真实路径、条件转移或路径相关性,就不能把任意拼接的图像帧当作同一条轨迹。

追问:网页里用相同随机种子展示从清晰到模糊,能叫一条DDPM轨迹吗?

要看代码怎样使用这个种子。如果每个时刻都由同一份基础噪声直接组合,它是方便教学的连续插值耦合,不能自动称为原始马尔可夫前向链。两者都可以用于展示噪声强度,但标签应说明构造方式。研究路径性质时必须使用对应过程的联合抽样规则。

返回当前页说明

相关阅读:Diffusion Models

把一个后验算到小数点后

两步α均为.8时,给定x0=2、x2=1.3,上一状态的分布是什么?

延续一维例子,设 α_1=α_2=.8,β_2=.2,因此 ᾱ_1=.8、ᾱ_2=.64。给定 x_0=2、x_2=1.3,后验方差为 .2×.2/.36=1/9,标准差为1/3。均值的两个系数恰好都为 √.8×.2/.36≈.4969,于是均值约为 .4969×2+.4969×1.3=1.6398。即使起点和当前点都固定,中间状态仍有不确定性。这里两系数相同只是所选参数的巧合,不能推广为一般结论。用大量模拟链筛选附近状态,可以检查解析分布的形状,但筛选窗口也会引入近似。在实践记录中,应把给定的条件、解析参数和抽样得到的值分开存放。否则学生可能拿一张具体样本与整个概率分布比较,再把合理的随机波动误诊为计算错误。

追问:后验均值比1.3大,是否意味着逆过程每一步都会让数值变大?

不会。这只是当前干净值、带噪值和日程共同决定的结果。换一份噪声或换一个数据点,均值可以向另一方向移动,实际抽样还包含随机项。逆过程在总体上朝数据分布演化,不要求每个坐标单调增加,也不要求某个简单像素统计量每一步都单调改善。

返回当前页说明

相关阅读:Diffusion Models

噪声损失的小误差怎样传到重建?

预测−.3而标签是−.5时,损失和重建估计分别是多少?

在 x_t=1.3、ᾱ=.64 的例子里,若 ε̂=−.3,噪声平方误差为 [−.3−(−.5)]²=.04。由 x̂_0=(x_t−√(1−ᾱ)ε̂)/√ᾱ 得到 x̂_0=(1.3+.18)/.8=1.85,比真实2少.15。两种误差的比例由噪声与信号系数决定:x0误差等于 −√[(1−ᾱ)/ᾱ] 乘噪声预测误差。同样大小的噪声误差,在信号极弱时会被放大成较大的干净值误差。这说明参数化与损失权重有实际后果,不能把不同时间点、不同输出目标的数值损失直接横向比较。因此诊断模型时可以按噪声等级分别统计误差,而不是把所有状态压成一个平均数。若总体损失下降但某些重要区间仍很差,采样链仍可能在这些区间积累明显偏差。

追问:能否直接比较epsilon模型和x0模型的训练loss大小?

不能只看原始数值。它们的目标尺度和时间依赖不同,同一预测质量可能对应不同的损失大小。比较时需要统一评价空间,或根据系数换算并说明时间加权方式。最终还应检查生成分布、条件满足程度与计算预算,因为训练目标只是系统性能的一部分。

返回当前页说明

相关阅读:Diffusion Models

把训练与生成写成两份可执行说明

只给别人一个神经网络文件,足够复现生成吗?

一份可复现的训练说明至少应说明数据如何归一化、时间怎样抽样、状态怎样加噪、网络预测什么以及损失如何加权。一份生成说明则要说明先验、时间序列、模型条件、预测到更新量的换算、随机项与最终解码。两份说明在若干位置必须严格衔接,但并非完全相同。课堂可以把它们作为两张接口表而不是数百行代码。这样学生拿到一个实现时,能够先问“这个变量代表什么”和“哪一步改变了分布”,再让 AI 帮忙读取具体库函数,减少只会替换函数名却不懂结果变化的情况。

追问:同一权重换采样器,是否一定无需其他修改?

不一定。某些采样器可以复用同一噪声预测器,但仍要求了解其参数化、时间坐标、训练日程和边界约定。若这些接口不匹配,代码可能运行而结果错误。应先建立一组已知输入与预测量的转换检查,再比较样本质量,不能把“没有报错”当作算法兼容的证据。

返回当前页说明

相关阅读:Diffusion Models

一维逆采样也可以核对到数值

均值1.6398、标准差1/3,再抽z=−.6,下一状态是多少?

继续此前的解析后验教学例,当前状态1.3的条件均值约为1.6398,采用方差1/9时标准差为1/3。若这一步另抽到 z=−.6,则下一状态为1.6398+(1/3)×(−.6)=1.4398。它不是1.6398,也不是干净值2。重复抽样会围绕均值散开;单次偏离并不证明预测错误。要检查采样实现,可以固定当前状态与预测器,抽很多次下一状态,再比较经验均值和方差,而不是仅拿某次输出同均值做差。这是一个完全受控的局部测试,不需要训练大模型。这也是区分推导错误与抽样波动的办法。

追问:如果采样结果比均值更远离真实x0,应不应该丢弃?

在这个受控后验问题里,一些合法样本本来就会离x0更远。若按照是否接近已知答案筛选,就改变了抽样分布,不能再称为原条件分布的无偏样本。生成评价也要区分预先规定的筛选过程与无筛选采样,并报告相应成本,不能悄悄只保留最好看的结果。

返回当前页说明

相关阅读:Diffusion Models

固定种子固定了哪些随机性?

同一个seed能保证不同模型、不同步数输出完全相同吗?

随机种子控制某个实现的伪随机数序列。对于迭代生成,随机性可能来自初始噪声、各步随机项以及其他实现细节。改变步数、张量形状或随机数调用顺序,可能改变后续实际使用的数值;即便保留初始噪声,换模型或采样器也会改变从噪声到结果的映射。因此公平对比应尽量保存实际初始张量并说明随机项处理,而不只写一个seed。不同硬件或算子实现还可能产生数值差异。本课局部实验可以做到逐数复核,大型系统则应明确复现到何种精度与统计层次。

追问:比较两个采样器,只固定初始噪声就足够公平吗?

这是很有用的控制,但仍要统一模型、条件、输出分辨率、计算预算和评价协议。若其中一个采样器还使用中途随机项,应记录其处理方式,重复多个种子观察分布差异。单个配对样本便于解释变化,不能替代总体质量、覆盖度与时延的统计比较。这两类证据承担的作用不同。

返回当前页说明

相关阅读:Diffusion Models

高斯score可以精确手算

对均值m、方差σ²的高斯,离中心越远意味着什么?

对一维高斯 N(m,σ²),log密度关于x的导数为 −(x−m)/σ²。若 m=0、σ²=4,在x=2处score为−.5;它指向均值,但强度取决于方差。若方差变成1,同一点的score变为−2,说明更窄的分布对偏离中心更敏感。不要只画单位化方向箭头后忘记幅值:步进动态同时依赖方向、强度与时间系数。在多峰分布中,score不是随手挑一个峰的高斯score,而是混合密度的log梯度,不同成分的相对后验权重会共同影响当前位置的方向。若程序输出与手算不符,应先检查方差是否被误当成标准差。

追问:把score箭头归一化后再采样,会不会只改变速度?

通常会改变动态,因为不同位置的相对幅值包含密度信息。只有在明确定义并分析新的时间参数化等条件下,才可能建立特定等价关系,不能任意归一化后宣称仍采样原分布。教学图可以为可视性缩放箭头,但必须标注显示缩放不等于算法实际使用的数值。

返回当前页说明

相关阅读:Diffusion Models

确定性DDIM的一步如何计算?

固定噪声起点后,少一次随机抽样具体改变了哪部分更新?

在一种常用DDIM记号中,先算 x̂_0=(x_t−√(1−ᾱ_t)ε̂)/√ᾱ_t;取η=0后,从t跳到更早的s可写为 x_s=√ᾱ_s x̂_0+√(1−ᾱ_s)ε̂。若沿用x_t=1.3、ᾱ_t=.64、精确预测ε̂=−.5,则x̂_0=2。再取ᾱ_s=.81,得到x_s=.9×2+√.19×(−.5)≈1.5821。它与前面DDPM的随机一步不同,是另一种构造下的确定性更新。只有在预测和系数匹配时,这个计算才有相应意义;一次精确的教学例不等于真实模型始终预测准确。新的时间点只决定重新组合的信号和噪声比例,并不要求在中间插入所有训练时刻。真正使用较少步数时,模型误差与较大的离散跨度会相互影响,需要通过受控比较确认收益和损失,而不能把本例中的精确预测假设带入结果解释。

追问:η=0时为何仍然叫生成,而不只是重建?

在真实生成中,起点是新抽取的噪声,预测干净值由模型给出,并没有已知原图可供重建。固定起点后更新是确定的,但换起点仍可得到另一结果。这里使用已知x0只是为了核对算术,教学计算中的可知量不能被误带到真实采样接口里。实际运行时,所有估计都来自模型预测。

返回当前页说明

相关阅读:Generative Models (part 2) · Denoising Diffusion Implicit Models

用二维向量看懂条件外推

无条件预测(1,0)、条件预测(1,1),g=2得到什么?

取一个纯教学向量例:同一状态与时间下,无条件预测f_u=(1,0),条件预测f_c=(1,1)。两者差值为(0,1),按本课公式g=2得到f_CFG=(1,2)。它不是两向量之间的平均,而是沿条件差值走到条件预测之外。若g=.5,则结果为(1,.5),才位于两者之间。图上的向量代表同一种模型输出量,不能一支当噪声另一支当速度。这个例子只解释几何组合,不证明外推后的方向一定提高真实条件概率,更不声称真实图像空间只有两个语义坐标。

追问:既然沿条件方向走得更远,g越大不是越好?

更远只是在模型预测空间中的几何事实,不等于在真实评价中越好。预测差值可能含误差,过强外推还会改变覆盖、自然度和数值稳定性。正确实验应扫描多个系数、多个随机起点,并同时观察几类指标,而不是挑一张最符合提示的图片来宣布系数越大越有效。

返回当前页说明

相关阅读:Generative Models (part 2)

压缩瓶颈:生成网络不能随意补回丢失的信息

把 latent 压得越小,是否只有速度收益?

当编码器把两个细节不同的图像映射到几乎一样的 latent,解码器就难以仅凭这个输入忠实恢复各自的区别。小文字、细纹理和精确空间关系可能先在压缩阶段受损。因而训练 latent 生成网络前,应先观察 x 与 D(E(x)):它检验表示系统保留了什么,避免把所有失败都归咎于扩散过程。这里的“重建界限”是针对给定信息通路与逐样本忠实度的诊断,而不是宣称重建 FID 永远是生成 FID 的严格下界。解码器可以凭学习到的先验补出逼真细节,但逼真不等于恢复原来的那一笔。源课件用小 KL 权重的 VAE 与判别器训练说明,表示质量本身也涉及多个目标。

追问:解码器能凭经验补细节,为什么还说压缩会形成瓶颈?

因为补出合理细节和识别原始细节是两件事。假设两个原图只在一个字上不同,却被编码成相同 latent,解码器看到的输入完全一样,无法知道这一次原来是哪一个字。它可以根据训练经验猜一个常见结果,甚至让总体图像很逼真,但不能保证逐样本正确。这个论证支持信息恢复的限制,并不直接给出所有感知指标或分布指标的排序。评价时必须先说清希望保真还是希望合理生成。

返回当前页说明

相关阅读:Generative Models (part 2)

Latent 尺度会改变有效信噪比

只是把 latent 乘一个常数,为什么训练难度也可能变?

考虑零均值且信号与噪声独立的一个坐标,令 z_t=a(t)z+b(t)ε,噪声方差为 1。该坐标的信噪比可写成 a(t)² Var(z)/b(t)²。若把 latent 换成 kz,却沿用原来的 a、b,信号方差变成 k² Var(z),同一 t 对应的实际污染程度也随之改变。把 latent 标准化并不是无关紧要的数值装饰,它决定网络在各时间点看到多清晰的数据。不同通道可能还有不同方差,所以单个尺度并不描述全部统计特性。比较两个 pipeline 时,应同时核对编码器输出的缩放、加噪系数以及输入预处理;仅仅确认它们都使用“同一噪声 schedule”还不够。如果信号与噪声并不独立,状态总方差的展开还会出现协方差项;此时不能再用两份独立方差的简单相加解释混合后的总尺度。信号与噪声各自方差的比值也不能单独描述这种相关扰动。

追问:既然网络很强,它不能自己学会适应任意 latent 尺度吗?

在足够数据与合适优化条件下,网络可能适应不同尺度,但这不表示训练条件等价。固定时间采样策略时,尺度变化会重新分配各时间点的信息量,也可能改变目标幅度和梯度大小。有限训练预算下,这些变化会影响哪些难度区间得到充分学习。要比较尺度策略,应明确是否同时调整噪声系数、输入归一化和损失权重,不能将观察到的差异全部解释为网络表达能力。

返回当前页说明

相关阅读:Generative Models (part 2)

一条线性路径:位置是 1.25,速度是 −3

训练样本怎样直接给出速度监督?

取一个刻意简化的一维训练配对:数据 x=2,噪声 z=−1,时间 t=0.25。按本课约定,x_t=(1−0.25)×2+0.25×(−1)=1.25;对 t 求导得到 v=z−x=−3。训练时把位置 1.25 与时间 0.25 交给网络,让预测接近 −3。若预测为 −2,单坐标平方误差为 1。这里固定的是一个监督配对,不是断言网络在这个位置唯一合法的速度永远为 −3;不同配对可能经过同一位置与时间。生成阶段也不会先拿到真正的 x=2 来计算目标,而是只调用已学会的速度场。这个数值例子分清了构造训练标签与实际生成时可用信息的差别。对固定端点,线性路径的导数不随时间改变;对整个数据分布,网络需要学习的条件平均速度却通常会随时间改变。这两句话指向不同对象。

追问:既然速度就是 z−x,生成时为什么还要神经网络?

在训练中我们主动抽取数据 x 与噪声 z,所以可以直接计算监督速度。生成新样本时,我们只有随机初态和当前状态,并不知道最终应该到达哪个真实数据点,因此不能直接套用 z−x。网络的作用是从许多监督配对中学习当前位置与时间对应的平均运动规则。这个规则产生新的传输轨迹,而不是要求推理时找到并取出训练时那一对端点。

返回当前页说明

相关阅读:Generative Models (part 2)

反向 Euler:负步长乘负速度,位置向正方向走

从 t 向更小的时间走,更新式应当加还是减?

设某一步当前位置为 x=0,网络给出的速度为 v=−3,时间从 t 降到 t−0.1,因此 Δt=−0.1。Euler 更新为 x_new=0+(−0.1)×(−3)=0.3。负速度描述随时间增加的变化,负步长则让我们沿生成方向反向行进,两者并不冲突。这个计算把该步速度视为常数;真实场通常会随位置与时间变化,因此走完整个步长后才重新查询会产生离散化误差。在足够光滑的常微分方程下,Euler 单步局部截断误差通常为 O(Δt²),固定区间的全局误差为 O(|Δt|)。这些阶数不包含网络拟合误差,也不保证图像评价指标按相同速度改善。因此测试求解器时,最好先用已知解析解的速度场确认实现,再把同样代码接到学习得到的网络上,避免符号错误被模型误差掩盖。

追问:多走几步一定能得到更好的图片吗?

更多且合理安排的步数通常能降低某个既定连续速度场的数值积分误差,但这个速度场本身可能没有学准。即使数值解更接近该场的理想轨迹,也不等于更接近真实数据分布,更不能保证每张图的人类偏好单调增加。比较步数时应固定模型、初始噪声和条件,并分别报告调用量、耗时及质量;若改变了求解器或引导强度,也需要单独记录这些变化。

返回当前页说明

相关阅读:Generative Models (part 2) · Generative Models: Diffusion and Flows

非均匀时间采样:把训练预算放到哪里

每个噪声水平都抽到一样多,是否就是最公平的训练?

有些时间区域的预测更容易,有些区域包含更多歧义。源课件用中间噪声区间说明为何可以采用非均匀时间抽样,例如将高斯变量经过 sigmoid 得到 logit-normal 时间。一般训练目标可写成 ∫p(t)w(t)E[ℓ|t]dt,因此抽样密度与损失权重共同决定各区间的贡献。如果为了降低估计方差改用 q(t) 抽样,却希望保留原来的目标,需要在 q 支持覆盖目标区域的条件下使用 p(t)/q(t) 的重要性校正;若不校正,就是有意改变时间权重。哪种设计更好要用有限训练预算下的结果检验。“中间最难”是具体路径与数据下的教学直觉,不能当成所有设置的普遍定理。

追问:我多抽困难时间点,再把 loss 简单平均,会发生什么?

这会让困难区间在期望损失中占更大比例,所以通常不仅改变计算分配,也改变了优化目标。这样做可以是有意的设计,不一定有问题;但应明确说明。如果你的目的只是用更有效的抽样估计原来的均匀时间目标,就需要合适的重要性权重,同时留意权重可能带来的方差。判断策略优劣必须结合相同训练预算下的生成指标,而不能仅看训练 loss 是否变小。

返回当前页说明

相关阅读:Generative Models (part 2) · Generative Models: Diffusion and Flows

质量—成本前沿:NFE、耗时与 FID 各回答什么

两次网络调用一定比四次更快、更好吗?

NFE 统计网络或场的评估次数,有助于说明采样器需要多少次查询,但每次查询的模型大小、分辨率、条件分支、批量与硬件不同,所以 NFE 不能直接替代墙钟时间。FID 则在指定特征空间中比较两组样本分布的统计量,受特征提取器、样本量和预处理影响,也不能单独证明文本条件服从性或每类内容的覆盖。较合理的比较是形成质量—成本点集:固定评估协议,报告延迟或吞吐、显存、NFE,并结合分布质量、条件正确性与人工检查。若一个配置在关心的成本和质量上均不劣且至少一项更优,才在这些维度上支配另一个;不存在脱离需求的唯一冠军。

追问:一个模型 FID 更低、NFE 更少,我可以直接选它吗?

这是有利证据,但还不足以替代任务判断。你需要确认两者在相同数据与预处理协议下计算 FID,确认 NFE 的计数口径一致,并实际测量目标硬件上的延迟。若任务是按文字生成,还要检查条件服从与失败类型;若需要多样性,还要看覆盖而非只看平均质量。只有当这些结果覆盖你的实际目标,且训练或部署成本可接受时,指标优势才构成选择依据。

返回当前页说明

相关阅读:Consistency Models · Generative Models (part 2) · Heusel et al. · TTUR / Fréchet Inception Distance