BEV(三) BEVFormer:Query 参数如何与 Feature 的 8 个 Head 交互

BEVFormer:Query 参数如何与 Feature 的 8 个 Head 交互

本文把下面这条计算链完整展开:

text 复制代码
Query 产生 Offset 和 Weight
            ↓
控制从多相机、多尺度 Feature 的哪里读取、读取多少
            ↓
8 个 Head 分别采样并加权
            ↓
拼接、相机融合、输出投影
            ↓
Dropout 后与原 Query 残差相加
            ↓
LayerNorm 得到新 Query

核心结论是:

Query 不直接与图像 Feature 做点积。Query 产生读取地址和混合权重;Feature 提供被读取的数据;融合结果经过投影后作为增量写回原 Query。


1. 先区分所有变量

以下变量都针对某个 BEV 格子 p:

变量 来源 Base 中的形状 作用
q p q_p qp BEV Query [256] 当前格子保存的状态
q ~ p \widetilde q_p q p Query 加位置编码 [256] 预测采样控制参数
Δ p , h , l , k \Delta_{p,h,l,k} Δp,h,l,k Query 路径 [2] 第 h,l,k 个采样偏移
α p , h , l , k \alpha_{p,h,l,k} αp,h,l,k Query 路径 标量 该采样结果的混合权重
F ~ c , l ( x , y ) \widetilde F_{c,l}(x,y) F c,l(x,y) 图像 Backbone/FPN [256] 相机 c、层级 l 的空间特征
V c , l ( h ) ( x , y ) V_{c,l}^{(h)}(x,y) Vc,l(h)(x,y) Feature 的 Value 投影 [32] Head h 的图像数据
f p , c , h , l , k f_{p,c,h,l,k} fp,c,h,l,k 双线性采样 [32] 从指定位置实际读取的特征
g p , c ( h ) g_{p,c}^{(h)} gp,c(h) Head 内加权求和 [32] 一个 Head 的融合结果
g p , c g_{p,c} gp,c 8 Head 拼接 [256] 一台相机对该 Query 的结果
g ‾ p \overline g_p gp 有效相机平均 [256] 六相机融合结果
o p o_p op 输出投影 [256] 准备写入 Query 的更新量

索引含义:

text 复制代码
c:相机,0~5
h:Head,0~7
l:FPN Level,0~3
k:每个 Head、每个 Level 的采样点,0~7

Base 还有四个柱高参考点。八个采样点不是"每个高度八个点",而是:

text 复制代码
4 个高度 × 每个高度 2 个偏移位置 = 8 个采样点

2. 八个 Head 到底是什么

八个 Head 不是八台相机、八个 Query、八个目标,也不是八个单独保存的向量。

8 是模型结构中人为设定的超参数:

python 复制代码
embed_dims = 256
num_heads = 8
head_dim = 256 // 8  # 32

因此同一个 Query 的一次 Attention 被组织为八条并行计算分支:

text 复制代码
Head 0:32 维 Value + 自己的 Offset + 自己的 Weight
Head 1:32 维 Value + 自己的 Offset + 自己的 Weight
...
Head 7:32 维 Value + 自己的 Offset + 自己的 Weight

Head 本身只是一个计算分支编号。每个 Head 具体关注什么位置、尺度和特征,由训练决定。


3. Feature 路径:图像数据怎样进入 Head

六相机图像先经过共享的 Backbone 和 FPN。某个相机、某个 FPN 层、某个空间位置的特征为:

F c , l F P N ( x , y ) ∈ R 256 . F^{\mathrm{FPN}}_{c,l}(x,y)\in\mathbb R^{256}. Fc,lFPN(x,y)∈R256.

加入相机编码与层级编码:

$$

\widetilde F_{c,l}(x,y)

F^{\mathrm{FPN}}_{c,l}(x,y)

+e_c^{\mathrm{cam}}

+e_l^{\mathrm{level}}.

然后经过 Value Projection: ##

V_{c,l}(x,y)

W_v\widetilde F_{c,l}(x,y)+b_v,

其中: W v ∈ R 256 × 256 . W_v\\in\\mathbb R\^{256\\times256}. Wv∈R256×256. 再将结果 reshape 为: V c , l ( x , y ) ∈ R 8 × 32 . V_{c,l}(x,y)\\in\\mathbb R\^{8\\times32}. Vc,l(x,y)∈R8×32. 即: ##

V_{c,l}

\operatorname{Concat}

\left(

V_{c,l}^{(0)},

V_{c,l}^{(1)},

\ldots,

V_{c,l}^{(7)}

\right),

其中: V c , l ( h ) ( x , y ) ∈ R 32 . V_{c,l}\^{(h)}(x,y)\\in\\mathbb R\^{32}. Vc,l(h)(x,y)∈R32. #### 不是机械切分原始 FPN 通道 由于切分前存在可学习的 W v W_v Wv,可以把它按输出行理解为八块: W v = \[ W v ( 0 ) W v ( 1 ) ⋮ W v ( 7 ) \] , W v ( h ) ∈ R 32 × 256 . W_v= \\begin{bmatrix} W_v\^{(0)}\\\\ W_v\^{(1)}\\\\ \\vdots\\\\ W_v\^{(7)} \\end{bmatrix}, \\qquad W_v\^{(h)}\\in\\mathbb R\^{32\\times256}. Wv= Wv(0)Wv(1)⋮Wv(7) ,Wv(h)∈R32×256. 所以: ##

V_{c,l}^{(h)}

W_v^{(h)}\widetilde F_{c,l}+b_v^{(h)}.

每个 Head 的 32 维 Value 都可以综合原始全部 256 个 Feature 通道。 *** ** * ** *** ### 4. Query 路径:产生每个 Head 的控制参数 原 Query 为: q p ∈ R 256 . q_p\\in\\mathbb R\^{256}. qp∈R256. 位置编码为 e p e_p ep。用于预测采样参数的向量是: q \~ p = q p + e p . \\widetilde q_p=q_p+e_p. q p=qp+ep. #### 4.1 产生 Offset ##

\Delta_p

W_\Delta\widetilde q_p+b_\Delta.

Base 中: W Δ ∈ R 512 × 256 , W_\\Delta\\in\\mathbb R\^{512\\times256}, WΔ∈R512×256, 因为: 512 = 8 h e a d × 4 l e v e l × 8 p o i n t × 2 x y . 512 =8_{\\mathrm{head}} \\times4_{\\mathrm{level}} \\times8_{\\mathrm{point}} \\times2_{xy}. 512=8head×4level×8point×2xy. reshape 后: ```text sampling_offsets [8 Head, 4 Level, 8 Point, 2 xy] ``` 第 `h` 个 Head 因而拥有自己的一组: Δ p , h , l , k = ( Δ x , Δ y ) . \\Delta_{p,h,l,k}=(\\Delta x,\\Delta y). Δp,h,l,k=(Δx,Δy). #### 4.2 产生 Weight ##

a_p

W_\alpha\widetilde q_p+b_\alpha.

Base 中 a p ∈ R 256 a_p\\in\\mathbb R\^{256} ap∈R256,因为: 256 = 8 h e a d × 4 l e v e l × 8 p o i n t . 256 =8_{\\mathrm{head}} \\times4_{\\mathrm{level}} \\times8_{\\mathrm{point}}. 256=8head×4level×8point. 输出 reshape 为: ```text attention_logits [8 Head, 4 Level, 8 Point] ``` 每个 Head 独立对自己的 `4 × 8 = 32` 个 Logit 做 Softmax: ##

\alpha_{p,h,l,k}

\frac{\exp(a_{p,h,l,k})}

{\displaystyle

\sum_{l'=0}{3}\sum_{k'=0}{7}

\exp(a_{p,h,l',k'})}.

因此: ∑ l = 0 3 ∑ k = 0 7 α p , h , l , k = 1. \\sum_{l=0}\^{3}\\sum_{k=0}\^{7} \\alpha_{p,h,l,k}=1. l=0∑3k=0∑7αp,h,l,k=1. Offset 决定"在哪里读",Weight 决定"读到的数据占多少"。 #### 4.3 Offset 一开始从哪里来 Offset 不是每次随机生成的。官方实现将 `sampling_offsets.weight` 初始化为零,将 bias 初始化为八个 Head 各自方向不同的放射状位置。训练开始后,`weight` 和 `bias` 都通过反向传播学习。 `attention_weights` 的 weight 和 bias 初始为零,因此初始 Softmax 权重都是 1 / 32 1/32 1/32;训练后才会针对不同 Query 形成不同的采样权重。 因此训练后的 Offset 是 Query 的确定性函数: Δ p = W Δ q \~ p + b Δ . \\Delta_p=W_\\Delta\\widetilde q_p+b_\\Delta. Δp=WΔq p+bΔ. 它的数值以当前 FPN 特征图的网格为尺度。比如 Δ x = 2 \\Delta x=2 Δx=2 表示在该 Level 上横向移动约两个 Feature cell;它不是移动两个 BEV 格子。Offset 可以是小数,所以采样点也可以落在四个 Feature cell 之间。 *** ** * ** *** ### 5. Query 和 Feature 第一次交互:Offset 控制读取地址 BEV 三维参考点投影到相机后得到几何基准位置: ρ p , c , m ∈ \[ 0 , 1 \] 2 . \\rho_{p,c,m}\\in\[0,1\]\^2. ρp,c,m∈\[0,1\]2. Base 使用四个高度锚点、每个高度两个 Offset。源码把八个 Offset 临时 reshape 为: ```text [8 Point, 2 xy] → [2 Offset-group, 4 Height, 2 xy] ``` 用扁平索引 k k k 表示时: k = 4 j + m , j ∈ { 0 , 1 } , m ∈ { 0 , 1 , 2 , 3 } . k=4j+m, \\qquad j\\in\\{0,1\\}, \\qquad m\\in\\{0,1,2,3\\}. k=4j+m,j∈{0,1},m∈{0,1,2,3}. 所以: m ( k ) = k   m o d   4. m(k)=k\\bmod4. m(k)=kmod4. 这就是下式中的 m ( k ) m(k) m(k):它指出第 k k k 个 Offset 应加到四个高度参考点中的哪一个。 Query 预测的 Offset 按当前 FPN 层宽高归一化。最终采样位置是: ##

s_{p,c,h,l,k}

\rho_{p,c,m(k)}

\left(

\frac{\Delta x_{p,h,l,k}}{W_l},

\frac{\Delta y_{p,h,l,k}}{H_l}

\right).

这里没有执行 Query 与 Feature 的向量乘法。 Query 产生的 Offset 改变了 Feature 的读取地址: ```text 几何基准位置 ρ + Query 预测偏移 Δ ↓ 最终 Feature 读取位置 s ``` *** ** * ** *** ### 6. 根据地址,从 Feature 中读取 32 维向量 采样位置 s = ( s x , s y ) s=(s_x,s_y) s=(sx,sy) 是 `[0,1]` 归一化坐标。对第 `l` 层特征图,连续特征坐标为: x = W l s x − 1 2 , y = H l s y − 1 2 . x=W_ls_x-\\frac{1}{2}, \\qquad y=H_ls_y-\\frac{1}{2}. x=Wlsx−21,y=Hlsy−21. 通过四邻域双线性插值,Head `h` 读取: ##

f_{p,c,h,l,k}

\mathcal B

\left(

V_{c,l}^{(h)},

s_{p,c,h,l,k}

\right).

结果为: f p , c , h , l , k ∈ R 32 . f_{p,c,h,l,k}\\in\\mathbb R\^{32}. fp,c,h,l,k∈R32. 所以: ```text Query 提供 s:在哪里读取 Feature 提供 f:读取到什么内容 ``` *** ** * ** *** ### 7. Query 和 Feature 第二次交互:Weight 控制贡献比例 Query 产生的 α p , h , l , k \\alpha_{p,h,l,k} αp,h,l,k 是一个标量;采样得到的 f p , c , h , l , k f_{p,c,h,l,k} fp,c,h,l,k 是一个 32 维向量。 二者交互为标量乘向量: α p , h , l , k f p , c , h , l , k . \\alpha_{p,h,l,k}f_{p,c,h,l,k}. αp,h,l,kfp,c,h,l,k. 例如: α = 0.7 , f = \[ 10 , 20 , 30 \] , \\alpha=0.7, \\qquad f=\[10,20,30\], α=0.7,f=\[10,20,30\], 则: ##

\alpha f

0.710,20,30

7,14,21.

Weight 不改变采样坐标,只改变这个采样特征对最终结果的贡献。 *** ** * ** *** ### 8. 一个 Head 的完整计算 每个 Head 在四个 FPN Level、每层八个位置采样。每层的八个位置来自"四个高度 × 每个高度两个 Offset",因此共有: 4 × 8 = 32 4\\times8=32 4×8=32 个采样结果。 第 `h` 个 Head 的融合结果为: ##

g_{p,c}^{(h)}

\sum_{l=0}^{3}

\sum_{k=0}^{7}

\alpha_{p,h,l,k}

f_{p,c,h,l,k}.

把 f f f 展开: ##

g_{p,c}^{(h)}

\sum_{l=0}^{3}

\sum_{k=0}^{7}

\alpha_{p,h,l,k}

\mathcal B

\left(

V_{c,l}^{(h)},

s_{p,c,h,l,k}

\right).

输出仍然是: g p , c ( h ) ∈ R 32 . g_{p,c}\^{(h)}\\in\\mathbb R\^{32}. gp,c(h)∈R32. *** ** * ** *** ### 9. 八个 Head 合并为一台相机的结果 八个 Head 分别输出: ```text Head 0 → g⁽⁰⁾[32] Head 1 → g⁽¹⁾[32] ... Head 7 → g⁽⁷⁾[32] ``` 将它们按通道拼接: ##

g_{p,c}

\operatorname{Concat}

\left(

g_{p,c}^{(0)},

g_{p,c}^{(1)},

\ldots,

g_{p,c}^{(7)}

\right).

所以: g p , c ∈ R 256 . g_{p,c}\\in\\mathbb R\^{256}. gp,c∈R256. 八个 Head 的目标不是产生八个检测结果,而是共同生成一个更丰富的 256 维 Query 更新候选。 *** ** * ** *** ### 10. 六台相机如何合并 设能看到格子 `p` 的有效相机集合为 V p \\mathcal V_p Vp: N p = max ⁡ ( 1 , ∣ V p ∣ ) . N_p=\\max\\left(1,\\left\|\\mathcal V_p\\right\|\\right). Np=max(1,∣Vp∣). 官方实现对有效相机结果做算术平均: ##

\overline g_p

\frac{1}{N_p}

\sum_{c\in\mathcal V_p}g_{p,c}.

这里没有额外的跨相机 Softmax。 在官方实现中,同一个 Query 被复制到它可见的相机分支,并且所有相机共享 W Δ W_\\Delta WΔ 和 W α W_\\alpha Wα。因此,同一个 Query 在不同相机分支产生的 Offset 和 Weight **数值相同** 。相机差异来自投影基准位置 ρ p , c , m \\rho_{p,c,m} ρp,c,m 和图像 Value V c , l V_{c,l} Vc,l,所以最终采样位置与读取内容不同。 *** ** * ** *** ### 11. 输出投影:把 Feature 结果变成 Query 更新量 六相机融合结果为: g ‾ p ∈ R 256 . \\overline g_p\\in\\mathbb R\^{256}. gp∈R256. 它来自图像 Value 空间,还需要经过输出投影: o p = W o g ‾ p + b o , o_p=W_o\\overline g_p+b_o, op=Wogp+bo, 其中: W o ∈ R 256 × 256 . W_o\\in\\mathbb R\^{256\\times256}. Wo∈R256×256. o p o_p op 可以理解为: > 本次相机观测准备写入该 BEV Query 的 256 维更新量。 `output_proj` 还会重新混合八个 Head 的通道,使结果适合与 Query 相加。 *** ** * ** *** ### 12. Dropout 不是残差 训练阶段对更新量应用 Dropout: d p = Dropout ⁡ ( o p ) . d_p=\\operatorname{Dropout}(o_p). dp=Dropout(op). 如果 Dropout 概率为 r r r,训练时每个通道近似为: ##

d_{p,i}

\frac{m_i}{1-r}o_{p,i},

\qquad

m_i\sim\operatorname{Bernoulli}(1-r).

Dropout 的作用是随机抑制更新分支中的部分通道,减少过拟合。 推理时: Dropout ⁡ ( o p ) = o p . \\operatorname{Dropout}(o_p)=o_p. Dropout(op)=op. 所以 Dropout 不是残差,也不会产生一条新的残差来源。 *** ** * ** *** ### 13. 原 Query 才是残差分支 残差相加为: ##

\widehat q_p

\underbrace{q_p}{\text{原 Query,残差分支}}
+
\underbrace{\operatorname{Dropout}(o_p)}
{\text{图像更新分支}}.

随后执行: ##

q_p^{\mathrm{new}}

\operatorname{LayerNorm}(\widehat q_p).

这里要区分代码边界:`SpatialCrossAttention.forward()` 只计算 q \^ p = q p + Dropout ⁡ ( o p ) . \\widehat q_p=q_p+\\operatorname{Dropout}(o_p). q p=qp+Dropout(op). 紧随其后的 LayerNorm 由外层 `BEVFormerLayer` 按 `cross_attn → norm` 的顺序执行。 残差的含义是: ```text 新状态 = 原状态 + 本次相机观测带来的修正量 ``` 它保留了 Query 中已有的: * BEV 位置先验; * Temporal Self-Attention 融合的历史信息; * 前面 Encoder 层已经写入的图像信息。 如果图像更新量接近零: o p ≈ 0 , o_p\\approx0, op≈0, 则: q \^ p ≈ q p . \\widehat q_p\\approx q_p. q p≈qp. 原状态不会被一次无效观测直接覆盖。 *** ** * ** *** ### 14. 一次交互的闭环 将所有阶段按依赖关系排列: ```text Query 路径 qₚ[256] ───────────────────────────────────────────────┐ │ │ ├─ + position ─→ Linear ─→ Offset Δ │ │ │ │ │ └─→ 最终采样位置 s │ │ │ └─ + position ─→ Linear ─→ Weight α │ │ Feature 路径 │ 图像 ─→ Backbone/FPN ─→ camera/level embed ─→ Wᵥ │ │ │ └→ 8 Head × 32 │ s ─────────────────────────→ 双线性采样 Feature │ │ │ └→ f[32] │ α ───────────────────────────────────────× │ │ │ 每个 Head 加权求和 │ │ │ 8 Head Concat → g[256] │ │ │ 有效相机平均 │ │ │ output_proj → o[256] │ │ Dropout(o) │ │ │ qₚ 残差路径 ────────────────────────────────────┴─→ 相加 ↓ LayerNorm ↓ qₚ_new[256] ``` 两种真正的 Query--Feature 交互是: 1. Query 的 Offset 控制从 Feature 的哪里读取; 2. Query 的 Weight 控制读到的 Feature 占多大比例。 第三步是状态融合:图像结果经过 W o W_o Wo 后与原 Query 相加。 *** ** * ** *** ### 15. 一个可以手算的双 Head 例子 为了方便手算,假设: ```text Query/输出维度:4 Head 数量:2 每个 Head:2 维 每个 Head 只采两个位置 推理阶段 Dropout 关闭 ``` 原 Query: q = \[ 1 , 2 , 1 , 0 \] . q=\[1,2,1,0\]. q=\[1,2,1,0\]. Query 为 Head 0 产生权重: α ( 0 ) = \[ 0.8 , 0.2 \] . \\alpha\^{(0)}=\[0.8,0.2\]. α(0)=\[0.8,0.2\]. 根据 Head 0 的 Offset,在 Feature 中读取: f 1 ( 0 ) = \[ 10 , 0 \] , f 2 ( 0 ) = \[ 0 , 8 \] . f_1\^{(0)}=\[10,0\], \\qquad f_2\^{(0)}=\[0,8\]. f1(0)=\[10,0\],f2(0)=\[0,8\]. 因此: g ( 0 ) = 0.8 \[ 10 , 0 \] + 0.2 \[ 0 , 8 \] = \[ 8 , 1.6 \] . g\^{(0)} =0.8\[10,0\]+0.2\[0,8\] =\[8,1.6\]. g(0)=0.8\[10,0\]+0.2\[0,8\]=\[8,1.6\]. Query 为 Head 1 产生权重: α ( 1 ) = \[ 0.25 , 0.75 \] . \\alpha\^{(1)}=\[0.25,0.75\]. α(1)=\[0.25,0.75\]. 根据 Head 1 的 Offset,在 Feature 中读取: f 1 ( 1 ) = \[ 2 , 6 \] , f 2 ( 1 ) = \[ 10 , 2 \] . f_1\^{(1)}=\[2,6\], \\qquad f_2\^{(1)}=\[10,2\]. f1(1)=\[2,6\],f2(1)=\[10,2\]. 因此: g ( 1 ) = 0.25 \[ 2 , 6 \] + 0.75 \[ 10 , 2 \] = \[ 8 , 3 \] . g\^{(1)} =0.25\[2,6\]+0.75\[10,2\] =\[8,3\]. g(1)=0.25\[2,6\]+0.75\[10,2\]=\[8,3\]. 拼接两个 Head: ##

g

\operatorname{Concat}

\left(g{(0)},g{(1)}\right)

=8,1.6,8,3.

为了演示,暂时假设 W o W_o Wo 是单位矩阵,则: o = g = \[ 8 , 1.6 , 8 , 3 \] . o=g=\[8,1.6,8,3\]. o=g=\[8,1.6,8,3\]. 推理阶段 Dropout 关闭,因此: q \^ = q + o . \\widehat q=q+o. q =q+o. q \^ = \[ 1 , 2 , 1 , 0 \] + \[ 8 , 1.6 , 8 , 3 \] = \[ 9 , 3.6 , 9 , 3 \] . \\widehat q =\[1,2,1,0\]+\[8,1.6,8,3\] =\[9,3.6,9,3\]. q =\[1,2,1,0\]+\[8,1.6,8,3\]=\[9,3.6,9,3\]. 最后再执行 LayerNorm 得到新 Query。 这个例子中的分工非常明确: ```text Query:产生两个 Head 的采样位置与权重 Feature:提供四个被采样的 2 维向量 Head:分别完成加权求和 残差:把结果写入原 Query,而不是覆盖原 Query ``` *** ** * ** *** ### 16. 与普通 QKᵀV Attention 的区别 普通 Attention 使用: ##

\alpha

\operatorname{Softmax}

\left(

\frac{QK^T}{\sqrt d}

\right).

权重来自 Query 与 Key 的相似度。 BEVFormer Spatial Cross-Attention 使用: Δ = W Δ q \~ + b Δ , \\Delta=W_\\Delta\\widetilde q+b_\\Delta, Δ=WΔq +bΔ, ##

\alpha

\operatorname{Softmax}

\left(W_\alpha\widetilde q+b_\alpha\right),

f = B ( V , ρ + Δ \^ ) , f=\\mathcal B(V,\\rho+\\widehat\\Delta), f=B(V,ρ+Δ ), g = ∑ α f . g=\\sum\\alpha f. g=∑αf. 当前层中不存在完整的 Query--Feature 点积矩阵。Feature 主要作为被动态寻址和加权的数据源。 *** ** * ** *** ### 17. 为什么后续层仍然能根据 Feature 调整控制参数 虽然当前层的 Weight 不是通过 q T f q\^Tf qTf 计算,但本层结束时: ##

q^{(r+1)}

\operatorname{LayerNorm}

\left(

q{®}+o{®}

\right).

$$

o ( r ) o^{(r)} o(r) 来自相机 Feature,所以新的 Query 已经包含图像信息。

下一层再用新 Query 预测:

Δ ( r + 1 ) , α ( r + 1 ) . \Delta^{(r+1)}, \qquad \alpha^{(r+1)}. Δ(r+1),α(r+1).

六层形成反馈迭代:

text 复制代码
第 1 层 Query → 控制采样 → 将图像 Feature 写入 Query
                      ↓
第 2 层 Query → 根据已有图像内容重新控制采样
                      ↓
第 3~6 层继续修正

18. 对应源码

18.1 定义 Value、Offset 和 Weight 投影

spatial_cross_attention.py

python 复制代码
self.sampling_offsets = nn.Linear(
    embed_dims,
    num_heads * num_levels * num_points * 2
)

self.attention_weights = nn.Linear(
    embed_dims,
    num_heads * num_levels * num_points
)

self.value_proj = nn.Linear(embed_dims, embed_dims)

18.2 Feature 投影并切为八个 Head

spatial_cross_attention.py

python 复制代码
value = self.value_proj(value)

value = value.view(
    bs,
    num_value,
    self.num_heads,
    -1
)

18.3 Query 产生每个 Head 的 Offset 和 Weight

spatial_cross_attention.py

python 复制代码
sampling_offsets = self.sampling_offsets(query).view(
    bs,
    num_query,
    self.num_heads,
    self.num_levels,
    self.num_points,
    2
)

attention_weights = self.attention_weights(query).view(
    bs,
    num_query,
    self.num_heads,
    self.num_levels * self.num_points
)

attention_weights = attention_weights.softmax(-1)

18.4 基准位置加 Offset

spatial_cross_attention.py

python 复制代码
sampling_offsets = sampling_offsets / offset_normalizer
sampling_locations = reference_points + sampling_offsets

18.5 三种输入在采样算子中相遇

spatial_cross_attention.py

python 复制代码
output = MultiScaleDeformableAttnFunction.apply(
    value,                 # 来自 Feature
    spatial_shapes,
    level_start_index,
    sampling_locations,    # 来自 Query 的 Offset + 几何参考点
    attention_weights,     # 来自 Query 的 Weight
    self.im2col_step
)

18.6 Dropout、残差和写回 Query

spatial_cross_attention.py

python 复制代码
slots = self.output_proj(slots)

return self.dropout(slots) + inp_residual

其中:

text 复制代码
slots        → 相机 Feature 产生的 Query 更新量
self.dropout → 只作用于更新分支
inp_residual → 原 Query,也就是残差分支

18.7 外层 LayerNorm

bevformer_base.py

python 复制代码
operation_order=(
    'self_attn', 'norm',
    'cross_attn', 'norm',
    'ffn', 'norm'
)

encoder.py

python 复制代码
elif layer == 'norm':
    query = self.norms[norm_index](query)

因此源码中的实际边界是:

text 复制代码
SpatialCrossAttention:q_hat = q + dropout(o)
BEVFormerLayer:        q_new = norm(q_hat)

19. 常见误解

误解一:八个 Head 是八个 Query

不是。一个 BEV 格子仍然只有一个 256 维 Query。八个 Head 是八条并行采样分支,最后重新拼成一个 256 维结果。

误解二:Head 是从 Feature 中检测出来的八个目标

不是。Head 数量 8 是结构参数。每个 Head 的数据由可学习的 Value Projection 从 Feature 中产生。

误解三:Query 与 Feature 先做逐元素乘法

不是。Query 先产生地址和权重;地址控制 Feature 采样,权重再乘采样后的 32 维 Feature。

误解四:Dropout 是残差

不是。原 Query 是残差;Dropout 只作用于相机更新量。

误解五:新 Feature 直接替换 Query

不是。图像融合结果经过 W o W_o Wo 后作为增量与原 Query 相加。


20. 一句话记忆

text 复制代码
Query 产生 Offset 决定"去哪里读";
Query 产生 Weight 决定"相信多少";
Feature 的 8 个 Head 提供"真正读到的数据";
output_proj 把数据变成 Query 更新量;
Dropout 只正则化更新量;
原 Query 通过残差路径保留并接收这次更新。
相关推荐
luoganttcc3 小时前
bev 系列 (一)BEVFormer-base:六相机特征怎样进入 BEV Query
数码相机·bev
快乐得小萝卜7 个月前
Lss-bev系列-2-部署插件IndexPut
ai·部署·bev·lss
Hi202402179 个月前
使用星图AI算力平台训练PETRV2-BEV模型
人工智能·自动驾驶·gpu·机器视觉·bev·算力平台
F_D_Z1 年前
SkyDiffusion:用 BEV 视角打开街景→航拍图像合成新范式
diffusion·sota·1024程序员节·bev·skydiffusion·视角变换·多图融合
JoannaJuanCV1 年前
BEV和OCC学习-5:数据预处理流程
深度学习·目标检测·3d·occ·bev
JoannaJuanCV1 年前
BEV和OCC学习-3:mmdet3d 坐标系
3d·occ·bev
机械心2 年前
深度学习视觉BEV 3D目标检测算法综述
深度学习·目标检测·3d·transformer·bev
maxruan2 年前
自动驾驶之BEV概述
人工智能·机器学习·自动驾驶·bev
__如果2 年前
论文阅读--Cross-view Transformers for real-time Map-view Semantic Segmentation
论文阅读·transformer·bev·鸟瞰