{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "396d9e19-0fe3-41ee-b144-71f17fbf696b",
   "metadata": {},
   "source": [
    "# Practical-3b (Convolutional Neural Network - MNIST Fashion Dataset)\n",
    "\n",
    "---\n",
    "\n",
    "Problem Statement: Convolutional neural network (CNN): Use MNIST Fashion Dataset and create a classifier to classify fashion clothing into\n",
    "categories.\n",
    "\n",
    "- Dataset link: https://www.kaggle.com/datasets/vipoooool/new-plant-diseases-dataset/data\n",
    "- Dataset available in [Datasets](https://git.kska.io/sppu-be-comp-content/DeepLearning/src/branch/main/Datasets/) directory.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "fd8945d9",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n",
      "I0000 00:00:1777821874.395594   86996 cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.\n",
      "I0000 00:00:1777821876.310503   86996 cpu_feature_guard.cc:227] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n",
      "To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
      "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n",
      "I0000 00:00:1777821879.617357   86996 cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.\n"
     ]
    }
   ],
   "source": [
    "# 1. Import Libraries\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Input, Conv2D, AvgPool2D, GlobalAveragePooling2D, Dense\n",
    "from tensorflow.keras.utils import to_categorical\n",
    "from sklearn.metrics import confusion_matrix, classification_report"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "859cbc0f",
   "metadata": {},
   "outputs": [],
   "source": "# 2. Load Dataset\n# Fashion MNIST is built into Keras — downloads automatically on first run\n(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n\n# --- Offline alternative (comment out tf.keras line above and use this instead) ---\n# import pandas as pd\n# train_df = pd.read_csv('fashion-mnist_train.csv')\n# test_df  = pd.read_csv('fashion-mnist_test.csv')\n# y_train = train_df['label'].values\n# y_test  = test_df['label'].values\n# X_train = train_df.drop('label', axis=1).values.reshape(-1, 28, 28)  # unflatten pixels to 28x28\n# X_test  = test_df.drop('label', axis=1).values.reshape(-1, 28, 28)\n\nprint(\"Training set shape:\", X_train.shape)   # (60000, 28, 28)\nprint(\"Test set shape:    \", X_test.shape)    # (10000, 28, 28)\nprint(\"Classes:\", np.unique(y_train))"
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "763c59ad",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 39464 characters]",
      "text/plain": [
       "<Figure size 1000x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 34184 characters]",
      "text/plain": [
       "<Figure size 1500x300 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 3. Exploratory Data Analysis (EDA)\n",
    "class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',\n",
    "               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\n",
    "\n",
    "# Class distribution\n",
    "unique, counts = np.unique(y_train, return_counts=True)\n",
    "plt.figure(figsize=(10, 4))\n",
    "plt.bar([class_names[i] for i in unique], counts)\n",
    "plt.xticks(rotation=45, ha='right')\n",
    "plt.title(\"Training Set Class Distribution\")\n",
    "plt.ylabel(\"Count\")\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# Sample images (one per class)\n",
    "plt.figure(figsize=(15, 3))\n",
    "for i, cls in enumerate(class_names):\n",
    "    idx = np.where(y_train == i)[0][0]   # index of first image for this class\n",
    "    plt.subplot(1, 10, i + 1)\n",
    "    plt.imshow(X_train[idx], cmap='gray')\n",
    "    plt.title(cls, fontsize=7)\n",
    "    plt.axis('off')\n",
    "plt.suptitle(\"Sample Image per Class\")\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d9b48211",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X_train shape: (60000, 28, 28, 1)\n",
      "y_train_cat shape: (60000, 10)\n"
     ]
    }
   ],
   "source": [
    "# 4. Preprocess Data\n",
    "# Reshape to add channel dimension: (samples, 28, 28) -> (samples, 28, 28, 1)\n",
    "X_train = X_train.reshape(-1, 28, 28, 1).astype('float32') / 255.0  # normalize to [0,1]\n",
    "X_test  = X_test.reshape(-1, 28, 28, 1).astype('float32') / 255.0\n",
    "\n",
    "# One-hot encode labels: e.g. class 3 of 10 -> [0,0,0,1,0,0,0,0,0,0]\n",
    "y_train_cat = to_categorical(y_train, num_classes=10)\n",
    "y_test_cat  = to_categorical(y_test,  num_classes=10)\n",
    "\n",
    "print(\"X_train shape:\", X_train.shape)\n",
    "print(\"y_train_cat shape:\", y_train_cat.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "44799339",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E0000 00:00:1777822594.307817   86996 cuda_platform.cc:52] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">Model: \"sequential\"</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1mModel: \"sequential\"\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\"> Layer (type)                    </span>┃<span style=\"font-weight: bold\"> Output Shape           </span>┃<span style=\"font-weight: bold\">       Param # </span>┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ conv2d (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Conv2D</span>)                 │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">28</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">28</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">64</span>)     │           <span style=\"color: #00af00; text-decoration-color: #00af00\">640</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ average_pooling2d               │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">14</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">14</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">64</span>)     │             <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │\n",
       "│ (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">AveragePooling2D</span>)              │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ conv2d_1 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Conv2D</span>)               │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">14</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">14</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">32</span>)     │        <span style=\"color: #00af00; text-decoration-color: #00af00\">18,464</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ average_pooling2d_1             │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">32</span>)       │             <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │\n",
       "│ (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">AveragePooling2D</span>)              │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ global_average_pooling2d        │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">32</span>)             │             <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │\n",
       "│ (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">GlobalAveragePooling2D</span>)        │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Dense</span>)                   │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">10</span>)             │           <span style=\"color: #00af00; text-decoration-color: #00af00\">330</span> │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1mLayer (type)                   \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape          \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m      Param #\u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ conv2d (\u001b[38;5;33mConv2D\u001b[0m)                 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m64\u001b[0m)     │           \u001b[38;5;34m640\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ average_pooling2d               │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m64\u001b[0m)     │             \u001b[38;5;34m0\u001b[0m │\n",
       "│ (\u001b[38;5;33mAveragePooling2D\u001b[0m)              │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ conv2d_1 (\u001b[38;5;33mConv2D\u001b[0m)               │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m32\u001b[0m)     │        \u001b[38;5;34m18,464\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ average_pooling2d_1             │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m32\u001b[0m)       │             \u001b[38;5;34m0\u001b[0m │\n",
       "│ (\u001b[38;5;33mAveragePooling2D\u001b[0m)              │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ global_average_pooling2d        │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m32\u001b[0m)             │             \u001b[38;5;34m0\u001b[0m │\n",
       "│ (\u001b[38;5;33mGlobalAveragePooling2D\u001b[0m)        │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense (\u001b[38;5;33mDense\u001b[0m)                   │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m10\u001b[0m)             │           \u001b[38;5;34m330\u001b[0m │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Total params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">19,434</span> (75.91 KB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m19,434\u001b[0m (75.91 KB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">19,434</span> (75.91 KB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m19,434\u001b[0m (75.91 KB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Non-trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> (0.00 B)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 5. Build the CNN Model\n",
    "model = Sequential()\n",
    "\n",
    "model.add(Input(shape=(28, 28, 1)))                                          # input: 28x28 grayscale image\n",
    "model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')) # 64 filters, extract features\n",
    "model.add(AvgPool2D(pool_size=(2, 2)))                                        # downsample to 14x14\n",
    "\n",
    "model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', padding='same')) # 32 filters, refine features\n",
    "model.add(AvgPool2D(pool_size=(2, 2)))                                        # downsample to 7x7\n",
    "\n",
    "model.add(GlobalAveragePooling2D())                                           # average each feature map to single value\n",
    "model.add(Dense(10, activation='softmax'))                                    # output: probability for each of 10 classes\n",
    "\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0fbd8434",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 6. Compile the Model\n",
    "# categorical_crossentropy: standard loss for multi-class one-hot classification\n",
    "model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "faf7009e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "W0000 00:00:1777822603.025974   86996 cpu_allocator_impl.cc:82] Allocation of 188160000 exceeds 10% of free system memory.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m34s\u001b[0m 18ms/step - accuracy: 0.5500 - loss: 1.2463 - val_accuracy: 0.6725 - val_loss: 0.9472\n",
      "Epoch 2/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m34s\u001b[0m 18ms/step - accuracy: 0.6951 - loss: 0.8683 - val_accuracy: 0.7125 - val_loss: 0.8282\n",
      "Epoch 3/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m34s\u001b[0m 18ms/step - accuracy: 0.7328 - loss: 0.7643 - val_accuracy: 0.7235 - val_loss: 0.7729\n",
      "Epoch 4/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m34s\u001b[0m 18ms/step - accuracy: 0.7576 - loss: 0.6995 - val_accuracy: 0.7400 - val_loss: 0.7323\n",
      "Epoch 5/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m35s\u001b[0m 18ms/step - accuracy: 0.7735 - loss: 0.6508 - val_accuracy: 0.7721 - val_loss: 0.6608\n",
      "Epoch 6/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m34s\u001b[0m 18ms/step - accuracy: 0.7856 - loss: 0.6153 - val_accuracy: 0.7830 - val_loss: 0.6361\n",
      "Epoch 7/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m35s\u001b[0m 19ms/step - accuracy: 0.7946 - loss: 0.5890 - val_accuracy: 0.7875 - val_loss: 0.6060\n",
      "Epoch 8/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m35s\u001b[0m 18ms/step - accuracy: 0.8011 - loss: 0.5678 - val_accuracy: 0.8054 - val_loss: 0.5709\n",
      "Epoch 9/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m33s\u001b[0m 18ms/step - accuracy: 0.8078 - loss: 0.5480 - val_accuracy: 0.8115 - val_loss: 0.5513\n",
      "Epoch 10/10\n",
      "\u001b[1m1875/1875\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 16ms/step - accuracy: 0.8132 - loss: 0.5339 - val_accuracy: 0.8146 - val_loss: 0.5401\n"
     ]
    }
   ],
   "source": [
    "# 7. Train the Model\n",
    "# validation_data uses test set to monitor performance after each epoch\n",
    "history = model.fit(X_train, y_train_cat, epochs=10, validation_data=(X_test, y_test_cat))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6c522de6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 5ms/step - accuracy: 0.8146 - loss: 0.5401\n",
      "Test Loss:     0.5401\n",
      "Test Accuracy: 81.46%\n"
     ]
    }
   ],
   "source": [
    "# 8. Evaluate the Model on Test Data\n",
    "loss, accuracy = model.evaluate(X_test, y_test_cat)\n",
    "print(f\"Test Loss:     {loss:.4f}\")\n",
    "print(f\"Test Accuracy: {accuracy*100:.2f}%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "66a6f583",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 43872 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 42772 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 9. Plot Training vs Validation Accuracy\n",
    "plt.plot(history.history['accuracy'], label='Training Accuracy')\n",
    "plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\n",
    "plt.title('Model Accuracy Over Epochs')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.legend()\n",
    "plt.grid(True)\n",
    "plt.show()\n",
    "\n",
    "# 10. Plot Training vs Validation Loss\n",
    "plt.plot(history.history['loss'], label='Training Loss')\n",
    "plt.plot(history.history['val_loss'], label='Validation Loss')\n",
    "plt.title('Model Loss Over Epochs')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "plt.grid(True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "7feeda9d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 5ms/step\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 92956 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      " T-shirt/top       0.75      0.79      0.77      1000\n",
      "     Trouser       0.98      0.94      0.96      1000\n",
      "    Pullover       0.73      0.74      0.74      1000\n",
      "       Dress       0.73      0.87      0.79      1000\n",
      "        Coat       0.73      0.65      0.69      1000\n",
      "      Sandal       0.97      0.89      0.93      1000\n",
      "       Shirt       0.53      0.47      0.50      1000\n",
      "     Sneaker       0.83      0.97      0.89      1000\n",
      "         Bag       0.94      0.95      0.94      1000\n",
      "  Ankle boot       0.96      0.88      0.92      1000\n",
      "\n",
      "    accuracy                           0.81     10000\n",
      "   macro avg       0.82      0.81      0.81     10000\n",
      "weighted avg       0.82      0.81      0.81     10000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 11. Confusion Matrix and Classification Report\n",
    "y_pred = np.argmax(model.predict(X_test), axis=1)  # predicted class index\n",
    "\n",
    "cm = confusion_matrix(y_test, y_pred)\n",
    "plt.figure(figsize=(10, 8))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n",
    "            xticklabels=class_names, yticklabels=class_names)\n",
    "plt.title('Confusion Matrix')\n",
    "plt.ylabel('Actual')\n",
    "plt.xlabel('Predicted')\n",
    "plt.xticks(rotation=45, ha='right')\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"\\nClassification Report:\\n\")\n",
    "print(classification_report(y_test, y_pred, target_names=class_names))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "3ff29f35",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 4ms/step\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 44360 characters]",
      "text/plain": [
       "<Figure size 2000x400 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 12. Visualize Sample Predictions\n",
    "# batch predict all test images, then pick 10 random ones to display\n",
    "all_preds = np.argmax(model.predict(X_test), axis=1)\n",
    "random_indices = np.random.choice(len(X_test), 10, replace=False)\n",
    "\n",
    "plt.figure(figsize=(20, 4))\n",
    "for i, idx in enumerate(random_indices):\n",
    "    plt.subplot(2, 5, i + 1)\n",
    "    plt.imshow(X_test[idx].reshape(28, 28), cmap='gray')\n",
    "    predicted = class_names[all_preds[idx]]\n",
    "    actual    = class_names[y_test[idx]]\n",
    "    color = 'green' if predicted == actual else 'red'   # green = correct, red = wrong\n",
    "    plt.title(f\"P: {predicted}\\nA: {actual}\", fontsize=8, color=color)\n",
    "    plt.axis('off')\n",
    "plt.suptitle(\"Sample Predictions (Green=Correct, Red=Wrong)\")\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c717de5-87a3-4a7b-85ed-9dcac1bb9cd5",
   "metadata": {},
   "source": [
    "---"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}