Meta-CLIP & 倒排索引

1 Abstract

对比语言-图像预训练 (CLIP) 是一种在计算机视觉领域取得重大进展的方法,推动了现代识别系统和生成模型的发展。论文认为,CLIP 成功的关键在于其数据,而非模型架构或预训练目标。然而,CLIP 原文仅提供了非常有限的数据及其收集方式信息,因此一些研究试图通过使用其模型参数进行筛选来重现 CLIP 的数据。在本研究中,论文旨在揭示 CLIP 的数据管理方法,并致力于将其向社区开放,因此本论文引入了基于元数据管理的语言-图像预训练 (MetaCLIP)。MetaCLIP 采用原始数据池和元数据(源自 CLIP 的概念),并在元数据分布上生成一个平衡的子集。论文的实验研究严格区分了模型和训练设置,仅专注于数据。 MetaCLIP 应用于 CommonCrawl,其 4 亿图文数据对在多个标准基准测试中均优于 CLIP 的数据。在零样本 ImageNet 分类中,MetaCLIP 的准确率达到了 70.8%,超过了 CLIP 在 ViT-bigG 模型上的 68.3%。在保持相同训练预算的情况下,扩展到 1B 数据后,准确率达到了 72.4%。论文的观察结果适用于各种模型规模,例如 ViT-bigG 的准确率达到了 82.1%。


2 Meta 数据清洗流程

2.1 构建"关键词词表"(Metadata)

就像在淘宝搜商品,得先知道你要搜"运动鞋""蓝牙耳机"等关键词。MetaCLIP 构建了一个包含 50 万个视觉相关词条 的元数据列表,来源包括:

  • Wikipedia 高频单词和词组
  • 热门词条标题
  • WordNet 的所有同义词集
  • 高互信息的二元词组(如 "gold medal")

这些词条被称为 metadata entries,用于后续匹配图文数据中是否出现。

2.2 子串匹配(Sub-string Matching)

这一步相当于:看每条图文描述中是否提到了词表中的关键词。

  • 对 16 亿图文对做匹配,看文本是否包含词表中的词条;
  • 一条文本可能匹配多个词条(平均约 3.5 个);
  • 匹配不到的(没用的)就剔除,比如乱码、ID、日期等。

结果:约一半图文对被保留,并建立映射:text → entry。

2.3 构建倒排索引(Inverted Indexing)

这一步就是反过来建图:

对于每个词条 entry,列出所有包含它的文本。

  • entry → list of matching texts;
  • 观察到极度长尾分布:大多数 entry 很少被匹配,少数 entry 出现非常频繁(如 "photo" 匹配了 5400 万次);
  • 太频繁的 entry 容易带来噪声或语义泛化(如"image""photo""the"等);

2.4 分布平衡(Balancing)

为了解决数据分布极不均衡的问题,MetaCLIP 提出一个"魔法数字":t = 20,000, 每个 entry 最多只保留 2 万条图文对。

  • 如果某个 entry 匹配的图文对 < t,就全保留;
  • 如果 > t,就随机采样 t 条
  • 每条图文对只要有一个匹配的 entry 被采样,就会被保留;
  • 最终形成的数据子集是:对每个 entry 分布相对均衡,既保留了多样性,也避免头部 entry 垄断数据分布。

3 倒排索引

倒排索引的核心思想是,给定一个"关键词",快速找到所有包含这个关键词的文档。这和我们平时用搜索引擎是一样的 —— 比如你在百度搜"猫",搜索引擎其实就是查找"有哪些网页包含‘猫'这个词"。例如,原始的顺序是:

文档 ➝ 包含哪些词(正向索引)

而倒排索引是:

词 ➝ 出现在哪些文档中(倒过来

3.1 算法步骤(构建 entry → text 的映射)

输入:

  • 数据集 D:一组图文对(或文档集合),每条数据包含一个文本;
  • 关键词列表 M(metadata entries):词汇表,比如 ["cat", "dog", "photo", "saxophone", …]。

输出:

  • 倒排索引表 I:字典,形如 {entry: [text_ids]}

算法流程(伪代码)

# 初始化倒排表
inverted_index = defaultdict(list)  # entry -> list of text_ids

for text_id, text in enumerate(D):        # 遍历所有文本
    for entry in M:                       # 遍历所有关键词
        if entry in text:                # 如果关键词出现在文本中(子串匹配)
            inverted_index[entry].append(text_id)

3.2 优化

假设:

  • 数据集包含 N 条文本(即 |D| = N);
  • metadata(关键词)数量为 K(即 |M| = K);
  • 每个文本平均长度为 L(字符);
  • 每个关键词长度为 (字符);

实际实现中可能会优化 entry in text(比如用 trie 树、正则、分词等)

  • 朴素实现(如上伪代码): 每条文本检查所有关键词, entry in textO(L)(Python 字符串子串匹配, KMP 算法), 时间复杂度:O(N * K * L)。对于几百万文本、几十万关键词,这个复杂度是不可接受的。

  • 文本先 token 化(如用空格分词)后存到set中, 改为词级匹配+哈希:
      text_tokens = set(text.split())
      for entry in M:
          if entry in text_tokens:
              ...
    

    复杂度:O(N * L + N * K)(大幅减少)

  • 构建关键词前缀树(Trie)

    • 用 Trie 存所有关键词,扫描每条文本时,只需在 Trie 中滑动匹配即可;

    • 复杂度可降为:O(N * L)(遍历文本 + Trie 查询)

    这是搜索引擎和大型系统中常用的做法。

方法 描述 时间复杂度 备注
朴素方法 每条文本遍历所有 entry O(N * K * L) 简单易实现,但慢
分词 + 词表查找 先分词再查 entry O(N * L) 适合词语级 entry
Trie 前缀树 用 Trie 匹配子串 O(N * L) 最优,适合大规模系统