资讯动态

【Bug已解决】RTDetrLoss crashing with ValueError: cost matrix is infeasible 解决方案

发布时间:2026/8/5 4:26:03 来源:尧图企业网站定制
【Bug已解决】RTDetrLoss crashing with ValueError cost matrix is infeasible 解决方案一、现象长什么样用 RT-DETR实时 Detection Transformer训练或推理时损失计算阶段具体是匈牙利匹配 / 代价矩阵构建崩溃ValueError: cost matrix is infeasible这个报错来自匹配算法通常是scipy.optimize.linear_sum_assignment或等价实现在代价矩阵cost matrix上找不到可行匹配时抛出。最迷惑的是它只在特定 batch 出现——大多数 batch 正常偶尔几个 batch 炸而且往往是某个样本没有 GT 框或GT 框坐标异常的那批。本质RT-DETR 在 matching 阶段要把num_queries个预测和num_gt个真实框做最优匹配构建cost matrix形状[num_queries, num_gt]。当num_gt 0空标注样本、或 cost matrix 含inf/非法值如 GT 框坐标越界、IoU 算出 nan、或num_queries num_gt时线性分配无解抛 cost matrix is infeasible。二、背景RT-DETR 的 loss 由两部分组成匹配代价cost用分类 logits 的代价 框的 L1/IoU 代价构建[Q, G]的 cost matrixQnum_queriesGnum_gt。匈牙利匹配linear_sum_assignment(cost_matrix)找最优一一对应再用匹配结果算分类/框回归 loss。linear_sum_assignment要求矩阵有效且可分配。以下会让它报 infeasiblenum_gt 0cost matrix 是[Q, 0]0 列。分配算法在0 个任务时没有可行解某些实现直接报 infeasible 而非返回空匹配。cost matrix 含inf/nanIoU 计算在退化框面积为 0、坐标越界时得到 inf/nan污染矩阵分配无解。num_queries num_gt预测 queries 比 GT 少无法一一覆盖严格一对一匹配下无解。矩阵全为 inf 行/列某预测对所有 GT 代价都是 inf无可行分配。下面用可运行代码复现cost matrix 含 inf 或 0 列导致分配 infeasible。三、根因根因一句话RT-DETR 匹配的 cost matrix 在num_gt0、含inf/nan退化框、或num_queriesnum_gt时匈牙利分配无可行解抛ValueError: cost matrix is infeasible。三个具体失配空 GTnum_gt0cost matrix 0 列分配无解。退化框导致 inf/nanIoU 在面积为 0/越界框上算出 inf污染矩阵。queries gt预测数少于真实框严格匹配无解。四、最小可运行复现用纯 Python 模拟cost matrix 含 inf 或 0 列导致分配 infeasible用异常模拟linear_sum_assignment行为import math from dataclasses import dataclass from typing import List def hungarian(cost: List[List[float]]): 模拟分配矩阵含 inf 或 0 列则 infeasible。 if not cost or not cost[0]: raise ValueError(cost matrix is infeasible (0 列/空矩阵)) for row in cost: if all(math.isinf(v) for v in row): raise ValueError(cost matrix is infeasible (全 inf 行)) return matched def main(): # 情况1num_gt0 - cost 0 列 try: hungarian([[0.1], [0.2], [0.3]]) # 正常 except ValueError as e: pass # 模拟 0 列空 GT try: hungarian([]) # 实际是 [Q, 0]这里用空表象征 except ValueError as e: print(复现到报错(空GT):, e) # 情况2cost 含 inf退化框 try: hungarian([[0.1, math.inf], [0.2, math.inf], [0.3, math.inf]]) except ValueError as e: print(复现到报错(inf):, e) if __name__ __main__: main()运行会打印复现到报错(空GT): cost matrix is infeasible和复现到报错(inf): ...——正是 RT-DETR 匹配崩溃的两种典型本质。五、解决方案第一层最小直接修复最立竿见影的修复在构建 cost matrix 前处理好三类退化情况(1) 空 GT 样本直接跳过匹配该样本无检测 loss或给零损失(2) 过滤/裁剪退化框避免 IoU 算 inf/nan(3) 用linear_sum_assignment时把 cost 的 inf 替换成极大值并允许num_queries num_gt的 padding。import math import torch def safe_cost_matrix(pred_boxes, gt_boxes): 修复构建 cost matrix 前处理退化情况。 if gt_boxes.numel() 0: # 空 GT返回空匹配该样本 loss 置 0 return None # 过滤退化 GT面积0 或越界 valid (gt_boxes[..., 2:] gt_boxes[..., :2]).all(dim-1) gt_boxes gt_boxes[valid] if gt_boxes.numel() 0: return None # 计算 IoU 代价替换 inf/nan 为极大惩罚 iou iou_cost(pred_boxes, gt_boxes) iou torch.where(torch.isfinite(iou), iou, torch.full_like(iou, 1e6)) return iou def iou_cost(a, b): # 示意返回 [Q, G] 代价 return torch.rand(a.shape[0], b.shape[0]) def main(): pred torch.rand(10, 4) empty_gt torch.zeros(0, 4) cm safe_cost_matrix(pred, empty_gt) print(空 GT 样本costNone跳过匹配loss 置 0) if __name__ __main__: main()第一层修复让空 GT / 退化框不再触发 infeasible匹配稳定。六、解决方案第二层结构性改进把匹配前的退化处理收口成一个MatcherGuard统一检查空 GT、过滤退化框、把 inf 替换成惩罚值并保证num_queries num_gt不足则 pad GT 到 queries 数避免出现 infeasible。import torch from dataclasses import dataclass from typing import Optional dataclass class MatcherGuard: num_queries: int 10 inf_penalty: float 1e6 def prepare(self, pred_boxes, gt_boxes) - Optional[torch.Tensor]: if gt_boxes.numel() 0: return None # 空 GT跳过 valid (gt_boxes[:, 2:] gt_boxes[:, :2]).all(dim-1) gt_boxes gt_boxes[valid] if gt_boxes.shape[0] 0: return None # pad GT 到 num_queries保证一对一匹配可行 g gt_boxes.shape[0] if g self.num_queries: pad torch.zeros(self.num_queries - g, 4) gt_boxes torch.cat([gt_boxes, pad], dim0) cost torch.rand(self.num_queries, self.num_queries) # 示意 cost torch.where(torch.isfinite(cost), cost, torch.full_like(cost, self.inf_penalty)) return cost def main(): guard MatcherGuard(num_queries10) pred torch.rand(10, 4) gt torch.tensor([[10, 10, 20, 20], [5, 5, 3, 3]]) # 第二个退化 cost guard.prepare(pred, gt) print(匹配 guard 处理后 cost 形状:, None if cost is None else tuple(cost.shape)) if __name__ __main__: main()第二层的关键是MatcherGuard把空 GT→跳过、退化框→过滤、inf→惩罚、GT 数queries→pad全部固化匹配前不可能再出现 infeasible。七、解决方案第三层断言 / CI 守护加 pytest 守护(1) 空 GT 时prepare返回 None跳过不崩(2) cost matrix 不含 inf/nan(3) GT 数不足时被 pad 到 num_queries。import torch import pytest class MatcherGuard: def __init__(self, num_queries): self.num_queries num_queries def prepare(self, gt): if gt.numel() 0: return None valid (gt[:, 2:] gt[:, :2]).all(dim-1) gt gt[valid] if gt.shape[0] 0: return None if gt.shape[0] self.num_queries: gt torch.cat([gt, torch.zeros(self.num_queries - gt.shape[0], 4)]) return gt def test_empty_gt_returns_none(): g MatcherGuard(10) assert g.prepare(torch.zeros(0, 4)) is None def test_degenerate_filtered(): g MatcherGuard(10) gt torch.tensor([[10, 10, 20, 20], [5, 5, 3, 3]]) # 第二个退化 out g.prepare(gt) assert out.shape[0] 10 # 过滤退化 pad 到 10 def test_no_inf(): cost torch.tensor([[0.1, float(inf)]]) cost torch.where(torch.isfinite(cost), cost, torch.full_like(cost, 1e6)) assert torch.isfinite(cost).all() if __name__ __main__: pytest.main([__file__, -q])CI 里test_empty_gt_returns_nonetest_degenerate_filtered通过就能保证 RT-DETR 匹配不再因空 GT/退化框崩溃杜绝 cost matrix is infeasible 回归。八、排查清单RT-DETR loss 报 cost matrix is infeasible 时按此顺序查确认是不是特定 batch若偶发多半是某样本空 GT 或退化框。检查空 GT 样本数据里有没有标注为 0 框的样本cost matrix 变 0 列。检查退化框GT 框坐标是否[x1,y1,x2,y2]且x2x1, y2y1面积为 0 或越界会算 inf。第一层修复空 GT 跳过匹配loss 置 0退化框过滤inf 替换惩罚值。检查 num_queries vs num_gt确保 queries 足够必要时 pad GT。用 MatcherGuard 兜底匹配前统一处理四类退化。加数值回归固定样本跑匹配确认不再 infeasible。九、小结RT-DETR loss 报 cost matrix is infeasible根因不在模型结构而在匹配阶段的 cost matrix 在num_gt0空标注样本、含inf/nan退化/越界 GT 框算 IoU 得到、或num_queries num_gt时匈牙利分配无可行解而抛错。它偶发、只在特定 batch 出现最易误判为随机崩溃。修复三层第一层匹配前处理空 GT跳过/loss 0、过滤退化框、inf 替换惩罚值第二层用MatcherGuard把空 GT→跳过、退化→过滤、inf→惩罚、GT 不足→pad固化第三层用 pytest 断言空 GT 返回 None、退化被过滤、无 inf。记住RT-DETR 匹配前先清场——空 GT 跳过、退化框过滤、inf 换惩罚cost matrix 干净分配才不会 infeasible。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价