File size: 2,968 Bytes
23dcffe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
---
license: mit
tags:
  - image-classification
  - multi-label-image-classification
  - pytorch
  - timm
  - dermatology
  - acne
library_name: timm
pipeline_tag: image-classification
---

# 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

```python
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.