Перейти к содержанию
Публикация AiManual

Как работает сверточная нейросеть: свертка, ядра, receptive field и визуальная иерархия

Разбираем механику CNN по шагам: как ядро скользит по изображению, почему сверточный слой обходится 9 весами там, где полносвязному нужно 225, и как padding, st

Коротко

Что будет в материале

  1. 01

    Что такое сверточная нейросеть и какую задачу решает свертка

  2. 02

    Как работает 2D-свертка: ядро скользит по изображению

  3. 03

    Padding и stride: как управлять размером выхода свертки

  4. 04

    Многоканальная свертка: чем фильтр отличается от ядра

Что такое сверточная нейросеть и какую задачу решает свертка

Сверточная нейросеть (CNN) строит представление изображения через 2D-свертку. Ядро, небольшая матрица весов, скользит по входу, поэлементно умножает свои веса на значения под ним и суммирует произведения в один выходной пиксель. Затем ядро повторяет ту же процедуру для каждой позиции и превращает одну двумерную матрицу в другую двумерную матрицу признаков. Разбор базовой механики свертки описывает операцию ровно в такой последовательности.

Ключевое свойство: каждый выходной признак складывается из входных признаков примерно из той же локации. Ядро 3x3 объединяет 9 пикселей, ядро 5x5 - 25. Размер ядра напрямую задает, сколько входных значений попадает в одно выходное.

Дальше по порядку: пошаговая механика с ручным расчетом, экономия параметров, padding и stride, многоканальные фильтры и смещение, линейность свертки и hard prior, receptive field с визуальной иерархией, ограничения подхода.

Как работает 2D-свертка: ядро скользит по изображению

Операция раскладывается на четыре шага.

  1. Накладываем ядро на окно входа того же размера.
  2. Поэлементно умножаем веса ядра на значения окна.
  3. Складываем все произведения и получаем одно число, выходной пиксель.
  4. Сдвигаем ядро на stride (по умолчанию на 1 пиксель) и повторяем, пока не пройдем все окна.

Ручной пример. Вход 5x5:

1  0  2  1  0
0  1  3  2  1
2  1  0  1  2
1  2  1  0  1
0  1  2  1  0

Ядро 3x3, которое реагирует на вертикальный перепад яркости:

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

Берем верхнее левое окно:

1 0 2
0 1 3
2 1 0

Считаем построчно: 1*1 + 0*0 + 2*(-1) = -1; 0*1 + 1*0 + 3*(-1) = -3; 2*1 + 1*0 + 0*(-1) = 2. Сумма: -1 - 3 + 2 = -2. Первый пиксель выхода равен -2.

Окно со второй по четвертую строку и со второго по четвертый столбец дает другое значение: (1*1 + 3*0 + 2*(-1)) + (1*1 + 0*0 + 1*(-1)) + (2*1 + 1*0 + 0*(-1)) = -1 + 0 + 2 = 1.

Полный проход по входу 5x5 дает матрицу 3x3: 9 выходных значений из 25 входных. Без padding размер уменьшается, потому что каждый проход съедает по одному пикселю с каждой стороны, а за границей матрицы значений для ядра просто нет.

Почему сверточный слой требует на порядок меньше параметров, чем полносвязный

Для того же примера: 25 входных значений и 9 выходных. Полносвязный слой связал бы каждый вход с каждым выходом и получил матрицу 25*9 = 225 весов. Сверточный слой обходится 9 весами ядра, в 25 раз меньше. Тот же источник приводит именно этот расчет.

Экономия берется из двух свойств:

  • локальность связей: выходной нейрон смотрит не на все входы, а только на окно под ядром;
  • связывание параметров (weight sharing): одно и то же ядро применяется во всех позициях, веса не дублируются.

С ростом картинки разрыв увеличивается. Сверточный слой 3x3 на 64 входных и 64 выходных каналах содержит 3*3*64*64 = 36 864 веса. Полносвязный слой на карте 32x32x64 (65 536 входов) с тем же числом выходов потребовал бы около 4,3 млрд весов.

Оговорка: меньше параметров не означает меньше вычислений. Каждый выходной пиксель требует K*K умножений, и на большом изображении суммарное число операций остается заметным. Экономия идет по весам, а значит по памяти и по объему данных, нужных для обучения.

Padding и stride: как управлять размером выхода свертки

Padding добавляет по краям фиктивные пиксели, обычно нулевые. Ядро при скольжении ставит настоящий пиксель в центр, а часть весов попадает на фиктивные значения за границей, поэтому выход сохраняет размер входа: матрица 5x5 с padding=1 после ядра 3x3 снова дает 5x5. В разборе padding описан так же, с тем же примером.

Stride задает шаг скольжения. При stride=2 ядро перепрыгивает через пиксель: выход становится меньше, вычислений меньше, поле зрения растет быстрее.

Размер выхода считается по формуле:

out = floor((W - K + 2P) / S) + 1

W - сторона входа, K - размер ядра, P - padding, S - stride. Числовые примеры:

  • 5x5, ядро 3x3, P=0, S=1: floor((5-3)/1)+1 = 3, выход 3x3.
  • 5x5, ядро 3x3, P=1, S=1: floor((5-3+2)/1)+1 = 5, выход 5x5, как на входе.
  • 5x5, ядро 3x3, P=1, S=2: floor((5-3+2)/2)+1 = 3, выход 3x3 при вдвое меньшем числе окон.
  • 224x224, ядро 7x7, P=3, S=2: floor((224-7+6)/2)+1 = 112, так устроен первый слой многих классических архитектур.

Для нечетного ядра padding (K-1)/2 сохраняет размер при stride=1: ядру 3x3 нужен P=1, ядру 5x5 нужен P=2. Этот режим называют same padding.

Практический смысл: padding сохраняет информацию у краев (без него граничные пиксели участвуют меньше внутренних), stride уменьшает разрешение и нагрузку, а заодно расширяет поле зрения следующих слоев.

Многоканальная свертка: чем фильтр отличается от ядра

Цветное изображение имеет три входных канала, и на входе слоя лежит тензор H x W x Cin, а не одна матрица.

Ядро, двумерная матрица весов для одного входного канала. Фильтр, набор ядер по числу входных каналов: для RGB фильтр 3x3 состоит из трех ядер 3x3 и содержит 27 весов.

Фильтр проходит по всем входным каналам сразу, результаты по каналам складываются в одно число, и к этой сумме добавляется смещение (bias), обучаемый скаляр на выходной канал. Один фильтр формирует один выходной канал. Число выходных каналов равно числу фильтров.

Пример: вход 32x32x3, 16 фильтров 3x3, padding=1, stride=1. Выход 32x32x16. Параметров: 16 * (3*3*3) + 16 = 448.

В PyTorch тензор весов слоя Conv2d имеет форму (out_channels, in_channels, kH, kW), а смещение хранится отдельным вектором длиной out_channels. Если bias отключить, слой останется линейным без сдвига; так иногда делают перед нормализацией, которая сама вносит сдвиг.

Почему свертка остается линейным преобразованием с эквивалентной матрицей весов

Каждый выходной пиксель, линейная комбинация входных значений. Весь слой записывается как умножение на матрицу: y = Wx + b. Матрица W получается огромной, но почти целиком заполнена нулями: ненулевые элементы стоят только там, где вход попадает под ядро. Ненулевые значения повторяются, потому что одно и то же ядро работает во всех позициях.

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

Нелинейность появляется после свертки: в активациях вроде ReLU и в pooling. До них стек сверточных слоев эквивалентен одной большой линейной операции.

Отсюда практический смысл для transfer learning. Ранние слои с фиксированным приором выучивают края, направленные перепады и простые текстуры, а такие признаки полезны в разных задачах. Веса, обученные на ImageNet, часто переносят на медицинские снимки, документы и промышленную дефектоскопию, дообучая последние слои. Ограничение: перенос работает, когда статистика новых данных близка к исходной. При сильном сдвиге, например при другой модальности или радикально другом масштабе объектов, выигрыш падает и сети нужна полноценная настройка большего числа слоев.

Receptive field: как сеть расширяет поле зрения

Receptive field, область входа, которая влияет на один нейрон конкретного слоя. У первого слоя с ядром 3x3 поле равно 3x3. У второго слоя 3x3 нейрон смотрит на выход первого, а через него на 5x5 исходных пикселей. Три слоя 3x3 дают 7x7.

Расчет ведут двумя числами: r, размер поля, и j, расстояние между соседними нейронами в координатах входа. Формулы: r_out = r_in + (k - 1) * j_in и j_out = j_in * s. На старте r=1, j=1.

СлойЯдро kStride sПоле rШаг j
13131
23151
33272
431112
532154
631234

Из таблицы видно, как работает масштаб. Обычные свертки 3x3 добавляли по 2 пикселя за слой. Stride=2 удвоил шаг, и следующие слои стали прибавлять 4 пикселя, потом 8. К шестому слою поле reaches 23x23, хотя ядро везде одно и то же 3x3.

Pooling считается по той же формуле, где k, размер окна. Слой 1x1 поле зрения не расширяет: он смешивает каналы в одной точке. Поэтому глубина и stride, основные инструменты управления полем зрения, а сокращение разрешения идет как побочный эффект.

Визуальная иерархия: от краев до целых объектов на примере GoogLeNet

Малые поля зрения ранних слоев видят края и перепады яркости. Средние слои собирают из них углы, дуги, повторяющиеся текстуры. Глубокие слои с полем в десятки и сотни пикселей отвечают за части объектов и объекты целиком.

GoogLeNet (Inception), победитель ILSVRC 2014, показала, как работать с несколькими масштабами внутри одного блока. Инцепшн-модуль содержит параллельные ветви: свертку 1x1, свертку 3x3, свертку 5x5 и max-pooling 3x3. Выходы ветвей склеиваются по оси каналов, и следующий слой получает признаки сразу трех масштабов.

Ветви 3x3 и 5x5 предварены свертками 1x1, которые сжимают число каналов. Свертка 1x1 здесь работает как линейное смешивание каналов в каждом пикселе: она снижает объем вычислений, не трогая пространственное разрешение и не расширяя поле зрения.

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

Ограничения CNN: adversarial examples и отличие от человеческого зрения

Adversarial example, вход, отличающийся от исходного на малую величину, которую человек не замечает, но модель уверенно относит его к другому классу. Классическая схема атаки: x' = x + ε * sign(∇x L), где ε небольшая константа, а градиент функции потерь по входу показывает, в какую сторону сдвигать каждый пиксель.

Устойчивость таких атак заложена в самой архитектуре. Слой линеен, поэтому малое изменение на входе предсказуемо меняет выход; на изображении 224x224 с десятками тысяч пикселей мелкие сдвиги складываются в заметный сдвиг логитов. Hard prior заставляет сеть опираться на локальные корреляции, а не на целостное представление объекта, и этой локальной статистикой можно управлять целенаправленно.

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

Защита остается открытой задачей. Состязательное обучение, то есть добавление adversarial-примеров в тренировочный набор, повышает устойчивость к конкретному типу атаки, но обычно снижает точность на чистых данных и не гарантирует защиту от новых схем. Численных замеров устойчивости здесь нет: они зависят от модели, датасета и метода атаки.

Что важно запомнить о работе сверточной нейросети

  • 2D-свертка: ядро скользит по входу, поэлементно умножает веса, суммирует в один пиксель и повторяет для всех позиций.
  • Экономия параметров: 25 входов и 9 выходов дают 225 весов у полносвязного слоя против 9 у свертки; причина в локальности связей и связывании параметров.
  • Padding сохраняет размер выхода, stride его уменьшает: out = floor((W - K + 2P)/S) + 1.
  • Ядро работает с одним входным каналом, фильтр объединяет ядра по всем входным каналам; число фильтров равно числу выходных каналов, смещение добавляется на канал.
  • Свертка линейна, hard prior из нулей и общих весов объясняет переносимость ранних признаков; нелинейность дают активации и pooling.
  • Receptive field растет с глубиной и stride: шесть слоев 3x3 с двумя stride=2 дают поле 23x23.
  • Ограничения: adversarial examples и расхождение с человеческим зрением остаются нерешенными.

Практические выводы для тех, кто собирает или отлаживает CNN:

  1. Считайте параметры и операции отдельно: свертка экономит память, но не обязательно FLOPs.
  2. Для сохранения разрешения берите P=(K-1)/2 при stride=1; для даунсэмплинга переходите на stride=2, а не на большое ядро.
  3. Проверяйте формы тензоров по формуле выхода до запуска обучения, это ловит основную часть ошибок сборки модели.
  4. Для переноса выбирайте веса с домена, близкого к вашей задаче; при сильном сдвиге размораживайте больше слоев.
  5. Оценивайте устойчивость к возмущениям отдельно от обычной точности, если модель работает там, где возможны целенаправленные атаки.

Начать стоит с ручного расчета одной свертки на бумаге: после него формулы размера выхода и receptive field перестают выглядеть магией и становятся рабочим инструментом.

Подписаться на канал