{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "AoAuVqmYggH6"
   },
   "source": [
    "### ЛАБОРАТОРНАЯ РАБОТА №3\n",
    "## Применение многослойного персептрона. Автоассоциативная ИНС"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "32GlCnm4ggH9"
   },
   "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": {
    "id": "k_B6wp5VggH_"
   },
   "source": [
    "Импорт библиотек:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "tHv-NgtJ30SO"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "from IPython.display import clear_output\n",
    "from torch import nn\n",
    "from sklearn.cluster import KMeans\n",
    "from sklearn.linear_model import LinearRegression\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4vwZ6H2DggIB"
   },
   "source": [
    "Среднеквадратическая ошибка:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "OGGq8mjQggIC"
   },
   "outputs": [],
   "source": [
    "def mse(y_pred, y_true):\n",
    "    return np.mean((y_pred - y_true) ** 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Содержание:  \n",
    "[1. Подготовка данных](#p_1)  \n",
    "[2. Сеть с радиально-базисными функциями](#p_2)  \n",
    "[3. Многослойный персептрон](#p_3)  \n",
    "[4. Сравнение моделей](#p_4)  \n",
    "[5. Прогнозирование курса доллара](#p_5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iaKX-JW9ggIC"
   },
   "source": [
    "## 1. Подготовка данных<a id=\"p_1\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "C4MQNIvaggIC"
   },
   "source": [
    "Функция с реализацией отображения Хенона — уравнения $x_{n+1} = 1 - \\alpha x_{n}^2 + y_{n}$ и $y_{n+1} = \\beta x_{n}$, где $α = 1.4$, $β = 0.3$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "3nMMP4Zj1Bwu"
   },
   "outputs": [],
   "source": [
    "def xenon_map(x=0, y=0, alpha=1.4, beta=0.3):\n",
    "    x_next = 1 - alpha * x ** 2 + y\n",
    "    y_next = beta * x\n",
    "    return x_next, y_next"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9HXiFkDoggID"
   },
   "source": [
    "Укажите количество точек во временном ряде, который будет получен из отображения Хенона:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "BUgZtjUyggID"
   },
   "outputs": [],
   "source": [
    "n_points = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XPr65SPMggIE"
   },
   "source": [
    "Сгенерируем необходимое количество точек — помним, что нужна только переменная $x$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "iJQi12wm22eR"
   },
   "outputs": [],
   "source": [
    "x, y = 0, 0\n",
    "xenon_data = []\n",
    "for i in range(n_points):\n",
    "    x, y = xenon_map(x, y)\n",
    "    xenon_data.append(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vKcPkUDCggIF"
   },
   "source": [
    "Поскольку исходная задача является задачей прогнозирования, то обучающая выборка должна включать в себя первый участок временного ряда, на котором будет обучаться модель. Валидационная выборка, на которой мы оцениваем качество предсказания модели, всегда должна идти после обучающей (аналогично с тестовой).\n",
    "\n",
    "Выделим, к примеру, первые 70% данных временного ряда `xenon_data` для обучающей выборки, следующие данные по 15% отнесём к вылидационной и тестовой выборкам (вторая и третья части данных соответственно):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "1ZMHrs0c4Ihy"
   },
   "outputs": [],
   "source": [
    "train_size = int(0.7 * n_points)\n",
    "valid_size = int(0.15 * n_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JDK1n0N-ggIF"
   },
   "source": [
    "Представим данные для выборок на графике:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 430
    },
    "id": "fcaFy3iWjeQQ",
    "outputId": "64abe542-cee1-4183-b616-6c7e9c41118e"
   },
   "outputs": [],
   "source": [
    "plt.plot(xenon_data, color='k', label='Xenon Data')\n",
    "plt.axvspan(0, train_size, alpha=0.35, color='blue', label='Train')\n",
    "plt.axvspan(train_size, train_size+valid_size, alpha=0.35, color='orange', label='Valid')\n",
    "plt.axvspan(train_size+valid_size, n_points, alpha=0.35, color='green', label='Test')\n",
    "plt.legend(loc='best')\n",
    "plt.grid(True, alpha=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pej4dfNRggIG"
   },
   "source": [
    "Пройдём с единичным шагом по всем данным `xenon_data` скользящим окном длиною `seq_length`(например, длиною 10). Таким образом сформируем пары «вход‑выход» для модели:\n",
    " - `X_data`: последовательности из `seq_length` элементов (скользящее окно по `xenon_data`),\n",
    " - `y_data`: элемент, следующий сразу за каждой последовательностью в `X_data`.\n",
    "\n",
    "***Примечание***. С помощью `[:-1]` мы убираем в `X_data` последнее окно, для которого нет «следующего значения» в `y_data`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "C2KR8vptSKVH"
   },
   "outputs": [],
   "source": [
    "seq_length = 10\n",
    "\n",
    "X_data = np.lib.stride_tricks.sliding_window_view(xenon_data, window_shape=seq_length)[:-1]\n",
    "y_data = xenon_data[seq_length:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wM5lRDRnLWY4"
   },
   "source": [
    "Посмотрим конец полученных данных. Видно, что последнее значение `y_data` не включено в окна из `X_data`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "13yK1ZsbUfWk",
    "outputId": "5e86b1ee-c455-4087-f1c9-b1accff382fd"
   },
   "outputs": [],
   "source": [
    "X_data[-2:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5639fOPUTl2Q",
    "outputId": "483d2e6d-36e5-4d4a-cbed-726dc0ed4eec"
   },
   "outputs": [],
   "source": [
    "y_data[-4:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "04Pd841VggII"
   },
   "source": [
    "Длина наших данных — без учёта последнего значения:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "k62uvZDNONY1"
   },
   "outputs": [],
   "source": [
    "assert len(X_data) == len(y_data) == n_points - seq_length"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "v8vK2daVggIJ"
   },
   "source": [
    "Разделим данные на обучающую, валидационную и тестовую выборку по заданному выше соотношению 70%/15%/15%:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "jvRzLs7rS9QF"
   },
   "outputs": [],
   "source": [
    "X_train, X_valid, X_test = X_data[:train_size], X_data[train_size:train_size+valid_size], X_data[train_size+valid_size:]\n",
    "y_train, y_valid, y_test = y_data[:train_size], y_data[train_size:train_size+valid_size], y_data[train_size+valid_size:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i4VgXIcRV91m"
   },
   "source": [
    "Отнормируйте или отстанлартизируйте входные и выходные данные.\n",
    "\n",
    "Поскольку нормирующие или стандартизирующие величины (минимум, максимум, среднее, СКО) всегда расчитываются только по значениям обучающей выборки, берём из исходных данных `xenon_data` также значения, которые попадут в последнее окно (и снова исключаем самое последнее значение):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "fjN96i_1ggIJ"
   },
   "outputs": [],
   "source": [
    "X_mean = np.mean(xenon_data[:train_size+seq_length-1])\n",
    "X_std = np.std(xenon_data[:train_size+seq_length-1], ddof=1)\n",
    "y_mean = np.mean(y_data[:train_size])\n",
    "y_std = np.std(y_data[:train_size], ddof=1)\n",
    "\n",
    "X_train_scaled = # Ваш код здесь\n",
    "X_valid_scaled = # Ваш код здесь\n",
    "X_test_scaled = # Ваш код здесь\n",
    "\n",
    "y_train_scaled = # Ваш код здесь\n",
    "y_valid_scaled = # Ваш код здесь\n",
    "y_test_scaled = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gGVJqQGUggIK"
   },
   "source": [
    "## 2. Сеть с радиально-базисными функциями<a id=\"p_2\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xm61W3LPggIK"
   },
   "source": [
    "РБФ-сеть (Radial Basis Function Network, RBF) — это нейронная сеть, которая использует радиально-базисные функции в качестве функций активации нейронов скрытого слоя. Архитектура такой сети обычно включает три слоя:\n",
    " - Входной слой — принимает входные данные (значения временного ряда).\n",
    " - Скрытый слой — содержит нейроны с радиальными базисными функциями. Каждый нейрон вычисляет расстояние между входным вектором и заранее определённым центром функции, а затем преобразует это расстояние с помощью радиальной функции.\n",
    " - Выходной слой — линейно комбинирует (взвешенно суммирует) выходы скрытых нейронов, чтобы получить итоговый результат.\n",
    "\n",
    "Таким образом, выходной сигнал РБФ‑сети вычисляется по формуле:\n",
    "\n",
    "$$\n",
    "y(\\mathbf{x}) = \\sum_{j=1}^{H} w_j \\cdot \\phi(\\|\\mathbf{x} - \\mathbf{c}_j\\|_2)\n",
    "$$\n",
    "где:\n",
    "* $y(\\mathbf{x})$ — выходной сигнал сети для входного вектора $\\mathbf{x}$;\n",
    "* $H$ — количество нейронов в скрытом слое;\n",
    "* $w_j$ — вес связи от $j$-го нейрона скрытого слоя к выходному слою;\n",
    "* $\\mathbf{c}_j$ — центр $j$-й радиально-базисной функции;\n",
    "* $\\phi$ — радиально-базисная функция;\n",
    "* $\\|\\mathbf{x} - \\mathbf{c}_j\\|_2$ — евклидово расстояние между входным вектором $\\mathbf{x}$ и центром $\\mathbf{c}_j$.\n",
    "\n",
    "Радиально-базисная функция — это функция, которая зависит только от расстояния между входным вектором и центром функции. Чаще всего используется гауссова функция:\n",
    "$$\n",
    "\\phi(r) = \\exp\\left(-\\frac{r^2}{2\\sigma^2}\\right)\n",
    "$$\n",
    "где $r = \\|\\mathbf{x} - \\mathbf{c}\\|_2$, $\\sigma$ — параметр ширины окна.\n",
    "\n",
    "Обучение РБФ‑сетей обычно проходит в два этапа:\n",
    "\n",
    "1. Определение параметров радиально-базисной функции (центров $\\mathbf{c}_i$ и ширин $\\sigma_i$):\n",
    "    * кластеризация (например, k‑means) для нахождения центров;\n",
    "    * эвристические методы или кросс‑валидация для ширин.\n",
    "\n",
    "2. Обучение выходных весов $w_j$:\n",
    "    * решение задачи линейной регрессии (с использованием обучения с учителем, например, методом наименьших квадратов или градиентного спуска).\n",
    "    \n",
    "Таким образом, на первом этапе сеть определяет, где в пространстве признаков сосредоточены основные закономерности данных. Для этого, например, используются алгоритмы кластеризации: объекты (значения временного ряда) внутри одного кластера получатся максимально похожи друг на друга, а объекты разных классов — максимально различны (т.о. определим важные зоны или паттерны ряда).\n",
    "\n",
    "После определения центров сеть анализирует, как каждая точка соотносится с ними. Это показывает гауссова РБФ — для каждой точки вычисляется расстояние до каждого из центров, и чем ближе точка к центру, тем выше значение функции (близко к 1), а также наоборот — чем дальше точка к центру, тем ниже (РБФ стремится к 0). Для каждой точки получается вектор активаций — набор чисел, показывающих, насколько она «похожа» на каждый из центров. Так, точка между двумя центрами даст средние значения для обоих, а точка рядом с одним центром — высокое значение для него и низкие для остальных.\n",
    "\n",
    "На этих активациях для получения весов между скрытым и выходным слоями обучается линейная регрессия. Она находит оптимальные веса для каждого выхода скрытого слоя: какие локальные паттерны важнее для прогноза, какие — менее значимы."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2y2jya41ggIL"
   },
   "source": [
    "Реализуем с помощью класса `RBFPredictor` РБФ-сеть с описанной выше архитектурой.\n",
    "\n",
    "При инициализации (`.__init__()`) задаётся `n_centers` — число центров (нейронов скрытого слоя), устанавливается параметр ширины окна `sigma` для гауссовой функции, создаётся модель линейной регрессии `linear_model` для выходного слоя (взята из библиотеки sklearn).\n",
    "\n",
    "Выбор `n_centers` центров из входных данных происходит в методе `.fit()`. Для этого используется алгоритм k‑means (взят из библиотеки sklearn).\n",
    "\n",
    "Далее в скрытом слое (метод `._radial_basis()`) для каждого входного вектора (объекта) вычисляются расстояния до всех центров. Расстояния преобразуются в значения гауссовой РБФ. На выходе получается матрица активаций размером (число объектов, число центров).\n",
    "\n",
    "На данной матрице активаций скрытого слоя `phi` и целевых значениях `y` обучается модель линейной регрессии `linear_model`. Веса регрессии становятся весами связей от скрытого слоя к выходному.\n",
    "\n",
    "При прогнозировании (метод `.predict()`) для новых данных вычисляются активации скрытого слоя (через гауссовы РБФ относительно тех же центров), а обученная линейная модель применяет веса к этим активациям и выдаёт финальный прогноз."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "yjn8NBbYXA5p"
   },
   "outputs": [],
   "source": [
    "class RBFPredictor:\n",
    "    def __init__(self, n_centers, sigma=1.0):\n",
    "        self.n_centers = n_centers\n",
    "        self.sigma = sigma\n",
    "        self.centers = None\n",
    "        self.linear_model = LinearRegression()\n",
    "\n",
    "\n",
    "    def _radial_basis(self, X, centers):\n",
    "        distances = np.zeros((X.shape[0], centers.shape[0]))\n",
    "        for i, center in enumerate(centers):\n",
    "            distances[:, i] = np.sqrt(np.sum((X - center) ** 2, axis=1))\n",
    "        return np.exp(-(distances ** 2) / (2 * self.sigma ** 2))\n",
    "\n",
    "\n",
    "    def fit(self, X, y):\n",
    "        # Выбираем центры с помощью кластеризации\n",
    "        kmeans = KMeans(n_clusters=self.n_centers, random_state=42)\n",
    "        self.centers = kmeans.fit(X).cluster_centers_\n",
    "        # Вычисляем выход скрытого слоя\n",
    "        phi = self._radial_basis(X, self.centers)\n",
    "        # Обучаем линейный выходной слой\n",
    "        self.linear_model.fit(phi, y)\n",
    "\n",
    "\n",
    "    def predict(self, X):\n",
    "        phi = self._radial_basis(X, self.centers)\n",
    "        return self.linear_model.predict(phi)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2IAX5UXUggIL"
   },
   "source": [
    "Обучите модель `model_rbf`. Для этого подберите подходящее количество центров `n_centers` и ширину окна `sigma`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "a5RQRqH5ggIM"
   },
   "outputs": [],
   "source": [
    "model_rbf = RBFPredictor(\n",
    "    # Ваш код здесь\n",
    ")\n",
    "\n",
    "model_rbf.fit(X_train_scaled, y_train_scaled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bOlEYLHGggIN"
   },
   "source": [
    "При подборе добейтесь примерно одинаковой (или хотя бы сопоставимой) ошибки на обучающей и вадилационной выборках:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "T1RLJMCyivU1",
    "outputId": "00671844-3026-4302-ecfc-0130c6f840f0"
   },
   "outputs": [],
   "source": [
    "pred_rbf_train = model_rbf.predict(X_train_scaled)\n",
    "pred_rbf_valid = model_rbf.predict(X_valid_scaled)\n",
    "\n",
    "print('Loss')\n",
    "print(f'Train: {mse(pred_rbf_train, y_train_scaled):.6f}')\n",
    "print(f'Valid: {mse(pred_rbf_valid, y_valid_scaled):.6f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "NLZo9mQKggIm"
   },
   "source": [
    "Проверка `model_rbf` на тестовых данных будет ниже."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "G2kFrJUAmWRz"
   },
   "source": [
    "## 3. Многослойный персептрон<a id=\"p_3\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3kvNrdq3ggIn"
   },
   "source": [
    "Представим входные и выходные данные в виде тензоров PyTorch:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "-GkRe60zptZd"
   },
   "outputs": [],
   "source": [
    "X_train_tensor = torch.tensor(X_train_scaled).float()\n",
    "y_train_tensor = torch.tensor(y_train_scaled).reshape(-1, 1).float()\n",
    "X_valid_tensor = torch.tensor(X_valid_scaled).float()\n",
    "y_valid_tensor = torch.tensor(y_valid_scaled).reshape(-1, 1).float()\n",
    "X_test_tensor = torch.tensor(X_test_scaled).float()\n",
    "y_test_tensor = torch.tensor(y_test_scaled).reshape(-1, 1).float()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "p6INgMk6ggIn"
   },
   "source": [
    "Реализуйте в классе `MLPPredictor` с помощью полносвязных слоёв `nn.Linear` многослойный персептрон. В качестве промежуточных функций активации используйте `nn.ReLU()`, а поскольку решается задача прогнозирования вещественных данных, на выходе сети функцию активации можно не добавлять."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Cg-m7jZNggIn"
   },
   "outputs": [],
   "source": [
    "class MLPPredictor(nn.Module):\n",
    "    def __init__(self, input_size):\n",
    "        super().__init__()\n",
    "        self.seq = nn.Sequential(\n",
    "            # Ваш код здесь\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        return self.seq(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YPF9PyDcycNn"
   },
   "source": [
    "Создайте экземпляр модели:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "T2WBlrBMhA08",
    "outputId": "41040db9-10fe-4705-e27a-9f42ebd7ac4f"
   },
   "outputs": [],
   "source": [
    "model_mlp = MLPPredictor(input_size=seq_length)\n",
    "model_mlp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CTVh4iOjggIp"
   },
   "source": [
    "Проверим, как модель обучается. Зададим оптимизатор и среднеквадратическую функцию потерь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "VGWgT-_UKY5S"
   },
   "outputs": [],
   "source": [
    "optimizer = torch.optim.SGD(model_mlp.parameters(), lr=0.01)\n",
    "criterion = nn.MSELoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-YGsKE_SggIp"
   },
   "source": [
    "Рассчитаем значение функции потерь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "8qFQWNrMZyzM",
    "outputId": "a7e21b4c-425d-46c5-8406-1a27c6c193ef"
   },
   "outputs": [],
   "source": [
    "pred_mlp_train = model_mlp(X_train_tensor)\n",
    "\n",
    "loss = criterion(pred_mlp_train, y_train_tensor)\n",
    "loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "drYc365KggIq"
   },
   "source": [
    "Выполните несколько раз эту и предыдущую ячейку, чтобы убедиться в уменьшении ошибки:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "UlggYk-KbfnF"
   },
   "outputs": [],
   "source": [
    "loss.backward()\n",
    "optimizer.step()\n",
    "optimizer.zero_grad()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "F1hRU9Uj07TE"
   },
   "source": [
    "Задайте параметры для обучения нейронной сети:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "nkJ3lGsgggIr"
   },
   "outputs": [],
   "source": [
    "# Перебор seed для инициализации параметров\n",
    "torch.manual_seed(seed=42)\n",
    "\n",
    "model_mlp = # Ваш код здесь\n",
    "\n",
    "epochs = # Ваш код здесь\n",
    "\n",
    "learning_rate = # Ваш код здесь\n",
    "momentum = # Ваш код здесь\n",
    "\n",
    "optimizer = # Ваш код здесь\n",
    "criterion = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9rpN21H-ggIs"
   },
   "source": [
    "**Обучение нейронной сети:**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "4tZGNdtWggIs"
   },
   "outputs": [],
   "source": [
    "loss_train_history, loss_valid_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_train_history, label='Train', color='green')\n",
    "        plt.plot(range(1, epoch+2), loss_valid_history, label='Valid', color='red')\n",
    "        plt.title(f'Epoch: {epoch + 1}, Loss Train: {loss_train_history[-1]:.6f}, Loss Valid: {loss_valid_history[-1]:.6f}')\n",
    "        plt.grid(True, alpha=0.3)\n",
    "        plt.legend(loc='best')\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "RbvISk2MfSC5"
   },
   "source": [
    "## 4. Сравнение моделей<a id=\"p_4\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CDKiO9MDggIt"
   },
   "source": [
    "Получим прогнозы на тестовой выборке от РБФ-сети и многослойного персептрона:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "tKZ0sdh0eB91"
   },
   "outputs": [],
   "source": [
    "pred_rbf_test = model_rbf.predict(X_test_scaled)\n",
    "\n",
    "with torch.no_grad():\n",
    "    pred_mlp_test = model_mlp(X_test_tensor).squeeze().numpy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "UA_BcjAjggIu"
   },
   "source": [
    "Поскольку обучение шло на нормированных или стандартизированных данных, приведём прогнозы к исходной шкале и рассчитаем среднеквадратическую ошибку:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "tl6X5LGZfjOY",
    "outputId": "bef46cea-f16c-41a2-8f12-8f3e967a5ac3"
   },
   "outputs": [],
   "source": [
    "pred_rbf_test_descaled = (pred_rbf_test  * y_std + y_mean)\n",
    "pred_mlp_test_descaled = (pred_mlp_test  * y_std + y_mean)\n",
    "\n",
    "print('Loss')\n",
    "print(f'RBF: {mse(pred_rbf_test_descaled, y_test):.6f}')\n",
    "print(f'MLP: {mse(pred_mlp_test_descaled, y_test):.6f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oLOykwtViDsY"
   },
   "source": [
    "Построим графики прогнозов для сравнения с исходными тестовыми данными:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 303
    },
    "id": "b0IlMe5EjpNl",
    "outputId": "6fa95c1b-3756-41fe-f020-358c07dd6b8c"
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize=(12, 4))\n",
    "\n",
    "plt.plot(y_test, label='True Values')\n",
    "plt.plot(pred_rbf_test_descaled, label='RBF Pred')\n",
    "plt.plot(pred_mlp_test_descaled, label='MLP Pred')\n",
    "plt.grid(True, alpha=0.3)\n",
    "plt.legend(loc='best')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Vs0-pqwUnqlL"
   },
   "source": [
    "## 5. Прогнозирование курса доллара<a id=\"p_5\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4Od-qc-cnqqe"
   },
   "source": [
    "По аналогии с отображением Хенона самостоятельно реализуйте прогнозирование курса доллара.\n",
    "\n",
    "Для этого скачайте актуальную информацию по курсу с сайта http://www.cbr.ru в формате `.xlsx`.\n",
    "\n",
    "Введите в виже строки имя скачанного файла с расширением:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Z0OkaJZ8ggIw"
   },
   "outputs": [],
   "source": [
    "dollar_course_filename = # Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "cIxIxznLggIx"
   },
   "source": [
    "Загрузим данные в массив NumPy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "oDN_CihKC4SB",
    "outputId": "14391340-b0b6-4017-8771-f54f6306ce71"
   },
   "outputs": [],
   "source": [
    "dollar_data = pd.read_excel(dollar_course_filename).curs[::-1]\n",
    "\n",
    "# По необходимости можно сгладить данные скользящим окном -\n",
    "# в данном случае берём среднее за 7 дней\n",
    "dollar_data = dollar_data.rolling(window=7).mean().dropna()\n",
    "\n",
    "dollar_data = dollar_data.values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "_HnJACO1ggIy"
   },
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mwXH9dIBggI4"
   },
   "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": {
    "id": "Je7Xm08vggI5"
   },
   "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
}
