Multi-Label Acne Type Classifier (efficientnet_b0)

Predicts which of five acne lesion types are present in a full-face photograph. This is a multi-label presence task: it does not count lesions and does not localise them.

Classes: Whitehead, Blackhead, Papule, Pustule, Nodule

Intended use and limitations

Research and educational use only. This model is not a medical device and must not be used for diagnosis or treatment decisions. It was trained on a limited academic dataset and has not been clinically validated. Performance across skin tones, ages, lighting conditions and camera hardware has not been audited and is very likely uneven; treat any output on under-represented skin tones as unreliable.

Architecture

  • Backbone: efficientnet_b0 (timm, ImageNet-pretrained)
  • Head: single linear layer, 5 logits, sigmoid activation at inference
  • Input: 320 x 320 RGB, ImageNet normalisation
  • Loss: BCEWithLogitsLoss with per-class pos_weight
  • Optimiser: AdamW, warmup + cosine decay, mixed precision
  • Per-class decision thresholds tuned on validation (see thresholds.json)

Training data

3,231 train / 693 validation / 693 test images, multi-label stratified. Sources include the public Kaggle dataset tiswan14/acne-dataset-image plus project-specific annotations.

Test-set results

Metric Value
Subset (exact-match) accuracy 0.9798
Label-wise accuracy 0.9951
Macro F1 0.9889
Micro F1 0.9877
Macro ROC-AUC 0.9993
Macro Average Precision 0.9978
Class Support Precision Recall F1 ROC-AUC
Whitehead 45 1.000 1.000 1.000 1.000
Blackhead 186 1.000 0.995 0.997 1.000
Papule 155 0.987 0.955 0.971 0.998
Pustule 151 0.974 0.980 0.977 0.998
Nodule 156 1.000 1.000 1.000 1.000

Usage

import torch, timm, numpy as np, cv2
from huggingface_hub import hf_hub_download

ckpt_path = hf_hub_download("charuka0/acne-multilabel-classifier", "best_model.pth")
ckpt = torch.load(ckpt_path, map_location="cpu", weights_only=False)

model = timm.create_model(ckpt["backbone"], pretrained=False, num_classes=5)
model.load_state_dict(ckpt["model_state_dict"]); model.eval()

img = cv2.cvtColor(cv2.imread("face.jpg"), cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (ckpt["img_size"], ckpt["img_size"])) / 255.0
img = (img - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]
x = torch.tensor(img).permute(2, 0, 1).unsqueeze(0).float()

probs = torch.sigmoid(model(x))[0].detach().numpy()
present = probs >= np.array(ckpt["thresholds"])
print(dict(zip(ckpt["classes"], zip(probs.round(3), present))))

Citation

Final Year Research Project, 2026. Trained on Kaggle free-tier GPU.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support