[688072]: / examples / classification / multi_classification_example_ptbxl.ipynb

Download this file

754 lines (753 with data), 88.6 kB

{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a2f082db",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-16T20:11:29.105213Z",
     "start_time": "2022-05-16T20:11:27.031042Z"
    }
   },
   "source": [
    "## Multi-class classification example\n",
    "\n",
    "In this example we show how the ecgxai package can be used to easily build a classification system for multiple classes (in this case for Sinus Rhythm (NSR), Sinus Arrhythmia (SA), Sinus Bradycardia (SB) and Sinus tachycardia (STach)). We train the model on the PTB-XL dataset and use 'double residual' convolution resnet architecture. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "60f323e8",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:52.277956Z",
     "start_time": "2022-05-17T15:29:51.301506Z"
    }
   },
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "# We first import the required data utilities from the ecgxai package\n",
    "from ecgxai.utils.dataset import PTBXLDataset\n",
    "from ecgxai.utils.transforms import ApplyGain, ToTensor\n",
    "\n",
    "# We also import some additional utilities from other packages for additionally functionality\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision.transforms import Compose"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "91f9b4b6",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:54.321272Z",
     "start_time": "2022-05-17T15:29:52.279290Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 21837/21837 [00:00<00:00, 193257.72it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using downloaded and verified file: /workspace/misc/PTB_XL/PTB_XL.tar.gz\n",
      "Trainset:\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-- Dataset distribution -- \n",
      "Full size: 19653\n",
      "[\"NSR\"] - Num entries: 16268 (82.8%)\n",
      "[\"SA\"] - Num entries: 700 (3.56%)\n",
      "[\"SB\"] - Num entries: 583 (2.97%)\n",
      "[\"STach\"] - Num entries: 748 (3.81%)\n",
      "\n",
      " Testset: \n",
      "-- Dataset distribution -- \n",
      "Full size: 2184\n",
      "[\"NSR\"] - Num entries: 1824 (83.5%)\n",
      "[\"SA\"] - Num entries: 72 (3.3%)\n",
      "[\"SB\"] - Num entries: 54 (2.47%)\n",
      "[\"STach\"] - Num entries: 78 (3.57%)\n"
     ]
    }
   ],
   "source": [
    "# The PTBXL dataset class automatically downloads and extract the PTB-XL 12 lead dataset\n",
    "dataset = PTBXLDataset(\n",
    "    # The path parameter defines where the data should be stored and were the data can be found in the future\n",
    "    path=\"/workspace/misc/PTB_XL\", \n",
    "    # We use the ApplyGain and ToTensor transformation (chained through the compose class)\n",
    "    # to format the data into the desired format. In the case of PTB-XL, all the input data\n",
    "    # is devided by 1000 in the ApplyGain transform to smoothen training (see the next cell for an example).\n",
    "    transform = Compose([ApplyGain(), ToTensor()]),\n",
    "    # The use_numpy parameter is set to True to speed up the loading of the data\n",
    "    use_numpy=True,\n",
    "    # As we are classifying Sinus Rhythm (NSR), Sinus Arrhythmia (SA), Sinus Bradycardia (SB) and Sinus tachycardia (STach) \n",
    "    # in this example we tell the dataset to use the 'NSR' 'SA', 'SB' and 'STach' labels. \n",
    "    labels=['NSR', 'SA', 'SB', 'STach']\n",
    ")\n",
    "\n",
    "# We then randomly split the dataset into a train and test set using a 90%-10% split\n",
    "trainset, testset = dataset.train_test_split(ratio=0.1, shuffle=True)\n",
    "\n",
    "print(\"Trainset:\")\n",
    "trainset.print_stats()\n",
    "\n",
    "print(\"\\n Testset: \")\n",
    "testset.print_stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "48f3fa75",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:54.337637Z",
     "start_time": "2022-05-17T15:29:54.322788Z"
    }
   },
   "outputs": [],
   "source": [
    "# The train and testset are then supplied to a pytorch dataloader which we can use to train the model\n",
    "train_loader = DataLoader(\n",
    "        trainset,\n",
    "        batch_size=64,\n",
    "        num_workers=8,\n",
    "        shuffle=True\n",
    ")\n",
    "\n",
    "test_loader = DataLoader(\n",
    "    testset,\n",
    "    batch_size=64,\n",
    "    num_workers=8\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5abd82c8",
   "metadata": {},
   "source": [
    "### Checking the data\n",
    "\n",
    "The cell below plots the first lead of a sample from the dataset which we get using the __\\_\\_getitem\\_\\_()__ function which returns a dictionary. The raw ecg is stored under the 'waveform' key. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7f570e7d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:54.482801Z",
     "start_time": "2022-05-17T15:29:54.338876Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_keys(['waveform', 'samplebase', 'gain', 'id', 'truebaseline_0', 'truebaseline_1', 'truebaseline_2', 'truebaseline_3', 'truebaseline_4', 'truebaseline_5', 'truebaseline_6', 'truebaseline_7', 'truebaseline_8', 'truebaseline_9', 'truebaseline_10', 'truebaseline_11', 'label'])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f1f3ea93130>]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3IAAAD4CAYAAACpFNTCAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAACS00lEQVR4nO2dd3gc1dXG37tdvVmybMu927iBMb2bXhMggTSSUFIgIfULBAgJKRBISCWFNCB0Qu9gMJjmhgvuXa7qsrq2zv3+mLmzs6vtM6ud2T2/5/FjabWavdq9c+8957znHMY5B0EQBEEQBEEQBGEdbLkeAEEQBEEQBEEQBJEeZMgRBEEQBEEQBEFYDDLkCIIgCIIgCIIgLAYZcgRBEARBEARBEBaDDDmCIAiCIAiCIAiL4cj1AOIxYsQIPmHChFwPgyAIgiAIgiAIIid8/PHH7Zzz2lg/M60hN2HCBKxevTrXwyAIgiAIgiAIgsgJjLG98X5G0kqCIAiCIAiCIAiLQYYcQRAEQRAEQRCExSBDjiAIgiAIgiAIwmKQIUcQBEEQBEEQBGExyJAjCIIgCIIgCIKwGGTIEQRBEARBEARBWAwy5AiCIAiCIAiCICyGIYYcY+wcxtg2xthOxthNMX7+dcbYBsbYOsbY+4yxWUa8LkEQBEGYkR0tvVixuyPXwyAIgiDyGN2GHGPMDuA+AOcCmAXgyhiG2qOc8zmc8/kA7gZwr97XJQiCIAizcubvluGz9y/P9TAIgiCIPMaIiNwiADs557s5534AjwO4WPsEznmP5tsSANyA1yUIgiAIgkiJx1fuw38/asz1MAiCIAzDYcA1xgDYr/n+AIBjop/EGLsewPcAuACcbsDrEgRBEARBpMRNz2wAAHzxuAm5HQhBEIRBDFuxE875fZzzyQB+BODWWM9hjF3HGFvNGFvd1tY2XEMjCIIgCIIgCADAu9vbMOGml7G7rS/XQyGIhBhhyB0EMFbzfYPyWDweB3BJrB9wzu/nnC/knC+sra01YGgEQRAEQRAEkTovrj8EAFjdeDjHIyGIxBhhyK0CMJUxNpEx5gJwBYAXtE9gjE3VfHs+gB0GvC5BEARBEARBGIqdMQBAiFNJByPZ1dYHf1DK9TDyCt2GHOc8COAGAK8D2ALgSc75JsbYHYyxi5Sn3cAY28QYWwc5T+4qva9LEARBEARBEEZjsymGnESGnFG09npxxm/fxR0vbcr1UPIKI4qdgHP+CoBXoh77iebrG414HYIgskuPNwAGoMzjzPVQCIIgCCIn2JUwh0QROcNo6/UBILmq0RhiyBEEkR8c/Ysl8AUlNN51fq6HQhAEQRA5QZVWUkTOMHyKpNLjtOd4JPnFsFWtJAjC/PhIu04QBEEUOCStNB5vIAQAcDnI9DASejeJvOTA4QFc/cAqdPT5cj0UgiAIgiAsBAPL9RDyDknxEzts9N4aCRlyRF5yz+vb8NbWVnywqyPXQyEIgiAIwkJwUCTOaBS1Kijt0FjIkDMpksQx4aaX8evXtuZ6KJZEyCE4rRgEQRAEQRA5RcThyEg2FjLkTEpAiUH/Y9nuHI+EIAiCSAdyIBGEtRG3MGMkAzQaSjs0FjLkTEowJM90WkP0QYswQQw//qCECTe9jAc/bMz1UHICFUgwN2Y3tH/24ibMvO21XA+DAChTzkDUZdHct5/lIEPOpARCckSOEm4zg9YJgsgdg365Otlv39iW45HkhiAZcqbG7B/Pfz5oxKBS4Y8g8gXRk4+klcZChpxJCYREXD+347A69PYRRA5Qbjx1HSswqImw+dBG4ejzIZIh5guJeoxDNeTo9jMUMuRMSlASETkiI2ihIIicISkhD6EsKDRIWmk+tJ8JfT5EqtAZzDiEAUd3n7GQIWdSggXqyTYKCt0TRO4IKTt2oUoMyVAwH9q5SBE5IhmUzmU84YgcvatGQoacSVFz5MgdlBFinaANO//ZdKgb6/d35XoYhIZCv+/IkDMfkYZcDgdCWILwGSK348gnJIrIZQVHrgdAxEZsOlTsRB+FfqAsBM7/4/sAgMa7zs/xSAiBVJiKShWtIcc5p+q5JiCokfmSoU0kQ6h6KHpkHJQjlx0oImdSCjW3xCjEQkFvI0EMP6EC36lDnKI/ZiMYZVwTRCqQ0W8cInea3lFjIUPOpIhqb3YbeXL1INEiTBDDTqHfdyHKxzId2rxzOpwTqVLoTikjodsuO5AhZ1KEDITsuMwQsghahAli+Cn0gzIZcuYjqNH7WmVfoMhh7lArLNJHYBgSvalZgQw5k0IROWMo9AMlQeQCqxyUs0VkjlwOB0KoaCNyVvlMaP/KHeKdp8/AOMINwQkjIUPOpAjvIRlymUFVKwkidxR6JIEicuYjaME+chYZZl5CZwjjoYBcdiBDzqQI7yFVO9OHVTZss1HoB3FCH4VeZEgbkaQ1yBxESCst8pmQEZE7xB5Y6Pm+RhKOyNF7aiRkyJkUUbXSToZcRpAsQh90fiD0UOj3XYh6lpkOK0oryZDLHeIeLnSZuJFIFJHLCmTImRSxiJAdpw/aCFNHG4Wj943QAxlyVOrebGg/E6sczgv8NsopYr7QZ2Ac1EcuO5AhZ1Jo8dAH9ZFLH4oiEEYxGAgBAFyOwtxi6F4yH1bMkbPKOPOREEkrDYdTsZOsUJi7rAUQiwgF5PRBkaXUCVFEjjCIAX8QAOAhQ47uJZOg/RysEiW1yjjzkXBEjj4DowhLK+k9NZLC3GUtAE10vSj6dvKmpYymFgBJHwhdDPjliJzHac/xSHIDGXLmI6IhuEU+E9q+coe4b0nVYxwkrcwOZMiZFDJA9BGWVtL7mCoSReQIg/AH5dOPo0Dbp4Qioj85HAihErG+WeRwTvtX7hCGP+2FxkHTOTuQIWdSwsVOCvMgpBex+NIinDokrSSMQsyfQl2/KCJnPoIW/ExImZM76AxhPDSfs4Mhhhxj7BzG2DbG2E7G2E0xfv49xthmxtgnjLG3GGPjjXjdfIbmuz7Epk0ezdTRJnXTu0boodDvOyp2Yj4kCxY7scgw85IQnSEMhwrHZAfdhhxjzA7gPgDnApgF4ErG2Kyop60FsJBzPhfA/wDcrfd18x2raPjNiqpvp/cxZSJKpltEekSYk3BELscDyRERhhwdXkyBFSNytH/lDpFSSbevcajFTshVbChGROQWAdjJOd/NOfcDeBzAxdoncM6Xcs4HlG+XA2gw4HXzGqtsNGZF1bfTKpwyJK0kjEIUCCBDjtQVZiGkSYyzyvpG+1fuEPOFPgPjsMp9ZzWMMOTGANiv+f6A8lg8rgbwaqwfMMauY4ytZoytbmtrM2Bo1oUWD31Qxan00RYAsOKCS/eMeSj0SAIVDjIf2r3AKvsCTZ3coUor6UMwDFWpQY21DGVYi50wxr4AYCGAe2L9nHN+P+d8Ied8YW1t7XAOzXSQLlsfQkZDh6jUiYzI5XAgGUIbrnkQRnWhbtgRzadpXpqCoAUjcjR3coeYLlaZK1bAiucKK+Aw4BoHAYzVfN+gPBYBY2wxgFsAnMI59xnwunkNTXh9iIMkVUlKnYhiJxZ830ISR4G2LTMd4aq7OR5IjiBppfmIbD9gjQ+FjIjcIYxoq8wVK6D2kaMcOUMxIiK3CsBUxthExpgLwBUAXtA+gTG2AMDfAVzEOW814DXznkIvFqCXIMki0sbqlfYoim0ewhKawiRkcadIPmKVhuASFcoxBWFVT44HkkeY+LazNLoNOc55EMANAF4HsAXAk5zzTYyxOxhjFylPuwdAKYCnGGPrGGMvxLkcoUCeOH2EaBFOm4gmxhb0mJn5cFZoFLpRbXWnSD4iWUQ6bnWJe74gUXqG4ZBjIjsYIa0E5/wVAK9EPfYTzdeLjXidQsIqydhmRTXkaOFIGW7xA0QoZMFB5ykhVVFQmDE5KnZiPoIWiXRRM3lzEFLTM3I8kDzCxLedpRnWYidE6pC0Uh+qvp1W4ZTROg/MfNCJB0XkzEO42ElhEqQcOdNhFQNJO7ZCj2znEsrnMh71PaW31FDIkDMpVjxImwm1dDBFNlPG6gUa6J4xD4V+30kWMRoKCe36ZmYDyerrcL6g5sgV+FpmJJwc7FmBDDmTQtEFfYRlEfQ+porV5WBBEx/OCg0rzh8joYic+bBMRM7i/TzzBcqRMx5xC9JbaixkyJkUOpPqg5p5po9VDjrxMLOXvdAIy5IKE6vfS/mIVQrQaPvd0f6VO8LpGTkeSB4hUUQuK5AhZ1IkCuvrgqpWpo/Vq6XR5mAewtLmwvxMrB7dzkeCVpFWaqsH09zJGaTqMR5x25GDwljIkDMpVKxDHyGSRaRNZI6Z9d43klaah0Jfv4IWif4UElbJW4yUVuZuHIUOnSGMR82RowCFoZAhZ1IoBK2PcKIyvX+pYhXpUTzoszYPkurNzvFAcoR2LpJH3xwELWLIaaMVZo4c5juk6jGecNVKelONhAw5kyKp0qQcD8SiUKJy+oQsLgcjuYZ5EOuWFeeREVBEznxElvXP4UCSYJXIYb5DznTjEfcd7dXGQoacSQlX96EJnwlBMoTTJkLSY5H3TXt/BKkhuGkQh59CjShQRM58WLEhOE2d3BEscFVBNggbxzkeSJ5BhpxJIX22PkIUwk8bKxZo0A7TKmMuBApdlmT1wkH5iFUqiZK00hz4g7I308xzxWpwOpdlBTLkTEqhe7T1Qu0H0ieyWloOB5IGEh16TEmhO1KCFJEzHRENwU38mVjF4MxnOOfwBkIA6DMwEjG1jd6qn1q9Hy+sP2TsRS2EI9cDIGITTgrN8UAsCOe84CMCmWDF3AzyXpuTQs9RlShHznSELCitLNT7J9cEQjxrRkchk40AhTcQwg//9wkA4KJ5ow27rpWgiJxJyUaxAM45vvDPFfjLOzsNu6YZ0a4RRnnDvYEQTrlnKV7Z0GTI9cxIRG5GDseRDtqP16jN4YlV+3DeH95DkBIsMyZbfeQ6+/1o7/MZes1skI0Kie/taMM3H/kYJ9+9lJwWGWCVqryRBmcOB1LA+IKh8DcmnitWI2wcG/emPrl6v2HXsipkyJkUYYAYKQHZ3d6P93e24+7Xthl2TTMSIaExaMfe3tKLvR0DuPu1rYZcz4xYMUcucszGXPNHT2/A5qYeNPd4jblgARLKkqLg1HuWYuEvlhh70SyQjej2F/+1Eq9saMa+zgHsae835JqFRFDicNnlI4+ZDWErrsP5hjcQtqDpMzAOnoV94Zk1B427mEUhQ86kZEMa+O62NuMuZmKyIU051CUf6t0OuyHXMyPaAJRV8nq094fRG+6dr+Sv0Z5teBY8rwDQ4w0CAA73+w29rtFkO9+0qXvQ+IvmOZLE4bQz+WuDPpTNh3pw8t1LcecrWwy5HkDSSjMg8uMAYz+DpdtacfF9HxSs2sPolg6+YAibD/Wo37f2FqbzlQw5kyIWcyMP1B39siRJeCXzlYiKcQatl83KwcntzN/3zoqV9ow+9HQPBtSvd7T26r5eoZINR5Q2yrWtxdyfjfacZsS8jM7pauoqzAOLHoISh9Mhr99GHSQ3HOzCvs4B/PP9PYZcD8iOyoBID19QG5Ez7ro3PrYW6/d3FazaQ7yXRkXEPznQDX9IwuKZIwEAu9sKU6mQv6dSi+NXTgJGSkA6FS+2PySha8DcHm09hELGezSblIWXGXI1cxIhB7PICULr6DDiXnlto5wDWeZ2oLnbG5krQaSMcAoYKQ1ftiOsKNAa3GYkso+c/uu1K064W8+fCQBoKdCDoB5CkgSnKq005po9g0HlesbN82AW9i8iPbQROSOd6UJR0NJj/jzfbGB0Eb8nV8n5cV86bjyA8Bm30CBDzqQEQqKHiXHX7OgLT/Km7vw9CERUMjRoxWhW3q/DA+Y+QOrBKsUAtEQWttF/vV5lo73h9Cno8QbR3leYG4NepCwoClbs6VS/7jb5fWh0npNYf8bXlKDEZc/rdShbhHhYjWKUgdTjDX8Og35jnD5UiTf3CAeew8ayshcWqiPGaMn9QCAEt8OG6fVlAIC1+w4bcl2rQYacSdF65Yw6DHX2+2G3yTEls+eY6CEoGS+LEIZvPnt8IvJ6LFKqy+jCNn0+2ZCrr/AAAAaU74n0yIa08q/v7FK/NntEzmiZslh/6ss9qCpx4XAeKyqyhRyRU3LkDJqYPZp5aNRnIkXkKhtySSJNfEqxkyKX3TCjQ2voN+exIz0RRufI9QwGMHNUOaqKXQCAf7xnnMTZSpAhZ1L8Gu2HEQfU1Y2dWL33sHqtVzbmbxl97UZo1IYtpKh9vqB62M83tA4DqxwguMGRj66BAErdDpR7nACAfoO87IWG0Ru29p6z2xi6Bs1tyERKK/W/B8LxVlPqQnWJK68dStkiJHG4lBw5w5QamsiKUYZcyOA1jUgfrxKRK3LaDXNGadesQo3IaXvzGbEudg8GUFnshMthA2PADCUyV2iQIWdSAiFjo0pvbG4BAHx24VgAwPNrD+m/qEmJjMgZswp3DwbgUQqdNOdpxTijCzQA8mK9urEza1Uwja5a2drrRV25G8UuuTopReQyI1ysyZgNe7tS3GTe2EpUFTtNLy0MRcxL/dcTf29VsQtVxRSRy4SQxOGwCWmlMdfceLAHk2pLAMCwlhCSwSoDIn1E+4Fil92wvUurIijcYifGOou7BwOoKJKdrucdMQr+YGFWAyVDzqQYnfA86A+h2GXHXZfOwfSRZVgwvkr3Nc1KNnK9ugYCmF5fDgBo7s7PRGWj5WCd/X5MvPkVXPa3jzD/jjf1XzAGRld4a+72or7cg2KXAwAwQBG5jDDckFEiUD+/eDYqi12ml4Yb3Ueua8APl8MGj9NGEbkMCUkcDjuDjRmj1HhjUzMOdg1ifHUxAOPSFUKSsYddQHYMT73lFTy2cp8xF8xzRI5ckcthmFPzpfVhFVShSiuNVtB09PlVWWVtmRu72/tNL7vPBmTImZSgwQcBbyCEiiInGGMYV1OM1jz2CEUYcgZs2N5ACL6ghEkjZM9rZ556w40+fG461K1+3T0YyEolTKNz5Fp6fBhZ7kGxW47I9fspIpcJRs8lIa0sdTtQXWx+Q0aO/hjXs+zwgB9VxfL6PaLUhbZen2V6PZqFoMRhYwx2GzNEWrl2fxcA4OoTJwEA+nzGOH20e79REtB3trUhEOL45cvG9bvLZ0RErshpM8wZ/OelOwEATjvDij3ZU6mYGSOd7CLNReSzz22oAAD85Z2d+i5sQciQMylGRxq8QQkep3w4HVnuzuvQvtG9xURC+4Qa2ZAzezQgUyI9wfrft492dUR8395nfCRTO0y9n7UkcbT2ejGy3IMSJSLXb9DhrNAw+h5sVcp1Vxa7UFXiNL20MMS5WljKCA4PBFTPc31FEXxBCV0ml5eaDYnLxjVjzBjjut+PEaVunDClBowBAwY5fYyueLq7rQ/XPrQaAMDyuX+OgYj2A0UGSSu162FAkSvs7RjQfV2rYWQqhAhGjCx3AwAWjq8GAPz93d26rmtFyJAzKUYfhLxKmVZArnzWNRCI6JWSTwgvplGe1y5hyI2QJTRmjwZkSsQBwgCpebSz4ECX8bmFRh569h8eQCDEUV/uRqlHGHIUkcuEkMFzaWVjJ8bXFKO6RBT7MLcRI0lc7VlmSKP6ATmpHwBGKR7oQ3maq5stgiEOm43Bzpgh6oB3trVhTKUHjDGUuByGFcEyOjVgW3Ov+rVov0AkRjQEL3I6DJG3Nin36lXHjcffvnAUgPxV9iTCSKWG6MU3skxeD8dWFwEAakpcuq5rRQy5qxlj5zDGtjHGdjLGborx85MZY2sYY0HG2GVGvGa+kw15oIjI1ZXLE/9gFg7WZkC8dw4bMyyhFgCqS1yoKDJ/NCBTIiJyBlwv2uA9eDjLhpxOg2HDQVkKWl9RhGKnHYwBvV5zGwxmxcgNm3OOFbs7cNykGgBQi32YuWm9pInIGWHIytLKcC4IENkXlEiOkLvaDegN5g2E0NzjRbVyaCxx2zFgmLRSU6zLgDmu7RkrHFREYrQROSMcMZsP9QAALpo/WnXEdBbg/Rs00EnR2ivP6zolIscYw6VHNqjn3EJCtyHHGLMDuA/AuQBmAbiSMTYr6mn7AHwZwKN6X69QiJSM6b+eLyCpVRfHVsmRpW8/tlb/hU2I2AdddpsheVOi+XBFkRPVJS7TV8zLFKPLXnf2+3HytFps+OlZAIBDWY7I6Y2+CsnskeMrYbMxlLoc6KWIXEYYGSnt94fQ4w2q1QHrKzwISRzt/eYtOhTiCPcsMyRHLoBKxZATBl2+OpSyhZC7MqY/n1Y0Aj99Rh0AGBqRCwSNXYd3tPah2GXHdSdPQlO319QOELPgDUhw2W2GNQQX9+rIco9q/H+wq13/hS2GkUqzdsUQHlHqVh+rLHaqraIKCSMicosA7OSc7+ac+wE8DuBi7RM4542c808AFGZt0Aww+lDtDYYjcgvGVQIIb0b5hni/HHZjciGEtLKySJZ1NeVpJNPo3lcdfX7UlLhQ5nGizOPIkiEX/lrvmDsUQ04clEs9DvR5yZDLBCOrVu5TcknKPEJaKEtozFz5TdKUutd7K3HO0aUUOwGgHgTzNVc3W4Qk2ZCTI3L6PhSxLog52VBdbFj7Ab/BrYeW7+7A0ROqMba6GP6ghLYs5CrnG75gCG6n3JvMKEcMIO8tYyrl9asQZftGKs06+31w2Jja8xUAKouc6PeHCq4NgRGG3BgA+zXfH1AeI3QgGSytHPSHUKQYch6nHfPGVmLSiFLd1zUj4Rw5m6HSyooiJ0aUurB67+GIPn/5QmQfOf3XOzzgVw+dYyqLcLDL+IN3ZNVKfdfq7Pejstip5jaVuo3zshcaRq5fv3xlMwCgTJGFicjc+gPdcX8n14hS94D+g2CfL4igxNUcObn6MNCZp8qAbBEIKdJKA4qdCNm4mrdY7kGHQRHiiB6yOu+d1Y2d2NPejyl1paimSG7KeANycTgbMyY94/CAHy67DcUuO2w2hhn1ZabP880GRuZ/dvT5UVXigk1TVErcj4XWgsBUma+MsesYY6sZY6vb2tpyPZycYnRPr9Zen5pbAQAlLrthVbbMhtj8XHZmkLTSD8bkg+TMUaKXnHmjAZlipBzOGwhhwB9SDTlfUMKSLS1qfx6jMEqqwTnHQx/tjagEWOohQy5TjJTQiJ5+i2eOBABMGlGCEaVurN13WNd1s0mIa4ud6LtWW69sIAgJkd3GUFnkpIhcmnQP+FGutODR6/QRhRZE6fOKYqdhVUSN7CG7RSl0ctlRDWrj5G5yACTFpxSHsxkUkWvvlZ2aTCkbWlPqMszwtxIR+Z8GKGiiC5uUF5EhlykHAYzVfN+gPJY2nPP7OecLOecLa2trDRiadTHyIDTgD6J7MKBuOgBQ7LLnbWl1tdiJ3WZMxbjBAMo9TthsDEcpjdSzIRPMNUa2vBAyRbHQCtnRit2d+i4chVH3SXuMxPMyjxO9JK3MCCPnkjcQwvyxlao0nDGGOWPKsfGgeSNykoF95ITTSEhKAaCKmoKnBedcPfjZbfojXS2i9LlSMa+iyAlfUDKkErSR0srm7kHYbQzTRpahyCXfP4N5Wq3aSHzBcETOiDPE/sMDGKc0jgfk+bJ2X5chjmYrESG51/m3d/T5UFMaaciJPOLuwcJaG40w5FYBmMoYm8gYcwG4AsALBly3oNGuHXpv9vBBQGvIOfI2IieimU6DcuS6BwOqN1Po281Y8XPAH8TejnCexqA/hLtf25ry4cLIPnKiIpeIyD301UUAwvmGRhE0SMIn7oWvnjBRfayMpJUZY6QjqscbVD2tgvoKD7a39GHp1lZd184W2j5yelegphjrd325Ry1pTiRnwB+CLyihusRtiLSypdcLl92mSrmMlHRFSCt1jnPJ5laMqvDAbmNqakW+th0yErnKt03pOaj/ep39fowoCxsdYxWjrqPA8hVDknFOis5+P6pL3BGPVSr7RKH12NRtyHHOgwBuAPA6gC0AnuScb2KM3cEYuwgAGGNHM8YOALgcwN8ZY5v0vm6+E9J4dPXaIuIgUF8e9uiWuO3o9+fngi7WCqfdZsgi3KUx5EYLQy4LpfT1ct1DH+OUe95RjbAHPmzEX97ZhT+9vSMlKahWzqt3zgnZiPCYHTGmAoDcnNZIInLkdIxZRKcXTaxSHyt1U7GTTDGyWFPvYADlUWXTjxwnf06PrNir69rZQuJQc+T0OkVEP0atokLOOTXfGmRWOjUKAcb09xdtU1IVhFSuwsADpFE5cpxz7GrrQ73SbogicqnjDYbgdthhY8YU/ursD7cPAcLrV2tvYRlyRsqGRTE1LUbeh1bCkBw5zvkrnPNpnPPJnPNfKo/9hHP+gvL1Ks55A+e8hHNewzmfbcTr5jORORb6Jnwsj25FkQtdA/68DO2HI3IGtR8YDDfj9TjtqCx24qVPmnRf12je3ymXMxbSHDFv7lu6C8fe+VbS3zey95cwHGtLw9IjAPj9kh26rhuNVnOvZ8MVETmRjwVQjpweIueSvmt1DwbU6oCCyxfKav6xGrmSmdBWrdQrITrUNYiqYmdEf6QxVUVo7fUVXHW2THl9UzMAwO20yVUrdX4m2gbtgFzRGDAmIhcMcbgc+vMrDw8EEJQ4zpszCoCcTgEAg36aM8kQ7ZpsBkTkQpJcdVZrdIxUjOt3tplTUZAtjFJqhCSOXl9QPVcIqNgJYSokA6ue7WyVoyBaj25DVRECIa42VcwnxCZtVPuB7sFAhLSrrsyNgYB5D/heZaN22SNv7+fWJk5dNbJqZWPHAJx2hoYqOYJp11SWMpLIqpWZD1pEp0vc4cNyuceJPl+QDssZEBGR0/G5BEISOgf8EYWaBA1VRab1vIYkrukjp+9azd1e1Gvy4wBgdEUROA/nahGJ2a3k6M4eXa4UsNB3Pa3cHtBGAvTn5vhDEtzK2q0ncijyuEdXyvu+cARQRC453mAIHofdkPYDhwf8kHg4zQAAjhgtF03b0z6g69pWIzIVIvPrCKVMtOS+zCNX9DU6hcPskCFnUoyMyL27vQ0ep22IRxcwp0RQL2ofOQM8r4DifdUsGGfMHIlmEzZWVVQ+6kYtDpKC7zyxDo0Jeh0ZWbWyxysfdLSlgb9w7LiIzcwIggZFfgZ8QyNywgglCVv6SBIgPno9c6mlxwvOgdEaJ5Sgqthl2uavEtdE5AxQVNSXRxqy4gCTr71AjWZEqRuMAZNGlMJm0y+t7BqMisgpXxtxgAyEJDiViol6VAZCFSGcACJHbjBPc+ONZNAfMqz9QKtS4VRE4QC5ENtR46vysmhaIvxBSXXq6lkXxbpXFiW5tyt95bpNui9kCzLkTIqRHt1BfxBzlBwlQbinTP4dBERkxogcOc75EO/r6EoRzTSXvl2YTE+t3g/OOZyOobf3qb95J+7vG1nspCcqigkApW4nOvv9hjZCDRmkuVcjchpDThidZjUWzIzcR02/PEytDhjDkKssdpq2l5q2j5zeg2Bn/9CIZKlbnqf5WnnYaPq8QZS4HLApfeT0rG+cc3T2+yP2BNWwNsKQC8p7v43pa5/T1BOZUuG0y83QB/I0N94ovIEQdrX1gzEYEpETqqe6KGdMfYUHH+3u0HVtqxEISfA49Du4hCFXHiW5B+ToOEkrCVPAOQzx6HLO0dTtxfyxlRGP57OWWI3IGdB+oN8fQlDiEZt2g1q50lyyCLHn//bN7Xh9U7Ma0U0VI3sX9niDQxbZKXVyA/onV+/Xd3ENRletLNZKK4vkw3IPFTxJmxDncBrgeRXrU2XR0A17VIXHtIoCIxUVXYP+IbkgQgLc58u/9Tsb9PuCqvGr10DadKgHnf1+zBlTqT5Wply7w4CWEAFJgtNug82mLz/rwOEBOGxM7T/IGEN1iQsdMVqtEGE+3CXnmrf2+uQcOZ2boXD41pVFOqPEmra/01zniGwSCElwK5FhPW+raAsUXQQLkM+2JK0kTIHWo6tn09lwsBu+oISGqsiiABV53DhR5Hq5DMiRUw+SGhnNKCXnYNn2dl3XziaHBwIRXueTpo4AAJw8LX5/RiOLnUTnFQLApUeOgdPODG2mHlm1UkdEzjc0IicM0Xy8R7IN15bf1/G59MbJhQDkvmrtfT5TFmySJK6REGV+HV8wBG9AGmLICUlRH0XkUqLPF0Sp8p7ZbPoagoviYUeMKVcfs9kYyj0OtaiKHgIhDpddn7Ty8/9cjr+/uxtBzTwEqNppKoiI5Q/Png6HnSGgc31pUwy56Kj6+XPlIjTXPrRa1/WtRCDE4TYgIif2hegiWIB8tjVr7nS2IEPOpGg9unrO1FubegEAcxoipZUiKTQftcTiQO+w6ZdWdisLgvYgNWmEHFnaZ3JPWkAjO7QxhiPGlKstLWIhRRy+9b12R59vSGlg4RE+bOCcCxrUl6bPF4CNAR5neEmsMFAuVWiEpHDlPT2HZvHeR+dCAOZWFUgc4fYxOjrJib+tojjyXipRpZUULU6FXk1Ezm7T51x4YtU+AIgoJw8Ak+tKDSmMFAhKcOiUVn6wU5bsnTo90nE3prIIBw6be9/KNd6A/BmOLPPAabMhqGcBgywPL/c4ImoUAMC0kWUAgK3NvbqubyX8IUl9H3RJK5V1UahmtFQWu0y5J2QTMuRMiqTpI6fLc6Fs9JMV40NgtzFUFDnRmYeGnIgsOR362w90DcrvjzYi4HLYcOr0Wqw/0KXr2tlG24/IbmPwOOwJm8GGJM3hU8eckySOlh5vRLsLQVWxy9C8zIi+NHrySbq9GFnuUftCAUBFsRM2BkMjiIVCSDKm2EePKqEZ6nmtUvN8zbeGBUOSasjqcYrsapWLE42MkyNHfQ5To88biJRW6vhQlmyRS8bXR61vC8ZWGdZHzmm3KY3L9V3rl5+aE/F9XbkbjR0DOGyABDRfEXukx2lT8+z1nCO2NfcOUUQBcgGeBeMqcfzkmoyvbSU457K0UkTkdNjHW5t7YLexmMXTKoocZMgR5kAbkdOziPQqSaHasuqCEaXuvNTLq8VODDBKhOcnWto0f2wldrf1wxc0r7QpGBWRc9ptEY9FI0Xk9WT+uu39PgRCPKYhV1nsNLR4iFF9aQ50DmJs1GbrdtgxpqoI+8mDnTbahth6JTQuu22INxvQVAo0oSHX7w+phoMeB8POVtlbP7ehMuJxIQGmPoep0e8LGZYjN7m2BACG5CCLvpN6c6oCkrwO6ym0IYyDMZWRbSsmjpDHTvLK+AhDzu20q2tYQEdUbmdrH+aNrYj5s3KPs2Ci6kGJg3MYIq1cs68LR42riimtrFR6JJutqng2IUPOhHAuT3gjqla29/lQVexUK8hpqczT6j7hYif6cwzDOXKRnh8hGzTr+8cgJ80L7Db5/QgmcINp8zL1LLLRZa+1VJe40GmgNzhggLTSH5Sw7kAXZo0uH/KziiKnqscnUicoSWofQz0RqR5vIKZ8BtBE5PrNdQ9yztHvC6qHDD3r977OAbgdNtRFReRsNoZil71gDoF60ebI2W36SsrXlLpxzMTqIY+Lhttr9h3O/OKQpZUutdhJZgMtdjkwc9TQ9WxKrazMIQdAfHyKPFaOyMn7YTDDm3jAH0RHvz9mRA6QI+uF8lkIY9jt0C+tbOnxqv0Ro6ksdkLiQF8BtdkgQ86EiDXDoR6E9Ex4X0T/Ei3lRc687EOkrVopf5/5tYRUJrpqnshZMXP+lDb65rDZYLcl9kSHInpfZf66ohhA7Iicy9BEZCMici9vOAR/UMKCcZVDflbuyU9nRzbhnMMfDOdC6FMUBGN6XQHzSiv9IQlBiat5fXoOLG9ubsHY6uKIfoyCQjoE6qU3Qlqpb04GNLJZLefPkYtXbGvJPOepzxfER7s7sKutT5FWZjbOeGMUDslCbyS/r2MAd726FR19Q1sIeQMhMAa47DY16hrIMPdRVNUVPUmjKXHbC+YeDgTluex26jtjcM7RmuRcC4TrGxQCZMiZELHJiHwlPZtOa48XdXEmfEWREz2D+beICBWEUeXPHYr3W4uQWpq1OlJQ4hFJ2jYbg8PGEnoWJU3vQj3OgyZFthM7R04uDay3T53AiBy559cdAgAcPWGol73M48CeBE3UiaEEJQ6JhyMUeu6/fZ0DQ6JRgppS+VDanqY8vKXHixN//TYas/S5hiug2sF0VB4UrWNEYZNoyJBLDc55hNRVb45cIBSONmsZVeGBjQEtOnJqX/mkCYDcxoDpyJGTxzjU+J9eX4YyjwMr9nRmPMZ84J43tuFv7+7CW0q+o5ZBfwgehx2MMdUZHMgwoeuAasjFjsiVuB0F0wvSb1BErnswAH9IGlIFVFCZxxXZ40GG3DDxx7d24P5lu1J6rpjgRuQrtfT4hiTKC8o9jryMyIWGvH+Zv4H7Dw+iusQVUQQDkA0SAIbKBPXwzJoDEd/7gxL8GiPHzmRJUaIcuVBEyXR9jWhddlvMROSqYhdCEjesN5twcjCWefuBroEATp5Wi9GVQ72mb25uQWe/nw7MaSCkSUUuff2CBv0hbDrYjSPHV8X8eYnbgYoiJw6lme/z4vpDOHB4EKf+5h386pUtmQ0uAULuWOJ2gCHzv7/HG4QvKOFCpUx5NKUeB0krU8AfkhCSuDofbTobgssNu4cenRx2G2rL3Hh5Q1PG127vlyNE/7pqIWwsc+eUPyjFHKPdxjC2qrjgCziJvFpRzEyLNxhSqxfrdaZvae4BAIyrjm3Ilbkd6PcHTdlCxWj2dsiOM7FmZXoPtvTI90i8iJyIOpvVyZ4NyJAbJu59czt+9crWlJ6rFuvQGR0Z8AfR2uvFmDhh/fIiJ3oGA3mXFCr+HiOklY3t/TFzDURhDLO0IPjek+sjvveHpBgROVvCHDltaWA9AbPmbi/qKzxDjF9Au8gaYwALT6nLbst4zAP+IIpjFNMAgC8cOx5AuBcQkZxw1TcxlzL7YNYf6EJQ4lgYx5ADgNGVRWkbctoWHPcv253R2BKxt0NeE0rdDtloyLD9gPi7oqsjCkpcFJFLBdGGRUTRkknMk19PgjOGbBEAakrcunJqewaDcNoZTp9RB7uOHDlR+TIWdeVutPYWtiEn8rViRW18gfA+aGf6DLnluzsxqbYkbvSotswNzhFT4plvPLfuIADgQJe8PmZaP+ZQt7wuxjfkFLVUDCM9XyFDzoRo+6Bpv0+XLU29kDgwe3T8ikkSB/rzLClULLouA4qddPb7VQmXlspiJ+w2Zrr8HIEvIEXIKB02Boc98QHm8EAA1YqhpTdHLt7hs7rE2EhmSBzSdLSaGPCHhkhnBWfMHAkA6OzP/43WKAb9YWkhkPn99/FeuWjEkePiG3JjKj1pVeB7fOU+PPXxgeRP1MEX/70CgByRtOmQx537h/cAAFPrymL+vMTtyJuG4NtberGztS8r1xYOLVHISS4ikvn1/CFJdbJGc84R9Wjt9SVs85IIbyCEIqcs65Ora2Y6Rh4zRw4A6srcalSjUOlWUkpiRW18mvxekZuaibLSH5SwbHsbJimVQmMhjJFC+DzEefYnF8wGkLnq5yv/WQUAGFke2zg2e9pLNiBDbphJZYEPR5T0Va3cdKgbAHDEmKERJSDcTDETLXFrjxcTbnoZT63en9ngskh0sRM9OSoHuwZj9rBijKGyyGloTzQj8YdCQ/rI2ZPkyHUN+FU5ZKaLrDcQwso9nTHz4wDjZQ9BKextz3TMg/4QimO05wCgGrb52KYjWwi5dqVOp8DHew9jcm0JqmJIdAWjK4uwtbk35Xv8pmc2YNOhnswGlCJiKMdOqsm4hLx2TZ5eH9uQK8sjaeVZv1uGxfe+m5Vr+1VDTt4PbDrK+gPxc+SAsIQu06bb2khapvmV/qCELU09ccdYV+ZBR5+vIOR8sfjPB3uwpUleA7pinH28gZBaIl+8hZk405uUyNH8sZVxnxM25PI/Qvr6pmYcPaFKVxEo7f1QVxan2IlyXiukatNkyA0D2s32nW1tSZ8fllbqy/FasbsTdWVu1McJQYsbIRO9vJAUPrpyX0ZjyyZGtR9YqSSE72qL7Sk2uieakfiD0pA+co4kkqKugYB6aM7kAME5x83PbAAwtO+VwOhKgyKvT0+pbjkiF7ugRJ3i9aO+S6kTbtkhb6iZzCVJ4vh472EsHD+0AI0WkdiebeMsEzxOu5KPlf7vLt/dkfQ5hVTxTg9BVVop7wd2nX3kAqHYOXIAMKVOLu+/4WB3RtcOhsItYDKVVt71qpzCEa96Zl25G1KByPli8bMXN6tfd8Zw0PmCEtzOcD4lkNkZYu2+LgDAqdPr4j5HGHLNeW7I9fmCaOr2otzjVN/TTNbFLU3ynB5R6lJzXqMRbSPysf5DPMiQGwa0h9ZUtOkiyqA2Tsxw09nV1oe5DZUxc5UAYJTSh6M1g/wfcRMJGZWZCFet1JcjJ6rh3XDalJg/ryp2ma6HlWAwEMLqveHKZHJEzqbmi0TT5wuiezCgyhUyec/e2tKKZ9fKOvivnjAh5nNEkRijIpkBSZL/NsYykr8M+kMYDITiRjbqytwodtnV6mOFDuccv3l9G/YnyA0V0UuRF5LJXNrV1ofuwQCOmhBfVgkAZ82uV59vFhgDvn7KZADIuGDFPiXPbsWPz4j7nFK3kwy5FBCGnJB26ZZWxikkAoQNuaYMi4kEJCk8TsaQoDZVXP79wR4A8R0owoG71yT53cNJ9HsSS+Kvjcip6S0ZTJjvPLEOADBtZOyIOhCuvGuWomnZok+Jjp0+sw4iRTmT91T0aPzPlxfFfQ5jDOUep6lbQxkNGXLDgDcQPmH+5PlNWL+/K+HzhSROaNwz3XSae7xxJW4A1P5MvRl4LsQCl2kuQDYJKSd6p87m1kIaMSNGsRNAjjiYNUfuyVUHsKstXF7dbhMRudjWzi4lP2VGvfy3ZvKeCfnJeXPq4zoPZI+cccVOQiEOp43JvaEyGPOKPXLkI54MgzGWVxI2vRw4PIg/L92JrzywKu5zRGEYoQTI5HMR+XGJCp0AwIz6MpS5HVjdeDjt18gGnHNwHl67M82R+3jvYYyrLo7begEASt12uTpthj2uCgV/dI6cjmqQ3YMB9PmCcDpir29uhxwNyFTWJUf7NONM897Ryum/s3hazOccN6kGDhvDO9uGlt7Pd/qjHM+xHCG+oDRUWpnmfNGei+LlKgKy6qrEZc/7fK4BpQ5DicsRzjvMYF+49bmNAMJqj3hUFBVW/1cy5IaBaGPn4vs+SCg3CkZV2cpkwg/6Q+gaCMQtOgFA7auTyaYjFjYzyuzVKmU6+5U0dXtR7LKj3BNbdmd0c+tM6PcFcco9S4c87o/KkrcxudhJvBw5IZUdXyPneOj5XL95auwIJiB7wyuLXYZ5IIM6pZWil9j3zox96AHkzefNzS0Zj3FVYyfe2pL575sJcd/vbO2Le7hp6/PBaWeqTDeTz2XFnk5Ul7gwMUGhAEDOe5o1uhwbD2UmZQPk1jCHDZyPQLiHZSY5coGQhNc2NWPmqLK4DhEgvH6TkyExQU1lW0CpWpnBnNze0ot5P3sDAOCOE5HTGw0IanLkMmmTIJwot54/ExfOGx3zORXFTtRXeNRm1YWE1oHocdrQ5wtiS1MPHlmxV33cGwiFi52wzIwOkYP360vnJH1uZbHL8hUWNxzoxpMJ6iUMKAa0KAAF6KuMHa9ipaCsyGlYiyMrQIbcMOALDo1afXIg/sFDLeCgQ1oppEaJInLiIJDJhBcLm56k8WwRVOR2DtXzk9l1EpXRB2TZXXuOksZ7vQHsbuvDS58cUsudJyIk8YQ5csKQG1ddDMaQ0SorIgOiB0885NxCYwzgkMThsNvkyEcGn8N7O9oxstyNCQkMhjKPQ+2vlwmX/+0jXP3g6ox/30z4NNGfeMb4X9/ZhUCIq6W7M8mRW7KlBcdOqk5oyAiOGFOBLU09Ee020uHeN7fjh//7JKPfjUaV8akFK9I/jG9V8kAm1ZYmfJ5oFE7ySjnfK546JBCM/EzkKGn6c/Ks3y1Tv770qIa4zyvzOHRF5LTjTHdvEWkS42sSO0BGVXgyln9aGe2+U1nkQr8viG8/tha3PLsRryj9//wREbnMcuS+qigWTpsRPz9OUFFkfRnghX9+H/+XYA0dUCsZOzKWVornT6krTRjlBJQeyRZ/T9OBDLlhQCutFHzhXyviPl/1IOqQVv757Z0AgFmjY8sCAXmRKnU7VP1yOghj04R2HIKKPEWcATOV0TR1D2J0RewefIC8GQYljvYcJI1f+9BqnP7bd/Gjp+XiIp8/ZlzC5wdCEuw2W9yI3KGuQVQVO1Gi9L7K5C0Th3yXPXYSsqCq2GWYJFUY7fYM8l4453hraysaqmI3axUcP2UEOvv9eddvMRO0h+VkvfVUb3aa9lVT9yB6vcG4VcmimTayFN6ApOtgKprV6kVEwrXyuHRnzValifBJU0ckfF4pGXIqR/1iiVqWPBrRazIsrQw7ff79/h784Kn1MX9Pi1a+esHcUQkNpfIiZ8aFFoJSuLVBJrl8okjO1LrEToC6ck9B9sbUGnJHTahCUOLYoaQVfPORNQCiInLCkEvzoCNywGtL40ujBUY6NnNNII4zTUgrtRG5dJ0p4px65aLEZx1ANo7NWoguG5AhNwzE8hT2eoNxPcjCq+vWIQ3c096PybUlas5TPGTvYfqLSFhaKf8/4I/8e7a39GJVY2fM3802gRCH02ZTvWnJ3r+P93bii/9aoS5CksRx3UOrsWZfV0Jp6ijFyEvnALn5UA+eXXtAl5SLc47lu8Pv7Ys3nJjUex8MyRG5eHNuMBBCqSIhzbQ8tzjsuJNE5KqKnYZIKznneGzlfrT1+sAyyJHrUMZw7KTElRFHlLoRlHhBVcGKh9ZoaIvjwLDbGM6ePVJ1pKT6ufiCIfxj2W5crxyoTp1em9LviaIqIq8uE3YY1MMsqBpymUd/xKHuiDEVCZ9XoxwSc+FIMhPCKPsoTqXP6FQFrbTyjpc2438p9BUU0ZoyjwN3fjqxXC7diFwwJKlnBLFOA4oTII25I0lcrViZSGEAyGuwWfO7s4mQMP7180diQYy2AFuaeiJz5FRnVHr38OzR5RhfU5ySoqCy2BmzDYJV0EbW4uWliYhccYQhl97riP23LE6qi5aR5R609Pgybj1lNciQGwZiReSAsJwtmqHFTtKbjP6ghF1tfWpFt0SUujOTgYibt6nbi5+/tBmzfvK6Gh0CZBnK5X/7KO3rGkFQkuCws5RLB3//yfV4b0e7KlE82DWIN5ScqETNiIWR19ydeq7B1Q+uwnefWI8FP38T3QOBjGSZ2sa5J00dgTkNFUmLzohcMonH3pTkzUt2HLCMI3LyGOL1LxKMrynB7vZ+3REu7f1jz0DCJspDnzw1scEwQqkslsmBeenWcEGBrz6wyrDIT67Y1hwuaR7Po1/ismNURZHqSEn1c1ndeBi/fGUL1iifSzJDRjBPaXXRqPO9NWLTFxFvEf3J5F46POCHw8ZQ5k58YBGy+UKTyAVDUsS62atxLsS6v8R+GjaQhla4TbZ+PvBhIwDgpW+dqBYJi0eZ25mWc/Sah1Zjxm2vqWPNVFqZjjFQWeRC92Cg4FQGwkly5PiqCCe3mBt3vLg5IiKXqbSSMWByEueqoKLIZenokcgHBIDWOI3NI6SVyvEg3fVWGHKx+vpGM6rCg8FACD2DhaFWIENuGIjeJC5X9PVvb41dNcoXlWuUriH3zJoDCEocM+I0ktVS5nGg15d5RA4A/vW+XO746TVDPZv3L9uV9rX1IvIMbCnmyJUrvaiEN+m3b2wDADx27bH4XALJYiYHKe1z593xBmb+5LW0N1MRzXrkmmPw0FflMrzJZFhayU6sCIkvEG5ym64nWJBqRG50ZRH8QUl3VSkhpbrz03Myyie5/lE58pMsaj1CiXx8tDu9CHNbry+iuuPbW1vxi5e3pHUNs/Hk6v0oVlqPxDPkAiEOl8OWtudV5DQ8d/0JeOabx6vvezJqSt2oKnbqbtpuRIsJYTQ4bZnfS395ZxeCEk/qzRcJ/01d1jPk/vtRI376wqaMfvfoXy7Bp//6ofr9oyvCvUxjfYaBFBqCz7jttbjr0YrdHVi3vwsnThmRNPcMAMqLHGmtbaK3rCRxBCWesbRSRNh+/9n5SZ9bWeyExFFwKgPxuVQUOXGiZs/8ygkT8KkFY/DR7g70+8PtB1RncAr38BubmnHpXz/EZ/72ETYe7EmaKy6oLZWLf1m1Qbs2F+28P74X8+8YjCGtTFdBI4yyeMXntKhqqZ7CKOhDhtwwMKAYcjecNgWMAT8+byaA+E2GRYSsskip+pZmjskKpZH1iVMSH+4B+RDU3pv+ASjVRefv7+4e9vB2MCSpJemB5Iaw8PAIA2m1ItGaH0N6oaW6xAWXw5ayIffJga4hj/mDEn7x8paIKFsyhP6+stipHvbmNlTiw5tOj/s7IkcOiP3Z+YIh1QBjyKwYwPYW+W9IFpETEa4OnfJKcWg7Z3Z92oeeHS29quFZkaSU8dwGOTK0VeN5TIVY1QR9Fi8V39rrw4SaEpS47AkMOdlpkE5SeyAk4fl1hwDI8yNRJDwWpZ70Ds+xeG9He8a/u6qxE8+tPaiqL0R5+ljRn0TEyzGJhcthw4hSN5oteFi57flNeODDRjV3Jh0ODwQiWvj8+rWt6tfrY6yxYp3QSitjrW/b4zTQFtLLi+bHrgIZTaalz3u8ATkiZ4tvcCZCRHWSlWYH5DxlIHk/z6VbW/GPZbtTHoPZ6Rrwo8hpVyNuoxVnrM3G8Okjx6jPEz8XkfVk97AvGMKTq/djS1MPViopJeIayagtUxq091tTIt0btc81xVAo9RsgrRRRbuF4T0R9gakVyJAbBpq7B2G3MXxn8VTsufN8VClltWMdhCSJY6nS36VCmbDpeC6CIUltylyTgkd7TGURDsUxKBMRb0ziJj5uUg0A+bD+42c3pn19PQwEQvC47GF9e5L3T7zPbb0+BENy0YRvnT5FbXoeD8YY6stTr/510Z8/AAD8+8sLcd/njlQf//cHe7D43nfR0pPadcSGLTZjgS2BBz+gyb2IVfBEmxcgRxFSGkoEIiLrSGLI1ZTI87JDZ27PWbNHYtKIElSVuNLuDXWmUoHuxjOmJn1umceJURWetA7ZAOCNUa3WZ8K+i6nS4w2gs9+P8+eOQm2ZO2aOXDiqYFOdDKkcRp9YtR+vbWoGEJ4f6VDmdqq/H49kDqXW3vD9J0kcv3hpc0pSWEniuPxvH+E7T6zD4nvfBYCMDuN9viCOu/MtAKnNS8Ca1Qe1B72NByOdI8mM/mSG392vbRvymOg9JVIVGGMQt3JlsVNVM9ygROi19HgDeEox5D6zcGzC1xZUFrvgDUhp91jddKhH6SOXWX7lv99vBJDaQbeqRH5OMknfVx5YhV++siVvco26BgIRhu7dl80DABwzsRonTa3FmEo5kiNyXcWeGkxgyW1p6sHsn7yOJVtasWhiON9apCokQ+T4xpMlmp39USlCr20cug6r7QecdtXBl760Ur73U8mRU9VSFlQrZIIhhhxj7BzG2DbG2E7G2E0xfu5mjD2h/HwFY2yCEa9rBQb8Qby6sRmjKz0RB9zaMndMQ+6pj/erUhFhYKQz4UXeUDKpnWB0pQe9vmDaEot4h+anlU0vKEmqBOu9HW1pXVsvg/4Qil129SAZ73Dw3+V7ce+b2/Gyksje2uvF65taEJI4xlYnrmQoqC5JX99+8tRanDenHvdcNhc1JWFj7N8f7Enps9ZG5LTYEtzNLnu4+EsolDhHLpOqlekYZTUGReR6vUF1UY/nZY/F2n3hohiXHhm/jLgWu42pRRNSJVZEzsrHIbEpjq8pRm2ZG+0x1i9RIdCpmW/JPhbOOZ5fJzufrlw0NqkDJRaTakvgD0oJD/rxKrYC8uerXY/3Hx7AP9/fg1PueSdpw+2+GK/pzCBH7o1NzWjv8+O8OfX46gkTU/qd+gqP5Q4r2ubtS6OaUic6MAPhqoyC6H1oToK8ysm1sizSbtMU6fKFMHt0BU6YUoOWHh/2aVq5tPf58Pd35dSAxTNHJhyXFmFIpVtMZOWeTqWPnDx37GlGc8XrpZJSUak4AVOtlmh1JYGgazCgnqsA4MSpI7DpZ2fj9Bny5/uLTx2BYydVq+0lUimY9r+P5VSWb58xFbdfOBsTlF6s3Sn2hhOG3J52a+ZPv721FSPL3WoaSqx2Vr3eAMrccjPwTKpW7m7rU/vUpZIjV1fmho2lV7/Ayug25BhjdgD3ATgXwCwAVzLGZkU97WoAhznnUwD8DsCv9b6uVXjgw0bsbuvH/LGRUqF4Hu1Bf9iLJxacdA7VQqKXqMGxltGKByrd5qDxDkW/eWM7Ft/7LvxBCQsnVOPqEyca1vw5VQb8QRRr+pXEWi8457jtuY3441s71McOHh5U86amj0y+GQKpy2h2KLKdr50yCQ4lWnH5wrF447snq8/5+7u7sWRL7LxJLV0DfrgcNhSlKN0AZG+0kIkEYpwOfIFwXkAmTYyFvOnyBP2VBMJ41W/IBdRKm4wxpGpnfV8pN/6by+dhXE1qBrvTbkMgTeu2zxfDI29hS05EGIqc9rjrl19tQWFLWdr89tZWrGo8jPE1xbjz03MzGttp0+V+TSLfKBaJIqpjKosiDLmAZjJtTiKpjdWvSETkGAN4ih/6r16R8ydvPndmUrmvYHSFJ6aUycysbuxUnXxapwqApM6S59YeUr/e1tyLdo0czeWwxXyv68rcuPyoBtWRalciXcGQBH9Idjjeer58ZHlOcSgAwK9f3Yr7lu6C085w+4XRR5r4CAnYU6uTV8MEwm0kPt57GEGJa3oQpqfG6RoIYNHEahS7kkcswtLK1NbgTYd6LJvDpaW524u6qGbSJZqiQqdNr8Pj1x2n5kLaVWdw/GuKGgE3njEVE0eU4PYLZwNARGXpRAincapVvrc195oqQvrx3sOoLnHjV5+aE7d/W89gUHVw2FJ4T6P55iNrsFJJGUol4uyw21BXZj21QqYYEZFbBGAn53w359wP4HEAF0c952IADypf/w/AGSyVuqwWZ9AfUqUe91wWeUCpLXWjLUYofURZWFbkccXPaYqHKKYwOUkfGYEw5FbEKd0cj0Rj2tnah/UHuuGyM9SVuTHgD+GDnZnnnwgOHB5IKlcRpfn7fcGE3jQR/tcWM9l0SD6wXX/aZMxLkh8nSLUHjMjTODPKs1tT6sb6n5yFpT84FQBi5so9v+4gTrjrbezvHEBztxd/X7YbUoxiCLEOQTeeMRUz6stww+lTElbg8gcluDW9c9LZKLyBEJ5UDi13X5b8IF4lDDmd0so+XxBlbnlRt6dRVKK1x4f6cg8u1eREJENupp6eVzqT/oyxeHj53pxUu9x4sBun/+Yd7GztQ2uPV733PE67vH7Fisgpc9CZRtXYRiUK8t+vHpPxWE+eJlceTdQbK9F9OrrSoxqmIYnjd29uV3+WrLGsSML/2smT1MecmmIJyaZl14AfJ9z1Ntr7/PjCseNSVgMA8r3U4w1iiVJl1wqsbDyMI8dVYeaocizf3RmhaEgUNQWAMVXhvp7bWnrRrDmoja7wYOPBHnUdeHTFPnz+n8vR2uuL+D2mFEYSuevFLjtmjpILHt375nbc/MwG3PzMBjz18QHMbajAsv87La3P5DLFmWVL4YQTkrja0mNvZ7+cX6r8oj2NdbjHG8Dmpp6Uc0urikXUMLWI3KV//RA/ezGz4jSZsK9jwPBKjqsaO7HhYDcmJWnNoEWoXOKtYcIRUV3iUvfXU6bVYtGEavzhivkpvUZdmQcjlIInifjTWztw2m/ewdm/X4Zfx5AQA7ITe2dr7FzPbBCSOHxBCSdPkxVgFcWxHdvdgwFVPSPe01ScxZxz/GHJDmxt7sWR4yrxr6sWqu9zMuorPGol8nzHCENuDID9mu8PKI/FfA7nPAigG0BN9IUYY9cxxlYzxla3tQ2vHC9TDnUNIhCSIvIrBB/tlo0Xl8M2JPG1usSFXl9wiJdYexgXB6FUF3POuSqtTCX8DADTlMhTY5oTPtbCdu1JkXIgh82GoxXN+P06E6YDIQkn/noprn1odcLniYNcY3t/woPkY6vkKXvVcRPw5eMnAAh73o8an3qhhVQjcmJ+jIzyBgLy4jdxRAlK3Y6IJH7BjY+vw8GuQdzz+jZVWnR8jEI2I0rdmDiiRM2Fu/uyufjumdPw2ndOxuTaUrWSXvIcufSklVrjMxX/jNNuQ6UBVQa10spUq1YO+IPo8wUxb2xFSmMV2G0sIkqTCv0ZFHKIpr3Ph1uf25h03meDf7y3G7vb+7H43nex6FdvYVA15GyoLXOjezCgtpwQqFUbHeGqscmWr+4BPxgDGjSH7XSpUQ5Smw/1DBmT4P0ExUzGV5dgb8cAOOd4Z1urKrcGkpd1F/f/sZPDW5o4jKeSI/f21la18NWFc1MrqCEQucivaMZrZg52DWJLUw+Om1yjStC0KgRtn8sv/2elGu0QdA0E1KIlrT1etcASADU639LjQyAk4cfPbsAHO+X1UrxPgCLDlriqfhFS3vHqeFqwZEsL6src+OzRY9UKeKlSV+ZBRZETLSnkPAknwZjKIuzvHMSBw4ORjctTXHJWK9GcKSk6cMs8TjCWPEdOy3DOsZPvWYprHjR2zRPtkBL1ho0mnjM4EJLQ0uPFP5X5eY3m7GOzMTz59eNwUpK2NlqmjSyLW/xO8Ns3t6vyy/d3xj4f3/rcRiy+dxkOHB4eA0Y4IUTj83iNuLsH/arCLNVzbUipF/G7JbJT7ebzZuKMNCTOx0+uwaq9nbqLYFkBUxU74ZzfzzlfyDlfWFub+k2QK/7+7i4cf9fbOOZXb2HRL99CrzeAAX8QF9/3ASbc9DK++oC8EL3+nZOH/K7Ib4qeZOIg9Pz1J6Rd3ef1TbJX9kvHjU/5byh1OzB9ZFlEL5BUiCX5OGVaHZ7+xvGq5vvNLS04clwVzpldj3e3t6nSoUzYoWzYySrLiffz9otmx20/cKhrEMu2t+FTC8Zgen0ZfnrRbCyeWaf+PJ2Nu7pE7seTKI/GGwjhFy9tgY2F9fCx6PMF8dqm5og8De3n8sL6Q2ok8a+fP3LI77scNiz9wak4bYb8twjDTCA2pQc/bBwy3uhiJ6l4y+54cTMm3PQyvvyflQCAp79xfNLfEdSUJPdAJkM25JTNIcUcOXHwOz/NA7PTbovbTD0eWpm0oN8fxKMr9kXk9mxv6cU722JLal9XCnhsb+nT1fA6Ewaixv9lpd2D22FXo6rRUS4xr5xpSCu7BwMo9zjV+zUTbDa58NATq/fjl3FaPCSS8s4aXY7Ofj92tPYNKf50yzMb4vyWjHDSjNIcEIvdYQdDsvX7e0/KUt+Pbj4dx0wa4t9MyDGTajCqwpOWUyKX/N//5L912sgy/OKSIwBErnFaZ8w729rw85c2R/z+Yyv3wR+S4HHasOlQD36gyKQB4NRp8rrX5wsMMdqPiTbkeDiHtUSRIr5w/YlYe9uZWHXLYqy6ZTFW3rIYnz8m9b1Uy+gUi4iJ/eroCWHnYYS0MoXNv3swgBseXQsAODnF3Hi7jaGyyJnmGjw8c+zxlXKNgNV7D+OtLZlHmjnnuObBVXgjqgjSEaPj51FGY4/jDP7L0l045ldv4W3FCfGNUyZnPE4g/aJz3oCkGlGccxxWPsdn1sjS4DN++66u8aSKqPRarewHVcWumFHe7sFwkZlUz7UPfdSonqFf+taJOHpCdeJfiGJ6fRk4R8pF5KyMEYbcQQDack4NymMxn8MYcwCoAJCels8EDPpD2N3Wp/57RClKIhbD93a0483NLRFRlSPHVaqeRy3COxF9EBLRkrpyd9xFJB4vfiLnD3zh2PQ2n1Nn1GJlY2daB9VYMjOHneGo8VX4i2JkLFDkiV85YQIAub9dpnysSBiSnfVE0Zb6ck/cg+S722Vv1qnTw84Ckcw9rro4pWRxgTi8xYrICjYc7EavL4izZ9cnLEksIoM728LSCK0ktczjwO72PhQ57RG6/mjEnxttyAlP7/3LduPnL22OKAzhC4Y0VbaSHz5DEse/P5C9kYwxXHfypKTtGrTUxJHmpcrSba2ytFKNyKXWpkNIiI+ZmN6m4LCzpLKvaGJF5DYd6sGPn92AVzaGvdtn/W6ZaiRF06KRjn3nibUJ5xkArN/fhdc2NhmSz7LpYHfMxz1Ou5rjFG3sCUeUO40+ctEFCDLl1vPlti6vRx3c/EEJr21swmsb40cUxD1/1u+W4bbnIyVk0eW1oxGy6fHVJZioyLZE/mqyfFMRzS522VEfI1qfCqMri3CwK/seeG9A3v/Srd6qpbFdHudxk2tQU+rG3IYKrNgTPgqIPNR4UVXByHKPWp1ZIIoo9XiDESqB6Pw2kXs2EBWRqyh2qg4KvYyp9CSNsADhaO/pmkhDOtLK5bs78Ls3t2PAH8K5R9QPyf9KRF2ZJ601eLh8BUs0xpseJUJjxwCWbGnFdf/9OOJsc9zk1J0ltqiIXEji2NPejw93yfvyuXPqcden5+h2pIypKkJrrw+72vrwxKp9Sef/ztY+HHH76wCAm57egAU/fxPeQAh1iqPYF5Swu60vo/Ye6SD2CKHskg25oc6Bpm6vJiInP5ZsjxLRx6+cMCGtM5lAqJ8e+qgR/THUb/mEEYbcKgBTGWMTGWMuAFcAeCHqOS8AuEr5+jIAb3MzZWumyNr9h3H6b99V/+2LKrv6zUfW4MbH16nff+2USXjmmyfEvMlF1ajoykbisGi3MbA0tMSCiSNK1JsqVcZWFYNzoD0NqVuse0JU2zp6QjXe+v4peFBpVn3MpBrcev5MtPf50Z5hXtSzihGYyIABNE0ji5zh9gNRC8bNiof97Nn16mNC0nrHxbPTWpTrlehdc4KkWiHp+NbpiUuKi0iq+Bu2NPWoOY9HT6hCrzeI7S19uGDuqCSjkv+W6L9Dqy3/7/K9uPIfK9TvfUFJ7SMnPy3xnNN6ys+aNRI/Pm9mytp1QM5nSeWgEwtJ4mozcGEspFq18vl1h+Bx2tQNL1WcNlvaVStjReQEseQesaqWdStGzugKD/Z3DuKOFzcPeY6Wi+/7AF9/eA1+v2R7wuclY3/nAA51e2MWr/E4bWpBhejKnEJ+6rDZ1INfsoINWm+tHs6dMwrnzK5HS48PS7eGI5w/evoTfP3hNVh/ILZhCkCN7EZz3px6pWJp/ENAS498SCly2dVDojB05Ry52H//6sZOtV3BfZ87MuPD4JjKoozvpXS44dG1OP237+LWDNvJBEMSDnYN4vQZdWqBjzljKiJaEIiKum9siozEPLZyH+5buhOAXMgrVt7i1Dp539vfOYBPDnZjRKkLe+48D1+JqgAqV4PkQyJyRpJqRK5TKdbSUFWEaSNlWWRk+4HEv3/F/cvxwIeNAICfpFGQBZCdxelEK9p6fbpUNSm/Tp9f7YEr8cR7ayK+oihFgHAvQABp7VPRzvS7X9uK037zDlbs6cRxk2pw72fm44pF4xJdIiUalDPYGb99Fz96ekNEEbZEvLD+EJ5Qqjn2DAbUPRwATv/tu/j8P1fE+1Vd+IMSOOf4qbIfiTNndQylTWuvF73eIErdYfUMkJq0sqbEhdsvnJ20pVEsRCuJh5fvw+zbX8dn//4RXt3QlLQKsRXRbcgpOW83AHgdwBYAT3LONzHG7mCMXaQ87V8AahhjOwF8D8CQFgVWYGpdGf5wxXz13+VHNeCnF87CH66Yj59fcoT6+H++cjTu/+JR+HaCg3tlvIicyDGx2TRa4tTGt3JPZ0LpXjzEoXZnqyzfuvqBVUk9dbHaDzg09e8n15ZGGF0z6uVk8jtf2Trk91JBeE97vcGEWn1xQC73OMLtBzRvoNYLpI2Oiecka2YdjYjI/X5J7IVXa7iKjToeIsfjt29uw7vb21TZwq8+NQcjND0Bb1YaysfDH4r9tziiNrD1+7tw56tbIElcLnaizZFLsNZxztXDw2nTa1OukKqloaoYzT3etOWKALBbY/B89mhZDGBjLKnBEJI4Njf1YHp9edqHZruNJS2NHk10tEpLkdM+5B6K1TC+xxtEeZEDD10tO0Xe2NyCW5/bMKS4xarGTjyofCaAXMznwQ8b8dBHjUkPlLva+vDtx9ZGFBLaoSTMnzd3VETPQyAyItfvC+LNzS3qpuzV5NHZU8yF6DYoIgcAtyhRubX7u/DAB3tw4+NrU2r07XLEng/TR5YjJHE0dXux6VB3hOxZ0Nztxchy+f786YWz8dz1J6jFMaLvpQ93tuPBDxvx4IeN+L//fQIA+P1n56vFWjJhVKUHLd2+jO6lVNnT3q9GSp5YvT8j77bIQZ7XUKk+NjtK5iYq6opomyhMcfMzG3DP63KBhxGl7oj58vh1x+K7i6fhmEnVcNgYHlm+Dy+uP4SF46tj3uc2xsABtCh7XF15+ntmMkZXFqHHG0SvN4COPh9ueHQNWmMYTSJPuKbEpRpwAbWHWeR+1TXgxzUPrsIfluzAh7vaVfWJ0y4rItKN6I4s96SUx6fl/mW71fFl4ov/eG+nOv9f/qRpyDU6+nxYv78LY6uL8XNFevunt1MzarSEJB6R9792X1fa1wDCRl9Q4pAkjr8v243qEhf+cMX8lAp7pcoFc0epSiYAeHbNwZTe328/tlb9utcXRHO3F58/Zhz+dOUCnDGjDmv3deF/Hx8Ystfsae/HjY+vVeWZqSBJHLva+vDDp9Zj2q2v4o9v7VR/Jno0Vpe40OsNRhhLwql2vOLkSkupocPBF12gaM2+LnzjkTUZzSezY4grinP+CoBXoh77ieZrL4DLjXitXFJb5sbF88N1XLRfp0tlnKpRwuvvsLOUG1oDsve/rdeH2aPL0x6LSJBe1diJVY2d+HBXB/67fG/CA3osmZkzgRF0xBh5XE+vOYDfXD437YO0Nr/lm4+sQeNd58d8ntjcyoucqjGqfftEsZDow+nnFo3Dyj2dKSeLC8ZWyYvF+3Gqcv75bXmx+/F5M5J6lSqL5Cjt/s5BXPXvlbhy0ViUuh249KgxajL7qAqPqkePh2g6PURaqTG0f33pHPzo6Q34+7u71dLtrhRz5ESE0eWw4T9fWZRwLPEYU1WEkMTR3ONFQ1XqFeGAyDLNInE9Fe/1S4r0eFwaFegEDjuDN5jewSWRrOV7T67Hw8v34oGvht+/lXs6h6wpPYMBlLmdmFJXhk8tGINn1x7Ew8v34eHl+yLugW8+skad72VuB3a09uH2F2SJ4Io9nfjzlQvi3nM/eGo91u7rwpeOG4+FSh7C6sbDcNhkqXRpVMTC7bCphtz9y3bjra2t+OOVC3DRvNFqQRS58WvsiHg03QMBtXquXsZWF2NEqQttvd4Ir3aZx4FebxBFTrs6Ri3x1q7jp9Tgd0uA9Qe61Dyk6LWnpdenynhcDluExFgrreSc49qHVqM/ysC/eP5oXdKs+Q2V8IckrGo8nJZsLB2i8w7f39murhsCzodW0tWyu012wBw9MZwPFu3cCkkcmw/14G3l8BdLgdFQVYS/ffEonPP79zClrhTHTqrBsUoOXHWJCyuV9WFRHPm0+ExEXlGy9TQTxHxu6vbiqdX78dInTTh6QjWuUuTzgk7NGMT9skORhUY3BF++uxNLtrRiyZZWlHkcaiXsuy+bi08tSK0fppaR5XILkZDEh0Sp/EFJXT+i6R4M4F/v78Ff39mF3b86L+XcVkni+PJ/VqFXU833pW+diCM0vf/EPTuvoULNa3xkxT788lNz0vrboptUL1fku1+Oev+ToRY7kTjWKY42iXNdZ79YeJx2nDdnFBaOr8LqvYdxqNuLLU29mJXGea6xvR+BEMeMUeW4cN5o7OscwFtbW/GDp9ZjSl1pxLr0nw/24Pl1h3DC5BH4zNGpNbq/4bE1eGVDWLb+uxiqD3EvHR7wq2uiMKKPmSTfjynnTg8E1IBHpvzhivkRKjkAw94OazgwVbGTQiLckDNyUmkb6qYqTQKAd7fLG99nF6Z2U2qZVFuK+nIP/vDWDny4S17w/vjWjiHNWi/403uYcNPLuPmZDfDH0HALaWUsKotdalnmiTe/gov+/H7SVgLPrj2Alz45hH5fEG29vojm2fGqMgnJTZnHEbPiVKNSxv3I8ZURv3fJgjFovOv8tHIMADm/4qJ5cuGM6BYOvd6Amhf4peMmJL2Wy2FTCwAAwJOrD2DmqDK4HXa1NYLLkfyWFZtMtFGqlV1oG2Ffcf9y+ecOkdcT3yhatr0Nq5WiG7ednzgymAhRoTDd/oVAWM63/vaz1MfkHDke8ZyfPL9R7ekEAIeUxsm3JIloxkIudsLxxKp9+Mp/VkZcNx6JInKA7CE8rNlUHlmxL6Ilw8d7O/HW1la1T9hvL58X8ft3v7YVv3hpMy7/24do6/Xh2pMmYv1PzsInPz0La247E2tuOxNHT6jCe9vbMPHmV3DLs7GLdoj37bK/fYTrH12D9j4fnllzEJNrS4cUIXns2mNR5nGq0spdbfKhc68SJRX3tFtryMWZS5xz/OT5jdjd3h9xuNNLbZkHS7a0wsaALxw7DmtvOxM/OGs6gHDUO5ro++rU6bV4+hvHqQ2mhREHQJX4AXI+3vr9XXGNge7BAHYq71HXQAD9/hB+ePZ0rLntTLzy7ZPw9DeO051fc4JS4OLKfyzXdZ14PLl6P5ZsacGYyiK89K0TAQDff3I9HlmxV33OC+sPYeLNr0REhaMJN6wOH04XRhUwCIQkvLdDzmGeNaocgZAUkS9kY8CJU0ZgRn05tv3iHLx640kRv9+qODPOm1OPr54Yu6k6U1pCiGhEaRK5fiaMqZT3kYOHB1UHS6yPubPfD5fdhlK3AzedOwNAOAonWlcM+IO47bmN6vt3/pxR6PUG1QPqyWlUR9RSXeKW2x/EuPfe29GGx5SiI9EM+kP46ztyo/TmBNLMYEjCTU9/gqsfWIWrH1iFLz8gG3HfWTwVj14rtxq54E/v43lN777n1h3CtJGluGLRODXfdGrUPsY5x09f2ITfvhG7BD8AtXn0N0+Vi5Ds7RjAadNr8dOLZsf9nVioLXs4xy7FwP79Z+endY10eOy6Y/Hk144DAPzz/d1Jz0hatikKngbFiXD1iRNx4xmyKuwPUUbXCqXH3R/f3jFEHg/IOao/eGq9WiiGcx5hxMVDrINaY6m9z4dpI0tV+TqLkqvGo2vQr56TM+Xi+WOw587z8DPN554sPceKkCGXI8rccsPq6FwZNSJnY2lJK7c0yTfx0WkWcRAs1FTNEry0PlLCKHIZHlu5D7+MoZVPFnH69IKwF+uTA9144MPGmIuI4LtPrMcNj65VIzDna3LDXv4ktryyxxuE22GD22GPuWC09vjAGCKkinr5lNKP7M0oudubm1vQ4w3i00eOSVjkRIv2c5g5qgyXK4a5WKSvTEGPf+Wisdj5y3OHGKXaMTjsNnw/KuLqETlyttiymUNdg/jSv8N5B5cdlb7TQCD06wfSMOQOHB5AW68PPd4gGJPvIUF0jtxjK/fhoY/24m/v7sK6/V3gnKOpexDlHkda5ae11+/1BvCjpzdg6ba2hI2nBakYJ0JaLarWvaJUJV2/vwtPKC0yLlAqbNpsDN9ZHJZr/+WdXfjn+3uwfr+c+3Xi1FpUFDvBGEN1iQvVJS58/pjxatNzUZwpmnCRG/m++mBnO5p7vDElZ9E5YEJCI3pyaaWVyXJ823p9eOgj2RgYSEPik4xLjxyDkeVuzG2oxOVHjY0oYFEWZxOPjsjNqC/HUeOrY9639765Xf27v6VIm+L142rq9qqRKNGcdtKIElSXuDBrtPwaetG2mkm31Pbejv6kHup735APgZcsGI3Zo8vxmYUN4Jzjlmc3quuEcFglkr2LuR4to/VoHEyBEMeyHW2YPrIME0YUIyhx1QEDyJEu4VhwO+xxI6na/OdohF+izxeEw8aGKBeMQETkDnYNYpfy+ff7hh7KO/r9qC5xgTE2pB2CzSYbEK9vasZ/l+/F7UoRns8fOw4Lx1dh6shSXLloLGoy3MvEvdDrGzpnEkXZtBHtngQOre0tfXh81X7sbOtDS68Xnf0+HDmuEhfMHY2jxlfhNKXYmDC6drX1oXswoO4NAPCpBWPgjXIaN3V78cCHjfjT2zvjzt0NShEOUcEZSK/tgECrKlh/oAtlbkdabQXSxWm34chxlQDkCpT/+aAx5d/d1SrPM/F3epx2fPuMqfA4bVi6rS1C7jgQkNfbA4cHVceslvX7u/G/jw+oUdlE9RNuVhwQQNiQ29Hah15vADtbe9Ha64uoBG63RZ5r+32xe991GRCRA2TDURsJT6cnpFXIP9PUIthsDLVlbmxtjpzAEcVOlImeTJoEANuaezG6wpOxgaKd3L+45Ajct3Qnnl4jR4SuOWnSkJ4usXpqJYrIAXLvs9Nn1Kmymbte3YqPdnXg/Z3tePJrx0X0b9MWinh4uXzY+96Z03DtSZNw0t1LceerW/G1GCV/e70BlCs3f/SCAcge2+piV0IZaLqcNr0OtWXuIYVcRFlxEQ1IhRn15dj4s7NR4rJHeOon1ZZgd1u/WgE0EYwxtUKllqKoQ+m3zpiKgUDYuyoO9PHyzU695x3160evPUY9cGSCOOjsbh/aAD0eJ/56KQA5n6TU5Yg4bIgmvwLx2d+3dBfuW7oLD199DLY09ahGTbo47SziQPmtx9ZibkMFxteEm8t29Plw9u/fU6PC/hTyiMTB+wdnTcdn71+O257bqESqwrImbdW97yyehm+dPhVPrd6Pm5SiPX/63AIcP7kmZsGOSxaMwSULxuDnL23Gv97fg2m3vIqzj6jHn65cAED2mu/p6Mcp02rxp88twNyfvqF6+rU9zZ7+xnER1xeHx0OKcdKoRuTkv7nIaY8pDe/xBnD0L5bgr184EmMq5c/iUwvGqNEII7jmpEm45qRJEY+J+2FyXWlEjqUgej0oLwpvjZ8+cgyeWXMQD351EZq7B/GjpzegpceLsdXF6uHovCPiGw6C5h7ZaZHJgTIZf/vCUfj6wx9j6dZWXLIgNdnXhgPduPDP7wOQo/dvfOfkIQf4lh4vmnu8uPakifjemdPBGMPdl83DWb97F4cHAnhmzUEs29GmVgJOJJP+gyKbi5bxaZeaAX8QH+zswPlzRsFuk+9pbdQ+Vh/OWCSSvjGljH6fN4gSt0N3RDQWdWUeOGwMGw50q0ZF1+DQw3CnYsgBsoz3xCkjcNsF8v0upJVibor1ZEZ9Of6XRquXeIg5/ts3tuN3UVEmpy3+/qhVGsSK5gGytPG8P74HQE5j0MonBf/5yiJcfN8H+HBXB6bd+qp6L52uMb72dvRjf+cgtjb3qJHcy/76ofrz5m5vzGh4c7cXZ88eGdGbsqYk/bORGpGTOBrbBzCptiStYimZ4LDbMLZa7iv469e24uL5o1OSnotcaG3hKLuN4VefmoPvPbket7+wCXd+WpaodvT5cdG80Xhh/SFc9e+VOGd2PZbtaFMDCWL/b+r2Ytotr6pz7+7L5uKcI+px/J1vo88XxHUnT4o4hwnVlDZ3DwA+d0zYAR0trfzBU+vx6sZmbL7jbFXpAcjSSj05ctG88u2TcN4f30N5HFWGlcm/v8hCnD6jDo+t3A9vIKR6foMhCQ4bA2MMNkTeVInY3tKL+YonJxO+dNx4DPiC8LjsuPTIBoytLsZV/16J/y7fi1mjytVE9yPGlEdUGdOSinF0z2VzsWxHG/p8Idz23Eb1AHDpXz/EZxY24PKFYzF/bCWu+2/YcHx3exvcDhsqi11JiyL0eCNL0gOREbm2Xm9GBWGSsWBsJT7RVMXTRhpHpXlwiyX1eeSaY7BuX1dE9CRdtJ5vwUlTR2gMOfnnnA+NlB04PKAu5n+8cgGOn5xav6L4Y7FjRn3ZkLnkDYTwz/d246rjJ0QYDlp5VUe/HyNKIzdvuyJDksfP1fLjC8ZVYu2+Lizb0YYNB7vxuUWZ9YVq6fENMcxe3tCEb546Rf3+qY8PoL3Ph08tGIP6Cg8+3NURs8G7FlF+vLrEhdsumIWfv7RZNeKqip347pnThkS67TaGSxaMQXufD4wxnDa9Lqnk9svHT4DHacP7Ozvw4vpD+NE509FQVYzGjn609fpw4TmjUe5x4g9XzMfW5l54HHZcOC9syEVHjqLn6Ksbm9HvC2oicrGlldube+ELSrj3ze249Xz5wHrZUQ0pH9Az5fKjxkLicpuB6Mg5MDSfVLvO3HHxETh6QjVOnjpCLZzS3BNeR645cWJKletERC7dBtOpcKySf5JqI+BgSMJf3w1LRHe29uHFTw7hgrmjIw6qwum2cEJ1xOO3nj8LX/r3SqzY04Hn1x3CvIYKrD/QjY/3duKjXR04Ykw5SlwO/PzlzQiGeMqSNrGGTq4twYHDgwiEpIjWCsnyal698SS1gEg8xJ/R6w1kRVYJyPfoEWMq1IqCgHwwjaaj36+2TSh2OfDwNcdoxilX14yWaJe4M98DtJw4tRYjSt1Yu29oRCaQoLCTNvc3nupA5KKPrylOmHf+43Nn4J3tbXj4o71hQ07TiuHTRzZgzb4u3PvGdtz/pYX4cGe76jwCgLe3tqB7MIDJtSWqAkWSOHa09uGEKSMi7rXoPSMV1KqVHGjqHky7Inim/OfLR2PxvcsAAMff9Tb+e3XyXHQhwS+PcuidN2cUvvfkery0/hB+fN4M2BjDgD+EmaPKMRgI4c3NLXhNkVB+/ZTJqgTYGwihyGlX61eXe5z49IIxcNhtqiw5Orol5LBail32iD570fuCOAO+uP4QxlXLvx+UJPT6goYVwQLC5x/r1ctPDhlyOWSKUi7590t2qB7poMRV7zFLkmMiCEkcBw4P4qwEcpJkjKoows8uDudnnTKtVvVEf04pYXvXp+fgikXjcMeLm9UeYlqiqyLGoqbUrSZmf7izHa9uDOuun1x9AE+uPoAL542OqDQXCHGcOUv20jHG8LWTJ+E/HzbGTK6PaBIdIyLQqilMYCRjq4vx3o52dUyicff5c0cZ4vEdVVGEUXP0HQBjGYHahVccZvd1DkS01ghJXI2G3X7hLDUnUC9jq4uHVAH89wd78Js3tqOiyIkvavIKhcxQEC31EDIkQM7XETLC+7+4EIvvfRf3L9sNAJjbMNQznAraZtxLvncKFt/77hBj965X5Yqs3z9rmhqZuOjP70cY+NFsaeqB084wqrIIXz1hQkRbh1W3LI4rV/Y47bghSTsLLWOri/HDs2cgKG3B+v1dOO0372DLHeeoxoUoAHPx/DG4OOWrRvL8ukNq7qzHaVcPBNr7TxjYh/sDan6wEa0HkuFy2PDFY8fHrAwKRDqhptSV4qxZ4bW01O1QJc3CKdPU7VWrBE5PscdRU5cXdkWJYTSVxS5MqCnGij2duCGF5z+8fO+QnJcbH18HzhER0ROtWqL7Lp40dQSqS1x4crUsqVw0sRptvT40dgzgyn8sx2cWNuArJ0xUpWGnzZDlaNFy7mjEPXTClBF4es0BBEMcqxvD997eGFVDtcwclbw4hIg69niDWTPkAER8zm6HLabstbPfF7PPLCCKTg01ltKtrBwPUUjrP+83QpJ4RDTWlyA3S9uyIF6PRdEy4PXvnJwwreCYSTU4ZlINHlYk1n/5/JER0srPLRqHW5/biDcU54s4i5x7RD1e3diM3yiy33kNFXj+Bjl/U+T5CyPjwnmj8eL6Q3FbjCRCBCYliaPXGxxiJGWLKXVl2PSzszFb6RX3xX+tTPIbUKt0FkcpZTxOO244bQr+vHQnfvbiZpyjnBOrS5z4x5cW4vpH1uDlDU2YWleatjJiSm2kke6w23DmrJERzrIvHjc+wuCL3hc8TjsG/CH86OmhOdzRf4se1FSlJK2VrAgZcjnki8eOx31Ld+Jv7+7CjWdMRZHLjkBIipA1pNIU9KGPGuEPSRlV40vEHRcfgWfWyInIWuljkSv2RpJur4+7Pj0Xi2eOxLr9Xfjv8nDi/Ivr5eqCoyo86kFT2/eryGWHPyhhX+dAhLQNUKSVakQu0pDjXM63mJ4Fr9qoCg8GAyHsauvHlLpStVR9JqX5s0UsKWRdWdiojbfhimqPQGo5eqkyqsKDj3Z1ICRxfLCzPSL/TuuF3tnaq0apROXGaLQV3l5YFx5vmceBF284EQe7BuFyMMwfOzQXNF2m1JVi5qjyiHLi6zSRN+1BJJn3b09bP8ZVF6sHSpfdBn9IwrUnTcyod04yrjlxEv63+gA6+v34xctbsHpvuBpqunz7jKn441s7cMn80Xhu3SE8vHwvLpovG/kepy1m1UpRjKK5x6tu3JlInjLFEUcypo02LfneKXF/X8gi93cOqJ9zqoZZU7cXI8vcWZNmnTq9Dg982Iir/r0STjuDLyjhH19aiCVbWnDDo2uxaEI1nvz6cWju9qr9n+6+dC6On1KDR1fsw1/e2YXvPLEOlywYg15vAP9+v1G9dnTRAcYYnr/+BOztGMB7O9pw9UkT8a/3w869/318IKI6p3DEHDk+tfvvqPFVeH79IQQljhfWh+/nTAoVxWPAH9QlD0/GmTPDB9o5YyqGtBoCgM4+f9xCOTYl71fbM2/BuEpDpaANVcXwhyS09voiJL+xKrsKtM3WYxV98gVDeOrjA6gucaWcG/7Itcfgg50dQ3IbbTaGco8DPd4g/vnebvXxn140W3UC15a5sf5AN+56dSt2tvapkanFMxXnr/I7mRzftcVOeryBCMl1tilxO3Da9FosTSEfW0us+fGtM6bg2bUHseFAt/p+CGfVnZfOwReOHY/JtUOjackQSgAtWrntdxdPw9UnDe3jCIT3hTKPA5XFTvzyknBlUn9Iwoe72vHZo407b6gGZP61kaNiJ7nE5bCpzZ3FYTkY4hH5TdG9ZGLxM2VTzuRGTESp26F6yxdNDEtronOtBOl6CiuKnbj0qAb8/JIj8N3FQw2ed354qur11pa6Fg1c/6FZ2AW9GmlluGql/LOWHh/a+3wpeW3T5USlctxTH8sHFmGAptvbJ5vE2lS1h8p4Sf+3PhduAJzqxpwKiyZWo88XxMo9nRFGHBB5kNAaZicojWJHRhXiEDKk7S29eEvTDNrtsGFcTTGOm1yDo8ZXZ3yIfv76EzC5tgSPKNKnyiJnxMFMVFCLbiifzPu3t3MgwgHzI8Ujqk3SN5LaMjceunoRytwOPLJiL7Y192LOmIqMyv9fflQDGAOuPnESxlQWYXNTD7YpOb8eh33I/QeEKxd6NBGKTCRPmZIsjzcZZR4nZo4qx9tbW9XIRKqGXHPPYFby4wSiGNS729uwZEsr3tvRjvX7u/CLl+TCVCsbOxGSOJ5S5H4XzhuNzxw9Fg1Vxfi/c8KeeM453t7ait8t2Q6P04Z/fGlhzNcbW12ME6eOwM3nzURdmUf9nK85cSIcNpta8Q4APt7bBQAxc6WiOWNGHRx2G5w2hvY+H3yaIg1GRDOFg2HQH4opNzeK46eEW0FUFrtUGbXAGwih3x+KqMYcPU4RCQJkY/DZb55g6BhFDlm0JFfkusZiR0vYkIuVI7d0ayv2dQ6k9FkL5jZU4hunTo65Pt9+oSzL/YXSAuNPVy7AyHIPrj9tMmrL3PjMQlnh87d3d2HJlhZsOtSDeQ0VamugLyt55cdn0JpDjMcbCMEbkDKK6unhh2cbkzvsdthx+cIGbG/txRpFSiuKQJV7nDhuck3aFbuB2EajNvJ84+KpQ6LeNhY2juUiZF6cOXMkjptco/47ZVotbj53pqHSSpEbm3/xOIrI5ZyfXDALD320F7c8txGfWjAGQUmK8MQnKgUPROYOpertTIcl3ztlSAncIlfsaROrwEaq3Lh4Kr59xhS8vqkZX394DcZUFsHtsOOFG05AQOIRUY6rjp+AX76yBQ8v34dfaLw4gLyxiIUjOkdOHLyyUbVoRn05Jo4oURPzRYNwI6UBevEohlo8h647hpH25Or96kHig5tON3Q8orR7rJLlohqZPyjhj0ovvg9uOh2jKzyYWleKeZqeOICoWgmc9Ts5r2BUhQfXnjTJMO/1vLGVeOv7p6rfVxQ51dL7/1i2G+/taMd5c+qHtJlIFpHb0tSDRRPCeXtfPWECjhpfhXkZSkBTYfboCmz42dm6rzO2uhh77pR7qj18zTE47Tfv4Nm1B+Gy22DTKAm00squgQAaqorw/o9Ox4A/iP2dg1mJPMbDiNc6YnQ5lu1oU5uyp+qsaer2Yma98U4kwdETqvHfqxdFyLC+/J9V0J6NO/p8+NPbOzGi1KUWvBF8/phxeGTFPrz0SRO++8Q6AMDHt56Zdrnu/ztnBm5VCnbs6xjAyfcsVUuQp3IwE5VR7ZroaUNVEQ4cHsSYKv35heL9GPCHIiqaGo228EtFkRObD0VKrIVTozpORNqu7P293gBGVXjwotL6wUiEsfPbN7bj0WuPAWMMrb1eNTcyFlrZfbTs88Od7fj6w2sAAL/61BEwgkuPasD3n5ILh5V7HGru7g/PnoEfnj0D/qCEc48YhVc2NOEv7+zCt06fgu9rCowdOa4qbt/ZZIjokXDaDXehjFmjy/GDs6apEtJkvPHdk+P+bOaocnAO7Grrx8XzjUmPiMXfv3gUTrp7qVqVNBqbjcm9HCWOjn4//EEpI0VIuoijQCaN7M0OGXI5xmG3YfHMkViypQXrD3TLESVtWXUWX1r5wc52tcrleXPqDa3EKIhVBVMbkfvy8RPwgHIQTyVHLhGMMZw9ux5fO2USLldK28fyEmkLO7T2eiPkgf2+oHrwYFHSSiHrqstCjgoge4ubur1qIrB2DGZAtByIjvaMrynG3o4BddO6/rTJ+Pu7crTzOUXGeM9lcyOMaSMQn5tItBZNmwG5TQQAbG2Wi6GcPXuk+vrRRhwgL9IhiavSxNsumIXz5owa8jyjqChyqp5HUdDgqycM7VuVyp6hzVNkjEU0brUKE2qK4bAxBCWu9iuMlePb1utDVXG4uEOq+WVGoXeNAoC6crda/MZhYymVf+eco7nbO6SJttEItUFNiQv+kISjlT5t/b4gVuzpxLNrD8IfkmLeG6KQwx/f2gGJy/ls6Rhxz19/At7f2R6xPmsjXvEadEffIkLGqY2e/u6z8+G02zC5Nn7hjFQRS/JgIASPjuJRqbBgXCVGVXhQWewckiMnirLEl1bKe1ePN6CqTIxmvJKf99HuDnxyoBtTR5bitHveGdK0Xou2imhfVI7cezvl3PbPLhxr+H4BAF+Jsca6HDY1+veXd3YlbD2RLiIi1632px3eiJw8hsTnuq+cMEHNRZ0Uo9iIYIFmX8nWGQiQHXxPf+N4zE7QzNzO5L2iSakEPSoLcyWasCGX9ZcadsiQMwE3njEVS7a04NK/foiF46tQpvFaxpNWvr6pGV/778fq99HltrOJNkfux+fNVA05I4wWxhhuPjd5HsR9nzsS1z+6Bot++RbW334WKoqc4Jyj3x+OyEW3H2jtVfLtYvTHMoKR5R68uP4QjlASlEUZaTPxrTOGFsg4YcoI7O3Yp75f2vYDB7sGccHcUWo/OyOJzk/Z8NOzwTnHJX/5UDXuHlspG0nJ5oRweMwfVwkGZNWIA2RZcPdgAJxz7O8cwHUnTxrS3BhITcaRLRnlcMIYw8hyDw52DUZ46bWN2oMhCe/vbMdiTVW64SaRauBH58zAzFHJDct6pRLeqxubUy6e0zMYxIA/lHXP84hSd8zow7/e34MVezpxp1JM5BunDm3dIoy2Ha19OGd2fcy1IhHzxlYOcbJoCyxFF0aIh/BHhot+yc4Oo3p/Dpe0EgCe+cbxYIzhT2/tQL8/BH9QUg1doTqoiSMtZkreb0eCPDq9aJ2/F9/3QUq/Iwqc1Jd7IvrIbTzYjb++swvVJS78+rK5ho5zRn0Ztjb34obTp8R9zhFjKjKOvMWDMTl6JAozlRso9UuVZM6n2y+cjU2HetDe50uoONA6xcfV6EvDee//TkuYR3lUEnWY3SafMQ4p+Yyjs1DJNxpxPqViJ0RWOGJMuRqVW733cIRH3hYlreweCGDZjja1updgOELTAm1EzggPdyacMbNOrVz10xc2YUZ9Gb543HhIPHwgiZZWZqMZuJbjJ9eohVoA4PwsGxNGcfuFs3DmzJGYpXjQbEopf0nxmJ2TQo8svYg+M4wxVQLa2uvFc2sPor7cgwkJPI1izCHO4QtKhjQRTUZFkRO+oITP/n05fEEprlw3FRlHot5bVuK6kyepDWQFYsMGgE7lMDS5zthc3nSIVexE5AHHMm5icYrSEDgkcVQXJz5gXzx/NJ5fdwgHu7LXQy4VotfpWHLQEo1j5ftnGVOkya0xlI5LsWXJCcrzRCRibkNlVtbsQU3bn2whDo9ijnUPBvDKhiYEQpLazzCekSaklZ0D/qxKcu/69By1J2WqeJw2VBY7sWx7O55fJ6s2PlCicb+8xBhJpZb/Xn0M9nb0Z0V1lAw7Yzg8ICJyw39kPiZGQZFonrju2JQc6beePxM7Wvpw+VENusakNz3FbmMIhTialHVxVGX210UbReSIbMIYw20XzFR7tWkXC1G5SvC3ZbvUvl+C0RWeYS2qoc2Ri24gO1x4nHb86JwZeHVjs1rFUEgqwobcUGml0c3AtVw4b7Rarvuprx+Xs0Nburgd9oiokHjfWntl+VhDFmUPJ04Zgfd3tuOG08KeVuGU+PZjazEYCOFbZ8T3wgpsSo6cLxCCJ0sRVy3CM7uyUa76ODOORDDZpnHGjLqsN5gdLq5YNHaIIScKNgDhCMScNIogGE20QXPf545M6j2OZkRZ+OCdLLovolCikl6uih+J6Fap24FTptfGPPQVa2SUUw2q7KstoDQ+Tpn9aAe5iByIj8qls0BNNGJ9G/Bn35ATiPWie9A/5B6J1w5Hbj/AMeALZTXX+opF4zBzVDkuvu8DuOw2/OjcGRFtUGJRU+LG7NEVeHrNAdz4+Dr18eoSV1Ycf7Vl7qy07UgFm42p0srhaj+gZW5D5ZDHRDNvQapqqOFUbiVCOPiaur1wOWxxC/4YiSh2kqydlxUhQ84kaMvo/+byeerXYjEX7GrtQ0NVEW49fxYqi52oLXNjRKl7WHOx4lWtHG4mjChBXZlbzX079TfvAADciqEmjEzx/rX0eDOqzJQqpW4H3v7+KVi7rwsLs1B4ZrgQB6g25X3NVgQTkHNfVjd2RlRN/NLxE/DcukNYvls2kr5+cvJIiZDweQMhXU3TU0Ub9XvnB6fGjRgma2D8+WONK6+ca9wOO/739eMi3n+HjakR8U4lJ2g42w1EEy2tPHHqiLQro2nzquLlfQnEGiSaGGe78Xk8hAHb5wvGjSKKqsNGKgm0+1IqRtNZs8Ky255BWcJ3+gxjpbhaW96dZWmlQOT9ib57goaqori97BiT750BfzDtgjPpMm9sJT66+XQUOe14Y1NLxM8mjSjBoe7BiEqW91w2FwsnVOObp0WuzSNKhvcsMhzIETl57cpFRC4Wf7xyAS5ZMDonhqUR2JV94VCfF6MqPMMyZ9QcOZJWEtnkX1ctxH+X741IRNX2xwLkxsSnTK8dFrlbPMxiyAHArRfMwrcfWxvx2JSRshdc7VeivH0HDw9iXDyvsEFMqi3FJAMS8nOJOHyKQiPRPaSMpLbMjXOjDo7aYj9uhy2lqK9diVz7glLW814ARBz+E8k+ExlyogpnPhGdJ2hTCqAAQEeSnKDhQBuN/9OVCzIqb62dj/NieMu1iGjrIUVClK383GRoJaWihUc0iyZW446LZ+MynbKrTHniumMjcuw6+2VHktHzJcK4HAanDxB2/Ny/LLJlTkOCKpxyD1lZAprNfneCUUqeUrRC4KcXzcaGg9245/Vt6mPzx1XC5TCm+IzZsduYpmqleQwnox0cw4lw8DV1DQ5bWhAVOyGGhTNmjsQZUYUAhGQMADYd6kZHvx+zstAHLR2GY1NJlQvnjsLC8VUIhjhOvmcpALncMBCZI/fAB3uwraU3orcPERshPfrh/z4BAFSVDO/mpfU+JyqnrMWmeK+9w5D3AoRznf7vnOkJn5doz8hGVTezoY3I/VlpI5Gtwg2pjkcgypjrYUQSuZdwJjV1DaKq2Dks0eJYDGgKE8RzAtptbEj7DKNI5bBWX+GJuHePm1yD59YdSliJLxO0dspwSStFjhwgy+IYA57XqA5iYWNye6GgxFE8jM7TaEPO5bDhC8eMh8dpx+/f3I5eXxDFcVoQ5SPa96PUJBE5qyP266ZuL45JomowCrWPXB5acjQrTY626tuWJrnZbq7Lk5e4zWPIMcYwurIInHN8/8xpEc2+1SpFnOPZtQdhtzFcuSh/pGzZIjoAVlk0vAdv7WY5LsWkalGgxRuQhuVwNm1kGZb+4FRMSBLhjbdnFIIRB8gFK0Sxk20t8vpVlcUIb/LxyJPbqKhtWRLJmyqt7PLmTFYJIKdu6BU/PiOhJE1EraNzlz979DgcM7EmaaGjdImUew6PtFIb+T1tRi2m1pXh+XWH8MOz4zuCbIwhEJLfm+IsSysjXjdqA/A47agoduLqEyfi8oUN8AfjNwvPR8SaUep25E0+c65x2Bj8IQnNPd5hKXQCaKWV+QcZciZHK63ccKALHqcNc7LYLDgVip3mmzaMsSHlssWiK3G5Ge9lRzaovZKI+ERvViOGWQpX4gq3j0hVO29jQFDi8AZDatXLbDMxhQNmLO/fPZfNxUlTYzdLzTfsNiCkHEZn1Jehoao4p4chxhh+fvFstem0EddLhPhTVzZ24oK5uatiKyRwP7to9rC/djIDVkhvYzlgjDbigPCBLt5rZgNt/7FzZo9CkcuetFS+1qDKZrGTIa8bNaW1xq6ZpIXDhVCoDHcz8FhcdlQDvnz8hFwPQzc2G0OvN4iQxIfNsWdTHfvD8nLDSu5nJpEQbfuB3e39mD6yLGfyHIGZpJWJEBuSPyihrc9nmSqSuUZ7OJ03tjJhb5psYLcx3HLezLi5PLGw2Zja16bIRLKfWBWyFk2sLpi56NBE5Jp7vGqD6lzyxSzJB2OhNVqTFUbJJsdPGYEXbjghpxVDkzFcude2HETk7DaGP1wxHwe7BlPeP7UG1XAacvYo58Rw5RGaFbH95aIZeDRfPHa82vzcyjhsDP1KP0L3MN33YlYnK0BmRcxz4iFiYrOFpZX7OgdMsRG7hinioRfh0Tw84AfnQFVx7hdiK6At7PeNU3JTrvjak9N7Xe3hbDgPPckQm8aVi8biwrmjUex2RFSozXdsNjlHddAfQtdAoGAMWEGk0ZDbeRmrjLmZcA/TvhKRIzeMRsrF88ek9fzINS130kqrOG6zhTBszZAfl+3qpcOFTWPIDZcDh4qdEDnDxhiaur249qHVOHB40DJNps2A2Aj7lAUjXxbBbKPdyHMd/U2VXHmvkyE2ja+fMrmgDDiBw2ZDSOJo7pHL7w9XhbJs89b3T1HL9SdCG5HLtSFnVr58/AQ88GHjsPUkZbDGZ5Ir5xRF5CIR83K4HA2JqMwTZ7TDxtRz2XBFxdWaCcPyasMLnWxNjp0xfLS7A4DcSPes2blrOxDN6Zom0mZEbEh9XnnBKKRKW3rQSiuz1TzdaOy23HivkyFy5Ao1Sd7GoJaZBpA3EblUy65rD8XDlbtpNX560Wz8dBhz97R2ynD1kcsErSE3nFExW9RbYub3aDiwm8iQy2WhKCOxMYaenEXk8s+UM8+Jh4iJmHwepw0v3HCCaZpt7vzluepG8+3Tp2BsitUFhxPxVvUqC0axiaptmhnt4dMyMlrTSivl/20muW+HG4fNhqAkoalbROQKo1qnIBeFNYjEpNukPFdofWipRH+NInqtMoMBk0vEfmgGdUq+OAQd9rC0crjuQfHO5aEdR4ac2RGLamWRyzRGHICIAhjfOytxL61cIRY9EZErMVGkxsxo9wqn3TxzLhHacZrJkOOKkKNQDTm7jSEkAU3dSkQulyX4cwBJK82Hdn0z857AcuRQizYWzHTuyAWqtDKHkcnnrz8hr/YQO2Po8w+vIadWrcxDcWVhu1osgFhUzdS7zSqIG7fXFwBgrgO+mbFZMCKndSyY6cB81ixZCm2GRPlcIBtyEtbt78bY6qKCK5wQachZ417Kd7Trm7a/m9nQjtMxjJGYWJV2Cxnx3g9nVDSaeWMrc952ykjsNqZGxoYvR07+Px/nd2GeLiyE8IZRoY70EXtfOEeusA6RmaItOpDLzSsdtAcdM+X13X7hLHzrjCkoLdD7125jCHFgf0c/Zo/Kn4NIqpipaiUhow1smNnBol3GhnNN8yltXAgZcQ8Xeq6gkWgdXMOWI4f87SOna2YyxqoZY28yxnYo/1fFed5rjLEuxthLel6vEBHz3cwSELPCGANjQJ9P3pjIGE4NrfPXKhE57UHHTHJQh92GurLCkhNqsdsYgiEJ+zsHMK7GfHm02SYiImeCHBsiupiTedaKaHKljBjwkyGnJVzshO5fo8iF5FwtdkLSyiHcBOAtzvlUAG8p38fiHgBf1PlaBQlJK/VhYwy9XpJWpoPdpNGtRDg0B7LhbmBOxMduY2ju9sIXlDC2qrAKnQBR+aYO8xoNhYT2M3FEl2g0ESxH0kovReQiEHtLoRd9MZKcROSoj1xcLgZwqvL1gwDeAfCj6Cdxzt9ijJ0a/TiRHDHdzVRS3UrYGYMvKAGg9zBVcpVkrwenzZwRuULHzpjaQ66uwAqdAJFRlXypOGd1tH3kzPyR2COcAMO3Dl+yYAx2tvZh/rjKvMwnShc1R84ie6EVsNuGP6c9LK3Mv0mt92Q7knPepHzdDGCkzusRUQgjJP+m3vAgzlFuh40OUikSWbXSGpuXNiJnlTEXAg47U6VaZi4skS0iotsmjv4UEtr1zcwVGW05mjsepx23XjBr2F7PKpC00ji0TorhinTaCjkixxhbAiBWF+pbtN9wzjljTNdbxBi7DsB1ADBu3Dg9l8o75owpz/UQLIk4SJGsMnW0feSsIieJzJGzxpgLAatUCMwW2sO4gyLFpsDMxpsWq+Ty5Ts9g3KxNKvshVZAROTcDlvEGplNxP2Uj1HmpIYc53xxvJ8xxloYY6M4502MsVEAWvUMhnN+P4D7AWDhwoV5+HZnTj71EBlOxPtGssrUiTxAWGPzctrpwGxGtLk9lcWFZ8jZLXgv5TtW2UrtJMs1BdtaegHQvmIkYikcznY0akPwPNS36d1ZXgBwlfL1VQCe13k9Ig60kGeG2AspIpc62rlmlXmnLVpAEjbzoJ0/ZZ7CM+Ry1QuMiI9VnKJWkYAWCvQZGIfYr4ezkm8+FzvRe+K5C8CZjLEdABYr34MxtpAx9k/xJMbYewCeAnAGY+wAY+xsna9bcFjlQG02SFqZPlacapE5chb8A/IU7bpViNIkrU+B1nBzYJVPYbgkZ0Rq2MmQMwwxt4c1Iseo2ElMOOcdAM6I8fhqANdovj9Jz+sUMmLOkTcoM4T3dTgXDKtjFY+1Fq1sjQ7M5kF8FowVZkRKe/ijNdwcWCVgb8V1OB+5/KgGPPXxAZAy2jjEXjDczj0by8/CgTQ1LQJ5gzKDcuTSx4qeYK2RQAdm8yAMOafdVpCfCzkVzIdV5iFNHXMQUrzpZFgbR64c7IwxSHkYkSNDzuSIxEzyBmWG2AyHq+lkPmDFAwQ1ATcnwpBxF+jnY0WnSL5jlU+EiuOYA0kSZzCrzBzzIxyvw5kjB8j3fh7acWTIWQXyBmWGepB00lRPFSvONRcdekyJUBIMZ0NjM2HFeynfscpnQoacORDl6q0yb6xALnLkAPkzzEM7jgw5q0CLSGaI981DEbmUcVnw0E2loc2J+FwK1dAmSbz5sMpe6nJYY5z5jiqtpIicYagRueF2sDOQtJIYfsSco7B+ZojE9uEO4VuZSgs2bqZKleZEzZEr0EOpVQprFBIWseMoImcSPr1gDABgwdjK3A4kj7DbcuNgZ0BeVjuhChAmR8w58gZlRjgiR5tiqlRYsHGzg07MpkREpAo1ImeV6E8hYZWPRBhyVhlvvnLGzJFovOv8XA8jr8iVIUfSSiKnkEQnM0QYnaSVqVNZ5Mr1ENKGpJXmxK4Y2IUaXQiEpFwPgYjCKsa1uGcKsW0Hkd+I/Xq4i9AxFi5ek08U5u5qQWgtz4xAUBhyNNVTxYo5coVqKJgd8bEUYjNwAPAHyZAzG1bZSkUU2yqGJ0GkilNx8A33uYwhL5WVZMiZHU6JtroQHnGKyKVPQ1VRroeQMuS1NieFHpHzU0TOdIQs4pEXeaWUH0/kG8I34bLnQFppjds/LShHziKQtDIzhEecip2kx8afnW2pOWeVJr+FRrFSXrpQP565DZW5HgIRRUe/P9dDSAnh/LDSOkwQqTDgDwEAfMHQ8L5wnlatJEPO5AhvHHnlMsOnGHLD3a/E6pS6rbU0lHmsNd5CobbMDQDoHgzkeCS5wWr3USHQ6w3meggpoUorae8n8oyVezoBAPs6B4b1dfP1TqJdxuQIaRI55TJDSJuKyZDLa5x2G9bedmauh0FEMbJcNuS6BgrTkCPMx8nTRgAAXvrWiTkeSWLUiBwZckSeIfLwP3fMuGF9XZuNqelK+URhJi5YCJFaQou5Pigil/9UlbhQVWK9ipv5TF2ZBwAZcoR5aKgqRuNd5+OIMRW5HkpChPOW9n4i37j2pEkAgNmjh/ceZAAskiKbFhSRMzlCH086eX0Uu2iqE8RwU6dIKwu56MdvLp+nSkwJIlVEpdcjx1XmdiAEYTDnHFGfk958jDHwPKxbSadbkyP08aST1wdJKwli+KkpJQPmsqMacj0EwoJUFrvw3PUnYNrI0lwPhSDyAhsDVa0khh9RVp3MOH0Md+NJgiDCsrBL5o/O8UgIwnrMH1uZ6yEQRB7BSFpJDD+iGWg+Tr7hhHLkCCI37P7VeaQoIAiCIHKKfJzOv8M0FTsxOcKjbZUmpmaFpJUEkRvIiCMIgiByTb5KK8mQMzmqIZePs28YoYbgBEEQBEEQhQkDy8uG4GTImRxhyEkUkdMFRQUIgiAIgiAKE0YROSIXiLYDQTLkMuLW82fiuEk1uR4GQRAEQRAEkSNsjOVhhhwVOzE9NsqR08U1J03CNUrzSYIgCIIgCKIwIWklMexcddwEAFSGmCAIgiAIgiAygTHkY9FKisiZnROnjkDjXefnehgEQRAEQRAEYUnyVVpJETmCIAiCIAiCIPIWxkhaSRAEQRAEQRAEYSkYqGrlEBhj1YyxNxljO5T/q2I8Zz5j7CPG2CbG2CeMsc/qeU2CIAiCIAiCIIhUsTHqIxeLmwC8xTmfCuAt5ftoBgB8iXM+G8A5AH7PGKvU+boEQRAEQRAEQRDJYXlZ60S3IXcxgAeVrx8EcEn0Ezjn2znnO5SvDwFoBVCr83UJgiAIgiAIgiCSYmP5acnpNeRGcs6blK+bAYxM9GTG2CIALgC74vz8OsbYasbY6ra2Np1DIwiCIAiCIAii0GHIz2InSdsPMMaWAKiP8aNbtN9wzjljLO47xBgbBeC/AK7inEuxnsM5vx/A/QCwcOHC/Hu3CYIgCIIgCIIYVhjLz2InSQ05zvnieD9jjLUwxkZxzpsUQ601zvPKAbwM4BbO+fKMR0sQBEEQBEEQBJEGch+5/LPk9EorXwBwlfL1VQCej34CY8wF4FkAD3HO/6fz9QiCIAiCIAiCINJCyj87TrchdxeAMxljOwAsVr4HY2whY+yfynM+A+BkAF9mjK1T/s3X+boEQRAEQRAEQRBJYYwVprQyEZzzDgBnxHh8NYBrlK8fBvCwntchCIIgCIIgCILIBBsD8rFspd6IHEEQBEEQBEEQhGlhjKSVBEEQBEEQBEEQloKBgeehtpIMOYIgCIIgCIIg8hZbfvYDJ0OOIAiCIAiCIIg8hjGSVhIEQRAEQRAEQVgJBpC0kiAIgiAIgiAIwkrIVSvzDzLkCIIgCIIgCILIWxhjkCgiRxAEQRAEQRAEYR1kaWWuR2E8ZMgRBEEQBEEQBJG32BgjQ44gCIIgCIIgCMJSMJC0kiAIgiAIgiAIwkowUB85giAIgiAIgiAIS2Fj+dkRnAw5giAIgiAIgiDyFkbSSoIgCIIgCIIgCGuRpwE5OHI9AIIgCIIgCIIgiGzxwc6OXA8hK5AhRxAEQRAEQRBE3vPx3sModtnj/nziiBJ4nPF/bjbIkCMIgiAIgiAIIm+5+dwZuPPVrbj0rx8mfN6S752CKXWlwzQq/ZAhRxAEQRAEQRBE3nLlMePQ2DGABeMqUe6Jb/7UV3iGcVT6IUOOIAiCIAiCIIi8pdzjxJ2fnpPrYRgOVa0kCIIgCIIgCIKwGGTIEQRBEARBEARBWAwy5AiCIAiCIAiCICwGGXIEQRAEQRAEQRAWgww5giAIgiAIgiAIi0GGHEEQBEEQBEEQhMUgQ44gCIIgCIIgCMJikCFHEARBEARBEARhMRjnPNdjiAljrA3A3lyPIwYjALTnehBEXkNzjMgmNL+IbELzi8gmNL+IbGPGOTaec14b6wemNeTMCmNsNed8Ya7HQeQvNMeIbELzi8gmNL+IbELzi8g2VptjJK0kCIIgCIIgCIKwGGTIEQRBEARBEARBWAwy5NLn/lwPgMh7aI4R2YTmF5FNaH4R2YTmF5FtLDXHKEeOIAiCIAiCIAjCYlBEjiAIgiAIgiAIwmKQIUcQBEEQBEEQBGExyJBLA8bYOYyxbYyxnYyxm3I9HsIaMMb+zRhrZYxt1DxWzRh7kzG2Q/m/SnmcMcb+qMyxTxhjR2p+5yrl+TsYY1fl4m8hzAdjbCxjbCljbDNjbBNj7EblcZpjhG4YYx7G2ErG2Hplfv1MeXwiY2yFMo+eYIy5lMfdyvc7lZ9P0FzrZuXxbYyxs3P0JxEmhDFmZ4ytZYy9pHxP84swDMZYI2NsA2NsHWNstfJYXuyRZMilCGPMDuA+AOcCmAXgSsbYrNyOirAIDwA4J+qxmwC8xTmfCuAt5XtAnl9TlX/XAfgrIC84AG4HcAyARQBuF4sOUfAEAXyfcz4LwLEArlfWJppjhBH4AJzOOZ8HYD6AcxhjxwL4NYDfcc6nADgM4Grl+VcDOKw8/jvleVDm5BUAZkNeD/+i7KsEAQA3Atii+Z7mF2E0p3HO52t6xOXFHkmGXOosArCTc76bc+4H8DiAi3M8JsICcM6XAeiMevhiAA8qXz8I4BLN4w9xmeUAKhljowCcDeBNznkn5/wwgDcx1DgkChDOeRPnfI3ydS/kw9AY0BwjDECZJ33Kt07lHwdwOoD/KY9Hzy8x7/4H4AzGGFMef5xz7uOc7wGwE/K+ShQ4jLEGAOcD+KfyPQPNLyL75MUeSYZc6owBsF/z/QHlMYLIhJGc8ybl62YAI5Wv480zmn9EUhSZ0QIAK0BzjDAIRfa2DkAr5MPLLgBdnPOg8hTtXFHnkfLzbgA1oPlFxOf3AP4PgKR8XwOaX4SxcABvMMY+ZoxdpzyWF3ukI9cDIIhCh3POGWPUB4TQBWOsFMDTAL7DOe+RndQyNMcIPXDOQwDmM8YqATwLYEZuR0TkC4yxCwC0cs4/ZoydmuPhEPnLiZzzg4yxOgBvMsa2an9o5T2SInKpcxDAWM33DcpjBJEJLUqoHsr/rcrj8eYZzT8iLowxJ2Qj7hHO+TPKwzTHCEPhnHcBWArgOMhyI+EM1s4VdR4pP68A0AGaX0RsTgBwEWOsEXLKyukA/gCaX4SBcM4PKv+3QnZGLUKe7JFkyKXOKgBTlUpKLshJtS/keEyEdXkBgKh4dBWA5zWPf0mpmnQsgG4l9P86gLMYY1VKcu1ZymNEgaPkh/wLwBbO+b2aH9EcI3TDGKtVInFgjBUBOBNyHuZSAJcpT4ueX2LeXQbgbc45Vx6/Qqk6OBFyIYGVw/JHEKaFc34z57yBcz4B8rnqbc7550HzizAIxlgJY6xMfA15b9uIPNkjSVqZIpzzIGPsBsgfmh3Avznnm3I8LMICMMYeA3AqgBGMsQOQqx7dBeBJxtjVAPYC+Izy9FcAnAc5UXsAwFcAgHPeyRj7OWSHAgDcwTmPLqBCFCYnAPgigA1KHhMA/Bg0xwhjGAXgQaUCoA3Ak5zzlxhjmwE8zhj7BYC1kJ0JUP7/L2NsJ+QiT1cAAOd8E2PsSQCbIVdavV6RbBJELH4Eml+EMYwE8KySbuAA8Cjn/DXG2CrkwR7JZEcGQRAEQRAEQRAEYRVIWkkQBEEQBEEQBGExyJAjCIIgCIIgCIKwGGTIEQRBEARBEARBWAwy5AiCIAiCIAiCICwGGXIEQRAEQRAEQRAWgww5giAIgiAIgiAIi0GGHEEQBEEQBEEQhMX4f3GQ6nB7WFr9AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 1080x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "example = trainset.__getitem__(0)\n",
    "\n",
    "# We can plot the keys of the dictionary to see what is available in the standard dataset configuration. The 'truebaseline' \n",
    "# keys are used for baseline correction in some datasets. \n",
    "print(example.keys())\n",
    "\n",
    "plt.figure(figsize=(15, 4))\n",
    "plt.plot(example['waveform'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68cf462a",
   "metadata": {},
   "source": [
    "### Initializing the model\n",
    "\n",
    "We import the CNNDoubleResidual architecture from the ecgxai package and pass it the required hyperparameters to deal with our dataset (e.g. 12 channels, 5000 measurements per lead). We choose to subsample (half the spatial dimension) each layer to limit the required number of parameters. \n",
    "\n",
    "We also define adittional Sequential and Linear layer to map the CNN output to the desired 2 classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "25b82bfe",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:54.974256Z",
     "start_time": "2022-05-17T15:29:54.483912Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "from ecgxai.network.doubleresidual.modules import CNNDoubleResidual, Reshape\n",
    "\n",
    "# Initualize the double reisdual convolutional resnet\n",
    "cnn = CNNDoubleResidual(\n",
    "    num_layers=10,\n",
    "    in_sample_dim=5000,\n",
    "    in_channels=12,\n",
    "    kernel_size=7,\n",
    "    dropout_rate=0.1,\n",
    "    sub_sample_every=1,\n",
    "    double_channel_every=4,\n",
    "    act_func=nn.ReLU(),\n",
    "    batchnorm=True\n",
    ")\n",
    "\n",
    "# use the calculate_output_dim to see what size the output of the CNN is\n",
    "cnn_output_dim, cnn_output_channels, cnn_output_samples = cnn.calculate_output_dim()\n",
    "\n",
    "# We pass this calculated size to a linear layer that then maps the data to 4 outputs per ecg\n",
    "class_model = nn.Sequential(\n",
    "    cnn,\n",
    "    nn.Linear(cnn_output_dim, 4),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2f328f37",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:29:59.290123Z",
     "start_time": "2022-05-17T15:29:54.975453Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.8/site-packages/torch/nn/functional.py:652: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at  /pytorch/c10/core/TensorImpl.h:1156.)\n",
      "  return torch.max_pool1d(input, kernel_size, stride, padding, dilation, ceil_mode)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "==============================================================================================================\n",
       "Layer (type:depth-idx)                                       Output Shape              Param #\n",
       "==============================================================================================================\n",
       "Sequential                                                   [1, 4]                    --\n",
       "├─CNNDoubleResidual: 1-1                                     [1, 432]                  --\n",
       "│    └─Sequential: 2-1                                       [1, 432]                  --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-1       [1, 12, 5000]             --\n",
       "│    │    │    └─MaxPool1d: 4-1                              [1, 12, 5000]             --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-1                       [1, 12, 5000]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-2                              [1, 12, 5000]             --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-3                     [1, 12, 5006]             --\n",
       "│    │    │    │    └─Conv1d: 5-4                            [1, 12, 5000]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-5                       [1, 12, 5000]             24\n",
       "│    │    │    │    └─ReLU: 5-6                              [1, 12, 5000]             --\n",
       "│    │    │    │    └─Dropout: 5-7                           [1, 12, 5000]             --\n",
       "│    │    │    │    └─ConstantPad1d: 5-8                     [1, 12, 5006]             --\n",
       "│    │    │    │    └─Conv1d: 5-9                            [1, 12, 5000]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-10                      [1, 12, 5000]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-2       [1, 12, 2500]             --\n",
       "│    │    │    └─MaxPool1d: 4-2                              [1, 12, 2500]             --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-11                      [1, 12, 5000]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-12                             [1, 12, 5000]             --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-13                    [1, 12, 5006]             --\n",
       "│    │    │    │    └─Conv1d: 5-14                           [1, 12, 2500]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-15                      [1, 12, 2500]             24\n",
       "│    │    │    │    └─ReLU: 5-16                             [1, 12, 2500]             --\n",
       "│    │    │    │    └─Dropout: 5-17                          [1, 12, 2500]             --\n",
       "│    │    │    │    └─ConstantPad1d: 5-18                    [1, 12, 2506]             --\n",
       "│    │    │    │    └─Conv1d: 5-19                           [1, 12, 2500]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-20                      [1, 12, 2500]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-3       [1, 12, 1250]             --\n",
       "│    │    │    └─MaxPool1d: 4-3                              [1, 12, 1250]             --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-21                      [1, 12, 2500]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-22                             [1, 12, 2500]             --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-23                    [1, 12, 2506]             --\n",
       "│    │    │    │    └─Conv1d: 5-24                           [1, 12, 1250]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-25                      [1, 12, 1250]             24\n",
       "│    │    │    │    └─ReLU: 5-26                             [1, 12, 1250]             --\n",
       "│    │    │    │    └─Dropout: 5-27                          [1, 12, 1250]             --\n",
       "│    │    │    │    └─ConstantPad1d: 5-28                    [1, 12, 1256]             --\n",
       "│    │    │    │    └─Conv1d: 5-29                           [1, 12, 1250]             1,020\n",
       "│    │    │    │    └─BatchNorm1d: 5-30                      [1, 12, 1250]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-4       [1, 24, 625]              --\n",
       "│    │    │    └─MaxPool1d: 4-4                              [1, 24, 625]              --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-31                      [1, 12, 1250]             24\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-32                             [1, 12, 1250]             --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-33                    [1, 12, 1256]             --\n",
       "│    │    │    │    └─Conv1d: 5-34                           [1, 24, 625]              2,040\n",
       "│    │    │    │    └─BatchNorm1d: 5-35                      [1, 24, 625]              48\n",
       "│    │    │    │    └─ReLU: 5-36                             [1, 24, 625]              --\n",
       "│    │    │    │    └─Dropout: 5-37                          [1, 24, 625]              --\n",
       "│    │    │    │    └─ConstantPad1d: 5-38                    [1, 24, 631]              --\n",
       "│    │    │    │    └─Conv1d: 5-39                           [1, 24, 625]              4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-40                      [1, 24, 625]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-5       [1, 24, 312]              --\n",
       "│    │    │    └─MaxPool1d: 4-5                              [1, 24, 312]              --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-41                      [1, 24, 625]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-42                             [1, 24, 625]              --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-43                    [1, 24, 631]              --\n",
       "│    │    │    │    └─Conv1d: 5-44                           [1, 24, 313]              4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-45                      [1, 24, 313]              48\n",
       "│    │    │    │    └─ReLU: 5-46                             [1, 24, 313]              --\n",
       "│    │    │    │    └─Dropout: 5-47                          [1, 24, 313]              --\n",
       "│    │    │    │    └─ConstantPad1d: 5-48                    [1, 24, 318]              --\n",
       "│    │    │    │    └─Conv1d: 5-49                           [1, 24, 312]              4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-50                      [1, 24, 312]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-6       [1, 24, 156]              --\n",
       "│    │    │    └─MaxPool1d: 4-6                              [1, 24, 156]              --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-51                      [1, 24, 312]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-52                             [1, 24, 312]              --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-53                    [1, 24, 318]              --\n",
       "│    │    │    │    └─Conv1d: 5-54                           [1, 24, 156]              4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-55                      [1, 24, 156]              48\n",
       "│    │    │    │    └─ReLU: 5-56                             [1, 24, 156]              --\n",
       "│    │    │    │    └─Dropout: 5-57                          [1, 24, 156]              --\n",
       "│    │    │    │    └─ConstantPad1d: 5-58                    [1, 24, 162]              --\n",
       "│    │    │    │    └─Conv1d: 5-59                           [1, 24, 156]              4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-60                      [1, 24, 156]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-7       [1, 24, 78]               --\n",
       "│    │    │    └─MaxPool1d: 4-7                              [1, 24, 78]               --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-61                      [1, 24, 156]              48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-62                             [1, 24, 156]              --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-63                    [1, 24, 162]              --\n",
       "│    │    │    │    └─Conv1d: 5-64                           [1, 24, 78]               4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-65                      [1, 24, 78]               48\n",
       "│    │    │    │    └─ReLU: 5-66                             [1, 24, 78]               --\n",
       "│    │    │    │    └─Dropout: 5-67                          [1, 24, 78]               --\n",
       "│    │    │    │    └─ConstantPad1d: 5-68                    [1, 24, 84]               --\n",
       "│    │    │    │    └─Conv1d: 5-69                           [1, 24, 78]               4,056\n",
       "│    │    │    │    └─BatchNorm1d: 5-70                      [1, 24, 78]               48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-8       [1, 48, 39]               --\n",
       "│    │    │    └─MaxPool1d: 4-8                              [1, 48, 39]               --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-71                      [1, 24, 78]               48\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-72                             [1, 24, 78]               --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-73                    [1, 24, 84]               --\n",
       "│    │    │    │    └─Conv1d: 5-74                           [1, 48, 39]               8,112\n",
       "│    │    │    │    └─BatchNorm1d: 5-75                      [1, 48, 39]               96\n",
       "│    │    │    │    └─ReLU: 5-76                             [1, 48, 39]               --\n",
       "│    │    │    │    └─Dropout: 5-77                          [1, 48, 39]               --\n",
       "│    │    │    │    └─ConstantPad1d: 5-78                    [1, 48, 45]               --\n",
       "│    │    │    │    └─Conv1d: 5-79                           [1, 48, 39]               16,176\n",
       "│    │    │    │    └─BatchNorm1d: 5-80                      [1, 48, 39]               96\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-9       [1, 48, 19]               --\n",
       "│    │    │    └─MaxPool1d: 4-9                              [1, 48, 19]               --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-81                      [1, 48, 39]               96\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ReLU: 5-82                             [1, 48, 39]               --\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3         --                        --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─ConstantPad1d: 5-83                    [1, 48, 45]               --\n",
       "│    │    │    │    └─Conv1d: 5-84                           [1, 48, 20]               16,176\n",
       "│    │    │    │    └─BatchNorm1d: 5-85                      [1, 48, 20]               96\n",
       "│    │    │    │    └─ReLU: 5-86                             [1, 48, 20]               --\n",
       "│    │    │    │    └─Dropout: 5-87                          [1, 48, 20]               --\n",
       "│    │    │    │    └─ConstantPad1d: 5-88                    [1, 48, 25]               --\n",
       "│    │    │    │    └─Conv1d: 5-89                           [1, 48, 19]               16,176\n",
       "│    │    │    │    └─BatchNorm1d: 5-90                      [1, 48, 19]               96\n",
       "│    │    └─ResidualMaxPoolDoubleConvBlockForward: 3-10      [1, 48, 9]                --\n",
       "│    │    │    └─MaxPool1d: 4-10                             [1, 48, 9]                --\n",
       "│    │    │    └─Sequential: 4                               --                        --\n",
       "│    │    │    │    └─BatchNorm1d: 5-91                      [1, 48, 19]               96\n",
       "│    │    │    │    └─ReLU: 5-92                             [1, 48, 19]               --\n",
       "│    │    │    │    └─ConstantPad1d: 5-93                    [1, 48, 25]               --\n",
       "│    │    │    │    └─Conv1d: 5-94                           [1, 48, 10]               16,176\n",
       "│    │    │    │    └─BatchNorm1d: 5-95                      [1, 48, 10]               96\n",
       "│    │    │    │    └─ReLU: 5-96                             [1, 48, 10]               --\n",
       "│    │    │    │    └─Dropout: 5-97                          [1, 48, 10]               --\n",
       "│    │    │    │    └─ConstantPad1d: 5-98                    [1, 48, 15]               --\n",
       "│    │    │    │    └─Conv1d: 5-99                           [1, 48, 9]                16,176\n",
       "│    │    │    │    └─BatchNorm1d: 5-100                     [1, 48, 9]                96\n",
       "│    │    └─Flatten: 3-11                                    [1, 432]                  --\n",
       "├─Linear: 1-2                                                [1, 4]                    1,732\n",
       "==============================================================================================================\n",
       "Total params: 128,860\n",
       "Trainable params: 128,860\n",
       "Non-trainable params: 0\n",
       "Total mult-adds (M): 27.98\n",
       "==============================================================================================================\n",
       "Input size (MB): 0.24\n",
       "Forward/backward pass size (MB): 5.93\n",
       "Params size (MB): 0.52\n",
       "Estimated Total Size (MB): 6.69\n",
       "=============================================================================================================="
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from torchinfo import summary\n",
    "\n",
    "batch_size = 16\n",
    "summary(class_model, input_size=(1, 12, 5000), depth=50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "547b2c7c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:33:41.880067Z",
     "start_time": "2022-05-17T15:33:41.691285Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The autoreload extension is already loaded. To reload it, use:\n",
      "  %reload_ext autoreload\n",
      "['y_hat', 'label']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.8/site-packages/torchmetrics/utilities/prints.py:36: UserWarning: Metric `AUROC` will save all targets and predictions in buffer. For large datasets this may lead to large memory footprint.\n",
      "  warnings.warn(*args, **kwargs)\n"
     ]
    }
   ],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "from ecgxai.systems.classification_system import ClassificationSystem\n",
    "from ecgxai.utils.loss import TW\n",
    "from ecgxai.utils.metrics import TMW\n",
    "\n",
    "from torchmetrics import MetricCollection, AUROC, F1Score, Accuracy, Precision, Recall\n",
    "\n",
    "metrics = {\n",
    "   'AUROC':  TMW(\n",
    "       AUROC(num_classes=4, average=None),\n",
    "       ['y_prob', 'label'],\n",
    "       int_args=['label'], \n",
    "       out_labels=['NSR', 'SA', 'SB', 'STach']\n",
    "   ),\n",
    "   'Precision': TMW(\n",
    "       Precision(num_classes=4, average=None),\n",
    "       ['y_prob', 'label'],\n",
    "       int_args=['label'],\n",
    "       out_labels=['NSR', 'SA', 'SB', 'STach']\n",
    "   ),\n",
    "   'Recall': TMW(\n",
    "       Recall(num_classes=4, average=None),\n",
    "       ['y_prob', 'label'],\n",
    "       int_args=['label'],\n",
    "       out_labels=['NSR', 'SA', 'SB', 'STach']\n",
    "   ),\n",
    "   'Accuracy': TMW(\n",
    "       Accuracy(num_classes=4, average=None),\n",
    "       ['y_prob', 'label'],\n",
    "       int_args=['label'],\n",
    "       out_labels=['NSR', 'SA', 'SB', 'STach']\n",
    "   ),\n",
    "   'F1': TMW(\n",
    "       F1Score(num_classes=4, average=None),\n",
    "       ['y_prob', 'label'],\n",
    "       int_args=['label'],\n",
    "       out_labels=['NSR', 'SA', 'SB', 'STach']\n",
    "   )\n",
    "} \n",
    "\n",
    "metrics = MetricCollection(metrics)\n",
    "    \n",
    "model = ClassificationSystem(\n",
    "    lr=0.001,\n",
    "    mode='multi_class',\n",
    "    model=class_model,\n",
    "    test_metrics=metrics,\n",
    "    loss=TW(nn.CrossEntropyLoss(), ['y_hat', 'label'], long_args=['label'], arg_max_args=['label'])\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "5d07f5b9",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-05-17T15:34:54.834212Z",
     "start_time": "2022-05-17T15:34:27.464009Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "GPU available: True, used: True\n",
      "TPU available: False, using: 0 TPU cores\n",
      "IPU available: False, using: 0 IPUs\n",
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n",
      "\n",
      "  | Name         | Type             | Params\n",
      "--------------------------------------------------\n",
      "0 | test_metrics | MetricCollection | 0     \n",
      "1 | loss         | TW               | 0     \n",
      "2 | prob_func    | Softmax          | 0     \n",
      "3 | model        | Sequential       | 128 K \n",
      "--------------------------------------------------\n",
      "128 K     Trainable params\n",
      "0         Non-trainable params\n",
      "128 K     Total params\n",
      "0.515     Total estimated model params size (MB)\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "33cde115b8034dac87ecfc5483599a18",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Training: 0it [00:00, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "286d63c167ee484f91c2608a591d9efb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Testing: 0it [00:00, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.8/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 64. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n",
      "  warning_cache.warn(\n",
      "/opt/conda/lib/python3.8/site-packages/torchmetrics/utilities/prints.py:36: UserWarning: No positive samples in targets, true positive value should be meaningless. Returning zero tensor in true positive score\n",
      "  warnings.warn(*args, **kwargs)\n",
      "/opt/conda/lib/python3.8/site-packages/torchmetrics/utilities/prints.py:36: UserWarning: No negative samples in targets, false positive value should be meaningless. Returning zero tensor in false positive score\n",
      "  warnings.warn(*args, **kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "DATALOADER:0 TEST RESULTS\n",
      "{'test_NSR': 0.9823483228683472,\n",
      " 'test_SA': 0.0,\n",
      " 'test_SB': 0.35889434814453125,\n",
      " 'test_STach': 0.6073887348175049,\n",
      " 'test_loss': 0.2440243363380432}\n",
      "--------------------------------------------------------------------------------\n",
      "Done\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.8/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 8. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n",
      "  warning_cache.warn(\n"
     ]
    }
   ],
   "source": [
    "import pytorch_lightning as pl\n",
    "import torch\n",
    "from pytorch_lightning.callbacks import ModelCheckpoint\n",
    "\n",
    "trainer = pl.Trainer(\n",
    "        max_epochs=10,\n",
    "        gpus= 1 if torch.cuda.is_available() else None,\n",
    "        logger=None,\n",
    "        callbacks=[\n",
    "            ModelCheckpoint(\n",
    "                save_last=True\n",
    "            ),\n",
    "        ]\n",
    "    )\n",
    "\n",
    "trainer.fit(model, train_loader)\n",
    "trainer.test(model=model, dataloaders=test_loader)\n",
    "\n",
    "print(\"Done\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44b18dd5-8324-4808-b5fc-d682b1d41cca",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}