多模态数据工程:图片/视频数据组织与处理¶
更新日期:2026-04-15
一、多模态数据的特殊挑战¶
| 挑战 | 相对纯文本 |
|---|---|
| 存储规模 | 10-100x (图片 vs 文本) |
| IO 带宽 | 训练时磁盘 IO 是瓶颈 |
| 预处理复杂度 | 图片解码、resize、patchify |
| 质量多样性 | 噪声图片、水印、重复更难识别 |
| 版权问题 | 图片版权比文本更敏感 |
二、数据源¶
2.1 图文对数据¶
2.2 视觉指令数据¶
2.3 视频数据¶
三、图片存储策略¶
3.1 不同存储方案对比¶
3.2 WebDataset 格式(主流)¶
# WebDataset: 将图片+元数据打包为 tar
# 每个 tar 包含数万到数百万张图
# 文件结构:
# dataset.tar
# ├── 000001.jpg
# ├── 000001.json (包含 caption, source, etc.)
# ├── 000001.txt (caption only)
# ├── 000002.jpg
# ├── 000002.json
# ├── ...
import webdataset as wds
# 读取
dataset = wds.WebDataset("dataset-{000..999}.tar")
.shuffle(1000)
.decode("pil")
.to_tuple("jpg", "json")
.map_tuple(transform, lambda x: x['caption'])
loader = DataLoader(dataset, batch_size=256, num_workers=16)
# 优势:
# - 顺序读取, 快
# - 支持流式 (可以从 S3/HTTP 直接读)
# - 分布式训练友好
# - HuggingFace/Meta/LAION 的主流格式
3.3 预编码特征策略¶
# 场景: ViT 冻结, 只训 projector + LLM
# 优化: 预先用 ViT 编码所有图片, 保存为 .npy
def precompute_features(dataset, vit):
for image, caption in dataset:
features = vit(image) # [576, 1024]
save_npy(features, f"features/{image_id}.npy")
# 训练时直接加载 features, 跳过 ViT 前向
# 训练速度提升 2-5x (ViT 是主要计算)
# 代价:
# - 不能端到端微调 ViT (希望微调就不能用)
# - 需要额外磁盘空间
四、Megatron 中的多模态数据¶
4.1 Megatron 的文本格式¶
4.2 多模态扩展方案¶
# 方案 1: 路径引用 (最常用)
# .bin 存文本 token, 额外 JSON 记录图片路径
# JSONL 格式:
# {"doc_id": 0, "images": ["img/001.jpg"], "image_positions": [42], "text_tokens": "..."}
# {"doc_id": 1, "images": ["img/002.jpg", "img/003.jpg"], "image_positions": [10, 50]}
# 训练 DataLoader:
def load_multimodal_sample(doc_id):
text_tokens = bin_file.read(doc_id)
metadata = json_file.read(doc_id)
images = []
for img_path in metadata['images']:
img = load_image(img_path)
img_tokens = vit_encode(img)
images.append(img_tokens)
# 将 image_tokens 插入到 text_tokens 的指定位置
final_tokens = interleave(text_tokens, images, metadata['image_positions'])
return final_tokens
# 方案 2: 预编码 + 二进制存储
# 将 ViT 特征直接写入 .bin
# 索引记录 (text_offset, feature_offset) 两种
五、多模态数据清洗¶
class MultimodalFilter:
def filter(self, sample):
image, caption = sample
# 1. 图片质量
if image.size[0] < 128 or image.size[1] < 128:
return False # 太小
if image.size[0] > 4096 or image.size[1] > 4096:
return False # 太大, 可能是扫描件
aspect = image.size[0] / image.size[1]
if aspect < 0.1 or aspect > 10:
return False # 极端长宽比
# 2. 图片内容
if is_nsfw(image):
return False
if is_watermark_heavy(image):
return False
# 3. Caption 质量
if len(caption) < 10:
return False # 太短
if len(caption) > 5000:
return False # 可能是文档, 不是 caption
# 4. 图文匹配
clip_score = compute_clip_similarity(image, caption)
if clip_score < 0.2:
return False # 图文不匹配
# 5. 去重
phash = perceptual_hash(image)
if phash in seen_hashes:
return False
seen_hashes.add(phash)
return True
六、视频数据处理¶
# 视频比图片处理更复杂
class VideoProcessor:
def process(self, video_path):
# 1. 解码
video = decode_video(video_path) # 可能是 H.264/H.265 等
# 2. 采样
# 策略: 均匀采样 8-16 帧
frames = uniform_sample(video, n=16)
# 3. 单帧处理 (同图片)
processed_frames = [self.process_image(f) for f in frames]
# 4. 时序信息
# 保留帧间时间差, 用于时间编码
return {
'frames': processed_frames,
'timestamps': [i * (total_duration / 16) for i in range(16)],
'duration': total_duration,
'fps': original_fps,
}
# 存储: 预采样后的 tensor 或 tar 归档
# 原始视频文件太大, 训练时直接解码慢
七、多模态数据配比¶
训练 VLM 时, 数据配比很关键:
参考文献¶
-
[1] LAION-5B. 2022. 论文
-
[2] DataComp. 2023. 论文
-
[3] Chen et al. ShareGPT4V. 2023. 论文
-
[4] Liu et al. LLaVA. NeurIPS 2023. 论文
-
[5] WebDataset
-
[6] InternVid. 2024. 论文
-
[7] WebVid-10M. 2021. 论文
↑ 上级 · B. 数据工程