问题:

在这里插入图片描述
重叠超过50%时,重叠过大导致NaN(Not a Number)的主要原因在于数值不稳定性和梯度爆炸。

注意力权重极端化:当重叠区域过大时,多个视角对同一位置的特征贡献相似,导致注意力权重计算出现极端值(接近0或1)。在softmax计算中,如果输入值过大,会出现数值溢出,产生NaN。

梯度爆炸:重叠区域的特征在反向传播时,梯度会从多个路径回传,如果这些梯度方向一致,会产生梯度累加效应。当重叠比例过高时,梯度可能呈指数级增长,最终超出浮点数表示范围。

特征冲突放大:不同视角对同一物体的特征提取存在差异,重叠过大时这些差异会被反复计算,在特征融合过程中产生数值不稳定。

核心理解:注意力机制的本质是"竞争"

在Transformer的注意力机制中,每个位置需要从所有输入特征中"选择"最相关的信息。这个过程通过计算注意力权重来实现,权重越大表示该特征越重要。

具体过程:

查询-键值匹配:每个BEV查询位置会计算与所有图像特征(键)的相似度

softmax归一化:相似度经过softmax函数,得到0-1之间的权重,且所有权重之和为1

为什么重叠过大会导致极端值:

当多个视角对同一位置的特征贡献高度相似时,它们在相似度计算中会得到几乎相同的分数。在softmax计算中:

如果多个特征分数都很高且接近,softmax会将这些分数"拉平",使它们都接近0

如果某个特征分数显著高于其他,softmax会将其权重推向1,其他推向0

数值不稳定的根源:

当重叠过大时,多个视角的特征相似度差异极小(比如0.0001的差异),在softmax的指数运算中,这种微小差异会被指数级放大。即使进行了缩放,然而导致梯度累加后出问题。

例如:

场景设定

假设我们有一个BEV3D模型,正在处理自动驾驶场景中的一辆汽车。

参数

  • 3个相机视图:前视、左视、右视
  • 每个视图都捕捉到了这辆车的不同部分
  • 模型想要预测BEV空间中车辆中心点处的特征

BEV网格中的一个位置 P(x=10, y=5, z=1.5) 对应车辆中心


Query-Key点积计算

1. BEV Query

模型在BEV位置P处有一个Query向量:

Q_P = [0.8, 0.2, -0.3, 0.5, ...]  # 维度d_k=256

2. 多视图中的对应位置

视图1(前视相机)

  • 投影到像素坐标:(320, 240)
  • 对应特征向量 K_front = [0.75, 0.25, -0.28, 0.52, …]
  • 相似度:Q_P · K_front = 0.8×0.75 + 0.2×0.25 + (-0.3)×(-0.28) + ...

视图2(左视相机)

  • 像素坐标:(150, 200)
  • 对应特征向量 K_left = [0.78, 0.22, -0.25, 0.51, …]
  • 相似度:Q_P · K_left = 0.8×0.78 + 0.2×0.22 + ...

视图3(右视相机)

  • 像素坐标:(490, 210)
  • 对应特征向量 K_right = [0.76, 0.24, -0.26, 0.53, …]
  • 相似度:Q_P · K_right = 0.8×0.76 + 0.2×0.24 + ...

实际计算(简化版)

假设每个向量只有4个维度以便计算:

# 维度d_k=4(实际是256,这里简化)
Q_P = [0.8, 0.2, -0.3, 0.5]  # 范数sqrt(0.8²+0.2²+0.3²+0.5²)≈1.0

K_front = [0.75, 0.25, -0.28, 0.52]  # 与Q_P高度相似
K_left  = [0.78, 0.22, -0.25, 0.51]  # 也很相似
K_right = [0.76, 0.24, -0.26, 0.53]  # 也很相似
K_back  = [0.1, -0.3, 0.6, -0.2]     # 不相关的背景位置

# 计算点积
dot_front = 0.8*0.75 + 0.2*0.25 + (-0.3)*(-0.28) + 0.5*0.52
         = 0.6 + 0.05 + 0.084 + 0.26
         = 0.994

dot_left  = 0.8*0.78 + 0.2*0.22 + (-0.3)*(-0.25) + 0.5*0.51
         = 0.624 + 0.044 + 0.075 + 0.255
         = 0.998

dot_right = 0.8*0.76 + 0.2*0.24 + (-0.3)*(-0.26) + 0.5*0.53
         = 0.608 + 0.048 + 0.078 + 0.265
         = 0.999

dot_back  = 0.8*0.1 + 0.2*(-0.3) + (-0.3)*0.6 + 0.5*(-0.2)
         = 0.08 - 0.06 - 0.18 - 0.10
         = -0.26

关键问题:当d_k很大时

实际中d_k=256,点积会累积更多项,值会非常大

假设每个维度对点积贡献约0.9(因为向量高度相似):

  • 每个维度的平均贡献:0.9
  • 256维的总点积:0.9 × 256 = 230.4

但更现实的是,向量通常经过归一化,点积反映的是余弦相似度:

  • 如果两个256维单位向量完全一致,点积最大为256
  • 实际上相似向量点积可能达到200-250

缩放前

dot_front ≈ 220.0
dot_left  ≈ 222.0  
dot_right ≈ 221.0
dot_back  ≈ -30.0

softmax计算(无缩放时)

# 点积值很大
z = [220.0, 222.0, 221.0, -30.0]

# 直接计算指数会溢出!
# math.exp(220) ≈ 2.5e+95
# math.exp(222) ≈ 1.3e+96
# 已经远超float32范围(3.4e+38)

# 数值溢出,得到inf
exp_z = [inf, inf, inf, 0.0]  # 最后一个exp(-30)很小但不是0

解决方案:缩放因子

缩放后(除以 d k = 256 = 16 \sqrt{d_k} = \sqrt{256} = 16 dk =256 =16):

z_scaled = [220/16, 222/16, 221/16, -30/16]
         = [13.75, 13.875, 13.8125, -1.875]

# 使用稳定softmax:先减去最大值13.875
z_stable = [13.75-13.875, 13.875-13.875, 13.8125-13.875, -1.875-13.875]
         = [-0.125, 0.0, -0.0625, -15.75]

# 计算softmax
exp_z = [exp(-0.125), exp(0), exp(-0.0625), exp(-15.75)][0.882, 1.0, 0.939, 1.4e-7]

sum_exp ≈ 0.882 + 1.0 + 0.939 + 0.000000142.821

softmax_output ≈ [0.882/2.821, 1.0/2.821, 0.939/2.821, 1.4e-7/2.821][0.313, 0.355, 0.333, ~0.0]

结果分析

视图 点积 缩放后 注意力权重
前视 220.0 13.75 31.3%
左视 222.0 13.875 35.5%
右视 221.0 13.8125 33.3%
背景 -30.0 -1.875 ~0%

关键观察

  1. 高度相似的视图:三个视图对同一BEV位置都有高相似度(~220),因为车辆在多视图中都可见
  2. 缩放的重要性:从220缩小到13.8,避免了数值溢出
  3. 注意力分布合理:三个相关视图获得几乎均等的注意力(31%-36%),背景被抑制
  4. 梯度保持良好:softmax输出在0.3-0.4之间,梯度不会消失

这个例子清晰地展示了:

  1. 多视图重叠 → 多个Key与同一Query高度相似 → 点积值非常大
  2. 无缩放时 → 点积值可达200+ → e 200 e^{200} e200 数值溢出
  3. 缩放后 → 点积缩小到合理范围 → softmax稳定计算
  4. 注意力机制 → 合理分配多视图权重 → 有效融合多视图信息

这就是为什么Transformer中的 1 d k \frac{1}{\sqrt{d_k}} dk 1缩放因子如此关键——它不仅控制方差,更是避免数值溢出的必要保障。

出现NaN的根本原因

重叠多了出现NaN的根本原因不是softmax本身,而是训练过程中的梯度爆炸或数值不稳定导致的连锁反应

虽然前面提到缩放因子和稳定softmax解决了理论上的数值溢出,但在实际训练中,当多视图重叠严重时,以下问题会累积导致NaN:

1. 梯度爆炸的连锁反应

问题根源:当多个视图对同一BEV位置都有极高相似度时,虽然softmax输出是合理的概率分布,但梯度计算可能不稳定

梯度公式 ∂ L ∂ z i = a i − y i \frac{\partial L}{\partial z_i} = a_i - y_i ziL=aiyi

当多个视图的 z i z_i zi都非常大且接近时:

  • 虽然 a i a_i ai在0.3-0.4之间,但 z i z_i zi本身可能很大(如200+)
  • 如果梯度回传到 z i z_i zi的上一层,可能引发梯度爆炸
  • 梯度爆炸导致网络参数更新过大,下一轮前向传播时 z i z_i zi变得更大

恶性循环

大z_i → 梯度爆炸 → 参数更新过大 → 下一轮z_i更大 → 梯度更爆炸 → NaN

2. 多视图几何投影的不稳定性

相机参数误差:当多个相机视角重叠严重时,微小的相机标定误差会被放大:

  • 同一3D点在不同视图中的投影位置略有偏差
  • 模型需要学习"对齐"这些略有偏差的特征
  • 如果相机参数不准,这个对齐过程可能不稳定

特征歧义:多个视图的相似特征可能对应不同的3D位置,模型难以区分,导致注意力权重震荡。

3. 实际训练中的解决方案

梯度裁剪

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

限制梯度范数,防止梯度爆炸。

梯度范数(Gradient Norm)是衡量神经网络训练过程中梯度大小的指标,它表示所有参数梯度向量的长度(模长)。

数学定义

对于一个包含 n n n 个参数的神经网络,梯度是一个 n n n 维向量:
g = [ ∂ L ∂ w 1 , ∂ L ∂ w 2 , … , ∂ L ∂ w n ] \mathbf{g} = \left[\frac{\partial L}{\partial w_1}, \frac{\partial L}{\partial w_2}, \dots, \frac{\partial L}{\partial w_n}\right] g=[w1L,w2L,,wnL]

梯度范数(L2范数)定义为:
∥ g ∥ 2 = ( ∂ L ∂ w 1 ) 2 + ( ∂ L ∂ w 2 ) 2 + ⋯ + ( ∂ L ∂ w n ) 2 \|\mathbf{g}\|_2 = \sqrt{\left(\frac{\partial L}{\partial w_1}\right)^2 + \left(\frac{\partial L}{\partial w_2}\right)^2 + \dots + \left(\frac{\partial L}{\partial w_n}\right)^2} g2=(w1L)2+(w2L)2++(wnL)2

学习率调整

  • 使用较小的学习率(如1e-4)
  • 配合学习率衰减策略

特征归一化

  • 在Query和Key计算前进行LayerNorm
  • 确保特征分布稳定

损失函数设计

  • 添加正则化项(L2正则化)
  • 使用smooth L1损失代替L2损失

4. 工程实践中的调试

当出现NaN时,通常需要:

  1. 检查梯度范数,如果过大则降低学习率或增加梯度裁剪
  2. 检查相机标定参数是否准确
  3. 检查数据集中是否存在极端重叠场景
  4. 添加数值检查,在训练过程中监控中间变量的数值范围

总结:虽然理论上缩放因子解决了softmax的数值问题,但实际训练中多视图重叠带来的梯度不稳定、几何投影误差累积等问题,仍然可能导致NaN。这需要通过梯度裁剪、学习率调整、特征归一化等工程手段综合解决。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐