
1. 为什么旋转目标检测必须理解“计算图”和“反向传播”1.1 你完全可以用自动微分但“工业级”三个字不允许先说说我为什么在这个系列里专门花一整卷来讲计算图和反向传播。很多人会觉得现在 PyTorch、TensorFlow 都自带 autogradloss 一 backprop梯度就自动算好了为什么还要理解这些底层机制答案恰恰藏在“工业级”这三个字里。工业级意味着你的模型不能只在论文数据集上跑通演示它要面对真实场景下的数据分布变化、训练不稳定、梯度爆炸、loss 不收敛、推理和训练行为不一致等一堆问题。这些问题绝大多数都发生在哪都发生在梯度对流这一层。你对计算图的控制力越弱遇到这些问题时就越像瞎子摸象。举个例子我在做旋转目标检测时经常要自定义算子。比如一个旋转 RoI Align 的前向计算框架里没有现成的你得自己写。你写前向计算的时候如果只返回输出张量而不写反向autograd 会提示你“no grad for this operation”——但是更麻烦的情况是它给你一个错误的梯度然后你的模型在训练集上表现还行一换环境就崩了。这种时候只有你真正理解了计算图中每个节点的梯度传播规则才能快速定位是采样坐标的梯度传错了还是角度分支的链式法则写错了。这就是为什么我要把计算图和反向传播放在“炼器”的核心位置。另外旋转目标检测还有一个普通水平框检测没有的痛点角度的周期性和 IoU 计算的不可导性。旋转框之间的 IoUIntersection over Union计算依赖多边形裁剪和多边形面积计算这个过程是高度分段、不可微的。你不能像普通水平框那样直接用 IoU 作为回归损失必须设计一个可导的替代损失函数。而这个替代损失函数的梯度形态直接决定了你的网络能不能收敛。1.2 旋转框参数化从五参数到梯度设计的第一道坎旋转目标检测中回归目标的表示方式一般有几种主流方案最常见的是五参数表示法即 (中心点 x, 中心点 y, 宽度 w, 高度 h, 角度 θ)。还有基于有序四顶点表示法即四个角点坐标以及基于矢量化表示法的比如长短边向量加一个角度。不同的参数化方式直接影响损失函数的设计和梯度的形态。以五参数为例角度 θ 通常被定义在一个范围内比如 [-90°, 0°) 或者 [0°, 180°)这个范围的选取是第一个坑。如果你直接把五参数送进网络回归角度分支最后输出一个实数值损失函数用 smooth L1 去约束那么你会在角度接近周期边界时遇到严重的“梯度断裂”问题。比如一个真实框的角度是 -89°预测框是 89°这两个框其实几乎一样差了 178°但旋转框有 180° 对称性所以等价但数值上差了 178smooth L1 的梯度会认为你需要往一个实际上误差很小的方向再调整 178 个单位。这就导致训练震荡、收敛极慢。严格的工业级做法是采用“长边定义法”或者“归一化角度回归”把角度范围压缩到 [-90°, 90°) 或者 [-45°, 45°)并且配合周期性的损失函数设计。这些听起来是损失函数的事但其实都是在为梯度的平稳性服务。所以这一卷要做的是把计算图、梯度、反向传播这几件事按照旋转目标检测的实际场景走一遍。我会尽量结合自己在实际训练中碰到的案例来写而不是纯追理论推导。2. 计算图构建前向传播背后的“拓扑图”2.1 计算图的本质把你的网络变成一张可微的“流水线”计算图本质上是一个有向无环图图中的节点代表张量或算子边代表数据流动方向。像 PyTorch 的 autograd 机制其实就是一个动态计算图每次前向传播时都会记录下这个图然后反向传播时沿着图的边逆序计算梯度。在动手实现旋转检测网络时我会重点考虑计算图的“可微性”边界。整个网络的前向传播可以拆分成几个阶段骨干网络特征提取、区域提议网络RPN或者查询生成、RoI 特征提取这里就是旋转 RoI Align、分类和回归头。每个阶段都有对应的梯度回传路径。先说骨干网络部分。这部分跟水平检测没什么区别卷积、ReLU、池化、批归一化都是现成的算子反向传播也都有框架原生支持。你需要关注的只是梯度的流动方向是否按照你的设计被阻断。在工业级训练里经常要冻结骨干网络的某些层或者给不同部分设置不同的学习率本质上是控制梯度流向这就得清楚某一层 stop gradient 之后下游的哪些参数还会更新。RPN 阶段同理它是基于锚框的或者基于查询的。如果是锚框方案锚框的正负样本分配是硬编码的逻辑不参与梯度传播。这里要注意样本分配是计算图中一个天然的“梯度阻断点”。你分配正样本给哪些锚框这些锚框对应的特征才进入后续的损失计算而分配操作本身没有可导的输入输出关系。然后到旋转 RoI Align这里有意思了。水平 RoI Align 用双线性插值从特征图中采样双线性插值是可导的梯度会回传到特征图坐标。旋转 RoI Align 进一步需要在旋转的矩形区域内做采样网格的旋转变换也就是每个采样点的坐标要对 RoI 的中心点和角度做仿射变换。这个仿射变换是可导的但问题是如果你的 RoI 框本身是通过回归头预估的角度和中心点坐标得到的那么这个梯度要不要传到回归头我不建议在工业级实现里让 RoI Align 的采样坐标梯度回传到回归头的前面层。因为 RoI 的坐标在每次 forward 时由 RPN 输出确定把特征图采样坐标的梯度传到 RPN 分支会导致训练不稳定而收益微乎其微。一般做法是直接用 detach 把 RoI 坐标截断只让特征图接受梯度。2.2 旋转框的不可导之处IoU 与多边形裁剪的尴尬这是整个旋转检测里计算图设计最烧脑的部分。水平框检测里IoU 损失之所以能用是因为两框重叠面积可以通过坐标的加减乘除算出来整个过程是可导的虽然有分段边界但整体上可以用 smooth L1 等缓解。旋转框呢两个旋转矩形的交集是一个凸多边形需要求每条边的交点、判断点的包含关系、计算多边形面积。你要把这一切写成可导的算子理论上可行但工程实现极其复杂而且数值稳定性差、梯度波动剧烈几乎不适合做端到端训练。所以工业界实际的做法要么用平滑替代损失比如将角度参数化后使用更精细的基于坐标变换的损失要么在设计计算图的时候把 IoU 计算放到“非可导模块”中。实际训练流程是先预测五参数然后通过一个自定义的“不可导 IoU 评估器”计算当前预测框与真实框之间的 IoU这个 IoU 只作为指标在验证集中跟踪而不参与损失函数训练损失则用另外设计的可导函数来监督。这种“指标不可导、损失可导”的双轨设计就是旋转目标检测网络计算图的核心架构思路。很多初学者不理解为什么会存在“训练用 smooth L1 却用 IoU 来评指标”这种错位不是因为评测指标不重要而是因为 IoU 这个指标在计算图里无法可靠地反向传播。我的建议是不要再纠结直接用 IoU 损失除非你在做针对性的研究和有强大的数值稳定性处理能力。先把前面这几层踏踏实实做好、跑通再考虑上近似可导 IoU。3. 梯度的推导逻辑与实现细节3.1 从五参数到损失函数每条回传路径的拆分这一节我们亲手把梯度用链式法则“拆”一遍看看旋转目标检测中到底有哪些梯度路径。先定义一个简化结构。假设网络预测输出为五维向量 (tx, ty, tw, th, tθ)经过解码后得到中心点坐标 (bx, by)、宽度 bw、高度 bh、角度 bθ。损失函数采用 smooth L1 版本比如有些实现会对除了角度之外的四参数用 smooth L1对角度单独用周期化处理后的 smooth L1。对位置和大小参数而言梯度计算跟普通目标检测完全一样。smooth L1 的定义是当误差绝对值小于等于 1 时损失为 0.5*x^2否则为 |x| - 0.5。它的一阶导数在边界处是连续的边界处导数为 1 和 -1这比 L1 在零点处不能求导要稳定也比 L2 对离群点不敏感。这就是为什么回归损失首选 smooth L1。真正要注意的是角度回归分支。假设我们把角度归一化到了 [-90°, 90°) 这个区间网络输出 tθ解码后得到 bθ目标的角度是 θ*。计算差值 dθ bθ - θ*。这时候如果 dθ 在 [-90°, 90°) 区间内问题不大直接用 smooth L1。但如果 dθ 超出了这个区间比如 dθ -120°说明预测值与目标值本来可以只差 60°却因为区间归一化被“拉开”了。更经典的坑是角度的边界跳变。假设你的角度取 [0°, 180°) 范围那么真实框是 179°预测框是 1°两者视觉上几乎重合因为 179° 和 1° 在 180° 对称性下等价但数值梯度会指向一个几乎相反的方向。这个问题在计算图里会产生巨大的、不稳定的梯度轻则训练震荡重则梯度爆炸。一个工程上稳健的处理方式是角度差归一化到 (-90°, 90°] 或 (-180°/4, 180°/4] 范围只出不进的实现方式。具体做法是构造一个角度差的归一化函数把所有角度差映射到对称区间内让误差在计算图中始终处于小范围。这类函数在反向传播时相当于给角度分支加了一个“梯度整形”的效果能有效绕开周期边界造成的梯度断裂。3.2 回传到 RoI Align双线性插值和仿射变换的梯度细节现在说说把损失梯度回传到特征图这中间经过的最有意思的一段旋转 RoI Align。我们用一个 RoI 框定义了输出特征图上的每个格子格子内的每个采样点都要通过仿射变换映射回原特征图坐标。仿射变换就是绕 RoI 中心旋转 θ 角再加上 RoI 的平移和缩放。整个过程可以用一组坐标变换公式表达。反向传播到这里时需要计算三组梯度一是输出特征图对输入特征图的梯度二是对 RoI 中心点、宽高、角度的梯度三是对变换过程中缩放系数的梯度。第二组梯度如果被允许继续往回归头传那么它就是旋转检测网络“自适应选择更好区域”的关键但就像我一直强调的实际工程中往往会截断 RoI 参数本身的梯度只保留特征图梯度。那双线性插值自己是怎么传播梯度的呢想象一个采样点的输出像素值是它四邻域输入像素值的加权和权重是浮点坐标的小数部分。反向传播时输出像素的梯度会按原权重分配回四个输入像素上。这本身不复杂难点在于浮点坐标本身也可能来自前一层的输出。如果你把采样坐标也当成可学参数那就要额外的雅可比计算复杂度会骤增而且梯度数值不稳定很难调。所以我强烈建议在工业级实现中对 RoI 的坐标和角度使用 detach 操作。这不是偷懒而是为了让计算图更清晰让优化问题更聚焦先让网络学好分类和回归头的角度预测再用这个角度去提特征而不是让特征提取反馈回来干扰角度回归。我早期在这个位置上踩过很深的坑一旦放开 RoI 坐标的梯度回传loss 大幅震荡而且很难调整恢复。3.3 梯度裁剪与梯度累积的工程意义聊完解析梯度再聊工程层面的梯度处理这两件事在某个层面上是密不可分的。为什么需要梯度裁剪因为旋转目标检测的损失函数经常面临“不稳定”的困扰。比如在训练的早期阶段角度分支的预测很离谱导致回归误差很大smooth L1 的梯度在没有平滑的情况下可能会超过正常范围。如果某个 batch 恰好包含特别困难的样本梯度范数会非常大参数更新一步就破坏了已有特征。常见的梯度裁剪方法有按值裁剪clip by value直接把梯度限制在 [-v, v] 之间简单直接但会破坏梯度的方向信息按范数裁剪clip by norm在梯度范数超过某个阈值时做等比缩放保留梯度方向的同时控制步长这个在工业级训练中用得最多。我一般从 max_norm 初始设为 10 或 20具体值要看损失函数的量级来调。如果用了 smooth L1且角度回归做了归一化处理梯度范数一般不会大得太离谱。梯度累积是一种解决显存不足的手段本质上是在多个 mini-batch 上累积梯度后再做一次参数更新等效于把 batch size 增大了 N 倍。在旋转检测这类高分辨率遥感影像任务里一个图可能有好几千个 RoI显存非常吃紧梯度累积几乎是必不可少的训练技能。实现梯度累积的时候有一个容易踩的坑如果你在用 batch norm 或者 syncBN梯度累积会造成 statistics 的更新频率和梯度更新频率不一致导致模型层统计量失真。我常用的一种做法是只在纯卷积、无 BN 的模块比如最后的回归头上做梯度累积或者干脆在累积梯度时不更新 BN 层的 running stats等真正参数更新时再更新。具体到你自己的网络上得根据训练稳定情况来选没有绝对通用的配置。4. 训练优化中反向传播相关的关键策略4.1 参数的梯度检查数值梯度与解析梯度对比如果你手动实现过旋转检测的自定义算子或自定义损失那么梯度检查gradient check是必须做的一步。梯度检查的核心思想很朴素对某个参数施加一个很小的扰动 ε用中心差分法求出该参数的数值梯度然后与反向传播算出的解析梯度做对比。一旦两者差异超过某个阈值一般用相对误差衡量就说明你在前向或反向的某段代码里写错了。这个检查在旋转检测里应当重点照顾两个地方角度分支和仿射变换。这两个部分的数学表达式通常比较长很容易在实现时多加一个负号或者反了旋转矩阵的方向。我建议在网络训练的初始阶段专门写一个 debug 脚本随机生成一批仿真的旋转框和真实框然后只跑这两个模块的梯度检查。测试通过之后再接入完整的训练流程。别偷懒因为梯度错误出现得越晚排查成本越高。数值梯度的计算还有一个大坑ε 的选取。ε 太小会导致浮点舍入误差占主导ε 太大则会导致数值逼近误差增大一般取 1e-5 到 1e-7 之间最优。而且中心差分法虽然计算量翻倍但精度远高于前向差分所以一定用中心差分。4.2 批量大小、学习率与优化器选择的梯度视角在旋转目标检测领域优化器和学习率的设置比普通分类任务更有讲究。因为回归分支尤其是角度分支和分类分支对学习率的敏感程度不一样。如果全部参数共用同一个学习率你会发现角度分支容易在“数值收敛但精度不够”的状态下停滞。我自己常用的一个办法是将角度分支的损失权重调低比如设为位置回归分支权重的 0.5~0.7并且给角度分支设置更低的学习率大约为主干网络的 0.1~0.5 倍。因为角度回归的梯度在单位变化下对损失的影响比位置参数更敏感稍大一点的学习率就容易反复震荡。优化器方面我不会一上来就推复杂的优化器SGD Momentum 在检测上依然很能打。Adam 等自适应优化器效果也不错但动量参数与梯度历史信息在旋转检测这类长尾分布上的表现不一定好。如果发现训练到后期部分参数梯度消失调到平滑区间后可以试着切换到 AdamW 搭配余弦退火有些时候会有奇效。批量大小对梯度的“质量”影响也不容忽视。检测任务每个样本的 RoI 数量不一尤其遥感图片中目标密集程度差异巨大。批次太小会导致每个 batch 的梯度估算方差大最直观的表现就是训练 loss 抖动厉害。调高 batch size 或者用梯度累积来模拟大 batch是在不确定场景中快速稳定训练的手段。4.3 梯度消失与局部最小值理论与实践的分离每次一聊到神经网络训练总有人搬出“反向传播如何解决梯度下降局部最小值”这类教科书问题。放到旋转检测的实际工程里我很少真的为“局部最小值”焦虑原因在于网络参数量巨大、损失面高维复杂真正的局部最小值反而不太常见。更常见的两个问题是梯度消失vanishing gradient和损失平面中的陡峭区域sharp minima。梯度消失主要出现在网络比较深的骨干结构上。虽然 ResNet 这类结构缓解了很多但当你把旋转检测头和候选框模块接在很深的特征提取层之后尤其是多尺度融合特征网络中深层的梯度回传到浅层时可能会被大量衰减。表现就是骨干网络前几层的参数几乎不更新模型只是“上半身”在拼命学。对策也不复杂先检查反向传播的梯度范数分布如果发现前几层梯度远小于最后一层就要考虑引入残差连接、或者训练时给前几层更高的学习率倍数再或者使用梯度裁剪与梯度归一化机制来平衡整体更新幅度。优先推荐残差连接这是经过验证的通用做法。5. 常见问题与排查技巧实录5.1 训练 Loss 为 NaN 或直接爆掉这个现象在旋转检测里出现得特别频繁。从我自己的经验来看最常见的元凶有两个角度分支的数值溢出、以及除法中除零。角度分支如果采用非归一化回归输出可能非常大在参与三角函数计算、旋转矩阵计算时就会产生 NaN。排查时先定位是哪一个分支导致的你可以把角度分支的输出固定为常数单独跑一轮看 loss 是否正常。我经常用这种二分法来定位可疑模块。除零问题通常出现在坐标变换中——当预测宽度或高度为 0 或者极其接近 0 时归一化步骤会除以宽高直接得到 NaN。在把预测的 w、h 解码到实际像素值时一定要加一个数值下限比如 max(w, 1e-4) 这样的扰动保护。如果是梯度爆掉而不是 NaN那就上梯度裁剪。我一般同时开两个保险按 norm 裁剪max_norm10~20以及整体 loss 权重缩放把总 loss 控制在个位数量级。有时候还要检查 label 分配是否合理比如某些角度定义下的极端长宽比框是否导致误差过大。5.2 角度回归不收敛或周期性震荡训练过程中最诡异的现象是loss 下降得很漂亮但到验证集上一看旋转框的角度预测和真实框差了 20 度甚至更多。这多半是角度周期性的锅。前面提到过的角度差归一化如果没做好模型会在训练初期学到“大致对齐”然后在周期边界上反复横跳。它学到的是一个携带跳变误差的近似恒等映射无法继续精修局部误差。排查时把角度分支单独拿出来分析打印预测角度和目标角度的分布直方图看看是不是集中在某些边界值附近。如果集中在 [-90°, 90°] 的端点说明周期归一化处理有问题。这时候回去检查角度差归一化函数务必保证任何输入都能映射到以目标角度为中心的对称区间。还有一个小技巧在 angle 分支的输出后加一个 tanh 缩放把网络输出直接限制在期望角度范围内这能让训练收敛明显加快。5.3 梯度方差过大样本选择与锚框分配还有一种情况是训练 loss 不爆炸但梯度方向很“乱”导致训练速度极慢。我排查时详细看了每个 batch 的梯度发现是由于样本选择的随机性导致的旋转目标检测里每个 training image 内目标的面积、方向差异极大如果正样本分配时混入了太多难以学习的“困难样本”梯度就会被少数离群样本主导。SGD 对这类情况尤其敏感。应对的思路是调整 RoI 采样策略和正负样本比例尽量让每个 batch 内的样本难度分布比较均衡。此外可以给每个样本的 loss 做权重归一化比如按样本面积或按梯度范数做采样让角度刁钻的小目标不至于因为梯度过大而带偏整体训练方向。这个问题不像前两个有明确的“一劳永逸”解法更依赖你在具体数据集上的实验。我的习惯是随时记录梯度范数和 loss 分布的 stats一旦发现异常波动可以快速比对不同的预处理与采样策略。5.4 一套实用的训练监控与排障流程最后整理一下我每次训练旋转检测模型时一定会做的监控清单供参考每一百步打印一次 loss 及各分支分类、回归、角度的平均值。如果某个分支在 1000 步内纹丝不动优先怀疑该分支没有梯度流经比如检测到 label 分配后没有正样本传入。用 hook 或者包装 module 记录每层参数梯度的均值与范数。重点看骨干网络前几层和角度分支梯度是否在合理范围内。定期在验证集上计算旋转框 R-IoU 与角度误差的分布而不只看 mAP。角度误差中位数是一个比均值稳定得多的指标。保留一个“固定随机种子 固定输入”的调试模式便于在同一批数据上对比修改前后效果。如果使用了梯度累积注意检查 BN 层统计量的变化必要时单独为 BN 层去掉累积。这套流程看似繁琐但能在问题刚冒头时立刻锁定方向而不是一切正常地跑了两天之后才发现第 100 个 epoch 的模型完全不可用。用一句话总结我的经验在工业级训练中对梯度的感知能力决定了你对模型的掌控能力。我自己最开始手写旋转检测网络时也一度迷信一些精巧的损失函数设计。踩过几轮之后才发现计算图结构的清晰度和梯度流的稳定性才是真正决定网络能否训练成功的基石。如果你也正在手搓自己的旋转目标检测网络这一卷把计算图、梯度与反向传播梳理清楚后面接参数调优和部署时你会明显感觉心中有底。