wikillm

Paper · 2022-12-14 · cs.CV

RTMDet: An Empirical Study of Designing Real-Time Object Detectors

RTMDet is a family of real-time one-stage detectors (tiny to extra-large). It uses CSPNeXt blocks with 5x5 depthwise convolutions, fewer but wider blocks, and a dynamic label assignment with IoU soft labels, trained with cached Mosaic/MixUp. Author-reported: 52.8 AP on COCO val2017 for RTMDet-x at 3.10 ms (FP16, NVIDIA 3090, TensorRT), with instance and rotated variants.

Object detectionLabel assignmentEfficient vision backbonesData augmentationobject-detectionreal-timelarge-kernellabel-assignmentinstance-segmentationrotated-object-detection

arXivPDFCodeMarkdown17 min read

AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

Object detectors find things in pictures and draw boxes around them. Self-driving cars, robots and drones need this to run fast, so the detector has to be both accurate and quick. This paper is a set of experiments that puts together a detector family, called RTMDet, aimed at that goal, and reports what helped.

The main changes are in three places. The building blocks inside the network use a larger filter (5 by 5 instead of 3 by 3) that sees more of the image at once. The network is made shallower, with fewer blocks and wider channels, so it runs faster on a GPU. Finally, during training, the rule that decides which predicted box should learn from which object is made softer: a box that overlaps the object well gets a stronger signal, instead of a plain yes or no. Training also reuses already-loaded images when it mixes them together, which saves time while loading data.

The same detector is then adapted to outline object shapes instead of drawing boxes, and to handle boxes that are tilted. The adaptations are small changes to the output part of the network. The authors report that the family has a good balance between accuracy and speed, and that it reaches new results on those two tasks. All numbers are the authors' own.

TL;DR

RTMDet is a family of one-stage object detectors built around CSPNeXt blocks with 5x5 depthwise convolutions, reduced block depth with wider channels, and a shared detection head §3.1 §3.2. Training uses a dynamic soft label assignment whose matching cost takes the IoU as a soft target, plus cached Mosaic and MixUp and a two-stage schedule §3.3. Author-reported: RTMDet-x reaches 52.8 AP on COCO val2017 at 3.10 ms per image (FP16, TensorRT, batch 1, NVIDIA 3090, NMS excluded) tab. 2 §4.2. Tiny to x models are provided, and the same network is extended to instance segmentation and rotated boxes §3.4.

Problem

Real-time detectors in the YOLO line were improved by better architectures and training recipes, but the authors argue that the design space of their components has not been studied systematically §1. Two specific costs are named. Large receptive fields help dense prediction, yet earlier ways of getting them (dilated convolutions, non-local blocks) are too expensive for real-time use §3.2. Re-parameterised 3x3 convolutions speed up inference but slow training, raise training memory and widen the quantisation error gap §3.2. In the training recipe, dynamic label assignment costs built from binary labels can prefer a box with a high class score and a poor location, and strong mixing augmentations produce samples that are "noisy" §3.3.

The paper assumes 2D object detection with standard COCO-style training (118K training images, 5K validation images) §4.1. Rotated detection is evaluated on DOTA, whose images are cropped into 1024x1024 patches §4.1.

Method

Paths in this section are relative to mmdet/models/ in the official code. Short names: assigner = task_modules/assigners/dynamic_soft_label_assigner.py, head = dense_heads/rtmdet_head.py, csp = layers/csp_layer.py, backbone = backbones/cspnext.py.

Macro architecture. RTMDet is a one-stage detector with a backbone, a neck that fuses multi-scale features with top-down and bottom-up paths, and a head that predicts a box and class at each location of each scale §3.1 fig. 2. The backbone and neck use CSP-style blocks. The head predicts classes and boxes for each scale; for instance segmentation, extra heads predict kernels and mask features fig. 2.

Basic building block. The authors replace the 3x3 convolution of a CSPDarkNet block with a 5x5 depthwise convolution, which they say enlarges the effective receptive field at low cost §3.2 fig. 3 (b). Ablation: 3x3 gives 50.0 AP at 2.10 ms, 5x5 gives 50.9 AP at 2.11 ms, and 7x7 gives 51.1 AP at 2.73 ms, with the 3x3 plus 5x5 combination chosen as the best speed-accuracy trade-off tab. 5 §4.3.

The block used in code has a 3x3 convolution, then a depthwise-separable convolution with a 5x5 kernel, with an identity shortcut when the shapes match [code: csp.py#L82-L139]. Each CSP layer can add a channel-attention module on its concatenated features [code: csp.py#L232-L233, csp.py#L244-L245].

Depth and width balance. The 5x5 depthwise plus point-wise pair adds layers, which slows parallel inference. The authors therefore use fewer blocks per stage and wider channels §3.2. Stage block counts are 3, 6, 6, 3 for the base (large) model [code: backbone.py#L56-L62, backbone.py#L135]. Reducing the second and third stages from 9 to 6 blocks cut latency by about 20% and cost 0.5 AP; adding channel attention recovered 0.4 AP, at 2.40 ms against 2.11 ms for the 3-6-6-3 model without it tab. 5 §4.3.

Backbone and neck balance. Instead of growing the backbone, the authors give the neck blocks a larger expansion ratio so that backbone and neck have similar capacity §3.2. In the ablation this gives similar accuracy at lower latency than the alternative split (63% backbone, 29% neck) tab. 5 §4.3.

Shared detection head. Heads share convolution weights across scales, but each scale has its own batch-normalisation layers. The authors say BN is cheaper than group normalisation at inference because it uses statistics computed during training §3.2. Ablation: a shared head with BN statistics gives 48.0 AP, a fully separate head 51.2 AP with 57.03M parameters, and the shared head with separate BN 51.3 AP with 52.32M parameters tab. 5 §4.3. In code, only the conv modules are shared across levels; each level keeps its own BN, and the classification and regression branches are separate [code: head.py#L629-L633].

Dynamic soft label assignment. Each training prior is matched to ground truth using a cost, and the number of positives per gt is chosen dynamically, following SimOTA §3.3. The cost is

C = \lambda_1 C_{cls} + \lambda_2 C_{reg} + \lambda_3 C_{center} eq. 1

with \lambda_1 = 1, \lambda_2 = 3, \lambda_3 = 1 §3.3 eq. 1.

The classification cost uses the IoU between prediction and gt as a soft label, inspired by GFL §3.3:

C_{cls} = CE(P, Y_{soft}) \times (Y_{soft} - P)^2 eq. 2

The regression cost uses the negative logarithm of IoU instead of GIoU, so that matches with low IoU are penalised more strongly §3.3:

C_{reg} = -\log(IoU) eq. 3

The centre cost is a soft prior, not a fixed centre region. The authors set \alpha = 10 and \beta = 3 §3.3:

C_{center} = \alpha^{\left| x_{pred} - x_{gt} \right| - \beta} eq. 4

Pseudocode
# Dynamic soft label assignment, one image  [§3.3] [eq. 1-4]
# priors (N, 2), strides (N,); boxes (N, 4) decoded; logits (N, C)
# gt_boxes (G, 4); gt_labels (G,)
def assign(priors, strides, boxes, logits, gt_boxes, gt_labels):
    # Candidates: priors whose centre lies inside some gt box
    # Not stated in the paper  [code: assigner#L116-L127]
    candidates = inside_any_box(priors, gt_boxes)
    # IoU between candidate boxes and gts  [§3.3]
    # [code: assigner#L154]
    ious = pairwise_iou(boxes[candidates], gt_boxes)    # (K, G)
    # Soft classification cost against the IoU target  [eq. 2]
    # [code: assigner#L157-L168]
    soft_target = one_hot(gt_labels) * ious[..., None]  # (K, G, C)
    cls_cost = bce_with_logits(logits[candidates], soft_target)
    cls_cost = cls_cost * (soft_target - sigmoid(logits[candidates])) ** 2
    cls_cost = cls_cost.sum(dim=-1)                     # (K, G)
    # Log-IoU regression cost, weight 3  [eq. 3]
    # [code: assigner#L155]
    reg_cost = -3.0 * log(ious + 1e-7)
    # Soft centre cost, distance divided by stride  [eq. 4]
    # [code: assigner#L150-L152]
    distance = norm(priors - gt_centres) / strides
    centre_cost = 10.0 ** (distance - 3.0)
    # Total cost, weights 1, 3 and 1  [eq. 1]  [code: assigner#L170]
    cost = cls_cost + reg_cost + centre_cost
    # Dynamic k per gt: sum of its 13 largest IoUs, truncated, min 1
    # [code: assigner#L203-L206]
    dynamic_k = max(1, int(sum(top13_ious(ious))))
    # Each gt takes its dynamic_k cheapest candidates  [code: assigner#L207]
    matches = take_cheapest(cost, dynamic_k)
    # A prior claimed by several gts keeps only the cheapest gt
    # [code: assigner#L214-L219]
    matches = keep_cheapest_gt(matches, cost)
    # The matched IoU is the classification target and the regression
    # loss weight  [code: head#L475-L476]
    return matches, matched_iou(matches, ious)

Cached Mosaic and MixUp. Mosaic and MixUp need several images per sample, which costs data loading time and can produce unrealistic samples §3.3. The authors keep a cache of already-loaded images and draw from it. A large cache with random eviction behaves like the uncached operations; a small cache with first-in-first-out eviction behaves like repeated augmentation, so the same image can appear with different augmentations in one batch §3.3. Speed: cached Mosaic takes 24.0 ms per 100 images against 87.1 ms uncached, and cached MixUp takes 12.4 ms against 19.3 ms tab. 7 §4.4. Implementation: transforms.py#L3344-L3420 (CachedMosaic, default cache 40, random pop) and transforms.py#L3579-L3660 (CachedMixUp, default cache 20, random pop) [code: transforms.py#L3416-L3417, transforms.py#L3657-L3658].

Two-stage training. The first stage (280 epochs) uses cached Mosaic and MixUp; the authors drop rotation and shear (used by YOLOX) because they break box alignment, and mix more images per sample instead §3.3 §4.4. The last 20 epochs use large-scale jittering, a fine-tuning stage close to the real data distribution §3.3 tab. 1. The stage switch is at epoch 280 in the code [code: rtmdet_l_8xb32-300e_coco.py#L177].

Optimisation. AdamW with weight decay 0.05 is used because SGD converged unstably under heavy augmentation; a flat-then-cosine learning rate and no decay on normalisation layers and biases each add accuracy §4.4 tab. 8. A pre-trained backbone with the RSB recipe adds a further 0.3 AP tab. 9 §4.5.

Instance segmentation (RTMDet-Ins). A CondInst-style branch is added: a mask feature head of 4 convolutions producing 8-channel features, and a kernel head predicting a 169-dimensional vector per instance that is split into three dynamic convolution kernels (88, 72 and 9 values) §3.4 fig. 4. The region prior uses the mask's centre of mass rather than the box centre, and the mask loss is dice loss §3.4. Not inspected in the code (see Paper vs code).

Rotated detection (RTMDet-R). Three changes: a 1x1 convolution predicts the angle, the box coder supports rotated boxes, and the GIoU loss is replaced by a rotated-IoU loss §3.4. COCO-pretrained weights can initialise the rotated model §3.4. Not inspected in the code (see Paper vs code).

Hyperparameters

Object detection and instance segmentation, 8 NVIDIA A100 GPUs §4.1.

NameValueWhereNotes
OptimiserAdamWtab. 1 §3.3 [code: rtmdet_l_8xb32-300e_coco.py#L139]config sets no betas
Base learning rate0.004tab. 1 [code: rtmdet_l_8xb32-300e_coco.py#L124]
Weight decay0.05; 0 for bias and norm layerstab. 1 §4.4 [code: rtmdet_l_8xb32-300e_coco.py#L139-L141]
Optimizer momentum0.9tab. 1listed for AdamW but the code sets no betas; paper does not say which beta it means
Batch size256 (8 GPUs x 32)tab. 1 [code: rtmdet_l_8xb32-300e_coco.py#L113]
Epochs300tab. 1 §4.1 [code: rtmdet_l_8xb32-300e_coco.py#L122]
Warm-up1000 iterations, lineartab. 1 [code: rtmdet_l_8xb32-300e_coco.py#L145-L149]start factor 1e-5 in code; not stated in paper
Learning-rate scheduleConstant to epoch 150, then cosine to 0.05 x base§4.4 [code: rtmdet_l_8xb32-300e_coco.py#L152-L158]"flat-cosine"; the final value 0.05 x base is not stated in paper
Input size640 x 640tab. 1
Stage 1 augmentation (epochs 1-280)cached Mosaic and MixUp; HSV colour jitter; random fliptab. 1 §3.3 [code: rtmdet_l_8xb32-300e_coco.py#L67-L81]HSV jitter is not mentioned in the paper
Stage 1 resize ratio(0.5, 2.0) for tiny and s; (0.1, 2.0) for m, l, x§4.4 [code: rtmdet_tiny_8xb32-300e_coco.py#L26] [code: rtmdet_l_8xb32-300e_coco.py#L71]
Stage 2 augmentation (last 20 epochs)random resize, random crop 640, HSV, fliptab. 1 [code: rtmdet_l_8xb32-300e_coco.py#L89-L95]the paper names large-scale jittering; the code uses resize-and-crop
Mixed images per sample (stage 1)"8"§3.3 §4.4no value of 8 found in the configs
Cache, Mosaic / MixUplarge (40 / 20, random pop) for s, l, m, x; tiny: 20 / 10, FIFO[code: rtmdet_tiny_8xb32-300e_coco.py#L18-L22, #L33-L39]the paper's small-cache FIFO variant is the tiny setting
Soft cls cost, IoU as targetyeseq. 2 [code: assigner#L163]
Regression cost weight3 (log IoU)eq. 3 §3.3 [code: assigner#L155]
Centre cost weight1, alpha = 10, beta = 3eq. 4 §3.3 [code: assigner#L152, assigner#L56]distance is divided by stride in code
Top-k for dynamic k13[code: rtmdet_l_8xb32-300e_coco.py#L52] [code: assigner#L57]not stated in paper
EMAExpMomentumEMA, momentum 0.0002tab. 1 (decay 0.9998) [code: rtmdet_l_8xb32-300e_coco.py#L170-L173] [code: ema.py#L39-L40]momentum ramps down from a higher value with gamma 2000 (code default)
Head2 stacked 3x3 convs, 1x1 predictions, SiLU, SyncBN, shared conv across scales§3.2 [code: rtmdet_l_8xb32-300e_coco.py#L3-L48] [code: head.py#L629-L633]
Regression outputexp on or off: on for l, m, x; off for tiny and s[code: rtmdet_l_8xb32-300e_coco.py#L46] [code: head.py#L686-L689]not stated in paper
Classification lossQualityFocalLoss, beta 2.0[code: rtmdet_l_8xb32-300e_coco.py#L39-L43]the ablation text says FocalLoss
Box lossGIoU, weight 2.0[code: rtmdet_l_8xb32-300e_coco.py#L44]
Test: score threshold0.001 before NMS§4.1 [code: rtmdet_l_8xb32-300e_coco.py#L59]ablations use 0.05 and 100 kept boxes §4.1
Test: pre-NMS top30000[code: rtmdet_l_8xb32-300e_coco.py#L57]not stated in paper
Test: NMS IoU0.65[code: rtmdet_l_8xb32-300e_coco.py#L60]not stated in paper
Test: max detections300§4.1 [code: rtmdet_l_8xb32-300e_coco.py#L61]
Model scale (deepen / widen / neck channels / neck blocks)tiny 0.167 / 0.375 / 96 / 1; s 0.33 / 0.5 / 128 / 1; m 0.67 / 0.75 / 192 / 2; l 1 / 1 / 256 / 3; x 1.33 / 1.25 / 320 / 4[code: rtmdet_tiny_8xb32-300e_coco.py#L7-L12] [code: rtmdet_s_8xb32-300e_coco.py#L5-L10] [code: rtmdet_m_8xb32-300e_coco.py#L3-L6] [code: rtmdet_x_8xb32-300e_coco.py#L3-L7]scale factors are not in the paper; the paper gives only the resulting sizes
Rotated (RTMDet-R)lr 0.00025; batch 8; 36 epochs; 1024 input; random flip and rotatetab. 1taken from the paper; the code is in MMRotate, not checked
DOTA crop1024 patches; overlap 256 (single scale) or 500 (multi scale, scales 0.5, 1.0, 1.5)§4.1not checked in code
Latency measurementFP16, TensorRT 8.4.3, cuDNN 8.2.0, batch 1, NVIDIA 3090; NMS excluded for detection, included for instance segmentation§4.1 tab. 3the benchmark pipeline is not in the repository

Results

Author-reported, COCO val2017, models trained 300 epochs without extra data or distillation tab. 2. Latency as in the Hyperparameters table. Full table from the paper (rows in source order):

ModelInputParams (M)FLOPs (G)Latency (ms)AP (%)AP50 (%)
YOLOv5-n640 (LB)1.92.31.5128.045.7
YOLOX-tiny416 x 4165.13.30.8232.850.3
YOLOv6-n640 (LB)4.35.60.7935.951.2
YOLOv6-tiny640 (LB)9.712.50.8640.356.6
RTMDet-tiny640 x 6404.88.10.9841.157.9
YOLOv5-s640 (LB)7.28.31.6337.456.8
YOLOX-s640 x 6409.013.41.2040.559.3
YOLOv6-s640 (LB)17.222.10.9243.560.4
PPYOLOE-s640 x 6407.98.71.3443.059.6
RTMDet-s640 x 6408.9914.81.2244.661.9
YOLOv5-m640 (LB)21.224.51.8945.464.1
YOLOX-m640 x 64025.336.91.6846.965.6
YOLOv6-m640 (LB)34.341.11.2148.5-
PPYOLOE-m640 x 64023.425.01.7549.065.9
RTMDet-m640 x 64024.739.31.6249.466.8
YOLOv5-l640 (LB)46.554.62.4649.067.3
YOLOX-l640 x 64054.277.82.1949.768.0
YOLOv6-l640 (LB)58.572.01.9151.0-
YOLOv7640 (LB)36.952.42.6351.2-
PPYOLOE-l640 x 64052.255.02.5751.468.6
RTMDet-l640 x 64052.380.22.4051.568.8
YOLOv5-x640 (LB)86.7102.92.9250.768.9
YOLOX-x640 x 64099.1141.02.9851.169.4
PPYOLOE-x640 x 64098.4103.33.0752.369.5
RTMDet-x640 x 64094.9141.73.1052.870.4

Instance segmentation, COCO val2017 tab. 3. Box and mask AP; latency includes box NMS and top-100 mask post-processing.

ModelEpochsParams (M)FLOPs (G)Latency (ms)Box APMask AP
CondInst-R50-FPN (800-1333)3633.9240.8-42.638.2
RTMDet-Ins-R50-FPN (800-1333)3635.9295.2-45.339.7
RTMDet-Ins-tiny (640)3005.611.81.7040.535.4
RTMDet-Ins-s (640)30010.221.51.9344.038.7
RTMDet-Ins-m (640)30027.654.12.6948.842.1
RTMDet-Ins-l (640)30057.4106.63.6851.243.7
RTMDet-Ins-x (640)300102.7182.75.3152.444.6

Rotated detection, DOTA-v1.0 test, mAP tab. 4: RTMDet-R-m and RTMDet-R-l reach 78.24 and 78.85 at single scale, and 80.26 and 80.54 at multiple scales (ImageNet pre-training). With COCO pre-training and multi-scale testing, RTMDet-R-l reaches 81.33 tab. 4 §4.2. Speed on DOTA-style input at 1024 tab. A1: RTMDet-R-m 5.79 ms (24.67M), RTMDet-R-l 8.20 ms (52.27M).

Other results: DOTA-v1.5, RTMDet-R-l multi-scale 78.12 mAP, against ReDet 76.80 tab. A2. HRSC2016, RTMDet-R-tiny 90.60 mAP07 tab. A3.

Ablations (all COCO val2017 unless stated; single runs, no spreads reported).

Model architecture tab. 5:

StudySettingParamsGFLOPsLatency (ms)AP
Kernel size3x3 / 5x5 / 7x750.80M / 50.92M / 51.10M79.61 / 79.70 / 80.342.10 / 2.11 / 2.7350.0 / 50.9 / 51.1
Blocks (large)3-9-9-3 / 3-6-6-3 / 3-6-6-3 with CA53.40M / 50.92M / 52.30M86.28 / 79.70 / 79.902.60 / 2.11 / 2.4051.4 / 50.9 / 51.3
Backbone / neck (small)47% / 45% / 63% / 29%8.54M / 9.01M15.76 / 15.851.21 / 1.3743.9 / 43.7
Backbone / neck (large)47% / 45% / 63% / 29%50.92M / 57.43M79.70 / 93.732.11 / 2.5750.9 / 51.0
HeadShared / Totally separate / Separate BN52.32M / 57.03M / 52.32M80.23 (all)2.44 (all)48.0 / 51.2 / 51.3

Training strategy:

StudySettingAP
Label assignment (ResNet-50, 1x)baseline / + soft cls cost / + soft centre prior / + log-IoU cost39.9 / 40.3 / 40.8 / 41.3
Other assignments (ResNet-50, 1x)ATSS / PAA / OTA / TOOD (w/o T-head) / Ours39.2 / 40.4 / 40.7 / 40.7 / 41.3
SimOTA vs ours (RTMDet-s)SimOTA / Ours43.2 / 44.5

Data augmentation tab. 7:

ModelStage 1Stage 2AP
RTMDet-sLSJLSJ42.3
RTMDet-sMosaic and MixUpMosaic and MixUp41.9
RTMDet-sCached Mosaic and MixUpCached Mosaic and MixUp41.9
RTMDet-sCached Mosaic and MixUpLSJ43.9
RTMDet-sCached (small) Mosaic and MixUpLSJ44.2
RTMDet-lLSJLSJ46.7
RTMDet-lMosaic and MixUpMosaic and MixUp49.8
RTMDet-lCached Mosaic and MixUpCached Mosaic and MixUp49.8
RTMDet-lCached Mosaic and MixUpLSJ51.3
RTMDet-lCached (small) Mosaic and MixUpLSJ51.1

With the YOLOX augmentation, RTMDet-s gets 42.9 and RTMDet-l 50.6; with the paper's augmentation, 44.2 and 51.3 tab. 7.

Optimisation, RTMDet-s tab. 8: SGD with cosine LR is unstable; AdamW with cosine 43.0; AdamW with flat-cosine 43.3; without norm and bias decay 44.2; with RSB pre-trained backbone 44.5.

Step by step from YOLOX-s tab. 9: 40.2 baseline (9.0M, 13.4G, 1.20 ms); AdamW and flat-cosine 40.6; new architecture 41.8 (10.07M, 14.8G, 1.22 ms); shared head 41.8 (8.89M); label assignment and loss 42.9; improved augmentation 44.2; RSB pre-trained backbone 44.5.

Paper vs code

Code: https://github.com/open-mmlab/mmdetection, linked from the paper's abstract. The paper points to the 3.x branch; the clone is the default branch at code_commit, which contains configs/rtmdet/. Licence Apache-2.0.

  • Classification loss. The ablation text describes a focal loss §4.4; the configs use QualityFocalLoss with beta 2.0 [code: rtmdet_l_8xb32-300e_coco.py#L39-L43].
  • Regression weighting and normalisation. The matched IoU is both the classification target and the weight of the regression loss; both losses are normalised by the summed IoU over the batch [code: head.py#L199-L204, head.py#L282-L287, head.py#L475-L476]. The paper does not describe this.
  • Candidate priors. Only priors whose centre is inside some gt box are candidates [code: assigner#L116-L127]. The paper does not state this.
  • Centre cost. The code uses the Euclidean distance divided by the stride, not a per-coordinate term [code: assigner#L150-L152]. Eq. 4 is written with a single coordinate difference and no stride.
  • Dynamic k. Sum of the top-13 IoUs, truncated to an integer, minimum 1 [code: assigner#L203-L206, rtmdet_l_8xb32-300e_coco.py#L52].
  • Stage 1 mixing. The paper says 8 images are mixed per sample §3.3; the configs use CachedMosaic (4 images) and CachedMixUp (2 images), and no "8" appears [code: rtmdet_l_8xb32-300e_coco.py#L67-L81].
  • Small-model cache. The paper says a small FIFO cache helps tiny and small models by about 0.5 AP §4.4. Only the tiny config uses FIFO with a small cache; the s config uses the larger random-pop defaults [code: rtmdet_s_8xb32-300e_coco.py#L15, #L26-L29; rtmdet_tiny_8xb32-300e_coco.py#L18-L22].
  • Stage 2. The paper names large-scale jittering §3.3 tab. 1. The code uses random resize, a 640 crop, HSV colour jitter and flip [code: rtmdet_l_8xb32-300e_coco.py#L89-L95]. The HSV jitter is also used in stage 1.
  • Regression output. exp is applied to the regression output for l, m and x, but not for tiny and s [code: head.py#L686-L689, rtmdet_tiny_8xb32-300e_coco.py#L12, rtmdet_l_8xb32-300e_coco.py#L46].
  • EMA. The paper gives decay 0.9998 tab. 1. The code uses momentum 0.0002 with a ramp that starts higher, with gamma 2000 [code: ema.py#L39-L40, rtmdet_l_8xb32-300e_coco.py#L172]. The ramp matters early in training only.
  • Learning rate. Warm-up starts at 1e-5 of the base rate, and cosine decay ends at 0.05 of the base rate [code: rtmdet_l_8xb32-300e_coco.py#L147, #L154]. Neither value is in the paper.
  • Test settings. Score threshold 0.001 and top 300 match §4.1. The pre-NMS top-30000 and NMS IoU 0.65 are not in the paper [code: rtmdet_l_8xb32-300e_coco.py#L57-L61].
  • Backbone. The stem is three convolutions; the P5 stages and block counts match the paper's 3-6-6-3 ablation [code: backbone.py#L56-L62, backbone.py#L104-L128, backbone.py#L135].
  • Not checked. RTMDet-Ins (instance segmentation) configs and the rotated model. The rotated code lives in MMRotate (named in the paper §4.2); it was not cloned. The rotated hyperparameters in tab. 1 are the paper's only.
  • Latency pipeline. The TensorRT benchmark code is not in the repository, so the latency numbers cannot be reproduced from it.

Pitfalls

  • Final AP for RTMDet-s differs. The main table reports 44.6 tab. 2; the ablation runs end at 44.5 tab. 8 tab. 9. The paper does not explain this.
  • Ablation evaluation is cheaper. The ablations use score threshold 0.05 and 100 kept boxes, which the authors say may cost about 0.3 AP §4.1. Do not compare ablation numbers with the main table directly.
  • Latency claims. Latency is FP16 TensorRT, batch 1, on one GPU, with NMS excluded for detection but included for instance segmentation tab. 3 §4.1. Speed comparisons with other models depend on that setup.
  • Text claims not matching the tables.
    • "300+ FPS" for RTMDet-x matches 3.10 ms tab. 2 (about 322 FPS).
    • "180 FPS" for RTMDet-Ins-x §1 does not match 5.31 ms in tab. 3 (about 188 FPS); the speed conditions differ.
    • RTMDet-s has "half" the parameters and computation of YOLOv6-s §4.2: the table gives 8.99M vs 17.2M parameters, but 14.8G vs 22.1G FLOPs (about 67%).
    • RTMDet-R-m is said to be 18.5% faster than PPYOLOE-R-l app. A.1; the table gives 5.79 vs 7.09 ms, which is 18.3% lower latency.
  • DOTA-v1.5 pre-training is not in the table. tab. A2 has no pre-training column; the text says COCO pre-training is the default there app. A.1.
  • Regression loss is IoU-weighted. Both the classification target and the box-loss weight are the matched IoU, and the loss is normalised by the summed IoU. An implementation that uses hard labels or unweighted boxes will not match the reported behaviour.
  • Dynamic k is truncated. Using rounding or no minimum changes which priors are matched.
  • Cost terms can be extreme. The centre cost is 10 to the power of (stride-normalised distance minus 3), so distant priors get very large costs. The log-IoU term uses a 1e-7 floor.
  • Cache behaviour depends on size. Random eviction with a large cache is close to uncached augmentation; FIFO with a small cache repeats images within a batch. Pick per model size as in the configs.

Lineage

  • Builds on: YOLO-series detectors and their training recipes, especially YOLOX and its SimOTA label assignment §1 §3.3; CSPNet-style blocks fig. 2; large-kernel depthwise convolutions such as ConvNeXt and RepLKNet §3.2; GFL for the IoU soft label §3.3; cached-data augmentation ideas from YOLOX-style Mosaic and MixUp §3.3.
  • Compared against: YOLOv5, YOLOX, YOLOv6, YOLOv7 and PPYOLOE on detection tab. 2; CondInst, SparseInst, SOLOv2 and Cascade R-CNN on instance segmentation tab. 3; ATSS, PAA, OTA and TOOD for label assignment tab. 6; PPYOLOE-R and the rotated detectors in tab. 4.
  • Superseded by: not known from this wiki.

Source and license

Adapted from “RTMDet: An Empirical Study of Designing Real-Time Object Detectors” by Chengqi Lyu, Wenwei Zhang, Haian Huang, Yue Zhou, Yudong Wang, Yanyi Liu, Shilong Zhang, Kai Chen (arXiv:2212.07784), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/open-mmlab/mmdetection at commit cfd5d3a985b0 (Apache-2.0). Any quoted code is shown under that license.