ответвлено от main/neurocomputers-python
882 строки
35 KiB
Plaintext
882 строки
35 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### ЛАБОРАТОРНАЯ РАБОТА №1.2\n",
|
|
"## ИЗУЧЕНИЕ ОСНОВНЫХ ПОНЯТИЙ ТЕОРИИ ИСКУССТВЕННЫХ НЕЙРОННЫХ СЕТЕЙ"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> Цель работы: изучение основных понятий теории искусственных нейронных сетей на примере простых задач распознавания логических функций («И», «ИЛИ», «исключающее ИЛИ»)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Импорт библиотек:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "bvIkPEqTXcpG"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"import numpy as np\n",
|
|
"import torch\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"from IPython.display import clear_output\n",
|
|
"\n",
|
|
"from torch import nn\n",
|
|
"\n",
|
|
"%matplotlib inline"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"В заключительной части данной лабораторной работы мы применяем PyTorch — современный фреймворк для научных вычислений и глубокого обучения в Python.\n",
|
|
"\n",
|
|
"PyTorch во многом напоминает NumPy по принципам работы и синтаксису:\n",
|
|
"- оперирует многомерными массивами (в PyTorch они называются *тензорами*, или *Tensors*), которые являются прямым аналогом `ndarray` из NumPy;\n",
|
|
"- поддерживает похожий синтаксис для базовых операций: индексация, срезы, арифметические действия;\n",
|
|
"- реализует векторизованные вычисления и механизм broadcasting (широковещательное сложение);\n",
|
|
"- позволяет выполнять стандартные математические операции с интуитивно понятными методами (например, `tensor.mean()` в PyTorch аналогичен `array.mean()` в NumPy).\n",
|
|
"\n",
|
|
"\n",
|
|
"Некоторые основные компоненты PyTorch:\n",
|
|
"\n",
|
|
"1. Тензоры (Tensors) \n",
|
|
" - многомерные массивы с поддержкой вычислений на GPU;\n",
|
|
" - аналог `numpy.ndarray`, но с расширенными возможностями для машинного обучения.\n",
|
|
"\n",
|
|
"\n",
|
|
"2. Автоматическое дифференцирование (Autograd) \n",
|
|
" - механизм для автоматического вычисления градиентов;\n",
|
|
" - критически важен для обучения нейронных сетей (реализует алгоритм обратного распространения ошибки). \n",
|
|
" \n",
|
|
"\n",
|
|
"3. Модули (nn.Module) \n",
|
|
" - базовые строительные блоки для создания нейронных сетей;\n",
|
|
" - включают слои, функции активации, функции потерь и др.\n",
|
|
"\n",
|
|
"\n",
|
|
"4. Оптимизаторы (optim) \n",
|
|
" - реализации популярных алгоритмов оптимизации: SGD, Adam, RMSprop и др.;\n",
|
|
" - упрощают обновление параметров модели.\n",
|
|
"\n",
|
|
"\n",
|
|
"Ключевые возможности PyTorch:\n",
|
|
"\n",
|
|
"- Создание и обучение нейронных сетей любой сложности — от простых перцептронов до трансформеров.\n",
|
|
"- Вычисления на CPU и GPU с минимальным изменением кода (достаточно переместить тензоры на устройство `cuda`).\n",
|
|
"- Динамические вычислительные графы — в отличие от статических графов в некоторых других фреймворках, PyTorch строит граф операций «на лету», что упрощает отладку и эксперименты.\n",
|
|
"- Гибкость в проектировании архитектур — легко создавать кастомные слои и модели, переопределять методы обратного прохода.\n",
|
|
"\n",
|
|
"\n",
|
|
"Преимущества для исследований и разработки:\n",
|
|
"\n",
|
|
"- Интуитивный интерфейс, близкий к NumPy — низкий порог входа для тех, кто знаком с NumPy.\n",
|
|
"- Гибкая система наследования — возможность создавать собственные модули и слои.\n",
|
|
"- Обширное сообщество — множество туториалов, примеров, библиотек и форумов.\n",
|
|
"- Совместимость с другими библиотеками — лёгкая интеграция с NumPy, SciPy, Matplotlib и др.\n",
|
|
"- Производительность — ускорение вычислений на GPU (в 10–100 раз по сравнению с CPU для крупных задач).\n",
|
|
"\n",
|
|
"\n",
|
|
"Официальная документация: https://pytorch.org/docs/stable/index.html"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"В качестве примера вышесказанного переписанная на PyTorch метрика accuracy: "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "l4lzUxFY0ej5"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def accuracy(y_pred, y_true):\n",
|
|
" return torch.sum(y_pred == y_true) / len(y_true)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Содержание: \n",
|
|
"[1. Решение задачи логического «И»](#p_1) \n",
|
|
"[2. Решение задачи логического «ИЛИ»](#p_2) \n",
|
|
"[3. Решение задачи логического «исключающего ИЛИ»](#p_3)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 1. Решение задачи логического «И»<a id=\"p_1\"></a>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Представим входные данные для решения задачи логического «И» в виде тензора `X_data`.\n",
|
|
"\n",
|
|
"Операции в нейронных сетях (умножение весов, активация, вычисление градиентов) выполняются только с вещественными числами, поэтому через `.float()` приводим тензор к вещественному типу данных — иначе далее возможна ошибка типа данных."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "Ej0DmW9uXlG0",
|
|
"outputId": "b3821094-83d4-40f1-faa6-7773535b9ea6"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"X_data = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]]).float()\n",
|
|
"print(X_data)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Аналогичная операция с выходным вектором:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "khRrzE2fXlR4",
|
|
"outputId": "a1aa980e-fa15-4c7c-ba68-18f0f5588f7f"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"y_and_data = torch.tensor([0, 0, 0, 1]).reshape(-1, 1).float()\n",
|
|
"print(y_and_data)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"**Создание нейронной сети в PyTorch: последовательная модель**\n",
|
|
"\n",
|
|
"Один из самых простых и наглядных способов создать нейронную сеть в PyTorch — использовать `nn.Sequential`. Это контейнер, который организует слои в последовательный пайплайн: данные проходят через каждый слой по порядку, от входа к выходу — выход предыдущего слоя автоматически становится входом следующего.\n",
|
|
"\n",
|
|
"Создадим данным способом нейронную сеть `model_seq` с одним слоем нейронов.\n",
|
|
"\n",
|
|
"Пояснения к выбранным слоям в её архитектуре:\n",
|
|
" 1. `nn.Linear(in_features=2, out_features=1)` — полностью соединённый (плотный, полносвязный) слой. \n",
|
|
" \n",
|
|
" Параметры:\n",
|
|
" - `in_features=2` — размерность входного вектора (2 признака, соответствующие входам логической операции);\n",
|
|
" - `out_features=1` — размерность выхода (1 нейрон для итогового результата).\n",
|
|
" \n",
|
|
" Математическая операция: \n",
|
|
" $$\n",
|
|
" y = W \\cdot X + b,\n",
|
|
" $$ \n",
|
|
"\n",
|
|
" 2. `nn.Sigmoid()` — функция активации (сигмоида).\n",
|
|
" \n",
|
|
"После создания экземпляр `model_seq` можно:\n",
|
|
"- вызывать как функцию: `model_seq(X_data)` — прямой проход через сеть;\n",
|
|
"- получать параметры: `model_seq.parameters()` — для оптимизации;\n",
|
|
"- выводить структуру: печать `model_seq` покажет последовательность слоёв."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "gqr4pECOoDpG",
|
|
"outputId": "145244ca-2ede-498b-8a01-9841f6df041e"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"model_seq = nn.Sequential(\n",
|
|
" nn.Linear(in_features=2, out_features=1),\n",
|
|
" nn.Sigmoid()\n",
|
|
")\n",
|
|
"\n",
|
|
"model_seq"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Переберём все обучаемые параметры сети `model_seq` — результатом будет список тензоров, содержащих исходные веса и смещения сети:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "32TRT1kv9_Ln",
|
|
"outputId": "49e48ed3-3909-46d6-ac66-c0b59d1b9a5b"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"[x for x in model_seq.parameters()]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Обратите внимание в выводе на флаг `requires_grad=True`.\n",
|
|
"\n",
|
|
"Он указывает, что для этого тензора в процессе обратного распространения ошибки нужно будет вычислять градиенты (производные) всех математических операций, через которые далее в коде этот тензор проходит. Без этого флага параметры не будут обновляться при обучении. Флаг при задании тензора также можно переключать на `False`.\n",
|
|
"\n",
|
|
"Как это работает:\n",
|
|
"\n",
|
|
" 1. Во время прямого прохода (`forward`) PyTorch строит вычислительный граф, запоминая операции с тензорами, у которых `requires_grad=True`.\n",
|
|
" 2. При вызове `loss.backward()` автоматически вычисляются градиенты по всем параметрам с `requires_grad=True`, при этом значение каждое градиенты сохраняются в атрибуте `.grad` тензора.\n",
|
|
" 3. Оптимизатор (объект, реализующий алгоритм обновления параметров модели на основе вычисленных градиентов — например, SGD) использует эти градиенты для обновления параметров."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Рассмотрим всё на примере.\n",
|
|
"\n",
|
|
"Выберем в качестве оптимизатора параметров `model_seq` SGD (с достаточно большим шагом обучения `lr` для наглядности) и среднеквадратичную функцию потерь:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "DMJYrTlE-VqS"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"optimizer = torch.optim.SGD(model_seq.parameters(), lr=1.5)\n",
|
|
"criterion = nn.MSELoss()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"<a id=\"loop_and\"></a>Пропустим входные данные `X_data` через `model_seq`:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "5xiqGW-opJNK",
|
|
"outputId": "44183227-fe44-45f7-fb95-3dff575d702e"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"z = model_seq(X_data)\n",
|
|
"z"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Атрибут `grad_fn=<SigmoidBackward0>` в выводе обозначает:\n",
|
|
"- Тензор `z` — результат работы сигмоиды в последнем слое сети;\n",
|
|
"- PyTorch «запомнил» эту операцию в своём вычислительном графе;\n",
|
|
"- При обратном распространении ошибки градиенты будут корректно рассчитаны по формуле производной сигмоиды и переданы дальше в сеть."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Рассчитаем значение функции потерь:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "kn_XCMP8-fVm",
|
|
"outputId": "f5813ee6-a1f3-4b34-acee-77978ecf7b8f"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"loss = criterion(z, y_and_data)\n",
|
|
"loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"В атрибуте `grad_fn=<SigmoidBackward0>` аналогично видно, что в вычислительном графе к тензорам применена функция среднеквадратичной ошибки.\n",
|
|
"\n",
|
|
"Вычислим градиенты функции потерь по всем параметрам модели согласно вычислительному графу:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "kWc5ip2O-wKp"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"loss.backward()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Обновим с помошью оптимизатора все параметры сети: "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "dxh8RIJV-1Hy"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"optimizer.step()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Посмотрим значения обновлённых параметров:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "AQt5IglN_BJx",
|
|
"outputId": "b916e29e-874d-4396-a447-3556d5b5757e"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"[x for x in model_seq.parameters()]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Метод `optimizer.zero_grad()` вызывается обязательно перед каждым последующим обратным проходом (`loss.backward()`) в цикле обучения для **обнуления градиентов всех параметров** модели, управляемых этим оптимизатором.\n",
|
|
"\n",
|
|
"Почему это необходимо? \n",
|
|
"\n",
|
|
"В PyTorch градиенты по умолчанию накапливаются (суммируются) при каждом вызове `loss.backward()`. Если не обнулять градиенты:\n",
|
|
"- значения градиентов будут расти с каждой итерацией;\n",
|
|
"- веса модели начнут обновляться некорректно (с учётом «старых» градиентов);\n",
|
|
"- возможен эффект «взрывных градиентов» (gradients explode), когда значения становятся настолько большими, что превращаются в `NaN` (не число)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "KRD01eVD_yjE"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"optimizer.zero_grad()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Сделайте несколько проходов (итераций) от [первого шага обратного распространения ошибки](#loop_and) до данной ячейки. Убедитесь, что ошибка сети `loss` понижается."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Рассмотрим ещё один способ создания сети в PyTorch.\n",
|
|
"\n",
|
|
"**Создание нейронной сети в PyTorch: через ООП**\n",
|
|
"\n",
|
|
"В PyTorch принято определять архитектуры нейронных сетей как классы, наследующие от `nn.Module`. Это позволяет:\n",
|
|
"- структурировать код в виде повторно используемых компонентов;\n",
|
|
"- чётко разделять инициализацию параметров (`__init__`) и прямой проход (`forward`);\n",
|
|
"- легко расширять и модифицировать модели;\n",
|
|
"- использовать встроенные механизмы PyTorch (оптимизаторы, сохранение/загрузка моделей).\n",
|
|
"\n",
|
|
"Пример сети с одним полносвязным слоем и сигмоидой в качестве функции активации:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "PF5yli7WXlVd"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class OneLayerNetwork(nn.Module):\n",
|
|
" def __init__(self, n_inputs=2, n_outputs=1):\n",
|
|
" super().__init__()\n",
|
|
" self.model = nn.Sequential(\n",
|
|
" nn.Linear(in_features=n_inputs, out_features=n_outputs),\n",
|
|
" nn.Sigmoid()\n",
|
|
" )\n",
|
|
"\n",
|
|
" def forward(self, X):\n",
|
|
" return self.model(X)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Сейчас все слои для удобства по-прежнему собраны в `nn.Sequential`. Чтобы повысить гибкость модели, в методе `__init__` можно по отдельности задать все необходимые слои, а в `forward` — определять порядок их применения. Такой способ даёт больше контроля над архитектурой (пример разберём в одной из следующих лабораторных работ).\n",
|
|
"\n",
|
|
"Сигмоиду здесь можно также заменить на `nn.ReLU` или другую функцию активации — но для отделения от экземплятор с исходной архитектурой создайте другой класс (например, `OneLayerReLUNetwork()`)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Создадим экземпляр однослойной сети этого класса для решения задачи логического «И»:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "kIuQLzUIwVwC",
|
|
"outputId": "91aa25b7-aae4-4655-e83b-4abac88531c8"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"model_one_layer = OneLayerNetwork()\n",
|
|
"model_one_layer"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"По аналогии с прошлым ноутбуком зададим \n",
|
|
" - данные для обучения (входные и выходные);\n",
|
|
" - экземпляр нейронной сети (через последовательность `nn.Sequential()` или класс `OneLayerNetwork()`);\n",
|
|
" - количество эпох или итераций в процессе обучения (желательно больше 5);\n",
|
|
" - скорость обучение и коэффициент импульса;\n",
|
|
" - оптимизатор (попробуйте разные алгоритмы — `torch.optim.SGD()`, `torch.optim.Adam()` и другие) (значение `learning_rate` передаётся в `lr`) (учтите, что в `torch.optim.Adam()` нет импульса);\n",
|
|
" - функцию потерь (можно оставить `nn.MSELoss()`, однако поскольку решается задача бинарной классификации, можно выбрать бинарную кросс-энтропию `nn.BCELoss()`).\n",
|
|
" \n",
|
|
" \n",
|
|
"*Примечание*. Вы можете использовать для обучения сети либо код ниже, либо модифицированный на его основе — например, реализовать функцию обучения нейронной сети и проверки её работы."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "p09jzhOY3MyQ"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"y_true = y_and_data\n",
|
|
"\n",
|
|
"# Перебор seed для инициализации параметров\n",
|
|
"torch.manual_seed(seed=42)\n",
|
|
"\n",
|
|
"model = # Ваш код здесь\n",
|
|
"\n",
|
|
"epochs = # Ваш код здесь\n",
|
|
"\n",
|
|
"learning_rate = # Ваш код здесь\n",
|
|
"momentum = # Ваш код здесь\n",
|
|
"\n",
|
|
"optimizer = # Ваш код здесь\n",
|
|
"criterion = # Ваш код здесь"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"**Обучение нейронной сети для решения задачи логического «И»:**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/",
|
|
"height": 556
|
|
},
|
|
"id": "qKimvoStsZXv",
|
|
"outputId": "725ec1a2-2d68-4f24-91c6-3469a39efbc4"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"loss_history = []\n",
|
|
"\n",
|
|
"for epoch in range(epochs):\n",
|
|
" \n",
|
|
" # Метод .train() переводит модель в режим обучения\n",
|
|
" model.train()\n",
|
|
"\n",
|
|
" optimizer.zero_grad()\n",
|
|
"\n",
|
|
" z = model(X_data)\n",
|
|
" model_answer_interpretation = (z >= 0.5).float()\n",
|
|
"\n",
|
|
" loss = criterion(z, y_true)\n",
|
|
" \n",
|
|
" # Метод .item() извлекает скалярное значение из тензора loss\n",
|
|
" loss_history.append(loss.item())\n",
|
|
"\n",
|
|
" loss.backward()\n",
|
|
"\n",
|
|
" optimizer.step()\n",
|
|
" \n",
|
|
" # Метод .eval() переводит модель в режим валидации/тестирования (об этом в следующей лабораторной)\n",
|
|
" model.eval()\n",
|
|
"\n",
|
|
" if (epoch + 1) % 5 == 0:\n",
|
|
"\n",
|
|
" clear_output(True)\n",
|
|
" plt.plot(range(1, epoch+2), loss_history, label='Loss')\n",
|
|
" plt.title(f'Epoch: {epoch + 1}, Loss: {loss:.6f}')\n",
|
|
" plt.grid(True, alpha=0.3)\n",
|
|
" plt.legend(loc='best')\n",
|
|
" plt.show()\n",
|
|
"\n",
|
|
" print('Test:')\n",
|
|
" for i in range(len(X_data)):\n",
|
|
" # Метод .detach() создаёт копию тензора, отсоединённую от вычислительного графа,\n",
|
|
" # чтобы избежать ненужного вычисления градиентов для исходного тензора\n",
|
|
" print(f'Input: {X_data.numpy()[i]}, Output: {torch.round(z[i].detach() * 100).numpy() / 100}, Prediction: {model_answer_interpretation[i].numpy()}, True: {y_true[i].numpy()}')\n",
|
|
"\n",
|
|
" print(f'Accuracy: {accuracy(model_answer_interpretation, y_true):.2f}')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Проверим качество обучения сети:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"assert accuracy(model_answer_interpretation, y_and_data) == 1\n",
|
|
"assert np.mean(loss_history[:5]) > np.mean(loss_history[-5:])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Поэкспериметируйте с параметрами обучения — сделайте несколько вариантов с разными функциями потерь, оптимизаторами, значениями скорости обучения и коэффициента импульса. Выберите лучшие по качеству обучения и количеству эпох."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 2. Решение задачи логического «ИЛИ»<a id=\"p_2\"></a>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"По аналогии с задачей логического «И» решите задачу логического «ИЛИ».\n",
|
|
"\n",
|
|
"Выходные данные:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "UGFSsTGh4Xyp",
|
|
"outputId": "40e554c0-51a0-4adb-ca8b-8e46c83c2e7d"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"y_or_data = torch.tensor([0, 1, 1, 1]).reshape(-1, 1).float()\n",
|
|
"print(y_or_data)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Ваш код здесь"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Ваш код здесь"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Проверьте качество обучения сети:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"assert accuracy(model_answer_interpretation, y_or_data) == 1\n",
|
|
"assert np.mean(loss_history[:5]) > np.mean(loss_history[-5:])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Поэкспериметируйте с параметрами обучения — сделайте несколько вариантов с разными функциями потерь, оптимизаторами, значениями скорости обучения и коэффициента импульса. Выберите лучшие по качеству обучения и количеству эпох."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 3. Решение задачи логического «исключающего ИЛИ»<a id=\"p_3\"></a>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Выходные данные:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "gNigOR9N5X3z",
|
|
"outputId": "297e81ad-0eb7-403a-8dbf-0df7706c934e"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"y_xor_data = torch.tensor([0, 1, 1, 0]).reshape(-1, 1).float()\n",
|
|
"print(y_xor_data)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "6hL-mvyF5T58"
|
|
},
|
|
"source": [
|
|
"Напомним, что однослойной сетью задачу «исключающего ИЛИ» решить нельзя. В нейронную сеть требуется добавить ещё один слой с как минимум двумя нейронами.\n",
|
|
"\n",
|
|
"Модифицируем для этого класс `OneLayerNetwork()` в класс `TwoLayersNetwork()` — в последовательсти в `self.model` добавим ещё один полносвязный слой с сигмоидой для активации. Добавлен также параметр `n_hiddens` — количество нейронов в скрытом слое. Обратите внимание на задание размерности в последовательности слоёв."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"id": "TGcgUAFQtXn7"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class TwoLayersNetwork(nn.Module):\n",
|
|
" def __init__(self, n_inputs=2, n_hiddens=2, n_outputs=1):\n",
|
|
" super().__init__()\n",
|
|
" self.model = nn.Sequential(\n",
|
|
" nn.Linear(in_features=n_inputs, out_features=n_hiddens),\n",
|
|
" nn.Sigmoid(),\n",
|
|
" nn.Linear(in_features=n_hiddens, out_features=n_outputs),\n",
|
|
" nn.Sigmoid()\n",
|
|
" )\n",
|
|
"\n",
|
|
" def forward(self, X):\n",
|
|
" return self.model(X)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Создадим экземпляр двухслойной сети этого класса для решения задачи «исключающего ИЛИ»:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "afK0vsG1t2oL",
|
|
"outputId": "bab9683b-fefe-4734-f948-340a353b99c8"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"model_two_layers = TwoLayersNetwork()\n",
|
|
"model_two_layers"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"По аналогии с задачами логического «И» и логического «ИЛИ» решите «задачу исключающего ИЛИ»:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Ваш код здесь\n",
|
|
"\n",
|
|
"# Вариант задания сети с разным количество нейронов в скрытом слое\n",
|
|
"n_hiddens = 2\n",
|
|
"model = TwoLayersNetwork(n_hiddens=n_hiddens)\n",
|
|
"\n",
|
|
"# Ваш код здесь"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Проверьте качество обучения сети:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"assert accuracy(model_answer_interpretation, y_xor_data) == 1\n",
|
|
"assert np.mean(loss_history[:5]) > np.mean(loss_history[-5:])"
|
|
]
|
|
},
|
|
{
|
|
"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": {
|
|
"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
|
|
}
|