Paper · 2021-05-27 · cs.CV
YOLO5Face: Why Reinventing a Face Detector
Treats face detection as generic object detection: YOLOv5 gets a five-point landmark head trained with wing loss, a stem block, smaller SPP kernels, a stride-64 P6 output for large faces, and ShuffleNetV2 backbones for tiny models. Author-reported WiderFace validation (Easy/Medium/Hard): YOLOv5x6 96.67/95.08/86.55; YOLOv5n 93.61/91.54/80.53 at 1.7M parameters.
Object detectionFace recognitionMulti-scale feature fusionEfficient vision backbonesface-detectionobject-detectionfacial-landmarksyoloreal-time
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-10-18, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Most face detectors are built around faces: special shapes, special tricks. This paper asks whether a general object detector, one built to find cars, dogs and chairs, can find faces just as well after some changes. The authors start from YOLOv5, a fast and widely used detector, and adapt it to faces.
The changes are small in number. The detector also predicts a handful of key points on each face, which helps later steps such as lining faces up for recognition. A few internal layers are swapped or resized so that very large faces are not missed and small faces are still seen. One set of models is built on a lightweight backbone meant for phones and other small devices. The authors release several model sizes, so a user can trade accuracy for speed.
TL;DR
- YOLO5Face adapts the YOLOv5 object detector to faces and treats face detection as a general object detection task §1.
- Main changes: a five-point landmark head with wing loss, a stem block, smaller SPP kernels, a P6 output for large faces, and ShuffleNetV2 backbones for the smallest models §3.2.
- Author-reported WiderFace validation results put the largest model (YOLOv5x6) ahead of the strongest compared detector in the table (SCRFD-34GF) on all three subsets. The smallest model (YOLOv5n) is ahead of SCRFD-2.5GF on Hard only §4.5 tab. 3.
- Use it if you need a single detector family from tiny mobile to large models; check the Pitfalls section first, since the paper and its code disagree on several training values.
Problem
Face detectors have usually been designed for faces, with anchor, scale and context choices made for small faces §2. The authors argue that large faces are easy to overlook and that a single detector family should cover a range of accuracy and speed targets, from high accuracy on servers to real-time use on embedded or mobile hardware §1 §2 §3.6. The paper assumes VGA-sized inputs: the longer image edge is scaled to 640 pixels §3.1.
Method
The detector is a YOLOv5-style network §3.1 fig. 1. It has a backbone that extracts features, a neck that combines them across scales, and a head that predicts boxes, confidence, class and landmarks. The backbone is CSPNet-style for the CSP models and ShuffleNetV2 for the two smallest models §3.1 tab. 1. The neck uses SPP followed by a PAN path §3.1 §3.5 §3.6.
Each anchor in the head outputs 16 values: 4 box values, 1 confidence, 1 class score and 10 landmark values (x and y for five points) §3.1 fig. 1. In the code the landmark count is added to the usual YOLOv5 output size [code: models/yolo.py#L35]. Landmark outputs are decoded like the box offsets, with the anchor size and grid position applied yolo.py. During training, landmark labels are normalised to the 0-1 range by the image width and height face_datasets.py.
Changes relative to YOLOv5, as listed in the paper §3.2:
- Landmark head. A five-point landmark regression output trained with the wing loss, added to the detection loss §3.3 eq. 1 eq. 2 eq. 3.
- Stem block. The first convolution, which YOLOv5 implements as a Focus layer, is replaced by a stem block with a stride-2 first down-sampling and more channels §3.2 §3.4. The paper says the block is similar to a published design it cites §3.4. The code's block has a stride-2 3x3 convolution, a branch of a 1x1 convolution and a second stride-2 3x3 convolution, and a parallel max-pool branch; the two are concatenated and fused with a 1x1 convolution common.py.
- SPP with smaller kernels. The SPP kernels become 7x7, 5x5 and 3x3 instead of 13x13, 9x9 and 5x5 §3.5. The code uses the same set, listed as [3, 5, 7] yolov5s.yaml.
- P6 output. An extra detection output at stride 64, with a 10x10x16 map for a 640-pixel input, meant for large faces §3.6.
- Augmentation. Up-down flipping is removed, and random cropping is said to help §3.2 §4.3. See Pitfalls for what the code does here.
- ShuffleNetV2 backbones. Two very small models use ShuffleNetV2 and a ShuffleNetV2 with half width §3.7 tab. 1.
The paper's model family is listed in tab. 1. The code has configurations for the same sizes and uses the depth and width multiples below [code: models/yolov5s.yaml#L3-L4] yolov5s6.yaml [code: models/yolov5n.yaml#L3-L4] yolov5n-0.5.yaml.
| Model | Backbone | (D, W) | P6 output | Source |
|---|---|---|---|---|
| YOLOv5s | YOLO5-CSPNet | (0.33, 0.50) | no | tab. 1 |
| YOLOv5s6 | YOLO5-CSPNet | (0.33, 0.50) | yes | tab. 1 |
| YOLOv5m | YOLO5-CSPNet | (0.50, 0.75) | no | tab. 1 |
| YOLOv5m6 | YOLO5-CSPNet | (0.50, 0.75) | yes | tab. 1 |
| YOLOv5l | YOLO5-CSPNet | (1.0, 1.0) | no | tab. 1 |
| YOLOv5l6 | YOLO5-CSPNet | (1.0, 1.0) | yes | tab. 1 |
| YOLOv5n | ShuffleNetV2 | not stated (code: 1.0, 1.0) | no | tab. 1 |
| YOLOv5n-0.5 | ShuffleNetV2-0.5 | not stated (code: 1.0, 0.5) | no | tab. 1 |
Landmark loss. Each landmark coordinate is scored with the wing loss, which is logarithmic for small errors and linear for large ones. Its curvature is set by a range parameter w and a curvature parameter e. The total landmark loss is a sum over the ten coordinates §3.3 eq. 1 eq. 2. The total training loss is the YOLOv5 loss plus lambda_L times the landmark loss eq. 3.
# Wing loss for one coordinate error [§3.3] [eq. 1]
# Input: error (scalar), w (range), e (curvature)
# Output: scalar loss
def wing(error, w, e):
# C joins the logarithmic and linear parts [eq. 1]
C = w - w * log(1 + w / e)
# Logarithmic branch for small errors, linear branch otherwise
# The paper writes x < w; the code tests |x| < w
# [eq. 1] [code: utils/loss.py#L95-L102]
if abs(error) < w:
return w * log(1 + abs(error) / e)
return abs(error) - C# Landmark loss for one prediction [§3.3] [eq. 2]
# Input: predicted, target: (10,) x/y values
# mask: (10,) 1 where the landmark is labelled, else 0
# Output: scalar landmark loss
def landmark_loss(predicted, target, mask):
# Sum the wing loss over the ten coordinates [eq. 2]
total = 0
for i in range(10):
total += mask[i] * wing(predicted[i] - target[i], w, e)
# The code divides by the number of labelled coordinates; eq. 2 does not
# [code: utils/loss.py#L113]
return total / (sum(mask) + eps)# Total training loss [eq. 3]
# Input: detection_loss (YOLOv5 box, objectness and class terms)
# landmark (output of landmark_loss)
# Output: scalar training loss
def total_loss(detection_loss, landmark, lambda_l):
# lambda_L = 0.5 in the paper, found by exhaustive search [§4.2]
# The code uses a gain of 0.005 scaled by 3 / (number of detection
# layers) instead [code: utils/loss.py#L138] [code: utils/loss.py#L184-L188]
return detection_loss + lambda_l * landmarkTest-time input size, VGA setting. The longer edge is scaled to 640 pixels and the shorter edge follows the aspect ratio. Its size is then rounded to a multiple of the largest stride: 32 without P6, 64 with P6 §3.1. The test script uses the same letterbox routine as training, which pads to multiples of 64 in auto mode test_widerface.py datasets.py.
# Resize for the VGA setting [§3.1]
# Input: image (H, W, 3), longest_edge (640)
# Output: padded image (H', W', 3), scale factor
def prepare_test_image(image, longest_edge=640):
# Longer edge becomes 640; the shorter edge keeps the aspect ratio [§3.1]
scale = longest_edge / max(image.height, image.width)
resized = resize(image, scale)
# Pad both sides to a multiple of the stride, 64 in auto mode
# [§3.1] [code: utils/datasets.py#L795-L796]
padded = pad_to_multiple(resized, 64)
return padded, scaleMulti-scale test. With the test flag, the network runs at scales 1, 0.83 and 0.67, and a left-right flipped pass is included §4.5 yolo.py. The test script leaves this off by default test_widerface.py.
Hyperparameters
Values from the paper §4.2 and the code's defaults. Where the paper is silent, the entry says so.
| Name | Value | Where | Notes |
|---|---|---|---|
| optimizer | SGD | §4.2 train.py | code uses Nesterov momentum |
| initial learning rate | 1e-2 | §4.2 hyp.scratch.yaml | |
| final learning rate | 1e-5 (paper) | §4.2 | code: lr0 x lrf = 2e-3 with a cosine schedule hyp.scratch.yaml train.py |
| weight decay | 5e-3 (paper) | §4.2 | code: 5e-4 hyp.scratch.yaml |
| momentum | 0.8 in warm-up, then 0.937 | §4.2 | code: 0.937 hyp.scratch.yaml; warm-up 0.8 hyp.scratch.yaml |
| warm-up | first 3 epochs | §4.2 | code: 3 epochs, at least 1000 iterations train.py |
| epochs | 250 | §4.2 | code default train.py |
| batch size | 64 | §4.2 | code default is 16 train.py |
| lambda_L (landmark weight) | 0.5 | §4.2 | found by exhaustive search; code uses gain 0.005 hyp.scratch.yaml |
| wing loss w, e | not stated | §3.3 | the paper refers to a wing-loss plot that is not in the source; code uses w = 10, e = 2 loss.py |
| training input size | not stated | §3.1 | code default 800 for training and test train.py |
| test input size | longer edge 640 | §3.1 | code default 640 test_widerface.py |
| test confidence threshold | not stated | code default 0.02 test_widerface.py | |
| test NMS IoU threshold | not stated | code default 0.5 test_widerface.py | |
| up-down flip | removed | §3.2 | code probability 0.0 hyp.scratch.yaml |
| left-right flip | not stated | code probability 0.5 hyp.scratch.yaml | |
| mosaic | not stated | §3.2 §4.3 | code probability 0.5 hyp.scratch.yaml |
| SPP kernels | 7, 5, 3 | §3.5 | the code's set is 3, 5, 7 yolov5s.yaml |
| pretrained initialisation | not stated | code default weights file is yolov5s.pt train.py; the README training command passes a 'pretrained models' path README.md | |
| hardware and training time | not stated |
Results
Author-reported. Setup: WiderFace validation set, Easy, Medium and Hard subsets §4.1 tab. 3. The paper says the focus is VGA inputs §4.5. Whether the table numbers use the multi-scale and flip test is not stated §4.5.
Main comparison, WiderFace validation tab. 3. Params and FLOPs as reported by the authors.
| Detector | Backbone | Easy | Medium | Hard | Params (M) | FLOPs (G) |
|---|---|---|---|---|---|---|
| DSFD | ResNet152 | 94.29 | 91.47 | 71.39 | 120.06 | 259.55 |
| RetinaFace | ResNet50 | 94.92 | 91.90 | 64.17 | 29.50 | 37.59 |
| HAMBox | ResNet50 | 95.27 | 93.76 | 76.75 | 30.24 | 43.28 |
| TinaFace | ResNet50 | 95.61 | 94.25 | 81.43 | 37.98 | 172.95 |
| SCRFD-34GF | Bottleneck ResNet | 96.06 | 94.92 | 85.29 | 9.80 | 34.13 |
| SCRFD-10GF | Basic ResNet | 95.16 | 93.87 | 83.05 | 3.86 | 9.98 |
| YOLOv5s (this paper) | YOLOv5-CSPNet | 94.33 | 92.61 | 83.15 | 7.075 | 5.751 |
| YOLOv5s6 (this paper) | YOLOv5-CSPNet | 95.48 | 93.66 | 82.8 | 12.386 | 6.280 |
| YOLOv5m (this paper) | YOLOv5-CSPNet | 95.30 | 93.76 | 85.28 | 21.063 | 18.146 |
| YOLOv5m6 (this paper) | YOLOv5-CSPNet | 95.66 | 94.1 | 85.2 | 35.485 | 19.773 |
| YOLOv5l (this paper) | YOLOv5-CSPNet | 95.9 | 94.4 | 84.5 | 46.627 | 41.607 |
| YOLOv5l6 (this paper) | YOLOv5-CSPNet | 96.38 | 94.90 | 85.88 | 76.674 | 45.279 |
| YOLOv5x6 (this paper) | YOLOv5-CSPNet | 96.67 | 95.08 | 86.55 | 141.158 | 88.665 |
| SCRFD-2.5GF | Basic ResNet | 93.78 | 92.16 | 77.87 | 0.67 | 2.53 |
| SCRFD-0.5GF | Depth-wise conv | 90.57 | 88.12 | 68.51 | 0.57 | 0.508 |
| RetinaFace | MobileNet0.25 | 87.78 | 81.16 | 47.32 | 0.44 | 0.802 |
| FaceBoxes | - | 76.17 | 57.17 | 24.18 | 1.01 | 0.275 |
| YOLOv5n (this paper) | ShuffleNetV2 | 93.61 | 91.54 | 80.53 | 1.726 | 2.111 |
| YOLOv5n0.5 (this paper) | ShuffleNetV2-0.5 | 90.76 | 88.12 | 73.82 | 0.447 | 0.571 |
Ablation on YOLOv5s, WiderFace validation tab. 2 §4.3. The paper says each panel changes one thing against its own baseline.
| Modification | Setting | Easy | Medium | Hard | Params (M) | FLOPs (G) |
|---|---|---|---|---|---|---|
| Stem block | Focus + conv | 93.56 | 92.54 | 82.56 | 7.091 | 6.174 |
| Stem block | Stem block | 94.13 | 92.87 | 82.79 | 7.075 | 5.751 |
| SPP kernel | 13, 9, 5 | 93.43 | 91.12 | 82.64 | - | - |
| SPP kernel | 7, 5, 3 | 94.33 | 92.61 | 84.15 | - | - |
| P6 block | no | 94.31 | 92.52 | 83.15 | 7.075 | 5.751 |
| P6 block | yes | 95.29 | 93.61 | 83.13 | 12.386 | 6.28 |
| Data augmentation | baseline (with Mosaic) | 91.34 | 90.21 | 83.54 | - | - |
| Data augmentation | minus up-down flipping | 91.87 | 90.56 | 83.58 | - | - |
| Data augmentation | plus ignoring small faces | 94.12 | 92.21 | 82.21 | - | - |
| Data augmentation | plus random crop | 94.34 | 92.58 | 83.17 | - | - |
Face recognition, WebFace test set tab. 4 §4.4. Lower FNMR is better. The metric combines masked and standard face recognition §4.4. The recogniser is an ArcFace setup, with the face detector swapped §4.4.
| Face detector | Training data | FNMR |
|---|---|---|
| RetinaFace | WiderFace | 0.1065 |
| YOLOv5s | WiderFace | 0.1060 |
| YOLOv5s | WiderFace + multi-task facial | 0.1058 |
| YOLOv5m | WiderFace | 0.1056 |
| YOLOv5m | WiderFace + multi-task facial | 0.1051 |
FDDB, cross-domain tab. 5 §4.6. True positive rate at 1000 false positives. The paper uses the original FDDB annotation without modification, and notes that the RefineFace result depends on a modified annotation §4.6.
| Method | TPR at 1000 FP |
|---|---|
| ASFD | 0.9911 |
| RefineFace (modified annotation) | 0.9911 |
| PyramidBox | 0.9869 |
| FaceBoxes | 0.9598 |
| YOLOv5s | 0.9843 |
| YOLOv5m | 0.9849 |
| YOLOv5l | 0.9867 |
| YOLOv5l6 | 0.9880 |
Paper vs code
Official code: https://github.com/deepcam-cn/yolov5-face at commit
152c688d551aefb973b7b589fb0691c93dab3564, under GPL-3.0. The paper's abstract
links this repository, and its owner name matches the authors' affiliation on
the title page (Shenzhen Deepcam) §abstract. No code was quoted here, only
paths and line numbers.
Discrepancies found:
- Final learning rate. Paper: 1e-5 §4.2. Code: cosine decay to lr0 x lrf = 2e-3 hyp.scratch.yaml train.py.
- Weight decay. Paper: 5e-3 §4.2. Code: 5e-4 hyp.scratch.yaml.
- Batch size. Paper: 64 §4.2. Code default: 16 train.py. The README does not pass a batch size README.md.
- Landmark weight. Paper: lambda_L = 0.5 §4.2. Code: gain 0.005, scaled by 3 over the number of detection layers hyp.scratch.yaml loss.py loss.py.
- Landmark loss normalisation. Paper: plain sum eq. 2. Code: divided by the number of labelled coordinates loss.py.
- Missing landmarks. Not described in the paper. The code gives coordinates with target -1 zero weight loss.py.
- Wing loss branch test. Paper text places the log branch on x < w, yet describes the nonlinear part as (-w, w). Code tests the absolute error [code: utils/loss.py#L95-L102].
- Random crop. Paper: random cropping helps §4.3. Code: no crop operation found in the training pipeline. A replicate-small-boxes routine exists but is commented out face_datasets.py face_datasets.py. The nearest active augmentation is random translate and scale, at 0.1 and 0.5 hyp.scratch.yaml.
- Ignoring small faces. Paper: needed for Mosaic to work §4.3. Code: the only ignore parameter found is in the validation conversion script, default 0 val2yolo.py. No training-time filter was found.
- Model list. Paper text: eight large models, including YOLOv5x and YOLOv5x6 §4.2. Table 1 lists six CSP models and two ShuffleNet models tab. 1. The repository has no yolov5x or yolov5x6 configuration at this commit.
- Pretrained initialisation. Not stated in the paper. The code defaults to a yolov5s pretrained file train.py.
- Test-time augmentation. Consistent: scales 1, 0.83 and 0.67, plus a left-right flip, behind the test flag §4.5 yolo.py.
No discrepancies were checked for hardware or training time, which the paper does not state.
Pitfalls
- Missing wing-loss plot. The text says the wing-loss curves are plotted in "Fig. 2". Figure 2 in the source is the precision-recall figure. The stem-block figure is commented out in the source §3.3 fig. 2.
- Results text versus tables. The text gives validation numbers of 96.9, 96.0 and 91.6 (Easy, Medium, Hard) for YOLOv5x6 and test numbers of 95.8, 94.9 and 90.5 §4.5. Neither set appears in any table. The validation row in tab. 3 gives 96.67, 95.08 and 86.55 for the same model. Implementers should use the table values and treat the text values as unverified.
- SPP Hard gain. The text says the smaller SPP kernels gain 1.41 points on Hard §4.3. The table values (84.15 and 82.64) give 1.51 tab. 2.
- Ablation versus main table. The ablation's YOLOv5s baseline (94.31, 92.52, 83.15) differs slightly from the main table's YOLOv5s row (94.33, 92.61, 83.15) tab. 2 tab. 3. The paper does not explain this.
- Table numbering. The FDDB text says "Table 4", while the FDDB table is Table 5 in the compiled order §4.6 tab. 5.
- Data augmentation table. Rows are labelled with minus and plus signs, which reads as cumulative, yet the text says the configurations are not incremental §4.3 tab. 2.
- Training input size. Not stated in the paper. The default in the code is 800. Choosing 640 for training is a guess and changes the scale distribution.
- Input shape for P6. The paper requires the shorter edge to be a multiple of 64 when P6 is used §3.1. The test letterbox pads to multiples of 64 [code: utils/datasets.py#L795-L796]. A custom pipeline that skips this padding may produce feature maps that do not line up in the neck. I did not run the code.
- Stem block reference. The paper cites a published design for the stem block §3.4. The bibliography entry is not checked here. The code's block is described above from its source common.py.
- SPP reference. The bibliography entry keyed as SPP carries the title of a different paper, on sigmoid-weighted linear units §3.5. Cite SPP from the paper's own text, not from the bibliography.
- Face recognition backbone. The paper names a 124-layer ResNet for the recognition backbone §4.4. That depth is unusual, so the name may be a typo. Unverified.
- Landmark usage. The paper shows landmarks only in face recognition, to align faces before recognition §3.3 §4.4. Its landmark quality claims are supported by a recognition metric and a qualitative figure fig. 3.
- Hidden costs. The P6 model adds parameters and FLOPs (12.386M and 6.28G for YOLOv5s6, against 7.075M and 5.751G for YOLOv5s) tab. 2 tab. 3. Larger models use 141M parameters at the top end tab. 3.
- Speed. The paper gives FLOPs and parameters, not measured latency on any hardware. Its real-time claim for embedded and mobile use has no latency table behind it §1 §5.
- Licence. The repository is GPL-3.0. Any verbatim code copied from it would carry copyleft obligations. This card uses paths, line numbers and pseudocode only.
Lineage
- Builds on: YOLOv5, released as code without a paper, and used as the baseline §3.1. YOLOv4 for the Mosaic augmentation §3.2 (arXiv:2004.10934). PAN, for the bottom-up path in the neck §3.6 (arXiv:1803.01534). ShuffleNetV2 for the two small backbones §3.7 (arXiv:1807.11164). The wing loss §3.3 (arXiv:1711.06753, the id given in the code's comment loss.py).
- Compared against: SCRFD (arXiv:2105.04714), TinaFace (arXiv:2011.13183), RetinaFace, DSFD, HAMBox, FaceBoxes and others, in the WiderFace comparison tab. 3.
- Superseded by: not stated in this wiki.
Source and license
Adapted from “YOLO5Face: Why Reinventing a Face Detector” by Delong Qi, Weijun Tan, Qi Yao, Jingfeng Liu (arXiv:2105.12931), 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/deepcam-cn/yolov5-face at commit 152c688d551a (GPL-3.0 (copyleft; only pseudocode and citations used here)). Any quoted code is shown under that license.