{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### ЛАБОРАТОРНАЯ РАБОТА №2\n", "## МНОГОСЛОЙНЫЙ ПЕРСЕПТРОН" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Цель работы: изучение алгоритмов обучения многослойного персептрона, выбор структуры и контроль качества обучения нейронной сети, решение задачи\n", "классификации многомерных данных.\n", ">\n", "> Задание\n", "> 1. Открыть хранящиеся в файле `min_water.csv` данные о сорока образцах минеральной воды. Проведя предварительный анализ данных, выделить из 23-х признаков наиболее информативные для классификации образцов признаки. Разделить имеющуюся выборку на три части: для обучения, для верификации, для тестирования. Сохранить исходные данные.\n", "> 2. Создать многослойный персептрон с одним нейроном в выходном слое. Решить задачу классификации имеющихся данных об образцах минеральной воды по пяти классам, используя при обучении алгоритм обратного распространения ошибки. Проанализировать полученные результаты.\n", "> 3. Создать многослойный персептрон с пятью нейронами в выходном слое и снова решить задачу классификации имеющихся данных об образцах минеральной воды по пяти классам. Проанализировать\n", "полученные результаты. По результатам п.2 выбрать наилучшую структуру ИНС для решения поставленной задачи.\n", "> 4. Решить задачу классификации образцов минеральной воды по четырем классам, предварительно пометив один из классов, как недоступный в процессе обучения. Проанализировать результаты классификации, предъявив обученной ИНС неизвестный сорт минеральной воды. Объяснить полученные результаты." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Импорт библиотек:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1768213447856, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "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 sklearn.model_selection import train_test_split\n", "\n", "from torch import nn\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pandas — это библиотека Python, представляющая собой «обёртку» над NumPy, специально разработанную для работы с табличными (структурированными) данными. Она предоставляет два ключевых типа данных: `Series` (одномерный массив с метками) и `DataFrame` (двумерная таблица с колонками разного типа). В отличие от «чистых» массивов NumPy, pandas позволяет: удобно индексировать данные по строкам и столбцам, обрабатывать пропуски (`NaN`), работать с разнородными типами данных в одной таблице, легко выполнять фильтрацию, группировку, агрегацию и слияния таблиц. При этом pandas сохраняет высокую производительность за счёт внутренней опоры на оптимизированные операции NumPy и C‑код.\n", "\n", "Официальная документация: https://pandas.pydata.org/docs/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Содержание: \n", "[1. Подготовка данных](#p_1) \n", "[2. Задача бинарной классификации](#p_2) \n", "[3. Задача многоклассовой классификации](#p_3) \n", "[4. Задача многоклассовой классификации (с исключением одного из классов)](#p_4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Подготовка данных" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Загрузим в датафрейм `data` данные о сорока образцах минеральной воды, хранящиеся в файле `min_water.txt`.\n", "\n", "Первый столбец содержит разметку для задачи бинарной классификации образцов воды (метки `0`и `1`), второй столбец — разметку для задачи многоглассовой классификации (каждому из 5 сортов минеральной воды соответствуют по 5 елассов их образцов). Оставшиеся 23 столбца рассматриваются как входные признаки." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 236 }, "executionInfo": { "elapsed": 112, "status": "ok", "timestamp": 1768213447972, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "PkNWYbQCpsl_", "outputId": "a19eff1c-593c-47b6-ed64-0aa6f84f56c2" }, "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": { "executionInfo": { "elapsed": 17, "status": "ok", "timestamp": 1768213447974, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "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": [ "Нам необходимо отобрать из исходных 23 признаков наиболее информативные и полезные для задач бинарной и многоклассовой классификации.\n", "\n", "Вычислим матрицу парных коэффициентов корреляции Пирсона для всех признаков в `X_data`. Это позволит нам:\n", "- выявить сильно коррелирующие признаки (|r| > 0,7–0,8), которые несут избыточную информацию;\n", "- снизить риск переобучения за счёт удаления дублирующих признаков;\n", "- упростить модель и ускорить обучение без потери качества;\n", "- понять структуру данных и взаимосвязи между признаками;\n", "- избежать проблем мультиколлинеарности в линейных моделях (когда коэффициенты становятся неустойчивыми из‑за сильной взаимной зависимости признаков)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 820 }, "executionInfo": { "elapsed": 27, "status": "ok", "timestamp": 1768213448004, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "-6Mo3AnWusFT", "outputId": "92e51428-c6a4-4e18-eab6-96e3af0a4eee" }, "outputs": [], "source": [ "X_data_correlation = X_data.corr(method='pearson')\n", "X_data_correlation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для наглядности построим тепловую карту корреляционной матрицы. Отметим на карте только те пары признаков, у которых коэффициент корреляции Пирсона больше 0.75:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 456 }, "executionInfo": { "elapsed": 369, "status": "ok", "timestamp": 1768213448385, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "usQSk41iuVuW", "outputId": "143b4366-59ca-48c8-ff7a-10442a2f0d24" }, "outputs": [], "source": [ "plt.figure(figsize=(10, 8))\n", "sns.heatmap(X_data_correlation > 0.75)\n", "plt.show()" ] }, { "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": {}, "outputs": [], "source": [ "X_data_filtered = X_data.loc[:, features]\n", "X_data_filtered.head(n=5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метрика accuracy (доля правильно классифицированных примеров от общего числа) будет общей и для задачи бинарной классификации, и для задачи многоклассовой классификации:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 21, "status": "ok", "timestamp": 1768213448636, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "vcEZHV2G1fSQ" }, "outputs": [], "source": [ "def accuracy(y_pred, y_true):\n", " return torch.sum(y_pred == y_true) / len(y_true)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Задача бинарной классификации" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Изучите [документацию](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html) к функции `train_test_split`, чтобы разделить исходные данные на обучающую, валидационную и тестовую выборки — стандартный этап подготовки данных для обучения и оценки моделей машинного обучения.\n", "\n", "В первом её вызове отделите, например, 20% исходных данных (`X_data_filtered`, `y_binary`) в тестовую выборку (`X_binary_test`, `y_binary_test`), 80% оставьте для обучения (`X_binary_train`, `y_binary_train`). Через параметр `stratify` сохраните соотношение классов в обеих выборках.\n", "\n", "Во втором вызове отделите 20% данных для обучения в тестовую выборку (`X_binary_valid`, `y_binary_valid`), 80% оставьте для финального обучения (`X_binary_train`, `y_binary_train`). Не забудьте про параметр `stratify`.\n", "\n", "*Примечание*. Валидационная выборка помогает подбирать гиперпараметры (например, число эпох, скорость обучения) и следить за переобучением. Тестовая выборка «заморожена» до конца обучения и используется только для итоговой оценки, чтобы избежать смещения в оценке качества. Их соотношение с обучающей выборкой может быть и иным." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_binary_train, X_binary_test, y_binary_train, y_binary_test = # Ваш код здесь\n", "\n", "X_binary_train, X_binary_valid, y_binary_train, y_binary_valid = # Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Входные признаки для нейронной сети необходимо либо нормализовывать, либо стандартизировать — чтобы привести их к единому масштабу. Это ускоряет сходимость градиентного спуска и делает обучение стабильнее (спуск движется более прямо к минимуму, требует меньше итераций для сходимости, менее подвержен «взрыву» или «исчезновению» градиентов).\n", "\n", "Нормализация (Min‑Max Scaling) приводит значения признака к фиксированному диапазону (обычно `[0, 1]`):\n", " \n", "$$\n", "x_{\\text{norm}} = \\frac{x - x_{\\min}}{x_{\\max} - x_{\\min}}\n", "$$ \n", "где: \n", "- $x$ — исходное значение признака; \n", "- $x_{\\min}$ — минимальное значение признака в выборке; \n", "- $x_{\\max}$ — максимальное значение признака в выборке.\n", "\n", "\n", "Стандартизация преобразует распределение признака к среднему `0` и стандартному отклонению `1`: \n", "$$\n", "x_{\\text{standard}} = \\frac{x - \\mu}{\\sigma}\n", "$$ \n", "где: \n", "- $x$ — исходное значение признака; \n", "- $\\mu$ — среднее значение признака в выборке; \n", "- $\\sigma$ — стандартное отклонение признака в выборке." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Произведём стандартизацию входных признаков (но по аналогии можете произвести нормализацию):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_means = X_binary_train.mean(axis=0)\n", "X_stds = X_binary_train.std(axis=0, ddof=1)\n", "\n", "X_binary_train = # Ваш код здесь\n", "X_binary_valid = # Ваш код здесь\n", "X_binary_test = # Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": { "id": "rP2B8eJk2sYX" }, "source": [ "Представим данные обучающей, валидационной и тестовой выборок в виде тензоров:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 54, "status": "ok", "timestamp": 1768213448693, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "jsWiE7Ke1p9S" }, "outputs": [], "source": [ "X_binary_train = torch.tensor(X_binary_train.values).float()\n", "X_binary_valid = torch.tensor(X_binary_valid.values).float()\n", "X_binary_test = torch.tensor(X_binary_test.values).float()\n", "\n", "y_binary_train = torch.tensor(y_binary_train.values).reshape(-1, 1).float()\n", "y_binary_valid = torch.tensor(y_binary_valid.values).reshape(-1, 1).float()\n", "y_binary_test = torch.tensor(y_binary_test.values).reshape(-1, 1).float()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создайте экземпляр нейронной сети для решения задачи бинарной классификации — например, через `nn.Sequential()`, как было показано в первой лабораторной работе.\n", "\n", "Важно соблюсти два ключевых условия при проектировании архитектуры:\n", "- число входных признаков в первом слое (`in_features`) должно точно соответствовать размерности признаков в обучающих данных (количеству столбцов в `X_binary_train`) (воспользуйтесь свойством `X_binary_train.shape`);\n", "- в выходном слое необходимо использовать один нейрон с сигмоидальной функцией активации — напомним, что это гарантирует, что сеть будет выдавать значение в диапазоне [0, 1], интерпретируемое как вероятность принадлежности к положительному классу в задаче бинарной классификации." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_binary = nn.Sequential(\n", " # Ваш код здесь\n", ")\n", "\n", "model_binary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Удостоверимся, что наша сеть работает — рассчитаем по её выходу, который представляет собой предсказанные вероятности принадлежности к классу 1:\n", " - значение функции потерь (бинарная кросс-энтропия);\n", " - метрику accuracy." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 22, "status": "ok", "timestamp": 1768213448710, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "zP1t3NsT3xiH", "outputId": "b722665a-ee83-4393-b45a-457076a3ac29" }, "outputs": [], "source": [ "y_binary_train_prob = model_binary(X_binary_train)\n", "\n", "criterion = nn.BCELoss()\n", "print(f'Loss: {criterion(y_binary_train_prob, y_binary_train).item():.6f}')\n", "print(f'Accuracy: {accuracy((y_binary_train_prob > 0.5).float(), y_binary_train).item():.3f}')" ] }, { "cell_type": "markdown", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 17, "status": "ok", "timestamp": 1768213448728, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "tfLMe06S5m4V", "outputId": "281962d3-e691-439e-9b39-bd463c7ad8a8" }, "source": [ "Теперь перейдём к обучению нейронной сети для решения поставленной задачи бинарной классификации.\n", "\n", "Зададим \n", " - экземпляр нейронной сети;\n", " - количество эпох или итераций в процессе обучения;\n", " - скорость обучение и коэффициент импульса;\n", " - оптимизатор;\n", " - функцию потерь (поскольку решается задача бинарной классификации, выберем бинарную кросс-энтропию `nn.BCELoss()`).\n", " \n", " \n", "*Примечание*. Вы можете использовать для обучения сети либо код ниже, либо модифицированный на его основе — например, реализовать функцию обучения нейронной сети и проверки её работы." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 9, "status": "ok", "timestamp": 1768213448730, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "z-pABN4e6bAf" }, "outputs": [], "source": [ "# Перебор seed для инициализации параметров\n", "torch.manual_seed(seed=42)\n", "\n", "model_binary = # Ваш код здесь\n", "\n", "epochs = # Ваш код здесь\n", "\n", "learning_rate = # Ваш код здесь\n", "momentum = # Ваш код здесь\n", "\n", "optimizer = # Ваш код здесь\n", "criterion = nn.BCELoss()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Обучение нейронной сети для решения задачи бинарной классификации:**" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 504 }, "executionInfo": { "elapsed": 22752, "status": "ok", "timestamp": 1768213471478, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "qKimvoStsZXv", "outputId": "e903f88f-542f-4ab7-c291-2be72f726d39" }, "outputs": [], "source": [ "loss_train_history, loss_valid_history = [], []\n", "\n", "for epoch in range(epochs):\n", " model_binary.train()\n", "\n", " optimizer.zero_grad()\n", "\n", " y_binary_train_prob = model_binary(X_binary_train)\n", "\n", " loss_train = criterion(y_binary_train_prob, y_binary_train)\n", " loss_train_history.append(loss_train.item())\n", "\n", " loss_train.backward()\n", "\n", " optimizer.step()\n", "\n", " model_binary.eval()\n", " \n", " # Отключаем градиенты для этапа валидации\n", " with torch.no_grad():\n", " y_binary_valid_prob = model_binary(X_binary_valid) \n", " loss_valid = criterion(y_binary_valid_prob, y_binary_valid)\n", " \n", " loss_valid_history.append(loss_valid.item())\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()\n", " \n", " print('Accuracy')\n", " print(f'Train: {accuracy((y_binary_train_prob > 0.5).float(), y_binary_train).item():.3f}')\n", " print(f'Valid: {accuracy((y_binary_valid_prob > 0.5).float(), y_binary_valid).item():.3f}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "После того, как вы подобрали параметры и добились приемлемого качества работы сети на обучающей и валидационной выборках, в качестве финального шага проверьте работу сети на отложенной тестовой выборке:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 35, "status": "ok", "timestamp": 1768213471518, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "V__6qHAe-7VS", "outputId": "61ab4f43-fc4d-4a07-d1a9-921f91a2d0fc" }, "outputs": [], "source": [ "with torch.no_grad():\n", " y_binary_test_prob = model_binary(X_binary_test)\n", " loss_test = criterion(y_binary_test_prob, y_binary_test)\n", " \n", "print(f'Loss Test: {loss_test.item():.6f}')\n", "print(f'Accuracy Test: {accuracy((y_binary_valid_prob > 0.5).float(), y_binary_valid).item():.3f}')" ] }, { "cell_type": "markdown", "metadata": { "executionInfo": { "elapsed": 30, "status": "ok", "timestamp": 1768213471544, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "ZtJ4WeKCATqU" }, "source": [ "Если качество схожее, нейронная сеть не переобучена.\n", "\n", "Поэкспериметируйте — сделайте несколько вариантов с разными архитектурами сети, оптимизаторами, значениями скорости обучения и коэффициента импульса. Выберите лучшие по качеству обучения и количеству эпох." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Задача многоклассовой классификации" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для решения задачи многоклассовой классификации по аналогии с задачей бинарной классификации:\n", " - с помощью функции `train_test_split` разбейте данные (`X_data_filtered`, `y_multiclass`) на обучающую (`X_multiclass_train`, `y_multiclass_train`), валидационную (`X_multiclass_valid`, `y_multiclass_valid`) и тестовую выборки (`X_multiclass_test`, `y_multiclass_test`) с сохранением соотншений классов (сортов минеральной воды);\n", " - стандартизируйте или нормализуйте входные признаки;\n", " - из векторов с выходным признаком вычтите единицу и представьте их в виде тензоров целочисленного формата (метод `.long()`)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_multiclass_train, X_multiclass_test, y_multiclass_train, y_multiclass_test = # Ваш код здесь\n", "\n", "X_multiclass_train, X_multiclass_valid, y_multiclass_train, y_multiclass_valid = # Ваш код здесь" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 25, "status": "ok", "timestamp": 1768213471611, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "CsGt0n6-B5WA", "outputId": "c555da32-2652-48a2-8954-21b76ffaa019" }, "source": [ "Для задач многоклассовой классификации в машинном обучении применяют функцию активации `softmax`, которая посредством нормализации преобразует вектор логитов (выходов последнего слоя) в распределение вероятностей: \n", " $$\n", " p_k = \\frac{e^{z_k}}{\\sum_{k=1}^{K} e^{z_k}},\n", " $$ \n", " где $z_k$ — выход для $k$-го класса, $K$ — число классов. \n", " - Свойства: \n", " - все выходы ∈ [0, 1]; \n", " - сумма выходов = 1; \n", " - максимальный выход соответствует наиболее вероятному классу.\n", "\n", "Реализованная в PyTorch функция потерь `nn.CrossEntropyLoss()` объединяет в себе `log_softmax` (численно устойчивая версия softmax + логарифм) и `NLLLoss` (отрицательная логарифмическая правдоподобность). Она принимает на вход именно логиты.\n", "\n", "Создадим экземпляр нейронной сети для решения задачи многоклассовой классификации — например, также через `nn.Sequential()`:\n", " - число входных признаков в первом слое (`in_features`) должно точно соответствовать размерности признаков в обучающих данных (количеству столбцов в `X_binary_train`) (воспользуйтесь свойством `X_binary_train.shape`);\n", " - количество нейроной в выходном слое должно совпадать с количеством классов;\n", " - в связи с тем, что сеть нам должна выдавать логиты, функцию активации на выходном слое можно не использовать (хотя можно взять `nn.ReLU()`)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 9, "status": "ok", "timestamp": 1768213471622, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "l2wN73_-BlzP", "outputId": "b630d430-2215-41f3-9a62-93db01a5207a" }, "outputs": [], "source": [ "model_multiclass = nn.Sequential(\n", " nn.Linear(in_features=X_multiclass_train.shape[1], out_features=y_multiclass.nunique()),\n", " nn.ReLU()\n", ")\n", "\n", "model_multiclass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Пропустив данные через эту сеть, получим логиты, представляющие собою вектора, чья длина равна количеству классов:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 21, "status": "ok", "timestamp": 1768213471644, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "4BTXxrftCJzK", "outputId": "e3568629-6f98-4b92-ef3b-92c607a92f9b" }, "outputs": [], "source": [ "y_multiclass_train_logits = model_multiclass(X_multiclass_train)\n", "y_multiclass_train_logits[:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Проверим работу функции потерь с ними:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 15, "status": "ok", "timestamp": 1768213471707, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "4ZQizLNZAT8_", "outputId": "113bf8b1-8c80-4a82-d09a-7db97eb2222f" }, "outputs": [], "source": [ "criterion = nn.CrossEntropyLoss()\n", "criterion(y_multiclass_train_logits, y_multiclass_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Отметим, что для задачи классификации нормализация через `softmax` не требуется — в качестве ответа (метки класса) берем индекс максимального элемента в логите:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 8, "status": "ok", "timestamp": 1768213471654, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "RBufWh2CDW1P", "outputId": "7c14a8d4-9306-429f-b870-50337e29ccaa" }, "outputs": [], "source": [ "y_multiclass_train_logits.argmax(dim=1)[:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метрика accuracy:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 48, "status": "ok", "timestamp": 1768213471704, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "Cz58yMsGDYmx", "outputId": "8c93fa6f-09d5-43b6-dadf-67022c236dc5" }, "outputs": [], "source": [ "accuracy(y_multiclass_train_logits.argmax(dim=1), y_multiclass_train).item()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "По аналогии с задачей бинарной классификации создайте, обучите и проверьте нейронную сеть для решения задачи многоклассовой классификации:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 6, "status": "ok", "timestamp": 1768213471720, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "V1CgE0O7Lz0g" }, "outputs": [], "source": [ "# Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": { "id": "3lHwSPaz35VQ" }, "source": [ "Поэкспериметируйте — сделайте несколько вариантов с разными архитектурами сети, оптимизаторами, значениями скорости обучения и коэффициента импульса. Выберите лучшие по качеству обучения и количеству эпох." ] }, { "cell_type": "markdown", "metadata": { "id": "1RTQgj4gLz5O" }, "source": [ "## 4. Задача многоклассовой классификации (с исключением одного из классов)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Введите метку класса, который хотите исключить (1, 2, 3, 4 или 5):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "label_to_exclude = # Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Маска (логическое условие) исключения в выходном признаке меток с этим классом:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "executionInfo": { "elapsed": 83, "status": "ok", "timestamp": 1768217647959, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "Ge_M2-suvOfq", "outputId": "7562cae1-5c7c-4ee8-c013-6428ec959db6" }, "outputs": [], "source": [ "mask_to_exclude = y_multiclass != label_to_exclude\n", "y_multiclass[mask_to_exclude]" ] }, { "cell_type": "markdown", "metadata": { "id": "3jVGVtJGw3Du" }, "source": [ "Значения входных признаков, которые соответствуют исключённому классу (`X_data_exclude`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 300 }, "executionInfo": { "elapsed": 139, "status": "ok", "timestamp": 1768217650943, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "i1AGN9v0w3eC", "outputId": "9c78af34-6e8d-4b3d-f535-51d00ac100a6" }, "outputs": [], "source": [ "X_data_exclude = X_data_filtered.loc[~mask_to_exclude, :]\n", "X_data_exclude" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Значения входных признаков, которые соответствуют оставленным классам (`X_data_include`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "executionInfo": { "elapsed": 109, "status": "ok", "timestamp": 1768217652781, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "81fKKw_xvyfc", "outputId": "c31b50ed-6c3b-4362-d8c4-65dde73b5615" }, "outputs": [], "source": [ "X_data_include = X_data_filtered.loc[mask_to_exclude, :]\n", "X_data_include" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для корректной работы с нейронной сетью модифицируем выходной признак с метками оставленных классов — значения меток выше исключённого класса уменьшим на единицу, чтобы выровнять их порядок (в дальнейшем вернём исходные). Результатом будет вектор `y_include`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "executionInfo": { "elapsed": 15, "status": "ok", "timestamp": 1768217655753, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "YeSoZPu1vakq", "outputId": "4d1b5c42-161c-4b41-8441-d4f77f16c68c" }, "outputs": [], "source": [ "y_include = y_multiclass[mask_to_exclude].apply(lambda y: y - 1 if y >= label_to_exclude else y)\n", "y_include" ] }, { "cell_type": "markdown", "metadata": { "executionInfo": { "elapsed": 39, "status": "ok", "timestamp": 1768217660798, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "uvneeNEq2gkW" }, "source": [ "По аналогии с задачами выше:\n", " - с помощью функции `train_test_split` разбейте данные (`X_data_include`, `y_include`) на обучающую (`X_include_train`, `y_include_train`), валидационную (`X_include_valid`, `y_include_valid`) и тестовую выборки (`X_include_test`, `y_include_test`) с сохранением соотншений классов (сортов минеральной воды);\n", " - стандартизируйте или нормализуйте входные признаки и представьте их в виде тензоров;\n", " - из векторов с выходным признаком вычтите единицу и представьте их в виде тензоров целочисленного формата (метод `.long()`)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "vXnQuhb7vaoL" }, "outputs": [], "source": [ "# Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": { "id": "LdbLhuA0vasw" }, "source": [ "По аналогии с задаче многоклассовой классификации создайте нейронную сеть для этих данных:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_include = nn.Sequential(\n", " # Ваш код здесь\n", ")\n", "\n", "model_include" ] }, { "cell_type": "markdown", "metadata": { "id": "BjsyXLHgva1X" }, "source": [ "Получим логиты (длина векторов уменьшилась до 4):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 44, "status": "ok", "timestamp": 1768217668726, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "HIsQ_5MR3Jal", "outputId": "74349107-0245-4696-eb9d-36f5e50ceaec" }, "outputs": [], "source": [ "y_include_train_logits = model_include(X_include_train)\n", "y_include_train_logits[:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Проверим работу функцию потерь на этих логитах и модимцированном выходном признаке:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 51, "status": "ok", "timestamp": 1768217670810, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "2XQimt7T3Xcc", "outputId": "1bf129bc-aed1-433e-8083-2cea475f86ee" }, "outputs": [], "source": [ "criterion = nn.CrossEntropyLoss()\n", "criterion(y_include_train_logits, y_include_train)" ] }, { "cell_type": "markdown", "metadata": { "id": "qu9a65IQva5f" }, "source": [ "По аналогии с задачей многоклассовой классификации с полным количеством классов создайте, обучите и проверьте нейронную сеть:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "8wQa5g4Bva-I" }, "outputs": [], "source": [ "# Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": { "id": "l4l-aUPz37ju" }, "source": [ "Поэкспериметируйте — сделайте несколько вариантов с разными архитектурами сети, оптимизаторами, значениями скорости обучения и коэффициента импульса. Выберите лучшие по качеству обучения и количеству эпох." ] }, { "cell_type": "markdown", "metadata": { "executionInfo": { "elapsed": 9, "status": "ok", "timestamp": 1768217700545, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "k4sqBf5q41AC" }, "source": [ "Значения входных признаков, которые соответствуют исключённому классу (`X_data_exclude`) так же, как и значения входных признаков из выборок выше, стандартизируйте или нормализируйте и представьте их в виде тензоров: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_data_exclude = # Ваш код здесь" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Пропустим обработанные признаки через обученную и проверенную нейронную сеть `model_include`, чтобы получить логиты.\n", "\n", "По данным логитам произведём классификацию (с учётом метки исключённого класса), чтобы ответить на вопрос — к каким сортам минеральной воды из оставленных соответствуют образцы исключённого класса:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 14, "status": "ok", "timestamp": 1768217702125, "user": { "displayName": "V N", "userId": "10751591882973173608" }, "user_tz": -180 }, "id": "4bflOWXX5YqN", "outputId": "5bea95d9-0e02-455e-9163-8f14ce3664c3" }, "outputs": [], "source": [ "y_exclude_logits = model_include(X_data_exclude)\n", "\n", "(y_exclude_logits.argmax(dim=1) + 1).apply_(lambda y: y + 1 if y >= label_to_exclude else y)" ] }, { "cell_type": "markdown", "metadata": {}, "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": { "authorship_tag": "ABX9TyP49vkWVMyMLjNYOlwKa9fi", "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 }