跳转至

多模态数据工程:图片/视频数据组织与处理

更新日期: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 的文本格式

# Megatron 标准格式: .bin (token IDs) + .idx (索引)
# 但这个格式只支持文本!
# 多模态需要扩展

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 时, 数据配比很关键:


参考文献


上级 · B. 数据工程