ResNet_Practice / app.py
dkrak737's picture
Update app.py
d16bf66 verified
# ── Gradio μ„€μΉ˜ ──────────────────────────
import gradio as gr
import torch
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
from datasets import load_dataset
# ── 클래슀 이름 κ°€μ Έμ˜€κΈ° ──────────────────
dataset = load_dataset("food101", split="train[:1%]")
class_names = dataset.features['label'].names
# ── λͺ¨λΈ μ •μ˜ (λ˜‘κ°™μ΄ λΆ™μ—¬λ„£κΈ°) ───────────
class BottleneckBlock(nn.Module):
expansion = 4
def __init__(self, in_channels, mid_channels, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, mid_channels, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(mid_channels)
self.conv2 = nn.Conv2d(mid_channels, mid_channels, kernel_size=3,
stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(mid_channels)
self.conv3 = nn.Conv2d(mid_channels, mid_channels * self.expansion,
kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(mid_channels * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != mid_channels * self.expansion:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, mid_channels * self.expansion,
kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(mid_channels * self.expansion)
)
def forward(self, x):
identity = x
out = self.relu(self.bn1(self.conv1(x)))
out = self.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
out += self.shortcut(identity)
out = self.relu(out)
return out
class ResNet50(nn.Module):
def __init__(self, num_classes=101):
super().__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer( 64, 64, blocks=3, stride=1)
self.layer2 = self._make_layer(256, 128, blocks=4, stride=2)
self.layer3 = self._make_layer(512, 256, blocks=6, stride=2)
self.layer4 = self._make_layer(1024, 512, blocks=3, stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(2048, num_classes)
def _make_layer(self, in_channels, mid_channels, blocks, stride):
layers = [BottleneckBlock(in_channels, mid_channels, stride=stride)]
for _ in range(1, blocks):
layers.append(BottleneckBlock(mid_channels * 4, mid_channels))
return nn.Sequential(*layers)
def forward(self, x):
x = self.maxpool(self.relu(self.bn1(self.conv1(x))))
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
# ── λͺ¨λΈ 뢈러였기 ─────────────────────────
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = ResNet50(num_classes=101)
model.load_state_dict(
torch.load('resnet50_food101.pth', map_location=device)
)
model = model.to(device)
model.eval()
# ── μ „μ²˜λ¦¬ ────────────────────────────────
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
# ── 예츑 ν•¨μˆ˜ ─────────────────────────────
def predict(image):
# PIL 이미지 β†’ ν…μ„œ λ³€ν™˜
img_tensor = transform(image).unsqueeze(0).to(device)
# unsqueeze(0) = (3,224,224) β†’ (1,3,224,224) 배치 차원 μΆ”κ°€
with torch.no_grad():
output = model(img_tensor)
probs = torch.softmax(output, dim=1) # 점수 β†’ ν™•λ₯ 
top5 = probs.topk(5) # μƒμœ„ 5개
# κ²°κ³Ό λ”•μ…”λ„ˆλ¦¬λ‘œ λ°˜ν™˜
result = {}
for i in range(5):
label = class_names[top5.indices[0][i].item()]
prob = top5.values[0][i].item()
result[label] = prob
return result
# ── Gradio μΈν„°νŽ˜μ΄μŠ€ ──────────────────────
demo = gr.Interface(
fn=predict, # 예츑 ν•¨μˆ˜
inputs=gr.Image(type="pil"), # μž…λ ₯: 이미지
outputs=gr.Label(num_top_classes=5), # 좜λ ₯: μƒμœ„ 5개 클래슀
title="πŸ” Food-101 λΆ„λ₯˜κΈ°",
description="μŒμ‹ 사진을 올리면 μ–΄λ–€ μŒμ‹μΈμ§€ λ§žμΆ°μ€˜μš”! (101κ°€μ§€ μŒμ‹ λΆ„λ₯˜)",
examples=[], # μ˜ˆμ‹œ 이미지 (있으면 μΆ”κ°€)
)
demo.launch()