视频生成模型深度解析:从 Video DiT 到 Sora 架构
1. 视频生成的核心挑战
1.1 为什么视频生成比图像生成更难?
视频生成不仅仅是”生成多帧图像”——它要求模型在时间维度上保持一致性和连贯性:
图像生成 vs 视频生成:
图像生成: 输入: 文本描述 输出: 单张 512×512 图像 空间维度: H × W × C 计算量: O(H × W)
视频生成: 输入: 文本描述 (+ 可选首帧/姿态) 输出: T 帧视频,每帧 H × W 空间维度: T × H × W × C 时间维度: 连续的 T 帧必须保持: → 物体外观一致(同一角色每帧看起来相同) → 运动连贯(物体移动轨迹平滑) → 物理合理(重力、光照、遮挡符合物理规律) → 时序自然(动作、表情变化流畅) 计算量: O(T × H × W) — 通常是图像的 10-100 倍1.2 视频生成的四类核心问题
┌──────────────────────────────────────────────────────────────┐│ 视频生成的四大挑战 │├──────────────────────────────────────────────────────────────┤│ ││ 1. 时序一致性 (Temporal Consistency) ││ ───────────────────────────────────────────────────── ││ 问题: 第 1 帧的狗和第 30 帧的狗要看起来是同一只狗 ││ 原因: 扩散模型逐帧独立生成,缺乏全局身份追踪 ││ 解法: 全视频序列联合建模(3D Attention / 视频 VAE) ││ ││ 2. 长程依赖 (Long-range Dependency) ││ ───────────────────────────────────────────────────── ││ 问题: 开场动作要影响结尾的响应 ││ 原因: 帧间距离越大,相关性越弱 ││ 解法: Transformer 的全局 Attention(比 RNN 更适合) ││ ││ 3. 计算可扩展性 (Computational Scalability) ││ ───────────────────────────────────────────────────── ││ 问题: 16 帧 1024×1024 = 1670 万像素 ││ 原因: 直接在像素空间扩散计算量爆炸 ││ 解法: 潜空间视频扩散 (Latent Video Diffusion) ││ ││ 4. 运动建模 (Motion Modeling) ││ ───────────────────────────────────────────────────── ││ 问题: 生成平滑、合理的运动轨迹 ││ 原因: 运动是高度结构化的,难以从静态数据中学习 ││ 解法: 光流监督 / 运动模块 / 物理先验 ││ │└──────────────────────────────────────────────────────────────┘1.3 视频生成模型演进时间线
2019: DDPM 提出 (Ho et al.) — 图像扩散基础奠定 │2020: DDPM 扩展到视频 (Ho et al.) — 首个视频扩散 │ 缺点: 逐帧独立生成,时序一致性差 │2022: LDM / Stable Diffusion — 潜空间扩散图像 │ 缺点: 只支持图像 │2022: VideoLDM (Blattmann et al.) — 首个 Latent Video Diffusion │ 引入视频 VAE,扩展 SD 到视频 │2023: Make-A-Video (Singer et al., Meta) — 无需文本视频的预训练 │ 分离空间(图像扩散)+ 时间(插帧) │2023: Align Your Latents (Zhao et al.) — 高质量视频潜空间 │2024: DiT (Peebles & Xie) — Transformer 替代 UNet │2024: Sora (OpenAI, Feb) — Video DiT,DiT 的视频扩展 │ 关键技术: 时空 patchify + 3D VAE + DiT 主干 │2024: CogVideoX (Tsinghua) — 开源视频 DiT,3D 注意力 │2024: Wan2.1 (ByteDance) — 开源视频 DiT,业界最佳 │2024: Open-Sora (Vision SE) — 开源复现 Sora │2025: Sora 2.0 / Stable Video Diffusion 2 / HunyuanVideo1.4 一句话概括视频扩散
视频扩散模型的核心是在图像扩散的基础上引入时间维度——通过视频 VAE 把 T×H×W×3 的视频压缩到潜空间得到 T_c × H_c × W_c × C_z 的潜变量序列,然后在潜空间执行扩散去噪,最后用视频 VAE 解码器重建视频帧序列。Sora 的关键创新是把 DiT 的 2D patchify 扩展为 3D 时空 patchify,使 Transformer 能同时建模空间关系和时间关系。
2. 视频 VAE:潜空间时空压缩
2.1 为什么需要视频 VAE?
视频 VAE 把视频从像素空间压缩到潜空间,与图像 VAE 的逻辑相同但增加了时间维度:
class VideoVAE: """ 视频 VAE: 压缩视频到低维潜空间。 继承自图像 VAE,增加了时间压缩维度。 """
def __init__(self, spatial_downsample=8, temporal_downsample=4): """ 参数: spatial_downsample: 空间压缩比 (H → H/8) temporal_downsample: 时间压缩比 (T → T/4) """ self.spatial_factor = spatial_downsample # 8 self.temporal_factor = temporal_downsample # 4
def encode(self, video): """ 编码视频到潜空间。
参数: video: (B, T, C, H, W) 原始视频 返回: z: (B, T', C', H', W') 潜变量序列 """ B, T, C, H, W = video.shape
# 空间压缩: H/8, W/8 # 时间压缩: T/4 # 通道扩展: C=3 → C'=16 或 16+16=32 (3D VAE)
z = self.encoder(video) # (B, T/4, 16, H/8, W/8) return z
def decode(self, z): """ 从潜空间解码到视频。
参数: z: (B, T', C', H', W') 潜变量序列 返回: video: (B, T, C, H, W) 重建视频 """ return self.decoder(z)
def compress_ratio(self): """ 压缩比计算。 以 16 帧 512×512 视频为例: """ T, H, W, C = 16, 512, 512, 3
# 像素数量 pixels = T * H * W * C # 16 × 512 × 512 × 3 = 12,582,912
# 潜空间大小 T_z = T // 4 # 4 H_z = H // 8 # 64 W_z = W // 8 # 64 C_z = 16 # 潜通道数
latent_pixels = T_z * H_z * W_z * C_z # 4 × 64 × 64 × 16 = 262,144
ratio = pixels / latent_pixels # ≈ 48 倍(与图像 LDM 相同) return ratio2.2 视频 VAE vs 图像 VAE
def vae_comparison(): """ 视频 VAE vs 图像 VAE。 """ return { "维度": { "图像 VAE": "3D (H, W, C)", "视频 VAE": "5D (T, H, W, C)", }, "编码器": { "图像 VAE": "2D Conv (H, W)", "视频 VAE": "3D Conv (T, H, W) — 同时压缩时空", }, "时间压缩": { "图像 VAE": "无", "视频 VAE": "T/4 或 T/8 — 减少时序冗余", }, "重建质量": { "图像 VAE": "高(静态纹理)", "视频 VAE": "中(时间闪烁是主要问题)", }, "时序一致性": { "图像 VAE": "无(逐帧编码)", "视频 VAE": "高(联合编码)", }, "代表模型": { "图像 VAE": "SD 的 VAE (4 ch latent)", "视频 VAE": "Sora 的 3D VAE, CogVideoX VAE, Wan VAE", }, }2.3 3D VAE 架构
Sora 和主流视频 DiT 使用 3D VAE,同时在时间和空间维度上进行压缩:
class VideoVAE3D(nn.Module): """ 3D 视频 VAE。 用于 Sora、CogVideoX、Wan2.1 等视频 DiT 模型。 """
def __init__(self, in_channels=3, latent_channels=16): super().__init__()
# ============ 3D 编码器 ============ self.encoder = nn.Sequential( # 块 1: T×512×512 → T×256×256 nn.Conv3d(in_channels, 128, kernel_size=3, padding=1), ResBlock3D(128, 128), ResBlock3D(128, 128), nn.Conv3d(128, 128, kernel_size=3, stride=(1, 2, 2), padding=1), # 只压缩空间
# 块 2: T×256×256 → T×128×128 nn.Conv3d(128, 256, kernel_size=3, padding=1), ResBlock3D(256, 256), ResBlock3D(256, 256), nn.Conv3d(256, 256, kernel_size=3, stride=(1, 2, 2), padding=1),
# 块 3: T×128×128 → T×64×64 nn.Conv3d(256, 512, kernel_size=3, padding=1), ResBlock3D(512, 512), ResBlock3D(512, 512), nn.Conv3d(512, 512, kernel_size=3, stride=(1, 2, 2), padding=1),
# 块 4: T×64×64 → T/2×64×64 (时间压缩开始) nn.Conv3d(512, 512, kernel_size=3, stride=(2, 1, 1), padding=1),
# 最终投影到潜空间 nn.GroupNorm(32, 512), nn.SiLU(), nn.Conv3d(512, latent_channels, kernel_size=3, padding=1), )
# ============ 量化/正则化 ============ # 视频 VAE 通常使用 VQ 或 EMA 量化(而非 KL) self.quant_conv = nn.Conv3d(latent_channels, latent_channels, 1)
# ============ 3D 解码器 ============ self.decoder = nn.Sequential( # 潜空间 → T/2×64×64 nn.Conv3d(latent_channels, 512, kernel_size=3, padding=1), ResBlock3D(512, 512),
# 时间上采样: T/2×64×64 → T×64×64 nn.Conv3d(512, 512, kernel_size=4, stride=(2, 1, 1), padding=1),
# 空间上采样: T×64×64 → T×512×512 nn.Conv3d(512, 512, kernel_size=3, padding=1), ResBlock3D(512, 512), ResBlock3D(512, 512), nn.ConvTranspose3d(512, 256, kernel_size=4, stride=(1, 2, 2), padding=1),
nn.Conv3d(256, 256, kernel_size=3, padding=1), ResBlock3D(256, 256), nn.ConvTranspose3d(256, 128, kernel_size=4, stride=(1, 2, 2), padding=1),
nn.Conv3d(128, 128, kernel_size=3, padding=1), nn.Conv3d(128, in_channels, kernel_size=3, padding=1), )
def encode(self, video): """ 参数: video: (B, C, T, H, W) — PyTorch 格式 返回: z: (B, latent_channels, T', H', W') """ return self.encoder(video)
def decode(self, z): return self.decoder(z)
class ResBlock3D(nn.Module): """3D 残差块。""" def __init__(self, in_channels, out_channels): super().__init__() self.norm1 = nn.GroupNorm(32, in_channels) self.conv1 = nn.Conv3d(in_channels, out_channels, 3, padding=1) self.norm2 = nn.GroupNorm(32, out_channels) self.conv2 = nn.Conv3d(out_channels, out_channels, 3, padding=1)
if in_channels != out_channels: self.shortcut = nn.Conv3d(in_channels, out_channels, 1) else: self.shortcut = nn.Identity()
def forward(self, x): h = F.silu(self.norm1(x)) h = self.conv1(h) h = F.silu(self.norm2(h)) h = self.conv2(h) return h + self.shortcut(x)3. 时空 Patchify:Video DiT 的核心
3.1 从 2D Patchify 到 3D Patchify
DiT 的核心是把图像划分为 patches。视频 DiT 把这个思想扩展到时间维度:
DiT (2D 图像): ┌────────────────────────────────────┐ │ 图像: 512×512×3 │ │ ↓ Patchify (p=2, 空间) │ │ 每帧: (512/8)×(512/8) = 64×64 patches │ │ 每 patch: 2×2×3×C = 12C 维度 │ │ Patch 数量: 64×64 = 4096 │ └────────────────────────────────────┘
Video DiT (3D 视频): ┌────────────────────────────────────┐ │ 视频: T×512×512×3 │ │ ↓ Patchify (p_t=2, p_h=2, p_w=2)│ │ 时空: (T/4)×(512/8)×(512/8) │ │ = T_s × 64 × 64 patches │ │ 每 patch: 2×2×2×3×C = 24C 维度 │ │ Patch 数量: T_s × 64 × 64 │ │ (T_s = T/4, 时间压缩 4 倍) │ └────────────────────────────────────┘
关键: Video DiT 把 3D 时空立方体展平成序列 → 输入: (T_s × 64 × 64) 个 tokens → 每个 token 维度: 2×2×2×3×C = 24C → 类似 DiT,通过线性投影到 D 维3.2 时空 Patchify 的 PyTorch 实现
class SpatioTemporalPatchify(nn.Module): """ 时空 Patchify 层。 将视频 (B, C, T, H, W) 转为 tokens 序列 (B, N, D)。 """
def __init__(self, patch_size_t=2, patch_size_h=2, patch_size_w=2, in_channels=16, hidden_size=1024): super().__init__() self.pt = patch_size_t # 时间 patch 大小 self.ph = patch_size_h # 空间 patch 大小 self.pw = patch_size_w # 空间 patch 大小 self.in_channels = in_channels # VAE 潜通道数
# 每个 patch 展平后的维度 patch_dim = in_channels * pt * ph * pw # 16 × 2 × 2 × 2 = 128
# 线性投影到 Transformer 维度 self.proj = nn.Linear(patch_dim, hidden_size)
def forward(self, z): """ 参数: z: (B, C, T_s, H_s, W_s) 潜变量视频 例如: (B, 16, 4, 64, 64) 返回: tokens: (B, N, D) Transformer 输入序列 其中 N = T_s/pt × H_s/ph × W_s/pw """ B, C, T, H, W = z.shape
# 1) 划分 patches # z: (B, C, T, H, W) → (B, T/pt, C, pt, H/ph, W/pw, ph, pw) z = z.view( B, T // self.pt, self.pt, H // self.ph, self.ph, W // self.pw, self.pw, C, ) # 转置: (B, T/pt, H/ph, W/pw, pt, ph, pw, C) z = z.permute(0, 1, 3, 5, 2, 4, 6, 7).contiguous()
# 2) 展平每个 patch: (B, T_s, H_s, W_s, pt×ph×pw×C) N_t = T // self.pt N_h = H // self.ph N_w = W // self.pw z = z.view(B, N_t * N_h * N_w, self.pt * self.ph * self.pw * C)
# 3) 线性投影到 hidden_size tokens = self.proj(z) # (B, N, D)
return tokens
class VideoDiT(nn.Module): """ 视频 DiT。 """
def __init__(self, hidden_size=1024, num_layers=16, num_heads=16, patch_size_t=2, patch_size_h=2, patch_size_w=2, in_channels=16): super().__init__()
# Patchify self.patchify = SpatioTemporalPatchify( patch_size_t, patch_size_h, patch_size_w, in_channels, hidden_size )
# Patch Embedding (与 DiT 相同) num_patches = self.patchify.num_patches self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size))
# Transformer self.transformer = TransformerBlock( hidden_size=hidden_size, num_heads=num_heads, mlp_ratio=4.0, )
# 输出投影 self.proj_out = nn.Linear(hidden_size, patch_size_t * patch_size_h * patch_size_w * in_channels)
# 时间步嵌入 self.time_embed = TimeEmbedding(hidden_size)
def forward(self, z, t, context): """ 参数: z: (B, C, T_s, H_s, W_s) 潜变量视频 t: (B,) 时间步 context: (B, seq_len, d) 文本 embedding """ # 1) Patchify x = self.patchify(z) # (B, N, D)
# 2) 添加位置编码 x = x + self.pos_embed
# 3) 时间步嵌入 t_emb = self.time_embed(t)
# 4) Transformer 前向 (带 AdaLN-Zero) for block in self.transformer.blocks: x = block(x, t_emb, context)
# 5) 逆 Patchify noise_pred = self.proj_out(x) # (B, N, patch_dim)
return noise_pred4. Sora 架构深度解析
4.1 Sora 的核心技术报告
2024 年 2 月,OpenAI 发布 Sora 技术报告。虽然没有公布完整代码和权重,但其核心设计可以从报告中推断:
Sora 的三大核心组件: ┌──────────────────────────────────────────────────────────────┐ │ │ │ 1. Video SpatioTemporal Patchification │ │ ───────────────────────────────────────────────────── │ │ 将视频压缩为时空 patches │ │ • 空间: 16×16 pixel patches │ │ • 时间: 可变长度 (由视频时长决定) │ │ • 每个 patch → 潜向量 (通过视频 VAE) │ │ • 类比 DiT: 将 patch 视为 "visual sentence" │ │ │ │ 2. Video DiT (Diffusion Transformer) │ │ ───────────────────────────────────────────────────── │ │ 用 Transformer 处理时空 patches │ │ • 替代 UNet (之前的方法) │ │ • 更好的 scaling 特性 │ │ • Transformer 的全局 Attention │ │ • 基于 Diffusion (非自回归) │ │ │ │ 3. Versatile World Simulator (视频作为世界模型) │ │ ───────────────────────────────────────────────────── │ │ Sora 不仅是视频生成器 │ │ • 可以作为视频条件的视频编辑 │ │ • 可以做图像到视频 │ │ • 可以做视频扩展 (Video Inpainting) │ │ • 可以做视频到视频风格迁移 │ │ │ └──────────────────────────────────────────────────────────────┘4.2 Sora 的视频生成能力
def sora_capabilities(): """ Sora 支持的视频生成任务。 """ return { "文生视频 (T2V)": { "输入": "一段文本描述", "输出": "最长 60 秒的连贯视频", "示例": "prompt = 'A stylish woman walking...'" }, "图生视频 (I2V)": { "输入": "一张图像 + 文本描述", "输出": "从图像开始延续的视频", "示例": "用 DALL-E 3 生成的图像 → 视频" }, "视频编辑": { "输入": "现有视频 + 文本指令", "输出": "符合指令的修改后视频", "示例": "'让狗戴帽子' → 视频中狗戴帽子" }, "视频扩展 (Inpainting/Outpainting)": { "输入": "部分视频 + 新内容描述", "输出": "扩展后的完整视频", "示例": "给定开头 5 秒,生成后续 55 秒" }, "视频到视频": { "输入": "视频 + 风格描述", "输出": "相同内容但不同风格", "示例": "写实风格 → 动漫风格" }, "动态封面": { "输入": "静态图像", "输出": "包含轻微运动的动态版本", "示例": "DALL-E 图像 → 轻微动画" }, "连接视频": { "输入": "两个视频片段", "输出": "在两片段间平滑过渡的视频", "示例": "视频 A → 自然过渡 → 视频 B" }, }4.3 Sora 的技术限制
def sora_limitations(): """ Sora 的技术限制(来自 OpenAI 报告)。 """ return { "物理交互": { "问题": "复杂物体交互的物理规律不准确", "示例": "杯子倒下时水不一定符合重力", "原因": "模型从视频数据中学习,缺乏物理先验", }, "长程一致性": { "问题": "视频越长,物体身份可能丢失", "示例": "第 30 秒的人物外貌可能与第 1 秒不同", "原因": "即使有 Transformer Attention,长序列仍困难", }, "精确计数": { "问题": "难以准确生成指定数量的事物", "示例": "prompt '5 只狗' → 可能生成 4 或 6 只", "原因": "视频中的数量信息不够精确", }, "空间细节": { "问题": "文字渲染和精细空间布局不准确", "示例": "生成正确文本的招牌困难", "原因": "视频空间分辨率有限", }, "相机轨迹": { "问题": "复杂相机运动可能不连贯", "示例": "360 度旋转镜头可能中途变形", "原因": "3D 一致性难以保证", }, }5. 开源视频 DiT 方案
5.1 CogVideoX:清华团队
CogVideoX 是最早的开源视频 DiT 之一(2024年7月):
def cogvideoX_architecture(): """ CogVideoX 架构特点。 """ return { "团队": "Tsinghua & Zhipu AI", "发布时间": "2024年7月", "模型规模": "5B 参数 (DiT) + 600M (Text Encoder)", "关键创新": "Expert Transformer (专家 Transformer)", "注意力机制": { "方法": "3D Full Attention", "特点": "所有 tokens 之间的时空 Attention", "问题": "O(N^2) 复杂度,N 是时空 tokens 数", }, "文本编码器": "T5Encoder (来自 Google Flan-T5)", "视频 VAE": "自研视频 VAE (4x 空间压缩,4x 时间压缩)", "分辨率": "480×480, 16 帧", "训练数据": "约 10M 文本-视频对", "开源": "权重完全开源,推理代码开源", }
def cogvideoX_transformer(): """ CogVideoX 的 Transformer 结构。 """ def forward(self, x, t, context, attention_mask=None): # x: (B, N, D) — N = T_s × H_s × W_s
# 每个 Transformer Block: # 1) Self-Attention (Q=K=V=x) x = x + self.attn1(self.norm1(x), attention_mask=attention_mask)
# 2) Cross-Attention (Q=x, K=V=context) x = x + self.attn2(self.norm2(x), context, attention_mask=attention_mask)
# 3) FFN x = x + self.mlp(self.norm3(x))
return x5.2 Wan2.1:字节跳动
Wan2.1 是目前开源社区效果最好的视频 DiT:
def wan21_architecture(): """ Wan2.1 架构特点。 """ return { "团队": "ByteDance (字节跳动)", "发布时间": "2025年1月", "模型规模": "14B 参数 (DiT) + 双文本编码器", "关键创新": "Causal 3D Attention (因果 3D 注意力)", "注意力机制": { "方法": "Causal 3D Attention", "特点": "时间维度因果 (只 attend 到之前和当前的帧)", "空间维度": "全空间 Attention", "优势": "比 Full Attention 更高效,适合长视频", }, "文本编码器": "T5-XXL (1.8B) + CLIP ViT-L (300M)", "视频 VAE": "自研 Wan VAE", "分辨率": "480P / 720P / 1080P", "帧数": "最长 5 分钟 (3000 帧 @ 10fps)", "长视频生成": "通过自回归方式生成长视频", "开源": "14B 模型完全开源 (HuggingFace)", "显存需求": "FP16 需要 ~28GB (单卡 A100)", }5.3 Open-Sora:开源复现 Sora
def opensora_architecture(): """ Open-Sora 架构特点。 """ return { "团队": "Vision SE (社区主导)", "发布时间": "2024年3月", "目标": "开源复现 Sora 的完整流程", "关键设计": "ST-DiT (Spatio-Temporal DiT)", "注意力机制": { "方法": "Causal 3D Attention", "空间: 全 Attention", "时间: Causal (只向前看)", }, "视频 VAE": "支持多种 VAE (自研, CogVideoX, Wan)", "特点": "完全开源,民主化视频生成技术", "社区": "活跃的贡献者生态", }5.4 三大开源方案对比
def opensource_comparison(): """ CogVideoX vs Wan2.1 vs Open-Sora 对比。 """ return { "模型规模": { "CogVideoX": "5B 参数", "Wan2.1": "14B 参数", "Open-Sora": "2B-9B (多个版本)", }, "注意力机制": { "CogVideoX": "3D Full Attention (全局)", "Wan2.1": "Causal 3D Attention (时间因果)", "Open-Sora": "Causal 3D Attention", }, "分辨率": { "CogVideoX": "480×480", "Wan2.1": "480P - 1080P", "Open-Sora": "16:9, 9:16, 1:1", }, "帧数": { "CogVideoX": "最多 32 帧", "Wan2.1": "最长 5 分钟", "Open-Sora": "最多 240 帧", }, "视频质量": { "CogVideoX": "★★★☆☆ (早期开源)", "Wan2.1": "★★★★★ (最佳开源)", "Open-Sora": "★★★★☆ (持续改进)", }, "开源程度": { "CogVideoX": "权重 + 代码完全开源", "Wan2.1": "权重 + 代码完全开源", "Open-Sora": "权重 + 代码完全开源", }, }6. Latent Video Diffusion(VideoLDM / LVDM)
6.1 VideoLDM:首个 Latent Video Diffusion
VideoLDM(Blattmann et al., 2022/2023)是首个把 Latent Diffusion 扩展到视频的工作:
VideoLDM 架构: ┌──────────────────────────────────────────────────────────────┐ │ │ │ 输入视频: T × 512 × 512 × 3 │ │ ↓ │ │ 视频 VAE (3D): T × 512 × 512 × 3 │ │ → T/4 × 64 × 64 × 4 (潜空间) │ │ ↓ │ │ 时间层 (Temporal Layers): │ │ 在潜空间添加时间建模层 (3D Conv / Temporal Attention) │ │ ↓ │ │ 空间层 (Spatial Layers): │ │ SD 的 UNet 保持不变 (处理每帧空间) │ │ ↓ │ │ 扩散过程: 在 T/4 × 64 × 64 潜空间执行 DDPM │ │ ↓ │ │ 视频 VAE 解码: 潜变量 → 视频 │ │ │ │ 关键设计: │ │ • 时间层插入 SD UNet 的 ResBlock 之间 │ │ • 时间层在潜空间的 T 维度上建模 │ │ • 冻结空间层(使用预训练 SD 权重) │ │ • 只训练时间层(高效微调) │ │ │ └──────────────────────────────────────────────────────────────┘6.2 VideoLDM 时间层实现
class TemporalLayer(nn.Module): """ VideoLDM 的时间层。 插入到 SD UNet 的 ResBlock 之间。 """ def __init__(self, channels, num_frames): super().__init__() self.channels = channels self.num_frames = num_frames
# 3D 卷积 (时间 + 空间) # 作用: 在时间维度上建模帧间关系 self.temporal_conv = nn.Sequential( nn.Conv3d(channels, channels, kernel_size=3, padding=1), nn.GroupNorm(32, channels), nn.SiLU(), nn.Conv3d(channels, channels, kernel_size=3, padding=1), )
# 残差连接 self.residual_scale = 0.1 # 小权重开始,避免破坏预训练
def forward(self, x): """ 参数: x: (B*T, C, H, W) — UNet 的特征 返回: 融合了时间信息的特征 """ BT, C, H, W = x.shape
# 恢复时间维度: (B*T, C, H, W) → (B, C, T, H, W) B = BT // self.num_frames x = x.view(B, self.num_frames, C, H, W) x = x.permute(0, 2, 1, 3, 4) # (B, C, T, H, W)
# 3D 卷积 (同时处理时间 + 空间) h = self.temporal_conv(x)
# 残差连接 h = x + self.residual_scale * h
# 展平回: (B, C, T, H, W) → (B*T, C, H, W) h = h.permute(0, 2, 1, 3, 4).reshape(BT, C, H, W) return h
class VideoLDMUNet(nn.Module): """ 带时间层的 VideoLDM UNet。 """
def __init__(self, base_unet, num_frames=16): super().__init__() self.unet = base_unet self.num_frames = num_frames
# 在每个 ResBlock 后插入时间层 self.temporal_layers = nn.ModuleList([ TemporalLayer(ch, num_frames) for ch in base_unet.channel_list ])
def forward(self, x_t, t, context): """ 参数: x_t: (B*T, C, H, W) — 展平的时空潜变量 t: (B,) — 时间步(所有帧相同) context: (B, seq_len, d) — 文本 embedding """ # 通过 UNet(处理空间信息) h = self.unet.forward_spatial(x_t, t, context)
# 通过时间层(处理时间信息) for temp_layer in self.temporal_layers: h = temp_layer(h)
return h6.3 LVDM:统一 Latent Video Diffusion
LVDM(Latent Video Diffusion Models, 2022)在 VideoLDM 基础上进一步优化:
def lvdm_improvements(): """ LVDM 相比 VideoLDM 的改进。 """ return { "长视频生成": { "VideoLDM": "固定长度 (如 16 帧)", "LVDM": "通过分层生成支持可变长度", }, "时间建模": { "VideoLDM": "3D Conv(局部时间)", "LVDM": "Temporal Attention(全局时间)", }, "训练效率": { "VideoLDM": "需要逐帧编码", "LVDM": "批量视频编码,更高效", }, "无条件生成": { "VideoLDM": "困难", "LVDM": "通过分层设计改善", }, }7. 关键技术:3D 注意力与因果掩码
7.1 3D 空间-时间注意力
视频 DiT 中的注意力需要在时空三维空间中计算:
def attention_3d(): """ 3D 时空注意力的计算方式。 """
def compute_full_3d_attention(tokens_3d, num_heads=16): """ 全 3D 注意力(所有 tokens 互相关注)。
参数: tokens_3d: (B, T, H, W, D) — 时空 tokens 返回: 输出: (B, T, H, W, D) — 更新后的 tokens """ B, T, H, W, D = tokens_3d.shape N = T * H * W # tokens 总数
# 展平: (B, T, H, W, D) → (B, N, D) x = tokens_3d.view(B, N, D)
# 分头 H_heads = num_heads d_head = D // H_heads
# Q, K, V: (B, N, D) Q = compute_Q(x) # (B, N, D) K = compute_K(x) V = compute_V(x)
# Reshape: (B, N, D) → (B, H, N, d_head) Q = Q.view(B, N, H_heads, d_head).transpose(1, 2) K = K.view(B, N, H_heads, d_head).transpose(1, 2) V = V.view(B, N, H_heads, d_head).transpose(1, 2)
# 注意力: (B, H, N, N) — O(N^2) 复杂度 attn = (Q @ K.transpose(-2, -1)) / (d_head ** 0.5) attn = F.softmax(attn, dim=-1)
# 输出: (B, H, N, d_head) → (B, N, D) out = (attn @ V).transpose(1, 2).reshape(B, N, D) return out.view(B, T, H, W, D)
def compute_causal_3d_attention(tokens_3d, num_heads=16): """ 因果 3D 注意力(时间维度因果)。
参数: tokens_3d: (B, T, H, W, D) 返回: 输出: (B, T, H, W, D) """ B, T, H, W, D = tokens_3d.shape N = T * H * W x = tokens_3d.view(B, N, D)
# Q, K, V (同上) Q, K, V = compute_QKV(x)
# 掩码: 时间维度因果 # 帧 t 只能 attend 到帧 0, 1, ..., t # 不能 attend 到帧 t+1, ..., T-1 mask = torch.zeros(T, T, device=tokens_3d.device) for i in range(T): for j in range(i + 1, T): mask[i, j] = float("-inf")
# 扩展掩码到 (B, H, N, N) mask_3d = mask.unsqueeze(0).unsqueeze(0) # (1, 1, T, T) mask_3d = mask_3d.unsqueeze(-1).unsqueeze(-1) # (1, 1, T, T, 1, 1) mask_3d = mask_3d.expand(B, H_heads, T, T, H * W, H * W)
# 应用掩码 attn = (Q @ K.transpose(-2, -1)) / (d_head ** 0.5) attn = attn + mask_3d # 因果掩码 attn = F.softmax(attn, dim=-1)
return (attn @ V).view(B, T, H, W, D)
return {"full_3d": compute_full_3d_attention, "causal_3d": compute_causal_3d_attention}7.2 注意力复杂度分析
def attention_complexity(): """ 注意力机制的计算复杂度对比。 """ return { "2D Full Attention (DiT, 图像)": { "图像尺寸": "512×512", "Patch 数 (p=2, 压缩8x)": "64×64 = 4096", "复杂度": "O(4096²) = 16.8M", }, "3D Full Attention (视频)": { "视频尺寸": "16×512×512", "Patch 数": "4×64×64 = 16384", "复杂度": "O(16384²) = 268M", }, "3D Causal Attention": { "视频尺寸": "16×512×512", "有效计算": "每帧只 attend 到之前帧", "复杂度": "O(T² × H² × W²) = 约 50M", "节省": "约 5x", }, "Sparse Attention (可选)": { "方法": "空间局部 + 时间稀疏", "复杂度": "O(T × H × W × k²)", "k": "局部窗口大小 (如 7)", }, }8. 训练与推理
8.1 视频 DiT 训练流程
def train_video_dit(video_model, vae, text_encoder, dataloader, optimizer, device, num_frames=16): """ 视频 DiT 训练循环。 """
def forward_batch(videos, prompts): """ 参数: videos: (B, T, C, H, W) 原始视频 prompts: list[str] 文本描述 """ B, T, C, H, W = videos.shape
# ============ 1) 视频 VAE 编码 ============ with torch.no_grad(): # (B, T, C, H, W) → (B, C, T, H, W) (PyTorch 格式) videos = videos.permute(0, 1, 2, 3, 4) z = vae.encode(videos) # (B, C', T', H', W')
# ============ 2) 文本编码 ============ context = text_encoder.encode(prompts) # (B, seq_len, d)
# ============ 3) 加噪 ============ t = torch.randint(0, 1000, (B,), device=device) noise = torch.randn_like(z) alpha_bar = diffusion_schedule(t)
# 对所有帧加相同噪声(时序一致性关键) alpha_bar = alpha_bar.view(B, 1, 1, 1, 1) z_noisy = alpha_bar.sqrt() * z + (1 - alpha_bar).sqrt() * noise
# ============ 4) Patchify ============ noise_pred = video_model(z_noisy, t, context)
# ============ 5) 损失 ============ loss = F.mse_loss(noise_pred, noise)
return loss
# 训练循环 video_model.train() for batch in tqdm(dataloader): videos, prompts = batch["video"].to(device), batch["prompt"]
loss = forward_batch(videos, prompts)
optimizer.zero_grad() loss.backward() optimizer.step()
return loss.item()8.2 视频 DiT 推理流程
@torch.no_grad()def generate_video(video_model, vae, text_encoder, prompt, num_frames=16, num_steps=50, height=512, width=512, guidance_scale=7.5): """ 视频 DiT 推理。
参数: prompt: str 文本描述 num_frames: int 生成帧数 num_steps: int DDPM 步数 guidance_scale: float CFG 强度 """
# 1) 文本编码 text_emb = text_encoder.encode([prompt]) uncond_emb = text_encoder.encode([""])
# 2) 初始化潜空间噪声 T_s = num_frames // 4 # 时间压缩后的帧数 H_s = height // 8 # 空间压缩后的高度 W_s = width // 8 # 空间压缩后的宽度 latent_shape = (1, 16, T_s, H_s, W_s) # (B, C', T', H', W')
z_T = torch.randn(latent_shape, device=device)
# 3) DDIM 采样 timesteps = torch.linspace(999, 0, num_steps, device=device).long()
for i, t in enumerate(tqdm(timesteps)): t_tensor = torch.tensor([t], device=device).expand(1)
# 条件预测 noise_pred_cond = video_model(z_T, t_tensor, text_emb)
# 无条件预测 (CFG) noise_pred_uncond = video_model(z_T, t_tensor, uncond_emb)
# CFG 合并 noise_pred = noise_pred_uncond + \ guidance_scale * (noise_pred_cond - noise_pred_uncond)
# DDIM 去噪步 z_T = ddim_step(z_T, noise_pred, t, timesteps[i - 1] if i > 0 else 0)
# 4) VAE 解码 video = vae.decode(z_T) # (1, C, T', H', W')
# 5) 后处理 video = video.permute(0, 2, 1, 3, 4) # → (1, T, C, H, W) video = video.cpu().numpy()
return video9. 技术组合与应用
9.1 视频生成 + 控制 (Video ControlNet)
def video_controlnet(): """ 视频条件控制。 将 ControlNet 扩展到视频领域。 """ return { "任务": "用姿态/深度/光流控制视频生成", "方法": "在 Video DiT 中加入条件编码器", "条件类型": { "姿态": "OpenPose 骨骼控制角色动作", "深度": "Depth Map 控制空间结构", "光流": "Optical Flow 控制运动轨迹", "边缘": "Edge Map 控制物体轮廓", }, "关键问题": { "时序一致性": "条件也需要在时间上保持一致", "长视频": "长序列条件注入困难", }, "代表工作": "ControlVideo (2023), VideoControlNet (2024)", }9.2 视频生成 + LoRA
def video_lora(): """ 视频 LoRA。 用 LoRA 高效微调视频模型。 """ return { "任务": "训练特定风格/角色的视频 LoRA", "方法": "在 Video DiT 中应用 LoRA", "目标层": { "DiT": "Q, K, V 投影 + FFN", "VAE": "通常不训练", "Text Encoder": "可选 (Q, K, V)", }, "挑战": { "显存": "视频模型更大,LoRA 参数量也更大", "数据": "高质量视频数据稀缺", "时序": "LoRA 需要保持时序一致性", }, }10. 总结
10.1 核心要点
| 维度 | 关键要点 |
|---|---|
| 核心架构 | Video DiT = DiT 的时空扩展(3D patchify + 3D VAE) |
| 视频 VAE | 3D Conv 同时压缩时空 (T/4 × H/8 × W/8) |
| 时空 Patchify | 每个 patch = p_t × p_h × p_w × C_in |
| 注意力机制 | 3D Full Attention(全局)vs Causal 3D(高效) |
| Sora 贡献 | 时空 patchify + DiT 主干 + 多任务视频生成 |
| 开源方案 | CogVideoX(5B)/ Wan2.1(14B,业界最佳)/ Open-Sora |
| VideoLDM | 在 SD UNet 中插入时间层,微调效率高 |
| 计算瓶颈 | Attention O(N²) 是最大挑战,需要稀疏/Causal 优化 |
10.2 技术演进路线
图像扩散 (DDPM, 2020): 像素空间 → O(512²) 计算量 ↓图像潜扩散 (LDM, 2022): 潜空间 48x 压缩 → O(64²) 计算量 ↓VideoLDM (2022-23): + 时间层 (Temporal Layer) → 时序一致性 ↓Video DiT / Sora (2024): + Transformer 替代 UNet → 更好的 scaling + 3D Patchify → 统一时空建模 ↓当前 (2025-26): 长视频生成 (Wan2.1 5 分钟) 高分辨率 (1080P) 开源生态 (CogVideoX, Wan2.1, Open-Sora) 多模态控制 (Video ControlNet)10.3 一句话总结
视频生成扩散模型的核心挑战是在时间维度上保持一致性——Video VAE 把视频压缩到潜空间,Video DiT 用 3D patchify 把时空信息展平为 token 序列,通过 Transformer 的全局 Attention 联合建模空间和时间关系,Sora 的技术报告则揭示了这一范式的巨大潜力:仅凭扩散 Transformer + 时空 patchify + 大规模训练,就能生成最长 60 秒的物理合理视频,引发了 2024 年的视频生成热潮,也催生了 CogVideoX、Wan2.1、Open-Sora 等开源替代方案的快速跟进。
10.4 推荐资源
论文: - Sora (OpenAI, 2024): "Video Generation Models as World Simulators" (技术报告) - VideoLDM (Blattmann et al., 2023): "Align Your Latents: High-Resolution Video Synthesis" - CogVideoX (Zhao et al., 2024): "CogVideoX: Large-Scale Open-Source Video Diffusion Transformer" - Wan2.1 (ByteDance, 2025): 14B 开源视频 DiT
代码: - THUDM/CogVideoX: https://github.com/THUDM/CogVideoX - Wan-AI/Wan2.1: https://github.com/Wan-AI/Wan2.1 - hpcaitech/Open-Sora: https://github.com/hpcaitech/Open-Sora
模型: - HuggingFace: THUDM/CogVideoX-5B - HuggingFace: Wan-AI/Wan2.1-I2V-14B
相关: - Make-A-Video (Meta, 2023): 无文本视频的预训练视频扩散 - LVDM (2022): Latent Video Diffusion Models - I2V (Image-to-Video): Pika, Runway Gen-2, Gen-3文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!

