在实现 Transformer、Deformable DETR 或多头注意力(Multi-Head Attention / MSDA)机制时,我们经常需要把 Batch 维度 (NNN) 和 Head 维度 (GGG) 打平为一个合并的 Batch 轴(N×GN \times GN×G),以便利用底层高效的算子(如 F.grid_sample 或 GEMM 矩阵乘法)进行并行计算。
1. 核心底层逻辑:PyTorch 的 C-Contiguous 内存模型
在 Python / C++ 中,多维 Tensor 在底层物理内存(RAM / 显存)中都是一维连续数组 。PyTorch 遵循类似 C 语言的 Row-Major(行优先/C-Contiguous) 储存方式。
连续性的核心规律:
在最右侧(低维)变动最快,在最左侧(高维)变动最慢。
假设我们有一个 3D Tensor,Shape 为 [B=2, N_anchor=2, G=2](即 2 个 Batch,每个 Batch 有 2 个 Anchor,每个 Anchor 有 2 个 Head):
python
import torch
# 构造一个 3D Tensor, 值为 0 到 7 的递增序列
x = torch.arange(8).view(2, 2, 2)
# Shape: [2, 2, 2] -> [B, N_anchor, G]
它的物理内存存储顺序如下(按从左到右依次存放):
| 内存物理地址 | 逻辑索引 [b, n, g] |
元素值 | 对应含义 |
|---|---|---|---|
| 0x00 | [0, 0, 0] |
0 | Batch 0, Anchor 0, Head 0 |
| 0x01 | [0, 0, 1] |
1 | Batch 0, Anchor 0, Head 1 |
| 0x02 | [0, 1, 0] |
2 | Batch 0, Anchor 1, Head 0 |
| 0x03 | [0, 1, 1] |
3 | Batch 0, Anchor 1, Head 1 |
| 0x04 | [1, 0, 0] |
4 | Batch 1, Anchor 0, Head 0 |
| 0x05 | [1, 0, 1] |
5 | Batch 1, Anchor 0, Head 1 |
| 0x06 | [1, 1, 0] |
6 | Batch 1, Anchor 1, Head 0 |
| 0x07 | [1, 1, 1] |
7 | Batch 1, Anchor 1, Head 1 |
正确做法
B, N_anchor, G
不能.view(B * G, N_anchor)
需要.permute(0, 2, 1) # B, G, N_anchor
然后 .contiguous()
.view(B * G, N_anchor)