Personal Color Classifier (ํผ์ค๋์ปฌ๋ฌ ๋ถ๋ฅ๊ธฐ)
ํ๊ตญ์ธ ์ผ๊ตด ์ด๋ฏธ์ง์์ ํผ์ค๋์ปฌ๋ฌ 4๊ณ์ ์ ํ์ ๋ถ๋ฅํ๋ EfficientNet-B0 ๊ธฐ๋ฐ ๋ชจ๋ธ์
๋๋ค.
๋ชจ๋ธ ์ฑ๋ฅ
| Metric |
Score |
| Test Accuracy (TTA) |
70.8% |
| Test Accuracy (๋จ์ผ ์ถ๋ก ) |
68.3% |
| Macro F1 |
0.71 |
ํด๋์ค๋ณ ์ฑ๋ฅ (TTA ๊ธฐ์ค)
| ํด๋์ค |
Precision |
Recall |
F1 |
| spring_warm (๋ด์) |
0.69 |
0.80 |
0.74 |
| summer_cool (์ฌ์ฟจ) |
0.97 |
0.84 |
0.90 |
| autumn_warm (๊ฐ์์) |
0.54 |
0.58 |
0.56 |
| winter_cool (๊ฒจ์ธ์ฟจ) |
0.68 |
0.61 |
0.64 |
ํ์ต ์ ๋ต
ImageNet pretrained EfficientNet-B0
โ Deep Armocromia base training (์์ค ๋๋ฉ์ธ: ์ ๋ฝ์ธ ์ผ๊ตด, ~4,000์ฅ)
โ Korean celebrity fine-tuning (ํ๊ฒ ๋๋ฉ์ธ: ํ๊ตญ ์
๋ฝ, ~2,368์ฅ)
- ์ ์ฒ๋ฆฌ: MTCNN ์ผ๊ตด ๊ฒ์ถ โ BiSeNet ํผ๋ถ ๋ง์คํน (๋จธ๋ฆฌ์นด๋ฝยท๋ยท์
์ ์ ๊ฑฐ)
- ์ ๊ทํ: Mixup (ฮฑ=0.2), Label smoothing (0.1), Strong augmentation
- TTA: 6-crop ํ๊ท (center crop + horizontal flip ร 3 scale)
ํ์ผ ๊ตฌ์ฑ
| ํ์ผ |
์ค๋ช
|
personal_color_korean_tuned_v2.pt |
๋ฉ์ธ ๋ชจ๋ธ โ ์ต๊ณ ์ฑ๋ฅ fine-tuned ์ฒดํฌํฌ์ธํธ |
deep_armocromia_efficientnet_b0_base_rgb.pt |
Base ์ฒดํฌํฌ์ธํธ (Deep Armocromia ํ์ต) |
src/infer_personal_color.py |
๋จ์ผ ์ด๋ฏธ์ง ์ถ๋ก ์คํฌ๋ฆฝํธ |
src/evaluate_personal_color.py |
ํ
์คํธ์
ํ๊ฐ ์คํฌ๋ฆฝํธ |
PROGRESS_SNAPSHOT.md |
์ ์ฒด ์คํ ๊ธฐ๋ก ์์ฝ |
์ฌ์ฉ ๋ฐฉ๋ฒ
์ค์น
pip install timm torch torchvision huggingface_hub
๋น ๋ฅธ ์ถ๋ก
import torch
import timm
from PIL import Image
from torchvision import transforms
from huggingface_hub import hf_hub_download
LABEL_ORDER = ["spring_warm", "summer_cool", "autumn_warm", "winter_cool"]
LABEL_KO = ["๋ด์", "์ฌ์ฟจ", "๊ฐ์์", "๊ฒจ์ธ์ฟจ"]
ckpt_path = hf_hub_download(
"jiwoonkim00/personal-color-classifier",
"personal_color_korean_tuned_v2.pt"
)
model = timm.create_model("efficientnet_b0.ra_in1k", pretrained=False, num_classes=4)
ckpt = torch.load(ckpt_path, map_location="cpu")
model.load_state_dict(ckpt["model_state_dict"])
model.eval()
tf = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
img = Image.open("face.jpg").convert("RGB")
x = tf(img).unsqueeze(0)
with torch.no_grad():
probs = torch.softmax(model(x), dim=1).squeeze()
pred = probs.argmax().item()
print(f"์์ธก: {LABEL_KO[pred]} ({LABEL_ORDER[pred]})")
print(f"ํ์ ๋: {probs[pred]:.1%}")
์ถ๋ก ์คํฌ๋ฆฝํธ ์ฌ์ฉ (์ผ๊ตด ์๋ ๊ฒ์ถ ํฌํจ)
pip install facenet-pytorch opencv-python-headless
python src/infer_personal_color.py \
--image path/to/image.jpg \
--checkpoint personal_color_korean_tuned_v2.pt
์ถ๋ ฅ ์์:
์์ธก ๊ฒฐ๊ณผ: ์ฌ์ฟจ (summer_cool)
ํ์ ๋: 0.84 (confident)
์ ์ฒด ํ๋ฅ :
๋ด์ (spring_warm): 0.06
์ฌ์ฟจ (summer_cool): 0.84 โ ์์ธก
๊ฐ์์ (autumn_warm): 0.04
๊ฒจ์ธ์ฟจ (winter_cool): 0.06
ํ์ ๋ ์ ์ฑ
| ํ์ ๋ |
๋ฑ๊ธ |
์ฒ๋ฆฌ |
| โฅ 0.70 |
confident |
๊ฒฐ๊ณผ ์ ๋ขฐ |
| 0.55 ~ 0.70 |
moderate |
์ฐธ๊ณ ์์ค |
| < 0.55 |
low |
์ฌ์ดฌ์ ๋๋ ์๋ ๋ณด์ ๊ถ์ฅ |
ํ๊ณ ๋ฐ ์ฃผ์์ฌํญ
- ํ๊ตญ ์
๋ฝ ์คํฌ๋ฆฐ์ท ๊ธฐ๋ฐ ๋ฐ์ดํฐ โ ๋ฉ์ดํฌ์
ยท์กฐ๋ช
ยทํธ์ง ํธํฅ ์กด์ฌ
- ๊ฐ์์(autumn_warm) โ ๋ด์(spring_warm) ํผ๋์ด ๊ฐ์ฅ ์ฆ์ (๋ ๋ค ์ํค)
- ํ๋กํ ํ์
/ํฌํธํด๋ฆฌ์ค ์คํ ๋ชฉ์ ์ผ๋ก ๊ฐ๋ฐ๋จ
- ์์ยท์ ๋ฌธ๊ฐ ์์ค์ ํผ์ค๋์ปฌ๋ฌ ์ง๋จ ๋๊ตฌ๋ก ์ฌ์ฉ ๋ถ๊ฐ
- ์ ๋ฌธ ์ง๋จ์ด ํ์ํ ๊ฒฝ์ฐ ์๊ฒฉ์ ๊ฐ์ถ ํผ์ค๋์ปฌ๋ฌ ์ ๋ฌธ๊ฐ์๊ฒ ๋ฌธ์ํ์ธ์
๋ฐ์ดํฐ์
| ๋ฐ์ดํฐ์
|
์ฉ๋ |
์ด๋ฏธ์ง ์ |
| Korean Celebrity (์์ฒด ์์ง) |
Fine-tuning |
~2,368์ฅ |
| Deep Armocromia |
Base training |
~4,920์ฅ |
๋ชจ๋ธ ์ํคํ
์ฒ
- ๋ฐฑ๋ณธ:
efficientnet_b0.ra_in1k (timm)
- ํ๋ผ๋ฏธํฐ: ์ฝ 5.3M
- ์
๋ ฅ: 224 ร 224 RGB (ํผ๋ถ ๋ง์คํน ์ ์ฉ ์ผ๊ตด ์ด๋ฏธ์ง)
- ์ถ๋ ฅ: 4-class softmax