libfacedetection.train: training YuNet face and pose models in plain PyTorch
The training program for libfacedetection for face detection and 5-landmark detection.
At a glance
- What is it?
- This repository is the training side of the libfacedetection project, a direct PyTorch pipeline for YuNet face detection on WIDER Face and for pose keypoint detection, with exporters to ONNX, TFLite and libfacedetection C++ weight data. It skips MMDetection, MMCV and Ultralytics entirely, and it is BSD-3-Clause licensed with a beta classification.
- Who is it for?
- Adopt this pipeline if you need to retrain YuNet style detectors, especially face models for the libfacedetection C++ library or pose models in a YOLO-pose label format, and you want plain PyTorch code you can read instead of a framework. Do not adopt it as a general detection platform, since the model family, output convention and label format are all fixed.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 110 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The training half of a detection library, without a framework
libfacedetection is a C++ library for face detection with 5-landmark support, and this repository is the training program behind it. The description calls it the training program for libfacedetection for face detection and 5-landmark detection, and the README is titled YuNet Training in PyTorch.
What the repository provides is a lightweight PyTorch training pipeline for YuNet-based detection tasks. Four things are in scope: YuNet face detection training on WIDER Face, face evaluation and export for ONNX, C++ weights and TFLite, YuNet pose detection training for YOLO-pose style datasets, and pose validation with COCO keypoint AP evaluation, visualization and ONNX export.
The design decision that separates it from most detection training code is stated in one line. The code is implemented directly with PyTorch and does not require MMDetection, MMCV, Ultralytics or a large detection framework. The reasons listed are that it is lightweight with small YuNet style models, that the exports are practical, that the structure is multi-task with face and pose as separate task implementations sharing a YuNet backbone, neck, training utilities, assignment, priors, bbox losses and NMS, that everything is easy to inspect as plain Python modules, and that it is CI-friendly with CPU smoke tests and small-sample modes.
For a reader who wants to know exactly what a loss does or how the priors are computed, this is the point of the project. The training loop is code you can open.
The repository layout reinforces it. Under yunet_train there is an engine directory for shared training and detection primitives, a models directory for the shared YuNet backbone, neck, layers and initialization, a tasks directory with face and pose subdirectories, a cli directory for command line entry points and a tools directory for dataset checks, visualization and debugging. Two design documents sit in doc, a pose adaptation plan and a multitask refactor plan, which tells you the architecture is still moving.
Environment setup and the deliberate choice not to pin torch
The installation sequence starts with the environment and PyTorch, and the PyTorch build is left to you because the CUDA version is a property of your machine. The documentation points to the official instructions at pytorch.org and gives a conda environment as the example:
conda create -n yunet python=3.11
conda activate yunet
# Install pytorch. This codebase has been tested with PyTorch 2.11.0 and CUDA 12.6.Then the repository is cloned and installed:
git clone https://github.com/ShiqiYu/libfacedetection.train.git
cd libfacedetection.train
python -m pip install -e ".[dev]"
# or
python -m pip install -r requirements.txtOptional extras cover the two paths that need extra packages, COCO keypoint evaluation and TFLite export:
python -m pip install -e ".[pose]" # COCO keypoint evaluation
python -m pip install -r requirements-tflite.txtThe one detail that is easy to miss and matters: torch is intentionally not pinned by this repository, so installing the package will not replace the PyTorch build in your environment. Everything else is pinned exactly, including numpy at 2.4.4, opencv-python at 4.13.0.92, onnx at 1.21.0 and onnxruntime at 1.25.1, and the project requires Python 3.11 or newer.
The package is named yunet-train and its version is 0.1.0, with the classifier Development Status :: 4 - Beta. The console entry points are numerous and descriptive: yunet-train, yunet-train-pose, yunet-eval-widerface, yunet-eval-pose, yunet-eval-pose-coco, yunet-export-onnx and yunet-export-pose-onnx, plus yunet-export-cpp and yunet-export-tflite.
From WIDER Face to a trained checkpoint
The face task starts from WIDER Face, and the expected directory layout is given precisely, which is a good sign for a pipeline meant to be reproducible:
data/widerface
|-- WIDER_train
| `-- images
|-- WIDER_val
| `-- images
`-- labelv2
|-- train
| `-- labelv2.txt
`-- val
|-- gt
`-- labelv2.txtThe labelv2 annotations come from SCRFD, and the tool directory holds a checker you run on both splits before you start:
python -m yunet_train.tools.check_widerface --split train --check-images 10
python -m yunet_train.tools.check_widerface --split val --check-images 10Training is one CLI with a variant selector. The CPU smoke test is the smallest possible run:
python -m yunet_train.cli.train --variant yunet_s --epochs 1 --batch-size 1 --workers 0 --device cpu --image-size 64 --limit-samples 1 --no-tensorboardA full run of the larger model is the same command with different numbers, and the interval flags matter for how you monitor a long run:
python -m yunet_train.cli.train --variant yunet_n --epochs 640 --batch-size 16 --workers 2 --device cuda --checkpoint-interval 80 --eval-interval 100 --work-dir work_dirs/yunet_nResuming after an interruption points at the latest checkpoint:
python -m yunet_train.cli.train --variant yunet_n --epochs 640 --batch-size 16 --workers 2 --device cuda --resume work_dirs/yunet_n/latest.pth --work-dir work_dirs/yunet_nThe work directory keeps a latest checkpoint, a best_loss checkpoint, metrics.csv, train.log and a tensorboard directory. Having both latest and best_loss separately is a practical detail, because a run that diverges late can leave you with a bad final checkpoint while the best one is still on disk.
Evaluating, and the one number the documentation gives
Evaluation on WIDER Face is a separate entry point, and the --save-preds flag writes the predictions out so you can inspect them rather than trust a number:
python -m yunet_train.cli.eval_widerface work_dirs/yunet_n/best_loss.pth --variant yunet_n --device cuda --batch-size 1 --workers 4 --output-dir work_dirs/yunet_n_widerface_eval --save-predsThere is a second mode for the checkpoint released with libfacedetection, and it takes a different path, the weights directory in the repository, plus a --mode origin flag:
python -m yunet_train.cli.eval_widerface weights/yunet_n.pth --variant yunet_n --device cuda --batch-size 1 --workers 4 --mode origin --output-dir work_dirs/legacy_yunet_n_eval --save-predsThe documentation reports one set of numbers for the released YuNet_n checkpoint on the WIDER Face val set:
AP_easy=0.892, AP_medium=0.883, AP_hard=0.811This is the only performance figure given, and it is worth understanding what it is for. It is a reference point for the released checkpoint, not a claim about what your retrained model will score. If your retrained model lands far below it, something in your data, schedule or export path is wrong. The evaluation command is therefore also the tool for validating that your own pipeline reproduces the baseline before you trust it for anything else.
Four export paths, one fixed output convention
What makes this repository more than a training script is the export layer, and specifically the C++ weight exporter. That is the path back into libfacedetection itself:
python -m yunet_train.cli.export_cpp work_dirs/yunet_n/best_loss.pth --variant yunet_n --output-file work_dirs/export/facedetectcnn-data.cppThe ONNX exporter has a static shape mode with a --verify flag and a dynamic export mode:
python -m yunet_train.cli.export_onnx work_dirs/yunet_n/best_loss.pth --variant yunet_n --shape 640 640 --verify --output-file work_dirs/export/yunet_n_640_640.onnxpython -m yunet_train.cli.export_onnx work_dirs/yunet_n/best_loss.pth --variant yunet_n --dynamic-export --output-file work_dirs/export/yunet_n_dynamic.onnxTFLite goes through onnx2tf, which is the reason for the separate requirements-tflite.txt:
python -m yunet_train.cli.export_tflite work_dirs/yunet_n/best_loss.pth --variant yunet_n --shape 640 640 --output-file work_dirs/export/yunet_n_640_640.tfliteThe exported ONNX has a fixed output order, and the documentation says it follows the original YuNet convention:
cls_8, cls_16, cls_32
obj_8, obj_16, obj_32
bbox_8, bbox_16, bbox_32
kps_8, kps_16, kps_32That ordering is a compatibility contract with any runtime that expects YuNet outputs, so an exporter that changed it silently would break every downstream consumer. The compare_inference command exists to check exactly that, evaluating an exported ONNX with ONNX Runtime:
python -m yunet_train.cli.compare_inference work_dirs/export/yunet_n_640_640.onnx --mode AUTO --eval --score-thresh 0.02 --nms-thresh 0.45 --out-dir work_dirs/compare_yunet_nIt also does single image inference, and it recognises ONNX filename prefixes yunet, scrfd, yolo5face and retinaface, which tells you the runtime path is meant to compare models from several detector families, not just your own output.
Pose detection on a YOLO-pose label format
The pose task is newer and structured differently. It trains a detection-style YuNet model that predicts person boxes and keypoints in one pass, and the initial target is the YOLO-pose label format with COCO17 keypoints, one flat row per object:
class cx cy w h x1 y1 v1 x2 y2 v2 ... x17 y17 v17Coordinates are normalized to image width and height, and the v field is the usual visibility flag, so the format matches what YOLO pose training expects rather than inventing a new one.
The default dataset path is a tiny one, which is a deliberate CI affordance:
data/coco8-pose
|-- images
| |-- train
| `-- val
`-- labels
|-- train
`-- valFull COCO keypoint AP evaluation needs the pose extra and a COCO keypoint annotation file:
python -m pip install -e ".[pose]"The pose training command mirrors the face one, with its own CLI module and a larger image size in the smoke test:
python -m yunet_train.cli.train_pose --data-root data/coco8-pose --variant yunet_n --epochs 1 --batch-size 1 --workers 0 --device cpu --image-size 160 --limit-samples 2Note that the CPU smoke test uses --limit-samples 2 for pose, against one for face, which is consistent with the pose input being larger.
The documentation is explicit that the pose work is early. The doc directory contains a pose-adaptation-plan.md and a multitask-refactor-plan.md, and the description says the initial target is the YOLO-pose label format. Treat the pose branch as a work in progress that you would be helping shape, not a settled component.
Where this stops, and what to compare it against
The boundary of the project is the model family. Everything here is YuNet: a small backbone with a neck, three detection scales at 8, 16 and 32, and five landmark outputs. The output convention is fixed to the original YuNet layout, the label format for pose is YOLO-pose with COCO17, and the WIDER Face annotations come from SCRFD. This is a pipeline for retraining YuNet style models, not a general detection framework wearing a YuNet hat.
That is also what makes it different from the alternatives. A framework based approach, with MMDetection, MMCV or Ultralytics, gives you a catalogue of model families, configuration driven experiments, and a much larger community around them. It also gives you a large dependency tree and configuration surfaces to learn. This repository gives you one model family, one training loop you can read, and exporters that go straight to the C++ weights libfacedetection needs. For the specific goal of producing a YuNet model for that library, the narrow tool is the faster path, and the C++ weight exporter does not exist in the frameworks.
The other cost is data preparation. WIDER Face with SCRFD labelv2 annotations has to be downloaded and laid out in a specific shape, and pose training wants a YOLO-pose style dataset. The pipeline is lightweight, but the datasets are not small, and the tool directory only checks that your layout is right rather than fetching anything.
On maintenance and terms, the repository is not archived and the last push was on 2026-07-10. There are no GitHub releases, so versioning runs through the package metadata, where the version is 0.1.0 and the classifier says Beta. The licence is BSD-3-Clause, which is permissive and carries no copyleft obligation on the models you train with it, though a trained model's terms still depend on the dataset you trained it on.
Editorial conclusion
Adopt this pipeline if you need to retrain YuNet style detectors, especially face models for the libfacedetection C++ library or pose models in a YOLO-pose label format, and you want plain PyTorch code you can read instead of a framework. Do not adopt it as a general detection platform, since the model family, output convention and label format are all fixed. Verify first that the released YuNet_n checkpoint scores the documented AP_easy=0.892, AP_medium=0.883 and AP_hard=0.811 on the WIDER Face val set with your own evaluation run, that your export output order matches the original YuNet convention, and that torch is not pinned by this repository so your installed PyTorch build stays intact.
Frequently asked questions
How do I install libfacedetection.train?
Create a Python 3.11 environment, install the PyTorch build matching your CUDA version, then clone the repository and run `python -m pip install -e ".[dev]"` or install from requirements.txt. Extra installs are `python -m pip install -e ".[pose]"` for COCO keypoint evaluation and requirements-tflite.txt for TFLite export.
Does installing libfacedetection.train replace my PyTorch installation?
No. The README states that torch is intentionally not pinned by this repository, so installing the package will not replace the PyTorch build in your environment. The codebase has been tested with PyTorch 2.11.0 and CUDA 12.6.
How do I train a YuNet face detector on WIDER Face?
Place WIDER Face under data/widerface with the WIDER_train, WIDER_val and labelv2 layout, check it with the check_widerface tool, then run the train CLI with a variant, for example `--variant yunet_n --epochs 640 --batch-size 16 --device cuda`, and use `--resume work_dirs/yunet_n/latest.pth` to continue an interrupted run.
What accuracy does the released YuNet_n checkpoint reach?
On the WIDER Face val set the documentation reports AP_easy=0.892, AP_medium=0.883 and AP_hard=0.811 for the released checkpoint, evaluated with the eval_widerface CLI in origin mode.
Can I export a trained model for the libfacedetection C++ library?
Yes. The export_cpp command writes the C++ weight data, for example `python -m yunet_train.cli.export_cpp work_dirs/yunet_n/best_loss.pth --variant yunet_n --output-file work_dirs/export/facedetectcnn-data.cpp`. ONNX and TFLite exporters are provided as well.
What license is libfacedetection.train released under?
BSD-3-Clause, declared in the pyproject.toml and with a LICENSE file at the repository root. The package version is 0.1.0 with a Development Status classifier of Beta.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/shiqiyu-libfacedetection-train)Was this useful?