ivantv commited on
Commit
f036844
·
verified ·
1 Parent(s): 9340038

Minimal bare-bones version

Browse files
Files changed (1) hide show
  1. app.py +26 -109
app.py CHANGED
@@ -1,8 +1,3 @@
1
- """
2
- CIFAR-100 Image Classification - Hugging Face Space (Fixed)
3
- Uses model from: https://huggingface.co/ivantv/cifar100-resnet18
4
- """
5
-
6
  import torch
7
  import torch.nn as nn
8
  from PIL import Image
@@ -10,7 +5,6 @@ import torchvision.transforms as transforms
10
  import gradio as gr
11
  from huggingface_hub import hf_hub_download
12
 
13
- # CIFAR-100 class names
14
  CIFAR100_CLASSES = [
15
  'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
16
  'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
@@ -28,31 +22,20 @@ CIFAR100_CLASSES = [
28
  'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman', 'worm'
29
  ]
30
 
31
- print("=" * 60)
32
- print("Loading CIFAR-100 Classification Model")
33
- print("=" * 60)
34
-
35
- # Define model architecture
36
  class BasicBlock(nn.Module):
37
  expansion = 1
38
-
39
  def __init__(self, in_channels, out_channels, stride=1):
40
  super(BasicBlock, self).__init__()
41
- self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3,
42
- stride=stride, padding=1, bias=False)
43
  self.bn1 = nn.BatchNorm2d(out_channels)
44
- self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3,
45
- stride=1, padding=1, bias=False)
46
  self.bn2 = nn.BatchNorm2d(out_channels)
47
-
48
  self.shortcut = nn.Sequential()
49
  if stride != 1 or in_channels != self.expansion * out_channels:
50
  self.shortcut = nn.Sequential(
51
- nn.Conv2d(in_channels, self.expansion * out_channels,
52
- kernel_size=1, stride=stride, bias=False),
53
  nn.BatchNorm2d(self.expansion * out_channels)
54
  )
55
-
56
  def forward(self, x):
57
  out = torch.relu(self.bn1(self.conv1(x)))
58
  out = self.bn2(self.conv2(out))
@@ -60,12 +43,10 @@ class BasicBlock(nn.Module):
60
  out = torch.relu(out)
61
  return out
62
 
63
-
64
  class ResNet(nn.Module):
65
  def __init__(self, block, num_blocks, num_classes=100):
66
  super(ResNet, self).__init__()
67
  self.in_channels = 64
68
-
69
  self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
70
  self.bn1 = nn.BatchNorm2d(64)
71
  self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
@@ -73,7 +54,6 @@ class ResNet(nn.Module):
73
  self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
74
  self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
75
  self.linear = nn.Linear(512 * block.expansion, num_classes)
76
-
77
  def _make_layer(self, block, out_channels, num_blocks, stride):
78
  strides = [stride] + [1] * (num_blocks - 1)
79
  layers = []
@@ -81,7 +61,6 @@ class ResNet(nn.Module):
81
  layers.append(block(self.in_channels, out_channels, stride))
82
  self.in_channels = out_channels * block.expansion
83
  return nn.Sequential(*layers)
84
-
85
  def forward(self, x):
86
  out = torch.relu(self.bn1(self.conv1(x)))
87
  out = self.layer1(out)
@@ -93,108 +72,46 @@ class ResNet(nn.Module):
93
  out = self.linear(out)
94
  return out
95
 
96
-
97
  def ResNet18():
98
  return ResNet(BasicBlock, [2, 2, 2, 2])
99
 
100
-
101
- # Load model
102
- print("📥 Downloading model from Hugging Face Hub...")
103
- model_path = hf_hub_download(
104
- repo_id="ivantv/cifar100-resnet18",
105
- filename="best_model.pth"
106
- )
107
-
108
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
109
- print(f"💻 Using device: {device}")
110
-
111
  model = ResNet18().to(device)
112
  model.load_state_dict(torch.load(model_path, map_location=device))
113
  model.eval()
114
- print("Model loaded successfully!")
115
- print("=" * 60)
116
 
117
- # Define image transforms
118
  transform = transforms.Compose([
119
  transforms.Resize((32, 32)),
120
  transforms.ToTensor(),
121
  transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))
122
  ])
123
 
124
-
125
- def predict_image(image):
126
- """Make prediction on uploaded image"""
127
  if image is None:
128
- return None
129
-
130
- try:
131
- # Convert to PIL Image if needed
132
- if not isinstance(image, Image.Image):
133
- image = Image.fromarray(image)
134
-
135
- # Convert to RGB if needed
136
- if image.mode != 'RGB':
137
- image = image.convert('RGB')
138
-
139
- # Preprocess image
140
- image_tensor = transform(image).unsqueeze(0).to(device)
141
-
142
- # Make prediction
143
- with torch.no_grad():
144
- output = model(image_tensor)
145
- probabilities = torch.nn.functional.softmax(output, dim=1)
146
-
147
- # Get top-5 predictions
148
- top_probs, top_indices = torch.topk(probabilities[0], 5)
149
-
150
- # Prepare results
151
- top_probs = top_probs.cpu().numpy()
152
- top_indices = top_indices.cpu().numpy()
153
-
154
- # Create predictions dictionary for Gradio
155
- predictions = {}
156
- for prob, idx in zip(top_probs, top_indices):
157
- class_name = CIFAR100_CLASSES[idx]
158
- predictions[class_name] = float(prob)
159
-
160
- return predictions
161
-
162
- except Exception as e:
163
- print(f"Error during prediction: {e}")
164
- return {"error": f"Prediction failed: {str(e)}"}
165
-
166
-
167
- # Create simple Gradio interface (avoiding the gr.Blocks bug)
168
- demo = gr.Interface(
169
- fn=predict_image,
170
- inputs=gr.Image(type="pil", label="Upload Image"),
171
- outputs=gr.Label(num_top_classes=5, label="Top-5 Predictions"),
172
- title="🖼️ CIFAR-100 Image Classification",
173
- description="""
174
- Upload an image to classify it into one of **100 CIFAR-100 categories**!
175
-
176
- **Model:** [ivantv/cifar100-resnet18](https://huggingface.co/ivantv/cifar100-resnet18)
177
- **Architecture:** ResNet-18 (11.2M parameters)
178
- **Test Accuracy:** 75.84%
179
-
180
- The model can recognize 100 categories including animals, vehicles, household objects, plants, and more.
181
- """,
182
- article="""
183
- ### About the Model
184
-
185
- This ResNet-18 model was trained on the CIFAR-100 dataset with 50 epochs of training, achieving 75.84% test accuracy.
186
 
187
- **Categories include:**
188
- - 🐾 Animals: bear, tiger, elephant, dolphin, fox, lion, wolf, etc.
189
- - 🚗 Vehicles: bicycle, bus, train, motorcycle, pickup_truck, etc.
190
- - 🏠 Objects: chair, table, lamp, clock, keyboard, etc.
191
- - 🌳 Plants: maple_tree, oak_tree, rose, tulip, orchid, etc.
192
 
193
- **Tips:** Use clear, well-lit images with simple backgrounds for best results.
194
- """,
195
- theme="soft"
 
 
 
 
 
196
  )
197
 
198
- if __name__ == "__main__":
199
- demo.launch()
200
 
 
 
 
 
 
 
1
  import torch
2
  import torch.nn as nn
3
  from PIL import Image
 
5
  import gradio as gr
6
  from huggingface_hub import hf_hub_download
7
 
 
8
  CIFAR100_CLASSES = [
9
  'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
10
  'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
 
22
  'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman', 'worm'
23
  ]
24
 
 
 
 
 
 
25
  class BasicBlock(nn.Module):
26
  expansion = 1
 
27
  def __init__(self, in_channels, out_channels, stride=1):
28
  super(BasicBlock, self).__init__()
29
+ self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
 
30
  self.bn1 = nn.BatchNorm2d(out_channels)
31
+ self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
 
32
  self.bn2 = nn.BatchNorm2d(out_channels)
 
33
  self.shortcut = nn.Sequential()
34
  if stride != 1 or in_channels != self.expansion * out_channels:
35
  self.shortcut = nn.Sequential(
36
+ nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
 
37
  nn.BatchNorm2d(self.expansion * out_channels)
38
  )
 
39
  def forward(self, x):
40
  out = torch.relu(self.bn1(self.conv1(x)))
41
  out = self.bn2(self.conv2(out))
 
43
  out = torch.relu(out)
44
  return out
45
 
 
46
  class ResNet(nn.Module):
47
  def __init__(self, block, num_blocks, num_classes=100):
48
  super(ResNet, self).__init__()
49
  self.in_channels = 64
 
50
  self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
51
  self.bn1 = nn.BatchNorm2d(64)
52
  self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
 
54
  self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
55
  self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
56
  self.linear = nn.Linear(512 * block.expansion, num_classes)
 
57
  def _make_layer(self, block, out_channels, num_blocks, stride):
58
  strides = [stride] + [1] * (num_blocks - 1)
59
  layers = []
 
61
  layers.append(block(self.in_channels, out_channels, stride))
62
  self.in_channels = out_channels * block.expansion
63
  return nn.Sequential(*layers)
 
64
  def forward(self, x):
65
  out = torch.relu(self.bn1(self.conv1(x)))
66
  out = self.layer1(out)
 
72
  out = self.linear(out)
73
  return out
74
 
 
75
  def ResNet18():
76
  return ResNet(BasicBlock, [2, 2, 2, 2])
77
 
78
+ print("Loading model...")
79
+ model_path = hf_hub_download(repo_id="ivantv/cifar100-resnet18", filename="best_model.pth")
 
 
 
 
 
 
80
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
 
81
  model = ResNet18().to(device)
82
  model.load_state_dict(torch.load(model_path, map_location=device))
83
  model.eval()
84
+ print("Model loaded!")
 
85
 
 
86
  transform = transforms.Compose([
87
  transforms.Resize((32, 32)),
88
  transforms.ToTensor(),
89
  transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))
90
  ])
91
 
92
+ def classify_image(image):
 
 
93
  if image is None:
94
+ return {}
95
+ if not isinstance(image, Image.Image):
96
+ image = Image.fromarray(image)
97
+ if image.mode != 'RGB':
98
+ image = image.convert('RGB')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ img_tensor = transform(image).unsqueeze(0).to(device)
101
+ with torch.no_grad():
102
+ output = model(img_tensor)
103
+ probs = torch.nn.functional.softmax(output, dim=1)[0]
104
+ top5_prob, top5_idx = torch.topk(probs, 5)
105
 
106
+ return {CIFAR100_CLASSES[idx]: prob.item() for prob, idx in zip(top5_prob, top5_idx)}
107
+
108
+ iface = gr.Interface(
109
+ fn=classify_image,
110
+ inputs=gr.Image(type="pil"),
111
+ outputs=gr.Label(num_top_classes=5),
112
+ title="CIFAR-100 Classifier",
113
+ description="ResNet-18 model trained on CIFAR-100 (75.84% accuracy)"
114
  )
115
 
116
+ iface.launch()
 
117