Age and gender from a face photograph (ConvNeXt-Tiny)

Estimates a person's age in years and their recorded sex from a cropped face photograph. Fine-tuned from ImageNet ConvNeXt-Tiny on the MORPH longitudinal face dataset.

On 5,002 held-out MORPH test images: 2.38 years mean absolute error, 99.6% on sex. For scale, on 200 images of the same split, MiVOLO v2 scores 5.10 years, Claude Sonnet 5 about 3.6, Qwen3.5-27B-VL 5.78 and DeepFace 7.21. The gap is not architecture - it is that this model has seen this population and they have not, which is the point worth taking away: a few minutes of fine-tuning on your own labelled data beats every general model on that data.

Using it

pip install torch torchvision safetensors huggingface_hub pillow numpy
from huggingface_hub import hf_hub_download
import importlib.util

spec = importlib.util.spec_from_file_location(
    "age_gender", hf_hub_download("Suhong/convnext-tiny-finetuned-age-gender-morph", "age_gender.py"))
age_gender = importlib.util.module_from_spec(spec)
spec.loader.exec_module(age_gender)

model = age_gender.load()
print(age_gender.predict(model, "face.jpg"))
# {'age': 41.3, 'gender': 'M', 'confidence': 0.999}

predict also takes a list of paths or PIL images and returns a list, which is much faster than calling it per image.

How it works

One ConvNeXt-Tiny backbone with two linear heads. Age is 47-way classification over the years 16 to 62, read out as the expected value of the predicted distribution, not its arg-max.

That readout is the whole design. A plain regression head compresses its estimates toward the mean of the training set, which is exactly how the off-the-shelf models fail here - DeepFace on this data runs +8.6 years on the under-25s and -6.2 years on the over-45s, pulling every face toward about 35. Classifying into one-year bins and taking the distribution's mean keeps the extremes and still gives a continuous answer.

Input: RGB face crop, resized to 180x180, centre-cropped to 160x160, normalised with the ImageNet statistics. The bundled age_gender.py does this for you; if you preprocess yourself, match that order.

Training

  • Data: MORPH Album 2, 40,012 training images, 5,001 for validation.
  • Recipe: 10 epochs, AdamW, cosine schedule, bf16 autocast, on one 16 GB RTX A5500; mild augmentation only (horizontal flip and a small random crop - nothing that changes apparent age). Cross-entropy on both heads.
  • Checkpoint kept by best validation MAE, not by final epoch.

The same recipe on three backbones

backbone age error sex
ConvNeXt-Tiny (this one) 2.38 y 99.6%
ResNet-34 2.52 y 99.4%
EfficientNet-B3 2.55 y 99.4%

Only the backbone differs - the heads, the data and the schedule are identical, so the difference in score is the backbone's. Paired per-image testing puts ConvNeXt-Tiny ahead of the other two by more than two standard errors, while ResNet-34 and EfficientNet-B3 are indistinguishable from each other.

Limitations, and they matter

  • It only knows 16 to 62. The head cannot output anything outside that range. A child or an 80-year-old will be answered confidently and wrongly.
  • It inherits MORPH's demographics. MORPH is heavily male and its ethnic makeup is not balanced; accuracy on groups thinly represented there will be worse than the headline number, and the headline number does not tell you which groups those are. Anyone deploying this should measure per-group error on their own population first.
  • "Gender" here is the binary sex label recorded in the dataset. It is not gender identity and cannot be.
  • Tested in-domain only. All the numbers on this page come from MORPH's own held-out split: tight, frontal, evenly lit crops. Expect worse in the wild.
  • Age from a photograph is an estimate, not a fact. Do not use it for anything consequential to the person in the photograph - age verification, access control, law enforcement, or screening of any kind. It is offered for research and teaching.

Provenance

Trained by @Suhong on MORPH Album 2, which is distributed under an academic licence from UNC Wilmington. The weights are released for non-commercial research use (CC BY-NC 4.0); no dataset images are included or redistributed here. If you plan to use MORPH yourself you need your own licence from UNC Wilmington.

Downloads last month
14
Safetensors
Model size
27.9M params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support