{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "practice_convnet_cifar10_strong.ipynb", "version": "0.3.2", "provenance": [], "collapsed_sections": [] }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "cells": [ { "metadata": { "id": "EmkK3B_LVS4t", "colab_type": "text" }, "cell_type": "markdown", "source": [ "---" ] }, { "metadata": { "id": "DqJ9IpyWVS4w", "colab_type": "text" }, "cell_type": "markdown", "source": [ "

Свёрточные нейронные сети: CIFAR10

" ] }, { "metadata": { "id": "2ZB1i5htVS4x", "colab_type": "text" }, "cell_type": "markdown", "source": [ "---" ] }, { "metadata": { "id": "fNQJ_TgsVS4y", "colab_type": "text" }, "cell_type": "markdown", "source": [ "В этом ноутбке мы посмотрим, насколько хорошо CNN будут предсказывать классы на более сложном датасете картинок -- CIFAR10. \n", "\n", "**Внимание:** Рассматривается ***задача классификации изображений***.\n", "\n", "***Свёрточная нейросеть (Convolutional Neural Network, CNN)*** - это многослойная нейросеть, имеющая в своей архитектуре помимо *полносвязных слоёв* (а иногда их может и не быть) ещё и **свёрточные слои (Conv Layers)** и **pooling-слои (Pool Layers)**. \n", "\n", "Собственно, название такое эти сети получили потому, что в основе их работы лежит операция **свёртки**.\n", "\n", "Сразу же стоит сказать, что свёрточные нейросети **были придуманы прежде всего для задач, связанных с изображениями**, следовательно, на вход они тоже \"ожидают\" изображение.\n", "\n", "* Например, вот так выглядит неглубокая свёрточная нейросеть, имеющая такую архитектуру: \n", "`Input -> Conv 5x5 -> Pool 2x2 -> Conv 5x5 -> Pool 2x2 -> FC -> Output`\n", "\n", " \n", " \n", "Свёрточные нейросети (простые, есть и намного более продвинутые) почти всегда строятся по следующему правилу: \n", "\n", "`INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*L -> FC` \n", "\n", "то есть: \n", "\n", "1). ***Входной слой***: batch картинок -- тензор размера `(batch_size, H, W, C)` или `(batch_size, C, H, W)`\n", "\n", "2). $M$ блоков (M $\\ge$ 0) из свёрток и pooling-ов, причём именно в том порядке, как в формуле выше. Все эти $M$ блоков вместе называют ***feature extractor*** свёрточной нейросети, потому что эта часть сети отвечает непосредственно за формирование новых, более сложных признаков поверх тех, которые подаются (то есть, по аналогии с MLP, мы опять же переходим к новому признаковому пространству, однако здесь оно строится сложнее, чем в обычных многослойных сетях, поскольку используется операция свёртки) \n", "\n", "3). $L$ штук FullyConnected-слоёв (с активациями). Эту часть из $L$ FC-слоёв называют ***classificator***, поскольку эти слои отвечают непосредственно за предсказание нужно класса (сейчас рассматривается задача классификации изображений)." ] }, { "metadata": { "id": "o09BQ_MrVS4z", "colab_type": "text" }, "cell_type": "markdown", "source": [ "\n", "

Свёрточная нейросеть на PyTorch

\n", "\n", "Ешё раз напомним про основные компоненты нейросети:\n", "\n", "- непосредственно, сама **архитектура** нейросети (сюда входят типы функций активации у каждого нейрона);\n", "- начальная **инициализация** весов каждого слоя;\n", "- метод **оптимизации** нейросети (сюда ещё входит метод изменения `learning_rate`);\n", "- размер **батчей** (`batch_size`);\n", "- количетсво **эпох** обучения (`num_epochs`);\n", "- **функция потерь** (`loss`); \n", "- тип **регуляризации** нейросети (`weight_decay`, для каждого слоя можно свой); \n", "\n", "То, что связано с ***данными и задачей***: \n", "- само **качество** выборки (непротиворечивость, чистота, корректность постановки задачи); \n", "- **размер** выборки; \n", "\n", "Так как мы сейчас рассматриваем **архитектуру CNN**, то, помимо этих компонент, в свёрточной нейросети можно настроить следующие вещи: \n", "\n", "- (в каждом ConvLayer) **размер фильтров (окна свёртки)** (`kernel_size`)\n", "- (в каждом ConvLayer) **количество фильтров** (`out_channels`) \n", "- (в каждом ConvLayer) размер **шага окна свёртки (stride)** (`stride`) \n", "- (в каждом ConvLayer) **тип padding'а** (`padding`) \n", "\n", "\n", "- (в каждом PoolLayer) **размер окна pooling'a** (`kernel_size`) \n", "- (в каждом PoolLayer) **шаг окна pooling'а** (`stride`) \n", "- (в каждом PoolLayer) **тип pooling'а** (`pool_type`) \n", "- (в каждом PoolLayer) **тип padding'а** (`padding`)" ] }, { "metadata": { "id": "wbB5RjlYVS40", "colab_type": "text" }, "cell_type": "markdown", "source": [ "

CIFAR10

" ] }, { "metadata": { "id": "6q-XNxf6VS41", "colab_type": "text" }, "cell_type": "markdown", "source": [ "" ] }, { "metadata": { "id": "wqx-IrGdVS42", "colab_type": "text" }, "cell_type": "markdown", "source": [ "**CIFAR10:** это набор из 60k картинок 32х32х3, 50k которых составляют обучающую выборку, и оставшиеся 10k - тестовую. Классов в этом датасете 10: `'plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'`." ] }, { "metadata": { "id": "iXgtzjz5VS43", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "# !pip install torch torchvision" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "9AQXJkzxVS47", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "import torch\n", "import torchvision\n", "from torchvision import transforms\n", "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "fc7hC-idVS4_", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "from tqdm import tqdm_notebook" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "GtnvcfS0VS5C", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "transform = transforms.Compose(\n", " [transforms.ToTensor(),\n", " transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])\n", "\n", "trainset = torchvision.datasets.CIFAR10(root='./data', train=True,\n", " download=True, transform=transform)\n", "trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,\n", " shuffle=True, num_workers=2)\n", "\n", "testset = torchvision.datasets.CIFAR10(root='./data', train=False,\n", " download=True, transform=transform)\n", "testloader = torch.utils.data.DataLoader(testset, batch_size=4,\n", " shuffle=False, num_workers=2)\n", "\n", "classes = ('plane', 'car', 'bird', 'cat',\n", " 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "tRmesD01VS5H", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "# случайный индекс от 0 до размера тренировочной выборки\n", "i = np.random.randint(low=0, high=50000)\n", "\n", "plt.imshow(trainloader.dataset.train_data[i]);" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "3I-smSRhVS5M", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Напишем свёрточную нейросеть для предсказания на CIFAR10." ] }, { "metadata": { "id": "Na7SmsRpVS5O", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "import torch.nn as nn\n", "import torch.nn.functional as F" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "SStmnkaDVS5R", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class SimpleConvNet(torch.nn.Module):\n", " def __init__(self):\n", " # вызов конструктора класса nn.Module()\n", " super(SimpleConvNet, self).__init__()\n", " # feature extractor\n", " self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)\n", " self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n", " self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)\n", " # classificator\n", " self.fc1 = nn.Linear(5 * 5 * 16, 120)\n", " self.fc2 = nn.Linear(120, 84)\n", " self.fc3 = nn.Linear(84, 10)\n", "\n", " def forward(self, x):\n", " x = self.pool(F.relu(self.conv1(x)))\n", " x = self.pool(F.relu(self.conv2(x)))\n", " # print(x.shape)\n", " x = x.view(-1, 5 * 5 * 16)\n", " x = F.relu(self.fc1(x))\n", " x = F.relu(self.fc2(x))\n", " x = self.fc3(x)\n", " return x" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "W6sP6_LQVS5U", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Обучим:" ] }, { "metadata": { "id": "k2m9rgUqVS5V", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "from tqdm import tqdm_notebook" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "jd9Bqjr_VS5Y", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "net = SimpleConvNet()\n", "\n", "loss_fn = torch.nn.CrossEntropyLoss()\n", "\n", "learning_rate = 1e-4\n", "optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate)\n", "\n", "# итерируемся\n", "for epoch in tqdm_notebook(range(3)):\n", "\n", " running_loss = 0.0\n", " for i, batch in enumerate(tqdm_notebook(trainloader)):\n", " # так получаем текущий батч\n", " X_batch, y_batch = batch\n", "\n", " # обнуляем веса\n", " optimizer.zero_grad()\n", "\n", " # forward + backward + optimize\n", " y_pred = net(X_batch)\n", " loss = loss_fn(y_pred, y_batch)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " running_loss += loss.item()\n", " # выводим качество каждые 2000 батчей\n", " if i % 2000 == 1999:\n", " print('[%d, %5d] loss: %.3f' %\n", " (epoch + 1, i + 1, running_loss / 2000))\n", " running_loss = 0.0\n", "\n", "print('Обучение закончено')" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "q4_QAnQqVS5d", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Посмотрим на accuracy на тестовом датасете:" ] }, { "metadata": { "id": "LJYGIIK4VS5e", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class_correct = list(0. for i in range(10))\n", "class_total = list(0. for i in range(10))\n", "\n", "with torch.no_grad():\n", " for data in testloader:\n", " images, labels = data\n", " y_pred = net(images)\n", " _, predicted = torch.max(y_pred, 1)\n", " c = (predicted == labels).squeeze()\n", " for i in range(4):\n", " label = labels[i]\n", " class_correct[label] += c[i].item()\n", " class_total[label] += 1\n", "\n", "for i in range(10):\n", " print('Accuracy of %5s : %2d %%' % (\n", " classes[i], 100 * class_correct[i] / class_total[i]))" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "akn5NOqBVS5l", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Проверим работу нейросети визуально (позапускайте ячейку несколько раз):" ] }, { "metadata": { "id": "1COUZ6-JVS5m", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "i = np.random.randint(low=0, high=10000)\n", "\n", "def visualize_result(index):\n", " image = testloader.dataset.test_data[index]\n", " plt.imshow(image)\n", " \n", " image = transform(image) # не забудем отмасштабировать!\n", " \n", " y_pred = net(image.view(1, 3, 32, 32))\n", " _, predicted = torch.max(y_pred, 1)\n", " \n", " plt.title(f'Predicted: {classes[predicted.numpy()[0]]}')\n", "\n", "visualize_result(i)" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "W0OjBCVzVS5q", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Улучшим свёрточную нейросеть: поэкспериментируем с архитектурой (количество слоёв, порядок слоёв), с гиперпараметрами слоёв (размеры kernel_size, размеры pooling'а, количество kernel'ов в свёрточном слое) и с гиперпараметрами, указанными в \"Компоненты нейросети\" (см. памятку выше)." ] }, { "metadata": { "id": "T3CbIaXMVS5r", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class BetterConvNet(nn.Module):\n", " def __init__(self):\n", " # вызов конструктора класса nn.Module()\n", " super(BetterConvNet, self).__init__()\n", " \n", " self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n", " \n", " self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)\n", " self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)\n", " self.conv3 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5)\n", " \n", " self.fc1 = nn.Linear(3 * 3 * 32, 120)\n", " self.fc2 = nn.Linear(120, 84)\n", " self.fc3 = nn.Linear(84, 10)\n", "\n", " def forward(self, x):\n", " x = self.pool(F.relu(self.conv1(x)))\n", " x = self.pool(self.conv3(F.relu(self.conv2(x))))\n", "# print(x.shape)\n", " x = x.view(-1, 3 * 3 * 32)\n", " x = F.relu(self.fc1(x))\n", " x = F.relu(self.fc2(x))\n", " x = self.fc3(x)\n", " return x" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "9lYy033EVS5u", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Обучим:" ] }, { "metadata": { "id": "neY20gjLVS5u", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "from tqdm import tqdm_notebook" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "RUd5NtraVS5y", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "net = BetterConvNet()\n", "\n", "loss_fn = torch.nn.CrossEntropyLoss()\n", "\n", "learning_rate = 1e-3\n", "optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate)\n", "\n", "# итерируемся\n", "for epoch in tqdm_notebook(range(5)):\n", "\n", " running_loss = 0.0\n", " for i, batch in enumerate(tqdm_notebook(trainloader)):\n", " # так получаем текущий батч\n", " X_batch, y_batch = batch\n", "\n", " # обнуляем веса\n", " optimizer.zero_grad()\n", "\n", " # forward + backward + optimize\n", " y_pred = net(X_batch)\n", " loss = loss_fn(y_pred, y_batch)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " # выведем текущий loss\n", " running_loss += loss.item()\n", " # выведем качество каждые 2000 батчей\n", " if i % 2000 == 1999:\n", " print('[%d, %5d] loss: %.3f' %\n", " (epoch + 1, i + 1, running_loss / 2000))\n", " running_loss = 0.0\n", "\n", "print('Обучение закончено')" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "FMoLalcpVS53", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class_correct = list(0. for i in range(10))\n", "class_total = list(0. for i in range(10))\n", "\n", "with torch.no_grad():\n", " for data in testloader:\n", " images, labels = data\n", " y_pred = net(images)\n", " _, predicted = torch.max(y_pred, 1)\n", " c = (predicted == labels).squeeze()\n", " for i in range(4):\n", " label = labels[i]\n", " class_correct[label] += c[i].item()\n", " class_total[label] += 1\n", "\n", "for i in range(10):\n", " print('Accuracy of %5s : %2d %%' % (\n", " classes[i], 100 * class_correct[i] / class_total[i]))" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "fUpLQxn0VS59", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Если качество ~70% в среднем, то текущая нейросеть вполне неплоха (однако на этом датасете известны архитектуры, дающие 95+% качества)." ] }, { "metadata": { "id": "4HIYgOvBVS59", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Посмотрим визуально на работу нейросети:" ] }, { "metadata": { "scrolled": true, "id": "ngmmxIcnVS5-", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "i = np.random.randint(low=0, high=10000)\n", "\n", "def visualize_result(index):\n", " image = testloader.dataset.test_data[index]\n", " plt.imshow(image)\n", " \n", " image = transform(image) # не забудем отмасштабировать!\n", " \n", " y_pred = net(image.view(1, 3, 32, 32))\n", " _, predicted = torch.max(y_pred, 1)\n", " \n", " plt.title(f'Predicted: {classes[predicted.numpy()[0]]}')\n", "\n", "visualize_result(i)" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "ujm2iRccVS6B", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Попробуем обучить ещё более сильную нейросеть:" ] }, { "metadata": { "id": "cuGcK9uwVS6C", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class StrongConvNet(nn.Module):\n", " def __init__(self):\n", " # вызов конструктора класса nn.Module()\n", " super(StrongConvNet, self).__init__()\n", " \n", " self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n", " \n", " self.dropout = nn.Dropout(p=0.2)\n", " \n", " self.conv1 = nn.Conv2d(in_channels=3, out_channels=8, kernel_size=5)\n", " self.bn1 = nn.BatchNorm2d(8)\n", " self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=1)\n", " self.bn2 = nn.BatchNorm2d(16)\n", " self.conv3 = nn.Conv2d(in_channels=16, out_channels=16, kernel_size=3)\n", " self.bn3 = nn.BatchNorm2d(16)\n", " self.conv4 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=1)\n", " self.bn4 = nn.BatchNorm2d(32)\n", " self.conv5 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3)\n", " self.bn5 = nn.BatchNorm2d(32)\n", " \n", " self.fc1 = nn.Linear(4 * 4 * 32, 128)\n", " self.fc2 = nn.Linear(128, 10)\n", "\n", " def forward(self, x):\n", " x = self.bn1(F.relu(self.conv1(x)))\n", " x = self.pool(x)\n", " x = self.bn2(F.relu(self.conv2(x)))\n", " x = self.bn3(F.relu(self.conv3(x)))\n", " x = self.pool(x)\n", " x = self.bn4(F.relu(self.conv4(x)))\n", " x = self.bn5(F.relu(self.conv5(x)))\n", "# print(x.shape)\n", " x = x.view(-1, 4 * 4 * 32)\n", " x = F.relu(self.fc1(x))\n", " x = self.dropout(x)\n", " x = self.fc2(x)\n", " return x" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "JBpSPH3RVS6I", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Обучим:" ] }, { "metadata": { "id": "CeXawofzVS6J", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "from tqdm import tqdm_notebook" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "sERs9TlFVS6M", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "from torch.optim import lr_scheduler" ], "execution_count": 0, "outputs": [] }, { "metadata": { "scrolled": true, "id": "dGiAuDBMVS6P", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "net = StrongConvNet()\n", "\n", "loss_fn = torch.nn.CrossEntropyLoss()\n", "\n", "num_epochs = 5\n", "\n", "optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate)\n", "learning_rate = 1e-3\n", "# новая фишка -- динамически изменяем LR\n", "scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs)\n", "\n", "for epoch in tqdm_notebook(range(num_epochs)):\n", " \n", " scheduler.step()\n", " \n", " running_loss = 0.0\n", " for i, batch in enumerate(tqdm_notebook(trainloader)):\n", " X_batch, y_batch = batch\n", " \n", " optimizer.zero_grad()\n", "\n", " y_pred = net(X_batch)\n", " loss = loss_fn(y_pred, y_batch)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " running_loss += loss.item()\n", " if i % 2000 == 1999:\n", " print('[%d, %5d] loss: %.3f' %\n", " (epoch + 1, i + 1, running_loss / 2000))\n", " running_loss = 0.0\n", "\n", "print('Обучение закончено')" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "V2lPUzf1VS6S", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "class_correct = list(0. for i in range(10))\n", "class_total = list(0. for i in range(10))\n", "\n", "with torch.no_grad():\n", " for data in testloader:\n", " images, labels = data\n", " y_pred = net(images)\n", " _, predicted = torch.max(y_pred, 1)\n", " c = (predicted == labels).squeeze()\n", " for i in range(4):\n", " label = labels[i]\n", " class_correct[label] += c[i].item()\n", " class_total[label] += 1\n", "\n", "for i in range(10):\n", " print('Accuracy of %5s : %2d %%' % (\n", " classes[i], 100 * class_correct[i] / class_total[i]))" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "ziioVipBVS6Z", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Посмотрим визуально на работу нейросети:" ] }, { "metadata": { "scrolled": true, "id": "KXagscMmVS6a", "colab_type": "code", "colab": {} }, "cell_type": "code", "source": [ "i = np.random.randint(low=0, high=10000)\n", "\n", "def visualize_result(index):\n", " image = testloader.dataset.test_data[index]\n", " plt.imshow(image)\n", " \n", " image = transform(image) # не забудем отмасштабировать!\n", " \n", " y_pred = net(image.view(1, 3, 32, 32))\n", " _, predicted = torch.max(y_pred, 1)\n", " \n", " plt.title(f'Predicted: {classes[predicted.numpy()[0]]}')\n", "\n", "visualize_result(i)" ], "execution_count": 0, "outputs": [] }, { "metadata": { "id": "Nl46YOn9VS6c", "colab_type": "text" }, "cell_type": "markdown", "source": [ "Даже обучив более глубокую и прокаченную (BatchNorm, Dropout) нейросеть на этих данных мы видим, что качество нас всё ещё не устраивает, в реальной жизни необходимо ошибаться не больше, чем на 5%, а часто и это уже много. Как же быть, ведь свёрточные нейросети должны хорошо классифицировать изображения? \n", "\n", "К сожалению, обучение нейросети с нуля на не очень большой выборке (а здесь она именно такая) часто приводит к переобучению, что плохо сказывается на тестовом качестве. \n", "\n", "Для того, чтобы получить более качественную модель, часто **до**обучают сильную нейросеть, обученную на ImageNet, то есть используют технику Transfer Learning. О ней речь пойдёт далее в нашем курсе." ] }, { "metadata": { "id": "jKpCkwdoVS6d", "colab_type": "text" }, "cell_type": "markdown", "source": [ "

Полезные ссылки

" ] }, { "metadata": { "id": "xIvuXTqGVS6e", "colab_type": "text" }, "cell_type": "markdown", "source": [ "1). *Примеры написания нейросетей на PyTorch (официальные туториалы) (на английском): https://pytorch.org/tutorials/beginner/pytorch_with_examples.html#examples \n", "https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html*" ] }, { "metadata": { "id": "Ek6M_VSsVS6e", "colab_type": "text" }, "cell_type": "markdown", "source": [ "2). Курс Стэнфорда: http://cs231n.github.io/" ] }, { "metadata": { "id": "OnIpwKGXVS6f", "colab_type": "text" }, "cell_type": "markdown", "source": [ "3). Практически исчерпывающая информация по основам свёрточных нейросетей (из cs231n) (на английском): \n", "\n", "http://cs231n.github.io/convolutional-networks/ \n", "http://cs231n.github.io/understanding-cnn/ \n", "http://cs231n.github.io/transfer-learning/" ] }, { "metadata": { "id": "yFuto766VS6g", "colab_type": "text" }, "cell_type": "markdown", "source": [ "4). Видео о Computer Vision от Andrej Karpathy: https://www.youtube.com/watch?v=u6aEYuemt0M" ] } ] }