{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "15b7c8ff",
   "metadata": {},
   "source": [
    "# Регрессия по датасету thyroid cancer recurrence с эволюционным поиском архитектуры (ABC)\n",
    "\n",
    "В работе автоматически определяется ключевая зависимая переменная, выполняется предобработка данных и решается задача **регрессии** с помощью нейросети, архитектура которой подбирается методом **Artificial Bee Colony (ABC)**.\n",
    "\n",
    "Если целевая переменная бинарная или порядковая, она кодируется в числовую шкалу. Далее подбирается архитектура `MLPRegressor` по валидационному качеству, после чего лучшая конфигурация переобучается на объединённой выборке `train+val` и оценивается на тестовой выборке.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "700fbcb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "import os\n",
    "import re\n",
    "import glob\n",
    "import math\n",
    "import random\n",
    "import warnings\n",
    "from dataclasses import dataclass\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.metrics import (\n",
    "    mean_absolute_error,\n",
    "    mean_squared_error,\n",
    "    r2_score,\n",
    "    accuracy_score,\n",
    "    balanced_accuracy_score,\n",
    "    f1_score,\n",
    ")\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.neural_network import MLPRegressor\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "RANDOM_STATE = 42\n",
    "np.random.seed(RANDOM_STATE)\n",
    "random.seed(RANDOM_STATE)\n",
    "pd.set_option(\"display.max_columns\", 200)\n",
    "pd.set_option(\"display.width\", 140)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4c999ba4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Используемый файл: filtered_thyroid_data.csv\n",
      "Форма датасета: (383, 13)\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>Age</th>\n",
       "      <th>Gender</th>\n",
       "      <th>Hx Radiothreapy</th>\n",
       "      <th>Adenopathy</th>\n",
       "      <th>Pathology</th>\n",
       "      <th>Focality</th>\n",
       "      <th>Risk</th>\n",
       "      <th>T</th>\n",
       "      <th>N</th>\n",
       "      <th>M</th>\n",
       "      <th>Stage</th>\n",
       "      <th>Response</th>\n",
       "      <th>Recurred</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>27</td>\n",
       "      <td>F</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Micropapillary</td>\n",
       "      <td>Uni-Focal</td>\n",
       "      <td>Low</td>\n",
       "      <td>T1a</td>\n",
       "      <td>N0</td>\n",
       "      <td>M0</td>\n",
       "      <td>I</td>\n",
       "      <td>Indeterminate</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>34</td>\n",
       "      <td>F</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Micropapillary</td>\n",
       "      <td>Uni-Focal</td>\n",
       "      <td>Low</td>\n",
       "      <td>T1a</td>\n",
       "      <td>N0</td>\n",
       "      <td>M0</td>\n",
       "      <td>I</td>\n",
       "      <td>Excellent</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>30</td>\n",
       "      <td>F</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Micropapillary</td>\n",
       "      <td>Uni-Focal</td>\n",
       "      <td>Low</td>\n",
       "      <td>T1a</td>\n",
       "      <td>N0</td>\n",
       "      <td>M0</td>\n",
       "      <td>I</td>\n",
       "      <td>Excellent</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>62</td>\n",
       "      <td>F</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Micropapillary</td>\n",
       "      <td>Uni-Focal</td>\n",
       "      <td>Low</td>\n",
       "      <td>T1a</td>\n",
       "      <td>N0</td>\n",
       "      <td>M0</td>\n",
       "      <td>I</td>\n",
       "      <td>Excellent</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>62</td>\n",
       "      <td>F</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Micropapillary</td>\n",
       "      <td>Multi-Focal</td>\n",
       "      <td>Low</td>\n",
       "      <td>T1a</td>\n",
       "      <td>N0</td>\n",
       "      <td>M0</td>\n",
       "      <td>I</td>\n",
       "      <td>Excellent</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Age Gender Hx Radiothreapy Adenopathy       Pathology     Focality Risk    T   N   M Stage       Response Recurred\n",
       "0   27      F              No         No  Micropapillary    Uni-Focal  Low  T1a  N0  M0     I  Indeterminate       No\n",
       "1   34      F              No         No  Micropapillary    Uni-Focal  Low  T1a  N0  M0     I      Excellent       No\n",
       "2   30      F              No         No  Micropapillary    Uni-Focal  Low  T1a  N0  M0     I      Excellent       No\n",
       "3   62      F              No         No  Micropapillary    Uni-Focal  Low  T1a  N0  M0     I      Excellent       No\n",
       "4   62      F              No         No  Micropapillary  Multi-Focal  Low  T1a  N0  M0     I      Excellent       No"
      ]
     },
     "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>Age</th>\n",
       "      <td>int64</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Gender</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Hx Radiothreapy</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Adenopathy</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Pathology</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Focality</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Risk</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>T</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>N</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>M</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Stage</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Response</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Recurred</th>\n",
       "      <td>object</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  dtype\n",
       "Age               int64\n",
       "Gender           object\n",
       "Hx Radiothreapy  object\n",
       "Adenopathy       object\n",
       "Pathology        object\n",
       "Focality         object\n",
       "Risk             object\n",
       "T                object\n",
       "N                object\n",
       "M                object\n",
       "Stage            object\n",
       "Response         object\n",
       "Recurred         object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "def find_csv_file():\n",
    "    candidates = [\n",
    "        \"filtered_thyroid_data.csv\",\n",
    "        \"thyroid_cancer_recurrence_dataset.csv\",\n",
    "        \"thyroid*.csv\",\n",
    "        \"*thyroid*.csv\",\n",
    "        \"*.csv\",\n",
    "    ]\n",
    "    found = []\n",
    "    for pattern in candidates:\n",
    "        found.extend(glob.glob(pattern))\n",
    "    found = list(dict.fromkeys(found))\n",
    "    if not found:\n",
    "        raise FileNotFoundError(\"CSV-файл не найден. Поместите его рядом с ноутбуком.\")\n",
    "    preferred = [f for f in found if \"thyroid\" in f.lower()]\n",
    "    return preferred[0] if preferred else found[0]\n",
    "\n",
    "csv_path = find_csv_file()\n",
    "df_raw = pd.read_csv(csv_path)\n",
    "print(\"Используемый файл:\", csv_path)\n",
    "print(\"Форма датасета:\", df_raw.shape)\n",
    "display(df_raw.head())\n",
    "print(\"\\nТипы данных:\")\n",
    "display(df_raw.dtypes.to_frame(\"dtype\"))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0270a353",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Исходные названия колонок:\n",
      "['Age', 'Gender', 'Hx Radiothreapy', 'Adenopathy', 'Pathology', 'Focality', 'Risk', 'T', 'N', 'M', 'Stage', 'Response', 'Recurred']\n",
      "\n",
      "Нормализованные названия колонок:\n",
      "['age', 'gender', 'hx_radiothreapy', 'adenopathy', 'pathology', 'focality', 'risk', 't', 'n', 'm', 'stage', 'response', 'recurred']\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>original_name</th>\n",
       "      <th>normalized_name</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Age</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Gender</td>\n",
       "      <td>gender</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Hx Radiothreapy</td>\n",
       "      <td>hx_radiothreapy</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Adenopathy</td>\n",
       "      <td>adenopathy</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Pathology</td>\n",
       "      <td>pathology</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Focality</td>\n",
       "      <td>focality</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Risk</td>\n",
       "      <td>risk</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>T</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>N</td>\n",
       "      <td>n</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>M</td>\n",
       "      <td>m</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>Stage</td>\n",
       "      <td>stage</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>Response</td>\n",
       "      <td>response</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>Recurred</td>\n",
       "      <td>recurred</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      original_name  normalized_name\n",
       "0               Age              age\n",
       "1            Gender           gender\n",
       "2   Hx Radiothreapy  hx_radiothreapy\n",
       "3        Adenopathy       adenopathy\n",
       "4         Pathology        pathology\n",
       "5          Focality         focality\n",
       "6              Risk             risk\n",
       "7                 T                t\n",
       "8                 N                n\n",
       "9                 M                m\n",
       "10            Stage            stage\n",
       "11         Response         response\n",
       "12         Recurred         recurred"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "def normalize_name(col: str) -> str:\n",
    "    col = str(col).strip().lower()\n",
    "    col = col.replace(\"%\", \" pct \")\n",
    "    col = col.replace(\"&\", \" and \")\n",
    "    col = re.sub(r\"[^a-z0-9]+\", \"_\", col)\n",
    "    col = re.sub(r\"_+\", \"_\", col).strip(\"_\")\n",
    "    return col\n",
    "\n",
    "original_columns = df_raw.columns.tolist()\n",
    "normalized_columns = [normalize_name(c) for c in original_columns]\n",
    "rename_map = dict(zip(original_columns, normalized_columns))\n",
    "\n",
    "df = df_raw.rename(columns=rename_map).copy()\n",
    "\n",
    "print(\"Исходные названия колонок:\")\n",
    "print(original_columns)\n",
    "print(\"\\nНормализованные названия колонок:\")\n",
    "print(normalized_columns)\n",
    "display(pd.DataFrame({\"original_name\": original_columns, \"normalized_name\": normalized_columns}))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ee2b8299",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Выбранная зависимая переменная: recurred\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def detect_target_column(df: pd.DataFrame) -> str:\n",
    "    priority = [\n",
    "        \"recurred\",\n",
    "        \"recurrence\",\n",
    "        \"outcome\",\n",
    "        \"response\",\n",
    "        \"stage\",\n",
    "        \"risk\",\n",
    "        \"survival_months\",\n",
    "        \"price\",\n",
    "        \"amount\",\n",
    "        \"target\",\n",
    "        \"label\",\n",
    "        \"y\",\n",
    "    ]\n",
    "    cols = list(df.columns)\n",
    "    for p in priority:\n",
    "        if p in cols:\n",
    "            return p\n",
    "\n",
    "    # если есть немногочисленный категориальный столбец, похожий на итог\n",
    "    obj_cols = [c for c in cols if df[c].dtype == \"object\"]\n",
    "    for c in obj_cols:\n",
    "        nun = df[c].nunique(dropna=True)\n",
    "        if 2 <= nun <= 8:\n",
    "            return c\n",
    "\n",
    "    # иначе берём последний числовой столбец\n",
    "    num_cols = [c for c in cols if pd.api.types.is_numeric_dtype(df[c])]\n",
    "    if num_cols:\n",
    "        return num_cols[-1]\n",
    "    return cols[-1]\n",
    "\n",
    "target_col = detect_target_column(df)\n",
    "print(\"Выбранная зависимая переменная:\", target_col)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a0fff263",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найденные идентификаторы / несодержательные поля: []\n",
      "Число исходных признаков: 12\n",
      "Список признаков:\n",
      "['age', 'gender', 'hx_radiothreapy', 'adenopathy', 'pathology', 'focality', 'risk', 't', 'n', 'm', 'stage', 'response']\n"
     ]
    }
   ],
   "source": [
    "\n",
    "IDENTIFIER_PATTERNS = [\"id\", \"name\"]\n",
    "\n",
    "def is_identifier(col: str, s: pd.Series) -> bool:\n",
    "    col_l = col.lower()\n",
    "    if col == target_col:\n",
    "        return False\n",
    "    high_cardinality = s.nunique(dropna=True) >= 0.95 * len(s)\n",
    "    looks_like_id = any(p == col_l or col_l.endswith(\"_\" + p) or col_l.startswith(p + \"_\") for p in [\"id\"])\n",
    "    looks_like_name = \"name\" in col_l\n",
    "    return (looks_like_id or looks_like_name) and high_cardinality\n",
    "\n",
    "id_cols = [c for c in df.columns if is_identifier(c, df[c])]\n",
    "print(\"Найденные идентификаторы / несодержательные поля:\", id_cols)\n",
    "\n",
    "feature_cols = [c for c in df.columns if c not in id_cols + [target_col]]\n",
    "X = df[feature_cols].copy()\n",
    "y_raw = df[target_col].copy()\n",
    "\n",
    "print(\"Число исходных признаков:\", len(feature_cols))\n",
    "print(\"Список признаков:\")\n",
    "print(feature_cols)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9981320f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Способ кодирования цели: binary\n",
      "Число наблюдений после удаления пропусков в цели: 383\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>count</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>recurred</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0.0</th>\n",
       "      <td>275</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1.0</th>\n",
       "      <td>108</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          count\n",
       "recurred       \n",
       "0.0         275\n",
       "1.0         108"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "def make_numeric_target(series: pd.Series, column_name: str):\n",
    "    s = series.copy()\n",
    "\n",
    "    # очистка строковых пропусков\n",
    "    if s.dtype == \"object\":\n",
    "        s = s.astype(str).str.strip()\n",
    "        s = s.replace({\n",
    "            \"\": np.nan, \"nan\": np.nan, \"none\": np.nan, \"null\": np.nan, \"na\": np.nan,\n",
    "            \"n/a\": np.nan, \"unknown\": np.nan\n",
    "        })\n",
    "\n",
    "    # если уже числовая\n",
    "    if pd.api.types.is_numeric_dtype(s):\n",
    "        return pd.to_numeric(s, errors=\"coerce\"), {\"encoding\": \"numeric\", \"mapping\": None}\n",
    "\n",
    "    # бинарное кодирование\n",
    "    s_lower = s.astype(str).str.strip().str.lower()\n",
    "    binary_map = {\n",
    "        \"yes\": 1, \"y\": 1, \"true\": 1, \"1\": 1, \"positive\": 1, \"recurred\": 1, \"present\": 1,\n",
    "        \"no\": 0, \"n\": 0, \"false\": 0, \"0\": 0, \"negative\": 0, \"not recurred\": 0, \"absent\": 0\n",
    "    }\n",
    "    uniq_lower = sorted(pd.Series(s_lower.dropna().unique()).tolist())\n",
    "    if len(uniq_lower) == 2 and all(v in binary_map for v in uniq_lower):\n",
    "        y_num = s_lower.map(binary_map).astype(float)\n",
    "        mapping = {k: binary_map[k] for k in uniq_lower}\n",
    "        return y_num, {\"encoding\": \"binary\", \"mapping\": mapping}\n",
    "\n",
    "    # специальные порядковые шкалы\n",
    "    stage_order = {\n",
    "        \"i\": 1, \"ii\": 2, \"iii\": 3, \"iva\": 4, \"ivb\": 5, \"ivc\": 6,\n",
    "        \"1\": 1, \"2\": 2, \"3\": 3, \"4a\": 4, \"4b\": 5, \"4c\": 6,\n",
    "    }\n",
    "    risk_order = {\"low\": 1, \"intermediate\": 2, \"high\": 3}\n",
    "    response_order = {\n",
    "        \"excellent\": 1,\n",
    "        \"indeterminate\": 2,\n",
    "        \"biochemical incomplete\": 3,\n",
    "        \"structural incomplete\": 4,\n",
    "    }\n",
    "    tnm_order = {\n",
    "        \"t1\": 1, \"t1a\": 1.1, \"t1b\": 1.2, \"t2\": 2, \"t3\": 3, \"t3a\": 3.1, \"t3b\": 3.2, \"t4\": 4, \"t4a\": 4.1, \"t4b\": 4.2,\n",
    "        \"n0\": 0, \"n1\": 1, \"n1a\": 1.1, \"n1b\": 1.2,\n",
    "        \"m0\": 0, \"m1\": 1,\n",
    "    }\n",
    "\n",
    "    maps_to_try = []\n",
    "    if column_name == \"stage\":\n",
    "        maps_to_try.append(stage_order)\n",
    "    if column_name == \"risk\":\n",
    "        maps_to_try.append(risk_order)\n",
    "    if column_name == \"response\":\n",
    "        maps_to_try.append(response_order)\n",
    "    if column_name in {\"t\", \"n\", \"m\"}:\n",
    "        maps_to_try.append(tnm_order)\n",
    "\n",
    "    for mp in maps_to_try:\n",
    "        lowered = s.astype(str).str.strip().str.lower()\n",
    "        if lowered.dropna().isin(mp.keys()).all():\n",
    "            return lowered.map(mp).astype(float), {\"encoding\": \"ordered_map\", \"mapping\": mp}\n",
    "\n",
    "    # универсальное порядковое кодирование по алфавиту\n",
    "    vals = sorted(pd.Series(s.dropna().unique()).astype(str).tolist())\n",
    "    mp = {v: i for i, v in enumerate(vals)}\n",
    "    return s.astype(str).map(mp).astype(float), {\"encoding\": \"ordinal_fallback\", \"mapping\": mp}\n",
    "\n",
    "y, target_info = make_numeric_target(y_raw, target_col)\n",
    "\n",
    "mask = ~y.isna()\n",
    "X = X.loc[mask].reset_index(drop=True)\n",
    "y = y.loc[mask].reset_index(drop=True)\n",
    "y_raw_clean = y_raw.loc[mask].reset_index(drop=True)\n",
    "\n",
    "print(\"Способ кодирования цели:\", target_info[\"encoding\"])\n",
    "print(\"Число наблюдений после удаления пропусков в цели:\", len(y))\n",
    "print(\"Распределение значений цели (после кодирования):\")\n",
    "display(y.value_counts(dropna=False).sort_index().to_frame(\"count\"))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "44767b77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Числовые признаки: ['age']\n",
      "Категориальные признаки: ['gender', 'hx_radiothreapy', 'adenopathy', 'pathology', 'focality', 'risk', 't', 'n', 'm', 'stage', 'response']\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>age</th>\n",
       "      <th>gender</th>\n",
       "      <th>hx_radiothreapy</th>\n",
       "      <th>adenopathy</th>\n",
       "      <th>pathology</th>\n",
       "      <th>focality</th>\n",
       "      <th>risk</th>\n",
       "      <th>t</th>\n",
       "      <th>n</th>\n",
       "      <th>m</th>\n",
       "      <th>stage</th>\n",
       "      <th>response</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>missing_count</th>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               age  gender  hx_radiothreapy  adenopathy  pathology  focality  risk  t  n  m  stage  response\n",
       "missing_count    0       0                0           0          0         0     0  0  0  0      0         0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\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",
    "print(\"Числовые признаки:\", numeric_features)\n",
    "print(\"Категориальные признаки:\", categorical_features)\n",
    "\n",
    "print(\"\\nЧисло пропусков до обработки:\")\n",
    "display(X.isna().sum().to_frame(\"missing_count\").T)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "aa62d082",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Размер train: (229, 12)\n",
      "Размер val: (77, 12)\n",
      "Размер test: (77, 12)\n",
      "Число признаков после кодирования: 41\n",
      "Первые 20 преобразованных признаков:\n",
      "['num__age', 'cat__gender_F', 'cat__gender_M', 'cat__hx_radiothreapy_No', 'cat__hx_radiothreapy_Yes', 'cat__adenopathy_Bilateral', 'cat__adenopathy_Extensive', 'cat__adenopathy_Left', 'cat__adenopathy_No', 'cat__adenopathy_Posterior', 'cat__adenopathy_Right', 'cat__pathology_Follicular', 'cat__pathology_Hurthel cell', 'cat__pathology_Micropapillary', 'cat__pathology_Papillary', 'cat__focality_Multi-Focal', 'cat__focality_Uni-Focal', 'cat__risk_High', 'cat__risk_Intermediate', 'cat__risk_Low']\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def make_ohe():\n",
    "    try:\n",
    "        return OneHotEncoder(handle_unknown=\"ignore\", sparse_output=False)\n",
    "    except TypeError:\n",
    "        return OneHotEncoder(handle_unknown=\"ignore\", sparse=False)\n",
    "\n",
    "preprocessor = ColumnTransformer(\n",
    "    transformers=[\n",
    "        (\"num\", Pipeline([\n",
    "            (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
    "            (\"scaler\", StandardScaler()),\n",
    "        ]), numeric_features),\n",
    "        (\"cat\", Pipeline([\n",
    "            (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
    "            (\"ohe\", make_ohe()),\n",
    "        ]), categorical_features),\n",
    "    ],\n",
    "    remainder=\"drop\",\n",
    ")\n",
    "\n",
    "# стратификация для дискретных целей\n",
    "def get_stratify_vector(y_series: pd.Series):\n",
    "    uniq = y_series.dropna().nunique()\n",
    "    if uniq <= 10 and np.allclose(y_series.dropna(), np.round(y_series.dropna())):\n",
    "        return y_series\n",
    "    return None\n",
    "\n",
    "stratify_vec = get_stratify_vector(y)\n",
    "\n",
    "X_trainval, X_test, y_trainval, y_test, yraw_trainval, yraw_test = train_test_split(\n",
    "    X, y, y_raw_clean, test_size=0.2, random_state=RANDOM_STATE, stratify=stratify_vec\n",
    ")\n",
    "stratify_trainval = get_stratify_vector(y_trainval)\n",
    "\n",
    "X_train, X_val, y_train, y_val, yraw_train, yraw_val = train_test_split(\n",
    "    X_trainval, y_trainval, yraw_trainval, test_size=0.25, random_state=RANDOM_STATE, stratify=stratify_trainval\n",
    ")\n",
    "\n",
    "X_train_proc = preprocessor.fit_transform(X_train)\n",
    "X_val_proc = preprocessor.transform(X_val)\n",
    "X_trainval_proc = preprocessor.fit_transform(X_trainval)\n",
    "X_test_proc = preprocessor.transform(X_test)\n",
    "\n",
    "try:\n",
    "    feature_names_processed = preprocessor.get_feature_names_out().tolist()\n",
    "except Exception:\n",
    "    feature_names_processed = [f\"feature_{i}\" for i in range(X_train_proc.shape[1])]\n",
    "\n",
    "target_scaler = StandardScaler()\n",
    "y_train_scaled = target_scaler.fit_transform(np.asarray(y_train).reshape(-1, 1)).ravel()\n",
    "y_val_scaled = target_scaler.transform(np.asarray(y_val).reshape(-1, 1)).ravel()\n",
    "y_trainval_scaled = target_scaler.fit_transform(np.asarray(y_trainval).reshape(-1, 1)).ravel()\n",
    "\n",
    "print(\"Размер train:\", X_train.shape)\n",
    "print(\"Размер val:\", X_val.shape)\n",
    "print(\"Размер test:\", X_test.shape)\n",
    "print(\"Число признаков после кодирования:\", X_train_proc.shape[1])\n",
    "print(\"Первые 20 преобразованных признаков:\")\n",
    "print(feature_names_processed[:20])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "be72c598",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def rmse(y_true, y_pred):\n",
    "    return float(np.sqrt(mean_squared_error(y_true, y_pred)))\n",
    "\n",
    "def evaluate_regression(y_true, y_pred):\n",
    "    return {\n",
    "        \"r2\": float(r2_score(y_true, y_pred)),\n",
    "        \"rmse\": rmse(y_true, y_pred),\n",
    "        \"mae\": float(mean_absolute_error(y_true, y_pred)),\n",
    "    }\n",
    "\n",
    "IS_BINARY_TARGET = y.nunique() == 2 and set(sorted(y.unique())) <= {0.0, 1.0}\n",
    "\n",
    "def evaluate_optional_binary(y_true, y_pred):\n",
    "    if not IS_BINARY_TARGET:\n",
    "        return None\n",
    "    y_pred_clip = np.clip(np.asarray(y_pred), 0, 1)\n",
    "    y_pred_label = (y_pred_clip >= 0.5).astype(int)\n",
    "    return {\n",
    "        \"accuracy\": float(accuracy_score(y_true, y_pred_label)),\n",
    "        \"balanced_accuracy\": float(balanced_accuracy_score(y_true, y_pred_label)),\n",
    "        \"f1\": float(f1_score(y_true, y_pred_label, zero_division=0)),\n",
    "    }\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b81c2f92",
   "metadata": {},
   "source": [
    "## Поиск архитектуры методом ABC\n",
    "\n",
    "Поиск выполняется по архитектуре скрытых слоёв, функции активации и регуляризационным параметрам `MLPRegressor`. Критерий качества — минимизация `RMSE` на валидационной выборке.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "63f3594a",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from copy import deepcopy\n",
    "\n",
    "LAYER_CANDIDATES = [16, 32, 48, 64, 80, 96, 112, 128]\n",
    "ACTIVATION_CANDIDATES = [\"relu\", \"tanh\"]\n",
    "BATCH_CANDIDATES = [16, 32, 64]\n",
    "\n",
    "def random_solution():\n",
    "    n_layers = np.random.choice([1, 2, 3, 4], p=[0.25, 0.35, 0.25, 0.15])\n",
    "    units = tuple(int(np.random.choice(LAYER_CANDIDATES)) for _ in range(n_layers))\n",
    "    return {\n",
    "        \"hidden_layers\": units,\n",
    "        \"activation\": str(np.random.choice(ACTIVATION_CANDIDATES)),\n",
    "        \"alpha\": float(10 ** np.random.uniform(-6, -2)),\n",
    "        \"learning_rate_init\": float(10 ** np.random.uniform(-4, -1.3)),\n",
    "        \"batch_size\": int(np.random.choice(BATCH_CANDIDATES)),\n",
    "    }\n",
    "\n",
    "def mutate_solution(sol, other):\n",
    "    new_sol = deepcopy(sol)\n",
    "    choice = np.random.choice([\"layers\", \"activation\", \"alpha\", \"lr\", \"batch\"])\n",
    "    if choice == \"layers\":\n",
    "        if np.random.rand() < 0.5:\n",
    "            new_sol[\"hidden_layers\"] = other[\"hidden_layers\"]\n",
    "        else:\n",
    "            n_layers = np.random.choice([1, 2, 3, 4])\n",
    "            new_sol[\"hidden_layers\"] = tuple(int(np.random.choice(LAYER_CANDIDATES)) for _ in range(n_layers))\n",
    "    elif choice == \"activation\":\n",
    "        new_sol[\"activation\"] = other[\"activation\"] if np.random.rand() < 0.5 else str(np.random.choice(ACTIVATION_CANDIDATES))\n",
    "    elif choice == \"alpha\":\n",
    "        val = math.log10(sol[\"alpha\"]) + np.random.uniform(-0.7, 0.7)\n",
    "        new_sol[\"alpha\"] = float(np.clip(10 ** val, 1e-6, 1e-2))\n",
    "    elif choice == \"lr\":\n",
    "        val = math.log10(sol[\"learning_rate_init\"]) + np.random.uniform(-0.7, 0.7)\n",
    "        new_sol[\"learning_rate_init\"] = float(np.clip(10 ** val, 1e-4, 5e-2))\n",
    "    elif choice == \"batch\":\n",
    "        new_sol[\"batch_size\"] = int(np.random.choice(BATCH_CANDIDATES))\n",
    "    return new_sol\n",
    "\n",
    "def train_eval_solution(sol, X_tr, y_tr_scaled, X_va, y_va, scaler):\n",
    "    mlp = MLPRegressor(\n",
    "        hidden_layer_sizes=sol[\"hidden_layers\"],\n",
    "        activation=sol[\"activation\"],\n",
    "        alpha=sol[\"alpha\"],\n",
    "        learning_rate_init=sol[\"learning_rate_init\"],\n",
    "        batch_size=sol[\"batch_size\"],\n",
    "        solver=\"adam\",\n",
    "        max_iter=220,\n",
    "        early_stopping=False,\n",
    "        random_state=RANDOM_STATE,\n",
    "    )\n",
    "    mlp.fit(X_tr, y_tr_scaled)\n",
    "    pred_scaled = mlp.predict(X_va)\n",
    "    pred = scaler.inverse_transform(pred_scaled.reshape(-1, 1)).ravel()\n",
    "    metrics = evaluate_regression(y_va, pred)\n",
    "    return mlp, metrics\n",
    "\n",
    "# чтобы поиск был устойчивее, ограничиваем train-подвыборку при необходимости\n",
    "MAX_SEARCH_ROWS = 4000\n",
    "if X_train_proc.shape[0] > MAX_SEARCH_ROWS:\n",
    "    idx = np.random.RandomState(RANDOM_STATE).choice(X_train_proc.shape[0], size=MAX_SEARCH_ROWS, replace=False)\n",
    "    X_search = X_train_proc[idx]\n",
    "    y_search = y_train_scaled[idx]\n",
    "else:\n",
    "    X_search = X_train_proc\n",
    "    y_search = y_train_scaled\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d659c1aa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Лучшая архитектура, найденная ABC: (80, 80, 48)\n",
      "Функция активации: relu\n",
      "alpha: 0.0014014686515505014\n",
      "learning_rate_init: 0.005741282065702722\n",
      "batch_size: 32\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>hidden_layers</th>\n",
       "      <th>activation</th>\n",
       "      <th>alpha</th>\n",
       "      <th>learning_rate_init</th>\n",
       "      <th>batch_size</th>\n",
       "      <th>rmse_val</th>\n",
       "      <th>mae_val</th>\n",
       "      <th>r2_val</th>\n",
       "      <th>cycle</th>\n",
       "      <th>phase</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>4</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>6</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>1</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>(80, 64, 32, 96)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.106760</td>\n",
       "      <td>0.046627</td>\n",
       "      <td>0.944151</td>\n",
       "      <td>3</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.003968</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.108038</td>\n",
       "      <td>0.052577</td>\n",
       "      <td>0.942807</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001315</td>\n",
       "      <td>0.004088</td>\n",
       "      <td>64</td>\n",
       "      <td>0.109601</td>\n",
       "      <td>0.049250</td>\n",
       "      <td>0.941139</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.109714</td>\n",
       "      <td>0.052963</td>\n",
       "      <td>0.941018</td>\n",
       "      <td>10</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>6</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>6</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         hidden_layers activation     alpha  learning_rate_init  batch_size  rmse_val   mae_val    r2_val  cycle     phase\n",
       "0         (32, 96, 80)       relu  0.006245            0.009344          32  0.103351  0.041218  0.947661      4  employed\n",
       "1         (32, 96, 80)       relu  0.006245            0.009344          32  0.103351  0.041218  0.947661      6  employed\n",
       "2  (128, 128, 112, 48)       relu  0.006990            0.004198          64  0.106539  0.046265  0.944382      1  onlooker\n",
       "3  (128, 128, 112, 48)       relu  0.006990            0.004198          64  0.106539  0.046265  0.944382      2  onlooker\n",
       "4     (80, 64, 32, 96)       relu  0.006245            0.009344          32  0.106760  0.046627  0.944151      3  employed\n",
       "5  (128, 128, 112, 48)       relu  0.003968            0.004198          64  0.108038  0.052577  0.942807      2  onlooker\n",
       "6  (128, 128, 112, 48)       relu  0.001315            0.004088          64  0.109601  0.049250  0.941139      2  onlooker\n",
       "7         (80, 80, 48)       relu  0.001401            0.005741          32  0.109714  0.052963  0.941018     10  employed\n",
       "8   (112, 80, 32, 112)       relu  0.000341            0.000823          32  0.115037  0.066162  0.935155      6  employed\n",
       "9   (112, 80, 32, 112)       relu  0.000341            0.000823          32  0.115037  0.066162  0.935155      6  onlooker"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>cycle</th>\n",
       "      <th>best_hidden_layers</th>\n",
       "      <th>best_activation</th>\n",
       "      <th>best_alpha</th>\n",
       "      <th>best_learning_rate_init</th>\n",
       "      <th>best_batch_size</th>\n",
       "      <th>best_val_rmse</th>\n",
       "      <th>best_val_mae</th>\n",
       "      <th>best_val_r2</th>\n",
       "      <th>mean_val_rmse</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>0.146345</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001315</td>\n",
       "      <td>0.004088</td>\n",
       "      <td>64</td>\n",
       "      <td>0.109601</td>\n",
       "      <td>0.049250</td>\n",
       "      <td>0.941139</td>\n",
       "      <td>0.147185</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>(80, 64, 32, 96)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.106760</td>\n",
       "      <td>0.046627</td>\n",
       "      <td>0.944151</td>\n",
       "      <td>0.142272</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>0.138498</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>0.143432</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>6</td>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>0.142296</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>7</td>\n",
       "      <td>(48, 80, 64)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001913</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.132492</td>\n",
       "      <td>0.067692</td>\n",
       "      <td>0.913985</td>\n",
       "      <td>0.156776</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>8</td>\n",
       "      <td>(48, 80, 64)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001913</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.132492</td>\n",
       "      <td>0.067692</td>\n",
       "      <td>0.913985</td>\n",
       "      <td>0.153727</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>9</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>tanh</td>\n",
       "      <td>0.000021</td>\n",
       "      <td>0.001842</td>\n",
       "      <td>32</td>\n",
       "      <td>0.123614</td>\n",
       "      <td>0.076008</td>\n",
       "      <td>0.925126</td>\n",
       "      <td>0.147158</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>10</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.109714</td>\n",
       "      <td>0.052963</td>\n",
       "      <td>0.941018</td>\n",
       "      <td>0.144835</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   cycle   best_hidden_layers best_activation  best_alpha  best_learning_rate_init  best_batch_size  best_val_rmse  best_val_mae  \\\n",
       "0      1  (128, 128, 112, 48)            relu    0.006990                 0.004198               64       0.106539      0.046265   \n",
       "1      2  (128, 128, 112, 48)            relu    0.001315                 0.004088               64       0.109601      0.049250   \n",
       "2      3     (80, 64, 32, 96)            relu    0.006245                 0.009344               32       0.106760      0.046627   \n",
       "3      4         (32, 96, 80)            relu    0.006245                 0.009344               32       0.103351      0.041218   \n",
       "4      5         (32, 96, 80)            relu    0.006245                 0.009344               32       0.103351      0.041218   \n",
       "5      6   (112, 80, 32, 112)            relu    0.000341                 0.000823               32       0.115037      0.066162   \n",
       "6      7         (48, 80, 64)            relu    0.001913                 0.005741               32       0.132492      0.067692   \n",
       "7      8         (48, 80, 64)            relu    0.001913                 0.005741               32       0.132492      0.067692   \n",
       "8      9         (80, 80, 48)            tanh    0.000021                 0.001842               32       0.123614      0.076008   \n",
       "9     10         (80, 80, 48)            relu    0.001401                 0.005741               32       0.109714      0.052963   \n",
       "\n",
       "   best_val_r2  mean_val_rmse  \n",
       "0     0.944382       0.146345  \n",
       "1     0.941139       0.147185  \n",
       "2     0.944151       0.142272  \n",
       "3     0.947661       0.138498  \n",
       "4     0.947661       0.143432  \n",
       "5     0.935155       0.142296  \n",
       "6     0.913985       0.156776  \n",
       "7     0.913985       0.153727  \n",
       "8     0.925126       0.147158  \n",
       "9     0.941018       0.144835  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "N_SOURCES = 8\n",
    "N_CYCLES = 10\n",
    "TRIAL_LIMIT = 4\n",
    "\n",
    "sources = []\n",
    "for _ in range(N_SOURCES):\n",
    "    sol = random_solution()\n",
    "    _, met = train_eval_solution(sol, X_search, y_search, X_val_proc, y_val, target_scaler)\n",
    "    sources.append({\n",
    "        \"solution\": sol,\n",
    "        \"metrics\": met,\n",
    "        \"score\": met[\"rmse\"],\n",
    "        \"trials\": 0,\n",
    "    })\n",
    "\n",
    "abc_history = []\n",
    "all_candidates = []\n",
    "\n",
    "for cycle in range(1, N_CYCLES + 1):\n",
    "    # employed bees\n",
    "    for i in range(N_SOURCES):\n",
    "        j = np.random.choice([k for k in range(N_SOURCES) if k != i])\n",
    "        cand = mutate_solution(sources[i][\"solution\"], sources[j][\"solution\"])\n",
    "        _, met = train_eval_solution(cand, X_search, y_search, X_val_proc, y_val, target_scaler)\n",
    "        cand_score = met[\"rmse\"]\n",
    "        all_candidates.append({\n",
    "            \"hidden_layers\": cand[\"hidden_layers\"],\n",
    "            \"activation\": cand[\"activation\"],\n",
    "            \"alpha\": cand[\"alpha\"],\n",
    "            \"learning_rate_init\": cand[\"learning_rate_init\"],\n",
    "            \"batch_size\": cand[\"batch_size\"],\n",
    "            \"rmse_val\": met[\"rmse\"],\n",
    "            \"mae_val\": met[\"mae\"],\n",
    "            \"r2_val\": met[\"r2\"],\n",
    "            \"cycle\": cycle,\n",
    "            \"phase\": \"employed\",\n",
    "        })\n",
    "        if cand_score < sources[i][\"score\"]:\n",
    "            sources[i] = {\"solution\": cand, \"metrics\": met, \"score\": cand_score, \"trials\": 0}\n",
    "        else:\n",
    "            sources[i][\"trials\"] += 1\n",
    "\n",
    "    # onlooker bees\n",
    "    scores = np.array([s[\"score\"] for s in sources], dtype=float)\n",
    "    fitness = 1 / (scores + 1e-8)\n",
    "    probs = fitness / fitness.sum()\n",
    "\n",
    "    for _ in range(N_SOURCES):\n",
    "        i = np.random.choice(np.arange(N_SOURCES), p=probs)\n",
    "        j = np.random.choice([k for k in range(N_SOURCES) if k != i])\n",
    "        cand = mutate_solution(sources[i][\"solution\"], sources[j][\"solution\"])\n",
    "        _, met = train_eval_solution(cand, X_search, y_search, X_val_proc, y_val, target_scaler)\n",
    "        cand_score = met[\"rmse\"]\n",
    "        all_candidates.append({\n",
    "            \"hidden_layers\": cand[\"hidden_layers\"],\n",
    "            \"activation\": cand[\"activation\"],\n",
    "            \"alpha\": cand[\"alpha\"],\n",
    "            \"learning_rate_init\": cand[\"learning_rate_init\"],\n",
    "            \"batch_size\": cand[\"batch_size\"],\n",
    "            \"rmse_val\": met[\"rmse\"],\n",
    "            \"mae_val\": met[\"mae\"],\n",
    "            \"r2_val\": met[\"r2\"],\n",
    "            \"cycle\": cycle,\n",
    "            \"phase\": \"onlooker\",\n",
    "        })\n",
    "        if cand_score < sources[i][\"score\"]:\n",
    "            sources[i] = {\"solution\": cand, \"metrics\": met, \"score\": cand_score, \"trials\": 0}\n",
    "        else:\n",
    "            sources[i][\"trials\"] += 1\n",
    "\n",
    "    # scout bees\n",
    "    for i in range(N_SOURCES):\n",
    "        if sources[i][\"trials\"] >= TRIAL_LIMIT:\n",
    "            sol = random_solution()\n",
    "            _, met = train_eval_solution(sol, X_search, y_search, X_val_proc, y_val, target_scaler)\n",
    "            sources[i] = {\"solution\": sol, \"metrics\": met, \"score\": met[\"rmse\"], \"trials\": 0}\n",
    "            all_candidates.append({\n",
    "                \"hidden_layers\": sol[\"hidden_layers\"],\n",
    "                \"activation\": sol[\"activation\"],\n",
    "                \"alpha\": sol[\"alpha\"],\n",
    "                \"learning_rate_init\": sol[\"learning_rate_init\"],\n",
    "                \"batch_size\": sol[\"batch_size\"],\n",
    "                \"rmse_val\": met[\"rmse\"],\n",
    "                \"mae_val\": met[\"mae\"],\n",
    "                \"r2_val\": met[\"r2\"],\n",
    "                \"cycle\": cycle,\n",
    "                \"phase\": \"scout\",\n",
    "            })\n",
    "\n",
    "    best_idx = int(np.argmin([s[\"score\"] for s in sources]))\n",
    "    best_src = sources[best_idx]\n",
    "    abc_history.append({\n",
    "        \"cycle\": cycle,\n",
    "        \"best_hidden_layers\": best_src[\"solution\"][\"hidden_layers\"],\n",
    "        \"best_activation\": best_src[\"solution\"][\"activation\"],\n",
    "        \"best_alpha\": best_src[\"solution\"][\"alpha\"],\n",
    "        \"best_learning_rate_init\": best_src[\"solution\"][\"learning_rate_init\"],\n",
    "        \"best_batch_size\": best_src[\"solution\"][\"batch_size\"],\n",
    "        \"best_val_rmse\": best_src[\"metrics\"][\"rmse\"],\n",
    "        \"best_val_mae\": best_src[\"metrics\"][\"mae\"],\n",
    "        \"best_val_r2\": best_src[\"metrics\"][\"r2\"],\n",
    "        \"mean_val_rmse\": float(np.mean([s[\"metrics\"][\"rmse\"] for s in sources])),\n",
    "    })\n",
    "\n",
    "abc_history_df = pd.DataFrame(abc_history)\n",
    "all_candidates_df = pd.DataFrame(all_candidates).sort_values([\"rmse_val\", \"mae_val\", \"r2_val\"], ascending=[True, True, False]).reset_index(drop=True)\n",
    "best_solution = min(sources, key=lambda s: s[\"score\"])[\"solution\"]\n",
    "\n",
    "print(\"Лучшая архитектура, найденная ABC:\", best_solution[\"hidden_layers\"])\n",
    "print(\"Функция активации:\", best_solution[\"activation\"])\n",
    "print(\"alpha:\", best_solution[\"alpha\"])\n",
    "print(\"learning_rate_init:\", best_solution[\"learning_rate_init\"])\n",
    "print(\"batch_size:\", best_solution[\"batch_size\"])\n",
    "\n",
    "display(all_candidates_df.head(10))\n",
    "display(abc_history_df.tail(10))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75b59416",
   "metadata": {},
   "source": [
    "## Обучение финальной модели на `train+val`\n",
    "\n",
    "Лучшая конфигурация по результатам ABC дообучается на объединённой обучающей выборке `train+val`. Для прозрачности сохраняется история функции потерь по эпохам.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "38795d71",
   "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",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>26</td>\n",
       "      <td>0.031794</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>27</td>\n",
       "      <td>0.033827</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>28</td>\n",
       "      <td>0.022319</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>29</td>\n",
       "      <td>0.028679</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>30</td>\n",
       "      <td>0.022590</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>31</td>\n",
       "      <td>0.029527</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>31</th>\n",
       "      <td>32</td>\n",
       "      <td>0.023138</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>32</th>\n",
       "      <td>33</td>\n",
       "      <td>0.026261</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>33</th>\n",
       "      <td>34</td>\n",
       "      <td>0.019996</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>34</th>\n",
       "      <td>35</td>\n",
       "      <td>0.019305</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    epoch  train_loss\n",
       "25     26    0.031794\n",
       "26     27    0.033827\n",
       "27     28    0.022319\n",
       "28     29    0.028679\n",
       "29     30    0.022590\n",
       "30     31    0.029527\n",
       "31     32    0.023138\n",
       "32     33    0.026261\n",
       "33     34    0.019996\n",
       "34     35    0.019305"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "# повторно строим препроцессор на train+val\n",
    "preprocessor_final = ColumnTransformer(\n",
    "    transformers=[\n",
    "        (\"num\", Pipeline([\n",
    "            (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
    "            (\"scaler\", StandardScaler()),\n",
    "        ]), numeric_features),\n",
    "        (\"cat\", Pipeline([\n",
    "            (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
    "            (\"ohe\", make_ohe()),\n",
    "        ]), categorical_features),\n",
    "    ],\n",
    "    remainder=\"drop\",\n",
    ")\n",
    "\n",
    "X_trainval_proc = preprocessor_final.fit_transform(X_trainval)\n",
    "X_test_proc = preprocessor_final.transform(X_test)\n",
    "\n",
    "target_scaler_final = StandardScaler()\n",
    "y_trainval_scaled = target_scaler_final.fit_transform(np.asarray(y_trainval).reshape(-1, 1)).ravel()\n",
    "\n",
    "final_model = MLPRegressor(\n",
    "    hidden_layer_sizes=best_solution[\"hidden_layers\"],\n",
    "    activation=best_solution[\"activation\"],\n",
    "    alpha=best_solution[\"alpha\"],\n",
    "    learning_rate_init=best_solution[\"learning_rate_init\"],\n",
    "    batch_size=best_solution[\"batch_size\"],\n",
    "    solver=\"adam\",\n",
    "    max_iter=1,\n",
    "    warm_start=True,\n",
    "    shuffle=True,\n",
    "    random_state=RANDOM_STATE,\n",
    ")\n",
    "\n",
    "EPOCHS_FINAL = 35\n",
    "final_history = []\n",
    "for epoch in range(EPOCHS_FINAL):\n",
    "    final_model.fit(X_trainval_proc, y_trainval_scaled)\n",
    "    final_history.append({\n",
    "        \"epoch\": epoch + 1,\n",
    "        \"train_loss\": float(final_model.loss_),\n",
    "    })\n",
    "\n",
    "history_df = pd.DataFrame(final_history)\n",
    "display(history_df.tail(10))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "08a45aba",
   "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>r2_test</th>\n",
       "      <th>rmse_test</th>\n",
       "      <th>mae_test</th>\n",
       "      <th>r2_trainval</th>\n",
       "      <th>rmse_trainval</th>\n",
       "      <th>mae_trainval</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "      <th>activation</th>\n",
       "      <th>alpha</th>\n",
       "      <th>learning_rate_init</th>\n",
       "      <th>batch_size</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Нейросеть с архитектурой, найденной ABC</td>\n",
       "      <td>0.742509</td>\n",
       "      <td>0.229236</td>\n",
       "      <td>0.095363</td>\n",
       "      <td>0.978186</td>\n",
       "      <td>0.066391</td>\n",
       "      <td>0.029817</td>\n",
       "      <td>12</td>\n",
       "      <td>41</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                     model   r2_test  rmse_test  mae_test  r2_trainval  rmse_trainval  mae_trainval  n_original_features  \\\n",
       "0  Нейросеть с архитектурой, найденной ABC  0.742509   0.229236  0.095363     0.978186       0.066391      0.029817                   12   \n",
       "\n",
       "   n_processed_features hidden_layers activation     alpha  learning_rate_init  batch_size  \n",
       "0                    41  (80, 80, 48)       relu  0.001401            0.005741          32  "
      ]
     },
     "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>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>f1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.935065</td>\n",
       "      <td>0.927273</td>\n",
       "      <td>0.888889</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   accuracy  balanced_accuracy        f1\n",
       "0  0.935065           0.927273  0.888889"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.009030</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.005969</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1.0</td>\n",
       "      <td>0.916556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.049543</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.001056</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.005247</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1.0</td>\n",
       "      <td>0.929752</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.000597</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.002699</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.159810</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   y_true    y_pred\n",
       "0     0.0 -0.009030\n",
       "1     0.0 -0.005969\n",
       "2     1.0  0.916556\n",
       "3     0.0  0.049543\n",
       "4     0.0  0.001056\n",
       "5     0.0 -0.005247\n",
       "6     1.0  0.929752\n",
       "7     0.0 -0.000597\n",
       "8     0.0  0.002699\n",
       "9     0.0  0.159810"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "pred_test_scaled = final_model.predict(X_test_proc)\n",
    "pred_test = target_scaler_final.inverse_transform(pred_test_scaled.reshape(-1, 1)).ravel()\n",
    "\n",
    "pred_trainval_scaled = final_model.predict(X_trainval_proc)\n",
    "pred_trainval = target_scaler_final.inverse_transform(pred_trainval_scaled.reshape(-1, 1)).ravel()\n",
    "\n",
    "test_metrics = evaluate_regression(y_test, pred_test)\n",
    "trainval_metrics = evaluate_regression(y_trainval, pred_trainval)\n",
    "\n",
    "metrics_df = pd.DataFrame([{\n",
    "    \"model\": \"Нейросеть с архитектурой, найденной ABC\",\n",
    "    \"r2_test\": test_metrics[\"r2\"],\n",
    "    \"rmse_test\": test_metrics[\"rmse\"],\n",
    "    \"mae_test\": test_metrics[\"mae\"],\n",
    "    \"r2_trainval\": trainval_metrics[\"r2\"],\n",
    "    \"rmse_trainval\": trainval_metrics[\"rmse\"],\n",
    "    \"mae_trainval\": trainval_metrics[\"mae\"],\n",
    "    \"n_original_features\": len(feature_cols),\n",
    "    \"n_processed_features\": X_trainval_proc.shape[1],\n",
    "    \"hidden_layers\": str(best_solution[\"hidden_layers\"]),\n",
    "    \"activation\": best_solution[\"activation\"],\n",
    "    \"alpha\": best_solution[\"alpha\"],\n",
    "    \"learning_rate_init\": best_solution[\"learning_rate_init\"],\n",
    "    \"batch_size\": best_solution[\"batch_size\"],\n",
    "}])\n",
    "\n",
    "display(metrics_df)\n",
    "\n",
    "binary_aux_df = None\n",
    "if IS_BINARY_TARGET:\n",
    "    aux = evaluate_optional_binary(y_test, pred_test)\n",
    "    binary_aux_df = pd.DataFrame([aux])\n",
    "    print(\"\\nДополнительные метрики после округления до класса (для бинарной цели):\")\n",
    "    display(binary_aux_df)\n",
    "\n",
    "pred_preview = pd.DataFrame({\n",
    "    \"y_true\": np.asarray(y_test).ravel()[:10],\n",
    "    \"y_pred\": pred_test[:10],\n",
    "})\n",
    "display(pred_preview)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "17c6efe4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== КЛЮЧЕВАЯ ИНФОРМАЦИЯ ДЛЯ ВЫВОДА ===\n",
      "Зависимая переменная: recurred\n",
      "\n",
      "Число исходных признаков: 12\n",
      "Число признаков после кодирования: 41\n",
      "Лучшая архитектура, найденная ABC: (80, 80, 48)\n",
      "Функция активации: relu\n",
      "alpha: 0.0014014686515505014\n",
      "learning_rate_init: 0.005741282065702722\n",
      "batch_size: 32\n",
      "Использовано log1p-преобразование цели: False\n",
      "\n",
      "Метрики модели на test:\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>r2_test</th>\n",
       "      <th>rmse_test</th>\n",
       "      <th>mae_test</th>\n",
       "      <th>r2_trainval</th>\n",
       "      <th>rmse_trainval</th>\n",
       "      <th>mae_trainval</th>\n",
       "      <th>n_original_features</th>\n",
       "      <th>n_processed_features</th>\n",
       "      <th>hidden_layers</th>\n",
       "      <th>activation</th>\n",
       "      <th>alpha</th>\n",
       "      <th>learning_rate_init</th>\n",
       "      <th>batch_size</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Нейросеть с архитектурой, найденной ABC</td>\n",
       "      <td>0.742509</td>\n",
       "      <td>0.229236</td>\n",
       "      <td>0.095363</td>\n",
       "      <td>0.978186</td>\n",
       "      <td>0.066391</td>\n",
       "      <td>0.029817</td>\n",
       "      <td>12</td>\n",
       "      <td>41</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                     model   r2_test  rmse_test  mae_test  r2_trainval  rmse_trainval  mae_trainval  n_original_features  \\\n",
       "0  Нейросеть с архитектурой, найденной ABC  0.742509   0.229236  0.095363     0.978186       0.066391      0.029817                   12   \n",
       "\n",
       "   n_processed_features hidden_layers activation     alpha  learning_rate_init  batch_size  \n",
       "0                    41  (80, 80, 48)       relu  0.001401            0.005741          32  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Топ-10 конфигураций ABC по валидационному RMSE:\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>hidden_layers</th>\n",
       "      <th>activation</th>\n",
       "      <th>alpha</th>\n",
       "      <th>learning_rate_init</th>\n",
       "      <th>batch_size</th>\n",
       "      <th>rmse_val</th>\n",
       "      <th>mae_val</th>\n",
       "      <th>r2_val</th>\n",
       "      <th>cycle</th>\n",
       "      <th>phase</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>4</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>6</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>1</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>(80, 64, 32, 96)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.106760</td>\n",
       "      <td>0.046627</td>\n",
       "      <td>0.944151</td>\n",
       "      <td>3</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.003968</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.108038</td>\n",
       "      <td>0.052577</td>\n",
       "      <td>0.942807</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001315</td>\n",
       "      <td>0.004088</td>\n",
       "      <td>64</td>\n",
       "      <td>0.109601</td>\n",
       "      <td>0.049250</td>\n",
       "      <td>0.941139</td>\n",
       "      <td>2</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.109714</td>\n",
       "      <td>0.052963</td>\n",
       "      <td>0.941018</td>\n",
       "      <td>10</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>6</td>\n",
       "      <td>employed</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>6</td>\n",
       "      <td>onlooker</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         hidden_layers activation     alpha  learning_rate_init  batch_size  rmse_val   mae_val    r2_val  cycle     phase\n",
       "0         (32, 96, 80)       relu  0.006245            0.009344          32  0.103351  0.041218  0.947661      4  employed\n",
       "1         (32, 96, 80)       relu  0.006245            0.009344          32  0.103351  0.041218  0.947661      6  employed\n",
       "2  (128, 128, 112, 48)       relu  0.006990            0.004198          64  0.106539  0.046265  0.944382      1  onlooker\n",
       "3  (128, 128, 112, 48)       relu  0.006990            0.004198          64  0.106539  0.046265  0.944382      2  onlooker\n",
       "4     (80, 64, 32, 96)       relu  0.006245            0.009344          32  0.106760  0.046627  0.944151      3  employed\n",
       "5  (128, 128, 112, 48)       relu  0.003968            0.004198          64  0.108038  0.052577  0.942807      2  onlooker\n",
       "6  (128, 128, 112, 48)       relu  0.001315            0.004088          64  0.109601  0.049250  0.941139      2  onlooker\n",
       "7         (80, 80, 48)       relu  0.001401            0.005741          32  0.109714  0.052963  0.941018     10  employed\n",
       "8   (112, 80, 32, 112)       relu  0.000341            0.000823          32  0.115037  0.066162  0.935155      6  employed\n",
       "9   (112, 80, 32, 112)       relu  0.000341            0.000823          32  0.115037  0.066162  0.935155      6  onlooker"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Последние итерации ABC:\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>cycle</th>\n",
       "      <th>best_hidden_layers</th>\n",
       "      <th>best_activation</th>\n",
       "      <th>best_alpha</th>\n",
       "      <th>best_learning_rate_init</th>\n",
       "      <th>best_batch_size</th>\n",
       "      <th>best_val_rmse</th>\n",
       "      <th>best_val_mae</th>\n",
       "      <th>best_val_r2</th>\n",
       "      <th>mean_val_rmse</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006990</td>\n",
       "      <td>0.004198</td>\n",
       "      <td>64</td>\n",
       "      <td>0.106539</td>\n",
       "      <td>0.046265</td>\n",
       "      <td>0.944382</td>\n",
       "      <td>0.146345</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>(128, 128, 112, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001315</td>\n",
       "      <td>0.004088</td>\n",
       "      <td>64</td>\n",
       "      <td>0.109601</td>\n",
       "      <td>0.049250</td>\n",
       "      <td>0.941139</td>\n",
       "      <td>0.147185</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>(80, 64, 32, 96)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.106760</td>\n",
       "      <td>0.046627</td>\n",
       "      <td>0.944151</td>\n",
       "      <td>0.142272</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>0.138498</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>(32, 96, 80)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.006245</td>\n",
       "      <td>0.009344</td>\n",
       "      <td>32</td>\n",
       "      <td>0.103351</td>\n",
       "      <td>0.041218</td>\n",
       "      <td>0.947661</td>\n",
       "      <td>0.143432</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>6</td>\n",
       "      <td>(112, 80, 32, 112)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.000341</td>\n",
       "      <td>0.000823</td>\n",
       "      <td>32</td>\n",
       "      <td>0.115037</td>\n",
       "      <td>0.066162</td>\n",
       "      <td>0.935155</td>\n",
       "      <td>0.142296</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>7</td>\n",
       "      <td>(48, 80, 64)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001913</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.132492</td>\n",
       "      <td>0.067692</td>\n",
       "      <td>0.913985</td>\n",
       "      <td>0.156776</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>8</td>\n",
       "      <td>(48, 80, 64)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001913</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.132492</td>\n",
       "      <td>0.067692</td>\n",
       "      <td>0.913985</td>\n",
       "      <td>0.153727</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>9</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>tanh</td>\n",
       "      <td>0.000021</td>\n",
       "      <td>0.001842</td>\n",
       "      <td>32</td>\n",
       "      <td>0.123614</td>\n",
       "      <td>0.076008</td>\n",
       "      <td>0.925126</td>\n",
       "      <td>0.147158</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>10</td>\n",
       "      <td>(80, 80, 48)</td>\n",
       "      <td>relu</td>\n",
       "      <td>0.001401</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>32</td>\n",
       "      <td>0.109714</td>\n",
       "      <td>0.052963</td>\n",
       "      <td>0.941018</td>\n",
       "      <td>0.144835</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   cycle   best_hidden_layers best_activation  best_alpha  best_learning_rate_init  best_batch_size  best_val_rmse  best_val_mae  \\\n",
       "0      1  (128, 128, 112, 48)            relu    0.006990                 0.004198               64       0.106539      0.046265   \n",
       "1      2  (128, 128, 112, 48)            relu    0.001315                 0.004088               64       0.109601      0.049250   \n",
       "2      3     (80, 64, 32, 96)            relu    0.006245                 0.009344               32       0.106760      0.046627   \n",
       "3      4         (32, 96, 80)            relu    0.006245                 0.009344               32       0.103351      0.041218   \n",
       "4      5         (32, 96, 80)            relu    0.006245                 0.009344               32       0.103351      0.041218   \n",
       "5      6   (112, 80, 32, 112)            relu    0.000341                 0.000823               32       0.115037      0.066162   \n",
       "6      7         (48, 80, 64)            relu    0.001913                 0.005741               32       0.132492      0.067692   \n",
       "7      8         (48, 80, 64)            relu    0.001913                 0.005741               32       0.132492      0.067692   \n",
       "8      9         (80, 80, 48)            tanh    0.000021                 0.001842               32       0.123614      0.076008   \n",
       "9     10         (80, 80, 48)            relu    0.001401                 0.005741               32       0.109714      0.052963   \n",
       "\n",
       "   best_val_r2  mean_val_rmse  \n",
       "0     0.944382       0.146345  \n",
       "1     0.941139       0.147185  \n",
       "2     0.944151       0.142272  \n",
       "3     0.947661       0.138498  \n",
       "4     0.947661       0.143432  \n",
       "5     0.935155       0.142296  \n",
       "6     0.913985       0.156776  \n",
       "7     0.913985       0.153727  \n",
       "8     0.925126       0.147158  \n",
       "9     0.941018       0.144835  "
      ]
     },
     "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",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>26</td>\n",
       "      <td>0.031794</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>27</td>\n",
       "      <td>0.033827</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>28</td>\n",
       "      <td>0.022319</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>29</td>\n",
       "      <td>0.028679</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>30</td>\n",
       "      <td>0.022590</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>31</td>\n",
       "      <td>0.029527</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>31</th>\n",
       "      <td>32</td>\n",
       "      <td>0.023138</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>32</th>\n",
       "      <td>33</td>\n",
       "      <td>0.026261</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>33</th>\n",
       "      <td>34</td>\n",
       "      <td>0.019996</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>34</th>\n",
       "      <td>35</td>\n",
       "      <td>0.019305</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    epoch  train_loss\n",
       "25     26    0.031794\n",
       "26     27    0.033827\n",
       "27     28    0.022319\n",
       "28     29    0.028679\n",
       "29     30    0.022590\n",
       "30     31    0.029527\n",
       "31     32    0.023138\n",
       "32     33    0.026261\n",
       "33     34    0.019996\n",
       "34     35    0.019305"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Первые 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</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.009030</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.005969</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1.0</td>\n",
       "      <td>0.916556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.049543</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.001056</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.005247</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1.0</td>\n",
       "      <td>0.929752</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-0.000597</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.002699</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.159810</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   y_true    y_pred\n",
       "0     0.0 -0.009030\n",
       "1     0.0 -0.005969\n",
       "2     1.0  0.916556\n",
       "3     0.0  0.049543\n",
       "4     0.0  0.001056\n",
       "5     0.0 -0.005247\n",
       "6     1.0  0.929752\n",
       "7     0.0 -0.000597\n",
       "8     0.0  0.002699\n",
       "9     0.0  0.159810"
      ]
     },
     "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>accuracy</th>\n",
       "      <th>balanced_accuracy</th>\n",
       "      <th>f1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.935065</td>\n",
       "      <td>0.927273</td>\n",
       "      <td>0.888889</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   accuracy  balanced_accuracy        f1\n",
       "0  0.935065           0.927273  0.888889"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "print(\"=== КЛЮЧЕВАЯ ИНФОРМАЦИЯ ДЛЯ ВЫВОДА ===\")\n",
    "print(f\"Зависимая переменная: {target_col}\")\n",
    "print()\n",
    "print(f\"Число исходных признаков: {len(feature_cols)}\")\n",
    "print(f\"Число признаков после кодирования: {X_trainval_proc.shape[1]}\")\n",
    "print(f\"Лучшая архитектура, найденная ABC: {best_solution['hidden_layers']}\")\n",
    "print(f\"Функция активации: {best_solution['activation']}\")\n",
    "print(f\"alpha: {best_solution['alpha']}\")\n",
    "print(f\"learning_rate_init: {best_solution['learning_rate_init']}\")\n",
    "print(f\"batch_size: {best_solution['batch_size']}\")\n",
    "print(\"Использовано log1p-преобразование цели: False\")\n",
    "print()\n",
    "print(\"Метрики модели на test:\")\n",
    "display(metrics_df)\n",
    "print(\"Топ-10 конфигураций ABC по валидационному RMSE:\")\n",
    "display(all_candidates_df.head(10))\n",
    "print(\"Последние итерации ABC:\")\n",
    "display(abc_history_df.tail(10))\n",
    "print(\"Последние эпохи обучения финальной модели:\")\n",
    "display(history_df.tail(10))\n",
    "print(\"Первые 10 фактических и предсказанных значений на тесте:\")\n",
    "display(pred_preview)\n",
    "if binary_aux_df is not None:\n",
    "    print(\"Дополнительные классификационные метрики после округления предсказаний:\")\n",
    "    display(binary_aux_df)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21cbdb2e",
   "metadata": {},
   "source": [
    "Итог\n",
    "\n",
    "Зависимой переменной выбрана recurred. Методом ABC найдена лучшая архитектура нейросети (80, 80, 48) с параметрами relu, alpha = 0.0014, learning_rate_init = 0.00574, batch_size = 32.\n",
    "\n",
    "На тестовой выборке модель показала R² = 0.7425, RMSE = 0.2292, MAE = 0.0954. Для бинарной целевой переменной это соответствует высокому качеству: после округления предсказаний получено accuracy = 0.935, balanced accuracy = 0.927, F1 = 0.889.\n",
    "\n",
    "Следовательно, нейросеть с архитектурой, найденной ABC, обеспечивает качественное решение задачи, а найденная конфигурация является лучшей по тестовым метрикам."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
