Files
neurocomputers-python/lab2/2_perceptron.ipynb

1371 строка
50 KiB
Plaintext
Исходник Ответственный История

Этот файл содержит невидимые символы Юникода
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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. Подготовка данных<a id=\"p_1\"></a>"
]
},
{
"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. Задача бинарной классификации<a id=\"p_2\"></a>"
]
},
{
"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",
"Нормализация (MinMax 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. Задача многоклассовой классификации<a id=\"p_3\"></a>"
]
},
{
"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. Задача многоклассовой классификации (с исключением одного из классов)<a id=\"p_4\"></a>"
]
},
{
"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
}