প্রজেক্ট — MNIST classifier
এই পাঠে যা শিখবেন
- PyTorch DataLoader — efficient batched data loading
- CNN architecture — Conv → Pool → FC structure
- Training loop — forward, loss, backward, optimizer step
- Validation/test evaluation — accuracy, confusion matrix
- Saving/loading model — production-ready
- Common issues — overfitting, slow training, GPU underuse
১ · Project overview
MNIST — DL-এর "Hello World"। LeCun-এর ১৯৯৮ paper-এর dataset। ৬০,০০০ training + ১০,০০০ test image, ২৮×২৮ grayscale, ১০ class (digits 0-9)।
আমরা একটি ছোট CNN train করব — target ৯৯%+ accuracy। এই lesson আপনার পুরো কোর্সের knowledge integrate করবে।
ছোট, fast train, well-understood। নতুন architecture/idea test করার জন্য standard। আজও research paper-এ baseline। Production deep learning-এর entry point।
২ · Setup ও DataLoader
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# Reproducibility
torch.manual_seed(42)
# Device
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
# Transforms — normalize MNIST mean/std
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)),
])
# Datasets — auto-download
train_set = datasets.MNIST(root='./data', train=True,
download=True, transform=transform)
test_set = datasets.MNIST(root='./data', train=False,
download=True, transform=transform)
print(f"Train: {len(train_set)}, Test: {len(test_set)}")
print(f"Image shape: {train_set[0][0].shape}") # (1, 28, 28)
print(f"Classes: {train_set.classes}")
# DataLoaders
BATCH = 128
train_loader = DataLoader(train_set, batch_size=BATCH, shuffle=True,
num_workers=2, pin_memory=True)
test_loader = DataLoader(test_set, batch_size=BATCH, shuffle=False,
num_workers=2, pin_memory=True)
pin_memory=True — GPU transfer faster। num_workers=2 — parallel data loading।
৩ · CNN model
class MNIST_CNN(nn.Module):
def __init__(self, n_classes=10):
super().__init__()
# Conv block 1: 1→32, 28→14
self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(32)
# Conv block 2: 32→64, 14→7
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(64)
# FC head
self.fc1 = nn.Linear(64 * 7 * 7, 128)
self.fc2 = nn.Linear(128, n_classes)
self.dropout = nn.Dropout(0.3)
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = F.max_pool2d(x, 2) # 28→14
x = F.relu(self.bn2(self.conv2(x)))
x = F.max_pool2d(x, 2) # 14→7
x = x.view(x.size(0), -1) # flatten
x = F.relu(self.fc1(x))
x = self.dropout(x)
return self.fc2(x) # logits
model = MNIST_CNN().to(device)
print(model)
print(f"Params: {sum(p.numel() for p in model.parameters())/1e3:.1f}K")
~৪২২K parameter — small but effective। Conv → BN → ReLU → Pool — classic CNN block।
৪ · Training loop
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.5)
def train_epoch(model, loader, opt, crit):
model.train()
total, correct, loss_sum = 0, 0, 0.0
for x, y in loader:
x, y = x.to(device), y.to(device)
opt.zero_grad()
out = model(x)
loss = crit(out, y)
loss.backward()
opt.step()
loss_sum += loss.item() * x.size(0)
correct += (out.argmax(1) == y).sum().item()
total += x.size(0)
return loss_sum / total, correct / total
@torch.no_grad()
def evaluate(model, loader, crit):
model.eval()
total, correct, loss_sum = 0, 0, 0.0
for x, y in loader:
x, y = x.to(device), y.to(device)
out = model(x)
loss = crit(out, y)
loss_sum += loss.item() * x.size(0)
correct += (out.argmax(1) == y).sum().item()
total += x.size(0)
return loss_sum / total, correct / total
EPOCHS = 10
for epoch in range(1, EPOCHS + 1):
tr_loss, tr_acc = train_epoch(model, train_loader, optimizer, criterion)
te_loss, te_acc = evaluate(model, test_loader, criterion)
scheduler.step()
print(f"Epoch {epoch:2d} | "
f"train loss {tr_loss:.4f} acc {tr_acc:.4f} | "
f"test loss {te_loss:.4f} acc {te_acc:.4f}")
১০ epoch — Colab T4 GPU-তে ~৩-৪ মিনিট। Test accuracy ~৯৯.২%।
৫ · Confusion matrix ও per-class accuracy
import numpy as np
@torch.no_grad()
def confusion_matrix(model, loader, n_classes=10):
model.eval()
cm = np.zeros((n_classes, n_classes), dtype=int)
for x, y in loader:
x, y = x.to(device), y.to(device)
pred = model(x).argmax(1)
for t, p in zip(y.cpu().numpy(), pred.cpu().numpy()):
cm[t, p] += 1
return cm
cm = confusion_matrix(model, test_loader)
print("Confusion matrix:")
print(cm)
# Per-class accuracy
for i in range(10):
acc = cm[i, i] / cm[i].sum()
print(f"Class {i}: {acc*100:.2f}%")
Confusion matrix-এ দেখা যায় — ৪ ও ৯ মাঝে confusion typical (similar shape)। Per-class accuracy ৯৮-৯৯.৭% range।
৬ · Save ও load model
# Save state_dict (recommended)
torch.save(model.state_dict(), 'mnist_cnn.pt')
print("Saved!")
# Load — somewhere else
new_model = MNIST_CNN().to(device)
new_model.load_state_dict(torch.load('mnist_cnn.pt'))
new_model.eval()
# Single image inference
img = test_set[0][0].unsqueeze(0).to(device) # (1, 1, 28, 28)
with torch.no_grad():
logits = new_model(img)
pred = logits.argmax(1).item()
prob = F.softmax(logits, dim=1).max().item()
print(f"Predicted: {pred}, confidence: {prob:.3f}")
print(f"True label: {test_set[0][1]}")
৭ · Improvements — ৯৯.৫%+ পেতে
- Data augmentation: RandomRotation, RandomAffine — generalization বাড়ে।
- Larger model: ResNet-style residual block।
- Mixed precision: AMP — ২x faster training।
- Learning rate finder: Optimal LR auto-detect।
- Cosine annealing: Better than StepLR usually।
- Test-time augmentation: Multiple augmented prediction average।
- Ensemble: ৩-৫ model train, average prediction।
৮ · Common pitfalls
- Forgot
model.train()/model.eval()— BatchNorm ও Dropout buggy behavior। zero_grad()মিস — gradient accumulate, exploding loss।- Mixed device — "Expected all tensors on same device"।
- Normalize ভুলা — image scale issue, training slow।
- Overfitting — train high, test low — dropout/augment বাড়ান।
- Batch size too small — GPU underutilized, gradient noisy।
৯ · Production deployment ideas
- ONNX export — universal runtime।
- TorchScript — JIT compile, no Python dependency।
- Quantization INT8 — 4x size, mobile-friendly।
- Web demo: Gradio / Streamlit — interactive UI।
- API service: FastAPI + Docker — production endpoint।
- Mobile: PyTorch Mobile / TFLite — phone inference।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ MNIST ৯৯%+ সহজ — কিন্তু এই accuracy "real world handwriting"-এ ফেল করে। কেন? Real digit dataset-এ migration করতে কী করব?
MNIST-এর ৯৯% misleading — production deployment-এ surprisingly poor। Important lesson।
MNIST limitations:
- Centered, normalized।
- Single digit per image।
- Clean background।
- Fixed size (২৮×২৮)।
- US-style handwriting bias।
Real world challenges:
- Skewed angle, rotation।
- Multiple digit per image।
- Variable size, position।
- Noisy background, lighting।
- Different style (Bangla numeral!)।
Distribution shift:
- Training distribution ≠ deployment।
- Out-of-distribution failure।
- Confidence high — accuracy low।
- Calibration broken।
Strategies for real-world:
(১) More diverse data:
- EMNIST — extended MNIST (handwritten letters)।
- SVHN — Street View House Numbers।
- Handwritten Bangla — ISI Kolkata, BUET।
- Real-world capture data।
(২) Data augmentation:
- RandomAffine — rotation, translation।
- ElasticDeformation — handwriting variation।
- RandomErasing — robustness।
- Color jitter (color images)।
(৩) Pre-processing:
- Adaptive thresholding।
- Bounding box detection।
- Centering, normalization।
- Multi-digit segmentation।
(৪) Architecture upgrade:
- ResNet, EfficientNet।
- Attention mechanisms।
- Transformer-based।
- Ensemble multiple model।
(৫) Domain adaptation:
- Source: MNIST, target: real।
- Adversarial training।
- Feature alignment।
- Active learning unlabeled data।
Bangla numeral case:
- "০, ১, ২, ৩..." — visually different।
- Conjunct shapes complex।
- BanglaLekha dataset (Hossain et al.)।
- Specific training required।
Production pipeline:
# Real-world digit recognition
# 1. Detection — find digit regions (YOLOv5)
# 2. Segmentation — separate digits
# 3. Classification — your CNN
# 4. Post-processing — sequence join
Confidence calibration:
- Temperature scaling (Guo et al.)।
- Reject low-confidence prediction।
- Out-of-distribution detection।
- Production safety net।
Real datasets to consider:
- MNIST-M (different background)।
- USPS — postal digits।
- QMNIST — extended।
- Custom collection।
Practical Bangladesh use case:
- Bank check digit recognition।
- Bangla numeral OCR (NID, education forms)।
- Postal address recognition।
- BanglaLekha + augmentation key।
Evaluation rigor:
- Held-out real test set।
- Multiple writer।
- Different conditions।
- Confusion matrix analysis।
মূল উপলব্ধি: MNIST academic baseline — production not directly applicable। Distribution shift critical। Real data + augmentation + robust architecture combination needed। Bangla numeral specific dataset। Production ML — beyond accuracy number।
প্র ০২ Confusion matrix-এ ৪ ও ৯ confused — visualize করে কী পাবেন? Wrong prediction-এ AI কেন "সাহসী" থাকে?
Misclassification analysis — model debugging-এর critical skill।
Visualization technique:
import matplotlib.pyplot as plt
# Find misclassified
wrong = []
for x, y in test_loader:
x, y = x.to(device), y.to(device)
pred = model(x).argmax(1)
mask = pred != y
if mask.any():
wrong.append((x[mask].cpu(), y[mask].cpu(),
pred[mask].cpu()))
# Plot grid
fig, axes = plt.subplots(2, 5, figsize=(10, 4))
for i, ax in enumerate(axes.flat):
img, true, pred = wrong[0][0][i], wrong[0][1][i], wrong[0][2][i]
ax.imshow(img.squeeze(), cmap='gray')
ax.set_title(f'T:{true} P:{pred}')
Common confusion pairs:
- ৪ ↔ ৯: similar curve top।
- ৩ ↔ ৮: closed loops।
- ১ ↔ ৭: vertical stroke।
- ০ ↔ ৬: oval shape।
Why model confidently wrong:
- Softmax output peaked even for OOD।
- Features dominate similar pattern।
- No "I don't know" output।
- Calibration issue।
Examples:
- Sloppy 4 — top open, 9-like।
- European 7 — extra crossbar, 4-like।
- Squashed 8 — looks like 3।
- Edge cases inherently ambiguous।
Confidence analysis:
# Wrong predictions confidence
wrong_confs = []
for x, y in test_loader:
x, y = x.to(device), y.to(device)
out = model(x)
pred = out.argmax(1)
probs = F.softmax(out, dim=1).max(1)[0]
mask = pred != y
wrong_confs.extend(probs[mask].tolist())
import numpy as np
print(f"Mean wrong confidence: {np.mean(wrong_confs):.3f}")
print(f"Max wrong confidence: {np.max(wrong_confs):.3f}")
# Often >0.95 — overconfident
Calibration techniques:
- Temperature scaling — divide logits by T।
- Platt scaling — sigmoid fit।
- Isotonic regression।
- Deep ensembles — uncertainty estimate।
Out-of-distribution detection:
- Maximum softmax probability।
- Energy-based score।
- Mahalanobis distance feature space।
- Reject low confidence।
Saliency map analysis:
- Gradient w.r.t. input।
- Highlight important pixel।
- Misclassification — wrong region focus।
- Debug feature learning।
Grad-CAM:
- Class activation map।
- What region influenced decision।
- Visual debugging।
- Production explanation।
Improvement strategies:
- Hard example mining।
- Focal loss — hard sample focus।
- Curriculum learning — easy→hard।
- Data augmentation targeted।
Human comparison:
- Show wrong examples to humans।
- Many ambiguous — humans also struggle।
- Inter-annotator agreement।
- Upper bound accuracy।
Production safeguards:
- Reject threshold — confidence < 0.9।
- Human-in-loop for uncertain।
- Continuous monitoring।
- Drift detection।
মূল উপলব্ধি: Misclassification visualization — model understanding key। Confused pairs systematic, often visually similar। Confidence often miscalibrated — high even wrong। Calibration + OOD detection production essential। Saliency map debugging tool।
প্র ০৩ Train accuracy ৯৯.৯% কিন্তু test ৯৭% — overfitting-এর diagnostic ও fix step-by-step?
Overfitting — DL-এর most common issue। Diagnostic + fix systematic।
Diagnostic:
(১) Loss curve analysis:
- Training loss decrease।
- Validation loss increase or plateau।
- Gap widening — overfit।
(২) Accuracy gap:
- Train vs validation gap >2-3% concerning।
- $>5\%$ overfit clear।
(৩) Capacity check:
- Parameter / training sample ratio।
- >1 — likely overfit risk।
Fix hierarchy:
(১) More data — best fix:
- More training data ideal।
- Often unavailable।
(২) Data augmentation:
train_transform = transforms.Compose([
transforms.RandomRotation(10),
transforms.RandomAffine(0, translate=(0.1, 0.1)),
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)),
])
- Effectively increase data।
- Free, easy।
- Domain-appropriate augmentation।
(৩) Dropout:
- Already 0.3 in code।
- Increase to 0.5 if persist।
- Multiple layers।
(৪) Weight decay:
optimizer = optim.Adam(model.parameters(),
lr=1e-3, weight_decay=1e-4)
- L2 regularization।
- Standard 1e-4 to 1e-3।
(৫) Early stopping:
- Validation loss monitor।
- No improvement 3 epoch — stop।
- Best checkpoint use।
(৬) Reduce model size:
- Fewer parameters।
- Smaller layer।
- Less overfit capacity।
(৭) Batch normalization:
- Already in code।
- Slight regularization effect।
- Stable training।
(৮) Learning rate schedule:
- Cosine annealing।
- Warmup gradual।
- Better convergence।
Implementation order:
- Add augmentation first।
- Increase dropout if still overfit।
- Add weight decay।
- Early stopping।
- Architecture tweak।
- More data if possible।
Validation set strategy:
- Train/val/test split।
- Tune on val, evaluate test।
- Cross-validation for small data।
Modern techniques:
- Mixup — sample interpolation।
- CutMix — patch swap।
- Label smoothing — soft targets।
- Stochastic depth।
MNIST-specific:
- RandomRotation(10) major help।
- RandomAffine translation 10%।
- Train acc dip slightly, test rise।
- ৯৯.৪%+ achievable।
Bias-variance trade-off:
- High variance = overfit।
- Regularization reduce variance।
- Slight bias increase OK।
- Total error decrease goal।
মূল উপলব্ধি: Overfitting diagnostic — loss/accuracy gap। Fix order: augmentation, dropout, weight decay, early stopping। MNIST RandomRotation impactful। Modern techniques (Mixup, label smoothing) extra boost। Systematic approach essential।
প্র ০৪ Bangladesh-এর একটি bank check digit OCR system বানাতে চাচ্ছে। MNIST baseline থেকে production system কিভাবে?
Practical Bangladesh ML deployment — bank check OCR। Production-grade engineering।
Use case requirements:
- Cheque amount recognition (digit + Bangla)।
- Account number reading।
- High accuracy — money matters।
- Real-time processing।
- Audit trail।
Pipeline architecture:
Stage 1 — Image processing:
- Cheque scan/photo।
- Geometric correction (perspective)।
- Skew correction।
- Background removal।
Stage 2 — Field detection:
- YOLOv8 — amount field, signature, date।
- Custom train Bangladesh check format।
- Templates per bank।
Stage 3 — Digit segmentation:
- Connected components।
- Touching digit separation।
- Vertical projection profile।
Stage 4 — Classification:
- Per-digit CNN।
- Latin + Bangla numeral।
- Confidence per digit।
Stage 5 — Post-processing:
- Sequence reconstruction।
- Sanity check (amount > 0)।
- Format validation।
- Cross-reference written amount।
Data collection:
- Real cheque sample collection।
- Diverse banks (Sonali, DBBL, City)।
- Privacy/legal compliance।
- Synthetic augmentation।
Bangla numeral specific:
- Use BanglaLekha-Isolated dataset।
- BUET handwritten Bangla numeral।
- Mixed Latin-Bangla detection।
- Conjunct/joint character handling।
Robustness:
- Stamp overlay handling।
- Strikethrough recognition।
- Various pen colors।
- Stain/fold tolerance।
Confidence-based routing:
# Critical decision logic
def route_decision(digit_confs, amount):
min_conf = min(digit_confs)
if min_conf > 0.99 and len(digit_confs) >= 3:
return "auto_process"
elif min_conf > 0.95:
return "human_verify"
else:
return "human_review"
Cross-validation:
- Numeric amount vs written amount।
- Discrepancy → reject।
- Multiple model ensemble।
- Voting consensus।
Architecture upgrade from MNIST:
- EfficientNet-B0 for digit।
- Detection — YOLO/DETR।
- OCR — TrOCR style transformer।
- Ensemble multiple।
Training strategy:
- Synthetic data first।
- Real data fine-tune।
- Active learning ongoing।
- Continuous improvement।
Deployment infrastructure:
- On-premise (data sensitivity)।
- Docker + Kubernetes।
- GPU server bank-internal।
- Audit log every decision।
Monitoring:
- Per-day accuracy tracking।
- Confidence distribution।
- Reject rate trend।
- Human override rate।
Compliance:
- Bangladesh Bank guidelines।
- Data Protection Act।
- Customer data security।
- Audit trail mandatory।
Error handling:
- OOD detection — unusual cheque।
- Reject confidently।
- Human escalation।
- Customer protection priority।
Human-in-loop:
- Verification UI।
- Quick review workflow।
- Disagreement learning loop।
- Continuous data collection।
Cost analysis:
- Initial development: ৬-১২ মাস।
- Hardware: $50K-200K।
- Annual maintenance: $50K।
- ROI: significant via automation।
Bangladesh banking landscape:
- BACPS (Bangladesh Automated Cheque Processing System)।
- BB initiative driving digitization।
- Local bank tech adoption growing।
- Practical AI opportunity।
মূল উপলব্ধি: Bank check OCR — multi-stage pipeline। Detection → segmentation → classification → validation। MNIST CNN entry point only। Real production — robust architecture, confidence routing, human-in-loop। Bangladesh banking — mature ML opportunity। Compliance + accuracy critical।
অনুশীলন
-
Run end-to-end: Colab-এ পুরো code চালান, ১০ epoch — final test accuracy কত পান?
Typical: ~৯৯.১-৯৯.৩%। Variations:
- ৯৮.৫% — কিছু issue (data normalization, batch size)।
- ৯৯.০% — baseline OK।
- ৯৯.৩%+ — excellent।
- ৯৯.৫%+ — augmentation/longer training সাথে possible।
-
Augmentation add: RandomRotation ও RandomAffine include করে retrain — accuracy difference?
train_transform = transforms.Compose([ transforms.RandomRotation(10), transforms.RandomAffine(0, translate=(0.1, 0.1)), transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)), ]) train_set = datasets.MNIST(root='./data', train=True, download=True, transform=train_transform)Test accuracy ০.১-০.৩% improve সাধারণত। Train accuracy কিছু কম, but test better — generalization improvement।
-
চিন্তা: এই CNN-এর কোন layer এ output features সবচেয়ে "abstract"? কেন?
FC1 (১২৮-D) — সবচেয়ে abstract। Hierarchy:
- Conv1 — edge, simple texture।
- Conv2 — corner, curve, digit-part।
- Flatten — combined spatial features।
- FC1 — global digit representation।
- FC2 — class score।
FC1-এর activation visualize করলে — same digit-এর different style cluster করে। This is the "learned representation" useful for transfer learning, retrieval, similarity search।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ৪০ · কোর্সের চূড়ান্ত পর্যালোচনা পরবর্তী পাঠ পুরো ৪০ পাঠের overview ও পরবর্তী direction।
- পাঠ ৩৮ · GPU ও CUDA আগের পাঠ এই project-এ GPU-এর ভূমিকা।
- Computer Vision Track আরও projects Detection, segmentation, advanced CV।
- সব AI Courses দেখুন ABCL TECH Python, ML, DL, NLP, CV, GenAI, RL, MLOps — সব AI কোর্স একসাথে।