Views
No views yet
CharlesCNorton/nms-verified
holds on its dense output. Under that invariant, greedy NMS is
provably equivalent to a sort + threshold filter on the score axis.
The deployment graph can drop NMS at zero AP cost.Core.v, Bridge.v, Probability.v, Pipeline.v). The keystone
collapse theorem in Core.v isTheorem nms_collapse_onepeak :
forall D,
NoDup D -> sorted_desc D -> one_peak D -> no_tie_clash D ->
filter_above (nms_sorted D) = filter_above D.Separated is in Bridge.v
(lipschitz_bridge_substantive, SepRespectingHead). Architectural
discharges of the bridge precondition appear in Pipeline.v:Theorem detr_equilibrium_yields_separated (* DETR / set-prediction *)
Theorem anchor_stride_yields_separated (* FCOS / RetinaNet / ATSS *)Pipeline.v:Theorem squared_hinge_sgd_pl_convergence
Theorem L_separated_sq_zero_iff_separated_general
Theorem real_training_to_collapseProbability.v
via Hoeffding's lemma (hoeffding_lemma_symmetric,
hoeffding_iid_finite_class_one_sided).R foundations.| model | std AP | NMS-free AP |
|---|---|---|
| pretrained FCOS_ResNet50_FPN_Weights.COCO_V1 | 0.391 | 0.174 |
| this checkpoint | 0.265 | 0.264 |
python experiments/train_with_spectral_norm.py \
--iters 120000 \
--batch-size 1 --image-size 640 --lr 1e-4 \
--peak-lambda 0.0 \
--iou-selectivity-lambda 200.0 \
--iou-selectivity-tau 0.5 --iou-selectivity-theta 0.3 \
--iou-selectivity-topk 2000 \
--iou-ratio-lambda 200.0 --iou-ratio-target 0.3 \
--center-sampling-radius 0.5 \
--no-spectral-norm \
--self-distill --teacher-theta 0.5 \
--mimic-lambda 1.0 \
--coco-image-dir /path/to/coco/train2017 --coco-image-limit 01import torch
2from safetensors.torch import load_file
3from torchvision.models.detection import (
4 fcos_resnet50_fpn, FCOS_ResNet50_FPN_Weights,
5)
6from torchvision.ops import boxes as box_ops
7
8model = fcos_resnet50_fpn(weights=FCOS_ResNet50_FPN_Weights.COCO_V1)
9model.load_state_dict(load_file("fcos_certified.safetensors"))
10model.eval().to("cuda")
11
12# Standard inference: torchvision's stock postprocess includes NMS.
13detections = model([image_tensor])
14
15# NMS-free inference: replace postprocess_detections with the same code
16# minus the batched_nms call. Output equals the standard path under the
17# one-peak invariant (Theorem nms_collapse_onepeak in the Rocq library).
18def nms_free_postproc(self, head_outputs, anchors, image_shapes):
19 cl, br, bc = head_outputs["cls_logits"], head_outputs["bbox_regression"], head_outputs["bbox_ctrness"]
20 out = []
21 for i, shape in enumerate(image_shapes):
22 boxes_i, scores_i, labels_i = [], [], []
23 for br_l, cl_l, bc_l, anc_l in zip(br, cl, bc, anchors[i]):
24 num_classes = cl_l.shape[-1]
25 scores = torch.sqrt(cl_l[i].sigmoid() * bc_l[i].sigmoid()).flatten()
26 keep = scores > self.score_thresh
27 scores = scores[keep]
28 topk = torch.where(keep)[0]
29 k = min(topk.numel(), self.topk_candidates)
30 scores, order = scores.topk(k)
31 topk = topk[order]
32 anc_idx = torch.div(topk, num_classes, rounding_mode="floor")
33 labels = topk % num_classes
34 boxes = self.box_coder.decode(br_l[i][anc_idx], anc_l[anc_idx])
35 boxes_i.append(box_ops.clip_boxes_to_image(boxes, shape))
36 scores_i.append(scores); labels_i.append(labels)
37 boxes_i = torch.cat(boxes_i); scores_i = torch.cat(scores_i); labels_i = torch.cat(labels_i)
38 order = scores_i.argsort(descending=True)[:self.detections_per_img]
39 out.append({"boxes": boxes_i[order], "scores": scores_i[order], "labels": labels_i[order]})
40 return out
41
42import types
43model.postprocess_detections = types.MethodType(nms_free_postproc, model)
44detections_nms_free = model([image_tensor])python experiments/coco_eval.py \
--ckpt fcos_certified.safetensors \
--theta 0.05 --top-k 100nms_cert.ml is the OCaml extraction of the decidable certifier
(Separated_check, Separated_dec, sep_certify_finite) from the
Rocq library. nms_cert_main.ml is a small CLI driver: detections in,
CERTIFIED <slack> or REJECTED out. Build:ocamlfind ocamlopt nms_cert.ml nms_cert_main.ml -o nms_cert./nms_cert TAU THETA SLACK < detections.txtscore x1 y1 x2 y2 with
integer scores and integer bounding-box pixels. CERTIFIED means
the corresponding Separated predicate holds on the input list at
that slack — by nms_collapse_onepeak, NMS is then equivalent to a
threshold filter on the same list.fcos_certified.safetensors — model weights (FCOS-ResNet50-FPN, 319 tensors).nms_cert.ml — extracted certifier (Separated_check, Separated_dec, sep_certify_finite).nms_cert_main.ml — CLI driver around the extracted certifier.README.md — this card.1@misc{nms_verified,
2 author = {Norton, Charles C.},
3 title = {nms-verified: a Rocq formalization of NMS collapse for dense detection},
4 howpublished = {\url{https://github.com/CharlesCNorton/nms-verified}},
5 note = {Model checkpoint at \url{https://huggingface.co/phanerozoic/nms-verified}},
6}