资讯动态

CLIP双塔架构与对比学习原理:零样本分类实践踩坑复盘

发布时间:2026/10/20 4:46:09 来源:尧图企业网站定制
“第三周1CLIP的学习”这个标题写在我学习笔记顶端时我其实有点心虚。前两周我把CLIP相关的论文、解析、别人跑通的demo都过了一遍可真轮到自己用CLIP做点事它就变成一个“背过书但不会做题”的学生让它找“一只戴帽子的狗”它记住了狗却没看见帽子让它区分“货车”和“卡车”它反问我要分类提示甚至同一张图换一次写法结果都能大变。这不是CLIP随机是我根本没理解它内部到底在做什么。这篇文章不是论文复述而是我的学习周记加踩坑复盘。如果你在做多模态方向或者想用CLIP做图文检索、零样本分类、特征抽取又或者和我一样卡在“懂概念但不会用”的阶段这篇记录应该能帮你节约不少试错时间。我第三周的目标很简单不再刷新论文而是把一个能用的CLIP从前到后彻底拆开搞清楚数据是怎么流的、训练是怎么对齐的、推理时每一步为什么要那样写。1. 为什么学了三周我才敢说大致看懂了CLIP1.1 前两周的四个“糊涂账”回头整理前两周的学习笔记我发现自己的问题非常集中基本可以概括成四个点。CLIP到底是分类模型还是特征提取模型论文里那个表格看起来像是分类精度可demo里它又能抽特征这两者的关系我没想明白。训练目标为什么选对比学习而不是让模型直接预测“这张图和这句话是不是一对”直觉上预测式任务更直接为什么要绕一圈。零样本分类究竟是怎么做到的模型没有见过目标类别的训练数据凭什么知道“狗”这个字对应像素上的什么形状。推理相似度计算时为什么要除以一个温度系数我试过不除或者乱填一个数结果都崩了但没人告诉我这个数的物理意义。这四个问题堆在一起导致我那两周的代码全是照着别人拼的自己完全没法变通。1.2 数据流视角从像素和文字到同一个向量空间第三周我换了个思路不先看公式而是把CLIP当成一条流水线来看。图像从一边进文字从另一边进中间经历三步编码、投影、对齐。第一步图像编码器把一张图片的像素矩阵压成一个向量这个向量保留的是图片的视觉内容比如“有一条狗、背景是草地、光线偏暖”。第二步文本编码器把一句话压缩成另一个向量保留的是语义内容比如“这一帧画面描述了一条在草地上的狗”。第三步也是CLIP真正花力气练的一步是把这两个向量映射到同一个向量空间里让“一条在草地上的狗”这么一句话对应的文本向量靠近“画面里真的有草和狗”那张图的图像向量。这个流程一旦在脑子里立起来我之前的第一个困惑就散了CLIP既不是分类模型也不是纯粹的特征模型它是一个双塔对齐模型。分类和检索都是对齐这个核心能力在外面套了一层壳而已。1.3 CLIP真正解决的问题是“对齐”我再打一个比方。银行柜台有十个窗口每天来一批办业务的客户。我们不知道客户具体要办什么但我们可以拿一句话播报“请A001号客户前往2号窗口”。CLIP的训练过程就是让系统学会“哪个客户该去哪个窗口”而不是学会“这位客户是VIP还是普通客户”。传统分类模型学的是后者给定一张图判断它属于哪个预定义类别。CLIP学的是前者给定一张图判断它和哪句话最匹配。类别只是把“寻找匹配这句话”的任务包装了一下换成“和类别名对应的那句话”。一旦理解了这点后面看零样本分类、图文检索、特征抽取其实都是同一套逻辑在不同任务上的表现。2. 双塔架构和对比学习CLIP的核心机制拆解2.1 两个编码器各管各的最后才汇合CLIP的双塔一个塔负责图像一个塔负责文字。图像塔常用的是卷积网络或者视觉Transformer把224×224或者更大分辨率的图片变成图像特征向量文本塔一般用Transformer把句子切分成子词后编码成一个文本特征向量。两个塔的结构可以完全不同、训练时可以部分冻结、升级时可以各换各的骨干网络因为CLIP在意的不是塔内部长什么样而是它们在共享的嵌入空间里能不能对上。接着两个向量都要过一个线性投影层把维度统一到多模态嵌入空间的同一个维度最后做L2归一化。归一化这一步很多人忽略但实际上它决定了后续相似度计算用什么度量——CLIP用的是余弦相似度而归一化之后余弦相似度和内积完全等价。换句话说做矩阵乘法求相似度的时候其实就是在算两张特征谁和谁的方向更一致。我刚开始会想当然地把图像特征和文本特征直接比较结果发现两个塔的输出维度根本不一样也谈不上相似度计算。看完架构图才意识到维度统一是交给投影层和归一化完成的不是编码器本身的责任。2.2 对比学习到底在对比什么正负样本的设定训练一个batch里通常有N个图文对也就是N张图和N句对应的描述。把它们全部送进双塔之后我们会得到一个N×N的相似度矩阵。这个矩阵的对角线是“这张图和自己那句描述”的相似度也就是正样本矩阵里其余位置都是负样本比如第一张图和第二句描述、第二张图和第五句描述它们本来就不该匹配。对比学习要做的事情特别纯粹让对角线上的相似度尽量高让对角线外的相似度尽量低。听上去和普通分类很像但有一个本质区别——分类需要提前定义好“类别”这个中间变量对比学习完全不需要预定义类别它只是朴素地认为“配对的正例应该靠近”。CLIP能在零样本场景下泛化根源就在这里它学到的不是某个固定类别的判别边界而是视觉和语言之间通用的对应规律。2.3 InfoNCE损失的直观理解以及为什么非要用大batchCLIP的训练损失是从InfoNCE变过来的通常写成对称的交叉熵形式。简单说它把每张图在相似度矩阵的那一行变成一次分类任务在N个文本候选中模型要找出哪个才是正确的那一个。同理每个文本在那一列也要从N个图像候选中认出自己的另一半。这里有一个绕不开的参数温度系数。假设某一行里有好几个候选文本的相似度都差不多模型就分不清哪个是正确的温度系数的作用是把相似度的差距放大逼模型做出更明确的区分。数值越小对比越尖锐数值越大分布越平缓。论文里的初始值大约是0.07并且把它设计成可学习的参数训练到后期它可以自己调成更适合数据分布的值。那为什么CLIP训练时要用非常大的batch原因很简单N越大每一行参与对比的负样本就越多模型学到的东西越扎实。只有两三个负样本时它很容易偷懒随便找出一个不太像的就能过关当负样本数量达到数万级别时模型被迫认真区分每一对细微差异。这也是为什么个人电脑很难从头训练一个完整CLIP显存和算力都卡在batch大小上。3. 零样本分类到底是怎么跑起来的从理论到代码3.1 推理流程一张图和一列句子比赛零样本分类的推理核心可以压缩成四行伪代码。第一步输入一张图片经过图像塔得到一个归一化后的图像特征第二步把每个候选类别名拼接进模板句比如“a photo of a {}”变成一句描述第三步所有描述经过文本塔得到一组归一化后的文本特征第四步用图像特征和所有文本特征做矩阵乘法得到相似度除以温度系数再经过softmax输出每个类别的概率。import torch # image_features: (1, dim) # text_features: (num_classes, dim) image_features image_encoder(processed_img) text_features text_encoder(processed_texts) image_features l2_normalize(image_features, dim-1) text_features l2_normalize(text_features, dim-1) logits image_features text_features.T / temperature probs torch.softmax(logits, dim-1)我第三周第一次手写这套流程时最大的感受是原来零样本分类没有任何“识别新类别”的魔法它只是把分类任务转化成了一次检索任务。模型手里拿着一张图在候选的类别描述里找最匹配的那一句。它没见过“金毛犬”的训练图但只要它理解语言中的“金毛犬”对应视觉上的金色、长毛、大型犬这些概念它就能把图匹配对。3.2 为什么模板词能提升精度一个朴素的分布对齐问题刚开始我很不理解为什么直接写类别名“dog”效果差改成“a photo of a dog”效果就好了。后来我用一句话说服了自己训练时CLIP看到的句子绝大多数是自然的完整描述比如“一个人骑着自行车在街上”而不是孤零零的“自行车”。推理时如果你只丢一个光秃秃的名词给它文本端的分布和训练时的分布就错位了。加模板本质上是把推理时的文本拉回训练时的语言分布区间。“a photo of a {类别}”是最常用也最有效的模板之一。如果你想进一步提升精度还可以准备一组模板比如把“a photo of a {}”“a bad photo of a {}”“a sculpture of a {}”全部各过一遍把每个类别的多个匹配分数合并后取平均或取最大这样能进一步压低偶然波动。论文里在公开基准上就是这样做的几十条模板平均下来效果往往比单模板高出几个点。3.3 数据预处理和超参数被忽略的决胜细节跑CLIP推理时真正坑我的往往是预处理不是模型结构。图像端要做的流程一般是调整大小、中心裁剪、转成Tensor、按CLIP专用的均值和方差做归一化。CLIP的归一化参数和常见分类模型不一样我用过一次ImageNet那套标准参数结果精度掉得很明显后来才改成CLIP自带的统计值。文本端则要注意句子要做子词切分序列长度通常要截断到一个固定上限比如77个token超过就截断不足则补占位符。mean [0.48145466, 0.4578275, 0.40821073] std [0.26862954, 0.26130258, 0.27577711]我建议把预处理封装成一个固定的处理函数每次换数据集都复用同一条链路不要每次重写。因为这一步一旦出错后面调什么参数都救不回来。4. 复现实验中的意外情况那些教学文档不会写的坑4.1 温度系数不是摆设一次对照实验让我长了记性我做过一次很草率的对照实验把推理时的温度系数从0.07改成0.1其他条件完全不变结果top-1精度大概掉了一个多点。当时我以为是随机波动反复跑了几次才发现不是。温度变大等于把相似度分布变平模型在两个接近的候选项面前会变得犹豫最终导致错误。温度系数还会放大过拟合。如果用很小的温度模型的输出分布会非常尖锐看起来“信心十足”但一旦遇到分布外数据这种信心就是虚假的。做实际项目时如果某个类别概率突然接近1不要急着相信先检查温度系数设置是否合理。4.2 类别名写法和模板措辞同一张图差出两成都有过我试过一个实验用三组文本去分类同一张猫的图片第一组直接用“cat”第二组用“a cat”第三组用“a photo of a cat”。前两种写法在相似度排序上确实不如第三种稳定但如果把模板换成完全不搭的表达式比如“a drawing of a {}”精度还会继续掉。更麻烦的是类别名的大小写也会影响结果因为文本编码器对大小写不是完全无感的。所以在业务场景里我养成了一个习惯给每类准备5到10个模板做一次小规模验证集评测选出在该领域表现最好的模板组合。这比盲目相信论文里给的通用模板要靠谱得多。我踩过的另一个细节是类别列表的顺序。因为softmax是对所有候选类别的分数做归一化如果一个关键类别被放到列表末尾而前面类别又很多分数偶尔会被稀释掉。虽然理论上不影响结果但实际浮点计算会带来微小波动。为了保险我尽量让候选类别数量保持在几十个以内类别太多时建议分层筛选。4.3 显存不够时的操作顺序先提特征再做运算CLIP的编码器本身不算特别大但如果你在同一个batch里既跑权重又存所有中间特征显存很快就撑不住。第三周我拿一台普通GPU做实验一次性输入200张图加200句文本直接爆显存。后来我把流程拆成三步先把所有图像单独过图像塔把图像特征存到磁盘或者内存里再把所有文本单独过文本塔把文本特征也存起来最后在CPU上做矩阵乘法和softmax。这样操作有几个额外的好处。特征可以复用同一批图像特征如果只是想换几个候选类别重新分类不需要重新过模型特征还可以缓存做大规模检索时先建好图像特征库后面来一个查询文本只需要算一次文本特征和库里的特征做匹配速度会快很多。内存占用从“图片数量乘以整张图的中间特征”变成了“图片数量乘以一个向量”完全不是一个量级。4.4 论文里的分数好看到了自己数据上却“翻车”分布偏差论文报告的分类精度可以达到百分之七八十这个数字能给人很大信心但直接迁移到自己数据上时往往会掉一截。原因在于预训练数据的分布和业务数据的分布不一致预训练语料里大多是常见的互联网图片和自然描述如果业务数据里全是监控视角、医学影像、或者垂直行业里的特殊风格CLIP的零样本能力会大打折扣。最稳妥的做法是在项目最开始先拿一小批带标注的样本做快速验证记录一个baseline。如果这个baseline距离业务要求太远就不要硬调模板词了那点提升杯水车薪。要么收集数据做微调要么换一个更贴近场景的预训练模型。第三周我给自己定了一条规矩任何新数据集必须先跑一次最简单的零样本实验拿到数字后再决定后面的技术路线不再凭论文分数拍脑袋。5. CLIP的边界它解决不了什么比它擅长什么更重要5.1 细粒度分类鸵鸟和鸸鹋的教训CLIP对常见概念的大类区分很强比如“猫”“狗”“汽车”“房子”但这些大概念一旦细下去它就明显吃力。我做过一个实验用“鸵鸟”和“鸸鹋”两个类别去分类一组鸟类照片结果错误率高得离谱因为它掌握的视觉样本里这两种鸟的区分度不够。这个现象不奇怪。CLIP是在约4亿个图文对上训练的网页图文对里关于“鸟类”的很多但关于“鸵鸟和鸸鹋的细微区别”这种细分描述相对少。对比学习只能学它见过的差异没见过的差异它就无能为力。因此如果你的业务是区分多种相似SKU商品、几十种近似的零件、或者细粒度的植物病害零样本CLIP只能作为初筛不能直接交结果。5.2 数量、顺序、空间关系CLIP的“一瞥式”理解更隐蔽的边界在于CLIP对组合性语义的理解。它擅长辨认“一张图里有什么”但不擅长回答“图里有三只猫”“猫在桌子下面”“猫的左边是狗”这类需要精细定位和数量统计的问题。我用一张同时包含两只狗和一只猫的图做过测试文本写成“two dogs and a cat”时还算勉强正确但换成“three dogs and a cat”时因为视觉上狗的特征过于明显模型居然也给出了较高分数。这说明CLIP的文本特征和图像特征对齐的是一种“整体氛围”不是严格逻辑。遇到要理解数量、顺序、空间位置关系的任务别指望零样本CLIP该上检测模型、视觉问答模型或者专门预训练的多模态模型就上。5.3 门槛测试用几张图快速摸清CLIP的底如果你想知道CLIP在具体任务上到底行不行不用做大规模评测先做一个最小测试。准备两类样本一类是你要识别的正类大概10张图一类是容易混淆的负类也准备10张图。用它们各自的类别名加模板跑一次零样本分类输出混淆矩阵。混淆矩阵能回答三个关键问题正类样本的识别率是否稳定负类样本是否被大量误判成正类哪些类别在语义上距离太近导致相似度明显偏高。我记得那次门槛测试里两个视觉上非常接近的品类把分数输出得几乎一模一样我当时就知道在纯零样本条件下不可能把它们分开。这个结论一天就能得到比闷头调模板高效得多。5.4 学完边界之后我的调整思路认清CLIP的边界不是什么坏事反而让我后面的路线清晰了。如果任务在CLIP的能力范围内直接用特征加分类器或者零样本模板就够如果任务落在细粒度或组合性语义上那就老老实实收集数据做微调或者换更专用的模型。微调CLIP时我给自己定了两个原则第一保留双塔对齐的训练方式不要简单把图像塔后面接一个分类头那样容易把预训练学到的跨模态对齐能力破坏掉第二微调数据至少要覆盖目标场景的典型分布只拿几十张样例去微调效果往往不如加几个好模板做零样本。第三周的学习让我最大的变化是不再把CLIP当成一个万能的模型去买而是把它当成一对“能对齐图文语义的编码器”。遇到任务先判断边界再决定用零样本、特征抽取还是微调整个过程变得可控多了。如果让我给刚开始学CLIP的人一个建议我会说先别看那么多解析先去把推理流程跑通再去读框架的源码细节最后回头对比论文里的公式你会发现自己读懂的深度完全不一样。第三周1只是这个学习计划里的一步但这一步踩扎实了后面再往多模态方向走底气会足很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑