{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c92cdbe3",
   "metadata": {},
   "source": [
    "# Классификация астероидов глубокой нейросетью с PSO и дообучением\n",
    "\n",
    "В работе автоматически определяется ключевая зависимая переменная, выполняется предобработка данных и строится глубокая нейросеть с числом скрытых слоёв больше 3.  \n",
    "Сначала веса сети инициализируются методом роевого поиска частиц (**PSO**), затем модель дополнительно дообучается градиентным методом.  \n",
    "В конце выводятся все метрики, необходимые для краткого итогового вывода по реальным результатам."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4133f9bb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<torch._C.Generator at 0x7f33f13ec050>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "import re\n",
    "import glob\n",
    "import copy\n",
    "import random\n",
    "import warnings\n",
    "from pathlib import Path\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.preprocessing import OneHotEncoder, StandardScaler\n",
    "from sklearn.metrics import (\n",
    "    accuracy_score,\n",
    "    balanced_accuracy_score,\n",
    "    precision_score,\n",
    "    recall_score,\n",
    "    f1_score,\n",
    "    roc_auc_score,\n",
    "    confusion_matrix,\n",
    "    classification_report\n",
    ")\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.utils.data import TensorDataset, DataLoader\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "SEED = 42\n",
    "random.seed(SEED)\n",
    "np.random.seed(SEED)\n",
    "torch.manual_seed(SEED)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cee4f30b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Используемый файл: Asteroid_Updated.csv\n",
      "Форма датасета: (839714, 31)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>a</th>\n",
       "      <th>e</th>\n",
       "      <th>i</th>\n",
       "      <th>om</th>\n",
       "      <th>w</th>\n",
       "      <th>q</th>\n",
       "      <th>ad</th>\n",
       "      <th>per_y</th>\n",
       "      <th>data_arc</th>\n",
       "      <th>...</th>\n",
       "      <th>UB</th>\n",
       "      <th>IR</th>\n",
       "      <th>spec_B</th>\n",
       "      <th>spec_T</th>\n",
       "      <th>G</th>\n",
       "      <th>moid</th>\n",
       "      <th>class</th>\n",
       "      <th>n</th>\n",
       "      <th>per</th>\n",
       "      <th>ma</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Ceres</td>\n",
       "      <td>2.769165</td>\n",
       "      <td>0.076009</td>\n",
       "      <td>10.594067</td>\n",
       "      <td>80.305532</td>\n",
       "      <td>73.597694</td>\n",
       "      <td>2.558684</td>\n",
       "      <td>2.979647</td>\n",
       "      <td>4.608202</td>\n",
       "      <td>8822.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.426</td>\n",
       "      <td>NaN</td>\n",
       "      <td>C</td>\n",
       "      <td>G</td>\n",
       "      <td>0.12</td>\n",
       "      <td>1.59478</td>\n",
       "      <td>MBA</td>\n",
       "      <td>0.213885</td>\n",
       "      <td>1683.145708</td>\n",
       "      <td>77.372096</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Pallas</td>\n",
       "      <td>2.772466</td>\n",
       "      <td>0.230337</td>\n",
       "      <td>34.836234</td>\n",
       "      <td>173.080063</td>\n",
       "      <td>310.048857</td>\n",
       "      <td>2.133865</td>\n",
       "      <td>3.411067</td>\n",
       "      <td>4.616444</td>\n",
       "      <td>72318.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.284</td>\n",
       "      <td>NaN</td>\n",
       "      <td>B</td>\n",
       "      <td>B</td>\n",
       "      <td>0.11</td>\n",
       "      <td>1.23324</td>\n",
       "      <td>MBA</td>\n",
       "      <td>0.213503</td>\n",
       "      <td>1686.155999</td>\n",
       "      <td>59.699133</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Juno</td>\n",
       "      <td>2.669150</td>\n",
       "      <td>0.256942</td>\n",
       "      <td>12.988919</td>\n",
       "      <td>169.852760</td>\n",
       "      <td>248.138626</td>\n",
       "      <td>1.983332</td>\n",
       "      <td>3.354967</td>\n",
       "      <td>4.360814</td>\n",
       "      <td>72684.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.433</td>\n",
       "      <td>NaN</td>\n",
       "      <td>Sk</td>\n",
       "      <td>S</td>\n",
       "      <td>0.32</td>\n",
       "      <td>1.03454</td>\n",
       "      <td>MBA</td>\n",
       "      <td>0.226019</td>\n",
       "      <td>1592.787285</td>\n",
       "      <td>34.925016</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Vesta</td>\n",
       "      <td>2.361418</td>\n",
       "      <td>0.088721</td>\n",
       "      <td>7.141771</td>\n",
       "      <td>103.810804</td>\n",
       "      <td>150.728541</td>\n",
       "      <td>2.151909</td>\n",
       "      <td>2.570926</td>\n",
       "      <td>3.628837</td>\n",
       "      <td>24288.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.492</td>\n",
       "      <td>NaN</td>\n",
       "      <td>V</td>\n",
       "      <td>V</td>\n",
       "      <td>0.32</td>\n",
       "      <td>1.13948</td>\n",
       "      <td>MBA</td>\n",
       "      <td>0.271609</td>\n",
       "      <td>1325.432765</td>\n",
       "      <td>95.861936</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Astraea</td>\n",
       "      <td>2.574249</td>\n",
       "      <td>0.191095</td>\n",
       "      <td>5.366988</td>\n",
       "      <td>141.576605</td>\n",
       "      <td>358.687607</td>\n",
       "      <td>2.082324</td>\n",
       "      <td>3.066174</td>\n",
       "      <td>4.130323</td>\n",
       "      <td>63507.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.411</td>\n",
       "      <td>NaN</td>\n",
       "      <td>S</td>\n",
       "      <td>S</td>\n",
       "      <td>NaN</td>\n",
       "      <td>1.09589</td>\n",
       "      <td>MBA</td>\n",
       "      <td>0.238632</td>\n",
       "      <td>1508.600458</td>\n",
       "      <td>282.366289</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 31 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      name         a         e          i          om           w         q  \\\n",
       "0    Ceres  2.769165  0.076009  10.594067   80.305532   73.597694  2.558684   \n",
       "1   Pallas  2.772466  0.230337  34.836234  173.080063  310.048857  2.133865   \n",
       "2     Juno  2.669150  0.256942  12.988919  169.852760  248.138626  1.983332   \n",
       "3    Vesta  2.361418  0.088721   7.141771  103.810804  150.728541  2.151909   \n",
       "4  Astraea  2.574249  0.191095   5.366988  141.576605  358.687607  2.082324   \n",
       "\n",
       "         ad     per_y  data_arc  ...     UB  IR  spec_B spec_T     G     moid  \\\n",
       "0  2.979647  4.608202    8822.0  ...  0.426 NaN       C      G  0.12  1.59478   \n",
       "1  3.411067  4.616444   72318.0  ...  0.284 NaN       B      B  0.11  1.23324   \n",
       "2  3.354967  4.360814   72684.0  ...  0.433 NaN      Sk      S  0.32  1.03454   \n",
       "3  2.570926  3.628837   24288.0  ...  0.492 NaN       V      V  0.32  1.13948   \n",
       "4  3.066174  4.130323   63507.0  ...  0.411 NaN       S      S   NaN  1.09589   \n",
       "\n",
       "  class         n          per          ma  \n",
       "0   MBA  0.213885  1683.145708   77.372096  \n",
       "1   MBA  0.213503  1686.155999   59.699133  \n",
       "2   MBA  0.226019  1592.787285   34.925016  \n",
       "3   MBA  0.271609  1325.432765   95.861936  \n",
       "4   MBA  0.238632  1508.600458  282.366289  \n",
       "\n",
       "[5 rows x 31 columns]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Типы данных:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>dtype</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>name</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>a</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>e</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>i</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>om</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>q</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ad</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>per_y</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>data_arc</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>condition_code</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>n_obs_used</th>\n",
       "      <td>int64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>H</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>neo</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pha</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>diameter</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>extent</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>albedo</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>rot_per</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>GM</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>BV</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>UB</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>IR</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>spec_B</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>spec_T</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>G</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>moid</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>class</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>n</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>per</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ma</th>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  dtype\n",
       "name             object\n",
       "a               float64\n",
       "e               float64\n",
       "i               float64\n",
       "om              float64\n",
       "w               float64\n",
       "q               float64\n",
       "ad              float64\n",
       "per_y           float64\n",
       "data_arc        float64\n",
       "condition_code   object\n",
       "n_obs_used        int64\n",
       "H               float64\n",
       "neo              object\n",
       "pha              object\n",
       "diameter         object\n",
       "extent           object\n",
       "albedo          float64\n",
       "rot_per         float64\n",
       "GM              float64\n",
       "BV              float64\n",
       "UB              float64\n",
       "IR              float64\n",
       "spec_B           object\n",
       "spec_T           object\n",
       "G               float64\n",
       "moid            float64\n",
       "class            object\n",
       "n               float64\n",
       "per             float64\n",
       "ma              float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Поиск CSV-файла\n",
    "candidate_files = [\n",
    "    \"Asteroid_Updated.csv\",\n",
    "    \"asteroid_updated.csv\",\n",
    "    \"Asteroid Updated.csv\",\n",
    "    \"asteroid.csv\",\n",
    "    \"asteroid_dataset.csv\",\n",
    "    \"Asteroid_Dataset.csv\"\n",
    "]\n",
    "\n",
    "csv_path = None\n",
    "for cand in candidate_files:\n",
    "    if Path(cand).exists():\n",
    "        csv_path = cand\n",
    "        break\n",
    "\n",
    "if csv_path is None:\n",
    "    matches = glob.glob(\"*.csv\")\n",
    "    if len(matches) == 1:\n",
    "        csv_path = matches[0]\n",
    "    elif len(matches) > 1:\n",
    "        # стараемся выбрать наиболее подходящий по имени\n",
    "        asteroid_matches = [m for m in matches if \"asteroid\" in m.lower()]\n",
    "        csv_path = asteroid_matches[0] if asteroid_matches else matches[0]\n",
    "\n",
    "if csv_path is None:\n",
    "    raise FileNotFoundError(\"CSV-файл не найден. Положите файл Asteroid_Updated.csv рядом с ноутбуком.\")\n",
    "\n",
    "print(f\"Используемый файл: {csv_path}\")\n",
    "df_raw = pd.read_csv(csv_path, low_memory=False)\n",
    "print(\"Форма датасета:\", df_raw.shape)\n",
    "display(df_raw.head())\n",
    "print(\"\\nТипы данных:\")\n",
    "display(df_raw.dtypes.rename(\"dtype\").to_frame())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0e0d59a4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>original_name</th>\n",
       "      <th>normalized_name</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>name</td>\n",
       "      <td>name</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>a</td>\n",
       "      <td>a</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>e</td>\n",
       "      <td>e</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>i</td>\n",
       "      <td>i</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>om</td>\n",
       "      <td>om</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>w</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>q</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ad</td>\n",
       "      <td>ad</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>per_y</td>\n",
       "      <td>per_y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>data_arc</td>\n",
       "      <td>data_arc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>condition_code</td>\n",
       "      <td>condition_code</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>n_obs_used</td>\n",
       "      <td>n_obs_used</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>H</td>\n",
       "      <td>h</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>neo</td>\n",
       "      <td>neo</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>pha</td>\n",
       "      <td>pha</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>diameter</td>\n",
       "      <td>diameter</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>extent</td>\n",
       "      <td>extent</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>albedo</td>\n",
       "      <td>albedo</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>rot_per</td>\n",
       "      <td>rot_per</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>GM</td>\n",
       "      <td>gm</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>BV</td>\n",
       "      <td>bv</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>UB</td>\n",
       "      <td>ub</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>IR</td>\n",
       "      <td>ir</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>spec_B</td>\n",
       "      <td>spec_b</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>spec_T</td>\n",
       "      <td>spec_t</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>G</td>\n",
       "      <td>g</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>moid</td>\n",
       "      <td>moid</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>class</td>\n",
       "      <td>class</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>n</td>\n",
       "      <td>n</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>per</td>\n",
       "      <td>per</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>ma</td>\n",
       "      <td>ma</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     original_name normalized_name\n",
       "0             name            name\n",
       "1                a               a\n",
       "2                e               e\n",
       "3                i               i\n",
       "4               om              om\n",
       "5                w               w\n",
       "6                q               q\n",
       "7               ad              ad\n",
       "8            per_y           per_y\n",
       "9         data_arc        data_arc\n",
       "10  condition_code  condition_code\n",
       "11      n_obs_used      n_obs_used\n",
       "12               H               h\n",
       "13             neo             neo\n",
       "14             pha             pha\n",
       "15        diameter        diameter\n",
       "16          extent          extent\n",
       "17          albedo          albedo\n",
       "18         rot_per         rot_per\n",
       "19              GM              gm\n",
       "20              BV              bv\n",
       "21              UB              ub\n",
       "22              IR              ir\n",
       "23          spec_B          spec_b\n",
       "24          spec_T          spec_t\n",
       "25               G               g\n",
       "26            moid            moid\n",
       "27           class           class\n",
       "28               n               n\n",
       "29             per             per\n",
       "30              ma              ma"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Типы данных после нормализации:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>a</th>\n",
       "      <th>e</th>\n",
       "      <th>i</th>\n",
       "      <th>om</th>\n",
       "      <th>w</th>\n",
       "      <th>q</th>\n",
       "      <th>ad</th>\n",
       "      <th>per_y</th>\n",
       "      <th>data_arc</th>\n",
       "      <th>...</th>\n",
       "      <th>ub</th>\n",
       "      <th>ir</th>\n",
       "      <th>spec_b</th>\n",
       "      <th>spec_t</th>\n",
       "      <th>g</th>\n",
       "      <th>moid</th>\n",
       "      <th>class</th>\n",
       "      <th>n</th>\n",
       "      <th>per</th>\n",
       "      <th>ma</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>dtype</th>\n",
       "      <td>object</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>...</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>object</td>\n",
       "      <td>object</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>object</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "      <td>float64</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1 rows × 31 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "         name        a        e        i       om        w        q       ad  \\\n",
       "dtype  object  float64  float64  float64  float64  float64  float64  float64   \n",
       "\n",
       "         per_y data_arc  ...       ub       ir  spec_b  spec_t        g  \\\n",
       "dtype  float64  float64  ...  float64  float64  object  object  float64   \n",
       "\n",
       "          moid   class        n      per       ma  \n",
       "dtype  float64  object  float64  float64  float64  \n",
       "\n",
       "[1 rows x 31 columns]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Нормализация названий столбцов\n",
    "def normalize_col(col: str) -> str:\n",
    "    col = str(col).strip().lower()\n",
    "    col = col.replace(\"%\", \"pct\")\n",
    "    col = re.sub(r\"[^a-z0-9]+\", \"_\", col)\n",
    "    col = re.sub(r\"_+\", \"_\", col).strip(\"_\")\n",
    "    return col\n",
    "\n",
    "df = df_raw.copy()\n",
    "orig_cols = df.columns.tolist()\n",
    "df.columns = [normalize_col(c) for c in df.columns]\n",
    "\n",
    "col_map = pd.DataFrame({\n",
    "    \"original_name\": orig_cols,\n",
    "    \"normalized_name\": df.columns\n",
    "})\n",
    "display(col_map)\n",
    "\n",
    "# Приведение некоторых object-столбцов к числам, если это возможно\n",
    "for col in df.columns:\n",
    "    if df[col].dtype == \"object\":\n",
    "        cleaned = (\n",
    "            df[col]\n",
    "            .astype(str)\n",
    "            .str.replace(\",\", \"\", regex=False)\n",
    "            .str.strip()\n",
    "            .replace({\"\": np.nan, \"nan\": np.nan, \"None\": np.nan})\n",
    "        )\n",
    "        parsed = pd.to_numeric(cleaned, errors=\"coerce\")\n",
    "        share_numeric = parsed.notna().mean()\n",
    "        if share_numeric >= 0.95:\n",
    "            df[col] = parsed\n",
    "\n",
    "print(\"Типы данных после нормализации:\")\n",
    "display(df.dtypes.rename(\"dtype\").to_frame().T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "d8e3169c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Выбранная зависимая переменная: pha\n",
      "Распределение классов:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>share</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pha</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.997552</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.002448</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        share\n",
       "pha          \n",
       "0    0.997552\n",
       "1    0.002448"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Автоматический выбор зависимой переменной\n",
    "target_candidates = [\n",
    "    \"pha\", \"is_hazardous\", \"hazardous\", \"hazardous_asteroid\",\n",
    "    \"potentially_hazardous\", \"neo\"\n",
    "]\n",
    "\n",
    "target_col = None\n",
    "for cand in target_candidates:\n",
    "    if cand in df.columns:\n",
    "        target_col = cand\n",
    "        break\n",
    "\n",
    "if target_col is None:\n",
    "    # запасной вариант: ищем бинарный категориальный столбец с информативным именем\n",
    "    for col in df.columns:\n",
    "        nun = df[col].nunique(dropna=True)\n",
    "        if nun == 2 and any(key in col for key in [\"haz\", \"risk\", \"class\", \"target\", \"pha\", \"neo\"]):\n",
    "            target_col = col\n",
    "            break\n",
    "\n",
    "if target_col is None:\n",
    "    raise ValueError(\"Не удалось автоматически определить целевую переменную для классификации.\")\n",
    "\n",
    "print(\"Выбранная зависимая переменная:\", target_col)\n",
    "\n",
    "# Бинаризация цели\n",
    "target_series = df[target_col].copy()\n",
    "\n",
    "if target_series.dtype == \"object\":\n",
    "    target_clean = (\n",
    "        target_series.astype(str)\n",
    "        .str.strip()\n",
    "        .str.lower()\n",
    "        .replace({\"\": np.nan, \"nan\": np.nan, \"none\": np.nan, \"null\": np.nan})\n",
    "    )\n",
    "    mapping = {\n",
    "        \"y\": 1, \"yes\": 1, \"true\": 1, \"1\": 1, \"t\": 1,\n",
    "        \"n\": 0, \"no\": 0, \"false\": 0, \"0\": 0, \"f\": 0\n",
    "    }\n",
    "    uniq = pd.Series(target_clean.dropna().unique())\n",
    "    if set(uniq.tolist()).issubset(set(mapping.keys())):\n",
    "        y = target_clean.map(mapping)\n",
    "    else:\n",
    "        vals = list(uniq.sort_values())\n",
    "        if len(vals) != 2:\n",
    "            raise ValueError(\n",
    "                f\"Целевая переменная {target_col} не является бинарной. Найденные значения: {vals[:10]}\"\n",
    "            )\n",
    "        y = (target_clean == vals[-1]).astype(float)\n",
    "else:\n",
    "    unique_vals = sorted(pd.Series(target_series.dropna().unique()).tolist())\n",
    "    if len(unique_vals) != 2:\n",
    "        raise ValueError(f\"Целевая переменная {target_col} не является бинарной.\")\n",
    "    y = (target_series == unique_vals[-1]).astype(float)\n",
    "\n",
    "df = df.loc[y.notna()].reset_index(drop=True)\n",
    "y = y.loc[y.notna()].astype(int).reset_index(drop=True)\n",
    "\n",
    "print(\"Распределение классов:\")\n",
    "display(\n",
    "    y.value_counts(normalize=True)\n",
    "     .sort_index()\n",
    "     .rename(\"share\")\n",
    "     .to_frame()\n",
    ")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "711b9785",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Удалённые несодержательные / проблемные признаки: ['albedo', 'bv', 'diameter', 'extent', 'g', 'gm', 'ir', 'name', 'rot_per', 'spec_b', 'spec_t', 'ub']\n",
      "\n",
      "Число исходных содержательных признаков: 18\n",
      "Числовые признаки: ['a', 'e', 'i', 'om', 'w', 'q', 'ad', 'per_y', 'data_arc', 'condition_code', 'n_obs_used', 'h', 'moid', 'n', 'per', 'ma']\n",
      "Категориальные признаки: ['neo', 'class']\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>a</th>\n",
       "      <th>e</th>\n",
       "      <th>i</th>\n",
       "      <th>om</th>\n",
       "      <th>w</th>\n",
       "      <th>q</th>\n",
       "      <th>ad</th>\n",
       "      <th>per_y</th>\n",
       "      <th>data_arc</th>\n",
       "      <th>condition_code</th>\n",
       "      <th>n_obs_used</th>\n",
       "      <th>h</th>\n",
       "      <th>moid</th>\n",
       "      <th>n</th>\n",
       "      <th>per</th>\n",
       "      <th>ma</th>\n",
       "      <th>neo</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>2.769165</td>\n",
       "      <td>0.076009</td>\n",
       "      <td>10.594067</td>\n",
       "      <td>80.305532</td>\n",
       "      <td>73.597694</td>\n",
       "      <td>2.558684</td>\n",
       "      <td>2.979647</td>\n",
       "      <td>4.608202</td>\n",
       "      <td>8822.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1002</td>\n",
       "      <td>3.34</td>\n",
       "      <td>1.59478</td>\n",
       "      <td>0.213885</td>\n",
       "      <td>1683.145708</td>\n",
       "      <td>77.372096</td>\n",
       "      <td>N</td>\n",
       "      <td>MBA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2.772466</td>\n",
       "      <td>0.230337</td>\n",
       "      <td>34.836234</td>\n",
       "      <td>173.080063</td>\n",
       "      <td>310.048857</td>\n",
       "      <td>2.133865</td>\n",
       "      <td>3.411067</td>\n",
       "      <td>4.616444</td>\n",
       "      <td>72318.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>8490</td>\n",
       "      <td>4.13</td>\n",
       "      <td>1.23324</td>\n",
       "      <td>0.213503</td>\n",
       "      <td>1686.155999</td>\n",
       "      <td>59.699133</td>\n",
       "      <td>N</td>\n",
       "      <td>MBA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2.669150</td>\n",
       "      <td>0.256942</td>\n",
       "      <td>12.988919</td>\n",
       "      <td>169.852760</td>\n",
       "      <td>248.138626</td>\n",
       "      <td>1.983332</td>\n",
       "      <td>3.354967</td>\n",
       "      <td>4.360814</td>\n",
       "      <td>72684.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>7104</td>\n",
       "      <td>5.33</td>\n",
       "      <td>1.03454</td>\n",
       "      <td>0.226019</td>\n",
       "      <td>1592.787285</td>\n",
       "      <td>34.925016</td>\n",
       "      <td>N</td>\n",
       "      <td>MBA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2.361418</td>\n",
       "      <td>0.088721</td>\n",
       "      <td>7.141771</td>\n",
       "      <td>103.810804</td>\n",
       "      <td>150.728541</td>\n",
       "      <td>2.151909</td>\n",
       "      <td>2.570926</td>\n",
       "      <td>3.628837</td>\n",
       "      <td>24288.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>9325</td>\n",
       "      <td>3.20</td>\n",
       "      <td>1.13948</td>\n",
       "      <td>0.271609</td>\n",
       "      <td>1325.432765</td>\n",
       "      <td>95.861936</td>\n",
       "      <td>N</td>\n",
       "      <td>MBA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2.574249</td>\n",
       "      <td>0.191095</td>\n",
       "      <td>5.366988</td>\n",
       "      <td>141.576605</td>\n",
       "      <td>358.687607</td>\n",
       "      <td>2.082324</td>\n",
       "      <td>3.066174</td>\n",
       "      <td>4.130323</td>\n",
       "      <td>63507.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>2916</td>\n",
       "      <td>6.85</td>\n",
       "      <td>1.09589</td>\n",
       "      <td>0.238632</td>\n",
       "      <td>1508.600458</td>\n",
       "      <td>282.366289</td>\n",
       "      <td>N</td>\n",
       "      <td>MBA</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          a         e          i          om           w         q        ad  \\\n",
       "0  2.769165  0.076009  10.594067   80.305532   73.597694  2.558684  2.979647   \n",
       "1  2.772466  0.230337  34.836234  173.080063  310.048857  2.133865  3.411067   \n",
       "2  2.669150  0.256942  12.988919  169.852760  248.138626  1.983332  3.354967   \n",
       "3  2.361418  0.088721   7.141771  103.810804  150.728541  2.151909  2.570926   \n",
       "4  2.574249  0.191095   5.366988  141.576605  358.687607  2.082324  3.066174   \n",
       "\n",
       "      per_y  data_arc  condition_code  n_obs_used     h     moid         n  \\\n",
       "0  4.608202    8822.0             0.0        1002  3.34  1.59478  0.213885   \n",
       "1  4.616444   72318.0             0.0        8490  4.13  1.23324  0.213503   \n",
       "2  4.360814   72684.0             0.0        7104  5.33  1.03454  0.226019   \n",
       "3  3.628837   24288.0             0.0        9325  3.20  1.13948  0.271609   \n",
       "4  4.130323   63507.0             0.0        2916  6.85  1.09589  0.238632   \n",
       "\n",
       "           per          ma neo class  \n",
       "0  1683.145708   77.372096   N   MBA  \n",
       "1  1686.155999   59.699133   N   MBA  \n",
       "2  1592.787285   34.925016   N   MBA  \n",
       "3  1325.432765   95.861936   N   MBA  \n",
       "4  1508.600458  282.366289   N   MBA  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Отбор содержательных признаков\n",
    "X = df.drop(columns=[target_col]).copy()\n",
    "\n",
    "explicit_drop = {\n",
    "    \"name\", \"id\", \"identifier\", \"full_name\", \"spkid\", \"pdes\",\n",
    "    \"orbit_id\", \"extent\"\n",
    "}\n",
    "\n",
    "to_drop = [c for c in X.columns if c in explicit_drop]\n",
    "\n",
    "# Удаляем почти пустые столбцы\n",
    "missing_share = X.isna().mean()\n",
    "to_drop += missing_share[missing_share > 0.80].index.tolist()\n",
    "\n",
    "# Удаляем высококардинальные строковые идентификаторы, но сохраняем короткие полезные категориальные столбцы\n",
    "candidate_keep = {\"neo\", \"class\", \"spec_b\", \"spec_t\"}\n",
    "obj_cols = X.select_dtypes(include=\"object\").columns.tolist()\n",
    "\n",
    "for col in obj_cols:\n",
    "    nun = X[col].nunique(dropna=True)\n",
    "    ratio = nun / max(len(X), 1)\n",
    "    avg_len = X[col].dropna().astype(str).str.len().mean() if X[col].notna().any() else 0\n",
    "    if col in candidate_keep:\n",
    "        continue\n",
    "    if nun > 50 or ratio > 0.10 or avg_len > 25:\n",
    "        to_drop.append(col)\n",
    "\n",
    "to_drop = sorted(set([c for c in to_drop if c in X.columns]))\n",
    "X = X.drop(columns=to_drop, errors=\"ignore\")\n",
    "\n",
    "numeric_features = X.select_dtypes(include=[np.number]).columns.tolist()\n",
    "categorical_features = [c for c in X.columns if c not in numeric_features]\n",
    "\n",
    "# Удаляем слишком разреженные категориальные столбцы\n",
    "cat_keep = []\n",
    "for col in categorical_features:\n",
    "    nun = X[col].nunique(dropna=True)\n",
    "    if 2 <= nun <= 30:\n",
    "        cat_keep.append(col)\n",
    "\n",
    "categorical_features = cat_keep\n",
    "X = X[numeric_features + categorical_features].copy()\n",
    "\n",
    "print(\"Удалённые несодержательные / проблемные признаки:\", to_drop)\n",
    "print(\"\\nЧисло исходных содержательных признаков:\", X.shape[1])\n",
    "print(\"Числовые признаки:\", numeric_features)\n",
    "print(\"Категориальные признаки:\", categorical_features)\n",
    "display(X.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5dd17774",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Размеры выборок:\n",
      "train: (493962, 18) (493962,)\n",
      "val: (164655, 18) (164655,)\n",
      "test: (164655, 18) (164655,)\n"
     ]
    }
   ],
   "source": [
    "# Разбиение на train / validation / test\n",
    "X_trainval, X_test, y_trainval, y_test = train_test_split(\n",
    "    X, y,\n",
    "    test_size=0.20,\n",
    "    random_state=SEED,\n",
    "    stratify=y\n",
    ")\n",
    "\n",
    "X_train, X_val, y_train, y_val = train_test_split(\n",
    "    X_trainval, y_trainval,\n",
    "    test_size=0.25,   # 0.25 * 0.80 = 0.20\n",
    "    random_state=SEED,\n",
    "    stratify=y_trainval\n",
    ")\n",
    "\n",
    "print(\"Размеры выборок:\")\n",
    "print(\"train:\", X_train.shape, y_train.shape)\n",
    "print(\"val:\", X_val.shape, y_val.shape)\n",
    "print(\"test:\", X_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "edbf9122",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Размерность после кодирования: 32\n",
      "Первые признаки после преобразования: ['num__a', 'num__e', 'num__i', 'num__om', 'num__w', 'num__q', 'num__ad', 'num__per_y', 'num__data_arc', 'num__condition_code', 'num__n_obs_used', 'num__h', 'num__moid', 'num__n', 'num__per', 'num__ma', 'cat__neo_N', 'cat__neo_Y', 'cat__class_AMO', 'cat__class_APO']\n"
     ]
    }
   ],
   "source": [
    "# Предобработка\n",
    "try:\n",
    "    ohe = OneHotEncoder(handle_unknown=\"ignore\", sparse_output=False)\n",
    "except TypeError:\n",
    "    ohe = OneHotEncoder(handle_unknown=\"ignore\", sparse=False)\n",
    "\n",
    "num_pipe = Pipeline([\n",
    "    (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
    "    (\"scaler\", StandardScaler())\n",
    "])\n",
    "\n",
    "cat_pipe = Pipeline([\n",
    "    (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
    "    (\"ohe\", ohe)\n",
    "])\n",
    "\n",
    "preprocessor = ColumnTransformer(\n",
    "    transformers=[\n",
    "        (\"num\", num_pipe, numeric_features),\n",
    "        (\"cat\", cat_pipe, categorical_features)\n",
    "    ],\n",
    "    remainder=\"drop\"\n",
    ")\n",
    "\n",
    "X_train_p = preprocessor.fit_transform(X_train)\n",
    "X_val_p = preprocessor.transform(X_val)\n",
    "X_test_p = preprocessor.transform(X_test)\n",
    "X_trainval_p = preprocessor.transform(X_trainval)\n",
    "\n",
    "feature_names = preprocessor.get_feature_names_out()\n",
    "print(\"Размерность после кодирования:\", X_train_p.shape[1])\n",
    "print(\"Первые признаки после преобразования:\", feature_names[:20].tolist())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "968b8521",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Тензоры и вспомогательные функции\n",
    "device = \"cpu\"\n",
    "\n",
    "X_train_t = torch.tensor(X_train_p, dtype=torch.float32, device=device)\n",
    "X_val_t = torch.tensor(X_val_p, dtype=torch.float32, device=device)\n",
    "X_test_t = torch.tensor(X_test_p, dtype=torch.float32, device=device)\n",
    "X_trainval_t = torch.tensor(X_trainval_p, dtype=torch.float32, device=device)\n",
    "\n",
    "y_train_t = torch.tensor(y_train.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "y_val_t = torch.tensor(y_val.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "y_test_t = torch.tensor(y_test.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "y_trainval_t = torch.tensor(y_trainval.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "\n",
    "input_dim = X_train_p.shape[1]\n",
    "hidden_layers = (64, 32, 16, 8)\n",
    "\n",
    "class DeepBinaryNet(nn.Module):\n",
    "    def __init__(self, input_dim, hidden_layers=(64, 32, 16, 8), dropout=0.10):\n",
    "        super().__init__()\n",
    "        layers = []\n",
    "        prev = input_dim\n",
    "        for h in hidden_layers:\n",
    "            layers.append(nn.Linear(prev, h))\n",
    "            layers.append(nn.ReLU())\n",
    "            layers.append(nn.Dropout(dropout))\n",
    "            prev = h\n",
    "        layers.append(nn.Linear(prev, 1))\n",
    "        self.net = nn.Sequential(*layers)\n",
    "\n",
    "    def forward(self, x):\n",
    "        return self.net(x)\n",
    "\n",
    "def make_model():\n",
    "    model = DeepBinaryNet(input_dim=input_dim, hidden_layers=hidden_layers, dropout=0.10).to(device)\n",
    "    return model\n",
    "\n",
    "def vector_to_model(model, vector):\n",
    "    pointer = 0\n",
    "    with torch.no_grad():\n",
    "        for param in model.parameters():\n",
    "            numel = param.numel()\n",
    "            block = vector[pointer:pointer + numel]\n",
    "            param.copy_(torch.tensor(block.reshape(param.shape), dtype=torch.float32, device=device))\n",
    "            pointer += numel\n",
    "\n",
    "def model_to_vector(model):\n",
    "    return np.concatenate([p.detach().cpu().numpy().ravel() for p in model.parameters()])\n",
    "\n",
    "def predict_proba(model, X_tensor, batch_size=4096):\n",
    "    model.eval()\n",
    "    preds = []\n",
    "    with torch.no_grad():\n",
    "        for start in range(0, len(X_tensor), batch_size):\n",
    "            xb = X_tensor[start:start + batch_size]\n",
    "            logits = model(xb)\n",
    "            probs = torch.sigmoid(logits)\n",
    "            preds.append(probs.cpu().numpy().ravel())\n",
    "    return np.concatenate(preds)\n",
    "\n",
    "def tune_threshold(y_true, y_prob):\n",
    "    thresholds = np.linspace(0.20, 0.80, 61)\n",
    "    best_thr = 0.50\n",
    "    best_score = -1\n",
    "    for thr in thresholds:\n",
    "        y_pred = (y_prob >= thr).astype(int)\n",
    "        score = f1_score(y_true, y_pred, average=\"macro\", zero_division=0)\n",
    "        if score > best_score:\n",
    "            best_score = score\n",
    "            best_thr = thr\n",
    "    return best_thr, best_score\n",
    "\n",
    "def evaluate_binary(y_true, y_prob, threshold=0.5):\n",
    "    y_pred = (y_prob >= threshold).astype(int)\n",
    "    metrics = {\n",
    "        \"accuracy\": accuracy_score(y_true, y_pred),\n",
    "        \"balanced_accuracy\": balanced_accuracy_score(y_true, y_pred),\n",
    "        \"precision_macro\": precision_score(y_true, y_pred, average=\"macro\", zero_division=0),\n",
    "        \"recall_macro\": recall_score(y_true, y_pred, average=\"macro\", zero_division=0),\n",
    "        \"f1_macro\": f1_score(y_true, y_pred, average=\"macro\", zero_division=0),\n",
    "        \"roc_auc\": roc_auc_score(y_true, y_prob) if len(np.unique(y_true)) == 2 else np.nan\n",
    "    }\n",
    "    return metrics, y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a40c7c3c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Размер train-подвыборки для PSO: (6000, 32)\n",
      "Размер val-подвыборки для PSO: (3000, 32)\n",
      "pos_weight: 399.0\n"
     ]
    }
   ],
   "source": [
    "# Подвыборки для PSO: это резко ускоряет эволюцию весов на большом датасете\n",
    "max_pso_train = 6000\n",
    "max_pso_val = 3000\n",
    "\n",
    "def stratified_subsample(X_df, y_sr, max_n, seed=SEED):\n",
    "    if len(X_df) <= max_n:\n",
    "        return X_df, y_sr\n",
    "    frac = max_n / len(X_df)\n",
    "    sampled_idx = (\n",
    "        pd.DataFrame({\"y\": y_sr})\n",
    "        .groupby(\"y\", group_keys=False)\n",
    "        .apply(lambda g: g.sample(max(1, int(round(len(g) * frac))), random_state=seed))\n",
    "        .index\n",
    "    )\n",
    "    return X_df.loc[sampled_idx], y_sr.loc[sampled_idx]\n",
    "\n",
    "X_train_small_df, y_train_small = stratified_subsample(X_train, y_train, max_pso_train)\n",
    "X_val_small_df, y_val_small = stratified_subsample(X_val, y_val, max_pso_val)\n",
    "\n",
    "X_train_small = preprocessor.transform(X_train_small_df)\n",
    "X_val_small = preprocessor.transform(X_val_small_df)\n",
    "\n",
    "X_train_small_t = torch.tensor(X_train_small, dtype=torch.float32, device=device)\n",
    "X_val_small_t = torch.tensor(X_val_small, dtype=torch.float32, device=device)\n",
    "y_train_small_t = torch.tensor(y_train_small.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "y_val_small_t = torch.tensor(y_val_small.values.reshape(-1, 1), dtype=torch.float32, device=device)\n",
    "\n",
    "pos_weight_value = (len(y_train_small) - y_train_small.sum()) / max(y_train_small.sum(), 1)\n",
    "pos_weight_tensor_small = torch.tensor([float(pos_weight_value)], dtype=torch.float32, device=device)\n",
    "\n",
    "print(\"Размер train-подвыборки для PSO:\", X_train_small.shape)\n",
    "print(\"Размер val-подвыборки для PSO:\", X_val_small.shape)\n",
    "print(\"pos_weight:\", float(pos_weight_value))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fd0784f7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Число параметров сети: 4865\n"
     ]
    }
   ],
   "source": [
    "# Функция качества для PSO\n",
    "base_model = make_model()\n",
    "n_params = model_to_vector(base_model).shape[0]\n",
    "print(\"Число параметров сети:\", n_params)\n",
    "\n",
    "criterion_small = nn.BCEWithLogitsLoss(pos_weight=pos_weight_tensor_small)\n",
    "\n",
    "def pso_fitness(weight_vector):\n",
    "    model = make_model()\n",
    "    vector_to_model(model, weight_vector)\n",
    "\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        logits_train = model(X_train_small_t)\n",
    "        logits_val = model(X_val_small_t)\n",
    "\n",
    "        loss_train = criterion_small(logits_train, y_train_small_t).item()\n",
    "        loss_val = criterion_small(logits_val, y_val_small_t).item()\n",
    "\n",
    "        prob_val = torch.sigmoid(logits_val).cpu().numpy().ravel()\n",
    "        thr, f1_val = tune_threshold(y_val_small.values, prob_val)\n",
    "\n",
    "    # минимизируем: основа — валидационный loss, плюс небольшой штраф за слабый F1\n",
    "    score = 0.75 * loss_val + 0.25 * loss_train + 0.20 * (1.0 - f1_val)\n",
    "    return score, loss_train, loss_val, f1_val, thr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "144ef323",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>iteration</th>\n",
       "      <th>best_score</th>\n",
       "      <th>best_train_loss</th>\n",
       "      <th>best_val_loss</th>\n",
       "      <th>best_val_f1_macro</th>\n",
       "      <th>best_threshold</th>\n",
       "      <th>mean_particle_score</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>11</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.075843</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>12</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>5.008521</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>13</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>9.494316</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>14</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>8.205984</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>15</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.066498</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    iteration  best_score  best_train_loss  best_val_loss  best_val_f1_macro  \\\n",
       "10         11    0.453244         0.461247       0.325759           0.531936   \n",
       "11         12    0.453244         0.461247       0.325759           0.531936   \n",
       "12         13    0.453244         0.461247       0.325759           0.531936   \n",
       "13         14    0.453244         0.461247       0.325759           0.531936   \n",
       "14         15    0.453244         0.461247       0.325759           0.531936   \n",
       "\n",
       "    best_threshold  mean_particle_score  \n",
       "10             0.8             2.075843  \n",
       "11             0.8             5.008521  \n",
       "12             0.8             9.494316  \n",
       "13             0.8             8.205984  \n",
       "14             0.8             2.066498  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# PSO: эволюция весов\n",
    "n_particles = 8\n",
    "n_iterations = 15\n",
    "w = 0.72\n",
    "c1 = 1.49\n",
    "c2 = 1.49\n",
    "clip_value = 1.5\n",
    "\n",
    "rng = np.random.default_rng(SEED)\n",
    "\n",
    "particles = rng.normal(loc=0.0, scale=0.20, size=(n_particles, n_params))\n",
    "velocities = rng.normal(loc=0.0, scale=0.05, size=(n_particles, n_params))\n",
    "\n",
    "personal_best = particles.copy()\n",
    "personal_best_scores = np.full(n_particles, np.inf)\n",
    "personal_best_details = [None] * n_particles\n",
    "\n",
    "global_best = None\n",
    "global_best_score = np.inf\n",
    "global_best_details = None\n",
    "\n",
    "pso_history = []\n",
    "\n",
    "for iteration in range(n_iterations):\n",
    "    iter_scores = []\n",
    "\n",
    "    for p in range(n_particles):\n",
    "        score, loss_tr, loss_val, f1_val, thr = pso_fitness(particles[p])\n",
    "        iter_scores.append(score)\n",
    "\n",
    "        if score < personal_best_scores[p]:\n",
    "            personal_best_scores[p] = score\n",
    "            personal_best[p] = particles[p].copy()\n",
    "            personal_best_details[p] = {\n",
    "                \"score\": score,\n",
    "                \"loss_train\": loss_tr,\n",
    "                \"loss_val\": loss_val,\n",
    "                \"f1_val\": f1_val,\n",
    "                \"threshold\": thr\n",
    "            }\n",
    "\n",
    "        if score < global_best_score:\n",
    "            global_best_score = score\n",
    "            global_best = particles[p].copy()\n",
    "            global_best_details = {\n",
    "                \"score\": score,\n",
    "                \"loss_train\": loss_tr,\n",
    "                \"loss_val\": loss_val,\n",
    "                \"f1_val\": f1_val,\n",
    "                \"threshold\": thr\n",
    "            }\n",
    "\n",
    "    for p in range(n_particles):\n",
    "        r1 = rng.random(n_params)\n",
    "        r2 = rng.random(n_params)\n",
    "        velocities[p] = (\n",
    "            w * velocities[p]\n",
    "            + c1 * r1 * (personal_best[p] - particles[p])\n",
    "            + c2 * r2 * (global_best - particles[p])\n",
    "        )\n",
    "        particles[p] = particles[p] + velocities[p]\n",
    "        particles[p] = np.clip(particles[p], -clip_value, clip_value)\n",
    "\n",
    "    pso_history.append({\n",
    "        \"iteration\": iteration + 1,\n",
    "        \"best_score\": global_best_score,\n",
    "        \"best_train_loss\": global_best_details[\"loss_train\"],\n",
    "        \"best_val_loss\": global_best_details[\"loss_val\"],\n",
    "        \"best_val_f1_macro\": global_best_details[\"f1_val\"],\n",
    "        \"best_threshold\": global_best_details[\"threshold\"],\n",
    "        \"mean_particle_score\": float(np.mean(iter_scores))\n",
    "    })\n",
    "\n",
    "pso_history_df = pd.DataFrame(pso_history)\n",
    "display(pso_history_df.tail())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "a2355200",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Порог после PSO: 0.8\n",
      "Метрики на тесте после PSO:\n",
      "{'accuracy': 0.9568248762564149, 'balanced_accuracy': 0.8880109719441571, 'precision_macro': 0.5221681602998851, 'recall_macro': 0.8880109719441571, 'f1_macro': 0.5314219314044559, 'roc_auc': 0.9717215147045432}\n"
     ]
    }
   ],
   "source": [
    "# Модель сразу после PSO\n",
    "model_pso = make_model()\n",
    "vector_to_model(model_pso, global_best)\n",
    "\n",
    "val_prob_pso = predict_proba(model_pso, X_val_t)\n",
    "test_prob_pso = predict_proba(model_pso, X_test_t)\n",
    "\n",
    "threshold_pso, best_val_f1_pso = tune_threshold(y_val.values, val_prob_pso)\n",
    "metrics_pso, test_pred_pso = evaluate_binary(y_test.values, test_prob_pso, threshold=threshold_pso)\n",
    "\n",
    "print(\"Порог после PSO:\", threshold_pso)\n",
    "print(\"Метрики на тесте после PSO:\")\n",
    "print(metrics_pso)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "2e433dde",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>epoch</th>\n",
       "      <th>train_loss</th>\n",
       "      <th>val_accuracy</th>\n",
       "      <th>val_balanced_accuracy</th>\n",
       "      <th>val_f1_macro</th>\n",
       "      <th>val_roc_auc</th>\n",
       "      <th>threshold</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>16</td>\n",
       "      <td>0.052293</td>\n",
       "      <td>0.988886</td>\n",
       "      <td>0.990716</td>\n",
       "      <td>0.649290</td>\n",
       "      <td>0.998138</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>17</td>\n",
       "      <td>0.052562</td>\n",
       "      <td>0.990750</td>\n",
       "      <td>0.987938</td>\n",
       "      <td>0.669013</td>\n",
       "      <td>0.998208</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>18</td>\n",
       "      <td>0.048724</td>\n",
       "      <td>0.991060</td>\n",
       "      <td>0.985618</td>\n",
       "      <td>0.672374</td>\n",
       "      <td>0.998246</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>19</td>\n",
       "      <td>0.050518</td>\n",
       "      <td>0.991995</td>\n",
       "      <td>0.982374</td>\n",
       "      <td>0.684475</td>\n",
       "      <td>0.998266</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>20</td>\n",
       "      <td>0.046750</td>\n",
       "      <td>0.990933</td>\n",
       "      <td>0.988029</td>\n",
       "      <td>0.671307</td>\n",
       "      <td>0.998385</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    epoch  train_loss  val_accuracy  val_balanced_accuracy  val_f1_macro  \\\n",
       "15     16    0.052293      0.988886               0.990716      0.649290   \n",
       "16     17    0.052562      0.990750               0.987938      0.669013   \n",
       "17     18    0.048724      0.991060               0.985618      0.672374   \n",
       "18     19    0.050518      0.991995               0.982374      0.684475   \n",
       "19     20    0.046750      0.990933               0.988029      0.671307   \n",
       "\n",
       "    val_roc_auc  threshold  \n",
       "15     0.998138        0.8  \n",
       "16     0.998208        0.8  \n",
       "17     0.998246        0.8  \n",
       "18     0.998266        0.8  \n",
       "19     0.998385        0.8  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Дообучение модели после PSO\n",
    "train_dataset = TensorDataset(X_train_t.cpu(), y_train_t.cpu())\n",
    "val_dataset = TensorDataset(X_val_t.cpu(), y_val_t.cpu())\n",
    "\n",
    "train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True)\n",
    "val_loader = DataLoader(val_dataset, batch_size=4096, shuffle=False)\n",
    "\n",
    "pos_weight_full = (len(y_train) - y_train.sum()) / max(y_train.sum(), 1)\n",
    "criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([float(pos_weight_full)], dtype=torch.float32))\n",
    "optimizer = torch.optim.Adam(model_pso.parameters(), lr=1e-3, weight_decay=1e-5)\n",
    "\n",
    "n_epochs = 20\n",
    "patience = 5\n",
    "best_state = copy.deepcopy(model_pso.state_dict())\n",
    "best_val_f1 = -np.inf\n",
    "best_threshold_ft = threshold_pso\n",
    "epochs_no_improve = 0\n",
    "\n",
    "finetune_history = []\n",
    "\n",
    "for epoch in range(n_epochs):\n",
    "    model_pso.train()\n",
    "    batch_losses = []\n",
    "\n",
    "    for xb, yb in train_loader:\n",
    "        xb = xb.to(device)\n",
    "        yb = yb.to(device)\n",
    "\n",
    "        optimizer.zero_grad()\n",
    "        logits = model_pso(xb)\n",
    "        loss = criterion(logits, yb)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        batch_losses.append(loss.item())\n",
    "\n",
    "    model_pso.eval()\n",
    "    with torch.no_grad():\n",
    "        val_logits = model_pso(X_val_t)\n",
    "        val_prob = torch.sigmoid(val_logits).cpu().numpy().ravel()\n",
    "\n",
    "    thr, val_f1 = tune_threshold(y_val.values, val_prob)\n",
    "    val_metrics, _ = evaluate_binary(y_val.values, val_prob, threshold=thr)\n",
    "\n",
    "    finetune_history.append({\n",
    "        \"epoch\": epoch + 1,\n",
    "        \"train_loss\": float(np.mean(batch_losses)),\n",
    "        \"val_accuracy\": val_metrics[\"accuracy\"],\n",
    "        \"val_balanced_accuracy\": val_metrics[\"balanced_accuracy\"],\n",
    "        \"val_f1_macro\": val_metrics[\"f1_macro\"],\n",
    "        \"val_roc_auc\": val_metrics[\"roc_auc\"],\n",
    "        \"threshold\": thr\n",
    "    })\n",
    "\n",
    "    if val_f1 > best_val_f1:\n",
    "        best_val_f1 = val_f1\n",
    "        best_state = copy.deepcopy(model_pso.state_dict())\n",
    "        best_threshold_ft = thr\n",
    "        epochs_no_improve = 0\n",
    "    else:\n",
    "        epochs_no_improve += 1\n",
    "\n",
    "    if epochs_no_improve >= patience:\n",
    "        break\n",
    "\n",
    "finetune_history_df = pd.DataFrame(finetune_history)\n",
    "display(finetune_history_df.tail())\n",
    "\n",
    "model_pso.load_state_dict(best_state)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "948a732b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Лучший порог после дообучения: 0.8\n",
      "Метрики на тесте после дообучения:\n",
      "{'accuracy': 0.992347635966111, 'balanced_accuracy': 0.9862632247767442, 'precision_macro': 0.6198904582912288, 'recall_macro': 0.9862632247767442, 'f1_macro': 0.6907578519652458, 'roc_auc': 0.998356569633455}\n",
      "\n",
      "Матрица ошибок:\n",
      "[[163000   1252]\n",
      " [     8    395]]\n",
      "\n",
      "Классификационный отчёт:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0     1.0000    0.9924    0.9961    164252\n",
      "           1     0.2398    0.9801    0.3854       403\n",
      "\n",
      "    accuracy                         0.9923    164655\n",
      "   macro avg     0.6199    0.9863    0.6908    164655\n",
      "weighted avg     0.9981    0.9923    0.9947    164655\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Финальная оценка после дообучения\n",
    "val_prob_ft = predict_proba(model_pso, X_val_t)\n",
    "test_prob_ft = predict_proba(model_pso, X_test_t)\n",
    "\n",
    "metrics_ft, test_pred_ft = evaluate_binary(y_test.values, test_prob_ft, threshold=best_threshold_ft)\n",
    "cm_ft = confusion_matrix(y_test.values, test_pred_ft)\n",
    "\n",
    "print(\"Лучший порог после дообучения:\", best_threshold_ft)\n",
    "print(\"Метрики на тесте после дообучения:\")\n",
    "print(metrics_ft)\n",
    "print(\"\\nМатрица ошибок:\")\n",
    "print(cm_ft)\n",
    "print(\"\\nКлассификационный отчёт:\")\n",
    "print(classification_report(y_test.values, test_pred_ft, digits=4, zero_division=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f529390e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>model</th>\n",
       "      <th>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>precision_macro</th>\n",
       "      <th>recall_macro</th>\n",
       "      <th>f1_macro</th>\n",
       "      <th>roc_auc</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Глубокая нейросеть: PSO + дообучение</td>\n",
       "      <td>0.992348</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.619890</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.690758</td>\n",
       "      <td>0.998357</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Глубокая нейросеть после PSO</td>\n",
       "      <td>0.956825</td>\n",
       "      <td>0.888011</td>\n",
       "      <td>0.522168</td>\n",
       "      <td>0.888011</td>\n",
       "      <td>0.531422</td>\n",
       "      <td>0.971722</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  model  accuracy  balanced_accuracy  \\\n",
       "0  Глубокая нейросеть: PSO + дообучение  0.992348           0.986263   \n",
       "1          Глубокая нейросеть после PSO  0.956825           0.888011   \n",
       "\n",
       "   precision_macro  recall_macro  f1_macro   roc_auc  n_original_features  \\\n",
       "0         0.619890      0.986263  0.690758  0.998357                   18   \n",
       "1         0.522168      0.888011  0.531422  0.971722                   18   \n",
       "\n",
       "   n_processed_features    hidden_layers  \n",
       "0                    32  (64, 32, 16, 8)  \n",
       "1                    32  (64, 32, 16, 8)  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Лучшая модель:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>model</th>\n",
       "      <th>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>precision_macro</th>\n",
       "      <th>recall_macro</th>\n",
       "      <th>f1_macro</th>\n",
       "      <th>roc_auc</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Глубокая нейросеть: PSO + дообучение</td>\n",
       "      <td>0.992348</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.61989</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.690758</td>\n",
       "      <td>0.998357</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  model  accuracy  balanced_accuracy  \\\n",
       "0  Глубокая нейросеть: PSO + дообучение  0.992348           0.986263   \n",
       "\n",
       "   precision_macro  recall_macro  f1_macro   roc_auc  n_original_features  \\\n",
       "0          0.61989      0.986263  0.690758  0.998357                   18   \n",
       "\n",
       "   n_processed_features    hidden_layers  \n",
       "0                    32  (64, 32, 16, 8)  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Сводная таблица результатов\n",
    "results_df = pd.DataFrame([\n",
    "    {\n",
    "        \"model\": \"Глубокая нейросеть после PSO\",\n",
    "        **metrics_pso,\n",
    "        \"n_original_features\": X.shape[1],\n",
    "        \"n_processed_features\": X_train_p.shape[1],\n",
    "        \"hidden_layers\": str(hidden_layers)\n",
    "    },\n",
    "    {\n",
    "        \"model\": \"Глубокая нейросеть: PSO + дообучение\",\n",
    "        **metrics_ft,\n",
    "        \"n_original_features\": X.shape[1],\n",
    "        \"n_processed_features\": X_train_p.shape[1],\n",
    "        \"hidden_layers\": str(hidden_layers)\n",
    "    }\n",
    "]).sort_values([\"f1_macro\", \"balanced_accuracy\", \"roc_auc\"], ascending=False).reset_index(drop=True)\n",
    "\n",
    "display(results_df)\n",
    "print(\"Лучшая модель:\")\n",
    "display(results_df.head(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "df49c00b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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/T3fqlWrWp+HjhgxAr/99pvOY+DAgXWK/UHGjBmj8/nr0qULAGDChAk6d1J26dIFZWVlOndK3v+1zc/PR0ZGBnr27ImioiJcu3YNAHDhwgXExcVh1qxZ2vNgjX+2nwCAadOm6Xx88OBByGSyKn9/vPXWWxAEgXNTyOgwaUtE+PLLL/Hbb7/h2LFjiI6ORmxsbJWTgICAAHz77bfIyMjApUuXtLczvfLKKzhy5AiAewnZ+09o/6k2id2HycnJqXIC+k+tW7fGE088odMiYdu2bejatStatmyp3datWzf88ccf+P7775Geno6MjAzk5ubWObbqZGVlYebMmXB1dYWFhQWcnZ21J/j3H0vTsuHtt9/G1atXtSfRteHk5ISnnnpKp0XCjh07YGJionNbl0qlwqeffgp/f3+Ym5vDyckJzs7OuHTp0iO/74SEBEilUvj5+els1/R2u19xcTHmz5+v7T2lOW5OTk6dP98JCQnw9/ev0gdP004hISFBZ3vz5s11PtYkcGtKqD7ouED177N169ba583NzbFs2TL88ssvcHV1Ra9evfDJJ5/o3DrYu3dvPPvss1i4cCGcnJwwYsQIbNq0qUFudyMiItI3CQkJMDExqVXSNiEhodrfvf/8vV/b8wOJRAKpVAp/f3+ddQqFAu7u7jr95ydOnIji4mJtG4br16/j/PnzePHFF7VrWrRoATc3N6xYsQKXLl2q8Rb2mJgY5ObmwsXFBc7OzjqPgoKCKkN+f/311yrrvv766wd+rhYsWAAPDw+8+uqrVZ6LiYnBlStXquwzICAAQNUhw7U1duxYnDp1SpvEO378ONLT06u0XPj555/RtWtXyOVyODg4aNsS1OU8FECVr5+pqSlatGhRZf2WLVvQvn17yOVyODo6wtnZGQcOHHis81CpVKrzdwUAuLm5wc7O7qHnoYD6XLS256Genp7o16+fzsPd3f2R487KykJqaqr28c/3/884NQlcLy+varffH/+VK1fwzDPPQKFQwNbWFs7OztpiGc1xNG34/jkrpDomJibw9PTU2ZaQkAAPD48qf0vWdP5PZOjEG39IRHqjc+fO6NSpU63WymQytGvXDu3atUNYWBj69u2Lbdu2oV+/ftpflpcuXcLIkSOrff2lS5cAqK8211Vqairc3Nweum7ixImYOXMmbt++jdLSUpw5c0ZnQBig7p16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tV3XmPbt24eRI0fizz//RM+ePcUOh4iIjBx72hKRUdu7dy/u3r1b63YKRERERETV2bBhA1q0aIEePXqIHQoRETUB7GlLREbp7NmzuHTpEhYvXoyQkJAGG0pFRERERLVnbW2NF154Aa6urvWyrjF8//33uHTpEg4cOIDPPvusxiFfRERE9YntEYjIKE2ePBlbt25FcHAwNm/eXO0AASIiIiKih5FIJLC2tsbYsWOxbt06mJiw9omIiBoek7ZEREREREREREREeoQ9bYmIiIiIiIiIiIj0CJO2RERERERERERERHqESVsiIiIiIiIiIiIiPdLkOqirVCrcuXMHNjY2nPpJREREpMcEQUB+fj48PDwglbLW4EF4jktERERkGGp7jtvkkrZ37tyBl5eX2GEQERERUS0lJSXB09NT7DD0Gs9xiYiIiAzLw85xm1zS1sbGBoD6E2NraytyNERERERUk7y8PHh5eWnP36hmPMclIiIiMgy1Pcdtcklbze1itra2PKElIiIiMgC83f/heI5LREREZFgedo7L5mBEREREREREREREeoRJWyIiIiIiIiIiIiI9wqQtERERERERERERkR5h0paIiIiIiIiIiIhIjzBpS0RERERERERERKRHmLQlIiIiIiIiIiIi0iNM2hIRERERERERERHpESZtiYiIiIiIiIiIiPQIk7ZEREREREREREREeoRJWyIiIiIiIiIiIiI9YiJ2AMZOqRIQEZeF9PwSuNjI0dnXATKpROywiIiIiIiIiIiI9ALzZ1UxaduADl1OwcL90UjJLdFuc1fIsWB4IAYFuYsYGRERERERERERkfiYP6se2yM0kEOXUzBta6TONxwApOaWYNrWSBy6nCJSZEREREREREREROJj/qxmTNo2AKVKwML90RCqeU6zbeH+aChV1a0gIiIiIiIiIiIybsyfPRiTtg0gIi6ryhWC+wkAUnJLEBGX1XhBERERERERERER6Qnmzx6MSdsGkJ5f8zdcXdYREREREREREREZE+bPHoxJ2wbgYiOv13VERERERERERETGhPmzB2PStgF09nWAu0IOSQ3PS6CegtfZ16ExwyIiIiIiIiIiItILmvxZTZp6/oxJ2wYgk0qwYHggANSYuF0wPBAyaU3PEhERERERERERGa/782c1acr5MyZtG8igIHesndARbtVcMRjczh2DgtxFiIqIiIiIiIiIiEg/tHSxqXa7RAL8d3xwk86fmYgdgDEbFOSO/oFuiIjLQnp+CeIyCrH6SAyOXE1DfEYhfJysxA6RiIiIiIiIiIhIFJtPxwEAnmrtgpd6tkBqbjEW/RyN7KJyCDXev940sNK2gcmkEoT5OWJEcDPMfMofPf2dUFahwgf7r0AQBLHDIyIiIiIiIiIianQ5RWX44XwyAOBfPX0R5ueIZzp64sUwHwDA9rMJIkYnPiZtG5FEIsHCp9vCVCbB8et38Wt0mtghERERERERERERNbrv/0pCcbkSrd1sENbCUbt93BNekEqAM7FZuHW3QMQIxcWkbSNr4WyNV3q1AAAs2h+NorIKkSMiIiIiIiIiIiJqPBVKFb45HQ8AmNrDFxLJvVYIHnYW6NvKBQDwfUSiGOHpBSZtRTC9b0s0s7NAck4xvjx2U+xwiIiIiIiIiIiIGs3hK2m4k1sCRyszPN3Bo8rz4zs3BwDsPn8bJeXKxg5PLzBpKwJLMxPMHx4IAFj/Zyxim3CpNxERERERERERNS0bT6kHkL3Q1RtyU1mV5/u0coa7Qo7sonIcvpLa2OHpBSZtRTIg0BV9WjmjXClgwU8cSkZERERERERERMYvKikH5xOyYSqTYELX5tWuMZFJMfYJLwDAtrNNs0UCk7YikUgk+GB4W5iZSHEiJgO/XG6aVw2IiIiIiIiIiKjp2FRZZTu8gwdcbOQ1rhtbOZAsIi4LN9PzGys8vcGkrYh8nKzwWm8/AMDin6NRWMqhZEREREREREREZJxSc0tw4FIKAGBqd98HrnVXWODJ1q4AgO1nkxo8Nn3DpK3IXu/jB097C6TkluC/v8eIHQ4REREREREREVGD+PZMPCpUAjr7OiComeKh61/oom6f8ENk0xtIxqStyOSmMnwwvC0A4OsTcU2y3JuIiIiIiIiIiIxbcZkS2yv70z6sylajV4AzmtlZILe4HAf/TmnI8PQOk7Z6oF+gK/q1cUGFSsD8fRxKRkRERERERERExmVvVDKyi8rhaW+B/oGutXqNTCrBuMqBZN9FNK2BZEza6okFw9vC3ESK07cysf9S07pyQERERERERERExksQBGw8qR5ANrmbD2RSSa1f+9wTXpBJJfgrPhs30prOHepM2uoJLwdLvN6nJQDgw5+jUcChZEREREREREREZARO3sxATHoBrMxkeK6ycra2XG3leKq1CwBo2ys0BUza6pFXe7eAt6Ml0vNL8dmRG2KHQ0RERERERERE9Ng0VbZjOnnBVm76yK9/vnIg2Z4mNJCMSVs9IjeV4YOn1UPJNp6Kx/XUplPyTURERERERERExufW3QIcu34XEgkwqZtPnfbRy98ZnvYWyCupwM9NpK0ok7Z6pm8rFwwIdIVSJeD9fZc5lIyIiIhIz3z55Zfw8fGBXC5Hly5dEBER8cD1OTk5mD59Otzd3WFubo6AgAAcPHhQ+7xSqcT7778PX19fWFhYwM/PD4sXL+Z5IBERERmFLafjAQBPtXaBr5NVnfYhlUowvrO62nb72YT6Ck2vMWmrh+YPD4TcVIqIuCzsi7ojdjhEREREVGnHjh2YPXs2FixYgMjISHTo0AEDBw5Eenp6tevLysrQv39/xMfHY/fu3bh+/To2bNiAZs2aadcsW7YMa9euxRdffIGrV69i2bJl+OSTT/D555831tsiIiIiahC5ReXYde42AGBqd9/H2teYTp4wkUoQmZiDa6l59RGeXmPSVg952lvi/570BwB8dPAq8krKRY6IiIiIiABg1apVePnllzFlyhQEBgZi3bp1sLS0xMaNG6tdv3HjRmRlZWHv3r3o3r07fHx80Lt3b3To0EG75vTp0xgxYgSGDh0KHx8fjB49GgMGDHhoBS8RERGRvttxLhHF5Uq0drNBmJ/jY+3LxUaO/oGuAIDvmsBAMiZt9dRLPX3RwskKd/NL8elvHEpGREREJLaysjKcP38e/fr1026TSqXo168fwsPDq33NTz/9hLCwMEyfPh2urq4ICgrCkiVLoFTeG6DRrVs3HD16FDduqM/5Ll68iJMnT2Lw4ME1xlJaWoq8vDydBxEREZE+qVCqsOW0upXB1O6+kEgkj71PTYuEPReSUVxm3APJmLTVU+Ym94aSbTkdj+g7PBEnIiIiElNGRgaUSiVcXV11tru6uiI1NbXa18TGxmL37t1QKpU4ePAg3n//faxcuRIffvihds3cuXMxbtw4tG7dGqampggJCcGsWbPwwgsv1BjL0qVLoVAotA8vL6/6eZNERERE9eTX6DQk5xTDwcoMTwd71Ms+e7R0QnMHS+SXVGD/JeNuKcqkrR7rFeCMIe3coBKA+RxKRkRERGRwVCoVXFxcsH79eoSGhmLs2LF47733sG7dOu2anTt3Ytu2bdi+fTsiIyOxZcsWrFixAlu2bKlxv/PmzUNubq72kZSU1Bhvh4iIiKjWNp6MAwBM6NIcclNZvexTKpVgXGf1xertRt4igUlbPfefoYGwMJXhXEI2fohMFjscIiIioibLyckJMpkMaWlpOtvT0tLg5uZW7Wvc3d0REBAAmezeHypt2rRBamoqysrKAABvv/22ttq2Xbt2ePHFF/Hmm29i6dKlNcZibm4OW1tbnQcRERGRvriYlINzCdkwlUkwoat3ve57TKgXTKQSRCXlGPWd6Uza6jkPOwu88ZR6KNnSg1eRW8yhZERERERiMDMzQ2hoKI4ePardplKpcPToUYSFhVX7mu7du+PmzZtQqVTabTdu3IC7uzvMzMwAAEVFRZBKdU/LZTKZzmuIiIiIDMmmU+oq2+HtPeBiK6/XfTvbmGNgW/UF8+0RCfW6b33CpK0B+FcPX/g5WyGzsAyrfr0udjhERERETdbs2bOxYcMGbNmyBVevXsW0adNQWFiIKVOmAAAmTpyIefPmaddPmzYNWVlZmDlzJm7cuIEDBw5gyZIlmD59unbN8OHD8dFHH+HAgQOIj4/Hjz/+iFWrVuGZZ55p9PdHRERE9LjS8krw86UUAMCU7r4Ncoznu6gHku29cAeFpRUNcgyxiZq0/fPPPzF8+HB4eHhAIpFg7969D1y/Z88e9O/fH87OzrC1tUVYWBgOHz7cOMGKyMxEikUjggAA355JwOXkXJEjIiIiImqaxo4dixUrVmD+/PkIDg5GVFQUDh06pB1OlpiYiJSUFO16Ly8vHD58GH/99Rfat2+PN954AzNnzsTcuXO1az7//HOMHj0ar7/+Otq0aYM5c+bg1VdfxeLFixv9/REREdWGUiUg/FYm9kUlI/xWJpQqzuChe74NT0CFSsATPvZo56lokGOEtXCEj6MlCkor8LORDiSTCCJOt/rll19w6tQphIaGYtSoUfjxxx8xcuTIGtfPmjULHh4e6Nu3L+zs7LBp0yasWLECZ8+eRUhISK2OmZeXB4VCgdzcXIPr/TVjeyR+vpSCkOZ2+OG1bpBKJWKHRERERNRgDPm8rbHxc0VERI3l0OUULNwfjZTcEu02d4UcC4YHYlCQu4iRkT4oKVcibOlRZBeVY+0LHTG4XcN9T6z74xY+/uUaOngqsG9GjwY7Tn2r7XmbqJW2gwcPxocffljrW79Wr16Nd955B0888QT8/f2xZMkS+Pv7Y//+/Q0cqX74z9BAWJnJcCExB7vP3xY7HCIiIiIiIiJqQg5dTsG0rZE6CVsASM0twbStkTh0OaWGV1JTsS8qGdlF5WhmZ4H+ga4NeqzRoZ4wlUlw8XauUd6VbtA9bVUqFfLz8+Hg4CB2KI3CTSHHrH4BAICPD11DTlGZyBERERERERERUVOgVAlYuD8a1d2urdm2cH80WyU0YYIgYOPJeADA5G4+MJE1bNrRyfr+gWSJDXosMRh00nbFihUoKCjAc889V+Oa0tJS5OXl6TwM2eTuPghwtUZWYRmWH+ZQMiIiIiIiIiJqeBFxWVUqbO8nAEjJLUFEXFbjBUV65fStTFxPy4elmQzPPeHVKMfUDCTbdyEZBUY2kMxgk7bbt2/HwoULsXPnTri4uNS4bunSpVAoFNqHl1fjfNM0FFPZvaFk2yMScel2jrgBEREREREREZHRS8+vOWFbl3VkfDaejAMAjAn1hMLCtFGOGdbCES2crFBYpsRPUcY1kMwgk7bff/89XnrpJezcuRP9+vV74Np58+YhNzdX+0hKSmqkKBtO1xaOGBnsAUEA3t97mbceEBEREREREVGDcrGR1+s6Mi5xGYU4ei0dADC5u2+jHVcikWB8Z3W17faIhEY7bmMwuKTtd999hylTpuC7777D0KFDH7re3Nwctra2Og9j8O8hbWBjboKLt3Ox4y/DT0QTERERERERkf7q7OsAd0XNCVkJAHeFHJ19m8bcIdK1+ZS6yvap1i7wdbJq1GM/G+oJM5kUl5PzjOqOdFGTtgUFBYiKikJUVBQAIC4uDlFRUUhMVDcPnjdvHiZOnKhdv337dkycOBErV65Ely5dkJqaitTUVOTmGt+EuIdxsZXjzf7qoWSfHL6GrEIOJSMiIiIiIiKihiGTSvD+0MAHrlkwPBAyqaSRIiJ9kVtcjl3nbwMApvZovCpbDQcrMwxupx5I9p0RDSQTNWl77tw5hISEICQkBAAwe/ZshISEYP78+QCAlJQUbQIXANavX4+KigpMnz4d7u7u2sfMmTNFiV9sE8O80drNBjlF5fjk0DWxwyEiIiIiIiIiI1amVAFQV9X+08rnOmBQkHvjBkR6YedfSSgqU6KVqw26+TmKEoOmRcK+qDvILykXJYb6ZiLmwfv06QNBqLkf6+bNm3U+Pn78eMMGZGBMZFIsHhmEMevC8f1fSXjuCS90bG4vdlhEREREREREZGRKK5RY8et1AMDsAQHo5O2A9LwSLDt8DXdySlBaoRI5QhJDhVKFzafjAQBTe/hAIhGn0rqLrwP8nK1w624h9kXdwYSu3qLEUZ8Mrqct6XrCxwHPdvQEAMzfx6FkRERERERERFT/tp5JxO3sYrjYmOOlHi0Q5ueIESHNMCnMBwCw6xzn7TRFv0WnITmnGA5WZhgR3Ey0OHQGkp1NfGCRqKFg0tYIzB3cGjZyE1xOzsN2I+rdQURERERERETiyyspxxe/xwAA3uwfAAszmfa5Z0KaQSoBIhNzcOtugVghkkg2Vg4ge75zc8hNZQ9Z3bBGh3rCzESK6JQ8XLxt+POvmLQ1As425pgzoBUAYPmha8goKBU5IiIiIiIiIiIyFuv/iEV2UTn8nK0wJtRT5zkXWzl6BzgDAH6oHEZFTcPft3PxV3w2TKQSvBgmfjsCO0szDG2n7qu8/WyCyNE8PiZtjcSErt5o62GLvJIKLPuFQ8mIiIiIiIiI6PGl5ZXgq5OxAIB3BrWGiaxqKmlMJy8AwJ7IZLZtbEI2VVbZDmvvDldbucjRqD3fRd0iYf/FFOQZ+EAyJm2NhEwqwaIRQQCAXedv43xClsgRERERERERkT5QqgSE38rEvqhkhN/KZFKNHsnqIzEoKVch1NseAwJdq13zVBsX2FmaIjWvBCdvZjRyhCSG9LwS7L90BwAwtYevyNHc08nbHv4u1iguV2LfhWSxw3ksTNoakVBvezzXSX2bwn/2XkGFkpMbiYiIiIiImrJDl1PQY9nvGL/hDGZ+H4XxG86gx7LfcehyitihkQG4mV6AnZUDxuYObg2JRFLtOnMTGUZ08ADAgWRNxdYzCShXCujkbY/2nnZih6N1/0CybQY+kIxJWyPz7qDWUFiY4mpKHraeMfz+HURERERERFQ3hy6nYNrWSKTkluhsT80twbStkUzc0kMtP3wNSpWAfm1c8YSPwwPXjg5Vt0j4NToNuUWGfVs6PVhJuRJbzyYC0K8qW41nO3rC3ESKa6n5uJCUI3Y4dcakrZFxtDbH2wPVQ8lW/noDd/M5lIyIiIiIiKipUaoELNwfjepqzDTbFu6PZqsEqtH5hCwcvpIGqQR4d1Crh64PamaLVq42KKtQaW+bJ+P0U9QdZBWWoZmdRY0tM8SksDTF0PaagWSJIkdTd0zaGqHxnZujvacC+aUVWHrwqtjhEBERERERUSOLiMuqUmF7PwFASm4JIuI4D4WqEgQBH1cOOR8T6gV/V5uHvkYikWBMZcvGXedvN2h8JB5BELCxcgDZpG7e1Q6m0wcvVA4k+/nSHeQWG2blt35+ZumxyKQSLB4RBIkE2HMhGWdjM8UOiYiIiIiIiBpRen7NCdu6rKOm5cjVdPwVnw1zEyne7B9Q69eNCG4GmVSCi0k5iEnLb8AISSzhtzJxLTUflmYyjO3UXOxwatSxuT1audqgpFyFHyMN8yICk7ZGqoOXHcY9of7PM3/fFZRzKBkREREREVGT4WIjr9d11HRUKFX45JC6ynZqD1+4KWr/PeJsY46+rVwAALtZbWuUNFW2o0M9obA0FTmamkkkEjxfWW27PcIwB5IxaWvE3hnYCvaWprielo8tp+PFDoeIiIiIiIgaSWdfB7gr5JDU8LwEgLtCjs6+Dx4uRU3PnshkxKQXwM7SFK/19nvk148OVbdI2HMhGRUsIDMqcRmFOHotHQAwuZuPuMHUwsiQZpCbSnEjrQCRidlih/PImLQ1YvZWZnh3UGsAwOojMUjL420vRERERERETYFMKsGC4YEPXLNgeCBk0prSutQUFZcpseq3GwCAGX1bQmHx6JWUT7Z2gYOVGe7ml+JETEZ9h0gi2nI6HoKg/hq3cLYWO5yHUliYYnh7DwDANgMcSMakrZF7rpMXOnjZoaC0Aks4lIyIiIiIiKjJGBTkjrUTOsLkH4lZOwtTrJ3QEYOC3EWKjPTV5tPxSM0rQTM7C0zo6l2nfZiZSDEiWJ0o23U+qT7DIxHlFpdj5zn113NKdx9xg3kE4ytbJBy4lILcIsMaSMakrZGTSiX4sHIo2b6oOzh9i1e5iIiIiIiImoonfBxQoVL3cgzxsgMAjOnkyYQtVZFdWIY1x28CAN4aEAC5qazO+9K0SDgSnY7swrJ6iY/EtetcEorKlPB3sUaPlk5ih1NrIV52aO1mg9IKFX4wsIFkTNo2Ae08FZjQRX2FjEPJiIiIiIiImo6IuCwAQCtXGzxbmUi7mV4gZkikp9Ycv4n8kgq0drPBiOBmj7Wvth4KBLrbokypwk8X79RThCQWpUrA5spZSVN7+EIiMZy2KhKJBC8Y6EAyJm2biDkDWsHBygw30wuwqXLSHxERERERERm3M7GZAICuLRwQ4GoDALiRxqQt6bqdXYQtpxMAAHMHt66XXseaatvd5w2rupGq+i06Dbezi2FvaYpnQh4voS+GESHNYGEqw830AvwVbzgDyZi0bSIUlqaYO/jeULKU3GKRIyIiIiIiIqKGdray0rZrC0cEuKoHByXnFKOwtELMsEjPrPrtBsqUKoS1cETvAOd62eeIYA+YSCX4OzkX11Lz6mWfJI6NlcV/z3dp/lhtM8RiKzfF0x3UfZa3n00QOZraY9K2CRnd0ROh3vYoKlPiwwMcSkZERERERGTMsgrLcC01HwDQ2dcBdpZmcLYxBwDEsEUCVYq+k4cfLyQDUFfZ1tet747W5niqjQsAYPc5VtsaqsvJuYiIy4KJVIIXu/qIHU6dPV/ZIuHg5VSD6bPMpG0TIpVKsGhEW0gl6ql5J2M4lIyIiIiIiMhYRcSpWyMEuFrD0dpc+28AuJGWL1pcpF8+OXwNggAMbe+ODpXD6urL6FAvAMDeqGTO1zFQmirboe3d4aaQixxN3bX3VKCthy3KDGggGZO2TUxbDwUmhvkAAOb/dBmlFUpxAyIiIiIiIqIGcSb2XmsEDX8XdV/bGCZtCcDpWxk4fv0uTKQSvD2gVb3vv08rZzhZmyGjoAzHr9+t9/1Tw0rPL8H+ykFyU7r7ihzN45FIJBjf2bAGkjFp2wS92T8ATtbmiL1biA0nYhF+KxP7opIRfisTSpX+f9MSERERERHRw90bQnYvacthZKQhCAKW/XINgPrWcR8nq3o/hqlMipHB6sFVu88n1fv+qWFtPZOIcqWAUG97BNdzFbYYRgR7wNJMhti7hdp+3/rMROwAqPEpLEzx7yGtMXvnRaw4fEPnOXeFHAuGB2JQkLtI0REREREREdHjyv5HP1sNTXsEVtrSwb9TcfF2LqzMZPi/J/0b7DijO3niq5NxOHo1HZkFpdpWHaSflCoBEXFZSM4pwubK1ghTDbzKVsNGbooRwR74LiIJ288m6lzQ0kestG2iLGqY9peaW4JpWyNx6HJKI0dERERERERE9UVTRebvYg2n+5Jk/pWVtndyS5BXUi5KbCS+cqUKyw+rq2xf7tVCO6CuIbR2s0W7ZgpUqATsi7rTYMehx3focgp6LPsd4zecwZxdl5BXUgGpBACM567s5zt7AwAOXU5Flp4PJGPStglSqgQs+jm62uc0/w0X7o9mqwQiIiIiIiIDVV1rBEB956WbrXqYUAxbJDRZ30ckIj6zCE7WZnipZ4sGP97oUE8AwO7zhjEAqik6dDkF07ZGIiW3RGe7SgBmbL9gNMV97TwVaNdMgTKlSu9bdjBp2wRFxGVV+U94PwFASm4JIgygvwcRERERERFVVVPSFgD82SKhSSsorcBnR2MAADOf8oe1ecN3zny6gwfMZFJEp+Thyp3cBj8ePRqlSsDC/dEPrKc1puK+57uoB5J9F5Gk1wPJmLRtgtLza07Y1mUdERERERER6Y/7+9l2aeFQ5XkOI2vavjoRi4yCMvg4WmJc5+aNckx7KzP0C3QBwGpbfdTUivue7uABKzMZ4jIKEV55gUsfMWnbBLnYyOt1HREREREREemPiPjq+9lqaIeRpbPStqm5m1+KDX/GAgDeHtgaprLGSwuNCfUCAOyLuoOyClWjHZcerqkV91mZm2BESDMAwPaziSJHUzMmbZugzr4OcFfIIanheQkAd4VcZ8IoERERERERGQZNa4TqqmyBe8PIbrA9QpPz+e8xKCxTooOnAkPauTXqsXv6O8HZxhxZhWX4/Vp6ox6bHiyjoLRW64ypuO/5yirzQ5dTcOhyCvZFJSP8VqZetYBg0rYJkkklWDA8EACqTdwKABYMD4RMWlNal4iIiIiIiPTVmVh1pW11/WwBdQUuAKTllSK3uLzR4iJxxWcUaqsK3x3cGhJJ4/7NbyKTYlRldSNbJOiH3KJyvLv7Ehb/fPWB64yxuC+omQLejpaoUAGvbY3EzO+jMH7DGfRY9rveDF1j0raJGhTkjrUTOsJNUfUqiYlUgpYuNiJERURERERERI8jp6gM11LzAABdfKtP2trITeFR+bcgh5E1Hct/vY4KlYA+rZzRzc9JlBhGh3oCAI5dT8fd/NpVd1L9EwQB+y/ewVOr/sCOc0kAgF4BTpCganGf5mNjK+47dDkFCZlFVban5pZg2tZIvUjcMmnbhA0KcsfJd5/Edy93xWfjgvHdy13QJ8AJFSoB7/5wCSo9KgknIiIiIiKihzsblwVBAFq6WMPZpmo/Ww1/DiNrUi4m5eDApRRIJMC7g1qLFoe/qw06eNlBqRKwLypZtDiasuScYvxryzn833cXkFFQipYu1tj9Whi+mdql2uI+N4Ucayd0xKAgd5Eirn9KlYCF+6OrfU6TCVu4P1r0Vgkmoh6dRCeTShDmd+/qq7ejFfqv+gPnE7Lx7ZkETOrmI15wRERERERE9EjOalsjPPg25gBXa/xx4y772jYBgiDg41+uAQCeCWmGNu62osYzJtQTF5NysOvcbfyrh2+jt2loqpQqAVtOx2PFr9dRVKaEmUyK6X1b4rU+LWBuIgOgLu7rH+iGiLgspOeXwMVG3RLBmCpsASAiLgspuTUPVRMApOSWICIuSydn1thYaUs6POwsMHdIGwDAskPXcDu7aqk4ERERERER6SftELIaWiNocBhZ0/HHjbsIj82EmUyK2f0DxA4Hw9t7wMxEiutp+bicnCd2OE1C9J08jFpzCot+jkZRmRJP+Njj4MwemNnPX5uw1dAU940IboYwP0ejS9gCQHp+zQnbuqxrKEzaUhUvdG6Ozj4OKCpT4t8/XoYgsE0CERERERGRvsspKsNVTT/bh1TatmJ7hCZBqbpXZTupmzc87S1FjghQWJpiQKArAGD3+SSRozFuJeVKLDt0DcO/OImLt3NhIzfBkmfaYccrYU16lpGLTdX5To+zrqEwaUtVSKUSfPxsO5iZSPHnjbvYE8k+M0RERERERPouorKfrZ+z1UOTDS1drAEAGQWlyC4sa4zwSAT7opJxLTUfNnITvN6npdjhaI3p5AUA2HfxDkorlCJHY5xO3czAwNV/Yu3xW1CqBAxp54ajs3vj+S7NITXC6tlH0dnXAe4KeZWhaxoSAO4KdWsIMTFpS9Vq4WyNN/upb5tY9HM0pzoSERER3efLL7+Ej48P5HI5unTpgoiIiAeuz8nJwfTp0+Hu7g5zc3MEBATg4MGD2ud9fHwgkUiqPKZPn97Qb4WIjMgZbT/bh/dgtDI3gae9BQC2SDBWJeVKrPz1BgDg9T4tYW9lJnJE9/Ro6QQ3Wzlyispx9Gq62OEYlezCMry18yJe+OosEjKL4GYrx4aJnbDmhVC42IpbOaovZFIJFgwPBIAqiVvNxwuGB4reGoJJW6rRyz19EdTMFrnF5fjgpytih0NERESkF3bs2IHZs2djwYIFiIyMRIcOHTBw4ECkp1f/R2dZWRn69++P+Ph47N69G9evX8eGDRvQrFkz7Zq//voLKSkp2sdvv/0GABgzZkyjvCciMg5n49T9bGuTtAWAAE2LhHS2SDBGW88kIDmnGG62ckzp7iN2ODpkUglGdVT/Htx9/rbI0RgHQRCwLyoZ/Vb9gR8ib0MiASaFeeO32b3Qv7IdBd0zKMgdayd0hJtCN5HtppBj7YSOGBTkLlJk95iIHQDpLxOZFMuebY+nvziFA3+nYPjlVAwKchM7LCIiIiJRrVq1Ci+//DKmTJkCAFi3bh0OHDiAjRs3Yu7cuVXWb9y4EVlZWTh9+jRMTU0BqCtr7+fs7Kzz8ccffww/Pz/07t27Yd4EERmd3KJyRKfUrp+thr+rNX6/lo4YVtoandzicnxx7CYA4M3+/pCbyh7yisY3OtQTa47fwvHr6UjPK2EV6GNIyirCe3sv488bdwEAAa7WWDqqPUK97UWOTL8NCnJH/0A3RMRlIT2/BC426pYIYlfYarDSlh6orYcCr/VuAQB4f99l5BaVixwRERERkXjKyspw/vx59OvXT7tNKpWiX79+CA8Pr/Y1P/30E8LCwjB9+nS4uroiKCgIS5YsgVJZfQ+/srIybN26FVOnToVEUv0fDaWlpcjLy9N5EFHTFhGv7mfbohb9bDUCXDTDyJi0NTbr/riFnKJy+LtY49mOnmKHU60WztYI9baHSgB+vMBZOnVRoVRhw5+xGPDpn/jzxl2YmUgxZ0AAfv6/nkzY1pJMKkGYnyNGBDdDmJ+j3iRsASZtqRb+70l/tHC2wt38Uiw5eFXscIiIiIhEk5GRAaVSCVdX3dsMXV1dkZqaWu1rYmNjsXv3biiVShw8eBDvv/8+Vq5ciQ8//LDa9Xv37kVOTg4mT55cYxxLly6FQqHQPry8vOr8nojIOJyJfbTWCMC99ggxaWyPYExSc0uw8WQcAOCdQa1hItPf1M/oUHVCedf52xAEQeRoDMvl5FyMXHMKHx28iuJyJbr4OuDQzJ6Y8aQ/zEz092tOtcevIj2U3FSGZc+2BwDsOJeEUzczRI6IiIiIyHCoVCq4uLhg/fr1CA0NxdixY/Hee+9h3bp11a7/+uuvMXjwYHh4eNS4z3nz5iE3N1f7SEpKaqjwichA1CVp29LFGhIJkFlYhowCDp82Fp/+dgOlFSo84WOPfm1cxA7ngYa2d4fcVIqb6QW4eDtX7HD0ilIlIPxWJvZFJSP8ViaUKnVSu6isAksOXsWIL0/hcnIebOUmWPZsO3z/Sle0cLYWOWqqT+xpS7XyhI8DJoZ545vwBMzdcwmHZ/WCpRm/fYiIiKhpcXJygkwmQ1pams72tLQ0uLlV3/vf3d0dpqamkMnu9RNs06YNUlNTUVZWBjOze9O8ExIScOTIEezZs+eBcZibm8Pc3Pwx3gkRGZPc4nv9bLv61q6fLQBYmMngZW+JxKwi3EjLh5M1f64Yupi0fOw6r76QN3dw6xrb7OgLW7kpBrV1w96oO9h9PgnBXnZih6QXDl1OwcL90UjJLdFuc1fIMTrUE3ujkpGUVQwAGNbeHfOHB9a6JQoZFlbaUq29M6g1PBRyJGUVY+WvN8QOh4iIiKjRmZmZITQ0FEePHtVuU6lUOHr0KMLCwqp9Tffu3XHz5k2oVCrtths3bsDd3V0nYQsAmzZtgouLC4YOHdowb4CIjNJfcZX9bJ2sHnmYE1skGJdlh65DJQAD27oi1Lv2CXwxjQ5Vt/j5KeoOSsqr7/felBy6nIJpWyN1ErYAkJJbgs9/v4mkrGJ4KOTYOLkTvni+IxO2RoxJW6o1a3MTfDSqHQBg46k4RCZmixwRERERUeObPXs2NmzYgC1btuDq1auYNm0aCgsLMWXKFADAxIkTMW/ePO36adOmISsrCzNnzsSNGzdw4MABLFmyBNOnT9fZr0qlwqZNmzBp0iSYmPCOJiKqPU1rhC6P0BpBI8BVfTs1h5EZpvtvod98Kg5HrqZBKgHeHtha7NBqrZufIzwUcuSVVOC36LSHv8CIKVUCFu6PxoO6+1qZyXBoVi882dr1AavIGPBskB5J31YuGBXSDHsuJOPd3Zfw8xs9YG4ie/gLiYiIiIzE2LFjcffuXcyfPx+pqakIDg7GoUOHtMPJEhMTIZXeq43w8vLC4cOH8eabb6J9+/Zo1qwZZs6ciXfffVdnv0eOHEFiYiKmTp3aqO+HiAzfmThNP9tHr6xkpa3hqu4WekCdBG3pYji9TaVSCZ4N9cTnv9/ErvO3MbxDzT3djV1EXFaVr+c/FZYpceVOHsL8Hv0iDRkWJm3pkb0/LBB/3LiLmPQCrDl2C2/2DxA7JCIiIqJGNWPGDMyYMaPa544fP15lW1hYGM6cOfPAfQ4YMICTs4nokeUWl+PKncp+tnWotPXXVNqm50MQBL3vgUpqmlvoq/utcfJmJg5dTsGgIPdGj6uunu2oTtqejLmL1NwSuCma5i3/6fkPTtg+6joybGyPQI/M3soMC0e0BQCsOX4T11LzRI6IiIiIiIioaToXf6+fresj9rMFAD9na0glQE5ROe4WlDZAhFTfHnYLvQTAwv3RUKoM50Kgj5MVOvs4QCUAey7cFjsc0dS2Py372DYNTNpSnQxt547+ga4oVwp4d/clg/plQEREREREZCzu9bOt29ApuakM3o5WANgiwVA87BZ6AeqhVRFxWY0XVD0YHeoJANh97naTvfOks68D3BVy1FTvLgHgrpCjs69hDJmjx8OkLdWJRCLBhyODYCM3wcXbudh0Kk7skIiIiIiIiJqcM7HqxFxdWiNo+LtwGJkhMdZb6Ie0d4eFqQyxGYWITMwROxxRyKQSLBgeWO1zmkTuguGBkEnZxqQpYNKW6szVVo73hrQBAKz49ToSMgtFjoiIiIiIiKjpUPezzQUAdPGte9JWM4zsBittDYKx3kJvbW6Cwe3cAAC7zyeJHI14BgW5Y+2EjlUSs24KOdZO6GhQvYrp8TBpS49l7BNeCGvhiJJyFeb+8HeTvYWBiIiIiIiosZ2Lz4JKAHydrB5rcJN2GBkrbQ2CMd9Cr2mR8PPFFBSXKUWORjzBXvbaNpQfj2qH717uipPvPsmEbRPDpC09FolEgo+fbQe5qRThsZnY8VfTvRpGRERERETUmM7GaVojPF5yrpWbptI2n4U4BkBzC311XylDv4W+q68jPO0tkF9agV+jU8UORzQnb2YAADp4KjCuc3OE+Tka5NeTHg+TtvTYvB2tMGdAKwDARweuIvUBDdGJiIiIiIiofmiHkD1GawRAXakrk0qQX1KBtLzS+giNGtiTrV1hb2laZbuh30IvlUrwbEd1te2uc7dFjkY8J2PuAgB6+DuJHAmJiUlbqhdTuvuig5cd8ksr8J+9l3l1loiIiIiIqAHllZTjcnJlP9vHrLQ1N5HBx9ESAFskGIpd55OQXVQOZ2szbJn6BD4bF2w0t9BrWiScupWB5JxikaNpfIIg4ORN9QWZHi2dRY6GxMSkLdULmVSCT55tD1OZBEeupuHA3ylih0RERERERGS0NP1sfRwt4a6weOz93RtGxqStviutUOLL328CAKb3bYneAS4YEdzMaG6h93KwRNcWDhAEYM/5pldtez0tHxkFpbAwlaGjt53Y4ZCImLSletPKzQav92kJAFiw7wqyC8tEjoiIiIiIiMg4nYnV9LN9vNYIGv6VSduYtIJ62R81nF3nbuNObglcbMwxrnNzscNpEKNDvQAAuyNvN7k7eU/GqPvZdvZ1gLmJTORoSExM2lK9er2vHwJcrZFZWIbFP0eLHQ4REREREZFR0vSzra+kbYCrNQDgRjorbfVZaYUSa46pq2xf7+MHualxJvWGtHODlZkMCZlFOJeQLXY4jUozhKwn+9k2eUzaUr0yN5Fh2bPtIZEAey4k49j1dLFDIiIiIiIiMir59djPVkPTHuFmWkGTq2w0JDsrq2xdbY23yhYALM1MMKSdujfvrnNJIkfTeEorlDhbWUXfvSWTtk0dk7ZU70Ka22Nqd18AwHt7/kZBaYXIERERERERERmPc/HZUAmAdz31swUAH0crmEglyC+tQEpuSb3sk+qXbpVtS6OtstUY00ndIuHApRQUlTWNvEJkQg6Ky5VwsjZHazcbscMhkTFpSw3irQEB8HKwwJ3cEnxy6JrY4RARERERERkNbWsE3/ppjQAAZiZS+DpZAVAPQiL9s/PcbaTklsDNVo6xT3iJHU6De8LHHt6OligsU+KXv1PFDqdRnKpsjdCjpSMkEsMfKkePh0lbahCWZib4eFR7AMA34Qn4Kz5L5IiIiIiIiIiMgzZp61c/rRE0Atw0w8iYtNU3OlW2fY23l+39JBIJRnf0BADsPn9b5Ggax4nKpC1bIxDApC01oO4tnTC28naGd3+4hJJypcgRERERERERGbb8knL8relnW4+VtgAQ4KJO2t5IK6jX/dLj2/lXkrbK9rlOxl9lqzEq1BMSCRAem4mkrCKxw2lQuUXl+Pt2DgCgp7+zuMGQXmDSlhrUv4e2gYuNOWLvFuLz32PEDoeIiIiIiMignUtQ97Nt7mAJD7v66WerEeBqDYCVtvqmtEKJL4/dAtB0qmw1mtlZoJuf+uLED5HGXW0bHpsBlQC0dLGGm0IudjikB5i0pQalsDDF4pFBAIB1f8Tiyp1ckSMiIiIiIiIyXNrWCC3qtzUCAPi7VrZHSC+ASiXU+/6pbnb8lYTUvKbTy/afxoSq3/MPkbeN+vvyRIymny1bI5CaqEnbP//8E8OHD4eHhwckEgn27t370NccP34cHTt2hLm5OVq2bInNmzc3eJz0eAa2dcPQdu5QqgS8s/sSKpQqsUMiIiIiIiIySGdi1fNCurao39YIAODjaAkzmRRFZUok5xTX+/7p0ZWUK7Gmssp2el8/mJs0nSpbjYFt3WBjboKkrGKcjTPeeTknbzJpS7pETdoWFhaiQ4cO+PLLL2u1Pi4uDkOHDkXfvn0RFRWFWbNm4aWXXsLhw4cbOFJ6XB883RYKC1NcuZOHDSfixA6HiIiIiIjI4OSXlOOypp9tAyRtTWRStHC2AgDEpLNFgj7Yee5ele1zTbDKFgAszGQY1sEdgPEOJEvKKkJCZhFkUgm6+tX//20yTKImbQcPHowPP/wQzzzzTK3Wr1u3Dr6+vli5ciXatGmDGTNmYPTo0fj0008bOFJ6XM425pg/LBAA8OmRG4i9y8b2REREREREj+JcQjaUKgHNHSzRrJ772WpoWiRwGJn4WGV7z+hQTwDAL5dTUFhaIXI09U9TZRviZQdrcxORoyF9YVA9bcPDw9GvXz+dbQMHDkR4eLhIEdGjGNWxGXoFOKOsQoW5P/xt1L1oiIiIiIiI6tvZytYIXXzrv5+tRoCLehjZDQ4jE52ml627oulW2Wp0bG6PFk5WKCpT4r9HY7AvKhnhtzKhNJK8wklNP1t/tkagewwqfZ+amgpXV1edba6ursjLy0NxcTEsLKpeaSwtLUVpaan247y8vAaPk6onkUiw5JkgDPj0T0TEZ2FbRCJe7OotdlhEREREREQG4d4Qsoa7fVo7jIyVtqIqKVdizfGbAIDX+7Zs0lW2gDqf0M5TgdiMQvzvz1jtdneFHAuGB2JQkLuI0T0elUrAqVvqpG1PJm3pPgZVaVsXS5cuhUKh0D68vJr21Smxedpb4t1BrQEAHx+8yub2REREREREtVBQWoG/tf1sG7DS1lVdaRuTns+7I0W0468kpOWVwkMhx3OdPMUOR3SHLqdgX9SdKttTc0swbWskDl1OESGq+nHlTh5yisphbW6C9p52YodDesSgkrZubm5IS0vT2ZaWlgZbW9tqq2wBYN68ecjNzdU+kpKSGiNUeoAXu3qjk7c9CsuUeO/HvyEIPBEgIiIiIiJ6kHPxWVCqBHg5WMDT3rLBjuPtaAUzEylKylVIyi5qsONQzVhlq0upErBwf3S1z2myCQv3Rxtsq4QTN+8CUFfQm8oMKk1HDcygvhvCwsJw9OhRnW2//fYbwsLCanyNubk5bG1tdR4kLqlUgo+fbQ8zmRTHr9+t9moZERERERER3XOmsp9tV9+GnSwvk0rQ0lnT15YtEsTwfUSitsp2DKtsERGXhZTckhqfFwCk5JYgIi6r8YKqR6dusjUCVU/UpG1BQQGioqIQFRUFAIiLi0NUVBQSExMBqKtkJ06cqF3/2muvITY2Fu+88w6uXbuGNWvWYOfOnXjzzTfFCJ8eQ0sXa8zs5w8AWLj/CjIKSh/yCiIiIiIioqbrbJy6n22XBuxnq6FpkcBhZI1PXWV7CwCrbDXS82tO2NZlnT4pKVfir/hsAED3lkzaki5Rk7bnzp1DSEgIQkJCAACzZ89GSEgI5s+fDwBISUnRJnABwNfXFwcOHMBvv/2GDh06YOXKlfjqq68wcOBAUeKnx/NKrxZo426L7KLyGm91ICIiIiIiauoKSytw6XZlP1vfhutnq3FvGBmTto3tu4hEpOdretlyJg8AuNjI63WdPomIy0JZhQruCjn8nK3EDof0jImYB+/Tp88D+5lu3ry52tdcuHChAaOixmIqk2L56PYY8eUp7L94B0938ED/QFexwyIiIiIiItIr5xKyoVQJ8LS3gJdDw/Wz1QioTNqyPULjKilXYm1lle30J1vCzMSgOlo2mM6+DnBXyJGaW4LqMkgSAG4KOTo3wgWN+qZpjdCjpRMkEonI0ZC+4U8AElVQMwVe7tkCAPCfvX8jr6Rc5IiIiIiIiIj0y5lYdWuEro3QGgG41x7h1t0Cgx3uZIg0VbbN7CwwJpRVthoyqQQLhgcCUCdoq7NgeCBkUsNLep6IqUzasp8tVYNJWxLdrH7+8HWyQlpeKZYevCZ2OERERERERHqlsZO2XvaWkJtKUVqhQmJWUaMcs6m7v5ft9L6ssv2nQUHuWDuhI9wUui0Q5KZSrJ3QEYOC3EWKrO4yCkoRnZIHgP1sqXr8KUCik5vK8PGodgDUVxZP38oQOSIiIiIiIiL9UFhagb8bsZ8tAEilErR04TCyxrT9bCLuVlbZjg71FDscvTQoyB0n330S373cFW8PbAUAUKoEdDPQhOfpW+qLMW3cbeFkbS5yNKSPmLQlvdClhSMmdG0OAJi3528UlylFjoiIiIiIiEh85xOyUaES0MyucfrZagS4VPa1TWXStqGVlCux9g9W2daGTCpBmJ8jXu/jh5Yu1ihXCjh0OVXssOrkZMxdAECPlo1TQU+Ghz8JSG+8O6g13BVyJGQW4dMjN8QOh4iIiIiISHSN3RpBI8CtMmmbzmFkDY1Vto9OIpFgZLAHAGBfVLLI0Tw6QRBwUtvP1lnkaEhfMWlLesNGboqPngkCAHx1IhYXk3LEDYiIiIiIiEhk95K2jdMaQUMzjCyG7REa1P1VtjOeZJXtoxgR3AyAus1AWl6JyNE8mriMQtzJLYGZTIrOPo37f5sMB38akF55srUrRgZ7QCUA7/5wCWUVKrFDIiIiIiIiEkVRWQUuVfazbexKW//K9gixdwtRoeTfZQ1l231Vts92ZJXto/BysETH5nYQBGD/xTtih/NITt5UV9mGetvDwkwmcjSkr5i0Jb0zf3hbOFiZ4VpqPtYcv4nwW5nYF5WM8FuZUKoEscMjIiIiIiJqFGL1swWAZnYWsDSToUypQnxmUaMeu6koKVdiHatsH8vIEHW17b4ow0rantC2RjDMIWrUOPgTgfSOg5UZPni6LQBg9ZEYjN9wBjO/j8L4DWfQY9nvOHQ5ReQIiYiIiIiIGp6mNUKXRm6NAABSqQT+LmyR0JC2nknA3fxSeNqzyrauhrZzh0wqwd/Jubh11zD6L1coVThzS/1/uyeTtvQATNqSXjKVSqrdnppbgmlbI5m4JSIiIiIio3cmNgtA47dG0PB3rRxGlmYYyTBDUlymxLo/YgEAM/qyyrauHK3N0asy8bnvgmEMJLt4Oxf5pRVQWJiirYdC7HBIj/GnAukdpUrAop+jq31O0xxh4f5otkogIiIiIiKjVVRWoR3OHCZS0lYzjOxGOitt69u2swnIKKissg1lle3j0LRI2Bt1B4Kg/3mCk5WtEbq3dISshoI1IoBJW9JDEXFZSMmtefKjACAltwQRcVmNFxQREREREVEjur+frae9hSgxaCptDa09glIl6PVslPurbP/vyZYwlTE18zj6B7rC0kyGxKwiXKi80KHPTlUOIevR0lnkSEjfmYgdANE/pefXnLCtyzoiIiIiIiJDc7ayNUIXXwdIJOJU4wVUJm1j7xairEJlELfwH7qcgoX7o3UKgdwVciwYHohBQe4iRnbP/VW2o9jL9rFZmplgQKAr9kbdwb4LyejY3F7skGpUUFqByMRsAECPluxnSw+m/z9xqclxsZHX6zoiIiIiIiJDoxlCJlY/WwDwUMhhbW6CCpWA+MxC0eKorUOXUzBta2SVOzf1aTaKusr2FgBW2danEZUtEn6+lIJypUrkaGp2NjYTFSoBzR0s0dzRUuxwSM/xpwPpnc6+DnBXyFHTtWQJ1FdKO/s2/gRVIiIiIiKihlZUVoGLt3MAiJu0lUgk8Nf0tdXzFglKlYCF+6NRXSMEfZqNsvVMAjIKyuDlwCrb+tSzpRMcrcyQWViGk5XtB/SRJrYe/qyypYdj0pb0jkwqwYLhgQBQY+J2wfBANuwmIiIiIiKjFJmQg3KlAA+FHF4O4vSz1QhwUbdIuJFWIGocD2MIs1GKyirwvz8rq2z7+rPKth6ZyKQY1l7d/mLfhWSRo6mZZggZWyNQbfAnBOmlQUHuWDuhI9wUui0QJABWjwvWm15ERERE1PR8+eWX8PHxgVwuR5cuXRAREfHA9Tk5OZg+fTrc3d1hbm6OgIAAHDx4UGdNcnIyJkyYAEdHR1hYWKBdu3Y4d+5cQ74NItJj97dGEKufrYam0lbfh5EZwmyUbWcSkVFQhuYOlnimYzPR4jBWmhYJv0anoaisQuRoqkrNLUFMegEkEqCbn3gV9GQ4OIiM9NagIHf0D3RDRFwW0vNK8OHBaNzNLxM7LCIiImrCduzYgdmzZ2PdunXo0qULVq9ejYEDB+L69etwcXGpsr6srAz9+/eHi4sLdu/ejWbNmiEhIQF2dnbaNdnZ2ejevTv69u2LX375Bc7OzoiJiYG9vf4OUiGihqVJ2nZpIX5LOM0wMn1vj1DbmScXk3IwtJ07TBq5yvX+KtsZ7GXbIEK87ODtaImEzCL8Fp2GEcH6lRg/VdkaoX0zBewszUSOhgwBf0qQXpNJJQjzc8SIkGZ4vrM3AOCHSP291YGIiIiM26pVq/Dyyy9jypQpCAwMxLp162BpaYmNGzdWu37jxo3IysrC3r170b17d/j4+KB3797o0KGDds2yZcvg5eWFTZs2oXPnzvD19cWAAQPg5+fXWG+LiPRIcZlSL/rZamiStvGZRSitUIocTc0eNhtFY+OpeAz67ASOXUuHIDRef1tNL9vmDpZ4JkS/konGQiKRYEQHDwDAXj1skaDpZ9udrRGolpi0JYMxqvL2kZMxd5GWJ94tLURERNQ0lZWV4fz58+jXr592m1QqRb9+/RAeHl7ta3766SeEhYVh+vTpcHV1RVBQEJYsWQKlUqmzplOnThgzZgxcXFwQEhKCDRs2NPj7ISL9FJmYjXKlAHeFHM0dxJ8u72prDhu5CZQqAXEZhWKHUyPNbJTq0rCSyse4zl5wsDLDzfQCTNn8FyZujMC11LwGj62orAL/+yMWAKtsG5qmRcKfMRnILCgVOZp7BEHgEDJ6ZPxJQQbD29EKnbztoRKAfVH6d9WMiIiIjFtGRgaUSiVcXV11tru6uiI1NbXa18TGxmL37t1QKpU4ePAg3n//faxcuRIffvihzpq1a9fC398fhw8fxrRp0/DGG29gy5YtNcZSWlqKvLw8nQcRGQd96mcLqKsX77VI0O9hZIOC3NHJu2prGTeFHGsndMTHo9rj+Nt98GrvFjCTSXEiJgNDPjuBeXsuNWiv261nEpBZyCrbxuDnbI12zRRQqgQc+DtF7HC0bqQV4G5+KeSmUoRW8z1KVB0mbcmgjOroCQD44Xxyo97KQkRERFQXKpUKLi4uWL9+PUJDQzF27Fi89957WLdunc6ajh07YsmSJQgJCcErr7yCl19+WWfNPy1duhQKhUL78PLyaoy3Q0SN4F7SVvx+thoBBjKMrLRCiWup6hgXjWiLz8YF47uXu+Lku09qh1nbyk0xb3AbHJndG0PbuUMlAN9FJKHv8uP48thNlJTXbwuI+6ts/49Vto1iRLD+tUg4EXMXANDZ1xHmJjKRoyFDwZ8WZFCGtnOHmYkU19PyEZ3CihIiIiJqPE5OTpDJZEhLS9PZnpaWBjc3t2pf4+7ujoCAAMhk9/5Aa9OmDVJTU1FWVqZdExgYqPO6Nm3aIDExscZY5s2bh9zcXO0jKSmprm+LiPRIcZkSUUk5AIAuvuL3s9XQVNpeT9XvpO3pW5koKK2Ai405JnTxxojgZgjzc4RMWrViubmjJb58oSN2vxaGDp4KFJYpsfzwdTy18g/si6q/IqFvw9VVtt6OrLJtLE938IBUAkQm5iAxs0jscADc62fbk/1s6REwaUsGRWFpiv5t1Lck7uFAMiIiImpEZmZmCA0NxdGjR7XbVCoVjh49irCwsGpf0717d9y8eRMqlUq77caNG3B3d4eZmZl2zfXr13Ved+PGDXh7e9cYi7m5OWxtbXUeRGT4LlT2s3WzlcPbUfx+thqapG1Mun63Rzh8Wd2qZkBbV0irSdRWp5OPA358vTs+GxcMD4UcyTnFmPl9FJ5ZcxrnE7IfK56isgr878/KXrZ9W8KEVbaNwsVWjm5+6uSoPrRWLKtQ4WxsFgD2s6VHw58YZHA0A8n2RSWjQql6yGoiIiKi+jN79mxs2LABW7ZswdWrVzFt2jQUFhZiypQpAICJEydi3rx52vXTpk1DVlYWZs6ciRs3buDAgQNYsmQJpk+frl3z5ptv4syZM1iyZAlu3ryJ7du3Y/369TpriKhpuL81gj70s9Xwr2yPkJBZWO/tA+qLUiXgt2j1nRCD2ro/0mulUglGBDfD0bf6YM6AAFiayRCVlINn157GjO2RSMqqW7XmN+EJyGKVrSi0LRLqsWq6riITs1FcroSTtRlaVV4AIaoNJm3J4PQKcIajlRkyCspwIiZD7HCIiIioCRk7dixWrFiB+fPnIzg4GFFRUTh06JB2OFliYiJSUu4NPvHy8sLhw4fx119/oX379njjjTcwc+ZMzJ07V7vmiSeewI8//ojvvvsOQUFBWLx4MVavXo0XXnih0d8fEYnrTGU1XtcW+tMaAQCcrc1hZ2kKlQDcuquf1bbn4rOQWVgGhYUputSxH7CFmQwznvTH8bf7YNwTXpBIgJ8vpeCpVX/g41+uIa+kvNb7KiytwPo/Nb1s/Vll28gGBbnB3ESKW3cLceWOuK0VT1bmLbq3dKp1BTgRAJiIHQDRozKVSfF0sAc2nYrHD5G30be1i9ghERERURMyY8YMzJgxo9rnjh8/XmVbWFgYzpw588B9Dhs2DMOGDauP8IjIQN3fz1bfkrYSiQQBLjaIiM9CTFoB2nooxA6pisNX1FW2T7VxeexhXy42cnz8bHtMDPPBRwejcepmJtb9cQu7ziXhzf4BGPeE10OTsN+eUVfZ+jhaYmRl1Sc1Hhu5Kfq1ccWBv1Ow90IygpqJ9z2r6Wfbg/1s6RHxUg8ZpGc7egIAfo1OQ25x7a92EhERERER6aMLidkoU6rgamuuV/1sNTQtEm6k6d8wMkEQcPiKup/twLbVD4asi0APW2z9Vxd8PakTWjhbIbOwDP/ZexlD/nsCf9y4W2W9UiUg/FYmdp5LwpfHbgJgla2YNC0Sfrp4B0qVOC0ScovKcel2DgD2s6VHx0pbMkhtPWwR4GqNG2kF+OXvFIzr3FzskIiIiIiIiOrsTNy91gj61M9WQzOM7Eaa/rVHuHInD8k5xZCbStHL37le9y2RSPBUG1f0CnDG9rOJ+PTIDdxIK8CkjRHoHeCM94a2QYCrDQ5dTsHC/dFIyS3RvlYmlUBuyoStWPq0coHCwhTp+aU4E5uJ7iJUuobHZkAlAH7OVnBXWDT68cmw8acHGSSJRIJRldW2eyLFnwZJRERERET0OO4NIdOv1ggamkrbmHT9q7TVVNn2CXCBhZmsQY5hKpNiUjcf/DGnL17q4QtTmQR/3LiLQav/xItfn8VrWyN1EraAuvJ2xvYLOHQ5pYa9UkMyM5FiSDv1ULq9F8TJG2haI/Ss54sJ1DQwaUsGa2RwM0gkQER8FhIz6zbNk4iIiIiISGwl5UpEJeYA0N+krabSNjGrCMVlSpGj0XXocmVrhCDXBj+WwtIU/xkWiN/e7I1Bbd2gEvDQAdkL90eLdnt+U6fpJ3zocipKyhv/+1YzhIz9bKkumLQlg+WmkGt/8P0o0lUzIiIiIiKixxV5Xz9bHz3sZwsATtbmcLAygyAAN9P1p0VC7N0CxKQXwEQqwZOtGj5pq+HjZIV1L4Zi/rA2D1wnAEjJLUFEZfsLalxP+DjAQyFHfmkFfr+W3qjHTsoqQnxmEWRSCbq0cGjUY5NxYNKWDNqojs0AAHsu3IYg8MolEREREREZnjOx6oReF1/97GerEaCHw8gOX0kDAIT5OUJhadrox3e0Nq/VuvT8kocvononlUrwdLA6b9DYLRJOVbZGCPGyg4288b83yfAxaUsGbWBbN1iZyZCQWYTIxGyxwyEiIiIiInpkZ/W8n62GdhiZHvW1PVTZz3ZgWzdRju9iI6/XdVT/RoaoWyQcv34XuUXljXbcE5VJ2x7+bI1AdcOkLRk0SzMTDK5sLP4DB5IREREREZGBKSlX4kJSDgCgq57fQu1fmbSNSdOP9ggpucW4mJQDiQQYENh4rRHu19nXAe4KOWqqj5YAcFfI0dlXv7+2xqy1my1au9mgTKnCwUYaCqdSCTh9k/1s6fEwaUsGT9Mi4eeLd0RpLE5ERERERFRXFxJzUFahgouNOXydrMQO54ECXPSrPcKvla0ROja3h4utOJWsMqkEC4YHAkCVxK3m4wXDAyGT6m/bi6ZgRCO3SIhOyUN2UTmszU3QwcuuUY5JxodJWzJ4XX0d4aGQI6+k8RuLExERERERPY4z97VG0Od+tsC99gi3s4tRWFohcjTAYW1rBHGqbDUGBblj7YSOcFPoJo7dFHKsndARg4LcRYqMNJ4OVrdIOBuXhTs5xQ1+vBMx6irbri0cYSpj6o3qxkTsAIgel1QqwTMdm+HLY7ewJ/I2hrTjL0QiIiKqKicnB19//TWuXr0KAGjbti2mTp0KhUIhcmRE1JRpkraGMF3e3soMTtbmyCgoxc30AlErCLMLy3A2Tj3ATax+tvcbFOSO/oFuiIjLQnp+CVxs1C0RWGGrH5rZWaCzjwMi4rPw08U7eK23X4Me7+TNuwCAHi31u0816Tem+8koPBPiCUDdWDyjoFTkaIiIiEjfnDt3Dn5+fvj000+RlZWFrKwsrFq1Cn5+foiMjBQ7PCJqonT72RpGcifAVT9aJBy5mgalSkBrNxt4O+pHWwmZVIIwP0eMCG6GMD9HJmz1zIjKgWQN3SKhpFyJv+LVg9J7+Ds36LHIuDFpS0ahpYs1OnjZoUIl4KeoO2KHQ0RERHrmzTffxNNPP434+Hjs2bMHe/bsQVxcHIYNG4ZZs2aJHR4RNVFRSep+ts425mih5/1sNTQtEmLSxR1Gdriyn+2gIPGrbMkwDG3nDlOZBNdS83E9teEuOvwVn4WyChXcFXL4ORvG/2vST0zaktF4tnIg2Z4Lt0WOhIiIiPTNuXPn8O6778LE5F53MBMTE7zzzjs4d+6ciJERVU+pEhB+KxP7opIRfisTSpUgdkjUAAypn62Gvx5U2haWVuDPGPXt5/rQGoEMg52lGXoHuAAA9kY1XLXtycp+tt1bOhnM/2vST0zaktEY1t4DpjIJLifnNehVMyIiIjI8tra2SExMrLI9KSkJNjY2IkREVLNDl1PQY9nvGL/hDGZ+H4XxG86gx7LfcehyitihUT27l7TV/362Gq0qK21viPg31x837qKsQoXmDpZo7caf4VR7IytbJPwUdQeqBroYdvKmOmnb09+pQfZPTQeTtmQ0HKzM0LeV+qoZq22JiIjofmPHjsW//vUv7NixA0lJSUhKSsL333+Pl156CePHjxc7PCKtQ5dTMG1rJFJyS3S2p+aWYNrWSCZujUhJuRKRiTkAgC6+htHPFgD8K5O2d3JLkF9SLkoMh6+kAlC3RmAlIz2Kfm1cYW1uguScYpxLyK73/WcWlOLKnTwAQDc/Jm3p8TBpS0ZlVEf1QLK9F5J5CxkRERFprVixAqNGjcLEiRPh4+MDHx8fTJ48GaNHj8ayZcvEDo8IgLolwsL90ajuLFazbeH+aJ7nGomLlf1snazNDarvpcLCFK625gDE6WtbVqHC71fTAQAD27o2+vHJsMlNZdqWGvsaoEXCqVvq6vnWbjZwtjGv9/1T08KkLRmVvq2dYWdpirS8Upy+lSF2OERERKQHlEolzpw5gw8++ADZ2dmIiopCVFQUsrKy8Omnn8LcnH9UkX6IiMuqUmF7PwFASm4JIuKyGi8oajBnYtVfx64tHAyuWlQ7jEyEvranb2Ugv7QCzjbmCPGyb/Tjk+HTtEg48HcKyipU9brvk5W9ltkageoDk7ZkVMxNZBjeXv0DeE9kwzUWJyIiIsMhk8kwYMAA5OTkwNLSEu3atUO7du1gaWkpdmhEOtLza07Y1mUd6bf7h5AZGn+Xyr62aY1faatpjTAg0BVSqWElu0k/dPNzgrONOXKKyvHnjbv1tl9BELRDyHr4O9fbfqnpYtKWjM6ojs0AAIcup6KgtELkaIiIiEgfBAUFITY2VuwwiB7IxUZer+tIf6n72ar7aRpi0jbA1RoAcKORK22VKgG/RacBgPYWd6JHJZNKtMVee+uxRUJcRiHu5JbATCZFZx/DGS5I+otJWzI6wV52aOFkheJyJQ5dThU7HCIiItIDH374IebMmYOff/4ZKSkpyMvL03kQ6YPOvg5wV8jxoNpBR2szdPZlMsCQKVUCtp9NQGmFCrZyU/g4Gl7Vv7+2PULjVtpGJmYjo6AMtnITg0x2k/7QtEg4cjWt3oq9Tt5UV9mGetvDwkxWL/ukpo1JWzI6EolEW227J/K2yNEQERGRPhgyZAguXryIp59+Gp6enrC3t4e9vT3s7Oxgb8+eiKQfZFIJFgwPfOCa/JIKnK28rZ4Mz6HLKeix7Hcs+vkqACCvpBw9PzmGQ5dTRI7s0fhXVtqm5pUgt7i80Y6rKcp5qo0rzEyYzqC6a9dMgRZOVigpV+FwPRV73WuNwH62VD/4U46M0sgQddI2PDYTyTnFIkdDREREYjt27Jj28fvvv2sfmo+J9MWgIHe8N6xNle1utnK0cbdBWYUKUzb/hWPX0kWIjh7HocspmLY1ssqwudTcEkzbGmlQiVtbuSncFeo2HTfTG6dFgiAI2n62A9u6NsoxyXhJJBKMCFbnDeqjRUKFUoXwW+oLaj1aMmlL9cNE7ACIGoKnvSW6tnDAmdgs7L2QjOl9W4odEhEREYmod+/eYodAVGtOVuYA1H1Dp/dtCRcbOTr7OqBcqcKM7ZE4cjUdr3x7Dv8dF4LB7dxFjpZqQ6kSsHB/NIRqnhMASAAs3B+N/oFukBnIcK0AVxuk5JbgemoBQr0bvmXHlTt5uJ1dDLmpFL0COOSJHt+IYA98euQGTt3MQHp+yWP1C794Oxf5pRVQWJgiqJmiHqOkpoyVtmS0RnX0BKBukSAI1Z0eERERUVOxadMm7Nq1q8r2Xbt2YcuWLSJERFSza6nqysUnfBwwIrgZwvwcIZNKIDeVYe2EUAxr745ypYAZ313A3gv1N0SHGk5EXFaVCtv7CQBScksQEZfVeEE9psYeRvZrZZVtL39nWJqx/owen4+TFYK97KASgJ8vPl6l+6nKfrbdWzoazIUX0n9M2pLRGhzkBrmpFLfuFuLS7VyxwyEiIiIRLV26FE5OVW9XdHFxwZIlS0SIiKhm11PVw/Fau9lUec5UJsVn40IwOtQTSpWAN3dG4buIxMYOkR5Ren7NCdu6rNMH2mFkjdQe4fCVNADAoCC3RjkeNQ0jg9UDyfY9ZosETT/b7myNQPWISVsyWjZyUwxsq/6FzoFkRERETVtiYiJ8fX2rbPf29kZiIhNepF+uV1batnKzrfZ5mVSCT55tjxe7ekMQgHl7/sbGk3GNGSI9otredv04t2c3toDKpO2NtIIGP1ZcRiGup+XDRCrBU63Zz5bqz7AOHpBJJbh4OxdxGYV12kdBaQUiE7MBAD1bsnUH1R8mbcmoPVvZIuGni3dQVqESORoiIiISi4uLCy5dulRl+8WLF+Ho6ChCRETVyy0ux53K2+hbuVattNWQSiVYNKItXu3VAgCw6OdofHnsZqPESI+us68D3BVy1HTTtASAu0Ldu9hQ+Luo2yPczS9FTlFZgx5LM4CsawtHKCxNG/RY1LQ4WZtrB4fVtd1MRFwmKlQCmjtYormjZX2GR01cnZK2SUlJuH37XuViREQEZs2ahfXr19dbYET1oXtLJ7jYmCO7qBzHr3PCLhERUVM1fvx4vPHGGzh27BiUSiWUSiV+//13zJw5E+PGjRM7PCItTX9Qd4X8ockpiUSCuYNbY1Y/fwDA8sPXsfzwNc5z0EMyqQQLhgdWO4hMk8hdMDzQoHphWpmboJmdBYCGr7Y9dFmdtB3I1gjUAEaG3GuRUJefnyfYGoEaSJ2Sts8//zyOHTsGAEhNTUX//v0RERGB9957D4sWLarXAIkeh0wqwTMhzQAAeyI5pIGIiKipWrx4Mbp06YKnnnoKFhYWsLCwwIABA/Dkk0+ypy3plWva1gg1V9neTyKRYFa/APx7SGsAwJfHbmHRz9FM3OqhQUHueKVn1TYtbgo51k7oiEFB7iJE9XgaYxhZam4JopJyAAADAtkagerfgEA3WJjKEJ9ZhIt1mIej6Wfb059JW6pfdUraXr58GZ07dwYA7Ny5E0FBQTh9+jS2bduGzZs312d8RI9tVGWLhKPX0hr8th0iIiLST2ZmZtixYweuXbuGbdu2Yc+ePbh16xY2btwIMzMzscMj0tIMIatt0lbjlV5+WDyiLQBg06l4/PvHv6FUMXGrbyQSdSXtU21c8Nm4YHz3clecfPdJg0zYAvf62sY0YNL2t2h1lW3H5nZwtTWcnr9kOKzMTdC/8oLAo7ZISMsrQUx6ASQSoJsf2y1R/apT0ra8vBzm5uYAgCNHjuDpp58GALRu3RopKSn1Fx1RPWjlZoO2HrYoVwrYf4nfn0RERE1ZQEAAxowZg2HDhsHb21vscIiq0Awha/2ISVsAeDHMB8tHt4dUAnwXkYS3dkahQsm5DvrkQmIOAGBgWzeMCG6GMD9Hg2qJ8E/+jTCM7FBlP1vNkGmihqBpkfDzpTuP9HNTU2XbrpkCdpa8CEz1y6QuL2rbti3WrVuHoUOH4rfffsPixYsBAHfu3OEgB9JLozp64sqdaOyJvI0Xu/IPNCIioqbo9u3b+Omnn5CYmIiyMt27b1atWiVSVET3CIJwrz2Cq22d9jGmkxfkpjK8uSMKe6PuoKRchf+OD4GZCWdQi61CqcLfyepbr0O87MQNpp600iZtG6bSNqeoDGdiswAwaUsNq6e/MxyszJBRUIZTtzLRO8C5Vq87eVOdtO3BfrbUAOr0m3vZsmX43//+hz59+mD8+PHo0KEDAOCnn37Stk0g0idPd/CATCrBhcQc3LrbsE3yiYiISP8cPXoUrVq1wtq1a7Fy5UocO3YMmzZtwsaNGxEVFSV2eEQAgJTcEuSXVMBEKoGfi1Wd9zO8gwfWTgiFmUyKQ1dS8cq351BSrqzHSKkurqflo7hcCRtzE/g5W4sdTr1o6WINiQTILCxDZkFpve//yNV0KFUCWrvZwMep7v8niB7GVCbF0HbqNiX7atkiQRCEe0lb9rOlBlCnpG2fPn2QkZGBjIwMbNy4Ubv9lVdewbp16+otOKL64mxjrr1S9iMHkhERETU58+bNw5w5c/D3339DLpfjhx9+QFJSEnr37o0xY8aIHR4RgHutEVo4W8HcRPZY++of6IqvJ3eC3FSK49fvYsqmv1BYWlEfYVIdaVojdPCyg9SAWyLcz8JMBi97SwAN0yLhcGVrhAGssqVGoGmRcPhKKorLHn6h60ZaAe7ml0JuKkWot31Dh0dNUJ2StsXFxSgtLYW9vfqbMiEhAatXr8b169fh4uJSrwES1ZdRHZsBAH68kAwVhzIQERE1KVevXsXEiRMBACYmJiguLoa1tTUWLVqEZcuWiRwdkZq2NYJb3Voj/FNPf2d8M7ULrM1NEB6biRe/Povc4vJ62Tc9uqikHABAsJG0RtAIcFVXDcek12+LhKKyCvx54y4AYBCTttQIOja3h5eDBQrLlPjtatpD15+IUX9/dvZ1fOwLbUTVqVPSdsSIEfjmm28AADk5OejSpQtWrlyJkSNHYu3atfUaIFF96dfGFTZyEyTnFONsXJbY4RAREVEjsrKy0vaxdXd3x61bt7TPZWRkiBUWkY7rqXkA6jaErCadfR2w9aUusJWbIDIxBy98dQZZhWUPfyHVO2NN2vo3UF/bP67fRWmFCl4OFmjjXn//J4hqIpFIMKKDutirNi0STlW2RujJfrbUQOqUtI2MjETPnj0BALt374arqysSEhLwzTff4L///W+9BkhUX+SmMgxrr+5RsyfytsjREBERUWPq2rUrTp48CQAYMmQI3nrrLXz00UeYOnUqunbtKnJ0RGr3hpDVb4Iq2MsO378SBkcrM1xOzsO49eFIzy+p12PQg+UWl+Nmurp9QHBzO3GDqWeaStv6bo+gaY0wMNANEolxtJMg/adpkfDHjbsPvMBVVqHSFoN1Z9KWGkidkrZFRUWwsVGfSPz6668YNWoUpFIpunbtioSEhEfa15dffgkfHx/I5XJ06dIFERERD1y/evVqtGrVChYWFvDy8sKbb76JkhKecFDtjOroCQA4+HdKrXrUEBERkXFYtWoVunTpAgBYuHAhnnrqKezYsQM+Pj74+uuvRY6OCChXqrQDc1vVY6WtRqCHLXa82hWutua4kVaAsf87gzs5xfV+HKrepds5AAAvBws4WZuLG0w983dRf7/GpOVDEOqnDV1ZhQpHr6UDAAYFsTUCNZ6WLjZo62GLCpWAA3+n1LguMjEbRWVKOFmb1evdEUT3q1PStmXLlti7dy+SkpJw+PBhDBgwAACQnp4OW9va91/asWMHZs+ejQULFiAyMhIdOnTAwIEDkZ6eXu367du3Y+7cuViwYAGuXr2Kr7/+Gjt27MC///3vurwNaoI6ed/rUfNrdKrY4RAREVEjadGiBdq3bw9A3Sph3bp1uHTpEn744Qd4e3uLHB0REHu3EOVKAdbmJvC0t2iQY7R0scHOV8PQzM4CcRmFGLMuHAmZhQ1yLNIVVTmELNjL+IYVtXSxhlQCZBeVI6OgflpvhMdmIr+kAk7W5ujY3Pg+Z6TfRgY/vEWCpjVC95ZORjNYkPRPnZK28+fPx5w5c+Dj44POnTsjLCwMgLrqNiQkpNb7WbVqFV5++WVMmTIFgYGBWLduHSwtLbFx48Zq158+fRrdu3fH888/Dx8fHwwYMADjx49/aHUukYZEIsGoEHW17Q+RD+9RQ0RERManoKAAeXl5Og8isV2r7Gcb4GrdoLeCeztaYddrYfB1skJyTjHGrAvHzXoeIEVVGWs/W0Ddhq65gyUAdbVtfdC0Rugf6MqEGDW64R08IJEA5xKykZRVVO2aEzH3krZEDaVOSdvRo0cjMTER586dw+HDh7Xbn3rqKXz66ae12kdZWRnOnz+Pfv363QtGKkW/fv0QHh5e7Wu6deuG8+fPa5O0sbGxOHjwIIYMGVKXt0FN1KiO6qtmJ2PuIi2PrTWIiIiagri4OAwdOhRWVlZQKBSwt7eHvb097OzsYG/PKi4S33VNP1u32t+5WFcedhbY8WpXtHK1QXp+Kcb+7wyi7+RBqRIQfisT+6KSEX4rE0pV/dzq3tQJgqBN2oYYWT9bjfocRqZUCfj1ShoAtkYgcbgp5Ojq6wgA+OninSrP5xaVa1ue9PRn0pYajkldX+jm5gY3Nzfcvq0e6OTp6YnOnTvX+vUZGRlQKpVwdXXV2e7q6opr165V+5rnn38eGRkZ6NGjBwRBQEVFBV577bUHtkcoLS1FaWmp9mNWUpC3oxU6edvjXEI29kUl45VefmKHRERERA1swoQJEAQBGzduhKurK4fakN7RJG0bqzeii40c37/SFRM3RuDv5Fw8u/YULM1MkHnf4B13hRwLhgdiUJB7o8RkrJKyipFZWAZTmQSB7g2flBdDK1cb/Badhuv1MIzsQmI2MgpKYSM3QVgLx3qIjujRjQzxQHhsJvZeSMbrffx0zhvCYzOhEgA/Zyu4KxqmnQ0RUMdKW5VKhUWLFkGhUMDb2xve3t6ws7PD4sWLoVKp6jtGrePHj2PJkiVYs2YNIiMjsWfPHhw4cACLFy+u8TVLly6FQqHQPry8vBosPjIcmoFkP5xPrrdm+URERKS/Ll68iE2bNmHs2LHo06cPevfurfMgEts1baVt4w20sbcyw7aXu6CFsxWKy1U6CVsASM0twbStkTh0ueZhPPRwF5KyAQCB7raQm8pEjqZh+LtaA6if9gia1ghPtXaBmUmdUhZEj21QkDvMZFLEpBcgOkW3+O/kzbsAgB5sjUANrE4/Ad977z188cUX+Pjjj3HhwgVcuHABS5Ysweeff47333+/VvtwcnKCTCZDWlqazva0tDS4uVV/C8T777+PF198ES+99BLatWuHZ555BkuWLMHSpUtrTBbPmzcPubm52kdSUtKjvVkySkPbucPMRIrraflVfgATERGR8XniiSd4Hkh6K7+kHMk5xQAar9JWw8rMBIWlFdU+pyltWLg/mq0SHoMx97PVCLivPcLjFMUIgoBDlUnbgW3ZGoHEo7AwxZOtXQAA+6J0WyScrOxn28PfudHjoqalTknbLVu24KuvvsK0adPQvn17tG/fHq+//jo2bNiAzZs312ofZmZmCA0NxdGjR7XbVCoVjh49qh1s9k9FRUWQSnVDlsnUVypr+sVgbm4OW1tbnQeRwtIU/duoW3Ps4UAyIiIio/fVV19h2bJl2LJlC86fP49Lly7pPB7Vl19+CR8fH8jlcnTp0uWhg3FzcnIwffp0uLu7w9zcHAEBATh48KD2+Q8++AASiUTn0bp160eOiwyTpg+oq6057CzNGvXYEXFZSMsrrfF5AUBKbgki4rIaLygjcyExBwAQ0tx4+2e3cLaCTCpBXkkF0vNr/n56mKsp+UjKKoa5iRS9WzEhRuIaGeIBAPgp6o72wlVSVhHiM4sgk0rQtYWDmOFRE1CnnrZZWVnVnkS2bt0aWVm1/2U+e/ZsTJo0CZ06dULnzp2xevVqFBYWYsqUKQCAiRMnolmzZli6dCkAYPjw4Vi1ahVCQkLQpUsX3Lx5E++//z6GDx+uTd4S1daojs1w4O8U7ItKxrzBrWEi4603RERExuru3bu4deuW9jwTACQSCQRBgEQigVKprPW+duzYgdmzZ2PdunXo0qULVq9ejYEDB+L69etwcXGpsr6srAz9+/eHi4sLdu/ejWbNmiEhIQF2dnY669q2bYsjR45oPzYxqfP4CTIw1xpxCNk/pefXbjBvbdeRrtIKJaLvqO/sM+ZKW3MTGbwdLRF7txA30vLhaiuv0340Vba9ApxhacafgSSuPq1cYCM3QWpeCc7GZaKbnxNO3VRX2QZ72cFGbipyhGTs6vRTsEOHDvjiiy/w3//+V2f7F198gfbt29d6P2PHjsXdu3cxf/58pKamIjg4GIcOHdIOJ0tMTNSprP3Pf/4DiUSC//znP0hOToazszOGDx+Ojz76qC5vg5q4XgHOcLQyQ0ZBGU7EZKBv66p/ZBEREZFxmDp1KkJCQvDdd9899iCyVatW4eWXX9YmgNetW4cDBw5g48aNmDt3bpX1GzduRFZWFk6fPg1TU/UfeD4+PlXWmZiY1NgmjIybZghZm0ZujQCoB5LV5zrSdTUlH2VKFewtTeHtaCl2OA0qwMWmMmlbgJ51vG38V7ZGID0iN5VhSJA7dpxLwr4Ld9DNzwknKpO27GdLjaFOSdtPPvkEQ4cOxZEjR7StDMLDw5GUlKRzm1dtzJgxAzNmzKj2uePHj+sGa2KCBQsWYMGCBXUJm0iHqUyKEcHNsPFUHH6IvM2kLRERkRFLSEjATz/9hJYtWz7WfsrKynD+/HnMmzdPu00qlaJfv34IDw+v9jU//fQTwsLCMH36dOzbtw/Ozs54/vnn8e677+rcLRYTEwMPDw/I5XKEhYVh6dKlaN68+WPFS4ZBjCFkGp19HeCukCM1twTVNZyTAHBTyNHZl7cB10VUonoIWQcvu8e6WGQIAlytcehK3YeRxWcU4lpqPmRSCfq14d9mpB9GhHhgx7kkHLycgoUj2uJ0ZdK2pz+TttTw6nQ/eO/evXHjxg0888wzyMnJQU5ODkaNGoUrV67g22+/re8YiRrMqI7NAAC/Rqcht7hc5GiIiIiooTz55JO4ePHiY+8nIyMDSqVSe2eYhqurK1JTU6t9TWxsLHbv3g2lUomDBw/i/fffx8qVK/Hhhx9q13Tp0gWbN2/GoUOHsPb/27v3uKjq/H/gr5nhMlwHuQ+IclFUREAxDc2svGdmly1rvWWtm4QrZe2q25pft1a7Wln+tNxMd22zctU0XdHwUhaGCiSKAiIIKjdB7nKbOb8/hhklQIfLzJnL6/l4zKM4c+bM6xwPcObN57w/69cjNzcXY8aMQXV1+8WPhoYGVFVVtXqQeRIEQTfSVoyirUwqwYppYQA0Bdr2rJgWBpnUsguOhpLaMgnZ0ADL7Wer1f+Wyci6IqFllO3dwe5G7+1M1JG7gzzg6ypHdX0zlu04jet1TZDbShHurxA7GlmBLjeJ8fPza9OW4Ndff8Vnn32GTz/9tNvBiIxhsJ8rBvi4ILO4Gv9LL8RTIziahYiIyBJNmzYNL730EtLT0zFkyBBdmwKthx9+2GDvrVar4e3tjU8//RQymQzR0dG4cuUK3nnnHd0dZFOmTNGtHxERgZEjR6Jv3774+uuv8dxzz7XZ5urVq7Fy5UqDZSbjKa5qQOWNJsikEvTzdhYlw+RwJdbPGoaVezJQWNm6d+2aJyMxOVwpSi5LkNZStI3q4yZqDmMIbSnaZhfX6PqFd0YCWyOQCZJKJRjSW4GijHrsTL0KAKhvUuP+d49gxbQw/nwkg2Jnb7JqEokEjw3zx+r/nceOlCss2hIREVmoBQsWAAD+/ve/t3muMxOReXp6QiaTobi4uNXy4uLiDvvRKpVK2NratmqFMGjQIBQVFaGxsRF2dm1HlLm5uSE0NBQXLlxod5vLli3D4sWLdV9XVVUhICBAr30g03K+SDNKOsjTCfY24k2uPDlciQlhvkjOLUdJVT3e2n8eVyvrOx5+S3dUXtuIS2V1AICo3m7ihjGCIE8n2EglqG5oRmFlPfzcHPR+bXFVPVLyKwAAE8NYtCXTsf9MIQ5mFLdZXlRZj9itKVg/axgLt2QwXWqPQGRJHhnqD6kESM4rR37LRRURERFZFrVa3eFD34ItANjZ2SE6OhqJiYmttp2YmKib6+G3Ro8ejQsXLkCtVuuWZWVlQalUtluwBYCamhrk5ORAqWz/g6C9vT1cXV1bPcg8idnP9rdkUgliQjwwfag/fhfdGwCw93T7bT/ozn5tGWUb7OUEhaPlzzJvZyNFkKcTgM63SDjQUhSLCnCDr4KT3pFpUKkFrNyT0e5z2h7gK/dkQKVuryM4UfexaEtWz8dVjtEtMz/uTL0ichoiIiIytMuXL7cqoHbW4sWLsXHjRmzZsgXnzp1DbGwsamtrMW/ePADAnDlzWk1UFhsbi/LycsTHxyMrKwt79+7FqlWrEBcXp1vnlVdewdGjR5GXl4eff/4Zjz76KGQyGZ5++umu7yiZBW0/24E+4hdtbzU1wg8A8ENWKarqOfdDV6S2TEIWFeAmbhAjurVFQmccYGsEMkHJueVtWsbcSgBQWFmP5Nxy44Uiq9Kp9giPPfbYbZ+vqKjoThYi0Tw+rDd+zL6GHamXsWhcP4uf2ZWIiMiahYWFIS0tDcHBwV16/YwZM1BaWorXXnsNRUVFiIqKwv79+3WTk+Xn50MqvTk2IiAgAAkJCXjppZcQEREBf39/xMfHY8mSJbp1Ll++jKeffhplZWXw8vLCPffcg+PHj8PLy6t7O0smz5RG2t4q1McZIV5OyCmtReK5Yjw6tLfYkczOzUnI3ETNYUz9fZyB9M6NtK2sa0JSThkAYNJgnzusTWQ8JdUdF2y7sh5RZ3WqaKtQ3H52PIVCgTlz5nQrEJEYJg72gZOdDJfK6pCSfx3Rfd3FjkREREQGIgjdv41x4cKFWLhwYbvPHTlypM2ymJgYHD9+vMPtbdu2rduZyPw0qdTIKdGMSBzoa1otLiQSCaZG+GFtYjb2ni5k0baT1GpB1x4hKqCXuGGMSDvSNqtE/5G2ieeL0awWEOrjjGAvcSbjI2qPt4t+rTr0XY+oszpVtP38888NlYNIVI52NpgyRIntpy5j+6krLNoSERERkcHlXatFo0oNRzsZevfSf9ImY3koQom1idn4Iesaquqb4Cq3/L6sPSW3rBZV9c2wt5FioNK0RlEbUqiPpuh6obgagiDodQfj/jOa1giT2RqBTMyIIHcoFXIUVdajvT/3SgD4KuQYEcT6ARkGe9oStXhsmD8A4LvTV1HfpP+EJERERGRe/vrXv8LdnR+wSHza1gihPi6QSk2vPVeojwv6eTujUaXG9+3Mnk4dS82vAAAM8VfAVmY9H7v7ejjBViZBbaMKVypu3HH9G40q/JBdCgCYyKItmRiZVIIV08IAaAq0t9J+vWJaGGQm+PObLIP1/PYguoO7gzzgp5Cjur4ZiedKxI5DREREBrJs2TK4ubmJHYPo5iRkJtbP9lZThygBAHtPF4qcxLykFVjfJGQAYCuTIthTM9pWn8nIjmaVor5JDX83Bwz2M60WIUQAMDlcifWzhsFX0boFgq9CjvWzhmFyuFKkZGQNWLQlaiGVSvBoy2jbHSmXRU5DRERExlBQUIBnn31W7BhkpUx1ErJbTY3QFCR+zL6GyhtNIqcxH2nafrZ93ETNIYb+LS0S9JmMLOFsS2uEcF9OBk0ma3K4EseWPIAv59+ND5+Kwpfz78axJQ+wYEsGx6It0S20EywcySrFtZoGkdMQERGRoZWXl2PLli1ixyArlVlcBcC0i7ahPi7ozxYJnXKjUYVzhZqC5dA+1jMJmdYA7WRkdxhp29isRuI5zTk1ia0RyMTJpBLEhHhgepQ/YkI82BKBjKJTE5ERWbp+3s6IDHDDrwUV2J12Fc/eEyR2JCIiIuqG3bt33/b5ixcvGikJUWs1Dc0oKNf0/Bzoa9q3hU+NUOKD77OxL70Qj0f3FjuOyTtztRIqtQAvF3v4KaxvVvn+uqLt7UfaHr9Yhqr6Zng62yG6r/UVt4mI7oRFW6LfeHyYP34tqMCO1Mss2hIREZm5Rx55BBKJBILQ3rzPGrwll8SgLWh5u9jD3clO5DS3N3WIpmj7Q3YpKm80QeFgK3Ykk5bWMglZVICbVf58CW1pj3ChpAZqtdDhJHva1ggTwnw4apGIqB1sj0D0Gw9F+MFWJsGZK1W6ySGIiIjIPCmVSuzYsQNqtbrdR0pKitgRyUplmkE/W63+Pi4I9XFGk0rAQbZIuCNtP9uhVtjPFgD6ejjBzkaKG00qXL5+o9111GoBB1rOpYlsjUBE1C4WbYl+w93JDvcP8AYA7EjlhGRERETmLDo6GqdOnerw+TuNwiUyFG3RdqAZFG0B4MEhmgl39qUXipzE9KXmXwegGWlrjWRSCUK8bj8ZWWrBdZRWN8DF3gajQjyMGY+IyGywaEvUjseGaXp17Uq9ApWaH+SIiIjM1Z///GeMGjWqw+f79euHw4cPGzERkcb5Iu0kZKbdz1ZrakvR9seWFgnUvpKqelytrIdEAkT0dhM7jmi0LRKyStov2iac1YyyvX+gN+xtZEbLRURkTli0JWrH/QO94OZoi+KqBvycc03sOERERNRF/v7+mDRpUofPOzk5YezYsUZMRAQIgoDzZjbSli0S9JPa0hoh1NsFzvbWO4VMaMtkZNnFNW2eEwQB+89o+tlODmdrBCKijrBoS9QOexsZpkX4AQB2pFwROQ0RERF1Vf/+/VFaWqr7esaMGSguZsGJxFVS3YCKuiZIJUA/b2ex4+ht6hDN9fHe01dFTmK6rL2frVZ/747bI5wvqkZ+eR3sbKQYG+pl7GhERGaDRVuiDjwerWmRsP9MEWoamkVOQ0RERF3x2361+/btQ21trUhpiDS0o2wDPZ0gtzWfW8OnRmhGRR67cA2VdWyR0B5r72erpR1pe6Gkpk27uYSzmlG29/b3hJMVj0YmIroTFm2JOhDZW4FgLyfcaFLpbt8hIiIiIuquzJZ+tubSGkGrn7cLBvi4oEkl4EAGr49/S6UWkH65EgAQZeUjbQPcHSG3laKhWY2C8rpWz2k/W00azNYIRES3w6ItUQckEgkeb5mQbEfKZZHTEBERUVdIJBJIJJI2y4jEpB1pO8DHPCYhu9XUCM2EZHvTC0VOYnqyS6pR26iCk50M/b3NqyDf02RSia71R+YtLRIuldXifFE1ZFIJxg/yESseEZFZ4L0IRLfxyFB/vJOQiaSLZbhScQP+bg5iRyIiIqJOEAQBzzzzDOzt7QEA9fX1WLBgAZycnFqtt2PHDjHikZXK1BZtzWykLQA8OESJNQezcCxb0yJB4WgrdiSTkZpfAQCI6O0GmZR/HAr1dsGZK1XILq7WjarVtkYYEeiOXk52YsYjIjJ5HGlLdBv+bg6ICfaAIAC7UjkhGRERkbmZO3cuvL29oVAooFAoMGvWLPj5+em+1j6IjKVZpUZ2SQ0A82uPAGgmThvo64JmtYAEtkhoJa2laGvtrRG0+rf0tc0qrtEtSzirmQhycjhbIxAR3QlH2hLdwWPD/JF0sQw7Ui7jhftCeEslERGRGfn888/FjkDUSl5ZHRqb1XCwlaGPu6PYcbpk6hAlzhdVY+/pQjw5PEDsOCYjraACACch0wr10bRHyGppj1BSVY+UlonaJg5mawQiojvhSFuiO5gyRAm5rRQ5pbU43TKxABERERFRV2hbI4T6OENqprfQP9jS1/anC9dQUdcochrTUNPQjKwSzb/tUBZtAQChLSNtL5bWolmlxoGMYggCEBngBqWCbeeIiO6ERVuiO3C2t8Hklh5MnJCMiIiIiLojs6gKADDQ1/wmIdMK8brZIuFAy+3u1u50QQUEQdNezdtVLnYck+Dv5gAHWxkaVWpcKq/T9bOdxFG2RER6YdGWSA+PDesNANj961U0NqtFTkNERERE5uq8GU9CdqupQzSjbfemF4qcxDSksjVCG1KpBP1bWiScyruOpJwyANBNSkZERLfHoi2RHkb384S3iz2u1zXhSGaJ2HGIiIiIyExltvT3NMdJyG51a4uE67VskcB+tu3r56Up2r57IBPNagH9vJwQ0rKMiIhuj0VbIj3IpBI8OtQfAPBftkggIiIioi6oa2xGfnkdAPMfaRvi5YxBSldNi4SMIrHjiEoQBF3RdmgfN1GzmJL9Zwpx8JymfUZJdQMAoKiqHvvPcHQ2EZE+WLQl0pO2RcKh8yUcTUBEREREnZZVXANBADyd7eHhbC92nG6bOkRzm/vedOsu2l6puIHS6gbYSCUI91eIHcck7D9TiNitKaiub261vKZBhditKSzcEhHpgUVbIj0N8HXBYD9XNKkEfHf6qthxiIiIiMjMnC/UTkJm3qNstR4cwhYJwM3WCAOVLpDbysQNYwJUagEr92RAuM06K/dkQKW+3RpERMSiLVEnaEfb/jflishJiIiIiMjcWMokZFrBLS0SVFbeIiEtvwIA+9lqJeeWo7CyvsPnBQCFlfVIzi03XigiIjPEoi1RJzwc6QeZVIK0ggrklNaIHYeIiIiIzEimhRVtAeChlgnJvjttvbe7p2r72Qb0EjeIiSip7rhg25X1iIisFYu2RJ3g5WKPsaFeAICdHG1LRERERHoSBAGZxZqiraW0RwButkj4OafMKlskNKnUOHOlEgAQxUnIAADeLvIeXY+IyFqxaEvUSY8N8wcA7Ey9AjX7MBERERGRHkprGlBe2wiJBOjvbTlF2yBPJ4S1tEhIOGt9LRLOF1ajoVkNV7kNgjycxI5jEkYEuUOpkEPSwfMSAEqFHCOC3I0Zi4jI7LBoS9RJ4wf5wEVugysVN/AL+zARERERkR60rRECPZzgYGdZk1VNbWmRsDfd+lokpBVcBwBEBrhBKu2oTGldZFIJVkwLA4A2hVvt1yumhUHG40VEdFss2hJ1ktxWpuvdtSPlsshpiIiIiMgc6PrZ+ljOKFutqbe0SCi3shYJqS2TkA3tw362t5ocrsT6WcPgq2jdAsFXIcf6WcMwOVwpUjIiIvNhI3YAInP02LDe+DK5APvSC/H36eEWN1qCiIiIiHrWeQuchEwr0NMJg/1ccfZqFRLOFuHpEX3EjmQ0abpJyNxEzWGKJocrMSHMF8m55Siproe3i6YlAkfYEhHphyNtibpgeN9e6OPuiNpGFQ5kWF/vLiIiIiLqHO1I20FKyyvaAjdbJOyzohYJlXVNuHitFoCmPQK1JZNKEBPigelR/ogJ8WDBloioE1i0JeoCiUSim5DsvylXRE5DRERERKZMpRaQVawdaesqchrDuLVFQllNg8hpjCPtcgUAINDDEe5OduKGISIii8OiLVEXPTa0NwDgx6xS7EsvxLdpV5CUUwaVWhA5GRERERGZkktltWhoVkNuK0Ufd0ex4xhEXw8nhPu7QqUWkHC2WOw4RpGar5mELIqjbImIyADY05aoi/p4OCLEywk5pbV44YsU3XKlQo4V08LYXJ+IiIiIANxsjRDq42LRt4c/OESJM1eqsC+9EL8fafl9bbX9bFm0JSIiQ+BIW6Iu2n+mEDmltW2WF1XWI3ZrCvafsZ5+XkRERETUMd0kZD6W2c9W62aLhGsW3yJBEAT8qi3a9uklbhgiIrJILNoSdYFKLWDlnox2n9M2R1i5J4OtEoiIiIhIN9J2gK9lF237ejhhiL8CagEW3yLhUlkdrtc1wc5GijClZfYpJiIicbFoS9QFybnlKKys7/B5AUBhZT2Sc8uNF4qIiIiITFJmyyRkAy10ErJbPdgy2nZv+lWRkxhWaoGmn+1gP1fY2fBjNRER9Tz+diHqgpLqjgu2XVmPiIiIiCxTXWMz8so0LbUsfaQtcLNFQlJOGa5ZcIuEtPwKAOxnS0REhsOiLVEXeLvIe3Q9IiIiIrJM2cU1EATAw8kOXi72YscxuD4ejre0SCgSO47BcBIyIiIyNBZtibpgRJA7lAo5Opr7VwJAqZBjRJC7MWMRERERkYmxln62t5oa0dIi4bRlTsxb36RCRmEVAGAYJyEjIiIDYdGWqAtkUglWTAsDgA4LtyumhUEm7ehZIiIiIrIG562xaNvSIuH4RctskXD2ahWaVAI8nOzQu5eD2HGIiMhCsWhL1EWTw5VYP2sYfBWtWyBIALz3ZCQmhyvFCUZEREREJiOzWDMic6AVFW0D3B0R0VvTImH/GctrkXBrawSJhIM0iIjIMFi0JeqGyeFKHFvyAL6cfzc+mBEFfzc5BABXK26IHY2IiIiITMDN9giuIicxLu1oW0tskaAt2g7t4yZqDiIismws2hJ1k0wqQUyIBx4Z6o+/TB4IAPjnsVzUNDSLnIyIiIiIxHStpgHXahohkQChPs5ixzGqB1uKtr/klqG02rJaJKTmXwcARAWwny0RERkOi7ZEPeihCD8Eezqhoq4J/066JHYcIiIiMpB169YhMDAQcrkcI0eORHJy8m3Xr6ioQFxcHJRKJezt7REaGop9+/a1u+6bb74JiUSCF1980QDJyZi0o2z7ujvC0c5G5DTGFeDuiEhti4SzltMi4VpNAy5fvwGJBIgIUIgdh4iILBiLtkQ9SCaVIO7+fgCAf/54EXWNHG1LRERkab766issXrwYK1asQEpKCiIjIzFp0iSUlJS0u35jYyMmTJiAvLw8bN++HZmZmdi4cSP8/f3brHvixAl88skniIiIMPRukBFY4yRkt5oaoRltu8+CWiSk5VcAAEK8nOEqtxU3DBERWTQWbYl62PQoP/Rxd0RZbSP+80u+2HGIiIioh61Zswbz58/HvHnzEBYWhg0bNsDR0RGbNm1qd/1NmzahvLwcu3btwujRoxEYGIixY8ciMjKy1Xo1NTWYOXMmNm7ciF69eNu1Jcgs0kxCZm39bLWmhN9skVBSXS9ymp6h62cb4CZqDiIisnws2hL1MBuZFAtbRttuOHoR9U0qkRMRERFRT2lsbMSpU6cwfvx43TKpVIrx48cjKSmp3dfs3r0bMTExiIuLg4+PD8LDw7Fq1SqoVK2vEeLi4jB16tRW2+5IQ0MDqqqqWj3I9GjbIwy00pG2Ae6OiAxwg1oAEs5YRouE1IKWfrachIyIiAyMRVsiA3h0mD/83RxwraYBXyZztC0REZGluHbtGlQqFXx8fFot9/HxQVFR+0WpixcvYvv27VCpVNi3bx+WL1+O9957D2+88YZunW3btiElJQWrV6/WK8fq1auhUCh0j4CAgK7vFBmEWi0gq7gGgPW2RwCAqUN8AQB7082/RYJaLeB0QSUAIIojbYmIyMBYtCUyAFuZFC/cHwIA2HA0h6NtiYiIrJharYa3tzc+/fRTREdHY8aMGXj11VexYcMGAEBBQQHi4+PxxRdfQC6X67XNZcuWobKyUvcoKCgw5C5QF+SX1+FGkwr2NlIEejiJHUc0Dw7RtkgoN/sWCTmlNahuaIaDrQwDfKy3EE9ERMbBoi2RgfwuujeUCjmKqxrwzanLYschIiKiHuDp6QmZTIbi4uJWy4uLi+Hr69vua5RKJUJDQyGTyXTLBg0ahKKiIl27hZKSEgwbNgw2NjawsbHB0aNHsXbtWtjY2LRpowAA9vb2cHV1bfUg06KdhKy/jzNkUonIacTTu5cjogLcIFhAi4TUlknIhvRWwEbGj9JERGRY/E1DZCD2NjLE3qcZbbv+8AU0NqtFTkRERETdZWdnh+joaCQmJuqWqdVqJCYmIiYmpt3XjB49GhcuXIBaffNaICsrC0qlEnZ2dhg3bhzS09ORlpamewwfPhwzZ85EWlpaq2IvmY/z2knIfFhQn9oy2va70+bdIiGVk5AREZERsWhLZEBPDg+At4s9rlbW478pHG1LRERkCRYvXoyNGzdiy5YtOHfuHGJjY1FbW4t58+YBAObMmYNly5bp1o+NjUV5eTni4+ORlZWFvXv3YtWqVYiLiwMAuLi4IDw8vNXDyckJHh4eCA8PF2UfqfusfRKyW01p6WubnGfeLRLSWoq27GdLRETGwKItkQHJbWV4fqxmtO3/O3IBTSqOtiUiIjJ3M2bMwLvvvovXXnsNUVFRSEtLw/79+3WTk+Xn56Ow8OaIwoCAACQkJODEiROIiIjAokWLEB8fj6VLl4q1C2QE2qKtNU9CpnVri4T9Ztoioa6xGZkto6eH9uklchoiIrIGNmIHILJ0vx/RB+uPXEBB+Q3sSr2CJ4ZzdmciIiJzt3DhQixcuLDd544cOdJmWUxMDI4fP6739tvbBpmP+iYV8spqAXCkrdZDEUqkFVTgu9OFmBMTKHacTjt9uRJqAfB1lcNXod+EgURERN3BkbZEBuZgJ8Mf7w0GAKw7fAHNHG1LREREZNGyi2ugFoBejrbwcrEXO45JmNLS1/ZEXjlKqsyvRQJbIxARkbGxaEtkBDNH9oW7kx3yyuqw5/RVseMQERERkQHpJiHzdYFEIhE5jWnwd3PA0D6aFgn/M8MWCWn5FQCAqD5uouYgIiLrIXrRdt26dQgMDIRcLsfIkSORnJx82/UrKioQFxcHpVIJe3t7hIaGYt++fUZKS9Q1TvY2eO6eIADAx4cuQKUWRE5ERERERIZycxIyV5GTmJapLaNt96YX3mFN06MdaTuUI22JiMhIRC3afvXVV1i8eDFWrFiBlJQUREZGYtKkSSgpKWl3/cbGRkyYMAF5eXnYvn07MjMzsXHjRvj7+xs5OVHnzYnpC4WDLXJKa7HPDC9UiYiIiEg/mcXaoi372d7qwVtaJBSbUYuEwsobKKqqh0wqwZDeCrHjEBGRlRC1aLtmzRrMnz8f8+bNQ1hYGDZs2ABHR0ds2rSp3fU3bdqE8vJy7Nq1C6NHj0ZgYCDGjh2LyMhIIycn6jwXua1utO1Hh7Kh5mhbIiIiIot0vmWk7QAWbVvxc3PAMG2LBDMaxKBtjRDq4wJHO87lTURExiFa0baxsRGnTp3C+PHjb4aRSjF+/HgkJSW1+5rdu3cjJiYGcXFx8PHxQXh4OFatWgWVStXh+zQ0NKCqqqrVg0gsc0cFwkVug6ziGiScNb9eXkRERER0e+W1jSitbgCgKfJRa9rRtvvSzedamJOQERGRGEQr2l67dg0qlQo+Pj6tlvv4+KCoqP1f4BcvXsT27duhUqmwb98+LF++HO+99x7eeOONDt9n9erVUCgUukdAQECP7gdRZygcbDFvVCAAYO2hCxAEjrYlIiIisiTaScj6uDvCyZ6jMn9L1yLhkvm0SEjV9rPlJGRERGREok9E1hlqtRre3t749NNPER0djRkzZuDVV1/Fhg0bOnzNsmXLUFlZqXsUFBQYMTFRW8/eEwQnOxnOFVbh+3Pt928mIiIiIvOUydYIt+Xn5oDovr3MpkVCs0qN9MuVADgJGRERGZdoRVtPT0/IZDIUFxe3Wl5cXAxfX992X6NUKhEaGgqZTKZbNmjQIBQVFaGxsbHd19jb28PV1bXVg0hMbo52mKsdbZuYzdG2RERERBZEW7TlJGQd04623WsGRdvM4mrcaFLBxd4GIV7OYschIiIrIlrR1s7ODtHR0UhMTNQtU6vVSExMRExMTLuvGT16NC5cuAC1Wq1blpWVBaVSCTs7O4NnJuopfxgTDEc7GdKvVOJIZqnYcYiIiIioh3ASsjt7cIhmkM6JvOsoqjTtFgnafrYRAQpIpRJxwxARkVURtT3C4sWLsXHjRmzZsgXnzp1DbGwsamtrMW/ePADAnDlzsGzZMt36sbGxKC8vR3x8PLKysrB3716sWrUKcXFxYu0CUZe4O9lh1t19AQAfcrQtERERkUVQqwVkFXOk7Z0oFZoWCQDwvzOmPdo2Nb8CADA0oJe4QYiIyOqIWrSdMWMG3n33Xbz22muIiopCWloa9u/fr5ucLD8/H4WFN3+JBwQEICEhASdOnEBERAQWLVqE+Ph4LF26VKxdIOqy+WOCYW8jRVpBBY5duCZ2HCIiIiK9qNQCknLK8G3aFSTllEGl5h+ftQqu16GuUQU7GykCPZzEjmPSpmpbJJw27aKtdqRtFPvZEhGRkYk+nenChQuxcOHCdp87cuRIm2UxMTE4fvy4gVMRGZ6Xiz1mjuyLTT/l4sPvs3FPP09IJLzlioiIiEzX/jOFWLknA4W33NKuVMixYloYJocrRUxmGrStEfp5OcNGZlZzPhvdg0OU+Pt3GTh5SdMiwVchFztSG1X1TcgprQEARPVxEzcMERFZHV5JEIno+bHBsLOR4uSl60i6WCZ2HCIiIqIO7T9TiNitKa0KtgBQVFmP2K0p2G/it7kbAych05+vQo7hLS0S9pnohGSnCyohCECAuwM8ne3FjkNERFaGRVsiEfm4yvHUXQEAgI8SL4ichoiIiKh9KrWAlXsy0F4jBO2ylXsyrL5VQiYnIeuUqREtLRJMtGibmn8dABDFfrZERCQCFm2JRLZgbAhsZRIkXSxDcm652HGIiIiI2kjOLW8zwvZWAoDCynqrv5Y5X1QFgEVbfU1paalx6tJ1FFbeEDlNW+xnS0REYmLRlkhkfm4OeGJ4y2jbQ9kipyEiIiJqq6S644JtV9azRPVNKuSV1QEABildRU5jHnwVctwVqG2RUCRymtYEQWDRloiIRMWiLZEJiB0bAhupBD9mX0NKy21YRERERKbC20W/SaL0Xc8SXSipgUotwM3RFt4u7H+qr6lDNKNtvzqRj2/TriApp8wk2mxcvn4DZbWNsJVJMNiPRXgiIjI+Fm2JTECAuyMeG+YPAPgokaNtTY1KLSApp8ykPkgQEREZ04ggdygVckg6eF4CQKmQY0SQuzFjmRRdP1sfF0gkHR0p+i25rQwAkFVcg/htaXh643Hc89Yh0Se20w6kCFO66jISEREZE4u2RCYi7v5+kEklOJxZitOXK8SOQy32nynEPW8dwtMbj5vUBwkiIiJjkkklWDEtDAA6LNyumBYGmdR6i5WZxZqi7UD2s9Xb/jOFWLYjvc3yosp6xG5NEfV6i60RiIhIbCzaEpmIvh5OmB7lBwBYm3hB5DQEaD5IxG5NaTPxiil8kCAiIjK2yeFKrJ81DL6Kti0Q3ngkHJNbJpWyVue1I219eSu9PlRqASv3ZKC9+5e0y1buyRDtDidd0baPmyjvT0RExKItkQmJu78fpBLg+3PFOHu1Uuw4Vs3UP0gQERGJYXK4EseWPIAv59+ND5+KwkAfzajSRpVa5GTiyyyqAgAM4EhbvSTnlrf5w/itBACFlfVIzi03XqgWDc0qnL2i+fccGtDL6O9PREQEsGhLZFJCvJzxUIRmtO3HhzjaVkym/EGCiIhITDKpBDEhHpge5Y/fDe8NADhwtljkVOKqqGtEcVUDABZt9VVS3fF1VlfW60nnCqvRqFKjl6Mt+no4Gv39iYiIABZtiUzOwgf6QSIB/nemSDehBRmfKX+QICIiMhUTw3wBAMl55bhe2yhyGvFoWyP07uUAZ3sbkdOYB2+Xtm02urNeT0prmYQsMsCNk8oREZFoWLQlMjGhPi54sKUn3EeHskVOY71M+YMEERGRqejj4YiBvi5QqQUkni8RO45ozhdqbqXnJGT6GxHkDqVC3uHEdgDQy9EWI4LcjZZJS9vPlq0RiIhITCzaEpmghQ/0AwDsTS/EhRKOthXDsD5ukNt2/CNSAkCpkIvyQYKIiMiUTBqsGW174GyRyEnEk1msnYSMRVt9yaQSrJgWBgAdFm4r6pqw/VSB8UK1SOUkZEREZAJYtCUyQYOUrpgY5gNBANYdzhE7jtVRqQUs+e9p1DfdflKVFdPCIJPyljkiIrJuEwf7AAB+yC7FjUaVyGnEoW2PMMDXVeQk5mVyuBLrZw2Dr6L1nUtKhRyjQzwgAFjy33R8ctR418PltY24VFYHAIjq7Wa09yUiIvotNlwiMlGLxvXHgYxifJt2BYvG9UeQp5PYkayCSi3gz9/8il1pV2EjleC5e4Kw+9errSYls5FK8NHTQzG5pY0FERGRNQtTuqJ3Lwdcvn4DP2SX6kbeWgu1WkBWS9GW7RE6b3K4EhPCfJGcW46S6np4u2juZJJKgLf2Z2LD0Rys/t95VNxowl8mDTB4j9lfW0bZBns6QeFoa9D3IiIiuh2OtCUyUeH+Cowb6A21AKw7fEHsOFZB3TLCdkfqFchaCrPLHhyEY0sewJfz78aqR8NhJ5OiWS3AWc6/eREREQGARCLRTUh24GyxyGmM70rFDdQ2qmArk/CP7F0kk0oQE+KB6VH+iAnxgEwqgUQiwdIpA7F0ykAAwPojOfjrznSo1IJBs7A1AhERmQoWbYlM2J/G9QcA7Ey9gvyW27TIMNRqAUt3nMb2U5chk0qw9qmhmDJEM5JW+0Hi9yP74vcj+wAANhjxNj0iIiJTp22RkHi+GM2q27cXsjTa1gghXs6wlfHjVU9bMDYEbz42BFIJ8GVyARZ9mYqGZsO14UjNvw4AGBrgZrD3ICIi0gevKohMWFSAG+4N9YJKLWD9UY62NRS1WsBfd6bj65OXIZUAH8yIwtSI9lsf/GFMEGRSCX66UIb0y5VGTkpERGSahvftBXcnO1TUNSE5r1zsOEaVWVQFQDMnARnGUyP6YN3vh8FOJsXe9EL8YctJ1DY09/j7qNWCrj1CVECvHt8+ERFRZ7BoS2Ti4sf1AwBsP3UZl69ztG1PU6sF/O3bM9h2ogBSCfD+jChMi/TrcP3evRzxcMvzHG1LRESkYSOTYtxAbwDW1yLh5iRk7GdrSFOGKLHpmbvgaCfDj9nXMOuzX1BR19ij75FbVouq+mbY20gxUMl/TyIiEheLtkQmLrqvO0b380CTSmCRsIcJgoDXdp/Bf37Jh0QCvPdkJKZH+d/xdc+PDQYA/O9MIfKu1Ro6JhERkVmYOFjb17YIgmDYvqOmJJNFW6O5p78nvvjDSCgcbJGaX4EZnxxHcVX9nV+op9T8CgDAEH8FW10QEZHo+JuIyAwsekDT2/brE5dRVNlzF6bWTBAE/N/us9h6XFOwffd3kXh0aG+9XjvQ1xX3D/CCWgA+/fGigZMSERGZhzH9PeFgK8PVynqcvVoldhyjaGhW4WLLH3AHsmhrFEP79MI3C2Lg42qPzOJq/G7Dz7hU1jN/RE8r0PSzjWI/WyIiMgEs2hKZgZHBHhgR5I5GlZqjbXuAIAhYuScDW5IuQSIB3n48Ao9H61ew1VowNgSApm1FSTUL6URERHJbGcaGegEAEs4WiZzGOHJKaqFSC3CV28DXVS52HKsR6uOC7QtGIdDDEQXlN/C7DUk4V9j9PxSkafvZ9nHr9raIiIi6i0VbIjMRP04z2vbL5HyU9OBtYNZGEAS8/t05bP45DwDw1mMReGJ4QKe3MyLIHUP7uKGxWY3NP+X1bEgiIiIzNXGwDwDr6WubWawpFA70dYVEIhE5jXUJcHfENwtGYZDSFaXVDXjykySc7MYkeDcaVThfqGl1MbQPJyEjIiLxsWhLZCZGhXggum8vNDSr8ekPvCW/KwRBwKp957Dpp1wAwOrHhuDJuzpfsAUAiUSiG2377+OXUF3f1GM5iYiIzNW4gT6QSSXILK62ir7vnIRMXF4u9tj2x7sxvG8vVNc3Y9Znv+BIZkmXtnXmaiWa1QK8XOzhp+CoaSIiEh+LtkRmQiKRYFHLaNutv1zCtZoGkROZF0EQ8Ob+89j4o6Zg+49Hw/H0iD7d2uaEQT4I8XJCdX0zvkzO74mYREREZk3haIu7g90BAAcyLL9FAichE5/CwRb/fm4k7hvghfomNf6w5SR2/3q109tJa5mELCrAjaOmiYjIJLBoS2RG7u3vicjeCtQ3qfHPluIj3ZkgCHg7IROfHNWMUH59+mDMHNm329uVSiV4/l7NaNvPjuWioVnV7W0SERGZu4lhvgCso0WCtmjLScjE5WAnw6ezh+PhSD80qwXEb0vF1uOXOrUNXT9bTkJGREQmgkVbIjNy62jbfyXloby2UeREpk8QBLx3IAvrj2gmcFv58GDMjgnsse1PH+oHH1d7FFc14NvUzo/qICIisjQTwjR9bU/lX0dpteXeGVRZ14TCSs08A6Es2orOzkaKD2ZEYfbdfSEIwN92ncHHh7IhCIJer9cWbYdyEjIiIjIRLNoSmZkHBnpjsJ8r6hpV2HSMo23v5P3vs/Hx4QsAgBXTwjB3VGCPbt/eRobn7gkCAGz4IQdqtX4fDIiIiCyVn5sDInorIAjA9+csd7Tt+SLNJGT+bg5wlduKnIYAzV1Qf58+GIse6AcAePdAFt7Ye+6O12clVfW4UnEDEgkQ0dvNCEmJiIjujEVbIjNz62jbzT/nobKOE2B15IPvs7A2MRsA8LepgzBvdJBB3ufpEX3gIrfBxdJaHLTgD6dERET6mjRY2yLBcvvaZhazn60pkkgkWDxxAJY/FAZA08LqL/89jWaVusPXpLaMsg31doGzvY0xYhIREd0Ri7ZEZmjCIB8M9HVBTUMzPv+Zo23b81FiNj74XlOwffXBQfjDmGCDvZeL3Baz79b0yN1wNEfv2/CIiIgs1cSWFgk/XShDTUOzyGkM4zwnITNpz90ThPeeiIRMKsH2U5fxwhcpqG9qf/4B9rMlIiJTxKItkRmSSiX40wOa0babjuWiqp6jbW+17vAFvHcwCwCwdMpAzL/XcAVbrXmjg2BnI0VqfgWSc8sN/n5ERESmrJ+3M4I8ndCoUuNIZonYcQyCk5CZvseje2PDrGjY2UhxIKMY8z4/0e4fEdLyKwCwny0REZkWFm2JzNSUcF/093ZGVX0z/vVznthxTMb6Izl4JyETAPDnSQOwYGyIUd7Xy8Uev4vuDUAz2paIiMiaSSQSTBysGW174KzltQ4SBAFZuqKtq8hp6HYmhPlgy7wRcLa3QdLFMvx+43GU1dycIE+lFnD6cgUAIIpFWyIiMiEs2hKZKalUgoUtkyz881iuxd562Bmf/pCDt/afBwC8MjEUcff3M+r7/3FMMKQS4HBmqW5yEiIiskzr1q1DYGAg5HI5Ro4cieTk5NuuX1FRgbi4OCiVStjb2yM0NBT79u3TPb9+/XpERETA1dUVrq6uiImJwf/+9z9D74ZBTQzT9LU9fL4Ejc0d9xM1R1cqbqC6oRm2MgmCvZzEjkN3EBPigS/n3w13JzucvlyJJz9JwtWKG1CpBWw/VYDaRhXkNlIEezqLHZWIiEiHRVsiM/ZQhB+CPZ1QUdeErccviR1HVP/88SJW7dMUbF8aH4qFLe0jjCnQ0wlTwpUAgE+OXjT6+xMRkXF89dVXWLx4MVasWIGUlBRERkZi0qRJKClpvw1AY2MjJkyYgLy8PGzfvh2ZmZnYuHEj/P39dev07t0bb775Jk6dOoWTJ0/igQcewPTp03H27Flj7VaPGxrgBi8Xe1Q3NCPpYpnYcXqUtjVCiJczbGX8SGUOhvRW4OvnY+CnkCOntBZT1/6Iu1d9jyX/TQcA1DerMfadw9h/plDkpERERBq8wiAyYzKpRDeadOMPF1HXaJ2jbTcdy8Ube88BABaN64/48cYv2Gpp2zHs/vUqLl+vEy0HEREZzpo1azB//nzMmzcPYWFh2LBhAxwdHbFp06Z219+0aRPKy8uxa9cujB49GoGBgRg7diwiIyN160ybNg0PPvgg+vfvj9DQUPzjH/+As7Mzjh8/bqzd6nFSqQQTwrQtEopETtOzOAmZeern7YxvYkfBx9Ue1+uaUFrT2Or5osp6xG5NYeGWiIhMAou2RGZuepQf+rg7oqy2Ef/5JV/sOEa3+adc/P27DADAnx7oh5dELNgCmlEco/t5QKUW8M8fc0XNQkREPa+xsRGnTp3C+PHjdcukUinGjx+PpKSkdl+ze/duxMTEIC4uDj4+PggPD8eqVaugUrU/k71KpcK2bdtQW1uLmJiYdtdpaGhAVVVVq4cpmthStD2YUQy1WhA5Tc/JZNHWbPm6yiF0cCpqF6/ckwGVBZ2vRERknli0JTJzNjIpFraMtt1w9CLqm9r/AGiJ/pWUh//boynYvnBfCBZPCIVEIhE51c3Rtl+dKMD12sY7rE1ERObk2rVrUKlU8PHxabXcx8cHRUXtjya9ePEitm/fDpVKhX379mH58uV477338MYbb7RaLz09Hc7OzrC3t8eCBQuwc+dOhIWFtbvN1atXQ6FQ6B4BAQE9s4M9LCbEA872NiipbkBay2RPliBTNwkZi7bmJjm3HCXVDR0+LwAorKxHcm658UIRERG1g0VbIgvw6DB/+Ls54FpNA7YlW8do263HL+G1bzV9/haMDcGfJw0wiYItANzTzxOD/Vxxo0mFLUl5YschIiKRqdVqeHt749NPP0V0dDRmzJiBV199FRs2bGi13oABA5CWloZffvkFsbGxmDt3LjIyMtrd5rJly1BZWal7FBQUGGNXOs3eRob7B3oDAA6cLRY5Tc9obFYjp7QGADDA11XkNNRZJdX1PboeERGRobBoS2QBbGVSvHC/ZnTn+qM5Fj/a9j+/5ONvu84AAP54bzCWTDadgi0ASCQS3WjbLT/nWW2vYSIiS+Tp6QmZTIbi4tYFyOLiYvj6+rb7GqVSidDQUMhkMt2yQYMGoaioCI2NN+/IsLOzQ79+/RAdHY3Vq1cjMjISH374YbvbtLe3h6ura6uHqZp4S19boaP70s3IxWs1aFYLcJHbwE8hFzsOdZK3i37/ZvquR0REZCgs2hJZiN9F94ZSIUdxVQO+OXVZ7DgG89WJfPx1p2aW3+fuCcKyKQNNqmCrNSXcF33cHXG9rglfnzDN0U9ERNR5dnZ2iI6ORmJiom6ZWq1GYmJih/1nR48ejQsXLkCtVuuWZWVlQalUws7OrsP3UqvVaGjo+DZuc3HfAC/YyaS4eK1WN0LVnOn62fq4mOQ1CN3eiCB3KBVydPQvJwGgVMgxIsjdmLGIiIjaYNGWyELY28gQe1/LaNvDF9DYrL7DK8zP1ycLsHSHpmA7b3Qg/jZ1kMl+WLKRSTH/3mAAwMYfc9Gksrx/DyIia7V48WJs3LgRW7Zswblz5xAbG4va2lrMmzcPADBnzhwsW7ZMt35sbCzKy8sRHx+PrKws7N27F6tWrUJcXJxunWXLluGHH35AXl4e0tPTsWzZMhw5cgQzZ840+v71NBe5LUb18wAAJFhAi4RzhZyEzJzJpBKsmKbpFf3bq0jt1yumhUEmNc1rTCIish4s2hJZkCeHB8DbxR5XK+uxI8WyRttuP3UZS/57GoIAPDMqEK89FGayBVutJ6J7w9PZDlcqbmDv6UKx4xARUQ+ZMWMG3n33Xbz22muIiopCWloa9u/fr5ucLD8/H4WFN3/uBwQEICEhASdOnEBERAQWLVqE+Ph4LF26VLdOSUkJ5syZgwEDBmDcuHE4ceIEEhISMGHCBKPvnyFMDNO0jjhwtv3J2sxJZlEVAE5CZs4mhyuxftYw+P6mvYWvQo71s4ZhcrhSpGREREQ3SQRLaCzVCVVVVVAoFKisrDTp3l9EXfXZsVy8/l0GAtwdcOjl+2ArM/+/zexMvYzFX/8KQQBm390Xf58+2OQLtlofH8rGuweyMNDXBf+LH2M2uYmITAGv2/Rn6seqpLoeI1clQhCApGUPQKlwEDtSl41anYirlfX4+vkY3kJv5lRqAcm55Siproe3i6YlAkfYEhGRoel73Wb+1RwiauX3I/rA09kOBeU3sCv1ithxuu3btCt4uaVgO3NkH6x82HwKtgAw++5AONnJcL6oGkeySsWOQ0REJApvFzmG9ekFAPg+w3xbJFTeaMLVynoAmp62ZN5kUgliQjwwPcofMSEeLNgSEZFJYdGWyMI42Mnwx5ZequsOX0CzGfdS3f3rVbz0VRrUAvD0iAC8Pj0cUjO7mFY42uLpEX0AABuO5IichoiISDwTwzTtI8y5r21WsaafrVIhh8LRVuQ0REREZMlYtCWyQDNH9kUvR1vkldXhOzPtpfrd6ZsF2xnDA/CPR4aYXcFW67kxQbCVSfBLbjlS86+LHYeIiEgUEwdr+toev1iGyromkdN0zfkiTdGW/WyJiIjI0Fi0JbJATvY2+MMYzWjbtYlZ+OnCNXybdgVJOWVQqU2rjbVKLSApp6xVvn3phYjflgaVWsDvontj9WPmW7AFAKXCAdOj/AEAG45ytC0REVmnIE8nhPo4o1kt4HBmidhxukQ7CdkAX9PrG0xERESWxUbsAERkGHNi+mLd4Qu4eK0OM//5i265UiHHimlhJjEr7v4zhVi5JwOFLb3hAMDN0RZVN5qgFoDHhvnjrccjzLpgq7VgbDC2n7qMAxnFyCmtQYiXs9iRiIiIjG5imC+yii8g4WwRHhnqL3acTsvkSFsiIiIyEo60JbJQP124hrpGVZvlRZX1iN2agv1nxG2bsP9MIWK3prQq2AJARZ2mYDsiyB3v/C7SYiaE6OftgvGDfCAIwKdHL4odh4iISBSTWlokHM0qRX1T2+sUUyYIgq49wgAWbYmIiMjAWLQlskAqtYCVezLafU7bHGHlngzRWiVo893u3QvK64yWx1hi79O0rNiZegXFVfV3WJuIiMjyhPu7QqmQo65RhWPZ18SO0ymFlfWorm+GjVTCO2aIiIjI4NgegcgCJeeWtxnBeisBmg8eD639Ea4Oxp/5uOpG023zAZp8ybnliAnxMFIqw4vu6467AnvhRN51bDqWi2UPDhI7EhERkVFJJBJMDPPBlqRLOJBRhPFhPmJH0pu2NUKwlxPsbDj2hYiIiAyLRVsiC1RSrd8oznMtHz5Mlb77YU4WjA3BibyT+OKXfLxwfz8oRCiaExERiWnSYF9sSbqE78+VQKUWzKYV0s3WCJyEjIiIiAyPRVsiC+TtItdrvfhx/RHqY/yebFnF1fgwMfuO6+m7H+bk/gHeCPVxRlZxDb745RJeuK+f2JGIiIiM6q4gdygcbFFe24iTeeUYGWwed9VkFlUB4CRkREREZBws2hJZoBFB7lAq5CiqrG+3b6wEgK9CjkXj+osyumWy2hdfnyy4Y74RQe7GjmZwUqkEz98bgpe/+RWbjuXh2dFBkNvKxI5FRERkNLYyKcYN9MaO1Cs4kFFsNkVb3UhbEf7gTURERNaHzZiILJBMKsGKaWEANAXQW2m/XjEtTLTbEU09n6E9HOUHP4Uc12oasCPlithxiIiIjG7iYF8AwIGMIgiCOBOjdkaTSo2c0hoAwACOtCUiIiIjYNGWyEJNDldi/axh8FW0bjHgq5Bj/axhmByuFCmZhqnnMyRbmRTPjQkGAHz6Qw5UatP/sEpERNST7g31hL2NFAXlN3Cu0LR77APAxdJaNKkEONvboHcvB7HjEBERkRVgewQiCzY5XIkJYb5Izi1HSXU9vF00LQdMZQSrqeczpKfuCsDaxGzkldUh4WwRHhxiuUVqIiKi33K0s8G9oV44mFGMAxlFCPMz3cm9VGoBe37V3BmjVNhDLQAyy79UISIiIpFxpC2RhZNJJYgJ8cD0KH/EhHiYXEHU1PMZipO9DebG9AUAbDiaYxa3hhIREfWkiWE+AIADZ4tFTtKx/WcKcc9bh/Dx4RwAQHZJLe556xD2nykUORkRERFZOhZtiYhEMndUIOS2Upy+XImknDKx4xARERnVuEE+kEqAjMIqFJTXiR2njf1nChG7NQWFlfWtlhdV1iN2awoLt0RERGRQLNoSEYnEw9keTw4PAACsP5ojchoiIiLjcneyw4ggdwDAgQzTGm2rUgtYuScD7d0Ho122ck8G+9ITERGRwbBoS0QkovljgiGTSvBj9jWcuVIpdhwiIiKjmhjmCwA4cLZI5CStJeeWtxlheysBQGFlPZJzy40XioiIiKwKi7ZERCIKcHfE1JZJyD754aLIaYiIiIxrQktf2xN55SiraRA5zU0l1R0XbLuyHhEREVFnsWhLRCSy58cGAwD2nr6K/DLT6+lHRERkKAHujhjs5wq1ACSeLxE7jo63i7xH1yMiIiLqLBZtiYhENthPgXtDvaAWgI0/crQtERFZl5stEkynr+1dgb0gt+34o5IEgFIh1/XkJSIiIuppLNoSEZmABS2jbb8+WYBrJnR7KBERkaFNHKxpkfBjdinqGptFTqOx8cdc1Dep231O0vLfFdPCIJNK2l2HiIiIqLtMomi7bt06BAYGQi6XY+TIkUhOTtbrddu2bYNEIsEjjzxi2IBERAYWE+yByN4KNDSrseXnPLHjEBERGc1AXxf0cXdEQ7MaP2SVih0Hh8+X4O2E8wCAp+4KgFLRugWCr0KO9bOGYXK4Uox4REREZCVsxA7w1VdfYfHixdiwYQNGjhyJDz74AJMmTUJmZia8vb07fF1eXh5eeeUVjBkzxohpiYgMQyKRYMHYEMR+kYJ/JV3CgrEhcLIX/Uc0ERGRwUkkEkwM88E/j+Ui4WyxqMXQnNIaLNqWCkEAnh4RgFWPDoFaAJJzy1FSXQ9vF01LBI6wJSIiIkMTfaTtmjVrMH/+fMybNw9hYWHYsGEDHB0dsWnTpg5fo1KpMHPmTKxcuRLBwcFGTEtEZDgTB/siyNMJlTea8GVyvthxiIiIjGbiYE1f28RzxWhStd+WwNCq6psw/18nUV3fjOF9e2Hlw+GQSCSQSSWICfHA9Ch/xIR4sGBLRERERiFq0baxsRGnTp3C+PHjdcukUinGjx+PpKSkDl/397//Hd7e3njuuefu+B4NDQ2oqqpq9SAiMkUyqQR/vFfzh6jPjuWisVmcD61ERETGFt23Fzyc7FBV34zk3HKjv79KLeDFbWm4WFoLpUKO9bOiYWcj+vgWIiIismKiXolcu3YNKpUKPj4+rZb7+PigqKio3dccO3YMn332GTZu3KjXe6xevRoKhUL3CAgI6HZuIiJDeXSoP7xc7FFYWY/dv14VOw4REZFRyKQSjB+k+UyQcLb9zwGGtOZgJg6dL4G9jRSfzI6Gl4u90TMQERER3cqs/nxcXV2N2bNnY+PGjfD09NTrNcuWLUNlZaXuUVBQYOCURERdJ7eV4dnRQQCAT47mQK0WRE5ERERkHJPCNUXbA2eLIQjG+/2359erWHc4BwDw5uNDENHbzWjvTURERNQRUWe58fT0hEwmQ3FxcavlxcXF8PX1bbN+Tk4O8vLyMG3aNN0ytVpz+7CNjQ0yMzMREhLS6jX29vawt+dfyonIfMy8uw/+3+ELyC6pwaHzJRgf5nPnFxEREZm5USGecLSToaiqHulXKo1SPD17tRJ/3v4rAOCP9wbj0aG9Df6eRERERPoQdaStnZ0doqOjkZiYqFumVquRmJiImJiYNusPHDgQ6enpSEtL0z0efvhh3H///UhLS2PrAyKyCK5yW/z+7j4AgA1Hc0ROQ0REZBxyWxnuG+AFwDgtEspqGvDHf51CfZMaY/p7YsnkgQZ/TyIiIiJ9id4eYfHixdi4cSO2bNmCc+fOITY2FrW1tZg3bx4AYM6cOVi2bBkAQC6XIzw8vNXDzc0NLi4uCA8Ph52dnZi7QkTUY54bHQQ7mRQnL13HyTzjT8hCREQkhkmDNXfbHThbfIc1u6dJpcYLX6TgSsUNBHo44uOnh0EmlRj0PYmIiIg6Q9T2CAAwY8YMlJaW4rXXXkNRURGioqKwf/9+3eRk+fn5kEpFry0TERmVt6scjw3zx7YTBdhwNAf/DHQXOxIREZHB3TfAGzZSCbJLanCxtAbBXs4GeZ/Xv8vAL7nlcLKTYeOc4VA42hrkfYiIiIi6SiIYs8u/CaiqqoJCoUBlZSVcXV3FjkNE1KGLpTUYt+YoBAE48NK9CPVxETsSEZFR8bpNf5Z0rGZ/9gt+zL6GpVMGYsHYkDu/oJO2Jedj6Y50AMCns6MxcXDbuTSIiIiIDEXf6zYOYSUiMlHBXs6YFKb5IPnJ0YsipyEiIjKOiboWCT3f1/bUpXIs//YMAGDxhFAWbImIiMhksWhLRGTCFtynGWH0bdoVXK24IXIaIiIiw5swSNMmLbWgAiVV9T223cLKG3j+3yloUgmYEu6Lhff367FtExEREfU0Fm2JiExYVIAb7g52R7NawD9/zBU7DhERkcH5KuSIDHCDIAAHz/XMhGT1TSo8/+9TuFbTgIG+Lnj3iUhIOfEYERERmTAWbYmITJy2n9+2E/moqGsUOQ0REZHhTRqsGW174Gz3i7aCIGDZjnScvlwJN0dbbJwzHE72os/HTERERHRbLNoSEZm4saFeGKR0RV2jCv9KuiR2HCIiIoOb2NLT/eeca6iub+rWtj47loudqVcgk0qw7vfDEODu2BMRiYiIiAyKRVsiIhMnkUiwYGwwAGDzz3m40agSOREREZFh9fN2RrCXE5pUAg5nlnZ5Oz9klWLVvnMAgL9NHYTR/Tx7KiIRERGRQbFoS0RkBqYOUaJ3LweU1zbim1MFYschIiIyuEmDNaNtD5wt6tLr867V4k9fpkItAL+L7o1nRgX2YDoiIiIiw2LRlojIDNjIpJg/RjPa9tMfLqJZpRY5ERERkWFNDNP0tT2SWYqG5s7dZVLT0Iz5/zqJyhtNiApwwxuPhEMi4cRjREREZD5YtCUiMhNPDg+Au5MdLl+/gb3phWLHISIiMqjI3m7wcbVHTUMzfs4p0/t1arWAl75KQ3ZJDbxd7PHJ7GjIbWUGTEpERETU81i0JSIyEw52MsyNCQQAbDh6EYIgiBuIiIjIgKRSCSa0jLY9cLZY79d9mJiNgxnFsJNJ8cnsaPi4yg0VkYiIiMhgWLQlIjIjc2L6wsFWhnOFVfgh+5rYcYiIiAxqYpimr+3BjGKo1Hf+Y+X+M4X4MDEbAPDGo+EY2qeXQfMRERERGQqLtkREZqSXkx2eGhEAANhwJEfkNERERIZ1d7AHXOQ2uFbTgLSC67dd93xRFRZ//SsAYN7oQDw5PMAYEYmIiIgMgkVbIiIz84cxwbCRSpB0sQy/FlSIHYeIyCqtW7cOgYGBkMvlGDlyJJKTk2+7fkVFBeLi4qBUKmFvb4/Q0FDs27dP9/zq1atx1113wcXFBd7e3njkkUeQmZlp6N0weXY2Ujww0BvA7VskXK9txPx/nURdowqjQjzw6oODjBWRiIiIyCBYtCUiMjP+bg54OMoPALDhKEfbEhEZ21dffYXFixdjxYoVSElJQWRkJCZNmoSSkpJ2129sbMSECROQl5eH7du3IzMzExs3boS/v79unaNHjyIuLg7Hjx/HwYMH0dTUhIkTJ6K2ttZYu2WytC0SEs4WtdvPvVmlxsIvU1BQfgMB7g5Y9/thsJHxYw4RERGZNxuxAxARUectGBuCHSlXsP9sES6W1iDYy1nsSEREVmPNmjWYP38+5s2bBwDYsGED9u7di02bNmHp0qVt1t+0aRPKy8vx888/w9bWFgAQGBjYap39+/e3+nrz5s3w9vbGqVOncO+99xpmR8zE2AFesLORIq+sDtklNQj1cWn1/Kp95/HThTI42smwcc5w9HKyEykpERERUc/hn6CJiMxQqI8Lxg30hiAAG3+8KHYcIiKr0djYiFOnTmH8+PG6ZVKpFOPHj0dSUlK7r9m9ezdiYmIQFxcHHx8fhIeHY9WqVVCpVB2+T2VlJQDA3d293ecbGhpQVVXV6mGpnO1tcE8/TwDAgbNFrZ7bfuoyNv2UCwB474lIDPR1NXo+IiIiIkPgSFsiIjO14L4QJJ4vwfaTl3Fvfy80qtTwdpFjRJA7ZFKJ2PFaUakFJOeWo6S63iQzMl/3mXpG5us+c8hoDNeuXYNKpYKPj0+r5T4+Pjh//ny7r7l48SIOHTqEmTNnYt++fbhw4QJeeOEFNDU1YcWKFW3WV6vVePHFFzF69GiEh4e3u83Vq1dj5cqV3d8hMzExzAeHzpfgvylXEODuCG8XOWxlEvx1ZzoAYNED/TBliFLklEREREQ9h0VbIiIzdVegO4K9nHCxtBaxX6TolisVcqyYFobJ4abx4XX/mUKs3JOBwsp63TJTysh83WfqGZmv+8whoylTq9Xw9vbGp59+CplMhujoaFy5cgXvvPNOu0XbuLg4nDlzBseOHetwm8uWLcPixYt1X1dVVSEgIMAg+U2BtOUPBLnXahG/LU2zTAKoBWD8IB+8OD5UxHREREREPY/tEYiIzNT+M4W4WNp2gpqiynrEbk3B/jOFIqRqbf+ZQsRuTWlV6AFMJyPzdZ+pZ2S+7jOHjMbk6ekJmUyG4uLiVsuLi4vh6+vb7muUSiVCQ0Mhk8l0ywYNGoSioiI0Nja2WnfhwoX47rvvcPjwYfTu3bvDHPb29nB1dW31sFT7zxRiyfbTbZarW+YkmzrEV1fUJSIiIrIULNoSEZkhlVrAyj0Z7T6nnVd75Z4MqNRtZ9k2Fm3G9hKYQkbm6z5Tz8h83WcOGY3Nzs4O0dHRSExM1C1Tq9VITExETExMu68ZPXo0Lly4ALVarVuWlZUFpVIJOzvNpFmCIGDhwoXYuXMnDh06hKCgIMPuiJm43Tmo9XZCplWdg0RERGQd2B6BiMgMJeeWtxn1disBQGFlPZ7YkAR3kWbRLq9tNOmMzNd9pp6R+bpP34zJueWICfEwXjCRLV68GHPnzsXw4cMxYsQIfPDBB6itrcW8efMAAHPmzIG/vz9Wr14NAIiNjcXHH3+M+Ph4/OlPf0J2djZWrVqFRYsW6bYZFxeH//znP/j222/h4uKCoiLNhFsKhQIODg7G30kTcaffd4B1noNERERk+Vi0JSIyQyXVt/8Aq5WSf93ASbrP1DMyX/eZekbm6z59fyZZihkzZqC0tBSvvfYaioqKEBUVhf379+smJ8vPz4dUevOGtoCAACQkJOCll15CREQE/P39ER8fjyVLlujWWb9+PQDgvvvua/Ven3/+OZ555hmD75Op0vfcsrZzkIiIiCwfi7ZERGbI20Wu13rzxwQh2MvZwGnad7G0Bht/zL3jemJlZL7uM/WMzNd9+mbU92eSJVm4cCEWLlzY7nNHjhxpsywmJgbHjx/vcHuCwNv726PvuWWN5yARERFZNhZtiYjM0IggdygVchRV1rfb508CwFchx9IpgyATaXIWlVrAd6cLTTYj83WfqWdkvu7TN+OIIHdjRyMroe/vO56DREREZGk4ERkRkRmSSSVYMS0MgOYD6620X6+YFiZaoQcw/YzM132mnpH5us8cMpJl4zlIRERE1opFWyIiMzU5XIn1s4bBV9H6llBfhRzrZw3D5HClSMluMvWMzNd9pp6R+brPHDKSZeM5SERERNZIIlhZA62qqiooFApUVlbC1dVV7DhERN2mUgtIzi1HSXU9vF00t4ia2ogjU8/IfN1n6hmZr/vEyMjrNv1Zw7Eyh+8TIiIiojvR97qNRVsiIiIiMkm8btMfjxURERGRedD3uo3tEYiIiIiIiIiIiIhMCIu2RERERERERERERCaERVsiIiIiIiIiIiIiE8KiLREREREREREREZEJYdGWiIiIiIiIiIiIyISwaEtERERERERERERkQli0JSIiIiIiIiIiIjIhLNoSERERERERERERmRAWbYmIiIiIiIiIiIhMCIu2RERERERERERERCaERVsiIiIiIiIiIiIiE2IjdgBjEwQBAFBVVSVyEiIiIiK6He31mvb6jTrGa1wiIiIi86DvNa7VFW2rq6sBAAEBASInISIiIiJ9VFdXQ6FQiB3DpPEal4iIiMi83OkaVyJY2dAFtVqNq1evwsXF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9tFHH+mO34gRI4Tjx48baY8MpyeumX57rO70/WuObnec6urqhIkTJwpeXl6Cra2t0LdvX2H+/Pltiq88p+bq1vnkk08EBwcHoaKiot1tWPo5pc/1wI0bN4QXXnhB6NWrl+Do6Cg8+uijQmFhYZvt3PoasT4jSlrCEBEREREREREREZEJYE9bIiIiIiIiIiIiIhPCoi0RERERERERERGRCWHRloiIiIiIiIiIiMiEsGhLREREREREREREZEJYtCUiIiIiIiIiIiIyISzaEhEREREREREREZkQFm2JiIiIiIiIiIiITAiLtkREREREREREREQmhEVbIiIiIiIiIiIiIhPCoi0RUTc988wzeOSRR1otu3TpEuRyOSQSiTihiIiIiIiMrKmpCZs3b8Y999wDLy8vODg4ICIiAm+99RYaGxvFjkdEZFZsxA5ARGSJli9fzoItEREREVmVs2fP4t///jdeeuklDB06FPX19UhPT8f//d//ISEhAQkJCbC1tRU7JhGRWeBIWyKiHpaeno4vvvgCf/rTnwAAR44cgUQi6fChdezYMYwZMwYODg4ICAjAokWLUFtbq3s+MDAQr7/+Op5++mk4OTnB398f69ata/Xea9aswZAhQ+Dk5ISAgAC88MILqKmp0T2/efNmuLm5tXpNXl4eJBIJ0tLSdMskEgl27dql+/qzzz6DRCLBiy++qFtWWFiIxx57DB4eHq32p6KiousHj4iIiIjMVnh4OBITE/H4448jODgYYWFhmDFjBn744QecOXMGH3zwAQB0eF1867Xm9evXMWfOHPTq1QuOjo6YMmUKsrOzdc8/++yziIiIQENDAwCgsbERQ4cOxZw5c3TrLFmyBKGhoXB0dERwcDCWL1+OpqYmoxwLIqLuYtGWiKiHLV26FNOmTcOoUaMAAKNGjUJhYSEKCwvx3//+FwB0XxcWFgIAcnJyMHnyZDz++OM4ffo0vvrqKxw7dgwLFy5ste133nkHkZGRSE1NxdKlSxEfH4+DBw/qnpdKpVi7di3Onj2LLVu24NChQ/jLX/7Srf2pra3F8uXL4ezs3Gr5yy+/jKysLOzfv7/VvhERERGRdbKxaf9mXi8vLzz22GP44osvdMs+//zzVtfEMTExrV7zzDPP4OTJk9i9ezeSkpIgCAIefPBBXdF17dq1qK2txdKlSwEAr776KioqKvDxxx/rtuHi4oLNmzcjIyMDH374ITZu3Ij333+/p3ebiMgg2B6BiKgH/fDDD0hISEB6ejoyMzMBAHZ2dvD19QUAuLu7A4Dua63Vq1dj5syZutEF/fv3x9q1azF27FisX78ecrkcADB69GjdhWloaCh++uknvP/++5gwYQIAtBqdEBgYiDfeeAMLFizA//t//6/L+/T2228jLCwMzc3NrZanpaVh1qxZuOuuu1rtGxERERFZt8GDB+PSpUutljU1NUEmk+m+dnNza3VNbGdnp/v/7Oxs7N69Gz/99JNuIMQXX3yBgIAA7Nq1C0888QScnZ2xdetWjB07Fi4uLvjggw9w+PBhuLq66rbzt7/9Tff/gYGBeOWVV7Bt27ZuD2ogIjIGjrQlIupBS5cuxdy5czFo0KBOve7XX3/F5s2b4ezsrHtMmjQJarUaubm5uvV+OwIhJiYG586d0339/fffY9y4cfD394eLiwtmz56NsrIy1NXVdWl/rl69ijVr1uC9995r81xQUBD27duH8vLyLm2biIiIiCzTvn37kJaW1uoxd+5cvV9/7tw52NjYYOTIkbplHh4eGDBgQKtr35iYGLzyyit4/fXX8fLLL+Oee+5ptZ2vvvoKo0ePhq+vL5ydnfG3v/0N+fn53d9BIiIj4EhbIqIesnPnTqSmpuLrr7/u9Gtramrw/PPPY9GiRW2e69Onj17byMvLw0MPPYTY2Fj84x//gLu7O44dO4bnnnsOjY2NcHR07HSuV199FU888QQiIyPbPPf+++9j5syZ8PT0hKOjI1QqVae3T0RERESWp2/fvm2W5eTkIDQ0tEffR61W46effoJMJsOFCxdaPZeUlISZM2di5cqVmDRpEhQKBbZt29buYAQiIlPEoi0RUQ9QqVR49dVX8ac//Qm9e/fu9OuHDRuGjIwM9OvX77brHT9+vM3X2lG9p06dglqtxnvvvQepVHMjRVcKyFppaWnYvn27rs3Db4WGhuKZZ55BWVkZ9uzZo2uXQERERETWqby8HLa2tnBxcWm1/OTJkzh8+DDefPNNvbYzaNAgNDc345dfftG1RygrK0NmZibCwsJ0673zzjs4f/48jh49ikmTJuHzzz/HvHnzAAA///wz+vbti1dffVW3/m9bNhARmTK2RyAi6gHff/89CgsLsWzZsi69fsmSJfj555+xcOFCpKWlITs7G99++22bich++uknvP3228jKysK6devwzTffID4+HgDQr18/NDU14aOPPsLFixfx73//Gxs2bGj3/err63WPW2fcFQRBt867776LxYsXw8/Pr91tHD9+HH/961+xfft2DB48GP7+/l3adyIiIiKyDPn5+YiKisJnn32GCxcu6K5Jp0+fjjFjxrSaf+F2+vfvj+nTp2P+/Pk4duwYfv31V8yaNQv+/v6YPn06ACA1NRWvvfYa/vnPf2L06NFYs2YN4uPjcfHiRd028vPzsW3bNuTk5GDt2rXYuXOnoXadiKjHsWhLRNQD6uvrsWTJEvTq1atLr4+IiMDRo0eRlZWFMWPGYOjQoXjttdfaFExffvllnDx5EkOHDsUbb7yBNWvWYNKkSQCAyMhIrFmzBm+99RbCw8PxxRdfYPXq1W3eq7KyEg4ODrrHwIEDAQAjR45sNfrAxcWlw0kaSktL8cQTT2DNmjUYNmxYl/aZiIiIiCxLeHg4VqxYgc2bN+Puu+/G4MGD8fbbb2PhwoU4cOBAq8nG7uTzzz9HdHQ0HnroIcTExEAQBOzbtw+2traor6/HrFmz8Mwzz2DatGkAgD/+8Y+4//77MXv2bKhUKjz88MN46aWXsHDhQkRFReHnn3/G8uXLDbXrREQ9TiLcOqyKiIhMVmBgIF588UW9Ryh0VlRUFHbt2oXAwECDbJ+IiIiIiIiI9MORtkREBACwt7eHRCIROwYRERERERGR1eNEZEREBAD45ZdfxI5ARERERERERGB7BCIiIiIiIiIiIiKTwvYIRERERERERERERCaERVsiIiIiIiIiIiIiE8KiLREREREREREREZEJYdGWiIiIiIiIiIiIyISwaEtERERERERERERkQli0JSIiIiIiIiIiIjIhLNoSERERERERERERmRAWbYmIiIiIiIiIiIhMCIu2RERERERERERERCbk/wMksMgKoGFt5wAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1400x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Графики\n",
    "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
    "\n",
    "axes[0].plot(pso_history_df[\"iteration\"], pso_history_df[\"best_val_loss\"], marker=\"o\")\n",
    "axes[0].set_title(\"PSO: лучшая validation loss\")\n",
    "axes[0].set_xlabel(\"Итерация\")\n",
    "axes[0].set_ylabel(\"Loss\")\n",
    "\n",
    "axes[1].plot(finetune_history_df[\"epoch\"], finetune_history_df[\"val_f1_macro\"], marker=\"o\")\n",
    "axes[1].set_title(\"Дообучение: validation F1-macro\")\n",
    "axes[1].set_xlabel(\"Эпоха\")\n",
    "axes[1].set_ylabel(\"F1-macro\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "99ee1714",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== КЛЮЧЕВАЯ ИНФОРМАЦИЯ ДЛЯ ВЫВОДА ===\n",
      "Зависимая переменная: pha\n",
      "\n",
      "Число исходных признаков: 18\n",
      "Число признаков после кодирования: 32\n",
      "Архитектура скрытых слоёв: (64, 32, 16, 8)\n",
      "\n",
      "Распределение классов:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>share</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pha</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.997552</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.002448</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        share\n",
       "pha          \n",
       "0    0.997552\n",
       "1    0.002448"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Использованные исходные признаки:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>feature</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>a</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>e</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>i</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>om</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ad</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>per_y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>data_arc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>condition_code</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>n_obs_used</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>h</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>moid</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>n</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>per</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ma</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>neo</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>class</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           feature\n",
       "0                a\n",
       "1                e\n",
       "2                i\n",
       "3               om\n",
       "4                w\n",
       "5                q\n",
       "6               ad\n",
       "7            per_y\n",
       "8         data_arc\n",
       "9   condition_code\n",
       "10      n_obs_used\n",
       "11               h\n",
       "12            moid\n",
       "13               n\n",
       "14             per\n",
       "15              ma\n",
       "16             neo\n",
       "17           class"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Метрики моделей на тестовой выборке:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>model</th>\n",
       "      <th>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>precision_macro</th>\n",
       "      <th>recall_macro</th>\n",
       "      <th>f1_macro</th>\n",
       "      <th>roc_auc</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Глубокая нейросеть: PSO + дообучение</td>\n",
       "      <td>0.992348</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.619890</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.690758</td>\n",
       "      <td>0.998357</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Глубокая нейросеть после PSO</td>\n",
       "      <td>0.956825</td>\n",
       "      <td>0.888011</td>\n",
       "      <td>0.522168</td>\n",
       "      <td>0.888011</td>\n",
       "      <td>0.531422</td>\n",
       "      <td>0.971722</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  model  accuracy  balanced_accuracy  \\\n",
       "0  Глубокая нейросеть: PSO + дообучение  0.992348           0.986263   \n",
       "1          Глубокая нейросеть после PSO  0.956825           0.888011   \n",
       "\n",
       "   precision_macro  recall_macro  f1_macro   roc_auc  n_original_features  \\\n",
       "0         0.619890      0.986263  0.690758  0.998357                   18   \n",
       "1         0.522168      0.888011  0.531422  0.971722                   18   \n",
       "\n",
       "   n_processed_features    hidden_layers  \n",
       "0                    32  (64, 32, 16, 8)  \n",
       "1                    32  (64, 32, 16, 8)  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Лучшая модель:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>model</th>\n",
       "      <th>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>precision_macro</th>\n",
       "      <th>recall_macro</th>\n",
       "      <th>f1_macro</th>\n",
       "      <th>roc_auc</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Глубокая нейросеть: PSO + дообучение</td>\n",
       "      <td>0.992348</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.61989</td>\n",
       "      <td>0.986263</td>\n",
       "      <td>0.690758</td>\n",
       "      <td>0.998357</td>\n",
       "      <td>18</td>\n",
       "      <td>32</td>\n",
       "      <td>(64, 32, 16, 8)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  model  accuracy  balanced_accuracy  \\\n",
       "0  Глубокая нейросеть: PSO + дообучение  0.992348           0.986263   \n",
       "\n",
       "   precision_macro  recall_macro  f1_macro   roc_auc  n_original_features  \\\n",
       "0          0.61989      0.986263  0.690758  0.998357                   18   \n",
       "\n",
       "   n_processed_features    hidden_layers  \n",
       "0                    32  (64, 32, 16, 8)  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Последние итерации PSO:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>iteration</th>\n",
       "      <th>best_score</th>\n",
       "      <th>best_train_loss</th>\n",
       "      <th>best_val_loss</th>\n",
       "      <th>best_val_f1_macro</th>\n",
       "      <th>best_threshold</th>\n",
       "      <th>mean_particle_score</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>6</td>\n",
       "      <td>0.762534</td>\n",
       "      <td>1.343865</td>\n",
       "      <td>0.446860</td>\n",
       "      <td>0.542887</td>\n",
       "      <td>0.8</td>\n",
       "      <td>1.565401</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>7</td>\n",
       "      <td>0.673986</td>\n",
       "      <td>0.651703</td>\n",
       "      <td>0.554118</td>\n",
       "      <td>0.522642</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.835579</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>8</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.104777</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>9</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>5.841053</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>10</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.617099</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>11</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.075843</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>12</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>5.008521</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>13</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>9.494316</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>14</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>8.205984</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>15</td>\n",
       "      <td>0.453244</td>\n",
       "      <td>0.461247</td>\n",
       "      <td>0.325759</td>\n",
       "      <td>0.531936</td>\n",
       "      <td>0.8</td>\n",
       "      <td>2.066498</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    iteration  best_score  best_train_loss  best_val_loss  best_val_f1_macro  \\\n",
       "5           6    0.762534         1.343865       0.446860           0.542887   \n",
       "6           7    0.673986         0.651703       0.554118           0.522642   \n",
       "7           8    0.453244         0.461247       0.325759           0.531936   \n",
       "8           9    0.453244         0.461247       0.325759           0.531936   \n",
       "9          10    0.453244         0.461247       0.325759           0.531936   \n",
       "10         11    0.453244         0.461247       0.325759           0.531936   \n",
       "11         12    0.453244         0.461247       0.325759           0.531936   \n",
       "12         13    0.453244         0.461247       0.325759           0.531936   \n",
       "13         14    0.453244         0.461247       0.325759           0.531936   \n",
       "14         15    0.453244         0.461247       0.325759           0.531936   \n",
       "\n",
       "    best_threshold  mean_particle_score  \n",
       "5              0.8             1.565401  \n",
       "6              0.8             2.835579  \n",
       "7              0.8             2.104777  \n",
       "8              0.8             5.841053  \n",
       "9              0.8             2.617099  \n",
       "10             0.8             2.075843  \n",
       "11             0.8             5.008521  \n",
       "12             0.8             9.494316  \n",
       "13             0.8             8.205984  \n",
       "14             0.8             2.066498  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Последние эпохи дообучения:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>epoch</th>\n",
       "      <th>train_loss</th>\n",
       "      <th>val_accuracy</th>\n",
       "      <th>val_balanced_accuracy</th>\n",
       "      <th>val_f1_macro</th>\n",
       "      <th>val_roc_auc</th>\n",
       "      <th>threshold</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>11</td>\n",
       "      <td>0.059016</td>\n",
       "      <td>0.990088</td>\n",
       "      <td>0.986368</td>\n",
       "      <td>0.660870</td>\n",
       "      <td>0.997949</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>12</td>\n",
       "      <td>0.075604</td>\n",
       "      <td>0.989821</td>\n",
       "      <td>0.986235</td>\n",
       "      <td>0.657890</td>\n",
       "      <td>0.997978</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>13</td>\n",
       "      <td>0.056072</td>\n",
       "      <td>0.990750</td>\n",
       "      <td>0.985463</td>\n",
       "      <td>0.668445</td>\n",
       "      <td>0.998064</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>14</td>\n",
       "      <td>0.052836</td>\n",
       "      <td>0.991540</td>\n",
       "      <td>0.983383</td>\n",
       "      <td>0.678229</td>\n",
       "      <td>0.998077</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>15</td>\n",
       "      <td>0.061145</td>\n",
       "      <td>0.990398</td>\n",
       "      <td>0.985286</td>\n",
       "      <td>0.664178</td>\n",
       "      <td>0.998118</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>16</td>\n",
       "      <td>0.052293</td>\n",
       "      <td>0.988886</td>\n",
       "      <td>0.990716</td>\n",
       "      <td>0.649290</td>\n",
       "      <td>0.998138</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>17</td>\n",
       "      <td>0.052562</td>\n",
       "      <td>0.990750</td>\n",
       "      <td>0.987938</td>\n",
       "      <td>0.669013</td>\n",
       "      <td>0.998208</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>18</td>\n",
       "      <td>0.048724</td>\n",
       "      <td>0.991060</td>\n",
       "      <td>0.985618</td>\n",
       "      <td>0.672374</td>\n",
       "      <td>0.998246</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>19</td>\n",
       "      <td>0.050518</td>\n",
       "      <td>0.991995</td>\n",
       "      <td>0.982374</td>\n",
       "      <td>0.684475</td>\n",
       "      <td>0.998266</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>20</td>\n",
       "      <td>0.046750</td>\n",
       "      <td>0.990933</td>\n",
       "      <td>0.988029</td>\n",
       "      <td>0.671307</td>\n",
       "      <td>0.998385</td>\n",
       "      <td>0.8</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    epoch  train_loss  val_accuracy  val_balanced_accuracy  val_f1_macro  \\\n",
       "10     11    0.059016      0.990088               0.986368      0.660870   \n",
       "11     12    0.075604      0.989821               0.986235      0.657890   \n",
       "12     13    0.056072      0.990750               0.985463      0.668445   \n",
       "13     14    0.052836      0.991540               0.983383      0.678229   \n",
       "14     15    0.061145      0.990398               0.985286      0.664178   \n",
       "15     16    0.052293      0.988886               0.990716      0.649290   \n",
       "16     17    0.052562      0.990750               0.987938      0.669013   \n",
       "17     18    0.048724      0.991060               0.985618      0.672374   \n",
       "18     19    0.050518      0.991995               0.982374      0.684475   \n",
       "19     20    0.046750      0.990933               0.988029      0.671307   \n",
       "\n",
       "    val_roc_auc  threshold  \n",
       "10     0.997949        0.8  \n",
       "11     0.997978        0.8  \n",
       "12     0.998064        0.8  \n",
       "13     0.998077        0.8  \n",
       "14     0.998118        0.8  \n",
       "15     0.998138        0.8  \n",
       "16     0.998208        0.8  \n",
       "17     0.998246        0.8  \n",
       "18     0.998266        0.8  \n",
       "19     0.998385        0.8  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Порог для модели после PSO: 0.8\n",
      "Порог для финальной модели: 0.8\n",
      "Первые 10 фактических и предсказанных значений на тесте для финальной модели:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>y_true</th>\n",
       "      <th>y_pred_prob</th>\n",
       "      <th>y_pred_label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>9.032457e-23</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>4.243968e-24</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>2.671288e-27</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>2.780701e-34</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>5.431574e-25</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0</td>\n",
       "      <td>1.827862e-20</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>0</td>\n",
       "      <td>1.920311e-26</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>0</td>\n",
       "      <td>7.251859e-31</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>0</td>\n",
       "      <td>1.259633e-21</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>0</td>\n",
       "      <td>0.000000e+00</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   y_true   y_pred_prob  y_pred_label\n",
       "0       0  9.032457e-23             0\n",
       "1       0  4.243968e-24             0\n",
       "2       0  2.671288e-27             0\n",
       "3       0  2.780701e-34             0\n",
       "4       0  5.431574e-25             0\n",
       "5       0  1.827862e-20             0\n",
       "6       0  1.920311e-26             0\n",
       "7       0  7.251859e-31             0\n",
       "8       0  1.259633e-21             0\n",
       "9       0  0.000000e+00             0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Матрица ошибок для модели после PSO:\n",
      "[[157216   7036]\n",
      " [    73    330]]\n",
      "Матрица ошибок для финальной модели:\n",
      "[[163000   1252]\n",
      " [     8    395]]\n"
     ]
    }
   ],
   "source": [
    "# === КЛЮЧЕВАЯ ИНФОРМАЦИЯ ДЛЯ ВЫВОДА ===\n",
    "print(\"=== КЛЮЧЕВАЯ ИНФОРМАЦИЯ ДЛЯ ВЫВОДА ===\")\n",
    "print(f\"Зависимая переменная: {target_col}\")\n",
    "print()\n",
    "print(f\"Число исходных признаков: {X.shape[1]}\")\n",
    "print(f\"Число признаков после кодирования: {X_train_p.shape[1]}\")\n",
    "print(f\"Архитектура скрытых слоёв: {hidden_layers}\")\n",
    "print()\n",
    "print(\"Распределение классов:\")\n",
    "display(\n",
    "    y.value_counts(normalize=True)\n",
    "     .sort_index()\n",
    "     .rename(\"share\")\n",
    "     .to_frame()\n",
    ")\n",
    "print(\"Использованные исходные признаки:\")\n",
    "display(pd.DataFrame({\"feature\": X.columns.tolist()}))\n",
    "print(\"Метрики моделей на тестовой выборке:\")\n",
    "display(results_df)\n",
    "print(\"Лучшая модель:\")\n",
    "display(results_df.head(1))\n",
    "\n",
    "print(\"Последние итерации PSO:\")\n",
    "display(pso_history_df.tail(10))\n",
    "\n",
    "print(\"Последние эпохи дообучения:\")\n",
    "display(finetune_history_df.tail(10))\n",
    "\n",
    "print(\"Порог для модели после PSO:\", threshold_pso)\n",
    "print(\"Порог для финальной модели:\", best_threshold_ft)\n",
    "\n",
    "print(\"Первые 10 фактических и предсказанных значений на тесте для финальной модели:\")\n",
    "preview_df = pd.DataFrame({\n",
    "    \"y_true\": y_test.values[:10],\n",
    "    \"y_pred_prob\": test_prob_ft[:10],\n",
    "    \"y_pred_label\": test_pred_ft[:10]\n",
    "})\n",
    "display(preview_df)\n",
    "\n",
    "print(\"Матрица ошибок для модели после PSO:\")\n",
    "print(confusion_matrix(y_test.values, (test_prob_pso >= threshold_pso).astype(int)))\n",
    "\n",
    "print(\"Матрица ошибок для финальной модели:\")\n",
    "print(confusion_matrix(y_test.values, test_pred_ft))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bdee38e",
   "metadata": {},
   "source": [
    "Итог\n",
    "\n",
    "Зависимой переменной выбрана pha. Построена глубокая нейросеть с архитектурой (64, 32, 16, 8), сначала обученная методом PSO, затем дообученная градиентным методом.\n",
    "\n",
    "Лучшая модель — PSO + дообучение: на тестовой выборке получено accuracy = 0.9923, balanced accuracy = 0.9863, F1-macro = 0.6908, ROC-AUC = 0.9984. По сравнению с моделью только после PSO качество существенно улучшилось, особенно по F1-macro и balanced accuracy.\n",
    "\n",
    "Следовательно, дообучение после весовой эволюции существенно повышает качество классификации, и лучшей является финальная модель PSO + fine-tuning."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
