Skip to content

Instantly share code, notes, and snippets.

@AhmedCoolProjects
Created November 14, 2025 11:58
Show Gist options
  • Select an option

  • Save AhmedCoolProjects/8283bc04546d61057d5251199091351f to your computer and use it in GitHub Desktop.

Select an option

Save AhmedCoolProjects/8283bc04546d61057d5251199091351f to your computer and use it in GitHub Desktop.
CNN application on MNIST.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyNwSYB4cHyoP/S1XVdQVdSW",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/AhmedCoolProjects/8283bc04546d61057d5251199091351f/cnn-application-on-mnist.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# First Application of CNN on MNIST"
],
"metadata": {
"id": "o9_Ufm9_yGfh"
}
},
{
"cell_type": "code",
"source": [
"import numpy as np\n",
"from sklearn.datasets import fetch_openml\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import OneHotEncoder\n",
"\n",
"def load_and_prepare_data_cnn():\n",
" print(\"Loading MNIST dataset...\")\n",
" X, y = fetch_openml('mnist_784', version=1, return_X_y=True, as_frame=False, parser='auto')\n",
"\n",
" # 1. Scale the data\n",
" X = X / 255.0\n",
"\n",
" # 2. Reshape for CNN: (num_samples, 784) -> (num_samples, 28, 28, 1)\n",
" X_reshaped = X.reshape(-1, 28, 28, 1)\n",
"\n",
" # 3. One-hot encode labels\n",
" y_reshaped = y.reshape(-1, 1)\n",
" encoder = OneHotEncoder(sparse_output=False, categories='auto')\n",
" Y_onehot = encoder.fit_transform(y_reshaped)\n",
"\n",
" print(\"Data loaded and prepared.\")\n",
"\n",
" # 4. Split the data\n",
" X_train, X_test, Y_train_onehot, Y_test_onehot, _, y_test_orig = train_test_split(\n",
" X_reshaped, Y_onehot, y.flatten(), test_size=0.1, random_state=42\n",
" )\n",
"\n",
" # 5. Transpose Y to (classes, m)\n",
" Y_train = Y_train_onehot.T\n",
" Y_test = Y_test_onehot.T\n",
"\n",
" y_test_orig = y_test_orig.astype(int)\n",
"\n",
" return X_train, Y_train, X_test, Y_test, y_test_orig"
],
"metadata": {
"id": "aepl0422yBCP"
},
"execution_count": 26,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"# Verifying\n",
"X_train, X_test, Y_train, Y_test, y_test_orig = load_and_prepare_data()\n",
"\n",
"image_index = 0 # You can change this to view other images\n",
"\n",
"# Reshape the 784-feature vector back to a 28x28 image\n",
"image = X_test[:, image_index].reshape(28, 28)\n",
"label = y_test_orig[image_index]\n",
"label_one_hot = Y_test[:, image_index]\n",
"\n",
"plt.imshow(image, cmap='gray')\n",
"plt.title(f\"Example Image - Label: {label}\")\n",
"plt.axis('off') # Hide axes ticks and labels\n",
"plt.show()\n",
"print(label)\n",
"print(label_one_hot)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 497
},
"id": "O-rEeXFbyJrE",
"outputId": "893afee5-4b4c-4ff2-db05-23d8b44fffae"
},
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading MNIST dataset...\n",
"Data loaded and prepared.\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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\n"
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"8\n",
"[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"class Activation_Functions:\n",
" @staticmethod\n",
" def heaviside(x: np.ndarray) -> np.ndarray:\n",
" return np.where(x < 0, 0, 1)\n",
"\n",
" @staticmethod\n",
" def relu(x: np.ndarray) -> np.ndarray:\n",
" return np.maximum(0, x)\n",
"\n",
" @staticmethod\n",
" def sigmoid(x: np.ndarray) -> np.ndarray:\n",
" return 1 / (1 + np.exp(-x))\n",
"\n",
" @staticmethod\n",
" def softmax(x: np.ndarray) -> np.ndarray:\n",
" exp_x = np.exp(x - np.max(x)) # for numerical stability\n",
" return exp_x / exp_x.sum(axis=0, keepdims=True) # axis=0 for column-wise\n",
"\n",
" @staticmethod\n",
" def sigmoid_derivative(x: np.ndarray) -> np.ndarray:\n",
" sig = Activation_Functions.sigmoid(x)\n",
" return sig * (1 - sig)\n",
"\n",
" @staticmethod\n",
" def relu_derivative(x: np.ndarray) -> np.ndarray:\n",
" return np.where(x > 0, 1, 0)\n",
""
],
"metadata": {
"id": "EFdO8uh2Cl4W"
},
"execution_count": 8,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class Cost_Functions:\n",
" @staticmethod\n",
" def mse(y_true: np.ndarray, y_pred: np.ndarray) -> float:\n",
" sq_errors = (y_true - y_pred) ** 2\n",
" return np.mean(sq_errors)\n",
"\n",
" @staticmethod\n",
" def ce(y_true: np.ndarray, y_pred: np.ndarray) -> float:\n",
" # Adding a small value to avoid log(0)\n",
" epsilon = 1e-15\n",
" m = y_pred.shape[1]\n",
" y_pred = np.clip(y_pred, epsilon, 1 - epsilon)\n",
" ce_loss_ = -np.sum(y_true * np.log(y_pred), axis=0) # -> (L, m)\n",
" ce_loss = (1/m) * np.sum(ce_loss_)\n",
" return np.squeeze(ce_loss)\n",
"\n",
" @staticmethod\n",
" def bce(y_true: np.ndarray, y_pred: np.ndarray) -> float:\n",
" # Adding a small value to avoid log(0)\n",
" epsilon = 1e-15\n",
" y_pred = np.clip(y_pred, epsilon, 1 - epsilon)\n",
" bce_loss_ = - (y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))\n",
" bce_loss = np.mean(bce_loss_)\n",
" return bce_loss\n",
"\n",
" @staticmethod\n",
" def ce_grad(y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:\n",
" # Gradient of Cross-Entropy Loss with respect to predictions\n",
" epsilon = 1e-15\n",
" y_pred = np.clip(y_pred, epsilon, 1 - epsilon)\n",
" grad = - (y_true / y_pred)\n",
" return grad\n",
"\n",
" @staticmethod\n",
" def bce_grad(y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:\n",
" # Gradient of Binary Cross-Entropy Loss with respect to predictions\n",
" epsilon = 1e-15\n",
" y_pred = np.clip(y_pred, epsilon, 1 - epsilon)\n",
" grad = - (y_true / y_pred) + ((1 - y_true) / (1 - y_pred))\n",
" return grad"
],
"metadata": {
"id": "rjhkf1NVCjDG"
},
"execution_count": 9,
"outputs": []
},
{
"cell_type": "code",
"source": [
"from typing import List, Tuple\n",
"\n",
"import numpy as np\n",
"\n",
"np.random.seed(42)\n",
"\n",
"class NN:\n",
" def __init__(self, s: List[int]):\n",
" '''\n",
" s: List of layer sizes, where s[0] is the input layer size, s[1] is the first hidden layer size, ..., s[L] is the output layer size\n",
" '''\n",
" self.s = s\n",
" self.L = len(s) - 1\n",
" # Xavier (Glorot) initialization for weights\n",
" self.weights = [np.random.randn(s[l], s[l-1]) * np.sqrt(1 / s[l-1]) for l in range(1, self.L + 1)]\n",
" self.biases = [np.zeros((s[l], 1)) for l in range(1, self.L + 1)]\n",
" self.f = Activation_Functions.sigmoid\n",
" self.f_output = Activation_Functions.softmax\n",
" self.cost = Cost_Functions.ce\n",
" self.cost_grad = Cost_Functions.ce_grad\n",
"\n",
" def forward_pass(self, X: np.ndarray) -> Tuple[np.ndarray, List[tuple]]:\n",
" '''\n",
" X: Input data of shape (n, m)\n",
" '''\n",
" cache = [] # only hidden and output layers\n",
" A = X\n",
"\n",
" for l in range(self.L):\n",
" A_prev = A\n",
"\n",
" W = self.weights[l]\n",
" b = self.biases[l]\n",
" # Linear Step\n",
" Z = np.dot(W, A_prev) + b\n",
" # Activation Step\n",
" if l == self.L - 1:\n",
" A = self.f_output(Z)\n",
" else:\n",
" A = self.f(Z)\n",
"\n",
" cache.append((A_prev, W, b, Z))\n",
"\n",
" return A, cache\n",
"\n",
" def compute_cost(self, AL: np.ndarray, Y: np.ndarray) -> float:\n",
" cost = self.cost(Y, AL)\n",
" return cost\n",
"\n",
" def backpropagation(self, AL: np.ndarray, Y: np.ndarray, cache: List[tuple]) -> dict:\n",
" '''\n",
" AL: Output of the network from forward pass\n",
" Y: True labels (one-hot encoded)\n",
" cache: List of tuples containing (A_prev, W, b, Z) for each layer from forward pass\n",
"\n",
" Returns:\n",
" grads: Dictionary containing gradients for weights and biases e.g., {'dW1': ..., 'db1': ..., 'dW2': ..., 'db2': ..., ...}\n",
" '''\n",
" grads = {}\n",
" m = Y.shape[1] # number of examples\n",
"\n",
" # ---- 1. Output Layer ----\n",
" Delta_L = AL - Y # (s[L], m)\n",
" A_prev, W_L, b_L, Z_L = cache[-1]\n",
"\n",
" # grads\n",
" grads[f'dW{self.L}'] = (1/m) * np.dot(Delta_L, A_prev.T)\n",
" grads[f'db{self.L}'] = (1/m) * np.sum(Delta_L, axis=1, keepdims=True)\n",
"\n",
" # ---- 2. Hidden Layers ----\n",
" Delta_next = Delta_L\n",
" dA_prev = np.dot(W_L.T, Delta_next)\n",
"\n",
" for l in reversed(range(self.L - 1)):\n",
" A_prev, W_l, b_l, Z_l = cache[l]\n",
"\n",
" Delta_l = dA_prev * Activation_Functions.sigmoid_derivative(Z_l)\n",
"\n",
" # grads\n",
" grads[f'dW{l+1}'] = (1/m) * np.dot(Delta_l, A_prev.T)\n",
" grads[f'db{l+1}'] = (1/m) * np.sum(Delta_l, axis=1, keepdims=True)\n",
"\n",
" dA_prev = np.dot(W_l.T, Delta_l)\n",
"\n",
" return grads, dA_prev\n",
"\n",
" def update_parameters(self, grads: dict, learning_rate: float = 0.001):\n",
" for l in range(self.L):\n",
" self.weights[l] -= learning_rate * grads[f'dW{l+1}']\n",
" self.biases[l] -= learning_rate * grads[f'db{l+1}']\n",
"\n",
" def train(self, X: np.ndarray, Y: np.ndarray, epochs: int = 100, learning_rate: float = 0.001, batch_size: int = 20, print_cost: bool = True) -> None:\n",
" '''\n",
" The main training loop with mini-batch gradient descent.\n",
" '''\n",
" costs = []\n",
" m = X.shape[1] # total number of samples\n",
"\n",
" for epoch in range(epochs):\n",
" # Shuffle the training data at the beginning of each epoch\n",
" permutation = np.random.permutation(m)\n",
" shuffled_X = X[:, permutation]\n",
" shuffled_Y = Y[:, permutation]\n",
"\n",
" # Iterate through mini-batches\n",
" for i in range(0, m, batch_size):\n",
" X_batch = shuffled_X[:, i:i + batch_size]\n",
" Y_batch = shuffled_Y[:, i:i + batch_size]\n",
"\n",
" # 1. Forward Pass for the batch\n",
" AL_batch, cache_batch = self.forward_pass(X_batch)\n",
" # 2. Backpropagation for the batch\n",
" grads_batch = self.backpropagation(AL_batch, Y_batch, cache_batch)\n",
" # 3. Update Parameters using batch gradients\n",
" self.update_parameters(grads_batch, learning_rate)\n",
"\n",
" # Calculate and print the overall cost after all batches for the epoch\n",
" AL_full, _ = self.forward_pass(X) # Forward pass on full dataset for cost calculation\n",
" cost = self.compute_cost(AL_full, Y)\n",
"\n",
" if print_cost and epoch % (epochs // 10 if epochs >= 10 else 1) == 0:\n",
" print(f\"Cost after epoch {epoch}: {cost:.4f}\")\n",
" if epoch % 10 == 0:\n",
" costs.append(cost)\n",
" return costs\n",
"\n",
" def predict(self, X: np.ndarray) -> np.ndarray:\n",
" AL, _ = self.forward_pass(X)\n",
" predictions = np.argmax(AL, axis=0)\n",
" return predictions\n"
],
"metadata": {
"id": "cGnid1IlCh_r"
},
"execution_count": 55,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class Conv_Layer:\n",
" def __init__(self, in_channels, num_filters, filter_size, stride=1, padding=0):\n",
" '''\n",
" in_channels: Depth of the input volume (e.g., 1 for grayscale, 3 for RGB)\n",
" num_filters: Number of filters to use (this will be the depth of the output volume)\n",
" filter_size: An integer, e.g., 3 for a 3x3 filter\n",
" stride: Step size of the filter\n",
" padding: The amount of zero-padding to add to the input\n",
" '''\n",
" self.C = num_filters\n",
" self.F = filter_size\n",
" self.S = stride\n",
" self.P = padding\n",
"\n",
" # 1. Initialize the filters with random weights and biases\n",
" self.filters = np.random.randn(self.F, self.F, in_channels, self.C) * 0.1\n",
" # one bias per filter\n",
" self.biases = np.zeros((1, 1, 1, self.C))\n",
"\n",
" def _zero_pad(self, X, pad):\n",
" '''\n",
" Helper function to add zero-padding to a batch of images X\n",
" X: Input data of shape (m, H, W, C)\n",
" '''\n",
" if self.P > 0:\n",
" return np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), 'constant', constant_values=0)\n",
" return X\n",
"\n",
" def _relu(self, Z):\n",
" '''\n",
" ReLU activation function\n",
" '''\n",
" return np.maximum(0, Z)\n",
"\n",
" def _relu_derivative(self, Z):\n",
" '''\n",
" Derivative of ReLU\n",
" '''\n",
" dZ = np.array(Z, copy=True)\n",
" dZ[Z <= 0] = 0\n",
" dZ[Z > 0] = 1\n",
" return dZ\n",
"\n",
" def forward_pass(self, A_prev):\n",
" '''\n",
" A_prev: Input data from the previous layer\n",
" Shape: (m, H_prev, W_prev, C_prev)\n",
" m: batch size\n",
" H_prev: height of input\n",
" W_prev: width of input\n",
" C_prev: number of channels in input\n",
"\n",
" Returns:\n",
" A: Output of the layer (after activation)\n",
" cache: A tuple of values needed for backward pass\n",
" '''\n",
"\n",
" # 1. Get dimensions\n",
" m, H_prev, W_prev, C_prev = A_prev.shape\n",
" # _, F, _, C_out = self.filters.shape # No need\n",
"\n",
" # 2. Compute output dimensions of the conv layer\n",
" # (H - F + 2P) / S + 1\n",
" H_out = int((H_prev - self.F + 2 * self.P) / self.S) + 1\n",
" W_out = int((W_prev - self.F + 2 * self.P) / self.S) + 1\n",
"\n",
" # 3. Apply padding to the input\n",
" X_padded = self._zero_pad(A_prev, self.P)\n",
"\n",
" # 4. Initialize output volume Z\n",
" Z = np.zeros((m, H_out, W_out, self.C))\n",
"\n",
" # 5. --- The Convolution Loop ---\n",
" for i in range(m): # loop over batch size\n",
" x_i = X_padded[i] # ith input image\n",
"\n",
" for h in range(H_out):\n",
" for w in range(W_out):\n",
" # find the corners of the current 3D slice\n",
" h_start = h * self.S\n",
" h_end = h_start + self.F\n",
" w_start = w * self.S\n",
" w_end = w_start + self.F\n",
" x_slice = x_i[h_start:h_end, w_start:w_end, :]\n",
"\n",
" # convolve with all filters\n",
" for c in range(self.C): # loop over filters\n",
" # Element-wise multiplication, then sum all 3 dimensions\n",
" conv_sum = np.sum(x_slice * self.filters[:, :, :, c])\n",
"\n",
" # Add bias\n",
" Z[i, h, w, c] = conv_sum + self.biases[0, 0, 0, c]\n",
"\n",
" # 6. Apply ReLU activation\n",
" A = self._relu(Z)\n",
"\n",
" # 7. Store values for backward pass\n",
" # we will need X_prev and our parameters to calculate gradients later\n",
" cache = (A_prev, Z, self.filters, self.biases, self.S, self.P)\n",
"\n",
" return A, cache\n",
"\n",
" def backpropagation(self, dA, cache):\n",
" '''\n",
" dA: Gradient w.r.t the output (A), shape: (m, H_out, W_out, C_out)\n",
" cache: Tuple of values from forward pass\n",
"\n",
" Returns:\n",
" dA_prev: Gradient w.r.t the input (A_prev), shape: (m, H_prev, W_prev, C_prev)\n",
" dW: Gradient w.r.t the filters (weights), shape: (F, F, C_prev, C_out)\n",
" db: Gradient w.r.t the biases\n",
" '''\n",
"\n",
" # 1. Unpack cache\n",
" (A_prev, Z, W, b, S, P) = cache\n",
" # 2. Get dimensions\n",
" (m, H_prev, W_prev, C_prev) = A_prev.shape\n",
" (_, H_out, W_out, C_out) = dA.shape\n",
" (F, _, _, _) = W.shape\n",
"\n",
" # 3. Initialize gradients\n",
" dA_prev = np.zeros_like(A_prev) # (m, H_prev, W_prev, C_prev)\n",
" dW = np.zeros_like(W) # (F, F, C_prev, C_out)\n",
" db = np.zeros_like(b) # (1, 1, 1, C_out)\n",
"\n",
" # 4. Calculate dZ\n",
" dZ = dA * self._relu_derivative(Z) # (m, H_out, W_out, C_out)\n",
"\n",
" # 5. Pad A_prev and dA_prev\n",
" A_prev_padded = self._zero_pad(A_prev, P)\n",
" dA_prev_padded = self._zero_pad(dA_prev, P)\n",
"\n",
" # 6. --- The Backpropagation Loop ---\n",
" for i in range(m):\n",
" x_padded_i = A_prev_padded[i]\n",
" da_prev_padded_i = dA_prev_padded[i]\n",
"\n",
" for h in range(H_out):\n",
" for w in range(W_out):\n",
" for c in range(C_out):\n",
" # Corners\n",
" h_start = h * S\n",
" h_end = h_start + F\n",
" w_start = w * S\n",
" w_end = w_start + F\n",
"\n",
" # Slice of A_prev_padded\n",
" x_slice = x_padded_i[h_start:h_end, w_start:w_end, :]\n",
" dz = dZ[i, h, w, c]\n",
"\n",
" # Calculate gradients\n",
" # 1. --- dA_prev (Transposed Convolution) ---\n",
" da_prev_padded_i[h_start:h_end, w_start:w_end, :] += W[:, :, :, c] * dz\n",
"\n",
" # 2. --- dW (Convolution with input) ---\n",
" dW[:, :, :, c] += x_slice * dz\n",
"\n",
" # 3. --- db (Sum) ---\n",
" db[:, :, :, c] += dz\n",
"\n",
" # Unpad dA_prev\n",
" if P > 0:\n",
" dA_prev[i, :, :, :] = da_prev_padded_i[P:-P, P:-P, :]\n",
" else:\n",
" dA_prev[i, :, :, :] = da_prev_padded_i\n",
"\n",
" self.dW = dW / m\n",
" self.db = db / m\n",
"\n",
" return dA_prev, self.dW, self.db\n",
"\n",
" def update_parameters(self, learning_rate):\n",
" self.filters -= learning_rate * self.dW\n",
" self.biases -= learning_rate * self.db"
],
"metadata": {
"id": "z_v-NzBNyMHx"
},
"execution_count": 65,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class Pool_Layer:\n",
" def __init__(self, filter_size=2, stride=2, mode='max'):\n",
" '''\n",
" mode: 'max' for Max Pooling, 'avg' for Mean Pooling\n",
" '''\n",
" self.filter_size = filter_size\n",
" self.stride = stride\n",
" self.mode = mode\n",
"\n",
" def _create_mask_from_window(self, x_slice):\n",
" '''\n",
" Helper function for max pooling backpropagation.\n",
" Creates a mask of same shape as x_slice, with a 1 at the position of the max value.\n",
" '''\n",
"\n",
" mask = (x_slice == np.max(x_slice))\n",
" return mask\n",
"\n",
" def forward_pass(self, A_prev):\n",
" '''\n",
" A_prev: Input data from the previous layer\n",
" Shape: (m, H_prev, W_prev, C_prev)\n",
"\n",
" Returns:\n",
" A: Output of the pooling layer\n",
" cache: A tuple of values needed for backpropagation\n",
" '''\n",
"\n",
" (m, H_prev, W_prev, C_prev) = A_prev.shape\n",
" F = self.filter_size\n",
" S = self.stride\n",
"\n",
" # Calculate output dimensions\n",
" H_out = int((H_prev - F) / S) + 1\n",
" W_out = int((W_prev - F) / S) + 1\n",
"\n",
" # Initialize output volume A\n",
" A = np.zeros((m, H_out, W_out, C_prev))\n",
"\n",
" # --- The Pooling Loop ---\n",
" for i in range(m): # loop over batch size\n",
" for h in range(H_out):\n",
" for w in range(W_out):\n",
" for c in range(C_prev):\n",
" h_start = h * S\n",
" h_end = h_start + F\n",
" w_start = w * S\n",
" w_end = w_start + F\n",
"\n",
" # get a slice of the input\n",
" a_slice = A_prev[i, h_start:h_end, w_start:w_end, c]\n",
"\n",
" # Perform pooling operation\n",
" if self.mode == 'max':\n",
" A[i, h, w, c] = np.max(a_slice)\n",
" elif self.mode == 'avg':\n",
" A[i, h, w, c] = np.mean(a_slice)\n",
" # Store values for backpropagation\n",
" cache = (A_prev, self.filter_size, self.stride, self.mode)\n",
"\n",
" return A, cache\n",
"\n",
" def backpropagation(self, dA, cache):\n",
" '''\n",
" dA: Gradient w.r.t the output of the pooling layer, shape: (m, H_out, W_out, C_out)\n",
" cache: Tuple of values from forward pass\n",
"\n",
" Returns:\n",
" dA_prev: Gradient w.r.t the input of the pooling layer, shape: (m, H_prev, W_prev, C_prev)\n",
" '''\n",
"\n",
" # 1. Unpack cache\n",
" (A_prev, F, S, mode) = cache\n",
"\n",
" # 2. Get dimensions\n",
" (m, H_prev, W_prev, C_prev) = A_prev.shape\n",
" (m, H_out, W_out, C_out) = dA.shape\n",
"\n",
" # 3. Initialize dA_prev\n",
" dA_prev = np.zeros_like(A_prev) # (m, H_prev, W_prev, C_prev)\n",
" # 4. --- The Backpropagation Loop ---\n",
" for i in range(m):\n",
" x_prev_i = A_prev[i]\n",
" for h in range(H_out):\n",
" for w in range(W_out):\n",
" for c in range(C_prev):\n",
" h_start = h * S\n",
" h_end = h_start + F\n",
" w_start = w * S\n",
" w_end = w_start + F\n",
"\n",
" da = dA[i, h, w, c]\n",
"\n",
" if mode == 'max':\n",
" x_slice = x_prev_i[h_start:h_end, w_start:w_end, c]\n",
" # Create mask\n",
" mask = self._create_mask_from_window(x_slice)\n",
" # Route the gradient to the position of the max\n",
" dA_prev[i, h_start:h_end, w_start:w_end, c] += mask * da\n",
" elif mode == 'avg':\n",
" # Distribute the gradient evenly\n",
" shape = (F, F)\n",
" average = da / (F * F)\n",
" dA_prev[i, h_start:h_end, w_start:w_end, c] += np.ones(shape) * average\n",
"\n",
" return dA_prev"
],
"metadata": {
"id": "X6Miyy2DCVX8"
},
"execution_count": 6,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class Flatten_Layer:\n",
" '''\n",
" A layer to flatten the 4D output of the Pool Layer into a 2D vector to be fed into the Dense NN.\n",
" '''\n",
" def __init__(self):\n",
" self.cache = None\n",
"\n",
" def forward_pass(self, A_prev: np.ndarray) -> np.ndarray:\n",
" '''\n",
" Converts (m, H, W, C) to (H*W*C, m)\n",
"\n",
" Returns:\n",
" A: np.ndarray -- Flattened output of shape (H*W*C, m)\n",
" cache: Original shape for backpropagation\n",
" '''\n",
" self.cache = A_prev.shape\n",
" (m, H, W, C) = A_prev.shape\n",
"\n",
" A = A_prev.reshape(m, -1).T # (H*W*C, m), -1 infers the size\n",
"\n",
" return A, self.cache\n",
"\n",
" def backpropagation(self, dA: np.ndarray) -> np.ndarray:\n",
" '''\n",
" Reshapes dA back to the original shape stored in cache.\n",
"\n",
" Returns:\n",
" dA_prev: np.ndarray -- Gradient reshaped to original dimensions\n",
" '''\n",
" original_shape = self.cache\n",
" dA_prev = dA.T.reshape(original_shape) # (m, H, W, C)\n",
"\n",
" return dA_prev"
],
"metadata": {
"id": "BsjTn1tNCY0g"
},
"execution_count": 7,
"outputs": []
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "Wyrz5LJfCbA1"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class CNN:\n",
" def __init__(self):\n",
" print(\"Initializing CNN...\")\n",
" # Layer 1: Conv -> Pool\n",
" self.conv1 = Conv_Layer(in_channels=1, num_filters=8, filter_size=3, stride=1, padding=0) # (m, 28, 28, 1) -> (m, 26, 26, 8)\n",
"\n",
" self.pool1 = Pool_Layer(filter_size=2, stride=2, mode='max') # (m, 26, 26, 8) -> (m, 13, 13, 8)\n",
" # Layer 2: Flatten\n",
" self.flatten = Flatten_Layer() # (m, 13, 13, 8) -> (m, 1352)\n",
" # Layer 3: Dense NN\n",
" dense_input_size = 13*13*8\n",
" self.dense_nn = NN(s=[dense_input_size, 64, 10]) # (1352, m) -> (10, m)\n",
" self.layers = [self.conv1, self.pool1, self.flatten, self.dense_nn]\n",
" print(\"CNN initialized.\")\n",
"\n",
" def forward_pass(self, X):\n",
" # Conv\n",
" A_conv1, cache_conv1 = self.conv1.forward_pass(X)\n",
" # Pool\n",
" A_pool1, cache_pool1 = self.pool1.forward_pass(A_conv1)\n",
" # Flatten\n",
" A_flat, cache_flat = self.flatten.forward_pass(A_pool1)\n",
" # Dense NN\n",
" AL, cache_dense = self.dense_nn.forward_pass(A_flat)\n",
"\n",
" # store all caches for backpropagation\n",
" caches = (\n",
" cache_conv1,\n",
" cache_pool1,\n",
" cache_flat,\n",
" cache_dense\n",
" )\n",
"\n",
" return AL, caches\n",
"\n",
" def compute_cost(self, AL, Y):\n",
" return self.dense_nn.compute_cost(AL, Y)\n",
"\n",
" def backpropagation(self, AL, Y, caches):\n",
" (cache_conv1, cache_pool1, cache_flat, cache_dense) = caches\n",
" # 4. Dense\n",
" grads_dense, dA_flat = self.dense_nn.backpropagation(AL, Y, cache_dense)\n",
" # 3. Flatten\n",
" dA_pool1 = self.flatten.backpropagation(dA_flat)\n",
" # 2. Pool\n",
" dA_conv1 = self.pool1.backpropagation(dA_pool1, cache_pool1)\n",
" # 1. Conv\n",
" dA_prev = self.conv1.backpropagation(dA_conv1, cache_conv1)\n",
"\n",
" # Store dense grads\n",
" self.dense_nn_grads = grads_dense\n",
"\n",
" def update_parameters(self, learning_rate=0.01):\n",
" # Update Conv layer parameters\n",
" self.conv1.update_parameters(learning_rate)\n",
" # Update Dense NN parameters\n",
" self.dense_nn.update_parameters(self.dense_nn_grads, learning_rate)\n",
"\n",
" def train(self, X, Y, epochs, learning_rate=0.01, batch_size=32):\n",
" print(f\"Starting training for {epochs} epochs...\")\n",
" costs = []\n",
" m = X.shape[0]\n",
" for epoch in range(epochs):\n",
" epoch_cost = 0\n",
" for i in range(0, m, batch_size):\n",
" X_batch = X[i:i+batch_size, :, :, :]\n",
" Y_batch = Y[:, i:i+batch_size]\n",
"\n",
" AL_batch, caches_batch = self.forward_pass(X_batch)\n",
" cost = self.compute_cost(AL_batch, Y_batch)\n",
" epoch_cost += cost * X_batch.shape[0]\n",
" self.backpropagation(AL_batch, Y_batch, caches_batch)\n",
" self.update_parameters(learning_rate)\n",
"\n",
" mean_epoch_cost = epoch_cost\n",
" costs.append(mean_epoch_cost)\n",
"\n",
" print(f\"Epoch {epoch+1}/{epochs}, Cost: {mean_epoch_cost:.4f}\")\n",
"\n",
" print(\"Training completed.\")\n",
" return costs\n",
"\n",
" def predict(self, X):\n",
" AL, _ = self.forward_pass(X)\n",
" predictions = np.argmax(AL, axis=0)\n",
" return predictions"
],
"metadata": {
"id": "SjHNxEsHCu2H"
},
"execution_count": 73,
"outputs": []
},
{
"cell_type": "code",
"source": [
"X_train, Y_train, X_test, Y_test, y_test_orig = load_and_prepare_data_cnn()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "pOhLOzN9OMf6",
"outputId": "0c08e4f4-d9b9-4822-8601-3991d75ca189"
},
"execution_count": 42,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading MNIST dataset...\n",
"Data loaded and prepared.\n",
"Initializing CNN...\n",
"CNN initialized.\n"
]
}
]
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "hQTuk_x8P7YD"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"cnn_model = CNN()\n",
"SUBSET_SIZE = 1000"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "MhO-hXVEPBAo",
"outputId": "498e52d9-8f18-4f5f-89b7-104ab795266d"
},
"execution_count": 74,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Initializing CNN...\n",
"CNN initialized.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"print(\"Training on subset of data...\")\n",
"X_train_subset = X_train[:SUBSET_SIZE]\n",
"Y_train_subset = Y_train[:, :SUBSET_SIZE]\n",
"costs = cnn_model.train(X_train_subset, Y_train_subset, epochs=25, learning_rate=0.01, batch_size=8)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "c2PG1URXHR7a",
"outputId": "040f3c8c-b9c0-4c52-ce1a-dc2f6163455b"
},
"execution_count": 75,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Training on subset of data...\n",
"Starting training for 25 epochs...\n",
"Epoch 1/25, Cost: 2324.9798\n",
"Epoch 2/25, Cost: 2261.9634\n",
"Epoch 3/25, Cost: 2189.5778\n",
"Epoch 4/25, Cost: 2037.9250\n",
"Epoch 5/25, Cost: 1783.1098\n",
"Epoch 6/25, Cost: 1486.0922\n",
"Epoch 7/25, Cost: 1232.4294\n",
"Epoch 8/25, Cost: 1042.2106\n",
"Epoch 9/25, Cost: 902.0981\n",
"Epoch 10/25, Cost: 796.7148\n",
"Epoch 11/25, Cost: 714.8593\n",
"Epoch 12/25, Cost: 649.2456\n",
"Epoch 13/25, Cost: 595.2510\n",
"Epoch 14/25, Cost: 549.9257\n",
"Epoch 15/25, Cost: 511.3004\n",
"Epoch 16/25, Cost: 477.9693\n",
"Epoch 17/25, Cost: 448.9134\n",
"Epoch 18/25, Cost: 423.3421\n",
"Epoch 19/25, Cost: 400.6551\n",
"Epoch 20/25, Cost: 380.3701\n",
"Epoch 21/25, Cost: 362.0987\n",
"Epoch 22/25, Cost: 345.5339\n",
"Epoch 23/25, Cost: 330.4288\n",
"Epoch 24/25, Cost: 316.5674\n",
"Epoch 25/25, Cost: 303.7871\n",
"Training completed.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"print(\"Evaluating on test set...\")\n",
"SUBSET_TEST_SIZE = 200\n",
"X_test_subset = X_test[:SUBSET_TEST_SIZE]\n",
"y_test_subset_orig = y_test_orig[:SUBSET_TEST_SIZE]\n",
"predictions = cnn_model.predict(X_test_subset)\n",
"accuracy = np.mean(predictions == y_test_subset_orig) * 100\n",
"print(f\"Test Set Accuracy: {accuracy:.2f}%\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "UpBwdim8OSel",
"outputId": "4a697d2b-6a1a-401d-b22c-81f29df2cd91"
},
"execution_count": 76,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Evaluating on test set...\n",
"Test Set Accuracy: 88.50%\n"
]
}
]
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "J90kzxWNW6ah"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment