7.8 KiB
Dataset Prompt 分配逻辑说明
本文档说明 MultiTextConcatDataset(纯文本)和 MultiVideoConcatDataset(视频训练)在不同配置下产生 prompts 的行为。
架构总览
| 使用场景 | Dataset 类 | 输出 |
|---|---|---|
| diffusion.py 训练 | MultiVideoConcatDataset |
frames + prompts |
| diffusion.py 推理 | MultiTextConcatDataset |
prompts |
| distillation.py 训练(backward_sim) | MultiTextConcatDataset |
prompts |
| distillation.py 可视化 | MultiTextConcatDataset |
prompts |
| inference.py | MultiTextConcatDataset |
prompts |
一、MultiTextConcatDataset(纯文本)
关键参数
| 参数 | 含义 |
|---|---|
num_blocks |
输出 prompt 列表的固定长度 |
chunks_per_shot |
每个 shot 重复的 block 数(0=使用 even_durations 均分) |
scene_cut_prefix |
切镜标记前缀,默认 "The scene transitions. " |
简写约定:0, 1, 2 表示不同 caption;p+X 表示 scene_cut_prefix + X
1. txt 模式(data_path 指向 .txt 文件)
每行一个 caption,每个 sample 取 idx 行的 caption,重复 num_blocks 次。
不加 scene_cut_prefix(单 shot 语义)。
data_path="prompts.txt", line[3]="A dog runs", num_blocks=12
→ [A, A, A, A, A, A, A, A, A, A, A, A]
无论 chunks_per_shot 设为多少,txt 模式始终单 shot 重复。
2. 目录模式(data_path 指向目录)
读取 caption/<subfolder>/*.json(不需要 video/ 目录)。
每个 JSON 的 caption 字段作为一个 shot 的文本。
Shot duration 三级 fallback
按优先级决定每个 shot 占多少个 block:
shot_durations.txt(per-folder 文件)— 每行或逗号分隔的整数chunks_per_shot(全局 config)— 所有 shot 统一重复固定次数_even_durations(均分)— 将num_blocks均匀分给所有 caption
长度处理
输出始终恰好 num_blocks 个 prompt:
- 超过 → 截断尾部
- 不足 → 用最后一个 caption 直接 padding(不加 scene_cut_prefix)
示例
3 caption, num_blocks=12, chunks_per_shot=4
captions: [0, 1, 2]
shot_durations: [4, 4, 4]
→ [0, 0, 0, 0, p+1, 1, 1, 1, p+2, 2, 2, 2]
3 caption, num_blocks=12, chunks_per_shot=0(even_durations)
captions: [0, 1, 2]
even_durations: base=4, extra=0 → [4, 4, 4]
→ [0, 0, 0, 0, p+1, 1, 1, 1, p+2, 2, 2, 2]
2 caption, num_blocks=12, chunks_per_shot=4
captions: [0, 1]
shot_durations: [4, 4], sum=8 < 12 → 最后一个 shot 扩展到 8
→ [0, 0, 0, 0, p+1, 1, 1, 1, 1, 1, 1, 1]
↑ padding 不加 prefix
5 caption, num_blocks=12, chunks_per_shot=4
captions: [0, 1, 2, 3, 4]
shot_durations: [4, 4, 4, 4, 4], 但 num_blocks=12 → clamped 到 [4, 4, 4]
→ [0, 0, 0, 0, p+1, 1, 1, 1, p+2, 2, 2, 2]
caption 3 和 4 被截断
2 caption, num_blocks=12, chunks_per_shot=0(even_durations)
captions: [0, 1]
even_durations: base=6, extra=0 → [6, 6]
→ [0, 0, 0, 0, 0, 0, p+1, 1, 1, 1, 1, 1]
1 caption, num_blocks=12
captions: [0]
→ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
单 shot,不加 prefix
shot_durations.txt 覆盖
captions: [0, 1, 2], shot_durations.txt 内容: "2, 6, 4", num_blocks=12
→ [0, 0, p+1, 1, 1, 1, 1, 1, p+2, 2, 2, 2]
二、MultiVideoConcatDataset(视频训练)
关键参数
| 参数 | 含义 |
|---|---|
total_segments |
总 segment 数量(= 1 + num_subsequent_segments) |
single_video_only |
config 中的 uniform_prompt,True 时只从一个视频采样 |
max_chunks_per_shot |
单镜头最大连续 chunk 数,超过则跳 1 秒做虚拟切镜(0=不限制) |
scene_cut_prefix |
切镜标记前缀 |
allow_padding |
视频不够时是否允许 padding(否则跳过该 folder) |
Prompt 由实际视频采样决定,逐 segment 从视频文件加载对应的 per-video caption。
1. 多视频自然拼接(max_chunks_per_shot=0,默认)
按视频文件顺序采样,切换视频文件时加 scene_cut_prefix:
video A 够采 3 chunks, video B 够采 4 chunks, total_segments=7
→ [A, A, A, p+B, B, B, B]
2. single_video_only=True
强制只从一个视频文件采样。视频不够长则整个 folder 被跳过:
video A 够采 7 chunks, total_segments=7
→ [A, A, A, A, A, A, A]
video A 只够采 5 chunks, total_segments=7
→ (失败,跳到下一个 folder)
3. max_chunks_per_shot=3(限制单镜头时长)
从同一视频连续采超过 3 chunks 后,跳 1 秒做虚拟切镜,加 scene_cut_prefix:
video A 很长, video B, total_segments=7, max_chunks_per_shot=3
→ [A, A, A, p+A, A, A, p+B]
↑跳1秒虚拟切镜 ↑换视频
如果跳 1 秒后 A 不够了,直接跳到 B:
video A 够 4 chunks(跳1秒后不够), video B, total_segments=7, max_chunks_per_shot=3
→ [A, A, A, p+B, B, B, B]
↑A跳1秒后不够,换B
4. single_video_only=True + max_chunks_per_shot=3
单视频内也可以做虚拟切镜:
video A 很长, total_segments=7, single_video_only=True, max_chunks_per_shot=3
→ [A, A, A, p+A, A, A, p+A]
5. 训练 padding(allow_padding=True)
视频不够时用最后一个 caption 直接 padding(不加 prefix):
video A 够采 3 chunks, video B 够采 2 chunks, total_segments=7, allow_padding=True
→ [A, A, A, p+B, B, B, B]
↑后 2 个用最后一个 caption padding
三、Multi-Shot Sink
通过 config 的 multi_shot_sink: true 开启。
开启后,当检测到某个 block 处于新场景的起始位置时,会在该 block 去噪完成并更新 cache 后,
将 KV cache 的 attention sink 从旧场景的第一帧迁移到新场景的第一帧。
同时会自动把全局 sink 长度设为 sink_size,不需要单独配置 global sink。
配置方式(yaml config 中添加):
sink_size: 8
multi_shot_sink: true # 默认 false,不迁移 sink
此选项在以下所有场景均生效:
| 场景 | Pipeline | 检测方式 |
|---|---|---|
| Diffusion trainer evaluation | CausalDiffusionInferencePipeline |
检查 prompt 是否以 scene_cut_prefix 开头 |
| Distillation trainer evaluation | CausalDiffusionInferencePipeline |
同上 |
离线推理 inference.py |
CausalDiffusionInferencePipeline |
同上 |
| Distillation backward simulation | SelfForcingTrainingPipeline |
trainer 预计算 scene_cut_mask 传入 conditional_dict |
| Streaming long tuning | SelfForcingTrainingPipeline |
同上(mask 随 chunk 自动 slice) |
实现机制
Inference pipeline(CausalDiffusionInferencePipeline):
直接检查每个 chunk 的 raw prompt 是否以 scene_cut_prefix 开头。
Training pipeline(SelfForcingTrainingPipeline):
由于 prompts 在进入 pipeline 前已编码为 embedding,无法从 embedding 反推文本。
因此 trainer 在编码前从原始 prompts 计算布尔列表 scene_cut_mask,
放入 conditional_dict["scene_cut_mask"] 一路透传到 pipeline。
对于 streaming training,scene_cut_mask 在 _slice_cond_dict_for_chunk 中随 prompt 一起 slice。
block: [0] [1] [2] [p+3] [4] [5] [p+6] [7]
mask: F F F T F F T F
sink: 0 0 0 →3 3 3 →6 6
↑更新sink ↑更新sink
当 multi_shot_sink: false(默认)时,sink 始终锚定在视频的第一帧,不做任何迁移。
scene_cut_prefix 由 DEFAULT_SCENE_CUT_PREFIX 常量定义(dataset.py),
inference pipeline 引用同一常量,确保训练和推理的切镜检测一致。
可通过 config 中的 scene_cut_prefix 字段统一覆盖。