{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Notebook for training and testing AI models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras import layers\n",
    "import pandas as pd\n",
    "from attention import CustomAttention\n",
    "import json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.8.0\n"
     ]
    }
   ],
   "source": [
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# CONSTANTS\n",
    "RAW_SLEEP_DATA_PATH = \".data/raw_bed_sleep-state.csv\"\n",
    "CLEANED_SLEEP_DATA_PATH = \".data/clean_bed_sleep-state.csv\"\n",
    "SLEEP_DATA_PATH = \".data/sleep_data_simple.csv\"\n",
    "UPDATED_SLEEP_DATA_PATH = \".data/updated_sleep_data.csv\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Parameters and Hyper-parameters\n",
    "SLEEP_STAGES = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cleaning Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "import csv\n",
    "import datetime\n",
    "import itertools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "datetime.datetime(2022, 4, 21, 10, 19, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200)))"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "datetime.datetime.strptime(\"2022-04-21T10:18:00+02:00\",\"%Y-%m-%dT%H:%M:%S%z\") + datetime.timedelta(minutes=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [],
   "source": [
    "def stage_probability(stage, to_test):\n",
    "    return 1.0 if stage == to_test else 0.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.833333333333333"
      ]
     },
     "execution_count": 150,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "((start_time - in_bed_time).seconds)/3600"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 201,
   "metadata": {},
   "outputs": [],
   "source": [
    "cleaned_info = []\n",
    "date_seen = set()\n",
    "previous_duration = 60\n",
    "with open(RAW_SLEEP_DATA_PATH, mode ='r') as raw_file:\n",
    "    csvFile = csv.reader(raw_file)\n",
    "    # max_count = 1\n",
    "    # stuff = set()\n",
    "    in_bed_time = None\n",
    "    current_sleep_id = -1\n",
    "    for index, lines in enumerate(csvFile):\n",
    "        if index == 0:\n",
    "            cleaned_info.append([\n",
    "                \"sleep_id\",\n",
    "                \"sleep_begin\",\n",
    "                \"stage_start\",\n",
    "                \"time_since_begin_sec\",\n",
    "                \"stage_duration_sec\",\n",
    "                \"stage_end\", \n",
    "                \"stage_value\",\n",
    "                \"awake_probability\",\n",
    "                \"light_probability\",\n",
    "                \"deep_probability\",\n",
    "                \"rem_probability\",\n",
    "            ])\n",
    "            continue\n",
    "        start_time = datetime.datetime.strptime(lines[0],\"%Y-%m-%dT%H:%M:%S%z\")\n",
    "        if start_time in date_seen:\n",
    "            continue\n",
    "        date_seen.add(start_time)\n",
    "        if not in_bed_time or in_bed_time > start_time:\n",
    "            current_sleep_id += 1\n",
    "            in_bed_time = start_time\n",
    "        # for duration, stage in enumerate(\n",
    "        # for offset, (duration, stage) in enumerate(\n",
    "        #     zip(\n",
    "        #         # itertools.accumulate(lines[1].strip(\"[]\").split(\",\"), lambda x,y: int(x)+int(y)//60, initial = 0), \n",
    "        #         map(int, lines[1].strip(\"[]\").split(\",\"))\n",
    "        #         map(int, lines[2].strip(\"[]\").split(\",\"))\n",
    "        #     )\n",
    "        #         # map(int, lines[2].strip(\"[]\").split(\",\"))\n",
    "        # ):\n",
    "        for offset, (duration, stage) in enumerate(zip(map(int, lines[1].strip(\"[]\").split(\",\")), map(int, lines[2].strip(\"[]\").split(\",\")))):\n",
    "            # print(f\"{(index, subindex) = }, {duration = }, {stage = }\")\n",
    "            # print(f\"{(index, duration) = } {stage = }\")\n",
    "            current_time = start_time + datetime.timedelta(seconds=offset*previous_duration)\n",
    "            cleaned_info.append([\n",
    "                current_sleep_id,\n",
    "                in_bed_time,\n",
    "                current_time, \n",
    "                (current_time - in_bed_time).seconds,\n",
    "                duration, \n",
    "                current_time + datetime.timedelta(seconds=duration), \n",
    "                stage,\n",
    "                stage_probability(0, stage),\n",
    "                stage_probability(1, stage),\n",
    "                stage_probability(2, stage),\n",
    "                stage_probability(3, stage),\n",
    "            ])\n",
    "            previous_duration = duration\n",
    "            # print(f\"{(index, subindex) = }, {val = }\")\n",
    "        # print(list())\n",
    "        # if index >= max_count:\n",
    "        #     break\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 202,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Finished Writing Cleaned Data\n"
     ]
    }
   ],
   "source": [
    "with open(CLEANED_SLEEP_DATA_PATH, 'w') as clean_file:\n",
    "    write = csv.writer(clean_file)\n",
    "    write.writerows(cleaned_info)\n",
    "print(\"Finished Writing Cleaned Data\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating DataFrame from clean raw data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get the cleaned data\n",
    "sleep_df_raw = pd.read_csv(CLEANED_SLEEP_DATA_PATH)#, parse_dates=[\"start\", \"end\"], infer_datetime_format=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Preprocess data: \n",
    "#   1. convert to datetime\n",
    "sleep_df_raw[\"sleep_begin\"] = pd.to_datetime(sleep_df_raw[\"sleep_begin\"], utc=True)\n",
    "sleep_df_raw[\"stage_start\"] = pd.to_datetime(sleep_df_raw[\"stage_start\"], utc=True)\n",
    "sleep_df_raw[\"stage_end\"] = pd.to_datetime(sleep_df_raw[\"stage_end\"], utc=True)\n",
    "#   2. Separate time, hour and minute\n",
    "#   MAYBE 3. smaller units: int16 or int8 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_minute(row, index):\n",
    "    return row[index].time().minute\n",
    "\n",
    "def get_hour(row, index):\n",
    "    return row[index].time().hour"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [],
   "source": [
    "sleep_df_raw[\"stage_start_hour\"] = sleep_df_raw.apply (lambda row: get_hour(row, \"stage_start\"), axis=1)\n",
    "sleep_df_raw[\"stage_start_minute\"] = sleep_df_raw.apply (lambda row: get_minute(row, \"stage_start\"), axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 551042 entries, 0 to 551041\n",
      "Data columns (total 13 columns):\n",
      " #   Column                Non-Null Count   Dtype              \n",
      "---  ------                --------------   -----              \n",
      " 0   sleep_id              551042 non-null  int64              \n",
      " 1   sleep_begin           551042 non-null  datetime64[ns, UTC]\n",
      " 2   stage_start           551042 non-null  datetime64[ns, UTC]\n",
      " 3   time_since_begin_sec  551042 non-null  int64              \n",
      " 4   stage_duration_sec    551042 non-null  int64              \n",
      " 5   stage_end             551042 non-null  datetime64[ns, UTC]\n",
      " 6   stage_value           551042 non-null  int64              \n",
      " 7   awake_probability     551042 non-null  float64            \n",
      " 8   light_probability     551042 non-null  float64            \n",
      " 9   deep_probability      551042 non-null  float64            \n",
      " 10  rem_probability       551042 non-null  float64            \n",
      " 11  stage_start_hour      551042 non-null  int64              \n",
      " 12  stage_start_minute    551042 non-null  int64              \n",
      "dtypes: datetime64[ns, UTC](3), float64(4), int64(6)\n",
      "memory usage: 54.7 MB\n"
     ]
    }
   ],
   "source": [
    "sleep_df_raw.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>sleep_id</th>\n",
       "      <th>sleep_begin</th>\n",
       "      <th>stage_start</th>\n",
       "      <th>time_since_begin_sec</th>\n",
       "      <th>stage_duration_sec</th>\n",
       "      <th>stage_end</th>\n",
       "      <th>stage_value</th>\n",
       "      <th>awake_probability</th>\n",
       "      <th>light_probability</th>\n",
       "      <th>deep_probability</th>\n",
       "      <th>rem_probability</th>\n",
       "      <th>stage_start_hour</th>\n",
       "      <th>stage_start_minute</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>60</td>\n",
       "      <td>2022-04-21 08:19:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8</td>\n",
       "      <td>18</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>2022-04-21 08:19:00+00:00</td>\n",
       "      <td>60</td>\n",
       "      <td>60</td>\n",
       "      <td>2022-04-21 08:20:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8</td>\n",
       "      <td>19</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>2022-04-21 08:20:00+00:00</td>\n",
       "      <td>120</td>\n",
       "      <td>60</td>\n",
       "      <td>2022-04-21 08:21:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8</td>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>2022-04-21 08:21:00+00:00</td>\n",
       "      <td>180</td>\n",
       "      <td>60</td>\n",
       "      <td>2022-04-21 08:22:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8</td>\n",
       "      <td>21</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>2022-04-21 08:18:00+00:00</td>\n",
       "      <td>2022-04-21 08:22:00+00:00</td>\n",
       "      <td>240</td>\n",
       "      <td>60</td>\n",
       "      <td>2022-04-21 08:23:00+00:00</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8</td>\n",
       "      <td>22</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551037</th>\n",
       "      <td>1132</td>\n",
       "      <td>2019-02-11 06:11:00+00:00</td>\n",
       "      <td>2019-02-11 13:17:00+00:00</td>\n",
       "      <td>25560</td>\n",
       "      <td>60</td>\n",
       "      <td>2019-02-11 13:18:00+00:00</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>13</td>\n",
       "      <td>17</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551038</th>\n",
       "      <td>1132</td>\n",
       "      <td>2019-02-11 06:11:00+00:00</td>\n",
       "      <td>2019-02-11 13:18:00+00:00</td>\n",
       "      <td>25620</td>\n",
       "      <td>60</td>\n",
       "      <td>2019-02-11 13:19:00+00:00</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>13</td>\n",
       "      <td>18</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551039</th>\n",
       "      <td>1132</td>\n",
       "      <td>2019-02-11 06:11:00+00:00</td>\n",
       "      <td>2019-02-11 13:19:00+00:00</td>\n",
       "      <td>25680</td>\n",
       "      <td>60</td>\n",
       "      <td>2019-02-11 13:20:00+00:00</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>13</td>\n",
       "      <td>19</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551040</th>\n",
       "      <td>1132</td>\n",
       "      <td>2019-02-11 06:11:00+00:00</td>\n",
       "      <td>2019-02-11 13:20:00+00:00</td>\n",
       "      <td>25740</td>\n",
       "      <td>60</td>\n",
       "      <td>2019-02-11 13:21:00+00:00</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>13</td>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551041</th>\n",
       "      <td>1132</td>\n",
       "      <td>2019-02-11 06:11:00+00:00</td>\n",
       "      <td>2019-02-11 13:21:00+00:00</td>\n",
       "      <td>25800</td>\n",
       "      <td>60</td>\n",
       "      <td>2019-02-11 13:22:00+00:00</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>13</td>\n",
       "      <td>21</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>551042 rows × 13 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "        sleep_id               sleep_begin               stage_start  \\\n",
       "0              0 2022-04-21 08:18:00+00:00 2022-04-21 08:18:00+00:00   \n",
       "1              0 2022-04-21 08:18:00+00:00 2022-04-21 08:19:00+00:00   \n",
       "2              0 2022-04-21 08:18:00+00:00 2022-04-21 08:20:00+00:00   \n",
       "3              0 2022-04-21 08:18:00+00:00 2022-04-21 08:21:00+00:00   \n",
       "4              0 2022-04-21 08:18:00+00:00 2022-04-21 08:22:00+00:00   \n",
       "...          ...                       ...                       ...   \n",
       "551037      1132 2019-02-11 06:11:00+00:00 2019-02-11 13:17:00+00:00   \n",
       "551038      1132 2019-02-11 06:11:00+00:00 2019-02-11 13:18:00+00:00   \n",
       "551039      1132 2019-02-11 06:11:00+00:00 2019-02-11 13:19:00+00:00   \n",
       "551040      1132 2019-02-11 06:11:00+00:00 2019-02-11 13:20:00+00:00   \n",
       "551041      1132 2019-02-11 06:11:00+00:00 2019-02-11 13:21:00+00:00   \n",
       "\n",
       "        time_since_begin_sec  stage_duration_sec                 stage_end  \\\n",
       "0                          0                  60 2022-04-21 08:19:00+00:00   \n",
       "1                         60                  60 2022-04-21 08:20:00+00:00   \n",
       "2                        120                  60 2022-04-21 08:21:00+00:00   \n",
       "3                        180                  60 2022-04-21 08:22:00+00:00   \n",
       "4                        240                  60 2022-04-21 08:23:00+00:00   \n",
       "...                      ...                 ...                       ...   \n",
       "551037                 25560                  60 2019-02-11 13:18:00+00:00   \n",
       "551038                 25620                  60 2019-02-11 13:19:00+00:00   \n",
       "551039                 25680                  60 2019-02-11 13:20:00+00:00   \n",
       "551040                 25740                  60 2019-02-11 13:21:00+00:00   \n",
       "551041                 25800                  60 2019-02-11 13:22:00+00:00   \n",
       "\n",
       "        stage_value  awake_probability  light_probability  deep_probability  \\\n",
       "0                 0                1.0                0.0               0.0   \n",
       "1                 0                1.0                0.0               0.0   \n",
       "2                 0                1.0                0.0               0.0   \n",
       "3                 0                1.0                0.0               0.0   \n",
       "4                 0                1.0                0.0               0.0   \n",
       "...             ...                ...                ...               ...   \n",
       "551037            1                0.0                1.0               0.0   \n",
       "551038            1                0.0                1.0               0.0   \n",
       "551039            1                0.0                1.0               0.0   \n",
       "551040            1                0.0                1.0               0.0   \n",
       "551041            1                0.0                1.0               0.0   \n",
       "\n",
       "        rem_probability  stage_start_hour  stage_start_minute  \n",
       "0                   0.0                 8                  18  \n",
       "1                   0.0                 8                  19  \n",
       "2                   0.0                 8                  20  \n",
       "3                   0.0                 8                  21  \n",
       "4                   0.0                 8                  22  \n",
       "...                 ...               ...                 ...  \n",
       "551037              0.0                13                  17  \n",
       "551038              0.0                13                  18  \n",
       "551039              0.0                13                  19  \n",
       "551040              0.0                13                  20  \n",
       "551041              0.0                13                  21  \n",
       "\n",
       "[551042 rows x 13 columns]"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sleep_df_raw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "sleep_data = sleep_df_raw[[\"sleep_id\", \"stage_start_hour\", \"stage_start_minute\", \"awake_probability\", \"rem_probability\",\"light_probability\", \"deep_probability\"]]\n",
    "sleep_data.insert(loc=1, column=\"minutes_since_begin\" , value= sleep_df_raw[\"time_since_begin_sec\"]//60)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   sleep_id  minutes_since_begin  stage_start_hour  stage_start_minute  \\\n",
      "0         0                    0                 8                  18   \n",
      "1         0                    1                 8                  19   \n",
      "2         0                    2                 8                  20   \n",
      "3         0                    3                 8                  21   \n",
      "4         0                    4                 8                  22   \n",
      "\n",
      "   awake_probability  rem_probability  light_probability  deep_probability  \n",
      "0                1.0              0.0                0.0               0.0  \n",
      "1                1.0              0.0                0.0               0.0  \n",
      "2                1.0              0.0                0.0               0.0  \n",
      "3                1.0              0.0                0.0               0.0  \n",
      "4                1.0              0.0                0.0               0.0  \n",
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 551042 entries, 0 to 551041\n",
      "Data columns (total 8 columns):\n",
      " #   Column               Non-Null Count   Dtype  \n",
      "---  ------               --------------   -----  \n",
      " 0   sleep_id             551042 non-null  int64  \n",
      " 1   minutes_since_begin  551042 non-null  int64  \n",
      " 2   stage_start_hour     551042 non-null  int64  \n",
      " 3   stage_start_minute   551042 non-null  int64  \n",
      " 4   awake_probability    551042 non-null  float64\n",
      " 5   rem_probability      551042 non-null  float64\n",
      " 6   light_probability    551042 non-null  float64\n",
      " 7   deep_probability     551042 non-null  float64\n",
      "dtypes: float64(4), int64(4)\n",
      "memory usage: 33.6 MB\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "print(sleep_data.head())\n",
    "print(sleep_data.info())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "sleep_data.to_csv(\".data/sleep_data_simple.csv\", index=False, index_label=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Development"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "TEST_SIZE = 365//2\n",
    "VALIDATION_SIZE = 365\n",
    "\n",
    "BATCH_SIZE = 64\n",
    "INPUT_TIME_STEP = 5 # in minutes\n",
    "INPUT_FEATURES_SIZE = 7\n",
    "MAX_EPOCHS = 20"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "SAMPLE_COUNT = 1000\n",
    "#   A R L D\n",
    "# A\n",
    "# R\n",
    "# L\n",
    "# D\n",
    "CONFUSION_MATRIX = np.array(\n",
    "    [\n",
    "        [66.2, 5.0, 22.5, 6.2],\n",
    "        [1.6, 60.7, 33.0, 4.7],\n",
    "        [3.8, 22.3, 55.4, 18.5],\n",
    "        [0.0, 1.3, 26.7, 72.0],\n",
    "    ]\n",
    ")/100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Import Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# sleep_data = pd.read_csv(SLEEP_DATA_PATH)\n",
    "sleep_data = pd.read_csv(UPDATED_SLEEP_DATA_PATH)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>sleep_id</th>\n",
       "      <th>minutes_since_begin</th>\n",
       "      <th>stage_start_hour</th>\n",
       "      <th>stage_start_minute</th>\n",
       "      <th>awake_probability_noisy</th>\n",
       "      <th>rem_probability_noisy</th>\n",
       "      <th>light_probability_noisy</th>\n",
       "      <th>deep_probability_noisy</th>\n",
       "      <th>awake_probability_original</th>\n",
       "      <th>rem_probability_original</th>\n",
       "      <th>light_probability_original</th>\n",
       "      <th>deep_probability_original</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>18.0</td>\n",
       "      <td>0.680</td>\n",
       "      <td>0.057</td>\n",
       "      <td>0.200</td>\n",
       "      <td>0.063</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>19.0</td>\n",
       "      <td>0.652</td>\n",
       "      <td>0.060</td>\n",
       "      <td>0.224</td>\n",
       "      <td>0.064</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>20.0</td>\n",
       "      <td>0.672</td>\n",
       "      <td>0.059</td>\n",
       "      <td>0.209</td>\n",
       "      <td>0.060</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>21.0</td>\n",
       "      <td>0.645</td>\n",
       "      <td>0.056</td>\n",
       "      <td>0.235</td>\n",
       "      <td>0.064</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.0</td>\n",
       "      <td>4.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>22.0</td>\n",
       "      <td>0.644</td>\n",
       "      <td>0.054</td>\n",
       "      <td>0.244</td>\n",
       "      <td>0.058</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551037</th>\n",
       "      <td>1132.0</td>\n",
       "      <td>426.0</td>\n",
       "      <td>13.0</td>\n",
       "      <td>17.0</td>\n",
       "      <td>0.041</td>\n",
       "      <td>0.193</td>\n",
       "      <td>0.576</td>\n",
       "      <td>0.190</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551038</th>\n",
       "      <td>1132.0</td>\n",
       "      <td>427.0</td>\n",
       "      <td>13.0</td>\n",
       "      <td>18.0</td>\n",
       "      <td>0.027</td>\n",
       "      <td>0.209</td>\n",
       "      <td>0.563</td>\n",
       "      <td>0.201</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551039</th>\n",
       "      <td>1132.0</td>\n",
       "      <td>428.0</td>\n",
       "      <td>13.0</td>\n",
       "      <td>19.0</td>\n",
       "      <td>0.032</td>\n",
       "      <td>0.220</td>\n",
       "      <td>0.574</td>\n",
       "      <td>0.174</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551040</th>\n",
       "      <td>1132.0</td>\n",
       "      <td>429.0</td>\n",
       "      <td>13.0</td>\n",
       "      <td>20.0</td>\n",
       "      <td>0.036</td>\n",
       "      <td>0.256</td>\n",
       "      <td>0.530</td>\n",
       "      <td>0.178</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>551041</th>\n",
       "      <td>1132.0</td>\n",
       "      <td>430.0</td>\n",
       "      <td>13.0</td>\n",
       "      <td>21.0</td>\n",
       "      <td>0.033</td>\n",
       "      <td>0.205</td>\n",
       "      <td>0.571</td>\n",
       "      <td>0.191</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>551042 rows × 12 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "        sleep_id  minutes_since_begin  stage_start_hour  stage_start_minute  \\\n",
       "0            0.0                  0.0               8.0                18.0   \n",
       "1            0.0                  1.0               8.0                19.0   \n",
       "2            0.0                  2.0               8.0                20.0   \n",
       "3            0.0                  3.0               8.0                21.0   \n",
       "4            0.0                  4.0               8.0                22.0   \n",
       "...          ...                  ...               ...                 ...   \n",
       "551037    1132.0                426.0              13.0                17.0   \n",
       "551038    1132.0                427.0              13.0                18.0   \n",
       "551039    1132.0                428.0              13.0                19.0   \n",
       "551040    1132.0                429.0              13.0                20.0   \n",
       "551041    1132.0                430.0              13.0                21.0   \n",
       "\n",
       "        awake_probability_noisy  rem_probability_noisy  \\\n",
       "0                         0.680                  0.057   \n",
       "1                         0.652                  0.060   \n",
       "2                         0.672                  0.059   \n",
       "3                         0.645                  0.056   \n",
       "4                         0.644                  0.054   \n",
       "...                         ...                    ...   \n",
       "551037                    0.041                  0.193   \n",
       "551038                    0.027                  0.209   \n",
       "551039                    0.032                  0.220   \n",
       "551040                    0.036                  0.256   \n",
       "551041                    0.033                  0.205   \n",
       "\n",
       "        light_probability_noisy  deep_probability_noisy  \\\n",
       "0                         0.200                   0.063   \n",
       "1                         0.224                   0.064   \n",
       "2                         0.209                   0.060   \n",
       "3                         0.235                   0.064   \n",
       "4                         0.244                   0.058   \n",
       "...                         ...                     ...   \n",
       "551037                    0.576                   0.190   \n",
       "551038                    0.563                   0.201   \n",
       "551039                    0.574                   0.174   \n",
       "551040                    0.530                   0.178   \n",
       "551041                    0.571                   0.191   \n",
       "\n",
       "        awake_probability_original  rem_probability_original  \\\n",
       "0                              1.0                       0.0   \n",
       "1                              1.0                       0.0   \n",
       "2                              1.0                       0.0   \n",
       "3                              1.0                       0.0   \n",
       "4                              1.0                       0.0   \n",
       "...                            ...                       ...   \n",
       "551037                         0.0                       0.0   \n",
       "551038                         0.0                       0.0   \n",
       "551039                         0.0                       0.0   \n",
       "551040                         0.0                       0.0   \n",
       "551041                         0.0                       0.0   \n",
       "\n",
       "        light_probability_original  deep_probability_original  \n",
       "0                              0.0                        0.0  \n",
       "1                              0.0                        0.0  \n",
       "2                              0.0                        0.0  \n",
       "3                              0.0                        0.0  \n",
       "4                              0.0                        0.0  \n",
       "...                            ...                        ...  \n",
       "551037                         1.0                        0.0  \n",
       "551038                         1.0                        0.0  \n",
       "551039                         1.0                        0.0  \n",
       "551040                         1.0                        0.0  \n",
       "551041                         1.0                        0.0  \n",
       "\n",
       "[551042 rows x 12 columns]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sleep_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Randmonizations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(arr):\n",
    "    val = np.exp(arr)\n",
    "    return val / sum(val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "metadata": {},
   "outputs": [],
   "source": [
    "def noisy_randomizer(confusion_matrix_index: int, sample_count: int = SAMPLE_COUNT, confusion_matrix = CONFUSION_MATRIX):\n",
    "    return np.random.multinomial(sample_count, confusion_matrix[confusion_matrix_index], size=1)[0]/sample_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_updated(dataframe):\n",
    "    dataframe_array = dataframe.to_numpy()\n",
    "    updated_probs = np.array(list(map(noisy_randomizer, np.argmax(dataframe_array[:, -4:], axis=1))))\n",
    "    columns_tmp = dataframe.columns.to_list() \n",
    "    noisy_column = list(map(lambda name: f\"{name}_noisy\", columns_tmp[-4:]))\n",
    "    og_column = list(map(lambda name: f\"{name}_original\", columns_tmp[-4:]))\n",
    "    return pd.DataFrame(np.concatenate([dataframe_array[:, :-4], updated_probs, dataframe_array[:, -4:]], axis=1), columns = columns_tmp[:-4]+noisy_column+og_column)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 174,
   "metadata": {},
   "outputs": [],
   "source": [
    "sleep_data_updated = create_updated(sleep_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "metadata": {},
   "outputs": [],
   "source": [
    "sleep_data_updated.to_csv(UPDATED_SLEEP_DATA_PATH, index=False, index_label=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Helper functions and class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def training_test_split_by_unique_index(data, index: str, test_size: int = 10):\n",
    "    test_ids = np.random.choice(data[index].unique(), size = test_size, replace=False)\n",
    "    return data[~data[index].isin(test_ids)], data[data[index].isin(test_ids)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Adapted from https://www.tensorflow.org/tutorials/structured_data/time_series\n",
    "class WindowGenerator():\n",
    "    def __init__(self, data, index: str = \"sleep_id\", input_width: int = INPUT_TIME_STEP, validation_size: int = VALIDATION_SIZE, test_size: int = TEST_SIZE, input_feature_slice: slice = slice(1,-4), label_feature_slice: slice = slice(-4,100), generate_data_now: bool = True):\n",
    "        # Partition data\n",
    "        self.training, self.testing = training_test_split_by_unique_index(data, index, test_size)\n",
    "        self.training, self.validation = training_test_split_by_unique_index(self.training, index, validation_size)\n",
    "\n",
    "        # Window paramters\n",
    "        self.input_width = input_width\n",
    "        self.label_width = 1\n",
    "        self.shift = 1\n",
    "\n",
    "        self.total_window_size = self.input_width + self.shift\n",
    "\n",
    "        self.input_slice = slice(0, input_width)\n",
    "        self.input_indices = np.arange(self.total_window_size)[self.input_slice]\n",
    "\n",
    "        self.label_start = self.total_window_size - self.label_width\n",
    "        self.labels_slice = slice(self.label_start, None)\n",
    "        self.label_indices = np.arange(self.total_window_size)[self.labels_slice]\n",
    "\n",
    "        self.input_feature_slice = input_feature_slice\n",
    "        self.label_feature_slice = label_feature_slice\n",
    "\n",
    "        self.sample_ds = self.make_dataset(data[data[index] == 0])\n",
    "\n",
    "        if generate_data_now:\n",
    "            self.training_ds = self.make_dataset(self.training, index)\n",
    "            self.validation_ds = self.make_dataset(self.validation, index)\n",
    "            self.testing_ds = self.make_dataset(self.testing, index)\n",
    "\n",
    "\n",
    "    def __repr__(self):\n",
    "        return \"WindowGenerator:\\n\\t\" +'\\n\\t'.join([\n",
    "            f'Total window size: {self.total_window_size}',\n",
    "            f'Input indices: {self.input_indices}',\n",
    "            f'Label indices: {self.label_indices}',\n",
    "        ])\n",
    "\n",
    "    def split_window(self, features):\n",
    "        inputs = features[:, self.input_slice, self.input_feature_slice]\n",
    "        inputs.set_shape([None, self.input_width, None])\n",
    "        \n",
    "        labels = tf.squeeze(features[:, self.labels_slice, self.label_feature_slice])\n",
    "        # labels.set_shape([None, self.label_width, None])\n",
    "        return inputs, labels\n",
    "\n",
    "    def make_dataset(self, data, index_group: str = \"sleep_id\", sort_by: str = \"minutes_since_begin\"):\n",
    "        ds_all = None\n",
    "        for i_group in data[index_group].unique():\n",
    "            subset_data = np.array(data[data[index_group] == i_group].sort_values(by=[sort_by]), dtype=np.float32)\n",
    "            ds = tf.keras.utils.timeseries_dataset_from_array(\n",
    "                data=subset_data,\n",
    "                targets=None,\n",
    "                sequence_length=self.total_window_size,\n",
    "                sequence_stride=1,\n",
    "                shuffle=False,\n",
    "                batch_size=BATCH_SIZE,)\n",
    "            ds_all = ds if ds_all is None else ds_all.concatenate(ds)\n",
    "        ds_all = ds_all.map(self.split_window)\n",
    "\n",
    "        return ds_all"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### General Model Helper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Adapted from https://www.tensorflow.org/tutorials/structured_data/time_series#linear_model\n",
    "def compile_and_fit(model, window: WindowGenerator, loss = tf.losses.CategoricalCrossentropy(from_logits=True), optimizer = tf.optimizers.Adam(), metrics = None, early_stop: bool = True, patience:int = 2, baseline = None, epochs: int = MAX_EPOCHS):\n",
    "    if metrics is None:\n",
    "        metrics = [tf.keras.metrics.CategoricalCrossentropy(from_logits=True), tf.keras.metrics.CategoricalAccuracy(), tf.keras.metrics.CategoricalHinge()]\n",
    "\n",
    "    callbacks = []\n",
    "    if early_stop:\n",
    "        early_stopping = tf.keras.callbacks.EarlyStopping(\n",
    "            monitor='val_loss',\n",
    "            patience=patience,\n",
    "            baseline = baseline,\n",
    "            mode='min'\n",
    "        )\n",
    "        callbacks.append(early_stopping)\n",
    "\n",
    "    model.compile(\n",
    "        loss=loss,\n",
    "        optimizer=optimizer,\n",
    "        metrics=metrics,\n",
    "    )\n",
    "\n",
    "    return model.fit(window.training_ds, epochs=epochs, validation_data=window.validation_ds, callbacks=callbacks)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Experimenting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# USE SUBSET OF DATA FOR EXPERIMENTING\n",
    "sleep_data_sub = sleep_data[sleep_data.sleep_id < 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "wg_sub = WindowGenerator(sleep_data_sub,validation_size=1, test_size=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ele[0][0] = array([[ 0.,  7., 40.,  1.,  0.,  0.,  0.],\n",
      "       [ 1.,  7., 41.,  1.,  0.,  0.,  0.],\n",
      "       [ 2.,  7., 42.,  1.,  0.,  0.,  0.],\n",
      "       [ 3.,  7., 43.,  1.,  0.,  0.,  0.],\n",
      "       [ 4.,  7., 44.,  1.,  0.,  0.,  0.],\n",
      "       [ 5.,  7., 45.,  1.,  0.,  0.,  0.],\n",
      "       [ 6.,  7., 46.,  1.,  0.,  0.,  0.],\n",
      "       [ 7.,  7., 47.,  1.,  0.,  0.,  0.],\n",
      "       [ 8.,  7., 48.,  1.,  0.,  0.,  0.],\n",
      "       [ 9.,  7., 49.,  1.,  0.,  0.,  0.]], dtype=float32)\n",
      "ele[0][0] = array([[64.,  8., 44.,  0.,  0.,  1.,  0.],\n",
      "       [65.,  8., 45.,  0.,  0.,  1.,  0.],\n",
      "       [66.,  8., 46.,  0.,  0.,  1.,  0.],\n",
      "       [67.,  8., 47.,  0.,  0.,  1.,  0.],\n",
      "       [68.,  8., 48.,  0.,  0.,  1.,  0.],\n",
      "       [69.,  8., 49.,  0.,  0.,  1.,  0.],\n",
      "       [70.,  8., 50.,  0.,  0.,  1.,  0.],\n",
      "       [71.,  8., 51.,  0.,  0.,  1.,  0.],\n",
      "       [72.,  8., 52.,  0.,  0.,  1.,  0.],\n",
      "       [73.,  8., 53.,  0.,  0.,  1.,  0.]], dtype=float32)\n",
      "ele[0][0] = array([[128.,   9.,  48.,   0.,   0.,   1.,   0.],\n",
      "       [129.,   9.,  49.,   0.,   0.,   1.,   0.],\n",
      "       [130.,   9.,  50.,   0.,   0.,   1.,   0.],\n",
      "       [131.,   9.,  51.,   0.,   0.,   1.,   0.],\n",
      "       [132.,   9.,  52.,   0.,   0.,   0.,   1.],\n",
      "       [133.,   9.,  53.,   0.,   0.,   0.,   1.],\n",
      "       [134.,   9.,  54.,   0.,   0.,   0.,   1.],\n",
      "       [135.,   9.,  55.,   0.,   0.,   0.,   1.],\n",
      "       [136.,   9.,  56.,   0.,   0.,   0.,   1.],\n",
      "       [137.,   9.,  57.,   0.,   0.,   0.,   1.]], dtype=float32)\n",
      "ele[0][0] = array([[192.,  10.,  52.,   0.,   1.,   0.,   0.],\n",
      "       [193.,  10.,  53.,   0.,   1.,   0.,   0.],\n",
      "       [194.,  10.,  54.,   0.,   1.,   0.,   0.],\n",
      "       [195.,  10.,  55.,   0.,   1.,   0.,   0.],\n",
      "       [196.,  10.,  56.,   0.,   1.,   0.,   0.],\n",
      "       [197.,  10.,  57.,   0.,   1.,   0.,   0.],\n",
      "       [198.,  10.,  58.,   0.,   1.,   0.,   0.],\n",
      "       [199.,  10.,  59.,   0.,   1.,   0.,   0.],\n",
      "       [200.,  11.,   0.,   0.,   1.,   0.,   0.],\n",
      "       [201.,  11.,   1.,   0.,   1.,   0.,   0.]], dtype=float32)\n",
      "ele[0][0] = array([[256.,  11.,  56.,   0.,   0.,   1.,   0.],\n",
      "       [257.,  11.,  57.,   0.,   0.,   1.,   0.],\n",
      "       [258.,  11.,  58.,   0.,   0.,   1.,   0.],\n",
      "       [259.,  11.,  59.,   0.,   0.,   1.,   0.],\n",
      "       [260.,  12.,   0.,   0.,   0.,   1.,   0.],\n",
      "       [261.,  12.,   1.,   0.,   0.,   1.,   0.],\n",
      "       [262.,  12.,   2.,   0.,   0.,   1.,   0.],\n",
      "       [263.,  12.,   3.,   0.,   0.,   1.,   0.],\n",
      "       [264.,  12.,   4.,   0.,   0.,   1.,   0.],\n",
      "       [265.,  12.,   5.,   0.,   0.,   1.,   0.]], dtype=float32)\n",
      "ele[0][0] = array([[320.,  13.,   0.,   0.,   1.,   0.,   0.],\n",
      "       [321.,  13.,   1.,   0.,   1.,   0.,   0.],\n",
      "       [322.,  13.,   2.,   0.,   1.,   0.,   0.],\n",
      "       [323.,  13.,   3.,   0.,   1.,   0.,   0.],\n",
      "       [324.,  13.,   4.,   0.,   1.,   0.,   0.],\n",
      "       [325.,  13.,   5.,   0.,   1.,   0.,   0.],\n",
      "       [326.,  13.,   6.,   0.,   1.,   0.,   0.],\n",
      "       [327.,  13.,   7.,   0.,   1.,   0.,   0.],\n",
      "       [328.,  13.,   8.,   0.,   1.,   0.,   0.],\n",
      "       [329.,  13.,   9.,   0.,   1.,   0.,   0.]], dtype=float32)\n",
      "ele[0][0] = array([[384.,  14.,   4.,   0.,   0.,   0.,   1.],\n",
      "       [385.,  14.,   5.,   0.,   0.,   0.,   1.],\n",
      "       [386.,  14.,   6.,   0.,   0.,   0.,   1.],\n",
      "       [387.,  14.,   7.,   0.,   0.,   0.,   1.],\n",
      "       [388.,  14.,   8.,   0.,   0.,   0.,   1.],\n",
      "       [389.,  14.,   9.,   0.,   0.,   0.,   1.],\n",
      "       [390.,  14.,  10.,   0.,   0.,   0.,   1.],\n",
      "       [391.,  14.,  11.,   0.,   0.,   0.,   1.],\n",
      "       [392.,  14.,  12.,   0.,   0.,   0.,   1.],\n",
      "       [393.,  14.,  13.,   0.,   0.,   0.,   1.]], dtype=float32)\n",
      "ele[0][0] = array([[448.,  15.,   8.,   0.,   1.,   0.,   0.],\n",
      "       [449.,  15.,   9.,   0.,   1.,   0.,   0.],\n",
      "       [450.,  15.,  10.,   0.,   1.,   0.,   0.],\n",
      "       [451.,  15.,  11.,   0.,   1.,   0.,   0.],\n",
      "       [452.,  15.,  12.,   0.,   1.,   0.,   0.],\n",
      "       [453.,  15.,  13.,   0.,   1.,   0.,   0.],\n",
      "       [454.,  15.,  14.,   0.,   1.,   0.,   0.],\n",
      "       [455.,  15.,  15.,   0.,   1.,   0.,   0.],\n",
      "       [456.,  15.,  16.,   0.,   1.,   0.,   0.],\n",
      "       [457.,  15.,  17.,   0.,   1.,   0.,   0.]], dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "for ele in wg_sub.training_ds.as_numpy_iterator():\n",
    "    print(f\"{ele[0][0] = }\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential_2\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " dense_4 (Dense)             (None, 10, 16)            128       \n",
      "                                                                 \n",
      " flatten_2 (Flatten)         (None, 160)               0         \n",
      "                                                                 \n",
      " dense_5 (Dense)             (None, 4)                 644       \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 772\n",
      "Trainable params: 772\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "BASELINE_UNITS = 16\n",
    "baseline_model = keras.Sequential(\n",
    "    [\n",
    "        layers.Input(shape=(INPUT_TIME_STEP, INPUT_FEATURES_SIZE)),\n",
    "        layers.Dense(BASELINE_UNITS),\n",
    "        # layers.Dense(BASELINE_UNITS),\n",
    "        # layers.Dense(BASELINE_UNITS),\n",
    "        layers.Flatten(),\n",
    "        layers.Dense(SLEEP_STAGES),\n",
    "    ]\n",
    ")\n",
    "baseline_model.build()\n",
    "print(baseline_model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/20\n",
      "8955/8955 [==============================] - 60s 7ms/step - loss: 2.7762 - categorical_crossentropy: 2.7745 - categorical_accuracy: 0.7612 - categorical_hinge: 15.1798 - val_loss: 0.2711 - val_categorical_crossentropy: 0.2710 - val_categorical_accuracy: 0.9546 - val_categorical_hinge: 5.1404\n",
      "Epoch 2/20\n",
      "8955/8955 [==============================] - 60s 7ms/step - loss: 0.3110 - categorical_crossentropy: 0.3111 - categorical_accuracy: 0.9418 - categorical_hinge: 1.5468 - val_loss: 0.2106 - val_categorical_crossentropy: 0.2105 - val_categorical_accuracy: 0.9583 - val_categorical_hinge: 0.3215\n",
      "Epoch 3/20\n",
      "8955/8955 [==============================] - 60s 7ms/step - loss: 0.2253 - categorical_crossentropy: 0.2254 - categorical_accuracy: 0.9551 - categorical_hinge: 0.2726 - val_loss: 0.2068 - val_categorical_crossentropy: 0.2067 - val_categorical_accuracy: 0.9582 - val_categorical_hinge: 0.3656\n",
      "Epoch 4/20\n",
      "8955/8955 [==============================] - 63s 7ms/step - loss: 0.2059 - categorical_crossentropy: 0.2059 - categorical_accuracy: 0.9582 - categorical_hinge: 0.2735 - val_loss: 0.1957 - val_categorical_crossentropy: 0.1957 - val_categorical_accuracy: 0.9591 - val_categorical_hinge: 0.3622\n",
      "Epoch 5/20\n",
      "8955/8955 [==============================] - 82s 9ms/step - loss: 0.2016 - categorical_crossentropy: 0.2016 - categorical_accuracy: 0.9587 - categorical_hinge: 0.2638 - val_loss: 0.1970 - val_categorical_crossentropy: 0.1970 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2868\n",
      "Epoch 6/20\n",
      "8955/8955 [==============================] - 72s 8ms/step - loss: 0.1995 - categorical_crossentropy: 0.1995 - categorical_accuracy: 0.9585 - categorical_hinge: 0.2506 - val_loss: 0.1973 - val_categorical_crossentropy: 0.1973 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.3453\n",
      "Epoch 7/20\n",
      "8955/8955 [==============================] - 75s 8ms/step - loss: 0.1991 - categorical_crossentropy: 0.1992 - categorical_accuracy: 0.9591 - categorical_hinge: 0.2518 - val_loss: 0.1969 - val_categorical_crossentropy: 0.1968 - val_categorical_accuracy: 0.9591 - val_categorical_hinge: 0.2474\n",
      "Epoch 8/20\n",
      "8955/8955 [==============================] - 75s 8ms/step - loss: 0.1983 - categorical_crossentropy: 0.1983 - categorical_accuracy: 0.9587 - categorical_hinge: 0.2560 - val_loss: 0.1957 - val_categorical_crossentropy: 0.1956 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2912\n",
      "Epoch 9/20\n",
      "8955/8955 [==============================] - 76s 8ms/step - loss: 0.1965 - categorical_crossentropy: 0.1965 - categorical_accuracy: 0.9595 - categorical_hinge: 0.2328 - val_loss: 0.1957 - val_categorical_crossentropy: 0.1957 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.3268\n",
      "Epoch 10/20\n",
      "8955/8955 [==============================] - 77s 9ms/step - loss: 0.1961 - categorical_crossentropy: 0.1961 - categorical_accuracy: 0.9598 - categorical_hinge: 0.2375 - val_loss: 0.1954 - val_categorical_crossentropy: 0.1954 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2518\n",
      "Epoch 11/20\n",
      "8955/8955 [==============================] - 72s 8ms/step - loss: 0.1980 - categorical_crossentropy: 0.1981 - categorical_accuracy: 0.9584 - categorical_hinge: 0.2387 - val_loss: 0.1948 - val_categorical_crossentropy: 0.1948 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2795\n",
      "Epoch 12/20\n",
      "8955/8955 [==============================] - 69s 8ms/step - loss: 0.1959 - categorical_crossentropy: 0.1959 - categorical_accuracy: 0.9594 - categorical_hinge: 0.2465 - val_loss: 0.1950 - val_categorical_crossentropy: 0.1950 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2824\n",
      "Epoch 13/20\n",
      "8955/8955 [==============================] - 65s 7ms/step - loss: 0.1960 - categorical_crossentropy: 0.1960 - categorical_accuracy: 0.9596 - categorical_hinge: 0.2413 - val_loss: 0.1955 - val_categorical_crossentropy: 0.1954 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.3859\n",
      "Epoch 14/20\n",
      "8955/8955 [==============================] - 68s 8ms/step - loss: 0.1959 - categorical_crossentropy: 0.1959 - categorical_accuracy: 0.9591 - categorical_hinge: 0.2280 - val_loss: 0.1941 - val_categorical_crossentropy: 0.1941 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.3046\n",
      "Epoch 15/20\n",
      "8955/8955 [==============================] - 77s 9ms/step - loss: 0.2031 - categorical_crossentropy: 0.2031 - categorical_accuracy: 0.9580 - categorical_hinge: 0.2336 - val_loss: 0.1949 - val_categorical_crossentropy: 0.1948 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.1873\n",
      "Epoch 16/20\n",
      "8955/8955 [==============================] - 68s 8ms/step - loss: 0.1966 - categorical_crossentropy: 0.1966 - categorical_accuracy: 0.9589 - categorical_hinge: 0.2361 - val_loss: 0.1938 - val_categorical_crossentropy: 0.1938 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2056\n",
      "Epoch 17/20\n",
      "8955/8955 [==============================] - 67s 7ms/step - loss: 0.1924 - categorical_crossentropy: 0.1924 - categorical_accuracy: 0.9599 - categorical_hinge: 0.2510 - val_loss: 0.1934 - val_categorical_crossentropy: 0.1934 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.3111\n",
      "Epoch 18/20\n",
      "8955/8955 [==============================] - 67s 7ms/step - loss: 0.2079 - categorical_crossentropy: 0.2079 - categorical_accuracy: 0.9569 - categorical_hinge: 0.3273 - val_loss: 0.1930 - val_categorical_crossentropy: 0.1929 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.2956\n",
      "Epoch 19/20\n",
      "8955/8955 [==============================] - 67s 7ms/step - loss: 0.1923 - categorical_crossentropy: 0.1923 - categorical_accuracy: 0.9599 - categorical_hinge: 0.3248 - val_loss: 0.1924 - val_categorical_crossentropy: 0.1924 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.6417\n",
      "Epoch 20/20\n",
      "8955/8955 [==============================] - 68s 8ms/step - loss: 0.1950 - categorical_crossentropy: 0.1950 - categorical_accuracy: 0.9595 - categorical_hinge: 0.3199 - val_loss: 0.1935 - val_categorical_crossentropy: 0.1935 - val_categorical_accuracy: 0.9592 - val_categorical_hinge: 0.5098\n"
     ]
    }
   ],
   "source": [
    "baseline_history = compile_and_fit(baseline_model, wg_sub)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_keys(['loss', 'categorical_crossentropy', 'categorical_accuracy', 'categorical_hinge', 'val_loss', 'val_categorical_crossentropy', 'val_categorical_accuracy', 'val_categorical_hinge'])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "baseline_history.history.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_history(baseline_history.history, \".history/baseline.csv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "def save_history(history, file_name: str = \"history.csv\"):\n",
    "    pd.DataFrame.from_dict(history).to_csv(file_name, index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-05-11 15:02:03.185843: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: .model/baseline/assets\n"
     ]
    }
   ],
   "source": [
    "baseline_model.save(\".model/baseline\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Data Prep\n",
    "\n",
    "All inputs follow: (batch_size, timesteps, input_dim)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-05-12 14:18:02.248350: I tensorflow/core/platform/cpu_feature_guard.cc:151] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA\n",
      "To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "WindowGenerator:\n",
       "\tTotal window size: 6\n",
       "\tInput indices: [0 1 2 3 4]\n",
       "\tLabel indices: [5]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wg = WindowGenerator(sleep_data)\n",
    "wg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4682"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(wg.training_ds)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ele[0].numpy()[0] = array([[ 0.   ,  8.   , 18.   ,  0.68 ,  0.057,  0.2  ,  0.063],\n",
      "       [ 1.   ,  8.   , 19.   ,  0.652,  0.06 ,  0.224,  0.064],\n",
      "       [ 2.   ,  8.   , 20.   ,  0.672,  0.059,  0.209,  0.06 ],\n",
      "       [ 3.   ,  8.   , 21.   ,  0.645,  0.056,  0.235,  0.064],\n",
      "       [ 4.   ,  8.   , 22.   ,  0.644,  0.054,  0.244,  0.058]],\n",
      "      dtype=float32)\n",
      "ele[1].numpy()[0] = array([1., 0., 0., 0.], dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "for ele in wg.sample_ds.take(1):\n",
    "    print(f\"{ele[0].numpy()[0] = }\")\n",
    "    print(f\"{ele[1].numpy()[0] = }\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model 1: LSTM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Hyper-parameters\n",
    "LSTM_UNITS = 8\n",
    "LSTM_LEARNING_RATE = 0.0001"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential_2\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " lstm (LSTM)                 (None, 8)                 512       \n",
      "                                                                 \n",
      " dense_2 (Dense)             (None, 4)                 36        \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 548\n",
      "Trainable params: 548\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "# Model Definition\n",
    "lstm_model = keras.Sequential()\n",
    "lstm_model.add(layers.Input(shape=(INPUT_TIME_STEP, INPUT_FEATURES_SIZE)))\n",
    "# lstm_model.add(layers.LSTM(LSTM_UNITS, stateful=False, return_sequences=True))\n",
    "# lstm_model.add(layers.LSTM(LSTM_UNITS, stateful=False, return_sequences=True))\n",
    "lstm_model.add(layers.LSTM(LSTM_UNITS, stateful=False, return_sequences=False))\n",
    "lstm_model.add(layers.Dense(SLEEP_STAGES))\n",
    "lstm_model.build()\n",
    "print(lstm_model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Model Training\n",
    "lstm_loss = tf.losses.CategoricalCrossentropy(from_logits=True)\n",
    "lstm_optm = tf.optimizers.Adam(learning_rate=LSTM_LEARNING_RATE)\n",
    "lstm_metrics = [tf.keras.metrics.CategoricalCrossentropy(from_logits=True), tf.keras.metrics.BinaryCrossentropy(from_logits=True),tf.keras.metrics.Accuracy()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9647/9647 [==============================] - 88s 9ms/step - loss: 0.6389 - categorical_crossentropy: 0.6388 - categorical_accuracy: 0.7669 - categorical_hinge: 0.5620 - val_loss: 0.2218 - val_categorical_crossentropy: 0.2217 - val_categorical_accuracy: 0.9551 - val_categorical_hinge: 0.2163\n"
     ]
    }
   ],
   "source": [
    "lstm_history = compile_and_fit(model=lstm_model, window=wg, epochs=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model 2: GRU"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Hyper-paramters\n",
    "GRU_UNITS = 16"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential_1\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " gru (GRU)                   (None, 16)                1200      \n",
      "                                                                 \n",
      " dense_1 (Dense)             (None, 4)                 68        \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 1,268\n",
      "Trainable params: 1,268\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "gru_model = keras.Sequential()\n",
    "gru_model.add(layers.Input(shape=(INPUT_TIME_STEP, INPUT_FEATURES_SIZE)))\n",
    "gru_model.add(layers.GRU(GRU_UNITS))\n",
    "gru_model.add(layers.Dense(SLEEP_STAGES))\n",
    "gru_model.build()\n",
    "print(gru_model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9647/9647 [==============================] - 97s 10ms/step - loss: 0.4700 - categorical_crossentropy: 0.4700 - categorical_accuracy: 0.8416 - categorical_hinge: 0.4278 - val_loss: 0.2088 - val_categorical_crossentropy: 0.2087 - val_categorical_accuracy: 0.9595 - val_categorical_hinge: 0.1954\n"
     ]
    }
   ],
   "source": [
    "gru_history = compile_and_fit(model=gru_model, window=wg, epochs=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model 3: Attention Mechanism"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "ATTENTION_UNITS = 32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " custom_attention (CustomAtt  (None, 32)               497       \n",
      " ention)                                                         \n",
      "                                                                 \n",
      " dense (Dense)               (None, 4)                 132       \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 629\n",
      "Trainable params: 629\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "am_model = keras.Sequential()\n",
    "am_model.add(layers.Input(shape=(INPUT_TIME_STEP, INPUT_FEATURES_SIZE)))\n",
    "am_model.add(CustomAttention(ATTENTION_UNITS))\n",
    "am_model.add(layers.Dense(SLEEP_STAGES))\n",
    "am_model.build()\n",
    "print(am_model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x12aaebe50>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "am_model.load_weights(\"TEST\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "am_history = compile_and_fit(model=am_model, window=wg, epochs=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1442/1442 [==============================] - 14s 4ms/step - loss: 0.1957 - categorical_crossentropy: 0.1957 - categorical_accuracy: 0.9598 - categorical_hinge: 0.1911\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[0.19572481513023376,\n",
       " 0.19574123620986938,\n",
       " 0.9597873687744141,\n",
       " 0.1910863220691681]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "am_model.evaluate(wg.testing_ds)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.59796184, -0.20803624, -0.2916372 , -0.7170148 ]],\n",
       "      dtype=float32)"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "answer = am_model.predict(np.array(\n",
    "    [[\n",
    "        [ 150.,  10., 20.,  0.5,  0.1,  0.,  0.4],\n",
    "        [ 151.,  10., 21.,  0.5,  0.2,  0.,  0.3],\n",
    "        [ 152.,  10., 22.,  0.5,  0.3,  0.,  0.2],\n",
    "        [ 153.,  10., 23.,  0.5,  0.4,  0.,  0.1],\n",
    "        [ 154.,  10., 24.,  0.2,  0.1,  0.,  0.],\n",
    "        [ 155.,  10., 25.,  0.2,  0.2,  0.,  0.],\n",
    "        [ 156.,  10., 26.,  0.2,  0.2,  0.1,  0.],\n",
    "        [ 157.,  10., 27.,  0.2,  0.25,  0.15,  0.],\n",
    "        [ 158.,  10., 28.,  0.2,  0.3,  0.2,  0.],\n",
    "        [ 159.,  10., 29.,  0.2,  0.3,  0.3,  0.]\n",
    "    ]]\n",
    "))\n",
    "norm = np.linalg.norm(answer)\n",
    "normalized_answer = answer/norm\n",
    "normalized_answer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "pd.DataFrame.from_dict(am_history.history).to_csv(f\"{HISTORY_DIR}/am_220512.csv\", index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on method save_weights in module keras.engine.training:\n",
      "\n",
      "save_weights(filepath, overwrite=True, save_format=None, options=None) method of keras.engine.sequential.Sequential instance\n",
      "    Saves all layer weights.\n",
      "    \n",
      "    Either saves in HDF5 or in TensorFlow format based on the `save_format`\n",
      "    argument.\n",
      "    \n",
      "    When saving in HDF5 format, the weight file has:\n",
      "      - `layer_names` (attribute), a list of strings\n",
      "          (ordered names of model layers).\n",
      "      - For every layer, a `group` named `layer.name`\n",
      "          - For every such layer group, a group attribute `weight_names`,\n",
      "              a list of strings\n",
      "              (ordered names of weights tensor of the layer).\n",
      "          - For every weight in the layer, a dataset\n",
      "              storing the weight value, named after the weight tensor.\n",
      "    \n",
      "    When saving in TensorFlow format, all objects referenced by the network are\n",
      "    saved in the same format as `tf.train.Checkpoint`, including any `Layer`\n",
      "    instances or `Optimizer` instances assigned to object attributes. For\n",
      "    networks constructed from inputs and outputs using `tf.keras.Model(inputs,\n",
      "    outputs)`, `Layer` instances used by the network are tracked/saved\n",
      "    automatically. For user-defined classes which inherit from `tf.keras.Model`,\n",
      "    `Layer` instances must be assigned to object attributes, typically in the\n",
      "    constructor. See the documentation of `tf.train.Checkpoint` and\n",
      "    `tf.keras.Model` for details.\n",
      "    \n",
      "    While the formats are the same, do not mix `save_weights` and\n",
      "    `tf.train.Checkpoint`. Checkpoints saved by `Model.save_weights` should be\n",
      "    loaded using `Model.load_weights`. Checkpoints saved using\n",
      "    `tf.train.Checkpoint.save` should be restored using the corresponding\n",
      "    `tf.train.Checkpoint.restore`. Prefer `tf.train.Checkpoint` over\n",
      "    `save_weights` for training checkpoints.\n",
      "    \n",
      "    The TensorFlow format matches objects and variables by starting at a root\n",
      "    object, `self` for `save_weights`, and greedily matching attribute\n",
      "    names. For `Model.save` this is the `Model`, and for `Checkpoint.save` this\n",
      "    is the `Checkpoint` even if the `Checkpoint` has a model attached. This\n",
      "    means saving a `tf.keras.Model` using `save_weights` and loading into a\n",
      "    `tf.train.Checkpoint` with a `Model` attached (or vice versa) will not match\n",
      "    the `Model`'s variables. See the\n",
      "    [guide to training checkpoints](https://www.tensorflow.org/guide/checkpoint)\n",
      "    for details on the TensorFlow format.\n",
      "    \n",
      "    Args:\n",
      "        filepath: String or PathLike, path to the file to save the weights to.\n",
      "            When saving in TensorFlow format, this is the prefix used for\n",
      "            checkpoint files (multiple files are generated). Note that the '.h5'\n",
      "            suffix causes weights to be saved in HDF5 format.\n",
      "        overwrite: Whether to silently overwrite any existing file at the\n",
      "            target location, or provide the user with a manual prompt.\n",
      "        save_format: Either 'tf' or 'h5'. A `filepath` ending in '.h5' or\n",
      "            '.keras' will default to HDF5 if `save_format` is `None`. Otherwise\n",
      "            `None` defaults to 'tf'.\n",
      "        options: Optional `tf.train.CheckpointOptions` object that specifies\n",
      "            options for saving weights.\n",
      "    \n",
      "    Raises:\n",
      "        ImportError: If `h5py` is not available when attempting to save in HDF5\n",
      "            format.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(am_model.save_weights)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-05-12 12:12:02.267722: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n",
      "WARNING:absl:Found untraced functions such as attention_score_vec_layer_call_fn, attention_score_vec_layer_call_and_return_conditional_losses, last_hidden_state_layer_call_fn, last_hidden_state_layer_call_and_return_conditional_losses, attention_score_layer_call_fn while saving (showing 5 of 14). These functions will not be directly callable after loading.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: .model/attention/assets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: .model/attention/assets\n"
     ]
    }
   ],
   "source": [
    "am_model.save(f\"{MODEL_DIR}/attention\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model Head-to-Head testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "HISTORY_DIR = \".history\"\n",
    "MODEL_DIR = \".model\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "base_history = pd.read_csv(f\"{HISTORY_DIR}/baseline.csv\")\n",
    "lstm_history = pd.read_csv(f\"{HISTORY_DIR}/lstm.csv\")\n",
    "gru_history = pd.read_csv(f\"{HISTORY_DIR}/gru.csv\")\n",
    "attn_history = pd.read_csv(f\"{HISTORY_DIR}/attention.csv\")\n",
    "history_columns = [\"categorical_crossentropy\", \"val_categorical_crossentropy\"] # \"categorical_accuracy\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<AxesSubplot:>"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXkAAAD4CAYAAAAJmJb0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAAwOklEQVR4nO3deXxU5fX48c+ZyUbYQyJrAqIUZQ2yCcoiVnGh4MLiCgiUUnHv9+vyxYW29tW6FK3LD0RZRFERENQWW7VCkSpoQHYQAQHZQ1jDlmXO7487iUOYkEkyyWRmzvv1GnKXZ+49ubmcufPMM+eKqmKMMSYyuUIdgDHGmIpjSd4YYyKYJXljjIlgluSNMSaCWZI3xpgIFhOqHScnJ2uzZs1CtXtjjAlLy5cvP6CqKYG2D1mSb9asGRkZGaHavTHGhCUR2V6a9tZdY4wxEcySvDHGRDBL8sYYE8FC1idvTLDl5uayc+dOTp06FepQjCm3hIQEmjRpQmxsbLm2Y0neRIydO3dSs2ZNmjVrhoiEOhxjykxVycrKYufOnZx//vnl2pZ115iIcerUKerVq2cJ3oQ9EaFevXpBeVdqSd5EFEvwJlIE61wOuyS/ce9RnvnnRo6czA11KMYYU+WFXZLfkXWCiYu2sO3A8VCHYowxVV7YJfm0eokA7Dh4IsSRGFM+ixYt4quvvqqUfV133XUcPny41M+bPn0699xzT/ADqmDbtm3jnXfeCXUYVULASV5E3CLynYj83c+6eBGZJSKbRWSZiDQLapQ+0pIsyZvIUBlJXlXxeDwsWLCAOnXqVOi+zrX/ynauJJ+Xl1fJ0YRWaYZQ3g9sAGr5WTcSOKSqF4rILcAzwJAgxHeWxLgYkmvEsyPLkrwp3u8/Xsf63UeDus1WjWrx1K9al9huxowZPP/884gI7dq1Y/DgwTz99NPk5ORQr149Zs6cycmTJ5k0aRJut5u3336bl19+mYsuuogxY8awY8cOAF588UUuu+wyMjMzue2229i9ezfdunXjs88+Y/ny5SQnJzNhwgSmTp0KwKhRo3jggQfYtm0bffv2pWvXrixfvpwFCxbQq1cvMjIySE5OPiu+t956i48//visGOvXr1/i77pv3z7GjBnD1q1bAZg4cSKNGjU6a/+vvPIKn3zyCSLC448/zpAhQ9izZw9Dhgzh6NGj5OXlMXHiRLp3787IkSPJyMhARBgxYgQPPvggW7ZsYezYsWRmZpKYmMjrr7/ORRddxPDhw6lVqxYZGRns3buXZ599loEDB/Loo4+yYcMG0tPTGTZsGHXr1uWDDz4gOzub/Px85s2bx4gRI9i6dSuJiYlMnjyZdu3aMX78eLZs2cLmzZs5cOAADz/8ML/+9a8ZOnQoN910EzfccAMAt99+O4MHD2bAgAFlPJsqT0BJXkSaANcDfwIe8tNkADDeOz0HeEVERCvoBrJpSdXsSt5USevWrePpp5/mq6++Ijk5mYMHDyIiLF26FBHhjTfe4Nlnn+Wvf/0rY8aMoUaNGvzP//wPALfddhsPPvggl19+OTt27KBv375s2LCB3//+9/Tp04fHHnuMf/7zn0yZMgWA5cuXM23aNJYtW4aq0rVrV3r16kXdunX54YcfePPNN7n00ktLjA/g8ssv9xtjSe677z569erFvHnzyM/PJzs7m0OHDp2x/7lz57Jy5UpWrVrFgQMH6Ny5Mz179uSdd96hb9++jBs3jvz8fE6cOMHKlSvZtWsXa9euBSjsYho9ejSTJk2iRYsWLFu2jLvvvpsvvvgCgD179rBkyRI2btxI//79GThwIH/5y194/vnn+fvfnY6H6dOns2LFClavXk1SUhL33nsvHTp0YP78+XzxxRcMHTqUlStXArB69WqWLl3K8ePH6dChA9dffz0jR47khRde4IYbbuDIkSN89dVXvPnmm+U7WSpJoFfyLwIPAzWLWd8Y+AlAVfNE5AhQDzjg20hERgOjAdLS0soQriMtKZFvtx0q8/NN5AvkirsifPHFFwwaNIjk5GQAkpKSWLNmTeGVa05OTrFfbvn8889Zv3594fzRo0fJzs5myZIlzJs3D4BrrrmGunXrArBkyRJuvPFGqlevDsBNN93El19+Sf/+/WnatOlZCb64+MD5IlkgMfrb3owZMwBwu93Url2bQ4cOnbH/JUuWcOutt+J2u6lfvz69evXi22+/pXPnzowYMYLc3FxuuOEG0tPTad68OVu3buXee+/l+uuv5+qrryY7O5uvvvqKQYMGFe739OnThdM33HADLpeLVq1asW/fvmJjveqqqwp/3yVLljB37lwA+vTpQ1ZWFkePOu/8BgwYQLVq1ahWrRpXXHEF33zzDTfccAN33303mZmZzJ07l5tvvpmYmPD4LmmJffIi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715NTU3V3bt368KFCzUxMVG3bt1auO2C55Q2xnOVDVBV7dKli37wwQeqqnry5Ek9fvz4Wft//vnn9a677lJV1Q0bNmhqaqqePHlS77nnHn377bdVVfX06dN64sQJnTNnjo4aNapw+4cPH9acnBzt1q2b7t+/X1VV33vvvcLt9erVSx966CFVVf3HP/6hV155paqqTps2TceOHVu4naeeekovueQSPXHihKqq9uvXT6dPn66qqlOmTNEBAwYU/k369u2r+fn5umnTJm3cuLGePHlSp0+frvfff7+qqn7//fdaXD6s9LIGVU1qUiLzV+4iJ89DXEzY9jyZilDCFXdF6dChA/v372f37t1kZmZSt25dGjRowIMPPsjixYtxuVzs2rWLffv20aBBg3Nu6/PPP+euu+4qvAIuqIW+du1aHn/8cQ4fPkx2djZ9+/Y967nnqsH+9ddfM3/+fMC5UUnBTUuKq/teq1YtunTp4rfGfHliLOrYsWPs2rWLG2+8EYCEhITCdb77X7JkCffeey8AF110EU2bNmXTpk1069aNP/3pT+zcuZObbrqJFi1a0LZtW373u9/xyCOP0K9fP3r06MHatWtZu3YtV111FeBUz2zYsGHhvm666SYAOnbsyLZt24qNt3///lSrVq3wmH7wwQcA3HnnnTz88MOF7QYPHozL5aJFixY0b96cjRs3MmjQIP74xz/y3HPPMXXqVIYPH17i8SmrsE7yaUmJqMKuwyc5P7l09bONqSiDBg1izpw57N27lyFDhjBz5kwyMzNZvnw5sbGxNGvWrFz12ocPH878+fNp374906dPZ9GiRWe18a3BHgylrU8fSIzB3v9tt91G165d+cc//sF1113Ha6+9Rp8+fVixYgULFizg8ccf58orr+TGG2+kdevWfP311363U1Cf3u12n7O/vTz16RMTE7nqqqv48MMPef/991m+vNy36yhWWF/+2jBKUxUNGTKE9957jzlz5jBo0CCOHDnCeeedR2xsLAsXLgy4BspVV13FtGnTOHHCOb8Lar8fO3aMhg0bkpubW1gTHc6soX6uGuwFt+QDeO+99wqfX1zd92DGeC41a9akSZMmhe8yTp8+XbhdX7614Ddt2sSOHTto2bIlW7dupXnz5tx3330MGDCA1atXs3v3bhITE7njjjv43//9X1asWEHLli3JzMwsTPK5ubmsW7euxNjOVZ++e/fuhcdy5syZZ9T1nz17Nh6Pp7APv2XLloBzT9777ruPzp07F97tqyJYkjcmyFq3bs2xY8do3LgxDRs25PbbbycjI4O2bdsyY8YMLrroooC2c80119C/f386depEenp64dDDP/7xj3Tt2pXLLrvsjG3dcsstPPfcc3To0IEtW7YUW4P9xRdfZMKECbRr147NmzcXVlUsru57MGMsyVtvvcVLL71Eu3bt6N69O3v37j2rzd13343H46Ft27YMGTKE6dOnEx8fz/vvv0+bNm1IT09n7dq1DB06lDVr1tClSxfS09P5/e9/z+OPP05cXBxz5szhkUceoX379qSnp5c4WuiKK65g/fr1pKenM2vWrLPWv/zyy0ybNq3wxuh/+9vfCtelpaXRpUsXrr32WiZNmlTYDdWxY0dq1apV4fXpRUNU+6VTp06akZFRrm14PMrFT/6TYd2b8X/XXRykyEy42rBhAxdfbOdBSU6cOEG1atUQEd577z3efffdEm/CYcpm+PDh9OvXj4EDB561bvfu3fTu3ZuNGzficvm/3vZ3TovIclXtFGgMYd0n73IJqUmJbM86HupQjAkby5cv55577kFVqVOnDlOnTg11SFFnxowZjBs3jgkTJhSb4IOlxCQvIgnAYiDe236Oqj5VpM1w4Dmce70CvKKqbwQ3VP+cksN2r1cTvtasWcOdd955xrL4+HiWLVtWIfvr0aNHifdIrWhjx47lv//97xnL7r///vC7tV4Jpk+f7nf50KFDGTp0aKXEEMiV/Gmgj6pmi0gssEREPlHVpUXazVLVe4If4rmlJSXyzY8HUdWzPsU20Sccz4O2bdsGbRRMuHj11VdDHUKVF6yu9EBu5K2qmu2djfU+qkwR99SkRLJP53HISg5HvYSEBLKysuweAybsqSpZWVlnfFegrALqkxcRN7AcuBB4VVX9vY+8WUR6ApuAB1X1Jz/bGQ2MBucT52DwHWGTVD0uKNs04alJkybs3LmTzMzMUIdiTLklJCTQpEmTcm8noCSvqvlAuojUAeaJSBtVXevT5GPgXVU9LSK/Ad4E+vjZzmRgMjija8obPDj1a8BJ8umpdYKxSROmYmNj/X4r05hoVqqPdVX1MLAQuKbI8ixVPe2dfQPoGJToApBa15vkbYSNMcacpcQkLyIp3it4RKQacBWwsUib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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD4CAYAAAD8Zh1EAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAAwXElEQVR4nO3deXiU9b3//+dnZpLJNiGBJOyIWg6IAkEQ3BDBDZcCLoArIqJfF9TqOUfxhwtt7VWrHqpYv1KtgigWBQRppXWFYyliDTSyi8AXIWxZgOzbzLx/f9yTYRKyDGS/5/24rrnm3uaed26G13zm3j5GRFBKKdX+OVq7AKWUUk1DA10ppWxCA10ppWxCA10ppWxCA10ppWzC1VpvnJKSIr17926tt1dKqXZp/fr1uSKSWtu8Vgv03r17k5GR0Vpvr5RS7ZIx5qe65ukuF6WUsgkNdKWUsgkNdKWUsolW24euVFOrrKwkKyuLsrKy1i5FqUaLiYmhR48eREVFhf0aDXRlG1lZWXg8Hnr37o0xprXLUeqUiQh5eXlkZWVx+umnh/063eWibKOsrIxOnTppmKt2zxhDp06dTvrXpga6shUNc2UXp/JZbneB/t2eI/zu79vR2/4qpVR17S7QN2bl8/rqXeSXVrZ2KUop1aa0u0BP9bgByCksb+VKlGqc1atXs3bt2hZ5r2uuuYZjx46d9Ovmz5/P9OnTm76gZrZnzx7ef//91i6jxbW7QE8LBHq2Brpq51oi0EUEv9/PypUrSUpKatb3qu/9W1p9ge71elu4mpbT7k5b1Ba6Cscv/7KFrQcKmnSd/bsl8uzPz25wuQULFvDSSy9hjGHgwIFMnDiR5557joqKCjp16sTChQspLS1l7ty5OJ1O3nvvPV599VX69evHfffdx969ewF4+eWXueiii8jJyeHWW2/lwIEDXHDBBXz++eesX7+elJQUZs+ezdtvvw3AtGnT+MUvfsGePXu46qqrGD58OOvXr2flypWMHDmSjIwMUlJSTqjv3Xff5S9/+csJNXbu3LnBv/Xw4cPcd9997N69G4DXX3+dbt26nfD+f/jDH/jb3/6GMYannnqKSZMmcfDgQSZNmkRBQQFer5fXX3+dCy+8kLvvvpuMjAyMMUydOpVHH32UXbt28eCDD5KTk0NcXBxvvvkm/fr1Y8qUKSQmJpKRkcGhQ4d44YUXuOmmm5gxYwbbtm0jPT2dO++8k+TkZD766COKiorw+XwsW7aMqVOnsnv3buLi4njjjTcYOHAgs2bNYteuXezcuZPc3Fwef/xx7rnnHiZPnswNN9zA+PHjAbjtttuYOHEi48aNO8VPU/Nod4F+vIWuF4+otmfLli0899xzrF27lpSUFI4cOYIxhnXr1mGM4U9/+hMvvPAC//M//8N9991HQkIC//Vf/wXArbfeyqOPPsrFF1/M3r17ueqqq9i2bRu//OUvGT16NE8++SR///vfeeuttwBYv3498+bN49tvv0VEGD58OCNHjiQ5OZkff/yRd955h/PPP7/B+gAuvvjiWmtsyMMPP8zIkSNZtmwZPp+PoqIijh49Wu39ly5dSmZmJt9//z25ubmcd955XHLJJbz//vtcddVVzJw5E5/PR0lJCZmZmezfv5/NmzcDBHcT3XvvvcydO5c+ffrw7bff8sADD/DVV18BcPDgQdasWcP27dsZO3YsN910E88//zwvvfQSf/3rXwFr19GGDRvYuHEjHTt25KGHHmLw4MEsX76cr776ismTJ5OZmQnAxo0bWbduHcXFxQwePJhrr72Wu+++m9///veMHz+e/Px81q5dyzvvvNO4D0szCCvQjTFjgFcAJ/AnEXm+xvzfA6MCo3FAmogkNWGdQQluFzFRDm2hq3qF05JuDl999RUTJkwgJSUFgI4dO7Jp06Zgi7SioqLOC0W++OILtm7dGhwvKCigqKiINWvWsGzZMgDGjBlDcnIyAGvWrOH6668nPj4egBtuuIF//OMfjB07ltNOO+2EMK+rPrAuygqnxtrWt2DBAgCcTicdOnTg6NGj1d5/zZo13HLLLTidTjp37szIkSP57rvvOO+885g6dSqVlZWMHz+e9PR0zjjjDHbv3s1DDz3Etddey5VXXklRURFr165lwoQJwfctLz/+/3/8+PE4HA769+/P4cOH66z1iiuuCP69a9asYenSpQCMHj2avLw8CgqsX3Tjxo0jNjaW2NhYRo0axb/+9S/Gjx/PAw88QE5ODkuXLuXGG2/E5Wp77eEG96EbY5zAa8DVQH/gFmNM/9BlRORREUkXkXTgVeCjZqi1qh7SPDEa6KrdeOihh5g+fTqbNm3ij3/8Y50Xi/j9ftatW0dmZmawpZqQkHBK71kV8k1dY1O+/yWXXMLXX39N9+7dmTJlCgsWLCA5OZnvv/+eSy+9lLlz5zJt2jT8fj9JSUnB7ZKZmcm2bduC63G73cHh+k5nDneb1Dz/u2p88uTJvPfee8ybN4+pU6eGta6WFs5B0WHAThHZLSIVwCKgvh1HtwB/bori6pLqcetBUdUmjR49msWLF5OXlwfAkSNHyM/Pp3v37gDVfqZ7PB4KCwuD41deeSWvvvpqcLxqF8BFF13Ehx9+CMBnn33G0aNHARgxYgTLly+npKSE4uJili1bxogRI066PqDOGhty2WWX8frrrwPg8/nIz88/YZkRI0bwwQcf4PP5yMnJ4euvv2bYsGH89NNPdO7cmXvuuYdp06axYcMGcnNz8fv93HjjjTz33HNs2LCBxMRETj/9dBYvXgxYof3999/XW1fNbVtbTQsXLgSsg9MpKSkkJiYC8PHHH1NWVkZeXh6rV6/mvPPOA2DKlCm8/PLLAPTv37/W9ba2cAK9O7AvZDwrMO0ExpjTgNOBrxpfWt1SE9zaQldt0tlnn83MmTMZOXIkgwYN4rHHHmPWrFlMmDCBIUOGBHd1APz85z9n2bJlpKen849//IM5c+aQkZHBwIED6d+/P3PnzgXg2Wef5bPPPuOcc85h8eLFdOnSBY/Hw7nnnsuUKVMYNmwYw4cPZ9q0aQwePPik6wPqrLEhr7zyCqtWrWLAgAEMGTKk2i6jKtdffz0DBw5k0KBBjB49mhdeeIEuXbqwevVqBg0axODBg/nggw945JFH2L9/P5deeinp6encfvvt/Pa3vwVg4cKFvPXWWwwaNIizzz6bjz/+uN66Bg4ciNPpZNCgQfz+978/Yf6sWbNYv349AwcOZMaMGdW+xAYOHMioUaM4//zzefrpp+nWrRsAnTt35qyzzuKuu+4Ke/u0NNPQFZfGmJuAMSIyLTB+BzBcRE44OdUY8wTQQ0QeqmNd9wL3AvTq1WvITz/V2fFGvZ75eDMfZx7g+2evPKXXK3vatm0bZ511VmuX0eTKy8txOp24XC6++eYb7r///mDrXTWtWbNmVTtQHaqkpIQBAwawYcMGOnTo0CL11PaZNsasF5GhtS0fzl79/UDPkPEegWm1uRl4sK4VicgbwBsAQ4cOPeVr91MT3OSXVlLu9eF2OU91NUq1C3v37mXixIn4/X6io6N58803W7ukiPPFF19w99138+ijj7ZYmJ+KcAL9O6CPMeZ0rCC/Gbi15kLGmH5AMvBNk1ZYi7RE6yBIblEF3ZNim/vtlGpVffr04d///ner1vCb3/wmuA+7yoQJE5g5c2YrVdQ8Zs2aVev0yy+/nFPdo9CSGgx0EfEaY6YDn2Kdtvi2iGwxxvwKyBCRFYFFbwYWSQvcNavq4qLsgjINdKVawMyZM20X3nYU1omUIrISWFlj2jM1xmc1XVn1S02IAfRqUaWUCtXu7uUCx3e56KmLSil1XLsM9E7x0RijLXSllArVLgPd5XTQKT6anCINdKWUqtIuAx0gJcFNdoEGumrfTvXS/tosX7681gt7msOFF154Sq+bNWsWL730UhNX0/wyMzNZuXJlwwu2snYb6Kket7bQlQrREoFedS/xluqYo673b2n1BXpbur9627tdWJjSPDHsys5t7TJUW/W3GXBoU9Ous8sAuPr5eheZMWMGPXv25MEHrevrZs2ahcvlYtWqVRw9epTKykqee+65sO+j/bvf/Y733nsPh8PB1VdfzfPPP8+bb77JG2+8QUVFBT/72c949913yczMZMWKFfzv//4vzz33XPBOgrXdQ3zXrl3cdtttFBcXM27cOF5++WWKiooQER5//PET7lu+evVqnn76aZKTk9m+fTs7duwgISGBoqKik6oxLi6uwb93586d3HfffeTk5OB0Olm8eDH79u2r9v4bN27k/vvvJyMjA5fLxezZsxk1ahRbtmzhrrvuoqKiAr/fz9KlS+nWrRsTJ04kKysLn8/H008/zaRJk1i/fj2PPfYYRUVFpKSkMH/+fLp27cqll17K8OHDWbVqFceOHeOtt95i+PDhPPPMM5SWlrJmzRqefPJJtm3bxq5du9i9eze9evXit7/9LVOnTiU3N5fU1FTmzZtHr169mDJlCjExMWRkZFBQUMDs2bO57rrruOSSS5gzZw7p6emAdfvi1157jUGDBoX1uaiTiLTKY8iQIdIYv125TX72/30ifr+/UetR9rF169bjIyufEHn7mqZ9rHyiwRo2bNggl1xySXD8rLPOkr1790p+fr6IiOTk5MiZZ54Z/NzGx8fXua6VK1fKBRdcIMXFxSIikpeXJyIiubm5wWVmzpwpc+bMERGRO++8UxYvXhycN3r0aNmxY4eIiKxbt05GjRolIiLXXnutvP/++yIi8vrrrwdrWLJkiVx++eXi9Xrl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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD4CAYAAAD8Zh1EAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAA0h0lEQVR4nO3dd3hUZd7/8fc9M0kmIYVAIAkJVREECaELSFdBZQELYEVE1h8q1t21PFiw7LOuuqx1wQaI4oMCgujGLqioIElEWiCESEkgFdL7zP37YyZDCGkkmUwy831d11xz5szJub85OfnMmfs0pbVGCCFE22dwdQFCCCGahwS6EEK4CQl0IYRwExLoQgjhJiTQhRDCTZhc1XBISIju0aOHq5oXQog2KS4uLktr3amm91wW6D169CA2NtZVzQshRJuklDpS23vS5SKEEG5CAl0IIdyEBLoQQrgJl/WhC9HcysvLSUlJoaSkxNWlCNFkZrOZyMhIvLy8GvwzEujCbaSkpBAQEECPHj1QSrm6HCEaTWtNdnY2KSkp9OzZs8E/J10uwm2UlJTQsWNHCXPR5iml6Nix4zl/25RAF25Fwly4i8asy20u0HccPsk/v9iPXPZXCCHO1OYCfVdKLku3HCKnqNzVpQghRKvS5gI9PMgMQFqeHMkg2rYtW7bw888/t0hbV155JTk5Oef8cytXrmThwoXNX5CTHT58mA8++MDVZbS4NhfoYZWBniuBLtq2lgh0rTVWq5WYmBjat2/v1Lbqar+l1RXoFRUVLVxNy2lzhy2GBdoC/YQEuqjDU5/uZd/xvGadZ78ugTz5p/71Trdq1SpefPFFlFJERUUxa9Ysnn32WcrKyujYsSOrV6+muLiYZcuWYTQaef/993n11Vfp27cvCxYs4OjRowC89NJLjB49mszMTG688UaOHz/OyJEj+frrr4mLiyMkJIQlS5awfPlyAObPn8/999/P4cOHmTx5MiNGjCAuLo6YmBjGjRtHbGwsISEhZ9X33nvv8emnn55VY2hoaL2/a3p6OgsWLCA5ORmApUuX0qVLl7Paf+211/j8889RSvHYY48xe/ZsTpw4wezZs8nLy6OiooKlS5cyatQobr/9dmJjY1FKMW/ePB544AEOHTrE3XffTWZmJn5+frz11lv07duXuXPnEhgYSGxsLGlpaTz//PNcd911PPLIIyQkJBAdHc2tt95KcHAwH3/8MQUFBVgsFjZs2MC8efNITk7Gz8+PN998k6ioKBYvXsyhQ4dISkoiKyuLhx56iD//+c/MmTOHa665hhkzZgBw0003MWvWLKZPn97Itck52lygdwrwwaAgLbfY1aUIcZa9e/fy7LPP8vPPPxMSEsLJkydRSrFt2zaUUrz99ts8//zz/Otf/2LBggX4+/vz17/+FYAbb7yRBx54gEsuuYSjR48yefJkEhISeOqpp5g4cSKPPvooX3zxBe+88w4AcXFxrFixgu3bt6O1ZsSIEYwbN47g4GAOHjzIu+++y8UXX1xvfQCXXHJJjTXW595772XcuHFs2LABi8VCQUEBp06dOqP99evXs3PnTn7//XeysrIYNmwYY8eO5YMPPmDy5MksWrQIi8VCUVERO3fuJDU1lT179gA4uonuuOMOli1bRu/evdm+fTt33XUX3333HQAnTpxg69at7N+/n2nTpnHdddfx3HPP8eKLL/LZZ58Btq6j+Ph4du3aRYcOHbjnnnsYNGgQGzdu5LvvvmPOnDns3LkTgF27drFt2zYKCwsZNGgQV111Fbfffjv//ve/mTFjBrm5ufz888+8++67TVtZnKDNBbqX0UCnAB/ZQhd1asiWtDN89913zJw5k5CQEAA6dOjA7t27HVukZWVltZ4o8s0337Bv3z7H67y8PAoKCti6dSsbNmwAYMqUKQQHBwOwdetWrr76atq1awfANddcw48//si0adPo3r37WWFeW31gOymrITXWNL9Vq1YBYDQaCQoK4tSpU2e0v3XrVm644QaMRiOhoaGMGzeOHTt2MGzYMObNm0d5eTkzZswgOjqaXr16kZyczD333MNVV13F5ZdfTkFBAT///DMzZ850tFtaWuoYnjFjBgaDgX79+pGenl5rrZdddpnj9926dSvr168HYOLEiWRnZ5OXZ/tGN336dHx9ffH19WXChAn8+uuvzJgxg7vuuovMzEzWr1/Ptddei8nU+uKzzfWhA4QF+cpOUdFm3HPPPSxcuJDdu3fzxhtv1HqyiNVqZdu2bezcudOxperv79+oNitDvrlrbM72x44dyw8//EBERARz585l1apVBAcH8/vvvzN+/HiWLVvG/PnzsVqttG/f3rFcdu7cSUJCgmM+Pj4+juG6Dmdu6DKpfvx35es5c+bw/vvvs2LFCubNm9egebW0Nhno4YFm2SkqWqWJEyeydu1asrOzATh58iS5ublEREQAnPE1PSAggPz8fMfryy+/nFdffdXxurILYPTo0Xz00UcAfPXVV5w6dQqAMWPGsHHjRoqKiigsLGTDhg2MGTPmnOsDaq2xPpMmTWLp0qUAWCwWcnNzz5pmzJgxfPjhh1gsFjIzM/nhhx8YPnw4R44cITQ0lD//+c/Mnz+f+Ph4srKysFqtXHvttTz77LPEx8cTGBhIz549Wbt2LWAL7d9//73Ouqov25pqWr16NWDbOR0SEkJgYCAAn3zyCSUlJWRnZ7NlyxaGDRsGwNy5c3nppZcA6NevX4OXUUtqk4EeFiSBLlqn/v37s2jRIsaNG8fAgQN58MEHWbx4MTNnzmTIkCGOrg6AP/3pT2zYsIHo6Gh+/PFHXnnlFWJjY4mKiqJfv34sW7YMgCeffJKvvvqKiy66iLVr1xIWFkZAQACDBw9m7ty5DB8+nBEjRjB//nwGDRp0zvUBtdZYn5dffpnNmzczYMAAhgwZckaXUaWrr76aqKgoBg4cyMSJE3n++ecJCwtjy5YtDBw4kEGDBvHhhx9y3333kZqayvjx44mOjubmm2/mH//4BwCrV6/mnXfeYeDAgfTv359PPvmkzrqioqIwGo0MHDiQf//732e9v3jxYuLi4oiKiuKRRx4540MsKiqKCRMmcPHFF/P444/TpUsXAEJDQ7nwwgu57bbbGrx8Wppy1RmXQ4cO1Y29Y9Gy7w/x3Of72fPUZPx9Wl8/lnCNhIQELrzwQleX0exKS0sxGo2YTCZ++eUX7rzzTsfWu2heixcvPmNHdVVFRUUMGDCA+Ph4goKCWqSemtZppVSc1npoTdO3yTQMr3Is+vmdG9fHKERbcfToUWbNmoXVasXb25u33nrL1SV5nG+++Ybbb7+dBx54oMXCvDHaZKBXHosugS48Qe/evfntt99cWsPf//53Rx92pZkzZ7Jo0SIXVeQcixcvrnH8pZdeypEjtd7Ks9Vom4EeVHlykRyLLkRLWLRokduFtztqkztFQwPl9H8hhKiuTQa62ctIh3benJBj0YUQwqFNBjrY+tHTZQtdCCEc2myghweZ5fR/IYSoos0GemiQWU7/F21eY0/tr8nGjRtrPLHHGUaNGtWon1u8eDEvvvhiM1fjfDt37iQmJsbVZdSrzQZ6eKCZk4VllJRbXF2KEK1CSwR65bXEW+rGHLW139LqCvTWdH31NnnYIpw+dDE9r4TuHc/tQkTCA3z+CKTtbt55hg2AK56rc5JHHnmErl27cvfddwO2LVKTycTmzZs5deoU5eXlPPvssw2+jvY///lP3n//fQwGA1dccQXPPfccb731Fm+++SZlZWWcf/75vPfee+zcuZNNmzbx/fff8+yzzzquJFjTNcQPHTrETTfdRGFhIdOnT+ell16ioKAArTUPPfTQWdct37JlC48//jjBwcHs37+fxMRE/P39KSgoOKca/fz86v19k5KSWLBgAZmZmRiNRtauXcuxY8fOaH/Xrl3ceeedxMbGYjKZWLJkCRMmTGDv3r3cdtttlJWVYbVaWb9+PV26dGHWrFmkpKRgsVh4/PHHmT17NnFxcTz44IMUFBQQEhLCypUrCQ8PZ/z48YwYMYLNmzeTk5PDO++8w4gRI3jiiScoLi5m69atPProoyQkJHDo0CGSk5Pp1q0b//jHP5g3bx5ZWVl06tSJFStW0K1bN+bOnYvZbCY2Npa8vDyWLFnC1KlTGTt2LK+88grR0dGA7fLFr7/+OgMHDmzQelErrbVLHkOGDNFN8WNipu7+8Gd626GsJs1HuI99+/adfhHzsNbLr2zeR8zD9dYQHx+vx44d63h94YUX6qNHj+rc3FyttdaZmZn6vPPO01arVWutdbt27WqdV0xMjB45cqQuLCzUWmudnZ2ttdY6K+v0Or9o0SL9yiuvaK21vvXWW/XatWsd702cOFEnJiZqrbXetm2bnjBhgtZa66uuukp/8MEHWmutly5d6qhh3bp1+tJLL9UVFRU6LS1Nd+3aVR8/flxv3rxZ+/n56eTkZMe8K3/mXGt88skn9QsvvFDr7zx8+HD98ccfa621Li4u1oWFhWe1/+KLL+rbbrtNa611QkKC7tq1qy4uLtYLFy7U77//vtZa69LSUl1UVKTXrVun58+f75h/Tk6OLisr0yNHjtQZGRlaa63XrFnjmN+4ceP0gw8+qLXW+r///a+eNGmS1lrrFStW6LvvvtsxnyeffFIPHjxYFxUVaa21njp1ql65cqXWWut33nlHT58+3fE3mTx5srZYLDoxMVFHRETo4uJivXLlSn3fffdprbU+cOCAri0Pz1in7YBYXUuutuEtdNslM6UfXdSoni1pZxk0aBAZGRkcP36czMxMgoODCQsL44EHHuCHH37AYDCQmppKeno6YWFhdc7rm2++4bbbbnNs2VZey3vPnj089thj5OTkUFBQwOTJk8/62bquIf7LL7+wceNGwHZTjcrrltR23fLAwECGDx9e4zXSm1Jjdfn5+aSmpnL11VcDYDabHe9VbX/r1q3cc889APTt25fu3buTmJjIyJEj+fvf/05KSgrXXHMNvXv3ZsCAAfzlL3/h4YcfZurUqYwZM4Y9e/awZ88eLrvsMsB2lcjw8HBHW9dccw0AQ4YM4fDhw7XWO23aNHx9fR3L9OOPPwbglltu4aGHHnJMN2vWLAwGA71796ZXr17s37+fmTNn8swzz/DCCy+wfPly5s6dW+/yaYgGBbpSagrwMmAE3tZan/XfopSaBSwGNPC71vrGZqmwFmFBtgUpR7qI1mbmzJmsW7eOtLQ0Zs+ezerVq8nMzCQuLg4vLy969OjRpOuNz507l40bNzJw4EBWrlzJli1bzpqm6jXEm8O5Xl+9ITU2d/s33ngjI0aM4L///S9XXnklb7zxBhMnTiQ+Pp6YmBgee+wxJk2axNVXX03//v355ZdfapxP5fXVjUZjnf3jTbm+up+fH5dddhmffPIJH330EXFxcQ2aV33q3SmqlDICrwNXAP2AG5RS/apN0xt4FBitte4P3N8s1dXB38dEgI9JzhYVrc7s2bNZs2YN69atY+bMmeTm5tK5c2e8vLzYvHlzg68Jctlll7FixQqKioqA09cuz8/PJzw8nPLycsc1veHMa4DXdQ3xytvCAaxZs8bx87Vdt7w5a6xLQEAAkZGRjm8PpaWljvlWVfVa5omJiRw9epQ+ffqQnJxMr169uPfee5k+fTq7du3i+PHj+Pn5cfPNN/O3v/2N+Ph4+vTpQ2ZmpiPQy8vL2bt3b7211XV99VGjRjmW5erVq8+4Lv3atWuxWq2OPvc+ffoAtnvA3nvvvQwbNsxxF6qmashRLsOBJK11sta6DFgDVN+j82fgda31KQCtdUazVFcPuS66aI369+9Pfn4+ERERhIeHc9NNNxEbG8uAAQNYtWoVffv2bdB8pkyZwrRp0xg6dCjR0dGOw/2eeeYZRowYwejRo8+Y1/XXX88LL7zAoEGDOHToUK3XEH/ppZdYsmQJUVFRJCUlOa4eWNt1y5uzxvq89957vPLKK0RFRTFq1CjS0tLOmuauu+7CarUyYMAAZs+ezcqVK/Hx8eGjjz7ioosuIjo6mj179jBnzhx2797N8OHDiY6O5qmnnuKxxx7D29ubdevW8fDDDzNw4ECio6PrPWpnwoQJ7Nu3j+joaD788MOz3n/11VdZsWKF46bbL7/8suO9bt26MXz4cK644gqWLVvm6EoaMmQIgYGBzXp99Xqvh66Uug6YorWeb399CzBCa72wyjQbgURgNLZumcVa6y9qmNcdwB0A3bp1G9LUq5fd8s528koq+OTu0U2aj3AP7no99OZWVFSEr68vSinWrFnD//3f/9V7wwjROHPnzmXq1Klcd911Z713/Phxxo8fz/79+zEYat62dtX10E1Ab2A8EAn8oJQaoLXOqTqR1vpN4E2w3eCiqY2GBZpJTM9s6myE8ChxcXEsXLgQrTXt27dn+fLlri7J46xatYpFixaxZMmSWsO8MRoS6KlA1yqvI+3jqkoBtmuty4E/lFKJ2AJ+R7NUWYvwIDMZ+aWUW6x4GdvsOVLCw+3evZtbbrnljHE+Pj5s377dKe2NGTOm3ntyOtvdd9/NTz/9dMa4++67r1Xf3q0xVq5cWeP4OXPmMGfOnGZvryGBvgPorZTqiS3IrweqH8GyEbgBWKGUCgEuAJKbsc4ahQX5ojVk5pfSpb2vs5sTbYDW+qyjClq7AQMGeNwt5V5//XVXl9Dq1dcdXpN6N2u11hXAQuBLIAH4SGu9Vyn1tFJqmn2yL4FspdQ+YDPwN6119jlXc44ct6KTY9EFtuOWs7OzG/WPIERrorUmOzv7jGPxG6JBfeha6xggptq4J6oMa+BB+6PFhFW5t6gQkZGRpKSkkJkp+1VE22c2m4mMjDynn2mzZ4rC6XuLyslFAsDLy6vGsxmF8BRtek9iez8vfEwG0uTeokII0bYDXSlFeJCZtLxSV5cihBAu16YDHSrPFpUtdCGEaPuBHii3ohNCCHCHQA/yJT2vBKtVDlUTQni2Nh/o4UFmyi2a7MIyV5cihBAu1eYDveqt6IQQwpO1+UCvPFtU+tGFEJ6uzQd65clFcqSLEMLTtflA7+jvg8mgZAtdCOHx2nygGw2K0EC5c5EQQrT5QAf7yUWyU1QI4eHcJ9BlC10I4eHcI9DtZ4vKdbCFEJ7MLQI9PMhMcbmFvOIKV5cihBAu4xaBHiZ3LhJCCPcI9NMnF8mx6EIIz+UWgR4aKLeiE0IItwj0zgFmlJLT/4UQns0tAt3bZCDE30e20IUQHs0tAh2w34pOAl0I4bncJtDD5PR/IYSHc59ADzLLUS5CCI/mVoGeV1JBUZmcXCSE8ExuE+iVx6JLt4sQwlO5TaCHBfoCEuhCCM/lNoEut6ITQng6twl0uZ6LEMLTuU2gm72MtPfzkiNdhBAey20CHSqPRS91dRlCCOESbhXotrNFZQtdCOGZ3CrQ5VZ0QghP5l6BHuhLVkEZpRUWV5cihBAtrkGBrpSaopQ6oJRKUko9UsP7c5VSmUqpnfbH/OYvtX6Vhy5m5Ek/uhDC85jqm0ApZQReBy4DUoAdSqlNWut91Sb9UGu90Ak1NljVQxe7dvBzZSlCCNHiGrKFPhxI0lona63LgDXAdOeW1ThycpEQwpM1JNAjgGNVXqfYx1V3rVJql1JqnVKqa00zUkrdoZSKVUrFZmZmNqLcuoU6ruciR7oIITxPc+0U/RToobWOAr4G3q1pIq31m1rroVrroZ06dWqmpk8L8DHRztsoW+hCCI/UkEBPBapucUfaxzlorbO11pV7It8GhjRPeedGKUVYkJl0Of1fCOGBGhLoO4DeSqmeSilv4HpgU9UJlFLhVV5OAxKar8RzEx7kK1voQgiPVO9RLlrrCqXUQuBLwAgs11rvVUo9DcRqrTcB9yqlpgEVwElgrhNrrlNYkJmfkrJc1bwQQrhMvYEOoLWOAWKqjXuiyvCjwKPNW1rjhAWaycgvpcJixWR0q/OmhBCiTm6XeGFBZixWTVZBmatLEUKIFuV2gR4u10UXQngotwv0MDkWXQjhodwv0APlbFEhhGdyu0Dv0M4bb6NBLqMrhPA4bhfolScXSR+6EMLTuF2gg60fXbpchBCexi0DPVzuXCSE8EBuGei2m0WXoLV2dSlCCNFi3DPQg8yUWaycLJSTi4QQnsMtA11OLhJCeCK3DPSwIF8A6UcXQngUtwx0uRWdEMITuWWgh/j7YDQo2UIXQngUtwx0o0HROcBHttCFEB7FLQMdkFvRCSE8jtsGeniQmRNyxUUhhAdx20APDbSd/i8nFwkhPIXbBnp4kJmiMgv5pRWuLkUIIVqE2wZ65bHo6bJjVAjhIdw20OVYdCGEp3HbQK+8c5Eciy6E8BRuG+ihcis6IYSHcdtA9zYZCPH3Ji1PDl0UQngGtw10sJ1cJF0uQghP4d6BHugrXS5CCI/h1oEeLjeLFkJ4ELcO9LAgMzlF5ZSUW1xdihBCOJ17B7ocuiiE8CBuHehycpEQwpO4daCHOe4tKocuCiHcn0cEumyhCyE8gVsHup+3iUCzSfrQhRAeoUGBrpSaopQ6oJRKUko9Usd01yqltFJqaPOV2DThQb4S6EIIj1BvoCuljMDrwBVAP+AGpVS/GqYLAO4Dtjd3kU0RJseiCyE8REO20IcDSVrrZK11GbAGmF7DdM8A/wRaVXrabkXXqkoSQginaEigRwDHqrxOsY9zUEoNBrpqrf9b14yUUncopWKVUrGZmZnnXGxjhAaaySoopazC2iLtCSGEqzR5p6hSygAsAf5S37Ra6ze11kO11kM7derU1KYbJDzIjNaQkS9b6UII99aQQE8FulZ5HWkfVykAuAjYopQ6DFwMbGotO0YrD11Ml350IYSba0ig7wB6K6V6KqW8geuBTZVvaq1ztdYhWuseWusewDZgmtY61ikVn6Nw+71FpR9dCOHu6g10rXUFsBD4EkgAPtJa71VKPa2UmubsApvKcbaoBLoQws2ZGjKR1joGiKk27olaph3f9LKaT6DZhK+XUbbQhRBuz63PFAVQSsl10YUQHsHtAx3kVnRCCM8ggS6EEG7CMwI90Ex6XgkWq3Z1KUII4TQeEejhQWYqrJrsglJXlyKEEE7jEYEeZj8WXXaMCiHcmUcEutyKTgjhCTwi0OXkIiGEJ/CIQO/g542XUckWuhDCrXlEoBsMilD7kS5CCOGuPCLQofJGF8WuLkMIIZzGYwI9TO4tKoRwcx4T6JW3otNaTi4SQrgnjwn00EAzpRVWcorKXV2KEEI4hccEeuWx6HJykRDCXXlMoMux6EIId+cxgS5niwoh3J3HBHonfx8MCtLk0EUhhJvymEA3GQ10CvCRLXQhhNvymEAH+7HoslNUCOGmPCrQwwPlzkVCCPflUYEut6ITQrgzjwv0/NIKCkorXF2KEEI0O48K9HA5Fl0I4cY8KtDDAiXQhRDuy6MCPdx+b1G5jK4Qwh15VKB3DvQBZAtdCOGePCrQzV5GOrTz5oQciy6EcEMeFehg60dPly10IYQb8rhAr7zRhRBCuBuPC/SwILOc/i+EcEseF+jhQWZOFpaRXyJ3LhJCuBePC/RLencCYM2vx1xciRBCNC+PC/Toru0Z2asjb29NprTC4upyhBCi2TQo0JVSU5RSB5RSSUqpR2p4f4FSardSaqdSaqtSql/zl9p87ppwHul5pXwcn+rqUoQQotnUG+hKKSPwOnAF0A+4oYbA/kBrPUBrHQ08Dyxp7kKb0yXnhzAgIog3vj+ExapdXY4QQjSLhmyhDweStNbJWusyYA0wveoEWuu8Ki/bAa06JZVS3DX+PA5nF/H5nhOuLkcIIZpFQwI9Aqi6BzHFPu4MSqm7lVKHsG2h31vTjJRSdyilYpVSsZmZmY2pt9lM7h9Gr07t+M/mQ2jdqj9/hBCiQZptp6jW+nWt9XnAw8BjtUzzptZ6qNZ6aKdOnZqr6UYxGBQLxp3HvhN5fJ/o2g8XIYRoDg0J9FSga5XXkfZxtVkDzGhCTS1mRnQE4UFm/rPlkKtLEUKIJmtIoO8AeiuleiqlvIHrgU1VJ1BK9a7y8irgYPOV6DzeJgPzx/Ti1z9OEnfkpKvLEUKIJqk30LXWFcBC4EsgAfhIa71XKfW0UmqafbKFSqm9SqmdwIPArc4quLndMLwrwX5e/GezbKULIdo2U0Mm0lrHADHVxj1RZfi+Zq6rxfh5m5g7qif//iaR/Wl59A0LdHVJQgjRKB53pmhNbh3VnXbeRpZKX7oQog2TQAfa+3lz44hufPr7cY5mF7m6HCGEaBQJdLv5Y3phMhh44wfZShdCtE0S6HahgWauHRLB2rgUMvLleulCiLZHAr2K/zf2PCosVpZvPezqUoQQ4pxJoFfRI6QdVw4I5/1tR8gtlhtgCCHaFgn0ahaMO4+C0gre33bE1aUIIcQ5kUCv5qKIIMZd0InlW/+guExugCGEaDsk0Gtw1/jzyC4s46NYuU2dEKLtkECvwfCeHRjSPZg3f0im3GJ1dTlCCNEgEug1qLwBRmpOMZt2Hnd1OUII0SBtL9BP7IKYv4HVuVvOE/t2pm9YAEu/P4RVblMnhGgD2l6gH9sOv74Jv7zm1GaUUtw5/jySMgr4JiHdqW0JIURzaHuBPmw+XPgn+PYpOLbDqU1dNSCcbh38+M8WuU2dEKL1a3uBrhRMew0CI2DdbVDkvBtTmIwG7hjbi53HcvglOdtp7QghRHNoe4EO4NseZq6A/DT45G5w4tbzdUMiCfH3kUvrCiFavbYZ6AARQ+DyZ+BADGxb6rRmzF5Gbr+kJz8ezGJ3Sq7T2hFCiKZqu4EOMGIB9J0KXz8BqXFOa+bmi7sRYDbxny1JTmtDCCGaqm0HulIw/TUICIe1c6E4xynNBJi9mDOyO1/sTSMpo8ApbQghRFO17UAH8A229afnHYdNC53Wn37b6J54Gw288b30pQshWqe2H+gAkUPh0sWQ8Cn8+pZTmgjx9+H6YV3Z8Fsqx3OKndKGEEI0hXsEOsDIhXDBFPhqERz/zSlN/HlsLwDe+jHZKfMXQoimcJ9AVwpmLIV2nW396SXNf0RKZLAf06K78MH2o7y+OYmisopmb0MIIRrLfQIdwK8DXLccco7Bpnud0p/+yJS+jD4/hBe+PMDY5zfzztY/KCmX66YLIVzPvQIdoNsImPQ47NsIse80++w7B5pZPncY6+8cRZ+wAJ75bB/jX9jC6u1HKKtovZfarbBYSUzPZ+Nvqaz46Q/yS+QWe0K4G+Wqa5QMHTpUx8bGOmfmVit8MAv++AHmfwPhUc5pB/j5UBYvfnmA+KM5dO3gy/2TLmDGoAiMBuW0NutTUFrB/hN57DuRx77jtuf9aflnfOCEBZp5ZsZFXNYv1GV1CiHOnVIqTms9tMb33DLQAQqzYNkl4OUH/+978AlwWlNaa7YkZvKvrw6wJzWP8zq144HLLuDKi8IxODHYtdak55Wy70SuI7j3Hc/jcHaRY5r2fl707xJIv/BA+nUJ5KIOCo7+zL07QkjIKOKqAeE8Oa0fnQPMTqtTCNF8PDPQAQ7/BO9Ohf7XwLVv23acOpHWmi/3pvGvrxI5mFHAheGB/OWyC5h0YWdUE9surbCQlFHAgbR8DqTlO8I7u7DMMU33jn624LaHd78ugYQFmm1taw0JmyDmIShIwxoxjNVhD/PMtgrMXgYWXXUhs4Z2bXKd7kxrTczuND6OT2F4zw5cPSiCzoHyQShalucGOsAPL8B3z8KfXoYhc53fHmCxaj79/Tj//iaRI9lFRHdtz18v78Po8zvWG5hWq+boySIOpOc7wnt/mm2r22K/0Ya30cAFYf70Cw+kf5cg+nUJpG9YAAFmr5pnmptiuynIgRgIi4KB18P3z0NFCVkjHuLuQyPYfjiXi3t14B/XRNEzpF1zL5I2b09qLk9/uo9fD58kxN+brIIyDArGXtCJ64ZEcumFoZi9jK4uU3gAzw50qxXevwaO/gLzv4Wwi5zfpl25xcr6uBRe+fYgx3NLGNGzA3+d3IdhPToAkJlf6gjsRHuAJ6YXUFzlqJluHfzoExZA37AA+oQF0Cc0gB4h7fAyNmB/ttUCO96Gb58GbYUJ/wMj7gSjyXalyk/vh8TP0V0v5rNej/E/3xdRWmHlvkm9uWNsr4a14eYy80t58csDfBR3jGA/b/5y+QVcP6wbh7MLWR+XwobfUjmRW0Kg2cSfBnbh2iGRDOraXr7pCKfx7EAHKMiw9af7BMIdW8DHv2XatSutsLDm12O8tjmJzPxSLgwPJCOv5IzukhB/b/qEBXBBaGV4B9K7sz/tfEyNazRtD3x6r+2iZedNgqlLILjHmdNoDbs+hM8fgooy8i/5Hx4+NpKYvRn0DQvgn9dGMbBr+0b/3m1ZaYWFlT8d5tXvkigptzB3VA/umdSbIN8zvwVZrJqfD2WxPi6FL/amUVJupVendlw7OJJrBkcQHuTrot/APRWUVrD1YBYDIoOIaN8Gl63WcOhb6DLYdph1I0igg+2Il1XTYcAsuHqZ0/vTa1JcZuG9bYf5NiGDHh3b2ba47Y8Qf5/maaS82Nad8vMrYG4PU56DAdfV/fvmHYdP74ODX0H30fzQbzF/+zaPzPxS5o7qyV8uv6DxHyxtjNaar/el8/eYBI5kFzGxb2cWXXUh53WqfyMgv6ScmN0nWBeXwo7Dp1AKLjk/hOuGRHJ5vzB8vaVLprGSMwtY9csR1sWlUFBagVJwcc+OXDskkikXheHf2tdPSwXs3QA/vQzpu+HSp+CS+xs1Kwn0Slv+CVv+13bHo8G3tGzbLSF5i60b5dQfEH2z7XrxDd0K0Bp2roYvHgVrBcXjn+R/M0by3vYUItr78verL2J8n85NKq/cYuVETgmnisro1sGP4HbeTZpfczuQls8zn+1ja1IW53f25/Gp/Rh3QadGzetwViEfx6ewPj6V1JxiAnxMXBUVznVDIhnSPVi6ZBrAatVsPpDBu78c4YfETLyMiqlRXbh6UAQ7j+XwcXwKh7OL8PUyMuWiMK4dHMnI8zq69JDhs5QVwW/vwy+vQs5RCLkARt+HHjATZWrcRlyTA10pNQV4GTACb2utn6v2/oPAfKACyATmaa2P1DVPlwS61QLvzbBtrQd0gdD+ENoPQi+Czv1sC9vUukKmQQqz4avH4PcPoEMvmPoS9BrXuHnlpsCme+DQd9BjDL8P+V8e/OokhzILmRHdhcen9qNjLd8mSsotpJwqJjWnmNRTxaScKnIMp+YUk5ZXcsbJuyH+3pzf2Z/enQPoHervGA7x927RwDtVWMaSrxNZvf0IAWYvHri0Nzdd3L1Z9iFYrZptf2SzPi6Vz/ecoKjMQrcOfgzu1p6+4bad2X3DAgkN9JGQt8stLmdt7DFW/XKEoyeLCA304aYR3blheDc6BZxe97TWxB89xfr4VD77/Th5JRWEBZqZMSiCawdH0DvUeYcq16vopO1m9tvfgOKTlIQNZUfkHD4pjOLXIzk8NKUPU6O6NGrWTQp0pZQRSAQuA1KAHcANWut9VaaZAGzXWhcppe4ExmutZ9c1X5cEOtiumf7be7Y+5oy9kHkALPa+bIPJFuqd+9nD3v4IjHBJF029tIZdH8GXj9quXTP6fhj7V/BqYt+i1hC/Cr5cBGjKJz3Nq7mXsPT7Q/j7mLh3Um8MSp0V2FkFZWfMxmhQhAeZiWjvS0SwL5HtfYkM9iPIz4uj2UUczMjnYEYBSekF5Jeevi5Oez8venf25/zOAfTu7O8Ie8chmM2k3GLl/W1HeOmbgxSUVnDziG7cf+kFTvvmUFhawed70vh89wkSTuRxPLfE8V57Py9HuF8Ybnu+IDTAo7ppDqTl8+4vh9kQn0pxuYVhPYK5dVQPJvcPq/fDtaTcwrcJGXwcn8KWxEwsVk1UZBDXDIpgWnQEHVrq22DOUaw/vwbxqzBUFLO73cW8VDKVbwttF/YL9vNiWI8OzB3Vg1HnhzSqiaYG+khgsdZ6sv31owBa63/UMv0g4DWt9ei65uuyQK/OUg7ZhyB9D2Tsg/S9kL4Pco+ensYcBJ0rt+b7Q8feth2rXn628Kx8NvmCoYWODDn5B3z2ACRvhshh8KdXbPU1p5yj8MlC+ON76DWB5FHP8devsok/mgOAt8lApD2sI9r7EhlcOexHRLAvoQE+mBqwlVt5gtTBjHySMgocIZ+YkU9O0elLFPj7mDi/sz+9OrWjg583Qb5eBPl5EeTrRaDZi0Bf23CQrxeBviZ8TLWH4ZYDGTz73wSSMgoY0zuEx6f244IW3qLLLSpnf5rtLN79aXkknMgnMT2fojLbUU5KQY+O7RxB3zfctsO8a7AfAEXlFgpKKsgvKSe/tIKCkgoKSu2v7cO29+3jSysoKCmnuNxKpwAfIoPtfzP7B23XYF9C/H2cejJcdRUWK98kpLPy58NsSz6Jj8nAjOgIbhnZnYsigho1z6yCUj7ZeZyP41PYezwPk0ExoW9nrh0cwYS+netcLxqjrMLKwd3bMG17lfPTv8CKYpNlFG9UTKUgsDfDe3ZgWM8ODO/RgfM7+zd5o6SpgX4dMEVrPd/++hZghNZ6YS3Tvwakaa2freG9O4A7ALp16zbkyJE6e2VcqyQXMhJsQV8Z8hn7oDSv7p8z+Z4Z8mcMVz6bwehtf3id47C37ciV75+3faO49EkYOg8MTtqS0xpil8NXj4MyYJ38dw52uZpgf29C2jXwn99qBUspVJRARantG5HRx7YcTL6236+GlVxrTXZhGQfTC0jKLCAp3bZF/0dWIbnF5Y7gq43Zy3A64M2nwz4jv5StSVn06OjHY1f1a5YTv5qL1ao5dqqIhBO2kN9/Ip8D6fkczi50dFd5Gw2UW60NuvZcO28j/mYT/j4mAsxeBJhN+JgMZOSXknKqmJOFZ36rqvohbQt8vyrB70fngOYJ/JOFZfzfr0dZve0Ix3NLiGjvyy0juzN7aNdm/Ya0Py2Pj+NT2fBbKpn5pbT38+LKAeFEBvviYzLibTLgYzTYnk22Z2+TAW/HOOOZ7xkNKAW7U3PZkZxNfuL3jM/8gHGGnRRqHz73vpyk826lT58LGdajA5H2D9/m1GKBrpS6GVgIjNNal9Y131azhX4utIbcY3Ay2bazo7zIdlRJeXGV4QaOs5TZvh1YymwPfY5XbOxzFVz5AgRFOOd3re7UYdvW+uEfodtICAizhXNlSNf1bCmre97KACbz6Udl0Jt87N98ahhv9MaijJRqEyVWA8VWIyUWRZHFSFGForDCQEEFFJQbKSiHvHJFXhnklinKtYHJAyK4MioSLy8v24ehMlZ7NlR5bTp7HNp2bL+22tYLrWsYZx92jLc/g+0DTBnObM8xrGzDVdorqrCSmFHM/rR8/sgqxMdksAe1FwE+RvzNRgK8Dfj7GAkwm/D3NuDvbcSoqFKDrvJsW+6FZRZO5JWSmlNCak4JKTklpJ4qISWnmGOnSskqKsf2UwpQeBsNhAWZMXsZMBoMmAwKo0GdfjaqM8Z7VXttMijySyv4el86ZRVWRp/fkVtH9mBS304YrVX+HxyPKuMq7M/WctvyqdzQMZiqDds3gqoMV2Dkp+RTrI9L4at9tsNLG0th5XJDHAtMnzLIkEShKZjUPnMIGX8XHTqFNXq+DW6/JbpclFKXAq9iC/OM+opqk4HuTFarbUWtvhLXNOzlB12iXVPjjrdtO3uUsgWrI4h9Gv5s8LJtsZeXQEWxLfjLi+0fAiX28VWHq01TXgzWitPL41w/DNs0+wdB1YBuQRp19kMprCg0BjRgtV/E1Vrltda2Zyu2rXs/k8bXYMVobeQGTWPYPwS0wQsMBvuHlAGtbL8HynDG71VZv1Zn1u9VUUC7siysQd0xjL4HBt3c9P1W5/Jr1BHoDTl4cwfQWynVE0gFrgdurNbAIOANbFvy9Ya5qIHBAAYfW+C1VgYDjLjD9mhNHB+G9oC3VlT5ACyv8kFZcXoLz2qxbblaLbbpteXMcY7X9vet1jPHKYM9WCu3tKs8O8ZVGV91HAD69LwcbVqrDVtqH09N7VUOqzrG22up/EA4a+vdWmWYs95X2oqqPm3Vac4aV9Nra7XuRJ+zuxZN3jV0OdqnNZhsy8BSXsvfuup6cPawslaAtqLq/TZV03irbRn2uRJDvxm2s65bkXqr0VpXKKUWAl9iO2xxudZ6r1LqaSBWa70JeAHwB9ba+yKPaq2nObFuIU5rCx+GQrSABn28aK1jgJhq456oMnxpM9clhBDiHMnVl4QQwk1IoAshhJuQQBdCCDchgS6EEG5CAl0IIdyEBLoQQrgJCXQhhHATLrvBhVIqE2js1blCgKxmLKe5SX1NI/U1XWuvUeprvO5a6xrvvOKyQG8KpVRsbdcyaA2kvqaR+pqutdco9TmHdLkIIYSbkEAXQgg30VYD/U1XF1APqa9ppL6ma+01Sn1O0Cb70IUQQpytrW6hCyGEqEYCXQgh3ESrDnSl1BSl1AGlVJJS6pEa3vdRSn1of3+7UqpHC9bWVSm1WSm1Tym1Vyl1Xw3TjFdK5SqldtofT9Q0LyfWeFgptdve9ln3+1M2r9iX3y6l1OAWrK1PleWyUymVp5S6v9o0Lb78lFLLlVIZSqk9VcZ1UEp9rZQ6aH8OruVnb7VPc1ApdWsL1faCUmq//e+3QSnVvpafrXNdcHKNi5VSqVX+jlfW8rN1/r87sb4Pq9R2WCm1s5afbZFl2CRa61b5wHZ3pENAL8Ab+B3oV22au4Bl9uHrgQ9bsL5wYLB9OABIrKG+8cBnLlyGh4GQOt6/Evgc2z3JLga2u/BvnYbthAmXLj9gLDAY2FNl3PPAI/bhR4B/1vBzHYBk+3OwfTi4BWq7HDDZh/9ZU20NWRecXONi4K8NWAfq/H93Vn3V3v8X8IQrl2FTHq15C304kKS1TtZalwFrgOnVppkOvGsfXgdMUvZ74Dmb1vqE1jrePpwPJAARLdF2M5oOrNI224D2SqlwF9QxCTiktW7smcPNRmv9A3Cy2uiq69m7wIwafnQy8LXW+qTW+hTwNTDF2bVprb/SWlfYX24DIpuzzXNVy/JriIb8vzdZXfXZs2MW8H/N3W5Lac2BHgEcq/I6hbMD0zGNfaXOBTq2SHVV2Lt6BgHba3h7pFLqd6XU50qp/i1bGRr4SikVp5Sq6c7ODVnGLeF6av8ncuXyqxSqtT5hH04DQmuYpjUsy3nYvnHVpL51wdkW2ruFltfSZdUalt8YIF1rfbCW9129DOvVmgO9TVBK+QPrgfu11nnV3o7H1o0wEHgV2NjC5V2itR4MXAHcrZQa28Lt10sp5Q1MA9bW8Larl99ZtO27d6s71lcptQioAFbXMokr14WlwHlANHACW7dGa3QDdW+dt/r/p9Yc6KlA1yqvI+3japxGKWUCgoDsFqnO1qYXtjBfrbX+uPr7Wus8rXWBfTgG8FJKhbRUfVrrVPtzBrAB29faqhqyjJ3tCiBea51e/Q1XL78q0iu7ouzPGTVM47JlqZSaC0wFbrJ/4JylAeuC02it07XWFq21FXirlrZdui7a8+Ma4MPapnHlMmyo1hzoO4DeSqme9q2464FN1abZBFQeTXAd8F1tK3Rzs/e3vQMkaK2X1DJNWGWfvlJqOLbl3SIfOEqpdkqpgMphbDvP9lSbbBMwx360y8VAbpWuhZZS61aRK5dfNVXXs1uBT2qY5kvgcqVUsL1L4XL7OKdSSk0BHgKmaa2LapmmIeuCM2usul/m6lrabsj/uzNdCuzXWqfU9Karl2GDuXqvbF0PbEdhJGLb+73IPu5pbCsvgBnbV/Uk4FegVwvWdgm2r967gJ32x5XAAmCBfZqFwF5se+y3AaNasL5e9nZ/t9dQufyq1qeA1+3LdzcwtIX/vu2wBXRQlXEuXX7YPlxOAOXY+nFvx7Zf5lvgIPAN0ME+7VDg7So/O8++LiYBt7VQbUnY+p4r18HKo766ADF1rQstuPzes69fu7CFdHj1Gu2vz/p/b4n67ONXVq53VaZ1yTJsykNO/RdCCDfRmrtchBBCnAMJdCGEcBMS6EII4SYk0IUQwk1IoAshhJuQQBdCCDchgS6EEG7i/wOJe2Iv2SfgTwAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "base_history[history_columns].plot()\n",
    "lstm_history[history_columns].plot()\n",
    "gru_history[history_columns].plot()\n",
    "attn_history[history_columns].plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "base_model = tf.keras.models.load_model(f\"{MODEL_DIR}/baseline\")\n",
    "lstm_model = tf.keras.models.load_model(f\"{MODEL_DIR}/lstm\")\n",
    "gru_model = tf.keras.models.load_model(f\"{MODEL_DIR}/gru\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "Unable to restore object of class 'Attention' likely due to name conflict with built-in Keras class '<class 'keras.layers.dense_attention.Attention'>'. To override the built-in Keras definition of the object, decorate your class with `@keras.utils.register_keras_serializable` and include that file in your program, or pass your class in a `keras.utils.CustomObjectScope` that wraps this load call.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m/Users/nowadmin/Documents/School Folder/CS 437/Lab/Final Project/tf_model.ipynb Cell 65'\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000068?line=0'>1</a>\u001b[0m attn_model \u001b[39m=\u001b[39m tf\u001b[39m.\u001b[39;49mkeras\u001b[39m.\u001b[39;49mmodels\u001b[39m.\u001b[39;49mload_model(\u001b[39mf\u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39m{\u001b[39;49;00mMODEL_DIR\u001b[39m}\u001b[39;49;00m\u001b[39m/attention\u001b[39;49m\u001b[39m\"\u001b[39;49m)\n",
      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py:67\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=64'>65</a>\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mException\u001b[39;00m \u001b[39mas\u001b[39;00m e:  \u001b[39m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=65'>66</a>\u001b[0m   filtered_tb \u001b[39m=\u001b[39m _process_traceback_frames(e\u001b[39m.\u001b[39m__traceback__)\n\u001b[0;32m---> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=66'>67</a>\u001b[0m   \u001b[39mraise\u001b[39;00m e\u001b[39m.\u001b[39mwith_traceback(filtered_tb) \u001b[39mfrom\u001b[39;00m \u001b[39mNone\u001b[39m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=67'>68</a>\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=68'>69</a>\u001b[0m   \u001b[39mdel\u001b[39;00m filtered_tb\n",
      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py:532\u001b[0m, in \u001b[0;36mKerasObjectLoader._revive_layer_or_model_from_config\u001b[0;34m(self, metadata, node_id)\u001b[0m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=529'>530</a>\u001b[0m builtin_layer \u001b[39m=\u001b[39m layers_module\u001b[39m.\u001b[39mget_builtin_layer(class_name)\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=530'>531</a>\u001b[0m \u001b[39mif\u001b[39;00m builtin_layer:\n\u001b[0;32m--> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=531'>532</a>\u001b[0m   \u001b[39mraise\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m(\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=532'>533</a>\u001b[0m       \u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39mUnable to restore object of class \u001b[39m\u001b[39m\\'\u001b[39;00m\u001b[39m{\u001b[39;00mclass_name\u001b[39m}\u001b[39;00m\u001b[39m\\'\u001b[39;00m\u001b[39m likely due to \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=533'>534</a>\u001b[0m       \u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39mname conflict with built-in Keras class \u001b[39m\u001b[39m\\'\u001b[39;00m\u001b[39m{\u001b[39;00mbuiltin_layer\u001b[39m}\u001b[39;00m\u001b[39m\\'\u001b[39;00m\u001b[39m. To \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=534'>535</a>\u001b[0m       \u001b[39m'\u001b[39m\u001b[39moverride the built-in Keras definition of the object, decorate \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=535'>536</a>\u001b[0m       \u001b[39m'\u001b[39m\u001b[39myour class with `@keras.utils.register_keras_serializable` and \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=536'>537</a>\u001b[0m       \u001b[39m'\u001b[39m\u001b[39minclude that file in your program, or pass your class in a \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=537'>538</a>\u001b[0m       \u001b[39m'\u001b[39m\u001b[39m`keras.utils.CustomObjectScope` that wraps this load call.\u001b[39m\u001b[39m'\u001b[39m) \u001b[39mfrom\u001b[39;00m \u001b[39me\u001b[39;00m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=538'>539</a>\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/saving/saved_model/load.py?line=539'>540</a>\u001b[0m   \u001b[39mraise\u001b[39;00m\n",
      "\u001b[0;31mRuntimeError\u001b[0m: Unable to restore object of class 'Attention' likely due to name conflict with built-in Keras class '<class 'keras.layers.dense_attention.Attention'>'. To override the built-in Keras definition of the object, decorate your class with `@keras.utils.register_keras_serializable` and include that file in your program, or pass your class in a `keras.utils.CustomObjectScope` that wraps this load call."
     ]
    }
   ],
   "source": [
    "attn_model = tf.keras.models.load_model(f\"{MODEL_DIR}/attention\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model_time():\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Scratch"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "final_lab",
   "language": "python",
   "name": "final_lab"
  },
  "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.4"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}