深度学习进阶(五)Vision Transformer
<p>在上一篇,我们已经完整介绍了原始 Transformer 的结构和整体传播逻辑。</p><p>从结果上看,Transformer 在 NLP 领域带来了范式级的突破:通过自注意力机制实现了对序列的全局建模能力。<br>
然而,实际上,原始 Transformer 依然只是一个<strong>面向序列数据的模型。</strong></p>
<p>自然而然地,这种全新的建模方式引起了相关的思潮,首当其冲的便是:</p>
<blockquote>
<p><strong>既然自注意力机制本质上是一种全局依赖建模方法,它是否真的只适用于“序列”这一种数据形式?</strong></p>
</blockquote>
<p>换句话说,如果我们不再局限于文本,而是将目光投向更广泛的数据类型,例如图像这种天然具有二维结构的信息,Transformer 是否依然能够发挥作用?</p>
<p>于是 Vision Transformer 应运而生。它尝试回答的问题便是:</p>
<blockquote>
<p><strong>如果抛开卷积结构,仅依赖注意力机制,能否完成视觉任务的建模?</strong></p>
</blockquote>
<p>我们以此为出发点,展开 Vision Transformer,即 <strong>ViT</strong> 的逻辑。</p>
<h1 id="1-vit-的核心思想">1. ViT 的核心思想</h1>
<p>ViT 提出于 2020 年的论文:<em><strong>An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale</strong></em>,它首次证明了<strong>纯 Transformer(无卷积)可以在视觉任务上达到 SOTA</strong> 。</p>
<p>你会发现 ViT 的提出距离原始 Transformer 的发表已经过去三年了,但这并不意味着Transformer 的发展进入了停滞期。<br>
实际上,这三年正是 Transformer 爆炸式扩展的阶段,只不过主要发生在 NLP 内部,在语言任务中完成充分验证之后,Transformer 逐渐被视为一种<strong>通用的序列建模框架</strong>。在此基础上,向视觉等其他模态的扩展,才成为顺理成章的下一步。</p>
<p>再回到 ViT 自身,其核心思路不难理解,可以一句话概括:</p>
<blockquote>
<p><strong>把图像切成“块(Patch)”,然后当作 token 序列送入 Transformer 的 encoder 。</strong></p>
</blockquote>
<h1 id="2vit-的数据处理">2.ViT 的数据处理</h1>
<p>要说明的是:对标 CV 任务,<strong>ViT 只使用了 Transformer 的 encoder 逻辑</strong>,因此,其重点其实是在对数据的处理上。</p>
<h2 id="21-patch-划分">2.1 Patch 划分</h2>
<p>首先,我们要把图像分割成一个个等大不重叠的块,即 Patch :<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213203586-2051823882.png" alt="image.png" loading="lazy"><br>
原始 ViT 设计输入图像大小为 <span class="math inline">\(224 \times 224 \times 3\)</span>,而 patch 大小是 <span class="math inline">\(16 \times 16\)</span> 。<br>
于是,最终得到 patch 数量即为:</p>
<p></p><div class="math display">\[N = \frac{224 \times 224}{16 \times 16} = 14 \times 14 = 196
\]</div><p></p><p>很好理解,再简单推广一下,对于输入图像:</p>
<p></p><div class="math display">\[\mathbf{X} \in \mathbb{R}^{H \times W \times C}
\]</div><p></p><p>如果设计每个 patch 大小为 <span class="math inline">\(P \times P\)</span>,那么得到的 patch 数量就是:</p>
<p></p><div class="math display">\[N = \frac{HW}{P^2}
\]</div><p></p><h2 id="22-展平">2.2 展平</h2>
<p>现在,我们已经把一张二维图像拆成了 <span class="math inline">\(N\)</span> 个小块。但仍然存在问题:<strong>Transformer 并不能直接处理二维结构的数据,它只能接收“向量序列”。</strong></p>
<p>因此,接下来要做的事情就很明确了:<strong>把每一个 Patch 转换为一个向量。</strong><br>
而转换方法就是最基本的<strong>展平</strong>:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213213511-1579450968.png" alt="image.png" loading="lazy"><br>
即对于每一个 patch,其原始形状为:</p>
<p></p><div class="math display">\[P \times P \times C
\]</div><p></p><p>我们将其按通道展开为一个一维向量:</p>
<p></p><div class="math display">\[\mathbf{x}_p \in \mathbb{R}^{P^2 \cdot C}
\]</div><p></p><p>以 ViT 中的配置 <span class="math inline">\(P = 16\)</span>、<span class="math inline">\(C = 3\)</span> 为例,则:</p>
<p></p><div class="math display">\[\mathbf{x}_p \in \mathbb{R}^{16 \times 16 \times 3 = 768}
\]</div><p></p><p>现在,每一个 patch 已经变成了一个<strong>固定长度的向量表示</strong>。</p>
<p>但是,我们在很早之前的图像处理基础中就提到过:<strong>展平会带来空间信息的损失</strong>。<br>
在这一步处理后,数据就无法再提供类似“鼻子要在嘴巴上面”的空间信息了,这也是我们在卷积内容中提到的用全连接网络处理图像的弊端之一。</p>
<p>显然,ViT 会采用别的措施来弥补这个问题,我们继续:</p>
<h2 id="23-线性映射">2.3 线性映射</h2>
<p>完成展开后,我们已经得到了每个 patch 的序列表示,但这里还有一个细节问题:<strong>展平后的向量维度是 <span class="math inline">\(P^2C\)</span>,而 Transformer 的输入维度通常是一个统一的隐藏维度 <span class="math inline">\(D\)</span>。</strong></p>
<p>因此,我们将每个 patch 展平后的向量通过一个线性变换映射到目标维度:</p>
<p></p><div class="math display">\[\mathbf{z}^i = \mathbf{x}^i \mathbf{W} + \mathbf{b}
\]</div><p></p><p><img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213157963-1494965739.png" alt="image.png" loading="lazy"></p>
<p>现在,<strong>一个 patch 就变成了一个 token</strong>。</p>
<p>不过显然,一个明显的问题就是:<strong>如果 <span class="math inline">\(P^2C=D\)</span> ,那这一步是不是就没有必要了?</strong><br>
答案当然是否定的,在这里,我们不仅要看维度关系,还要想到<strong>线性层本身在这里的语义作用:</strong><br>
如果我们不对原始二维信息“序列化”,那么在一个常见的卷积网络中,我们会使用卷积层来进行特征提取,其运算逻辑本身仍是可学习参数下的加权求和。<br>
而现在,我们把 patch 转换为 token ,输入线性层本身仍然是特征提取的过程,类比来说:<strong>patch 就相当于感受野,而权重就是卷积核。</strong></p>
<p>而我们最终得到的向量,其实就相当于完成了<strong>对 patch 的词嵌入</strong>过程。</p>
<h2 id="24--cls-token">2.4 token</h2>
<p>在正式进入模型前,我们对输入的处理仍没有结束,我们把一幅图像转变为一组 token ,那么一个问题是:</p>
<blockquote>
<p><strong>在分类任务中,我们应该使用哪一个 token 来代表整张图像?</strong></p>
</blockquote>
<p>如果发散来想,可能的做法包括对所有 token 做平均或拼接所有 token 后再融合等。<br>
但 ViT 的选择是: <strong>引入一个专门用于“汇聚全局信息”的 token,即 token。</strong><br>
在更广的应用任务中,也叫<strong>全局 token</strong> 。</p>
<p><img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213158082-1163200716.png" alt="image.png" loading="lazy"><br>
展开来说:对于原本划分为 <span class="math inline">\(N\)</span> 个 token 的图像:</p>
<p></p><div class="math display">\[[\mathbf{z}^1, \mathbf{z}^2, \dots, \mathbf{z}^N]
\]</div><p></p><p>我们在最前面拼接一个 token:</p>
<p></p><div class="math display">\[[\mathbf{x}_{cls}, \mathbf{z}^1, \mathbf{z}^2, \dots, \mathbf{z}^N]
\]</div><p></p><p>这里的 <span class="math inline">\(\mathbf{x}_{cls}\)</span> 是一个<strong>随机初始化的可学习参数</strong>,与其他 token 具有完全相同的维度。</p>
<p>要强调的是, token 是一个可学习参数,所以<strong>这里的“汇聚全局信息”是我们为其赋予的语义并在之后的逻辑里实现的。</strong><br>
我们先继续传播过程,在下面就会展开其具体逻辑:</p>
<h2 id="25--位置编码-pe">2.5位置编码 PE</h2>
<p>现在,我们还有一个遗留问题:</p>
<blockquote>
<p><strong>由patch 转换得到 token 失去了彼此之间的“位置信息”。</strong></p>
</blockquote>
<p>不像 CNN ,现在模型并不知道哪个 patch 在左上角、哪个 patch 在右下角、哪些 patch 是相邻的。<br>
这显然是不合理的, <strong>因为在视觉任务中位置本身就是一种极其重要的信息。</strong></p>
<p>因此,ViT 延续了 NLP 中的机制:<strong>位置编码</strong>。</p>
<p></p><div class="math display">\[\mathbf{X}_{input} = \mathbf{E}_{embedding} + \mathbf{E}_{pos}
\]</div><p></p><p>但和原始 Transformer 的正余弦固定编码不同,ViT 使用的是<strong>可学习位置编码</strong>:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213157242-1074714232.png" alt="image.png" loading="lazy"></p>
<p>展开来说,在 ViT 中,位置编码被建模为一组<strong>可学习的参数矩阵</strong>,模型为每一个 token 位置都分配一个对应的向量表示,<strong>这些向量在训练过程中与模型参数一起被优化</strong>。</p>
<p>假设输入序列包含 <span class="math inline">\(N+1\)</span> 个 token(其中包含 1 个 token 和 <span class="math inline">\(N\)</span> 个 patch token),则位置编码可以表示为:</p>
<p></p><div class="math display">\[\mathbf{E}_{pos} \in \mathbb{R}^{(N+1) \times D}
\]</div><p></p><p>其中,每一行都对应一个位置的可学习向量,以此完成 PE 的注入。</p>
<p>到这里,我们就完成了数据的所有处理,我们留下了两个问题没有详细展开:</p>
<ol>
<li><strong>ViT 究竟如何弥补空间信息的损失?</strong></li>
<li><strong> token 如何“汇聚全局信息”?</strong></li>
</ol>
<p>二者在 ViT 的 Transformer 逻辑便可以被解答。</p>
<h1 id="3-vit-的-transformer-逻辑">3. ViT 的 Transformer 逻辑</h1>
<p>就像最开始说的,ViT 只使用了 Transformer 的 encoder ,我们在前面的内容里已经详细展开过了,这里我们摆一下原论文中的完整传播图:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408213157501-422458737.png" alt="image.png" loading="lazy"><br>
网络结构本身十分清晰,我们从刚刚的问题出发,展开这部分里要理解的内容:</p>
<h2 id="31-vit-究竟如何弥补空间信息的损失">3.1 ViT 究竟如何弥补空间信息的损失?</h2>
<p>对于这个问题,可能在刚刚的位置编码可能会有所启发:</p>
<blockquote>
<p><strong>既然 PE 是可学习的,那难道空间信息是训练出来,体现在 PE 中的吗?</strong></p>
</blockquote>
<p>如果只停留在这里,就片面,甚至错误了。<br>
实际上,<strong>真正完成“空间关系建模”的,是自注意力机制。</strong></p>
<p>展开来说:首先,我们已经不只一次提到过自主力机制的全局建模能力了,也就是说,当patch 转换成的 token 序列输入自注意力中后, <strong>每一个 patch token,都可以直接和所有其他 patch 建立联系。</strong></p>
<p>通过注意力我们可以学习得到不同 patch 间的相关性,但是仅仅知道这个不够的,如果向量里没有位置信息,我们就无法学习到位置上的关联,因此 PE 仍然必不可少。<br>
二者结合,虽然模型不知道“左上角”这个概念,但它可以学到:</p>
<ul>
<li>哪些 patch 经常一起出现(例如:眼睛和鼻子)。</li>
<li>哪些 patch 存在相对结构(例如:天空通常在地面上方)。</li>
</ul>
<p>到这里,关键点就出现了:<strong>ViT 不同于卷积让空间结构作为已知信息加入训练,空间信息其实是 ViT 的统计学习结果。</strong><br>
<img src="https://img2024.cnblogs.com/blog/3708248/202604/3708248-20260408214155982-1276003034.png" alt="image" loading="lazy"><br>
其实从直觉角度,对这种逻辑先想到的应该是它的局限:<strong>对数据量的高要求</strong>。<br>
但它的优势在于全局建模能力:CNN 想看到全局,必须叠很多层或者用大卷积核,但 ViT 一步到位:<strong>第一层注意力,就已经是全局感受野。</strong></p>
<p>最终,对这个问题的答案是:</p>
<blockquote>
<p><strong>ViT 并没有“恢复”空间结构,而是用“位置编码 + 自注意力”在数据中重新学习空间结构。</strong></p>
</blockquote>
<h2 id="32-cls-token-如何汇聚全局信息">3.2 token 如何“汇聚全局信息”?</h2>
<p>接下来我们看第二个问题: <strong>为什么一个随机初始化的向量,最后可以代表整张图像?</strong></p>
<p><strong>其核心仍然是自注意力机制</strong>:首先在 Transformer 中,每一层的注意力都会做一件事情:</p>
<blockquote>
<p><strong>每个 token 都会从“所有 token”那里收集信息(加权求和)。</strong></p>
</blockquote>
<p>因此, token 在每一层都会执行:</p>
<p></p><div class="math display">\[\mathbf{x}_{cls}^{(l+1)} = \sum_{j} \alpha_{cls,j} \mathbf{z}_j^{(l)}
\]</div><p></p><p>到这里,我们知道了 token 会逐层提炼信息,但问题依然存在:</p>
<blockquote>
<p><strong>不仅 token 在提炼,其他 token 也都在提炼,为什么只有你能代表全局?</strong></p>
</blockquote>
<p>没错,从计算上讲:所有 token 都在做全局信息融合,也就是说每个 patch token 都能“看到全局”, 最终其实都包含了一定程度的全局信息。</p>
<p><strong>实际上,这是因为在设计里只有 CLS 被当作“最终输出”来使用。</strong><br>
在分类任务中,ViT 的最终输出是:</p>
<p></p><div class="math display">\[\text{logits} = \text{MLP}(\mathbf{x}_{cls}^{(L)})
\]</div><p></p><p>也就是说:<strong>只有 token 会被送入输出层完成分类,并直接参与损失函数计算。</strong><br>
因此,从反向传播来说:<strong>只有 token 会被“强制要求”去学习对分类最有用的全局信息。</strong><br>
这才是它能汇聚全局语义的原因,是我们手动设计的监督驱动,让其在反向传播中不断学习全局语义。<br>
同样给个较官方的答案:</p>
<blockquote>
<p><strong> token 并不是天然具备“全局语义”的特殊结构,而是由于它是唯一直接参与监督训练的 token,因此被优化为一个能够表达整张图像语义的表示。</strong></p>
</blockquote>
<h1 id="4总结">4.总结</h1>
<p>ViT 的核心作用可以概括为:<strong>将图像转化为 token 序列,并在统一的 Transformer 框架下完成全局建模</strong>。</p>
<p>相比传统 CNN,它最关键的突破在于:<strong>不再依赖局部感受野与平移不变性等空间先验,而是通过自注意力机制直接在全局范围内建立 patch 之间的联系</strong>。这使得模型从一开始就具备全局感受野,无需通过多层堆叠逐步扩展,从而在表达能力上具备更高的灵活性。</p>
<p>同时,ViT 并没有显式“恢复”图像的空间结构,而是通过<strong>位置编码(PE)与自注意力机制的结合,在数据中学习空间关系</strong>。</p>
<p>总体来看,ViT 的核心意义在于:<strong>它将 Transformer 从“序列建模框架”推广为“通用感知建模框架”</strong>。<br>
这种从“结构先验驱动”到“数据驱动建模”的转变,不仅重新定义了视觉任务的建模方式,也为后续<strong>多模态模型</strong>的发展奠定了统一的基础。</p>
<p>但是,ViT 仍有其局限,那就是需要<strong>超大规模的训练数据</strong>,自然,也就出现了针对这一问题的改进,我们在下一篇再展开介绍。</p><br><br>
来源:https://www.cnblogs.com/Goblinscholar/p/19837411
頁:
[1]