Skip to main content

06 - 画:图像视频怎么被生成

你的语音合成服务跑得好好的:并发 16,吞吐稳定,延迟也能接受。

产品说想换个音质更好的模型。你换了。同样的卡、同样的并发,吞吐掉了一半多

你按老经验排查:

  • 加大批处理?完全没用,吞吐一点没动。
  • 看显存?一动不动 —— 不像以前那样随着生成越用越多。
  • 踢一个请求出去腾地方?踢了等于白算,它没有中间状态可以留。

三条经验全部失灵。原因是新模型的第二段不是「一次算完」的声码器,而是一类要反复算几十轮的模型 —— 它叫扩散(diffusion)。

而你那套调度经验,是照着自回归攒出来的。

这一篇只回答两个问题
  1. 扩散在干什么,为什么值得多算三十遍?
  2. 你的哪些经验会在它面前失效?

第 2 个才是这一篇存在的理由。你不做图像生成也躲不开它 —— 语音合成、音乐生成的后半段现在大量用扩散。

一、它在干什么

先看它到底多算了什么。

一个普通的声码器:码本进去,波形出来,一次前向搞定。

一个扩散模型:先凭空生成一片纯随机噪声(播出来就是沙沙声),然后拿一个网络反复处理这片噪声,处理二三十轮,每一轮都跑一遍完整的网络,最后才变成能听的声音。

第一反应肯定是:一次算完的事,为什么要算三十次?

训练与推理是同一条路的两个方向训练 →干净数据加一点噪再加一点纯噪声网络学的是「给一个带噪样本,说出里面掺了多少噪」推理 ←随机噪声去一点噪再去一点成品每一个箭头都是一次完整的网络前向为什么这样更好学「从纯噪声一步跳到成品」是极难的映射;「把一个稍微糊一点的图变清楚一点」是简单任务。扩散把难题拆成了几十上百个易题。代价直接写在推理账单上拆成 N 个易题,推理就要跑 N 次网络。自回归生成 100 个 token跑 100 次,但每次只算一个位置;扩散每次都算整张图的全部位置。这条差异贯穿全篇:自回归的成本随「要生成多少」增长,扩散的成本随「要多少步」增长,而后者跟输出长度无关。
「成本与输出长度无关」是扩散服务最反直觉的性质:生成一张 512×512 的图和一张 1024×1024 的图,步数一样,差别只在每步的计算量。这让它的延迟高度可预测,也让它的批处理逻辑跟 LLM 完全不同。

答案在于这样更好学

「从一片噪声直接跳到成品」是个极难的映射,模型学不好。但「把一个有点糊的东西变清楚一点」是个简单任务 —— 扩散把一个难题拆成了三十个易题。

代价就写在推理账单上:拆成三十个易题,推理时就要跑三十遍网络。

它跟自回归的根本差别在这里:自回归是「一次定一小块,能看见前面定好的全部」,按位置或时间排序;扩散是所有位置一起从糊到清楚,按清晰度排序。文字和音频有天然的先后,适合自回归;一张图的左上角和右下角谁在前?没有答案,所以图像归扩散。

二、成本从哪来:步数

既然要跑 N 遍,N 是多少就直接等于成本。这十年扩散领域的进展,几乎全在回答同一个问题:能不能少跑几步而不掉质量。

同一个模型家族,采样步数的四次下降(横条长度按步数取对数感受)DDPM · 随机去噪约 1000 步 · 每步都要采一次随机数,路径弯绕DDIM · 确定性求解20~50 步 · 去掉随机项后可以大步跨越,同一噪声给同一结果流匹配 / 整流10~30 步 · 训练时就把噪声到数�据的路径学成近似直线蒸馏后的少步模型1~4 步 · 用多步模型当老师,把一整条轨迹压进几步里流匹配为什么能拉直DDPM 学的是「这一小步该往哪挪」,路径弯弯绕绕;流匹配直接学「从这个噪声到那个样本的直线速度」,路径越直,大步跨越的误差越小。服务端要记住的两件事① 步数是可配的,是延迟与质量之间最粗但最有效的旋钮② 开了 CFG 的话每步算两遍,实际网络前向次数是步数的两倍
②在估算成本时经常被漏掉。MiniMax Music 3 的声学段配置是「Euler 求解器 30 步、CFG 尺度 1.7」,真实的网络前向次数是 60 次,不是 30 次。看到「20 步出图」的宣传时,先问一句有没有算 CFG。

标题里的流匹配(flow matching)就是这条线上的一步。

它的想法可以这样理解:从噪声到成品是一条路径。早期方法学的是「这一小步该往哪挪」,路径弯弯绕绕,所以只能小步慢走;流匹配训练时就把这条路径学成近似直线。路越直,一步能跨得越远,需要的步数就越少。

算成本时最容易漏的一项

很多模型会开一个叫 CFG 的东西(无分类器引导),做法是每一步都算两遍再外推,让结果更贴合提示词。

所以「30 步」的真实网络前向次数是 60 次。看到「20 步出图」的宣传时,先问一句有没有算 CFG。

顺带说清楚两个会反复出现的词:

  • 潜空间(latent space):扩散一般不直接在像素上做,太贵。先用一个压缩网络把 1024×1024 的图缩成 128×128 的特征,去噪全在这个小尺寸上做,最后再解回像素。这个压缩后的空间就是潜空间。
  • DiT(Diffusion Transformer):早期扩散的骨干是卷积网络 U-Net,DiT 把它换成了 Transformer——做法跟 02 篇的 ViT 一样,把潜空间切成小块,每块当一个 token。

DiT 这个替换对服务的意义有两条,下一节要用到:没有 KV cache 可复用(注意力是全局双向的,每步都要重算全部 token),以及序列可能极长(一段 5 秒 720p 视频的潜空间可以到数万块)。

三、你的哪些经验会失效

这是这一篇真正要交付的东西。开场那三条失灵的经验,在下表里都能找到出处:

你习惯的(自回归)扩散这边后果
步数不定,模型自己决定何时停步数写死在配置里,请求进来就知道要跑多少延迟反而好预测了,这条是好事
每步计算量很小,瓶颈在指令发得太慢每步都要算整张潜表示优化方向完全相反:那边压启动开销,这边压计算量
显存随生成不断增长恒定容量好规划,但你那套「留抢占余量」的算法用不上
前缀相同就能跳过重算,收益巨大没有前缀这个概念,两个提示词毫无共享前缀缓存那套完全失效
批可以随时进出,谁完事谁走同进同出,一批开跑就锁死加大批处理不但没用,还会让尾延迟翻倍
显存不够就把请求踢出去,之后重算踢出去等于全部白算抢占这条路没了

开场那三条对应的是第 5、3、6 行。

「同进同出」那一行最要命。SGLang 的扩散服务因此把动态批处理做成了默认关闭的开关,而且要求「同模型、同采样形状」才能合批。延迟敏感或者流量形状很杂的场景,官方建议就是别开。

四、它在语音链路里长在哪

你不做图像也会撞上它,因为语音这边现在是两段分工:内容和韵律交给自回归,声音的质感细节交给扩散。

MiniMax Music 3 的实际配置 —— 一个把两种范式串起来的完整例子AR 段 · Qwen3 骨干每步一帧,25 帧/秒一帧 8 个 RVQ 码本深度解码器骨干出 c0,4 层网络顺序解出 c1 到 c7流匹配 DiT每 200 帧一窗,跳 100 帧Euler 30 步,CFG 1.7DAC 类解码器512 倍上采样32 kHz 立体声为什么要这么分工AR 擅长的:长程结构歌词唱到哪、旋律怎么走、什么时候进副歌 —— 这些是有先后依赖的,必须知道前面唱了什么才能决定后面代价:一秒音乐 25 步,成本随长度线性涨扩散擅长的:局部细节同样的码本,还原成波形时的音色纹理、混响、高频质感,没有先后依赖,整块一起生成反而更自然代价:每窗 30 步 × CFG 两遍 = 60 次前向,与窗长无关
这套分工在语音领域已经收敛:CosyVoice 系、Ming-Omni 的 talker(CFM + DiT)、MiniMax Music 3 走的是同一个套路。它的另一个好处是两段可以放在不同的 GPU 上,AR 段吃显存带宽、扩散段吃算力,互不抢占。

五、加速手段全是「赌」

自回归那边的前缀缓存是精确复用 —— 命中之后结果逐位相同,纯赚。扩散这边没有这种好事,它的加速手段全是跳过计算

手段跳什么靠什么判断能跳
Cache-DiT跳 Transformer 里的某些块动态判断,要按模型调参
TeaCache跳整步相邻两步的特征差异小于阈值就复用上一步
Spectrum跳选定的步预测下一步的特征
这三种都在赌同一件事

赌「这一步算了也跟没算差不多」。赌对了省时间,赌错了掉质量。

所以接受扩散加速就是接受输出会变。验收只能靠人听人看,不能拿 04 篇那套「逐位一致」去卡。

六、顺带一提:扩散思路进了语言模型

还有一条更新的路:把扩散的思路搬到离散 token 上,一次确定多个位置,而不是一步一个。sglang-omni 里的 LLaDA2.0-Uni 就是这类。

LLaDA2.0-Uni 的架构:视觉输入经 SigLIP-VQ 编码为离散语义 token,与文本 token 一起在统一的掩码预测骨干里处理,输出经文本反分词器或扩散解码器还原
出处:sglang-omni docs/_static/image/llada2.0_uni_architecture.png。它把理解与生成统一成掩码 token 预测:图像走 SigLIP-VQ 变成离散语义 token,输出既能解回文本,也能过扩散解码器还原成图像。

对推理框架来说,它的意义是又一次打破了「一步一个 token」的假设 —— 调度器的组批、停止条件、缓存复用逻辑都得另做一套。SGLang-Omni 目前对它的处理很保守:默认关掉 CUDA Graph,明确标为实验路径。新范式接入的常规姿势就是这样,先跑通,再谈优化。

下一篇07 - 三种计算为什么合不来。听、想、说三段凑在一起,调度器该听谁的。