{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "from matplotlib import pyplot as plt\n",
    "\n",
    "import torch\n",
    "from torch import nn\n",
    "\n",
    "from torch.utils.data import DataLoader\n",
    "\n",
    "from torchvision import datasets, transforms"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "print(torch.__version__)",
   "id": "54e90d51ae4bd57c",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Проверьте, что у `torch` есть доступ к графическому ускорителю и драйверу `cuda`.",
   "id": "ffa8f8dc18b1d743"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "### Датасет MNIST и полносвязные сети",
   "id": "f611578a1ade6254"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "В этот раз рассмотрим немного более сложный датасет. Вместо двумерных данных перейдем к данным 28х28-мерным. Исследуем классический датасет MNIST, состоящий из рукописных монохромных чисел.",
   "id": "707fa4b627140213"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.1307,), (0.3081,))  # почему именно эти значения? напишите в комментариях\n",
    "])\n",
    "\n",
    "train_dataset = datasets.MNIST(root='./data', train=True,  download=True, transform=transform)\n",
    "test_dataset  = datasets.MNIST(root='./data', train=False, download=True, transform=transform)\n",
    "\n",
    "train_dataloader = DataLoader(\n",
    "    train_dataset,\n",
    "    batch_size=32,\n",
    "    shuffle=True   # перемешиваем на каждом эпохе\n",
    ")\n",
    "valid_dataloader = DataLoader(\n",
    "    test_dataset,\n",
    "    batch_size=32,\n",
    "    shuffle=False  # порядок важен при оценке\n",
    ")"
   ],
   "id": "4b3ef463f5ecd4ef",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Определим полносвязную нейронную сеть через nn.Sequential. Определите ее таким образом, чтобы суммарное количество параметров не превышало 100 тысяч. Функции активации -- сигмоиды, все слои полносвязные, ничего лишнего.",
   "id": "dc71f2029d890793"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "model = nn.Sequential(\n",
    "    nn.Flatten(),                # [B, 1, 28, 28] → [B, 784]\n",
    "    ... # место для кода\n",
    ")"
   ],
   "id": "885010d843114de4",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Выведите слои и общее количество параметров в сети",
   "id": "867ecc8eed38edc3"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "1018e4e41f6c5a2b",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Какая используется в данном случае инициализация весов по умолчанию? Как ее можно изменить? Используйте нормальное распределение Ксавьера для инициализации весов.",
   "id": "fd9187b7bd724916"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "8938dbf8b63ae023",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Определим функцию потерь и оптимизатор",
   "id": "40b0ae2c3fa0f9da"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.SGD(model.parameters())\n",
    "loaders = {\"train\": train_dataloader, \"valid\": valid_dataloader}"
   ],
   "id": "dc8c05d44c89ed6d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
    "model = model.to(device)"
   ],
   "id": "974cb273e95499d9",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Напишите цикл обучения на 30 эпох",
   "id": "aedba0be527c346b"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "max_epochs = 30\n",
    "\n",
    "statistics = {\n",
    "    \"train_loss\": [],\n",
    "    \"train_acc\": [],\n",
    "    \"val_loss\": [],\n",
    "    \"val_acc\": []\n",
    "}\n",
    "\n",
    "for epoch in range(1, max_epochs+1):\n",
    "    # TRAIN\n",
    "    model.train()\n",
    "    running_loss, correct, total = 0.0, 0, 0\n",
    "    for images, labels in train_dataloader:\n",
    "        images, labels = images.to(device), labels.to(device)\n",
    "        ... # место для кода\n",
    "    statistics[\"train_loss\"].append(running_loss / total)\n",
    "    statistics[\"train_acc\"].append(correct / total)\n",
    "\n",
    "    # EVAL\n",
    "    model.eval()\n",
    "    running_loss, correct, total = 0.0, 0, 0\n",
    "    with torch.no_grad():\n",
    "        for images, labels in valid_dataloader:\n",
    "            images, labels = images.to(device), labels.to(device)\n",
    "            ... # место для кода\n",
    "    statistics[\"val_loss\"].append(running_loss / total)\n",
    "    statistics[\"val_acc\"].append(correct / total)\n",
    "\n",
    "    print(f\"Epoch {epoch:3d}/{max_epochs} │ Train Loss: {statistics['train_loss'][-1]:.4f}  Acc: {statistics['train_acc'][-1]:.4f} │ Val Loss: {statistics['val_loss'][-1]:.4f}  Acc: {statistics['val_acc'][-1]:.4f}\")"
   ],
   "id": "a02731c96c9088d7",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Сделайте визуализацию значений функции потерь, а также точности на тренировочных и валидационных данных с помощью `matplotlib`. Найдите точку ранней остановки",
   "id": "aae917a2d50d53c2"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "4c3f9129c993e3e1",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "Теперь заменим функции активации. Выберите новую функцию активации, исходя из своего номера в группе. Возьмите остаток от деления своего номера на 5.\n",
    "\n",
    "0. ReLU\n",
    "1. GELU\n",
    "2. SiLU\n",
    "3. Leaky ReLU\n",
    "4. ELU\n",
    "\n",
    "С модифицированной нейронной сетью проведите тот же эксперимент, что и выше, и сравните результаты (выведите их на один график для визуализации). Подходит ли существующая инициализация весов для новой функции активации? Если нет, исправьте это."
   ],
   "id": "d394e6b431985c4b"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "15f8649b19e90153",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "Выберите более продвинутый оптимизатор. Снова возьмите свой номер в группе, разделите на 4 и выберите оптимизатор в зависимости от остатка от деления:\n",
    "\n",
    "0. SGD + momentum\n",
    "1. Adam\n",
    "2. AdaGrad\n",
    "3. Adam + weight decay\n",
    "\n",
    "Результаты сравните с предыдущими экспериментами"
   ],
   "id": "3d80067291beb9dc"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Добавьте DropOut слои в нейросеть. Сравните скорость сходимости и итоговые результаты.",
   "id": "16baa47dbdde5cf"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "9718c185e222d9e2",
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
