{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VhB5tU1IGSIh"
   },
   "source": [
    "### ЛАБОРАТОРНАЯ РАБОТА №3\n",
    "## Применение многослойного персептрона. Автоассоциативная ИНС"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Цель работы: знакомство с применением многослойного персептрона для решения задач сжатия данных, прогнозирования временных рядов и распознавания образов.\n",
    ">\n",
    "> Задание\n",
    "> 1. Открыть файл с данными по минеральной воде, который использовался при решении задач классификации в предыдущей лабораторной работе. Построить и обучить автоассоциативные нейронные сети с 2-мя и 3-мя нейронами в скрытом слое:  \n",
    "> а) для исходных данных из 5-ти классов;  \n",
    "> б) для исходных данных из 4-х классов.  \n",
    "> Провести визуализацию данных в скрытом слое каждой сети на плоскость и в 3-х мерное пространство.  Проанализировать полученные результаты. Выбрать и сохранить автоассоциативные ИНС, обеспечивающие наилучшее сжатие исходных данных.  \n",
    "> 2. Исследовать возможности ИНС по прогнозированию поведения нелинейных динамических систем (построение странного аттрактора) на примере отображения Хенона. Аттрактор Хенона может быть получен из уравнений $x_{n+1} = 1 - \\alpha x_{n}^2 + y_{n}$ и $y_{n+1} = \\beta x_{n}$, где $α = 1.4$, $β = 0.3$.\n",
    "> Для прогнозирования предлагается использовать многослойный персептрон и сеть с радиально-базисными функциями.\n",
    "> Постройте также прогноз курса доллара на один день вперед. В качестве исходных данных загрузить актуальные данные с сайта центрального банка России (http://www.cbr.ru).\n",
    "> 3. Решить задачу распознавания 9-ти изображений самолетов. Исходные данные (файлы avia1.bmp, …, avia9.bmp) необходимо предварительно преобразовать в набор векторов со значениями признаков 0 или 1. Обученная нейронная сеть должна правильно определять модель самолета и его класс (истребитель/бомбардировщик). Принадлежность модели к определенному классу выбирается студентом самостоятельно."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Импорт библиотек:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "FwCw1KfEpbsm"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "import torch\n",
    "import matplotlib.pyplot as plt\n",
    "from IPython.display import clear_output\n",
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "from torch import nn\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Содержание:  \n",
    "[1. Подготовка данных](#p_1)  \n",
    "[2. Автоассоциативная нейронная сеть на полных данных](#p_2)  \n",
    "[3. Автоассоциативная нейронная сеть на неполных данных](#p_3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Подготовка данных<a id=\"p_1\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Загрузим в датафрейм `data` данные о сорока образцах минеральной воды, хранящиеся в файле `min_water.txt`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 256
    },
    "id": "PkNWYbQCpsl_",
    "outputId": "ee2dd8c3-2d7d-4962-bc40-a5cc2dded5db"
   },
   "outputs": [],
   "source": [
    "data = pd.read_csv('min_water.csv')\n",
    "data.head(n=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вынесем в отдельные переменные:\n",
    " - `y_binary` — выходной признак для задачи бинарной классификации (первый столбец датафрейма);\n",
    " - `y_multiclass` — выходной признак для задачи многоклассовой классификации (второй столбец датафрейма);\n",
    " - `X_data` — входные признаки (оставшиеся столбцы)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "D_QiYrPkpsu0"
   },
   "outputs": [],
   "source": [
    "y_binary = data.iloc[:, 0]\n",
    "y_multiclass = data.iloc[:, 1]\n",
    "\n",
    "X_data = data.iloc[:, 2:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выпишите в список `features` отобранные в прошлой лабораторной работе признаки (формат: `features = ['VAR1', 'VAR2']`):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "features = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Датафрейм с отобранными входными признаками `X_data_filtered`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 206
    },
    "id": "d6eTUCk2Cjdc",
    "outputId": "d89643dd-a410-42b2-9b91-c4534966fd1d"
   },
   "outputs": [],
   "source": [
    "X_data_filtered = X_data.loc[:, features]\n",
    "X_data_filtered.head(n=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Произведите нормализацию или стандартизацию (на выбор) отобранных входных данных `X_data_filtered`. Результат сохраните в переменную `X_data_preprocessed`, которую затем представьте в виде тензора `X_data_tensor`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "HclxngwAGue-"
   },
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "\n",
    "X_data_preprocessed = # Ваш код здесь\n",
    "\n",
    "X_data_tensor = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "AtGktiZmJiUV",
    "outputId": "f9edb4f0-c5ab-4a62-af28-6552c981512f"
   },
   "source": [
    "## 2. Автоассоциативная нейронная сеть на полных данных<a id=\"p_2\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jsWiE7Ke1p9S"
   },
   "source": [
    "Автоассоциативная сеть (или автоассоциативная память) — тип нейронной сети, способный восстанавливать полный шаблон данных по его частичному или зашумлённому представлению.\n",
    "\n",
    "Типичная автоассоциативная сеть содержит как минимум три скрытых слоя:\n",
    " - первый скрытый слой выполняет нелинейное кодирование входных данных (энкодер);\n",
    " - средний слой («узкое горло» или «бутылочное горло») формирует сжатое представление данных — в результате обучения выдаёт компактное кодирование;\n",
    " - последний скрытый слой служит декодером: восстанавливает исходные данные из сжатого представления.\n",
    "\n",
    "Цель обучения: в процессе минимизации ошибки воспроизведения сеть стремится сделать выходной сигнал максимально близким к входному. Это эквивалентно оптимальному кодированию в «узком горле» сети."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Допишите класс `Autoencoder` структурами энкодера и декодера на основе полносвязных слоёв `nn.Linear`. В качестве функций активации используйте `nn.ReLU()`. При этом на выходе энкодера функцию активации можно не применять — это позволит сохранить отрицательные значения в кодированном представлении."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Autoencoder(nn.Module):\n",
    "    def __init__(self, n_inputs, n_hiddens, bottleneck_size):\n",
    "        super().__init__()\n",
    "        self.encoder = nn.Sequential(\n",
    "            # Ваш код здесь\n",
    "        )\n",
    "        self.decoder = nn.Sequential(\n",
    "            # Ваш код здесь\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        encoded = self.encoder(x)\n",
    "        decoded = self.decoder(encoded)\n",
    "        return decoded"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Создайте экземпляр модели с двумя нейронами в «узком горле»:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_2 = Autoencoder(# Ващ код здесь\n",
    "print(model_2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "tGS2_TqjK_SI",
    "outputId": "a1a63c33-5dd5-461e-8d20-97fc34455f21"
   },
   "source": [
    "Пропустим данные через эту модель для её проверки:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Y3GS-3AaKYt7",
    "outputId": "981a4cd8-3b19-4bf6-ac07-ddd13e3df131"
   },
   "outputs": [],
   "source": [
    "model_2(X_data_tensor[:3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Удостоверимся, что размерность её выхода совпадает с размерностью её входа:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert X_data_tensor[:3].shape == model_2(X_data_tensor[:3]).shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проверим, как модель обучается. Зададим оптимизатор и среднеквадратическую функцию потерь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "VGWgT-_UKY5S"
   },
   "outputs": [],
   "source": [
    "optimizer = torch.optim.SGD(model_2.parameters(), lr=1.5)\n",
    "criterion = nn.MSELoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассчитаем значение функции потерь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5kKYW89_KY8j",
    "outputId": "94e90485-ca86-4204-e61d-75d247a87fc4"
   },
   "outputs": [],
   "source": [
    "decoded = model_2(X_data_tensor)\n",
    "\n",
    "loss = criterion(decoded, X_data_tensor)\n",
    "loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выполните несколько раз эту и предыдущую ячейку, чтобы убедиться в уменьшении ошибки:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "G_JCVLCkKZCa"
   },
   "outputs": [],
   "source": [
    "loss.backward()\n",
    "optimizer.step()\n",
    "optimizer.zero_grad()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Задайте параметры для обучения автоассоциативной сети с двумя нейронами в «узком горле»:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(seed=42)\n",
    "\n",
    "model_2 = # Ваш код здесь\n",
    "\n",
    "epochs = # Ваш код здесь\n",
    "\n",
    "learning_rate = # Ваш код здесь\n",
    "momentum = # Ваш код здесь\n",
    "\n",
    "optimizer = # Ваш код здесь\n",
    "criterion = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обучение нейронной сети:**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_history = []\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    # Ваш код здесь\n",
    "\n",
    "    if (epoch + 1) % 5 == 0:\n",
    "\n",
    "        clear_output(True)\n",
    "        plt.plot(range(1, epoch+2), loss_history, label='Loss')\n",
    "        plt.title(f'Epoch: {epoch + 1}, Loss: {loss_history[-1]:.6f}')\n",
    "        plt.grid(True, alpha=0.3)\n",
    "        plt.legend(loc='best')\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "После обучения сети получим двумерные данные с выхода энкодера:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "HFcSAdGoOaog",
    "outputId": "36330737-7606-4a6a-e532-6bf372deb34d"
   },
   "outputs": [],
   "source": [
    "encoded_2d = model_2.encoder(X_data_tensor).detach().numpy()\n",
    "print(encoded_2d[:3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим двумерную диаграмму рассеяния и отметим классы с помощью `y_multiclass`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 430
    },
    "id": "KkToPCInQQ1J",
    "outputId": "244bae8a-94a9-4817-a564-69c630efea98"
   },
   "outputs": [],
   "source": [
    "scatter = plt.scatter(x=encoded_2d[:, 0], y=encoded_2d[:, 1], c=y_multiclass, cmap='viridis')\n",
    "plt.grid(True, alpha=0.3)\n",
    "\n",
    "# Код для легенды\n",
    "handles, labels = scatter.legend_elements(prop='colors')\n",
    "plt.legend(handles, labels, loc='best', title='Classes')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "По аналогии обучите автоассоциативную сеть с тремя нейронами в «узком горле»:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(seed=42)\n",
    "\n",
    "model_3 = # Ваш код здесь\n",
    "\n",
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "После обучения сети получите трёхмерные данные с выхода энкодера:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "encoded_3d = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим трёхмерную диаграмму рассеяния:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 653
    },
    "id": "RpqVg5EeT72I",
    "outputId": "2f5e070d-aa49-4eab-cd8e-277bd3a2c4b5"
   },
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(10, 8))\n",
    "ax = fig.add_subplot(111, projection='3d')\n",
    "\n",
    "scatter = ax.scatter(\n",
    "    xs=encoded_3d[:, 0],\n",
    "    ys=encoded_3d[:, 1],\n",
    "    zs=encoded_3d[:, 2],\n",
    "    c=y_multiclass,\n",
    "    cmap='viridis',\n",
    "    s=50\n",
    ")\n",
    "\n",
    "ax.grid(True, alpha=0.3)\n",
    "\n",
    "handles, labels = scatter.legend_elements(prop='colors', alpha=0.8)\n",
    "ax.legend(handles, labels, loc='best', title='Classes')\n",
    "\n",
    "# Настраиваем угол обзора:\n",
    "# elev — высота, azim — азимут\n",
    "ax.view_init(elev=20, azim=45)\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сохраним в бинарные файлы `.npy` выходы энкодеров обеих моделей — для следующей лабораторной работы:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.save('encoded_2d.npy', encoded_2d)\n",
    "np.save('encoded_3d.npy', encoded_3d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Автоассоциативная нейронная сеть на неполных данных<a id=\"p_3\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выберите класс, который нужно исключить:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "label_to_exclude = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Создадим маску для исключения данных этого класса:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "WwlU6wRqXy3H"
   },
   "outputs": [],
   "source": [
    "mask_to_exclude = y_multiclass != label_to_exclude"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Данные исключены:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "I7sTKdWgQRWQ",
    "outputId": "95d2a292-7e2f-401e-e473-7d5f09b0d7fb"
   },
   "outputs": [],
   "source": [
    "X_data_tensor[mask_to_exclude].shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "По аналогии с предыдущим пунктом реализуйте обучение автоассоциативных сетей с двумя и тремя нейронами в «узком горле» на неполных данных (т.е. на каждой эпохе вместо полных данных `X_data_tensor` на модель нужно подавать неполные данные `X_data_tensor[mask_to_exclude]`).\n",
    "\n",
    "Результаты выходов энкодеров в обоих случаях также сохраните в отдельные бинарные файлы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "w3kJHSW3azk0"
   },
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Литература:\n",
    "1. Бородкин А.А., Елисеев В.Л. Основы и применение искусственных нейронных сетей. Сборник лабораторных работ: методическое пособие. – М.: Издательский дом МЭИ, 2017.\n",
    "2. MachineLearning.ru  —  профессиональный информационно-аналитический ресурс, посвященный машинному обучению, распознаванию образов и интеллектуальному анализу данных: http://www.machinelearning.ru\n",
    "3. Modern State of Artificial Intelligence  —  Online Masters program at MIPT: https://girafe.ai/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
