<jupyter_start><jupyter_text><jupyter_code>import torch
from torch import nn
from torch.nn import functional as F<jupyter_output><empty_output><jupyter_text>Data<jupyter_code>filenames = {
    "train": {
        "images": "train-images-idx3-ubyte.gz", 
        "labels": "train-labels-idx1-ubyte.gz"
    },
    "test": {
        "images": "t10k-images-idx3-ubyte.gz", 
        "labels": "t10k-labels-idx1-ubyte.gz"
    }
}
for fn in [fn for v in filenames.values() for fn in v.values()]:
  !wget -q "http://yann.lecun.com/exdb/mnist/{fn}"
import gzip
import numpy as np

images = dict()
labels = dict()

image_size = 28
for split, fns in filenames.items():
  num_samples = 60000 if split == "train" else 10000

  with gzip.open(fns["images"], 'r') as f:  
    f.read(16)
    buf = f.read(image_size * image_size * num_samples)
    data = torch.frombuffer(buf, dtype=torch.uint8).float()
    images[split] = data.reshape(num_samples, 1, image_size, image_size)
  
  with gzip.open(fns["labels"],'r') as f:
    f.read(8)
    buf = f.read(num_samples)
    labels[split] = torch.frombuffer(buf, dtype=torch.uint8).type(torch.int64)
images["train"].shape, labels["train"].shape
images["test"].shape, labels["test"].shape
import matplotlib.pyplot as plt

def plot_test_img(i: int):
  print(labels["test"][i])
  image = images["test"][i].squeeze().numpy()
  plt.imshow(image, cmap="gray")
  plt.show()

plot_test_img(0)<jupyter_output>tensor(7)<jupyter_text>Model<jupyter_code>class ImageClassifier(nn.Module):
  out_channels = 32
  kernel_size = 3
  stride = 1
  padding = 1

  def __init__(self, in_channels, width, height, num_classes):
    super(ImageClassifier, self).__init__()

    self.conv1 = nn.Conv2d(in_channels, self.out_channels, self.kernel_size, self.stride, self.padding)
    self.activation = nn.ReLU()
    self.pool = nn.MaxPool2d(2, 2)

    self.flatten = nn.Flatten()
    self.cls_head = nn.Linear(self.out_channels * (width // 2) * (height // 2), num_classes)
  
  def forward(self, inputs):
    x = self.conv1(inputs)
    x = self.activation(x)
    assert x.shape[2:] == inputs.shape[2:]

    x = self.pool(x)
    assert x.shape[2] == (inputs.shape[2] // 2)
    assert x.shape[3] == (inputs.shape[3] // 2)

    out = self.cls_head(self.flatten(x))

    return out
model = ImageClassifier(in_channels=1, width=28, height=28, num_classes=len(labels["train"].unique()))
model(images["train"][:2])<jupyter_output><empty_output><jupyter_text>Optimization<jupyter_code>dataset = torch.utils.data.TensorDataset(images["train"], labels["train"])
dataloader = torch.utils.data.DataLoader(dataset, batch_size=64, shuffle=True)
X, y = next(iter(dataloader))
X.shape, y.shape
pred = model(X)
assert torch.argmax(F.softmax(pred, dim=1), dim=1).shape == y.shape
loss_fn = nn.CrossEntropyLoss()
loss_fn(pred, y)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
epochs = 1
for _ in range(epochs):
  for (X, y) in dataloader:
    optimizer.zero_grad()

    pred = model(X)
    loss = loss_fn(pred, y)

    loss.backward()
    optimizer.step()<jupyter_output><empty_output><jupyter_text>Inference<jupyter_code>plot_test_img(0)
torch.argmax(F.softmax(model(images["test"][:1]), dim=1), dim=1)
X, y = next(iter(dataloader))
y
torch.argmax(F.softmax(model(X), dim=1), dim=1)<jupyter_output><empty_output><jupyter_text>Evaluation metrics<jupyter_code>pred_test = model(images["test"])
pred_test = torch.argmax(F.softmax(pred_test, dim=1), dim=1)
pred_test.shape, labels["test"].shape
from sklearn.metrics import classification_report

print(classification_report(labels["test"], pred_test))<jupyter_output>precision    recall  f1-score   support

           0       0.93      0.99      0.96       980
           1       0.98      0.97      0.98      1135
           2       0.93      0.93      0.93      1032
           3       0.98      0.88      0.93      1010
           4       0.99      0.80      0.89       982
           5       0.98      0.90      0.94       892
           6       0.96      0.95      0.96       958
           7       0.98      0.71      0.82      1028
           8       0.85      0.96      0.91       974
           9       0.66      0.98      0.79      1009

    accuracy                           0.91     10000
   macro avg       0.92      0.91      0.91     10000
weighted avg       0.92      0.91      0.91     10000
{'fraction_non_alphanumeric': 0.14433463571592736, 'fraction_numerical': 0.05607906228453229, 'mean_word_length': 2.827616534740545, 'pattern_counts': {'":': 6, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '80479', 'license': 'MIT', 'n_tokens_mistral': 1733, 'n_tokens_neox': 1620, 'n_words': 309, 'path': 'MNIST_Digit_Classification_with_CNN.ipynb', 'repo_name': 'jppgks/DL-from-Scratch-with-PyTorch', 'size': '4351'}