244 lines
7.8 KiB
Markdown
244 lines
7.8 KiB
Markdown
# 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=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 中添加):
|
||
|
||
```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` 字段统一覆盖。
|