多模态大模型原理:一切皆向量,一切皆可生成

引子

这个系列前文讲了好几篇,全是纯文本大模型------输入是文字,输出也是文字,一切都是 token。

但你有没有好奇过:为什么 ChatGPT 能回答"这张图里有什么"?为什么你发给它一张手绘数学题,它能帮你解题?

图片不是 token,LLM 不直接认像素。那它是怎么"看懂"图片的?

反过来问:你让它"画一幅日落图",它是怎么"生成"一张图片的?它输出的 token 怎么变成像素的?

这两个问题分别对应多模态大模型的两面:输入端 (理解多模态)和输出端(生成多模态)。


一、先说结论

多模态大模型并不是另一套架构,而是在文本大模型的两端各接了几根管道:

bash 复制代码
输入侧:图片/音频/视频 → 各模态编码器 → 向量序列 → 拼入 LLM
输出侧:LLM 生成文本;扩散模型 DiT 生成图片/视频;声码器生成音频
LLM 核心本身------Attention + FFN 的堆叠------完全不变。

前文讲的所有东西:Attention 公式、残差连接、LayerNorm、KV Cache、预测下一个 token,在多模态模型里照用不误。


二、核心思路:把一切都变成向量

前文第三篇讲过,文本进入 LLM 核心(堆叠 Attention + FFN)之前要走两步:

bash 复制代码
文字 → Token → 向量 → 进入多层 Attention + FFN 堆叠

LLM 核心只操作向量。它不关心这些向量是从文字来的还是从图片来的。只要是向量,它就能处理。

多模态的核心思路就这么简单:给每种模态各配一个编码器,把图片、音频都翻译成向量,然后交给同一个 LLM 核心处理。

bash 复制代码
文本大模型:
  文字 → Tokenizer → Embedding → [向量₁, 向量₂, 向量₃] → 多层 Attention + FFN

多模态大模型:
  文字 → Tokenizer → Embedding → [向量₁, 向量₂, 向量₃]  ─┐
                                                       ├→ 拼在一起 → 多层 Attention + FFN
  图片 → 图像编码器 → 投影层 → [向量ₐ, 向量ᵦ, 向量ᵧ, ...] ─┘

对 LLM 核心来说,它看到的就是一个向量序列------前几个来自图片,后面几个来自文字。它用同样的 Attention 机制处理所有向量,根本不区分来源。


三、图片是怎么变成向量的

文本变向量,前文讲透了:文字 → BPE 分词 → Token ID → 查 Embedding 表。

图片的路径不同,但目标一样------变成一组向量。

3.1 把图片切成"碎片":ViT

最常用的方法叫 ViT(Vision Transformer),思路非常直接:

bash 复制代码
一张 224×224 的图片
    │
    ▼ 切成 14×14 = 196 个小方块(每个 16×16 像素)
    │
    ▼ 每个小方块通过线性层,变成一个向量(比如 768 维)
    │
    ▼ 加上位置编码(告诉模型"这个方块在左上角还是右下角")
    │
    ▼ 得到 196 个向量

这 196 个向量就是图片的"token"。每个向量代表图片的一个局部区域------就像文本的一个 token 代表一个词或子词。

为什么要切成 patch,而不是把整张图压成一个向量? 如果把整张 224×224 图直接压成一个向量,会丢失空间细节。切成 196 个 patch 后,每个 patch 保留局部信息,后续 Attention 让这些 patch 互相"交流",模型就能同时看到整体和局部。

和文本 token 的对比:

文本 图片
原始输入 字符串 像素矩阵
"分词"方式 BPE 切成子词 切成 16×16 方块(patch)
每个"token" 一个子词 一个图片方块
变成向量 查 Embedding 表 线性投影(矩阵乘法)
向量维度 768 768(对齐到同一维度)
典型数量 几十到几千 196~1024 个

3.2 投影层:对齐到同一个空间

图像编码器(如 ViT)输出的向量和文本 Embedding 的向量,通常不在同一个空间里------维度不同,语义含义也不同。

需要一个投影层(通常一两层线性网络),把图像向量"翻译"到文本模型的向量空间:

bash 复制代码
图像编码器输出(1024维) → 投影层(1024 → 4096) → 和文本向量同维度(4096维)

投影层的权重是训练时学出来的。训练目标是让投影后的图像向量,和对应文本描述的向量尽量接近------"一只猫"的图像向量应该靠近"cat"的文本向量。

3.3 以 LLaVA 为例

LLaVA(Large Language and Vision Assistant)是最经典的多模态架构之一,结构清晰,看一张图就够了:

bash 复制代码
输入:"请描述这张图片" + [一张猫的照片]

┌──────────────────────────────────────────────┐
│  文本部分                                      │
│  "请描述这张图片" → Tokenizer → Embedding      │
│  → [向量₁, 向量₂, 向量₃, 向量₄, 向量₅]         │
└──────────────────────┬───────────────────────┘
                       │  拼接
                       ▼
┌──────────────────────┴───────────────────────┐
│  图片部分                                      │
│  猫的照片 → ViT 编码器 → 196 个图像向量         │
│  → 投影层 → [向量ₐ, 向量ᵦ, ..., 向量₁₉₆]       │
└──────────────────────┬───────────────────────┘
                       ▼
[向量₁, ..., 向量₅, 向量ₐ, ..., 向量₁₉₆]
                       │
                       ▼
              多层 Attention + FFN(和前文一模一样)
                       │
                       ▼
              逐 token 生成回答:
              "这张图片中有一只橘色的猫..."

LLM 核心和前文完全一样,唯一的改动是在前面加了一条图像处理的管线:模型的"大脑"没变,只是"眼睛"是新装的。


四、Attention 是怎么工作的

看上面的 LLaVA 流程图,有个细节还没展开:文本向量和图像向量混在一起,Attention 怎么知道哪些是文字、哪些是图片?

答案是:它不需要知道,序列顺序已经解决了这个问题。

输入的向量序列是这样的:

bash 复制代码
输入序列:[patch₁, patch₂, ..., patch₁₉₆, 请, 描述, 这张, 图片, ...]
                      ↑ 图片部分              ↑ 文本部分

因果掩码决定了每个位置能看到谁:

所以 patch 为什么要在文字前面?因为生成是自左向右的------生成"描述"这个词时还不知道自己要说什么,必须先看到整张图。把 patch 放在前面,标准因果掩码自然让每个文本 token 可以回看所有 patch,不需要任何特殊设计。


五、音频和视频:同一套思路

图片讲完了,但多模态的世界远不止图片。音频、视频,都可以走同样的路子:编码成向量序列 → 投影到文本空间 → 交给 LLM 核心处理。

音频、视频、图片的编码思路其实是一回事:

bash 复制代码
图片:  像素矩阵 → 切 patch → 向量序列
音频:  频谱图   → 切 patch → 向量序列
视频:  帧序列   → 每帧切 patch → 向量序列
文本:  字符串   → BPE 分词  → 向量序列

区别只在于"切分方式"和"用什么编码器"。

5.1 音频:让模型"听懂"声音

一段语音进入模型之前,需要先做一层预处理:

bash 复制代码
原始波形(PCM)→ 短时傅里叶变换(STFT)→ 梅尔频谱图(Mel Spectrogram)→ 编码器 → 向量序列

为什么要转成频谱图? 原始音频波形每秒包含上万个采样点(16kHz 采样率 = 每秒 16000 个数字),直接处理太贵,而且不直观。

转成梅尔频谱图之后,相当于把声音"翻译"成了一张图:横轴是时间,纵轴是频率,颜色深浅代表能量大小。这张图再交给类似 ViT 的编码器去处理------音频的"图片化",思路和把图片切成 patch 一模一样。

bash 复制代码
[一段5秒的语音,16kHz采样]
    │
    ▼  STFT + 梅尔滤波器
[频谱图: 约 500帧 × 80频带]
    │
    ▼  切成 16×16 的 patch(时间×频率)
[约 2000 个音频 patch]
    │
    ▼  线性投影 → 每个 patch 一个向量
[约 2000 个音频向量]

每帧约 2040ms,相邻帧重叠 50%,所以 1 秒音频大约产生 50100 个向量。一段 30 秒的语音就能产生几千个音频向量,直接塞进 LLM 核心。

OpenAI 的 Whisper 是目前最常用的音频编码器之一------它的编码器部分基于 Transformer Encoder 架构,输入梅尔频谱图,输出向量序列,再通过跨注意力机制接入 LLM。GPT-4o 的音频理解、Gemini 的语音理解,大多是这个路子。

5.2 视频:时间维度的挑战

视频比图片多了一个维度:时间

图片的信息是静态的,一张图切 196 个 patch 就够了。但视频是一连串图片(帧)的序列,模型不仅要理解"每一帧里有什么",还要理解"帧和帧之间发生了什么"------物体的运动、场景的切换、动作的先后顺序。

视频的处理思路是先从时间轴上采样帧,再对每帧分别做图片的处理

bash 复制代码
[一段30帧/秒的30秒视频 = 900帧]
    │
    ▼ 均匀采样(比如每隔15帧取1帧)
[30帧]
    │
    ▼  每帧 resize 到 224×224,通过 ViT 切 patch
[每帧 196 个 patch × 30帧 = 5880 个 patch]
    │
    ▼  每个 patch 投影成一个向量
[5880 个视频向量]

1 秒视频如果采样 2~3 帧,一段 1 分钟的视频就可能产生几千到上万个视频向量。随之而来的有三个实际问题:上下文窗口很快被填满;Attention 计算量随 token 数量平方增长,1 万个 token 的 Attention 矩阵就是 1 亿个元素;推理成本比处理图片贵上几十到上百倍。

这也是为什么目前的视频理解模型大多只处理短视频(几十秒),或者对视频先做"描述压缩"------用另一个模型先把视频转成一段文字描述,再让 LLM 处理。

视频还有一个图片没有的问题:帧的顺序很重要 。图片的 patch 可以加位置编码,视频除了每帧内部的空间位置编码,还需要全局时间位置编码,让模型知道哪帧在前、哪帧在后。

主流做法是给每个帧分配一个时间戳 embedding,和帧内 patch 的空间位置编码加在一起:

bash 复制代码
patch_final_vector = patch_vector + 空间位置编码 + 时间戳编码

这样,Attention 机制就能同时学到帧内关系(猫的各个部位在一帧内的空间关系)和帧间关系(猫从左跳到右的运动轨迹)。


六、输出端:模型怎么"生成"图片和音频

前面讲的都是输入------把图片、音频、视频变成向量,让模型"看懂"。但多模态还有另一半:输出

当模型能看图之后,它是怎么把"一只猫"的意图,变成一张真正的猫的图片的?当你让 ChatGPT 画一幅日落,它是怎么一步步生成像素的?

6.1 两种生成范式,两个 Transformer

多模态大模型的输出有两种完全不同的生成范式,共用同一套骨架------Attention + FFN 堆叠,但各有各的玩法:

范式 代表 注意力掩码 训练目标 生成方式
自回归 Transformer(GPT) LLM 文本生成 因果掩码(只能看过去) 预测下一个 Token 逐个 token 前向生成
扩散 Transformer(DiT) 图像/视频生成 无掩码(双向可见) 预测噪声 ε 从噪声迭代去噪(T 步)

两种模型的"大脑"是一样的(堆叠 Attention + FFN),差别只在于:掩码方式不同、输出不同、训练目标不同。

6.2 图片生成:扩散 Transformer(DiT)

自回归 LLM 生成图片直接输出像素很难,主流方案是扩散 Transformer(DiT)------它也是一个 Transformer,骨架和 LLM 完全一样(Attention + FFN 堆叠),但有三处不同:

区别一:注意力无掩码GPT 有因果掩码,每个 token 只能看到之前的 token。DiT 没有掩码,所有位置两两可见------就像一个"双向"的 Transformer。

区别二:输入是噪声 + 条件 DiT 输入的不是 token 序列,而是:带噪潜变量 z_t + 时间步 t + 文本条件。LLM 输出的向量通过交叉注意力或 AdaLN 调制注入进去。

区别三:训练目标和生成方式

  • • 训练目标:MSE(ε, ε̂) --- 预测噪声,不是预测 token
  • • 生成方式:从纯噪声 z_T 开始,迭代 T 步去噪,每步预测并去掉一部分噪声,最终得到干净图像
bash 复制代码
用户输入:"画一只在草地上奔跑的狗"

    ▼  LLM 理解用户意图,输出文本条件嵌入

    ▼  DiT 主干(Attention + FFN 堆叠,无因果掩码)
           输入:噪声 z_T + 文本条件
           迭代 T 步:预测噪声 → 去除部分噪声 → 降噪到 z_{T-1} → ...
           最终输出:干净潜变量 z_0

    ▼  VAE 解码器
           把潜变量映射回像素空间 → 512×512 图片

具体代表:GPT-4o 用 DALL·E 3(DiT),Gemini 用 ImageFX(DiT)。LLaVA 这类理解型模型本身只理解图片,生成靠调用外部 DiT(如 Stable Diffusion)。

DiT 和 GPT 骨架相同(都是 Attention + FFN),差别只在三点:DiT 没有因果掩码、输入是噪声而非 token、目标是预测噪声而非下一个 token。

6.3 音频生成:声学 Token 的路子

图片生成靠扩散模型,音频生成靠另一套:声学 Token

流程是:

bash 复制代码
用户输入:"用温柔的声音朗读这段文字"

    ▼  LLM 理解内容 + 情感意图

    ▼  LLM 输出两类 token:
         - 文本 token(说话内容)
         - 声学 token(音高、语速、停顿)

    ▼  声学解码器(基于 Transformer 架构的声码器)
         把声学 token 还原成波形
         输出真实的人声音频

这里的核心思想是:把声学特征也 token 化------像文本 token 一样,让 LLM 预测声学 token,再把 token 变回波形。

GPT-4o 的语音对话就是这个路子:输入音频 → Whisper 编码器 → LLM → 声学 token → 声码器 → 输出语音。全程 token 流,打通了文本和语音的边界。

6.4 视频生成:最难的那块

视频生成是当前最前沿、也最困难的方向。

为什么难? 因为视频 = 图片 × 时间。生成一张图已经很难了,还要保证几十帧、上百帧之间的连贯性------人物不能跳变,动作要流畅,光影要一致。

目前主流的视频生成模型(如 Sora、Runway、Pika)走的是扩散模型 + 时序建模的路子:

bash 复制代码
用户输入:"一只猫从左向右奔跑"

    ▼  LLM 解析意图,输出文本条件嵌入

    ▼  时序 DiT 主干(Attention + FFN 堆叠,无掩码)
         从噪声开始,在时间轴上逐步去噪
         保证帧间连贯性(通过时序 Attention 机制)

    ▼  VAE 解码器 → 输出 16~60fps,时长 5~60 秒视频

时序 DiT 和图片 DiT 的区别在于:在每帧内部切 patch 的基础上,还要在时间轴上加时序位置编码,让模型区分"第 1 秒的猫"和"第 3 秒的猫"。

目前多模态大模型大多还不具备高质量视频生成能力 ,原因是:视频 token 数量太大(几分钟视频可能有数十万 token),超出 LLM 核心处理能力;时序一致性难以保证;计算成本极高。所以现在常见的产品形态是:LLM 做理解+规划,时序 DiT(如 Sora)做生成,两者配合工作,而不是一个模型搞定一切。

6.5 完整的双向多模态架构

讲到这里,可以画一张完整的架构图了。从用户视角看,这就是一个整体------你给它文字+图片,它给你文字+图片,只是内部用了两套不同的生成机制。

以用户输入"把图中的狗给删除" + [一张狗的照片] 为例:

bash 复制代码
用户输入: 文字 "把图中的狗给删除" + 图片

                    │                                           │
          ┌─────────▼──────────┐                 ┌─────────────▼─────────────┐
          │    文本 Tokenizer   │                 │    图片编码器 (ViT)         │
          │  → Embedding 层     │                 │  → 切成 patch → 投影层      │
          └─────────┬──────────┘                 └─────────────┬─────────────┘
                    │                                           │
                    ▼                                           ▼
          ┌──────────────────┐                   ┌──────────────────────────┐
          │  文字向量序列     │                   │  图片向量序列 [patch₁..]  │
          └────────┬─────────┘                   └────────────┬─────────────┘
                   │                                          │
                   └──────────────┬───────────────────────────┘
                                  │ 拼接 → [patch₁..,文字向量序列]
                                  ▼
                    ┌──────────────────────────────────────┐
                    │          LLM 核心(同一个 Transformer) │
                    │      堆叠 Attention + FFN × N          │
                    │  输入: 向量序列(图片 patch + 文本)     │
                    │  输出: 文本 token(自回归因果掩码)       │
                    └──────────┬───────────────┬────────────┘
                               │               │
                    ┌──────────┘               └──────────────────┐
                    ▼                                             ▼
           自回归生成文本                    LLM 输出的文本条件嵌入(Text Embedding)
                    │                                             │
                    ▼                                             ▼
              直接输出文字                          注入 DiT(扩散 Transformer)
                                                                │
                                                   从噪声 z_T 迭代去噪 T 步
                                                                │
                                                                ▼
                                                    潜变量 z_0 → VAE 解码 → 像素(编辑后的图片)

总结一下:输入侧,文字和图片各走各的管线,最后拼在一起进同一个 LLM 核心;输出侧,LLM 自回归生成文字,同时把文本条件注入给 DiT,由 DiT 生成图片。两套机制骨架相同(都是 Attention + FFN 堆叠),但输入、掩码和训练目标都不同。

最后要纠正一个常见误解:扩散模型不是 LLM 的"解码器",它是一个独立的 Transformer,只是靠 LLM 提供文字条件来控制生成内容。


七、多模态带来的新问题

7.1 上下文窗口压力更大

前文第八篇讲过上下文窗口的限制。多模态让这个问题更严重:

bash 复制代码
一段文本:      "请描述这张图片" → 约 5 个 token
一张图片:      224×224, 16×16 patch → 196 个 token
一张高清图:    448×448, 16×16 patch → 784 个 token

一张图片就吃掉了几百个 token 的窗口空间。如果对话中包含多张图片,窗口会被快速占满。

这也是为什么很多多模态模型会对图像做压缩------比如只保留最重要的几十个 patch 向量,而不是全部 196 个。

7.2 推理成本更高

更多的 token 意味着更大的 KV Cache 和更多的 Attention 计算(n²)。处理一张图片的推理成本远高于处理一句话。

7.3 对齐的难度

让图像向量和文本向量在同一个空间里"说同一种语言",这件事比字面上听起来要难得多。投影层的训练如果不到位,模型很可能"看到了"图片但还是"看不懂"------图片的向量和文字的向量虽然在数学上碰巧维度相同,但在语义上各说各话。


八、总结

读完前文再看多模态,你会发现核心其实没变多少。

LLM 那部分完全照旧:Attention 公式、残差连接、LayerNorm、KV Cache,一个都没改。

真正新加的只有两件事:入口处,给图片、音频、视频各配一个编码器 + 投影层,把它们变成向量序列,拼进 LLM 的输入里;出口处,图片和视频不走 LLM 的自回归管线,而是交给扩散 DiT,音频走声学 Token 加声码器。

两套生成机制(自回归 LLM 生成文字,扩散 DiT 生成图片/视频)骨架相同------都是 Attention + FFN 的堆叠------只是输入、掩码和训练目标不同。

说白了,多模态就是给同一个大脑接了两组新的感官和手脚。


文章首发于 「小小寰宇」

相关推荐
Artech3 小时前
[MAF的Agent管道详解-03]连接LLM的IChatClient对象
ai·llm·openai·agent·maf·agent管道·ichatclient
leo03083 小时前
三种 Norm 一张图读懂:LayerNorm、RMSNorm、AdaRMS
人工智能·llm
wangruofeng3 小时前
DeepSeek 和小米都在降价,为什么公司反而快烧不起 Token 了?
llm·deepseek
sugar__salt3 小时前
从Python列表切片到LLM接口实战:零基础AI编程落地教程
开发语言·python·ai·prompt·transformer·ai编程
爱听歌的周童鞋5 小时前
Learn-Claude-Code | 笔记 | Tools & Execution | s03_new Permission
llm·agent·tools·permission·execution·claude code
我滴老baby5 小时前
Transformer深入详解-现代大模型核心架构
深度学习·架构·transformer
带娃的IT创业者5 小时前
开源语音AI的边界:从 `luongnv89/claude-howto` 看前沿技术的落地实践
人工智能·开源·llm·大语言模型·claude·语音交互·语音ai
Mem0rin5 小时前
[LLM基础] Transformer 库的使用
android·深度学习·transformer
学习中.........5 小时前
万字硬核解析:从零看透 Transformer 与大语言模型(架构、数学与工程实践)
语言模型·架构·transformer