跳转至
图像与视频生成推理手册 视频 下载 notebook

长视频与一致性:分段、滑窗与自回归

5 秒的视频已经要十万 token,一分钟的视频按同样的方式做就是百万 token——注意力的平方项让"一次生成整条长视频"在任何硬件上都不现实。所以长视频一定是**分段**的:要么把前一段的末尾当下一段的条件,要么用滑动窗口,要么干脆把模型做成自回归的、按块因果生成。分段带来两个新问题:段与段之间的重复计算,以及误差沿着段累积造成的漂移。这一章把三条路的成本算清楚,用一个玩具把漂移演示出来,再看自回归视频模型为什么让"KV Cache"重新出现在扩散推理里——以及它对服务意味着什么。

自测:能答上来就可以跳过本章

  1. 为什么不能把 60 秒的视频当一个序列一次生成?算一下 token 数和注意力成本。
  2. 分段生成怎么保证段与段之间连贯?重叠的代价是什么?
  3. 什么是漂移?为什么分段越多越严重?有哪些缓解办法?
  4. 自回归视频扩散模型是怎么工作的?它和普通的双向扩散模型在推理成本上有什么本质区别?
  5. 自回归视频模型里的 KV Cache 和 LLM 的有什么异同?
自测参考答案(先自己答,再展开对照)
  1. 60 秒 720p 24 fps 约 1440 帧,潜帧 361,token 约 130 万;注意力 \(\propto N^2\),比 5 秒视频贵 300 倍,一步就是百万 TFLOP 量级,显存里的激活也放不下。
  2. 把前一段的最后几帧(经 VAE 编码的潜帧)作为下一段的条件(首帧条件的推广),或者让相邻两段重叠几帧、在重叠区把两段的去噪结果混合。重叠的帧要算两次,重叠越多越连贯、重复计算越多。
  3. 每一段的生成都带一点误差(颜色偏移、细节变化),下一段以它为条件就把误差继承并叠加,几十段之后画面明显变色、主体走样。缓解:锚定首帧或参考图(每段都看得到最初的条件)、定期重锚、噪声重调度、把条件帧的权重调高、训练时模拟推理的分布(Self-Forcing)。
  4. 把视频按时间分成块,块内双向注意力、块间因果:生成第 \(k\) 块时只看前面的块。于是前面块的 K、V 可以缓存,新块的注意力成本只和"新块 × 全部已有块"成正比,整条视频的成本随长度线性增长而不是平方;而且可以流式输出。
  5. 相同:都是把已算过的 K、V 存下来给后面复用,显存随长度增长,需要分页 / 淘汰。不同:视频每个"位置"是一整块(几千上万 token)而不是一个 token;每块内部还要做多步去噪,缓存的是去噪完成后的 K、V;长视频里通常只保留最近若干块(滑动窗口)加锚块,而不是全部历史。

为什么必须分段

def tokens(seconds, fps=24, h=720, w=1280, ft=4, fs=8, p=2):
    frames = int(seconds * fps) + 1
    T = 1 + (frames - 1) // ft
    return T * (h // fs // p) * (w // fs // p)

d, L = 5120, 40
print(f"{'时长':>6} {'token 数':>10} {'一步注意力 PFLOP':>15} {'相对 5 秒':>8}")
base = None
for sec in (5, 10, 20, 60):
    n = tokens(sec)
    attn = L * 4 * n * n * d / 1e15
    base = base or attn
    print(f"{sec:>5}s {n:>10,} {attn:>15.1f} {attn / base:>7.0f}×")
输出
    时长    token 数     一步注意力 PFLOP   相对 5 秒
    5s    111,600            10.2       1×
   10s    219,600            39.5       4×
   20s    435,600           155.4      15×
   60s  1,299,600          1383.6     136×

60 秒是 5 秒的 150 倍——不是慢一点,是不可能。所以长视频一定要切。

三条路

路线 做法 成本 连贯性 代表
分段 + 条件拼接 每段独立生成,前一段末尾几帧当下一段的条件 线性,每段付一次首帧条件 靠条件帧,容易漂移 各家 I2V 模型的"续写"
滑动窗口 / 重叠去噪 窗口沿时间滑动,重叠区混合两边的结果 线性 × (1 + 重叠比例) 更平滑,仍会漂移 FreeNoise、Gen-L-Video
自回归(块因果) 块内双向、块间因果,缓存前面块的 K、V 线性,有 KV Cache 训练时专门对抗漂移 CausVid、Self-Forcing、MAGI-1、LTX 的流式模式
① 分段 + 条件拼接 段 1 段 2 段 3 前一段末尾几帧当下一段的条件;成本线性,靠条件帧,容易漂移(红:条件帧) ② 滑动窗口 / 重叠去噪 段 1 段 2 段 3 窗口沿时间滑动,重叠区混合两边的结果;多付重叠比例的计算,更平滑 ③ 自回归(块因果) 段 1 段 2 段 3 块内双向、块间因果,缓存前面块的 K、V;要专门训练,训练时就对抗漂移(绿箭头:读前面块的 KV) 一次生成的注意力是平方的,分段把它变成线性;真正的代价是漂移——误差沿着段累积,颜色、身份、运动慢慢跑偏
图:长视频的三条路——分段拼接、滑动窗口、自回归

前两条用的是普通的双向扩散模型(不改模型,靠推理流程),第三条要专门训练。先看前两条的成本:

def segmented_cost(seconds, seg_seconds=5, overlap_seconds=1):
    """分段生成的总注意力成本(相对一段 5 秒),重叠的帧算两次"""
    n_seg = max(1, round((seconds - overlap_seconds) / (seg_seconds - overlap_seconds)))
    per_seg = (tokens(seg_seconds) / tokens(5)) ** 2
    return n_seg, n_seg * per_seg

print(f"{'时长':>6} {'一次生成(相对)':>12} {'分 5 秒段/重叠 1 秒':>16} {'段数':>4} {'重叠带来的重复':>12}")
for sec in (5, 10, 20, 60):
    one = (tokens(sec) / tokens(5)) ** 2
    n_seg, seg = segmented_cost(sec)
    no_overlap = sec / 5
    print(f"{sec:>5}s {one:>12.0f}× {seg:>16.1f}× {n_seg:>4} {seg / no_overlap - 1:>11.0%}")
输出
    时长     一次生成(相对)    分 5 秒段/重叠 1 秒   段数      重叠带来的重复
    5s            1×              1.0×    1          0%
   10s            4×              2.0×    2          0%
   20s           15×              5.0×    5         25%
   60s          136×             15.0×   15         25%

分段把平方变成了线性,重叠 1 秒多付两成多的计算。真正的代价不在算力,在漂移。

漂移:误差怎么沿着段累积

每一段的生成都不完美:条件帧经过 VAE 编码再解码有损失,去噪本身有随机性,模型对"自己生成的画面"的分布也不完全熟悉。下一段以它为条件,误差就被继承;几十段之后画面会明显偏色、主体走样。用一个最简单的玩具把它量出来:每一段把上一段的末状态当条件,生成时带一点随机偏移,比较"只看上一段"和"同时锚定首帧"两种做法:

import torch

torch.manual_seed(0)
def generate_segment(cond, anchor, w_anchor, noise=0.08):
    """玩具:新段的状态 = 条件的加权组合 + 一点生成误差(偏移是有方向的,模拟 VAE 往返和模型偏好)"""
    target = (1 - w_anchor) * cond + w_anchor * anchor
    return target + noise * (torch.randn_like(cond) + 0.3)       # +0.3 是系统性偏移:每段都往同一个方向偏一点

first = torch.zeros(16)                                          # 首帧(作为锚)
print(f"{'段数':>4} {'只看上一段:离首帧的距离':>22} {'锚定首帧(权重 0.3)':>16} {'每 5 段重锚一次':>14}")
for mode in range(1):
    chain, anchored, periodic = first.clone(), first.clone(), first.clone()
    for k in range(1, 41):
        chain = generate_segment(chain, first, 0.0)
        anchored = generate_segment(anchored, first, 0.3)
        periodic = generate_segment(periodic, first, 1.0 if k % 5 == 0 else 0.0)
        if k in (1, 4, 5, 9, 10, 19, 20, 39, 40):
            print(f"{k:>4} {chain.norm():>22.2f} {anchored.norm():>16.2f} {periodic.norm():>14.2f}")
输出
  段数           只看上一段:离首帧的距离     锚定首帧(权重 0.3)      每 5 段重锚一次
   1                   0.30             0.37           0.30
   4                   0.71             0.44           0.77
   5                   0.84             0.45           0.29
   9                   1.54             0.60           1.06
  10                   1.77             0.79           0.35
  19                   2.56             0.41           1.06
  20                   2.60             0.63           0.21
  39                   4.69             0.41           1.01
  40                   4.85             0.32           0.24

只看上一段时,距离随段数线性增长(系统性偏移在累加);锚定首帧把它压在一个有界的范围里不再增长;定期重锚在两次重锚之间照样漂(第 4、9、19、39 段),重锚那一段又猛地跳回去(真实视频里就是"每隔几秒画面闪一下")。真实系统里的缓解手段都是这个玩具的变体:

  • 参考图 / 首帧始终在条件里(主体一致性):每一段都能看到最初的锚;
  • 把条件帧的来源做干净:条件用潜变量而不是解码再编码的像素,避免 VAE 往返的损失;
  • 噪声重调度(FreeNoise 一类):相邻窗口共享一部分初始噪声,减少段间的随机差异;
  • 训练时对抗漂移:让模型在训练时就以"自己生成的帧"为条件(Self-Forcing、DMD 式的自回归蒸馏),这是自回归模型那条路的核心。

自回归视频扩散:KV Cache 回来了

把视频按时间分成块(比如每块 4 个潜帧),块内双向注意力、块间因果:生成第 \(k\) 块时只看前面的块。这带来三个推理上的变化:

  1. 前面块的 K、V 可以缓存。生成新块时,新块的 token 对全部已有块做注意力,但已有块的 K、V 不用重算——和 LLM 的 KV Cache 一回事。成本从平方变线性:
def ar_cost(n_blocks, block_tokens, d, L, window=None):
    """自回归生成 n 个块的注意力成本:第 k 块对前面 min(k, window) 个块的 K、V 做注意力(块内也算)"""
    total = 0
    for k in range(1, n_blocks + 1):
        visible = k if window is None else min(k, window)
        total += L * 4 * block_tokens * (visible * block_tokens) * d
    return total

blk = tokens(5) // 5                                             # 每块约 1 秒:Wan 720p 的 1/5
print(f"{'时长':>6} {'双向一次生成':>12} {'自回归(看全部历史)':>16} {'自回归(窗口 10 块)':>16}")
for sec in (5, 20, 60):
    n_blocks = sec
    full = L * 4 * tokens(sec) ** 2 * d
    print(f"{sec:>5}s {full / 1e15:>10.0f} PF {ar_cost(n_blocks, blk, d, L) / 1e15:>14.0f} PF {ar_cost(n_blocks, blk, d, L, 10) / 1e15:>14.0f} PF")
输出
    时长       双向一次生成       自回归(看全部历史)     自回归(窗口 10 块)
    5s         10 PF              6 PF              6 PF
   20s        155 PF             86 PF             63 PF
   60s       1384 PF            747 PF            227 PF
  1. 可以流式输出:第一块生成完就能解码、播放,用户感知的延迟是"第一块"而不是"整条"。配合少步蒸馏(每块 4 步),1.3B 规模的模型在一张 H100 上能做到接近实时(CausVid、Self-Forcing 报告的是每秒十几帧的流式生成)。

  2. 暴露偏差要在训练时解决:模型训练时看到的是真实的历史帧,推理时看到的是自己生成的,分布不一样,误差照样会累积。Self-Forcing 这类方法在训练时就用模型自己生成的块当历史,把推理时的分布直接教给模型——这是自回归路线能走到几十秒而不崩的关键。

和 LLM 的 KV Cache 比

LLM 自回归视频扩散
缓存的单位 一个 token 的 K、V 一整块(几千 token)的 K、V,而且是去噪完成后的
一次前向处理 1 个新 token 1 个新块 × 去噪步数(每步都要对历史做注意力)
历史长度 全部,直到上下文上限 通常只保留最近若干块 + 锚块(滑动窗口),否则显存和成本都线性涨
显存 随序列线性增长,需要分页 同样随块数增长,块大所以更快碰到上限
复用 前缀缓存跨请求复用 同一条视频内复用;跨请求复用只在"续写同一视频"时有

推理系统这边的含义:自回归视频模型是第一类能用上 LLM 服务那套机制的生成模型——分页的 KV 池、按块的调度、流式输出、续写请求的缓存命中(见手写 mini-sglang和分页 KV Cache)。SGLang Diffusion、vLLM-Omni 这些框架把扩散和 LLM 放在同一个引擎里,看中的正是这一点。

服务层的含义

长视频把生成变成了长任务:

  • 流式输出:按段 / 按块解码并返回,用户看到第一段的延迟决定体验;
  • 断点与取消:几分钟的任务要能中途取消(省下后面的算力),最好能从某一段续生成;
  • 资源占用:一条长视频占着一张或几张卡几分钟,调度器要按"预计时长"排队,而不是按请求数;
  • 质量监控:漂移是渐进的,可以在段间做轻量的一致性检测(颜色直方图、参考图相似度),超出阈值就重锚或告警。

这些在生成服务的调度里展开。

怎么讲清楚

讲"长视频怎么生成",先给不可能性:60 秒的 token 是 5 秒的 12 倍、注意力 150 倍,必须分段。再给三条路:分段条件拼接(线性成本,靠条件帧,易漂移)、滑窗重叠(更平滑,多付两成计算)、自回归块因果(线性成本、有 KV Cache、能流式,但要专门训练并解决暴露偏差——Self-Forcing 用自己生成的块当训练历史)。漂移的本质是系统性误差沿段累加,锚定首帧或参考图能把它压成常数。最后点出系统含义:自回归视频模型让分页 KV、块调度、流式输出这套 LLM 服务机制第一次用到了生成模型上。

练习

  1. 把漂移玩具里的系统性偏移从 0.3 改成 0(只有随机误差),只看上一段的距离怎么增长?这说明漂移的两种来源各是什么性质?
参考答案

距离按 \(\sqrt{k}\) 增长(随机游走),比线性慢得多。系统性偏移(VAE 往返的固定损失、模型对某类画面的偏好)线性累加,是漂移的主要来源;随机误差只带来缓慢的扩散。所以缓解漂移首先要消除系统性的偏(条件用潜变量、模型训练时见过自己的输出),其次才是减少随机性。

  1. 自回归生成里,把窗口从 10 块改成 3 块,60 秒的成本降到多少?代价是什么?
参考答案

成本接近 \(n \times 3 \times\) 块成本,比窗口 10 再降约 3 倍。代价是模型只能看到最近 3 秒,更早的内容只能靠锚块或参考图维持——长程一致性(人物离开画面再回来)会变差。实际系统常用"最近几块 + 首块(锚)"的组合。

  1. 一个服务同时跑 5 秒的短视频请求和 60 秒的长视频请求。按"请求数"排队会出什么问题?你会怎么设计?
参考答案

一条 60 秒的请求占卡的时间是短请求的 12 倍以上,按请求数排队会让短请求排在长请求后面等好几分钟。应该按预计耗时(token 数 × 步数)排队并分池:长任务用专门的实例或固定配额,短任务保证低延迟;长任务按段流式返回并支持取消;过载时拒绝或降级(减少段数、降分辨率)长任务而不是让所有人一起慢。

小结

  • 长视频不能一次生成:60 秒的注意力是 5 秒的 150 倍;必须分段,分段把平方变线性,重叠多付两成。
  • 漂移是系统性误差沿段累加,只看上一段时线性增长;锚定首帧 / 参考图把它压成常数,噪声重调度和训练时对抗(Self-Forcing)是进一步的手段。
  • 自回归块因果模型让 KV Cache 回到扩散推理:成本线性、能流式、少步蒸馏后接近实时;但要专门训练解决暴露偏差,历史通常只保留最近几块加锚块。
  • 长视频是长任务:按预计时长排队、流式输出、可取消、段间一致性监控——这是 LLM 服务那套机制在生成模型上的第一次落地。

发现错误或有更好的写法?在 GitHub 上编辑此页提交问题