# Comp-Attn 设计文档 > 基于论文 "Comp-Attn: Present-and-Align Attention for Compositional Video Generation" 的实现总结 ## 概述 Comp-Attn 是一种 **composition-aware cross-attention** 变体,采用 **"Present-and-Align"** 范式,用于解决多主体视频生成中的两个核心问题: | 挑战 | 描述 | 解决方案 | |-----|------|---------| | **Subject Presence** | 并非所有主体都能在视频中呈现 | SCI(条件层面) | | **Inter-subject Relations** | 主体间的交互和空间关系错位 | LAM(注意力层面) | ## 核心组件 ### 1. Subject-aware Condition Interpolation (SCI) SCI 在 **条件编码阶段** 增强每个主体的语义表示,确保所有主体都能被"召回"。 #### 工作原理 ``` ┌─────────────────────────────────────────────────────────────┐ │ 输入: prompt 中的主体 tokens + 独立编码的 anchor embeddings │ ├─────────────────────────────────────────────────────────────┤ │ Step 1: 计算语义显著性 (Saliency) │ │ - 比较 prompt 中主体 token 与 anchor 的余弦相似度 │ │ - 使用 softmax(τ=0.2) 归一化得到显著性分数 │ │ - 低显著性 = 主体在 prompt 上下文中被"淹没" │ ├─────────────────────────────────────────────────────────────┤ │ Step 2: 计算语义差异 (Delta) │ │ - delta_i = anchor_i * N - Σ(anchors) │ │ - 表示每个主体相对于其他主体的独特语义 │ ├─────────────────────────────────────────────────────────────┤ │ Step 3: 自适应插值 │ │ - scale = ω * (1 - saliency) │ │ - context' = context + delta * scale │ │ - 显著性越低,增强越多;早期时间步增强越强 │ └─────────────────────────────────────────────────────────────┘ ``` #### 关键代码 ```python # 显著性计算 def compute_saliency(prompt_vecs, anchor_vecs, tau=0.2): cosine = cosine_similarity(prompt_vecs, anchor_vecs) scores = exp(cosine / tau) return scores.diagonal() / scores.sum(dim=1) # 应用 SCI omega = 1.0 - (timestep / 1000.0) # 时间步调度 scale = omega * (1.0 - saliency) context = context + delta * scale ``` ### 2. Layout-forcing Attention Modulation (LAM) LAM 在 **注意力计算阶段** 动态调制注意力分布,使其与预定义的空间布局对齐。 #### 工作原理 ``` ┌─────────────────────────────────────────────────────────────┐ │ 输入: Q (视频 tokens), K/V (文本 tokens), Layout (bbox) │ ├─────────────────────────────────────────────────────────────┤ │ Step 1: 计算原始 attention scores │ │ attn_scores = Q @ K^T / sqrt(d) │ ├─────────────────────────────────────────────────────────────┤ │ Step 2: 构建调制函数 │ │ g_plus = max(scores) - scores (增强调制) │ │ g_minus = min(scores) - scores (抑制调制) │ ├─────────────────────────────────────────────────────────────┤ │ Step 3: 计算 IOU 引导的强度 │ │ - adapt_mask: 当前注意力分布 > mean 的区域 │ │ - layout_mask: 目标 bbox 区域 │ │ - iou = intersection / union │ │ - strength = 1 - iou (IOU 越低,调制越强) │ ├─────────────────────────────────────────────────────────────┤ │ Step 4: 应用动态调制 │ │ - 在 bbox 内增强对应主体 token (g_plus) │ │ - 抑制其他主体 token (g_minus) │ │ - bias = g_k * strength * layout_mask │ │ - final_scores = attn_scores + bias │ └─────────────────────────────────────────────────────────────┘ ``` #### LAM vs 传统 Layout Control | 方法 | 机制 | 缺点 | |-----|------|-----| | **硬掩码 (Hard Mask)** | 强制注意力只在 bbox 内 | 无法适应多样的物体形状 | | **LAM (Ours)** | IOU 引导的动态软调制 | ✅ 灵活适应不同形状 | ### 3. 关键帧插值 遵循论文附录 F 的设计,支持用 4 个关键帧描述运动轨迹,然后线性插值到所有帧: ```python # 输入: 4 个关键帧的 bbox keyframe_bboxes = [frame_0, frame_1, frame_2, frame_3] # 输出: 81 帧的 bbox(线性插值) all_bboxes = F.interpolate(keyframe_bboxes, size=81, mode='linear') ``` ## 架构设计 ``` ┌─────────────────────────────────────────────────────────────┐ │ WanVideoCompAttnPipeline │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ CompAttnUnit │───▶│ CompAttnMerge │ │ │ │ (SCI 预处理) │ │ (CFG 合并) │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ model_fn_wrapper (动态注入) │ │ │ │ - apply_sci(): 修改 context │ │ │ │ - build_layout_mask(): 构建空间掩码 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ patched cross_attention │ │ │ │ - lam_attention(): IOU 引导的注意力调制 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ## Prompt-BBox 绑定机制 ### 使用变量拼接(推荐) 使用 Python 变量定义主体,通过 f-string 拼接 prompt,代码层面天然表达绑定关系: ```python # 定义主体变量 subject0 = "red car" subject1 = "blue bicycle" # 使用变量拼接 prompt prompt = f"A {subject0} drives left, a {subject1} rides right" # subjects 和 bboxes 顺序一一对应 subjects = [subject0, subject1] bboxes = [bbox0, bbox1] # bbox0 对应 subject0, bbox1 对应 subject1 ``` ### 绑定原理 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 1. 用户定义 subjects 和 bboxes(顺序一一对应) │ │ │ │ subject0 = "red car" │ │ subject1 = "blue bicycle" │ │ subjects = [subject0, subject1] │ │ bboxes = [bbox0, bbox1] │ ├─────────────────────────────────────────────────────────────────────────┤ │ 2. Tokenize prompt,搜索每个 subject 的 token 位置 │ │ │ │ prompt = f"A {subject0} drives while a {subject1} rides..." │ │ ↑↑↑↑↑↑↑ ↑↑↑↑↑↑↑↑↑↑↑↑ │ │ token indices token indices │ ├─────────────────────────────────────────────────────────────────────────┤ │ 3. 建立 subject_token_mask (关联 token 位置与 bbox) │ │ │ │ subject_token_mask[0, ...] = True # subject0 tokens │ │ subject_token_mask[1, ...] = True # subject1 tokens │ ├─────────────────────────────────────────────────────────────────────────┤ │ 4. 在推理时应用: │ │ - SCI: 根据 mask 增强对应 token 的语义 │ │ - LAM: 在 bbox 区域内增强对应 token 的注意力 │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 重要约束 1. **subjects 必须在 prompt 中出现**:使用变量拼接确保这一点 2. **顺序一一对应**:`bboxes[i]` 对应 `subjects[i]` 3. **精确匹配**:subject 字符串需要能被分词器识别为完整的 token 序列 ## 使用方法 ### 基本用法 ```python from diffsynth.pipelines.wan_video_comp_attn import WanVideoCompAttnPipeline from diffsynth.models.comp_attn_model import CompAttnConfig # 创建 pipeline pipe = WanVideoCompAttnPipeline.from_pretrained(...) # 1. 定义主体变量 subject0 = "red car" subject1 = "blue bicycle" # 2. 定义运动轨迹 (4 个关键帧) bbox0 = [(100, 250, 220, 380), (200, 250, 320, 380), (300, 250, 420, 380), (400, 250, 520, 380)] bbox1 = [(350, 260, 410, 400), (450, 260, 510, 400), (550, 260, 610, 400), (650, 260, 710, 400)] # 3. 使用变量拼接 prompt prompt = f"A {subject0} drives from left to center while a {subject1} rides to the right" # 4. 配置 Comp-Attn(顺序一一对应) comp_attn = CompAttnConfig( subjects=[subject0, subject1], # 变量列表 bboxes=[bbox0, bbox1], # 对应的 bbox 列表 enable_sci=True, enable_lam=True, interpolate=True, ) # 5. 生成视频 video = pipe(prompt=prompt, comp_attn=comp_attn) ``` ### 运动轨迹辅助函数 ```python def create_moving_bbox(start_x, end_x, y_center, box_width, box_height, num_keyframes=4): """创建从 start_x 移动到 end_x 的关键帧 bbox 序列""" keyframes = [] for i in range(num_keyframes): progress = i / (num_keyframes - 1) center_x = start_x + (end_x - start_x) * progress left = center_x - box_width / 2 right = center_x + box_width / 2 top = y_center - box_height / 2 bottom = y_center + box_height / 2 keyframes.append((left, top, right, bottom)) return keyframes # 使用示例 car_trajectory = create_moving_bbox( start_x=100, end_x=500, y_center=300, box_width=120, box_height=80, ) ``` ### 配置参数 | 参数 | 类型 | 默认值 | 说明 | |-----|------|-------|------| | `subjects` | List[str] | - | 主体名称列表(必须出现在 prompt 中) | | `bboxes` | List | None | 每个主体每个关键帧的 bbox | | `enable_sci` | bool | True | 是否启用 SCI | | `enable_lam` | bool | True | 是否启用 LAM | | `temperature` | float | 0.2 | 显著性计算的温度参数 τ | | `apply_to_negative` | bool | False | 是否对负样本 prompt 应用 | | `interpolate` | bool | False | 是否对关键帧 bbox 进行插值 | ### BBox 格式 ```python # 格式: (left, top, right, bottom) 像素坐标 # 支持多种输入形式: # 1. 静态布局(所有帧相同) bbox = (100, 200, 300, 400) # 2. 关键帧布局(4帧,会被插值) bboxes = [ (100, 200, 300, 400), # 关键帧 0 (150, 200, 350, 400), # 关键帧 1 (200, 200, 400, 400), # 关键帧 2 (250, 200, 450, 400), # 关键帧 3 ] ``` ## 与论文的对应关系 | 论文章节 | 实现位置 | 状态 | |---------|---------|-----| | Sec 3.2 SCI | `compute_saliency()`, `compute_delta()`, `apply_sci()` | ✅ | | Sec 3.3 LAM | `lam_attention()`, `build_layout_mask_from_bboxes()` | ✅ | | Appendix D 关键帧插值 | `interpolate_bboxes()` | ✅ | | Training-free 集成 | `patch_cross_attention()`, `wrap_model_fn()` | ✅ | ## 性能特点 根据论文数据: - **T2V-CompBench** 性能提升: +15.7% (Wan2.1-14B), +11.7% (Wan2.2-A14B) - **推理时间增加**: 仅 ~5% - **兼容性**: Wan, CogVideoX, VideoCrafter2, FLUX ## 注意事项 1. **主体名称必须精确匹配**: `subjects` 中的字符串必须能在 prompt 中找到对应的 token 2. **BBox 使用像素坐标**: 不是归一化坐标 3. **关键帧数量**: 推荐使用 4 个关键帧描述运动轨迹 4. **温度参数**: τ 过小会导致显著性估计不稳定,过大会削弱增强效果 5. **State tokens**: per-frame state control adds extra context tokens and attention bias. Keep the number of states small to reduce overhead. ## Per-frame State Control You can inject per-frame instance states (e.g., "running" -> "idle") with: - `state_texts`: list of state names per subject - `state_weights`: per-frame weights `(M, F, S)` or `(B, M, F, S)` - `state_scale`: bias strength for state tokens - `state_template`: default `"{subject} is {state}"` The implementation appends state tokens to the context and applies a per-frame attention bias based on the current token time index. ## 参考 - 论文: "Comp-Attn: Present-and-Align Attention for Compositional Video Generation" - 作者: Hongyu Zhang, Yufan Deng, et al. (Peking University, Tsinghua University)