diff --git a/README.md b/README.md
index ca63d26..a76fdec 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,35 @@
-# neuralnet
-Neural Network Implementation in NumPy
+# Neural Network Implementation in NumPy
+
+A "from scratch" implementation of classic feed-forward neural networks for
+binary/multi-class classification using ReLU activations, cross entropy loss and
+sigmoid/softmax output.
+
+Read through the documentation in `neuralnet.py` for a description of the
+implementation.
+
+An example usage of `neuralnet.py` is given in the `Usage.ipynb` notebook.
+
+Alternatively you can open the whole code in Google Colab -> [here](https://colab.research.google.com/github/michabirklbauer/neuralnet/neuralnet-colab.ipynb).
+
+## Requirements
+
+`neuralnet.py` is purely implemented in NumPy:
+- [NumPy](https://numpy.org/): `pip install numpy`
+
+To run the examples in the `Usage.ipynb` notebook locally please install the
+requirements noted in `requirements.txt`:
+- [Requirements](https://github.com/michabirklbauer/neuralnet/blob/master/requirements.txt): `pip install -r requirements.txt`
+
+## Data
+
+The following datasets are used in the examples:
+- Multi-class classification: [MNIST](http://yann.lecun.com/exdb/mnist/index.html)
+- Binary-class classification: [Breast Cancer Wisconsin (Diagnostic) Data Set](https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29)
+
+## License
+
+- [MIT](https://github.com/michabirklbauer/neuralnet/blob/master/LICENSE)
+
+## Contact
+
+- [micha.birklbauer@gmail.com](mailto:micha.birklbauer@gmail.com)
diff --git a/Usage.ipynb b/Usage.ipynb
new file mode 100644
index 0000000..f49c779
--- /dev/null
+++ b/Usage.ipynb
@@ -0,0 +1,1262 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "a60f041d-d688-4b00-8bc1-3e01da0d947f",
+ "metadata": {},
+ "source": [
+ "# **Example Usage of `neuralnet.py`**\n",
+ "\n",
+ "### **Multi-Class Classification**\n",
+ "\n",
+ "### **Dataset: [MNIST](http://yann.lecun.com/exdb/mnist/index.html)**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "c7a8280c-d0b9-41d5-88e1-993db76a73b4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from zipfile import ZipFile as zip\n",
+ "\n",
+ "with zip(\"data.zip\") as f:\n",
+ " f.extractall()\n",
+ " f.close()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "579b7aa9-24c5-4dd1-b8b7-719cbb1f7b09",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from neuralnet import NeuralNetwork\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "from matplotlib import pyplot as plt\n",
+ "from sklearn.metrics import accuracy_score\n",
+ "from sklearn.preprocessing import OneHotEncoder\n",
+ "from sklearn.model_selection import train_test_split"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "a8e4f9b1-140c-42ac-9b04-11e23b27d1eb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data = pd.read_csv(\"multiclass_train.csv\")\n",
+ "train, test = train_test_split(data, test_size = 0.3)\n",
+ "train_data = train.loc[:, train.columns != \"label\"].to_numpy() / 255\n",
+ "train_target = train[\"label\"].to_numpy()\n",
+ "test_data = test.loc[:, test.columns != \"label\"].to_numpy() / 255\n",
+ "test_target = test[\"label\"].to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "f9a8ba9c-7255-40b3-9e5f-f999e89eb257",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " label | \n",
+ " pixel0 | \n",
+ " pixel1 | \n",
+ " pixel2 | \n",
+ " pixel3 | \n",
+ " pixel4 | \n",
+ " pixel5 | \n",
+ " pixel6 | \n",
+ " pixel7 | \n",
+ " pixel8 | \n",
+ " ... | \n",
+ " pixel774 | \n",
+ " pixel775 | \n",
+ " pixel776 | \n",
+ " pixel777 | \n",
+ " pixel778 | \n",
+ " pixel779 | \n",
+ " pixel780 | \n",
+ " pixel781 | \n",
+ " pixel782 | \n",
+ " pixel783 | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 17929 | \n",
+ " 4 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 2630 | \n",
+ " 9 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 31584 | \n",
+ " 6 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 12668 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 1407 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 32370 | \n",
+ " 1 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 17461 | \n",
+ " 5 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 5079 | \n",
+ " 1 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 29413 | \n",
+ " 5 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 33385 | \n",
+ " 7 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
29400 rows × 785 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " label pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 \\\n",
+ "17929 4 0 0 0 0 0 0 0 0 \n",
+ "2630 9 0 0 0 0 0 0 0 0 \n",
+ "31584 6 0 0 0 0 0 0 0 0 \n",
+ "12668 0 0 0 0 0 0 0 0 0 \n",
+ "1407 0 0 0 0 0 0 0 0 0 \n",
+ "... ... ... ... ... ... ... ... ... ... \n",
+ "32370 1 0 0 0 0 0 0 0 0 \n",
+ "17461 5 0 0 0 0 0 0 0 0 \n",
+ "5079 1 0 0 0 0 0 0 0 0 \n",
+ "29413 5 0 0 0 0 0 0 0 0 \n",
+ "33385 7 0 0 0 0 0 0 0 0 \n",
+ "\n",
+ " pixel8 ... pixel774 pixel775 pixel776 pixel777 pixel778 \\\n",
+ "17929 0 ... 0 0 0 0 0 \n",
+ "2630 0 ... 0 0 0 0 0 \n",
+ "31584 0 ... 0 0 0 0 0 \n",
+ "12668 0 ... 0 0 0 0 0 \n",
+ "1407 0 ... 0 0 0 0 0 \n",
+ "... ... ... ... ... ... ... ... \n",
+ "32370 0 ... 0 0 0 0 0 \n",
+ "17461 0 ... 0 0 0 0 0 \n",
+ "5079 0 ... 0 0 0 0 0 \n",
+ "29413 0 ... 0 0 0 0 0 \n",
+ "33385 0 ... 0 0 0 0 0 \n",
+ "\n",
+ " pixel779 pixel780 pixel781 pixel782 pixel783 \n",
+ "17929 0 0 0 0 0 \n",
+ "2630 0 0 0 0 0 \n",
+ "31584 0 0 0 0 0 \n",
+ "12668 0 0 0 0 0 \n",
+ "1407 0 0 0 0 0 \n",
+ "... ... ... ... ... ... \n",
+ "32370 0 0 0 0 0 \n",
+ "17461 0 0 0 0 0 \n",
+ "5079 0 0 0 0 0 \n",
+ "29413 0 0 0 0 0 \n",
+ "33385 0 0 0 0 0 \n",
+ "\n",
+ "[29400 rows x 785 columns]"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "6d2c5098-ba6b-4533-be14-a7e1395c944b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "one_hot = OneHotEncoder(sparse = False, categories = \"auto\")\n",
+ "train_target = one_hot.fit_transform(train_target.reshape(-1, 1))\n",
+ "test_target = one_hot.transform(test_target.reshape(-1, 1))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "7417c6e4-fd30-498c-a4de-5657ffb0e5f1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "---- Model Summary ----\n",
+ "Layer 1: relu\n",
+ "W: (32, 784) b: (32, 1)\n",
+ "Trainable parameters: 25120\n",
+ "Layer 2: relu\n",
+ "W: (16, 32) b: (16, 1)\n",
+ "Trainable parameters: 528\n",
+ "Layer 3: softmax\n",
+ "W: (10, 16) b: (10, 1)\n",
+ "Trainable parameters: 170\n"
+ ]
+ }
+ ],
+ "source": [
+ "NN = NeuralNetwork(input_size = train_data.shape[1])\n",
+ "NN.add_layer(32, \"relu\")\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(10, \"softmax\")\n",
+ "NN.compile(loss = \"categorical crossentropy\")\n",
+ "NN.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "bc46f780-ff80-43ec-8eae-0e31ecd39a30",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training epoch 1...\n",
+ "Current loss: 0.4636323587703678\n",
+ "Epoch 1 done!\n",
+ "Training epoch 2...\n",
+ "Current loss: 0.22331880492244685\n",
+ "Epoch 2 done!\n",
+ "Training epoch 3...\n",
+ "Current loss: 0.1736999159275795\n",
+ "Epoch 3 done!\n",
+ "Training epoch 4...\n",
+ "Current loss: 0.14369509648982923\n",
+ "Epoch 4 done!\n",
+ "Training epoch 5...\n",
+ "Current loss: 0.12427214647108864\n",
+ "Epoch 5 done!\n",
+ "Training epoch 6...\n",
+ "Current loss: 0.1101834383565226\n",
+ "Epoch 6 done!\n",
+ "Training epoch 7...\n",
+ "Current loss: 0.10044103041530172\n",
+ "Epoch 7 done!\n",
+ "Training epoch 8...\n",
+ "Current loss: 0.09091286128970821\n",
+ "Epoch 8 done!\n",
+ "Training epoch 9...\n",
+ "Current loss: 0.08300819622254964\n",
+ "Epoch 9 done!\n",
+ "Training epoch 10...\n",
+ "Current loss: 0.07745555155379909\n",
+ "Epoch 10 done!\n",
+ "Training epoch 11...\n",
+ "Current loss: 0.07170223282036263\n",
+ "Epoch 11 done!\n",
+ "Training epoch 12...\n",
+ "Current loss: 0.068338226505863\n",
+ "Epoch 12 done!\n",
+ "Training epoch 13...\n",
+ "Current loss: 0.06136732501577605\n",
+ "Epoch 13 done!\n",
+ "Training epoch 14...\n",
+ "Current loss: 0.0559277977809122\n",
+ "Epoch 14 done!\n",
+ "Training epoch 15...\n",
+ "Current loss: 0.05419667267944242\n",
+ "Epoch 15 done!\n",
+ "Training epoch 16...\n",
+ "Current loss: 0.050900625678517726\n",
+ "Epoch 16 done!\n",
+ "Training epoch 17...\n",
+ "Current loss: 0.04833006784938205\n",
+ "Epoch 17 done!\n",
+ "Training epoch 18...\n",
+ "Current loss: 0.04177950013769969\n",
+ "Epoch 18 done!\n",
+ "Training epoch 19...\n",
+ "Current loss: 0.040692531474785014\n",
+ "Epoch 19 done!\n",
+ "Training epoch 20...\n",
+ "Current loss: 0.039062151996810276\n",
+ "Epoch 20 done!\n",
+ "Training epoch 21...\n",
+ "Current loss: 0.040631697529318576\n",
+ "Epoch 21 done!\n",
+ "Training epoch 22...\n",
+ "Current loss: 0.03587460961150384\n",
+ "Epoch 22 done!\n",
+ "Training epoch 23...\n",
+ "Current loss: 0.03348255795122354\n",
+ "Epoch 23 done!\n",
+ "Training epoch 24...\n",
+ "Current loss: 0.031529388385383085\n",
+ "Epoch 24 done!\n",
+ "Training epoch 25...\n",
+ "Current loss: 0.029643544721363053\n",
+ "Epoch 25 done!\n",
+ "Training epoch 26...\n",
+ "Current loss: 0.028773139206400806\n",
+ "Epoch 26 done!\n",
+ "Training epoch 27...\n",
+ "Current loss: 0.022705054266604976\n",
+ "Epoch 27 done!\n",
+ "Training epoch 28...\n",
+ "Current loss: 0.02103327505716646\n",
+ "Epoch 28 done!\n",
+ "Training epoch 29...\n",
+ "Current loss: 0.027954782898974195\n",
+ "Epoch 29 done!\n",
+ "Training epoch 30...\n",
+ "Current loss: 0.026080962157392643\n",
+ "Epoch 30 done!\n",
+ "Training finished after epoch 30 with a loss of 0.026080962157392643.\n"
+ ]
+ }
+ ],
+ "source": [
+ "hist = NN.fit(train_data, train_target, epochs = 30, batch_size = 16, learning_rate = 0.05)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "5d833848-9d24-47b3-b690-d736a50ebe4c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjkAAAHHCAYAAABdm0mZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABFFElEQVR4nO3deXxU1f3/8fdMkpns+w6RsMmmhMoSI6IoqYCIoqC4tFJq9aviglR/Sm1B7dcvVK2lFsVqVax1QVTUoqISAUWjCAiIIAKyhCU72Ukmmbm/P0IGIoshzMxNJq/n43Efydy5M/OZ20vz9pxzz7EYhmEIAADAz1jNLgAAAMAbCDkAAMAvEXIAAIBfIuQAAAC/RMgBAAB+iZADAAD8EiEHAAD4JUIOAADwS4QcAADglwg5ANoki8WiBx544KRft3PnTlksFs2fP9/jNQFoXwg5AI5r/vz5slgsslgsWrly5VHPG4ahtLQ0WSwWXXLJJSZU2HrLly+XxWLRG2+8YXYpALyEkAPgZwUHB+uVV145av+KFSu0Z88e2e12E6oCgBMj5AD4WRdffLEWLlyohoaGZvtfeeUVDRw4UMnJySZVBgDHR8gB8LOuueYalZSU6OOPP3bvczgceuONN3Tttdce8zXV1dX6/e9/r7S0NNntdvXq1UuPPfaYDMNodlxdXZ3uuusuJSQkKCIiQpdeeqn27NlzzPfcu3evfvvb3yopKUl2u139+vXT888/77kvegw//vijrrzySsXGxio0NFRnn3223nvvvaOO+8c//qF+/fopNDRUMTExGjRoULPWr8rKSk2dOlXp6emy2+1KTEzUL3/5S61du9ar9QMdGSEHwM9KT09XVlaWXn31Vfe+Dz74QOXl5br66quPOt4wDF166aX629/+plGjRunxxx9Xr169dM8992jatGnNjv3d736nOXPm6KKLLtLs2bMVFBSkMWPGHPWeBQUFOvvss7V06VLddttt+vvf/64ePXrohhtu0Jw5czz+nZs+85xzztGHH36oW2+9VQ8//LBqa2t16aWXatGiRe7jnn32Wd1xxx3q27ev5syZowcffFADBgzQV1995T7m5ptv1rx58zR+/Hg99dRTuvvuuxUSEqLNmzd7pXYAkgwAOI4XXnjBkGR8/fXXxty5c42IiAijpqbGMAzDuPLKK40LLrjAMAzD6NKlizFmzBj3695++21DkvG///u/zd5vwoQJhsViMbZt22YYhmGsW7fOkGTceuutzY679tprDUnGzJkz3ftuuOEGIyUlxSguLm527NVXX21ERUW569qxY4chyXjhhRdO+N2WLVtmSDIWLlx43GOmTp1qSDI+++wz977Kykqja9euRnp6uuF0Og3DMIzLLrvM6Nev3wk/LyoqypgyZcoJjwHgWbTkAGiRq666SgcPHtTixYtVWVmpxYsXH7er6v3331dAQIDuuOOOZvt///vfyzAMffDBB+7jJB113NSpU5s9NgxDb775psaOHSvDMFRcXOzeRo4cqfLycq90+7z//vsaMmSIzj33XPe+8PBw3XTTTdq5c6c2bdokSYqOjtaePXv09ddfH/e9oqOj9dVXX2nfvn0erxPAsRFyALRIQkKCsrOz9corr+itt96S0+nUhAkTjnnsrl27lJqaqoiIiGb7+/Tp436+6afValX37t2bHderV69mj4uKilRWVqZnnnlGCQkJzbbJkydLkgoLCz3yPX/6PX5ay7G+x7333qvw8HANGTJEPXv21JQpU/T55583e80jjzyijRs3Ki0tTUOGDNEDDzygH3/80eM1Azgs0OwCALQf1157rW688Ubl5+dr9OjRio6O9snnulwuSdKvfvUrTZo06ZjH9O/f3ye1HEufPn20ZcsWLV68WEuWLNGbb76pp556SjNmzNCDDz4oqbElbNiwYVq0aJE++ugjPfroo/rLX/6it956S6NHjzatdsCf0ZIDoMUuv/xyWa1Wffnll8ftqpKkLl26aN++faqsrGy2//vvv3c/3/TT5XJp+/btzY7bsmVLs8dNd145nU5lZ2cfc0tMTPTEVzzqe/y0lmN9D0kKCwvTxIkT9cILL2j37t0aM2aMe6Byk5SUFN166616++23tWPHDsXFxenhhx/2eN0AGhFyALRYeHi45s2bpwceeEBjx4497nEXX3yxnE6n5s6d22z/3/72N1ksFnfLRdPPJ554otlxP71bKiAgQOPHj9ebb76pjRs3HvV5RUVFrfk6P+viiy/WqlWrlJub695XXV2tZ555Runp6erbt68kqaSkpNnrbDab+vbtK8MwVF9fL6fTqfLy8mbHJCYmKjU1VXV1dV6pHQDdVQBO0vG6i440duxYXXDBBbr//vu1c+dOZWRk6KOPPtI777yj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78UzVw4AAB7Xqpac+++/X88995xmz56toUOHSpJWrlypBx54QLW1tXr44Yc9WqS/cy/tQHcVAAAe06qQ8+KLL+pf//qXe/VxSerfv786deqkW2+9lZBzkppWIi+muwoAAI9pVXdVaWmpevfufdT+3r17q7S09JSL6mjiWIkcAACPa1XIycjI0Ny5c4/aP3fuXPXv3/+Ui+po4sNZ2gEAAE9rVXfVI488ojFjxmjp0qXuOXJyc3OVl5en999/36MFdgQs0gkAgOe1qiXn/PPP1w8//KDLL79cZWVlKisr0xVXXKHvvvtOL730kqdr9HtNY3JKaxxyuli/CgAAT7AYHlwVcv369TrrrLPkdDo99ZYeV1FRoaioKJWXl7eZJSganC71uP8DSdLqP2a7u68AAECj1vz9blVLDjwrMMCqmNAgSXRZAQDgKYScNuLwrMfcYQUAgCcQctqIuDAGHwMA4EkndXfVFVdcccLny8rKTqWWDi01OkSS9M3uMo3NSDW5GgAA2r+TasmJioo64dalSxddf/313qrVr106oDHYvLEmTwcdbXfgNgAA7cVJteS88MIL3qqjwzu/Z4LSYkOUV3pQ/92wT1cNSjO7JAAA2jXG5LQRVqtF12V2kST958tdJlcDAED7R8hpQ64alCZboFUb9pRrfV6Z2eUAANCuEXLakNgwmy45M0USrTkAAJwqQk4bc93ZjV1W767fp7IabicHAKC1CDltzFmnRatvSqTqGlx6Y80es8sBAKDdIuS0MRaLRb/OOjwA2cWCnQAAtAohpw26bECqIuyB2llSo8+3F5tdDgAA7RIhpw0KtQVq/MDOkqSXchmADABAaxBy2qhfnX2aJGnp5gLtKztocjUAALQ/hJw2qkdihM7uFiuXIb22arfZ5QAA0O4QctqwX5+dLkl69es8ORpc5hYDAEA70yZCzpNPPqn09HQFBwcrMzNTq1atOu6xzz77rIYNG6aYmBjFxMQoOzv7hMe3Zxf1S1JChF1FlXX6aFO+2eUAANCumB5yFixYoGnTpmnmzJlau3atMjIyNHLkSBUWFh7z+OXLl+uaa67RsmXLlJubq7S0NF100UXau3evjyv3vqAAq64Z3LhQJzMgAwBwciyGYZg6EUtmZqYGDx6suXPnSpJcLpfS0tJ0++2367777vvZ1zudTsXExGju3Lm6/vrrf/b4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sWnTJuOmm24yoqOjjfz8fLNLa7N+//vfG8uXLzd27NhhfP7550Z2drYRHx9vFBYWml1am1JZWWl88803xjfffGNIMh5//HHjm2++MXbt2mUYhmHMnj3biI6ONt555x1jw4YNxmWXXWZ07drVOHjwoMmVm+tE562ystK4++67jdzcXGPHjh3G0qVLjbPOOsvo2bOnUVtba3bpprnllluMqKgoY/ny5cb+/fvdW01NjfuYm2++2TjttNOMTz75xFi9erWRlZVlZGVlmVi1+X7uvG3bts146KGHjNWrVxs7duww3nnnHaNbt27GeeedZ3Ll5rrvvvuMFStWGDt27DA2bNhg3HfffYbFYjE++ugjwzA8d60RcjzkH//4h3HaaacZNpvNGDJkiPHll1+aXVKbNnHiRCMlJcWw2WxGp06djIkTJxrbtm0zu6w2Z9myZYako7ZJkyYZhtF4G/mf/vQnIykpybDb7caIESOMLVu2mFt0G3Ci81ZTU2NcdNFFRkJCghEUFGR06dLFuPHGGzv8f5Qc63xJMl544QX3MQcPHjRuvfVWIyYmxggNDTUuv/xyY//+/eYV3Qb83HnbvXu3cd555xmxsbGG3W43evToYdxzzz1GeXm5uYWb7Le//a3RpUsXw2azGQkJCcaIESPcAccwPHetWQzDMFrZsgQAANBmMSYHAAD4JUIOAADwS4QcAADglwg5AADALxFyAACAXyLkAAAAv0TIAQAAfomQA6BDslgsevvtt80uA4AXEXIA+NxvfvMbWSyWo7ZRo0aZXRoAPxJodgEAOqZRo0bphRdeaLbPbrebVA0Af0RLDgBT2O12JScnN9tiYmIkNXYlzZs3T6NHj1ZISIi6deumN954o9nrv/32W1144YUKCQlRXFycbrrpJlVVVTU75vnnn1e/fv1kt9uVkpKi2267rdnzxcXFuvzyyxUaGqqePXvq3XffdT934MABXXfddUpISFBISIh69ux5VCgD0LYRcgC0SX/60580fvx4rV+/Xtddd52uvvpqbd68WZJUXV2tkSNHKiYmRl9//bUWLlyopUuXNgsx8+bN05QpU3TTTTfp22+/1bvvvqsePXo0+4wHH3xQV111lTZs2KCLL75Y1113nUpLS92fv2nTJn3wwQfavHmz5s2bp/j4eN+dAACnznNrigJAy0yaNMkICAgwwsLCmm0PP/ywYRiNKzvffPPNzV6TmZlp3HLLLYZhGMYzzzxjxMTEGFVVVe7n33vvPcNqtbpXE09NTTXuv//+49YgyfjjH//oflxVVWVIMj744APDMAxj7NixxuTJkz3zhQGYgjE5AExxwQUXaN68ec32xcbGun/Pyspq9lxWVpbWrVsnSdq8ebMyMjIUFhbmfn7o0KFyuVzasmWLLBaL9u3bpxEjRpywhv79+7t/DwsLU2RkpAoLCyVJt9xyi8aPH6+1a9fqoosu0rhx43TOOee06rsCMAchB4ApwsLCjuo+8pSQkJAWHRcUFNTsscVikcvlkiSNHj1au3bt0vvvv6+PP/5YI0aM0JQpU/TYY495vF4A3sGYHABt0pdffnnU4z59+kiS+vTpo/Xr16u6utr9/Oeffy6r1apevXopIiJC6enpysnJOaUaEhISNGnSJP3nP//RnDlz9Mwzz5zS+wHwLVpyAJiirq5O+fn5zfYFBga6B/cuXLhQgwYN0rnnnquXX35Zq1at0nPPPSdJuu666zRz5kxNmjRJDzzwgIqKinT77bfr17/+tZKSkiRJDzzwgG6++WYlJiZq9OjRqqys1Oeff67bb7+9RfXNmDFDAwcOVL9+/VRXV6fFixe7QxaA9oGQA8AUS5YsUUpKSrN9vXr10vfffy+p8c6n1157TbfeeqtSUlL06quvqm/fvpKk0NBQffjhh7rzzjs1ePBghYaGavz48Xr88cfd7zVp0iTV1tbqb3/7m+6++27Fx8drwoQJLa7PZrNp+vTp2rlzp0JCQjRs2DC99tprHvjmAHzFYhiGYXYRAHAki8WiRYsWady4cWaXAqAdY0wOAADwS4QcAADglxiTA6DNoRcdgCfQkgMAAPwSIQcAAPglQg4AAPBLhBwAAOCXCDkAAMAvEXIAAIBfIuQAAAC/RMgBAAB+iZADAAD80v8H5Zt/orha694AAAAASUVORK5CYII=\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def plot_history(hist):\n",
+ " plt.plot(hist)\n",
+ " plt.title(\"Model Loss\")\n",
+ " plt.xlabel(\"Epochs\")\n",
+ " plt.ylabel(\"Loss\")\n",
+ " plt.show()\n",
+ " \n",
+ "plot_history(hist);"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "7f30a5bf-caca-44fc-8d5a-8ed8863600e6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training accuracy: 0.9939455782312925\n",
+ "Test accuracy: 0.9577777777777777\n"
+ ]
+ }
+ ],
+ "source": [
+ "train_predictions = np.argmax(NN.predict(train_data), axis = 1)\n",
+ "print(\"Training accuracy: \", accuracy_score(train[\"label\"].to_numpy(), train_predictions))\n",
+ "test_predictions = np.argmax(NN.predict(test_data), axis = 1)\n",
+ "print(\"Test accuracy: \", accuracy_score(test[\"label\"].to_numpy(), test_predictions))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "5d3ea10a-9a1a-4f7b-8dad-3a112b3e5add",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def predict_image(index):\n",
+ " current_image = test_data[index, :]\n",
+ " prediction = np.argmax(NN.predict(current_image), axis = 1)\n",
+ " label = test[\"label\"].to_numpy()[index]\n",
+ " print(\"Prediction: \", prediction)\n",
+ " print(\"Label: \", label)\n",
+ " \n",
+ " current_image = current_image.reshape((28, 28)) * 255\n",
+ " plt.gray()\n",
+ " plt.imshow(current_image, interpolation = \"nearest\")\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "e143b0a5-0cf2-43b7-894c-8497e17b4461",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [9]\n",
+ "Label: 9\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaAAAAGdCAYAAABU0qcqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAa80lEQVR4nO3df2xV9f3H8ddtaa+I7WWltLd3FCiosMiPRQZdozIcDdAlCEoWAZOBYRBZMQPGdCwKui3pZItjLgyTxcDM5MfIBCLZWKDQEl0BQQm6zYayOiDQIiy9F4otSD/fP4j364UWPJd7efdeno/kJNx7z6f37fGEJ7f39tTnnHMCAOAWy7AeAABweyJAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADARA/rAa7W0dGhkydPKicnRz6fz3ocAIBHzjmdO3dOoVBIGRldv87pdgE6efKkiouLrccAANyk48ePq1+/fl0+3u2+BZeTk2M9AgAgAW7093nSArRq1SoNHDhQd9xxh0pLS7V///4vtY5vuwFAerjR3+dJCdDGjRu1ePFiLV++XO+9955GjhypiRMn6vTp08l4OgBAKnJJMGbMGFdZWRm9ffnyZRcKhVxVVdUN14bDYSeJjY2NjS3Ft3A4fN2/7xP+CujixYs6ePCgysvLo/dlZGSovLxcdXV11+zf3t6uSCQSswEA0l/CA3TmzBldvnxZhYWFMfcXFhaqqanpmv2rqqoUCASiG5+AA4Dbg/mn4JYuXapwOBzdjh8/bj0SAOAWSPjPAeXn5yszM1PNzc0x9zc3NysYDF6zv9/vl9/vT/QYAIBuLuGvgLKzszVq1ChVV1dH7+vo6FB1dbXKysoS/XQAgBSVlCshLF68WLNmzdI3vvENjRkzRitXrlRra6uefPLJZDwdACAFJSVAjz/+uD755BMtW7ZMTU1N+vrXv67t27df88EEAMDty+ecc9ZDfFEkElEgELAeAwBwk8LhsHJzc7t83PxTcACA2xMBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADCR8AC98MIL8vl8MdvQoUMT/TQAgBTXIxlf9L777tPOnTv//0l6JOVpAAApLCll6NGjh4LBYDK+NAAgTSTlPaAjR44oFApp0KBBeuKJJ3Ts2LEu921vb1ckEonZAADpL+EBKi0t1dq1a7V9+3atXr1ajY2Neuihh3Tu3LlO96+qqlIgEIhuxcXFiR4JANAN+ZxzLplP0NLSogEDBujll1/WnDlzrnm8vb1d7e3t0duRSIQIAUAaCIfDys3N7fLxpH86oHfv3rr33nvV0NDQ6eN+v19+vz/ZYwAAupmk/xzQ+fPndfToURUVFSX7qQAAKSThAVqyZIlqa2v18ccf6x//+IceffRRZWZmasaMGYl+KgBACkv4t+BOnDihGTNm6OzZs+rbt68efPBB7d27V3379k30UwEAUljSP4TgVSQSUSAQsB4DKe7hhx+Oa933v/99z2t69erlec2UKVM8r9m9e7fnNe+++67nNZK0atUqz2uu9+MWuD3d6EMIXAsOAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADDBxUhxS1VUVHheU1VV5XnN0KFDPa+RpOzs7LjWefXBBx94XjN8+PAkTNK5trY2z2sqKys9r1mzZo3nNUgdXIwUANAtESAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwARXw4aysrLiWvfb3/7W85pZs2Z5XtOzZ0/Pa6qrqz2vkaSNGzd6XvPXv/7V85r//e9/ntfk5eV5XjNjxgzPayTppZde8rzms88+87wmnquWf/zxx57XwAZXwwYAdEsECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkuRgo9+eSTca177bXXPK/55z//6XnNqlWrPK/5wx/+4HmNJF2+fDmudelm3759nteMHj3a85r777/f85pDhw55XgMbXIwUANAtESAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmelgPgMTq0cP7/9KZM2fG9VyfffaZ5zVPP/205zU1NTWe1+DmXLx48ZY8z+TJkz2v4WKk6YNXQAAAEwQIAGDCc4D27NmjyZMnKxQKyefzacuWLTGPO+e0bNkyFRUVqWfPniovL9eRI0cSNS8AIE14DlBra6tGjhzZ5S8JW7FihV555RW9+uqr2rdvn3r16qWJEyeqra3tpocFAKQPz+9YV1RUqKKiotPHnHNauXKlnnvuOU2ZMkWS9Prrr6uwsFBbtmzR9OnTb25aAEDaSOh7QI2NjWpqalJ5eXn0vkAgoNLSUtXV1XW6pr29XZFIJGYDAKS/hAaoqalJklRYWBhzf2FhYfSxq1VVVSkQCES34uLiRI4EAOimzD8Ft3TpUoXD4eh2/Phx65EAALdAQgMUDAYlSc3NzTH3Nzc3Rx+7mt/vV25ubswGAEh/CQ1QSUmJgsGgqquro/dFIhHt27dPZWVliXwqAECK8/wpuPPnz6uhoSF6u7GxUYcOHVJeXp769++vhQsX6he/+IXuuecelZSU6Pnnn1coFNLUqVMTOTcAIMV5DtCBAwf08MMPR28vXrxYkjRr1iytXbtWzzzzjFpbWzVv3jy1tLTowQcf1Pbt23XHHXckbmoAQMrzOeec9RBfFIlEFAgErMdIWfPmzfO85tVXX43rubZt2+Z5zSOPPBLXc0Hy+Xye19xzzz1xPdcHH3zgeU1WVpbnNQ899JDnNe+8847nNbARDoev+76++afgAAC3JwIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJjw/OsY0L119Ztnk+Hq33yL5Bo0aJDnNR999FESJkmcpqYm6xFgiFdAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJLkYKGBg7dqznNX/605+SMEnivPvuu57X/Oc//0nCJEgVvAICAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAExwMdI0E88FIeM1ffp0z2s2bNjgeU11dbXnNfHq1auX5zVz5871vGbFihWe12RmZnpec+bMGc9rJCk/P9/zGufcLVmD9MErIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABBcjTTM7d+70vOb111+P67m+973veV6zZcsWz2vivaBmPLKzsz2viecioVu3bvW85te//rXnNYsXL/a8RpK++93vxrUO8IJXQAAAEwQIAGDCc4D27NmjyZMnKxQKyefzXfMtldmzZ8vn88VskyZNStS8AIA04TlAra2tGjlypFatWtXlPpMmTdKpU6ei2/r1629qSABA+vH8IYSKigpVVFRcdx+/369gMBj3UACA9JeU94BqampUUFCgIUOGaP78+Tp79myX+7a3tysSicRsAID0l/AATZo0Sa+//rqqq6v10ksvqba2VhUVFbp8+XKn+1dVVSkQCES34uLiRI8EAOiGEv5zQNOnT4/+efjw4RoxYoQGDx6smpoajR8//pr9ly5dGvOzCpFIhAgBwG0g6R/DHjRokPLz89XQ0NDp436/X7m5uTEbACD9JT1AJ06c0NmzZ1VUVJTspwIApBDP34I7f/58zKuZxsZGHTp0SHl5ecrLy9OLL76oadOmKRgM6ujRo3rmmWd09913a+LEiQkdHACQ2jwH6MCBA3r44Yejtz9//2bWrFlavXq1Dh8+rD/+8Y9qaWlRKBTShAkT9POf/1x+vz9xUwMAUp7nAI0bN07OuS4f//vf/35TA+HmXLp0yfOaJUuWxPVcLS0tntc88sgjntf06tXL85rW1lbPayRp//79ntf85Cc/8bymvr7e8xog3XAtOACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJhI+K/kRuo5c+ZMXOsWLlx4S9b07dvX85pPPvnE8xrcnIsXL1qPgBTDKyAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQXI0W3x4VFr8jI8P7vxYEDByZ+kC785S9/uWXPhfTAKyAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQXIwVSRGZmpuc1o0ePTsIkQGLwCggAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAA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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "7b1dd60a-05eb-4c3a-9209-ba598be45bb9",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [1]\n",
+ "Label: 1\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaAAAAGdCAYAAABU0qcqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAaUklEQVR4nO3dfUyV9/3/8dfRytEqHIsIB+pNUau2VWnmlBFb106issb7P7RrUl2NTgfNlLUubqvWbQmrXWrj4uz+WGWm1XYuU1OTmSgKpBtovIsx24gQNjECrmaeo1jQyOf3h7+eb08F7YXn8Obm+Ug+iZxzfTjvXTvx2YtzOPqcc04AAHSyPtYDAAB6JwIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMPGQ9wFe1trbq0qVLSkxMlM/nsx4HAOCRc07Xrl1TRkaG+vRp/zqnywXo0qVLGj58uPUYAIAHVFdXp2HDhrV7f5f7EVxiYqL1CACAGLjf3+dxC9C2bdv02GOPqX///srOztbx48e/1j5+7AYAPcP9/j6PS4A+/vhjFRYWauPGjTp16pSysrI0a9YsXb58OR4PBwDojlwcTJ061eXn50e+vn37tsvIyHBFRUX33RsKhZwkFovFYnXzFQqF7vn3fcyvgG7evKmTJ08qNzc3clufPn2Um5urioqKu45vaWlROByOWgCAni/mAfrss890+/ZtpaWlRd2elpamhoaGu44vKipSIBCILN4BBwC9g/m74NavX69QKBRZdXV11iMBADpBzH8PKCUlRX379lVjY2PU7Y2NjQoGg3cd7/f75ff7Yz0GAKCLi/kVUEJCgiZPnqySkpLIba2trSopKVFOTk6sHw4A0E3F5ZMQCgsLtXTpUn3zm9/U1KlT9e6776qpqUnf//734/FwAIBuKC4BWrx4sf773/9qw4YNamho0NNPP62DBw/e9cYEAEDv5XPOOeshviwcDisQCFiPAQB4QKFQSElJSe3eb/4uOABA70SAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMPGQ9AIDe6/333/e8Z+7cuZ73pKSkeN6D+OMKCABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwwYeRAoiJl19+uVP2/O9///O8B10TV0AAABMECABgIuYBevPNN+Xz+aLW+PHjY/0wAIBuLi6vAT311FM6fPjw/z3IQ7zUBACIFpcyPPTQQwoGg/H41gCAHiIurwGdP39eGRkZGjVqlF566SVduHCh3WNbWloUDoejFgCg54t5gLKzs1VcXKyDBw9q+/btqq2t1bPPPqtr1661eXxRUZECgUBkDR8+PNYjAQC6IJ9zzsXzAa5evaqRI0fqnXfe0fLly++6v6WlRS0tLZGvw+EwEQK6oY78Ts/777/veU9Hfg9o6NChnvfgwYVCISUlJbV7f9zfHTB48GCNHTtW1dXVbd7v9/vl9/vjPQYAoIuJ++8BXb9+XTU1NUpPT4/3QwEAupGYB+i1115TWVmZ/v3vf+vvf/+7FixYoL59++rFF1+M9UMBALqxmP8I7uLFi3rxxRd15coVDR06VM8884wqKyv5GSwAIErc34TgVTgcViAQsB4D6NXGjh3rec+5c+c87+nIL6lfuXLF8x7+A9jG/d6EwGfBAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAm4v4P0gHofn72s5953tORDxbtiOPHj3fK4yD+uAICAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACT4NG+jBkpOTO7TvySefjPEkbWtqavK85+23347DJLDAFRAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIPIwV6sC1btnRo3+TJk2M8Sds2bNjgeU9paWnsB4EJroAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABN8GCnQTSQnJ3ve8+STT8ZhkrY1NTV53nPmzJnYD4JugysgAIAJAgQAMOE5QOXl5ZozZ44yMjLk8/m0b9++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9Kjnh3NOBQUF2rt3r44cOaLMzMyo+ydPnqx+/fpFPR+qqqp04cKFHvV8uN95aMuZM2ckqWs9H6zfBfF1fPTRR87v97vi4mL3j3/8w61cudINHjzYNTQ0WI/WqX784x+70tJSV1tb6/72t7+53Nxcl5KS4i5fvmw9Wlxdu3bNnT592p0+fdpJcu+88447ffq0+89//uOcc+7Xv/61Gzx4sNu/f787e/asmzdvnsvMzHSff/658eSxda/zcO3aNffaa6+5iooKV1tb6w4fPuy+8Y1vuMcff9w1Nzdbjx4zq1evdoFAwJWWlrr6+vrIunHjRuSYVatWuREjRrgjR464EydOuJycHJeTk2M4dezd7zxUV1e7X/ziF+7EiROutrbW7d+/340aNcpNnz7dePJo3SJAzjn329/+1o0YMcIlJCS4qVOnusrKSuuROt3ixYtdenq6S0hIcI8++qhbvHixq66uth4r7o4ePeok3bWWLl3qnLvzVuw33njDpaWlOb/f72bMmOGqqqpsh46De52HGzduuJkzZ7qhQ4e6fv36uZEjR7oVK1b0uP9Ia+t/vyS3Y8eOyDGff/65++EPf+geeeQR9/DDD7sFCxa4+vp6u6Hj4H7n4cKFC2769OkuOTnZ+f1+N2bMGPf666+7UChkO/hX8M8xAABMdPnXgAAAPRMBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYOL/AaybWnWEzlcfAAAAAElFTkSuQmCC\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "5d69171e-e864-44a6-9e51-183002a47c90",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [1]\n",
+ "Label: 1\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(3)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "747cd3d0-29d2-444b-83dc-1c4b57687704",
+ "metadata": {},
+ "source": [
+ "### **Binary-Class Classification**\n",
+ "\n",
+ "### **Dataset: [Breast Cancer Wisconsin (Diagnostic) Data Set](https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29)**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "7711d2b6-5aab-471c-aa45-06e0c5ddcb2c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data = pd.read_csv(\"binaryclass_train.csv\", header = None)\n",
+ "data[\"label\"] = data[1].apply(lambda x: 1 if x == \"M\" else 0)\n",
+ "train, test = train_test_split(data, test_size = 0.3)\n",
+ "train_data = train.loc[:, ~train.columns.isin([0, 1, \"label\"])].to_numpy()\n",
+ "train_target = train[\"label\"].to_numpy()\n",
+ "test_data = test.loc[:, ~test.columns.isin([0, 1, \"label\"])].to_numpy()\n",
+ "test_target = test[\"label\"].to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "eaa8f0cc-78f0-4984-b701-437195559a4a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " 0 | \n",
+ " 1 | \n",
+ " 2 | \n",
+ " 3 | \n",
+ " 4 | \n",
+ " 5 | \n",
+ " 6 | \n",
+ " 7 | \n",
+ " 8 | \n",
+ " 9 | \n",
+ " ... | \n",
+ " 23 | \n",
+ " 24 | \n",
+ " 25 | \n",
+ " 26 | \n",
+ " 27 | \n",
+ " 28 | \n",
+ " 29 | \n",
+ " 30 | \n",
+ " 31 | \n",
+ " label | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 421 | \n",
+ " 906564 | \n",
+ " B | \n",
+ " 14.690 | \n",
+ " 13.98 | \n",
+ " 98.22 | \n",
+ " 656.1 | \n",
+ " 0.10310 | \n",
+ " 0.18360 | \n",
+ " 0.145000 | \n",
+ " 0.063000 | \n",
+ " ... | \n",
+ " 18.34 | \n",
+ " 114.10 | \n",
+ " 809.2 | \n",
+ " 0.13120 | \n",
+ " 0.36350 | \n",
+ " 0.32190 | \n",
+ " 0.11080 | \n",
+ " 0.2827 | \n",
+ " 0.09208 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 306 | \n",
+ " 89344 | \n",
+ " B | \n",
+ " 13.200 | \n",
+ " 15.82 | \n",
+ " 84.07 | \n",
+ " 537.3 | \n",
+ " 0.08511 | \n",
+ " 0.05251 | \n",
+ " 0.001461 | \n",
+ " 0.003261 | \n",
+ " ... | \n",
+ " 20.45 | \n",
+ " 92.00 | \n",
+ " 636.9 | \n",
+ " 0.11280 | \n",
+ " 0.13460 | \n",
+ " 0.01120 | \n",
+ " 0.02500 | \n",
+ " 0.2651 | \n",
+ " 0.08385 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 542 | \n",
+ " 921644 | \n",
+ " B | \n",
+ " 14.740 | \n",
+ " 25.42 | \n",
+ " 94.70 | \n",
+ " 668.6 | \n",
+ " 0.08275 | \n",
+ " 0.07214 | \n",
+ " 0.041050 | \n",
+ " 0.030270 | \n",
+ " ... | \n",
+ " 32.29 | \n",
+ " 107.40 | \n",
+ " 826.4 | \n",
+ " 0.10600 | \n",
+ " 0.13760 | \n",
+ " 0.16110 | \n",
+ " 0.10950 | \n",
+ " 0.2722 | \n",
+ " 0.06956 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 492 | \n",
+ " 914062 | \n",
+ " M | \n",
+ " 18.010 | \n",
+ " 20.56 | \n",
+ " 118.40 | \n",
+ " 1007.0 | \n",
+ " 0.10010 | \n",
+ " 0.12890 | \n",
+ " 0.117000 | \n",
+ " 0.077620 | \n",
+ " ... | \n",
+ " 26.06 | \n",
+ " 143.40 | \n",
+ " 1426.0 | \n",
+ " 0.13090 | \n",
+ " 0.23270 | \n",
+ " 0.25440 | \n",
+ " 0.14890 | \n",
+ " 0.3251 | \n",
+ " 0.07625 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 568 | \n",
+ " 92751 | \n",
+ " B | \n",
+ " 7.760 | \n",
+ " 24.54 | \n",
+ " 47.92 | \n",
+ " 181.0 | \n",
+ " 0.05263 | \n",
+ " 0.04362 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " ... | \n",
+ " 30.37 | \n",
+ " 59.16 | \n",
+ " 268.6 | \n",
+ " 0.08996 | \n",
+ " 0.06444 | \n",
+ " 0.00000 | \n",
+ " 0.00000 | \n",
+ " 0.2871 | \n",
+ " 0.07039 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 304 | \n",
+ " 89296 | \n",
+ " B | \n",
+ " 11.460 | \n",
+ " 18.16 | \n",
+ " 73.59 | \n",
+ " 403.1 | \n",
+ " 0.08853 | \n",
+ " 0.07694 | \n",
+ " 0.033440 | \n",
+ " 0.015020 | \n",
+ " ... | \n",
+ " 21.61 | \n",
+ " 82.69 | \n",
+ " 489.8 | \n",
+ " 0.11440 | \n",
+ " 0.17890 | \n",
+ " 0.12260 | \n",
+ " 0.05509 | \n",
+ " 0.2208 | \n",
+ " 0.07638 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 170 | \n",
+ " 87139402 | \n",
+ " B | \n",
+ " 12.320 | \n",
+ " 12.39 | \n",
+ " 78.85 | \n",
+ " 464.1 | \n",
+ " 0.10280 | \n",
+ " 0.06981 | \n",
+ " 0.039870 | \n",
+ " 0.037000 | \n",
+ " ... | \n",
+ " 15.64 | \n",
+ " 86.97 | \n",
+ " 549.1 | \n",
+ " 0.13850 | \n",
+ " 0.12660 | \n",
+ " 0.12420 | \n",
+ " 0.09391 | \n",
+ " 0.2827 | \n",
+ " 0.06771 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 56 | \n",
+ " 857637 | \n",
+ " M | \n",
+ " 19.210 | \n",
+ " 18.57 | \n",
+ " 125.50 | \n",
+ " 1152.0 | \n",
+ " 0.10530 | \n",
+ " 0.12670 | \n",
+ " 0.132300 | \n",
+ " 0.089940 | \n",
+ " ... | \n",
+ " 28.14 | \n",
+ " 170.10 | \n",
+ " 2145.0 | \n",
+ " 0.16240 | \n",
+ " 0.35110 | \n",
+ " 0.38790 | \n",
+ " 0.20910 | \n",
+ " 0.3537 | \n",
+ " 0.08294 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 439 | \n",
+ " 909410 | \n",
+ " B | \n",
+ " 14.020 | \n",
+ " 15.66 | \n",
+ " 89.59 | \n",
+ " 606.5 | \n",
+ " 0.07966 | \n",
+ " 0.05581 | \n",
+ " 0.020870 | \n",
+ " 0.026520 | \n",
+ " ... | \n",
+ " 19.31 | \n",
+ " 96.53 | \n",
+ " 688.9 | \n",
+ " 0.10340 | \n",
+ " 0.10170 | \n",
+ " 0.06260 | \n",
+ " 0.08216 | \n",
+ " 0.2136 | \n",
+ " 0.06710 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 424 | \n",
+ " 907145 | \n",
+ " B | \n",
+ " 9.742 | \n",
+ " 19.12 | \n",
+ " 61.93 | \n",
+ " 289.7 | \n",
+ " 0.10750 | \n",
+ " 0.08333 | \n",
+ " 0.008934 | \n",
+ " 0.019670 | \n",
+ " ... | \n",
+ " 23.17 | \n",
+ " 71.79 | \n",
+ " 380.9 | \n",
+ " 0.13980 | \n",
+ " 0.13520 | \n",
+ " 0.02085 | \n",
+ " 0.04589 | \n",
+ " 0.3196 | \n",
+ " 0.08009 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
398 rows × 33 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " 0 1 2 3 4 5 6 7 8 \\\n",
+ "421 906564 B 14.690 13.98 98.22 656.1 0.10310 0.18360 0.145000 \n",
+ "306 89344 B 13.200 15.82 84.07 537.3 0.08511 0.05251 0.001461 \n",
+ "542 921644 B 14.740 25.42 94.70 668.6 0.08275 0.07214 0.041050 \n",
+ "492 914062 M 18.010 20.56 118.40 1007.0 0.10010 0.12890 0.117000 \n",
+ "568 92751 B 7.760 24.54 47.92 181.0 0.05263 0.04362 0.000000 \n",
+ ".. ... .. ... ... ... ... ... ... ... \n",
+ "304 89296 B 11.460 18.16 73.59 403.1 0.08853 0.07694 0.033440 \n",
+ "170 87139402 B 12.320 12.39 78.85 464.1 0.10280 0.06981 0.039870 \n",
+ "56 857637 M 19.210 18.57 125.50 1152.0 0.10530 0.12670 0.132300 \n",
+ "439 909410 B 14.020 15.66 89.59 606.5 0.07966 0.05581 0.020870 \n",
+ "424 907145 B 9.742 19.12 61.93 289.7 0.10750 0.08333 0.008934 \n",
+ "\n",
+ " 9 ... 23 24 25 26 27 28 29 \\\n",
+ "421 0.063000 ... 18.34 114.10 809.2 0.13120 0.36350 0.32190 0.11080 \n",
+ "306 0.003261 ... 20.45 92.00 636.9 0.11280 0.13460 0.01120 0.02500 \n",
+ "542 0.030270 ... 32.29 107.40 826.4 0.10600 0.13760 0.16110 0.10950 \n",
+ "492 0.077620 ... 26.06 143.40 1426.0 0.13090 0.23270 0.25440 0.14890 \n",
+ "568 0.000000 ... 30.37 59.16 268.6 0.08996 0.06444 0.00000 0.00000 \n",
+ ".. ... ... ... ... ... ... ... ... ... \n",
+ "304 0.015020 ... 21.61 82.69 489.8 0.11440 0.17890 0.12260 0.05509 \n",
+ "170 0.037000 ... 15.64 86.97 549.1 0.13850 0.12660 0.12420 0.09391 \n",
+ "56 0.089940 ... 28.14 170.10 2145.0 0.16240 0.35110 0.38790 0.20910 \n",
+ "439 0.026520 ... 19.31 96.53 688.9 0.10340 0.10170 0.06260 0.08216 \n",
+ "424 0.019670 ... 23.17 71.79 380.9 0.13980 0.13520 0.02085 0.04589 \n",
+ "\n",
+ " 30 31 label \n",
+ "421 0.2827 0.09208 0 \n",
+ "306 0.2651 0.08385 0 \n",
+ "542 0.2722 0.06956 0 \n",
+ "492 0.3251 0.07625 1 \n",
+ "568 0.2871 0.07039 0 \n",
+ ".. ... ... ... \n",
+ "304 0.2208 0.07638 0 \n",
+ "170 0.2827 0.06771 0 \n",
+ "56 0.3537 0.08294 1 \n",
+ "439 0.2136 0.06710 0 \n",
+ "424 0.3196 0.08009 0 \n",
+ "\n",
+ "[398 rows x 33 columns]"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "751772e4-e90b-4c11-ae63-cdb9602529e7",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "---- Model Summary ----\n",
+ "Layer 1: relu\n",
+ "W: (16, 30) b: (16, 1)\n",
+ "Trainable parameters: 496\n",
+ "Layer 2: relu\n",
+ "W: (16, 16) b: (16, 1)\n",
+ "Trainable parameters: 272\n",
+ "Layer 3: sigmoid\n",
+ "W: (1, 16) b: (1, 1)\n",
+ "Trainable parameters: 17\n"
+ ]
+ }
+ ],
+ "source": [
+ "NN = NeuralNetwork(input_size = train_data.shape[1])\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(1, \"sigmoid\")\n",
+ "NN.compile(loss = \"binary crossentropy\")\n",
+ "NN.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "0f010d0a-ceef-4824-b36b-9752547248f1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training finished after epoch 1000 with a loss of 0.17439436582044646.\n"
+ ]
+ }
+ ],
+ "source": [
+ "hist = NN.fit(train_data, train_target, epochs = 1000, batch_size = 32, learning_rate = 0.01, verbose = 0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "f4a324e2-2070-43d5-8cb6-8b07cbb69078",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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NWLECK1fv14LFy60XcqPTnZ2th588EHNnTtXUVFRtsv50fJ6verRo4f+8Ic/SJK6d++u9evX65VXXtHw4cMtV/fj8u677+rtt9/WpEmT1LlzZ61evVojR45URkYG17oRY+jnOM2aNVNYWFi1VRH79u1Tenq6papOX/fdd59mzJihTz/9VC1atPAdT09PV1lZmfLy8vzaH3+d09PTa/x9qHoNlUM7+/fv17nnnqvw8HCFh4frs88+09///neFh4crLS2N61wHmjdvrk6dOvkd69ixo3bu3Cnp2HUK9vdGenq69u/f7/d6RUWFDh06xHU+zmOPPaYnnnhCN954o84++2zdcssteuihhzRmzBhJXOv6UFfXtD7/LiGoHCcyMlLnnXee5s+f7zvm9Xo1f/589e7d22JlpxdjjO677z5NmzZNn3zySbXuwPPOO08RERF+13nTpk3auXOn7zr37t1b69at8/vDMXfuXMXHx1f7odFYXXrppVq3bp1Wr17te/To0UPDhg3z/Zrr/MP17du32vL6b7/9Vq1atZIktW7dWunp6X7XuaCgQEuWLPG7znl5eVqxYoWvzSeffCKv16tevXo1wLc4PRw5ckROp/+PpbCwMHm9Xklc6/pQV9e0d+/e+vzzz1VeXu5rM3fuXLVv3/4HDftIYnnyiSZPnmxcLpeZOHGi+frrr81dd91lEhMT/VZFILh77rnHJCQkmAULFpi9e/f6HkeOHPG1ufvuu03Lli3NJ598YpYvX2569+5tevfu7Xu9atnswIEDzerVq83s2bNNSkoKy2ZP4vhVP8ZwnevC0qVLTXh4uBk9erT57rvvzNtvv21iYmLMW2+95WszduxYk5iYaD744AOzdu1ac9VVV9W4vLN79+5myZIlZuHChaZdu3aNeslsTYYPH27OOOMM3/Lk999/3zRr1sz8v//3/3xtuNbfX2FhoVm1apVZtWqVkWReeOEFs2rVKrNjxw5jTN1c07y8PJOWlmZuueUWs379ejN58mQTExPD8uT68o9//MO0bNnSREZGmp49e5rFixfbLum0IqnGx4QJE3xtSkpKzL333muSkpJMTEyMueaaa8zevXv9zrN9+3YzePBgEx0dbZo1a2YeeeQRU15e3sDf5vRyYlDhOteN//3vf6ZLly7G5XKZDh06mPHjx/u97vV6zW9/+1uTlpZmXC6XufTSS82mTZv82hw8eNDcdNNNJjY21sTHx5vbbrvNFBYWNuTXCHkFBQXmwQcfNC1btjRRUVHmzDPPNE8++aTfkleu9ff36aef1vh38vDhw40xdXdN16xZY/r162dcLpc544wzzNixY+ukfocxx235BwAAEEKYowIAAEIWQQUAAIQsggoAAAhZBBUAABCyCCoAACBkEVQAAEDIIqgAAICQRVABcNpzOByaPn267TIA1AOCCoAf5NZbb5XD4aj2uPzyy22XBuBHINx2AQBOf5dffrkmTJjgd8zlclmqBsCPCT0qAH4wl8ul9PR0v0fVHVMdDofGjRunwYMHKzo6Wmeeeabee+89v/evW7dOP/nJTxQdHa2mTZvqrrvuUlFRkV+bN954Q507d5bL5VLz5s113333+b2em5ura665RjExMWrXrp0+/PBD32uHDx/WsGHDlJKSoujoaLVr165asAIQmggqAOrdb3/7W1133XVas2aNhg0bphtvvFEbN26UJBUXF2vQoEFKSkrSsmXLNGXKFM2bN88viIwbN04jRozQXXfdpXXr1unDDz9U27Zt/T7j2Wef1c9+9jOtXbtWV1xxhYYNG6ZDhw75Pv/rr7/WrFmztHHjRo0bN07NmjVruAsAoPbq5NaGABqt4cOHm7CwMNOkSRO/x+jRo40xlXfTvvvuu/3e06tXL3PPPfcYY4wZP368SUpKMkVFRb7XP/roI+N0Ok1OTo4xxpiMjAzz5JNPBqxBkvnNb37je15UVGQkmVmzZhljjBk6dKi57bbb6uYLA2hQzFEB8INdcsklGjdunN+x5ORk36979+7t91rv3r21evVqSdLGjRvVrVs3NWnSxPd637595fV6tWnTJjkcDu3Zs0eXXnpp0Bq6du3q+3WTJk0UHx+v/fv3S5LuueceXXfddVq5cqUGDhyoq6++Wn369KnVdwXQsAgqAH6wJk2aVBuKqSvR0dGn1C4iIsLvucPhkNfrlSQNHjxYO3bs0MyZMzV37lxdeumlGjFihP785z/Xeb0A6hZzVADUu8WLF1d73rFjR0lSx44dtWbNGhUXF/te//LLL+V0OtW+fXvFxcUpKytL8+fP/0E1pKSkaPjw4Xrrrbf04osvavz48T/ofAAaBj0qAH4wt9utnJwcv2Ph4eG+CatTpkxRjx491K9fP7399ttaunSpXn/9dUnSsGHD9PTTT2v48OF65plndODAAd1///265ZZblJaWJkl65plndPfddys1NVWDBw9WYWGhvvzyS91///2nVN9TTz2l8847T507d5bb7daMGTN8QQlAaCOoAPjBZs+erebNm/sda9++vb755htJlStyJk+erHvvvVfNmzfXO++8o06dOkmSYmJiNGfOHD344IM6//zzFRMTo+uuu04vvPCC71zDhw9XaWmp/vrXv+rRRx9Vs2bNdP31159yfZGRkRo1apS2b9+u6Oho9e/fX5MnT66Dbw6gvjmMMcZ2EQB+vBwOh6ZNm6arr77adikATkPMUQEAACGLoAIAAEIWc1QA1CtGlwH8EPSoAACAkEVQAQAAIYugAgAAQhZBBQAAhCyCCgAACFkEFQAAELIIKgAAIGQRVAAAQMgiqAAAgJD1/wHUSBsv4MOUJAAAAABJRU5ErkJggg==\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plot_history(hist);"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "8b581b00-8d8e-4ea6-80c1-8f11dfbad692",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training accuracy: 0.9346733668341709\n",
+ "Test accuracy: 0.9298245614035088\n"
+ ]
+ }
+ ],
+ "source": [
+ "train_predictions = np.round(NN.predict(train_data))\n",
+ "print(\"Training accuracy: \", accuracy_score(train[\"label\"].to_numpy(), train_predictions))\n",
+ "test_predictions = np.round(NN.predict(test_data))\n",
+ "print(\"Test accuracy: \", accuracy_score(test[\"label\"].to_numpy(), test_predictions))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b49a45e0-9906-4a34-912c-f3ec10ea2fa2",
+ "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.10.8"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/data.zip b/data.zip
new file mode 100644
index 0000000..52bfd04
Binary files /dev/null and b/data.zip differ
diff --git a/neuralnet-colab.ipynb b/neuralnet-colab.ipynb
new file mode 100644
index 0000000..bd1f3b0
--- /dev/null
+++ b/neuralnet-colab.ipynb
@@ -0,0 +1,1923 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "c84d618c-446a-47ea-ac6d-133eb91f9411",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "# **Implementation of a Neural Network *\"from scratch\"* with NumPy**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "2dc17e4f-82ae-4023-84aa-7b3a7f36a6ac",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "import numpy as np\n",
+ "from typing import Tuple\n",
+ "from typing import List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "1c6ec59d-b246-4e8e-8488-f2281ec42d1d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class LayerInitializer:\n",
+ " \"\"\"\n",
+ " Functions for layer weight initialization.\n",
+ " \"\"\"\n",
+ "\n",
+ " # He normal initialization\n",
+ " @staticmethod\n",
+ " def he_normal(size: Tuple[int], fan_in: int) -> np.array:\n",
+ " \"\"\"\n",
+ " HE NORMAL INITIALIZATION\n",
+ " Draws samples from a truncated normal distribution centered at 0 mean\n",
+ " with stddev = sqrt(2 / fan_in) where fan_in is the number of input\n",
+ " units per unit in the layer.\n",
+ " Parameters:\n",
+ " - size: Tuple[int] (rows, columns)\n",
+ " shape of the initialized weight matrix\n",
+ " - fan_in: int\n",
+ " number of input units per unit in the layer\n",
+ " Returns:\n",
+ " - np.array (rows, columns)\n",
+ " He normal initialized weight matrix\n",
+ " Ref:\n",
+ " https://arxiv.org/abs/1502.01852\n",
+ " \"\"\"\n",
+ " return np.random.normal(0, math.sqrt(2 / fan_in), size = size)\n",
+ "\n",
+ " # Glorot / Xavier normal initialization\n",
+ " @staticmethod\n",
+ " def glorot_normal(size: Tuple[int], fan_in: int, fan_out: int) -> np.array:\n",
+ " \"\"\"\n",
+ " GLOROT / XAVIER NORMAL INITIALIZATION\n",
+ " Draws samples from a truncated normal distribution centered at 0 mean\n",
+ " with stddev = sqrt(2 / (fan_in + fan_out)) where fan_in is the number of\n",
+ " input units per unit in the layer and fan_out is the number of output\n",
+ " units per unit in the layer.\n",
+ " Parameters:\n",
+ " - size: Tuple[int] (rows, columns)\n",
+ " shape of the initialized weight matrix\n",
+ " - fan_in: int\n",
+ " number of input units per unit in the layer\n",
+ " - fan_out: int\n",
+ " number of output units per unit in the layer\n",
+ " Returns:\n",
+ " - np.array (rows, columns)\n",
+ " Glorot normal initialized weight matrix\n",
+ " Ref:\n",
+ " http://proceedings.mlr.press/v9/glorot10a.html\n",
+ " \"\"\"\n",
+ " return np.random.normal(0, math.sqrt(2 / (fan_in + fan_out)), size = size)\n",
+ "\n",
+ " # Bias initialization\n",
+ " @staticmethod\n",
+ " def bias(size: Tuple[int]):\n",
+ " \"\"\"\n",
+ " BIAS INITIALIZATION\n",
+ " Initializes the bias vector / matrix with zeros.\n",
+ " Parameters:\n",
+ " - size: Tuple[int] (rows, columns)\n",
+ " shape of the initialized bias vector / matrix\n",
+ " Returns:\n",
+ " - np.array (rows, columns)\n",
+ " Zero initialized bias vector / matrix\n",
+ " Ref:\n",
+ " https://cs231n.github.io/neural-networks-2/\n",
+ " \"\"\"\n",
+ " return np.zeros(shape = size)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "88017f0b-882c-4195-9e20-564626be8284",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class ActivationFunctions:\n",
+ " \"\"\"\n",
+ " Layer activation functions.\n",
+ " \"\"\"\n",
+ "\n",
+ " # Rectified Linear Units\n",
+ " @staticmethod\n",
+ " def relu(x: np.array, derivative: bool = False) -> np.array:\n",
+ " \"\"\"\n",
+ " RECTIFIED LINEAR UNITS\n",
+ " ReLU activation function.\n",
+ " Parameters:\n",
+ " - x: np.array\n",
+ " input matrix to apply activation function to\n",
+ " - derivative: bool\n",
+ " if set to 'True' returns the derivative instead\n",
+ " DEFAULT: False\n",
+ " Returns:\n",
+ " - np.array (same shape as x)\n",
+ " activated x / derivative of x\n",
+ " Ref:\n",
+ " https://en.wikipedia.org/wiki/Rectifier_(neural_networks)\n",
+ " \"\"\"\n",
+ " if not derivative:\n",
+ " return np.maximum(x, 0)\n",
+ " else:\n",
+ " return np.where(x > 0, 1, 0)\n",
+ "\n",
+ " # Sigmoid activation function\n",
+ " @staticmethod\n",
+ " def sigmoid(x: np.array, derivative: bool = False) -> np.array:\n",
+ " \"\"\"\n",
+ " SIGMOID / LOGISTIC FUNCTION\n",
+ " Sigmoid activation function.\n",
+ " Parameters:\n",
+ " - x: np.array\n",
+ " input matrix to apply activation function to\n",
+ " - derivative: bool\n",
+ " if set to 'True' returns the derivative instead\n",
+ " DEFAULT: False\n",
+ " Returns:\n",
+ " - np.array (same shape as x)\n",
+ " activated x / derivative of x\n",
+ " Refs:\n",
+ " https://en.wikipedia.org/wiki/Sigmoid_function\n",
+ " https://en.wikipedia.org/wiki/Activation_function\n",
+ " \"\"\"\n",
+ " def f_sigmoid(x: np.array) -> np.array:\n",
+ " return 1 / (1 + np.exp(-x))\n",
+ "\n",
+ " if not derivative:\n",
+ " return f_sigmoid(x)\n",
+ " else:\n",
+ " return f_sigmoid(x) * (1 - f_sigmoid(x))\n",
+ "\n",
+ " # Softmax activation function\n",
+ " @staticmethod\n",
+ " def softmax(x: np.array, derivative: bool = False) -> np.array:\n",
+ " \"\"\"\n",
+ " SOFTMAX FUNCTION\n",
+ " Stable softmax activation function.\n",
+ " Parameters:\n",
+ " - x: np.array\n",
+ " input matrix to apply activation function to\n",
+ " Returns:\n",
+ " - np.array (same shape as x)\n",
+ " activated x\n",
+ " Refs:\n",
+ " https://en.wikipedia.org/wiki/Softmax_function\n",
+ " https://eli.thegreenplace.net/2016/the-softmax-function-and-its-derivative/\n",
+ " \"\"\"\n",
+ " if not derivative:\n",
+ " n = np.exp(x - np.max(x)) # stable softmax\n",
+ " d = np.sum(n, axis = 0)\n",
+ " return n / d\n",
+ " else:\n",
+ " raise NotImplementedError(\"Softmax derivative not implemented!\")\n",
+ " # https://stackoverflow.com/questions/54976533/derivative-of-softmax-function-in-python\n",
+ " # xr = x.reshape((-1, 1))\n",
+ " # return np.diagflat(x) - np.dot(xr, xr.T)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "654dc815-7e9e-46de-9f01-7d124736fbf5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class LossFunctions:\n",
+ " \"\"\"\n",
+ " Loss functions for neural net fitting.\n",
+ " \"\"\"\n",
+ "\n",
+ " # binary cross entropy loss\n",
+ " @staticmethod\n",
+ " def binary_cross_entropy(y_true: np.array, y_predicted: np.array) -> np.array:\n",
+ " \"\"\"\n",
+ " BINARY CROSS ENTROPY LOSS\n",
+ " Cross entropy loss for binary-class classification.\n",
+ " L[BCE] = - p(i) * log(q(i)) - (1 - p(i)) * log(1 - q(i))\n",
+ " where\n",
+ " - p(i) is the true label\n",
+ " - q(i) is the predicted sigmoid probability\n",
+ " Parameters:\n",
+ " - y_true: np.array (1, sample_size)\n",
+ " true label vector\n",
+ " - y_predicted: np.array (1, sample_size)\n",
+ " the sigmoid probability\n",
+ " Returns:\n",
+ " - np.array (sample_size,)\n",
+ " loss for every given sample\n",
+ " Ref:\n",
+ " https://en.wikipedia.org/wiki/Cross_entropy\n",
+ " \"\"\"\n",
+ " losses = []\n",
+ " for i in range(y_true.shape[1]):\n",
+ " ## stable BCE\n",
+ " losses.append(float(-1 * (y_true[:, i] * np.log(y_predicted[:, i] + 1e-7) + (1 - y_true[:, i]) * np.log(1 - y_predicted[:, i] + 1e-7))))\n",
+ " ## unstable BCE\n",
+ " # losses.append(float(-1 * (y_true[:, i] * np.log(y_predicted[:, i]) + (1 - y_true[:, i]) * np.log(1 - y_predicted[:, i]))))\n",
+ " return np.array(losses)\n",
+ "\n",
+ " # categorical cross entropy loss\n",
+ " @staticmethod\n",
+ " def categorical_cross_entropy(y_true: np.array, y_predicted: np.array) -> np.array:\n",
+ " \"\"\"\n",
+ " CATEGORICAL CROSS ENTROPY LOSS\n",
+ " Cross entropy loss for binary- and multi-class class classification.\n",
+ " L[CCE] = - sum[from i = 0 to n]( p(i) * log(q(i)) )\n",
+ " where\n",
+ " - p(i) is the true label\n",
+ " - q(i) is the predicted softmax probability\n",
+ " - n is the number of classes\n",
+ " Parameters:\n",
+ " - y_true: np.array (n_classes, sample_size)\n",
+ " one-hot encoded true label vector\n",
+ " - y_predicted: np.array (n_classes, sample_size)\n",
+ " the softmax probabilities\n",
+ " Returns:\n",
+ " - np.array (sample_size,)\n",
+ " loss for every given sample\n",
+ " Ref:\n",
+ " https://en.wikipedia.org/wiki/Cross_entropy\n",
+ " \"\"\"\n",
+ " losses = []\n",
+ " for i in range(y_true.shape[1]):\n",
+ " ## stable CCE\n",
+ " # losses.append(float(-1 * np.sum(y_true[:, i] * np.log(y_predicted[:, i] + 1e-7))))\n",
+ " ## unstable CCE\n",
+ " losses.append(float(-1 * np.sum(y_true[:, i] * np.log(y_predicted[:, i]))))\n",
+ "\n",
+ " return np.array(losses)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "6c6c347f-a0d8-4494-b8cf-f29295f42877",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class NeuralNetwork:\n",
+ " \"\"\"\n",
+ " Implementation of a classic feed-forward neural network that is trained via\n",
+ " backpropagation. Adopts a Keras-like interface for convenient usage (see\n",
+ " https://michabirklbauer.github.io/neuralnet for examples).\n",
+ " \"\"\"\n",
+ "\n",
+ " # constructor\n",
+ " def __init__(self, input_size: int):\n",
+ " \"\"\"\n",
+ " CONSTRUCTOR\n",
+ " Initializes the neural network model.\n",
+ " Parameters:\n",
+ " - input_size: int\n",
+ " nr. of features in the training data\n",
+ " Returns:\n",
+ " - None\n",
+ " Example usage:\n",
+ " NN = NeuralNetwork(data.shape[1])\n",
+ " \"\"\"\n",
+ " self.input_size = input_size\n",
+ " self.architecture = []\n",
+ " self.layers = []\n",
+ "\n",
+ " # adding layers\n",
+ " def add_layer(self, units: int, activation: str = \"relu\", initialization: str = None) -> None:\n",
+ " \"\"\"\n",
+ " LAYER MANAGEMENT\n",
+ " Construct the neural network architecture by adding different layers.\n",
+ " Parameters:\n",
+ " - units: int\n",
+ " nr. of units in the layer\n",
+ " - activation: str, one of (\"relu\", \"sigmoid\", \"softmax\")\n",
+ " activation function of the layer\n",
+ " DEFAULT: \"relu\"\n",
+ " - initialization: str, one of (\"he\", \"glorot\")\n",
+ " weight initialization to use\n",
+ " DEFAULT: None, \"relu\" layers are 'he normal' initialized,\n",
+ " all other layers are 'glorot normal'\n",
+ " initialized\n",
+ " Returns:\n",
+ " - None\n",
+ " Example usage:\n",
+ " NN = NeuralNetwork(data.shape[1])\n",
+ " NN.add_layer(16, \"relu\", \"glorot\")\n",
+ " NN.add_layer(8)\n",
+ " NN.add_layer(1, \"sigmoid\")\n",
+ " \"\"\"\n",
+ " if initialization == None:\n",
+ " if activation == \"relu\":\n",
+ " layer_init = \"he\"\n",
+ " else:\n",
+ " layer_init = \"glorot\"\n",
+ " else:\n",
+ " layer_init = initialization\n",
+ "\n",
+ " self.architecture.append({\"units\": units, \"activation\": activation, \"init\": layer_init})\n",
+ "\n",
+ " # compiling model\n",
+ " def compile(self, loss: str = \"categorical crossentropy\") -> None:\n",
+ " \"\"\"\n",
+ " MODEL INITIALIZATION\n",
+ " Initializes all parameters of the neural network architecture and\n",
+ " prepares the model for training.\n",
+ " Parameters:\n",
+ " - loss: str, one of (\"binary crossentropy\", \"categorical crossentropy\")\n",
+ " the loss function that should be used for training\n",
+ " DEFAULT: \"categorical crossentropy\"\n",
+ " Returns:\n",
+ " - None\n",
+ " Example usage:\n",
+ " NN = NeuralNetwork(data.shape[1])\n",
+ " NN.add_layer(16, \"relu\", \"glorot\")\n",
+ " NN.add_layer(8)\n",
+ " NN.add_layer(1, \"sigmoid\")\n",
+ " NN.compile(\"binary crossentropy\")\n",
+ " \"\"\"\n",
+ " self.loss = loss\n",
+ "\n",
+ " # initialize all layer weights and biases\n",
+ " for i in range(len(self.architecture)):\n",
+ " units = self.architecture[i][\"units\"]\n",
+ " activation = self.architecture[i][\"activation\"]\n",
+ " init = self.architecture[i][\"init\"]\n",
+ "\n",
+ " units_previous_layer = self.input_size\n",
+ " if i > 0:\n",
+ " units_previous_layer = self.architecture[i - 1][\"units\"]\n",
+ " units_next_layer = 0\n",
+ " if i < len(self.architecture) - 1:\n",
+ " units_next_layer = self.architecture[i + 1][\"units\"]\n",
+ "\n",
+ " if init == \"he\":\n",
+ " W = LayerInitializer.he_normal((units, units_previous_layer), fan_in = units_previous_layer)\n",
+ " b = LayerInitializer.bias((units, 1))\n",
+ " elif init == \"glorot\":\n",
+ " W = LayerInitializer.glorot_normal((units, units_previous_layer), fan_in = units_previous_layer, fan_out = units_next_layer)\n",
+ " b = LayerInitializer.bias((units, 1))\n",
+ " else:\n",
+ " raise NotImplementedError(\"Layer initialization '\" + init + \"' not implemented!\")\n",
+ "\n",
+ " self.layers.append({\"W\": W, \"b\": b, \"activation\": activation})\n",
+ "\n",
+ " # forward propagation\n",
+ " def __forward_propagation(self, data: np.array) -> None:\n",
+ " \"\"\"\n",
+ " FORWARD PROPAGATION (INTERNAL)\n",
+ " Internal function calculating the forward pass of A(Wx + b).\n",
+ " - The result of 'Wx + b' (L) is stored in self.layers[layer][\"L\"]\n",
+ " - The result of 'Activation(L)' (A) is stored in self.layers[layer][\"A\"]\n",
+ " Parameters:\n",
+ " - data: np.array\n",
+ " input data for the forward pass\n",
+ " Returns:\n",
+ " - None, \"L\" and \"A\" are set in the layer dictionary, to retrieve the\n",
+ " last layer output call 'self.layers[-1][\"A\"]'\n",
+ " \"\"\"\n",
+ "\n",
+ " for i in range(len(self.layers)):\n",
+ "\n",
+ " if i == 0:\n",
+ " A = data\n",
+ " else:\n",
+ " A = self.layers[i - 1][\"A\"]\n",
+ "\n",
+ " # Wx + b where x is the input data for the first layer and otherwise\n",
+ " # the output (A) of the previous layer\n",
+ " self.layers[i][\"L\"] = self.layers[i][\"W\"].dot(A) + self.layers[i][\"b\"]\n",
+ " if self.layers[i][\"activation\"] == \"relu\":\n",
+ " self.layers[i][\"A\"] = ActivationFunctions.relu(self.layers[i][\"L\"])\n",
+ " elif self.layers[i][\"activation\"] == \"sigmoid\":\n",
+ " self.layers[i][\"A\"] = ActivationFunctions.sigmoid(self.layers[i][\"L\"])\n",
+ " elif self.layers[i][\"activation\"] == \"softmax\":\n",
+ " self.layers[i][\"A\"] = ActivationFunctions.softmax(self.layers[i][\"L\"])\n",
+ " else:\n",
+ " raise NotImplementedError(\"Activation function '\" + layer[\"activation\"] + \"' not implemented!\")\n",
+ "\n",
+ " # back propagation\n",
+ " def __back_propagation(self, data: np.array, target: np.array, learning_rate: float = 0.1) -> float:\n",
+ " \"\"\"\n",
+ " BACK PROPAGATION (INTERNAL)\n",
+ " Internal function for learning layer weights and biases using gradient\n",
+ " descent and back propagation.\n",
+ " Parameters:\n",
+ " - data: np.array\n",
+ " input data\n",
+ " - target: np.array\n",
+ " class labels of the input data\n",
+ " - learning_rate: float\n",
+ " learning rate / how far in the direction of the gradient to\n",
+ " go\n",
+ " DEFAULT: 0.1\n",
+ " Returns:\n",
+ " - float\n",
+ " loss of the current forward pass\n",
+ " \"\"\"\n",
+ " # forward pass\n",
+ " self.__forward_propagation(data)\n",
+ "\n",
+ " output = self.layers[-1][\"A\"]\n",
+ " batch_size = data.shape[1]\n",
+ " loss = 0\n",
+ "\n",
+ " # calculate loss of the current forward pass\n",
+ " if self.loss == \"categorical crossentropy\":\n",
+ " losses = LossFunctions.categorical_cross_entropy(y_true = target, y_predicted = output)\n",
+ " # reduction by sum over batch size\n",
+ " loss = float(np.sum(losses) / batch_size)\n",
+ " elif self.loss == \"binary crossentropy\":\n",
+ " losses = LossFunctions.binary_cross_entropy(y_true = target, y_predicted = output)\n",
+ " # reduction by sum over batch size\n",
+ " loss = float(np.sum(losses) / batch_size)\n",
+ " else:\n",
+ " raise NotImplementedError(\"Loss function '\" + self.loss + \"' not implemented!\")\n",
+ "\n",
+ " # calculate and back pass the derivate of the loss w.r.t the output\n",
+ " # activation function\n",
+ " # this implementation suppports CCE + Softmax and BCE + Sigmoid in the\n",
+ " # output layer\n",
+ " if self.loss == \"categorical crossentropy\" and self.layers[-1][\"activation\"] == \"softmax\":\n",
+ " # for categorical cross entropy loss the derivative of softmax simplifies to\n",
+ " # P(i) - Y(i)\n",
+ " # where P(i) is the softmax output and Y(i) is the true label\n",
+ " # https://www.ics.uci.edu/~pjsadows/notes.pdf\n",
+ " # https://math.stackexchange.com/questions/945871/derivative-of-softmax-loss-function\n",
+ " previous_layer_activation = data.T if len(self.layers) == 1 else self.layers[len(self.layers) - 2][\"A\"].T\n",
+ " dL = self.layers[-1][\"A\"] - target\n",
+ " dW = dL.dot(previous_layer_activation) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ "\n",
+ " # parameter tracking\n",
+ " previous_dL = np.copy(dL)\n",
+ " previous_W = np.copy(self.layers[-1][\"W\"])\n",
+ "\n",
+ " # update\n",
+ " self.layers[-1][\"W\"] -= learning_rate * dW\n",
+ " self.layers[-1][\"b\"] -= learning_rate * db\n",
+ " elif self.loss == \"binary crossentropy\" and self.layers[-1][\"activation\"] == \"sigmoid\":\n",
+ " # for binary cross entropy loss the derivative of the loss function is\n",
+ " # L' = -1 * (Y(i) / P(i) - (1 - Y(i)) / (1 - P(i)))\n",
+ " # where P(i) is the sigmoid output and Y(i) is the true label\n",
+ " # and we multiply that with the derivative of the sigmoid function [1]\n",
+ " # https://math.stackexchange.com/questions/2503428/derivative-of-binary-cross-entropy-why-are-my-signs-not-right\n",
+ " previous_layer_activation = data.T if len(self.layers) == 1 else self.layers[len(self.layers) - 2][\"A\"].T\n",
+ " # [1]\n",
+ " # A = np.clip(self.layers[-1][\"A\"], 1e-7, 1 - 1e-7)\n",
+ " # derivative_loss = -1 * np.divide(target, A) + np.divide(1 - target, 1 - A)\n",
+ " # dL = derivative_loss * ActivationFunctions.sigmoid(self.layers[-1][\"L\"], derivative = True)\n",
+ " # alternatively we can directly simplify the derivative of the binary cross entropy loss\n",
+ " # with sigmoid activation function to\n",
+ " # P(i) - Y(i)\n",
+ " # where P(i) is the sigmoid output and Y(i) is the true label\n",
+ " # done in [2]\n",
+ " # https://math.stackexchange.com/questions/4227931/what-is-the-derivative-of-binary-cross-entropy-loss-w-r-t-to-input-of-sigmoid-fu\n",
+ " # [2]\n",
+ " dL = (self.layers[-1][\"A\"] - target) / batch_size\n",
+ " dW = dL.dot(previous_layer_activation) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ "\n",
+ " # parameter tracking\n",
+ " previous_dL = np.copy(dL)\n",
+ " previous_W = np.copy(self.layers[-1][\"W\"])\n",
+ "\n",
+ " # update\n",
+ " self.layers[-1][\"W\"] -= learning_rate * dW\n",
+ " self.layers[-1][\"b\"] -= learning_rate * db\n",
+ " else:\n",
+ " raise NotImplementedError(\"The combination of '\" + self.loss + \" loss' and '\" + self.layers[i][\"activation\"] + \" activation' is not implemented!\")\n",
+ "\n",
+ " # back propagation through the remaining hidden layers\n",
+ " for i in reversed(range(len(self.layers) - 1)):\n",
+ "\n",
+ " if i == 0:\n",
+ " if self.layers[i][\"activation\"] == \"relu\":\n",
+ " dL = previous_W.T.dot(previous_dL) * ActivationFunctions.relu(self.layers[i][\"L\"], derivative = True)\n",
+ " dW = dL.dot(data.T) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ " elif self.layers[i][\"activation\"] == \"sigmoid\":\n",
+ " dL = previous_W.T.dot(previous_dL) * ActivationFunctions.sigmoid(self.layers[i][\"L\"], derivative = True)\n",
+ " dW = dL.dot(data.T) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ " else:\n",
+ " raise NotImplementedError(\"Activation function '\" + self.layers[i][\"activation\"] + \"' not implemented for hidden layers!\")\n",
+ "\n",
+ " # parameter tracking\n",
+ " previous_dL = np.copy(dL)\n",
+ " previous_W = np.copy(self.layers[i][\"W\"])\n",
+ "\n",
+ " #update\n",
+ " self.layers[i][\"W\"] -= learning_rate * dW\n",
+ " self.layers[i][\"b\"] -= learning_rate * db\n",
+ " else:\n",
+ " if self.layers[i][\"activation\"] == \"relu\":\n",
+ " dL = previous_W.T.dot(previous_dL) * ActivationFunctions.relu(self.layers[i][\"L\"], derivative = True)\n",
+ " dW = dL.dot(self.layers[i - 1][\"A\"].T) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ " elif self.layers[i][\"activation\"] == \"sigmoid\":\n",
+ " dL = previous_W.T.dot(previous_dL) * ActivationFunctions.sigmoid(self.layers[i][\"L\"], derivative = True)\n",
+ " dW = dL.dot(self.layers[i - 1][\"A\"].T) / batch_size\n",
+ " db = np.reshape(np.sum(dL, axis = 1), (-1, 1)) / batch_size\n",
+ " else:\n",
+ " raise NotImplementedError(\"Activation function '\" + self.layers[i][\"activation\"] + \"' not implemented for hidden layers!\")\n",
+ "\n",
+ " # parameter tracking\n",
+ " previous_dL = np.copy(dL)\n",
+ " previous_W = np.copy(self.layers[i][\"W\"])\n",
+ "\n",
+ " #update\n",
+ " self.layers[i][\"W\"] -= learning_rate * dW\n",
+ " self.layers[i][\"b\"] -= learning_rate * db\n",
+ "\n",
+ " return loss\n",
+ "\n",
+ " # neural network architecture summary\n",
+ " def summary(self) -> None:\n",
+ " \"\"\"\n",
+ " MODEL SUMMARY\n",
+ " Print a summary of the neural network architecture.\n",
+ " Parameters:\n",
+ " - None\n",
+ " Returns:\n",
+ " - None, prints a summary of the neural network architecture to\n",
+ " stdout\n",
+ " Example usage:\n",
+ " NN.summary()\n",
+ " \"\"\"\n",
+ " print(\"---- Model Summary ----\")\n",
+ " for i, layer in enumerate(self.layers):\n",
+ " print(\"Layer \" + str(i + 1) + \": \" + layer[\"activation\"])\n",
+ " if \"L\" in layer:\n",
+ " print(\"W: \" + str(layer[\"W\"].shape) + \" \" +\n",
+ " \"b: \" + str(layer[\"b\"].shape) + \" \" +\n",
+ " \"L: \" + str(layer[\"L\"].shape) + \" \" +\n",
+ " \"A: \" + str(layer[\"A\"].shape))\n",
+ " else:\n",
+ " print(\"W: \" + str(layer[\"W\"].shape) + \" \" +\n",
+ " \"b: \" + str(layer[\"b\"].shape))\n",
+ " print(\"Trainable parameters: \" + str(\n",
+ " layer[\"W\"].shape[0] * layer[\"W\"].shape[1] +\n",
+ " layer[\"b\"].shape[0] * layer[\"b\"].shape[1]))\n",
+ "\n",
+ " # train neural network on data\n",
+ " def fit(self, X: np.array, y: np.array, epochs: int = 100, batch_size: int = 32, learning_rate: float = 0.1, verbose: int = 1) -> List[float]:\n",
+ " \"\"\"\n",
+ " TRAIN MODEL\n",
+ " Train the neural network.\n",
+ " Parameters:\n",
+ " - X: np.array (samples, features)\n",
+ " input data to train on\n",
+ " - y: np.array (samples, labels) or (labels,)\n",
+ " labels of the input data\n",
+ " - epochs: int\n",
+ " how many iterations to train\n",
+ " DEFAULT: 100\n",
+ " - batch_size: int\n",
+ " how many samples to use per backward pass\n",
+ " DEFAULT: 32\n",
+ " - learning_rate: float\n",
+ " learning rate / how far in the direction of the gradient to\n",
+ " go\n",
+ " DEFAULT: 0.1\n",
+ " - verbose: int, one of (0, 1) / bool\n",
+ " print information for every epoch\n",
+ " DEFAULT: 1 (True)\n",
+ " Returns:\n",
+ " - List[float]\n",
+ " loss history over all epochs\n",
+ " Example usage:\n",
+ " NN.fit(data_train, labels_train)\n",
+ " \"\"\"\n",
+ " # reshaping inputs\n",
+ " if y.ndim == 1:\n",
+ " y = np.reshape(y, (-1, 1))\n",
+ "\n",
+ " data = X.T\n",
+ " target = y.T\n",
+ " sample_size = data.shape[1]\n",
+ "\n",
+ " history = []\n",
+ "\n",
+ " # train network\n",
+ " for i in range(epochs):\n",
+ " if verbose:\n",
+ " print(\"Training epoch \" + str(i + 1) + \"...\")\n",
+ " # generate random batches of size batch_size\n",
+ " idx = np.random.choice(sample_size, sample_size, replace = False)\n",
+ " batches = np.array_split(idx, math.ceil(sample_size / batch_size))\n",
+ " batch_losses = []\n",
+ " for batch in batches:\n",
+ " current_data = data[:, batch]\n",
+ " current_target = target[:, batch]\n",
+ " batch_loss = self.__back_propagation(current_data, current_target, learning_rate = learning_rate)\n",
+ " batch_losses.append(batch_loss)\n",
+ " history.append(np.mean(batch_losses))\n",
+ " if verbose:\n",
+ " print(\"Current loss: \", np.mean(batch_losses))\n",
+ " print(\"Epoch \" + str(i + 1) + \" done!\")\n",
+ "\n",
+ " print(\"Training finished after epoch \" + str(epochs) + \" with a loss of \" + str(history[-1]) + \".\")\n",
+ "\n",
+ " return history\n",
+ "\n",
+ " # predict data with fitted neural network\n",
+ " def predict(self, X: np.array) -> np.array:\n",
+ " \"\"\"\n",
+ " GENERATE PREDICTIONS\n",
+ " Predict labels for the given input data.\n",
+ " Parameters:\n",
+ " - X: np.array (samples, features) or (features,)\n",
+ " input data to predict\n",
+ " Returns:\n",
+ " - np.array\n",
+ " predictions\n",
+ " Example usage:\n",
+ " NN.predict(data_test)\n",
+ " \"\"\"\n",
+ " if X.ndim == 1:\n",
+ " X = np.reshape(X, (1, -1))\n",
+ "\n",
+ " self.__forward_propagation(X.T)\n",
+ "\n",
+ " return self.layers[-1][\"A\"].T"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a60f041d-d688-4b00-8bc1-3e01da0d947f",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "# **Example Usage of `neuralnet.py / class NeuralNetwork`**\n",
+ "\n",
+ "### **Multi-Class Classification**\n",
+ "\n",
+ "### **Dataset: [MNIST](http://yann.lecun.com/exdb/mnist/index.html)**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bb8e53f6-c5bf-4221-8d49-f3bc804b438d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!wget https://raw.githubusercontent.com/michabirklbauer/neuralnet/master/data.zip"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "c7a8280c-d0b9-41d5-88e1-993db76a73b4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from zipfile import ZipFile as zip\n",
+ "\n",
+ "with zip(\"data.zip\") as f:\n",
+ " f.extractall()\n",
+ " f.close()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "579b7aa9-24c5-4dd1-b8b7-719cbb1f7b09",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "from matplotlib import pyplot as plt\n",
+ "from sklearn.metrics import accuracy_score\n",
+ "from sklearn.preprocessing import OneHotEncoder\n",
+ "from sklearn.model_selection import train_test_split"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "a8e4f9b1-140c-42ac-9b04-11e23b27d1eb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data = pd.read_csv(\"multiclass_train.csv\")\n",
+ "train, test = train_test_split(data, test_size = 0.3)\n",
+ "train_data = train.loc[:, train.columns != \"label\"].to_numpy() / 255\n",
+ "train_target = train[\"label\"].to_numpy()\n",
+ "test_data = test.loc[:, test.columns != \"label\"].to_numpy() / 255\n",
+ "test_target = test[\"label\"].to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "f9a8ba9c-7255-40b3-9e5f-f999e89eb257",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " label | \n",
+ " pixel0 | \n",
+ " pixel1 | \n",
+ " pixel2 | \n",
+ " pixel3 | \n",
+ " pixel4 | \n",
+ " pixel5 | \n",
+ " pixel6 | \n",
+ " pixel7 | \n",
+ " pixel8 | \n",
+ " ... | \n",
+ " pixel774 | \n",
+ " pixel775 | \n",
+ " pixel776 | \n",
+ " pixel777 | \n",
+ " pixel778 | \n",
+ " pixel779 | \n",
+ " pixel780 | \n",
+ " pixel781 | \n",
+ " pixel782 | \n",
+ " pixel783 | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 25164 | \n",
+ " 7 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 11904 | \n",
+ " 9 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 37833 | \n",
+ " 1 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 6101 | \n",
+ " 5 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 25019 | \n",
+ " 3 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 21390 | \n",
+ " 7 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 7601 | \n",
+ " 3 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 224 | \n",
+ " 1 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 37582 | \n",
+ " 4 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 12926 | \n",
+ " 2 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " ... | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
29400 rows × 785 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " label pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 \\\n",
+ "25164 7 0 0 0 0 0 0 0 0 \n",
+ "11904 9 0 0 0 0 0 0 0 0 \n",
+ "37833 1 0 0 0 0 0 0 0 0 \n",
+ "6101 5 0 0 0 0 0 0 0 0 \n",
+ "25019 3 0 0 0 0 0 0 0 0 \n",
+ "... ... ... ... ... ... ... ... ... ... \n",
+ "21390 7 0 0 0 0 0 0 0 0 \n",
+ "7601 3 0 0 0 0 0 0 0 0 \n",
+ "224 1 0 0 0 0 0 0 0 0 \n",
+ "37582 4 0 0 0 0 0 0 0 0 \n",
+ "12926 2 0 0 0 0 0 0 0 0 \n",
+ "\n",
+ " pixel8 ... pixel774 pixel775 pixel776 pixel777 pixel778 \\\n",
+ "25164 0 ... 0 0 0 0 0 \n",
+ "11904 0 ... 0 0 0 0 0 \n",
+ "37833 0 ... 0 0 0 0 0 \n",
+ "6101 0 ... 0 0 0 0 0 \n",
+ "25019 0 ... 0 0 0 0 0 \n",
+ "... ... ... ... ... ... ... ... \n",
+ "21390 0 ... 0 0 0 0 0 \n",
+ "7601 0 ... 0 0 0 0 0 \n",
+ "224 0 ... 0 0 0 0 0 \n",
+ "37582 0 ... 0 0 0 0 0 \n",
+ "12926 0 ... 0 0 0 0 0 \n",
+ "\n",
+ " pixel779 pixel780 pixel781 pixel782 pixel783 \n",
+ "25164 0 0 0 0 0 \n",
+ "11904 0 0 0 0 0 \n",
+ "37833 0 0 0 0 0 \n",
+ "6101 0 0 0 0 0 \n",
+ "25019 0 0 0 0 0 \n",
+ "... ... ... ... ... ... \n",
+ "21390 0 0 0 0 0 \n",
+ "7601 0 0 0 0 0 \n",
+ "224 0 0 0 0 0 \n",
+ "37582 0 0 0 0 0 \n",
+ "12926 0 0 0 0 0 \n",
+ "\n",
+ "[29400 rows x 785 columns]"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "6d2c5098-ba6b-4533-be14-a7e1395c944b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "one_hot = OneHotEncoder(sparse = False, categories = \"auto\")\n",
+ "train_target = one_hot.fit_transform(train_target.reshape(-1, 1))\n",
+ "test_target = one_hot.transform(test_target.reshape(-1, 1))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "7417c6e4-fd30-498c-a4de-5657ffb0e5f1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "---- Model Summary ----\n",
+ "Layer 1: relu\n",
+ "W: (32, 784) b: (32, 1)\n",
+ "Trainable parameters: 25120\n",
+ "Layer 2: relu\n",
+ "W: (16, 32) b: (16, 1)\n",
+ "Trainable parameters: 528\n",
+ "Layer 3: softmax\n",
+ "W: (10, 16) b: (10, 1)\n",
+ "Trainable parameters: 170\n"
+ ]
+ }
+ ],
+ "source": [
+ "NN = NeuralNetwork(input_size = train_data.shape[1])\n",
+ "NN.add_layer(32, \"relu\")\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(10, \"softmax\")\n",
+ "NN.compile(loss = \"categorical crossentropy\")\n",
+ "NN.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "bc46f780-ff80-43ec-8eae-0e31ecd39a30",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training epoch 1...\n",
+ "Current loss: 0.43747370824596604\n",
+ "Epoch 1 done!\n",
+ "Training epoch 2...\n",
+ "Current loss: 0.21528007156258966\n",
+ "Epoch 2 done!\n",
+ "Training epoch 3...\n",
+ "Current loss: 0.16742503623911392\n",
+ "Epoch 3 done!\n",
+ "Training epoch 4...\n",
+ "Current loss: 0.13877936553368508\n",
+ "Epoch 4 done!\n",
+ "Training epoch 5...\n",
+ "Current loss: 0.12099309045421619\n",
+ "Epoch 5 done!\n",
+ "Training epoch 6...\n",
+ "Current loss: 0.1072971880634624\n",
+ "Epoch 6 done!\n",
+ "Training epoch 7...\n",
+ "Current loss: 0.09396355017990504\n",
+ "Epoch 7 done!\n",
+ "Training epoch 8...\n",
+ "Current loss: 0.08720308198194518\n",
+ "Epoch 8 done!\n",
+ "Training epoch 9...\n",
+ "Current loss: 0.07927159779935378\n",
+ "Epoch 9 done!\n",
+ "Training epoch 10...\n",
+ "Current loss: 0.07284107143112058\n",
+ "Epoch 10 done!\n",
+ "Training epoch 11...\n",
+ "Current loss: 0.06600162705624461\n",
+ "Epoch 11 done!\n",
+ "Training epoch 12...\n",
+ "Current loss: 0.06342602649693302\n",
+ "Epoch 12 done!\n",
+ "Training epoch 13...\n",
+ "Current loss: 0.05783998850656874\n",
+ "Epoch 13 done!\n",
+ "Training epoch 14...\n",
+ "Current loss: 0.05052314129523882\n",
+ "Epoch 14 done!\n",
+ "Training epoch 15...\n",
+ "Current loss: 0.04563600268741524\n",
+ "Epoch 15 done!\n",
+ "Training epoch 16...\n",
+ "Current loss: 0.04470639462592896\n",
+ "Epoch 16 done!\n",
+ "Training epoch 17...\n",
+ "Current loss: 0.043506537043299306\n",
+ "Epoch 17 done!\n",
+ "Training epoch 18...\n",
+ "Current loss: 0.03815045738567615\n",
+ "Epoch 18 done!\n",
+ "Training epoch 19...\n",
+ "Current loss: 0.038454017529732515\n",
+ "Epoch 19 done!\n",
+ "Training epoch 20...\n",
+ "Current loss: 0.034033571538281876\n",
+ "Epoch 20 done!\n",
+ "Training epoch 21...\n",
+ "Current loss: 0.03033063122611392\n",
+ "Epoch 21 done!\n",
+ "Training epoch 22...\n",
+ "Current loss: 0.02789381646483783\n",
+ "Epoch 22 done!\n",
+ "Training epoch 23...\n",
+ "Current loss: 0.02688368926764838\n",
+ "Epoch 23 done!\n",
+ "Training epoch 24...\n",
+ "Current loss: 0.02944480698302673\n",
+ "Epoch 24 done!\n",
+ "Training epoch 25...\n",
+ "Current loss: 0.02519994251217897\n",
+ "Epoch 25 done!\n",
+ "Training epoch 26...\n",
+ "Current loss: 0.02679484096626338\n",
+ "Epoch 26 done!\n",
+ "Training epoch 27...\n",
+ "Current loss: 0.01805071452172742\n",
+ "Epoch 27 done!\n",
+ "Training epoch 28...\n",
+ "Current loss: 0.021675299545706767\n",
+ "Epoch 28 done!\n",
+ "Training epoch 29...\n",
+ "Current loss: 0.027434799817775905\n",
+ "Epoch 29 done!\n",
+ "Training epoch 30...\n",
+ "Current loss: 0.024449728356841036\n",
+ "Epoch 30 done!\n",
+ "Training finished after epoch 30 with a loss of 0.024449728356841036.\n"
+ ]
+ }
+ ],
+ "source": [
+ "hist = NN.fit(train_data, train_target, epochs = 30, batch_size = 16, learning_rate = 0.05)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "5d833848-9d24-47b3-b690-d736a50ebe4c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjkAAAHHCAYAAABdm0mZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABFGUlEQVR4nO3deXxU9b3/8fdMlsm+hywQCJusklSQGBWXElmkCAqKy62Ua/Wn4kKpfVTqLai9Xqy1liqK1dalrYrFvVRxieAaRYGgKKLsgeyJZCXbzPf3R8xgBHQIM3OSyev5eJxHMmfOzHzm9GDe/Z7vYjPGGAEAAAQYu9UFAAAA+AIhBwAABCRCDgAACEiEHAAAEJAIOQAAICARcgAAQEAi5AAAgIBEyAEAAAGJkAMAAAISIQdAt2Sz2XTrrbce8+t2794tm82mxx57zOs1AehZCDkAjuqxxx6TzWaTzWbTu+++e9jzxhhlZGTIZrPpJz/5iQUVdt26detks9n0zDPPWF0KAB8h5AD4QWFhYXryyScP2//WW29p3759cjgcFlQFAN+PkAPgB5177rlatWqV2traOu1/8sknNXbsWKWmplpUGQAcHSEHwA+65JJLVFVVpddff929r6WlRc8884wuvfTSI76moaFBv/zlL5WRkSGHw6Fhw4bp7rvvljGm03HNzc36xS9+oeTkZEVHR+u8887Tvn37jvie+/fv13//938rJSVFDodDo0aN0iOPPOK9L3oEO3fu1IUXXqiEhARFRETolFNO0X/+85/Djrvvvvs0atQoRUREKD4+XuPGjevU+lVXV6cFCxYoMzNTDodDffr00TnnnKONGzf6tH6gNyPkAPhBmZmZys3N1VNPPeXe98orr6impkYXX3zxYccbY3TeeefpT3/6k6ZMmaJ77rlHw4YN069+9SstXLiw07E///nPtWzZMk2aNEl33nmnQkJCNG3atMPes6ysTKeccoreeOMNXXfddfrzn/+sIUOG6IorrtCyZcu8/p07PvPUU0/Vq6++qmuvvVZ33HGHmpqadN555+n55593H/fwww/rhhtu0MiRI7Vs2TLddtttys7O1ocffug+5uqrr9aKFSs0a9YsPfDAA7rpppsUHh6urVu3+qR2AJIMABzFo48+aiSZjz76yCxfvtxER0ebxsZGY4wxF154oTn77LONMcYMGDDATJs2zf26F154wUgy//u//9vp/WbPnm1sNpvZvn27McaYwsJCI8lce+21nY679NJLjSSzZMkS974rrrjCpKWlmcrKyk7HXnzxxSY2NtZd165du4wk8+ijj37vd1u7dq2RZFatWnXUYxYsWGAkmXfeece9r66uzgwcONBkZmYap9NpjDFmxowZZtSoUd/7ebGxsWb+/PnfewwA76IlB4BHLrroIh08eFCrV69WXV2dVq9efdRbVS+//LKCgoJ0ww03dNr/y1/+UsYYvfLKK+7jJB123IIFCzo9Nsbo2Wef1fTp02WMUWVlpXubPHmyampqfHLb5+WXX9b48eN1+umnu/dFRUXpqquu0u7du/X5559LkuLi4rRv3z599NFHR32vuLg4ffjhhyouLvZ6nQCOjJADwCPJycnKy8vTk08+qeeee05Op1OzZ88+4rF79uxRenq6oqOjO+0fMWKE+/mOn3a7XYMHD+503LBhwzo9rqio0IEDB/TQQw8pOTm50zZv3jxJUnl5uVe+53e/x3drOdL3+PWvf62oqCiNHz9eQ4cO1fz58/Xee+91es1dd92lLVu2KCMjQ+PHj9ett96qnTt3er1mAIcEW10AgJ7j0ksv1ZVXXqnS0lJNnTpVcXFxfvlcl8slSfqv//ovzZ0794jHjBkzxi+1HMmIESO0bds2rV69WmvWrNGzzz6rBx54QIsXL9Ztt90mqb0lbMKECXr++ef12muv6Q9/+IN+//vf67nnntPUqVMtqx0IZLTkAPDY+eefL7vdrg8++OCot6okacCAASouLlZdXV2n/V988YX7+Y6fLpdLO3bs6HTctm3bOj3uGHnldDqVl5d3xK1Pnz7e+IqHfY/v1nKk7yFJkZGRmjNnjh599FHt3btX06ZNc3dU7pCWlqZrr71WL7zwgnbt2qXExETdcccdXq8bQDtCDgCPRUVFacWKFbr11ls1ffr0ox537rnnyul0avny5Z32/+lPf5LNZnO3XHT8vPfeezsd993RUkFBQZo1a5aeffZZbdmy5bDPq6io6MrX+UHnnnuu1q9fr4KCAve+hoYGPfTQQ8rMzNTIkSMlSVVVVZ1eFxoaqpEjR8oYo9bWVjmdTtXU1HQ6pk+fPkpPT1dzc7NPagfA7SoAx+hot4u+bfr06Tr77LN1yy23aPfu3crKytJrr72mF198UQsWLHD3wcnOztYll1yiBx54QDU1NTr11FOVn5+v7du3H/aed955p9auXaucnBxdeeWVGjlypKqrq7Vx40a98cYbqq6u7tL3efbZZ90tM9/9njfffLOeeuopTZ06VTfccIMSEhL0+OOPa9euXXr22Wdlt7f//8RJkyYpNTVVp512mlJSUrR161YtX75c06ZNU3R0tA4cOKB+/fpp9uzZysrKUlRUlN544w199NFH+uMf/9ilugF4wNrBXQC6s28PIf8+3x1Cbkz7UOtf/OIXJj093YSEhJihQ4eaP/zhD8blcnU67uDBg+aGG24wiYmJJjIy0kyfPt0UFRUdNoTcGGPKysrM/PnzTUZGhgkJCTGpqalm4sSJ5qGHHnIfc6xDyI+2dQwb37Fjh5k9e7aJi4szYWFhZvz48Wb16tWd3usvf/mLOeOMM0xiYqJxOBxm8ODB5le/+pWpqakxxhjT3NxsfvWrX5msrCwTHR1tIiMjTVZWlnnggQe+t0YAx8dmzHemHwUAAAgA9MkBAAABiZADAAACEiEHAAAEJEIOAAAISIQcAAAQkAg5AAAgIPW6yQBdLpeKi4sVHR0tm81mdTkAAMADxhjV1dUpPT3dPRHnD+l1Iae4uFgZGRlWlwEAALqgqKhI/fr18+jYXhdyoqOjJbWfpJiYGIurAQAAnqitrVVGRob777gnel3I6bhFFRMTQ8gBAKCHOZauJnQ8BgAAAYmQAwAAAhIhBwAABCRCDgAACEiEHAAAEJAIOQAAICARcgAAQEAi5AAAgIBEyAEAAAGJkAMAAAISIQcAAAQkQg4AAAhIhBwvcbqMymubtKeqwepSAACACDle88HOKo3/v3xd+fePrS4FAACIkOM1iVGhkqTK+haLKwEAABIhx2uSohySpK8bW9TmdFlcDQAAIOR4SXxEqGw2yRipupHWHAAArEbI8ZIgu00JEe23rKq4ZQUAgOUIOV7UccuKkAMAgPUIOV50qPNxs8WVAAAAQo4XdbTkEHIAALAeIceLOlpyqhq4XQUAgNUIOV7kbsmpoyUHAACrEXK8KImWHAAAug1CjhclRnaMrqIlBwAAqxFyvIilHQAA6D4IOV707dFVxhiLqwEAoHcj5HhRR0tOc5tL9c1tFlcDAEDvRsjxoojQYEWEBkli1mMAAKxGyPEy99IODXQ+BgDASoQcL+u4ZVVRR0sOAABWIuR4GS05AAB0D4QcL3NPCEifHAAALEXI8bKOCQFZpBMAAGsRcryMlhwAALoHQo6XJUbRkgMAQHdAyPGyQ0s7EHIAALASIcfLkt2jq7hdBQCAlQg5XtZxu+pAY6tanS6LqwEAoPci5HhZXHiIguw2SVI1rTkAAFimW4Sc+++/X5mZmQoLC1NOTo7Wr1/v0etWrlwpm82mmTNn+rbAY2C325QQSb8cAACsZnnIefrpp7Vw4UItWbJEGzduVFZWliZPnqzy8vLvfd3u3bt10003acKECX6q1HOJ7pBDSw4AAFaxPOTcc889uvLKKzVv3jyNHDlSDz74oCIiIvTII48c9TVOp1OXXXaZbrvtNg0aNMiP1XomOfqbzse05AAAYBlLQ05LS4s2bNigvLw89z673a68vDwVFBQc9XW33367+vTpoyuuuOIHP6O5uVm1tbWdNl/raMlhQkAAAKxjaciprKyU0+lUSkpKp/0pKSkqLS094mveffdd/e1vf9PDDz/s0WcsXbpUsbGx7i0jI+O46/4hTAgIAID1LL9ddSzq6ur005/+VA8//LCSkpI8es2iRYtUU1Pj3oqKinxc5aGVyOmTAwCAdYKt/PCkpCQFBQWprKys0/6ysjKlpqYedvyOHTu0e/duTZ8+3b3P5WqfiyY4OFjbtm3T4MGDO73G4XDI4XD4oPqj65j1uKqBlhwAAKxiaUtOaGioxo4dq/z8fPc+l8ul/Px85ebmHnb88OHD9emnn6qwsNC9nXfeeTr77LNVWFjol1tRnkhiaQcAACxnaUuOJC1cuFBz587VuHHjNH78eC1btkwNDQ2aN2+eJOnyyy9X3759tXTpUoWFhWn06NGdXh8XFydJh+23UsftKjoeAwBgHctDzpw5c1RRUaHFixertLRU2dnZWrNmjbsz8t69e2W396iuQ+6Ox1X1LTLGyGazWVwRAAC9j80YY6wuwp9qa2sVGxurmpoaxcTE+OQzmlqdGv7bNZKkzUsmKTY8xCefAwBAb9GVv989q4mkhwgLCVK0o72RjAkBAQCwBiHHRxKjWNoBAAArEXJ85FDnY1pyAACwAiHHR9wtOQ205AAAYAVCjo+4l3aooyUHAAArEHJ8xH27ilmPAQCwBCHHRzpmPWZCQAAArEHI8ZHESFYiBwDASoQcH6ElBwAAaxFyfMTd8ZiWHAAALEHI8ZHkb0JObVObmtucFlcDAEDvQ8jxkZjwYAXb2xfmrGauHAAA/I6Q4yM2m+3QhIB1hBwAAPyNkONDHXPlVDJXDgAAfkfI8aFE9/pVtOQAAOBvhBwfSorsWImclhwAAPyNkONDSdGsRA4AgFUIOT6UGMmEgAAAWIWQ40MdfXIqaMkBAMDvCDk+xNIOAABYh5DjQx1DyKsYQg4AgN8Rcnwo6VtDyF0uY3E1AAD0LoQcH0r4puNxm8uotqnV4moAAOhdCDk+FBpsV0xYsCTmygEAwN8IOT7WMVdOJZ2PAQDwK0KOjyVFsrQDAABWIOT4mHslcm5XAQDgV4QcHzs0woqQAwCAPxFyfMzdktPA7SoAAPyJkONjHUs7VNbRkgMAgD8RcnwsuWNpB1pyAADwK0KOjyXSJwcAAEsQcnyso+Mx8+QAAOBfhBwf6+h4XN/cpqZWp8XVAADQexByfCzaEazQoPbTTL8cAAD8h5DjYzabTUkdw8gZYQUAgN8QcvzA3fm4gZADAIC/EHL8wD0hYB23qwAA8BdCjh+4R1jRkgMAgN8QcvygoyWHlcgBAPAfQo4fJEV2zJVDSw4AAP5CyPGDpGhacgAA8DdCjh8k0pIDAIDfEXL8gKUdAADwP0KOH3RMBljd0CyXy1hcDQAAvQMhxw/iI9tDjstIBw62WlwNAAC9AyHHD0KC7IqPCJFEvxwAAPyFkOMniVF0PgYAwJ8IOX6S+M0tKzofAwDgH4QcP0mK/maRTlpyAADwC0KOnyRFMiEgAAD+RMjxE/rkAADgX4QcP2FCQAAA/IuQ4yfulcgbaMkBAMAfCDl+ksTtKgAA/IqQ4ycdSzvQ8RgAAP8g5PhJR8fjxhanGlvaLK4GAIDAR8jxk8jQIIWFtJ9uWnMAAPA9Qo6f2Gw2JUbSLwcAAH8h5PhRR78chpEDAOB7hBw/6hhhxdIOAAD4HiHHjw7NlUNLDgAAvkbI8aOOEVYVdbTkAADga4QcP3LfrqIlBwAAnyPk+NGhCQFpyQEAwNcIOX7E0g4AAPgPIcePElnaAQAAvyHk+FHHZIDVjS1yuozF1QAAENgIOX6UEBkqm00yRqqm8zEAAD7VLULO/fffr8zMTIWFhSknJ0fr168/6rHPPfecxo0bp7i4OEVGRio7O1v/+Mc//Fht1wXZbUqI6Jgrh345AAD4kuUh5+mnn9bChQu1ZMkSbdy4UVlZWZo8ebLKy8uPeHxCQoJuueUWFRQU6JNPPtG8efM0b948vfrqq36uvGvolwMAgH9YHnLuueceXXnllZo3b55GjhypBx98UBEREXrkkUeOePxZZ52l888/XyNGjNDgwYN14403asyYMXr33Xf9XHnXMMIKAAD/sDTktLS0aMOGDcrLy3Pvs9vtysvLU0FBwQ++3hij/Px8bdu2TWecccYRj2lublZtbW2nzUqJ7pBDSw4AAL5kaciprKyU0+lUSkpKp/0pKSkqLS096utqamoUFRWl0NBQTZs2Tffdd5/OOeecIx67dOlSxcbGureMjAyvfodjlRjZsRI5LTkAAPiS5beruiI6OlqFhYX66KOPdMcdd2jhwoVat27dEY9dtGiRampq3FtRUZF/i/2O5GhWIgcAwB+CrfzwpKQkBQUFqaysrNP+srIypaamHvV1drtdQ4YMkSRlZ2dr69atWrp0qc4666zDjnU4HHI4HF6t+3h0tOTQ8RgAAN+ytCUnNDRUY8eOVX5+vnufy+VSfn6+cnNzPX4fl8ul5uae0TJCx2MAAPzD0pYcSVq4cKHmzp2rcePGafz48Vq2bJkaGho0b948SdLll1+uvn37aunSpZLa+9iMGzdOgwcPVnNzs15++WX94x//0IoVK6z8Gh7rGEJOx2MAAHzL8pAzZ84cVVRUaPHixSotLVV2drbWrFnj7oy8d+9e2e2HGpwaGhp07bXXat++fQoPD9fw4cP1z3/+U3PmzLHqKxyTjpacqoZmGWNks9ksrggAgMBkM8b0qkWUamtrFRsbq5qaGsXExPj98xtb2jRycfvEhVtum6woh+U5EwCAbq8rf7975OiqniwiNFgRoUGSGGEFAIAvEXIsQL8cAAB8j5BjAUZYAQDge4QcCyRGdkwISEsOAAC+QsixQFIUSzsAAOBrhBwLuIeRE3IAAPAZQo4F3B2PG7hdBQCArxByLODueFxHSw4AAL5CyLFAR0tOFS05AAD4DCHHAvTJAQDA9wg5FugIOV83tqrV6bK4GgAAAhMhxwJx4SGyf7Mu59fcsgIAwCcIORaw221KiOyY9ZiQAwCALxByLMKEgAAA+BYhxyLuzscNhBwAAHyBkGMR94SAddyuAgDAFwg5FnFPCEhLDgAAPkHIsYh7QkA6HgMA4BOEHIu4W3LoeAwAgE8QciySREsOAAA+RcixSGIkSzsAAOBLhByLJEUfmgzQGGNxNQAABB5CjkUSI9tvV7U4XaprbrO4GgAAAg8hxyJhIUGKcgRLol8OAAC+QMixEEs7AADgO4QcCyVG0fkYAABfIeRYqKNfDiuRAwDgfYQcCx0aYUVLDgAA3kbIsVBSJBMCAgDgK4QcC9GSAwCA7xByLHRo1mNacgAA8DZCjoU6ViKvbKAlBwAAbyPkWMi9EnkdIQcAAG8j5FioYzLA2qY2tbS5LK4GAIDAQsixUExYiILtNklSdQP9cgAA8CZCjoXsdtuhfjmMsAIAwKsIORbrGGFFyAEAwLsIORbraMlhGDkAAN5FyLFYchQtOQAA+EKXQk5RUZH27dvnfrx+/XotWLBADz30kNcK6y3cLTl0PAYAwKu6FHIuvfRSrV27VpJUWlqqc845R+vXr9ctt9yi22+/3asFBjrmygEAwDe6FHK2bNmi8ePHS5L+9a9/afTo0Xr//ff1xBNP6LHHHvNmfQEvsSPk0JIDAIBXdSnktLa2yuFo/+P8xhtv6LzzzpMkDR8+XCUlJd6rrhc41PGYlhwAALypSyFn1KhRevDBB/XOO+/o9ddf15QpUyRJxcXFSkxM9GqBgY6OxwAA+EaXQs7vf/97/eUvf9FZZ52lSy65RFlZWZKkl156yX0bC5759hByY4zF1QAAEDiCu/Kis846S5WVlaqtrVV8fLx7/1VXXaWIiAivFdcbJES2h5w2l1HtwTbFRoRYXBEAAIGhSy05Bw8eVHNzszvg7NmzR8uWLdO2bdvUp08frxYY6BzBQYoJa8+aFdyyAgDAa7oUcmbMmKG///3vkqQDBw4oJydHf/zjHzVz5kytWLHCqwX2Bh3DyOl8DACA93Qp5GzcuFETJkyQJD3zzDNKSUnRnj179Pe//1333nuvVwvsDdwhh2HkAAB4TZdCTmNjo6KjoyVJr732mi644ALZ7Xadcsop2rNnj1cL7A1YiRwAAO/rUsgZMmSIXnjhBRUVFenVV1/VpEmTJEnl5eWKiYnxaoG9waGQQ0sOAADe0qWQs3jxYt10003KzMzU+PHjlZubK6m9VedHP/qRVwvsDZKYKwcAAK/r0hDy2bNn6/TTT1dJSYl7jhxJmjhxos4//3yvFddbJNLxGAAAr+tSyJGk1NRUpaamulcj79evHxMBdlFS5KEJAQEAgHd06XaVy+XS7bffrtjYWA0YMEADBgxQXFycfve738nlcnm7xoCXFM3tKgAAvK1LLTm33HKL/va3v+nOO+/UaaedJkl69913deutt6qpqUl33HGHV4sMdIm05AAA4HVdCjmPP/64/vrXv7pXH5ekMWPGqG/fvrr22msJOceoo09OXXObmlqdCgsJsrgiAAB6vi7drqqurtbw4cMP2z98+HBVV1cfd1G9TUxYsEKD2v+nYEJAAAC8o0shJysrS8uXLz9s//LlyzVmzJjjLqq3sdls31qNnH45AAB4Q5duV911112aNm2a3njjDfccOQUFBSoqKtLLL7/s1QJ7i6Qoh0pqmuiXAwCAl3SpJefMM8/Ul19+qfPPP18HDhzQgQMHdMEFF+izzz7TP/7xD2/X2Ct0tOSU1jZZXAkAAIHBZowx3nqzzZs366STTpLT6fTWW3pdbW2tYmNjVVNT062WoPjja9t035vbNWFokv5xRY7V5QAA0K105e93l1py4H0XjcuQzSa981Wldlc2WF0OAAA9HiGnm8hIiNBZJyRLkp5av9fiagAA6PkIOd3IpTkDJEmrNuxTc1v3veUHAEBPcEyjqy644ILvff7AgQPHU0uvd/awZKXFhqmkpklrtpRqRnZfq0sCAKDHOqaWnNjY2O/dBgwYoMsvv9xXtQa84CC7Lj65vyTpiQ+5ZQUAwPE4ppacRx991Fd14BtzTs7QvW9+pfW7qvVVWZ2GpkRbXRIAAD0SfXK6mdTYMOWN6COJ1hwAAI5Htwg5999/vzIzMxUWFqacnBytX7/+qMc+/PDDmjBhguLj4xUfH6+8vLzvPb4nuuybDsjPbtyngy10QAYAoCssDzlPP/20Fi5cqCVLlmjjxo3KysrS5MmTVV5efsTj161bp0suuURr165VQUGBMjIyNGnSJO3fv9/PlfvO6UOS1D8hQnVNbfr3J8VWlwMAQI/k1RmPuyInJ0cnn3yye8FPl8uljIwMXX/99br55pt/8PVOp1Px8fFavny5R52eu+uMx9+1Yt0O/X7NF8rOiNML80+zuhwAACzV42Y8bmlp0YYNG5SXl+feZ7fblZeXp4KCAo/eo7GxUa2trUpISDji883Nzaqtre209QQXjuunkCCbCosOaMv+GqvLAQCgx7E05FRWVsrpdColJaXT/pSUFJWWlnr0Hr/+9a+Vnp7eKSh929KlSzsNc8/IyDjuuv0hKcqhKaPTJElPMgMyAADHzPI+Ocfjzjvv1MqVK/X8888rLCzsiMcsWrRINTU17q2oqMjPVXbdZTntc+a8uGm/6pvbLK4GAICexdKQk5SUpKCgIJWVlXXaX1ZWptTU1O997d13360777xTr732msaMGXPU4xwOh2JiYjptPUXOwAQNTo5UQ4tTL2wKnI7VAAD4g6UhJzQ0VGPHjlV+fr57n8vlUn5+vnJzc4/6urvuuku/+93vtGbNGo0bN84fpVrCZrO5h5M/8eFeWdxHHACAHsXy21ULFy7Uww8/rMcff1xbt27VNddco4aGBs2bN0+SdPnll2vRokXu43//+9/rt7/9rR555BFlZmaqtLRUpaWlqq+vt+or+NSsk/rJEWzX1pJabSo6YHU5AAD0GJaHnDlz5ujuu+/W4sWLlZ2drcLCQq1Zs8bdGXnv3r0qKSlxH79ixQq1tLRo9uzZSktLc2933323VV/Bp2IjQvSTMemSpCeZARkAAI9ZPk+Ov/WUeXK+bePer3XBA+/LEWzX+t/kKTYixOqSAADwqx43Tw4886OMOI1Ii1Fzm0vPbtxndTkAAPQIhJweoL0Dcvtw8ic+3EMHZAAAPEDI6SFm/qivIkODtKOiQR/uqra6HAAAuj1CTg8R5QjWedl9JbUPJwcAAN+PkNODdNyyWrOlRJX1zRZXAwBA90bI6UFG941VVkacWp1Gz2ygAzIAAN+HkNPDdLTmPPnhXrlcdEAGAOBoCDk9zPQx6YoOC9be6ka9u73S6nIAAOi2CDk9THhokGad1E9S+3ByAABwZIScHqjjltUbW8tVVttkcTUAAHRPhJweaGhKtMZnJsjpMnr6oyKrywEAoFsi5PRQl53S3prz1Pq9anO6LK4GAIDuh5DTQ00ZnaqEyFCV1DRp3bYKq8sBAKDbIeT0UI7gIF04lg7IAAAcDSGnB7tkfPstq3VfVqioutHiagAA6F4IOT1YZlKkJgxNkjHSyo9YzwoAgG8j5PRwl37TmvP0R/vUSgdkAADcCDk9XN7IFCVHO1RZ36zXPy+zuhwAALoNQk4PFxJk18UnZ0iS/l6w29piAADoRgg5AeDi8f0VbLfpg53V+vfmYqvLAQCgWyDkBIC+ceGaf/YQSdJvX9yi8jqWegAAgJATIK778RCNSo/RgcZWLXr2UxljrC4JAABLEXICREiQXfdclK3QILvyvyjXMxv2WV0SAACWIuQEkGGp0VpwzlBJ0u3//lzFBw5aXBEAANYh5ASYqyYM0o/6x6muuU2/fvYTblsBAHotQk6ACQ6y6+4Ls+QItuudryr1xIfMhAwA6J0IOQFocHKUfj1luCTp/17eqr1VrGsFAOh9CDkB6menZipnYIIaW5y66ZnNcrm4bQUA6F0IOQHKbrfp7guzFBEapPW7qvXo+7utLgkAAL8i5ASwjIQI3TJthCTprjVfaEdFvcUVAQDgP4ScAHfp+P6aMDRJzW0u/fJfm9XGSuUAgF6CkBPgbDab7po9RtFhwSosOqC/vL3T6pIAAPALQk4vkBYbrlunj5IkLXvjS31RWmtxRQAA+B4hp5e44KS+yhuRolan0cKnN6uljdtWAIDARsjpJWw2m/7vgtGKiwjR5yW1Wr52u9UlAQDgU4ScXqRPdJj+d+ZoSdL9a7frk30HrC0IAAAfIuT0Mj8Zk65pY9LkdBn98l+b1dTqtLokAAB8gpDTC/1uxmglRTn0VXm9/vT6l1aXAwCATxByeqGEyFAtveBESdJD7+zUhj3VFlcEAID3EXJ6qXNGpmjWSf1kjPTLf21WY0ub1SUBAOBVhJxebPH0kUqNCdPuqkbdtWab1eUAAOBVhJxeLDY8RHfNHiNJeuz93Xr98zKLKwIAwHsIOb3cGSck67Kc/pKka/65QS9s2m9xRQAAeAchB7r1vFGakZ2uNpfRgqcL9eh7u6wuCQCA40bIgUKC7PrTRdn62amZkqTb/v25/vjaNhljrC0MAIDjQMiBJMlut2nJ9JG6adIJkqT73tyuW17YIqeLoAMA6JkIOXCz2Wy67sdDdcf5o2WzSU9+uFfXPblRzW3MigwA6HkIOTjMZTkDdP+lJyk0yK5XtpRq3qMfqb6ZeXQAAD0LIQdHdO6JaXps3smKDA3S+zuqdMlDH6iyvtnqsgAA8BghB0d16pAkPXXVKUqIDNWn+2t00YMFKqputLosAAA8QsjB9xrTL07PXJ2rvnHh2lnZoNkPvq9tpXVWlwUAwA8i5OAHDUqO0rPXnKoTUqJUVtusi/5SwKKeAIBuj5ADj6TGhulf/y9XJ/WPU83BVl321w+1dlu51WUBAHBUhBx4LC4iVP/8eY7OGpasplaXrnz8Y5aBAAB0W4QcHJOI0GA9fPk4zfzWMhCPvMsyEACA7oeQg2MWEmTXPRdla95pmZKk21d/rjtf+YLZkQEA3QohB11it9u0+CeHloF48K0dmvMXhpgDALoPQg66rGMZiGVzshXlCNbHe77W1D+/o2c37GNxTwCA5Qg5OG4zf9RXr9w4QSdnxqu+uU2/XLVZ1z21STWNrVaXBgDoxQg58IqMhAitvCpXN006QcF2m/7zSYmm/Pltvb+90urSAAC9FCEHXhNkb7999ew1p2pgUqRKapp02d8+1P+9vJWVzAEAfkfIgddlZcTpPzecrkvG95cx0kNv79TM+9/Xl2UsBwEA8B9CDnwiIjRYSy84UQ9fPk4JkaHaWlKr6fe9q8fe20WnZACAXxBy4FPnjEzRmgUTdOYJyWpuc+nWf3+unz36kcprm6wuDQAQ4Ag58Lk+0WF6bN7Juu28UXIE2/XWlxWa8ud39NpnpVaXBgAIYIQc+IXNZtPcUzO1+vrTNTItRtUNLbrqHxt087OfqKG5zeryAAABiJADvxqaEq3n55+q/3fmINls0sqPijTt3ne0ae/XVpcGAAgwhBz4nSM4SIumjtATP89RWmyYdlc1ataK9/XH17appc1ldXkAgABByIFlTh2cpDU3nqEZ2elyGem+N7fr/AfeY6g5AMArLA85999/vzIzMxUWFqacnBytX7/+qMd+9tlnmjVrljIzM2Wz2bRs2TL/FQqfiI0I0Z8v/pHuv/QkxUWE6LPiWv3kvnf18Ns7WdUcAHBcLA05Tz/9tBYuXKglS5Zo48aNysrK0uTJk1VeXn7E4xsbGzVo0CDdeeedSk1N9XO18KVpY9L02oIzdPawZLW0uXTHy1t1ycMfsKo5AKDLbMbCmdlycnJ08skna/ny5ZIkl8uljIwMXX/99br55pu/97WZmZlasGCBFixYcEyfWVtbq9jYWNXU1CgmJqarpcNHjDFa+VGR/nf152pocSoyNEiLp4/UReMyZLPZrC4PAGCRrvz9tqwlp6WlRRs2bFBeXt6hYux25eXlqaCgwGuf09zcrNra2k4bui+bzaZLxvfXKzeeoZMz49XQ4tSvn/1UP3/8Y5XXMYEgAMBzloWcyspKOZ1OpaSkdNqfkpKi0lLvTRK3dOlSxcbGureMjAyvvTd8p39i+6rmvzl3uEKD7Mr/olyT//S2Xv60xOrSAAA9hOUdj31t0aJFqqmpcW9FRUVWlwQPBdltuuqMwfr3NxMIft3Yqmuf2KgFKzepprHV6vIAAN2cZSEnKSlJQUFBKisr67S/rKzMq52KHQ6HYmJiOm3oWYalRuuF+afpurOHyG6TXigs1uRlb+udryqsLg0A0I1ZFnJCQ0M1duxY5efnu/e5XC7l5+crNzfXqrLQTYUG23XT5GF65ppTNTApUqW1Tfrp39Zr8Ytb1NjCshAAgMNZertq4cKFevjhh/X4449r69atuuaaa9TQ0KB58+ZJki6//HItWrTIfXxLS4sKCwtVWFiolpYW7d+/X4WFhdq+fbtVXwF+dlL/eP3nhtN1ee4ASdLfC/bonHve1t2vbtNXTCIIAPgWS4eQS9Ly5cv1hz/8QaWlpcrOzta9996rnJwcSdJZZ52lzMxMPfbYY5Kk3bt3a+DAgYe9x5lnnql169Z59HkMIQ8c73xVoV+t+kSltYdGXQ1PjdZ52emaPiZdGQkRFlYHAPCmrvz9tjzk+BshJ7AcbHHqtc9L9e/NxXrrywq1Og9dzif1j9N5WemaNiZdydEOC6sEABwvQo4HCDmB60Bji17ZUqqXCov1wa4qdVzZdpt02pAkTc9K1+RRqYoND7G2UADAMSPkeICQ0zuU1TZp9SclemlzsTYXHXDvDw2y66xhyTovO10Th6coPDTIuiIBAB4j5HiAkNP77K5s0L83F+ulzcX6qrzevT8yNEiTRqVq9th+OnVwIstGAEA3RsjxACGn9zLG6IvSOr20uVgvFRZr/4GD7ucGJUXq0pz+mj22n+IiQi2sEgBwJIQcDxByILUHno17D+i5jfv0wqb9amhxSpIcwXb9ZEy6/uuU/srOiKN1BwC6CUKOBwg5+K765ja9WLhf//xgr7aWHFrAdWRajP7rlAGakZ2uSEewhRUCAAg5HiDk4GiMMdpUdED//GCPVn9SopY2lyQpyhGs83/UV5ed0l/DU7lmAMAKhBwPEHLgiQONLXpmwz498eFe7apscO8fNyBe/3XKAE0ZnaqwEEZmAYC/EHI8QMjBsTDG6P0dVXriwz167bMytbna/7kkRIbqwrH9dMn4/spMirS4SgAIfIQcDxBy0FXltU1a+VGRnlq/VyU1h5aSGNMvVueemKZzR6epfyJLSQCALxByPEDIwfFqc7q0dluFnvhwj97+skKub/0LGt03RueemKZpJ6ZpQCItPADgLYQcDxBy4E2V9c169bNSvfxpiQp2VHUKPCPTYjRtTJrOPTFNA7mlBQDHhZDjAUIOfKWqvlmvfV6mlz8t0fs7quT8VuIZkRajaSemauqJaRqcHGVhlQDQMxFyPEDIgT9UN7Totc9K9Z8jBJ7hqdHtfXhOTNOQPgQeAPAEIccDhBz429cNLXr98zL959MSvbe90j1CS2oPPDOy++q87HT1jQu3sEoA6N4IOR4g5MBKBxpb3Le03v2qc+AZn5mg87LTde6JaUqIZP0sAPg2Qo4HCDnoLg40tuiVLaV6sXC/PtxVrY5/icF2m844IVkzstN1zsgURYSypAQAEHI8QMhBd1RSc1D/3lysFwuL9VnxofWzwkOCNGlUimZkp2vC0GSFBNktrBIArEPI8QAhB93d9vI6vVRYrBc3F2tPVaN7f3xEiKaNSdOM7L4a2z9edjsrpAPoPQg5HiDkoKcwxqiw6IBeLCzW6k+KVVnf4n6ub1y4po5O1Qkp0cpIiFD/xAilxoQpiOADIEARcjxAyEFP1OZ0qWBnlV4sLNaaLaWqb2477JiQIJv6xoW3h55vbR0hKCYsxILKAcA7CDkeIOSgp2tqdSp/a7kKdlZqb/VBFVU3at/XjWp1fv8/5biIkEOhJyFCJ/aN1elDkwg/AHoEQo4HCDkIRE6XUVltk/ZWN2pvdaOKvvnZ8fu3b3V9W5DdprED4nX2sD46a1iyhqdGy2bjlheA7oeQ4wFCDnqjhuY2FX3dqL1V7cFnd1WDCnZUaUdFQ6fjUmPCdPbwZJ01rI9OG5KkKAfD1wF0D4QcDxBygEOKqhu1blu51m6r0Ps7KtXU6nI/FxJk08mZCe5WniF9omjlAWAZQo4HCDnAkTW1OvXhrmqt/aJc67aVa/e3hq9L7SO6zh6erLOH9VHu4EQmKQTgV4QcDxByAM/sqmzQum3lWretQgU7q9TSdqiVJzTYrkkjU3ThuAydPiSJoesAfI6Q4wFCDnDsDrY4VbCzUmu/qNDabeXa9/VB93OpMWG64KS+mj22nwYls6o6AN8g5HiAkAMcH2OMPiuu1TMb9umFwv060Njqfm7sgHjNHttPPxmTpmiGpgPwIkKOBwg5gPc0tzn15tZyrdqwT+u2latjUfWwELumjErVheMylDsokSUoABw3Qo4HCDmAb5TXNun5Tfu1asM+bS+vd+/vGxeuWSf11ayx/TQgMdLCCgH0ZIQcDxByAN8yxmjzvhqt+rhIL20uVl3ToSUoxg9M0Oyx/TR1dCq3swAcE0KOBwg5gP80tTr12udlembDPr3zVYU6/msTZLdpTL9YnTIoUbmDEjUuM54h6QC+FyHHA4QcwBolNQf13Mb9enbjPu38zkzLwXabsjLidMqgBOUOStLYAfEKDw2yqFIA3REhxwOEHMB6+75u1Ac7q/XBzioV7KjS/gMHOz0fEmRTdkacu6XnpAHxCgsh9AC9GSHHA4QcoPspqm5Uwc4qfbCzSh/sqFJxTVOn50OD7O2hZ3CixmcmKDnaoYjQIEU6ghURGiRHsJ0lJ4AAR8jxACEH6N6MMSqqPtjeyvNNS09pbdP3vibYbusUeqIcwYoIDVakI8j9MzI0WBGOYEU7ghUdFqyY8BDFhIUoJjz4m58hig4LVkiQ3U/fFMCx6Mrfb3r6AehWbDab+idGqH9ihC46OUPGGO2pamxv5dlZpc37alR7sFUNLW3uBUXbXEa1TW2q/dZIrq4KDwk6LPh0hKG48FBlZcQpd3AiK7QDPQAtOQB6LKfLqLGlTQ3NTjW0tKmx2an65rb2fS1ONTa3ffO4/fmG5jbVN7WprqlNtU2tqj3Y8bNVDS1Ojz832G7TSQPideYJyTpjaLJGpccw4SHgY9yu8gAhB8CRtDldqm9u6xR8aps6/15R16T3d1Rpz3dWaE+IDNXpQ5J0xgnJmjA0SSkxYRZ9CyBwEXI8QMgBcLz2VDXo7a8q9faXFSrYUaX65s63yYanRrsDz8mZCYwMA7yAkOMBQg4Ab2p1urRp7wG9/WWF3vmqQp/sr9G3/6saFmJXzsBETRiapDH94mSMkdNl1OZq/9nqdHV63P7Tdeixs/2nyxgNTIrUSQPilRTlsO4LAxYh5HiAkAPAl6obWvTu9kq982WF3v6qQmW1zV7/jMzECJ00IF7jBiRo7IB4De0TRZ8gBDxCjgcIOQD8xRijr8rr9faXFXrrywoVVTcqyG5TsN3e/jPI9s1jW+f9HY+DbAqy2xVst8lljLaW1OrLsvrDPic6LFgn9Y/X2AHtW1ZGHKO/EHAIOR4g5ADoyWoaW7Wp6Gtt2NO+FRYdUON3RobZbdKItBh36Dmpf7z6xYczYSJ6NEKOBwg5AAJJm9OlL0rr3KFnw56vD1smQ5KSox0anR6jUemxGpUeo5HpMeqfEEHwQY9ByPEAIQdAoCupOaiNew60h569X+uz/TVqcx3+n/poR7BGpMdo1LfCz5A+Ucz6jG6JkOMBQg6A3uZgi1Ofl9Tos+Jafba/Vp+X1GpbaZ1anK7Djg0NsuuE1CiNSovVqL7tAWh4aowi6eMDixFyPEDIAYD2oe/by+vbg09xewDaWlyruubDl8aw2aT02HD1jQ9Xv/hw9YuPUMY3P/vFhystNkzB3az1p9XpUsmBJlU1NGtInyhFh4VYXRKOEyHHA4QcADiyjsVRO0JPx8/yuu8fBh9ktyk1Jkz94sOVkRDhDkL9vglFqTHeD0HGGH3d2Kq91Y3aW92oom+2jsfFBw6q4w6dzSad0CdaP+of980WryHJDLvvaQg5HiDkAMCxqaxvdgeJfV8f/GZr1P5vfj/Sba/vigwNUlRYsCIdwYpyBCsyNFhRYd/87ghSlCNEUY4g9/Pt+9u32oOtKvq6UXurDoWYfV8fPGym6e9yBNsVEx6iiiOEtGhHsLIy4tzBJzsjXgmRoV0+R99mjFFji1ONLU4lRYX2mM7dxhhtLanTm1+UqaSmSZmJkRqUHKnByVHqFx9ueWsdIccDhBwA8B6Xy6iivln7vu4cgDp+3+9hCOqq1Jgw9U+IUL+EcPVPiOi0JUU5ZLfbVF7XpMK9B7Sp6IA27f1am4tqdLD18AVZMxMj9KP+8e3BJyNew9OiFRJklzFGdc1tqq5vUVVDi6rqm1Xd0P579Tdb++/N7mOa29q/85h+sbpywiBNHZ1qeUg4koMtTr2/o1L5X5Rr7RflKqlpOuJxIUE2DUiM1ODkSA1KjtLg5Kj2AJQUpdgI/9wKJOR4gJADAP7jchlVN7aovql9RfiGb1aGb//dqfrmVtU3O90rxNd/a7X4+uY2NbS0KTI0+FB4SYxQRnyE+7ZYV9YFa3O69GVZvTYVfa1Ne9uDz46KhsOOcwTbFRcRouqGFrU6j+9PZd+4cM07LVMXj+9v+USNxQcO6s0vyvXmF+V6b3ulO5BJ7cuQnD4kWSekRGlPdaN2VjRoZ0V9p2O+KykqVIOSotytPoOSIzWkT5QGJEZ6tW5CjgcIOQCA76ppbFXhvvbA0xF8aps63w6LCA1SQmSoEiND239GOdy/tz8OVULkoX1NrU7984O9+nvBblU1tEhqv012aU5//ey0TKXFhvvluzldRpv3HdCbW8uV/0W5tpbUdnq+b1y4fjy8j348oo9yByUeFhxdLqPimoPaWdGgHRX1nX6W1h655Wdonyi9vvBMr34PQo4HCDkAgB/ichntqmpQY7NTCVHtwaarq8k3tTr1/Kb9+us7O90tRsF2m34yJk0/nzBIo/vGerN0SVJdU6ve+apS+VvLtW5buTtkSe0zYp/UP15nD++jiSP6aFhKdJf7DdU3t2lXRYN2VtZrR3m9dlQ2aGdFg05IidKfL/6Rt76OJEKORwg5AAAruFxG674s10Nv79QHO6vd+08dnKgrzxiks05I7lLYqKxv1ufFtdpa0j4H0ufFtdpRUa9vz/8YHRasM09I1sQRfXTmCX281snanwg5HiDkAACstmV/jR5+Z6dWf1Ii5zdpZGifKP18wkDNyO57xFYjp8tod1VDe5gpPhRojjbEf1BypCYO76MfD0/RuMz4Hj+TNSHHA4QcAEB3sf/AQT323i49tb7IPSQ+KcqhubkDlDs4UdvK6tyB5ouSuiOOCrPZpIGJkRqR1r4m2ci0GI1Ii1FqbJi/v45PEXI8QMgBAHQ3tU2tenp9kR59b5eKjzKMW2of/TQ8tT3MjEhrDzTDU6N7xbIbhBwPEHIAAN1Vq9Ollz8t0aPv7VZJzcHDAs3ApEgF9dKZmrvy9zvwox8AAD1ESJBdM7L7akZ2X6tLCQg9uxcSAADAURByAABAQCLkAACAgETIAQAAAYmQAwAAAhIhBwAABCRCDgAACEiEHAAAEJC6Rci5//77lZmZqbCwMOXk5Gj9+vXfe/yqVas0fPhwhYWF6cQTT9TLL7/sp0oBAEBPYXnIefrpp7Vw4UItWbJEGzduVFZWliZPnqzy8vIjHv/+++/rkksu0RVXXKFNmzZp5syZmjlzprZs2eLnygEAQHdm+dpVOTk5Ovnkk7V8+XJJksvlUkZGhq6//nrdfPPNhx0/Z84cNTQ0aPXq1e59p5xyirKzs/Xggw/+4OexdhUAAD1PV/5+W9qS09LSog0bNigvL8+9z263Ky8vTwUFBUd8TUFBQafjJWny5MlHPb65uVm1tbWdNgAAEPgsDTmVlZVyOp1KSUnptD8lJUWlpaVHfE1paekxHb906VLFxsa6t4yMDO8UDwAAujXL++T42qJFi1RTU+PeioqKrC4JAAD4QbCVH56UlKSgoCCVlZV12l9WVqbU1NQjviY1NfWYjnc4HHI4HO7HHV2QuG0FAEDP0fF3+1i6ElsackJDQzV27Fjl5+dr5syZkto7Hufn5+u666474mtyc3OVn5+vBQsWuPe9/vrrys3N9egz6+rqJInbVgAA9EB1dXWKjY316FhLQ44kLVy4UHPnztW4ceM0fvx4LVu2TA0NDZo3b54k6fLLL1ffvn21dOlSSdKNN96oM888U3/84x81bdo0rVy5Uh9//LEeeughjz4vPT1dRUVFio6Ols1m8+p3qa2tVUZGhoqKihi5dQw4b8eOc9Y1nLeu4bx1Deft2H3fOTPGqK6uTunp6R6/n+UhZ86cOaqoqNDixYtVWlqq7OxsrVmzxt25eO/evbLbD3UdOvXUU/Xkk0/qf/7nf/Sb3/xGQ4cO1QsvvKDRo0d79Hl2u139+vXzyXfpEBMTwwXdBZy3Y8c56xrOW9dw3rqG83bsjnbOPG3B6WD5PDmBhDl4uobzduw4Z13DeesazlvXcN6OnbfPWcCPrgIAAL0TIceLHA6HlixZ0mk0F34Y5+3Ycc66hvPWNZy3ruG8HTtvnzNuVwEAgIBESw4AAAhIhBwAABCQCDkAACAgEXIAAEBAIuR4yf3336/MzEyFhYUpJydH69evt7qkbu3WW2+VzWbrtA0fPtzqsrqdt99+W9OnT1d6erpsNpteeOGFTs8bY7R48WKlpaUpPDxceXl5+uqrr6wpthv5ofP2s5/97LDrb8qUKdYU200sXbpUJ598sqKjo9WnTx/NnDlT27Zt63RMU1OT5s+fr8TEREVFRWnWrFmHrSXY23hy3s4666zDrrerr77aooq7hxUrVmjMmDHuSf9yc3P1yiuvuJ/31rVGyPGCp59+WgsXLtSSJUu0ceNGZWVlafLkySovL7e6tG5t1KhRKikpcW/vvvuu1SV1Ow0NDcrKytL9999/xOfvuusu3XvvvXrwwQf14YcfKjIyUpMnT1ZTU5OfK+1efui8SdKUKVM6XX9PPfWUHyvsft566y3Nnz9fH3zwgV5//XW1trZq0qRJamhocB/zi1/8Qv/+97+1atUqvfXWWyouLtYFF1xgYdXW8+S8SdKVV17Z6Xq76667LKq4e+jXr5/uvPNObdiwQR9//LF+/OMfa8aMGfrss88kefFaMzhu48ePN/Pnz3c/djqdJj093SxdutTCqrq3JUuWmKysLKvL6FEkmeeff9792OVymdTUVPOHP/zBve/AgQPG4XCYp556yoIKu6fvnjdjjJk7d66ZMWOGJfX0FOXl5UaSeeutt4wx7ddWSEiIWbVqlfuYrVu3GkmmoKDAqjK7ne+eN2OMOfPMM82NN95oXVE9RHx8vPnrX//q1WuNlpzj1NLSog0bNigvL8+9z263Ky8vTwUFBRZW1v199dVXSk9P16BBg3TZZZdp7969VpfUo+zatUulpaWdrr3Y2Fjl5ORw7Xlg3bp16tOnj4YNG6ZrrrlGVVVVVpfUrdTU1EiSEhISJEkbNmxQa2trp+tt+PDh6t+/P9fbt3z3vHV44oknlJSUpNGjR2vRokVqbGy0orxuyel0auXKlWpoaFBubq5XrzXLF+js6SorK+V0Ot0LinZISUnRF198YVFV3V9OTo4ee+wxDRs2TCUlJbrttts0YcIEbdmyRdHR0VaX1yOUlpZK0hGvvY7ncGRTpkzRBRdcoIEDB2rHjh36zW9+o6lTp6qgoEBBQUFWl2c5l8ulBQsW6LTTTnMvflxaWqrQ0FDFxcV1Opbr7ZAjnTdJuvTSSzVgwAClp6frk08+0a9//Wtt27ZNzz33nIXVWu/TTz9Vbm6umpqaFBUVpeeff14jR45UYWGh1641Qg4sMXXqVPfvY8aMUU5OjgYMGKB//etfuuKKKyysDL3BxRdf7P79xBNP1JgxYzR48GCtW7dOEydOtLCy7mH+/PnasmUL/eSO0dHO21VXXeX+/cQTT1RaWpomTpyoHTt2aPDgwf4us9sYNmyYCgsLVVNTo2eeeUZz587VW2+95dXP4HbVcUpKSlJQUNBhvb7LysqUmppqUVU9T1xcnE444QRt377d6lJ6jI7ri2vv+A0aNEhJSUlcf5Kuu+46rV69WmvXrlW/fv3c+1NTU9XS0qIDBw50Op7rrd3RztuR5OTkSFKvv95CQ0M1ZMgQjR07VkuXLlVWVpb+/Oc/e/VaI+Qcp9DQUI0dO1b5+fnufS6XS/n5+crNzbWwsp6lvr5eO3bsUFpamtWl9BgDBw5Uampqp2uvtrZWH374IdfeMdq3b5+qqqp69fVnjNF1112n559/Xm+++aYGDhzY6fmxY8cqJCSk0/W2bds27d27t1dfbz903o6ksLBQknr19XYkLpdLzc3N3r3WvNs3undauXKlcTgc5rHHHjOff/65ueqqq0xcXJwpLS21urRu65e//KVZt26d2bVrl3nvvfdMXl6eSUpKMuXl5VaX1q3U1dWZTZs2mU2bNhlJ5p577jGbNm0ye/bsMcYYc+edd5q4uDjz4osvmk8++cTMmDHDDBw40Bw8eNDiyq31feetrq7O3HTTTaagoMDs2rXLvPHGG+akk04yQ4cONU1NTVaXbplrrrnGxMbGmnXr1pmSkhL31tjY6D7m6quvNv379zdvvvmm+fjjj01ubq7Jzc21sGrr/dB52759u7n99tvNxx9/bHbt2mVefPFFM2jQIHPGGWdYXLm1br75ZvPWW2+ZXbt2mU8++cTcfPPNxmazmddee80Y471rjZDjJffdd5/p37+/CQ0NNePHjzcffPCB1SV1a3PmzDFpaWkmNDTU9O3b18yZM8ds377d6rK6nbVr1xpJh21z5841xrQPI//tb39rUlJSjMPhMBMnTjTbtm2ztuhu4PvOW2Njo5k0aZJJTk42ISEhZsCAAebKK6/s9f+n5EjnS5J59NFH3cccPHjQXHvttSY+Pt5ERESY888/35SUlFhXdDfwQ+dt79695owzzjAJCQnG4XCYIUOGmF/96lempqbG2sIt9t///d9mwIABJjQ01CQnJ5uJEye6A44x3rvWbMYY08WWJQAAgG6LPjkAACAgEXIAAEBAIuQAAICARMgBAAABiZADAAACEiEHAAAEJEIOAAAISIQcAL2SzWbTCy+8YHUZAHyIkAPA7372s5/JZrMdtk2ZMsXq0gAEkGCrCwDQO02ZMkWPPvpop30Oh8OiagAEIlpyAFjC4XAoNTW10xYfHy+p/VbSihUrNHXqVIWHh2vQoEF65plnOr3+008/1Y9//GOFh4crMTFRV111lerr6zsd88gjj2jUqFFyOBxKS0vTdddd1+n5yspKnX/++YqIiNDQoUP10ksvuZ/7+uuvddlllyk5OVnh4eEaOnToYaEMQPdGyAHQLf32t7/VrFmztHnzZl122WW6+OKLtXXrVklSQ0ODJk+erPj4eH300UdatWqV3njjjU4hZsWKFZo/f76uuuoqffrpp3rppZc0ZMiQTp9x22236aKLLtInn3yic889V5dddpmqq6vdn//555/rlVde0datW7VixQolJSX57wQAOH7eW1MUADwzd+5cExQUZCIjIzttd9xxhzGmfWXnq6++utNrcnJyzDXXXGOMMeahhx4y8fHxpr6+3v38f/7zH2O3292riaenp5tbbrnlqDVIMv/zP//jflxfX28kmVdeecUYY8z06dPNvHnzvPOFAViCPjkALHH22WdrxYoVnfYlJCS4f8/Nze30XG5urgoLCyVJW7duVVZWliIjI93Pn3baaXK5XNq2bZtsNpuKi4s1ceLE761hzJgx7t8jIyMVExOj8vJySdI111yjWbNmaePGjZo0aZJmzpypU089tUvfFYA1CDkALBEZGXnY7SNvCQ8P9+i4kJCQTo9tNptcLpckaerUqdqzZ49efvllvf7665o4caLmz5+vu+++2+v1AvAN+uQA6JY++OCDwx6PGDFCkjRixAht3rxZDQ0N7uffe+892e12DRs2TNHR0crMzFR+fv5x1ZCcnKy5c+fqn//8p5YtW6aHHnrouN4PgH/RkgPAEs3NzSotLe20Lzg42N25d9WqVRo3bpxOP/10PfHEE1q/fr3+9re/SZIuu+wyLVmyRHPnztWtt96qiooKXX/99frpT3+qlJQUSdKtt96qq6++Wn369NHUqVNVV1en9957T9dff71H9S1evFhjx47VqFGj1NzcrNWrV7tDFoCegZADwBJr1qxRWlpap33Dhg3TF198Ial95NPKlSt17bXXKi0tTU899ZRGjhwpSYqIiNCrr76qG2+8USeffLIiIiI0a9Ys3XPPPe73mjt3rpqamvSnP/1JN910k5KSkjR79myP6wsNDdWiRYu0e/duhYeHa8KECVq5cqUXvjkAf7EZY4zVRQDAt9lsNj3//POaOXOm1aUA6MHokwMAAAISIQcAAAQk+uQA6Ha4iw7AG2jJAQAAAYmQAwAAAhIhBwAABCRCDgAACEiEHAAAEJAIOQAAICARcgAAQEAi5AAAgIBEyAEAAAHp/wPcA0mX0rR8DgAAAABJRU5ErkJggg==\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def plot_history(hist):\n",
+ " plt.plot(hist)\n",
+ " plt.title(\"Model Loss\")\n",
+ " plt.xlabel(\"Epochs\")\n",
+ " plt.ylabel(\"Loss\")\n",
+ " plt.show()\n",
+ " \n",
+ "plot_history(hist);"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "7f30a5bf-caca-44fc-8d5a-8ed8863600e6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training accuracy: 0.9894897959183674\n",
+ "Test accuracy: 0.9495238095238095\n"
+ ]
+ }
+ ],
+ "source": [
+ "train_predictions = np.argmax(NN.predict(train_data), axis = 1)\n",
+ "print(\"Training accuracy: \", accuracy_score(train[\"label\"].to_numpy(), train_predictions))\n",
+ "test_predictions = np.argmax(NN.predict(test_data), axis = 1)\n",
+ "print(\"Test accuracy: \", accuracy_score(test[\"label\"].to_numpy(), test_predictions))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "5d3ea10a-9a1a-4f7b-8dad-3a112b3e5add",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def predict_image(index):\n",
+ " current_image = test_data[index, :]\n",
+ " prediction = np.argmax(NN.predict(current_image), axis = 1)\n",
+ " label = test[\"label\"].to_numpy()[index]\n",
+ " print(\"Prediction: \", prediction)\n",
+ " print(\"Label: \", label)\n",
+ " \n",
+ " current_image = current_image.reshape((28, 28)) * 255\n",
+ " plt.gray()\n",
+ " plt.imshow(current_image, interpolation = \"nearest\")\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "e143b0a5-0cf2-43b7-894c-8497e17b4461",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [5]\n",
+ "Label: 5\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "7b1dd60a-05eb-4c3a-9209-ba598be45bb9",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [4]\n",
+ "Label: 4\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "5d69171e-e864-44a6-9e51-183002a47c90",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Prediction: [2]\n",
+ "Label: 2\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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JgggAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmPA555x1E58VDocVCASs2wAuaVlZWZ5r2traPNf897//9Vxz0003ea45efKk5xpcvFAodN5jiTMgAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgAkCCABgggACAJgggAAAJoZaNwAg+YTDYc81sTzw89ChQwOyHyQnzoAAACYIIACACc8BtGPHDs2dO1cFBQXy+XzatGlT1HrnnJ566inl5+dr2LBhKikp0b59++LVLwAgRXgOoJ6eHk2ZMkWrV6/ud/2qVav0/PPP64UXXtDOnTt1+eWXq6ysTCdOnLjoZgEAqcPzTQgVFRWqqKjod51zTs8995x+9KMf6c4775QkvfTSS8rLy9OmTZu0YMGCi+sWAJAy4noNqK2tTV1dXSopKYksCwQCKioqUlNTU781vb29CofDUQMAkPriGkBdXV2SpLy8vKjleXl5kXWfV1NTo0AgEBmjR4+OZ0sAgCRlfhdcdXW1QqFQZHR0dFi3BAAYAHENoGAwKEnq7u6OWt7d3R1Z93l+v19ZWVlRAwCQ+uIaQIWFhQoGg6qvr48sC4fD2rlzp4qLi+O5KwDAIOf5Lrhjx46ptbU18rqtrU179uxRdna2xowZo2XLlumnP/2prrvuOhUWFurJJ59UQUGB5s2bF8++AQCDnOcA2rVrl26//fbI66qqKknSokWLVFtbqxUrVqinp0cPP/ywjhw5oltuuUV1dXW67LLL4tc1AGDQ8znnnHUTnxUOhxUIBKzbAC5psdyN+v7773uu2blzp+eaz/6aB5JbKBQ673V987vgAACXJgIIAGCCAAIAmCCAAAAmCCAAgAkCCABgggACAJgggAAAJgggAIAJAggAYIIAAgCYIIAAACYIIACACc9/jgFA6isvL/dcc8UVV3iu+eCDDzzXIHVwBgQAMEEAAQBMEEAAABMEEADABAEEADBBAAEATBBAAAATBBAAwAQBBAAwQQABAEwQQAAAEwQQAMAEDyMFcJbi4uIB2c/7778/IPtBcuIMCABgggACAJgggAAAJgggAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgAkeRoqkl5eX57kmPT09pn0dOHAgprpkdfnll8dUN2fOHM81x44d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gAAAJgggAIAJAggAYCL1Hk2MAfPJJ58MSA0G3qlTpzzXLF682HNNQ0OD55orr7zScw3HXXLiDAgAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgAkCCABgggACAJgggAAAJgggAIAJn3POWTfxWeFwWIFAwLoNAMBFCoVCysrKOud6zoAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgAkCCABgggACAJgggAAAJgggAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgAkCCABgggACAJgggAAAJgggAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmPAUQDU1NZo+fboyMzOVm5urefPmqaWlJWqbWbNmyefzRY0lS5bEtWkAwODnKYAaGxtVWVmp5uZmbdu2TadOnVJpaal6enqitlu8eLE6OzsjY9WqVXFtGgAw+A31snFdXV3U69raWuXm5mr37t2aOXNmZPnw4cMVDAbj0yEAICVd1DWgUCgkScrOzo5a/vLLLysnJ0eTJk1SdXW1jh8/fs7v0dvbq3A4HDUAAJcAF6PTp0+7OXPmuJtvvjlq+e9+9ztXV1fn9u7d6/74xz+6q6++2t11113n/D4rV650khgMBoORYiMUCp03R2IOoCVLlrixY8e6jo6O825XX1/vJLnW1tZ+1584ccKFQqHI6OjoMJ80BoPBYFz8uFAAeboG9KmlS5dq69at2rFjh0aNGnXebYuKiiRJra2tGj9+/Fnr/X6//H5/LG0AAAYxTwHknNOjjz6qjRs3qqGhQYWFhRes2bNnjyQpPz8/pgYBAKnJUwBVVlZq3bp12rx5szIzM9XV1SVJCgQCGjZsmPbv369169bpjjvu0MiRI7V3714tX75cM2fO1OTJkxPyAwAABikv1310js/51q5d65xzrr293c2cOdNlZ2c7v9/vrr32Wvf4449f8HPAzwqFQuafWzIYDAbj4seF3vt9/z9YkkY4HFYgELBuAwBwkUKhkLKyss65nmfBAQBMEEAAABMEEADABAEEADBBAAEATBBAAAATBBAAwAQBBAAwQQABAEwQQAAAEwQQAMAEAQQAMEEAAQBMEEAAABMEEADABAEEADBBAAEATBBAAAATBBAAwAQBBAAwQQABAEwQQAAAEwQQAMAEAQQAMEEAAQBMJF0AOeesWwAAxMGF3s+TLoCOHj1q3QIAIA4u9H7uc0l2ytHX16eDBw8qMzNTPp8val04HNbo0aPV0dGhrKwsow7tMQ9nMA9nMA9nMA9nJMM8OOd09OhRFRQUKC3t3Oc5Qwewpy8kLS1No0aNOu82WVlZl/QB9inm4Qzm4Qzm4Qzm4QzreQgEAhfcJuk+ggMAXBoIIACAiUEVQH6/XytXrpTf77duxRTzcAbzcAbzcAbzcMZgmoekuwkBAHBpGFRnQACA1EEAAQBMEEAAABMEEADAxKAJoNWrV+uaa67RZZddpqKiIr399tvWLQ24p59+Wj6fL2pMnDjRuq2E27Fjh+bOnauCggL5fD5t2rQpar1zTk899ZTy8/M1bNgwlZSUaN++fTbNJtCF5uGBBx446/goLy+3aTZBampqNH36dGVmZio3N1fz5s1TS0tL1DYnTpxQZWWlRo4cqSuuuELz589Xd3e3UceJ8UXmYdasWWcdD0uWLDHquH+DIoBeffVVVVVVaeXKlXrnnXc0ZcoUlZWV6dChQ9atDbgbbrhBnZ2dkfG3v/3NuqWE6+np0ZQpU7R69ep+169atUrPP/+8XnjhBe3cuVOXX365ysrKdOLEiQHuNLEuNA+SVF5eHnV8rF+/fgA7TLzGxkZVVlaqublZ27Zt06lTp1RaWqqenp7INsuXL9eWLVu0YcMGNTY26uDBg7r77rsNu46/LzIPkrR48eKo42HVqlVGHZ+DGwRmzJjhKisrI69Pnz7tCgoKXE1NjWFXA2/lypVuypQp1m2YkuQ2btwYed3X1+eCwaD7xS9+EVl25MgR5/f73fr16w06HBifnwfnnFu0aJG78847TfqxcujQISfJNTY2OufO/Nunp6e7DRs2RLb54IMPnCTX1NRk1WbCfX4enHPutttuc9/73vfsmvoCkv4M6OTJk9q9e7dKSkoiy9LS0lRSUqKmpibDzmzs27dPBQUFGjdunBYuXKj29nbrlky1tbWpq6sr6vgIBAIqKiq6JI+PhoYG5ebmasKECXrkkUd0+PBh65YSKhQKSZKys7MlSbt379apU6eijoeJEydqzJgxKX08fH4ePvXyyy8rJydHkyZNUnV1tY4fP27R3jkl3cNIP++jjz7S6dOnlZeXF7U8Ly9P//znP426slFUVKTa2lpNmDBBnZ2deuaZZ3TrrbfqvffeU2ZmpnV7Jrq6uiSp3+Pj03WXivLyct19990qLCzU/v379cMf/lAVFRVqamrSkCFDrNuLu76+Pi1btkw333yzJk2aJOnM8ZCRkaERI0ZEbZvKx0N/8yBJ9913n8aOHauCggLt3btXP/jBD9TS0qI///nPht1GS/oAwv+pqKiIfD158mQVFRVp7Nixeu211/TQQw8ZdoZksGDBgsjXN954oyZPnqzx48eroaFBs2fPNuwsMSorK/Xee+9dEtdBz+dc8/Dwww9Hvr7xxhuVn5+v2bNna//+/Ro/fvxAt9mvpP8ILicnR0OGDDnrLpbu7m4Fg0GjrpLDiBEjdP3116u1tdW6FTOfHgMcH2cbN26ccnJyUvL4WLp0qbZu3ao33ngj6s+3BINBnTx5UkeOHInaPlWPh3PNQ3+KiookKamOh6QPoIyMDE2bNk319fWRZX19faqvr1dxcbFhZ/aOHTum/fv3Kz8/37oVM4WFhQoGg1HHRzgc1s6dOy/54+PAgQM6fPhwSh0fzjktXbpUGzdu1Ouvv67CwsKo9dOmTVN6enrU8dDS0qL29vaUOh4uNA/92bNnjyQl1/FgfRfEF/HKK684v9/vamtr3fvvv+8efvhhN2LECNfV1WXd2oD6/ve/7xoaGlxbW5t78803XUlJicvJyXGHDh2ybi2hjh496t5991337rvvOknul7/8pXv33Xfdf/7zH+ecc88++6wbMWKE27x5s9u7d6+78847XWFhofv444+NO4+v883D0aNH3WOPPeaamppcW1ub2759u5s6daq77rrr3IkTJ6xbj5tHHnnEBQIB19DQ4Do7OyPj+PHjkW2WLFnixowZ415//XW3a9cuV1xc7IqLiw27jr8LzUNra6v78Y9/7Hbt2uXa2trc5s2b3bhx49zMmTONO482KALIOed+85vfuDFjxriMjAw3Y8YM19zcbN3SgLvnnntcfn6+y8jIcFdffbW75557XGtrq3VbCffGG284SWeNRYsWOefO3Ir95JNPury8POf3+93s2bNdS0uLbdMJcL55OH78uCstLXVXXXWVS09Pd2PHjnWLFy9Ouf+k9ffzS3Jr166NbPPxxx+77373u+7KK690w4cPd3fddZfr7Oy0azoBLjQP7e3tbubMmS47O9v5/X537bXXuscff9yFQiHbxj+HP8cAADCR9NeAAACpiQACAJgggAAAJgggAIAJAggAYIIAAgCYIIAAACYIIACACQIIAGCCAAIAmCCAAAAmCCAAgIn/B1eq7YuwiWtRAAAAAElFTkSuQmCC\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "predict_image(3)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "747cd3d0-29d2-444b-83dc-1c4b57687704",
+ "metadata": {},
+ "source": [
+ "### **Binary-Class Classification**\n",
+ "\n",
+ "### **Dataset: [Breast Cancer Wisconsin (Diagnostic) Data Set](https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29)**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "7711d2b6-5aab-471c-aa45-06e0c5ddcb2c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data = pd.read_csv(\"binaryclass_train.csv\", header = None)\n",
+ "data[\"label\"] = data[1].apply(lambda x: 1 if x == \"M\" else 0)\n",
+ "train, test = train_test_split(data, test_size = 0.3)\n",
+ "train_data = train.loc[:, ~train.columns.isin([0, 1, \"label\"])].to_numpy()\n",
+ "train_target = train[\"label\"].to_numpy()\n",
+ "test_data = test.loc[:, ~test.columns.isin([0, 1, \"label\"])].to_numpy()\n",
+ "test_target = test[\"label\"].to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "id": "eaa8f0cc-78f0-4984-b701-437195559a4a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " 0 | \n",
+ " 1 | \n",
+ " 2 | \n",
+ " 3 | \n",
+ " 4 | \n",
+ " 5 | \n",
+ " 6 | \n",
+ " 7 | \n",
+ " 8 | \n",
+ " 9 | \n",
+ " ... | \n",
+ " 23 | \n",
+ " 24 | \n",
+ " 25 | \n",
+ " 26 | \n",
+ " 27 | \n",
+ " 28 | \n",
+ " 29 | \n",
+ " 30 | \n",
+ " 31 | \n",
+ " label | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 361 | \n",
+ " 901041 | \n",
+ " B | \n",
+ " 13.30 | \n",
+ " 21.57 | \n",
+ " 85.24 | \n",
+ " 546.1 | \n",
+ " 0.08582 | \n",
+ " 0.06373 | \n",
+ " 0.03344 | \n",
+ " 0.02424 | \n",
+ " ... | \n",
+ " 29.20 | \n",
+ " 92.94 | \n",
+ " 621.2 | \n",
+ " 0.1140 | \n",
+ " 0.16670 | \n",
+ " 0.12120 | \n",
+ " 0.05614 | \n",
+ " 0.2637 | \n",
+ " 0.06658 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 186 | \n",
+ " 874217 | \n",
+ " M | \n",
+ " 18.31 | \n",
+ " 18.58 | \n",
+ " 118.60 | \n",
+ " 1041.0 | \n",
+ " 0.08588 | \n",
+ " 0.08468 | \n",
+ " 0.08169 | \n",
+ " 0.05814 | \n",
+ " ... | \n",
+ " 26.36 | \n",
+ " 139.20 | \n",
+ " 1410.0 | \n",
+ " 0.1234 | \n",
+ " 0.24450 | \n",
+ " 0.35380 | \n",
+ " 0.15710 | \n",
+ " 0.3206 | \n",
+ " 0.06938 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 199 | \n",
+ " 877500 | \n",
+ " M | \n",
+ " 14.45 | \n",
+ " 20.22 | \n",
+ " 94.49 | \n",
+ " 642.7 | \n",
+ " 0.09872 | \n",
+ " 0.12060 | \n",
+ " 0.11800 | \n",
+ " 0.05980 | \n",
+ " ... | \n",
+ " 30.12 | \n",
+ " 117.90 | \n",
+ " 1044.0 | \n",
+ " 0.1552 | \n",
+ " 0.40560 | \n",
+ " 0.49670 | \n",
+ " 0.18380 | \n",
+ " 0.4753 | \n",
+ " 0.10130 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 389 | \n",
+ " 90312 | \n",
+ " M | \n",
+ " 19.55 | \n",
+ " 23.21 | \n",
+ " 128.90 | \n",
+ " 1174.0 | \n",
+ " 0.10100 | \n",
+ " 0.13180 | \n",
+ " 0.18560 | \n",
+ " 0.10210 | \n",
+ " ... | \n",
+ " 30.44 | \n",
+ " 142.00 | \n",
+ " 1313.0 | \n",
+ " 0.1251 | \n",
+ " 0.24140 | \n",
+ " 0.38290 | \n",
+ " 0.18250 | \n",
+ " 0.2576 | \n",
+ " 0.07602 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 388 | \n",
+ " 903011 | \n",
+ " B | \n",
+ " 11.27 | \n",
+ " 15.50 | \n",
+ " 73.38 | \n",
+ " 392.0 | \n",
+ " 0.08365 | \n",
+ " 0.11140 | \n",
+ " 0.10070 | \n",
+ " 0.02757 | \n",
+ " ... | \n",
+ " 18.93 | \n",
+ " 79.73 | \n",
+ " 450.0 | \n",
+ " 0.1102 | \n",
+ " 0.28090 | \n",
+ " 0.30210 | \n",
+ " 0.08272 | \n",
+ " 0.2157 | \n",
+ " 0.10430 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 430 | \n",
+ " 907914 | \n",
+ " M | \n",
+ " 14.90 | \n",
+ " 22.53 | \n",
+ " 102.10 | \n",
+ " 685.0 | \n",
+ " 0.09947 | \n",
+ " 0.22250 | \n",
+ " 0.27330 | \n",
+ " 0.09711 | \n",
+ " ... | \n",
+ " 27.57 | \n",
+ " 125.40 | \n",
+ " 832.7 | \n",
+ " 0.1419 | \n",
+ " 0.70900 | \n",
+ " 0.90190 | \n",
+ " 0.24750 | \n",
+ " 0.2866 | \n",
+ " 0.11550 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 371 | \n",
+ " 9012568 | \n",
+ " B | \n",
+ " 15.19 | \n",
+ " 13.21 | \n",
+ " 97.65 | \n",
+ " 711.8 | \n",
+ " 0.07963 | \n",
+ " 0.06934 | \n",
+ " 0.03393 | \n",
+ " 0.02657 | \n",
+ " ... | \n",
+ " 15.73 | \n",
+ " 104.50 | \n",
+ " 819.1 | \n",
+ " 0.1126 | \n",
+ " 0.17370 | \n",
+ " 0.13620 | \n",
+ " 0.08178 | \n",
+ " 0.2487 | \n",
+ " 0.06766 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 465 | \n",
+ " 9113239 | \n",
+ " B | \n",
+ " 13.24 | \n",
+ " 20.13 | \n",
+ " 86.87 | \n",
+ " 542.9 | \n",
+ " 0.08284 | \n",
+ " 0.12230 | \n",
+ " 0.10100 | \n",
+ " 0.02833 | \n",
+ " ... | \n",
+ " 25.50 | \n",
+ " 115.00 | \n",
+ " 733.5 | \n",
+ " 0.1201 | \n",
+ " 0.56460 | \n",
+ " 0.65560 | \n",
+ " 0.13570 | \n",
+ " 0.2845 | \n",
+ " 0.12490 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 60 | \n",
+ " 858970 | \n",
+ " B | \n",
+ " 10.17 | \n",
+ " 14.88 | \n",
+ " 64.55 | \n",
+ " 311.9 | \n",
+ " 0.11340 | \n",
+ " 0.08061 | \n",
+ " 0.01084 | \n",
+ " 0.01290 | \n",
+ " ... | \n",
+ " 17.45 | \n",
+ " 69.86 | \n",
+ " 368.6 | \n",
+ " 0.1275 | \n",
+ " 0.09866 | \n",
+ " 0.02168 | \n",
+ " 0.02579 | \n",
+ " 0.3557 | \n",
+ " 0.08020 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 426 | \n",
+ " 907409 | \n",
+ " B | \n",
+ " 10.48 | \n",
+ " 14.98 | \n",
+ " 67.49 | \n",
+ " 333.6 | \n",
+ " 0.09816 | \n",
+ " 0.10130 | \n",
+ " 0.06335 | \n",
+ " 0.02218 | \n",
+ " ... | \n",
+ " 21.57 | \n",
+ " 81.41 | \n",
+ " 440.4 | \n",
+ " 0.1327 | \n",
+ " 0.29960 | \n",
+ " 0.29390 | \n",
+ " 0.09310 | \n",
+ " 0.3020 | \n",
+ " 0.09646 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
398 rows × 33 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " 0 1 2 3 4 5 6 7 8 \\\n",
+ "361 901041 B 13.30 21.57 85.24 546.1 0.08582 0.06373 0.03344 \n",
+ "186 874217 M 18.31 18.58 118.60 1041.0 0.08588 0.08468 0.08169 \n",
+ "199 877500 M 14.45 20.22 94.49 642.7 0.09872 0.12060 0.11800 \n",
+ "389 90312 M 19.55 23.21 128.90 1174.0 0.10100 0.13180 0.18560 \n",
+ "388 903011 B 11.27 15.50 73.38 392.0 0.08365 0.11140 0.10070 \n",
+ ".. ... .. ... ... ... ... ... ... ... \n",
+ "430 907914 M 14.90 22.53 102.10 685.0 0.09947 0.22250 0.27330 \n",
+ "371 9012568 B 15.19 13.21 97.65 711.8 0.07963 0.06934 0.03393 \n",
+ "465 9113239 B 13.24 20.13 86.87 542.9 0.08284 0.12230 0.10100 \n",
+ "60 858970 B 10.17 14.88 64.55 311.9 0.11340 0.08061 0.01084 \n",
+ "426 907409 B 10.48 14.98 67.49 333.6 0.09816 0.10130 0.06335 \n",
+ "\n",
+ " 9 ... 23 24 25 26 27 28 29 \\\n",
+ "361 0.02424 ... 29.20 92.94 621.2 0.1140 0.16670 0.12120 0.05614 \n",
+ "186 0.05814 ... 26.36 139.20 1410.0 0.1234 0.24450 0.35380 0.15710 \n",
+ "199 0.05980 ... 30.12 117.90 1044.0 0.1552 0.40560 0.49670 0.18380 \n",
+ "389 0.10210 ... 30.44 142.00 1313.0 0.1251 0.24140 0.38290 0.18250 \n",
+ "388 0.02757 ... 18.93 79.73 450.0 0.1102 0.28090 0.30210 0.08272 \n",
+ ".. ... ... ... ... ... ... ... ... ... \n",
+ "430 0.09711 ... 27.57 125.40 832.7 0.1419 0.70900 0.90190 0.24750 \n",
+ "371 0.02657 ... 15.73 104.50 819.1 0.1126 0.17370 0.13620 0.08178 \n",
+ "465 0.02833 ... 25.50 115.00 733.5 0.1201 0.56460 0.65560 0.13570 \n",
+ "60 0.01290 ... 17.45 69.86 368.6 0.1275 0.09866 0.02168 0.02579 \n",
+ "426 0.02218 ... 21.57 81.41 440.4 0.1327 0.29960 0.29390 0.09310 \n",
+ "\n",
+ " 30 31 label \n",
+ "361 0.2637 0.06658 0 \n",
+ "186 0.3206 0.06938 1 \n",
+ "199 0.4753 0.10130 1 \n",
+ "389 0.2576 0.07602 1 \n",
+ "388 0.2157 0.10430 0 \n",
+ ".. ... ... ... \n",
+ "430 0.2866 0.11550 1 \n",
+ "371 0.2487 0.06766 0 \n",
+ "465 0.2845 0.12490 0 \n",
+ "60 0.3557 0.08020 0 \n",
+ "426 0.3020 0.09646 0 \n",
+ "\n",
+ "[398 rows x 33 columns]"
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "751772e4-e90b-4c11-ae63-cdb9602529e7",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "---- Model Summary ----\n",
+ "Layer 1: relu\n",
+ "W: (16, 30) b: (16, 1)\n",
+ "Trainable parameters: 496\n",
+ "Layer 2: relu\n",
+ "W: (16, 16) b: (16, 1)\n",
+ "Trainable parameters: 272\n",
+ "Layer 3: sigmoid\n",
+ "W: (1, 16) b: (1, 1)\n",
+ "Trainable parameters: 17\n"
+ ]
+ }
+ ],
+ "source": [
+ "NN = NeuralNetwork(input_size = train_data.shape[1])\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(16, \"relu\")\n",
+ "NN.add_layer(1, \"sigmoid\")\n",
+ "NN.compile(loss = \"binary crossentropy\")\n",
+ "NN.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "0f010d0a-ceef-4824-b36b-9752547248f1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training finished after epoch 1000 with a loss of 0.166389194481977.\n"
+ ]
+ }
+ ],
+ "source": [
+ "hist = NN.fit(train_data, train_target, epochs = 1000, batch_size = 32, learning_rate = 0.01, verbose = 0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "f4a324e2-2070-43d5-8cb6-8b07cbb69078",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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l0s9e/ash09xfigtLVVAgOePpcDAQLlcLklc63Ohrq5pr169tHDhQlVVVbnbzJ07V23atDmrYR9JLE8+3owZM4zT6TRvvPGGWb9+vbn77rtNbGysx6oInNg999xjYmJizBdffGH27dvnfpSWlrrbjBw50jRr1sx8/vnnZvny5aZXr16mV69e7vNHls3279/frF692nzyyScmISGBZbMnceyqH2O4znVh6dKlJigoyIwfP95s3rzZvPXWWyY8PNz8+9//dreZOHGiiY2NNR9++KH59ttvzQ033OB1eWeXLl3MkiVLzFdffWVat27doJfMejN8+HDTpEkT9/LkDz74wDRu3Nj89re/dbfhWp++oqIis2rVKrNq1Sojybzwwgtm1apVZufOncaYurmm+fn5Jikpyfz0pz8169atMzNmzDDh4eEsTz5X/vrXv5pmzZqZkJAQ06NHD7N48WLbJZ1XJHl9TJ061d2mrKzMjBo1ysTFxZnw8HBz0003mX379nm8zo4dO8yAAQNMWFiYady4sXnwwQdNVVVVPX+a88vxQYXrXDf+85//mI4dOxqn02natm1rpkyZ4nHe5XKZRx991CQlJRmn02n69u1rMjMzPdocPHjQ3HbbbSYyMtJER0ebESNGmKKiovr8GH6vsLDQ3HfffaZZs2YmNDTUXHDBBWbcuHEeS1651qdvwYIFXv9NHj58uDGm7q7pmjVrzGWXXWacTqdp0qSJmThxYp3U7zDmmC3/AAAA/AhzVAAAgN8iqAAAAL9FUAEAAH6LoAIAAPwWQQUAAPgtggoAAPBbBBUAAOC3CCoAznsOh0OzZs2yXQaAc4CgAuCs/PznP5fD4aj1uO6662yXBuAHIMh2AQDOf9ddd52mTp3qcczpdFqqBsAPCT0qAM6a0+lUcnKyx+PIHVMdDocmT56sAQMGKCwsTBdccIHee+89j+evXbtW//d//6ewsDDFx8fr7rvvVnFxsUeb119/XR06dJDT6VRKSoruvfdej/MHDhzQTTfdpPDwcLVu3VofffSR+9yhQ4c0bNgwJSQkKCwsTK1bt64VrAD4J4IKgHPu0Ucf1ZAhQ7RmzRoNGzZMt956qzZs2CBJKikp0bXXXqu4uDgtW7ZM7777rubNm+cRRCZPnqyMjAzdfffdWrt2rT766CO1atXK4z2efPJJ/fjHP9a3336rgQMHatiwYcrLy3O///r16zVnzhxt2LBBkydPVuPGjevvAgA4c3Vya0MADdbw4cNNYGCgiYiI8HiMHz/eGHP4btojR470eE7Pnj3NPffcY4wxZsqUKSYuLs4UFxe7z//3v/81AQEBJjs72xhjTGpqqhk3bpzPGiSZ3//+9+6vi4uLjSQzZ84cY4wxgwcPNiNGjKibDwygXjFHBcBZu/rqqzV58mSPY40aNXL/vlevXh7nevXqpdWrV0uSNmzYoM6dOysiIsJ9vk+fPnK5XMrMzJTD4dDevXvVt2/fE9bQqVMn9+8jIiIUHR2t3NxcSdI999yjIUOGaOXKlerfv79uvPFG9e7d+4w+K4D6RVABcNYiIiJqDcXUlbCwsFNqFxwc7PG1w+GQy+WSJA0YMEA7d+7Uxx9/rLlz56pv377KyMjQ888/X+f1AqhbzFEBcM4tXry41tft2rWTJLVr105r1qxRSUmJ+/zXX3+tgIAAtWnTRlFRUUpPT9f8+fPPqoaEhAQNHz5c//73vzVp0iRNmTLlrF4PQP2gRwXAWauoqFB2drbHsaCgIPeE1XfffVfdunXTZZddprfeektLly7VP/7xD0nSsGHD9Pjjj2v48OF64okntH//fv3617/WT3/6UyUlJUmSnnjiCY0cOVKJiYkaMGCAioqK9PXXX+vXv/71KdX32GOPqWvXrurQoYMqKio0e/Zsd1AC4N8IKgDO2ieffKKUlBSPY23atNHGjRslHV6RM2PGDI0aNUopKSmaPn262rdvL0kKDw/Xp59+qvvuu0/du3dXeHi4hgwZohdeeMH9WsOHD1d5ebn+/Oc/6ze/+Y0aN26soUOHnnJ9ISEhGjt2rHbs2KGwsDBdfvnlmjFjRh18cgDnmsMYY2wXAeCHy+FwaObMmbrxxhttlwLgPMQcFQAA4LcIKgAAwG8xRwXAOcXoMoCzQY8KAADwWwQVAADgtwgqAADAbxFUAACA3yKoAAAAv0VQAQAAfougAgAA/BZBBQAA+C2CCgAA8Fv/D/IzotoErG9XAAAAAElFTkSuQmCC\n",
+ "text/plain": [
+ "