sapbot commited on
Commit
016eee7
Β·
verified Β·
1 Parent(s): d1d221e

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +41 -0
  2. mnist_1k_best.pth +3 -0
  3. train.py +242 -0
README.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language: en
3
+ license: cc0-1.0
4
+ tags:
5
+ - mnist
6
+ - tiny-model
7
+ - early-stopping
8
+ ---
9
+
10
+ # Tiny MNIST Classifier
11
+
12
+ - **Parameters**: 970 (<1000)
13
+ - **Test accuracy**: 92.35%
14
+ - **Epochs trained**: 45 (early stopping after 5 epochs without improvement)
15
+
16
+ This model was trained on RX 6600.
17
+
18
+ ## Full results
19
+
20
+ | Metric | Value |
21
+ |---------------------------|-----------------|
22
+ | Total parameters | 970 |
23
+ | Best validation loss | 0.2463 |
24
+ | Final test accuracy | 92.35% |
25
+ | Early stopping patience | 5 |
26
+ | Training epochs | 45 |
27
+
28
+ ## Model architecture
29
+
30
+ AvgPool(4x4) β†’ Linear(49β†’16) β†’ ReLU β†’ Dropout(0.2) β†’ Linear(16β†’10)
31
+
32
+ ## How to use
33
+
34
+ ```python
35
+ import torch
36
+ from train import TinyMNISTModel
37
+
38
+ model = TinyMNISTModel()
39
+ model.load_state_dict(torch.load("mnist_1k_best.pth"))
40
+ model.eval()
41
+ ```
mnist_1k_best.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dc2c283e0d10a5ea7104d0cb15ab5db0574c20befe0e811003edb7787a5c37af
3
+ size 6016
train.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ train_mnist_1k_tqdm.py
3
+
4
+ Trains a tiny MNIST model (<1000 params) until convergence,
5
+ using tqdm progress bars and early stopping.
6
+ """
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.optim as optim
11
+ import torchvision
12
+ import torchvision.transforms as transforms
13
+ from torch.utils.data import DataLoader, random_split
14
+ from tqdm import tqdm
15
+ import numpy as np
16
+ import os
17
+ import sys
18
+
19
+ # -------------------------------
20
+ # 0. Automatic device fallback
21
+ # -------------------------------
22
+ def get_device():
23
+ if torch.cuda.is_available():
24
+ try:
25
+ test_tensor = torch.randn(1, 1, 28, 28).cuda()
26
+ _ = torch.nn.functional.avg_pool2d(test_tensor, 4)
27
+ return torch.device('cuda')
28
+ except Exception as e:
29
+ print(f"GPU error: {e}\nFalling back to CPU.")
30
+ return torch.device('cpu')
31
+ return torch.device('cpu')
32
+
33
+ device = get_device()
34
+ print(f"Using device: {device}")
35
+
36
+ # -------------------------------
37
+ # 1. Model (970 parameters)
38
+ # -------------------------------
39
+ class TinyMNISTModel(nn.Module):
40
+ def __init__(self):
41
+ super().__init__()
42
+ self.pool = nn.AvgPool2d(4, 4)
43
+ self.fc1 = nn.Linear(7*7, 16)
44
+ self.relu = nn.ReLU()
45
+ self.dropout = nn.Dropout(0.2)
46
+ self.fc2 = nn.Linear(16, 10)
47
+
48
+ def forward(self, x):
49
+ x = self.pool(x)
50
+ x = x.view(x.size(0), -1)
51
+ x = self.fc1(x)
52
+ x = self.relu(x)
53
+ x = self.dropout(x)
54
+ x = self.fc2(x)
55
+ return x
56
+
57
+ # -------------------------------
58
+ # 2. Data
59
+ # -------------------------------
60
+ transform = transforms.Compose([
61
+ transforms.ToTensor(),
62
+ transforms.Normalize((0.1307,), (0.3081,))
63
+ ])
64
+
65
+ full_train = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
66
+ test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
67
+
68
+ # Split 90% train, 10% validation
69
+ val_size = int(0.1 * len(full_train))
70
+ train_size = len(full_train) - val_size
71
+ train_dataset, val_dataset = random_split(full_train, [train_size, val_size])
72
+
73
+ batch_size = 64
74
+ train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
75
+ val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
76
+ test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
77
+
78
+ # -------------------------------
79
+ # 3. Training with early stopping + tqdm
80
+ # -------------------------------
81
+ model = TinyMNISTModel().to(device)
82
+ criterion = nn.CrossEntropyLoss()
83
+ optimizer = optim.Adam(model.parameters(), lr=0.001)
84
+
85
+ patience = 5
86
+ best_val_loss = float('inf')
87
+ epochs_no_improve = 0
88
+ best_model_state = None
89
+
90
+ print("\nπŸ‹οΈ Training until convergence (early stopping patience = 5)\n")
91
+
92
+ epoch = 0
93
+ while True:
94
+ # Training phase with tqdm
95
+ model.train()
96
+ train_loss = 0.0
97
+ train_bar = tqdm(train_loader, desc=f"Epoch {epoch+1} [Train]", leave=False)
98
+ for images, labels in train_bar:
99
+ images, labels = images.to(device), labels.to(device)
100
+ optimizer.zero_grad()
101
+ outputs = model(images)
102
+ loss = criterion(outputs, labels)
103
+ loss.backward()
104
+ optimizer.step()
105
+ train_loss += loss.item()
106
+ train_bar.set_postfix(loss=loss.item())
107
+ train_loss /= len(train_loader)
108
+
109
+ # Validation phase
110
+ model.eval()
111
+ val_loss = 0.0
112
+ correct = 0
113
+ total = 0
114
+ val_bar = tqdm(val_loader, desc=f"Epoch {epoch+1} [Val]", leave=False)
115
+ with torch.no_grad():
116
+ for images, labels in val_bar:
117
+ images, labels = images.to(device), labels.to(device)
118
+ outputs = model(images)
119
+ loss = criterion(outputs, labels)
120
+ val_loss += loss.item()
121
+ _, pred = torch.max(outputs, 1)
122
+ total += labels.size(0)
123
+ correct += (pred == labels).sum().item()
124
+ val_bar.set_postfix(loss=loss.item())
125
+ val_loss /= len(val_loader)
126
+ val_acc = 100.0 * correct / total
127
+
128
+ # Print progress line (outside tqdm to keep clean)
129
+ print(f"Epoch {epoch+1:3d} | Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.2f}%")
130
+
131
+ # Early stopping logic
132
+ if val_loss < best_val_loss:
133
+ best_val_loss = val_loss
134
+ epochs_no_improve = 0
135
+ best_model_state = model.state_dict().copy()
136
+ else:
137
+ epochs_no_improve += 1
138
+ if epochs_no_improve >= patience:
139
+ print(f"\nπŸ›‘ Early stopping after {epoch+1} epochs (no improvement for {patience} epochs).")
140
+ break
141
+
142
+ epoch += 1
143
+
144
+ # Restore best model
145
+ model.load_state_dict(best_model_state)
146
+
147
+ # -------------------------------
148
+ # 4. Final evaluation on full test set
149
+ # -------------------------------
150
+ def evaluate(loader, name="Test"):
151
+ model.eval()
152
+ correct = 0
153
+ total = 0
154
+ with torch.no_grad():
155
+ for images, labels in tqdm(loader, desc=f"Evaluating on {name}", leave=False):
156
+ images, labels = images.to(device), labels.to(device)
157
+ outputs = model(images)
158
+ _, pred = torch.max(outputs, 1)
159
+ total += labels.size(0)
160
+ correct += (pred == labels).sum().item()
161
+ acc = 100.0 * correct / total
162
+ print(f"{name} accuracy: {acc:.2f}%")
163
+ return acc
164
+
165
+ test_acc = evaluate(test_loader, "full test set")
166
+ total_params = sum(p.numel() for p in model.parameters())
167
+
168
+ # -------------------------------
169
+ # 5. TL;DR summary
170
+ # -------------------------------
171
+ tldr = f"""
172
+ ╔══════════════════════════════════════════════════════════╗
173
+ β•‘ TL;DR – Tiny MNIST β•‘
174
+ ╠══════════════════════════════════════════════════════════╣
175
+ β•‘ Parameters: {total_params:<48}β•‘
176
+ β•‘ Training epochs until convergence: {epoch+1:<31}β•‘
177
+ β•‘ Best validation loss: {best_val_loss:.4f}<40 spaces>β•‘ -- actually align manually
178
+ β•‘ Final test accuracy: {test_acc:.2f}%<39 spaces>β•‘
179
+ β•‘ Early stopping patience: {patience} epochs<36 spaces>β•‘
180
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
181
+ """
182
+ print(tldr)
183
+
184
+ # Save model
185
+ torch.save(model.state_dict(), "mnist_1k_best.pth")
186
+
187
+ # -------------------------------
188
+ # 6. Generate README.md (HF style)
189
+ # -------------------------------
190
+ readme_content = f"""---
191
+ language: en
192
+ license: apache-2.0
193
+ tags:
194
+ - mnist
195
+ - tiny-model
196
+ - tqdm
197
+ - early-stopping
198
+ ---
199
+
200
+ # Tiny MNIST Classifier – with tqdm progress bars
201
+
202
+ - **Parameters**: {total_params} (<1000)
203
+ - **Test accuracy**: {test_acc:.2f}%
204
+ - **Epochs trained**: {epoch+1} (early stopping after {patience} epochs without improvement)
205
+
206
+ This script trains until convergence and shows **tqdm** progress bars for each batch.
207
+
208
+ ## TL;DR
209
+
210
+ ```bash
211
+ python train_mnist_1k_tqdm.py
212
+ ```
213
+
214
+ ## Full results
215
+
216
+ | Metric | Value |
217
+ |---------------------------|-----------------|
218
+ | Total parameters | {total_params} |
219
+ | Best validation loss | {best_val_loss:.4f} |
220
+ | Final test accuracy | {test_acc:.2f}% |
221
+ | Early stopping patience | {patience} |
222
+ | Training epochs | {epoch+1} |
223
+
224
+ ## Model architecture
225
+
226
+ AvgPool(4x4) β†’ Linear(49β†’16) β†’ ReLU β†’ Dropout(0.2) β†’ Linear(16β†’10)
227
+
228
+ ## How to use
229
+
230
+ ```python
231
+ import torch
232
+ from train_mnist_1k_tqdm import TinyMNISTModel
233
+
234
+ model = TinyMNISTModel()
235
+ model.load_state_dict(torch.load("mnist_1k_best.pth"))
236
+ model.eval()
237
+ ```
238
+ """
239
+ with open("README.md", "w") as f:
240
+ f.write(readme_content)
241
+
242
+ print("βœ… README.md generated. Model saved as mnist_1k_best.pth")