2、Transformer基础回顾:自注意力机制原理,多头注意力(MSA),位置编码,Transformer Encoder结构

好,咱们正式开始搭建MobileViT之前,我先把Transformer的核心部件拆开揉碎了讲一遍。

你可能会问:做目标检测,为什么要学Transformer?

嗯,这个问题我当年也问过自己。后来在项目中用MobileViT替换纯CNN做特征提取,发现小目标召回率直接提升了8个点。说白了,Transformer的全局建模能力,是CNN很难替代的。

2.1 自注意力机制原理

自注意力(Self-Attention)是整个Transformer的基石。它的核心思想就一句话:每个位置都跟所有位置做一次“亲密度计算”。

我习惯用一个比喻来理解:假设你有一句话“I love you”,每个词都要看看其他词跟自己有多“相关”。比如“love”会重点关注“I”和“you”,因为它们在语法上紧密相连。

具体怎么算?三个步骤:

  1. 生成Q、K、V:每个输入向量通过三个不同的线性变换,得到Query、Key、Value。
  2. 计算注意力分数:Q与所有K做点积,得到相似度分数。
  3. 加权求和:分数经过Softmax归一化后,对V进行加权求和。

公式长这样:

Attention(Q, K, V) = softmax(Q × K^T / √d_k) × V

这里有个细节:除以√d_k是为了防止点积结果过大,导致Softmax梯度消失。d_k是Key的维度,我一般设成64或128。

核心要点:自注意力让每个位置都能“看到”全局,这是CNN做不到的。CNN靠堆叠卷积层扩大感受野,而自注意力一步到位。

我的经验:在项目中,如果输入序列长度超过512,自注意力的计算量会爆炸。MobileViT通过局部窗口+全局注意力的方式解决了这个问题,后面会细讲。

2.2 多头注意力(MSA)

单头自注意力虽然能建模全局关系,但它只有一个“视角”。

你想想看,一句话里可能有语法关系、语义关系、指代关系……一个头怎么可能全抓住?

所以就有了多头注意力(Multi-Head Self-Attention, MSA)。

做法很简单:

  • 把Q、K、V拆成h个头(我常用8个头)
  • 每个头独立做自注意力
  • 最后把所有头的输出拼接起来,再经过一个线性层

公式:

MultiHead(Q, K, V) = Concat(head_1, ..., head_h) × W_O
其中 head_i = Attention(Q × W_Q_i, K × W_K_i, V × W_V_i)

每个头关注不同的特征子空间。比如一个头关注主语-谓语关系,另一个头关注形容词-名词关系。

避坑指南:我曾经在项目中把头的数量设成32,以为越多越好。结果模型不仅没涨点,反而显存爆了。后来发现8-16个头就够用,头数太多反而会让每个头学到的信息碎片化。

2.3 位置编码

自注意力有个“致命”问题:它不关心位置顺序。

比如“我打你”和“你打我”,在自注意力看来是一样的——因为词与词之间的交互是对称的。

这显然不合理,对吧?

所以我们需要给模型注入位置信息。这就是位置编码(Positional Encoding)的作用。

Transformer原文用的是正弦余弦编码:

PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

其中pos是位置索引,i是维度索引。这种编码的好处是:

  • 值域在[-1, 1]之间,稳定
  • 可以外推到更长的序列(虽然效果会变差)

在MobileViT中,用的是可学习的位置编码。说白了就是初始化一个可训练的矩阵,让模型自己学位置信息。

我的建议:如果你的任务输入尺寸固定,用可学习位置编码效果更好。如果输入尺寸变化大,用正弦余弦编码更稳妥。

2.4 Transformer Encoder结构

好了,把上面三个部件拼起来,就是Transformer Encoder的一个Block。

标准结构如下:

  1. 多头自注意力(MSA)
  2. 残差连接 + LayerNorm
  3. 前馈网络(FFN):两层线性层,中间加ReLU/GELU激活
  4. 残差连接 + LayerNorm

用伪代码表示:

def transformer_encoder_block(x):
    # 第一步:多头自注意力 + 残差
    attn_out = multi_head_self_attention(x)
    x = layer_norm(x + attn_out)
    
    # 第二步:前馈网络 + 残差
    ffn_out = feed_forward_network(x)
    x = layer_norm(x + ffn_out)
    
    return x

这里有两个设计细节:

  • Pre-LN vs Post-LN:我习惯用Pre-LN(先LayerNorm再进子层),训练更稳定,收敛更快。
  • FFN的隐藏层大小:一般是d_model的4倍。比如d_model=256,FFN隐藏层就是1024。

重要提醒:MobileViT不是直接堆叠标准Transformer Block,而是把Transformer嵌入到CNN的局部特征中。但Encoder的核心结构——MSA + FFN + 残差连接——是完全保留的。

2.5 知识体系总览

下面这张图把Transformer基础的核心逻辑串起来了:

Transformer基础核心知识体系 自注意力机制 Q、K、V生成 点积相似度计算 Softmax + 加权求和 多头注意力MSA h个头并行计算 拼接 + 线性变换 多视角特征融合 位置编码 正弦余弦编码 可学习位置编码 注入位置信息 Transformer Encoder Block MSA → 残差连接 → LayerNorm FFN → 残差连接 → LayerNorm MobileViT的核心组件来源

从图中可以看到,自注意力是基础,多头注意力是增强,位置编码是补充,三者共同构成了Transformer Encoder。而MobileViT正是基于这个结构,做了视觉领域的适配。

个人习惯:我在搭建模型时,会先画一张这样的结构图,把每个模块的输入输出尺寸标清楚。这样写代码的时候思路特别清晰,不容易出错。

好了,Transformer基础就回顾到这里。这些内容虽然基础,但非常重要——MobileViT的所有改进都是在这个框架上做的。理解透了,后面搭建模型就会轻松很多。

可可精品资料库