Individual identification of mountain gorillas (Gorilla beringei beringei) by facial recognition.
Pipeline: photo, then YOLO for face detection, then MegaDescriptor-T-224 for a 768-dimensional embedding, then cosine similarity against a gallery to return an identity or "Unknown".
This repository contains only the V1 training pipeline: extraction, training, and gallery construction. It does not include photos, crops, or model weights. Those are either regenerated locally or downloaded from HuggingFace.
Android app is in a separate repository: https://github.com/tit-exe/GorillaIdentifier_AndroidApp
| Metric | Value |
|---|---|
| Recognized individuals | 66 |
| Top-1 accuracy | 93.0% |
| Top-3 accuracy | 96.1% |
| Mean F1 | 0.981 |
| Composite score | 0.808 |
| Rejection threshold | 0.4689 |
| Separability gap | 0.43511 |
| Backbone | MegaDescriptor-T-224 (Swin Transformer Tiny, 27.5M parameters) |
| Training time | about 66 minutes on an RTX 3050 4 GB |
These numbers come from the benchmark run on the held-out validation set after training (see output/v1_gorilla/diagnostics.json once you've trained your own model). Top-1 and top-3 accuracy measure how often the correct individual appears at rank 1 or in the top 3 matches. The rejection threshold is the cosine-similarity cutoff below which a face is reported as unknown, calibrated by maximizing F1 on the validation set. The separability gap is the average similarity gap between an individual's own exemplars and its closest rival; a higher gap means less confusion between individuals.
Open Anaconda Prompt (not PowerShell or cmd):
conda create -n gorilla_id python=3.10 -y
conda activate gorilla_id
# PyTorch with CUDA must be installed before requirements.txt.
# PyPI serves a CPU-only build by default.
pip install torch==2.4.1+cu124 torchvision==0.19.1+cu124 --index-url https://download.pytorch.org/whl/cu124
# Remaining dependencies
pip install -r requirements.txt
# Check the installation
python check_env.pyEvery line should print [OK]. If you see [!!] GPU present but PyTorch has NO CUDA, uninstall PyTorch and reinstall it with the --index-url shown above.
Each new terminal session needs conda activate gorilla_id, then a move to the repository root. Every path used by the pipeline is relative to that root, defined in config.yaml, so the repository can be cloned anywhere on any drive.
The gorilla face detector and the trained V1 model are hosted on HuggingFace at tit0000/GorillaIdentifier. Fetch the detector with:
python models/download_models.pyThis downloads models/yolo_gorilla.pt, which crop extraction needs. Add --version all to also fetch the trained checkpoint, the gallery, and the TFLite files. The MegaDescriptor-T-224 backbone does not need a manual download: it is fetched automatically from the HuggingFace Hub the first time v1_megadesc_arcface/train.py runs, and cached locally afterward (see config.yaml to change the cache location).
Create one subfolder per individual inside data/photos/:
data/photos/
SB Humba/
IMG_001.jpg
IMG_002.jpg
...
ADF Anangana/
...
JUV Bakunzi/
...
The folder name's prefix sets how many ArcFace sub-centers that individual gets during training. More sub-centers give the model more tolerance for appearance variation, which matters for young individuals whose look changes quickly.
| Prefix | Category | Sub-centers |
|---|---|---|
SB |
Silverback | 1 |
ADF |
Adult female | 1 |
AD |
Adult | 1 |
SAF / SAM |
Subadult female / male | 1 |
JUV |
Juvenile | 2 |
BB |
Baby / blackback | 2 |
| (none) | Unspecified | 1 |
Aim for at least 30 photos per individual, with varied angles and conditions. Photos do not need to be pre-cropped; YOLO detects faces automatically. See data/photos/README.md and data/wild_images/raw/README.md for more detail on both folders.
python v1_megadesc_arcface/train.py --min-photos 5Prints a report listing which individuals are included or excluded, and how many crops are available for each.
python v1_megadesc_arcface/train.py --extractYOLO detects faces in every photo and saves 224x224 crops to data/crops/known/.
python common/review_crops.pyA drag-and-drop interface for checking each crop. Press Enter to validate, Del to delete, R to reject, and Q to quit.
python v1_megadesc_arcface/train.pyTraining resumes automatically if interrupted. It writes its results to output/v1_gorilla/. best.pt is the best checkpoint found during training, gallery_gorilla.json is the gallery of exemplars kept after filtering for intra-individual quality, and diagnostics.json holds the complete metrics, training curves, and confusion matrix.
If a model is already trained and you only want to recompute the metrics:
python v1_megadesc_arcface/train.py --benchmark-onlypython prepare_gorilla_gallery.pyThis converts output/v1_gorilla/gallery_gorilla.json, the internal training format, into output/v1_gorilla/gallery.json, a simplified format that any downstream client can consume directly: a mobile app, a batch inference script, a web demo, and so on.
GorillaIdentifier/
├── v1_megadesc_arcface/
│ └── train.py All-in-one script: extract, train, build the gallery, benchmark
├── common/
│ ├── config_loader.py Centralizes every path, reads config.yaml
│ └── review_crops.py PyQt5 tool for reviewing crops
├── data/ Not tracked by git, see .gitignore
│ ├── photos/ Field photos per individual, fill this yourself
│ └── wild_images/raw/ Background images for the "unknown" class, fill this yourself
├── models/ Not tracked by git, download from HuggingFace
│ ├── README.md Explains what goes here and where to get it
│ ├── download_models.py Fetches the weights from HuggingFace
│ └── yolo_gorilla.pt
├── output/v1_gorilla/ Not tracked by git, generated by the pipeline
│ ├── best.pt
│ ├── gallery_gorilla.json
│ ├── gallery.json generated by prepare_gorilla_gallery.py
│ └── diagnostics.json
├── export_tflite_gorilla.py Exports the backbone + detector to TFLite (WSL, for the app)
├── check_env.py Environment diagnostic
├── prepare_gorilla_gallery.py Converts the internal gallery to the simplified format
├── config.yaml Centralized settings: paths and hyperparameters
├── requirements.txt Python dependencies
├── ADDING_INDIVIDUALS.md How the gallery works and how to add individuals safely
├── docs/figures/ Result figures shown in this README
├── README.md This file
└── TECHNICAL_DOCUMENTATION.md Architecture, loss functions, curriculum, and augmentation details
Field photo
|
v
[YOLO yolo_gorilla.pt] face detection, 640x640 input
| 224x224 crop
v
[MegaDescriptor-T-224] Swin Transformer Tiny, pretrained for multi-species re-identification
| L2-normalized 768D vector
v
[gallery.json] max cosine similarity over up to 25 exemplars per individual
|
v
similarity >= 0.4689 and margin >= 0.08 -> individual identified, with a score
similarity < 0.4689 or margin < 0.08 -> "Unknown"
The total training loss combines three terms: loss = L_ArcFace + lambda_inv * L_invariance + lambda_sup * L_SupCon.
| Term | Role |
|---|---|
| L_ArcFace (sub-center, scale 64, margin 0.50) | Angular separation between individuals |
| L_invariance | Pushes the embedding of a clean image close to that of its degraded version, for robustness in the field |
| L_SupCon (temperature 0.07) | Maximizes intra- and inter-individual separability within each batch |
| Phase | Epochs | Backbone | lambda_inv | lambda_sup |
|---|---|---|---|---|
| A: init | 3 | Frozen | 0 | 0 |
| B: warmup | 15 | Unfrozen | 0.15 | 0.10 |
| C: learning | 20 | Unfrozen | 0.25 | 0.15 |
| D: consolidation | 15 | Unfrozen | 0.25 | 0.15 |
Early stopping applies during phase D, with a patience of 12 epochs measured on the composite score.
| Augmentation | What it does |
|---|---|
_LowRes |
Downscales to 8 to 95% of original resolution then upscales, simulating a distant photo |
_JPEG |
Adds compression artifacts at quality 10 to 80, matching phone or WhatsApp photos |
_ForestShadow |
Darkens horizontal or vertical bands, simulating foliage-filtered sunlight |
_ColorTemp |
Shifts the color temperature between warm and cold |
| Standard set | RandomResizedCrop, horizontal flip at 0.5, rotation up to 20 degrees, color jitter |
There is no vertical flip, since gorillas are always upright in field photos.
TECHNICAL_DOCUMENTATION.md covers the full detail: equations, hyperparameters, and the reasoning behind each design choice.
- Train a YOLO face-detection model for the target species.
- Replace
models/yolo_gorilla.ptwith that model. - Organize photos in
data/photos/<Individual>/, following the same one-folder-per-individual convention. - Edit
speciesandproject_nameinconfig.yaml. - Run the pipeline as described above.
MegaDescriptor-T is pretrained across many species, so it performs reasonably well even with a limited dataset.
The YOLO detector misses most faces photographed at more than roughly 60 degrees off-axis. Juveniles and babies change appearance quickly, so their gallery entries should be refreshed every three to six months. More generally, gradual changes in individual appearance over time mean the model benefits from retraining roughly once a year.
Čermák et al. (2024). WildlifeDatasets: A Comprehensive Library for Animal Re-Identification. WACV 2024.
Deng et al. (2019). ArcFace: Additive Angular Margin Loss for Deep Face Recognition. CVPR 2019.
Deng et al. (2020). Sub-center ArcFace: Boosting Face Recognition by Large-Scale Noisy Web Faces. ECCV 2020.
Khosla et al. (2020). Supervised Contrastive Learning. NeurIPS 2020.


