Предыдущий ‹‹ Введение в компьютерное зрение с помощью PyTorch (2/6)

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

В этом модуле мы узнаем о сверточных нейронных сетях (CNN), которые специально разработаны для компьютерного зрения.

Компьютерное зрение отличается от общей классификации тем, что, когда мы пытаемся найти на изображении определенный объект, мы сканируем изображение в поисках определенных шаблонов и их комбинаций. Например, ища кошку, мы сначала можем искать горизонтальные линии, которые могут образовывать усы, а затем определенная комбинация усов может сказать нам, что на самом деле это изображение кошки. Важно взаимное расположение и наличие тех или иных узоров, а не их точное положение на изображении. Для извлечения шаблонов мы будем использовать понятие сверточных фильтров. Но сначала давайте загрузим все зависимости и функции, которые мы определили ранее.

!wget https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/pytorchcv.py
import torch
import torch.nn as nn
import torchvision
import matplotlib.pyplot as plt
from torchinfo import summary
import numpy as np

from pytorchcv import load_mnist, train, plot_results, plot_convolution, display_dataset
load_mnist(batch_size=128)

Сверточные фильтры

Сверточные фильтры — это небольшие окна, которые просматривают каждый пиксель изображения и вычисляют средневзвешенное значение соседних пикселей. Они определяются матрицами весовых коэффициентов.

Давайте посмотрим примеры применения двух разных сверточных фильтров к нашим рукописным цифрам MNIST:

plot_convolution(torch.tensor([[-1.,0.,1.],[-1.,0.,1.],[-1.,0.,1.]]),'Vertical edge filter')
plot_convolution(torch.tensor([[-1.,-1.,-1.],[0.,0.,0.],[1.,1.,1.]]),'Horizontal edge filter')

Первый фильтр называется фильтром вертикальных границ и определяется как:

 -1 0 1
 -1 0 1 
 -1 0 1

Когда этот фильтр проходит через относительно однородное поле пикселей, все значения в сумме дают 0. Однако, когда он сталкивается с вертикальным краем изображения, генерируется высокое значение всплеска. Вот почему на изображениях выше вы можете видеть вертикальные края, представленные высокими и низкими значениями, а горизонтальные края усреднены.

Противоположная ситуация происходит, когда мы применяем фильтр горизонтальных краев, при котором горизонтальные линии усиливаются, а вертикальные усредняются.

Если применить фильтр 3×3 к изображению размером 28×28 — размер изображения станет 26×26, поскольку фильтр не выходит за границы изображения. Однако в некоторых случаях нам может потребоваться сохранить размер изображения одинаковым, и в этом случае изображение дополняется нулями с каждой стороны.

В классическом компьютерном зрении к изображению применялись несколько фильтров для создания признаков, которые затем использовались алгоритмом машинного обучения для создания классификатора. Однако при глубоком обучении мы создаем сети, которые обучаются лучшим сверточным фильтрам для решения задачи классификации.

Для этого мы вводим сверточные слои.

Сверточные слои

Сверточные слои определяются с использованием конструкции nn.Conv2d. Нам необходимо указать следующее:

  • in_channels — количество входных каналов. В нашем случае мы имеем дело с изображением в оттенках серого, поэтому количество входных каналов равно 1. Цветное изображение имеет 3 канала (RGB).
  • out_channels — количество используемых фильтров. Мы будем использовать 9 различных фильтров, что даст сети множество возможностей изучить, какие фильтры лучше всего подходят для нашего сценария.
  • kernel_size — размер скользящего окна. Обычно используются фильтры 3×3 или 5×5. Выбор размера фильтра обычно выбирается экспериментальным путем, то есть путем испытания фильтров разных размеров и сравнения полученной точности.

Простейшая CNN будет содержать один сверточный слой. Учитывая входной размер
28 × 28, после применения девяти фильтров 5 × 5 мы получим тензор
9 × 24 × 24 (пространственный размер меньше, поскольку существует только 24 позиции, где скользящий интервал длиной 5 может уместиться в 28 пикселей). Здесь результат каждого фильтра представлен отдельным каналом изображения. Таким образом, первое измерение 9 соответствует количеству фильтров.

После свертки мы сглаживаем тензор 9 × 24 × 24 в один вектор размером 5184, а затем добавляем линейный слой, чтобы получить 10 классов. Мы также используем функцию активации relu между слоями.

class OneConv(nn.Module):
    def __init__(self):
        super(OneConv, self).__init__()
        self.conv = nn.Conv2d(in_channels=1,out_channels=9,kernel_size=(5,5))
        self.flatten = nn.Flatten()
        self.fc = nn.Linear(5184,10)

    def forward(self, x):
        x = nn.functional.relu(self.conv(x))
        x = self.flatten(x)
        x = nn.functional.log_softmax(self.fc(x),dim=1)
        return x

net = OneConv()

summary(net,input_size=(1,1,28,28))
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
├─Conv2d: 1-1                            [1, 9, 24, 24]            234
├─Flatten: 1-2                           [1, 5184]                 --
├─Linear: 1-3                            [1, 10]                   51,850
==========================================================================================
Total params: 52,084
Trainable params: 52,084
Non-trainable params: 0
Total mult-adds (M): 0.18
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.04
Params size (MB): 0.21
Estimated Total Size (MB): 0.25
==========================================================================================

Вы можете видеть, что эта сеть содержит около 50 тысяч обучаемых параметров по сравнению с примерно 80 тысячами в полностью связанных многоуровневых сетях. Это позволяет нам достигать хороших результатов даже на небольших наборах данных, поскольку сверточные сети гораздо лучше обобщают.

Обратите внимание, что количество параметров сверточного слоя довольно мало и не зависит от разрешения изображения! В нашем случае мы использовали 9 фильтров размером 5 × 5, таким образом, количество параметров составляет
9 × 5 × 5 + 9 = 234. Хотя мы пропустили это в нашем обсуждении выше, но сверточный фильтр также имеет предвзятость. Большинство параметров нашей сети берутся из последнего плотного слоя.

hist = train(net,train_loader,test_loader,epochs=5)
plot_results(hist)
Epoch  0, Train acc=0.947, Val acc=0.969, Train loss=0.001, Val loss=0.001
Epoch  1, Train acc=0.979, Val acc=0.975, Train loss=0.001, Val loss=0.001
Epoch  2, Train acc=0.985, Val acc=0.977, Train loss=0.000, Val loss=0.001
Epoch  3, Train acc=0.988, Val acc=0.975, Train loss=0.000, Val loss=0.001
Epoch  4, Train acc=0.988, Val acc=0.976, Train loss=0.000, Val loss=0.001

Как видите, нам удалось добиться более высокой точности и гораздо быстрее по сравнению с предыдущими полностью подключенными сетями. Мы также можем визуализировать веса наших обученных сверточных слоев, чтобы попытаться понять, что происходит:

fig,ax = plt.subplots(1,9)
with torch.no_grad():
    p = next(net.conv.parameters())
    for i,x in enumerate(p):
        ax[i].imshow(x.detach().cpu()[0,...])
        ax[i].axis('off')

Вы можете видеть, что некоторые из этих фильтров выглядят так, как будто они могут распознавать косые штрихи, в то время как другие выглядят довольно случайными.

Еда на вынос

Сверточный слой позволяет нам извлекать из изображения определенные шаблоны изображений, так что окончательный классификатор основан на этих функциях. Однако мы можем использовать тот же подход для извлечения шаблонов внутри пространства признаков, наложив еще один сверточный слой поверх первого. В следующем разделе мы узнаем о многослойных сверточных сетях.

Приятного обучения!
Далее ››Введение в компьютерное зрение с помощью PyTorch (4/6)