你在 Midjourney 里敲下“一只穿着宇航服的橘猫,赛博朋克风格”,几十秒后,一张细节丰富的画就出来了。
你有没有想过一个更玄的问题:它到底是怎么“无中生有”的?一个程序,没有画笔、没有颜料、没有画布,凭什么能凭一句话,就画出一张从未存在过的画?
答案就藏在近几年 AI 绘画(Stable Diffusion、Midjourney、DALL·E 等)背后的核心技术——扩散模型(Diffusion Model)里。它也是 AIGC(AI 生成内容)这波浪潮最硬核的地基之一。今天我们把这件事一次讲透。
一、先给个直觉:从“去噪”说起
要理解扩散模型,别一上来就啃公式,先从一件人人都熟悉的事说起——照片去噪。
你有一张老照片,上面全是噪点和划痕。你希望有一台机器,能把噪点去掉,还原出干净的画面。这个“去噪”能力,直觉上挺好理解。
现在,把这件事反过来想:
如果我有一台“去噪机器”,它能把“带噪的图”还原成“干净的图”。那我能不能反着用——先造一张“纯噪点”(全是随机噪点,什么都没有),然后让这台机器去噪,不就“无中生有”出一张图了吗?
这就是扩散模型的核心直觉。它不神奇,就是“去噪”这个朴素想法的逆向应用。
二、正向:给图片“泼脏水”,一步步变成纯噪点
要训练出那台“去噪机器”,得先有“带噪图”和“干净图”的配对数据。可现实中哪有这种配对?
扩散模型的妙招是:自己“造”带噪图。
具体做法叫正向扩散:
拿一张真实的干净图片,给它加一点点噪点;再加一点点;再加一点点……加很多步之后,图片就被“淹没”了,变成一团纯随机噪点。
用 Mermaid 看这个过程:
flowchart LR
A[真实图片] -->|加一点噪| B[略糊] -->|再加噪| C[更糊] -->|反复加噪| D[纯噪点]
关键点来了:这个“加噪”过程是固定的、人为设计的,不需要学习——每一步加多少噪,是提前定好的公式。你随手就能造出“任意一步的带噪图”。
这样,训练数据就有了:“第 t 步的带噪图” 和 “第 t-1 步的略干净图” 的配对,要多少有多少。
三、反向:训练模型学会“一步步擦掉噪点”
有了配对数据,就可以训练那台“去噪机器”了。它学的是:
给定一张“带噪图”,预测出它“干净一点”的样子(或者直接预测出图里混入的噪点)。
这一步一步地做,就是反向扩散:
flowchart LR
A[纯噪点] -->|去一点噪| B[有轮廓] -->|再去噪| C[清晰些] -->|反复去噪| D[生成的图片]
于是整个扩散模型,就是两个过程的组合:
- 正向加噪(固定,不用学):把图毁成噪点,用来造训练数据;
- 反向去噪(要学,这是模型本体):学会从噪点一步步“擦”出一张图。
一旦模型学会了“去噪”,它就能从一团纯随机噪点出发,一步步“擦”出一张全新的图。 这就是 AI 绘画“无中生有”的全部秘密——它不是“画”出来的,是“擦”出来的。
四、文生图:让文字来“指挥”去噪的方向
但还有一个问题:从纯噪点去噪,能“擦”出无数种可能的图,怎么保证擦出的是“宇航员橘猫”,而不是别的?
答案是:给去噪过程加一个“条件”——文字。
文生图模型会把你的文字(“宇航员橘猫”)编码成一个条件向量,然后在每一步去噪时,把这个条件“喂”给模型,告诉它:
“往这个方向擦——要擦出一只宇航员橘猫,不是别的。”
这样,同一个去噪过程,因为条件不同,就能生成不同的图。文字,就是那个“指挥棒”,引导着噪点一步步被擦成你想要的样子。
用图串一下完整的文生图流程:
flowchart LR
A[文字提示词] --> B[文字编码器<br/>转成条件向量]
C[随机噪点] --> D[去噪网络 U-Net<br/>+ 条件向量]
B --> D
D --> E[擦掉一步噪点]
E -->|重复几十步| F[最终图片]
五、为什么这条路走得通:把难题拆成“一步步”
看到这儿,你可能想问:直接让模型“从噪点一步生成图”不行吗?为什么要费劲地“一步步去噪”?
这恰恰是扩散模型的精妙之处。它借鉴了一个朴素的思想:
一个很难的、一步到位的问题,往往可以拆成一连串“只前进一小步”的简单问题。
直接“从噪点生成一张画”,难如登天。但“把一张略微带噪的图,擦干净一点点”,就简单得多。扩散模型把“生成”这个大难题,拆成了几十上百个“去一点点噪”的小步骤,每一步都简单、可学,串起来却能完成惊人的生成。
这个“把难事拆小步”的思路,和推理模型“先想再答、一步步来”其实是相通的。很多 AI 的突破,都藏在这“不追求一步到位,而是一步步逼近”的智慧里。
六、用 NumPy 感受一下“加噪”到底在干什么
去噪模型太复杂,但“加噪”这一步,用 NumPy 几行代码就能看明白:
import numpy as np
# 模拟一张"图片"(这里用 4x4 的灰度图)
image = np.array([[0.8, 0.2, 0.9, 0.1],
[0.3, 0.7, 0.5, 0.6],
[0.9, 0.4, 0.2, 0.8],
[0.1, 0.5, 0.3, 0.9]])
def add_noise(img, step, total_steps=50):
"""给图片加噪,step 越大,噪点越多"""
noise = np.random.randn(*img.shape) # 标准正态分布的随机噪点
# 噪点的比例随 step 增大而增大,最终逼近纯噪点
alpha = 1.0 - step / total_steps
return alpha * img + (1 - alpha) * noise
print("原图:\n", image.round(2))
print("\n第 10 步加噪:\n", add_noise(image, 10).round(2))
print("\n第 50 步加噪(接近纯噪点):\n", add_noise(image, 50).round(2))
跑一下你会看到,随着 step 增大,图片越来越“糊”,最后几乎看不清原本的样子,只剩一片随机噪点。正向加噪就是这么简单直接;而反向去噪,就是训练一个模型,把这个过程一步步“倒放”回去。
七、写给你的两条建议
- 先抓住“加噪是固定的、去噪是学出来的”这个核心。这是扩散模型和很多生成模型最本质的区别——它不直接学“怎么生成”,而是学“怎么去噪”,然后靠“反向去噪”来生成。
- 理解“文字是条件、是方向”。文生图里,文字不是“命令”,而是“约束去噪方向的引导”。理解了这一点,你就明白为什么同一句提示词、不同随机噪点,会生成不同的图——因为起点(噪点)是随机的,文字只管方向,不管“每一笔落在哪”。
最后说句实在话:扩散模型正在彻底改写“创作”的定义。以前“画一张画”是人的专属能力,现在,它被拆解成了“去噪”这个机器擅长的任务。
技术的进步,很多时候不是“教会机器做人做的事”,而是“把人的事,重新定义成机器能做的事”。