Deep Learning: как это работает? 2 часть

Deep Learning: как это работает? 2 часть


ILSVRC


The ImageNet Large Scale Visual Recognition Challenge — это такое ежегодное соревнование, на котором ресечеры сравнивают свои сетки в задачах обнаружения и классификации объектов на фотографиях.


Это соревнование являлось толчком для развития:


— Архитектур нейронных сетей

— личных методов и практик, которыми пользуются и по сей день


По этому графику видно, как развивались алгоритмы классификации с течением времени:




По оси x — года и алгоритмы (с 2012 года — сверточные нейронные сети).

По оси y — процент ошибок на выборке из top-5 error.


Top-5 error — это способ оценивания модели: модель возвращает некое распределение вероятностей и если среди топ-5 вероятностей есть истинное значение (метка класса) класса, то ответ модели считается правильным. Соответственно, (1 — top-1 error) — это всем знакомая точность (accuracy).


Архитектуры CNN


LeNet-5




Появилась аж в 1998 году! Была предназначена для распознавания рукописных букв и цифр. Под subsampling (понижение размерности) здесь подразумевается pooling-слой.


Архитектура:


CONV 5x5, stride = 1

POOL 2x2, stride = 2

CONV 5x5, stride = 1

POOL 5x5, stride = 2

FC (120, 84)

FC (84, 10)


Сейчас эта архитектура имеет только историческую значимость. Подобную архитектуру легко имплементировать «ручками» в любом современном фреймворке для глубокого обучения.


AlexNet




Картинка не дублируется. Так изображена архитектура, потому что архитектура AlexNet тогда не влезала на одно устройство GPU, поэтому «половина» сети работала на одной GPU, а вторая — на другой.


Появилась в 2012 году. С нее и начался прорыв в том самом ILSVRC — она победила все stat-of-art модели того времени. После этого люди поняли, что нейронные сети действительно работают :)


Архитектура более конкретно:




Если приглядеться на архитектуру AlexNet, то можно увидеть, что за 14 лет (с появления LeNet-5) не произошло почти никаких изменений, кроме кол-ва слоев.


Важно:


  • Мы берем нашу исходную картинку 227x227x3 и понижаем ее размерность (по высоте и ширине), но увеличиваем кол-во каналов. Такая часть архитектуры «кодирует» изначальное представление объекта (encoder).
  • Впервые была применена функция активации ReLU. Подробнее про ReLu и другие функции активаций можно почитать тут.
  • 60 миллионов обучаемых параметров.
  • Основная часть обучаемых параметров приходится на полносвязные слои.


Примечания:


  • Local Response Norm — это способ нормализации, который использовался в сетях того времени. Сейчас используют batch-normalization.
  • Слева указано кол-во обучаемых параметров в том или ином слое, справа — кол-во FLOPs, требуемых на выполнение.
  • Запись вида FC 4096 означает, что полносвязный (Fully-connected) слой имеет 4096 выходных нейронов.
  • Запись вида Max Pool 3x3s2 означает, что слой пулинга имеет фильтр 3x3, шаг = 2.
  • Запись вида Conv 11x11s4, 96 означает, что слой свертки имеет фильтр 11x11xNc, шаг = 4, кол-во таких фильтров равно 96. Теперь кол-во таких фильтров — это кол-во каналов для следующего слоя (то самое Nc). Считаем, что начальное изображение имеет три канала (R, G и B).


VGGNet


Архитектура:



Появилась в 2014 году.


Две версии — VGG16 и VGG19. Основная идея — использовать вместо больших сверток (11x11 и 5x5) маленькие свертки (3x3). Интуиция в использовании больших сверток простая — мы хотим получать больше информации от соседних пикселей, но гораздо лучше использовать маленькие фильтры чаще.


И вот почему:


  • Каждый последующий слой свертки обладает информацией о предыдущем. И чем сильнее мы углубляемся, тем больше информации последний сверточный слой содержит о первом. Т.е. мы добились того, чего хотели добиться большими свертками, но в малом количестве.
  • Мы слабее уменьшаем размерность нашего изображения => можем применять больше сверток.
  • Больше сверток — больше активаций, больше активаций — больше нелинейности, а нелинейность — это как раз то, чего мы добиваемся.


Важно:


— Во время обучения нейронной сети для алгоритма обратного распространения ошибки важно сохранять представления объекта (для нас — исходного изображения) на всех этапах (свертках, пулингах) прямого распространения (forward pass — это когда мы подаем на вход картинку и движемся к выходу, к результату). Такое представления объекта может дорого стоить в плане памяти. Взгляните:




Получается примерно 96 МБ на изображение — и это только для forward pass'а. Для backward pass'a (bwd на картинке) — во время вычисления градиентов — примерно в два раза больше. Выходит такая интересная картина: наибольшее число обучаемых параметров расположено в полносвязных слоях, а наибольшую память занимают представления объекта после сверточных и пулинговых слоев. С — синергия.


— Сеть имеет 138 миллионов обучаемых параметров в вариации 16-ти слоев и 143 миллиона параметров в вариации 19-ти слоев.


GoogLeNet


Архитектура:



Появилась в 2014 году.


Красные кружочки — это так называемый Inception module.


Давайте взглянем на него поближе:



Мы берем карту признаков (feature map) с предыдущего слоя, применяем к ней какое-то количество сверток с разными фильтрами, потом конкатенируем полученное. Интуиция простая: мы хотим получить различные представления нашей карты признаков, используя фильтры разных размеров. Свертки 1x1 используются для того, чтобы не так сильно наращивать кол-во каналов после каждого такого inception-блока. Т.е. когда у карты признаков большое кол-во каналов, и хотят уменьшить это кол-во, не изменяя высоту и ширину карты признаков, используют свертку размерности 1x1.


Также в сети присутствуют три блока-классификатора, вот так выглядит один из них (тот что справа для нас):




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


Зачем нужны еще два лишних выхода сети? Дело все в так называемой проблеме затухающего градиента (vanishing gradient problem):


Суть в том, что при выполнении обратного распространения ошибки градиент банально стремится к нулю. Чем глубже сеть — тем она более подвержена этому явлению. Почему так происходит? Когда мы выполняем backward pass, мы идем от выхода ко входу, вычисляя градиенты сложных функций. Производная сложной функции (chain rule) — это, по сути, умножение. И вот так, умножая какие-то значения по пути от выхода ко входу, мы встречаем числа, которые близки к нулю, и, как следствие, веса нейронной сети практически не обновляются. Частично это проблема функций активаций типа sigmoid, у которых выход лежит в каком-то фиксированном диапазоне. Ну и частично эта проблема решается путем использования функции активации ReLu. Почему частично? Потому что никто не дает гарантии на значения обучаемых параметров и представления входного объекта во всех картах признаков.


Важно:


  • Сеть имеет 22 слоя (это чуть больше, чем имеет предыдущая сеть).
  • Число обучаемых параметров равно пяти миллионам, что в разы и разы меньше, чем у предыдущих двух сетей.
  • Появление сверток 1x1.
  • Используются Inception блоки.
  • Вместо полносвязных слоев теперь свертки 1x1, которые понижают глубину и, как следствие, понижают размерность полносвязных слоев и так называемый global avegare pooling (подробнее можно прочитуть тут).
  • Архитектура имеет 3 выхода(итоговый ответ взвешивается).


ResNet


Архитектура (вариант ResNet-34):



Появилась в 2015 году.


Основным новшеством стало большое количество слоев и, так называемые, residual блоки. Эти блоки используются в качестве борьбы с проблемой затухающего градиента. Связь между такими residual-блоками называется shortcut (стрелочки на картинке). Теперь по этим шорткатам градиент и будет доходить до всех нужных параметров, тем самым обучая сеть :)


Важно:


  • Вместо полносвязных слоев — average global pooling.
  • Residual-блоки.
  • Сеть превзошла человека в распознавании образов на датасете ImageNet (top-5 error).
  • Впервые использован batch-normalization.
  • Используется техника инициализации весов (интуиция: из определенной инициализации весов сеть сходится (обучается) быстрее и лучше).
  • Максимальная глубина — 152 слоя!


Небольшое отступление


Проблема затухающего градиента актуальна для всех глубоких нейронных сетей.Существует и ее антагонист — проблема взрывающегося градиента (exploding gradient problem), которая также актуальная для всех глубоких нейронных сетей. Суть понятная из названия — градиент становится слишком большим, что вызывает NaN (not a number, бесконечность). Решение очевидно — ограничить значение градиента, в противном случае — уменьшить его значение (нормировать). Такая техника называется «clipping».

Transfer learning


Transfer learning — это метод обучения нейронных сетей, при котором мы берем уже обученную на каких-то данных модель для дальнейшего дообучения для решения другой задачи. Например, у нас есть модель EfficientNet-B5, которая обучена на ImageNet-датасете (1000 классов). Теперь, в самом простом случае, мы изменяем ее последний classifier-слой (скажем, для классификации объектов 10-ти классов).


Взгляните на картинку ниже:




Encoder — это слои subsampling'а (свертки и пулинги).


Замена последнего слоя в коде выглядит так (фреймворк — pytorch, среда — google colab):


Загружаем обученную модель EfficientNet-b5 и смотрим на ее classifier-слой:




Изменяем этот слой на другой:




Decoder нужен, в частности, в задаче сегментации (об этом далее).


Стратегии transfer learning


Стоит добавить, что по умолчанию все слои модели, которые мы хотим обучать дальше, обучаемы. Мы можем «заморозить» веса некоторых слоев.


Для заморозки всех слоев:




Чем меньше слоев мы обучаем — тем меньше нам нужно вычислительных ресурсов для обучения модели. Всегда ли эта техника оправданна?


В зависимости от количества данных, на которых мы хотим обучить сеть, и от данных, на которых была обучена сеть, есть 4 варианта развития событий для transfer learning'а (под «мало» и «много» можно принять условную величину 10k):


  • У Вас мало данных, и они похожи на данные, на которых была обучена сеть до этого. Можно попробовать обучать только несколько последних слоев.
  • У Вас мало данных, и они не похожи на данные, на которых была обучена сеть до этого. Самый печальный случай. Тут уже, скорее всего, придется просто подбирать модель под объем данных, т.к. даже обучение всех слоев может не помочь.
  • У Вас много данных, и они похожи на данные, на которых была обучена сеть до этого. Можно попробовать не обучать всю сеть целиком, а обойтись лишь обучением нескольких последний слоев.
  • У Вас много данных, и они не похожи на данные, на которых была обучена сеть до этого. Лучше обучать практически всю сеть.


Semantic segmentation


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




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


Подходов и нюансов здесь очень много. Чего стоит только архитектура сети ResNeSt-269 :)


Интуиция — на входе изображение (h, w, c), на выходе хотим получить маску (h, w) или (h, w, c), где c — количество классов (зависит от данных и модели). Давайте теперь после нашего encoder'а добавим decoder и обучим их.


Decoder будет состоять, в частности, из слоев upsampling'а (повышения размерности). Повышать размерность можно просто «вытягивая» по высоте и ширине наш feature map на том или ином шаге. При «вытягивании» можно использовать билинейную интерполяцию (в коде это будет просто один из параметров метода).


Архитектура сети deeplabv3+:




Если не углубляться в детали, то можно заметить, что сеть использует архитектуру encoder-decoder.


Более классический вариант, архитектура сети U-net:




Что это за серые стрелочки? Это, так называемые, skip connections. Дело в том, что encoder «кодирует» наше входное изображение с потерями. Для того, чтобы минимизировать такие потери — как раз и используют skip connections.


В этой задаче мы можем применить transfer learning — например, можем взять сеть с уже обученным encoder'ом, дописать decoder и обучить его.


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


Instance segmentation


Более сложный вариант задачи сегментации. Его суть в том, что мы хотим не только классифицировать каждый пиксель входного изображения, а еще и как-то выделять различные объекты одного класса:




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


Подходов здесь тоже несколько. Самый простой и интуитивный состоит в том, что мы обучаем две разные сети. Первую мы обучаем классифицировать пиксели для каких-то классов (semantic segmentation), а вторую — для классификации пикселей между объектами классов. Мы получаем две маски. Теперь мы можем вычесть из первой вторую и получить то, что хотели :)


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


Object detection


Подаем на вход изображение, а на выходе хотим увидеть что-то в духе:




Самое интуитивное, что можно сделать — «бегать» по изображению различными прямоугольниками и, используя уже обученный классификатор, определять — есть ли на данном участке интересующий нас объект. Такая схема имеет место быть, но она, очевидно, не самая лучшая. У нас ведь есть сверточные слои, которые каким-то образом иначе интерпретируют feature map «до»(А) в feature map «после»(Б). При этом мы знаем размерности фильтров свертки => знаем какие пиксели из А в какие пиксели Б преобразовались.


Давайте взглянем на YOLO v3:




YOLO v3 использует разные размерности feature map'ов. Это делается, в частности, для того, чтобы корректно детектировать объекты разного размера.


Далее происходит конкатенация всех трех scale'ов:




Выход сети, при входном изображении 416х416, 13х13х(B * (5 + С)), где С — количество классов, B — количество боксов для каждого региона (у YOLO v3 их 3). 5 — это такие параметры, как: Px, Py — координаты центра объекта, Ph, Pw — высота и ширина объекта, Pobj — вероятность того, что объект находится в этом регионе.


Давайте посмотрим на картинку, так будет чуть более понятно:




YOLO отсеивает данные предсказания изначально по objectness score по какому-то значению (обычно 0.5-0.6), а затем по non-maximum suppression.

Источники: https://habr.com/ru/articles/511372/
https://habr.com/ru/articles/513444/

Report Page