# LAB 20 · 尺度跨度与两对象匹配 按问题继续 步幅占格、两对象排列、分类/位置/IoU联合匹配代价 对应课堂(5 页) 可先阅读已有结果,再决定是否运行。重算时请核对各节记录的数据、参数、环境与日期。 **学习路径:** [一张图怎样变成一组框](/course/study/image-detection.html) · [像素、边界与对象身份](/course/study/pixels-instances.html)。两个入口引用同一份现有材料,不需要重复阅读或运行。 本册只回答两个选读问题,不是 DETR 训练实验。看 [P38 的多尺度表示](/course/pages/l3-r2-p38.html) 时,可查目标在 stride 4/8/16/32 网格中的跨度;看 [P39 的对象身份](/course/pages/l3-r2-p39.html) 时,可查无序对象如何对应。 先看 [尺度跨度](/course/blocks/multiscale-architecture-comparison.html) 或 [由框与概率计算匹配](/course/blocks/set-matching-real-inputs.html),再修改对应条件。匹配用类别概率、框 L1 与 IoU 构造代价,再穷举两个对象的排列;跨度和匹配解决不同问题。 产出一个判断:交换预测槽顺序后,最小总成本是否不变?为什么小于一格不等于信息必定消失?读完回到 [CLS 08](https://deepnote.com/project/38755e4e-23f6-4f5f-8c68-8a2e4114b1c1/notebook/2d81e2a1936043349eacb71c53c0defa);完整图像到框学习另见 [LAB 10](https://deepnote.com/project/38755e4e-23f6-4f5f-8c68-8a2e4114b1c1/notebook/a14237bfa9dd413d959ab4e458e11f42)。 ## 同一组对象换了排列,损失不该凭空变大 检测输出是对象集合。两个预测slot与两个真值框没有天然同序关系。本例从类别概率、归一化框L1距离与IoU构造成本 Cᵢⱼ=−log pᵢ(cⱼ)+2||bᵢ−gⱼ||₁+(1−IoU),穷举2!种排列得到最小匹配。系数是教学设定;DETR常用的分类/L1/GIoU组合须按实际实现区分。 前置尺度算例记录16px目标在stride4/8/16/32上对应的连续跨度4/2/1/0.5格,不把0.5格误说为必定完全消失。FPN负责多尺度表示;二分匹配负责输出对应,两者不能相互替代。 本册没有图像编码器或参数训练;真实图像到框的完整链在 [LAB 10](https://deepnote.com/workspace/ise3309jingao-5ba66b0a-4283-4f5a-ae06-3f68d3201e99/project/38755e4e-23f6-4f5f-8c68-8a2e4114b1c1/notebook/a14237bfa9dd413d959ab4e458e11f42?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content=a14237bfa9dd413d959ab4e458e11f42&utm_term=get_notebook)。 [此段独立阅读](/course/notebook-blocks/6ab1753a596940e5aceba3b7dd7537c5.html)展开完整实现代码 ``` # Writable ephemeral workspace for detached readonly cloud runs. import os, tempfile os.chdir(tempfile.mkdtemp(prefix='lec03_lab23_')) import numpy as np from itertools import permutations boxes=np.array([[.61,.12,.91,.43],[.08,.10,.32,.40]]) truth=np.array([[.10,.10,.35,.42],[.60,.10,.90,.45]]) prob=np.array([[.15,.85],[.9,.1]]); labels=np.array([0,1]) def iou(a,b): wh=np.maximum(0,np.minimum(a[2:],b[2:])-np.maximum(a[:2],b[:2])); inter=wh.prod() return inter/(np.prod(a[2:]-a[:2])+np.prod(b[2:]-b[:2])-inter) C=np.array([[-np.log(prob[i,labels[j]])+2*abs(boxes[i]-truth[j]).sum()+1-iou(boxes[i],truth[j]) for j in range(2)] for i in range(2)]) costs={str(p):sum(C[i,j] for i,j in enumerate(p)) for p in permutations(range(2))} print('cost matrix',C.round(4).tolist(),'assignment costs',costs,'selected',min(costs,key=costs.get)) assert min(costs,key=costs.get)=='(1, 0)' for stride in [4,8,16,32]: print('stride',stride,'target span',16/stride,'cells') print('with 3 predictions/2 targets, unmatched slot needs no-object classification, not an invented third target') ``` [此段独立阅读](/course/notebook-blocks/0f45eba379c5437685bb52cf7ae38a2d.html) ## 已完成的基准运行 以下是本轮本地独立 Python 进程的实际标准输出,云端执行另行记录。 ``` cost matrix [[5.0971, 0.4509], [0.4779, 5.6026]] assignment costs {'(0, 1)': np.float64(10.699705077879926), '(1, 0)': np.float64(0.9288002118875242)} selected (1, 0) stride 4 target span 4.0 cells stride 8 target span 2.0 cells stride 16 target span 1.0 cells stride 32 target span 0.5 cells with 3 predictions/2 targets, unmatched slot needs no-object classification, not an invented third target ``` 本例左侧预测实际上来自slot1,因此交叉匹配便宜。只交换预测slot顺序,最小总成本应保持;固定按索引配对则会改变损失。若只有一个预测正确,匹配仍会选最低成本,但这并不意味着两物体均被成功检测。框损失、分类损失与评价是不同对象。[DETR官方matcher](https://github.com/facebookresearch/detr/blob/main/models/matcher.py)可对照这里的教学成本;LAB10展示从真实图像得到候选的前半条链。 [此段独立阅读](/course/notebook-blocks/3cdc52c2a4254fcebc30f166d5043b8f.html) #### 先心算一个更小的匹配例 成本矩阵为 `[[1.2,.3],[.2,1.0]]`。按相同索引对应,总代价 2.2;交换对应,总代价 .5,因此选交叉。上方当前代码进一步从类别概率、真实坐标和 IoU 得到成本,说明这些数并非只能手填。 想比较匹配与难例加权,读 [选读:难例权重和对象匹配不是一件事](/course/blocks/focal-versus-assignment.html);两者解决不同问题。 [此段独立阅读](/course/notebook-blocks/043de8a70dd54f4c88bdb062e0590d04.html)