Описание проблемы распознавания выражения лица

Здравствуйте и вновь добро пожаловать на наши занятия – «Обработка данных: практическое глубокое обучение в Theano и TensorFlow».

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

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

Итак, в чём заключается распознавание выражения лица?

Ну, люди очень хорошо распознают чувства человека по его лицу. Но может ли это сделать компьютер? Обратите внимание – компьютер не понимает чувства. Это лишь обучение компьютера, какой набор пикселей на рисунке коррелируется с каким-либо чувством. Но всё равно, это интересная задача, потому что вы, к примеру, можете определить состояние человека лишь по фотографии в социальных сетях. Скорее всего, в Facebook и других соцсетях есть специальные команды, которые занимаются тем же.

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

Итак, каковы же нужные нам данные?

Это набор чёрно-белых изображений лиц размером 48×48. Изображения были предварительно обработаны, чтобы лица находились по центру, а каждое изображение занимало примерно одинаковый объём. Каждое изображение лица выражает чувство, а наша задача в том, чтобы классифицировать, какое из семи чувств оно выражает – нуль для гнева, единица для отвращения, двойка для страха, тройка для счастья, четвёрка для грусти, пятёрка для удивления и шестёрка – для нейтрального выражения лица.

Данные отображены в формате CSV, причём первый столбец означает метку, второй – пространственно разделённые пиксели в изображении, а третий – принадлежат ли данные к учебному или проверочному набору. Последний столбец мы проигнорируем, поскольку сами случайным образом поделим данные на учебный и проверочный наборы.

Итак, обратимся к изображениям, но прежде пройдёмся по уже написанному мною коду для их отображения.

Сначала мы, как обычно, импортируем библиотеки Numpy и Matplotlib. У нас также файл данных util.py и функция getData из этого файла, которая загружает данные и предварительно их обрабатывает.

import numpy as np

import matploylib as plt

from util import getData

У нас также есть таблица меток, а её входными переменными являются целые числа от 0 до 6. Значение меток мы уже обсудили ранее.

label_map – (‘Anger’, ‘Disgust’, ‘Fear’, ‘Happy’, ‘Sad’, ‘Surprise’, ‘Neutral’)

Теперь перейдём к основной функции. Первая строка основной функции – это просто получение данных. Следующим идёт бесконечный цикл прохода. Для каждого из семи чувств мы выбираем все входные данные, соответствующие этому чувству, и находим номер соответствующих входных данных. Затем случайным образом выбираем данные и выводим на экран. Поскольку наши данные заданы плоским вектором, изображение необходимо перерисовать в размере 48×48; цвет указан как серый, поскольку наши фотографии являются чёрно-белыми. Далее мы выводим на экран изображение с соответствующим чувством. Далее, поскольку цикл бесконечный, я написал код с запросом, выйти из цикла или нет. Если вы желаете выйти, надо нажать заглавное Y, после чего бесконечный цикл завершается.

def main():

X, Y = getData(balance_ones=False)

while True:

for i in xrange(7):

x, y = X[Y==i], Y[Y==1]

N = len(y)

j = np.random.choise(N)

plt.imshow(x[j].reshape(48, 48), cmap=’gray’)

plt.title(label_map[y[j]])

plt.show()

prompt = raw_input(‘Quit? Enter Y:\n’)

if prompt == ‘Y’:

break

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

Поскольку у нас занятия по логистической регрессии, мы ещё не умеем пользоваться функцией мягкого максимума или брать её производную. Вместо этого преобразуем проблему в задачу двоичной классификации. Мы это сделаем путём изучения классов с метками только 0 и 1. Как вы сможете убедиться, это будет несколько сложно, поскольку у нас 4953 примера с меткой 0 и лишь 547 примеров с меткой 1. На следующей лекции я объясню, в чём сложность, но настоятельно рекомендую вам перед просмотром подумать об этом самостоятельно.

Ещё один вопрос, требующий внимания, – когда мы в концов концов используем софтмакс и будем классифицировать все семь классов – станет наша задача легче или труднее? Для ответа на этот вопрос задумайтесь, какой будет ожидаемый коэффициент классификации, если бы мы выбирали наугад? Предположим, данные на 50% состоят из класса 0, а 50% – из класса 1, и мы угадываем результат случайным образом. Каким будет ожидаемый коэффициент классификации? Очевидно, 50%, поскольку у нас есть 50%-й шанс угадать правильно.

Теперь предположим, что у нас есть K классов с равным количеством примеров в каждом классе. В этом случае наш ожидаемый коэффициент классификации будет составлять лишь 1/K, то есть правильно угадать теперь уже намного труднее. Поскольку в нашем случае K = 7, то попытка случайно угадать будет давать нам лишь 14% точности, или 86% ошибок. Лучший показатель точности на конкурсе Kaggle составил около 70%. Мы не станем стремиться к столь высокой точности, поскольку это требует очень много усилий, но увидим, что получится, когда используем подход «plug and play» с несколькими разными значениями гиперпараметров. Впрочем, если вы пожелаете самостоятельно настроить гиперпараметры для достижения ещё лучших результатов, то это только приветствуется.

Теперь обсудим структуру наших данных. Не забывайте, что это изображения, то есть по природе своей являются матрицами. Каждое из них является набором пикселей 48×48 и, таким образом, в общем у нас получается 2304 размерности для каждого примера. Важное замечание – они у нас не цветные. Если бы они были цветными, то количество размерностей возросло бы до 3*48*48 – то есть до 6912 размерностей для каждого примера, что потребует куда более интенсивных вычислений. И это при условии, что у нас всего три цвета – красный, зелёный и синий.

Ещё один нюанс состоит в том, что логистическая регрессия и обычные нейронные сети, в отличие от свёрточных нейронных сетей, работают с плоскими векторами. Это значит, что на самом деле мы работаем не с матрицей размерности 48×48, а сглаживаем изображение, так что на практике мы имеем дело с вектором размерности 2304. Это происходит потому, что первая строка изображения представлена первыми 48 элементами вектора с номерами от 1 до 48, вторая строка представлена очередными 48 элементами с номерами от 49 до 96 и так далее.

Что это значит? Это значит, что мы игнорируем пространственные соотношения для данных, и нам совершенно неважно, что пиксель 49 находится рядом с пикселем 1. Мы обрабатываем каждый пиксель одинаково и независимо относительно других. Как вы впоследствии убедитесь, при использовании свёрточных нейронных сетей это будет уже не так.

Следующим необходимым этапом предварительной обработки является нормализация данных. Значение пикселя, выражающее его яркость, варьируется в диапазоне от 0 до 255, но мы хотим преобразовать его так, чтобы он находится в диапазоне от 0 до 1. Вероятно, вы понимаете нормализацию как вычитание текущего значения величины от её среднего значения и последующее деление на стандартное отклонение. Но поскольку у нас в качестве данных значения пикселей, принимающие строго положительное значение, нам достаточно поделить их на максимальное значение. Причина, по которой мы хотим преобразовать данные в столь малый диапазон, заключается в том, что именно в нём функции, использующиеся в нейронных сетях, являются наиболее динамичными. Так, если вы взглянете на графики функций гиперболического тангенса или сигмоиды, то увидите, что самый крутой наклон находится в районе между -1 и +1. При этом за пределами этого диапазона наклон весьма пологий, поэтому работать вне его малоинтересно.

Теперь остановимся на некоторых трудностях при решении данной задачи. Не забывайте, что глубокое обучение – это на самом деле не чёрный ящик, работающий по принципу «подставь и решай», а потому для успешной работы необходимо правильно подобрать значения гиперпараметров.

Итак, в чём же трудности?

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

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

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

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

Уверен, что все изучающие этот курс будут благодарны!

Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: