Files
nvlabs--longlive/utils/DATASET_PROMPT_LOGIC.md
T
2026-07-13 12:31:40 +08:00

244 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
1. **`shot_durations.txt`**per-folder 文件)— 每行或逗号分隔的整数
2. **`chunks_per_shot`**(全局 config)— 所有 shot 统一重复固定次数
3. **`_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=0even_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=0even_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 中添加):
```yaml
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` 字段统一覆盖。