多模态模型实现原理
适合读者:有一点 Python / PyTorch 基础,知道张量、
nn.Linear、训练五步。
阅读目标:搞清多模态「输入如何变成统一表示、如何融合、如何训练与推理」,而不是死记论文名词。
1. 多模态是什么
模态(Modality)= 信息的一种形态。常见有:
| 模态 | 例子 | 计算机里通常先变成 |
|---|---|---|
| 文本 | 句子、问题、字幕 | token 序列 → 向量序列 |
| 图像 | 照片、截图 | 像素 / 图像块 → 向量序列 |
| 音频 | 语音、音乐 | 波形 / 频谱 → 向量序列 |
| 视频 | 连续帧 + 可选声音 | 时空特征序列 |
| 表格 / 数值 | 学习时长、出勤率 | 特征向量(你做过的小模型) |
多模态模型= 能同时理解并联合使用两种及以上模态的模型。
典型任务:
- 图文检索:「找和这句话最像的图」
- 视觉问答(VQA):看图回答问题
- 图像描述:给图片写一段话
- 语音助手:听声音 + 看屏幕再回答
- 文档理解:PDF 里的文字 + 版面 + 插图一起读
和单模态的本质差别:
单模态:同一种数据 → 一种编码器 → 输出 多模态:多种数据 → 各自编码 → 对齐/融合 → 联合推理 → 输出2. 核心原理(先建立一张总图)
多模态实现可以压成一句话:
把不同模态编码到可比较、可交互的表示空间,再在这个空间里融合与推理。
总流程:
图像 / 文本 / 音频 ... │ ▼ 各模态编码器(Encoder) │ ▼ 投影 / 对齐(Projection / Alignment) │ ▼ 融合模块(Fusion) │ ▼ 任务头(分类 / 生成 / 检索)三个关键词:
- 编码:每种模态先变成向量(或向量序列)
- 对齐:让「狗的图片」和「狗」这个词在空间里靠近
- 融合:让模型在推理时能互相参照(看图答题时既看字也看图)
没有对齐,融合只是把两堆无关数字拼在一起;没有融合,模型很难做「结合两种信息才能回答」的任务。
3. 为什么要对齐:共享语义空间
不同模态原始形态差很远:
- 图像:像素网格
- 文本:离散 token
- 音频:时间序列波形
直接相加或拼接,模型很难知道「左边这堆数」和「右边那堆数」在说同一件事。
因此常见做法是学一个共享语义空间:
图片编码 → 投影 → 向量 v_img 文本编码 → 投影 → 向量 v_txt 训练目标:若图文匹配,则 v_img 与 v_txt 相似; 若不匹配,则尽量不相似。相似度常用余弦相似度。训练后会出现:
- 「一只猫」的文本向量 ≈ 猫图片的向量
- 「汽车」远离猫图片向量
这就是 CLIP 一类模型的核心思想:对比学习对齐图文。
直觉公式(概念层面):
sim(vimg,vtxt)=vimg⋅vtxt∥vimg∥ ∥vtxt∥ \text{sim}(v_{img}, v_{txt}) = \frac{v_{img} \cdot v_{txt}}{\|v_{img}\|\,\|v_{txt}\|}sim(vimg,vtxt)=∥vimg∥∥vtxt∥vimg⋅vtxt
批量里让正确图文对的相似度相对更高,就是在「拉近配对、推远非配对」。
4. 各模态怎么变成向量
4.1 文本编码器
常见路径:
字符串 → 分词(Tokenizer)得到 token id → Embedding 查表 → Transformer Encoder → 句向量或 token 序列输出两种常用形态:
- 一个向量:整句摘要(适合检索)
- 一串向量:每个 token 一个(适合生成、细粒度交互)
4.2 图像编码器
现代常见路径(Vision Transformer 思路):
图片 → 切成图像块(Patch) → 每个块变成向量 → Transformer Encoder → 图像特征序列(或再池化成一个向量)也可以用 CNN(ResNet 等)提特征,再投影。入门理解用 ViT 路径即可。
4.3 音频编码器
波形 → 分帧 / 梅尔频谱等 → CNN 或 Transformer → 音频特征序列语音识别、语音翻译多模态系统里很常见。
4.4 数值 / 表格(连接你已学的小模型)
你做过的「学习时长、出勤、作业」本质也是一种模态,只是更简单:
数值特征 → 可选 MLP/Linear → 向量多模态系统里,表格特征同样可以投影后与文本/图像一起融合。
5. 融合怎么做:四种常见结构
「融合」决定模型如何把多路信息合在一起用。
5.1 早期融合(Early Fusion)
很早就拼在一起:
把特征在输入侧拼接 → 一个网络一起处理- 优点:实现简单
- 缺点:各模态采样率、长度、噪声差异大时不好训
- 适合:强对齐、结构相近的输入
5.2 晚期融合(Late Fusion)
各自走完,再在决策层合并:
图像分支 → 分数A 文本分支 → 分数B 最终 = 融合(A, B) # 如加权平均、再接一层 MLP- 优点:分支独立、好调试
- 缺点:模态之间交互弱,难做深度跨模态推理
- 适合:分类、简单打分
5.3 中间融合 / 交叉注意力(最常见于强多模态)
用注意力让一种模态「查询」另一种模态:
文本特征作为 Query 图像特征作为 Key / Value → Cross-Attention → 文本在「看图」后更新自己的表示这样回答「图里有几只狗」时,问题里的词可以去图像特征里找依据。
5.4 编码器 + 大语言模型(当前主流工程形态)
很多「能看图聊天」的模型(如 LLaVA 一类思路)结构是:
图像 → 视觉编码器(常冻结或少训) → 投影层(MLP / Linear)把视觉特征映到 LLM 词向量空间 → 与文本 token 拼成一段序列 → 送进大语言模型生成回答伪流程:
[图像特征1, 图像特征2, ..., 用户问题tokens...] → LLM → 逐词生成答案对工程实现来说,这是目前最好懂的一条路:
视觉侧负责看,语言侧负责说;投影层负责翻译成 LLM 能读的「外语」。
6. 投影层:被低估但极关键的零件
不同编码器输出的维度、分布往往不同。例如:
- 视觉编码器输出维度
1024 - LLM 词嵌入维度
4096
中间需要Projection(投影头):
# 概念代码,不是完整可跑项目vision_feat=vision_encoder(image)# [B, T, 1024]lang_tokens=proj(vision_feat)# [B, T, 4096]# 再与文本 embedding 在序列维拼接,送入 LLM投影层可以很简单(一层Linear),也可以是小型 MLP。
它的职责不是「认识世界」,而是:
- 对齐维度
- 尽量把视觉特征翻译到语言模型习惯的空间
很多高效微调方案会:
- 冻住视觉编码器
- 冻住大部分 LLM
- 主要训练投影层 + 少量 LoRA
这与「大模型微调」里的参数高效思想一致。
7. 训练原理:通常分阶段,而不是一次到位
多模态系统很少「随机初始化后一次训完所有能力」,更常见是课程式训练。
阶段 A:单模态预训练(可借用现成模型)
- 文本:现成 LLM / 文本 Encoder
- 图像:现成 ViT / CLIP 视觉塔
- 音频:现成语音 Encoder
目的:先让每个塔自己会提取好特征。
阶段 B:跨模态对齐(Contrastive / 匹配)
典型目标(CLIP 风格):
- 一批图文对里,识别「谁和谁是一对」
- 拉近正样本对,推远负样本对
练完后,模型具备检索与粗语义对齐能力,但未必会流畅答题。
阶段 C:多模态指令微调(SFT)
数据形态类似:
输入:图片 + 「请描述这张图」 输出:一段标准回答或:
输入:图片 + 「图中的人在做什么?」 输出:「他在骑自行车。」训练方式和语言模型微调类似:让模型生成的下一个词接近标准答案。
差异只是输入序列里混进了视觉(或音频)特征 token。
阶段 D(可选):偏好对齐 / 安全对齐
用更好/更差回答做偏好优化,让输出更稳、更符合人类偏好。对入门非必须。
8. 推理原理:前向怎么走
以「看图回答」为例:
1. 读入图片,视觉编码器提取特征序列 2. 投影到语言空间,得到「视觉伪 token」 3. 用户问题分词,变成文本 token 4. 拼接:视觉伪 token + 文本 token 5. LLM 自回归生成:一次预测下一个词,循环直到结束检索任务则不同,通常不生成长文本:
图 → 向量 文 → 向量 算相似度,取 Top-K所以实现前先问清任务:
- 生成类:要解码器 / LLM
- 检索类:要共享向量空间 + 相似度
- 分类类:要融合特征 + 分类头
任务不同,融合与输出头都不同,但「编码 → 对齐 → 融合」骨架不变。
9. 用 PyTorch 视角看「内核」是什么
结合你已学的小模型知识,多模态并没有换一套物理学,仍然是:
| 层级 | 作用 |
|---|---|
| 张量 | 图像、文本、音频最终都变成张量 |
nn.Module | 各编码器、投影、融合、任务头都是模块 |
| 前向 | 推理 = 按图计算一遍 |
| 损失 + Autograd + Optimizer | 训练 = 用损失驱动参数更新 |
变复杂的只是模块变多、数据管道变复杂:
小模型: x_数值 → Linear → 概率 多模态: x_图 → VisionEnc → Proj ┐ ├→ Fusion / LLM → 输出 x_文 → TextEnc / Tokenizer ┘训练五步仍然成立:
zero_grad → forward → loss → backward → optimizer.step只是forward里要同时处理多种输入,loss可能是对比损失、生成损失或二者组合。
10. 三种经典实现路线对照
| 路线 | 代表思想 | 擅长 | 实现要点 |
|---|---|---|---|
| 双塔检索 | CLIP | 图文互搜、向量库 | 双编码器 + 对比学习 |
| 交叉编码 | 早期 VQA / 融合 Transformer | 细粒度匹配、分类 | Cross-Attention 融合 |
| 视觉语言模型 VLM | LLaVA 等 | 看图聊天、复杂问答 | 视觉塔 + 投影 + LLM |
选型建议:
- 只要「以图搜文 / 以文搜图」→ 双塔
- 只要「看图选类别 / 简单问答选项」→ 融合 + 分类头也可能够
- 要「自由文本回答、多轮对话」→ VLM 路线
11. 数据长什么样(实现时最容易忽略)
多模态质量高度依赖配对数据。
检索 / 对齐数据:
image_path, caption cat.jpg, 一只橙色的猫趴在窗台 car.jpg, 一辆红色轿车停在路边视觉问答数据:
image_path, question, answer shop.jpg, 货架上有几种饮料?, 三种指令数据:
image_path, instruction, response chart.png, 请总结这张图的趋势, 销售额连续三个月上升...实现时要注意:
- 图文是否真对齐(错配会直接毒化对齐)
- 分辨率、长宽比、文本长度要统一预处理
- 缺失模态(只有图没有文)要有策略:丢弃、掩码或单模态降级
12. 一个最小「概念级」模块划分
若用 PyTorch 拆项目,常见文件/模块是:
vision_encoder.py # 图像 → 特征 text_encoder.py # 文本 → 特征(检索双塔时) projector.py # 维度对齐 / 空间翻译 fusion.py # 拼接、注意力融合等 llm_wrapper.py # 可选:接生成式语言模型 loss.py # 对比损失 / 生成损失 dataset.py # 同时读图和文本 train.py # 训练循环 infer.py # 检索或生成推理最小检索模型的前向概念:
# 概念代码img_vec=normalize(proj_img(vision_encoder(images)))txt_vec=normalize(proj_txt(text_encoder(texts)))logits=img_vec @ txt_vec.T# 相似度矩阵# 对比学习:对角线位置应是配对样本最小看图对话前向概念:
# 概念代码v=projector(vision_encoder(images))# [B, T_v, D]t=embed_tokens(question_ids)# [B, T_t, D]seq=concat(v,t,dim=seq_len)# 序列维拼接out=llm(inputs_embeds=seq)# 再做语言建模13. 和 RAG、纯微调的关系
多模态系统里这三者经常一起出现:
| 技术 | 作用 |
|---|---|
| 多模态编码 / VLM | 让模型「看得懂」图或音 |
| RAG | 检索外部知识(也可检索多模态向量库) |
| 微调(LoRA 等) | 让模型更会按你的格式与领域说话 |
例如企业文档助手:
用户上传截图 → 多模态模型理解截图内容 → RAG 检索内部知识库 → LLM 组织最终回答不要把「多模态」理解成只有一种训练方式;它是输入形态扩展,训练仍可用对比、SFT、RAG 增强等组合。
14. 实现时常见误区
以为拼接就是多模态
不对齐语义时,拼接只是把两路噪声捆在一起。一上来就端到端猛训所有模块
更容易崩。更稳的是:强预训练塔 + 先训投影 / 对齐 + 再指令微调。忽略序列长度
一张图可能变成几十到数百个视觉 token,再加长文本,上下文很容易爆。用分类准确率衡量一切
生成任务要看文本质量;检索任务要看 Recall@K;问答要看是否吃到了图像证据。模态不平衡
若文本极强、图像分支几乎不被梯度更新,会出现「根本没看图也能靠语言先验瞎答」。需要在数据与损失上强迫模型用图。
15. 学习路径(接你当前进度)
你已经走过:张量 →nn.Linear→ 单任务小模型。建议这样接多模态:
| 步骤 | 学什么 | 验收 |
|---|---|---|
| 1 | 复习向量相似度、余弦距离 | 能手算/代码算两向量是否接近 |
| 2 | 做最小双塔:假图特征 + 假文特征对比学习 | 配对样本相似度高于非配对 |
| 3 | 读懂「视觉特征投影进 LLM」的数据流 | 能画出 LLaVA 式框图 |
| 4 | 跑一个现成小 VLM 或 CLIP 推理 demo | 会调用、会看输入输出 |
| 5 | 再谈训练:冻哪些层、训哪些层 | 能解释为何常冻视觉塔 |
不必先啃完所有论文。先能画出数据流,再能改一处模块观察变化,就比只记名词扎实。
16. 一张最终对照图
┌─ Vision Encoder ─┐ Image ──────────►│ │──► Projector ──┐ └──────────────────┘ │ ▼ Shared / Fused Space ▲ ┌─ Text / Audio Enc ─┐ │ Text/Audio ─────►│ │──► Projector ┘ └───────────────────┘ │ ▼ 检索:算相似度 分类:融合后接分类头 生成:送入 LLM 解码17. 总结
多模态实现原理可以收束为四句:
- 编码:各模态先变成向量或向量序列
- 对齐:用对比学习等手段,让同一语义的不同模态表示靠近
- 融合:用拼接、晚期融合或交叉注意力 / LLM 序列拼接做联合推理
- 训练:通常「预训练单模态塔 → 对齐 → 指令微调」,推理时按任务走检索或生成
它和 PyTorch 小模型开发共享同一套内核:张量计算 + 模块化前向 + 损失驱动的反向更新。多出来的,是「多种输入如何被翻译到同一套可计算语言里」。
读完本文后,若继续动手,优先做「双塔相似度」最小实验,再看「图像特征如何拼进 LLM 输入」,两条线分别对应检索式与生成式多模态的主干。