{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c8e3063f",
   "metadata": {},
   "source": [
    "# Lesson 29: PyTorch neural network demonstration part 1\n",
    "## Notebook set-up\n",
    "### Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b0ccadc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using device: cpu\n"
     ]
    }
   ],
   "source": [
    "# Third party imports\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "\n",
    "# Set random seeds for reproducibility\n",
    "torch.manual_seed(315)\n",
    "np.random.seed(315)\n",
    "\n",
    "# Check for GPU availability\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "print(f'Using device: {device}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c50a7c28",
   "metadata": {},
   "source": [
    "## 1. Load preprocessed data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "342e5a42",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training samples: 15480\n",
      "Testing samples: 5160\n",
      "Features: ['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']\n",
      "Label: MedHouseVal\n"
     ]
    }
   ],
   "source": [
    "data = pd.read_pickle('https://gperdrizet.github.io/FSA_devops/assets/data/unit4/preprocessed_housing_data.pkl')\n",
    "\n",
    "training_df = data['training_df']\n",
    "testing_df = data['testing_df']\n",
    "features = data['features']\n",
    "label = data['label']\n",
    "\n",
    "print(f'Training samples: {len(training_df)}')\n",
    "print(f'Testing samples: {len(testing_df)}')\n",
    "print(f'Features: {features}')\n",
    "print(f'Label: {label}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2e9ea1f",
   "metadata": {},
   "source": [
    "## 2. PyTorch nn.Sequential model\n",
    "\n",
    "The `nn.Sequential` container is the simplest way to build a neural network in PyTorch. It allows you to create models layer-by-layer in a linear stack, similar to Keras Sequential API."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb8ad557",
   "metadata": {},
   "source": [
    "### 2.1. Create PyTorch Tensors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "908d2ab1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X_train shape: torch.Size([15480, 8])\n",
      "y_train shape: torch.Size([15480, 1])\n"
     ]
    }
   ],
   "source": [
    "X_train = torch.tensor(training_df[features].values, dtype=torch.float32).to(device)\n",
    "y_train = torch.tensor(training_df[label].values, dtype=torch.float32).unsqueeze(1).to(device)\n",
    "\n",
    "X_test = torch.tensor(testing_df[features].values, dtype=torch.float32).to(device)\n",
    "y_test = torch.tensor(testing_df[label].values, dtype=torch.float32).unsqueeze(1).to(device)\n",
    "\n",
    "print(f'X_train shape: {X_train.shape}')\n",
    "print(f'y_train shape: {y_train.shape}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa0c98e7",
   "metadata": {},
   "source": [
    "### 2.2 Build DNN model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "880ed4fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequential(\n",
      "  (0): Linear(in_features=8, out_features=32, bias=True)\n",
      "  (1): ReLU()\n",
      "  (2): Dropout(p=0.2, inplace=False)\n",
      "  (3): Linear(in_features=32, out_features=64, bias=True)\n",
      "  (4): ReLU()\n",
      "  (5): Dropout(p=0.2, inplace=False)\n",
      "  (6): Linear(in_features=64, out_features=32, bias=True)\n",
      "  (7): ReLU()\n",
      "  (8): Dropout(p=0.2, inplace=False)\n",
      "  (9): Linear(in_features=32, out_features=1, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "sequential_model = nn.Sequential(\n",
    "    nn.Linear(8, 32), # Similar to 'Dense' in TensorFlow\n",
    "    nn.ReLU(),        # Activation function for previous layer\n",
    "    nn.Dropout(0.2),\n",
    "    nn.Linear(32, 64),\n",
    "    nn.ReLU(),\n",
    "    nn.Dropout(0.2),\n",
    "    nn.Linear(64, 32),\n",
    "    nn.ReLU(),\n",
    "    nn.Dropout(0.2),\n",
    "    nn.Linear(32, 1)\n",
    ").to(device)\n",
    "\n",
    "print(sequential_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "598d1f7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "criterion = nn.MSELoss()\n",
    "optimizer = optim.Adam(sequential_model.parameters(), lr=1e-2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "ac71f3d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_model(\n",
    "        model: nn.Sequential,\n",
    "        X_train: torch.tensor,\n",
    "        y_train: torch.tensor,\n",
    "        criterion: nn.MSELoss,\n",
    "        optimizer: optim.Adam,\n",
    "        epochs: int = 100\n",
    "):\n",
    "    '''Training loop in PyTorch'''\n",
    "\n",
    "    history = {'loss': []}\n",
    "\n",
    "    for epoch in range(epochs):\n",
    "\n",
    "        # Initialization\n",
    "        model.train()\n",
    "        optimizer.zero_grad()\n",
    "\n",
    "        # Forward pass\n",
    "        predictions = model(X_train)\n",
    "\n",
    "        # Calculate loss\n",
    "        loss = criterion(predictions, y_train)\n",
    "\n",
    "        # Save loss in history\n",
    "        history['loss'].append(loss.item())\n",
    "\n",
    "        # Backwards pass\n",
    "        loss.backward()\n",
    "\n",
    "        # Update weights\n",
    "        optimizer.step()\n",
    "\n",
    "        print(f'Epoch: {epoch}, loss: {loss.item():.4f}')\n",
    "\n",
    "    return history"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab8f98c6",
   "metadata": {},
   "source": [
    "### 2.4. Train model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "2183f6bd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0, loss: 0.5258\n",
      "Epoch: 1, loss: 0.5103\n",
      "Epoch: 2, loss: 0.4952\n",
      "Epoch: 3, loss: 0.4878\n",
      "Epoch: 4, loss: 0.4743\n",
      "Epoch: 5, loss: 0.4597\n",
      "Epoch: 6, loss: 0.4573\n",
      "Epoch: 7, loss: 0.4533\n",
      "Epoch: 8, loss: 0.4399\n",
      "Epoch: 9, loss: 0.4301\n",
      "Epoch: 10, loss: 0.4216\n",
      "Epoch: 11, loss: 0.4121\n",
      "Epoch: 12, loss: 0.4112\n",
      "Epoch: 13, loss: 0.4113\n",
      "Epoch: 14, loss: 0.3998\n",
      "Epoch: 15, loss: 0.3983\n",
      "Epoch: 16, loss: 0.3902\n",
      "Epoch: 17, loss: 0.3843\n",
      "Epoch: 18, loss: 0.3827\n",
      "Epoch: 19, loss: 0.3784\n",
      "Epoch: 20, loss: 0.3731\n",
      "Epoch: 21, loss: 0.3693\n",
      "Epoch: 22, loss: 0.3675\n",
      "Epoch: 23, loss: 0.3613\n",
      "Epoch: 24, loss: 0.3615\n",
      "Epoch: 25, loss: 0.3621\n",
      "Epoch: 26, loss: 0.3572\n",
      "Epoch: 27, loss: 0.3516\n",
      "Epoch: 28, loss: 0.3500\n",
      "Epoch: 29, loss: 0.3465\n",
      "Epoch: 30, loss: 0.3441\n",
      "Epoch: 31, loss: 0.3425\n",
      "Epoch: 32, loss: 0.3421\n",
      "Epoch: 33, loss: 0.3379\n",
      "Epoch: 34, loss: 0.3374\n",
      "Epoch: 35, loss: 0.3352\n",
      "Epoch: 36, loss: 0.3338\n",
      "Epoch: 37, loss: 0.3328\n",
      "Epoch: 38, loss: 0.3266\n",
      "Epoch: 39, loss: 0.3260\n",
      "Epoch: 40, loss: 0.3241\n",
      "Epoch: 41, loss: 0.3266\n",
      "Epoch: 42, loss: 0.3253\n",
      "Epoch: 43, loss: 0.3191\n",
      "Epoch: 44, loss: 0.3200\n",
      "Epoch: 45, loss: 0.3154\n",
      "Epoch: 46, loss: 0.3152\n",
      "Epoch: 47, loss: 0.3079\n",
      "Epoch: 48, loss: 0.3128\n",
      "Epoch: 49, loss: 0.3092\n",
      "Epoch: 50, loss: 0.3055\n",
      "Epoch: 51, loss: 0.3096\n",
      "Epoch: 52, loss: 0.3082\n",
      "Epoch: 53, loss: 0.3050\n",
      "Epoch: 54, loss: 0.3074\n",
      "Epoch: 55, loss: 0.3034\n",
      "Epoch: 56, loss: 0.3024\n",
      "Epoch: 57, loss: 0.2991\n",
      "Epoch: 58, loss: 0.2980\n",
      "Epoch: 59, loss: 0.2994\n",
      "Epoch: 60, loss: 0.2942\n",
      "Epoch: 61, loss: 0.2965\n",
      "Epoch: 62, loss: 0.2927\n",
      "Epoch: 63, loss: 0.2936\n",
      "Epoch: 64, loss: 0.2945\n",
      "Epoch: 65, loss: 0.2934\n",
      "Epoch: 66, loss: 0.2928\n",
      "Epoch: 67, loss: 0.2910\n",
      "Epoch: 68, loss: 0.2904\n",
      "Epoch: 69, loss: 0.2888\n",
      "Epoch: 70, loss: 0.2929\n",
      "Epoch: 71, loss: 0.2854\n",
      "Epoch: 72, loss: 0.2877\n",
      "Epoch: 73, loss: 0.2859\n",
      "Epoch: 74, loss: 0.2836\n",
      "Epoch: 75, loss: 0.2828\n",
      "Epoch: 76, loss: 0.2818\n",
      "Epoch: 77, loss: 0.2798\n",
      "Epoch: 78, loss: 0.2816\n",
      "Epoch: 79, loss: 0.2804\n",
      "Epoch: 80, loss: 0.2801\n",
      "Epoch: 81, loss: 0.2796\n",
      "Epoch: 82, loss: 0.2809\n",
      "Epoch: 83, loss: 0.2796\n",
      "Epoch: 84, loss: 0.2780\n",
      "Epoch: 85, loss: 0.2778\n",
      "Epoch: 86, loss: 0.2780\n",
      "Epoch: 87, loss: 0.2753\n",
      "Epoch: 88, loss: 0.2770\n",
      "Epoch: 89, loss: 0.2759\n",
      "Epoch: 90, loss: 0.2745\n",
      "Epoch: 91, loss: 0.2749\n",
      "Epoch: 92, loss: 0.2718\n",
      "Epoch: 93, loss: 0.2753\n",
      "Epoch: 94, loss: 0.2718\n",
      "Epoch: 95, loss: 0.2757\n",
      "Epoch: 96, loss: 0.2759\n",
      "Epoch: 97, loss: 0.2708\n",
      "Epoch: 98, loss: 0.2729\n",
      "Epoch: 99, loss: 0.2699\n"
     ]
    }
   ],
   "source": [
    "sequential_history = train_model(\n",
    "    model = sequential_model,\n",
    "    X_train = X_train,\n",
    "    y_train = y_train,\n",
    "    criterion = criterion,\n",
    "    optimizer = optimizer \n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c789a57a",
   "metadata": {},
   "source": [
    "### 2.5. Plot learning curves"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "5db9c174",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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2vqO/46IiN1eRHQHcrkcEyZMwA8//IBz587V2v/3V8hvtbj8vYUlNjYWMTExLV9oI4wcORIKhQIrV67U2/7JJ5806PwJEyZAFEUsXbq01r5b925tbQ1HR0ccPHhQb/+nn37a6HonTJgAuVyOjRs3YsuWLRgzZgwsLCx0+wMDA+Hn54f3338fxcXFtc7/52v+RMaOLT9EBABYu3Ytdu7cWWv73Llz8c4772Dfvn0IDg7GU089he7duyM/Px8nT57Enj17kJ+fDwAYM2YMtm7divHjx2P06NFITk7GqlWr0L179zq/lKXi4uKCuXPn4oMPPsADDzyA8PBwnD59Gjt27ICjo+MdBy2PGDECU6ZMwccff4zLly8jPDwcWq0Whw4dwogRI/Dcc88BAJ588km88847ePLJJxEUFISDBw/i0qVLja7X2dkZI0aMwIcffoiioiJMnDhRb79MJsPnn3+OUaNGoUePHoiIiICHhwfS09Oxb98+WFtb45dffmn07yVqrxh+iAgAarWC3DJ9+nR4enoiLi4Or7/+OrZu3YpPP/0UDg4O6NGjh97Yk+nTpyMzMxOfffYZdu3ahe7du2PDhg3YsmUL9u/f30p30jDvvvsuzM3NsWbNGuzZswchISH4/fffMXjwYKhUqjuev27dOvTu3RtffPEFXn75ZdjY2CAoKAgDBw7UHbN48WLk5OTg+++/x3fffYdRo0Zhx44dcHZ2bnS9EydOxJ49e2BlZYX777+/1v7hw4cjJiYGb7zxBj755BMUFxfD1dUVwcHBePrppxv9+4jaM0FszRGARERtWEFBAezs7PDmm2/i1VdflbocImohHPNDREaprKys1rbly5cDqGlFIaL2i91eRGSUNm/ejPXr1+P++++HpaUlDh8+jI0bN+K+++7DoEGDpC6PiFoQww8RGaXevXtDoVBg2bJlUKvVukHQb775ptSlEVEL45gfIiIiMioc80NERERGheGHiIiIjArH/NRBq9UiIyMDVlZWTVqhmYiIiFqPKIooKiqCu7s7ZLL623cYfuqQkZEBLy8vqcsgIiKiu5CWlgZPT8969zP81MHKygpAzcOztraWuBoiIiJqCLVaDS8vL933eH0Yfupwq6vL2tqa4YeIiMjA3GnICgc8ExERkVFh+CEiIiKjwvBDRERERoXhh4iIiIwKww8REREZFYYfIiIiMioMP0RERGRUGH6IiIjIqDD8EBERkVFh+CEiIiKjwvBDRERERoXhh4iIiIwKw08r0mpFXMkuQl5xhdSlEBERGS2Gn1Y055uTCP3wILafuSF1KUREREaL4acVdXOzBgCcTL0pcSVERETGi+GnFfXraAuA4YeIiEhKDD+tKMDLFoIApOWXIbuoXOpyiIiIjBLDTyuyUpmgs7MVAODktQJpiyEiIjJSDD+trF9HOwDAKXZ9ERERSYLhp5X162ALADhxjeGHiIhICgw/rexWy8+Z9EJUVmslroaIiMj4MPy0Ml9HC9iam6CyWouEG2qpyyEiIjI6DD+tTBAE9OtQ0/pzkl1fRERErY7hRwK6cT8c9ExERNTqGH4koHvjiy0/RERErY7hRwJ9PG0hE4CMwnLcKCyTuhwiIiKjwvAjAQulAl1d/1zni5MdEhERtSqGH4kE/tn1xXW+iIiIWhfDj0S4yCkREZE0GH4kcut19/PpapRXaSSuhoiIyHgw/Eikg705HCxMUanR4nxGodTlEBERGQ2GH4kIgqB75Z2DnomIiFoPw4+EdDM9c9wPERFRq2H4kdDfV3gXRVHaYoiIiIwEw4+EenvaQiETkF1Uges3OdkhERFRa2D4kZCZqRw93P+c7JBdX0RERK2C4UdigR3tAQDHUxh+iIiIWgPDj8SCvGsGPR/nIqdEREStguFHYreWuUjMVKOovEriaoiIiNo/hh+JuVir4GlnBq0IxKcVSF0OERFRu8fw0wYE/dn6w3E/RERELY/hpw0I9K4Z9HyC436IiIhaHMNPG3Cr5edU6k1otJzskIiIqCUx/LQBnV2sYKVUoKRSg4uZaqnLISIiatckDz8rVqyAt7c3VCoVgoODERcXV++x69evhyAIeh+VSqV3jCiKWLx4Mdzc3GBmZobQ0FBcvny5pW+jSeQyAQF/W+qCiIiIWo6k4Wfz5s2IjIzEkiVLcPLkSfTp0wdhYWHIzs6u9xxra2vcuHFD97l27Zre/mXLluHjjz/GqlWrEBsbCwsLC4SFhaG8vLylb6dJgjjZIRERUauQNPx8+OGHeOqppxAREYHu3btj1apVMDc3x9q1a+s9RxAEuLq66j4uLi66faIoYvny5fjPf/6DBx98EL1798ZXX32FjIwMbNu2rRXu6O7dmuyQLT9EREQtS7LwU1lZiRMnTiA0NPSvYmQyhIaGIiYmpt7ziouL0bFjR3h5eeHBBx/E+fPndfuSk5ORmZmpd00bGxsEBwff9poVFRVQq9V6n9YW4GULmQCkF5Qhs7Btt1IREREZMsnCT25uLjQajV7LDQC4uLggMzOzznO6dOmCtWvX4qeffsKGDRug1WoxcOBAXL9+HQB05zXmmgAQFRUFGxsb3cfLy6spt3ZXLJQKdHOrWeT0+LX8Vv/9RERExkLyAc+NERISgqlTpyIgIADDhg3D1q1b4eTkhM8++6xJ1124cCEKCwt1n7S0tGaquHE42SEREVHLkyz8ODo6Qi6XIysrS297VlYWXF1dG3QNExMT9O3bF1euXAEA3XmNvaZSqYS1tbXeRwq3Jjs8mcrwQ0RE1FIkCz+mpqYIDAxEdHS0bptWq0V0dDRCQkIadA2NRoOzZ8/Czc0NAODj4wNXV1e9a6rVasTGxjb4mlK61fJzPkON0spqiashIiJqnyTt9oqMjMSaNWvw5Zdf4sKFC5g9ezZKSkoQEREBAJg6dSoWLlyoO/7111/H77//jqtXr+LkyZN44okncO3aNTz55JMAat4EmzdvHt588038/PPPOHv2LKZOnQp3d3eMGzdOiltsFHdbM7jZqKDRilzklIiIqIUopPzlEydORE5ODhYvXozMzEwEBARg586dugHLqampkMn+ymc3b97EU089hczMTNjZ2SEwMBB//PEHunfvrjtm/vz5KCkpwaxZs1BQUIDBgwdj586dtSZDbKsCO9ph+5kbOJZ8EwP9HKUuh4iIqN0RRFHkYlL/oFarYWNjg8LCwlYf/7MpLhULtp6Fp50Z9r80HAq5QY1JJyIikkxDv7/5zdrGPBjgAQcLU1y/WYbtZ25IXQ4REVG7w/DTxpiZyjFjsA8AYOX+JLBhjoiIqHkx/LRBT9zTEZZKBRKzirD3Yv3rnBEREVHjMfy0QTZmJph8TwcANa0/RERE1HwYftqomYN8YCqX4fi1m4hL5nIXREREzYXhp41ytlZhQqAnAGDl/isSV0NERNR+MPy0YU8P9YVMAPYl5uDCjdZfaZ6IiKg9Yvhpw7wdLXB/r5qlOzj2h4iIqHkw/LRxs4f7AQC2n8lAlrpc4mqIiIgMH8NPG9fD3QZ9PG2gFYEjV3KlLoeIiMjgMfwYgJA/1/j6IylP4kqIiIgMH8OPARjo5wAAiEnK44zPRERETcTwYwCCvO1gIheQXlCG1PxSqcshIiIyaAw/BsDcVIG+XnYAalp/iIiI6O4x/BiIkD+7vjjuh4iIqGkYfgzE38MPx/0QERHdPYYfA9G3gy2UChlyiytwJbtY6nKIiIgMFsOPgVAq5OjvbQ+AXV9ERERNwfBjQEL+9so7ERER3R2GHwOiCz9X86DVctwPERHR3WD4MSC9PWxgqVSgsKwKCVzlnYiI6K4w/BgQhVyGAT41437Y9UVERHR3GH4MzEDdK+9c5JSIiOhuMPwYmHt8a8JPXHI+qjRaiashIiIyPAw/Bqa7mzVszExQUqnB2fRCqcshIiIyOAw/BkYmExDiy1feiYiI7hbDjwHifD9ERER3j+HHAAX71rzxdTL1Jqo57oeIiKhRGH4MUGdnK1irFCit1HC+HyIiokZi+DFAMpmAoD/X+TqWclPiaoiIiAwLw4+BCvK2AwAcS86XuBIiIiLDwvBjoAboWn7yIYpc54uIiKihGH4MVC9PG5gqZMgrqURybonU5RARERkMhh8DpVTIEeBpC6Cm9YeIiIgahuHHgOnG/XDQMxERUYMx/Biw/j5/jfshIiKihmH4MWCBHe0gCMC1vFJkq8ulLoeIiMggMPwYMGuVCbq6WgNg1xcREVFDMfwYuAG6cT/s+iIiImoIhh8DF+TNcT9ERESNwfBj4Pr/GX4u3FCjqLxK4mqIiIjaPoYfA+dqo4KXvRm0InAytUDqcoiIiNo8hp924FbrD9f5IiIiujOGn3ZgAMf9EBERNRjDTztwa9BzfFoBKqo1EldDRETUtjH8tAN+ThawtzBFRbUWr2w9BzUHPhMREdWL4acdEAQBc0f6QxCAH05eR/hHB3Hoco7UZREREbVJDD/txLSB3tg8KwQdHcyRUViOKV/E4ZUfz6K4olrq0oiIiNoUhp92ZICPPXbMHYJpIR0BAN/GpmLS6hiUMAARERHpMPy0M+amCix9sCe+fSoYDhamOJeuxgsbT0GjFaUujYiIqE1g+GmnBvo5Ys20ICgVMkRfzMYb2xOkLomIiKhNYPhpx/p1sMNHEwMAAOv/SMG6I8nSFkRERNQGMPy0c/f3csOCUV0BAK9vT8DuhCyJKyIiIpIWw48ReHqoLx4b4AVRBF7YeApXc4qlLomIiEgykoefFStWwNvbGyqVCsHBwYiLi2vQeZs2bYIgCBg3bpze9unTp0MQBL1PeHh4C1RuOARBwOsP9kR/bzuUVWnw8+kMqUsiIiKSjKThZ/PmzYiMjMSSJUtw8uRJ9OnTB2FhYcjOzr7teSkpKXjppZcwZMiQOveHh4fjxo0bus/GjRtbonyDYiKXYXxfTwDAH1fyJK6GiIhIOpKGnw8//BBPPfUUIiIi0L17d6xatQrm5uZYu3ZtvedoNBpMnjwZS5cuha+vb53HKJVKuLq66j52dnYtdQsGZaCfAwDgVNpNlFZy7h8iIjJOkoWfyspKnDhxAqGhoX8VI5MhNDQUMTEx9Z73+uuvw9nZGTNnzqz3mP3798PZ2RldunTB7NmzkZd3+5aOiooKqNVqvU971NHBHB62ZqjSiDieclPqcoiIiCQhWfjJzc2FRqOBi4uL3nYXFxdkZmbWec7hw4fxxRdfYM2aNfVeNzw8HF999RWio6Px7rvv4sCBAxg1ahQ0mvpXO4+KioKNjY3u4+XldXc31cYJgoCQP1t//khi1xcRERknhdQFNFRRURGmTJmCNWvWwNHRsd7jJk2apPvnXr16oXfv3vDz88P+/fsxcuTIOs9ZuHAhIiMjdT+r1ep2G4AG+jng+xPXEZOUK3UpREREkpAs/Dg6OkIulyMrS3/emaysLLi6utY6PikpCSkpKRg7dqxum1arBQAoFAokJibCz8+v1nm+vr5wdHTElStX6g0/SqUSSqWyKbdjMG61/JxNL0RhWRVszEwkroiIiKh1SdbtZWpqisDAQERHR+u2abVaREdHIyQkpNbxXbt2xdmzZxEfH6/7PPDAAxgxYgTi4+Prbam5fv068vLy4Obm1mL3YkjcbMzg62gBrQjEJedLXQ4REVGrk7TbKzIyEtOmTUNQUBAGDBiA5cuXo6SkBBEREQCAqVOnwsPDA1FRUVCpVOjZs6fe+ba2tgCg215cXIylS5diwoQJcHV1RVJSEubPn49OnTohLCysVe+tLQvxc8DV3BL8kZSLe7u73PkEIiKidkTS8DNx4kTk5ORg8eLFyMzMREBAAHbu3KkbBJ2amgqZrOGNU3K5HGfOnMGXX36JgoICuLu747777sMbb7xhNN1aDTHQzxHfxKZyvh8iIjJKgiiKotRFtDVqtRo2NjYoLCyEtbW11OU0u7ziCgS+uQcAcOzVUDhZMRgSEZHha+j3t+TLW1Drc7BUoqurFQDg6FW2/hARkXFh+DFSgzrVTBfA+X6IiMjYMPwYqVtLXXC+HyIiMjYMP0ZqgI895DIBKXmlSC8ok7ocIiKiVsPwY6SsVCbo5WEDAIhh1xcRERkRhh8jdqvr648r7PoiIiLjwfBjxAb61Qx6PnwlF1otZzwgIiLjwPBjxIK87WClUiC7qAKxXOqCiIiMBMOPEVOZyDGqZ80isj+fTpe4GiIiotbB8GPkxgV4AAB+PXMDFdUaiashIiJqeQw/Ri7Y1wEu1kqoy6uxPzFH6nKIiIhaHMOPkZPLBDzQxx0A8FM8u76IiKj9Y/ghPPhn19eeC9lQl1dJXA0REVHLYvgh9HC3hp+TBSqrtdh1LlPqcoiIiFoUww9BEATdwOef4jMkroaIiKhlMfwQgL+6vv5IykW2ulziaoiIiFoOww8BADo4mKNvB1toReDn02z9ISKi9ovhh3TY9UVERMaA4Yd0Rvd2g1wm4Gx6IZJyiqUuh4iIqEUw/JCOo6USQ/xrFjv94cR1iashIiJqGQw/pOfRIC8AwHfHr6OyWitxNURERM2P4Yf03NvdBU5WSuQWV+D3BM75Q0RE7Q/DD+kxkcswqX9N6883R1MlroaIiKj5MfxQLZMGdIBMAGKu5uFKNgc+ExFR+8LwQ7V42JrhX12dAQDfxrL1h4iI2heGH6rT5Hs6AgC+P5GGskqNxNUQERE1H4YfqtMwfyd42plBXV6N7Wc46SEREbUfDD9UJ5lMwOPBHQAAG9j1RURE7QjDD9Xr0SAvmMgFnE4rwLn0QqnLISIiahYMP1QvR0slRvV0AwB8E3tN4mqIiIiaB8MP3dbkP7u+tp3KQHFFtcTVEBERNR3DD93WAB97+DpaoKxKg13nOOMzEREZPoYfui1BEDCurwcA4MdT6RJXQ0RE1HQMP3RH4/8MP0eScpFZWC5xNURERE3D8EN35GVvjv7edhBF4Kd4tv4QEZFhY/ihBhnf1xMAu76IiMjwMfxQg4zu5QZTuQwXM4uQkKGWuhwiIqK7xvBDDWJjboKR3WoWO/3x1HWJqyEiIrp7DD/UYLcGPv8UnwGNVpS4GiIiorvD8EMNNryLM+zMTZBdVIEjV3KlLoeIiOiuMPxQg5kqZBjT2x0ABz4TEZHhYvihRhnfr6bra+e5TJRwuQsiIjJADD/UKH29bOHz53IXi346h/2J2Vzzi4iIDIpC6gLIsAiCgIcDPfHerkRsPZmOrSfTIZcJ6OVhA39nS2i0Iio1WlRragZEzxjsgwE+9hJXTURE9BeGH2q0p4b4wsPWDH8k5eLo1Xyk5pciPq0A8WkFtY5NLyjDL88Pbv0iiYiI6sHwQ41mqpBhXF8P3YKn6QVliL2ahxuF5TCRCzCR1/Smvr49AWfTC5GlLoeLtUrKkomIiHQYfqjJPGzN8FA/z1rbf4rPQHxaAaIvZOPx4A4SVEZERFQbBzxTi7m3uwsAIPpClsSVEBER/YXhh1rMreUwDl/JRVmlRuJqiIiIajD8UIvp4mIFD1szVFRrcZgzQhMRURvB8EMtRhAEXdfXngR2fRERUdvQqPCzbNkylJWV6X4+cuQIKioqdD8XFRVhzpw5zVcdGbxbXV/RF7Oh5WKoRETUBjQq/CxcuBBFRUW6n0eNGoX09L/WeCotLcVnn33WfNWRwQv2cYClUoHc4gqcSS+UuhwiIqLGhR9RFG/7M9E/mSpkGNbZCQC7voiIqG3gmB9qcbe6vvbwlXciImoDJA8/K1asgLe3N1QqFYKDgxEXF9eg8zZt2gRBEDBu3Di97aIoYvHixXBzc4OZmRlCQ0Nx+fLlFqicGmpEF2fIBOBiZhGu3yyVuhwiIjJyjZ7h+fPPP4elpSUAoLq6GuvXr4ejoyMA6I0HaojNmzcjMjISq1atQnBwMJYvX46wsDAkJibC2dm53vNSUlLw0ksvYciQIbX2LVu2DB9//DG+/PJL+Pj4YNGiRQgLC0NCQgJUKi6xIAU7C1MEdbRHXEo+oi9kY9pAb6lLIiIiIyaIjRi44+3tDUEQ7nhccnJyg64XHByM/v3745NPPgEAaLVaeHl54fnnn8eCBQvqPEej0WDo0KGYMWMGDh06hIKCAmzbtg1ATauPu7s7XnzxRbz00ksAgMLCQri4uGD9+vWYNGlSg+pSq9WwsbFBYWEhrK2tG3QO3d7qg0l4+7eLGOLviK9nBktdDhERtUMN/f5uVMtPSkpKU+vSqaysxIkTJ7Bw4ULdNplMhtDQUMTExNR73uuvvw5nZ2fMnDkThw4d0tuXnJyMzMxMhIaG6rbZ2NggODgYMTEx9YafiooKvVf21Wr13d4W1WNkNxe8/dtFHL2ah7PXC9HL00bqkoiIyEhJNuYnNzcXGo0GLi4uettdXFyQmZlZ5zmHDx/GF198gTVr1tS5/9Z5jbkmAERFRcHGxkb38fLyasytUAP4OVmii4sVqjQixn5yGA99egTbTqWjoprLXhARUetqVPiJiYnB9u3b9bZ99dVX8PHxgbOzM2bNmqXXgtKcioqKMGXKFKxZs0Y3xqi5LFy4EIWFhbpPWlpas16faqyeGogH+rjDRC7gZGoB5m2Ox6B39uLXMzekLo2IiIxIo7q9Xn/9dQwfPhxjxowBAJw9exYzZ87E9OnT0a1bN7z33ntwd3fHa6+9dsdrOTo6Qi6XIytL//XnrKwsuLq61jo+KSkJKSkpGDt2rG6bVqutuQmFAomJibrzsrKy4ObmpnfNgICAemtRKpVQKpV3rJmapqODBT5+rC+yi7phU1wavo1NRaa6HAu2nsHwLk6wUDZ6/D0REVGjNarlJz4+HiNHjtT9vGnTJgQHB2PNmjWIjIzExx9/jO+++65B1zI1NUVgYCCio6N127RaLaKjoxESElLr+K5du+Ls2bOIj4/XfR544AGMGDEC8fHx8PLygo+PD1xdXfWuqVarERsbW+c1SRrOViq8MNIfh/5vBHwcLVBUXo0fT6Xf+UQiIqJm0Ki/at+8eVNvPM2BAwcwatQo3c/9+/dvVJdRZGQkpk2bhqCgIAwYMADLly9HSUkJIiIiAABTp06Fh4cHoqKioFKp0LNnT73zbW1tAUBv+7x58/Dmm2/C399f96q7u7t7rfmASHomchmm3NMRr29PwFcxKZgc3KFBbxMSERE1RaPCj4uLC5KTk+Hl5YXKykqcPHkSS5cu1e0vKiqCiYlJg683ceJE5OTkYPHixcjMzERAQAB27typC1ipqamQyRo3Jnv+/PkoKSnBrFmzUFBQgMGDB2Pnzp2c46eNejjIE+//nohLWcWIuZqHgX7NO56LiIjonxo1z8/s2bNx+vRpvPvuu9i2bRu+/PJLZGRkwNTUFADwzTffYPny5Th27FiLFdwaOM9P6/rPtrPYcDQVYT1c8NmUIKnLISIiA9XQ7+9GNau88cYbUCgUGDZsGNasWYPVq1frgg8ArF27Fvfdd9/dV01GaVqINwBgd0IWl78gIqIW16huL0dHRxw8eBCFhYWwtLSEXC7X279lyxZYWVk1a4HU/vm7WGFQJwccuZKHDUdTsWBUV6lLIiKidqxR4WfGjBkNOm7t2rV3VQwZr2kh3jhyJQ+bjqViXqg/VCbyO59ERER0FxoVftavX4+OHTuib9++aMRQIaI7GtnNBZ52Zrh+sww/x2fg0f6cZZuIiFpGo8LP7NmzsXHjRiQnJyMiIgJPPPEE7O3tW6o2MiJymYAp93RE1I6LWP9HCh4J8uRr70RE1CIaNeB5xYoVuHHjBubPn49ffvkFXl5eePTRR7Fr1y62BFGTPRrkBaVChoQbahy9mi91OURE1E41emFTpVKJxx57DLt370ZCQgJ69OiBOXPmwNvbG8XFxS1RIxkJOwtTPBzoCQB467cEaLQM1ERE1PyatKq7TCaDIAgQRREaDVfnpqb7972dYaVU4Fy6Gt8d5wKzRETU/BodfioqKrBx40bce++96Ny5M86ePYtPPvkEqampsLS0bIkayYg4Wiox797OAID3diWisLRK4oqIiKi9aVT4mTNnDtzc3PDOO+9gzJgxSEtLw5YtW3D//fc3ehkKovpMDemITs6WyC+pxEd7LkldDhERtTONWt5CJpOhQ4cO6Nu3723fxNm6dWuzFCcVLm8hvUOXczDlizjIZQJ+e2EIurhy8kwiIrq9hn5/N+pV96lTp/L1Y2oVQ/ydENbDBbvOZ+H17eexYWYw/9sjIqJm0ehJDolay39Gd8e+xBwcuZKHnecyMaqXm9QlERFRO8CBOtRmedmb45mhvgCAuZvi8X/fn8HlrCKJqyIiIkPH8ENt2uzhnTC4kyMqNVpsPp6Gez86iIh1cfgjKVfq0oiIyEA1asCzseCA57bnxLV8rDmYjF0Jmbj1X+zLYV3w7IhO0hZGRERtRkO/v9nyQwYhsKM9Vk0JxL4Xh+OxATWLnr63KxF7ErIkroyIiAwNww8ZFG9HC0Q91BtT7ukIAJi3OR5XsjkOiIiIGo7hhwzS4rHdMcDHHsUV1XjqqxMoLONM0ERE1DAMP2SQTOQyfDq5HzxszZCcW4IXNp7iQqhERNQgDD9ksBwtlfhsSiBUJjIcuJSDZbsuSl0SEREZAIYfMmg9PWyw7OE+AIDPDlxFTFKexBUREVFbx/BDBu+BPu54PLgDAGDB1jMoq9RIXBEREbVlDD/ULiwc1RVuNipcyyvFB78nSl0OERG1YQw/1C5YqUzw9vheAIC1R5JxKvWmxBUREVFbxfBD7caIrs4Y39cDWhGY//0ZVFSz+4uIiGpj+KF2ZfGY7nC0NMXl7GKs2HtF6nKIiKgNYvihdsXOwhRLH+gJAPh0fxLOpRdKXBEREbU1DD/U7tzfyxXhPVxRrRXx1FfHkVFQJnVJRETUhjD8ULsjCAKiHuoFPycL3Cgsx5QvYnGzpFLqsoiIqI1g+KF2yc7CFF/NDIabjQpJOSWIWH8MJRXVUpdFRERtAMMPtVsetmb4euYA2JqbID6tALO/OYnKaq3UZRERkcQYfqhd6+RshXXT+8PMRI6Dl3Iwb/MpFJSyC4yIyJgx/FC717eDHVZNCYRCJuC3s5kYsmwfVuy7gtJKdoMRERkjhh8yCsM6O+GrmQPQ1dUKReXVeG9XIoa9tx9fH72GKg27woiIjIkgiqIodRFtjVqtho2NDQoLC2FtbS11OdSMtFoRP5/OwAe7E5GWX/MK/MOBnnj/kT4SV0ZERE3V0O9vtvyQUZHJBIzr64HoyOFYMrY7BAH4/sR1xKcVSF0aERG1EoYfMkqmChkiBvngob6eAIC3fk0AG0GJiIwDww8ZtZfDukBlIsOxlJvYdT5T6nKIiKgVMPyQUXO1UWHWEF8AwDs7LnIeICIiI8DwQ0bv6WF+cLJSIiWvFF8fvSZ1OURE1MIYfsjoWSgVePHezgCAj6Mv33ESxLJKDfK5VhgRkcFSSF0AUVvwSJAX1v+RgouZRfho9yVEDPJBfmkl8osrkVdSgeTcUlzOKsLl7GKk3SyFTBCw6olA3NvdRerSiYiokTjPTx04z49xOngpB1PXxjX4eDtzE+yaNxTO1qoWrIqIiBqqod/fbPkh+tPQzk54oI87fj6dAQtTOewsTOFgYQo7C1N0sDeHv4sV/J0t4e1ggRnrjyHhhhovf38G6yP6QxAEqcsnIqIGYstPHdjyY9yqNFqYyG8/HO5yVhHG/O8wKqq1eP3BHpga4t06xRERUb04wzPRXbpT8AEAfxcrLBzVFQDw1q8XcCW7qKXLIiKiZsLwQ3SXpoZ4Y2hnJ1RUazF3UzznCCIiMhAMP0R3SSYT8N7DvWFnboLzGWp8tOeS1CUREVEDMPwQNYGLtQpRD/UCAHx2IAnn0gslroiIiO6E4YeoicJ7umFMbzdoRWDh1rOo1rD7i4ioLWP4IWoGi8d2h7VKgbPphfgyhktkEBG1ZQw/RM3A2UqFBaO6AQA++D0RGQVlEldERET1YfghaiaT+nshqKMdSis1WPzTeXAKLSKitkny8LNixQp4e3tDpVIhODgYcXH1Ly+wdetWBAUFwdbWFhYWFggICMDXX3+td8z06dMhCILeJzw8vKVvgwgymYCoh3rBRC5gz4Us7DqfKXVJRERUB0nDz+bNmxEZGYklS5bg5MmT6NOnD8LCwpCdnV3n8fb29nj11VcRExODM2fOICIiAhEREdi1a5feceHh4bhx44bus3Hjxta4HSL4u1jhmWF+AIAlP59HbnGFxBUREdE/Sbq8RXBwMPr3749PPvkEAKDVauHl5YXnn38eCxYsaNA1+vXrh9GjR+ONN94AUNPyU1BQgG3btt11XVzegpqivEqDUf89hOTcEpiZyDGxvxeeHOIDTztzqUsjImrX2vzyFpWVlThx4gRCQ0P/KkYmQ2hoKGJiYu54viiKiI6ORmJiIoYOHaq3b//+/XB2dkaXLl0we/Zs5OXlNXv9RPVRmcix4vF+6OlhjbIqDdb/kYJh7+3HvzfHcxkMIqI2QLJV3XNzc6HRaODi4qK33cXFBRcvXqz3vMLCQnh4eKCiogJyuRyffvop7r33Xt3+8PBwPPTQQ/Dx8UFSUhJeeeUVjBo1CjExMZDL5XVes6KiAhUVf3VPqNXqJt4dGbvu7tb45bnBOHwlF6sOJOHIlTz8eCodO87dwLrpAxDi5yB1iURERkuy8HO3rKysEB8fj+LiYkRHRyMyMhK+vr4YPnw4AGDSpEm6Y3v16oXevXvDz88P+/fvx8iRI+u8ZlRUFJYuXdoa5ZMREQQBQ/ydMMTfCWeuF+CtXy8gNjkfM9Yfw/qI/gj2ZQAiIpKCZN1ejo6OkMvlyMrK0tuelZUFV1fXes+TyWTo1KkTAgIC8OKLL+Lhhx9GVFRUvcf7+vrC0dERV65cqfeYhQsXorCwUPdJS0tr/A0R3UZvT1t8OWMAhnZ2QlmVBhHrj+FYSr7UZRERGSXJwo+pqSkCAwMRHR2t26bVahEdHY2QkJAGX0er1ep1Wf3T9evXkZeXBzc3t3qPUSqVsLa21vsQNTeViRyrpwRiiL8jSis1mL42DieuMQAREbU2SV91j4yMxJo1a/Dll1/iwoULmD17NkpKShAREQEAmDp1KhYuXKg7PioqCrt378bVq1dx4cIFfPDBB/j666/xxBNPAACKi4vx8ssv4+jRo0hJSUF0dDQefPBBdOrUCWFhYZLcI9HfqUzkWDM1CIM6OaCkUoNpa4/h8OVcqcsiIjIqko75mThxInJycrB48WJkZmYiICAAO3fu1A2CTk1NhUz2Vz4rKSnBnDlzcP36dZiZmaFr167YsGEDJk6cCACQy+U4c+YMvvzySxQUFMDd3R333Xcf3njjDSiVSknukeifVCZyfD61P2asP4aYq3l44otYRAzyxv+Fd4XKpO5B+URE1HwkneenreI8P9Qayio1eOu3BGw4mgoA8HOywIePBqCPl620hRERGag2P88PkbEzM5XjzXG98OWMAXC2UiIppwQPrfwD7+9KhLq8SuryiIjaLbb81IEtP9TaCkor8Z9t57D9zA0AgJVKgYiB3ogY5AM7C1OJqyMiMgwN/f5m+KkDww9JZcfZG/hg9yVcyS4GAJibyvHEPR0x0M8BzlYqOFkp4WBhCplMkLhSIqK2h+GnCRh+SEparYhd5zPxv71XkHCj9mzjcpmAnu7WWDG5H9cLIyL6G4afJmD4obZAFEXsS8zGt7FpSC8oQ05ROfJKKnHr/9iurlb4YfZAWCgNbqJ2IqIWwfDTBAw/1FZVa7RIzi3BY2tikVtcgfu6u2DVE4HsBiMiAt/2ImqXFHIZ/F2ssHpqIEzlMvyekIUPd1+SuiwiIoPC8ENkgPp1sEPUQ70AAJ/su4Kf4tMlroiIyHBwsACRgZoQ6IlL2UX47MBVzP/+DKo1Irq7W8PbwQJmppwpmoioPgw/RAZsflhXXMkqRvTFbLy45bRuu4u1Et3crBF5b2f09rSVrkAiojaIA57rwAHPZEiKK6rx/q5EnEorQEpuCQrL/podWiYATw3xxb/v7cx1w4io3ePbXk3A8EOGrKC0EldzS7D+SAp+Pp0BAPB2MMe7E3oj2NdB4uqIiFoOw08TMPxQe7EnIQuvbjuLLHUFAGBcgDumhHREvw52EAS+Hk9E7QvDTxMw/FB7UlhWhXd2XMDGuDTdti4uVnhsgBfG9/OEjZmJhNURETUfhp8mYPih9ig+rQAbjl7D9jMZKK/SAgCUChkGdXLEiK7O+FdXZ3jYmklcJRHR3WP4aQKGH2rPCsuqsO1UOr6NTUViVpHevi4uVogY5I1JAzpIVB0R0d1j+GkChh8yBqIoIjGrCHsvZmPfxWycuHYT2j//NPhhdggCO9pLWyARUSMx/DQBww8Zo4LSSiz66Tx+OZ2BPl62+HH2QK4ZRkQGhWt7EVGj2JqbYtHobjA3leN0WgF+OZMhdUlERC2C4YeIdJytVZgz3A8A8O6Oiyiv0khcERFR82P4ISI9Tw7xhbuNChmF5fjicLLU5RARNTuGHyLSozKRY354VwDAp/uuILuoXOKKiIiaF8MPEdXyQB939PGyRUmlBh/+fknqcoiImhXDDxHVIpMJWDS6GwBg8/E0rD+SjDPXCxo8BujEtZv4+ug15BRVtGSZRER3ha+614GvuhPVePabk/j17A3dz3KZAH9nS9zj64BnhvnB1Uald3yVRouPdl/CygNJEMWa40d0ccLDgZ74V1cXmCr49y0iajmc56cJGH6IahRXVOOzA0k4lVqA8xmFuFlapdunMpFh5mAfPD3MD9YqE1zLK8ELm+JxOq0AAODraIGruSW64+3MTbBkbA+M6+vR2rdBREaC4acJGH6IahNFETcKy3HmegHWHErGiWs3AdSEmvF9PbH5WCpKKjWwVikQ9VBvjO7thivZRfj+RDq2nryO7KIKqExk2PfScLjZcA0xImp+DD9NwPBDdHuiKOL3hCy8u/Mirub81bozwNseH00KqLVAarVGi8fWHMWxlJuY0M8THzzap7VLJiIjwPDTBAw/RA1TrdFi8/E0bIxLRXgPV8we3gnyepbEOJ1WgAdXHAEAbH9+MHp62LRmqURkBBh+moDhh6hlzN10Cj/FZ+AeX3tsfOoeCALXDiOi5sO1vYiozXk5rAtMFTIcvZqPPReypS6HiIwUww8RtRpPO3PMHOwDAIj67QKqNFqJKyIiY8TwQ0Stas5wPzhYmOJqbgm+jU1FWaUG+y5m47WfzyN8+UEs2nYO7I0nopbEMT914Jgfopb19dFrWLTtHFQmMmhFoLJavwVo6QM9MG2gtzTFEZHB4pgfImqzHuvvBX9nS5RXaVFZrYWHrRkeD+6A6X8Gnrd+vYBz6YXSFklE7ZZC6gKIyPgo5DKsnzEAR67kIrCjHXwdLSAIAkRRREZBGX5PyMKz357E9ucHw0plInW5RNTOsOWHiCThYWuGR4O84OdkqXvlXRAEvPdwH3jYmuFaXikWbD3L8T9E1OwYfoioTbExN8H/Hu8LhUzAr2du4JvYVKlLIqJ2hgOe68ABz0TSW30wCW//dhGmChkCO9jBQqmAlUoBC6Ucgzs5Ibynq9QlElEb09Dvb475IaI26cnBvohLvok9F7IQczVPb9+3sanY++JweDtaSFQdERkyhh8iapNkMgGfTQlETFIe8ksrUVxejZKKavxyJgNnrhfis4NJiHqot9RlEpEBYvghojZLLhMw2N9Rb1vfDrZ4eFUMvj9xHS+M9IebjVmt81LzSlFUUYUe7lw8lYhq44BnIjIoQd72GOBjjyqNiDUHk2vtv5pTjPD/HsTojw/ju+NpElRIRG0dww8RGZznRnQCAHwbdw15xRW67ZXVWszdFI/SSg0A4P9+OIOf4tMlqZGI2i6GHyIyOEP8HdHb0wblVVqsO5Ki2/7B7kScTS+ErbkJxgW4QxSByO9OY+e5G9IVS0RtDsMPERkcQRAwZ3hN68+XMSlQl1fh8OVcfHbgKgDg3Qm98eGjAZjQzxMarYjnN55C9IWsO173Wl4JisqrWrR2IpIeww8RGaT7urugs4slisqr8d89lxH5XTwAYHJwB4T1cIVMJmDZw70xto87qjQiZm84iUOXc+q93nfH0zDsvf0Y8FY05n9/GvFpBZxdmqid4iSHdeAkh0SGYdupdMzbHK/7uZOzJX55bjDMTOW6bVUaLZ779iR2nc+CykSGr2cGo7+3vd51/kjKxdQv4lCt1f/jsLubNaaGdMQjQV6Qy4QWvRciajqu6k5E7d6Y3m7oYG8OADCVy/DxpL56wQcATOQy/O+xfhjW2QnlVVrMWHcMZ64X6PYn5RRj9oaTqNaKGNPbDd89HYLxfT1gqpAh4YYaC7aexQubTqGiWlNnDcUV1TiWkg+tln+PJDIUDD9EZLAUchkWjuoKlYkMSx/sge7udf9Nz1Qhw6onAhHsY4+iimpMXRuHxMwi5JdUYsb6Yygsq0LfDrZ4/5E+GOBjj48mBiB24UgsGNUVJvKaNcZmrj+O4opqvesevpyLez88gEdWxWDRT+fYTUZkINjtVQd2exEZFlEUdSvD305xRTWe+DwW8WkFcLJSwtPODKdSC+BpZ4Yf5wyCk5Wy1jmHLufg6a9PoLRSgz6eNlgXMQAqExmifruIr49e0zv21fu74amhvs12X0TUOA39/mb4qQPDD1H7VVhahYmrY3AxswgAYKVU4Ic5A9HZxarec06nFWD6ujjcLK2Cr6MFNKKIa3mlAICpIR3haqPCsp2JEARg5eR+CO/p1ir3QkT6OOaHiKgONuYm2PBkMPydLWGqkGHF5H63DT4A0MfLFlueGQh3GxWu5pbgWl4p3G1U2DAzGK8/2BOzh/lhakhHiCIwb3M84tMKWudmiOiusOWnDmz5IWr/qjVaFJVXw87CtMHnZBSU4ZUfz8LLzhwvh3eBtcpE73pPfXUc+xJz4Ghpih/nDILXn4Oxiah1sNurCRh+iOhuFFdU45FVMbhwQw0bMxMM8XfEEH9HDPZ3godt7QVYiah5GUy314oVK+Dt7Q2VSoXg4GDExcXVe+zWrVsRFBQEW1tbWFhYICAgAF9//bXeMaIoYvHixXBzc4OZmRlCQ0Nx+fLllr4NIiJYKhVYOz0IHR3MUVhWhe1nbuD/fjiLQe/sxcgP9uPgpfonWSSi1iNp+Nm8eTMiIyOxZMkSnDx5En369EFYWBiys7PrPN7e3h6vvvoqYmJicObMGURERCAiIgK7du3SHbNs2TJ8/PHHWLVqFWJjY2FhYYGwsDCUl5e31m0RkRFzszHDnshh2PJMCF4Y6Y9+HWwhlwlIyinBzC+P4ffzmVKXSGT0JO32Cg4ORv/+/fHJJ58AALRaLby8vPD8889jwYIFDbpGv379MHr0aLzxxhsQRRHu7u548cUX8dJLLwEACgsL4eLigvXr12PSpEkNuia7vYioORWWVeGVrWfx69kbUMgELJ8UgDG93aUui6jdafPdXpWVlThx4gRCQ0P/KkYmQ2hoKGJiYu54viiKiI6ORmJiIoYOHQoASE5ORmZmpt41bWxsEBwcfNtrVlRUQK1W632IiJqLjZkJ/jspAOP7eqBaK+KFjafww4nrUpdFZLQUUv3i3NxcaDQauLi46G13cXHBxYsX6z2vsLAQHh4eqKiogFwux6effop7770XAJCZmam7xj+veWtfXaKiorB06dK7vRUiojtSyGX44JE+UCpk2HQsDS99fxo3Sysxsb8XrP721hhQ85e749du4vvj11FapcHb43vWOoaI7p5k4eduWVlZIT4+HsXFxYiOjkZkZCR8fX0xfPjwu77mwoULERkZqftZrVbDy8urGaolIvqLTCbg7fG9oDKRY/0fKXjz1wt4d+dFBPs4YGQ3Z/T3tsfByznYcvw6knNLdOdZKhWIeqiXhJUTtS+ShR9HR0fI5XJkZWXpbc/KyoKrq2u958lkMnTq1AkAEBAQgAsXLiAqKgrDhw/XnZeVlQU3t79mWM3KykJAQEC911QqlVAqa09rT0TU3GQyAUvGdoennRm+jU3F1dwSHL6Si8NXcvWOMzeVY0QXZ/x69gY2xqUivKcrhnV2kqhqovZFsjE/pqamCAwMRHR0tG6bVqtFdHQ0QkJCGnwdrVaLiooKAICPjw9cXV31rqlWqxEbG9uoaxIRtSRBEPDkEF/sfWk49r44DP8Z3Q0hvg5QKmTo722HZQ/3xrFXQ7Ficj9MH+gNAPi/78+gsKxK2sKJ2glJu70iIyMxbdo0BAUFYcCAAVi+fDlKSkoQEREBAJg6dSo8PDwQFRUFoGZsTlBQEPz8/FBRUYHffvsNX3/9NVauXAmg5g+UefPm4c0334S/vz98fHywaNEiuLu7Y9y4cVLdJhFRvXydLOHrZIknh9S9IOr88C7Yn5iNlLxSvLE9Ae8/0ke3L1tdjqXbE3D2eiE+mhiAwI52rVU2kUGTNPxMnDgROTk5WLx4MTIzMxEQEICdO3fqBiynpqZCJvurcaqkpARz5szB9evXYWZmhq5du2LDhg2YOHGi7pj58+ejpKQEs2bNQkFBAQYPHoydO3dCpVK1+v0RETWVuakC7z/SB498FoPvT1zHqJ6u+FdXZ2w5fh1v/poAdXk1AGDG+mPY8kzIHdcpIyIub1EnzvNDRG3NW78mYM2hZDhZKdHZxRJHruQBAHp52EAmAKevF8LVWoUf5gy841Ia2epyHEnKhZmJAsM6O8HMVN4at0DU4hr6/W1wb3sRERmjF+/rgr0Xs5GUU4KcogooFTK8eF9nzBjkg6LyajzyWQyuZBdjyhex+P6ZgbD/24KtFdUanLxWgAOXcnDgUg4u3PhrLjNzUzlCu7lgbB93DO3sCKWCQYjaP7b81IEtP0TUFp1OK8CM9cfQzc0ab47rCW9HC92+jIIyPLzyD2QUlqOPpw0WjemOYyk38UdSLo6l5KO8Sqs7VhCAnu42yC+pRHpBmW67pVKB7u7W8He2hL+zJTq7WKGHuw1szJs2x1BBaSWuZBcjyNu+SdchuhOu6t4EDD9E1FaJoghBEOrcdyW7GI+s+gM3S2u/FeZoaYqh/k4Y1sUJgzs5wsFSCVEUEZ9WgF9O38CvZzOQpa6odZ7KRIZpA73xzFA/2P2tNamhCkorMf7TP5CcW4LVUwJxX4/6pzIhaiqGnyZg+CEiQxWfVoBpa+OgFUXc4+uAQX4OGNjJEf7OlvWGJgDQakVcyFTjclYxLmUV4XJ2MS5mqpGWX9MyZKVUYOYQH8wc7NPg2aYrq7WYujYWR6/mAwACvGzx45yBt62DqCkYfpqA4YeIDFlltRZymQC5rGkhQxRF7L2Yjfd/v6QbJ2RnboIh/k7o4mqFzi5W6OJiBU87M8j+8btEUcSCH85i8/E0WCoVqNRoUVmtxXdPh2CAD7u/qGVwwDMRkZEyVTTP/LWCIGBkNxeM6OKM387dwIe7L+FqTgl+Pp0BnP7rOAcLUzxxT0dMDekIB8ua2fI/P5SMzcfTIBOA/z3WF78nZGFjXCpWH0xi+CHJseWnDmz5ISKqrVqjxZGkPCRkqHEpqwiJmUW4klOMyuqawdRKhQwTAj3R3c0ai346B1EEFo/pjhmDfXA1pxgjPzwAUQT2RA5FJ2fOR0TNjy0/RETUrBRyGYZ1dtJbY6xKo8Wu85lYffAqzlwvxLexqbp9k4M7IGKQN4Camazv7eaC3xOysPrgVSx7uM8/L0/UaiRb24uIiAyfiVyGMb3d8dOzg7B51j0I7eYMABjW2QmvPdBDb3Dz08NqlvDYdioD2epyveuk5Zfim9hryCuu/cYZUXNjt1cd2O1FRHT3CkorYa0yqTUIGgAeXvkHjl+7idnD/fB/4V2h1Yr4JvYa3v7tIsqqNLAwlSNikA+eGuLb5PmFyPg09PubLT9ERNSsbM1N6ww+ADBraE3rz4aj13ApqwhT1sZi0U/nUValgb2FKUoqNfhk3xUMXrYX/91zGUXlXMmemh9bfurAlh8iopah1YoI/egAruaUQBAAUayZSHFBeFdMCfHG7oQsfLT7EhKzigAATlZKvDWuJydHpAZhyw8REbU5MpmAp4bUtP6IItCvgy12zB2K6YN8IJcJCO/pih1zh+Djx/rCx9ECOUUVmPX1CczddAo3Syolrp7aC7b81IEtP0RELadKo8V/91yGs7USk4M71jsZY3mVBsv3XMbqg0nQijVLdLzxYE+E93TlLNFUJ87w3AQMP0REbUd8WgFe3nIal7OLAdSEoKCO9gjytkN/b3v0cLeGQs6ODGL4aRKGHyKitqWiWoP/RV/BmkNXUVGt1dtnpVRgaGcnjOjqjOFdnOD45yzTZHwYfpqA4YeIqG0qr9LgbHohjqXk43jKTRxPyYe6vFq3XxCAPp62iBjkjQf6uNfZPVZcUY09CVlwtlIi2NehyWugUdvB8NMEDD9ERIZBqxVx+noB9l3MRvTFbJzPUOv2BXW0w2sP9EBPDxsANcFpw9Fr+HR/EvL/HDztbKXE6N5ueKCPOwK8bBs1lii9oAy/nM6AUiHDA33cdeuakXQYfpqA4YeIyDBlqcux+VgaVu5PQlmVBoIATAzyQi9PG6zYewUZhTUzS3vZm0FdVo3Csr/mEXK2UsLTzgzOViq4WCvhbK2Ch60ZvB0t4ONgARtzE1RptIi+kI1Nx1Jx4FIObn2DmsgFhHZzwaP9vTDU34mtSRJh+GkChh8iIsN2o7AM7+y4iJ/iM/S2u9moMHekPx4O9IRWBA5dzsHPpzOwOyELpZWa217T1twEMkHQtRoBwD2+9iir1OD09UK93zE1xBuPB3eAjRlnqW5NDD9NwPBDRNQ+HE/JxxvbE5BeUIanh/phSkhHqEzktY4rq9Qg4YYaOUXlyFJXILuoHJmFFUi7WYqU3BJkF/215pijpSkeDvTCxP5e8HG0AABcuKHG5mNp2BafjoLSmtYkS6UCk/p7YcZgH7jbmrXODRs5hp8mYPghImpfRFFs0txApZXVuJZXiqLyagR42cJUUfer9eVVGmw/cwNrDl7VzVKtkAkI8XOAvYUpbM1MYGNmAiuVCYorarrdbpZWoqC0Ci7WSrwc1hVOVhw7dLcYfpqA4YeIiJpCFEXsv5SD1QeuIuZqXoPPc7FW4tPJ/RDY0b7WvtS8UiTlFKNfBzsu+loPhp8mYPghIqLmcj6jEOcz1FCXVaGgtAqFZVUoKq+ChVIBW3MT2JqZwlKlwBeHk3EluxgKmYD/jO6GaQO9IQgCEjLUWHkgCb+eyYBWBOQyAYEd7DCiqzNGdnOGv7MlZ7z+E8NPEzD8EBFRayupqMb8H87g1zM3AACje7mhrEqDvRezdcd42JohvaBM77xOzpaYHNwBD/X1vOsWoZKKamhEEdaqus8XRRGrD17FL2cyUFqhQXFFNUorNSiv0uDJIb5YMKrrXf3e5sbw0wQMP0REJAVRFLH2SAqifruAam3N17NMAEb3dsfsYX7o7m6NtPxS7E/Mxt6L2fgjKU8347XKRIaxvd3xQIA7Kqq0yC6qGbidW1yBoI72eDCg7kkfL9xQY/Lnsaiq1mL9jAEI7GhX65hVB5Lwzo6L9db91YwBGNrZqZmewt1j+GkChh8iIpLSsZR8vP3bBXR1tcLTQ/3g/edbZf9UVF6FbfEZ+OboNVzMLLrtNSMGeWPR6O6Q/W0OostZRZi0+ijy/nx931KpwFczB6Bfh78C0E/x6Zi7KR4AMHekPwZ1coSFUg4LUwXWHLqKb2JT4Wajws55QyV/tZ/hpwkYfoiIyJCIooiTqQX4JvYajqXkw87cFE6WSjhb17w5tjEuDQAwprcbPni0D5QKOa7mFGPi6qPIKapATw9rWJgqEJucDyulAl8/GYwAL1v8kZSLaWvjUKUR8eRgH/xnTHe931taWY37/3sIKXmleKifBz58NKBWbRqtCJmAVhmXxPDTBAw/RETUnvwUn46XtpxGlUbEQD8HvDq6G2auP45MdTm6ulph06x7YKqQYfq6Y4hLzoeVSoElY3tg6c/nUVRRjdG93fC/SX31Wo1uOXEtH4+sioFWBD6bEoiwHq4AgCqNFuuPpODjvZfRy8MGKx7vBzsL0xa9T4afJmD4ISKi9ubQ5Rw88/UJlPxtJmt/Z0tsmnWPbl2ykopqRKw7hriUfN0xA3zs8dWMAXVODnnLOzsuYtWBJDhammLXvKG4lFWMxT+dw+XsYt0xPo4WWDe9f71deM2hod/fdc/SRERERO3KEH8nbH46BI6WNa0vPo4W+ObJYL0FWS2UCqyL6I/+3jVjfjo5W2LNlKDbBh8A+Pe9/ujiYoXc4ko88MkRPLbmKC5nF8PewhSv3N8VHrZmSM4twfhPj+DEtfzbXqs1sOWnDmz5ISKi9ur6zVL8euYGxvfzgLOVqs5jSiursTshC0P8nWDfwK6qc+mFGLfiCKq1IgQBmBzcAS/d1wW25qbILirHk18ex5nrhTBVyPDBI30wto97c94WAHZ7NQnDDxERUeNtO5WO3Rey8MxQP/TytNHbV1pZjbmb4rE7IQsAsGhMd8wc7NOsv5/dXkRERNSqxvX1wIrH+9UKPgBgbqrAqicCMWOQDxQyAd1crSSosAZbfurAlh8iIqKWcyW7GJ2cLZv9umz5ISIiojapJYJPYzD8EBERkVFh+CEiIiKjwvBDRERERoXhh4iIiIwKww8REREZFYYfIiIiMioMP0RERGRUGH6IiIjIqDD8EBERkVFh+CEiIiKjwvBDRERERoXhh4iIiIwKww8REREZFYXUBbRFoigCANRqtcSVEBERUUPd+t6+9T1eH4afOhQVFQEAvLy8JK6EiIiIGquoqAg2Njb17hfEO8UjI6TVapGRkQErKysIgtBs11Wr1fDy8kJaWhqsra2b7bpUNz7v1sNn3Xr4rFsPn3Xraa5nLYoiioqK4O7uDpms/pE9bPmpg0wmg6enZ4td39ramv8jtSI+79bDZ916+KxbD59162mOZ327Fp9bOOCZiIiIjArDDxERERkVhp9WpFQqsWTJEiiVSqlLMQp83q2Hz7r18Fm3Hj7r1tPaz5oDnomIiMiosOWHiIiIjArDDxERERkVhh8iIiIyKgw/REREZFQYflrRihUr4O3tDZVKheDgYMTFxUldksGLiopC//79YWVlBWdnZ4wbNw6JiYl6x5SXl+PZZ5+Fg4MDLC0tMWHCBGRlZUlUcfvxzjvvQBAEzJs3T7eNz7r5pKen44knnoCDgwPMzMzQq1cvHD9+XLdfFEUsXrwYbm5uMDMzQ2hoKC5fvixhxYZJo9Fg0aJF8PHxgZmZGfz8/PDGG2/orQ3FZ313Dh48iLFjx8Ld3R2CIGDbtm16+xvyXPPz8zF58mRYW1vD1tYWM2fORHFxcZNrY/hpJZs3b0ZkZCSWLFmCkydPok+fPggLC0N2drbUpRm0AwcO4Nlnn8XRo0exe/duVFVV4b777kNJSYnumH//+9/45ZdfsGXLFhw4cAAZGRl46KGHJKza8B07dgyfffYZevfurbedz7p53Lx5E4MGDYKJiQl27NiBhIQEfPDBB7Czs9Mds2zZMnz88cdYtWoVYmNjYWFhgbCwMJSXl0tYueF59913sXLlSnzyySe4cOEC3n33XSxbtgz/+9//dMfwWd+dkpIS9OnTBytWrKhzf0Oe6+TJk3H+/Hns3r0b27dvx8GDBzFr1qymFydSqxgwYID47LPP6n7WaDSiu7u7GBUVJWFV7U92drYIQDxw4IAoiqJYUFAgmpiYiFu2bNEdc+HCBRGAGBMTI1WZBq2oqEj09/cXd+/eLQ4bNkycO3euKIp81s3p//7v/8TBgwfXu1+r1Yqurq7ie++9p9tWUFAgKpVKcePGja1RYrsxevRoccaMGXrbHnroIXHy5MmiKPJZNxcA4o8//qj7uSHPNSEhQQQgHjt2THfMjh07REEQxPT09CbVw5afVlBZWYkTJ04gNDRUt00mkyE0NBQxMTESVtb+FBYWAgDs7e0BACdOnEBVVZXes+/atSs6dOjAZ3+Xnn32WYwePVrvmQJ81s3p559/RlBQEB555BE4Ozujb9++WLNmjW5/cnIyMjMz9Z61jY0NgoOD+awbaeDAgYiOjsalS5cAAKdPn8bhw4cxatQoAHzWLaUhzzUmJga2trYICgrSHRMaGgqZTIbY2Ngm/X4ubNoKcnNzodFo4OLiorfdxcUFFy9elKiq9ker1WLevHkYNGgQevbsCQDIzMyEqakpbG1t9Y51cXFBZmamBFUatk2bNuHkyZM4duxYrX181s3n6tWrWLlyJSIjI/HKK6/g2LFjeOGFF2Bqaopp06bpnmddf6bwWTfOggULoFar0bVrV8jlcmg0Grz11luYPHkyAPBZt5CGPNfMzEw4Ozvr7VcoFLC3t2/ys2f4oXbj2Wefxblz53D48GGpS2mX0tLSMHfuXOzevRsqlUrqcto1rVaLoKAgvP322wCAvn374ty5c1i1ahWmTZsmcXXty3fffYdvvvkG3377LXr06IH4+HjMmzcP7u7ufNbtGLu9WoGjoyPkcnmtt16ysrLg6uoqUVXty3PPPYft27dj37598PT01G13dXVFZWUlCgoK9I7ns2+8EydOIDs7G/369YNCoYBCocCBAwfw8ccfQ6FQwMXFhc+6mbi5uaF79+5627p164bU1FQA0D1P/pnSdC+//DIWLFiASZMmoVevXpgyZQr+/e9/IyoqCgCfdUtpyHN1dXWt9VJQdXU18vPzm/zsGX5agampKQIDAxEdHa3bptVqER0djZCQEAkrM3yiKOK5557Djz/+iL1798LHx0dvf2BgIExMTPSefWJiIlJTU/nsG2nkyJE4e/Ys4uPjdZ+goCBMnjxZ98981s1j0KBBtaZsuHTpEjp27AgA8PHxgaurq96zVqvViI2N5bNupNLSUshk+l+FcrkcWq0WAJ91S2nIcw0JCUFBQQFOnDihO2bv3r3QarUIDg5uWgFNGi5NDbZp0yZRqVSK69evFxMSEsRZs2aJtra2YmZmptSlGbTZs2eLNjY24v79+8UbN27oPqWlpbpjnnnmGbFDhw7i3r17xePHj4shISFiSEiIhFW3H39/20sU+aybS1xcnKhQKMS33npLvHz5svjNN9+I5ubm4oYNG3THvPPOO6Ktra34008/iWfOnBEffPBB0cfHRywrK5OwcsMzbdo00cPDQ9y+fbuYnJwsbt26VXR0dBTnz5+vO4bP+u4UFRWJp06dEk+dOiUCED/88EPx1KlT4rVr10RRbNhzDQ8PF/v27SvGxsaKhw8fFv39/cXHHnusybUx/LSi//3vf2KHDh1EU1NTccCAAeLRo0elLsngAajzs27dOt0xZWVl4pw5c0Q7OzvR3NxcHD9+vHjjxg3pim5H/hl++Kybzy+//CL27NlTVCqVYteuXcXVq1fr7ddqteKiRYtEFxcXUalUiiNHjhQTExMlqtZwqdVqce7cuWKHDh1ElUol+vr6iq+++qpYUVGhO4bP+u7s27evzj+fp02bJopiw55rXl6e+Nhjj4mWlpaitbW1GBERIRYVFTW5NkEU/zaNJREREVE7xzE/REREZFQYfoiIiMioMPwQERGRUWH4ISIiIqPC8ENERERGheGHiIiIjArDDxERERkVhh8iogYQBAHbtm2TugwiagYMP0TU5k2fPh2CINT6hIeHS10aERkghdQFEBE1RHh4ONatW6e3TalUSlQNERkytvwQkUFQKpVwdXXV+9jZ2QGo6ZJauXIlRo0aBTMzM/j6+uL777/XO//s2bP417/+BTMzMzg4OGDWrFkoLi7WO2bt2rXo0aMHlEol3Nzc8Nxzz+ntz83Nxfjx42Fubg5/f3/8/PPPLXvTRNQiGH6IqF1YtGgRJkyYgNOnT2Py5MmYNGkSLly4AAAoKSlBWFgY7OzscOzYMWzZsgV79uzRCzcrV67Es88+i1mzZuHs2bP4+eef0alTJ73fsXTpUjz66KM4c+YM7r//fkyePBn5+fmtep9E1AyavDQqEVELmzZtmiiXy0ULCwu9z1tvvSWKoigCEJ955hm9c4KDg8XZs2eLoiiKq1evFu3s7MTi4mLd/l9//VWUyWRiZmamKIqi6O7uLr766qv11gBA/M9//qP7ubi4WAQg7tixo9nuk4haB8f8EJFBGDFiBFauXKm3zd7eXvfPISEhevtCQkIQHx8PALhw4QL69OkDCwsL3f5BgwZBq9UiMTERgiAgIyMDI0eOvG0NvXv31v2zhYUFrK2tkZ2dfbe3REQSYfghIoNgYWFRqxuquZiZmTXoOBMTE72fBUGAVqttiZKIqAVxzA8RtQtHjx6t9XO3bt0AAN26dcPp06dRUlKi23/kyBHIZDJ06dIFVlZW8Pb2RnR0dKvWTETSYMsPERmEiooKZGZm6m1TKBRwdHQEAGzZsgVBQUEYPHgwvvnmG8TFxeGLL74AAEyePBlLlizBtGnT8NprryEnJwfPP/88pkyZAhcXFwDAa6+9hmeeeQbOzs4YNWoUioqKcOTIETz//POte6NE1OIYfojIIOzcuRNubm5627p06YKLFy8CqHkTa9OmTZgzZw7c3NywceNGdO/eHQBgbm6OXbt2Ye7cuejfvz/Mzc0xYcIEfPjhh7prTZs2DeXl5fjoo4/w0ksvwdHREQ8//HDr3SARtRpBFEVR6iKIiJpCEAT8+OOPGDdunNSlEJEB4JgfIiIiMioMP0RERGRUOOaHiAwee++JqDHY8kNERERGheGHiIiIjArDDxERERkVhh8iIiIyKgw/REREZFQYfoiIiMioMPwQERGRUWH4ISIiIqPC8ENERERG5f8Bv/iwcQSox/kAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title('Learning curve')\n",
    "plt.plot(sequential_history['loss'])\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('MSE')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6465b559",
   "metadata": {},
   "source": [
    "## 3. PyTorch nn.Module class-based model\n",
    "\n",
    "The `nn.Module` class provides more flexibility than `nn.Sequential`. It allows you to create models via subclassing with custom forward methods, non-linear topology, shared layers, and multiple inputs or outputs, similar to the Keras Functional API."
   ]
  }
 ],
 "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.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
