前阵子帮一个朋友调一个图像检索和描述匹配的小功能,图像模型单独跑、文本模型单独跑都没问题,一旦把两者接到一起,准确率直接跳水。排查到最后发现,问题不在两个模型本身,而在我根本没想清楚它们各自的特征到底该怎么“相遇”。这件事让我重新把CNN和RNN这两条技术线梳理了一遍,也顺便把多模态处理从最基本的搭建逻辑到今天的Transformer主线走通了一遍。这篇博文就想聊聊这个脉络——CNN和RNN为什么是AI跨域理解的基石,它们各自解决了什么问题,以及多模态处理是如何在这两块地基上一点点进化的。适合想系统梳理技术路线,或者正打算上手多模态项目的同学,当作一份可以对照的实战笔记来读。
1. 从像素和字序讲起:CNN与RNN各自的看家本领
1.1 CNN:用“局部窗口”理解图像
今天大家聊大模型、聊多模态,很容易把CNN当成一个“过气”的架构。但真要把视觉特征抽好,卷积神经网络依然是大多数场景下最稳的选择,没有之一。它的核心设计非常朴素:图像在计算机眼里是像素矩阵,如果让全连接网络去学,一个100x100的输入光第一层权重就要上百万参数,训练既慢又容易过拟合。CNN换了个思路——用一个固定大小的窗口在图上滑动,同一组权重在不同位置反复使用,这叫做“参数共享”。窗口关注的区域叫“感受野”,滑动的过程就是卷积。
参数共享背后其实藏着一个很强的先验:自然图像在空间上具有“平移不变性”。一只猫出现在图片左上角和右下角,对人类来说是同一只猫,对模型来说也应该尽量给出类似的判断。卷积把这种先验写进了结构里,所以CNN能用很小的参数量获得很好的泛化能力。池化层则是配合卷积做“信息压缩”:每个小区域内取最大值或平均值,减少后续计算量,同时保留最显著的特征。层数加深之后,浅层学到的是边缘、颜色块、纹理,深层学到的是眼睛、轮毂、建筑轮廓这类语义部件,这种从低层到高层的层级抽象,正是视觉理解最需要的组织方式。
在多模态场景里,CNN通常充当“视觉编码器”的角色。图像进来之后,经过卷积和池化变成一组特征图,再被压缩成向量或特征序列,供后续的文本模态去“读取”。可以这么理解:CNN先把一张图翻译成机器能读懂的结构化笔记,后面的模块才能拿这份笔记去做检索、生成、问答。没有这个压缩和提纯的过程,让Transformer直接处理原始像素,计算量会大到完全没法训练。
1.2 RNN:用“记忆卡片”理解序列
RNN处理的是另一类先验:顺序性。语言有先后顺序,“我吃苹果”和“苹果吃我”的单词相同含义相反;一段视频里的动作也有时间顺序;甚至连传感器采集的信号流,某些结论也依赖前因后果。RNN的做法是维护一个隐状态向量,每读入一个输入就更新一次状态,把这个状态作为“记忆卡片”传给下一个时间步。也就是说,模型在处理第t个词时,手里不仅握着当前词的信息,还握着前t-1个词的所有压缩记忆。
听起来不错,但实战里有一个著名的毛病:普通RNN在长序列上很容易梯度消失。原因在于反向传播时,梯度要从最后一个时间步一路传回第一个时间步,中间要反复乘以同一个权重矩阵。如果这个矩阵的特征值小于1,梯度越传越小,前面的信息根本学不到;特征值大于1,则梯度爆炸。为了解决这个问题,业界普遍采用的是升级版本——长短期记忆网络(LSTM)或者门控循环单元(GRU)。它们的共同点是加入了“门”机制:遗忘门决定旧记忆保留多少,输入门决定新信息写入多少,输出门决定当前隐状态对外暴露多少。用大白话说,就是给记忆卡片加了一支笔和一块橡皮,重要的信息使劲记,不重要的随时擦掉。
在多模态处理中,RNN家族通常负责文本序列的编码。早期很多图像描述系统就是“CNN抽特征 + RNN生成文字”的结构:CNN把图片压缩成一个向量,作为RNN生成第一个词的初始状态,之后每个词都在前一个词的基础上继续生成。这种配合在当时的榜单上非常能打,也是“多模态底下两块基石”最直观的体现。
1.3 两者如何撑起多模态时代的底座
把CNN和RNN放到一起对比,会看得更清楚它们各自的边界。
| 维度 | CNN | RNN / LSTM / GRU |
|---|---|---|
| 核心先验 | 空间局部性、平移不变性 | 时序依赖、状态延续 |
| 擅长任务 | 图像分类、目标检测、图像特征提取 | 文本建模、语音帧序列、时间序列预测 |
| 处理方式 | 窗口滑动、并行度高 | 逐时间步递归、天然串行 |
| 主要局限 | 难以直接刻画长距离全局关系 | 长序列效率低,梯度容易出问题 |
| 多模态中的角色 | 视觉编码器,抽结构化特征 | 序列编码器/解码器,建模语言顺序 |
“基石”这个词不是修辞。任何一个多模态任务,终归要处理两类最基本的信息组织方式:一类像图像,信息铺在二维空间里,关键在“哪里有什么”;另一类像文本,信息按时间展开,关键在“什么先什么后”。CNN和RNN恰好各自回答了这两个问题。即使后来的模型不再直接叫“卷积神经网络”或“循环神经网络”,它们的设计理由依然深深嵌在现代架构里,只是换了外衣。
2. 单一感官的天花板:多模态为什么绕不开“对齐”问题
2.1 多模态的难点是“对齐”,不是“相加”
很多人刚接触多模态时的第一反应是:把一张图的特征向量和一句话的特征向量拼在一起,再丢给分类器或者生成器,不就行了吗?这个思路不能说错,但只适用于极简单的场景。问题在于,图像特征和文本特征各自处于完全不同的向量空间里:图像向量的每个维度对应的是边缘、纹理、颜色分布,文本向量的每个维度对应的是语义、语法、上下文;硬把它们拼接起来,模型并不知道“橘猫”这两个字应该和图像里的哪一团像素对应。
多模态的真正难点,在于跨模态“对齐”。对齐这个词听起来学术,落到实际任务里就非常具体:给模型看一张橘猫趴在窗台上的照片,再给它一句“一只橘猫靠在窗边看雨”,模型要么能做图文检索(给定文本找图、给定图找句),要么能做图文生成(看图写句、写句生图),而要做到这些,它必须先学会把文字里的“橘猫”和图像里那块橘色的像素团建立对应关系。这也正是“跨域理解”在这个语境下的含义——把不同感官通道输入的信息,翻译成同一套语义坐标系里的描述,让模型可以在模态之间互相检索、互相解释、互相生成。
2.2 对齐粒度:从整体句子到像素级别
对齐不是只有一个粒度。刚开始做多模态的人,最容易犯的错就是把对齐想成一个“全局对全局”的问题,但实际上粒度不同,难度天差地别。
句子级别对齐是最粗的粒度:给一批已经配对好的“图像-文本”对,让模型学会判断一张图和一句话是否匹配。这种任务相对简单,因为全局特征已经足够区分。区域级别对齐就麻烦一些:一句“橘猫在窗台上,窗外在下雨”,模型需要知道“窗台”对应图中哪个区域,“雨”对应窗外那些条纹状的像素,这要求视觉特征保留下空间信息,不能把所有区域揉成一团。再往下是词元级别对齐:某个词在图像里具体对应哪个目标框,这个通常需要目标检测器或者注意力机制辅助定位,成本最高,但对描摹细节的帮助也最大。
选哪种粒度,取决于你的业务目标。做图文检索,句子级通常够用;做看图写详细描述,至少要做到区域级;做细粒度编辑,比如“把第二只猫换成同样姿势的狗”,就要接近词元级。我见过不少项目死在“上来就想做最细对齐”上,标注成本撑不住,模型也学不动。稳妥的做法是先粗后细,把粗粒度跑通了再逐步往下扎。
2.3 现实中数据关联远比想象中稀缺
另一个容易被低估的坑是数据。做单模态模型,数据量大不大只是效果好坏的问题;做多模态模型,数据有没有“关联”直接决定了项目能不能启动。所谓关联,就是同一份数据里不同模态必须指向同一件事:一段视频要配上对应的语音转写,一张故障图要配上对应的维修记录,一个商品页面的图片要配上对应的卖点文案。
实际工程场景里,这类成对数据往往是稀缺的。我自己接触过的某个制造业质检项目里,图像检出的缺陷和维修系统里的文本记录是分开存的,时间戳对不上,批次也对不上,光做数据对齐就花了两周。更麻烦的是,人工标注配对非常贵,尤其涉及专业术语的场景,标注员还得先培训。所以启动多模态项目前,第一件事不是选模型,而是盘数据:有多少天然配对的数据?缺失的能不能低成本补?如果答案都是否定的,那再好的架构也是空中楼阁。
3. 进化的第一站:从“晚融合”到注意力机制
3.1 早期融合策略:拼接、后融合、反复交互
早期多模态模型要解决的核心问题,是“两个模态的特征什么时候碰面”。按融合时机不同,大致分成三条路。
早融合(Early Fusion)指把原始特征或浅层特征直接拼接、相加,再喂给后面的网络。优点是结构简单,端到端训练容易;缺点是特征才刚提取出来就被搅在一起,模型压力很大,效果通常一般。晚融合(Late Fusion)指各模态先各自独立编码,到快出结果时才做融合,比如各自得到特征向量后拼接再分类。优点是各模态可以分别用成熟模型预训练,训练时各干各的,交互少不容易崩;缺点是信息交互来得太晚,很多细节关系学不到。混合融合(Hybrid Fusion)则是穿插多个交互层,想模仿人类“边看边想边说”的过程。效果通常最好,但训练复杂度和计算开销也最高,调参难度直线上升。
| 融合策略 | 交互时机 | 优点 | 缺点 | 典型适用 |
|---|---|---|---|---|
| 早融合 | 输入层附近 | 实现简单,端到端 | 交互过早,模态特性被冲淡 | 特征维度低的轻量任务 |
| 晚融合 | 输出层之前 | 各模态可独立优化,稳定 | 交互太少,细节关系丢失 | 双塔检索、粗粒度分类 |
| 混合融合 | 多个中间层 | 细粒度对齐效果好 | 训练慢,调参难度高 | 图像描述、视觉问答 |
这里想多说一句:没有绝对最优的融合方式,只有最匹配任务的融合方式。比如做“文本搜图”,用晚融合的双塔结构就很合适,因为两个塔可以各自把图、文编码成向量,再用向量距离排序;但做“看图回答问题”,问题里的每个词都可能要盯着图像里的某个细节,单靠晚融合肯定不够,必须用更深度的交互。
3.2 注意力机制:让模型自己决定“看哪里”
混合融合听起来很美好,但“在哪几层融合、融合时互相看什么”这个问题,早期是靠人来拍脑袋的。注意力机制的出现,把这份权利交还给了模型本身。
注意力机制的本质,可以理解成“基于相关性的加权平均”。用图书馆检索来类比:你手里拿着一张纸条,上面写着要找的主题(这叫查询,Query);书架上每本书的封面上贴着标签(键,Key);你要做的是把纸条和每个标签比对,算出相似度,然后按相似度高低去取对应的书(值,Value)。严格来说这是缩放点积注意力的简化版,但思路完全一致。注意力机制的价值在于:它把“我应该关注哪里”这件事变成了可微分的计算,模型可以通过反向传播自动学会调整关注的区域。
放在多模态场景里,注意力机制的效果很直观。比如看图写描述的模型在生成“窗台”这个词时,通过注意力权重能看到它把大量概率集中在图像靠下的水平线上;生成“雨”时,注意力又飘向了窗外那些纹理区域。你可以把注意力矩阵可视化出来,颜色深的地方就是模型正在“看”的位置。我第一次跑出这种可视化图的时候,真切感受到模型不再是玄学,它的每一步决策都有迹可循。
3.3 注意力取代循环依赖后带来的连锁反应
注意力机制真正改变格局,是2017年那篇著名论文提出“只用注意力”的架构之后。这里不重复论文推导细节,我只说它给CNN和RNN各自带来了什么连锁反应。
对RNN来说,注意力替代了它的“串行记忆”职责。RNN必须一个词接一个词地处理,当序列很长时,哪怕有门控机制,早期的信息也容易被后续信息淹没。注意力机制则允许序列里任意两个位置直接互动,计算路径从“逐时间步传递”变成了“一步直达”,长距离依赖问题迎刃而解。对CNN来说,注意力替代了它的“局部感受野”限制。卷积的感受野是有限的,要看到整张图必须层层堆叠;注意力天然就是全局的,任何一个位置都能直接看到图上其他所有位置。反过来,Transformer也保留了CNN和RNN的部分遗产——视觉分块加位置编码,相当于在序列化之前先做了一次类似卷积的局部特征化;位置编码则是用显式方式补上了顺序信息,承担了一部分RNN当年的职责。
所以我更愿意把注意力机制的兴起看作是“继承者”,而不是“颠覆者”。它用更灵活的交互方式,把CNN的空间先验和RNN的时序先验整合成了统一的全局建模能力。从那以后,多模态模型在架构层面的核心工作,就变成了“如何让不同模态的token在同一个注意力空间里充分且高效地交互”。
4. 大模型时代:Transformer如何包揽CNN与RNN的家务
4.1 统一token化:图像与文本从此“语言相通”
要说Transformer给多模态带来最大的改变,我首选“统一token化”。在这之前,图像和文本的结构是完完全全不同的——图像是二维像素矩阵,文本是一维词序列,要让它们在一个模型里协作,必须费尽心思搭桥。Transformer的做法很粗暴:图像不也是数据吗?那就把图像切成固定大小的补丁块(patch),每个补丁块拉直后线性映射成一个向量,再加上位置编码,得到一串“视觉token”;文本按词表切分,得到一串“文本token”。到这一步,图像和文本在形式上已经统一了:都是一串向量,都带位置信息,都能丢进同一个Transformer编码器。
Patch的大小是一个关键超参数。切得越小,细节保留越多,但token数量剧增,计算量也跟着暴涨;切得太大,细节丢失,全局建模能力再强也补不回来。实际项目中图像分块尺寸、序列最大长度通常要按显存和任务联合调。我之前在某个图像描述项目里,把分块从32x32改成16x16,description的BLEU分数涨了将近三个点,但训练时间也多了将近一倍,这个权衡需要你按自己的算力去定。
4.2 单一向量空间:把不同模态装进同一个坐标系
统一token化解决的是“形式统一”,真正让两种模态“语义可对话”的,是把它们映射到同一个向量空间。所谓向量空间,你可以理解成一个巨大的坐标系,每个向量是坐标系里的一个点。这个空间在设计之初就被赋予一个期望:语义相近的内容,向量距离近;语义无关的内容,向量距离远。猫的照片和“猫”这个字对应的向量应该挨得很近,而“挖掘机”则离得远远的。
训练一个多模态模型的很大一部分工作,就是在调这个坐标系。方法上,比较常用的是“双塔+对比学习”的思路:图像塔把图片编码成向量,文本塔把描述编码成向量,然后在一个批次里让匹配的图像-文本对互相靠近,让不匹配的互相推远。另一个思路是“单塔+cross-attention”的思路:图像token和文本token直接放进同一个Transformer,让它们通过自注意力在每一层充分交融。双塔结构工程上更容易部署,因为图像和文本可以分别离线抽取向量、建索引,上线后做相似度检索非常快;单塔结构交互更细腻,生成类任务通常表现更好,但推理时要把两种模态一起送进去,计算开销更大。没有谁比谁绝对更好,对应到业务里做检索优先双塔,做生成优先单塔。
4.3 对比学习目标:拉近匹配,推远不匹配
对比学习听起来高深,拆开就是一句话:把匹配的样本对在向量空间里拉近,把不匹配的推远。具体做法是用一个batch里所有样本两两组成pair,正样本对是本来就配对好的图-文,负样本对是“错位拼接”的图-文。比如batch里有“猫的照片-猫的描述”和“狗的照片-狗的描述”,那么“猫的照片”搭配“狗的描述”就是负样本。模型要学的是让所有正样本对的相似度高于负样本对,本质上是个排序问题。
实操里有个细节值得提醒:batch size不要太小。负样本越丰富,模型学到的边界越清晰。我踩过的一个坑是,前期为了省显存把batch size压到16,对比损失下降得倒是平稳,但下游检索准确率一直上不去;后来换成梯度累积把有效batch size提到64,效果立刻上了一个台阶。原因是batch太小的时候,“难负样本”不够,模型很容易用颜色、背景这种浅层线索把图-文糊弄过去,真正语义层面的对齐没有学到。
5. 工程实践中的选型经验与常见误区
5.1 选型判断:别被大模型带偏,小任务仍可用CNN/RNN
现在的技术讨论几乎被Transformer和大模型淹没,但真到了工程落地,我建议先抛开“流行”二字,回到任务本身。如果只是一个单纯的图像分类任务,或者要在资源受限的嵌入式设备上跑实时视觉识别,CNN依然是更经济的选择,速度和稳定性都有保障。如果任务对象是短文本序列、且推理环境对显存极其敏感,RNN家族里的GRU也是值得考虑的选项。我自己在好几个低功耗项目里都选择了轻量卷积网络加一个简单序列模型的组合,效果足够,功耗还低。
多模态项目的架构选型,应该从“数据量和业务形式”倒推,而不是从“最火的模型”正推。数据量少,就别一开始就上几十亿参数的大底座,先用一个CNN视觉塔加一个文本编码塔把流程跑通,验证业务可行性;数据量大且对齐质量高,再考虑用大规模预训练模型做初始化,效果会有明显提升。
5.2 容易被忽略的工程细节:数据配比、精度、归一化
选对架构只是第一步,真正决定多模态项目成败的,往往是那些不起眼的工程细节。
第一个是数据配比。假设你的训练集里文本量是图像量的十倍,模型很容易学会“只读文本就能回答大部分问题”,视觉分支进入“躺平”状态。直观表现就是视觉特征学的约等于零,去掉图像分支效果几乎不变。解决办法是在每个batch里刻意控制图文配对比例,必要时对多出的一方做降采样,逼着模型真正依赖双模态信息。
第二个是混合精度训练。多模态对齐任务里,对比损失常常在一个很小的数值范围内震荡。一旦用fp16混合精度训练,小数值被截断的风险就会放大,出现“loss降得很漂亮、下游指标一动不动”的诡异现象。我遇到这种情况时,会把对齐相关损失的scaler调大,或者关键阶段切回fp32训练几十个step,问题基本就能缓解。
第三个是特征归一化。图像塔和文本塔输出的向量尺度往往不一样,如果不做L2归一化就直接算相似度,尺度大的模态会天然占优势。实测下来,对两个塔的输出都做归一化再求余弦相似度,比不做或者只做一边要稳定得多。
5.3 一个可上手的轻量多模态检索流程
如果你想快速感受一下多模态项目从零怎么串起来,我提供一个之前自己验证过的轻量流程,全部基于开源组件和预训练模型,不需要自己训练大规模底座。
第一步,准备数据。最少只需要几百对“图像-文本”配对数据,哪怕先从网上手工整理一小批都行。第二步,抽特征。图像用预训练的视觉Backbone抽向量,文本用一个预训练的语言编码模型抽向量,都做L2归一化。第三步,建一个简单的对比学习头,把两个向量映射到同一个低维空间,用对比损失微调几个epoch。第四步,评估。对每个文本,在库里检索最相似的图像,看Top-5准确率。
# 示意:轻量级双塔对齐流程的骨架(伪代码) def encode_image(img): # 用预训练视觉Backbone抽特征,例如深度残差网络 feat = image_backbone(img) return normalize(feat) def encode_text(txt): # 用预训练文本编码器抽特征 feat = text_backbone(txt) return normalize(feat) def contrastive_loss(img_vec, txt_vec, temperature=0.07): # 计算相似度矩阵,正样本在batch对角线上 logits = img_vec @ txt_vec.T / temperature labels = arange(len(img_vec)) loss = cross_entropy(logits, labels) return loss这个流程整个跑下来,哪怕只有几百对数据,也能让你直观体会到“对齐”是怎么通过对比损失刻进模型里的。别小看这个最小闭环,我后续好几个正式项目,都是先用它验证数据是否有足够的对齐信号,再决定要不要投入资源上更大的模型。
5.4 误区清单:几个反复踩坑的典型想法
我把这些年看到过的典型误区列成一份清单,每一条都是真实发生过的案例:
- “多模态就是特征拼接”。拼接不等于对齐,只会让模型面对两个互不理解的特征空间。
- “模态越多越好”。多余的模态如果缺乏对齐标注,反而引入噪声。加模态前先问自己一句:它带来的是信息增益还是标注负担。
- “Loss很低,说明模型学好了”。多模态任务里Loss低可能只是因为模型找到了某个模态的捷径,一定要在下游任务上做独立评估。
- “有了开源大底座,数据少也没关系”。底座能帮你初始化,但领域数据有它自己的分布,盲目少样本硬调,经常得到“看起来能用、一上线就崩”的模型。
这些误区背后其实都是同一个问题:把多模态理解当成一个纯模型问题,而忽略了它本质上是一个“数据+架构+训练策略”三方协同的系统工程。
我这些年做多模态相关项目最大的体会,是别急着上复杂模型。先把模态对齐的“最小闭环”跑通,用少量人工标注验证模型真的学会了对应关系,再谈规模和效率。CNN、RNN、注意力机制,说到底都是在对“如何关联信息”这个问题给出不同时代的答案;理解它们的来路,比记住几个新名词更能帮你在项目里做出靠谱的判断。以后如果再有人问“多模态到底难在哪”,我会说难在让两个原本各说各话的模型,终于学会了用同一种语言描述这个世界。