Skip to content

Repository files navigation

YOLOX Trainer — Face & Document Detection

Adaptation of the YOLOX framework (Megvii BaseDetection) to train a custom detector for faces (face) and identity documents (doc_quad), and export it to ONNX for downstream inference (browser, edge, server — anywhere with an ONNX runtime).


TL;DR

Fine-tune YOLOX-S on a custom VOC dataset (derived from MIDV-2020) with 2 classes (face, doc_quad). Config in exps/example/custom/yolox_doc_face.py. Train with tools/train.py, export to ONNX with export_correct_ONNX.py (which enables decode_in_inference so the consumer doesn't have to decode boxes), and validate with compare_models.py.


1. Scope

This repo covers only training and ONNX export. The resulting .onnx is a self-contained artifact you can plug into any ONNX-compatible runtime (onnxruntime, onnxruntime-web, TensorRT, OpenVINO, etc.). Real-time inference clients live outside this repo.

End-to-end flow:

┌───────────────┐    ┌────────────┐    ┌───────────────┐    ┌──────────────┐
│  MIDV-2020    │ →  │ Convert to │ →  │   Training    │ →  │ ONNX Export  │
│  (download)   │    │  VOC XML   │    │ (fine-tune S) │    │ (.pth → .onnx)│
└───────────────┘    └────────────┘    └───────────────┘    └──────────────┘

2. About YOLOX

YOLOX is an anchor-free object detector from the YOLO family (Megvii, 2021). Key features:

  • Anchor-free — no predefined anchor boxes, simpler design.
  • Decoupled head — separates classification and regression.
  • SimOTA — dynamic label assignment during training.
  • Strong augmentation — Mosaic + MixUp by default.
  • Variants: Nano, Tiny, S, M, L, X, Darknet53.

This project starts from the COCO-pretrained YOLOX-S checkpoint (yolox_s.pth) and fine-tunes it.


3. Repository layout

yolox-trainer/
├── yolox/                    # Framework (models, data, utils, evaluators)
│   ├── core/                 # Trainer, launcher
│   ├── data/                 # Datasets (COCO, VOC), dataloaders, augment
│   ├── evaluators/           # COCO / VOC evaluators
│   ├── exp/                  # Base Exp class
│   ├── layers/               # Custom layers
│   ├── models/               # Backbone, FPN, YOLOXHead
│   ├── tools/                # Module entrypoints
│   └── utils/                # Logger, metrics, postprocess
│
├── tools/                    # CLI scripts
│   ├── train.py              # Training
│   ├── eval.py               # Evaluation
│   ├── demo.py               # Image/video inference
│   ├── export_onnx.py        # Generic ONNX export (upstream)
│   └── trt.py                # TensorRT export
│
├── exps/
│   ├── default/              # Official configs (yolox_s, yolox_m, …)
│   └── example/
│       ├── custom/
│       │   └── yolox_doc_face.py   # ⭐ Custom config
│       └── yolox_voc/        # VOC reference templates
│
├── datasets/                 # Data (NOT included in repo!)
│   └── VOCdevkit/VOC2020/
│
├── demo/                     # Demos per deployment backend
│   ├── ONNXRuntime/
│   └── TensorRT/, ncnn/, OpenVINO/, MegEngine/, nebullvm/
│
├── docs/                     # Upstream framework tutorials
│
├── compare_models.py         # ⭐ Compare PyTorch vs ONNX output
├── export_correct_ONNX.py    # ⭐ ONNX export tuned for this model
├── hubconf.py                # torch.hub entrypoint
├── requirements.txt
├── setup.py / setup.cfg
└── README.md                 # ← this document

⭐ = added/customized for this project.


4. Custom configuration

exps/example/custom/yolox_doc_face.py:

class Exp(MyExp):
    def __init__(self):
        self.num_classes = 2
        self.depth = 0.33                    # "S" size
        self.width = 0.50
        self.warmup_epochs = 1
        self.class_names = ["doc_quad", "face"]
        self.mosaic_prob = 1.0
        self.mixup_prob  = 1.0
        self.hsv_prob    = 1.0
        self.flip_prob   = 0.5
        self.exp_name = "yolox_doc_face"
  • Classes: doc_quad (idx 0), face (idx 1).
  • Backbone: YOLOX-S (depth=0.33, width=0.50).
  • Dataset: VOCDetection from datasets/VOCdevkit/ with splits ("2020", "train") and ("2020", "val").
  • Evaluator: VOCEvaluator → Pascal VOC-style mAP metrics.

5. Dataset

The repo does not ship the data. You must download and convert it to VOC manually.

Source

Expected structure

datasets/VOCdevkit/
├── annotations_cache/      # Auto-generated (DELETE if you add data)
└── VOC2020/
    ├── Annotations/        # *.xml (VOC format)
    ├── JPEGImages/         # *.jpg
    └── ImageSets/Main/
        ├── train.txt       # filenames without extension
        └── val.txt

XML format

<annotation>
  <filename>image001.jpg</filename>
  <size><width>2268</width><height>4032</height><depth>3</depth></size>
  <object>
    <name>face</name>
    <bndbox><xmin>100</xmin><ymin>200</ymin><xmax>300</xmax><ymax>400</ymax></bndbox>
  </object>
  <object>
    <name>doc_quad</name>
    <bndbox><xmin>500</xmin><ymin>600</ymin><xmax>800</xmax><ymax>900</ymax></bndbox>
  </object>
</annotation>

Rules

  • Each image001.jpg requires image001.xml with the same base name.
  • train.txt / val.txt: one filename per line, without extension.
  • Typical 80/20 split.
  • ⚠️ If you add new images, delete datasets/VOCdevkit/annotations_cache/ before the next training run.

6. Installation

Requirements

  • Python 3.8 (Anaconda recommended)
  • CUDA 11.8 + NVIDIA GPU (recommended; CPU works but is very slow)
  • Windows 10/11, macOS or Linux

Steps

# 1. Environment
conda create -n yolox python=3.8 -y
conda activate yolox

# 2. PyTorch (adjust CUDA to your system)
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia -y
# CPU-only: conda install pytorch torchvision torchaudio cpuonly -c pytorch -y

# 3. YOLOX in editable mode
cd /path/to/yolox-trainer
pip install -v -e .

# 4. Extra dependencies
pip install cython pycocotools tensorboard onnx onnxruntime

# 5. Verify
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
python -c "import yolox; print('OK')"

# 6. Base YOLOX-S weights
# Download manually from:
#   https://github.com/Megvii-BaseDetection/YOLOX/releases
# and place yolox_s.pth at the repo root.

7. Training

conda activate yolox

python tools/train.py \
    -f exps/example/custom/yolox_doc_face.py \
    -d 1 \              # 1 GPU
    -b 8 \              # total batch size
    --fp16 \            # mixed precision
    -o \                # occupy GPU memory upfront
    -c yolox_s.pth      # initial checkpoint (transfer learning)

Useful flags:

  • -d N: number of GPUs.
  • -b N: total batch size. Recommended 8 × num_gpus. If OOM, lower it.
  • --cache: cache images in RAM (faster, requires lots of RAM).
  • --logger wandb: integrate with Weights & Biases.

Outputs

YOLOX_outputs/yolox_doc_face/
├── train_log.txt
├── tensorboard logs
├── latest_ckpt.pth
├── best_ckpt.pth        ← exported to ONNX
└── epoch_*.pth

8. Evaluation

python tools/eval.py \
    -f exps/example/custom/yolox_doc_face.py \
    -c YOLOX_outputs/yolox_doc_face/best_ckpt.pth \
    -b 8 -d 1 --conf 0.001 --fp16 --fuse

Reported metrics: Pascal VOC-style mAP (not COCO), because the experiment uses VOCEvaluator.


9. ONNX export

Use export_correct_ONNX.py (tuned for this model). Do not use the generic tools/export_onnx.py — it doesn't set critical params like decode_in_inference=True.

python export_correct_ONNX.py

What it does

  1. Loads exps/example/custom/yolox_doc_face.py.
  2. Sets num_classes=2, test_conf=0.25, nmsthre=0.45, test_size=(640, 640).
  3. Loads the checkpoint (⚠️ path is hardcoded in the script — update it if it changes).
  4. Enables model.head.decode_in_inference = True → box decoding happens inside the ONNX graph.
  5. Exports with opset_version=11, dynamic_axes for dynamic batch, do_constant_folding=True.
  6. Verifies the ONNX by loading it with onnxruntime and comparing outputs against PyTorch (mean diff must be < 0.001).

Output

  • yolox_doc_face_fixed.onnx at the repo root.
  • Output tensor: [batch, num_anchors, 7] containing [x, y, w, h, objectness, p_doc_quad, p_face] already decoded.

10. PyTorch vs ONNX validation

python compare_models.py \
    --image prueba_doc.jpg \
    --checkpoint YOLOX_outputs/yolox_doc_face_20250805/best_ckpt.pth \
    --onnx yolox_doc_face_fixed.onnx

Prints PyTorch detections (postprocess + NMS) and the top-3 ONNX candidates per class. Useful to catch regressions after re-exporting.


11. Inference demo

Image

python tools/demo.py image \
    -f exps/example/custom/yolox_doc_face.py \
    -c YOLOX_outputs/yolox_doc_face/best_ckpt.pth \
    --path assets/dog.jpg \
    --conf 0.25 --nms 0.45 --tsize 640 \
    --save_result --device gpu

Video

python tools/demo.py video \
    -f exps/example/custom/yolox_doc_face.py \
    -c YOLOX_outputs/yolox_doc_face/best_ckpt.pth \
    --path /path/to/video.mp4 \
    --conf 0.25 --nms 0.45 --tsize 640 \
    --save_result --device gpu

Results land in YOLOX_outputs/yolox_doc_face/vis_res/.

Direct ONNX inference

python demo/ONNXRuntime/onnx_inference.py \
    -m yolox_doc_face_fixed.onnx \
    -i prueba_doc.jpg

12. Troubleshooting

Symptom Likely cause Fix
CUDA out of memory Batch too large / small GPU Lower -b (e.g. -b 4 or -b 2)
module 'yolox' not found Env not activated or broken install conda activate yolox and pip install -v -e .
ONNX detections in wrong locations Forgot decode_in_inference=True Re-export with export_correct_ONNX.py
Weird metrics after adding images Stale cache rm -rf datasets/VOCdevkit/annotations_cache/
onnxruntime can't find the model Wrong relative path Use absolute path or --onnx <path>
PyTorch vs ONNX diff > 0.001 Incompatible opset or unexported layers Change opset_version or check custom layers

13. Resources


14. Citation

If you use this work in research, please cite the original YOLOX paper:

@article{yolox2021,
  title={YOLOX: Exceeding YOLO Series in 2021},
  author={Ge, Zheng and Liu, Songtao and Wang, Feng and Li, Zeming and Sun, Jian},
  journal={arXiv preprint arXiv:2107.08430},
  year={2021}
}

15. License

Apache License 2.0 — inherited from the upstream YOLOX project. See LICENSE.

About

Custom YOLOX-S trainer for two-class face + identity-document detection. Trains on a VOC-formatted dataset and exports a self-contained ONNX model (decoder fused into the graph) for downstream inference.

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages