Skip to content

omargarraoui/face-consensus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

face-consensus

Label-distribution learning for estimating the aggregate human rating of facial attractiveness from photographs.

The model predicts, for an aligned face crop, the distribution of scores that a defined population of human raters would assign. The point estimate is the expectation of that distribution; the spread is reported as first-class output. The target is a statistical aggregate of subjective preferences from a specific rater population — not an objective property of a person. See ETHICS.md for scope and prohibited uses.

The released checkpoint is trained on SCUT-FBP5500 (1–5 scale, 60 raters/image, 5500 images). The architecture and pipeline support any 1–N_BINS rating scale; N_BINS is set in src/fbp/__init__.py.

What it does

  • Distributional regression: a backbone (ViT-B/16 or ResNet-50) with an N_BINS-bin distribution head (5 for the released SCUT-FBP5500 checkpoint), trained with KL divergence against the empirical rating histogram plus a Huber penalty on the expectation. Strictly dominates scalar regression and N_BINS-class classification on this task (preserves ordinality, models rater disagreement, yields calibrated uncertainty).
  • Rating normalization: per-rater z-scoring, trimmed aggregation, percentile-anchored rescaling to [1, N_BINS].
  • Fairness audit: every evaluation reports metrics per demographic subgroup and per photo condition, with hard gates on inter-group MAE gaps.
  • Adversarial debiasing (optional): gradient-reversal probe that penalizes demographic information in the penultimate features.
  • Interpretability: Grad-CAM (CNN and ViT via token reshaping) and model-agnostic occlusion sensitivity.

Install

pip install -e ".[dev]"

Face detection/alignment for raw photos additionally requires pip install -e ".[align]".

Data format

Two input CSVs, produced however you collect data:

ratings.csv — one row per individual rating:

column type
image_id str
rater_id str each rater needs ≥ 50 ratings for z-scoring
score int 1–N_BINS (1–5 for SCUT-FBP5500)

images.csv — one row per image:

column type
image_id str
image str path relative to the image root, aligned 256×256 crop
subject_id str used for subject-disjoint splits
subgroup columns str e.g. gender, age_bracket, ethnicity, photo_source

Build the training manifest (normalized means, rating histograms, subject-disjoint splits):

python scripts/prepare_data.py --ratings ratings.csv --images images.csv --out data/manifest

Reproducing the released checkpoint (SCUT-FBP5500)

The released checkpoint is trained on SCUT-FBP5500 (Liang et al., 2018, arXiv:1801.06345), which ships its own 60-rater-per-image distributions and an official 3300/2200 train/test split. scripts/prepare_scut_fbp5500.py converts the dataset's All_Ratings.xlsx and split files directly into manifests (no prepare_data.py step needed, since the rater-level data is already in long form).

Download SCUT-FBP5500_v2.1.zip from the official release and extract it under data/raw/SCUT-FBP5500/ (gitignored), then symlink the image folder to image_root and build the manifests:

unzip SCUT-FBP5500_v2.1.zip -d data/raw/SCUT-FBP5500
ln -s raw/SCUT-FBP5500/SCUT-FBP5500_v2/Images data/images

python scripts/prepare_scut_fbp5500.py \
    --ratings-xlsx "data/raw/SCUT-FBP5500/SCUT-FBP5500_v2/All_Ratings.xlsx" \
    --split-dir "data/raw/SCUT-FBP5500/SCUT-FBP5500_v2/train_test_files/split_of_60%training and 40%testing" \
    --out data/manifest

Train

python scripts/train.py --config configs/scut_fbp5500.yaml

configs/vit_b16.yaml and configs/resnet50.yaml are reference configs for the ViT-B/16 and ResNet-50 backbones described in docs/DESIGN.md; they expect manifests built with prepare_data.py.

Checkpoints, config snapshot, and per-epoch metrics (global and per subgroup) are written to the run directory.

Evaluate

python scripts/evaluate.py --checkpoint runs/scut_fbp5500/best.pt --manifest data/manifest/test.csv \
    --image-root data/images

Reports MAE, RMSE, Pearson r, Spearman ρ, R², uncertainty calibration, and the subgroup table. A run fails the audit if any subgroup MAE gap exceeds the configured threshold.

Predict

python scripts/predict.py --checkpoint runs/scut_fbp5500/best.pt --image face.jpg

Output is {score, uncertainty}. Uncertainty is the predicted rater disagreement (std of the predicted distribution); a score without its uncertainty is not a valid output of this system.

Browser demo (GitHub Pages)

web/ is a static page that runs the model client-side with ONNX Runtime Web — images never leave the browser. Enable Pages (Settings → Pages → GitHub Actions); .github/workflows/pages.yml deploys web/ on push.

python scripts/export_onnx.py --checkpoint runs/scut_fbp5500/best.pt --out web/model.onnx

Without --checkpoint the export uses untrained weights and the page labels the scores as noise. The committed web/model.onnx is already exported from runs/scut_fbp5500/best.pt (trained: true in web/model_meta.json; metrics in docs/MODEL_CARD.md). Local preview: python -m http.server -d web.

Repository layout

src/fbp/
  data/         dataset, transforms, rating normalization, alignment
  models/       backbone factory, distribution head
  engine/       trainer, evaluation loop
  interpret/    grad-cam, occlusion sensitivity
  losses.py     KL + Huber distributional loss, gradient-reversal probe
  metrics.py    regression metrics, uncertainty calibration
  fairness.py   subgroup report and gap gates
scripts/        prepare_data, prepare_scut_fbp5500, train, evaluate, predict, export_onnx
configs/        YAML experiment configs
web/            static demo page (ONNX Runtime Web, deployed to GitHub Pages)
docs/DESIGN.md  full methodology: dataset design, annotation protocol,
                architecture comparison, bias analysis, validation
docs/MODEL_CARD.md  model card for the released checkpoint (data, metrics, fairness audit)

Tests

pytest

Caveats that are not optional reading

The prediction ceiling is the reliability of the human consensus itself (split-half, Spearman-Brown corrected). Cross-cultural transfer degrades correlations by 0.2–0.3. Roughly 30–50% of rating variance is intra-identity — the model scores a photograph, not a person. Full analysis in docs/DESIGN.md.

License

MIT for the code. Use of any trained model is additionally constrained by ETHICS.md.

About

Label-distribution learning for estimating aggregate human ratings of facial attractiveness, with uncertainty and fairness auditing. Trained on SCUT-FBP5500.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors