MMPT 数据集构建实战:基于 S3D 特征提取与去重预分词的 Howto100M 多模态数据预处理全流程
【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm
MMPT(Masked Multimodal Pre-training,即 Kosmos-2 中基于 fairseq 的多模态预训练框架)的DATASET.md专门面向“视频-文本”数据这一最棘手环节,给出了从原始视频下载到可直接喂入训练器的 S3D 特征与 BERT token 分片(ShardedTensor)的完整工程化方案。本文以该文档为骨架,结合仓库内scripts/video_feature_extractor、scripts/text_token_extractor与mmpt/processors、mmpt/utils等源码,逐层拆解 Howto100M 的预处理管线,并说明 Youcook、MSRVTT 等下游数据集的接入方式,帮助读者理解并复现 MMPT 的数据准备全流程。
一、数据预处理的整体布局:视频与文本两条管线
MMPT 承认“视频数据的下载与处理是最具挑战性的环节”,因此将预处理工作拆成两个独立脚本目录:
- 视频特征提取:scripts/video_feature_extractor(深度改编自 antoine77340 的 video_feature_extractor 项目);
- 文本预分词:scripts/text_token_extractor。
两条管线最终汇聚到统一的ShardedTensor存储格式,训练阶段由 mmpt/datasets/mmdataset.py 与 mmpt/datasets/fairseqmmdataset.py 消费。整个数据流的完整装配关系(meta 处理器、视频处理器、文本处理器、aligner 如何组合)可以在 projects/task/how2.yaml 中看到:
dataset: meta_processor: ShardedHow2MetaProcessor train_path: data/how2/how2_s3d_train.lst val_path: data/how2/how2_s3d_val.lst video_processor: ShardedVideoProcessor vfeat_dir: data/feat/feat_how2_s3d_shard_small text_processor: ShardedTextProcessor tfeat_dir: data/feat/feat_how2_s3d_shard_small/raw_caption_dedup.bert-base-uncased. aligner: FixedLenAligner可见离线预处理的目标,就是把“原始视频文件 + 原始字幕”转换成vfeat_dir(视频特征分片)与tfeat_dir(文本 token 分片)两组可直接随机访问的 numpy 分片文件。
二、S3D 视频特征提取:模型准备与 PathBuilder 路径跟踪
2.1 预训练模型放置
视频特征提取使用预训练 S3D 模型(HowTo100M 上的 S3D 预训练权重),需要把两个文件放到指定位置:
pretrained_models/s3d_dict.npypretrained_models/s3d_howto100m.pth
2.2 PathBuilder:视频 id 到特征路径的自动映射
文档强调实现了一个PathBuilder来自动维护“视频 id → 源视频路径 → 特征输出路径”的映射,依赖conda install -c anaconda pandas;视频解码则需要pip install ffmpeg-python。
查看 scripts/video_feature_extractor/pathbuilder.py 可以看到其核心逻辑:
- 若
feature_dir/meta_plan.json已存在,则直接加载该 JSON 作为计划(支持断点续跑); - 否则遍历
video_dirs(以逗号分隔的目录列表),把每个视频文件(或子目录分片中的文件)的文件名(去扩展名)作为video_id,建立video_id → 视频路径字典; - 为每个视频 id 生成
feature_dir/{video_id}.npy(若启用shards参数则按idx % shards分发到子目录); - 将
{"video_path": [...], "feature_path": [...]}写入meta_plan.json,之后训练/提取脚本直接复用该计划。
PathBuilder还支持split参数(如"0/10"),把全部视频按 chunk 切分到不同机器,配合文档“建议在多台机器上并行提取”的做法,实现水平扩展。
2.3 extract.py:特征提取器与解码配置
scripts/video_feature_extractor/extract.py 内置了多种 CNN 类型的解码配置:
| type | fps | size | centercrop | 输出特征维度 |
|---|---|---|---|---|
2d | 1 | 224 | 否 | 2048 |
3d | 24 | 112 | 是 | 2048 |
s3d | 30 | 224 | 是 | 512 |
vmz | 24 | 112 | 是 | 512 |
vae | 2 | 256 | 是 | 1024(LongTensor) |
其中s3d对应 fps=30、224×224、中心裁剪、输出 512 维video_embedding(见extract.py中对batch_features['video_embedding']的取用)。其他可调参数还包括:
--batch_size(默认 64):特征前向的批大小;--half_precision(默认 0):置 1 时将特征保存为float16,显著节省磁盘并加速训练(文档推荐默认开启);--l2_normalize(默认 1):特征做 L2 归一化;--num_decoding_thread(默认 4):视频解码的并行线程数;--hflip(默认 0):是否随机水平翻转增强。
2.4 一键脚本 s3d.sh
Howto100M 的完整提取命令封装在 scripts/video_feature_extractor/how2/s3d.sh:
python scripts/video_feature_extractor/extract.py \ --vdir <path_to_video_folder> \ --fdir data/feat/feat_how2_s3d \ --type=s3d --num_decoding_thread=4 \ --batch_size 32 --half_precision 1运行前替换<path_to_video_folder>为已下载的 Howto100M 视频目录,输出特征将写入data/feat/feat_how2_s3d/(每个视频一个.npy文件)。解码侧由 videoreader.py 配合RandomSequenceSampler完成采样,预处理(resize、crop)由 preprocessing.py 负责。
三、Howto100M 预处理的三处关键差异
文档明确指出,MMPT 的预处理与既有论文相比有三处不同,这三点直接影响自监督信号的质量:
3.1 使用 raw_caption.json 而非 caption.json
Howto100M 官方发布了两份字幕:caption.json已人工去除停用词,raw_caption.json是原始字幕。MMPT 选用raw_caption.json,目的是让文本侧保持“纯自监督”状态——不做人工清洗,让模型自己学习停用词等语言结构,避免监督信号被人工干预污染。
3.2 去除为实时可读性设计的部分重复文本
Howto100M 的字幕片段存在大量因“实时滚动字幕可读性”而引入的部分重复(相邻片段文字互相包含/重叠)。这些重复会破坏文本与视频片段的对齐监督,因此需要专门的去重处理器。
实现位于 mmpt/processors/dedupprocessor.py 的CaptionDedupProcessor,其_dedup逻辑(见 dedupprocessor.py#L119-L207)逐条扫描相邻片段并分情况处理:
- 当前文本是上一片段的子集(
texts[-1].endswith(text))→ 并入上一片段,只扩展结束时间; - 当前文本是上一片段的超集(
text.startswith(texts[-1]))→ 用当前文本替换,并合并时间区间; - 部分重叠 → 以 0.5 概率随机选择“重叠部分并入前一片段尾部”或“前一片段尾部并入当前片段开头”,并调用
random_merge保持start/end/text三元组的一致性。
去重后每个片段仍满足end >= start且文本非空(代码中有断言校验)。该处理器还内置了统计能力(save_stat/print_stat),对前 4096 个视频统计t_clip_len、clip_tps、video_tps等指标,用于评估去重效果;注释中的示例统计显示去重后clip_tps从约 2.49 下降到约 0.88(字幕冗余度显著降低)。
3.3 用 ShardedTensor 分片替代 h5py
训练时需要按 video_id 随机访问特征,MMPT 没有采用常用的 h5py,而是自研了 mmpt/utils/shardedtensor.py 中的ShardedTensor:
from_list(xs):把多个变长数组(如不同长度的视频特征序列)沿 axis=0 拼接成单一data数组,并用starts记录每个样本的起止偏移;__getitem__(i):通过data[starts[i]:starts[i+1]]以 O(1) 完成随机访问;save:将_starts.npy与_data.npy两个 numpy 文件落盘;load支持mmap_mode,可内存映射按需加载。
相比 h5py,这种“连续内存 + 偏移表”的格式加载更快(内存连续、无文件句柄开销),文档中的评价是“比 h5py 更快”。
四、视频侧三步走:提取 → 划分 → 分片
4.1 提取视频特征
编辑并运行s3d.sh(见上文),默认以 fp16 存储特征以节省空间并加速训练。文档建议在多台机器上并行执行(配合PathBuilder的split参数切分视频列表)。
4.2 划分 train/val 视频 id 列表
将可用的视频 id 拆分为两个清单文件:
data/how2/how2_s3d_train.lstdata/how2/how2_s3d_val.lst
每行一个视频 id(不带扩展名),这两个文件在训练配置 projects/task/how2.yaml 中对应train_path与val_path。
4.3 打包为 ShardedTensor
运行 scripts/video_feature_extractor/shard_feature.py,其Shard类:
- 按 train/val 两个列表读取 video_id;
- 以
shard_size=4096为步长把每个 split 切成若干 shard(train_0, train_1, ...); - 对每个 shard,逐个
np.load对应vfeat_dir/{video_id}.npy,再ShardedTensor.from_list拼接并save到target_dir/{split}_{shard_idx}; - 同时把每个 shard 内的 video_id 清单 pickle 成
{split}_meta.pkl,供训练时按 meta 索引加载。
默认输出目录为data/feat/feat_how2_s3d_shard_small,与 projects/task/how2.yaml 中vfeat_dir完全对应。注意Shard.__call__中tfeat_dir参数当前仅用于展示,文本分片由下一节的 pretokenization 脚本负责生成。
五、文本侧:去重 → BERT 预分词 → 分片
5.1 清洗与去重
执行命令:
python -m mmpt.processors.dedupprocessor该模块的__main__(见 dedupprocessor.py#L210-L236)完成三步:
- 若
data/how2/raw_caption.pkl不存在,先把data/how2/raw_caption.json中的每条字幕json.dumps后序列化为 pickle; - 构造
CaptionDedupProcessor并对全部视频执行去重(内部用 tqdm 显示进度); finalize输出data/how2/raw_caption_dedup.pkl——这正是后续预分词脚本的输入。
如需单条视频调试,可在__main__注释的 demo 中调用deduper.single("HfIeQ9pzL5U")对比去重前后的片段列表。
5.2 预分词并分片
把去重后的data/how2/raw_caption_dedup.pkl分词成 sharded numpy 数组:
python scripts/text_token_extractor/pretokenization.py scripts/text_token_extractor/configs/bert-base-uncased.yaml配置 scripts/text_token_extractor/configs/bert-base-uncased.yaml 只有四个字段:
dataset: bert_name: bert-base-uncased caption_pkl_path: data/how2/raw_caption_dedup.pkl use_fast: true target_dir: data/feat/feat_how2_s3d_shard_smallpretokenization.py 的处理流程分两阶段:
- tokenize 阶段:用
PKLJSONStrTextProcessor读取 pkl 中的字幕,DataLoader(num_workers=16)并行调用 HuggingFaceAutoTokenizer(bert-base-uncased、use_fast=True,即 Rust 加速分词器)逐条转成input_ids,输出data/how2/raw_caption_dedup.bert-base-uncased.pkl(若已存在则跳过); - sharding 阶段:先读取
{split}_meta.pkl拿到视频 id 到 shard 的映射,再按 shard 调用numpify——为每个片段生成[start, end]时间戳数组(float32)和长度max_cap_len=32的定长 token 数组(int32,不足补 -1),分别以ShardedTensor保存为.startends与.caps_ids后缀文件。
文件名前缀由raw_caption_dedup.+bert-base-uncased.拼接而成,正好匹配 projects/task/how2.yaml 中的tfeat_dir: data/feat/feat_how2_s3d_shard_small/raw_caption_dedup.bert-base-uncased.。此后训练时文本处理器通过ShardedTensor.load(mmap_mode)按需读取,而不再逐条分词。
六、Youcook、MSRVTT 等下游数据集:即时分词的轻量方案
对于端到端任务(如视频-文本检索、视频问答),MMPT 使用与 Howto100M 同源的 Youcook2 与 MSRVTT 版本(源自 Howto100M 与 MILNCE 项目配套发布的数据)。接入方式与 Howto100M 的第一阶段类似——同样走 S3D 特征提取——但文本不再离线预分词,而是直接从 meta 数据读取文本、训练时即时分词(on-the-fly tokenization)。
这一点在 mmpt/processors/processor.py 的TextProcessor中体现得很清楚:它持有AutoTokenizer,在__call__中实时把文本字符串转成input_ids;类注释明确警告“on-the-fly tokenization 对 how2 这种超大数据集太慢,因此主要用于端任务”。下游数据集的完整配置可直接查看 projects/task/youcook.yaml、projects/task/vtt.yaml(以及对应的检索版youcook_videoclip.yaml、vtt_videoclip.yaml、零样本测试版test_youcook_zs.yaml等)。数据按约定下载到data/youcook、data/msrvtt目录,训练/验证时由各个MetaProcessor读取。
七、小结:一份可直接照搬的预处理路线图
MMPT 的数据准备思路可以浓缩为以下路线图,适用于任何“大规模视频-文本”预训练数据的接入:
- 视频:下载原始视频 → 放置 S3D 预训练权重(
pretrained_models/s3d_dict.npy、s3d_howto100m.pth)→ 按需修改并运行s3d.sh提取 512 维 fp16 特征 → 生成{split}_train/val.lst→ 运行shard_feature.py打成ShardedTensor分片; - 文本:以
raw_caption.json为原始输入 →python -m mmpt.processors.dedupprocessor去除片段重复 → 配置bert-base-uncased.yaml后运行pretokenization.py完成 BERT 预分词与.startends/.caps_ids分片; - 下游任务:复用同一套视频特征,文本走
TextProcessor即时分词,用 projects/task 下的 yaml 配置即可衔接训练。
通过“离线预分词 + ShardedTensor 分片”组合,Howto100M 这种百万级规模数据集在训练期可以做到接近零解码开销的随机访问;而去重与raw_caption的选择,则为视频-文本对齐提供了更纯净的自监督信号。这套管线同样构成了 Kosmos-2 中 MMPT 框架的数据底座,理解了它,也就掌握了复用该框架接入任意视频数据集的关键。
【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考