共计 1308 个字符,预计需要花费 4 分钟才能阅读完成。
物理直觉:扩散模型像什么?
想象把一滴墨水滴进清水杯里——最初墨水集中在一个点(清晰图像),随着时间推移逐渐扩散成模糊的云雾状(噪声)。扩散模型的核心思想就是 学习如何逆转这个过程:我们教神经网络观察那杯浑浊的水,一步步还原出最初的墨水痕迹。
三阶段实现拆解
1. 前向扩散:有计划地搞破坏
前向过程就像定时往图像里加胡椒粉,关键是要 控制每次撒的量。这里用余弦调度(比线性更稳定)计算 β_t:
def cosine_beta_schedule(timesteps, s=0.008):
"""余弦噪声调度,s 控制平滑度"""
steps = timesteps + 1
x = torch.linspace(0, timesteps, steps)
alphas_cumprod = torch.cos(((x / timesteps) + s) / (1 + s) * math.pi * 0.5) ** 2
alphas_cumprod = alphas_cumprod / alphas_cumprod[0]
betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1])
return torch.clip(betas, 0, 0.999)
2. 反向去噪:UNet 的侦探工作
UNet 的任务是预测噪声——就像侦探从模糊照片里找出哪些是原始内容,哪些是后来添加的干扰。关键设计:
- 残差连接:像搭建跨层桥梁,防止深层网络遗忘早期特征
- 注意力机制:让网络知道图像不同区域的关系(比如狗鼻子和眼睛应该协调)

3. 采样:从混沌中创造秩序
采样时我们带着 UNet 玩「大家来找茬」游戏:
- 先生成一张纯噪声图片(就像电视雪花屏)
- 让 UNet 指出噪声在哪里
- 擦掉部分噪声,重复直到清晰
def sample(model, image_size, timesteps=1000):
"""从噪声逐步生成图像"""
x = torch.randn(1, 3, image_size, image_size) # 初始噪声
for t in reversed(range(timesteps)):
noise_pred = model(x, t) # UNet 预测噪声
x = remove_noise(x, noise_pred, t) # 根据预测去噪
return x
避坑实战手册
硬件限制应对
- 梯度累积:当 GPU 放不下大 batch 时
for _ in range(accum_steps): loss.backward() # 不立即更新,累积梯度 optimizer.step() # 累积够再更新
参数调节经验
| 参数 | 推荐值 | 作用 |
|---|---|---|
| 学习率 | 1e-4 ~ 3e-4 | 太大导致震荡,太小收敛慢 |
| batch size | 32 ~ 128 | 显存不足时可减小并配合梯度累积 |
常见问题解决
- 生成碎片化:尝试调大 UNet 通道数
- 颜色偏差:检查数据标准化是否一致
- 重复模式:增加 dropout 或噪声多样性
思考与延伸
如果改变噪声添加策略(比如前期加噪快后期慢),会对生成效果产生什么影响?推荐阅读论文《Improved Denoising Diffusion Probabilistic Models》探索进阶技巧。
关键收获:扩散模型本质是学习「噪声的规律」,掌握这点就抓住了核心。代码完整版已上传 GitHub(见文末链接),包含猫狗图像生成实战案例。
正文完
发表至: 未分类
近一天内
