Перейти к содержанию

Модуль 9. Свёртки и представления

Чему вы научитесь в этом модуле

  • Называть два ключевых предположения о данных, которые свёрточная архитектура встраивает в модель, и объяснять, в каких случаях эти предположения ложны.
  • Подсчитывать число параметров свёрточного и полносвязного слоёв и объяснять, почему разница между ними составляет три порядка величины.
  • Реализовывать свёртку и пулинг с нуля и проверять их корректность с помощью gradient check.
  • Демонстрировать на данных, что свёрточная сеть корректно обрабатывает сдвиг изображения, а полносвязная — нет.
  • Визуализировать обученные ядра первого слоя и распознать в них детекторы границ.

Время: примерно две недели. Пререквизиты: модуль 8. Ноутбук: открыть в Colab · notebooks/09-convolutions.ipynb

Зачем нужен этот модуль

Модуль 8 закончился поражением: на табличных данных нейросеть проиграла бустингу. Объяснение было структурным — столбцы таблицы можно переставить местами без какого-либо влияния на задачу, а значит, пространственной структуры, из которой сеть могла бы извлечь пользу, просто нет.

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

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

Что не так с полносвязным слоем на изображениях

Возьмём изображение размером 28×28 пикселей в оттенках серого. Полносвязный слой с 128 нейронами потребует:

\[784 \times 128 + 128 = 100\,480 \text{ параметров}\]

Сто тысяч параметров на один-единственный слой — и это ещё на маленькой картинке. Но число параметров — не главная проблема. Главное — в двух фундаментальных недостатках.

Полносвязный слой не знает, что пиксели расположены рядом друг с другом. Для него вход — просто вектор из 784 чисел без какой-либо пространственной связи. Если перемешать пиксели одинаковым образом во всех обучающих изображениях, слой обучится ровно так же — ему всё равно. Вся информация о том, что соседние пиксели связаны между собой, была безвозвратно выброшена в тот момент, когда двумерная картинка была растянута в одномерный вектор.

Полносвязный слой не знает, что деталь может переместиться. Кот в левом верхнем углу изображения и тот же самый кот в правом нижнем — для полносвязного слоя это два совершенно разных входных вектора. Ему придётся выучить отдельный «детектор кота» для каждой возможной позиции в кадре.

Два предположения, которые вносит свёртка

Свёрточная архитектура устраняет обе описанные проблемы, встраивая в модель два утверждения о природе данных.

Локальность. Важные визуальные признаки формируются из соседних пикселей. Граница объекта, угол, текстура — всё это видно уже в маленьком окне 3×3, и для их обнаружения не нужно одновременно анализировать весь кадр. Это означает, что каждый нейрон может «смотреть» только на маленький фрагмент изображения.

Разделение весов (weight sharing). Детектор вертикальной границы одинаково полезен в любой точке кадра. Значит, одно и то же ядро (набор весов) можно протащить по всему изображению, вместо того чтобы заводить отдельные веса для каждой позиции.

Из второго предположения вытекает свойство, называемое трансляционной эквивариантностью: если сдвинуть входное изображение, выход сети сдвинется точно так же. Признак остаётся обнаруженным — просто его положение в выходной карте изменилось соответственно.

Это именно предположения, а не абсолютные истины

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

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

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

Правило всё то же, что и с моментом в модуле 4, и со случайным лесом в модуле 6: архитектурный приём вносит предположение, и работает он ровно тогда, когда предположение соответствует действительности.

Свёрточный слой

Ядро (фильтр) размера \(k \times k\) скользит по изображению. В каждой позиции вычисляется скалярное произведение ядра с тем фрагментом изображения, который оно накрыло. Это то самое скалярное произведение из модуля 3, и означает оно то же самое: насколько данный фрагмент изображения похож на тот паттерн, который ядро «ищет».

Размер выходной карты при шаге \(s\) и паддинге (отступе) \(p\):

\[n_{\text{вых}} = \left\lfloor \frac{n_{\text{вх}} + 2p - k}{s} \right\rfloor + 1\]

Слой обычно содержит несколько ядер, каждое из которых формирует свой выходной канал. Каждое ядро ищет свой паттерн: одно специализируется на горизонтальных границах, другое — на вертикальных, третье — на пятнах определённого размера.

Число параметров свёрточного слоя:

\[k \times k \times C_{\text{вх}} \times C_{\text{вых}} + C_{\text{вых}}\]

Это число не зависит от размера изображения. Слой из 8 ядер размера 3×3 на одноканальном входе содержит всего 80 параметров — и столько же будет как на картинке 28×28, так и на 1024×1024. Для сравнения: полносвязный слой на том же входе потребовал бы сто тысяч параметров.

Пулинг (pooling)

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

Max-pooling 2×2 берёт максимальное значение в каждом окне размером 2×2, уменьшая пространственный размер карты вдвое по каждому измерению. Попутно объём вычислений снижается вчетверо, что и позволяет строить глубокие сети.

Но главное — пулинг меняет тип симметрии сети. Свёртка обеспечивает эквивариантность: при сдвиге входа выход сдвигается аналогично. Признак обнаруживается, просто его позиция в карте меняется. Пулинг же превращает эквивариантность в инвариантность: при сдвиге входа выход не меняется вовсе (в пределах размера окна пулинга).

Одной свёртки для настоящей инвариантности недостаточно

Если собрать сеть как свёртка → пулинг 2×2 → flatten (растягивание в вектор) → полносвязный слой, она не будет устойчива к сдвигу изображения. Пулинг 2×2 способен компенсировать сдвиг на один пиксель, но далее операция flatten снова жёстко привязывает всё к конкретным позициям: полносвязный слой видит не «признак обнаружен», а «признак обнаружен в ячейке номер 37».

Это не теоретическое рассуждение. В ноутбуке такая архитектура при сдвиге изображения на два пикселя показывает accuracy 0.75 — ровно столько же, сколько обычная полносвязная сеть без свёрток. Свёртка присутствует, но инвариантности нет.

Проблема решается глобальным пулингом: максимум (или среднее) берётся не по маленькому окну, а по всей карте целиком, и от каждого ядра остаётся одно число — «сработало или нет», безразлично где именно. С глобальным пулингом та же самая сеть показывает 1.000 на сдвигах до трёх пикселей и 0.98 на сдвигах в пять пикселей.

Разница между двумя вариантами — одна строка кода, но в ней заключён весь смысл модуля.

Что выучивает первый слой

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

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

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

На более глубоких слоях интерпретация становится сложнее: второй слой обычно реагирует на углы и дуги, далее — на фрагменты объектов, а на верхних слоях прямая интерпретация ядер перестаёт работать. Вопрос о том, что именно представляют верхние слои, — предмет отдельной исследовательской области; одна из статей на эту тему будет разобрана в модуле 24.

Аугментации данных

Приём, который на первый взгляд выглядит как жульничество, но таковым не является.

Мы отражаем, поворачиваем, сдвигаем обучающие изображения, меняем их яркость и контрастность — и тем самым якобы «увеличиваем» объём обучающих данных. Но в действительности данных не стало больше: преобразованные изображения не несут новой информации о мире.

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

Из этого понимания вытекает правило выбора аугментаций: применяйте только те преобразования, при которых метка объекта действительно не меняется. Зеркальное отражение цифры 6 даёт не цифру 6 (а цифру, похожую на 9). Поворот рентгеновского снимка на 180 градусов даёт изображение, которое в реальности не встречается. Аугментация, нарушающая смысл задачи, — это добавление шума под видом полезных данных.

Перенос обучения (transfer learning)

Первые слои свёрточной сети выучивают универсальные детекторы границ и текстур — а эти визуальные примитивы одинаковы для любой задачи компьютерного зрения. Значит, их можно не обучать заново каждый раз.

Практическая схема: берётся сеть, обученная на большом наборе изображений (например, ImageNet), её ранние слои «замораживаются» (их веса не обновляются), а последние слои переобучаются под вашу конкретную задачу. На тысяче ваших специфических изображений такой подход работает несравнимо лучше, чем обучение сети с нуля, — потому что тысячи изображений достаточно для дообучения «головы» (верхних слоёв), но совершенно недостаточно для того, чтобы с нуля выучить качественные детекторы границ и текстур.

Это самый практичный приём всей части III, и в реальной работе он применяется значительно чаще, чем обучение свёрточной сети с нуля.

Практическая часть

Часть 1. Работа с ноутбуком

Откройте notebooks/09-convolutions.ipynb.

Что содержится внутри:

  1. Свёртка вручную, проверенная на известном ядре: оператор Собеля должен обнаружить границы на изображении.
  2. Подсчёт параметров: свёрточный слой против полносвязного на одной и той же задаче.
  3. Свёрточная сеть с нуля — свёртка, ReLU, пулинг, полносвязная «голова». Каждый компонент проверен gradient check.
  4. Обучение на синтетических изображениях: три класса геометрических фигур.
  5. Сдвиг изображения на два пикселя: что происходит с полносвязной сетью и что — со свёрточной.
  6. Визуализация обученных ядер первого слоя. Ищите детекторы границ.
  7. Сравнение на одних данных: свёрточная сеть, полносвязная сеть и бустинг из модуля 6.

Часть 2. Аугментации на собственной задаче

Возьмите любой набор изображений — подойдут и ваши собственные фотографии.

  1. Составьте список преобразований, при которых метка объекта точно не меняется.
  2. Отдельно перечислите те преобразования, при которых метка меняется или становится бессмысленной.
  3. Обучите модель без аугментаций и с аугментациями из первого списка. Сравните результаты по правилам модуля 7.
  4. Теперь добавьте одно преобразование из второго списка и посмотрите, что произойдёт с качеством.

Четвёртый пункт выполняют редко, но именно он наглядно демонстрирует, что аугментация — это утверждение о природе задачи, а не бесплатное увеличение объёма выборки.

Задание

  1. Вычислите размер выхода свёртки для входа 32×32, ядра 5×5, шага 2, паддинга 1. Проверьте результат с помощью кода.
  2. Реализуйте average-pooling (пулинг средним) и сравните его с max-pooling на тех же данных. Объясните наблюдаемую разницу в результатах.
  3. Сконструируйте задачу, в которой свёрточная сеть проигрывает полносвязной. Подсказка: сделайте так, чтобы положение признака на изображении было важной информацией.
  4. Возьмите обученную свёрточную сеть и подайте на вход одно и то же изображение, сдвинутое на 1, 2, 4 и 8 пикселей. Постройте график того, как падает уверенность модели в правильном классе.
  5. Визуализируйте обученные ядра первого слоя при различных начальных инициализациях. Всегда ли в результате получаются детекторы границ?

Проверка усвоения

  1. Какие два предположения о данных вносит свёрточная архитектура?
  2. Почему число параметров свёрточного слоя не зависит от размера входного изображения?
  3. Чем эквивариантность отличается от инвариантности и какое из этих свойств обеспечивает пулинг?
  4. Почему свёрточная архитектура вредна на табличных данных?
  5. Что обычно выучивает первый свёрточный слой и почему именно это?
  6. Создаёт ли аугментация данных новую информацию? Если нет, то каков её реальный эффект?
  7. Какое преобразование нельзя применять к изображениям рукописных цифр и почему?
  8. Почему перенос обучения работает лучше обучения с нуля на маленькой выборке?

Что дальше

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

Свёрточная сеть выигрывает не потому, что она сложнее. Она выигрывает потому, что знает о данных то, чего полносвязный слой не знает.