Занятие 8
Лабораторная 8¶
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
import torch
from torch import nn
from torch.nn import functional as F
from torch.utils.data import TensorDataset, DataLoader
from torchvision import transforms as tfs
import os
from torchvision.datasets import MNIST
1.Свёртка¶
Сверточная операция(свёртка) в сверточной нейронной сети (CNN) используется для извлечения признаков из входных данных, таких как изображения. Эта операция выполняется с помощью фильтров (ядер).
Двумерная свертка¶
В случае изображений, мы имеем двумерное изображение $I$ с размерностью$H \times W$ и двумерное ядро (или фильтр) $F$ размерностью $M \times N$.
Двумерная свертка изображения с ядром выполняется путем скольжения фильтра $F$ по изображению и вычисления суммы поэлементных произведений пикселей изображения и элементов фильтра:
$(I * F)[i, j]$ = $\sum_{m=0}^{M-1} \sum_{n=0}^{N-1} I[i+m, j+n] \cdot F[m, n] $
Это вычисление содержит операцию свертки. Процесс повторяется для каждой позиции на изображении и в результате получается новая матрица, называемая feature map, которая выявляет определенные признаки изображения.
Скорее всего сейчас ничего непонятно, давайте рассмотрим пример.
Предположим, у нас есть следующее изображение (представленное в виде матрицы): \begin{bmatrix} 1 & 2 & 3 & 4\\ 2 & 1 & 1 & 3\\ 4 & 3 & 1 & 0\\ 0 & 0 & 1 & 5\\ \end{bmatrix}
$a_{11} = 1, a_{12} = 2, a_{13} = 3, a_{14} = 4, a_{21} = 2$ и т.д
И пусть у нас будет следующее ядро свертки (или фильтр):
\begin{bmatrix} 1 & 2 \\ 7 & 1 \\ \end{bmatrix}$b_{11} = 1, b_{12} = 2, b_{21} = 7, b_{22} = 1$
Выполним свертку изображения с этим ядром шаг за шагом.
Результатом свёртки будет матрица размером 3 $\times$ 3.
\begin{bmatrix} с_{11} & c_{12} & c_{13} \\ c_{21} & c_{22} & c_{23} \\ c_{31} & c_{32} & c_{33} \\ \end{bmatrix}$c_{11} = a_{11} * b_{11} + a_{12} * b_{12} + a_{21} * b_{21} + a_{22} * b_{22}$
$c_{12} = a_{12} * b_{11} + a_{13} * b_{12} + a_{22} * b_{21} + a_{23} * b_{22}$
$c_{13} = a_{13} * b_{11} + a_{14} * b_{12} + a_{23} * b_{21} + a_{24} * b_{22}$
$c_{21} = a_{21} * b_{11} + a_{22} * b_{12} + a_{31} * b_{21} + a_{32} * b_{22}$
$c_{22} = a_{22} * b_{11} + a_{23} * b_{12} + a_{32} * b_{21} + a_{33} * b_{22}$
$c_{23} = a_{23} * b_{11} + a_{24} * b_{12} + a_{33} * b_{21} + a_{34} * b_{22}$
$c_{31} = a_{31} * b_{11} + a_{32} * b_{12} + a_{41} * b_{21} + a_{42} * b_{22}$
$c_{32} = a_{32} * b_{11} + a_{33} * b_{12} + a_{42} * b_{21} + a_{43} * b_{22}$
$c_{33} = a_{33} * b_{11} + a_{34} * b_{12} + a_{43} * b_{21} + a_{44} * b_{22}$
Процесс свёртки обычно включает в себя два основных гиперпараметра:
Размер окна свёртки: Это размер ядра. В примере выше размер ядра 2$×$2.
Шаг (Stride): Это расстояние между центрами соседних окон ядер. В примере выше stride = 1.
Задача1 (1 балл)¶
Дано входное изображение(двумерный тензор):
\begin{bmatrix} 1 & 2 & 3 & 4 & 6\\ 2 & 1 & 1 & 3 & 1\\ 4 & 3 & 1 & 0 & 0\\ 0 & 0 & 1 & 5 & 7\\ 3 & 10 & 4 & 5 & 1\\ \end{bmatrix}Дана свёртка:
\begin{bmatrix} 1 & 0 & 0\\ 0 & 1 & 1\\ 1 & 0 & 1\\ \end{bmatrix}Примените данную свёртку к изображению.
2.Pooling¶
Pooling (или пулинг) в сверточных нейронных сетях (CNN) используется для уменьшения размерности признаковых карт, получаемых после сверточных слоев. Он помогает упрощать и концентрировать информацию, уменьшая объем вычислений и сделав модель более устойчивой к масштабным вариациям входных данных.
Основные типы пулинга в CNN:
Max Pooling: Для каждой области во входной признаковой карте выбирается максимальное значение. Max pooling помогает выделить самые активные признаки из пространственных областей.
Average Pooling: Для каждой области входной признаковой карты вычисляется среднее значение. Average pooling позволяет усреднить информацию из пространственных областей.
Процесс пулинга обычно включает в себя два основных параметра:
Размер окна пулинга: Это размер области, для которой вычисляется максимальное или среднее значение. Обычно используются окна размером 2x2 или 3x3.
Шаг (Stride): Это расстояние между центрами соседних окон пулинга. Обычно используется шаг 2, что делает пулинг-слои в полтора раза меньше по размерам.
Рассмотрим пример:
Исходная матрица: \begin{bmatrix} 1 & 1 & 1 & 0 \\ 3 & 2 & 2 & 4 \\ 3 & 2 & 3 & 2 \\ 0 & 1 & 3 & 3 \\ \end{bmatrix}
Применение Max Pooling с окном 2x2 и шагом 2:
Выбираем первую область 2x2 из исходной матрицы: \begin{bmatrix} 1 & 1 \\ 3 & 2 \\ \end{bmatrix} Максимальное значение здесь: 3
Выбираем вторую область 2x2 (с пропуском каждой второй строки и столбца) из исходной матрицы: \begin{bmatrix} 1 & 0 \\ 2 & 1 \\ \end{bmatrix} Максимальное значение здесь: 2
Выбираем третью область 2x2 (с пропуском каждой второй строки и столбца) из исходной матрицы: \begin{bmatrix} 3 & 2 \\ 0 & 1 \\ \end{bmatrix} Максимальное значение здесь: 3
Таким образом, после применения операции Max Pooling с окном 2x2 и шагом 2 получаем новую матрицу: \begin{bmatrix} 3 & 2 \\ 3 & 3 \\ \end{bmatrix}
Задача2 (2 балла)¶
Реализуйте функцию, принимающую на вход матрицу, свёртку для неё(размера 2 на 2) и вычисляющую результат свёртки с stride = 1.
3.Пэддинг¶
Пэддинг (padding) - это процесс добавления нулей или других значения по краям входной матрицы в глубоком обучении. Пэддинг обычно используется в сверточных нейронных сетях перед применением операции свертки или пулинга. Основная цель пэддинга состоит в том, чтобы сохранить размерность входных данных или упростить реализацию операций свертки и пулинга.
Задача3 (1 балл)¶
Реализуйте функцию, принимающую на вход матрицу и осуществяющую padding.
4.Свёрточная Нейронная сеть¶
Свёрточная нейронная сеть (Convolutional Neural Network, CNN) - это особый вид нейронных сетей, разработанный для обработки и анализа многомерных данных, таких как изображения. CNN успешно применяются в компьютерном зрении, распознавании образов, анализе временных рядов и других областях, где важна обработка многомерных данных.
Основные компоненты свёрточной нейронной сети:
- Сверточный слой (Convolutional Layer): Этот слой выполняет операцию свертки, когда ядро фильтра применяется к входным данным для извлечения признаков.
- Пулинговый слой (Pooling Layer): Данный слой уменьшает размерность пространства признаков путем объединения (например, максимум или среднее значения в каждой области).
- Полносвязные слои (Fully Connected Layers): Эти слои используются для принятия решений на основе признаков, извлеченных предыдущими слоями.
- Функции активации (Activation Functions): Обычно применяются функции активации, такие как ReLU (Rectified Linear Unit), чтобы вводить нелинейность в сеть и улучшить ее способность обобщения.
Преимущества свёрточных нейронных сетей:
- Работа с пространственной структурой данных: CNN способны учитывать пространственную локальность и общие закономерности во входных данных, таких как пиксели изображений.
- Работа с сокращенной размерностью: Пулинговые слои позволяют уменьшить размерность пространства признаков, упрощая задачу обработки.
- Способность извлекать признаки: Свёрточные слои способны извлекать локальные признаки из входных данных, позволяя сети автоматически выявлять особенности в данных.
data_tfs = tfs.Compose([
tfs.ToTensor(),
tfs.Normalize((0.5), (0.5))
])
# install for train and test
root = './'
train_dataset = MNIST(root, train=True, transform=data_tfs, download=True)
val_dataset = MNIST(root, train=False, transform=data_tfs, download=True)
train_dataloader = DataLoader(train_dataset, batch_size=128)
valid_dataloader = DataLoader(val_dataset, batch_size=128)
Архитектура LeNet¶
LeNet — это структура(архитектура) сверточной нейронной сети, предложенная в 1998 году.
Архитектура:
Входное изображение размером 28(height)$×$28(widht)$×$(1 channel).
Свёртка1(ядро: 5$×$5, пэддинг: 2, каналов на выходе: 6). На выходе: 28(height)$×$28(widht)$×$(6 channels)
Pooling1(ядро: 2$×$2, stride: 2). На выходе: 14(height)$×$14(widht)$×$(6 channels)
Свёртка2(ядро: 5$×$5, пэддинг: 0, каналов на выходе: 16). На выходе: 10(height)$×$10(widht)$×$(16 channels)
Pooling2(ядро: 2$×$2, stride: 2). На выходе: 5(height)$×$5(widht)$×$(16 channels)
Линейный слой1 (120 нейронов)
Линейный слой2 (84 нейрона)
Выходной слой (10 нейронов)
После Свёртки1 и Свёртки два следует функция активации.
После Pooling2 следует flatten.
Параметры LeNet¶
Поскольку обучение нейронной сети это поиск оптимальных параметров, давайте посчитаем количество параметров (весов) LeNet.
Свёртка1: 6 ядер размера 5$×$5 + 6 параметров bias = 156.
Pooling1: параметров нет!
Свёртка2: 16 ядер размера 5$×$5$×$6 + 16 параметров bias = 2416.
Pooling2: параметров нет!
Линейный слой1: входной слой: 5$×$5$×$16 = 400, выходной: 120. Число параметров 400$×$120 + 120 = 48120.
Линейный слой2: входной слой: 120, выходной: 84. Число параметров 120$×$84 + 84 = 10164.
Выходной слой: входной слой: 84, выходной: 10. Число параметров 84$×$10 + 10 = 850.
Итоговое число параметров: 156 + 2416 + 48120 + 10164 + 850 = 61706.
Реализуем архитектуру на python в виде класса LeNet.
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# Свёртка1
self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding = 2)
# Свёртка2
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
# Pooling1
self.pool1 = nn.MaxPool2d(kernel_size=2, stride = 2)
# Pooling2
self.pool2 = nn.MaxPool2d(kernel_size=2, stride = 2)
# Линейный слой1
self.fc1 = nn.Linear(16 * 5 * 5, 120)
# Линейный слой2
self.fc2 = nn.Linear(120, 84)
# Выходной слой
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Свёртка1 + активация Тангес
x = F.tanh(self.conv1(x))
# Pooling1
x = self.pool1(x)
# Свёртка1 + активация Тангес
x = F.tanh(self.conv2(x))
# Pooling2
x = self.pool2(x)
# Flatten
size_ = int(x.nelement() / x.shape[0])
x = x.view(-1, size_)
# Линейный слой1 + активация Тангес
x = F.tanh(self.fc1(x))
# Линейный слой2 + активация Тангес
x = F.tanh(self.fc2(x))
# Выходной слой
x = self.fc3(x)
return x
device = "cuda" if torch.cuda.is_available() else "cpu"
model = LeNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
loaders = {"train": train_dataloader, "valid": valid_dataloader}
Обратите внимание! Мы используем .to(device), что означает, что мы перемещаем наши объекты на GPU и обучаемся там же.
max_epochs = 10
accuracy = {"train": [], "valid": []}
for epoch in range(max_epochs):
for k, dataloader in loaders.items():
epoch_correct = 0
epoch_all = 0
for x_batch, y_batch in dataloader:
if k == "train":
model.train()
optimizer.zero_grad()
outp = model(x_batch.to(device))
else:
model.eval()
with torch.no_grad():
outp = model(x_batch.to(device))
preds = outp.argmax(-1)
correct = (preds == y_batch).sum()
all = y_batch.shape[0]
epoch_correct += correct.item()
epoch_all += all
if k == "train":
loss = criterion(outp, y_batch.to(device))
loss.backward()
optimizer.step()
if k == "train":
print(f"Epoch: {epoch+1}")
print(f"Loader: {k}. Accuracy: {epoch_correct/epoch_all}")
accuracy[k].append(epoch_correct/epoch_all)
Вспомните, какую точность давали другие алгоритмы на данном датасете. Свёрточная нейронная сеть показала лучший результат!
Задача (3 балла)¶
Для датасета CIFAR10, постройте и обучите следующую нейронную сеть.
Архитектура:
Входное изображение размером 32(height)$×$32(widht)$×$(3 channel).
Свёртка1(ядро: 3$×$3, пэддинг: 0, каналов на выходе: 6). На выходе: ВАШ ОТВЕТ.
Pooling1(ядро: 2$×$2, stride: 2). На выходе: ВАШ ОТВЕТ.
Свёртка2(ядро: 3$×$3, пэддинг: 0, каналов на выходе: 12). На выходе: ВАШ ОТВЕТ.
Pooling2(ядро: 2$×$2, stride: 2). На выходе: ВАШ ОТВЕТ.
Линейный слой1 (256 нейронов)
Линейный слой2 (128 нейрона)
Линейный слой3 (64 нейрона)
Выходной слой (10 нейронов)
После Свёртки1 и Свёртки два следует функция активации: ReLU.
После Pooling2 следует flatten.
Сколько параметров имеет данная архитектура? ВАШ ОТВЕТ.
Если всё сделает правильно, то легко получите точность выше 60 на 10 эпохах!
Архитектура AlexNet¶
Рассмотрите файл(AlexNet) во вложении (скачать файл AlexNet.jpg).
Задача 5(3 балла)¶
Реализуйте класс, соответствующий архитектуре AlexNet