ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

以文搜图和以图搜图,为什么不能共用一个库

以文搜图和以图搜图,为什么不能共用一个库

版权与内容来源声明
本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何不合规的软件获取方式,也不对任何收益结果作承诺。转载请注明出处。

一、三个词混着用,库建完就废

「以文搜图」和「以图搜图」能不能共用一个库,只取决于一个前提——两路编码器是不是成对训练出来的;这个前提不成立,共用一个库只会让两边的检索一起失效。

先看现场:两路查询打的是同一张表

一个图文检索服务上线第一天,两个入口都有人用。文本入口搜「红色的登山鞋」,返回的十条里有六条是无关风景;图片入口上传一张鞋子的照片,返回的十条整整齐齐全是风景照。

翻配置发现,问题不在界面,在库:文本用一个通用句向量模型编码,图片用另一个图像分类网络的中间层特征,两个向量写进同一张表、同一个向量列,查询时算余弦相似度。代码没报错,接口也没超时,就是结果不对。

混用的根源:把「多模态」当成一个筐

这三个词经常出现在同一段技术介绍里,都顶着「多模态」的帽子,边界却是三件事:

  • 单模态嵌入:只吃一种输入(纯文本或纯图像),把「同一种模态里像的东西」放到一起。
  • 图文对比嵌入(跨模态):把图片和文字压进同一个向量空间,让两个模态互相可查。
  • 生成式图文模型:输出是内容本身(一张图、一段描述),优化的是「生成得像不像、答得对不对」。

把它们混起来的典型动作,就是「随便挑两个编码器,把向量放同一个库」。这一步一旦做了,后面无论换多大模型、加多少数据,都补不回来——因为问题不在数据量,在训练目标。

混用的代价:不是差一点,是接近瞎猜

余弦相似度是一个「比较」。比较成立的前提是两个向量在同一个坐标系里。两个各自独立训练出来的编码器,坐标原点、方向和数值尺度都不一样,它们的余弦值没有解释意义。

这不是「效果打点折」,而是「退回到随机」。后文实测给出的数字是:未对齐时命中情况与随机基线几乎重合;一旦对齐,8 对图文能全部命中。差别不在模型大小,在有没有成对训练。

二、单模态嵌入:只管「同类相聚」,管不了跨模态

单模态嵌入(single-modal embedding)的职责只有一条:在同一种模态内部,把「像的东西」拉到一起。它从来没有承诺过「文字能和图片对上」。

它是什么:一种模态进,一个向量出

文本编码器把一句话压成一个定长向量;图像编码器把一张图压成一个定长向量。这是两种独立的模型,训练时各自看各自的数据:文本模型看的是句子之间的相似关系(同义句靠近),图像模型看的是图片之间的相似关系(同类图靠近)。

一句话判断它管什么:它负责「排序」,不负责「配对」——它能把同类排到前面,但不能保证文字排到对应的图前面。

它管什么:模态内部的远近关系

  • 聚类与去重:一批文本或一批图片,按远近先分组。
  • 同类召回:用一张图找「长得像的图」,用一句话找「意思像的话」。
  • 当特征用:把向量接给分类器或排序模型。

不该用它做什么:跨模态检索和「拼接对齐」

这是最常踩的两脚:

  • 拆开跨模态用。拿文本编码器的输出直接去查图像向量库(或反过来)。两边各自在自家模态里都挺好,凑在一起命中就退到随机附近。
  • 拼接当对齐。把文本向量和图像向量首尾接成一条更长的向量,指望它是「跨模态向量」。这只是把两个不可比的东西拼长了,维度变了,可比性没变。

想让图文可比,必须让两个编码器在同一次训练里一起学——这就进入了下一个概念。

三、图文对比嵌入:职责是让图文共享一个空间,前提是成对训练

图文对比嵌入(跨模态)是唯一能让「以文搜图」和「以图搜图」共用一个库的东西;但它成立的前提很硬——两路编码器必须在图文配对数据上一起训练。

它是什么:成对图文一起训出来的两个编码器

做一次对比学习:一个批次里放若干图文配对,训练目标是让成对的图文向量更接近、不配对的更远。约束加在两个编码器上,结果就是它们的输出落在同一个向量空间里。因为空间是共享的,所以文本能查到图,图也能查到文本。

这件事在官方文档里怎么写的,逐字核过一遍。CLIP 论文摘要(OpenAI,arXiv:2103.00020)里,这件事被写成:「the simple pre-training task of predicting which caption goes with which image is an efficient and scalable way to learn SOTA image representations from scratch on a dataset of 400 million (image, text) pairs collected from the internet」。摘要接着说明训练后的用法:「After pre-training, natural language is used to reference learned visual concepts (or describe new ones) enabling zero-shot transfer of the model to downstream tasks.」

官方仓库 README 的说法更直白:「CLIP (Contrastive Language-Image Pre-Training) is a neural network trained on a variety of (image, text) pairs.」而 CLIP 的模型卡写明了训练目标:「These encoders are trained to maximize the similarity of (image, text) pairs via a contrastive loss.」——注意主语是复数:两个编码器,一起被同一个损失约束。

论文摘要还给出这个做法在 ImageNet 上的位置:「we match the accuracy of the original ResNet-50 on ImageNet zero-shot without needing to use any of the 1.28 million training examples it was trained on.」

不该用它做什么:生成、精细计数、事后「对齐」

  • 别让它生成。它输出的永远是向量,不是像素,也不是句子。要内容,去找生成模型。
  • 别拿它做精细计数和空间关系。同一份模型卡在「局限」一节里写明,CLIP 在细粒度分类和数物体这类任务上比较吃力,且明确把未做域内测试的部署列为超出适用范围。
  • 别先分开训再「对齐」。共享空间是训练出来的,不是事后配出来的。两个已经各自训练好的编码器,中间加一个投影层去「蹭」到一起,本质上还是重新训了一遍,且需要配对数据——那不如直接做对比训练。

实测:未对齐与对齐后的相似度矩阵

下面用合成向量模拟两路编码器的输出。未对齐 = 图、文各加较大的独立噪声;对齐后 = 噪声很小、且共享同一个语义中心方向。矩阵的行是图,列是文,对角线就是正确配对。

🧪 实测环境:Python 3.13.12 / macOS / numpy 2.5.4

importnumpyasnp rng=np.random.default_rng(11);N,D=8,16centers=rng.normal(size=(N,D));centers/=np.linalg.norm(centers,axis=1,keepdims=True)img_un=centers+rng.normal(size=(N,D))*1.0txt_un=centers+rng.normal(size=(N,D))*1.0img_tr=centers+rng.normal(size=(N,D))*0.15txt_tr=centers+rng.normal(size=(N,D))*0.15norm=lambdax:x/np.linalg.norm(x,axis=1,keepdims=True)defdiag_gap(img,txt):S=norm(img)@ norm(txt).T diag=np.diag(S).mean()off=(S.sum()-np.trace(S))/(N*(N-1))returndiag,off,S np.set_printoptions(precision=2,suppress=True,linewidth=140)forname,im,txin[("未对齐时相似度矩阵(对角线应无明显优势):",img_un,txt_un),("对齐后相似度矩阵(对角线明显领先):",img_tr,txt_tr)]:d,o,S=diag_gap(im,tx)print(name);print(S);print(f" 对角线均值={d:.3f}非对角均值={o:.3f}差距={d-o:+.3f}");print()hit=lambdaim,tx:(norm(im)@ norm(tx).T).argmax(axis=1)==np.arange(N)print(f"图文检索命中:未对齐{hit(img_un,txt_un).mean()*100:.0f}% → 对齐后{hit(img_tr,txt_tr).sum()}/{N}(8 对)")
未对齐时相似度矩阵(对角线应无明显优势): [[-0.16 0. -0.21 0.14 -0.15 -0.3 -0.29 0.24] [ 0.09 0.21 -0.23 0.41 -0.06 0.1 -0.26 -0.11] [-0.15 -0.37 0.23 -0.3 0.58 0.34 0.02 0.05] [-0.26 -0.34 0.13 -0.28 0.17 -0.11 0.17 0.05] [-0.14 -0.11 0.21 0.17 0.31 -0.15 -0.05 0.05] [-0.01 0.05 -0.57 0.11 0.25 0.04 -0.42 -0.14] [-0.34 -0.06 0.11 -0.13 -0.58 -0.38 -0.23 -0.14] [-0.12 -0.13 0.36 -0.13 0.16 -0.14 0.21 0.1 ]] 对角线均值=0.027 非对角均值=-0.048 差距=+0.075 对齐后相似度矩阵(对角线明显领先): [[ 0.73 -0.04 0.24 -0.1 -0.03 0.22 0.05 0.03] [ 0.02 0.78 -0.36 0.11 0.01 -0.04 0.07 -0.41] [ 0.28 -0.08 0.82 -0.22 -0.2 0.16 -0.54 0.22] [-0.45 -0.01 0.28 0.64 -0.01 0.05 -0.14 0.14] [-0.34 0.09 -0.18 0.4 0.58 0.16 0.09 -0.25] [-0.13 0.15 0.37 -0.05 0.03 0.59 -0.44 -0.13] [ 0.27 0.18 -0.22 -0.18 -0.08 0.17 0.66 -0.01] [-0.06 -0.36 -0.04 0.13 -0.1 0.03 -0.07 0.87]] 对角线均值=0.709 非对角均值=-0.023 差距=+0.732 图文检索命中:未对齐 12% → 对齐后 8/8(8 对)

读法在后文第五节展开,这里先钉一个数字:未对齐时,对角线的平均相似度只比非对角线高 0.075,8 对图文的命中情况与随机基线(约 12.5%)基本重合。

四、生成式图文模型:负责生成,不负责检索

生成式图文模型的价值在「造内容」,把它当检索编码器用是错位使用——它的目标函数里没有「向量距离可比」这一项。

它是什么:输出是内容本身,不是向量

扩散类文生图模型、图像描述模型、多模态对话模型都属于这一类。它们吃提示(或图片),吐像素或文字。你可以从网络中间抠出一个隐表示,但这个表示是为「生成本身」服务的,不是为了「两两比较」训练的。

不该用它做什么:当检索编码器

  • 不要用它的中间表示入库检索。维度和数值尺度都不是为距离可比设计的,算出来的相似度没有解释意义。
  • 不要拿它的「打分」代替检索打分。生成模型判断「这段描述像不像这张图」和向量距离是不是一回事,两者不能互换。
  • 一条能跑但走远了的路线:先让多模态模型给每张图写一段描述,再把描述丢给纯文本检索。这属于「生成 + 单模态检索」的两段式,能跑通,但它和「共享向量空间」是两套东西——多了一层描述损耗,描述写错就等于检索错。

一张表:三者的边界对照

概念输入 → 输出管什么(职责)不管什么典型误用
单模态嵌入单一模态 → 向量同模态内部的远近与排序(聚类 / 去重 / 同类召回)跨模态的可比性拿文本向量去查图像向量;把两路向量拼接当跨模态
图文对比嵌入(跨模态)图 / 文 → 同一个向量空间图文共享空间,支撑双向检索与零样本分类生成像素或句子;精细计数与空间关系让它生成内容;先分开训再事后「对齐」
生成式图文模型提示 → 像素 / 文本生成内容(文生图 / 图生文 / 图文问答)向量距离的可比性把中间表示当检索编码器;用生成打分代替检索打分

向量检索与多模态检索的动手章节:这一章讲的是「编码器怎么分工」,配套视频课里有 Embedding+RAG 的完整动手部分,正好接得上。放在资料包里,扫码即可获取:

五、判断对齐与否:看对角优势,不看平均相似度

判断两路编码器是否共享空间,只看一个量——正确配对的对角优势;整体平均相似度是个会骗人的指标。

判据:对角优势,不是平均相似度

把图库向量和文本向量摆成一个矩阵(行是图,列是文),对角线就是「正确的图文配对」。两件事同时成立才算对齐:

  1. 对角线上的相似度明显高于非对角线;
  2. 用「每张图找最相似的文本」做检索,命中情况显著高于随机基线(N 对图文,随机基线约为 1/N)。

为什么不能看平均相似度?因为平均相似度会被「整体偏置」拉平。如果所有图向量和所有文本向量都挤在一个很窄的锥形区域里,彼此都挺像,平均值可以很高,但对角线并不突出,检索照样错。所以验收要看「对角减非对角」的差,而不是一个总和。

官方仓库 README 也把衡量方式写明了:模型返回的是「The values are cosine similarities between the corresponding image and text features, times 100.」——用余弦相似度衡量图文匹配,这是共同口径。

实测:未对齐与对齐后的四个量

🧪 实测环境:Python 3.13.12 / macOS / numpy 2.5.4;合成向量模拟,8 对图文 / 16 维,随机种子固定,取数日期 2026-10-13

状态对角线均值非对角均值差距(对角 − 非对角)检索命中(8 对)
未对齐(两个编码器各自独立)0.027−0.048+0.0751/8(12%,随机基线约 12.5%)
对齐后(共享同一语义中心、成对训练)0.709−0.023+0.7328/8(全部命中)

口径说明:上表来自合成向量模拟,不是真实模型的跑分;它要验证的是结构关系——未对齐时对角优势极小、命中回到随机附近;对齐后对角优势被拉开、命中拉满。同一套口径取自本文第三节的原样输出。

还有一个容易忽略的评测口径问题:同一个数字,可能测的不是同一回事。CLIP 论文摘要给出的定位是「we match the accuracy of the original ResNet-50 on ImageNet zero-shot without needing to use any of the 1.28 million training examples it was trained on」——站在同一个 ImageNet 准确率上,一边是在 128 万条标注样本上全监督训出来的,一边是零样本迁移过来的,数值相同、含义不同。落到本文场景就是:一个只报「平均相似度 0.5」的系统,不足以说明它对齐了,要看这个 0.5 的构成里,正确配对是不是被单独拉起来了。

组合分工与停手条件

查询类型用哪个编码器编码查询检索哪个库备注
以文搜图文本编码器图像向量库查询与库必须来自同一对训练好的编码器
以图搜图图像编码器图像向量库用同一路图像编码器,别混进别的视觉模型
图文混合检索文本、图像各编一次,分别召回再融合图像向量库融合权重按评测口径调,别凭手感定
生成图像 / 描述生成式图文模型不涉及向量库生成结果若要入库,得先过对比嵌入编码器

落到代码上,「不共用一个库」其实只有两条接线规则:入库和查询必须走同一路编码器,两路编码器必须来自同一次成对训练。

⚠️代码待验证

# 以文搜图:入库与查询必须走同一对编码器(此处以文本查询为例)img_db=image_encoder(all_images)# 入库:图像编码器img_db=img_db/np.linalg.norm(img_db,axis=1,keepdims=True)q=text_encoder([user_query])# 查询:与它配对的文本编码器q=q/np.linalg.norm(q,axis=1,keepdims=True)scores=q @ img_db.T# 同一空间里算余弦相似度top10=np.argsort(-scores[0])[:10]

什么时候该停手,给三条可执行的判断:

  • 对角差距只在 0.0x 量级,且检索命中接近 1/N→ 没对齐,别上线;先回去查两路编码器是不是成对训练的。
  • 换过任何一路编码器(哪怕只换了图像那一路)→ 空间就变了,上面这张表要重跑,不能沿用旧结论。
  • 验收时别用「平均相似度看起来挺高」→ 那是上一节说的假信号;要么看对角优势,要么看给定查询下的命中情况。

大模型学习路线图与工程落地专题:本章的判据最终要落到一个能验收的检索服务上,配套资料里的路线图从「能跑通」到「能上线」分了阶段。放在资料包里,扫码即可获取:

附表 A:本文引用事实与出处对照表

#事实(含逐字原文的按原文照录)出处(文档 / 发布方 / 链接)本文位置
1「the simple pre-training task of predicting which caption goes with which image is an efficient and scalable way to learn SOTA image representations from scratch on a dataset of 400 million (image, text) pairs collected from the internet」《Learning Transferable Visual Models From Natural Language Supervision》摘要,OpenAI,https://arxiv.org/abs/2103.00020第三章
2「After pre-training, natural language is used to reference learned visual concepts (or describe new ones) enabling zero-shot transfer of the model to downstream tasks.」同上,论文摘要第三章
3「we match the accuracy of the original ResNet-50 on ImageNet zero-shot without needing to use any of the 1.28 million training examples it was trained on.」同上,论文摘要第三章
4「CLIP (Contrastive Language-Image Pre-Training) is a neural network trained on a variety of (image, text) pairs.」CLIP 仓库 README,OpenAI,https://github.com/openai/CLIP第三章
5「The values are cosine similarities between the corresponding image and text features, times 100.」CLIP 仓库 README,OpenAI,https://github.com/openai/CLIP第五章
6「These encoders are trained to maximize the similarity of (image, text) pairs via a contrastive loss.」;同一份模型卡在局限一节说明 CLIP 在细粒度分类、数物体等任务上表现较弱,并把未做域内测试的部署列为超出适用范围CLIP Model Card,OpenAI,https://github.com/openai/CLIP/blob/main/model-card.md第三章、第四章
7合成向量模拟:未对齐时对角均值 0.027 / 非对角均值 −0.048 / 差距 +0.075 / 命中 1-of-8;对齐后 0.709 / −0.023 / +0.732 / 8-of-8(8 对、16 维,随机种子固定)本文实测(本地运行,取数日期 2026-10-13)第三章、第五章

写在最后:这篇用到的资料

写这篇文章时,把相关的官方文档和源码又翻了一遍,顺手也整理了几份配套的东西:

  • 大模型学习路线图:从零基础到能自己动手做 Agent,按阶段说明每一步该学什么、哪些可以先跳过
  • 《LangChain + LangGraph + MCP 智能体开发实战》视频课:7 个模块,从私有化部署、Embedding+RAG 到 MCP+Agent 全流程
  • AI 大模型知识库(在线可查):Agent Skills 从入门到落地、Claude Skills 完全指南等专题,按目录浏览即可
  • 640 套 AI 大模型行业报告 + 经典 PDF 书籍:看行业落地案例和别人怎么做的时候用得上
  • 大模型零基础到精通教学视频:跟着敲一遍,比只读文档快得多

资料是我自己整理的,放在下面这个码上,扫码即可获取:






添加时备注「AI」,优先通过。

资料按「先路线、再动手、最后查漏」的顺序整理好了,建议先看学习路线那一份,照着它挑一条适合自己当前基础的路径再往下看。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进