IT加油站

图像token化:LLM如何不“看”像素却能“看见”图像

30浏览 4天前 软件教程 MA123315

原文:https://dev.to/shrsv/image-tokenization-how-an-llm-learns-to-see-without-seeing-pixels-1g3c(作者 @shrsv)

语言模型并不知道像素是什么。

给 transformer 一张 JPEG,至少从概念上讲,你不能简单地说:

image.jpg -> LLM


模型期望的是向量序列。语言天然如此:分词器(tokenizer)将文本转换为离散的 token(词元) ID,这些 ID 变成嵌入向量,然后 transformer 处理得到的序列。

图像则不同。

一张 1024 x 1024 的图像包含超过一百万个像素。将这些像素直接送入 transformer 会生成极其庞大的序列,使得普通的注意力机制变得异常昂贵。

因此,多模态系统执行了一个关键的工程操作:

image
  |
  v
visual representation
  |
  v
image tokens / visual tokens
  |
  v
language model


有趣的是,“图像 token 化”并非某一种算法,而是一个完整的设计空间,涉及图块(patch)尺寸、可学习的视觉编码器、压缩、投影到语言模型的嵌入空间,有时还包括激进的 token 剪枝。

多模态架构中有许多部分看起来不过是管道工程,而这一部分正是其中之一——直到你意识到,它很大程度上决定了模型能看到什么、运行成本有多高,甚至能进行哪些类型的视觉推理。

1. 核心问题:图像相比文本大得离谱

考虑一张普通的 RGB 图像:

1024 x 1024 x 3 = 3,145,728 values


在还没做任何智能处理之前,你就有大约 310 万个标量值。

与之对比:

"A dog is sitting on the grass."

~ 7-10 language tokens, depending on tokenizer


当然,像素并不等同于语言 token。关键在于序列长度。

Transformer 处理序列。对于普通的稠密自注意力机制,开销大致按以下方式扩展:

attention cost ~ O(N^2)


其中 N 是序列长度。

如果我们天真地让每个像素都成为一个 token:

N = 1024 x 1024 = 1,048,576


那么成对注意力关系的数量大约为:

N^2
= 1,048,576^2
~ 1.1e12


这意味着每一层都有超过一万亿次成对交互。

这就是为什么“直接把像素当成 token”并不是一个特别有用的策略。

还有一个更深层的问题:相邻像素高度冗余。一块 16 x 16 的天空图块(patch)包含 256 个像素,但这些像素所承载的语义信息,远没有比该图块的紧凑表示多出 256 倍。

所以第一个关键的技巧是:

在让语言模型对其进行推理之前,先压缩局部视觉结构。

这一观察直接引出了图像图块的概念。

2. 历史技巧:将二维图像转换为序列

一个重要的早期实验是 OpenAI 的 iGPT,由 Mark Chen、Alec Radford、Ilya Sutskever 及同事于 2020 年报告。

这个想法出奇地直白:将图像展平为序列,然后训练一个 GPT 风格的 transformer 来预测下一个视觉符号。该工作证明,原本围绕序列预测设计的 transformer,即使没有显式引入传统视觉机制,也能学习到有意义的图像表示。([OpenAI][1])

但 iGPT 也暴露了根本性的扩展问题。全分辨率像素会产生巨大的序列。

论文明确提到,由于 transformer 的内存需求随上下文长度呈二次方增长,因此必须降低图像分辨率并压缩颜色信息。([OpenAI][2])

随后出现了一个更具影响力的简化方案。

2020 年,Alexey Dosovitskiy 与 Google Research 的合作者提出了 Vision Transformer(视觉 Transformer),简称 ViT。

ViT 没有将每个像素视为一个 token,而是将图像划分为固定大小的方形图块,并把每个图块当作一个 token。对于标准的 224 x 224 图像,使用 16 x 16 的图块:

224 / 16 = 14

14 x 14 = 196 patches


于是:

1,048,576 pixels   ->   196 visual tokens


对于 1024 x 1024 的图像,如果使用相同的 16 x 16 图块尺寸,我们会得到:

1024 / 16 = 64
64 x 64 = 4096 tokens


这仍然很大,但比一百万要好处理得多。ViT 证明,一个基于图像图块的纯 transformer 就能表现得非常好,而不需要依赖卷积来提供主要的视觉机制。([arXiv][3])

这一图块思想成为了现代视觉-语言系统的基础之一。

3. 图像词元(image token)到底是什么?

一个有用的心智模型是:

image
  |
  +-- patch 1
  +-- patch 2
  +-- patch 3
  ...
  +-- patch N
       |
       v
   linear projection
       |
       v
   embedding vector


假设图像是:

224 x 224


图块(patch)尺寸为:

16 x 16


那么共有:

(224 / 16) x (224 / 16)
= 14 x 14
= 196 patches


每个图块包含:

16 x 16 x 3 = 768


个原始 RGB 值。

一个简单的图块嵌入可以表示为:

z_i = x_i W + b


其中:

x_i = flattened pixels of patch i
W   = learned projection matrix
b   = bias
z_i = resulting embedding


如果模型的隐藏维度是,比如说:

D = 768


那么:

x_i : 768 values
z_i : 768-dimensional vector


于是 transformer 不再直接看到一个原始像素小方块,而是看到这个方块的学得表示(learned representation)。

这个区别很重要。

把得到的向量称为「图像词元」可能有点误导,因为它并不一定等同于像这样的语言词元:

"cat"


它更接近:

一个表示视觉输入某个区域的学得向量(learned vector)。

而且随着它通过视觉编码器,这个表示会逐渐变得更具语义性。

早期层可能编码类似这样的特征:

edges
textures
color transitions


后期层可能表示:

eyes
faces
objects
text
spatial relationships


确切的解释是分布式的,而非干净地局部化,但这种层级结构很有用。

因此,现代多模态模型通常看起来像这样:

image
  |
  v
Vision Transformer
  |
  v
visual feature sequence
  |
  v
projection / connector
  |
  v
LLM-compatible embeddings
  |
  v
language transformer


LLM 永远不需要直接操作数百万个像素。

4. 词元预算是真正工程开始的地方

这是开发者往往低估的部分。

图块尺寸决定词元数量。

对于宽度为 W、高度为 H、正方形图块尺寸为 P 的图像:

tokens = (W / P) x (H / P)


忽略填充和其他架构特定细节。

由于两个维度都与 P 成反比,词元数量大致按以下方式缩放:

tokens ~ 1 / P^2


这个平方关系是残酷的。

比较 8 x 8 图块和 16 x 16 图块:

P = 8

tokens = (224 / 8)^2
       = 28^2
       = 784


对比:

P = 16

tokens = (224 / 16)^2
       = 14^2
       = 196


所以图块尺寸减半会使词元数量增加:

784 / 196 = 4x


而密集注意力使得两两交互的计算量大约再按平方增长:

4x more tokens
=> ~16x more attention-pair work


这是图像词元化(image tokenization)的核心权衡:

smaller patches
    |
    +-- more spatial detail
    +-- better access to small objects/text
    +-- more tokens
    +-- more compute
    +-- more KV-cache / activation pressure


考虑一个文档图像。

在 1024 x 1024 下:

P = 32
tokens = 32 x 32 = 1024

P = 16
tokens = 64 x 64 = 4096

P = 8
tokens = 128 x 128 = 16,384


现在想象附加用户的文本提示:

4096 image tokens
+ 100 text tokens


图像主导了上下文。

这就是多模态上下文窗口可能具有欺骗性的原因。一个宣称具有非常大上下文窗口的模型,在以下两者之间可能仍存在实际差别:

100,000 text tokens


与:

100,000 multimodal tokens


因为生成和关注这些视觉词元的计算和内存特性可能非常不同。

成本核算直接来自数学。

假设一个请求包含:

4 images
1024 x 1024
16 x 16 patches


每张图像贡献:

4096 tokens


所以:

4 x 4096 = 16,384 visual tokens


模型还没看到用户文本,就已经有这么多视觉词元。

现在假设每秒服务 100 个请求:

16,384 x 100
= 1,638,400 visual tokens / second


这就是为什么图像预处理、词元压缩、批处理策略和序列长度分布成为生产问题,而不是学术细节。

5. 令人惊讶的部分:图像 token(image tokens)通常不会直接进入 LLM

假设你的视觉编码器产生:

N visual tokens
x
D_v dimensions


而你的 LLM 期望嵌入的维度为:

D_l dimensions


这两个维度很可能并不匹配。

因此多模态架构需要一个桥接层。

概念上:

Vision encoder:
[N, D_v]

        |
        | projection / connector
        v

LLM:
[N', D_l]


有几种方法可以构建这个桥接层。

一种简单的方法是使用一个可学习的投影:

z_llm = z_vision W


其中:

z_vision : D_v
W        : D_v x D_l
z_llm    : D_l


语言模型随后可以将这些向量与文本嵌入一起使用。

这听起来微不足道,但它是多模态系统中最关键的架构边界之一:

vision representation
        |
        | semantic interface
        v
language representation


换句话说,语言模型不一定需要知道图像编码器是如何工作的,它只需要一个接口,让有用的视觉信息能够进入它的计算过程。

DeepMind 的 Flamingo 于 2022 年由 Jean-Baptiste Alayrac 及其合作者提出,将这一接口思想体现得尤为明确。Flamingo 并不是简单地把视觉特征当作普通的文本 token,而是使用 Perceiver 风格的机制压缩视觉信息,并通过门控交叉注意力层让语言模型有选择地关注视觉流。([arXiv][4])

这一区别之所以重要,是因为存在一个根本性的设计问题:

我们应该将图像强行塞入语言模型的 token 流中,还是给语言模型一个它可以查询的独立视觉记忆?

这两种选择会带来略有不同的缩放特性。

一个有用的概念对比是:

Strategy A: visual tokens become part of the sequence

[text][image][image][image][image][text]

Strategy B: language queries a visual representation

[text] ----\
[text] -----+--> cross-attention --> visual memory
[text] ----/


当原始视觉表示非常庞大时,第二种方法会灵活得多。

6. 为什么“更多图像 token”并不一定更好

我们很容易得出这样的结论:

more patches = more information = better model


但这通常并不成立。

想象一张包含以下内容的图像:

sky
grass
mountain
person
small road sign


天空可能占据图像的 40%。

我们真的需要数千个同等重要的 token 来描述天空吗?

很可能不需要。

我们真正关心的是信息密度。

因此,一种可能的策略是:

easy / redundant region
        |
        v
   few tokens

complex / informative region
        |
        v
   many tokens


这引出了自适应或分层的 token 化(tokenization)。

概念上:

coarse grid
    |
    +-- boring region ------> stop
    |
    +-- complex region -----> split
                              |
                              +-- split again


一份文档就是一个完美的例子。

假设你拍摄了一页纸,上面有:

large blank margins
header
two columns of dense text
small diagram


在所有地方均匀分配相同的视觉分辨率是浪费的。

理想情况下,你会把 token 花在:

tiny text
fine diagrams
tables
logos
faces


而用更少的 token 处理:

blank paper
uniform walls
sky
large flat backgrounds


随着多模态模型从固定图像基准转向实际应用,这一点变得越来越重要。

想象一个正在查看屏幕截图的编程助手。

它可能不关心其中 95% 的像素。

但它会密切关注:

error message
line number
function name
small icon
button state


因此,token 化变成了一个信息分配问题。

你可以把理想的 tokenizer 想象成在尝试解决类似这样的问题:

maximize useful visual information
subject to a token budget


这比单纯选择图块(patch)大小要有趣得多。

7. 运维:图像 token 实际上是计算货币

一旦你从基础设施的视角来审视多模态推理,视觉 token 化(visual tokenization)就开始变成一个定价问题。

假设你的应用需要处理:

1000 个请求 / 秒


而每个请求包含:

2 张图像
每张图像约 1000 个视觉 token


那么系统每秒钟要处理:

2 x 1000 x 1000
= 2,000,000 个视觉 token


在大规模场景下,视觉 token 数量会成为以下指标的主要决定因素之一:

GPU 显存
注意力 FLOPs
预填充延迟
批处理效率
吞吐量
每请求成本


预填充阶段尤其关键。

逐 token 生成答案只是其中一半的工作。在开始生成之前,模型必须先处理输入上下文。

如果输入包含:

20,000 个视觉 token
+ 500 个文本 token


那么 LLM 必须先消化一个 20,500 token 的上下文,然后才能开始作答。

这就产生了一个重要的实际不对称:

简短的文本问题
+
庞大的图像表示
=
昂贵的请求


即便最终答案只是:

「是的,这是一张柱状图。」


这就是为什么生产环境的多模态系统往往关心那些听起来几乎平淡无奇的细节:

图像缩放
宽高比处理
token 池化
视觉特征缓存
批处理
早期压缩
动态分辨率


它们都是在试图控制进入昂贵语言模型计算的视觉 token 的数量和有效性。

这里还有一个微妙的系统工程教训:

昂贵的对象往往不是图像本身,而是你把它转换成 transformer 必须处理的东西之后的图像。

500 KB 的 JPEG 和 5 MB 的 JPEG,在预处理之后可能变成几乎相同的计算负载。

与此同时,两张视觉上相似的图像,如果一张产生:

400 个视觉 token


而另一张产生:

4000 个视觉 token


它们的成本可能天差地别。

因此,磁盘上的文件大小并不能很好地衡量多模态推理成本。

8. 开发者心智模型

当你遇到一个新的多模态模型时,与其去想:

「大语言模型是如何『看』图像的?」


不如改问五个更具体的问题:

1. 视觉编码器是什么?
2. 什么构成一个视觉 token?
3. 一张图像会产生多少个视觉 token?
4. 视觉特征是怎么映射进语言模型的?
5. 如果有多张图像,会发生什么?


一个简化的架构可能是这样的:

                图像
                  |
                  v
        +-------------------+
        |    视觉编码器      |
        |  图块化(patchify)|
        |    自注意力        |
        |    特征构建        |
        +-------------------+
                  |
                  v
        [v1 v2 ... vN]
                  |
                  v
        +-------------------+
        |      连接器        |
        |   投影 / 重采样 /  |
        |    交叉注意力      |
        +-------------------+
                  |
                  v
        [z1 z2 ... zM]
                  |
           +------+------+
           |             |
        文本 token   视觉 token
           |             |
           +------+------+
                  |
                  v
                LLM
                  |
                  v
                输出


关键的结构变量往往是 M,而不只是原始图像中的像素数量。

这个数字告诉你,语言模型实际需要在多少视觉信息上进行推理。

正是在这里,一个看似简单的预处理选择,变成了首要的模型设计决策。

结论:图像 token 化是横亘在「看」与「推理」之间的瓶颈

从图像到 LLM 的过程,本质上是一条压缩管线:

数百万像素
    |
    v
数千个图块(patch)
    |
    v
数百/数千个视觉嵌入(visual embedding)
    |
    v
紧凑的多模态表示
    |
    v
语言模型推理


工程挑战在于:既要压缩得足够激进,让推理成本可负担;又不能丢掉那些有意义的视觉细节。

一个很小的图块(patch)可以保留路牌上的文字,却会让 token 数量爆炸式增长。

一个很大的图块计算上很便宜,却可能把路牌变成无法辨认的噪声。

均匀的 token 化虽然简单,却会把容量浪费在不重要的区域上。

激进的压缩很便宜,却可能破坏语言模型所需的空间细节。

所以真正要优化的目标不是:

最少的图像 token 数量


而更接近:

每个视觉 token 的最大有用信息量


这个思路带来了一个出乎意料的广泛影响。更好的多模态模型,可能主要不来自把 LLM 做得更大,而来自构建更好的决策方式——决定图像的哪些部分值得被表示、以什么分辨率表示、以什么形式表示

这样一来,图像 token 化就不再只是一个输入格式化的技巧,而更像是视觉世界里 tokenizer 本身。

下次当你看到一个号称能理解 4K 截图、扫描版 PDF、或一个提示词里十二张图像的模型时,值得追问的问题不只是「它的上下文窗口有多大?」

而是:

它为了理解真正重要的内容,实际消耗了多少视觉 token?


[1]: https://openai.com/index/image-gpt/?utm_source=chatgpt.com "Image GPT | OpenAI"

[2]: https://cdn.openai.com/papers/Generative_Pretraining_from_Pixels_V2.pdf?utm_source=chatgpt.com "Generative Pretraining from Pixels"

[3]: https://arxiv.org/abs/2010.11929?utm_source=chatgpt.com "An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale"

[4]: https://arxiv.org/abs/2204.14198?utm_source=chatgpt.com "Flamingo: a Visual Language Model for Few-Shot Learning"

原文:https://dev.to/shrsv/image-tokenization-how-an-llm-learns-to-see-without-seeing-pixels-1g3c(作者 @shrsv)

#图像token化 #LLM #ViT #多模态 #图块