Главная/ Блог / Нейросети Часть 3

Откуда нейросеть знает, что на картинке котик? Все, что вы хотели знать о сверточных нейросетях

[ Нейросети Часть 3 ]

Откуда нейросеть знает, что на картинке котик? Все, что вы хотели знать о сверточных нейросетях

Smartiqa Article
  • Дата: 3 ноября 2020
  • Автор: Калинин Даниил

Этот материал относится к циклу статей о нейронных сетях


Как нейронные сети получили свое название, из каких компонентов они состоят, как они обучаются и главное - являются ли они полноценным искусственным интеллектом, способным заменить человека?

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

В статье рассказывается о принципе работы сверточных нейронных сетей

В статье рассказывается о принципе работы GAN-моделей и методах их обучения.

Рассмотрим основы фреймворка PyTorch, на примерах научимся создавать тензоры, обращаться к ним по индексам, делать срезы, работать с осями, считать разные метрики и находить ошибку. Все это позволит нам написать свою нейронную сеть в следующем уроке.

Вы наверняка слышали такой термин: компьютерное зрение, но задумывались ли вы, что за ним стоит? Как может компьютер что-то видеть, да еще и понимать, что он увидел? На эти и другие вопросы я собираюсь ответить в этой статье.

Как компьютер работает с изображениями?

Единственное, с чем умеет работать компьютер – это числа. Поэтому все картинки в представлении компьютера, будь то милые животные, мемы или обои на рабочий стол – это просто набор чисел. Каждый пиксель картинки можно окрасить только в один цвет, поэтому в представлении компьютера это просто матрица (таблица) пикселей. Значение в каждой ячейке такой матрицы – инструкция компьютеру: "данный пиксель окрасить в такой-то цвет". Но как задать цвет числом? Существует множество способов: мы остановимся на черно-белых изображениях и rgb-системе.

Итак, в каждой ячейке-пикселе картинки хранится одно или несколько чисел. Здесь я буду говорить о 8-битных изображениях. То есть каждое число лежит в интервале от 0 до 255, всего можно задать 256 оттенков цвета. Очень часто картинки бывают и 16-битными (каждому цвету можно задать 16384 оттенка) и т.д., но в нейронных сетях их обычно сжимают до 8-битных для экономии памяти.

Если в каждой ячейке картинки хранится только одно число, то компьютер воспринимает ее как черно-белую, а само число – показатель градации серого: 0 – идеально белый, 255 – идеально черный, а 254 числа между ними – это оттенки серого.

Но если мы хотим цветную картинку, то в каждую ячейку изображения придется записывать три числа: градации красного – R (англ. red – красный), градации зеленого – G (англ. green – зеленый), градации синего – B (англ. blue – синий), по той же схеме 0 – идеально белый, 255 – идеально красный/зеленый/синий, а все остальное – оттенки. С помощью комбинации красного, синего и зеленого можно представить все остальные цвета, поэтому они и были выбраны. Компьютер, выводя изображение на экран, читает очередную ячейку и для каждого пикселя смешивает красный, зеленый и синий в пропорциях, которые вы указали. Так и работает обработка изображений.

Читайте также

Мы же уже узнали о полносвязной нейросети, почему она не может обрабатывать картинки?

Если коротко: может, но делает это плохо. Сейчас я расскажу почему. Но для начала согласитесь, что сложно воспринимать информацию, не имея перед глазами примера. Поэтому давайте возьмем картинку котика, и поделим ее на условные пиксели. Так мы будем примерно понимать, как картинку видит компьютер.
Картинка в условном представлении компьютера
Картинка в условном представлении компьютера
Скажу еще раз: сетка на картинке – это не пиксели, но для простоты мы будем считать это пикселями. Нумерация сделана для удобства (и да, матрицы нумеруются с нуля в компьютере). Давайте для удобства временно представим, что цвет в каждой клеточке задается только одним числом. Так будет проще.

А теперь представим такую задачу: мы хотим научить нейросеть определять: котик на картинке, или собака. Как бы мы решали такую задачу с помощью полносвязной нейросети? Поскольку один нейрон может воспринять только одно число, нам пришлось бы "вытянуть" нашу картинку в последовательность чисел.

Хорошо, скажете вы, давайте вытянем, в чем тут проблема? Еще 20 лет назад ученые думали: ни в чем. И вытягивали. Такие сети работали, но при большом числе классов они показывали очень плохую точность, чуть больше 50% (это значит, что сеть почти случайно решала, кто на картинке). Например, если мы хотим не определить, кошка или собака на картинке, а понять, что за животное изображено: кошка, собака, слон, жираф, ягуар и т.д., то такая сеть не справится. Так в чем же дело?

Задумайтесь, как вы сами понимаете, кто на картинке. Вы смотрите на картинку, видите ушки, носик, лапки, шерсть и милые глазки и говорите: котик. Но много ли вы сможете увидеть в "вытянутой" картинке? Определите, кто изображен вот здесь.
Суть градиента действительно проста, и я уверен, что читатель уже уловил ее, но все же приведу здесь еще один рисунок. На нем изображена уже более сложная функция, зависящая не от одной, а от двух переменных.
вытянутый в линию котик
Вытянутый в линию котик
На самом деле, вы уже знаете, что здесь котик, но если бы не знали, вам было бы труднее понять, что это кот. А если бы мы не пользовались условными пикселями, вы бы вообще не поняли, кто здесь изображен. Ваш мозг выделяет какие-то абстрактные признаки. Вы видите, что много пикселей одного цвета образуют треугольник, а ваш мозг подсказывает: это кошачье ухо, вы видите много черных пикселей и по краям коричневые, а мозг говорит: это глазки.

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

Операция свертки

Очередное страшное слово, обозначающее простую операцию.
Разберемся сразу на примере. Возьмем матрицу 4 на 4 состоящую из единиц и нулей и заполним ее случайно:
1 | 0 | 1 | 0
0 | 1 | 0 | 1
0 | 0 | 1 | 0
1 | 1 | 1 | 1
Теперь возьмём другую матрицу 2 на 2 – будем называть ее фильтр.
0 | 1
1 | 0

Теперь мы можем представить себе свертку. Мы накладываем фильтр на нашу матрицу и умножаем число матрицы на соответствующее число фильтра. Затем мы складываем все произведения и получаем один элемент результативной матрицы. Дальше мы сдвигаем фильтр на один элемент. После проделываем все то же самое, пока фильтр не дойдет до конца.

Таким образом получим следующую матрицу:
1*0+0*1+0*1+1*0 | 0*0+1*1+1*1+0*0 | 1*0+0*1+0*1+1*0
0*0+1*1+0*1+0*0 | 1*0+0*1+0*1+1*0 | 0*0+1*1+1*1+0*0
0*0+0*1+1*1+1*0 | 0*0+1*1+1*1+1*0 | 1*0+0*1+1*1+1*0

Или, если посчитать:
0 | 2 | 0
1 | 0 | 2
1 | 2 | 1

Чтобы было легче понять, вот анимированный пример. Из него несложно увидеть, как получается число в зеленой матрице. Синяя матрица – исходная, зеленая – результативная, а темная область, которая перемещается по зеленой матрице – и есть наш фильтр.
Операция свертки
Операция свертки
Например, самый верхний левый пиксель зеленой картинки получается так:

12 = 3*0 + 3*1 + 2*2 + 0*2 + 0*2 + 1*0 + 3*0 + 1*1 + 2*2.

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

Мы хотим выделить какие-то абстрактные признаки пикселей, стоящих рядом. Для этого мы просто смотрим на пиксели стоящие рядом через специальный фильтр. Это тот же самый фильтр, что мы использовали раньше, но теперь это не просто матрица с непонятными числами. В фильтре содержится абстрактный признак, который мы хотим выделить из картинки. Чтобы найти этот признак мы постепенно двигаем фильтр, сравнивая рисунок в нем с частью изображения, которую он перекрывает.

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

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

Итак, вернемся к примеру с котиком. Один фильтр – один абстрактный признак. Таким образом, мы можем сделать фильтр, который реагирует на ушко котика.
Безжалостная свертка котика
Безжалостная свертка котика
Вот это темно-синяя табличка, которая "ездит" по изображению котика и есть наш фильтр. Единички на нем отмечены так, чтобы он реагировал на ухо. Действительно, если мысленно соединить единички ломанной линией, то получится контур левого (левого для нас, правого – для котика) уха. Поэтому когда такой фильтр наложится на ухо, получится большое число: 10, по мере удаления фильтра от уха число становится все меньше и меньше. А когда фильтр вообще не перекрывает ухо, число обращается в 0.

Для полной ясности давайте повторим, как работает свертка:
  1. Мы выбираем фильтр, накладываем его в левый верхний угол изображения
  2. Каждое значение клетки фильтра умножается на перекрытое ей значение пикселя картинки
  3. Полученные произведения складываются и записываются в новую "свернутую" картинку
  4. Фильтр сдвигается на одну клетку вправо, операции 1-3 повторяются, пока фильтр не пройдет всю картинку.

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

Причем здесь нейросети

У читателя, наверное, появился вполне справедливый вопрос: причем тут нейросеть? Помните фильтры, которые проходятся по изображению? В этих фильтрах лежат абстрактные признаки: в одном – ушко, в другом – носик, в третьем – лапки и так далее. Эти фильтры составляет не человек, иначе вам бы не рассказывали о них в статье о машинном обучении. Изначально эти фильтры заполнены случайными числами, их никак нельзя интерпретировать, нейросеть просто не знает, что за признаки есть на картинке.

Дальше она действует так.
  1. Она проходится по картинке, выделяет совершенно абсурдные признаки,
  2. Пытается по ним предсказать: котик на картинке или собачка.
  3. У нее ничего не получается, ведь она из картинки выделила что-то случайное
  4. Нейросеть находит ошибку (например, ошибка 10, если нейросеть не угадала, и 0, если угадала)
  5. Ошибка как-то зависит от фильтров свертки, поэтому можно взять градиент
  6. По взятому градиенту нейросеть понимает, как менять числа в фильтре так, чтобы ошибка стала меньше
  7. Повторяя шаги 1-6 много-много раз на большом количестве разных картинок, модель понимает, какими должны быть фильтры

После такого обучения мы можем отдать нейросети картинку котика, которого она никогда раньше не видела и с восторгом заметить, что модель правильно определила, кто на картинке. А если мы визуализируем фильтры, которые нейросеть настроила, то с еще большим восторгом обнаружим, что это не какие-то непонятные рисунки, а очертания лапок, ушек, хвостика, усиков, мордочки и так далее. То есть нейросеть выделила те же признаки, что и человек, когда смотрит на животных. Такое выделение признаков, понятных человеку, свойственно всем сверточным нейросетям.
Надеюсь, после прочтения этой статьи, вы поняли, как нейросеть узнает что на картинке котик. Математика в сочетании с большими вычислительными мощностями способна на удивительные вещи.
3 НОЯБРЯ / 2020
Как вам материал?

Читайте также