File size: 5,125 Bytes
2720e2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# ── 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()