Robust video facial expression recognition with:
- fairness-aware modeling,
- heavy teacher and fast student architectures,
- teacher-student distillation,
- ablation and comparison suites,
- paper artifact generation,
- checkpoint publishing utilities.
This README is a complete runbook for everything implemented in this repository.
Main objective: optimize both overall FER quality and subgroup robustness (worst_group_f1) on diverse data.
Implemented capabilities:
- Metadata building from generic datasets and from coded
Recordingsdatasets (labels.csv+Coding.txt). - Video preprocessing (face-centric clips + quality features).
- Training:
- Stage 1 SSL warmup,
- Stage 2 supervised training,
- Stage 3 GroupDRO fine-tuning,
- teacher-student distillation.
- Evaluation with publication metrics:
accuracy,macro_f1,weighted_f1,balanced_accuracy,worst_group_f1,ece,brier, per-group F1, confusion matrix.
- Automated experiment suites:
- ablation suite,
- model comparison suite with speed/efficiency benchmarking.
- Paper output generation:
- markdown + LaTeX tables,
- metric and tradeoff figures.
- Publish interface for checkpoints:
- package folder + model card + manifest + zip.
fairfactor-temporal-fer/
apps/
preprocess_ui.py
publish_ui.py
configs/
fairfactor_stage1_ssl.yaml
fairfactor_stage2_sup.yaml
fairfactor_stage3_groupdro.yaml
teacher_heavy_stage2.yaml
student_fast_stage2.yaml
distill_teacher_student.yaml
suites/
ablation_suite.yaml
comparison_suite.yaml
data/
annotations_template.csv
scripts/
build_metadata.py
preprocess_dataset.py
train_stage1_ssl.py
train_stage2.py
train_stage3.py
train_distill.py
eval.py
run_ablation_suite.py
run_comparison_suite.py
build_paper_artifacts.py
publish_checkpoint.py
run_train.sh
launch_preprocess_ui.sh
launch_publish_ui.sh
src/fairfactor/
data/
losses/
models/
train/
utils/
cd /home/ans353/Documents/myproject/fairfactor-temporal-fer
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .
export PYTHONPATH=srcOptional sanity checks:
python -c "import torch; print('torch', torch.__version__)"
python -c "import cv2; print('cv2 ok')"video_id, path, subject_id, emotion_label, ethnicity, gender, age_bucket, split
emotion_code, ethnicity_code, cohort_code, cohort_label
After preprocessing:
processed_path, frames_in, frames_out, face_detected_ratio, quality_blur, quality_brightness, status
If your videos are in data/Recordings with:
data/Recordings/labels.csvdata/Recordings/Coding.txt
then metadata builder automatically:
- parses
XX-YY-ZZ-WWnaming convention, - maps emotion codes to text labels (
happy/sad/angry/surprised/neutral), - maps ethnicity codes to text labels,
- fills required training columns,
- handles slight filename irregularities where possible.
All training/eval configs support:
data:
group_cols: [ethnicity]This controls which metadata columns define fairness groups.
- Ethnicity only:
group_cols: [ethnicity] - Gender only:
group_cols: [gender] - Age only:
group_cols: [age_bucket] - Intersectional:
group_cols: [ethnicity, gender, age_bucket]
Current configs in this repo are set to ethnicity-only.
source .venv/bin/activate
export PYTHONPATH=src
python scripts/build_metadata.py \
--video-root data/Recordings \
--output data/metadata_recordings.csvOptional explicit paths:
python scripts/build_metadata.py \
--video-root data/Recordings \
--labels-csv data/Recordings/labels.csv \
--coding-txt data/Recordings/Coding.txt \
--output data/metadata_recordings.csvpython scripts/build_metadata.py \
--video-root data/raw_videos \
--annotations data/annotations.csv \
--output data/metadata.csvpython scripts/preprocess_dataset.py \
--metadata-csv data/metadata_recordings.csv \
--output-dir data/processed_clips \
--output-metadata data/metadata_recordings_processed.csv \
--target-size 224 \
--target-fps 8 \
--min-face-size 40 \
--bbox-margin 0.2sed -i 's#data/metadata_processed.csv#data/metadata_recordings_processed.csv#g' configs/*.yamlbash scripts/launch_preprocess_ui.shBefore training, ensure your config data.metadata_csv points to the processed metadata CSV.
python scripts/train_stage1_ssl.py --config configs/fairfactor_stage1_ssl.yamlpython scripts/train_stage2.py --config configs/fairfactor_stage2_sup.yamlpython scripts/train_stage3.py --config configs/fairfactor_stage3_groupdro.yamlpython scripts/train_stage2.py --config configs/teacher_heavy_stage2.yamlExpected artifact:
outputs/teacher_stage2/best_stage2.pt
python scripts/train_stage2.py --config configs/student_fast_stage2.yamlExpected artifact:
outputs/student_stage2/best_stage2.pt
Set teacher.checkpoint in configs/distill_teacher_student.yaml to your trained teacher checkpoint, then run:
python scripts/train_distill.py --config configs/distill_teacher_student.yamlExpected artifacts:
outputs/student_distill/best_distill.ptoutputs/student_distill/result_distill.json
# R3D-18 baseline
python scripts/train_stage2.py --config configs/r3d18_stage2.yaml
# R(2+1)D-18 baseline
python scripts/train_stage2.py --config configs/r2plus1d18_stage2.yamlExpected artifacts:
outputs/r3d18_stage2/best_stage2.ptoutputs/r2plus1d18_stage2/best_stage2.pt
bash scripts/run_train.shpython scripts/eval.py \
--config configs/student_fast_stage2.yaml \
--checkpoint outputs/student_stage2/best_stage2.pt \
--split test \
--output outputs/student_stage2/eval_test.jsonpython scripts/eval.py \
--config configs/distill_teacher_student.yaml \
--checkpoint outputs/student_distill/best_distill.pt \
--split test \
--output outputs/student_distill/eval_test.jsonConfig: configs/suites/ablation_suite.yaml
Generate experiment configs only:
python scripts/run_ablation_suite.py \
--suite-config configs/suites/ablation_suite.yamlExecute full ablation training/evaluation suite:
python scripts/run_ablation_suite.py \
--suite-config configs/suites/ablation_suite.yaml \
--executeOutputs:
outputs/ablation/ablation_summary.csvoutputs/ablation/ablation_summary.json
Config: configs/suites/comparison_suite.yaml
This suite compares:
teacher_full,r3d18_baseline_full,r2plus1d18_baseline_full,student_plain,student_distilled.
Before running comparison, make sure all corresponding checkpoints exist:
outputs/teacher_stage2/best_stage2.ptoutputs/r3d18_stage2/best_stage2.ptoutputs/r2plus1d18_stage2/best_stage2.ptoutputs/student_stage2/best_stage2.ptoutputs/student_distill/best_distill.pt
python scripts/run_comparison_suite.py \
--suite-config configs/suites/comparison_suite.yamlOutputs:
outputs/comparison/comparison_summary.csvoutputs/comparison/comparison_summary.json
python scripts/build_paper_artifacts.py \
--comparison-csv outputs/comparison/comparison_summary.csv \
--ablation-csv outputs/ablation/ablation_summary.csv \
--output-dir outputs/paperGenerates:
outputs/paper/table_main.mdoutputs/paper/table_main.texoutputs/paper/table_main.csvoutputs/paper/figure_main_metrics.pngoutputs/paper/figure_efficiency_tradeoff.pngoutputs/paper/figure_ablation_delta.png(if ablation CSV provided)outputs/paper/paper_artifacts_summary.json
python scripts/publish_checkpoint.py \
--checkpoint outputs/student_distill/best_distill.pt \
--model-name fairfactor_student_distilled_v1 \
--config configs/distill_teacher_student.yaml \
--metrics outputs/student_distill/result_distill.json \
--output-root outputs/published \
--license MIT \
--contact you@example.comOptional extras:
- add multiple
--extra-file path/to/file - disable zip with
--no-zip
bash scripts/launch_publish_ui.shPackaging outputs include:
- packaged artifacts directory,
MODEL_CARD.md,manifest.jsonwith SHA256 hashes,- optional
.ziparchive.
Use these in order for a full experiment cycle on Recordings, including SOTA baseline comparison and ablation.
# 0) setup
cd /home/ans353/Documents/myproject/fairfactor-temporal-fer
source .venv/bin/activate
export PYTHONPATH=src
# 1) metadata from Recordings labels/coding
python scripts/build_metadata.py \
--video-root data/Recordings \
--output data/metadata_recordings.csv
# 2) preprocessing
python scripts/preprocess_dataset.py \
--metadata-csv data/metadata_recordings.csv \
--output-dir data/processed_clips \
--output-metadata data/metadata_recordings_processed.csv
# 3) (optional) ssl warmup
python scripts/train_stage1_ssl.py --config configs/fairfactor_stage1_ssl.yaml
# 4) FairFactor stage2 (required for stage3 init checkpoint)
python scripts/train_stage2.py --config configs/fairfactor_stage2_sup.yaml
# 5) FairFactor stage3 GroupDRO
python scripts/train_stage3.py --config configs/fairfactor_stage3_groupdro.yaml
# 6) teacher
python scripts/train_stage2.py --config configs/teacher_heavy_stage2.yaml
# 7) student baseline
python scripts/train_stage2.py --config configs/student_fast_stage2.yaml
# 8) distillation
python scripts/train_distill.py --config configs/distill_teacher_student.yaml
# 9) SOTA baselines for comparison
python scripts/train_stage2.py --config configs/r3d18_stage2.yaml
python scripts/train_stage2.py --config configs/r2plus1d18_stage2.yaml
# 10) evaluate distilled model
python scripts/eval.py \
--config configs/distill_teacher_student.yaml \
--checkpoint outputs/student_distill/best_distill.pt \
--split test \
--output outputs/student_distill/eval_test.json
# 11) ablation suite
python scripts/run_ablation_suite.py \
--suite-config configs/suites/ablation_suite.yaml \
--execute
# 12) comparison suite (includes teacher, student, distilled, R3D-18, R(2+1)D-18)
python scripts/run_comparison_suite.py \
--suite-config configs/suites/comparison_suite.yaml
# 13) paper artifacts
python scripts/build_paper_artifacts.py \
--comparison-csv outputs/comparison/comparison_summary.csv \
--ablation-csv outputs/ablation/ablation_summary.csv \
--output-dir outputs/paper
# 14) publish packaged checkpoint
python scripts/publish_checkpoint.py \
--checkpoint outputs/student_distill/best_distill.pt \
--model-name fairfactor_student_distilled_v1 \
--config configs/distill_teacher_student.yaml \
--metrics outputs/student_distill/result_distill.json \
--output-root outputs/publishedModuleNotFoundError: fairfactor
- Run:
export PYTHONPATH=src(orpip install -e .).
ModuleNotFoundError: cv2
- Install deps:
pip install -e ..
- OOM during training
- Reduce
clip_len,image_size,batch_sizein config.
- Pretrained backbone download issues
- The backbone loader falls back to random init automatically.
- Unknown labels in metadata
- Typically caused by non-coded filenames in
Recordings. - Inspect rows with
emotion_label == unknownin metadata CSV.
If you publish results from this repository, release:
- config files used,
- split metadata CSV,
- best checkpoints,
result_*.jsonfiles,- subgroup metrics and model card.