{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fd998c20",
   "metadata": {},
   "source": [
    "# Deep Learning"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28588085",
   "metadata": {},
   "source": [
    "## 1. Linear Regression\n",
    "https://d2l.ai/chapter_linear-regression/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7eab6bb",
   "metadata": {},
   "source": [
    "### 1.1. Linear regression from scratch in NumPy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1a84b5f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/3: 2.8028630762446722\n",
      "2/3: 0.005944852663986381\n",
      "3/3: 6.104357869966443e-05\n",
      "\n",
      "w = [ 1.99956731 -3.39973415]\n",
      "b = 4.198903523007357\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# Define the true weights and bias of the model\n",
    "w_true = np.array([2, -3.4])\n",
    "b_true = 4.2\n",
    "\n",
    "# Construct a random generator, seeded for reproducibility\n",
    "rng = np.random.default_rng(seed=0)\n",
    "\n",
    "# Generate the inputs (from a standard normal distribution) and outputs (with some Gaussian noise)\n",
    "number_examples = 1000\n",
    "input_size = len(w_true)\n",
    "X = rng.normal(0, 1, (number_examples, input_size))\n",
    "y = np.matmul(X, w_true) + b_true + rng.normal(0, 0.01, number_examples)\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 3\n",
    "batch_size = 10\n",
    "learning_rate = 0.03\n",
    "\n",
    "# Initialize the weights and bias to recover\n",
    "w = rng.normal(0, 1, input_size)\n",
    "b = 0\n",
    "\n",
    "# Initialize an array for the mean loss over the minibatches of every epoch\n",
    "epoch_loss = np.zeros(number_epochs)\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Generate random indices for all the examples\n",
    "    example_indices = np.arange(number_examples)\n",
    "    rng.shuffle(example_indices)\n",
    "    \n",
    "    # Initialize a list for the mean loss over the examples of every minibatch\n",
    "    batch_loss = []\n",
    "    \n",
    "    # Loop over the examples in minibatches\n",
    "    for j in np.arange(0, number_examples, batch_size):\n",
    "        \n",
    "        # Get the indices of the examples for one minibatch\n",
    "        batch_indices = example_indices[j:min(j+batch_size, number_examples)]\n",
    "        \n",
    "        # Get the inputs and outputs for the current minibatch\n",
    "        X_batch = X[batch_indices, :]\n",
    "        y_batch = y[batch_indices]\n",
    "        \n",
    "        # Compute the predicted outputs\n",
    "        y_hat = np.matmul(X_batch, w) + b\n",
    "        \n",
    "        # Compute the loss between the predicted and true outputs\n",
    "        l = 0.5*np.power(y_hat-y_batch, 2)\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        batch_loss.append(np.mean(l))\n",
    "        \n",
    "        # Update the weights and bias using stochastic gradient descent (SGD)\n",
    "        w = w - learning_rate*np.mean(X_batch*(y_hat-y_batch)[:, None], axis=0)\n",
    "        b = b - learning_rate*np.mean(y_hat-y_batch, axis=0)\n",
    "        \n",
    "    # Save the mean loss for the current epoch\n",
    "    epoch_loss[i] = np.mean(batch_loss)\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: {epoch_loss[i]}')\n",
    "    \n",
    "# Print the predicted weights and bias\n",
    "print('')\n",
    "print(f'w = {w}')\n",
    "print(f'b = {b}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1cb9d9b",
   "metadata": {},
   "source": [
    "### 1.2. Linear regression from scratch in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "01b5b48f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/3: 3.0891919136047363\n",
      "2/3: 0.006769159343093634\n",
      "3/3: 6.386495078913867e-05\n",
      "\n",
      "w = tensor([ 1.9992, -3.3997], requires_grad=True)\n",
      "b = tensor([4.1992], requires_grad=True)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "# Define the true weights and bias of the model\n",
    "w_true = torch.tensor([2, -3.4])\n",
    "b_true = 4.2\n",
    "\n",
    "# Generate inputs and outputs\n",
    "number_examples = 1000\n",
    "input_size = len(w_true)\n",
    "X = torch.normal(0, 1, (number_examples, input_size))\n",
    "y = torch.matmul(X, w_true) + b_true + torch.normal(0, 0.01, [number_examples])\n",
    "\n",
    "# Define a function to read the dataset in random minibatches\n",
    "def batch(X, y, batch_size):\n",
    "    \n",
    "    # Generate random indices for all the examples\n",
    "    number_examples = X.shape[0]\n",
    "    example_indices = torch.randperm(number_examples)\n",
    "    \n",
    "    # Loop over the examples in minibatches\n",
    "    for i in range(0, number_examples, batch_size):\n",
    "        \n",
    "        # Get the indices of the examples for one minibatch\n",
    "        batch_indices = example_indices[i:min(i+batch_size, number_examples)]\n",
    "        \n",
    "        # Return the input and output for the current minibatch and continue the iteration in the function\n",
    "        yield X[batch_indices], y[batch_indices]\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 3\n",
    "batch_size = 10\n",
    "learning_rate = 0.03\n",
    "\n",
    "# Initialize the weights and bias to recover, requiring the gradients to be computed\n",
    "w = torch.normal(0, 1, [input_size], requires_grad=True)\n",
    "b = torch.zeros(1, requires_grad=True)\n",
    "\n",
    "# Initialize an array for the mean loss over the minibatches of every epoch\n",
    "epoch_loss = torch.zeros(number_epochs)\n",
    "        \n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Initialize a list for the mean loss over the examples of every minibatch\n",
    "    batch_loss = []\n",
    "    \n",
    "    # Loop over the examples in minibatches\n",
    "    for X_batch, y_batch in batch(X, y, batch_size):\n",
    "        \n",
    "        # Compute the predicted outputs\n",
    "        y_hat = torch.matmul(X_batch, w) + b\n",
    "        \n",
    "        # Compute the loss between the predicted and true outputs\n",
    "        l = 0.5*(y_hat-y_batch)**2\n",
    "        \n",
    "        # Compute the gradient on l wrt w and b\n",
    "        # (sum and not mean as the gradients will be divided by the batch size during SGD)\n",
    "        l.sum().backward()\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        batch_loss.append(l.mean())\n",
    "        \n",
    "        # Temporarily sets all the requires_grad flags to false\n",
    "        with torch.no_grad():\n",
    "            \n",
    "            # Update the weights and bias using SGD\n",
    "            # (use augmented assignments to avoid modifying existing variables)\n",
    "            w -= learning_rate*w.grad/len(l)\n",
    "            b -= learning_rate*b.grad/len(l)\n",
    "            \n",
    "            # Set the gradients to zeros to avoid accumulating gradients\n",
    "            w.grad.zero_()\n",
    "            b.grad.zero_()\n",
    "            \n",
    "    # Save the mean loss for the current epoch\n",
    "    epoch_loss[i] = sum(batch_loss)/len(batch_loss)\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: {epoch_loss[i]}')\n",
    "    \n",
    "# Print the predicted weights and bias\n",
    "print('')\n",
    "print(f'w = {w}')\n",
    "print(f'b = {b}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb27c36f",
   "metadata": {},
   "source": [
    "### 1.3. Linear regression using APIs in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "219ae16b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/3: 2.8517115116119385\n",
      "2/3: 0.0001156603466370143\n",
      "3/3: 0.00010338309220969677\n",
      "\n",
      "w = tensor([[ 2.0001, -3.3987]])\n",
      "b = tensor([4.1996])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from torch.utils import data\n",
    "from torch import nn\n",
    "\n",
    "# Define the true weights and bias of the model\n",
    "w_true = torch.tensor([2, -3.4])\n",
    "b_true = 4.2\n",
    "\n",
    "# Generate inputs and outputs\n",
    "number_examples = 1000\n",
    "input_size = len(w_true)\n",
    "X = torch.normal(0, 1, (number_examples, input_size))\n",
    "y = torch.matmul(X, w_true) + b_true + torch.normal(0, 0.01, [number_examples])\n",
    "\n",
    "# Define a function to read the dataset in random minibatches by using data iterator\n",
    "def batch(X, y, batch_size):\n",
    "    data_set = data.TensorDataset(*(X, y))\n",
    "    return data.DataLoader(data_set, batch_size, shuffle=True)\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 3\n",
    "batch_size = 10\n",
    "learning_rate = 0.03\n",
    "\n",
    "# Define the model with a fully-connected layer\n",
    "model = nn.Sequential(nn.Linear(input_size, 1))\n",
    "\n",
    "# Initialize the parameters\n",
    "model[0].weight.data.normal_(0, 0.01)\n",
    "model[0].bias.data.fill_(0)\n",
    "\n",
    "# Define the loss function (mean squared error, without the 0.5 factor)\n",
    "loss = nn.MSELoss()\n",
    "\n",
    "# Define the optimization algorithm (SGD)\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "\n",
    "# Initialize an array for the mean loss over the minibatches of every epoch\n",
    "epoch_loss = torch.zeros(number_epochs)\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Initialize a list for the mean loss over the examples of every minibatch\n",
    "    batch_loss = []\n",
    "    \n",
    "    # Loop over the examples in minibatches\n",
    "    for X_batch, y_batch in batch(X, y, batch_size):\n",
    "        \n",
    "        # Compute the predicted outputs\n",
    "        y_hat = model(X_batch)\n",
    "        \n",
    "        # Compute the loss between the predicted and true outputs\n",
    "        l = loss(y_hat, y_batch[:, None])\n",
    "        \n",
    "        # Save the loss for the current minibatch\n",
    "        batch_loss.append(l)\n",
    "        \n",
    "        # Set the gradients to zero\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # Computes the gradient\n",
    "        l.backward()\n",
    "        \n",
    "        # Performs a single parameter update\n",
    "        optimizer.step()\n",
    "        \n",
    "    # Save the mean loss for the current epoch\n",
    "    epoch_loss[i] = sum(batch_loss)/len(batch_loss)\n",
    "        \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: {epoch_loss[i]}')\n",
    "    \n",
    "# Print the predicted weights and bias\n",
    "print('')\n",
    "print(f'w = {model[0].weight.data}')\n",
    "print(f'b = {model[0].bias.data}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02d77f99",
   "metadata": {},
   "source": [
    "### 1.4. Linear regression using higher-level APIs in Keras"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "1ef4ff12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/3\n",
      "100/100 [==============================] - 0s 469us/step - loss: 2.8948\n",
      "Epoch 2/3\n",
      "100/100 [==============================] - 0s 413us/step - loss: 1.1406e-04\n",
      "Epoch 3/3\n",
      "100/100 [==============================] - 0s 449us/step - loss: 1.0830e-04\n",
      "\n",
      "w = [[ 2. ]\n",
      " [-3.4]]\n",
      "b = [4.199775]\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "# Define the true weights and bias of the model\n",
    "w_true = tf.constant([2, -3.4], shape=[2, 1])\n",
    "b_true = tf.constant(4.2)\n",
    "\n",
    "# Generate inputs and outputs\n",
    "number_examples = 1000\n",
    "input_size = len(w_true)\n",
    "tf.random.set_seed(0)\n",
    "X = tf.random.normal([number_examples, input_size], 0, 1)\n",
    "y = tf.matmul(X, w_true) + b_true + tf.random.normal([number_examples], 0, 0.01)\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 3\n",
    "batch_size = 10\n",
    "learning_rate = 0.03\n",
    "\n",
    "# Define the model with a densely-connected NN layer with initialized parameters\n",
    "model = tf.keras.Sequential([tf.keras.layers.Dense(1, \\\n",
    "                                                   kernel_initializer=tf.initializers.RandomNormal(mean=0, stddev=0.01), \\\n",
    "                                                   bias_initializer='zeros')])\n",
    "\n",
    "# Configure the model for training with SGD optimizer and MSE loss\n",
    "model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate), \\\n",
    "              loss=tf.keras.losses.MeanSquaredError())\n",
    "\n",
    "# Train the model given the batch size and number of epochs\n",
    "model.fit(x=X, y=y, batch_size=batch_size, epochs=number_epochs, verbose=1)\n",
    "\n",
    "# Print the predicted weights and bias\n",
    "print('')\n",
    "print(f'w = {model.get_weights()[0]}')\n",
    "print(f'b = {model.get_weights()[1]}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be5ccf99",
   "metadata": {},
   "source": [
    "## 2. Softmax Regression\n",
    "https://d2l.ai/chapter_linear-classification/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15f958cf",
   "metadata": {},
   "source": [
    "### 2.1. Fashion-MNIST dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6b555412",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA/4AAAB3CAYAAAC6y5tAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABFSklEQVR4nO2dd9hcVbn27wVGKdIDhJaETiCEECChSigHSKTaEA5Nvw9BDyjgQRFFOYhSDkj5EPRCRSmhd4QImNAJGlqAkNBDgEAIXRRF2N8fe2bltzezJvO+edvs3L/rypXnndmz95rV9+z7eZ6QZZmMMcYYY4wxxhhTTRbq7QIYY4wxxhhjjDGm+/CNvzHGGGOMMcYYU2F842+MMcYYY4wxxlQY3/gbY4wxxhhjjDEVxjf+xhhjjDHGGGNMhfGNvzHGGGOMMcYYU2Ha4sY/hPBCCGHHxHvbhBCm93SZjGkHQggHhRDuwd9ZCGGt3iyTMcb0NK3OfSGEwbVjP9UT5aoS5fWmwfu3hBAO7Mkymc7RbN9tTLvSbB3o7P54XvNeX6Nbb/xDCH/Dv49DCP/A3//ZFdfIsuzuLMvWnUc5Gk5gIYR9QgjjvNAX6Yl2Mx2n1o/rbfFaCOH3IYTP9na5TBq02XshhLdDCPeFEA4NIbTFj64LEiGEfUMIk2vja1btJmXr+TznHSGE/9tVZawiIYSta+PinRDCmyGEe0MIm/V2uUyazrZZlmVjsiz7Q5PzttUGuqfwGKkGpT3cWyGEP4YQVuvtcrUjtbX1rRDCZ3q7LN1FCGF0COGlrj5vt24+syz7bP2fpBcl7YbXLunOa0tSCzfyn5d0c3eXo91otd36wg8lfaEMPcxutXYZIWlTST/q5fI0ZQFsn0bslmXZEpIGSTpZ0vcl/bbRgSGEhXuyYCYnhHCUpDMl/VzSipIGSjpX0h69WKzKE0JYUtJNkv6fpGUlrSLpfyT9szfLZdJ0V5t5rWhMO48Rt2lD6nu4lSS9prxdTQcIIQyWtI2kTNLuvVua9qPPPHUKIfQPIdxUeyr2Zgjh7tJTseEhhCm1XzwvDyEsUvtc4ReR2i9q3w8hTJH0fgjhUuWbuBtrv7J9r3bcQpL+Q9J4SXfVPv527ZgtQggLhRB+FEKYEUKYHUK4MISwVO2zdYXAN0IIr9SeDv1399dS71Kv61r9virpghDCZ0IIZ9bq4ZWa/Zna8Z/49T5AShNCGBtCmFp7Gvoy6zCEsGsI4RE8JR2G98ptvMAtLlmWvSzpFklDQ0mt0upTxhDCUrV+/Xqtn/+o1u8/U6v3oTh2+dov1SvU/nb7dJAsy97JsuwGSXtLOjCEMDTkqo3zQgg3hxDel7RdCGHlEMLVtXZ5PoTw7fo5QggjQ/5U+t2Qqz5+UXt9kRDCxSGEN2pt8tcQwoq99FXbitq8foKk/8qy7Josy97PsuzDLMtuzLLs6HnMccvU1q3XQ/704aYQwqq1936mfHNyTm1dOaf3vmWfZR1JyrLs0izLPsqy7B9Zlt2aZdmUEMKaIYQJtT49J4RwSQhh6foHa/PMf4cG+4La+0fX1uZXQghf50VDCJ8PITxcG0czQwjH99QXrgDJNqsfEEI4rTYeng8hjMHrcW2q7Q/uDSGcEUJ4Q9Llkn4laYvaeHm7Z79Wn6XZGDkohHBPk/peKoTw29o4eDmEcGKo/bg8r/FFQghDaufep/a31//5JMuyDyRdJWl9ad5zUgjhgJDv094IIRwXFmxXjAMkTZL0e0kF16HanuqXIVdTvBdCeCCEsGajk4RcSTMzhDC6wXufqY2rF2t7rV+FEBZtUqYQQjinthZNCyHsgDdWDiHcEPJ722dCCAeXrvOJ/UUIYXHle/yVw1zF9codqKMkfebGX9J3Jb0kaXnlT1yOVf5rTp2vSNpF0uqShkk6qMm59lH+NH/pLMv2UfGp9am1Y0ZKei7LsjmSPld7benaMffXzn+QpO0krSHps5LKG7ftJK0taSdJ319ABuEA5b86D5L0DUk/lLS5pOGSNlJer60+hf6tpENqT0OHSpogSSGEjSX9TtIhkpaT9GtJN4SipIdt/O/5+0rtR8jlYWMlvTUfp/l/kpZS3r+3VT6Zfi3Lsn9KukZ5Hdf5iqQ7syyb7faZP7Is+4vyuW6b2kv7SvqZpCUk3SfpRkmPKn+ys4OkI0IIO9eOPUvSWVmWLSlpTUlX1F4/UHlbrqa8TQ6V9I9u/zLVYAtJi0i6NvF+szluIUkXKJ8PByqv83MkKcuyH0q6W9JhtXXlsG4qfzvzlKSPQgh/CCGMCSEsg/eCpJMkrSxpiPK+fXzp8w33BSGEXST9t/If99eWVF6b31c+3y2tfJ76Zghhzy76TlWnWZtJ0ihJ0yX1l3SqpN+GEELiXKMkPad8z7ef8nnr/tp4WbpbSt9+zE99/17SvyWtJWlj5XvV+kOBVsaXQggjJP1J0uFZll3q9b9rCCEspvwhwKTaS8k5KYSwvnIF2n8qVwospXx/sKBygKRLav92Dp98yPFV5aqYZSQ9o3x/VaC2Rlwq6YtZlt3R4BonK//Rbbjy8bOKpB83KdMoSc8qH4c/kXRNCGHZ2nuXKd/zrSzpS5J+HkLYvvZew/1FlmXvSxoj6RUorl9pcv3WybKsR/5JekHSjk3eP0HS9ZLWSnx2P/x9qqRf1ezRkl4qHfv1eV1b0k8lHVezByv/keFTeP/Pkr6Fv9eV9KGkT+H49Upl+m1P1WdvtFutrv8laRG8/6yksfh7Z0kv1OyDJN1TOl9Wb2PlP8gcImnJ0jHnSfpp6bXpkrZNtfGC8K/2vf8m6W1JM5QvBEMa9N07JP3fRm1Qr39JC9facn28d4ikO2r2jpKexXv3SjrA7dOpNvvEvKd8sf+h8o3ZhXh9lKQXS8f+QNIFNfsu5Qta/9IxX1f+o8Gw3v7O7fZP+Wbq1SbvJ+e4BscOl/QW/o5j0f+S9TukNg5eUn6TcoOkFRsct6ekh/H3C0rvC34n6WS8tw7XngbnPlPSGTV7cHlO9b/W2qy23jyD4xar1eWA2t/ltak81xXWK//rfH3X3v+npEXx/j6SJiau0Wh8/U/tmqPxutf/zrfjC5q7h/tQ0iuSNkwcyznpx5IuLbXzv9Tknqqq/yRtXau7/rW/p0k6Eu//XtJv8PdYSdPwd6Z8TzVD0tDSuev746D8h5g18d4Wkp5PlOmgWlsGvPYXSfsr/0HtI0lL4L2TJP2+Zje7hxot3N921b9eeeIfQhgI6cLfai//r/JfZm4NITwXQjim9LFXYf9d+RP4FDNbKMZYNffvX1l5x6gzQ/lNP39Zmll6v0tkGH2c17NcolSnUT21Wg9fVN4OM0IId4YQtqi9PkjSd2sysrdrkr/VSudtpY2ryJ5Zli2dZdmgLMu+pc4/1e0vqZ8+2Xb1X5EnSloshDAq5P5UwzX3iajbZ/5ZRdKbNZt1NUi5tIt1e6zmzjv/R/lNzLSQy/l3rb1+kfKnMpfV5GKnhhD6dfu3qAZvSOrfRJKanONCCIuFEH5dk2C+q/yHmaWDYzW0TJZlT2ZZdlCWZasqV36tLOnMEMKKIYTLQi5RflfSxcrnLZLaF6ysT67Pkdq8NjHkLhrvKH/SXD63SZBqs9rbr+K4v9fM1H7N60QLdLK+Bylf42dhLfm1pLq7Xivj61BJ92XFJ6Je/+ePPbNczbKIpMMk3RlCGDCPOakwn9Xa+Y0eLndf4UBJt2a5WluSxqkk99e87xePkHRFlmWPJ66xvPIfVx5EHx9fez3Fy1ntbr1GfZ+wsqQ3syx7r/Refa89P/dQnaJXbvyzLHsxKwaQU5Zl72VZ9t0sy9ZQHqzhKPpIdPQSzf4OIQxQLpd5KHG8lP96Mwh/D1T+S+treG210vtdI8Po25TrqlE91evhfeWDR1Ks97knyrK/Zlm2h/KF6DrNlS3PlPSz2g1u/d9iWZZd2qQcCyrv1/5fDK8NaHRgiTnKfzUtt93LkpRl2UfK22Of2r+bMHG5feaDkEdjXkVSPf4F62qm8l+VWbdLZFk2VpKyLHs6y92XVpB0iqSrQgiLZ7lP+v9kWba+pC0l7apcDmfmzf3Kn4ztmXi/2Rz3XeVqsFFZ7n5RdxurS209DjpAlmXTlD+xGao80GKm/InYksql4CnJeJlZ+uT6TMYpf2q6WpZlSyn3LW/13AaU2qzDH5/H36ZEB+p7pvJ5rT/WkiWzLNug9n4r4+tQSQNDCGeUzuv1fz7J8ngN1yh/Gry1ms9JsyStWv9szdd8uZ4tce9T+95fkbRtCOHVkMcaO1LSRiGEjTpwqi9L2jOE8J3E+3OUP1TbAH18qfr9aoJVSi5N9X3CK5KWDSEsUXrv5ZrdbH/RLeOoz/j4hzxYyFq1intH+WD4uItO/5pyP+Y6YySNx68zr9euxWMulXRkCGH1kKdM+7mky7Oiv9JxtSc+G0j6mvLgNAsal0r6UciDv/VXLkm6uPbeo5I2CCEMD3nQpePrHwohfDqE8J8hhKWyLPtQ0rua297nSzq09gtoCCEsHvLAJxw4RlKWZa8rn0D2CyEsHPIgVg0DmZQ+V7+x/1kIYYkQwiBJR2lu20n5QrS3cin0OLzu9ukEIYQla0/oL5N0cZZljzU47C+S3gt5cKRFa206tPZjgUII+4UQls+y7GPlckFJ+jiEsF0IYcPak+Z3lf+o01XzZ6XJsuwd5fPWL0MIe9bm9H4h96c9Vc3nuCWUbxDeDrk/309Kpy+vPQaEENYLIXw3zA2IuJryHxonKa/bv0l6J4SwiqSjO3DqKyQdFEJYP+S+tOV2WUL5U5gPQggjlcfZMC0wjzabX16TtGoI4dNdcK5K0Nn6zrJslqRbJZ1eW3sWCnlAv21rh7Qyvt5THkPjcyGEk2uvef3vAmp1t4dyP/Qn1XxOukrSbiGELWtj43gtmD9U7qn83nB95SrU4crdYO5Wxx50vKI8ftJ3QgjfLL9Z21+dL+mMMDeg9SphbqylRqwg6du1vcOXa+W6OcuymcrdME8KeRDmYcqVm/U9RLP9xWuSlgu1wPJdRZ+58VcegOd25RPR/ZLOzbJsYhed+yTlFft2yCPHF9L41WQzP5N0b+2YzZX7CF6kXLr5vKQPJB1eOu+dyt0T/izptCzLbu2i8rYTJ0qaLGmKpMeUqyhOlKQsy55SHrvhdklPa+4Tzjr7S3oh5DKzQ5XfYCrLssmSDlYeJOst5XV8UDd/j3bmYOWL9huSNlA+ybTC4coVA88pb5txyvu9JCnLsgdq76+sPLpo/XW3T8e4MYTwnvInJT+U9AvlPxR+gtoPMrsqX9CeV/7L82+UB/OR8k3YEyF3kTpL0lezLPuHcpXHVcpv+p9UPjdd1E3fp3JkWXa68h++fqT8h+CZymWY16nJHKdcbruo8naapFwOSM6S9KWQR9w+u1u/RHvynvK4Fg+EPKvFJEmPK1dS/I/ytKXvSPqj8oCjLZFl2S3K22aC8vlpQumQb0k6oTYuf6y5ajMzb5q12fwyQdITkl4NIcyZ18ELCPNT3wdI+rSkqcrX6quUq12lFsdXlmVvKw+SOSaE8FOv//PNjbX1+13l9x0HZln2hJrMSbX3D1f+0GCW8vuk2WqDlI5dzIHK4x29mGXZq/V/yvvif4YOZJDIsuxF5Tf/x4TGWbC+r7xvT6rdo9yuXN2X4gHl97FzlLfrl7Isq7tj7KM8dswryl1mf5Jl2e2195rdQ01T/sPAc7V70y5xAQhFl4TqU+sYr0paI8uydzt5jsHKN+X9MkcsNcYYY4wxxnQzNRXy25LWzrLs+V4ujmkz+tIT/55iWeXR/Dt102+MMcYYY4wxPUEIYbeaG9rikk5T/nT4hd4tlWlHFrgb/yzLZmdZdl5vl8MYY4wxxhhj5sEemhssbm3lbn4LlmTbdAkLnNTfGGOMMcYYY4xZkFjgnvgbY4wxxhhjjDELEr7xN8YYY4wxxhhjKkzLqQ8kqX///tngwYO7qSjN+eCDD6L94osvRnuZZZaJ9mKLLRbtEEJDm+d56623ov2Zz3wm2gMGDIj2wgsvPD/FnicvvPCC5syZM1/5OHu6Xd5///1ov/HGG9H+1KfmdifWG+v/3/9unATh05+em7b373//e8PjP/zww2ivu26zrBrzTzu2C+vq3Xfnxq6cM2duViS2yyKLLBLthRaa+xsgz8O2XnzxxaO9yiqrNPxsd9Mu7fLPf87NsvO3v/0t2m+//Xa02RbLLbdctDmPpear9957L9qs/2WXXTbayy+/fGeK3inapV1SfPzxx9H+xz/+0dBOrS+cl/r16xftRRddtMvL2VHavV2qzIMPPjgny7L5GqQ90TZ0B/3Xv/4VbY4Nrg0cAx0ldf6llurSNNZN8Zjpm7hd+iZul75Js3bp0I3/4MGDNXny5E4VgosHN02t8uSTT0b7sMMOi/ZXvvKVaG+88cbR5o0kb0ifeOKJaF977bXRXmONNaL9ve99L9pLL710h8vaETbddNP5Psf8tEtn+Otf/xrtCy+8MNq8eVliiSWizfrnTSj7wcCBA6P9yCOPRHv27NnRfv3116M9ceLEzhS9ZXqjXeZ3jLCuJkyYm7b6/PPPjzb785AhQ6LNH754g3n//fdHe/PNN4/2z3/+82i3eoMzv99Pap/x8vzzczPs3HnnndG+/vrro82b9P333z/aI0aMiPa0adOiffXVV0f79ttvjzY33fvtt1+0v/GNb3Sq7J2hr7YLb+il9I9U/HGGawTtYcOGRZvjZdasWdFeccUVo73RRhs1vFZXjINW6avtYqQQwoz5PUcrbcMxwP6fer0Mb8b50IVjY9SoUdHmg5OOMmPG3CqZOnVqtHfZZZdotzpmWv1+ZTxm+iZul76J26Vv0qxdOnTj3wqpTU1qsn744YcLf19++eXR5kaXT8e4STv22GOj/eabb3aorOuss060H3300WifdNJJ0eYitvPOOxc+/93vfjfaG264YYeu3c7ccccd0X788cejzTbmjQ/bizf+VGvwF33enPbv3z/aL7zwQqfL3Fdp5SaAdXbWWWcV3uMNIJ8O82aQGzf+aHPNNdc0vB6f2PDJ/gMPPBDtLbfcMtq8gd12222jffjhhxfOy/auArfccku0zzjjjMJ7/DGE9U+VBfvzV7/61Wi/9tpr0eav4PwBbaWVVoo2x85VV10V7TPPPDPaO+64Y7TPPvvsT36ZitJswz99+vRoU0Hx1FNPRXvKlCnRZj2zL1PFwTHIsT18+PBod/fNvjGE/a2Vm+FDDjmk8DfVS/zBi/MU16WUGoYPZvg0n/Mab/b58GD8+PHR5njbfffdC2X94he/GO3O/MhhjDFVxzOgMcYYY4wxxhhTYXzjb4wxxhhjjDHGVJgul/qnZIwMNnbAAQdEmxJ7qSiP/OxnPxttSmcps6QLAIOSvfPOO9FmUKZU0DkycuTIaFO6ed999xWOo+R96623jvbFF1/c8LxVgQHfVl999WjT1WK11VaLNmV2DMpHCSGPodSfMnIeT5l0FYOCPPvss9Heddddo132n2RdUaLPfk55Jv1+6IKROp4ydcZY4Fhju9x2223RvvfeewtlpYT0C1/4gtoRtsu4ceOiXXb1oZQ1JTPlGFlyySUbXo9zFNuIx7PdKZvdYostov3SSy9Fmy5KknT66ac3vHYVYfuxTgYNGhRt+uyzb9N/n3NOKkgjJcn0X+wKn8gFHe4TUuMrtb7zs6QzLhjcE9D9iW4kdCnsDTcPft+UzP0HP/hBtBnjRZJWXnnlaHM94PzF/RbHD12YvvnNb0abcxPHFa9FNz+6DHA/d8UVVxTKyhgERx55ZLRTbW6MMQsafuJvjDHGGGOMMcZUGN/4G2OMMcYYY4wxFabLpf4p9tprr2hTjkWZl1SUwn300UfRppyS8BjKuSi55DGkFfkXXQwYkbtc1rvvvjvaTD3IlGlVgVGvKf+mdJzuALRXWGGFaFMuTikfI2yzjXj8XXfdFe12lvqnpJ+UXjKCezkyPuuE56Lkm3XINqKkPyXvZ9ulJOUcF5Td8jyS9Mtf/jLaO+20U7Tp0tPXoSx++eXTKbhZD3QX4jzGOqTLDKPH87NsX0rQCc/JMcUxwkwcknTTTTdFm24lVYTye6497P+rrrpqtC+66KJoM/3r2LFjo82MCZzveX66JtENpNVUmKY1WpHSd1RuT5c+SXrsscei/fTTT0ebGYY45956663RZj/rKVKuEM8991y0OSdQwi8V5xrWHc/F7C88nnu9K6+8MtqU61PSTxcm7tt4Ldp0DZCKbZPaP7ayrzTVpT42e8LtJpW1KfV6qs+38tmOvm6a00q98V7lnnvuifaYMWPmeU62NfdtnSkfaaWN/cTfGGOMMcYYY4ypML7xN8YYY4wxxhhjKky3Sv0ffPDBaFPyRWkXpcplKIl8+eWXG75OGRvlEinJDKEUmTLmJZZYItqUfTaTY/Aav/nNb6JdxYjZc+bMiTalLpSFM8ovI/OnXDP42ZSkmX2lHHm4CjAa8quvvhptyh8p35aKffLvf/97tFmfKWkjbfZfyst5Th6TyiBA2X7ZNYZluuGGG6K97777ql046KCDon3GGWdEuyz7p8ybY4T1Rj796U9Hm+4zhP2AUtkUPCcl7pzTpOrJ+7kmUM4sFV1dHnnkkWhT3kzZ8jPPPBNt1ifXjldeeSXajPLONY/ZBFj/++yzT8PXF2RakVimsl2kuPDCC6O9+eabR5suemeffXa0KSEvZx5ilP4RI0ZE+8wzz4z28OHD51mmniK1b/nzn/8cbc7tnPOl4jye2q9xjqNrGueyG2+8MdqsH45J7u1S6w3Hd1nuyvWRbTt69OjkZ8yCRaM5hS4i7Gvsm53JxtLK/EVamcs6ek7L+zsH5xm2C/cEvNej297iiy8ebc6fzBjX7H4ylbWGr6c+n3JtJ37ib4wxxhhjjDHGVBjf+BtjjDHGGGOMMRWmW6X+EydOjDYl25QSl2X4lDUwAu6pp54abUrJKNGk5JLH8JyU8VCuSUnPQw89FG3K/8pyXsrK+D2uvvrqaFdR6k8ZP+uZdTB16tRoU5Zfln/XScnvKGnmMTx/VWA9UepPmVE5mjtlmTyOfZvtkpIQUQ6WknOmIsVSckRpJ116ymW6/fbbo91OUn9KtbbYYotoX3/99YXjRo0aFW3WJ9uLLjCUkXOe4XjhZzn3MAvA7NmzG5abEtqTTz654TFVgfJ+yu2l4nyy1lprRXvKlCnRZhsPGDAg2ozMTxkxj//LX/4Sba5N22+/fbQ5Tu+9995oU0IuSRtvvLFMx2BGHY47RuafPHlytN98881oH3jggdHedttto005f/nztDmGKQdlP+tLcA1Nud1Jxe/FNYOk1h7u4egGljomtafiPMj9B/eSUnGNYqYCSv07E0HbVIOPP/44rqNXXHFFfJ2uh8OGDYs2+yAzSQ0cODDadKOTpHfffTfaa6+9drS5N0plBOK5OC5YDkq5ec6ll1462qn7qDIcLxyTnDu55+T1vv71r0v6pPtpVUi5yE6YMCHat912W7S53rPOuG9jhpeDDz442s2y26XcP3jPyv7Rihuon/gbY4wxxhhjjDEVxjf+xhhjjDHGGGNMhelWzdNVV10VbcoVUpH4paIsghJWyiIol2DmgLr0RJJ+/etfR3uDDTaINqVhlHKssMIK0T7yyCOjfe6550a7LGnhuRjFcdq0adF+6qmnol2WcrYTlK5QysS6pSSQUhXKl5idgVKVVMRyysUph2EE/KpAuTGlVpT9l6WW/JtySEalXnPNNaM9ePDgaLOeUxFJ6RrDPsAouIzWzPOUJXBs77KctB359re/HW1G9JakQYMGRZuyPtYt65/9n7Af8Dx8nfMSz0NJ7JgxY+Z5rarAfsd5vfwe56iddtop2qwf9m0ew7WDMn6uZ2wjSsrZB9h25TmNMlHKpKtOK1GouU9gJgW6ZnD/wL0Bs3Ewg8NRRx0VbbrMlMuz3nrrRZtugZR9ci7uq1J/Zppgvy3vc+gmxO/F9T7lTsYxwGvwGO4NeQxtrj28FstWPm8qO4pZcHnnnXfinM6sLieeeGK06cY1fvz4aLPvMyvF888/X7gG90z3339/tLmXfe2116LNLFncE3C95z3Fcsst1/AY7sm4D6MLQFn2T/eFN954I9r8fpzvuG97+umnJX3S3aYqcH4jf/3rX6NN9z/uxWlz3/Dwww9H+3vf+160yxkjNtxww2gPGTIk2nQlZDm23HLLaNddUJtF9/cTf2OMMcYYY4wxpsL4xt8YY4wxxhhjjKkw3Sr1f/TRR6PNiIeUIJSjlBNKVcnOO+8cbUogGdH3tNNOi/Zee+0VbUo3KUNjBGXK9yg3o7xQKkrOaPO7UurTzlJ/SlWXWGKJaFNqRGkdI4Sy3iibpExvq622ijbrkjJASq1SWQDama9+9avR3mabbaJ9ySWXRJuRiiXp2GOPjTYlWSnYFqx/2pRzpdxZGIn/pJNOivZmm20WbbooSEUZGyOvtxMp6Sqjs0vSD3/4w4afZx1QEsj6p0yPcyWPoWQvFWmbr++2224Nj6kKrBvOPWVXMvZhfoZzF/s8XTbY9ozkT7n4E088EW22b0oGyHOW2/Gll16Kditjuyqwz3Mt4NpBtyGOBc6PjORP1z/Kd7mXIGUXEUI3AGbmoBvb7373u2hzbRs6dGjyvD0BZfzcO9F9ryxx5ffi3obrcapPE45Lwj1gKoI14fjkvqRcvnZdY0z30a9fv+gGyb7G7ByUU9NdiDYl8swAIhXHy4UXXhjtXXbZJdqUiLMce++9d7Q5z3Dfxj7P13n/Q+k3XQPoeiwVM0lxraS7G9dGukF87Wtfk1Rc59od3ldwvaEbF/sK64n7ZtYzbe6P6QLG9Uwquq9dc8010WYbcQ9y/vnnR7s+f5fvV4mf+BtjjDHGGGOMMRXGN/7GGGOMMcYYY0yF8Y2/McYYY4wxxhhTYbrcx58pJej/TT8W+vCVUw7Q75L+c4R+lPTvYzok+tjSb4P+KHydvvhkpZVWivYrr7xSeI/fif4g9NGlL9CBBx7Y8BrtAH2BUml86D/IY9jGU6dOjTZTzr344ovRZso51iX9aarkV1SH6T1Yr9ttt120GYtCKvpm0g+YfZv1Rn8vpnlhfbIv8zyMuUFfWvoqMR5BOQUZr11OK9MulH3G63CekKQ11lgj2kz3Q79Yxspge6d8Z1mf9LtjmXj8wIEDE9+iejAlUirFpVT0D+b6Qj9jrkFM//eb3/ym4WfLsSzqcA7k3Mj24ppV9q1myqcFycc/5ddPuC5wjpowYUK099tvv2j/6le/6rLyMe0V599NNtkk2mxL9i1+tjdgf6MPKOu87G9Kn+J111032lzX2QapGA0cl6k1hnBNYnszBhNjdkjFcVZOJ1tlUnXIem4ldkYqhk0KtinP2SqpebGVlJ6d4YMPPtD06dMlFX3xZ86cGW3G4WDKS/rlM/Uy92dScT3g3ohjn2t5ap3mHMLYFdxD8zuUU1vWYRpsxjgrv8fv/cwzz0SbaePee++9T1yvHeNtdbTMxx13XLRTqcQ5n/LekHvde+65J9qMFVDu7yNGjIg20/ryvOecc060Gc/k6quvllTcX5bxE39jjDHGGGOMMabC+MbfGGOMMcYYY4ypMF0u9T/llFOiTekJJVnNUuRRmkmpF2URlMxQhkbZEGWSPA/Pz/QylIVdfvnl0abEnXKz8mf4Hsvx4IMPqgqk0roRfm/KBfv37x9tSlooNWe7UFJFeTj7TSo1UDvD1FJ//vOfo12X7kjSrbfeWvgM3UfOPffcaFOWT9kW2yUl8Uu5bFDKRxktJUUnn3xytMty/mWWWSbaTFHC1CUp9552g1Iy1jnrkBJg1iH7NsdFWQpeJ5UCq1lKsqrB+Yk2pYlScc5JSZ3Zb5l+8frrr4/26NGjo03XJI67VKo+rouUDQ4fPrxQ1pQLQdVpRebL8fK5z32uoU1Y5xxTqWul0jpJxTbjnEaXqjFjxjQ8fsaMGQ2v11NQJp9aQ8t7spT8m+tEKrVxK23JY1JzGV9nGcpy/gEDBkSbewfuKTheq0Ir9dysT9dpRd7PfcaJJ54Y7bIrbCv0tMvmpz71qbjHYLo89hvK+zlvt3K8JF133XXR3nTTTaNNd4KNNtoo2nRPolvghhtuGG3K7ZmqjylLubZxnKfcrKXiuKD7IOdLnpd9qD7+21Hq31FXEs7znM9538f9XOpeiGsP67hcHroEcH/MuuY9LlNFtoKf+BtjjDHGGGOMMRXGN/7GGGOMMcYYY0yF6XKpP2UolCJQbkw5ZFlWxgiGlIyNGjUq2pSupCRmlOhQdkGpBGVNlMBQsrfOOutE+/333y+UldfgeRmtfs8991QVSGUtIKyPpZZaKtqMQkoon2GUU/YBRvunhLdZxMp25Zhjjok2+yb705AhQwqfueGGG6J9wgknNDwv5XSUMaeyUvDaKRcAjgVKwThOKYeTitFvGe22XeX9zSIar7LKKtFmBGB+hm3Bz7Ofp17nGKR8jNHtV1111YblZptKrck7+zpcUziXlKX+fC8VeZ1QjrfjjjtGm1GWUzJytlfqWnQlKLty8POtyHQXVFqJIk/4ekpa3gxKYtmf2Ea8BqWevT3WuCcjnIvK+xzuh1JR2FN7oVQk/1S9p7Iu8bosK6NZS8WsA/z8I488Eu0qSv1Jaq5ope+NGzcu2qyzK6+8Mtqc45i5a5999on2pZde2lJZ6W5y6qmnRvtHP/pRS5/vKB999FHs36uvvnp8fZttton2+PHjo825nXsvjonymDriiCOiTRk/12a6cm611VYNy8GI/WPHjo32o48+Gu0nn3wy2qx/Sr8p56eLgSRNmjQp2nSbJuuvv360mV2mnhGgt+e0noD3qVxvOL9xT9aKyxHXqrK7BK/BPsjPcA596aWX5v0lgJ/4G2OMMcYYY4wxFcY3/sYYY4wxxhhjTIXpco3Gt771rYY2o+M//fTT0T7vvPMKn2eUSkqAGeGS0mJKhVLSvhQpSRqlTJSQDhs2rPB5yqKqDuuqLGtu9DrlrJQ6Esq9KV+i1J9SWLZFZySafZ299tor2pSCMTMEo0VL0u677x5tRp0dOHBgtCkbomSSEqJytNc6lHGxLSjJpJyaUavPOOOMwrn4Hsf5xhtv3NBuZygnZd1yvuKcOGjQoGizzpnBhK4xPIYS8ZQrUxWh/O7dd9+NNuuDUZKlYkYSriOUxNJme9G9iOsFbbY1XSo4N3LcUf5ZnlcpuWY/YJYU01okeMowU3Ndq+4UbJc//OEP0d51112jve+++0ab7gApN7meghHIuRZwz1OW/NLdkX207DJUJyXv5/GpSOAsE9uJdcjXy23J67Ec06dPb3i9dibVX1N9l/tuSvfvv//+aDNr0BprrBFtuo1xHqR0+eabb2616JHLLrss2g888ECHP99R/v3vf0dpPiXYdGvgWsJ9DvefzLjCvask7bDDDg0/zz542mmnRZv7qosuuijalPp/7WtfizYzykycODHadHPhGnPVVVdFu5wFg3twupYxQwPPRdl/fd/X0fuuvkDq3o9rBu9bWB90NeJeg3sFHsM9B/sQ+1/Z5Z3n4tzHvsl7Yq5J9Qx45XMSP/E3xhhjjDHGGGMqjG/8jTHGGGOMMcaYCtNjWlDKVEeOHBltSiKkYhRMSpYoHaesISWnJJR1pCTrPD/lOZS/MGPBgkYqOixlSpQxMvJxSt5ICeG9994bbcoOGR1z1qxZ0U7JNdsZRmhlvbIONt9888JnWG+PPfZYtFOSR5KSB6ZkmKno2SwfJa7Dhw8vfJ5RdBkVnRK1qsD2S0mRUxHIU1H9OYdyfKVcaSgXqyKpKOFcHyiNkz653jSiFYl+ak5jW6TcLp566qloMxovZc5SUSJIaWkVpP7lOaYnMxWwr6TWkWauZJRo0jWpLrGUpEMOOSTalNf39h6CklXOM3R7KUtEOWY4NlJt1sranNqrpa7LuYzzYHnMpLIpcO/QV0hlheF3LWf6IKn6p5z72GOPjfbll18ebc4tK620UrS5N2fdsk8wsjvl6Mcdd1yyrHRDZDmOOuqoaE+bNi3adG/cZJNNkuftKIsttlg833XXXRdfp+Sd9XHnnXdGm9+BkfvLUf1POeWUaLMP/+///m+0uWc666yzok3XL7Y93TF22223aH/729+ONt0nuV4wkn95r3XjjTdGe+bMmdEeOnRotNkf6dZQ34umXH76Mhw7nK8477Ofcv5gJgvuCVJ7EGYm470l7znLbpkpl1z2j//6r/+KNl1V6u2R2sdLfuJvjDHGGGOMMcZUGt/4G2OMMcYYY4wxFaZbpf6UGlC6QAlLWa7EiKEpCUZK4tRqVN55kZJGUw5XJiWD7kkJY0+RitRLiT5fT9UbI4QSRrBmm1JiU8V6pSSU/YkSLErEpHSkfUYCTUmOW+mz/CzlfrwWJXAsT1mCTlkg5YiUpTGScF+nmVyV9cx+y7mPklXC8cLjKflaccUVo03ZPyWcVYdrCvsdXy9L+ynT5jyTmtO4FrBuKfWnFJLjIiWBpAyQfWOppZYqHEcpNu0q0Ffm71ayw1BGKRWls/vss0+0b7rppmj/6U9/ijb7B12cegNG7C/L5OuUZaccWySVzSIlMeXrrBO2AfcQlMKyvzSb4/idmG2GLg69ScrtlDST9xNm/rn66qujzWxTzIy1wQYbRJttzEjjdI3iHMc6p0sL9yOXXHJJtClrL5+L0cjZxpzjeB/QlSy00ELxu9xyyy3xddYNxzTXCNocx+XsXqxDZjKim+aaa64Z7f333z/a11xzTbQ5vkaMGBFtZqph/TFLEMcLy13OmsT3+Hlmj7rggguizTZqJiXv63BtTo03ujtwH5HKPMJ5jHtizmkcjyxDeS7mHoH7xFS/O/roo6Nd72fN5kk/8TfGGGOMMcYYYyqMb/yNMcYYY4wxxpgK061Sf8pNKIEklLxI0pJLLhntVuQYKVlyR6WEPH8qGnZZikkoy2lFPthupGR6lP6wDimDTsm2Nttss2iz/tjurEvKmlLyw3aGdUx5EGV55bqk/D4VJT4lw0xFFU5lvuDx7AN8vVnEccpM2caUYbaT1D9Vf1JR7kcJHSWPlNkRyr/ZvpRkpuZDlonRZElZytuusG7Yxzn3s89Jaflwyh2G9Umb56WkjpLAVPl4frq5lKND0y2halL/3iQlzySMzF3uQ4ceemi0L7roomizvcaOHRvtF154Idqtyri7C7oLsc+nIkdLxf7dSgTv1LhivXN9Iyn3M643XPvL9clxws/0lSxAqUwkKc4+++xon3feeYX3OF9QAkyJMuea8vzSqEyptmNbcH0qZ02pU85ece211zY87sQTT4z2L3/5y2gPGjQo2hdffLGk4tzdWT744ANNnz5dUlE+z+83derUaG+zzTbRphyb2ZSGDRtWuAbvYZipaeDAgdGufydJsTxSMWI/XSXvueeeaPNeipmTuLdgG3G8/PGPfyyUlZm1jjzyyGgz80xqba1npOnO7EGp+YNrMY9h3bTqipmC7g50nU25+RHWP+fM1P1Ss/Lxe7AOpkyZEu1m96aN8BN/Y4wxxhhjjDGmwvjG3xhjjDHGGGOMqTA9pvlMSeEpm5CKUknKIijhoOQmJe9PRU9NReyn9IwSTX62ihL+VknJxdkWlDTzmFT0/lS0f0pbUtLCvhIVuitJyZdYH+VI8JRopmT5qbpqReLHcUepXSryOaPNl+WcHD/8PKMvtxPNpGSUejFiMOV+nGdYV5RkUg5G+SOPp9xypZVWijazKFQRSpJTst5mLkHsj5zH2K6tuFSk2iIlVaYknOO3vDbxvMzsYeYPtjtl+Mcff3y0OT+tsMIKhc8zgvraa68dbfYhui/1trw/JZHm/opjifJhqbhOc27iXo3rR8o9JrWHI9wPsg34HbjGlCNX83uk3Hd47ZQLalfy0EMPRfu2226LNiXebAv2Ha6N5f3SqquuGm26gbGu+DrhvMh6Tu07UvMj24vz1QMPPFC4HtclRixfZZVVok3ZOdfG888/X1Ixe01nWWSRReKY5dzL7ATrrrtutOnKw33skCFDok13BUnaYostok1Xrptvvjna/C6c2ynvZ30yY8Iee+zR8Px07aO7waxZs6K9++67F8rKvkJ3jFGjRkV7k002ifZ1110X7Xp7pdx2OkvKFasrXRTvuuuuaHM+p0sFxwjX7JS7IMvHz/L7pLJYlPfoqYj83EfwGGaDoLtICj/xN8YYY4wxxhhjKoxv/I0xxhhjjDHGmArTY1L/lNy4LJfl361IkVPXSEn6Uy4AqTKkosw2u3YVodSLEilGcaekiJIWRpwljFBPyV1KRk7ZT2/LJ7sb1gH7FiVpUlESlyIl30vVc8pm/aeiJFP+WR5fPBdlUX0l4nJXcvfdd0ebmUtScn2OBco733777WinollTGkooy509e3a0y9LlZtkJ+jKckyiBY0Ti8jrA8fP4449Hm1F7UxH0U3XDtmDb0S1n8uTJ0WYEXsqWy1G3OX7Kkdb7Mq1Eze+u66Wyz3DsMNL20UcfHW1KjSm/Pf300wvXS631jzzySLSfe+65aFP62xvQBY+w3lIuKlJals/jUmMjtcciqSwabD+OMUrFy648HPt0WeC5OBdSat6VzJ49W+ecc46kogw3lVWB34/1TTlvuS4pC2f9s07oHpDaU3C+4zW4h2O78DuksiiUo4xzHuC8yH0fz9td7n9ZlsX6ZcR+fo+JEydGm/P2yiuvHG2u3eVMRHThIKzz7bffPtoce3QB4F5qww03jPbIkSOjzTpje3G94Dgv78WffvrpaFPqz3Lstdde0aarQP2YVjJ9dIRW1gxmWuH+h+O/vC/iOORxrGf2c449ZmBK9QOO25RLFPfrzHxR7u/cP3Jsc1xx7EyaNEkdoX12ecYYY4wxxhhjjOkwvvE3xhhjjDHGGGMqTI9J/VuF8gzKlFJy4Fak+62QimTO17ta0tKuUAZEOQylRpR9rbXWWvM8J6W2PA+jxlKOxeOrQivuIuWo/qk+SXlQKrpxKvJ/qhwpeT7PT+lZOQpxK9Gl+zopWXw56vrUqVOjTSkgZbeUj3GMUMpKyTDbnvLAFBwj48aNi/YRRxxROK6d5P2E8zT7YyqCfvk9yu5S8wnbgpI9ymzZFpTssv8///zz0WZ0aMo2x48fX7g25Z0cY9OmTYv2euut17DcvUlKqtlsfZ4fV7mUCxIlz8xw8Ytf/CLalNwyEvmVV17Z4XLwO7SaWaInoLsQ51rOZRwLdEeSivN2av1ItS3rIdXGqf7C4zluOQ6HDh1a+AznYY5FloNjurtYbrnltP/++0uSNttss/j6vffeG226Gs2YMSPalP1yvSi7XKTagq4MlHynXFhZt6mMWYRzJfd/rO9ym7ItKY9OueOwjT//+c9Lkq6//vqG5ekIH374YXRJZeR71g3nbc7BPP7CCy+MdtlFa9lll402969se9YPI+jT3Yh7qcMPPzzaDz74YLS5h9h4442jzT06M5dMmDChUNYxY8ZEe8SIEdHmnMG+RVeBev+Yn/uuRtx///3R/vGPfxxtfieWL5XNqrz/ZJ3TtTLlnsq2oyz/8ssvjzbHNvcB7OOsfzJlypRocz8hFTN2cIxxnuY8lrpGivbc8RljjDHGGGOMMaYlfONvjDHGGGOMMcZUmF6P6l8mJfuiJCglY07ZqYwArUjJeEwzqX/Vo/qzXShBeumll6LN+qFkjPKlFJTkUsZDSU4rcvQFDUo32Ubs56ko/aSVrBv8LMcI24t9Y+211y6ci1GvKQvsaqlYd5KSxf/pT38q/E05N9uIckHKOxldmlJutinlX5SJMTI8pX90DaDUmZF8pU+2U7vAvsZ6ojR56623LnyG7UcJXcqVjHLaVtYCnpPjIlXHzIpSdkvgGOG12ynCP+nKOTu1jqf2D8cff3y0GZWZ44gSzs7AvsU26u0MNKno+JSNcszsvPPOhc+zjuhek3J74thIRYbneOPxPA/nTV6X36c8rq644opoUz6bygrQndT7KN0RKOsmrCe6BT3zzDPRLst56RabisyfaiPONdxj8XVKpRlNnK9Tnt/MpYVtllrvORdyHq2P765w71x44YXjGsy966uvvhrtTTfdNNqcK5599tmGrw8ePLhwDbYT90nbbbddtFkfdNditHq6DNCdIOXKxv0EP8v9Adckqeh+sO6660Z77Nix0WYEfM5rdRcMjs35oT4nfOc734mvsY9zLU6t46TsWkrpPm3yzjvvRJv1ecwxxzT87HnnnRftlVZaKdqU+tOdjBmeuA/jvk1qLcsZ66OcqWle+Im/McYYY4wxxhhTYXzjb4wxxhhjjDHGVJg+F9Wf0hjKGijhS8mXUlJMyiZSkXf5OiUUpCyTMWnZHOVclB2loIz5ySefjDYlM6k2rQqU3FGmmJLnS0W5M+uE4yUlT0+5wLSSySIl1WdZBw4cWPjM5MmTo81xnpJZtxOUw0rSsGHDos06oUwvleWglUwNqSjJjGpNt4KUi4HUvlJ/9kHKQFkf5Xmi2ViqQ9ksJa68Hscd65YuFbwWMzvwmOWXXz7a5ci+7CuMppySNvYVUjJ8rp/lSNj1SNuSNHr06HleoxW3gZ/85CfR5prOsXrttdfO8zzNXPxSmU76kjtGaj+TyvbCuVkquu1xLW9F6s++ymNSGYAIZcypDCplVx7K0Fluzg+tZESZXxZeeOFYFu6R2M9TknfWMcdCOftNag+U2teyvXiuViL883jOU4y0zmwE5QwEKeky3U24/2F/rGeZ4Jw7P9T7EvsgI8lTgs064/y11157Rbss9b/vvvuizawAtHnt888/P9qsJ7o+sM532WWXaNMt4ZRTTon2E088Ee2DDz442htttFGhrCeddFK02TfZlnSJ4F6hPo66Yv82Z84c/eEPf5BU3J9w3eQ4YvnKMvk65XmbMn7eb9DNkn2MLhIHHnhgtK+77rpo77bbbtGmiw7LyiwMEydOjDbrrTzntjI/cozwmPr8mPqc5Cf+xhhjjDHGGGNMpfGNvzHGGGOMMcYYU2H6nNQ/FZWXpKSEhDKKlNStlWj/lN40kxotSFHmKX+iVIvyFtYbo4unYFRKRjWnvIo25TntDOU4qf5IKXEZSupa6ec8PpUFgHAcpdwHUi42ZQkcr83Pl2WB7QKlXYzoKhWlkZSZsn5SmTJIKpJtyk2AkZUZqZjjhfLMdiblksLxUo4EzTkq5T6WkqWm2o6f5TGUHbO9WP8c/yNHjiyUlXMrIwlTEtsXSa2FU6dOjTbl2lKxzfi9m0UKbwTdKCi55Xi8++67O3TO8vdpxXXqxRdf7NA1uhPWJ+cTusSwfzaT+nNO4brO+YuRybmu83W6zbA/U7bLOuRYouy53DYsH6XV/K5dJRlvFbo7tOKmw/Kl1mupWA9sv9R6yrU85UabOp71zPbiutLMRTD1PVL7C9ZTPYJ+KhJ7R+jXr1+UcPN8Q4YMiTbrkntORrrfdttto/3www8XrrHFFltEm1L1VBYN7pPoApVqX64fjz/+eLQ32GCDaNNNhufknkUqRplnn6A7DNue62n9Gql9Z0fo169fnCsow6ekn/MS3UhTLiZllx660NTdR8qf5zxBm32Tbh6cY5jNgfMYy815LOWaKxXnx45mtKtnYUjtESU/8TfGGGOMMcYYYyqNb/yNMcYYY4wxxpgK0+ek/q1EiGxFVp+SLqfOQ5kLX6cEg1KdBZmUzJX1Q+kmZSspKE3i8anIlV0hL+oLsK+lJPnN3BpScryUHDUlD6LNz9JOjRG2BWVT5WjxKal/K2O1L0K5crm+KXVkv6XkmPWWkme+9dZbDY9nu/Naq6++erQZnZjHM7qtVJTgtpKBo6+QkoxS4sbIyFIxs0SKVMaJVCYLSgopDyxH6a9DuSTlzOuuu27huLvuuqthmXoju0z9+3Z07eXxW265ZdcXrAQjWNclj5J00003dfqcZRlmar7iHEB3td6G8w/HOucBlr3cb9mGnL8oVU1FhqcsmXWy+eabRzvlDsBzcl1hWQcMGFAoK/9eb731os25sFm0674AJejN5O2tuFCaIh988IGmT58uSbrsssvi63V3AqnoysD1Y9y4cdF+9tlno025t1SU0zMi/k477RRtugdQFl52TavDfcAzzzwTbe6bGcmfrhI85pFHHimclxlOuGenSxz3HRxHkyZN+sSxnaVfv35R4s/xzWw2vA7nFcrnmSWHtlTcJ3GPwNdT2Ss4F7E+6b7GtqMrAscpz8/yle9nOLfyPboB0a2J2YfqbdzsftVP/I0xxhhjjDHGmArjG39jjDHGGGOMMabC+MbfGGOMMcYYY4ypMD3mKN2ZdHf0KW6FVlKSpcrEz/K6Kb/aBY1UejLWG/1P6DPVCkxpQh88+sySqvj4k1TMiWZ1yT6ZSpGXSuPTShyAVLyL1Fij3yjTy5SvTbtdffzpG1aeq5iGjL5WbBf6jKdSJdK3lX2ePt9MYbbppptGmz7iTDdYTrVE/8F28vFP0cwvlvNJKlUf65ntQpvHp1K+st3pg0dfSvYH+ipK6fWmnG6tJ+jI+p06luOcqbGkYh8+5phjor3vvvvO83onnHBCtMePHx/tI444ItplP9zugO3FMdXb0Fc1FXeC/fmBBx4ovEc/Z/osc/7ieVN7Jp6He4hWfGmZtow+s7fddluhrPTdZbwAjhmmNzMLFgsttFD04afPPfe37GvsN6NGjWr4etmXmjFfuDY8+OCD0U6lpySMNcC9FMfqrFmzGn6WfZxp5sprCn3RGWuAY5t7c9r1mDSpPXpHWGyxxTR8+HBJxXR5F1xwQbS5D2YawlRcnXIsj1SaTNZnan/ANY17O+6ruG9L7RXY7qlUheXjaLNd2LcYV6KerpLvl/ETf2OMMcYYY4wxpsL4xt8YY4wxxhhjjKkwPaaXTsmHy1DKQGlGilS6sVTKuZS8OSX7T6WMavb5KkJpDOuEchrK7Mqy1XnBlD6pFHe8VjnFUhVI9cFBgwYlP0OJENODUCaWqitKMlMyfMIysa1TKVCapSFMSaHaCUrjyrIytgWlg5zTKP9OpapMSdcoSWNKns9//vPR5hjkZ8sy5Hatf8L+SPkiZZdSMf0O5d/s26lUiXydMjq2BSWWHJtcd/hZlruZ+1IqdVpP8N577+mOO+6QVFyfOcfQRYSppFgHrKeyPJQpqk4//fRo77jjjtHmGnHrrbdG+6yzzor26NGjo33yyScnvtH8kVrrOW/2hjtGitmzZ0d7rbXWijbTQlJ2Wk6Rx/md34tzGfsnx1LKRTC1h+DrqfRi7DvleZfXqKdtk4pjq+p7NZPm448/jmsC12jub2+//fZob7zxxtEeOXJktOm2cvfddxeuwXWdbgBcdylnpwsA07tyzUilG2RKYY7NlAsZx7lUTCHLct9yyy3R3mGHHaLN8VZ3IeAa1hUce+yx0a7L/yXptNNOizal7WxHfldK8qXi/Mwyp9b71D1rKlUz58PUeQhfL5eV7USXJfYJpvMbNmxYtPfbbz9J0plnntnwupKf+BtjjDHGGGOMMZXGN/7GGGOMMcYYY0yF6dOh0VPS/VQE/pRNeURKdpGKqE4W5Kj+lA6lokU2k640OoZ1TvleSiJL6V9XRBLtC7A+Un2T0q4yKck924iS9FTE8lYi63NcpGSYjDJbbiOWNSWXaidef/31aJfnDEakpqSWcwjle6wDRq2mbLqVLCeUuvI8HGs8p1RsM0r/+jqUw1HySHngjBkzCp9hhOONNtoo2myXlAsMxwvbjuOLx3AOpMsBX6cMuyxBZjnY13razelf//pXrDfWH8vOtuDcwz7Icq+22mqFa9TliVJRtkjZ7X333Rftxx57LNpbb711tOkmQLcEzj3dJcNnNomdd965W67RGVLueKyHOXPmRLvcD/m9OL9wfk+tH5z7Vl999YbHpPYNHHuU83JPwPKU/2Z/43etYkYg0xqLLLKI1l9/fUnFfQv7xJe//OVoc12gmxijudOWiuvKTTfdFG32YbqEcS81dOjQaHMPwf0393l0p2Q5eH6Og7ILLtdNulINGTIk2szkQYn93nvvLal59PiOUB/vHN/M/kJ7woQJ0aZrANcnZpiS0u58rFv2Ax6fckVeddVVo835kPNQK/eNXKuk9Dz4H//xH9FmG2255ZbzvAbxE39jjDHGGGOMMabC+MbfGGOMMcYYY4ypMD2meWo1kiollE8//XS0KcGgFIQ2JW18PRUlnuek3CPFghzVn1C+SShPoTyQpKSzlDWl2jrlGtDOsE9R7tOqDP9LX/pStCknpqyM10jJhHlMyv2AbcE2YjTYTTfdNFlWSsJazZbRl6FUsOzaUo6cX4cyPbY3XR8o62Y78no8hvazzz4b7ZSLU3muKkf6bRcoi6SMmP2RMnxJ2mOPPaLNiMusn5Tcj3Jhth0lhXTLYXuxv3NupMS67PLyhS98IdopKX1PsNxyy+mggw5q+XjWOWWijE7M16ViPdM9g/J+zm+Ufe67777RLrsQ1OmJKPts11/84hfRPu6447r92s2gaw/70eDBg6PNPsz5RCpmFuE8x+M41/B6lLzSzSCVsYnl4zGpfQCjoEvFsZFyOUm5HJjqs+iii8ZsLszq0l0ccMAB3X6N3oauCvNDKuNaI7bffvtoT5o0qeEx06ZNK/zN+YrzAdciZtDiGr/mmmu2XLZ2wE/8jTHGGGOMMcaYCuMbf2OMMcYYY4wxpsL0ufCmjAJLiRml+JQSUiZMGXkr0n1KOnkeRmqk3Iwy2jKpLAJVgTIZRnPu379/tCljTknxU1J/yvIoCaSsj23KvtHOsH+lslJwTJT5wQ9+0C3l6g5SmTOafb++DF2RyvJRjgXC702pOccLI7SOGzcu2nQH2GGHHRqeM1WvlOiuscYahTJtt912Dcva16HEMCU3fOihh5KfT8m/KdEnnNcpSeY8xs+m+gDnLq47ZdnyWmutFe1mmT36GnTbol11KJ0/7LDDeq8gJTbYYINocx6YMmVKtH/2s59Fuxz1nvstrvdcuzgX3nDDDdFmnXD8PPXUU9Gm7JZz3E477RTt1LzG8khFl4XJkydHm9HMt9pqKxljqs16663X9O86dBlcUKjeHaoxxhhjjDHGGGMivvE3xhhjjDHGGGMqTI9J/ZtFlSYjRoyINiVqlGqlZPyUgzGaLK+Xit5MGRpl55SVjRw5MlnuKsr7CSOg7rbbbtGmNG/ZZZeNdko+nKqnAQMGRJsSV9Y/I5yzb7QzrLN11lkn2oxOPWrUqOTnUxH/+2KWCUbffv7556O9ySab9EZx5ptzzz032mV5LOeivffeO9p0F2IE2ZkzZ0abbgPNsiTU+eIXv9jw9S9/+cvz/GwV4ZxUlvPTpYJS/FQWEq4FXHd4HrZ9KxkZ6JbA6zaLjlx1V7Kq8dOf/rS3ixChlPX73/9+tO+5555o77777tFmNOvO0JtZDCj1/853vhPtrbfeOtrludoYYxYkvIMwxhhjjDHGGGMqjG/8jTHGGGOMMcaYChNSUuGGB4fwuqQZ3VecBZJBWZYtP+/D0rhdugW3S9/E7dI3cbv0TdwufRe3Td/E7dI3cbv0TdwufZNku3Toxt8YY4wxxhhjjDHthaX+xhhjjDHGGGNMhfGNvzHGGGOMMcYYU2F842+MMcYYY4wxxlQY3/gbY4wxxhhjjDEVxjf+xhhjjDHGGGNMhfGNvzHGGGOMMcYYU2F842+MMcYYY4wxxlQY3/gbY4wxxhhjjDEVxjf+xhhjjDHGGGNMhfn/0d98noCzZSgAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import tensorflow as tf\n",
    "\n",
    "# Get the Fashion-MNIST dataset, with train and test inputs and outputs\n",
    "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "\n",
    "# Normalize the inputs\n",
    "X_train = X_train/255\n",
    "X_test = X_test/255\n",
    "\n",
    "# Translate the outputs into labels\n",
    "label_list = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\n",
    "label_train = [label_list[i] for i in y_train]\n",
    "label_test = [label_list[i] for i in y_test]\n",
    "\n",
    "# Show a single example for the different classes\n",
    "number_classes = len(label_list)\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_classes):\n",
    "    j = np.where(y_train==i)[0][0]\n",
    "    plt.subplot(1, number_classes, i+1)\n",
    "    plt.imshow(X_train[j, :, :], cmap='binary')\n",
    "    plt.title(label_list[i])\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1fce402",
   "metadata": {},
   "source": [
    "### 2.2. Softmax regression from scratch in NumPy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3672ad3c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=0.646; train_accuracy=0.751; test_accuracy=0.784\n",
      "2/10: train_loss=0.441; train_accuracy=0.814; test_accuracy=0.799\n",
      "3/10: train_loss=0.398; train_accuracy=0.826; test_accuracy=0.810\n",
      "4/10: train_loss=0.375; train_accuracy=0.831; test_accuracy=0.811\n",
      "5/10: train_loss=0.360; train_accuracy=0.836; test_accuracy=0.813\n",
      "6/10: train_loss=0.348; train_accuracy=0.841; test_accuracy=0.821\n",
      "7/10: train_loss=0.340; train_accuracy=0.843; test_accuracy=0.818\n",
      "8/10: train_loss=0.334; train_accuracy=0.845; test_accuracy=0.829\n",
      "9/10: train_loss=0.329; train_accuracy=0.846; test_accuracy=0.831\n",
      "10/10: train_loss=0.324; train_accuracy=0.848; test_accuracy=0.834\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import random\n",
    "import tensorflow as tf\n",
    "\n",
    "# Get the train and test inputs and outputs\n",
    "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "number_train = len(X_train)\n",
    "number_test = len(X_test)\n",
    "\n",
    "# Normalize and flatten the inputs\n",
    "input_size = np.size(X_train[0])\n",
    "X_train = np.reshape(X_train/255, (number_train, input_size))\n",
    "X_test = np.reshape(X_test/255, (number_test, input_size))\n",
    "\n",
    "# Derive one-hot versions of the train outputs\n",
    "output_size = 10\n",
    "Y_train = np.zeros((number_train, output_size))\n",
    "Y_train[np.arange(number_train), y_train] = 1\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Initialize the weights and bias to recover\n",
    "W = np.random.default_rng().normal(0, 0.01, size=(input_size, output_size))\n",
    "b = np.zeros(output_size)\n",
    "\n",
    "# Initialize lists for the mean train loss and accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Initialize a list for the test accuracy overall for every epoch\n",
    "test_accuracy = [None]*number_epochs\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Generate random indices for all the train examples\n",
    "    train_indices = np.arange(number_train)\n",
    "    random.shuffle(train_indices)\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for j in np.arange(0, number_train, batch_size):\n",
    "        \n",
    "        # Get the indices of the train examples for one minibatch\n",
    "        batch_indices = train_indices[j:min(j+batch_size, number_train)]\n",
    "        \n",
    "        # Get the train inputs and outputs for the minibatch\n",
    "        X = X_train[batch_indices, :]\n",
    "        y = y_train[batch_indices]\n",
    "        Y = Y_train[batch_indices]\n",
    "        \n",
    "        # Compute the predicted outputs (logits)\n",
    "        O = np.matmul(X, W) + b\n",
    "        \n",
    "        # Compute the softmax of the logits (indirectly to avoid numerical stability issues)\n",
    "        O = O-np.max(O, axis=1)[:, None]\n",
    "        O_exp = np.exp(O)\n",
    "        Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "        \n",
    "        # Compute the mean cross-entropy loss for the minibatch and save it\n",
    "        l = np.mean(np.log(np.sum(O_exp, axis=1)-np.sum(Y*O, axis=1)))\n",
    "        train_loss[i].append(l)\n",
    "        \n",
    "        # Compute the mean accuracy for the minibatch and save it\n",
    "        a = np.mean(np.argmax(Y_hat, axis=1)==y)\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Update the weights and bias using SGD\n",
    "        dl = Y_hat-Y\n",
    "        W = W-learning_rate*np.matmul(X.T, dl)/np.shape(X)[0]\n",
    "        b = b-learning_rate*np.mean(dl, axis=0)\n",
    "        \n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = np.mean(train_loss[i])\n",
    "    train_accuracy[i] = np.mean(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for the current epoch\n",
    "    O = np.matmul(X_test, W) + b\n",
    "    O = O-np.max(O, axis=1)[:, None]\n",
    "    O_exp = np.exp(O)\n",
    "    Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "    test_accuracy[i] = np.mean(np.argmax(Y_hat, axis=1)==y_test)\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "number_examples = 10\n",
    "O = np.matmul(X_test[:number_examples, :], W) + b\n",
    "O = O-np.max(O, axis=1)[:, None]\n",
    "O_exp = np.exp(O)\n",
    "Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "y_hat = np.argmax(Y_hat, axis=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(np.reshape(X_test[i, :], (28, 28))*255, cmap='binary')\n",
    "    plt.title(f'True: {label_list[y_test[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "294bdf78",
   "metadata": {},
   "source": [
    "### 2.3. Softmax regression from scratch in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fd4c2069",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=0.785; train_accuracy=0.750; test_accuracy=0.791\n",
      "2/10: train_loss=0.570; train_accuracy=0.813; test_accuracy=0.802\n",
      "3/10: train_loss=0.524; train_accuracy=0.826; test_accuracy=0.820\n",
      "4/10: train_loss=0.501; train_accuracy=0.831; test_accuracy=0.825\n",
      "5/10: train_loss=0.486; train_accuracy=0.838; test_accuracy=0.823\n",
      "6/10: train_loss=0.475; train_accuracy=0.839; test_accuracy=0.825\n",
      "7/10: train_loss=0.466; train_accuracy=0.842; test_accuracy=0.832\n",
      "8/10: train_loss=0.457; train_accuracy=0.845; test_accuracy=0.832\n",
      "9/10: train_loss=0.451; train_accuracy=0.847; test_accuracy=0.825\n",
      "10/10: train_loss=0.447; train_accuracy=0.848; test_accuracy=0.828\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "import torchvision\n",
    "from torch.utils import data\n",
    "\n",
    "# Get the dataset (transform the image data from PIL type to normalized 32-bit floating point tensors)\n",
    "fmnist_train = torchvision.datasets.FashionMNIST(root='data', train=True, download=True, \n",
    "                                                 transform=torchvision.transforms.ToTensor())\n",
    "fmnist_test = torchvision.datasets.FashionMNIST(root='data', train=False, download=True,\n",
    "                                                transform=torchvision.transforms.ToTensor())\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Use data iterators to read a minibatch at each iteration, shuffling the examples for the train set and using 4 processes\n",
    "train_iter = data.DataLoader(fmnist_train, batch_size, shuffle=True, num_workers=4)\n",
    "test_iter = data.DataLoader(fmnist_test, batch_size, shuffle=False, num_workers=4)\n",
    "\n",
    "# Initialize the parameters to recover, requiring the gradients to be computed\n",
    "input_size = fmnist_train[0][0].nelement()\n",
    "output_size = 10\n",
    "W = torch.normal(0, 0.01, size=(input_size, output_size), requires_grad=True)\n",
    "b = torch.zeros(output_size, requires_grad=True)\n",
    "\n",
    "# Initialize lists for the mean train loss, train and test accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "test_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for X, y in train_iter:\n",
    "        \n",
    "        # Compute the logits, after flattening the images\n",
    "        O = torch.matmul(torch.reshape(X, (-1, input_size)), W) + b\n",
    "\n",
    "        # Compute the softmax of the logits\n",
    "        O_exp = torch.exp(O)\n",
    "        Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "        \n",
    "        # Compute the cross-entropy loss (use the indices of the true classes in y_batch \n",
    "        # to get the corresponding probabilities in y_batch, for all the examples)\n",
    "        l = -torch.log(Y_hat[range(Y_hat.shape[0]), y])\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        train_loss[i].append(torch.mean(l).item())\n",
    "        \n",
    "        # Compute the mean accuracy for the current minibatch and save it\n",
    "        a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Compute the gradient on l with respect to W and b\n",
    "        # (sum and not mean as the gradients will be divided by the batch size during SGD)\n",
    "        torch.sum(l).backward()\n",
    "        \n",
    "        # Disable gradient calculation for the following operations not to be differentiable\n",
    "        with torch.no_grad():\n",
    "            \n",
    "            # Update the weights and bias using SGD\n",
    "            # (use augmented assignments to avoid modifying existing variables)\n",
    "            W -= learning_rate*W.grad/len(l)\n",
    "            b -= learning_rate*b.grad/len(l)\n",
    "            \n",
    "            # Set the gradients to zeros to avoid accumulating gradients\n",
    "            W.grad.zero_()\n",
    "            b.grad.zero_()\n",
    "    \n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = sum(train_loss[i])/len(train_loss[i])\n",
    "    train_accuracy[i] = sum(train_accuracy[i])/len(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for every epoch, in minibatches\n",
    "    with torch.no_grad():\n",
    "        for X, y in test_iter:\n",
    "            O = torch.matmul(torch.reshape(X, (-1, input_size)), W) + b\n",
    "            O_exp = torch.exp(O)\n",
    "            Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "            a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "            test_accuracy[i].append(a)\n",
    "    test_accuracy[i] = sum(test_accuracy[i])/len(test_accuracy[i])\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "for X, y in test_iter:\n",
    "    break\n",
    "number_examples = 10\n",
    "O = torch.matmul(torch.reshape(X[:number_examples], (-1, input_size)), W) + b\n",
    "O_exp = torch.exp(O)\n",
    "Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "y_hat = torch.argmax(Y_hat, dim=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X[i][0], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f34134ef",
   "metadata": {},
   "source": [
    "### 2.4. Softmax regression using APIs in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "105cccbb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=0.783; train_accuracy=0.752; test_accuracy=0.793\n",
      "2/10: train_loss=0.570; train_accuracy=0.813; test_accuracy=0.810\n",
      "3/10: train_loss=0.525; train_accuracy=0.827; test_accuracy=0.814\n",
      "4/10: train_loss=0.500; train_accuracy=0.834; test_accuracy=0.827\n",
      "5/10: train_loss=0.486; train_accuracy=0.837; test_accuracy=0.822\n",
      "6/10: train_loss=0.473; train_accuracy=0.840; test_accuracy=0.824\n",
      "7/10: train_loss=0.465; train_accuracy=0.843; test_accuracy=0.832\n",
      "8/10: train_loss=0.458; train_accuracy=0.844; test_accuracy=0.833\n",
      "9/10: train_loss=0.453; train_accuracy=0.847; test_accuracy=0.829\n",
      "10/10: train_loss=0.447; train_accuracy=0.848; test_accuracy=0.822\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "from torch import nn\n",
    "from torch.utils import data\n",
    "import torchvision\n",
    "\n",
    "# Get the dataset (transform the image data from PIL type to normalized 32-bit floating point tensors)\n",
    "fmnist_train = torchvision.datasets.FashionMNIST(root='data', train=True, download=True, \n",
    "                                                 transform=torchvision.transforms.ToTensor())\n",
    "fmnist_test = torchvision.datasets.FashionMNIST(root='data', train=False, download=True,\n",
    "                                                transform=torchvision.transforms.ToTensor())\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Use data iterators to read a minibatch at each iteration, shuffling the examples for the train set and using 4 processes\n",
    "train_iter = data.DataLoader(fmnist_train, batch_size, shuffle=True, num_workers=4)\n",
    "test_iter = data.DataLoader(fmnist_test, batch_size, shuffle=False, num_workers=4)\n",
    "\n",
    "# Define the model, with a flatten layer to reshape the inputs before the fully-connected layer\n",
    "input_size = fmnist_train[0][0].nelement()\n",
    "output_size = 10\n",
    "model = nn.Sequential(nn.Flatten(), nn.Linear(input_size, output_size))\n",
    "\n",
    "# Initialize the parameters by applying a function recursively to every submodule\n",
    "def init(m):\n",
    "    if isinstance(m, nn.Linear):\n",
    "        nn.init.normal_(m.weight, std=0.01)\n",
    "model.apply(init);\n",
    "\n",
    "# Define the loss function (with no reduction applied to the output, no mean, no sum, none)\n",
    "loss = nn.CrossEntropyLoss(reduction='none')\n",
    "\n",
    "# Define the optimization algorithm\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "\n",
    "# Initialize lists for the mean train loss, train and test accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "test_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for X, y in train_iter:\n",
    "        \n",
    "        # Compute the predicted outputs\n",
    "        Y_hat = model(X)\n",
    "        \n",
    "        # Compute the loss\n",
    "        l = loss(Y_hat, y)\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        train_loss[i].append(torch.mean(l).item())\n",
    "        \n",
    "        # Compute the mean accuracy for the current minibatch and save it\n",
    "        a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Set the gradients to zero\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # Compute the gradient\n",
    "        l.mean().backward()\n",
    "        \n",
    "        # Performs a single parameter update\n",
    "        optimizer.step()\n",
    "        \n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = sum(train_loss[i])/len(train_loss[i])\n",
    "    train_accuracy[i] = sum(train_accuracy[i])/len(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for every epoch, in minibatches\n",
    "    with torch.no_grad():\n",
    "        for X, y in test_iter:\n",
    "            Y_hat = model(X)\n",
    "            a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "            test_accuracy[i].append(a)\n",
    "    test_accuracy[i] = sum(test_accuracy[i])/len(test_accuracy[i])\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "for X, y in test_iter:\n",
    "    break\n",
    "number_examples = 10\n",
    "Y_hat = model(X[:number_examples])\n",
    "y_hat = torch.argmax(Y_hat, dim=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X[i][0], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85e54ce7",
   "metadata": {},
   "source": [
    "### 2.5. Softmax regression using higher-level APIs in Keras"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b089c2d6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "235/235 [==============================] - 1s 765us/step - loss: 0.7853 - accuracy: 0.7506\n",
      "Epoch 2/10\n",
      "235/235 [==============================] - 0s 755us/step - loss: 0.5703 - accuracy: 0.8134\n",
      "Epoch 3/10\n",
      "235/235 [==============================] - 0s 737us/step - loss: 0.5254 - accuracy: 0.8254\n",
      "Epoch 4/10\n",
      "235/235 [==============================] - 0s 733us/step - loss: 0.5009 - accuracy: 0.8323\n",
      "Epoch 5/10\n",
      "235/235 [==============================] - 0s 747us/step - loss: 0.4847 - accuracy: 0.8368\n",
      "Epoch 6/10\n",
      "235/235 [==============================] - 0s 751us/step - loss: 0.4743 - accuracy: 0.8402\n",
      "Epoch 7/10\n",
      "235/235 [==============================] - 0s 788us/step - loss: 0.4651 - accuracy: 0.8421\n",
      "Epoch 8/10\n",
      "235/235 [==============================] - 0s 802us/step - loss: 0.4582 - accuracy: 0.8449\n",
      "Epoch 9/10\n",
      "235/235 [==============================] - 0s 785us/step - loss: 0.4523 - accuracy: 0.8462\n",
      "Epoch 10/10\n",
      "235/235 [==============================] - 0s 757us/step - loss: 0.4466 - accuracy: 0.8479\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "\n",
    "# Get the train and test inputs and outputs\n",
    "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "X_train = X_train/255\n",
    "X_test = X_test/255\n",
    "input_size = X_train[0, :, :].shape\n",
    "output_size = 10\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Define a model with flattened inputs and a densely-connected NN layer\n",
    "model = tf.keras.Sequential([tf.keras.layers.Flatten(input_shape=input_size),\n",
    "                             tf.keras.layers.Dense(output_size,\n",
    "                                                   activation=None,\n",
    "                                                   kernel_initializer=tf.initializers.RandomNormal(mean=0, stddev=0.01), \n",
    "                                                   bias_initializer='zeros')])\n",
    "\n",
    "# Configure the model with SGD optimizer, cross-entropy loss (with integers, not one-hot), and accuracy metrics\n",
    "model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate), \\\n",
    "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), \\\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "# Train the model\n",
    "model.fit(x=X_train, y=y_train, batch_size=batch_size, epochs=number_epochs, verbose=1)\n",
    "\n",
    "# Show some predictions\n",
    "number_examples = 10\n",
    "Y_hat = model.predict(X_test[:number_examples, :, :])\n",
    "y_hat = np.argmax(Y_hat, axis=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X_test[i, :, :], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y_test[i]]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe72f8cf",
   "metadata": {},
   "source": [
    "## 3. Multilayer Perceptron\n",
    "https://d2l.ai/chapter_multilayer-perceptrons"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a88b3082",
   "metadata": {},
   "source": [
    "### 3.1. MLP from scratch in NumPy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "47d50dd8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=0.928; train_accuracy=0.625; test_accuracy=0.748\n",
      "2/10: train_loss=0.462; train_accuracy=0.790; test_accuracy=0.786\n",
      "3/10: train_loss=0.389; train_accuracy=0.820; test_accuracy=0.814\n",
      "4/10: train_loss=0.357; train_accuracy=0.830; test_accuracy=0.820\n",
      "5/10: train_loss=0.338; train_accuracy=0.838; test_accuracy=0.822\n",
      "6/10: train_loss=0.327; train_accuracy=0.842; test_accuracy=0.835\n",
      "7/10: train_loss=0.317; train_accuracy=0.845; test_accuracy=0.827\n",
      "8/10: train_loss=0.313; train_accuracy=0.847; test_accuracy=0.834\n",
      "9/10: train_loss=0.305; train_accuracy=0.851; test_accuracy=0.821\n",
      "10/10: train_loss=0.301; train_accuracy=0.852; test_accuracy=0.822\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import random\n",
    "import tensorflow as tf\n",
    "\n",
    "# Get the train and test inputs and outputs\n",
    "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "number_train = len(X_train)\n",
    "number_test = len(X_test)\n",
    "\n",
    "# Normalize and flatten the inputs\n",
    "input_size = np.size(X_train[0])\n",
    "X_train = np.reshape(X_train/255, (number_train, input_size))\n",
    "X_test = np.reshape(X_test/255, (number_test, input_size))\n",
    "\n",
    "# Derive one-hot versions of the train outputs\n",
    "output_size = 10\n",
    "Y_train = np.zeros((number_train, output_size))\n",
    "Y_train[np.arange(number_train), y_train] = 1\n",
    "\n",
    "# Initialize the weights and biases to recover\n",
    "hidden_size = 256\n",
    "W0 = np.random.default_rng().normal(0, 0.01, size=(input_size, hidden_size))\n",
    "b0 = np.zeros(hidden_size)\n",
    "W1 = np.random.default_rng().normal(0, 0.01, size=(hidden_size, output_size))\n",
    "b1 = np.zeros(output_size)\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Initialize lists for the mean train loss and accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Initialize a list for the test accuracy overall for every epoch\n",
    "test_accuracy = [None]*number_epochs\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Generate random indices for all the train examples\n",
    "    train_indices = np.arange(number_train)\n",
    "    random.shuffle(train_indices)\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for j in np.arange(0, number_train, batch_size):\n",
    "        \n",
    "        # Get the indices of the train examples for one minibatch\n",
    "        batch_indices = train_indices[j:min(j+batch_size, number_train)]\n",
    "        \n",
    "        # Get the train inputs and outputs for the minibatch\n",
    "        X = X_train[batch_indices, :]\n",
    "        y = y_train[batch_indices]\n",
    "        Y = Y_train[batch_indices]\n",
    "        \n",
    "        # Compute the outputs of the model (with ReLU)\n",
    "        H = np.matmul(X, W0) + b0\n",
    "        H[H<0] = 0\n",
    "        O = np.matmul(H, W1) + b1\n",
    "        \n",
    "        # Compute the softmax of the logits (indirectly to avoid numerical stability issues)\n",
    "        O = O-np.max(O, axis=1)[:, None]\n",
    "        O_exp = np.exp(O)\n",
    "        Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "        \n",
    "        # Compute the mean cross-entropy loss for the minibatch and save it\n",
    "        l = np.mean(np.log(np.sum(O_exp, axis=1)-np.sum(Y*O, axis=1)))\n",
    "        train_loss[i].append(l)\n",
    "        \n",
    "        # Compute the mean accuracy for the minibatch and save it\n",
    "        a = np.mean(np.argmax(Y_hat, axis=1)==y)\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Compute the derivative of the loss wrt the output of the output layer\n",
    "        dl1 = Y_hat-Y\n",
    "        \n",
    "        # Derive the derivative of the loss wrt the output of the hidden layer (using the chain rule)\n",
    "        dl0 = np.matmul(dl1, W1.T)\n",
    "               \n",
    "        # Update the weights and biases of the output layer using SGD\n",
    "        W1 = W1-learning_rate*np.matmul(H.T, dl1)/np.shape(H)[0]\n",
    "        b1 = b1-learning_rate*np.mean(dl1, axis=0)\n",
    "        \n",
    "        # Update the weights and biases of the hidden layer using SGD\n",
    "        W0 = W0-learning_rate*np.matmul(X.T, dl0)/np.shape(X)[0]\n",
    "        b0 = b0-learning_rate*np.mean(dl0, axis=0)\n",
    "\n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = np.mean(train_loss[i])\n",
    "    train_accuracy[i] = np.mean(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for the current epoch\n",
    "    H = np.matmul(X_test, W0) + b0\n",
    "    H[H<0] = 0\n",
    "    O = np.matmul(H, W1) + b1\n",
    "    O = O-np.max(O, axis=1)[:, None]\n",
    "    O_exp = np.exp(O)\n",
    "    Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "    test_accuracy[i] = np.mean(np.argmax(Y_hat, axis=1)==y_test)\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "number_examples = 10\n",
    "H = np.matmul(X_test[:number_examples, :], W0) + b0\n",
    "H[H<0] = 0\n",
    "O = np.matmul(H, W1) + b1\n",
    "O = O-np.max(O, axis=1)[:, None]\n",
    "O_exp = np.exp(O)\n",
    "Y_hat = O_exp/np.sum(O_exp, axis=1)[:, None]\n",
    "y_hat = np.argmax(Y_hat, axis=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(np.reshape(X_test[i, :], (28, 28))*255, cmap='binary')\n",
    "    plt.title(f'True: {label_list[y_test[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1670681",
   "metadata": {},
   "source": [
    "### 3.2. MLP from scratch in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "eccca470",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=1.041; train_accuracy=0.646; test_accuracy=0.736\n",
      "2/10: train_loss=0.605; train_accuracy=0.786; test_accuracy=0.790\n",
      "3/10: train_loss=0.520; train_accuracy=0.818; test_accuracy=0.770\n",
      "4/10: train_loss=0.482; train_accuracy=0.831; test_accuracy=0.822\n",
      "5/10: train_loss=0.455; train_accuracy=0.841; test_accuracy=0.806\n",
      "6/10: train_loss=0.432; train_accuracy=0.847; test_accuracy=0.834\n",
      "7/10: train_loss=0.419; train_accuracy=0.851; test_accuracy=0.838\n",
      "8/10: train_loss=0.403; train_accuracy=0.858; test_accuracy=0.839\n",
      "9/10: train_loss=0.393; train_accuracy=0.861; test_accuracy=0.830\n",
      "10/10: train_loss=0.383; train_accuracy=0.864; test_accuracy=0.849\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "import torchvision\n",
    "from torch.utils import data\n",
    "\n",
    "# Get the dataset (transform the image data from PIL type to normalized 32-bit floating point tensors)\n",
    "fmnist_train = torchvision.datasets.FashionMNIST(root='data', train=True, download=True, \n",
    "                                                 transform=torchvision.transforms.ToTensor())\n",
    "fmnist_test = torchvision.datasets.FashionMNIST(root='data', train=False, download=True,\n",
    "                                                transform=torchvision.transforms.ToTensor())\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Use data iterators to read a minibatch at each iteration, shuffling the examples for the train set and using 4 processes\n",
    "train_iter = data.DataLoader(fmnist_train, batch_size, shuffle=True, num_workers=4)\n",
    "test_iter = data.DataLoader(fmnist_test, batch_size, shuffle=False, num_workers=4)\n",
    "\n",
    "# Initialize the parameters to recover, requiring the gradients to be computed\n",
    "input_size = fmnist_train[0][0].nelement()\n",
    "output_size = 10\n",
    "hidden_size = 256\n",
    "W0 = torch.normal(0, 0.01, size=(input_size, hidden_size), requires_grad=True)\n",
    "b0 = torch.zeros(hidden_size, requires_grad=True)\n",
    "W1 = torch.normal(0, 0.01, size=(hidden_size, output_size), requires_grad=True)\n",
    "b1 = torch.zeros(output_size, requires_grad=True)\n",
    "\n",
    "# Initialize lists for the mean train loss, train and test accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "test_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for X, y in train_iter:\n",
    "        \n",
    "        # Compute the outputs of the model (with ReLU), after flattening the images\n",
    "        H = torch.matmul(torch.reshape(X, (-1, input_size)), W0) + b0\n",
    "        H[H<0] = 0\n",
    "        O = torch.matmul(H, W1) + b1\n",
    "\n",
    "        # Compute the softmax of the logits\n",
    "        O_exp = torch.exp(O)\n",
    "        Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "        \n",
    "        # Compute the cross-entropy loss (use the indices of the true classes in y_batch \n",
    "        # to get the corresponding probabilities in y_batch, for all the examples)\n",
    "        l = -torch.log(Y_hat[range(Y_hat.shape[0]), y])\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        train_loss[i].append(torch.mean(l).item())\n",
    "        \n",
    "        # Compute the mean accuracy for the current minibatch and save it\n",
    "        a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Compute the gradient on l with respect to W and b\n",
    "        # (sum and not mean as the gradients will be divided by the batch size during SGD)\n",
    "        torch.sum(l).backward()\n",
    "        \n",
    "        # Disable gradient calculation for the following operations not to be differentiable\n",
    "        with torch.no_grad():\n",
    "            \n",
    "            # Update the weights and biases using SGD\n",
    "            # (use augmented assignments to avoid modifying existing variables)\n",
    "            W1 -= learning_rate*W1.grad/len(l)\n",
    "            b1 -= learning_rate*b1.grad/len(l)\n",
    "            W0 -= learning_rate*W0.grad/len(l)\n",
    "            b0 -= learning_rate*b0.grad/len(l)\n",
    "            \n",
    "            # Set the gradients to zeros to avoid accumulating gradients\n",
    "            W1.grad.zero_()\n",
    "            b1.grad.zero_()\n",
    "            W0.grad.zero_()\n",
    "            b0.grad.zero_()\n",
    "    \n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = sum(train_loss[i])/len(train_loss[i])\n",
    "    train_accuracy[i] = sum(train_accuracy[i])/len(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for every epoch, in minibatches\n",
    "    with torch.no_grad():\n",
    "        for X, y in test_iter:\n",
    "            H = torch.matmul(torch.reshape(X, (-1, input_size)), W0) + b0\n",
    "            H[H<0] = 0\n",
    "            O = torch.matmul(H, W1) + b1\n",
    "            O_exp = torch.exp(O)\n",
    "            Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "            a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "            test_accuracy[i].append(a)\n",
    "    test_accuracy[i] = sum(test_accuracy[i])/len(test_accuracy[i])\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "for X, y in test_iter:\n",
    "    break\n",
    "number_examples = 10\n",
    "H = torch.matmul(torch.reshape(X[:number_examples], (-1, input_size)), W0) + b0\n",
    "H[H<0] = 0\n",
    "O = torch.matmul(H, W1) + b1\n",
    "O_exp = torch.exp(O)\n",
    "Y_hat = O_exp/torch.sum(O_exp, 1, keepdim=True)\n",
    "y_hat = torch.argmax(Y_hat, dim=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X[i][0], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31226c90",
   "metadata": {},
   "source": [
    "### 3.3. MLP using APIs in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "77678470",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/10: train_loss=1.040; train_accuracy=0.645; test_accuracy=0.712\n",
      "2/10: train_loss=0.598; train_accuracy=0.790; test_accuracy=0.799\n",
      "3/10: train_loss=0.518; train_accuracy=0.819; test_accuracy=0.810\n",
      "4/10: train_loss=0.478; train_accuracy=0.833; test_accuracy=0.825\n",
      "5/10: train_loss=0.454; train_accuracy=0.839; test_accuracy=0.833\n",
      "6/10: train_loss=0.431; train_accuracy=0.848; test_accuracy=0.837\n",
      "7/10: train_loss=0.415; train_accuracy=0.854; test_accuracy=0.843\n",
      "8/10: train_loss=0.405; train_accuracy=0.858; test_accuracy=0.849\n",
      "9/10: train_loss=0.391; train_accuracy=0.862; test_accuracy=0.839\n",
      "10/10: train_loss=0.382; train_accuracy=0.864; test_accuracy=0.853\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "from torch import nn\n",
    "from torch.utils import data\n",
    "import torchvision\n",
    "\n",
    "# Get the dataset (transform the image data from PIL type to normalized 32-bit floating point tensors)\n",
    "fmnist_train = torchvision.datasets.FashionMNIST(root='data', train=True, download=True, \n",
    "                                                 transform=torchvision.transforms.ToTensor())\n",
    "fmnist_test = torchvision.datasets.FashionMNIST(root='data', train=False, download=True,\n",
    "                                                transform=torchvision.transforms.ToTensor())\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Use data iterators to read a minibatch at each iteration, shuffling the examples for the train set and using 4 processes\n",
    "train_iter = data.DataLoader(fmnist_train, batch_size, shuffle=True, num_workers=4)\n",
    "test_iter = data.DataLoader(fmnist_test, batch_size, shuffle=False, num_workers=4)\n",
    "\n",
    "# Define the model, with a flatten layer to reshape the inputs, two fully-connected layer, and a ReLU in-between\n",
    "input_size = fmnist_train[0][0].nelement()\n",
    "hidden_size = 256\n",
    "output_size = 10\n",
    "model = nn.Sequential(nn.Flatten(), \n",
    "                      nn.Linear(input_size, hidden_size), \n",
    "                      nn.ReLU(), \n",
    "                      nn.Linear(hidden_size, output_size))\n",
    "\n",
    "# Initialize the parameters by applying a function recursively to every submodule\n",
    "def init(m):\n",
    "    if isinstance(m, nn.Linear):\n",
    "        nn.init.normal_(m.weight, std=0.01)\n",
    "model.apply(init);\n",
    "\n",
    "# Define the loss function (with no reduction applied to the output, no mean, no sum, none)\n",
    "loss = nn.CrossEntropyLoss(reduction='none')\n",
    "\n",
    "# Define the optimization algorithm\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "\n",
    "# Initialize lists for the mean train loss, train and test accuracy over the minibatches for every epoch\n",
    "train_loss = [[] for _ in range(number_epochs)]\n",
    "train_accuracy = [[] for _ in range(number_epochs)]\n",
    "test_accuracy = [[] for _ in range(number_epochs)]\n",
    "\n",
    "# Loop over the epochs\n",
    "for i in range(number_epochs):\n",
    "    \n",
    "    # Loop over the train examples in minibatches\n",
    "    for X, y in train_iter:\n",
    "        \n",
    "        # Compute the predicted outputs\n",
    "        Y_hat = model(X)\n",
    "        \n",
    "        # Compute the loss\n",
    "        l = loss(Y_hat, y)\n",
    "        \n",
    "        # Save the mean loss for the current minibatch\n",
    "        train_loss[i].append(torch.mean(l).item())\n",
    "        \n",
    "        # Compute the mean accuracy for the current minibatch and save it\n",
    "        a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "        train_accuracy[i].append(a)\n",
    "        \n",
    "        # Set the gradients to zero\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # Compute the gradient\n",
    "        l.mean().backward()\n",
    "        \n",
    "        # Performs a single parameter update\n",
    "        optimizer.step()\n",
    "        \n",
    "    # Derive the mean train loss and accuracy for the current epoch\n",
    "    train_loss[i] = sum(train_loss[i])/len(train_loss[i])\n",
    "    train_accuracy[i] = sum(train_accuracy[i])/len(train_accuracy[i])\n",
    "    \n",
    "    # Compute the test outputs and derive the test accuracy for every epoch, in minibatches\n",
    "    with torch.no_grad():\n",
    "        for X, y in test_iter:\n",
    "            Y_hat = model(X)\n",
    "            a = torch.mean((torch.argmax(Y_hat, dim=1)==y)*1.0).item()\n",
    "            test_accuracy[i].append(a)\n",
    "    test_accuracy[i] = sum(test_accuracy[i])/len(test_accuracy[i])\n",
    "    \n",
    "    # Print the progress\n",
    "    print(f'{i+1}/{number_epochs}: train_loss={train_loss[i]:.3f}; train_accuracy={train_accuracy[i]:.3f}; test_accuracy={test_accuracy[i]:.3f}')\n",
    "    \n",
    "# Show some predictions\n",
    "for X, y in test_iter:\n",
    "    break\n",
    "number_examples = 10\n",
    "Y_hat = model(X[:number_examples])\n",
    "y_hat = torch.argmax(Y_hat, dim=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X[i][0], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y[i].item()]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7455abd",
   "metadata": {},
   "source": [
    "### 3.4. MLP using higher-level APIs in Keras"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "099a67b6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "235/235 [==============================] - 1s 2ms/step - loss: 1.0377 - accuracy: 0.6388\n",
      "Epoch 2/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.5989 - accuracy: 0.7903\n",
      "Epoch 3/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.5175 - accuracy: 0.8191\n",
      "Epoch 4/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.4774 - accuracy: 0.8320\n",
      "Epoch 5/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.4517 - accuracy: 0.8424\n",
      "Epoch 6/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.4316 - accuracy: 0.8482\n",
      "Epoch 7/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.4165 - accuracy: 0.8530\n",
      "Epoch 8/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.4021 - accuracy: 0.8579\n",
      "Epoch 9/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.3896 - accuracy: 0.8623\n",
      "Epoch 10/10\n",
      "235/235 [==============================] - 0s 2ms/step - loss: 0.3800 - accuracy: 0.8653\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1296x144 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "\n",
    "# Get the train and test inputs and outputs\n",
    "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "X_train = X_train/255\n",
    "X_test = X_test/255\n",
    "input_size = X_train[0, :, :].shape\n",
    "hidden_size = 256\n",
    "output_size = 10\n",
    "\n",
    "# Define the parameters for the training\n",
    "number_epochs = 10\n",
    "batch_size = 256\n",
    "learning_rate = 0.1\n",
    "\n",
    "# Define a model with flattened inputs, a densely-connected NN layer with a ReLU, and another one without activation\n",
    "model = tf.keras.Sequential([tf.keras.layers.Flatten(input_shape=input_size), \n",
    "                             tf.keras.layers.Dense(hidden_size, \n",
    "                                                   activation='relu', \n",
    "                                                   kernel_initializer=tf.initializers.RandomNormal(mean=0, stddev=0.01), \n",
    "                                                   bias_initializer='zeros'), \n",
    "                             tf.keras.layers.Dense(output_size, \n",
    "                                                   activation=None, \n",
    "                                                   kernel_initializer=tf.initializers.RandomNormal(mean=0, stddev=0.01), \n",
    "                                                   bias_initializer='zeros')])\n",
    "\n",
    "# Configure the model with SGD optimizer, cross-entropy loss (with integers, not one-hot), and accuracy metrics\n",
    "model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate), \\\n",
    "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), \\\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "# Train the model\n",
    "model.fit(x=X_train, y=y_train, batch_size=batch_size, epochs=number_epochs, verbose=1)\n",
    "\n",
    "# Show some predictions\n",
    "number_examples = 10\n",
    "Y_hat = model.predict(X_test[:number_examples, :, :])\n",
    "y_hat = np.argmax(Y_hat, axis=1)\n",
    "label_list = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']\n",
    "plt.figure(figsize=(18, 2))\n",
    "for i in range(number_examples):\n",
    "    plt.subplot(1, number_examples, i+1)\n",
    "    plt.imshow(X_test[i, :, :], cmap='binary')\n",
    "    plt.title(f'True: {label_list[y_test[i]]}\\n Pred: {label_list[y_hat[i]]}')\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "379bfb40",
   "metadata": {},
   "outputs": [],
   "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.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}