Усы, лапы и хвост: как нейронная сеть распознает котиков и другие объекты

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Распознавание изображений — классический пример использования нейронных сетей. Вспомним, как происходит процесс обучения сети, в чем возникают сложности и зачем в разработке использовать биологию. Подробности под катом. В рассказе нам поможет Дмитрий Сошников — технический евангелист Microsoft, член Российской ассоциации искусственного интеллекта, преподаватель функционального и логического программирования ИИ в МАИ, МФТИ и ВШЭ, а также наших курсов.

Представьте, что у нас есть множество картинок, которые нужно отсортировать по двум стопкам с помощью нейронной сети. Каким образом это можно сделать? Конечно, все зависит от самих объектов, но мы всегда можем выделить какие-то особенности.

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

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

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

Банки фильтров и биологическое зрение

image
С помощью разных фильтров можно выделять различные фрагменты изображения, которые затем обнаруживать и исследовать в виде отдельных свойств. Например, подавать на вход традиционному машинному обучению или нейросетям. Если нейросеть имеет дополнительную информацию о структуре объектов, с которыми она работает, то качество работы возрастает. В области машинного зрения наработаны банки фильтров — наборы фильтров для выделения основных особенностей объектов. Похожая «архитектура» используется и в биологии. Ученые считают, что человеческое зрение не определяет все изображение целиком, а выделяет характерные особенности, уникальные черты, по которым мозг и идентифицирует объект. Соответственно, для быстрого и корректного распознавания объекта можно определить максимально уникальные черты. К примеру, у котов это могут быть усы — веерные горизонтальные черточки на изображении.
image

Разделение весов (Weight Sharing)

Чтобы сети не приходилось отдельно учиться распознавать котиков в разных частях картинки, мы «разделяем» веса, отвечающие за распознавание, между различными фрагментами входных сигналов.

Это требует специализированной архитектуры сети:

  • сверточные сети для работы с изображениями
  • рекуррентные сети для работы с текстом / последовательностями

Нейронные сети, эффективно использующиеся в распознавании изображений, в которых применяются специальные свёрточные слои (Convolution Layers).

Основная идея заключается в следующем:

  • Используем weight sharing для создания «фильтрующего окна», пробегающего по изображению
  • Примененный к изображению фильтр помогает выделить фрагменты, важные для распознавания
  • В то время как в традиционном машинном зрении фильтры конструировали вручную, нейросети позволяют нам сконструировать оптимальные фильтры с помощью обучения
  • Фильтрацию изображения можно естественным образом совместить с вычислением нейронной сети

image
Для обработки изображений используется свертка, как и в обработке сигналов. Опишем функцию свертки со следующими параметрами:
  • kernel — ядро свёртки, матрица весов
  • pad — сколько пискелей надо добавить к изображению по краям
  • stride — частота применения фильтра. Например, для stride=2 будем брать каждый второй пиксель изображения по вертикали и горизонтали, уменьшив разрешение вдвое

In [1]: def convolve(image, kernel, pad = 0, stride = 1):     rows, columns = image.shape     output_rows = rows // stride     output_columns = columns // stride     result = np.zeros((output_rows, output_columns))     if pad > 0:         image = np.pad(image, pad, 'constant')         kernel_size = kernel.size     kernel_length = kernel.shape[0]     half_kernel = kernel_length // 2       kernel_flat = kernel.reshape(kernel_size, 1)     offset = builtins.abs(half_kernel-pad)     for r in range(offset, rows - offset, stride):         for c in range(offset, columns - offset, stride):             rr = r - half_kernel + pad             cc = c - half_kernel + pad               patch = image[rr:rr + kernel_length, cc:cc + kernel_length]             result[r//stride,c//stride] = np.dot(patch.reshape(1, kernel_size), kernel_flat)                 return result 

 In [2]: def show_convolution(kernel, stride = 1):     """Displays the effect of convolving with the given kernel."""     fig = pylab.figure(figsize = (9,9))     gs = gridspec.GridSpec(3, 3, height_ratios=[3,1,3])     start=1     for i in range(3):                 image = images_train[start+i,0]         conv = convolve(image, kernel, kernel.shape[0]//2, stride)         ax = fig.add_subplot(gs[i])         pylab.imshow(image, interpolation='nearest')         ax.set_xticks([])         ax.set_yticks([])         ax = fig.add_subplot(gs[i + 3])         pylab.imshow(kernel, cmap='gray', interpolation='nearest')         ax.set_xticks([])         ax.set_yticks([])         ax = fig.add_subplot(gs[i + 6])         pylab.imshow(conv, interpolation='nearest')         ax.set_xticks([])         ax.set_yticks([])             pylab.show() 

 In [3]: blur_kernel = np.array([[1, 4, 7, 4, 1],                         [4, 16, 26, 16, 4],                         [7, 26, 41, 26, 7],                         [4, 16, 26, 16, 4],                         [1, 4, 7, 4, 1]], dtype='float32') blur_kernel /= 273 

Фильтры

Blur

Фильтр размытия позволяет сгладить неровности и подчеркнуть общую форму объектов.

image
In [4]: show_convolution(blur_kernel) 

Вертикальные края

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

image
In [5]: vertical_edge_kernel = np.array([[1, 4, 0, -4, 1],                                    [4, 16, 0, -16, -4],                                    [7, 26, 0, -26, -7],                                    [4, 16, 0, -16, -4],                                    [1, 4, 0, -4, -1]], dtype='float32') vertical_edge_kernel /= 166 

 In [6]: show_convolution(vertical_edge_kernel) 

Горизонтальные края

Аналогичный фильтр можно построить для выделения горизонтальных штрихов на изображении.

 In [7]: horizontal_bar_kernel = np.array([[0, 0, 0, 0, 0],                                  [-2, -8, -13, -8, -2],                                  [4, 16, 26, 16, 4],                                  [-2, -8, -13, -8, -2],                                  [0, 0, 0, 0, 0]], dtype='float32') horizontal_bar_kernel /= 132 

 In [8]: show_convolution(horizontal_bar_kernel) 

Контурный фильтр

Также можно построить фильтр 9x9, который будет выделять контуры изображения.

 In [9]: blob_kernel = np.array([[0, 1, 1, 2, 2, 2, 1, 1, 0],                        [1, 2, 4, 5, 5, 5, 4, 2, 1],                        [1, 4, 5, 3, 0, 3, 5, 4, 1],                        [2, 5, 3, -12, -24, -12, 3, 5, 2],                        [2, 5, 0, -24, -40, -24, 0, 5, 2],                        [2, 5, 3, -12, -24, -12, 3, 5, 2],                        [1, 4, 5, 3, 0, 3, 5, 4, 1],                        [1, 2, 4, 5, 5, 5, 4, 2, 1],                        [0, 1, 1, 2, 2, 2, 1, 1, 0]], dtype='float32') blob_kernel /= np.sum(np.abs(blob_kernel))  

 In [10]: show_convolution(blob_kernel) 

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

image
Если применить схожий подход к поиску котиков на картинке, быстро выяснится, что признаков у четвероногого для обучения нейросети масса, и все они разные: хвосты, уши, усы, носы, шерсть и окраска. И у каждого кота может быть ничего общего с другим. Нейросеть с небольшим количеством данных о структуре объекта не сможет понять, что один кот лежит, а второй стоит на задних лапах.

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

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

К примеру, у нас есть изображение i, 2 сверточных фильтра w c выходами o. Элементы выходного изображения будут вычисляться следующим образом:

Тренировка весов

Алгоритм таков:

  • Фильтр с одними и теми же весами применяется ко всем пикселям изображения.
  • При этом фильтр «пробегает» по всему изображению.
  • Мы хотим обучать эти веса (общие для всех пикселей) по алгоритму обратного распространения.
  • Для этого надо свести применение фильтра к однократному умножению матриц.
  • В отличие от полносвязного слоя, весов для обучения будет меньше, а примеров — больше.
  • Хитрость — im2col

im2col

Начнем с изображения x, где каждый пиксель соответствует букве:

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

Проблемы анализа изображений

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

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

В конце концов, кривой код тоже никто не отменял.

Чтобы научить работе с нейронными сетями, справляться с ее обучением и определять, где на практике можно воспользоваться машинным обучением, мы с Дмитрием Сошниковым разработали специальный курс Neuro Workshop. Конечно, на нем рассказывается и о том, как решать перечисленные выше проблемы.

Neuro Workshop пройдет 2 раза:


Выбирайте удобный день, приходите и задавайте Дмитрию свои вопросы.

Источник: habr.com

Комментарии: