diff --git a/tests/test_rf100vl_train.py b/tests/test_rf100vl_train.py index 3d87aef..267f31b 100644 --- a/tests/test_rf100vl_train.py +++ b/tests/test_rf100vl_train.py @@ -85,6 +85,11 @@ def test_generate_data_yaml_uses_all_splits_and_sorted_category_ids(tmp_path): # (ecdetseg/configs/ecdet/ecdet.yml), and LibreYOLO's ECConfig defaults to # amp with the inherited float16 dtype. fp32 would match neither. "ec": "fp16", + # rf-detr trains under autocast with the dtype hardcoded to torch.bfloat16 + # (rfdetr/engine.py::get_autocast_args at tag 1.2.0, the release current + # when Roboflow published their RF100-VL numbers) and ModelConfig.amp + # defaults to True. fp32 would be a deviation, not the conservative choice. + "rfdetr": "bfloat16", } @@ -146,11 +151,35 @@ def test_dense_rfdetr_selects_fallback_and_micro_dataset_keeps_one_batch(): } plan = rf100vl_train.select_batch_plan(recipe, spec, facts) assert plan["run_variant"] == "fallback" - assert plan["physical_batch"] == 2 + assert plan["physical_batch"] == 4 assert plan["effective_batch"] == 16 assert plan["expected_batches_per_epoch_minimum"] == 1 +def test_rfdetr_recipe_matches_roboflows_own_rf100vl_settings(): + """RF-DETR's authors state their RF100-VL numbers came from the rf-detr + defaults with one override: batch 16 and grad accum 1, not the library's + default batch 4 / accum 4. Autocast in rf-detr 1.2.0 is hardcoded to + bfloat16, so fp32 is a deviation too. Both are easy to reintroduce by + copying another family's recipe, and neither is visible in a result table, + so pin them here. + """ + recipe = rf100vl_train.load_recipe( + rf100vl_train.recipe_path_for_family("rfdetr"), + family="rfdetr", + ) + protocol = recipe["protocol"] + assert protocol["physical_batch"] == 16, "Roboflow ran batch 16, grad accum 1" + assert protocol["precision"] == "bfloat16", "rf-detr autocasts to bfloat16" + # A post-resize image cache pins one resolution per image, which silently + # defeats the multi_scale sampling this recipe asks for. + assert not protocol.get("cache", False) + assert recipe["train"]["multi_scale"] is True + # Sizes n/s/m inherit the batch; only l steps down, and only for VRAM. + for size in ("n", "s", "m"): + assert "physical_batch" not in recipe["sizes"][size] + + def test_rfdetr_l_oom_fallback_reduces_batch(): spec = get_spec("rfdetr-l") recipe = rf100vl_train.load_recipe( diff --git a/va_bench/recipes/rf100vl/rfdetr.json b/va_bench/recipes/rf100vl/rfdetr.json index 8054568..1a707b2 100644 --- a/va_bench/recipes/rf100vl/rfdetr.json +++ b/va_bench/recipes/rf100vl/rfdetr.json @@ -6,23 +6,36 @@ "commit": "3e3c9dffc9e4bd41a363f1dd6ca1c736e145a5d8", "paths": ["libreyolo/models/rfdetr/config.py"], "license": "MIT", - "normalization": "LibreYOLO RF-DETR fine-tune defaults under the RF100-VL fixed skeleton" + "normalization": "Roboflow's own RF100-VL settings for RF-DETR under the RF100-VL fixed skeleton", + "upstream_reference": { + "repository": "https://github.com/roboflow/rf-detr", + "ref": "1.2.0", + "paths": ["rfdetr/config.py", "rfdetr/engine.py"], + "license": "Apache-2.0", + "protocol_statement": "https://github.com/roboflow/rf-detr/issues/266#issuecomment-3110260396", + "quote": "The RF-DETR numbers for the current release are using the default arguments but with batch size 16 and grad accum 1", + "known_deviations": [ + "Roboflow initialised from private Objects365 checkpoints that were never released; we start from the public COCO rf-detr-*.pth. Maintainer estimates the gap at ~0.1 mAP.", + "Roboflow's published table predates their own fix for an incorrectly initialised detection head, so their numbers are not reproducible by any current code.", + "Size l runs physical_batch 2 because 704 px at batch 16 does not fit 16 GB; every other size runs Roboflow's batch 16 / grad accum 1.", + "Resize uses cv2 INTER_LINEAR without antialias." + ] + } }, "protocol": { "epochs": 100, "effective_batch": 16, - "physical_batch": 4, + "physical_batch": 16, "selection_metric": "valid_mAP50_95", "eval_interval": 1, "patience": 0, "ema": true, "seed": 0, - "precision": "fp32", - "cache": "disk" + "precision": "bfloat16" }, "dense_oom_fallback": { "max_annotations_per_image_threshold": 200, - "physical_batch": 2 + "physical_batch": 4 }, "sizes": { "n": {"imgsz": 384}, @@ -55,9 +68,8 @@ "multi_scale": true, "expanded_scales": true, "crop_resize_prob": 0.5, - "backbone_lr_mult": 0.1, "clip_max_norm": 0.1, "ema_decay": 0.993, - "workers": 0 + "workers": 2 } }