视觉 Transformer(Vision Transformer, ViT)中完全保留了 Q、K、V 这套机制,而且用法和原始 Transformer 在 NLP 中几乎一模一样。

下面我用最直白的方式解释:ViT 里也有 Q、K、V,它们干的事情也是一样的——让图像的不同区域“互相看、互相参考”。


🖼️ 第一步:图像怎么变成“词”?

在 NLP 中,输入是一串词(如 “cat sat on mat”)。
在 ViT 中,图像被切成小块(patches),比如一张 224×224 的图,切成 16×16 的小块,得到 14×14 = 196 个图像块。

每个图像块被展平 + 线性变换 → 变成一个向量,就像一个“视觉词”(visual token)。

所以输入变成了:

[patch1, patch2, ..., patch196]  → 形状 (196, d)

这就和 NLP 的词序列形式一样了!


🔍 第二步:Q、K、V 怎么来的?

对这 196 个 patch 向量,ViT 做的事情和 Transformer 完全相同:

  • 每个 patch 向量 xix_ixi​ 通过三个可学习矩阵生成:
    • qi=xiWQq_i = x_i W_Qqi​=xi​WQ​ → Query:这个区域“想关注什么特征”
    • ki=xiWKk_i = x_i W_Kki​=xi​WK​ → Key:这个区域“能被如何匹配”
    • vi=xiWVv_i = x_i W_Vvi​=xi​WV​ → Value:这个区域“实际包含什么视觉信息”

✅ 所以,Q、K、V 在 ViT 中依然存在,而且意义类似:

  • Q:我在找什么样的视觉模式?(比如边缘、纹理、物体部件)
  • K:我这个区域看起来像什么?(供别人来匹配)
  • V:我这里真正有什么内容?(用于最终融合)

👀 第三步:注意力在图像中起什么作用?

举个例子:

假设图像中有 一只狗,它的头在 patch A,尾巴在 patch Z。

当模型处理 patch A(狗头)时:

  • 它的 Query(q_A)会去和所有 patch 的 Key(k_1 到 k_196)算相似度
  • 发现 k_Z(狗尾巴)和 q_A 有一定相关性(因为都是“狗”的一部分)
  • 于是注意力权重会给 patch Z 一些分数
  • 最终输出 = 所有 patch 的 Value 加权和 → 狗头的表示里就融入了尾巴的信息!

👉 这就是 全局上下文建模:哪怕两个区域离得很远,也能建立联系!


❓那和 CNN 有什么区别?

  • CNN:只能通过滑动窗口看局部(比如 3×3 邻域),要看到全局得堆很多层。
  • ViT(带 QKV 注意力):第一层就能让任意两个 patch 互相通信,天生具有全局感受野。

✅ 总结

问题回答
ViT 有没有 Q、K、V?有!完全保留
Q、K、V 在图像中是什么意思?Q=“我想看啥”,K=“我能被谁看”,V=“我有啥内容”
为什么需要 Q 和 K 算相关性?为了知道图像中哪个区域该关注哪个区域(比如眼睛关注鼻子,车轮关注车身)
是不是只关注自己?不是!恰恰是要跨区域关注,这才是注意力的价值

所以,不管是文字还是图像,Transformer 的核心思想不变:

通过 Q-K 匹配,动态决定从哪些地方(V)提取信息。

在视觉任务中,这就变成了:让图像的不同部分学会“互相理解”。

如果你看 ViT 的代码(比如 PyTorch 实现),你会发现它的 Attention 模块和 NLP 里的几乎一模一样 😊


Q、K、V 是通过输入乘以三个“全局共享”的可学习矩阵 WQW_QWQ​、WKW_KWK​、WVW_VWV​ 得到的;所有位置的 q 都共用同一个 WQW_QWQ​,k 共用 WKW_KWK​,v 共用 WVW_VWV​。


🧩 举个具体例子(以 ViT 为例)

假设一张图像被切成 4 个 patch(为了简单):

  • 输入:X=[x1,x2,x3,x4]X = [x_1, x_2, x_3, x_4]X=[x1​,x2​,x3​,x4​],每个 xi∈Rdx_i \in \mathbb{R}^{d}xi​∈Rd(比如 d=768)
  • 所以 X∈R4×dX \in \mathbb{R}^{4 \times d}X∈R4×d

模型内部有三个固定的、可训练的权重矩阵(对整个模型来说是全局的):

  • WQ∈Rd×dkW_Q \in \mathbb{R}^{d \times d_k}WQ​∈Rd×dk​
  • WK∈Rd×dkW_K \in \mathbb{R}^{d \times d_k}WK​∈Rd×dk​
  • WV∈Rd×dvW_V \in \mathbb{R}^{d \times d_v}WV​∈Rd×dv​

💡 注意:这三个矩阵不是每个位置单独学的,而是整个模型只有一套,所有 patch 共用!

然后计算:

  • Q=XWQ=[x1WQ,  x2WQ,  x3WQ,  x4WQ]=[q1,q2,q3,q4]Q = X W_Q = [x_1 W_Q,\; x_2 W_Q,\; x_3 W_Q,\; x_4 W_Q] = [q_1, q_2, q_3, q_4]Q=XWQ​=[x1​WQ​,x2​WQ​,x3​WQ​,x4​WQ​]=[q1​,q2​,q3​,q4​]
  • K=XWK=[k1,k2,k3,k4]K = X W_K = [k_1, k_2, k_3, k_4]K=XWK​=[k1​,k2​,k3​,k4​]
  • V=XWV=[v1,v2,v3,v4]V = X W_V = [v_1, v_2, v_3, v_4]V=XWV​=[v1​,v2​,v3​,v4​]

✅ 所以:

  • 所有 q 都用同一个 WQW_QWQ​ → 这叫参数共享
  • 但因为输入 xix_ixi​ 不同,所以 q1,q2,...q_1, q_2, ...q1​,q2​,... 是不同的向量

🔍 怎么理解 WQW_QWQ​、WKW_KWK​、WVW_VWV​ 的作用?

可以把它们看作三个“翻译器”,把原始输入“翻译”成三种不同角色的语言:

矩阵角色比喻
WQW_QWQ​查询翻译器把“我是什么”翻译成“我想找什么”
WKW_KWK​关键词翻译器把“我是什么”翻译成“我能被怎么找到”
WVW_VWV​内容翻译器把“我是什么”翻译成“我真正要传递的信息”

🌰 举个图像例子:

  • 一个 patch 是“狗的眼睛”
    • WQW_QWQ​ 把它变成:“我在找和眼睛相关的区域(比如鼻子、耳朵)”
    • WKW_KWK​ 把它变成:“我是面部特征,可以被‘找脸’的 query 匹配”
    • WVW_VWV​ 把它变成:“我包含高亮、圆形、深色等视觉细节”

这些“翻译规则”(即 WQ,WK,WVW_Q, W_K, W_VWQ​,WK​,WV​)是通过大量数据训练出来的。模型会自动学会:

  • 什么时候该让 Q 关注局部纹理?
  • 什么时候该让 K 响应“物体整体”类的查询?
  • V 应该保留哪些信息用于后续融合?

❓为什么可以共用同一个 WQW_QWQ​?

因为 Transformer 假设:不管输入出现在序列的哪个位置,它的“角色转换规则”是一样的。

  • 在 NLP 中:“猫”不管是句首还是句尾,它作为“名词”的性质不变
  • 在 CV 中:一个“边缘 patch”不管在图像左上角还是右下角,它作为“边缘”的语义也不变

所以用同一套 WQW_QWQ​ 是合理且高效的(减少参数,提升泛化)。

⚠️ 补充:有些改进模型(如 Deformable DETR、CoaT)会引入位置相关的投影,但标准 Transformer/ViT 是位置无关的线性投影。


🧠 再打个比方(工厂流水线)

想象一个工厂:

  • 原料:4 个零件 x1x_1x1​ 到 x4x_4x4​
  • 三条生产线:
    • 生产线 Q:用同一套模具 WQW_QWQ​,把每个零件压成“需求卡”
    • 生产线 K:用同一套模具 WKW_KWK​,把每个零件压成“标签卡”
    • 生产线 V:用同一套模具 WVW_VWV​,把每个零件压成“内容包”

然后系统拿所有“需求卡”去匹配所有“标签卡”,决定怎么组合“内容包”。

✅ 模具(WQ,WK,WVW_Q, W_K, W_VWQ​,WK​,WV​)是固定的,但原料不同 → 产出就不同。


✅ 最终总结

问题回答
QKV 是怎么来的?Q=XWQQ = XW_QQ=XWQ​, K=XWKK = XW_KK=XWK​, V=XWVV = XW_VV=XWV​
所有 q 共用 WQW_QWQ​ 吗?是的!全局共享,参数不随位置变化
WQ,WK,WVW_Q, W_K, W_VWQ​,WK​,WV​ 是什么?可学习的线性变换矩阵,相当于三个“角色翻译器”
它们是怎么训练的?和整个网络一起,通过反向传播自动优化(目标:让注意力机制有效)
为什么能共用?因为模型假设:语义角色与位置无关,同一类输入应被同样“翻译”

我们用 Vision Transformer (ViT) 的一个具体、真实、带数字的例子,一步一步说明 Q、K、V 以及对应的权重矩阵 WQW_QWQ​、WKW_KWK​、WVW_VWV​ 的维度、大小和计算过程。


🖼️ 场景设定(以 ViT-Base 为例)

这是 Google 提出的标准 ViT 模型中最常用的配置:

参数值
输入图像尺寸224 × 224 × 3
Patch 大小16 × 16
Patch 数量(224/16)2=14×14=196(224/16)^2 = 14 \times 14 = 196(224/16)2=14×14=196
每个 patch 展平后维度16×16×3=76816 \times 16 \times 3 = 76816×16×3=768
Embedding 维度(即 d)768
注意力头数(多头)12(但我们先看单头简化版)
dk=dv=dd_k = d_v = ddk​=dv​=d(标准 ViT 中通常设为相等)768

💡 为了简单,我们先看单头注意力(single-head),后面再说多头。


第一步:输入 → Patch Embeddings

原始图像 → 切成 196 个 patch → 每个 patch 展平成 768 维向量 → 加上位置编码。

所以输入矩阵:
X∈R196×768 X \in \mathbb{R}^{196 \times 768} X∈R196×768

  • 196 行:每个 patch(token)
  • 768 列:每个 token 的特征维度

第二步:定义 WQ,WK,WVW_Q, W_K, W_VWQ​,WK​,WV​

在标准 ViT 中,这三个矩阵的形状是:

WQ∈R768×768WK∈R768×768WV∈R768×768\\ W_Q \in \mathbb{R}^{768 \times 768} \\ W_K \in \mathbb{R}^{768 \times 768} \\ W_V \in \mathbb{R}^{768 \times 768} WQ​∈R768×768WK​∈R768×768WV​∈R768×768

✅ 因为输入维度是 768,输出 Q/K/V 的维度也设为 768(也可以不同,但 ViT 默认相同)。

这些是可训练参数,模型一开始随机初始化,训练后学会“如何翻译”。


第三步:计算 Q, K, V

进行矩阵乘法:

Q=XWQ⇒(196×768)×(768×768)=(196×768)K=XWK⇒(196×768)V=XWV⇒(196×768) Q = X W_Q \quad \Rightarrow \quad (196 \times 768) \times (768 \times 768) = (196 \times 768) \\ K = X W_K \quad \Rightarrow \quad (196 \times 768) \\ V = X W_V \quad \Rightarrow \quad (196 \times 768) Q=XWQ​⇒(196×768)×(768×768)=(196×768)K=XWK​⇒(196×768)V=XWV​⇒(196×768)

所以:

  • Q: 196 个 query 向量,每个 768 维 → 形状 (196, 768)
  • K: 196 个 key 向量,每个 768 维 → 形状 (196, 768)
  • V: 196 个 value 向量,每个 768 维 → 形状 (196, 768)

🔍 注意:虽然 Q 和 K 都是 768 维,但它们是通过不同的线性变换得到的,所以内容不同!


第四步:计算注意力(核心!)

现在我们要算:每个 patch 应该关注哪些其他 patch?

1. 计算相似度(Q 和 K 的点积)

Scores=QKT⇒(196×768)×(768×196)=(196×196) \text{Scores} = Q K^T \quad \Rightarrow \quad (196 \times 768) \times (768 \times 196) = (196 \times 196) Scores=QKT⇒(196×768)×(768×196)=(196×196)

结果是一个 196×196 的矩阵,其中:

  • 第 i 行第 j 列 = qi⋅kjq_i \cdot k_jqi​⋅kj​ → 表示 patch i 的 query 与 patch j 的 key 的相关性

✅ 举例:

  • Scores[0, 50] 很大 → 说明第 0 个 patch(比如左上角天空)在关注第 50 个 patch(可能是云朵)
  • Scores[100, 100] 可能不大 → 自己不一定最相关!
2. Softmax 归一化

对每一行做 softmax,得到注意力权重:
A=softmax(QKT768)∈R196×196 A = \text{softmax}\left( \frac{QK^T}{\sqrt{768}} \right) \in \mathbb{R}^{196 \times 196} A=softmax(768​QKT​)∈R196×196

每行和为 1,表示“当处理 patch i 时,给其他所有 patch 分配的注意力比例”。

3. 加权求和 V

Output=AV⇒(196×196)×(196×768)=(196×768) \text{Output} = A V \quad \Rightarrow \quad (196 \times 196) \times (196 \times 768) = (196 \times 768) Output=AV⇒(196×196)×(196×768)=(196×768)

最终输出:每个 patch 都融合了全局信息的新表示。


📊 总结维度表(单头 ViT)

名称符号形状说明
输入XXX(196, 768)196 个图像块,每个 768 维
查询权重WQW_QWQ​(768, 768)可学习,所有 patch 共用
键权重WKW_KWK​(768, 768)可学习,共用
值权重WVW_VWV​(768, 768)可学习,共用
查询Q=XWQQ = X W_QQ=XWQ​(196, 768)每个 patch 的“我想关注什么”
键K=XWKK = X W_KK=XWK​(196, 768)每个 patch 的“我能被怎么匹配”
值V=XWVV = X W_VV=XWV​(196, 768)每个 patch 的“我有什么内容”
注意力分数QKTQK^TQKT(196, 196)所有 patch 两两之间的相关性
输出AVA VAV(196, 768)融合全局信息后的新特征

🔁 补充:多头注意力(Multi-Head)怎么办?

ViT-Base 实际用的是 12 个头。做法是:

  • 把 WQW_QWQ​ 拆成 12 个小矩阵,每个头有自己的 WQh∈R768×64W_Q^h \in \mathbb{R}^{768 \times 64}WQh​∈R768×64(因为 768 ÷ 12 = 64)
  • 同理 WKh,WVh∈R768×64W_K^h, W_V^h \in \mathbb{R}^{768 \times 64}WKh​,WVh​∈R768×64
  • 每个头算自己的 Q_h, K_h, V_h ∈ (196, 64)
  • 每个头输出 (196, 64),12 个头拼起来 → (196, 768)
  • 最后再过一个线性层(WO∈R768×768W_O \in \mathbb{R}^{768 \times 768}WO​∈R768×768)整合

但核心思想不变:还是 Q-K 匹配,V 融合。


✅ 最后用一句话帮你记住:

在 ViT 中,196 个图像块各自通过同一套三个“翻译器”(WQ,WK,WVW_Q, W_K, W_VWQ​,WK​,WV​)变成 Q、K、V;然后每个块用自己的 Q 去扫描所有块的 K,决定从谁的 V 里拿信息,最终得到更聪明的表示。


如果你用 PyTorch 写 ViT,这部分代码大概长这样(简化):

# X: [196, 768]
Q = X @ W_Q   # [196, 768]
K = X @ W_K   # [196, 768]
V = X @ W_V   # [196, 768]

attn = (Q @ K.T) / math.sqrt(768)   # [196, 196]
attn = attn.softmax(dim=-1)

output = attn @ V  # [196, 768]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更多推荐