File size: 3,706 Bytes
b1fe7e3 | 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """Gradio ์น ๋ฐ๋ชจ: Food-101 101๊ฐ ์์ ํด๋์ค ๋ถ๋ฅ๊ธฐ.
์ด ํ์ผ์ Hugging Face Spaces์์ ์๋ ์คํ๋ฉ๋๋ค (app_file: app.py).
์๋ ๋ฐฉ์:
1) ๊ธฐ๋ณธ๊ฐ: ํ๋ธ์ 'nateraw/food' ๊ณต๊ฐ ๋ชจ๋ธ ์ฌ์ฉ
(Pretrained ResNet + Food-101 fine-tuning์ผ๋ก ~90% ์ ํ๋)
2) ์ง์ ํ์ตํ MyResNet์ด ์๋ค๋ฉด ์๋ USE_CUSTOM_RESNET=True๋ก ๋ณ๊ฒฝ
"""
import torch
import torch.nn.functional as F
import gradio as gr
# ============================================================
# ์ค์
# ============================================================
USE_CUSTOM_RESNET = False # True: ์์ฒด MyResNet ์ฌ์ฉ
MODEL_ID = "nateraw/food" # ๋๋ "your-username/my-resnet18-food101"
# ============================================================
# ๋ชจ๋ธ ๋ก๋ฉ
# ============================================================
print(f"๋ชจ๋ธ ๋ก๋ฉ ์ค: {MODEL_ID}")
if USE_CUSTOM_RESNET:
# ์์ฒด ํ์ตํ MyResNet ๋ถ๋ฌ์ค๊ธฐ
from configuration_myresnet import MyResNetConfig
from modeling_myresnet import MyResNetForImageClassification
from torchvision.transforms import Compose, Resize, ToTensor, Normalize
model = MyResNetForImageClassification.from_pretrained(MODEL_ID)
_transform = Compose([
Resize((224, 224)),
ToTensor(),
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
def preprocess(image):
return _transform(image.convert("RGB")).unsqueeze(0)
else:
# ๊ณต๊ฐ ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ (AutoImageProcessor๊ฐ ์ ์ฒ๋ฆฌ ์๋ ์ฒ๋ฆฌ)
from transformers import AutoImageProcessor, AutoModelForImageClassification
processor = AutoImageProcessor.from_pretrained(MODEL_ID)
model = AutoModelForImageClassification.from_pretrained(MODEL_ID)
def preprocess(image):
inputs = processor(images=image.convert("RGB"), return_tensors="pt")
return inputs["pixel_values"]
model.eval()
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
id2label = model.config.id2label
print(f"๋๋ฐ์ด์ค: {device}")
print(f"ํด๋์ค ์: {len(id2label)}")
# ============================================================
# ์์ธก ํจ์
# ============================================================
def classify(image):
"""์ด๋ฏธ์ง๋ฅผ Top-5 ์์ ํด๋์ค๋ก ๋ถ๋ฅํฉ๋๋ค."""
if image is None:
return {}
pixel_values = preprocess(image).to(device)
with torch.no_grad():
logits = model(pixel_values=pixel_values).logits
probs = F.softmax(logits, dim=-1)[0].cpu()
top5_probs, top5_idx = torch.topk(probs, k=5)
return {
id2label[idx.item()].replace("_", " ").title(): float(prob)
for prob, idx in zip(top5_probs, top5_idx)
}
# ============================================================
# Gradio UI
# ============================================================
TITLE = "๐ฝ๏ธ Food Image Classifier"
DESCRIPTION = """
์์ ์ฌ์ง์ ์
๋ก๋ํ๋ฉด **Food-101** ๋ฐ์ดํฐ์
์ 101๊ฐ ์์ ์ค ๊ฐ์ฅ ์ ์ฌํ ๊ฒ์ ์ฐพ์ **Top-5** ๊ฒฐ๊ณผ๋ก ๋ณด์ฌ์ค๋๋ค.
**์ง์ ์์ ์์:** ๐ Pizza ยท ๐ฃ Sushi ยท ๐ Hamburger ยท ๐ฅฉ Steak ยท ๐ฅ Pancakes ยท ๐ Ramen ยท ๐ฆ Ice Cream ยท ๐ฅ Bibimbap ยท ๐ฎ Tacos ยท ๐ฅ Gyoza ยท ...
**๋ชจ๋ธ:** ResNet-18 (Pretrained on ImageNet, Fine-tuned on Food-101)
"""
demo = gr.Interface(
fn=classify,
inputs=gr.Image(type="pil", label="์์ ์ด๋ฏธ์ง ์
๋ก๋"),
outputs=gr.Label(num_top_classes=5, label="์์ธก ๊ฒฐ๊ณผ"),
title=TITLE,
description=DESCRIPTION,
flagging_mode="never",
)
if __name__ == "__main__":
demo.launch()
|