4.10 视觉语言模型CLIP
1.为什么要让模型同时看懂图和文字
说起来,前面很多章我们聊的都是单边的事情。要么是卷积网络盯着图片做分类,要么是语言模型对着一段文字做预测,两边各跑各的,井水不犯河水。这种格局训练起来固然清爽,可一旦遇到稍微复杂的需求就使不上劲。比方说我拿一张照片去问模型,这只猫在做什么呢,单边的视觉模型多半只能吐出一个猫的标签,至于做什么、什么颜色、在哪儿,它一句话都搭不上来。
OpenAI在2021年发布的CLIP(Contrastive Language-Image Pre-training,对比式语言-图像预训练)想做的就是把这堵墙拆掉。它的野心说起来其实挺朴素,就是让一个模型同时看懂图和文字,把这两种截然不同的信号拉到同一个表示空间里去。为了这件事,他们从互联网上爬了大约4亿对图文配对来训练。4亿这个数字,我记得当时看到时愣了一下,5.1 节里我们聊过预训练,预训练的核心思路就是靠海量数据把通用的东西先学到手,CLIP用的数据量,恰恰是把这种思路搬到了多模态的舞台上。
CLIP最后学出来的本事,用一句话概括,就是图像和文字可以在同一片空间里被比较,谁的向量离谁近,谁就算是在说同一件事。这个能力听起来不起眼,可它后来成了整整一大波多模态模型的地基,这件事我们放到后面慢慢说。
2.双塔结构:两个编码器各管一边
CLIP的整体结构,业界管它叫双塔。什么叫双塔呢,就是两个编码器各自独立地工作,一座塔管图,一座塔管文字,两座塔的输出最后在某一个汇合处碰头比较。
负责图的那座塔,叫图像编码器,记作 , 就是function(函数)的缩写, 代表Image(图像)。它把一张图吃进去,吐出一个固定长度的向量。CLIP论文里这座塔用了两种结构,一种是ResNet(3.10 节里提过的那种带残差连接的卷积网络),另一种是更新一些的ViT(把图片切成小块再喂给Transformer)。负责文字的那座塔,叫文本编码器,记作 , 代表Text(文本)。它用的是Transformer那套编码器结构,把一句话吃进去,也吐出一个固定长度的向量。两座塔各自独立,参数也不共享,各学各的本事。
光各自吐向量还不够,还得想办法让这两边的向量能直接比较。最常用的比较方式叫余弦相似度,记作 , 和 是两个待比较的向量,公式是这样:
分子 是两个向量的点积,分母里的 和 各自是向量的长度(模长),整个式子的几何含义就是两个向量夹角的余弦值。夹角越小,方向越一致,相似度就越接近 。夹角越大,相似度就越接近 ,甚至取到负值。把图像向量和文本向量都用这个公式一比,就能知道两者对得对不上。
接下来是训练目标。CLIP用的就是4.9 节里聊过的对比学习,思路记得吧,让匹配的样本对靠近,让不匹配的样本对拉开。具体到一个训练用的批量里,假设批量大小是 , 表示这个批量里一共有多少对图文,那么这个批量其实暗含 个正样本对(图和它原本配的那句文字)以及 个负样本对(图和别人的文字胡乱配)。CLIP要优化的损失函数长这样:
上面这个式子只写了图像到文本这一个方向,即拿一张图去批量里的 句文字里挑配对的那一句。CLIP实际优化的,是这个方向和反过来的文本到图像方向(把 和 的角色对调,拿一句文字去 张图里挑配对的那一张)取平均之后的对称损失,两个方向一起算,对齐才稳。
里头符号不少,我一个一个解释。 是第 张图经过图像编码器得到的向量, 是第 句文字经过文本编码器得到的向量, 是批量里第 句文字的向量, 是另一个用来在批量里挨个遍历的编号。 是指数函数, 是一个可学习的温度参数(temperature),用来控制这一步算出来的分布有多尖锐, 越小,模型对最相似的那一项就越敏感。整个分母其实是把第 张图和批量里所有 句文字的相似度都指数化以后再相加,分子只挑出真正配对的那一句。这个形式本质上就是一个softmax(1.4 节里我们从信息论的角度聊过它),只不过作用的对象是图文之间的相似度。这种损失在对比学习里头有个专门的名字,叫InfoNCE损失。说穿了,模型在每个批量里要做的,就是从 句文字当中把真正属于这张图的那一句挑出来。
我记得有一部讲翻译官的剧里头有这么一句台词,做翻译难就难在要从一堆近义词里挑出最贴切的那一个。CLIP在训练时干的活,和这句台词描述的处境挺像,只不过挑的对象从近义词换成了配对的文字。
3.零样本分类:不用专门训练就能分类
讲到这儿,CLIP最让人眼前一亮的本事可以登场了,就是零样本分类。
传统的图像分类是怎么做的呢,先定好一堆类别(猫、狗、车、飞机),再拿带标签的数据专门去训一个分类头,模型最后在这么几个固定类别里头挑一个。这种做法的毛病在于,类别一旦定死,想加一个新类别就得重新标注、重新训练,费力不讨好。小明之前做过一个识别校园植物的小项目,光是采集本地标注就熬了半个月,后来想加几种新植物又得再熬一遍,他换了CLIP的思路以后,半天就把新类别接上了。
CLIP的做法完全不走这条老路,它把分类这件事重新表述成一次图文匹配。具体怎么做呢,先把每一个类别的名字写成一句完整的自然语言,比方说一张狗的照片,对应的句子不妨写成这张是一只狗。把这些候选句子都送进文本编码器,得到每个类别对应的文本向量。再把要分类的那张图送进图像编码器,得到图像向量。最后挨个算图像向量和每个类别文本向量之间的余弦相似度,谁的相似度最高,模型就把这张图归到那个类别。整个过程里,模型没有为新分类任务做任何专门的训练,它只是把预训练阶段学到的那套对齐能力直接搬过来用。
这里头其实藏着5.1 节预训练思路的影子。预训练的赌注在于,前头把通用的本事学扎实了,后面遇到具体任务就能少花钱多办事。CLIP把这个赌注押得更狠,连专门的分类头都不给你接了,直接靠语义对齐去回答。零样本之所以能做到,恰恰是因为CLIP在4亿对图文上学到的,是图和文字之间真正意义上的对应关系。有了这种对应关系打底,它才能泛化到从没见过的类别上去,换一套类别名就立马能开干。
当年那张著名的CLIP能力图,横轴是预训练数据量,纵轴是在各个基准数据集上的零样本精度,曲线一路往上爬,最后在不少任务上能跟专门训练过的ResNet打平甚至反超。这个观察后来被总结成一句很朴素的话,就是数据量上去以后,对比式预训练会涌现出(涌现这个词在5.1 节提过)泛化到新类别的能力。有个运动员曾经说过一句大意如此的话,基本功练到位了,临场怎么变都接得住,CLIP的能力图大概就是这个意思的算法版本。
4.打通视觉和语言之后
CLIP最长远的意义,其实要从它对后续研究的带动来衡量。它真正打通的事情,是让图像和文字有了同一种语言。
这件事以前其实挺难的。4.9 节里我们聊对比学习,主要还是视觉这边自家的家务事,图像和图像之间做拉近拉远。CLIP把这种思路跨界搬到了图文之间,等于说给视觉这边的向量配上了一个文字这边的向量,两者可以无缝比较。这种对齐带来的好处,是后面所有多模态模型都吃到了红利。
举几个例子。BLIP(Salesforce提出的视觉语言模型)借鉴了CLIP那种图文编码器并排对齐的思路,自己用的是基于ViT的图像编码器和基于BERT的文本编码器,并提出了编码器与解码器混合的MED架构,再配合带标题过滤的自举训练,能干的事情就从匹配扩展到了看图说话。Flamingo(DeepMind推出的多模态模型)更进一步,把图像编码器的输出喂给语言模型,让模型可以就着几张图、几句话的上下文做对话。再往后到了文生图这边,不管是Stable Diffusion还是那一批以文生图为主业的模型,文本那头怎么把一句话稳稳地变成一个能控制图像生成的条件信号,背后大都借用了CLIP那套文本编码器或者它的对齐思路。说起来CLIP本身并不直接生成图,可它把文字这把钥匙打磨好了,后面的人拿这把钥匙去开门,门才好开。
我之前翻过一本讲建筑史的小书,里头有一句话大概意思是,地基打得深的房子,将来加几层都不会慌。CLIP在多模态这条线上扮演的角色,就有点像这个地基。
5.能用来做什么
最后我们说点接地气的,CLIP到底能用在哪些地方。
第一类是零样本识别新类别。前面讲过了,给定一样新的东西,比方说一种没见过的鸟类、一个新出的商品型号,只要能把它的名字写成一两句描述,CLIP就能直接用来识别,不用重新训练。这个对那种类别频繁更新的场景特别划算,电商、内容审核、长尾识别都能沾上光。
第二类是以图搜文和以文搜图。把整个图库的图都过一遍图像编码器,得到一堆图像向量。再把候选的文字(标签、标题、描述)都过一遍文本编码器,得到一堆文本向量。两边向量都存起来,检索的时候,给一张图就去找文本向量里余弦相似度最高的那一句,给一句话就反过来去找图像向量里最像的那一张。整个过程不需要标注数据,也不需要专门训一个检索模型,搭好两座塔就能上手。小张之前帮一家小书店做书封检索,用的就是这个套路,用户拍一张封面就能把书找出来,连库存都顺手对上了。
第三类是作为更复杂的多模态系统的组件。这一点前面提过了,很多后头的模型(BLIP、Flamingo、文生图那一批)都把CLIP当成一个现成的对齐模块来用,等于说CLIP自己成了一个被反复调用的接口。
练习
Q1. CLIP为什么采用"双塔"结构,而不是把图像和文字塞进同一个网络一起处理?
双塔就是图像编码器 和文本编码器 各自独立工作、参数不共享,两座塔的输出最后在某处汇合做比较。这样做的好处是图和文两边可以各自选最适合的骨干(图这边用ResNet或ViT,文这边用Transformer),而且可以把整个图库和候选文字提前各自编码成向量存起来,检索时只算余弦相似度,不用每次重新跑编码器,效率高。如果塞进一个网络,每次查询都得重算,扩展性差。
Q2. CLIP做零样本分类时,假设批量大小 ,InfoNCE损失的分母里只剩配对那一项,这时候损失还能正常起作用吗?
不能正常起作用。InfoNCE依赖批量里的负样本( 个错配对)来提供对比信号。 时分母里只有那一个正样本,softmax输出恒为 ,损失恒为 ,模型学不到任何区分正负的东西。这就是为什么CLIP训练时要用很大的批量(论文里动辄上万),批量越大、负样本越多,对比学习才越有信号。这也是CLIP需要海量算力的重要原因。
Q3. 易错点:有人说"CLIP做零样本分类时,类别名写成'猫'和写成'一只可爱的猫'效果一样",这对吗?
不对。CLIP是把每个类别名先扩写成一句完整自然语言(比如"一张猫的照片")再过文本编码器,文本侧算的是这句话的语义。类别名措辞不同,得到的文本向量也不同,余弦相似度排序就可能变。CLIP论文里专门讨论过这种"提示工程"(prompt engineering),用合适的提示模板(比如"一张{类别}的照片")通常比单写类名精度更高。这说明零样本分类对文本表述是敏感的,不是随便写个名就行。