最近我把一套视频与音频联合生成的蒸馏方案整理并开源了,项目名叫 DMAD,核心目标一句话就能说清:把 H3 这个原本要靠几十步采样才能出片的视频基座模型,直接压到 4 步推理,而且生成的视频自带原生音频,不需要再走一遍 TTS 或者后期音画合成。这个工作我断断续续做了将近两个季度,中间踩了不少坑,尤其是"4 步"这个数字,绝对不是什么体面话,是反复试出来的上限和下限之间一个很微妙的位置。
这几个月我观察到,视频生成领域里大家都在往"少步采样"方向挤,但真正把音频和视频作为一个整体去蒸馏的方案还是太少。多数项目是先做视频蒸馏,音频靠外挂模型补,出来的东西两条线各管各的,对口型和环境音就只能碰运气。DMAD 不一样的地方在于,它在设计蒸馏目标时就把跨模态同步这件事作为一个显式约束放进了 loss 里,最后 4 步采样同时出画面和声音。下面我把整体方案、训练细节和调优过程中踩过的坑都摊开讲。
1. 为什么要折腾"蒸馏到 4 步"这一件事
1.1 多步采样到底贵在哪
H3 这类视频扩散模型,训练阶段用的是连续的噪声到数据的映射,可以理解为模型学会了一条从高斯噪声到视频帧序列再加音频波形的"路径"。采样时如果步数太少,模型没机会把这条路径走完整,画面会糊、动作会闪,声音更是没法听。所以最初的推理配置往往需要 30 到 50 步 DPM-Solver,才能达到能用的质量。
成本算一下就清楚了。50 步采样,每一步都要过一遍几十 B 参数的模型,加上视频帧序列和音频波形的序列长度特别长,注意力计算量会暴涨。我拿单张 A100 80G 做过基准,一个 5 秒 720p 视频片段,光推理就要接近 20 秒。这个速度放到产品里根本没法用,用户点一下生成,喝杯水回来还在转圈。更别说后面还要做音频后期、视频剪辑,整个链路算下来,"生成得慢"成了最劝退的瓶颈。
1.2 4 步不是拍脑袋定的,是算出来的
很多人以为蒸馏就是把步数减到最小,最好 1 步出结果,但实际采样步数并不是越低越好。步数太少,学生模型要在一个极小的轨迹里强行模拟教师的整个生成过程,表达能力会不够,容易出现画面细节丢失、面部僵硬这类问题;步数太多,又失去了加速的意义。
我做过一组消融:1 步、2 步、4 步、8 步各训了一版,对比 FVD 和音画同步分数。1 步版本画面基本是"能看轮廓但没细节",音频有噪声感;2 步版细节稍微回来一点,但动作连贯性还是不满意;4 步是个很明显的拐点,画面质量跟 8 步版差距缩到很小,推理时间却只多了一点。如果只看推理成本,4 步比 50 步便宜了十倍多,这是一个工程上非常划算的甜点位置。
1.3 蒸馏任务和传统加速的区别
大家可能接触过用蒸馏给图像生成模型加速,但视频蒸馏的难度完全不是一个量级。图像只有空间维度,视频多了一条时间轴,音频又多了时间上的波形约束,三者之间还要保持同步。DMAD 做的不是简单地把每一步的输出拿来对齐,而是把整个"多步轨迹"蒸馏成"少步跳跃"。
这个思路其实可以类比成教人走一条熟悉的路:教师模型知道每个路口怎么拐,走了 50 步;学生模型可以直接从起点跳到终点附近,再补几步微调。难点在于"跳过中间路口"时不能把路边的标志看漏,映射到模型里就是你得保证中间特征层的信息没有被老师"带偏"。所以我特别关注了中间层对齐,不只是最终输出对齐。
2. DMAD 的蒸馏方案长什么样
2.1 整体训练框架:三路 loss 一起使劲
DMAD 的训练框架可以概括成三部分叠加:由教师生成的中间特征做回归约束,用对抗判别器做质量兜底,再加上一个跨模态同步对齐项。
第一路是最常见的 logit 蒸馏,让学生的输出分布贴近教师。第二路是 patch-level 的对抗 loss,判别器去区分"教师生成结果"和"学生生成结果",逼着学生把纹理细节补上。第三路是我自己加得比较重的部分:把视频帧的特征和音频波形特征投影到同一个隐空间里算相似度,如果画面里有人在说话,那音频波形就应该对应到对应的唇部动作,否则相似度就会很低,这个信号会作为梯度传回模型。
三路 loss 不是说简单加在一起就行。我一开始直接各乘一个系数相加,结果质量不稳定。后来改成两阶段策略:前期以第一路和第二路为主,让模型先"学会大致怎么生成";等训练到中期再把第三路权重调大,集中精力修正音画错位。这个节奏上的调整,最终让同步分数涨了很多。
2.2 学生模型初始化:从教师截断还是随机
学生模型的结构直接复用教师的 backbone,只是把采样步数相关的模块做了一些改动。第一种做法是拿教师权重直接初始化学生,好处是模型一上来就有比较强的先验,不用从零开始学;坏处是学生可能会过度依赖教师的多步路径,少步推理时反而放不开手脚。
我最后选择的是"教师权重初始化 + 前若干步完全不更新某些注意力层"的做法。具体来说,前面几轮训练里我会冻结跨模态注意力模块,只更新后面的输出层,让模型先稳定住基本生成能力,再逐步放开。实验结果对比随机初始化,最终生成质量在 CLIP Score 上高了 3 个点左右,且收敛速度快了很多。
2.3 边界条件与时间步设计的细节
蒸馏少步模型时,时间步的采样策略很容易被忽视。教师模型在多步采样时,每个时间步对应一个噪声水平,但学生模型只有 4 步,如果直接教它预测所有时间步的噪声,信息量过载,训练不稳定。
我的做法是把时间步离散成 4 个区间,每个区间内用均匀采样,让学生学会看当前处于哪个区间,并且只负责把该区间的噪声清干净。同时在训练中给时间步加一个很小的随机偏移,模拟推理时可能遇到的偏差,这样模型不会死记硬背训练时的固定步长。这个"抖动"最终让推理时的鲁棒性提高不少,即使换一个采样器和步长配置也能保持画质稳定。
3. 视频和"原生音频"联合生成,难在哪
3.1 原生音频和 pipeline 式 TTS 的本质区别
先解释一下我理解的"原生音频":模型直接生成音频波形或对应的离散音频编码,而不是先用文本转语音合成一段人声,再根据视频里对象的位置做空间化后期。前者是"从一开始声音就长在画面里",后者是"把两块布拼在一起再缝线"。
原生音频的好处非常多。人声的口型节奏、环境音的反射感、风声雨声和画面里的树叶晃动,天然是耦合的。如果是两套管线分别处理,这些关系很难对得上。缺点是模型要去学音频的底层规律,数据的需求量和训练难度都会明显上升。DMAD 坚持走原生音频路线,是因为我测试过外挂 TTS 方案,遇到复杂场景基本没法看。
3.2 音画同步不只是对齐时间轴
最初我天真地以为,视频和音频共用一条时间轴就能自然同步,事实证明不是这样。模型可以把音频整体平移,让声音始终比画面晚一帧,这个误差在损失函数上看不出太大差异,但人眼非常敏感。光靠时间轴标签是训不出"该对齐"的效果的。
所以我额外设计了一个同步判别器,输入是视频帧小片段和对应音频片段在时间维上的匹配状态,正样本是原始数据里的真匹配,负样本把音频平移若干帧,让模型去学"什么叫做对的同步关系"。这个做法把口型和声音的错位问题基本解决。实际生成时,如果画面里有人在说话,嘴唇动作和声音的对应关系明显比不加这个模块时自然得多。
3.3 联合 VAE 和共享 position encoding
视频帧和音频波形的数据结构差异很大,一个来自图像空间,一个来自一维音频空间。DMAD 的做法是先各自过独立的编码器,把两种模态都映射到同一维度的特征序列里,再接上共享的位置编码。这个共享位置编码让模型在注意力计算时能"看见"视频帧和音频片段之间的相对位置关系。
比如一段画面里有火车从远处开过来,视频特征在第 30 帧附近,音频特征在波形第 1.5 秒附近,模型通过位置编码知道它们属于同一个时间点,从而在生成特征时互相照应。这个设计听起来简单,但对显存的压力不小,因为视频加音频的 token 数经常超过百万级别,我后端用了一些稀疏注意力的手段来降低计算量。
4. 训练实操:从数据准备到评估闭环
4.1 数据清洗:既要画面干净也要声音干净
训练这种联合模型,数据是最大的成本。我主要用了公开的短视频片段和部分自采的音频视频对,总量大概一个中型规模的数据集。清洗比想象的更花时间,因为画面和声音必须在同一个视频里都能用,有一侧不合格就要丢。
具体有这几个规则:画面模糊的丢,画面切换过快没连贯性的丢,音频里有底噪过大或削波的丢,说话内容和字幕不一致的丢,还有含有明显可见品牌标识的片段也直接排除。清洗完以后数据量缩到原始的三分之一左右,但训练出来效果反而更好,说明脏数据对模型的污染远比想象中严重。
4.2 关键超参与训练节奏
训练时主要的超参组合我整理了一下,方便复现时对照:
- 帧长切成约 96 帧,约 5 秒,分辨率按 720p 训练,推理时可以缩放
- 音频采样率 24kHz,波形片段和视频片段时间长度严格一致
- batch size 设置为 16,梯度累积 4 步,显存控制在单卡 80G 左右
- 学习率从 2e-5 开始,前 1000 步做 warmup,之后用余弦衰减到 2e-6
- 蒸馏温度设为 1.0,不做额外平滑,避免学生学到过于模糊的分布
- 对抗 loss 权重 0.4,同步 loss 权重在中期从 0.2 提到 0.8
训练节奏上,我会在每 2000 步抽一次样,人工观察几段生成的视频和音频是否崩。这个习惯帮我提前发现了好几次平安度过早期风险。
4.3 评估指标、评测集和自己看的样本
自动评估我用三个维度:视频质量用 FVD 和 CLIP Score,音频质量用音频领域的 Frechet Audio Distance,音画同步用我自己写的一个对齐分数,计算视频中嘴唇区域特征和音频能量变化的互信息,越高越好。
但说实话,真正能发现问题的场景还是人眼。我固定了 20 个评测 prompt,每次训练迭代都生成同一批内容,对比不同版本的差异。这个做法比看一堆指标有用得多,因为很多问题,比如画面闪动、音频发闷,很难用一个指标量化。我会把每版生成结果存下来,按时间顺序播放对比,直观感受模型在往哪个方向走。
5. 我踩过的坑和调优记录
5.1 早期训练崩掉的几个典型原因
训练前期最常遇到 NaN 和剧烈 loss 震荡。NaN 大概率出在混合精度下的梯度溢出,尤其是对抗 loss 参与之后,判别器梯度绝对值会突然变大。解决办法是给判别器加梯度裁剪,同时把梯度累积步数调低。loss 震荡的主要原因是时间步抖动太大,导致模型在不同噪声水平之间来回切换。我最终把抖动范围缩到步长的一半以内,情况就稳定了。
还有一个看似离谱但很常见的坑:数据加载时视频和音频的时长没严格对齐,零点几秒的偏差就足以让同步 loss 训练方向混乱。我后来在 DataLoader 里直接按帧索引和音频采样点一一对应,不在管线上做任何随机裁剪,彻底杜绝了这个隐患。
5.2 对抗 loss 带来的画面过饱和
对抗训练刚开始时,画面颜色会变得非常艳,尤其是大面积天空和肤色,看起来像过度调过滤镜。这是因为判别器对高频纹理更敏感,模型学会了"使劲提高对比度"来骗过判别器,但真实感反而下降。
解决方式是在对抗 loss 之外加了一个感知损失,用预训练好的 VGG 网络提取图像特征,让生成图像和教师输出的特征在感知层面尽量接近。感知损失不关心像素级差异,关注的是"看起来像不像同一种内容",可以有效压制过饱和问题。加了之后,画面饱和度恢复到了正常水平,同时也保留住了细节纹理。
5.3 音频对不齐:从"听起来还行"到"对得上"
我遇到过一种很隐蔽的问题:音频单独听起来很真实,脚步声也自然,但跟画面里人的动作就是快半拍或者慢半拍。单独听音频、单独看视频都觉得没问题,合在一起就特别难受。
后来我抓了很多样本逐帧对比,发现问题出在模型的生成过程中,声音的"事件时刻"比画面特征"提前"出来了。这不是简单的后处理能修好的,必须在训练时显式建模事件边界。我于是引入了一个音频事件对齐损失,先检测音频里的瞬态事件,再和视频帧中的运动变化做匹配,强迫模型的隐空间里两者共享同一套时间参考系。调完这个之后,拍手、关门、脚步这些瞬态声音都能和画面动作准确对上。
6. 开源部署与推理优化
6.1 仓库结构和模型卡的坑
开源仓库我重点整理了几件事:训练代码、推理脚本、模型权重、以及一组评测生成样本。模型卡里的描述我特意写得比一般仓库更详细,把训练数据来源、清洗规则、预期使用范围和已知限制都列出来了,这样可以减少很多无谓的 issue。
有朋友反馈说照着模型卡跑推理,发现显存占用跟描述不一致,后来排查发现是不同的视频长度对显存影响很大,越长越吃显存。我在模型卡里补了一张不同帧数对应的显存表,并且把自动 chunk 推理的逻辑并了进去,这样运行单段长视频时,会被切成分段处理,显存峰值就不会顶到天花板。
6.2 4 步推理的实际速度和显存占用
我们在一张 A100 80G 上实测,4 步生成 5 秒 720p 视频加音频,大约 2.8 秒完成。对比 50 步采样版本,后者需要约 18 秒,加速比接近 6.5 倍。如果是 1080p 分辨率,4 步需要约 5.5 秒,因为序列长度变长,注意力计算量涨了上去。显存方面,720p 生成大约占用 28G,1080p 占用 52G,所以推理时如果用消费级显卡,建议先用 720p 跑。
速度测试我分别跑了纯 PyTorch 版和 TensorRT 优化版,后者把采样环节的算子做了融合,4 步推理还能再快 20% 左右。但 TensorRT 版对输入分辨率灵活性限制较大,目前只适配固定尺寸。
6.3 与完整多步模型以及加速方案的对比
这套蒸馏相比直接用磁盘缓存加速或者模型剪枝,最大的优势是效果退化很小。我把 4 步版和 50 步教师版放在同一批 prompt 上生成,用人工盲测对比,约 60% 的人认为两者"差别不大",25% 的人反而认为 4 步版在某些场景下更干净。
如果跟市面上常见的 LCM 类加速方案相比,DMAD 主要赢在跨模态一致性上。LCM 类方案更多是针对单模态扩散模型的,直接把类似策略套到音视频联合生成上,容易变成画面出来了音频是糊的,因为音频和视频的采样难度差异太大。DMAD 把音频特有的波形约束和同步约束都揉进了蒸馏目标,整体表现稳定得多。
我从这个项目里最直观的感受是,少步视频生成不是一个纯算法问题,它更像一个系统工程。数据、训练节奏、评估方式,每一个环节都会影响最终能不能在 4 步里稳定输出。后面这个仓库我还会持续维护,把更多消融实验和采样器适配结果放上来,有新的进展再和大家同步。