3. 经典模型架构:从协同嵌入(Co-Embedding)到Transformer,双塔模型与单塔模型的对比
跨模态检索的核心,说白了就是让计算机理解「这张图」和「这句话」是不是在说同一件事。我刚开始接触这个领域时,觉得这问题挺玄乎的——图像是像素矩阵,文本是离散符号,怎么让它们对齐?
嗯,这就要从模型架构说起了。这些年,跨模态检索的架构演进大致经历了三个阶段:协同嵌入(Co-Embedding)、双塔模型、单塔模型。今天咱们就把这三兄弟掰开揉碎了讲清楚。
一句话总结:协同嵌入是「把猫和狗都关进同一个笼子」;双塔模型是「给猫和狗各建一个房间,但用同一把尺子量它们」;单塔模型是「让猫和狗直接对话」。
3.1 协同嵌入(Co-Embedding):把不同模态塞进同一个向量空间
协同嵌入是最早的思路,也是后来所有架构的基石。它的想法很直接:
- 用一个编码器处理图像(比如CNN),得到一个向量。
- 用另一个编码器处理文本(比如RNN或BERT),也得到一个向量。
- 然后通过训练,让匹配的图文对在向量空间里距离近,不匹配的距离远。
我在2018年做过一个电商商品检索项目,用的就是这种思路。当时我们拿ResNet-50提取图片特征,拿Word2Vec+BiLSTM提取商品标题特征,然后算余弦相似度。效果嘛……只能说「能用,但不够好」。
为什么不够好?因为协同嵌入有个硬伤:两个编码器是独立训练的。图像编码器只看图像,文本编码器只看文本,它们之间唯一的交互就是最后的损失函数。这就像两个人在不同房间各自学习,只在考试时对答案——很难真正理解对方。
避坑指南:我曾经在训练协同嵌入模型时,发现图像和文本的向量分布完全不在一个量级上——图像向量的范数普遍是文本向量的10倍。后来加了L2归一化才解决。所以,做协同嵌入一定要做向量归一化,否则训练根本收敛不了。
3.2 双塔模型(Two-Tower):独立编码,共享空间
双塔模型其实是协同嵌入的「升级版」。它保留了「两个编码器」的结构,但做了几个关键改进:
- 编码器更强了:图像塔从CNN升级到ViT(Vision Transformer),文本塔从RNN升级到BERT。
- 交互更早了:虽然两个塔还是独立编码,但在训练时会用对比学习(Contrastive Learning)让两个塔的参数互相影响。
- 检索更快了:因为两个塔的向量可以提前算好、建索引,所以线上检索时只需要一次向量相似度计算。
你想想看,双塔模型为什么能成为工业界的主流?原因很简单:快。我做过一个千万级商品的跨模态检索系统,双塔模型在GPU上单次检索只需要5毫秒,而单塔模型要200毫秒。在实时场景下,这个差距是致命的。
| 特性 | 协同嵌入 | 双塔模型 | 单塔模型 |
|---|---|---|---|
| 编码器交互 | 无(独立训练) | 通过损失函数间接交互 | 直接交互(Cross-Attention) |
| 检索速度 | 快(可预计算) | 快(可预计算) | 慢(需实时计算) |
| 细粒度匹配 | 差 | 中等 | 好 |
| 典型模型 | VSE++ | CLIP, ALIGN | UNITER, ViLBERT |
| 适用场景 | 小规模检索 | 大规模实时检索 | 高精度离线匹配 |
3.3 单塔模型(Single-Tower):让模态直接对话
单塔模型走的是另一条路。它不再把图像和文本分开编码,而是把它们拼在一起,喂给一个统一的Transformer。这个Transformer内部通过Self-Attention和Cross-Attention,让图像和文本直接交互。
为什么会这样?因为双塔模型有个天然缺陷:两个塔的向量是「全局」的。你拿一张「一只白猫坐在红沙发上」的图片,双塔模型只能得到一个全局向量,它不知道「白猫」对应文本里的哪个词,「红沙发」又对应哪个区域。而单塔模型可以做到「区域-词」级别的对齐。
我记得在做一个医疗影像报告生成项目时,双塔模型总是把「左肺上叶有结节」匹配到「右肺下叶有阴影」——因为全局向量太粗糙了。换成单塔模型后,准确率直接提升了15%。
注意:单塔模型虽然精度高,但计算量也大得吓人。一张图和一段文本拼在一起,序列长度可能是512甚至1024,Self-Attention的计算复杂度是O(n²)。所以单塔模型基本只能做离线处理,或者小规模候选集的精排。
3.4 双塔 vs 单塔:怎么选?
这个问题我经常被问到。我的建议是:看场景。
- 如果你做的是大规模实时检索(比如搜索引擎、电商推荐),选双塔模型。先粗排,再用单塔模型精排。
- 如果你做的是高精度离线匹配(比如数据集清洗、图文对齐),选单塔模型。精度优先,速度可以妥协。
- 如果你刚入门,我建议从双塔模型开始。CLIP的代码网上到处都是,跑通一个基线再优化,比直接啃单塔模型要快得多。
下面这张图是我自己总结的架构演进路线,你可以对照着看:
3.5 从Transformer视角重新理解
其实,不管是双塔还是单塔,底层都是Transformer。区别在于:
- 双塔模型:两个独立的Transformer,各自处理自己的模态,最后用对比学习拉近。
- 单塔模型:一个统一的Transformer,图像和文本在Self-Attention里直接交互。
我个人习惯把双塔模型叫做「编码器-编码器」架构,把单塔模型叫做「编码器-解码器」架构。前者适合检索,后者适合生成。
举个例子,CLIP就是典型的双塔模型。它的图像编码器是ViT,文本编码器是Transformer,两个编码器输出的向量通过对比学习对齐。而UNITER是单塔模型,它把图像区域特征和文本词特征拼在一起,喂给一个12层的Transformer。
一个小技巧:如果你想把双塔模型改成单塔模型,最简单的办法就是把两个编码器的输出拼起来,再加一个Cross-Attention层。我在一个图文匹配任务上试过,只加一层Cross-Attention,Recall@1就提升了8%。
3.6 实战中的选择策略
最后,我分享一个我在项目中实际使用的选择策略:
- 先看数据量:如果数据量小于10万,直接上单塔模型。如果数据量大于100万,先上双塔模型做粗排。
- 再看延迟要求:如果要求100毫秒内返回结果,只能用双塔模型。如果允许1秒以上,可以考虑单塔模型。
- 最后看精度需求:如果Top-1准确率要求90%以上,必须用单塔模型做精排。
我曾经在一个视频检索项目里踩过坑:一开始直接上了单塔模型,结果线上延迟飙到2秒,用户疯狂投诉。后来改成「双塔粗排(召回Top-100)+ 单塔精排(重排序)」的级联架构,延迟降到50毫秒,准确率还提升了3%。
嗯,这就是架构选择的艺术——没有最好的模型,只有最合适的方案。