Свёрточные сети: свёртка, пулинг, метрики качества
- О чём эта тема
- Сеть из конспекта 3 (Flatten + Dense) обучаема, но не использует то, что соседние пиксели изображения связаны между собой. Вводится операция свёртки — способ обработки изображения, явно учитывающий эту связь, — и слои, из которых строится свёрточная сеть (CNN): свёртка, ReLU, пулинг, полносвязный слой. Отдельно вводятся метрики качества классификации.
- Аннотация
- Конспект начинается с объяснения, почему связка Flatten + Dense неэффективна для изображений. Затем вводится операция свёртки с её параметрами — ядром, шагом (stride) и паддингом — и рассматриваются слои ReLU и пулинга в вариантах max, average и sum. Из этих элементов собирается типовая архитектура свёрточной сети: свёртка → ReLU → пулинг → … → Flatten → Dense. Отдельный раздел посвящён метрикам качества классификации — матрице ошибок, accuracy, precision и recall. Завершается конспект реализацией свёрточной сети на Keras для набора данных CIFAR-10.
- Пререквизиты
- Конспект 3: Dense, Sequential, compile/fit, softmax, категориальная перекрёстная энтропия.
- Мотивация
- Слой
Flattenиз конспекта 3 разворачивает изображение \(32\times32\times3\) в вектор длины 3072, теряя при этом информацию о том, какие пиксели были соседями по вертикали и горизонтали, — для полносвязного слоя это просто список из 3072 независимых чисел. Кроме того, один только первый слойDense(512)на этом векторе занимает 1,5 миллиона параметров (конспект 3, формула (3.1)). Операция свёртки решает обе проблемы: она применяется к соседним пикселям явно и использует один и тот же небольшой набор весов (ядро) по всему изображению.
1. Операция свёртки
Изображение для сети — это матрица чисел (значений яркости), а не рисунок. Ниже — иллюстрация из презентации курса: слева фотография, справа те же данные в виде чисел, которые фактически получает сеть.
Операция свёртки — это не умножение матриц (в смысле произведения матриц из линейной алгебры, как в полносвязном слое конспекта 3), а скользящее окно: небольшая матрица весов (ядро, англ. kernel) поэлементно умножается на область изображения того же размера, произведения суммируются — это даёт одно число выхода, — после чего окно сдвигается. Классический пример из обработки изображений — оператор Собеля, выделяющий вертикальные границы:
Формально для входа \(X\), ядра \(K\) размера \(k\times k\) и позиции выхода \((i,j)\):
Ядро в CNN, в отличие от оператора Собеля, не задаётся вручную — его веса (значения \(K_{a,b}\)) подбираются обучением, тем же градиентным спуском (конспект 2, формула (2.5)), что и веса полносвязного слоя.
1.1. Что распознаёт ядро: признак
Признак (англ. feature) — это некоторый узнаваемый паттерн во входных данных (например, изгиб контура), на который настроено ядро: чем больше область под окном похожа на этот паттерн, тем больше по модулю значение свёртки (4.1) в этой точке. Ниже — пример из презентации курса: контур мыши и ядро-детектор изгиба (кривой).
В этой позиции пиксели под окном образуют изгиб, близкий по форме к тому, на который настроено ядро-детектор кривой, — свёртка в этой точке даёт большое по модулю значение (в примере из презентации — 6600, при том что там же поясняется: «a large number», то есть значение явно выделяется на фоне остальных):
А в другой позиции (нос, без выраженного изгиба под тем же углом) отклик того же ядра равен нулю — паттерн, на который настроено ядро, здесь отсутствует:
Именно в этом смысле CNN «распознаёт признаки»: не по одному изображению целиком, а по локальному совпадению области входа с паттерном, на который настроено ядро. В реальной сети веса ядра (в отличие от показанного здесь фиксированного детектора кривой) находятся обучением, и какой именно паттерн окажется «выучен» — определяется данными, а не задаётся заранее.
1.2. Шаг и паддинг
Два параметра управляют тем, как окно проходит по изображению. Шаг (stride) \(S\) — на сколько пикселей сдвигается окно на каждом шаге (\(S=1\) — окно сдвигается на 1 пиксель, \(S=2\) — через один). Паддинг (padding) — кольцо нулей толщиной \(P\), добавляемое по краям входа перед свёрткой, чтобы окно могло встать и на краевые пиксели. Размер выхода для входа \(N\times N\) и ядра \(k\times k\):
Без паддинга (\(P=0\), режим valid) выход меньше входа. При \(P=\lfloor (k-1)/2\rfloor\) и \(S=1\) (режим same) выход получается того же размера, что и вход.
Входная матрица подобрана иллюстративно (не измерена): левая половина — значения около 2, правая — около 8, что имитирует вертикальную границу. Нажимайте «Шаг →», чтобы смотреть, как окно последовательно проходит по входу и заполняется выход, либо «К результату», чтобы сразу увидеть готовую карту. Переключите ядро на «Собель горизонтальный» и сравните выход с «тождественным» — граница проявится как большие по модулю значения ровно на стыке половин.
2. Слои ReLU и пулинга
После свёртки к каждому значению выхода применяется функция активации ReLU (конспект 1, формула (1.7)) — без неё стек из нескольких свёрток математически сводился бы к одной свёртке (композиция линейных операций линейна).
Слой пулинга уменьшает пространственный размер карты признаков, заменяя каждое непересекающееся окно \(p\times p\) одним числом. Три распространённых варианта — максимум, среднее и сумма значений в окне:
Max-pooling — самый употребительный вариант: он сохраняет самый сильный отклик признака в окне и делает результат менее чувствительным к небольшому сдвигу объекта на изображении. Идея та же и для настоящей фотографии, а не только для числовой таблицы: пулинг уменьшает пространственное разрешение карты признаков, сохраняя при этом узнаваемое содержимое. Ниже — иллюстрация из презентации курса: исходная фотография и результат уменьшения после max-, average- и sum-пулинга.
2.1. Полносвязный слой в конце сети
После нескольких пар «свёртка + пулинг» карта признаков становится маленькой по пространственным
размерам, но глубокой по числу каналов. Её разворачивают в вектор (тем же Flatten, что и
в конспекте 3) и подают на один или несколько слоёв Dense — тех же полносвязных слоёв,
что уже были описаны:
2.2. Сборка архитектуры целиком
Типичная CNN — это несколько повторений блока «свёртка → ReLU → пулинг», извлекающих признаки всё
более высокого уровня, за которыми следует один или два слоя Dense, принимающих решение
о классе:
3. Метрики качества классификации
Для каждого примера сеть либо угадала класс, либо нет. Зафиксируем один класс как «положительный» (остальные — «отрицательный») и назовём \(\hat y\) ответ сети, \(y\) — истинную метку. Возможны четыре исхода:
- TP (true positive) — положительный класс предсказан верно;
- TN (true negative) — отрицательный класс предсказан верно;
- FP (false positive) — предсказан положительный класс, а истинный — отрицательный;
- FN (false negative) — предсказан отрицательный класс, а истинный — положительный.
Таблица с числом примеров в каждой из четырёх категорий называется матрицей ошибок (confusion matrix): строки — истинный класс, столбцы — ответ сети.
| предсказан положительный | предсказан отрицательный | |
|---|---|---|
| истинно положительный | TP | FN |
| истинно отрицательный | FP | TN |
В терминах проверки статистических гипотез (где отрицательный класс соответствует нулевой гипотезе) ячейки FP и FN — это то же самое, что ошибка первого рода и ошибка второго рода соответственно:
- FP — ошибка первого рода: отвергли верную нулевую гипотезу (сеть «нашла» положительный класс там, где его не было);
- FN — ошибка второго рода: не отвергли неверную нулевую гипотезу (сеть «пропустила» действительно положительный пример).
Через элементы матрицы ошибок выражаются метрики качества:
Accuracy — доля верных ответов среди всех примеров. Precision — доля верных среди тех, что сеть отнесла к положительному классу (насколько можно доверять положительному ответу сети). Recall — доля найденных сетью среди всех действительно положительных примеров (насколько полно сеть находит нужный класс).
4. Реализация: CNN на Keras для CIFAR-10
Каждому слою, разобранному выше, соответствует свой класс в tensorflow.keras.layers:
| Понятие | Класс в Keras |
|---|---|
| свёртка (4.1) | Conv2D(filters, kernel_size, strides, padding, activation) |
| max-пулинг | MaxPool2D(pool_size) |
| average-пулинг | AveragePooling2D(pool_size) |
| Flatten (конспект 3) | Flatten() |
| полносвязный слой (конспект 3) | Dense(units, activation) |
Продолжение кода из конспекта 3 — та же загрузка и подготовка CIFAR-10, но вместо
Flatten + Dense в начале сети теперь стоят свёрточные слои:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten, Dense, Dropout model = Sequential() model.add(Conv2D(filters=32, kernel_size=(3, 3), input_shape=(32, 32, 3), padding='same', activation='relu')) model.add(Dropout(0.2)) model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding='same')) model.add(MaxPool2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(512, activation='relu')) model.add(Dense(10, activation='softmax'))
Conv2D(filters=32, kernel_size=(3,3), padding='same', activation='relu') — это слой из
32 разных ядер размера \(3\times3\) (каждое — свой набор весов, обучаемый отдельно), с паддингом
same (формула (4.2) с \(S=1\)) и ReLU сразу после свёртки. MaxPool2D(pool_size=(2,2))
— max-пулинг с окном \(2\times2\) (по умолчанию его шаг тоже равен 2, окна не пересекаются).
Dropout(0.2) появится и будет объяснён отдельно, в конспекте о переобучении. Компиляция и
обучение (compile, fit) — в точности как в конспекте 3, меняется только
архитектура model.
Conv2D число нейронов, как в Dense. Параметр
filters — это число независимых ядер свёртки в слое (глубина выходной карты
признаков), а не число нейронов в обычном смысле; пространственный размер выхода определяется
формулой (4.2), а не значением filters.
Контрольные вопросы
-
Свёртка применяет одно и то же небольшое ядро локально, скользя по всему изображению; в полносвязном слое каждый нейрон имеет свои веса и зависит сразу от всех входов.
-
Согласно формуле (4.2) размер выхода уменьшится: больший знаменатель S даёт меньшее целое значение при том же числителе.
-
Без нелинейности между свёртками стек из нескольких свёрток математически сводился бы к одной свёртке, поскольку композиция линейных операций линейна.
-
Нет. Пулинг — фиксированная операция (максимум, среднее или сумма значений в окне) без параметров, которые находились бы градиентным спуском.
-
На несбалансированных классах accuracy может быть высокой даже у модели, которая не находит ни одного примера редкого класса (recall = 0). Precision и recall показывают эту проблему явно.
-
Число независимых ядер свёртки в слое (глубина выходной карты признаков). Это не число нейронов в смысле Dense и не определяет напрямую пространственный размер выхода — тот задаётся формулой (4.2).