就像在淘宝搜商品,得先知道你要搜"运动鞋""蓝牙耳机"等关键词。MetaCLIP 构建了一个包含 50 万个视觉相关词条 的元数据列表,来源包括:
这些词条被称为 metadata entries
,用于后续匹配图文数据中是否出现。
这一步相当于:看每条图文描述中是否提到了词表中的关键词。
结果:约一半图文对被保留,并建立映射:text → entry。
这一步就是反过来建图:
对于每个词条 entry,列出所有包含它的文本。
为了解决数据分布极不均衡的问题,MetaCLIP 提出一个"魔法数字":t = 20,000, 每个 entry 最多只保留 2 万条图文对。
倒排索引的核心思想是,给定一个"关键词",快速找到所有包含这个关键词的文档。这和我们平时用搜索引擎是一样的 —— 比如你在百度搜"猫",搜索引擎其实就是查找"有哪些网页包含‘猫'这个词"。例如,原始的顺序是:
文档 ➝ 包含哪些词(正向索引)
而倒排索引是:
词 ➝ 出现在哪些文档中(倒过来)
输入:
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)
假设:
N
条文本(即 |D| = N
);K
(即 |M| = K
);L
(字符);ℓ
(字符);实际实现中可能会优化 entry in text
(比如用 trie 树、正则、分词等)
朴素实现(如上伪代码): 每条文本检查所有关键词, entry in text
是 O(L)
(Python 字符串子串匹配, KMP 算法), 时间复杂度:O(N * K * L)
。对于几百万文本、几十万关键词,这个复杂度是不可接受的。
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) | 最优,适合大规模系统 |