ViT Transformer 的阅读?(应该算是阅读吧)

引言

在快要到 2026 年的今天, ViT 相比于当下的复杂的结构而言, 已经显得比较简单了, 我读论文的时候的最大感觉是, 它充满了 Transformer 在各领域蓬勃发展的野蛮生长的气息.
但是作为 Transformer 在 CV 领域的里程碑式的工作, 并且我作为这方面的初学者, 我觉着还是需要读一下这一篇论文An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale, 做一个简单的记录.

ViT 的整体结构

ViT 的整体结构如下图所示:
1
可以看到, 他的特殊处理是在于输入部分, 传统的 CNN 是通过 kernel 来滑动提取局部信息, 这样的一个 CNN 的输出很难直接送入 Transformer 中进行处理, 因为
Transformer 需要的是一个序列化的输入, 而 CNN 的输出是一个三维的 feature map.

因此, 相较于同期的其他处理, ViT 直接将输入图像划分为若干个小的 patch, 然后将每个 patch 展平并映射到一个固定维度的向量空间中, 形成一个序列化的输入, 这样就可以直接送入 Transformer 中进行处理.

  • 具体来说, 假设输入图像的尺寸为 (高度, 宽度, 通道数), 我们将其划分为大小为的若干个不重叠的 patch, 则总共会得到个 patch.
  • 每个 patch 被展平为一个向量, 并通过一个线性投影映射到一个维的向量空间中, 形成一个序列化的输入矩阵.
  • 此外, 为了让模型能够捕捉到位置信息, ViT 还引入了可学习的位置编码, 将其与输入序列相加, 形成最终的输入表示.
  • 接下来, 这个序列化的输入就可以直接送入标准的 Transformer Encoder 中进行处理, 经过多层的 Transformer Encoder Layer 的处理后, 得到最终的输出表示.

其具体的一个维数变换大概是这样:
为什么新加上的 class token work?

  • 因为在 transformer 中, 两两 token 之间是可以相互 attention 的, 因此 class token 可以和所有的 patch token 进行 attention, 从而聚合全局的信息, 这样我们就可以在最终的输出中使用 class token 来进行分类任务.

一些其他的细节

相对于 CNN 而言, ViT 的先验信息很少, 因此在中小数据集上的表现并不理想, 论文中提到需要在大规模数据集上进行预训练, 然后再进行微调, 才能取得较好的效果.

此外, ViT 的 attention 机制也与 Transformer 类似, 主要包括 Multi-Head Self-Attention 和 Feed-Forward Neural Network (FFN) 两个部分, 具体的计算过程与 Transformer 中的 Self-Attention 类似, 这里就不再赘述.

总的来说, ViT 通过将图像划分为 patch 并使用 Transformer 进行处理, 提供了一种新的思路来解决计算机视觉中的图像分类问题, 并且在大规模数据集上取得了优异的表现, 成为计算机视觉领域的重要里程碑.

回顾一下Transformer

引言

Transformer 在Attention is All You Need一文中被提出, 本来想读一下原文的, 但是时间并不太够, 因此我们这里就简单捋一下就行.

整体结构

Transformer 的整体结构如下图所示:
Transformer架构图
可以看到, 其主要由 Encoder 和 Decoder 两部分组成.

  • Transformer 的工作流程:
    • 首先获取输入每一个词的表示向量, 由单词的 embedding 和位置的 embedding 相加得到.
    • 然后将输入到 Encoder 中, 经过多层的 Encoder Layer 的处理, 得到编码后的表示.
      • 表示, 其中是序列长度, 是词向量的维度.
    • 接着将目标序列的输入输入到 Decoder 中, 经过多层的 Decoder Layer 的处理, 并结合 Encoder 的输出, 最终得到预测结果.如下图:
      Transformer Decoder架构图
      • 使用的过程中, 翻译到单词时, 需要通过Mask操作掩盖住未来的信息, 以防止模型在预测时看到未来的词.

OK, 下面我们来具体看看 Encoder Layer 和 Decoder Layer 的结构.

Self-Attention 机制

Transformer 的核心是 Self-Attention 机制, 其结构如下图所示:

Self-Attention架构图

  • 左侧为Encoder block
  • 右侧为Decoder block
  • 红圈中的部分为Multi-Head Attention机制, 是由多个 Self-Attention 组成的.
  1. 可以看到Encoder block包含一个Multi-Head Attention层.
  2. Decoder block包含两个Multi-Head Attention层, 第一个用于处理目标序列的输入, 第二个用于结合 Encoder 的输出.
  3. 每个 Attention 层后面都跟着一个**Feed-Forward Neural Network (FFN)**层.

因为Self-Attention机制是 Transformer 的核心, 因此我们重点来看一下它的计算过程.

dsa

上图是Self-Attention的计算流程图, 计算时需要用到三个矩阵: Query (), Key (), Value (), 实际过程中, 这三个矩阵都是通过输入的表示经过线性变换得到的.

Q, K, V 的计算

Self-Attention机制中, 对于输入的表示, 可以使用线性变换矩阵来计算:

2

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from math import sqrt


class SelfAttention(nn. Module):
def __init__(self, d_model, d_k, d_v):
" " "
input: X: (batch_size, n, d_model)
q: (batch_size, n, d_k)
k: (batch_size, n, d_k)
v: (batch_size, n, d_v)
" " "
super(SelfAttention, self).__init__()
self.d_k = d_k
self.W_Q = nn. Linear(d_model, d_k)
self.W_K = nn. Linear(d_model, d_k)
self.W_V = nn. Linear(d_model, d_v)
self._norm_factor = sqrt(d_k)

def forward(self, X):
Q = self.W_Q(X) # Q: (batch_size, n, d_k)
K = self.W_K(X) # K: (batch_size, n, d_k)
V = self.W_V(X) # V: (batch_size, n, d_v)

scores = torch.matmul(Q, K.transpose(-2, -1)) / sqrt(self.d_k) # (batch_size, n, n)
attn_weights = torch.softmax(scores, dim=-1) # (batch_size, n, n)
output = torch.matmul(attn_weights, V) # (n_batch_size, n, d_v)

return output

因此, 当我们得到了后, 就可以计算 Attention 的输出了:

3

得到之后, 使用 Softmax 函数对每一行进行归一化, 即每一行的和都变为 1.

4

最后将归一化后的权重矩阵与相乘, 得到最终的 Attention 输出.

5

上图中softmax矩阵的第一行可以理解为单词 1 对其他单词的关注程度, 最终单词 1 的输出等于所有单词的值加权求和.

Multi-Head Attention

上一步中, 我们已经知道怎么使用 Self-Attention 机制来计算 Attention 的输出了, 但是 Transformer 中使用的是Multi-Head Attention机制, 其结构如下图所示:

Multi-Head Attention架构图

从上图中可以看到Multi-Head Attention机制包含多个并行的 Self-Attention 头, 每个头都有自己的一组线性变换矩阵.

首先将输入分别传递到 h 个 Self-Attention 头中, 得到 h 个不同的 Attention 输出, 下面是 h = 8 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from math import sqrt

class MultiHeadAttention(nn. Module):
def __init__(self, d_model, d_k, d_v, h):
" " "
input: X: (batch_size, n, d_model)
q: (batch_size, d_model, d_k)
k: (batch_size, d_model, d_k)
v: (batch_size, d_model, d_v)
" " "
super(MultiHeadAttention, self).__init__()
self.h = h
self.d_k = d_k
self.d_v = d_v

self.W_Q = nn. ModuleList([nn. Linear(d_model, d_k) for _ in range(h)])
self.W_K = nn. ModuleList([nn. Linear(d_model, d_k) for _ in range(h)])
self.W_V = nn. ModuleList([nn. Linear(d_model, d_v) for _ in range(h)])
self.linear = nn. Linear(h * d_v, d_model)

def forward(self, X):
heads = []
for i in range(self.h):
Q = self.W_Q[i](X)
K = self.W_K[i](X)
V = self.W_V[i](X)

scores = torch.matmul(Q, K.transpose(-2, -1)) / sqrt(self.d_k)
attn_weights = torch.softmax(scores, dim=-1)
head = torch.matmul(attn_weights, V) # (batch_size, n, d_v)
heads.append(head)

concat_heads = torch.cat(heads, dim=-1) # (batch_size, n, h * d_v)
output = self.linear(concat_heads) # (batch_size, n, d_model)

return output

6

得到 8 个输出后, 将它们在最后一个维度上进行拼接, 得到一个新的表示, 然后通过一个线性变换矩阵将拼接后的表示映射回原始的维度.

7

可见Multi-Head Attention输出的矩阵维度与输入矩阵的维度相同, 这样就可以方便地将其与后续的层进行连接.

Other components

剩余的层比较简单, 因此不再赘述.

Decoder Layer

Decoder Layer 的结构如下图红框内所示:

8

其与 Encoder Layer 的主要区别在于多了一个Masked Multi-Head Attention层, 该层用于处理目标序列的输入, 并且在计算 Attention 时会掩盖住未来的信息, 以防止模型在预测时看到未来的词.

第一个Multi-Head Attention

我们重点解释一下 Mask 操作.

  1. 第一步是 Decoder 的输入矩阵和 Mask 矩阵, Mask 矩阵是一个上三角矩阵, 用于掩盖未来的信息.
  2. 接下来的操作和之前的 Self-Attention 机制类似, 通过输入矩阵计算., 之后计算.
  3. 然后将 Mask 矩阵应用到上, 将被掩盖的位置设置为负无穷大, 这样在 Softmax 计算时, 这些位置的权重会变为 0.

9

  1. 最后进行 Softmax 归一化, 并与相乘, 得到最终的 Attention 输出.

第二个Multi-Head Attention

第二个 Multi-Head Attention 层与 Encoder Layer 中的 Multi-Head Attention 层类似, 只是这里的来自于 Encoder 的输出, 而来自于第一个 Attention 层的输出.

根据 Encoder 的输出计算得到, 根据上一个 Attention 的输出计算得到, 然后计算 Attention 的输出.

时间复杂度分析

Transformer 的时间复杂度主要来自于 Self-Attention 机制. 对于一个长度为的序列, Self-Attention 的时间复杂度为, 其中是词向量的维度. 这是因为在计算时, 需要进行的矩阵乘法, 每个元素的计算涉及到维的向量点积.
因此, 对于一个包含层 Encoder 和 Decoder 的 Transformer 模型, 总的时间复杂度为.

总结

Transformer 应该是这样的.