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 text是O(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) | 最优,适合大规模系统 |