Модуль 9. Свёртки и представления¶
Чему вы научитесь в этом модуле
- Называть два ключевых предположения о данных, которые свёрточная архитектура встраивает в модель, и объяснять, в каких случаях эти предположения ложны.
- Подсчитывать число параметров свёрточного и полносвязного слоёв и объяснять, почему разница между ними составляет три порядка величины.
- Реализовывать свёртку и пулинг с нуля и проверять их корректность с помощью gradient check.
- Демонстрировать на данных, что свёрточная сеть корректно обрабатывает сдвиг изображения, а полносвязная — нет.
- Визуализировать обученные ядра первого слоя и распознать в них детекторы границ.
Время: примерно две недели. Пререквизиты: модуль 8.
Ноутбук: открыть в Colab · notebooks/09-convolutions.ipynb
Зачем нужен этот модуль¶
Модуль 8 закончился поражением: на табличных данных нейросеть проиграла бустингу. Объяснение было структурным — столбцы таблицы можно переставить местами без какого-либо влияния на задачу, а значит, пространственной структуры, из которой сеть могла бы извлечь пользу, просто нет.
С изображениями ситуация принципиально иная. Если вы перемешаете пиксели изображения случайным образом, картинка перестанет существовать. Соседние пиксели связаны между собой (несут информацию друг о друге), и одна и та же визуальная деталь может встретиться в любом месте кадра. Это и есть структура, и архитектура, которая учитывает эту структуру, побеждает архитектуру, которая её игнорирует.
Свёрточная нейросеть — первый случай в нашем курсе, когда выигрыш в качестве обеспечивается не усовершенствованием оптимизации, а предположением о природе данных, встроенным прямо в архитектуру модели.
Что не так с полносвязным слоем на изображениях¶
Возьмём изображение размером 28×28 пикселей в оттенках серого. Полносвязный слой с 128 нейронами потребует:
Сто тысяч параметров на один-единственный слой — и это ещё на маленькой картинке. Но число параметров — не главная проблема. Главное — в двух фундаментальных недостатках.
Полносвязный слой не знает, что пиксели расположены рядом друг с другом. Для него вход — просто вектор из 784 чисел без какой-либо пространственной связи. Если перемешать пиксели одинаковым образом во всех обучающих изображениях, слой обучится ровно так же — ему всё равно. Вся информация о том, что соседние пиксели связаны между собой, была безвозвратно выброшена в тот момент, когда двумерная картинка была растянута в одномерный вектор.
Полносвязный слой не знает, что деталь может переместиться. Кот в левом верхнем углу изображения и тот же самый кот в правом нижнем — для полносвязного слоя это два совершенно разных входных вектора. Ему придётся выучить отдельный «детектор кота» для каждой возможной позиции в кадре.
Два предположения, которые вносит свёртка¶
Свёрточная архитектура устраняет обе описанные проблемы, встраивая в модель два утверждения о природе данных.
Локальность. Важные визуальные признаки формируются из соседних пикселей. Граница объекта, угол, текстура — всё это видно уже в маленьком окне 3×3, и для их обнаружения не нужно одновременно анализировать весь кадр. Это означает, что каждый нейрон может «смотреть» только на маленький фрагмент изображения.
Разделение весов (weight sharing). Детектор вертикальной границы одинаково полезен в любой точке кадра. Значит, одно и то же ядро (набор весов) можно протащить по всему изображению, вместо того чтобы заводить отдельные веса для каждой позиции.
Из второго предположения вытекает свойство, называемое трансляционной эквивариантностью: если сдвинуть входное изображение, выход сети сдвинется точно так же. Признак остаётся обнаруженным — просто его положение в выходной карте изменилось соответственно.
Это именно предположения, а не абсолютные истины
Оба утверждения могут оказаться ложными для конкретной задачи, и тогда свёрточная архитектура принесёт вред, а не пользу.
В табличных данных соседство столбцов не несёт никакого смысла: признак «возраст» стоит рядом с «городом» лишь потому, что так сложился порядок в таблице. Свёртка по такой строке навяжет модели связь между соседними столбцами, которой в действительности не существует.
В задаче, где положение признака важно само по себе — например, когда дефект детали имеет значение только в определённой зоне, — разделение весов уничтожает именно ту информацию, которая нужна для решения задачи.
Правило всё то же, что и с моментом в модуле 4, и со случайным лесом в модуле 6: архитектурный приём вносит предположение, и работает он ровно тогда, когда предположение соответствует действительности.
Свёрточный слой¶
Ядро (фильтр) размера \(k \times k\) скользит по изображению. В каждой позиции вычисляется скалярное произведение ядра с тем фрагментом изображения, который оно накрыло. Это то самое скалярное произведение из модуля 3, и означает оно то же самое: насколько данный фрагмент изображения похож на тот паттерн, который ядро «ищет».
Размер выходной карты при шаге \(s\) и паддинге (отступе) \(p\):
Слой обычно содержит несколько ядер, каждое из которых формирует свой выходной канал. Каждое ядро ищет свой паттерн: одно специализируется на горизонтальных границах, другое — на вертикальных, третье — на пятнах определённого размера.
Число параметров свёрточного слоя:
Это число не зависит от размера изображения. Слой из 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.
Что содержится внутри:
- Свёртка вручную, проверенная на известном ядре: оператор Собеля должен обнаружить границы на изображении.
- Подсчёт параметров: свёрточный слой против полносвязного на одной и той же задаче.
- Свёрточная сеть с нуля — свёртка, ReLU, пулинг, полносвязная «голова». Каждый компонент проверен gradient check.
- Обучение на синтетических изображениях: три класса геометрических фигур.
- Сдвиг изображения на два пикселя: что происходит с полносвязной сетью и что — со свёрточной.
- Визуализация обученных ядер первого слоя. Ищите детекторы границ.
- Сравнение на одних данных: свёрточная сеть, полносвязная сеть и бустинг из модуля 6.
Часть 2. Аугментации на собственной задаче¶
Возьмите любой набор изображений — подойдут и ваши собственные фотографии.
- Составьте список преобразований, при которых метка объекта точно не меняется.
- Отдельно перечислите те преобразования, при которых метка меняется или становится бессмысленной.
- Обучите модель без аугментаций и с аугментациями из первого списка. Сравните результаты по правилам модуля 7.
- Теперь добавьте одно преобразование из второго списка и посмотрите, что произойдёт с качеством.
Четвёртый пункт выполняют редко, но именно он наглядно демонстрирует, что аугментация — это утверждение о природе задачи, а не бесплатное увеличение объёма выборки.
Задание¶
- Вычислите размер выхода свёртки для входа 32×32, ядра 5×5, шага 2, паддинга 1. Проверьте результат с помощью кода.
- Реализуйте average-pooling (пулинг средним) и сравните его с max-pooling на тех же данных. Объясните наблюдаемую разницу в результатах.
- Сконструируйте задачу, в которой свёрточная сеть проигрывает полносвязной. Подсказка: сделайте так, чтобы положение признака на изображении было важной информацией.
- Возьмите обученную свёрточную сеть и подайте на вход одно и то же изображение, сдвинутое на 1, 2, 4 и 8 пикселей. Постройте график того, как падает уверенность модели в правильном классе.
- Визуализируйте обученные ядра первого слоя при различных начальных инициализациях. Всегда ли в результате получаются детекторы границ?
Проверка усвоения¶
- Какие два предположения о данных вносит свёрточная архитектура?
- Почему число параметров свёрточного слоя не зависит от размера входного изображения?
- Чем эквивариантность отличается от инвариантности и какое из этих свойств обеспечивает пулинг?
- Почему свёрточная архитектура вредна на табличных данных?
- Что обычно выучивает первый свёрточный слой и почему именно это?
- Создаёт ли аугментация данных новую информацию? Если нет, то каков её реальный эффект?
- Какое преобразование нельзя применять к изображениям рукописных цифр и почему?
- Почему перенос обучения работает лучше обучения с нуля на маленькой выборке?
Что дальше¶
В модуле 10 — последовательности. Там структура данных иная: не пространственное соседство пикселей, а порядок элементов во времени, и архитектура под неё будет другой. Модуль завершится механизмом внимания, из которого собран трансформер, — и его мы тоже напишем с нуля.
Свёрточная сеть выигрывает не потому, что она сложнее. Она выигрывает потому, что знает о данных то, чего полносвязный слой не знает.