Предобучение ограниченными машинами Больцмана для распознавания реальных изображений

image
Доброго времени суток. Этот топик рассчитан на тех, кто имеет представление об ограниченных машинах Больцмана (restricted Boltzmann machine, RBM) и их использовании для предобучения нейронных сетей. В нем мы рассмотрим особенности применения ограниченных машин Больцмана для работы с изображениями, взятыми из реального мира, поймем, почему стандартные типы нейронов плохо подходят для этой задачи и как их улучшить, а также немного пораспознаем выражения эмоций на человеческих лицах в качестве эксперимента. Те, кто представления o RBM не имеет, могут его получить, в частности, отсюда:

Реализация Restricted Boltzmann machine на c#,
Предобучение нейронной сети с использованием ограниченной машины Больцмана


Почему все плохо


Ограниченные машины Больцмана изначально были разработаны с использованием стохастических бинарных нейронов, как видимых, так и скрытых. Применение такой модели для работы с бинарными даными совершенно очевидно. Однако подавляющее большинство реальных изображений — не бинарны, а представлены как минимум оттенками серого с целым значением яркости каждого пикселя от 0 до 255. Одно из возможных решений проблемы — изменим значения яркости так, чтобы они лежали в промежутке 0..1 (поделим на 255), и будем считать, что пиксели на самом деле бинарны, а полученные значения представляют вероятность установки каждого конкретного пикселя в единицу. Попробуем использовать этот подход для распознавания рукописных символов (MNIST) и вуаля — все работает и работает чудесно! Почему же все плохо?

А потому, что во множестве реальных изображений интенсивность некоторого пикселя почти всегда почти в точности равна средней интенсивности его соседей. Потому интенсивность должна иметь большую вероятность быть близкой к средней и малую вероятность быть даже немного отдаленной от нее. Сигмоидальная (логистическая) функция не позволяет достичь такого распределения, хотя и работает в отдельных случаях, где это оказывается неважно (например, рукописные символы)[1].



Как сделать, чтобы было хорошо...


Нам нужнен способ представления видимых нейронов, который способен говорить, что интенсивность скорее всего равна, скажем, 0.61, менее вероятно 0.59 или 0.63, и очень-очень маловероятно 0.5 или 0.72. Функция плотности вероятности при этом должна выглядеть примерно так:

Да это же нормальное распределение! По крайней мере его можно использовать для моделирования такого поведения нейронов, что мы и сделаем, сделав значения видимых нейронов случайными величинами с нормальным распределением вместо распределения Бернулли. Нужно заметить, что нормальное распределение удобно использовать не только для работы с реальными изображениями, но и со многими другими данными, представлеными действительными числами из диапазона [-∞;+∞], для которых не имеет смысла сведение значений к бинарному виду или вероятностям из диапазона [0;1][2]. Скрытые нейроны при этом остаются бинарными и мы получаем так называемую Gaussian-Binary RBM, распределения значений нейронов для которой задаются формулами[3]






а энергия машины Больцмана равна



где hid — множество индексов скрытых нейронов,
vis — множество индексов видимых нейронов,
b — bias (смещение),
σi — стандартное отклонение для і-го видимого нейрона,
wi,j — вес связи между i-м и j-м нейроном,
N(x | μ, σ2) — вероятность значения х для переменной с нормальным распределением с матожиданием μ и дисперсией σ2.

Рассмотрим, как изменяется энергия RBM при изменении vi. Компонент bi (bias) отвечает за желаемое значение i-го видимого нейрона (интенсивность соответсвующего пикселя изображения), а сама энергия растет квадратически с отклонением от этого значения:

Последний компонент формулы, зависящий как от vi, так и от hi, и представляющий их взаимодействие, зависит от vi линейно:

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

Однако и здесь возникают трудности. Во-первых, для каждого видимого нейрона нужно подобрать в результате обучения подходящий параметр σi. Во-вторых, малые значения σi сами по себе порождают трудности в обучении, обуславливая сильное воздействие видимых нейронов на скрытые и слабое воздействие скрытых нейронов на видимые:


В-третьих, значение видимого нейрона теперь может неограниченно расти, заставляя энергию неограниченно падать, из-за чего обучение становится намного менее стабильным. Для решения первых двух проблем Джеффри Хинтон предлагает нормализовать все тренировочные данные перед началом обучения так, чтобы они имели нулевое матожидание и единичную дисперсию, после чего полагать параметр σi в вышеуказанных уравнениях равным единице[4]. Кроме того, такой подход позволяет использовать точно такие же формулы для сбора статистики и обучения RBM методом CD-n, что и в обычном случае (с использованием только бинарных нейронов). Третья проблема решается простым уменьшением скорости обучения (learning rate) на 1-2 порядка.



… и еще лучше


В результате мы научились хорошо представлять действительные (real-valued) данные видимыми нейронами ограниченной машины Больцмана, однако внутреннее, скрытое состояние все еще бинарно. Можно ли как-то улучшить скрытые нейроны, заставить их нести в себе больше информации? Оказывается можно. Очень легко, оставив скрытые нейроны бинарными, заставить их отображать натуральные числа, большие 1. Для этого возьмем один какой-нибудь скрытый нейрон и создадим множество его копий с точно такими же весами от видимых нейронов (weigth sharing) и обучаемым смещением bi, однако при расчете вероятностей будем отнимать от смещения каждого нейрона фиксированные значения, получив множество в остальном одинаковых нейронов со смещениями bi-0.5, bi-1.5, bi-2.5, bi-3.5… В результате мы получим целочисленную версию rectified linear unit с добавлением шума с дисперсией σ(x) = (1 + exp(-x))-1 (из-за вероятностной природы нейронов). Проще говоря, чем больше будет значение x = bi + ∑vjwi,j на входе такого нейрона, тем больше его копий активируются одновременно, при этом количество всех активированных копий и будет отображаемым натуральным числом:



Однако в реальности создавать большое количество копий для каждого нейрона затратно, потому что это в такое же большое количество раз увеличивает количество рассчетов сигмоидальной функции на каждой итерации обучения/работы RBM. Потому поступим кардинально — создадим сразу бесконечное количество копий для каждого нейрона! Теперь мы имеем простое приближение, позволяющее нам считать получающееся значение для каждого нейрона по одной единственной простой формуле[1,5]:



Таким образом наши скрытые нейроны превратились из бинарных в rectified linear units с Гауссовским шумом, при этом алгоритм обучения остался нетронутым (ведь мы предполагаем, что на самом деле это все те же бинарные нейроны, только с бесконечным количеством вышеописанных копий). Теперь они умеют представлять не только 0 и 1, и даже не только натуральные, но все неотрицательные действительные числа! Дисперсия σ(x)∊ [0;1] гарантирует, что полностью неактивные нейроны не будут создавать шума и шум не будет становиться очень большим при увеличении х. Кроме того, приятный бонус: использование таких нейронов позволяет все-таки обучить параметр σi для каждого нейрона, если предварительная нормализация данных по каким-то причинам невозможна или нежелательна[1,2], но на этом подробно останавливаться не будем.



Реализация обучения


Вынося матожидание из формулы нормального распределения, значение видимого нейрона можно считать по формуле


где N(μ, σ2) — случайная величина с нормальным распределением, матожиданием μ и дисперсией σ2.

Джеффри Хинтон в [4,5] предлагает не использовать Гауссовский шум в реконструкциях видимых нейронов при обученни. Аналогично использованию чистых вероятностей в случае бинарных нейронов вместо выбора 0 или 1, это ускоряет обучение за счет уменьшения шума и чуть меньшего времени работы одного шага алгоритма (не нужно считать N(0,1) для каждого нейрона). Последовав совету Хинтона получаем полностью линейные видимые нейроны:



Значение скрытого нейрона считаем по формуле



Для реализации самого обучения используем точно такие же формулы, что и для обычных бинарных нейронов в CD-n.



Эксперимент


В качестве эксперимента выберем что-нибудь поинтереснее, чем простое распознавание лиц или рукописных символов. Например, будем распознавать, какая эмоция выражается на лице человека. Используем для обучения и тестирования изображения из баз
Cohn-Kanade AU-Coded Facial Expression Database (CK+),
Yale Face Database,
Indian Face Database,
The Japanese Female Facial Expression (JAFFE) Database.

Изо всех баз выберем только изображения с пометкой конкретной эмоции (одной из восьми: нейтральное выражение, гнев, страх, отвращение, радость, удивление, презрение, грусть). Получим 719 изображений. 70% случайно выбранных изображений (500 штук) используем в качестве тренировочных, а 30% оставшихся (219 штук) — в качестве проверочных (validation data) (в нашем случае они могут использоваться как тестовые, поскольку мы не подбираем с их помощью никакие параметры). Для реализации будем использовать MATLAB 2012b. На каждом изображении выделим лицо с помощью стандартного vision.CascadeObjectDetector, полученную квадратную область расширим вниз на 10% для того, чтобы подбородок полностью входил в обрабатываемое изображение. Полученное изображение лица сожмем до размера 70х64, переведем в оттенки серого и применим к нему гистограмную эквализацию для выравнивания контраста на всех изображениях. После этого каждое изображение развернем в вектор 1х4480 и сохраним соотвествующие вектора в матрицы train_x и val_x. В матрицах train_y и val_y сохраним соотвествующие желаемые вектора-выходы классификатора (размер 1х8, 1 в позиции эмоции, представленной вектором-входом, 0 в остальных позициях). Данные готовы, пора приступать к собственно эксперименту.

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

Количества нейронов в каждом слое нашей нейросети: 4480 — 200 — 300 — 500 — 8. Такие маленькие количества нейронов в скрытых слоях выбраны для того, чтобы исключить переобучение (overfitting) и простое запоминание сетью всех входных изображений, так как их количество невелико. Сначала обучим нейросеть с сигмоидальной функцией активации, а для предобучения используем обычные бинарные RBM.

tx = double(train_x)/255;
ty = double(train_y);

vx = double(val_x)/255;    
vy = double(val_y);

% train DBN (stack of RBMs)

dbn.sizes = [200 300 500];

opts.numepochs = 100;
opts.batchsize = 25; 
opts.momentum  =   0.5;
opts.alpha     =   0.02;                          
opts.vis_units = 'sigm';                            % Sigmoid visible and hidden units
opts.hid_units = 'sigm';                           

dbn = dbnsetup(dbn, tx, opts);
dbn = dbntrain(dbn, tx, opts);

% train NN

nn = dbnunfoldtonn(dbn, 8);
nn.activation_function              = 'sigm';       %  Sigmoid hidden units
nn.learningRate                     = 0.05;         
nn.momentum                         = 0.5;          
nn.output                           = 'softmax';    %  Softmax output to get probabilities
nn.errfun                           = @nntest;      %  Error function to use with plotting
                                                    %  calculates misclassification rate
opts.numepochs = 550;
opts.batchsize = 100;
opts.plot = 1;
opts.plotfun = @nnplotnntest;                       % Plotting function

nn = nntrain(nn, tx, ty, opts,vx,vy);    



График обучения нейросети:


Средняя ошибка на проверочных данных среди 10 запусков с каждый раз новой случайной выборкой тренировочных и проверочных (validation) данных составила 36.26%.

Теперь обучим нейросеть с rectified linear функцией активации, а для предобучения используем описанные нами RBM.

tx = double(train_x)/255;
ty = double(train_y);
normMean = mean(tx);
normStd = std(tx);

vx = double(val_x)/255;    
vy = double(val_y);

tx = normalize(tx, normMean, normStd);  %normalize data to have mean 0 and variance 1
vx = normalize(vx, normMean, normStd);

% train DBN (stack of RBMs)

dbn.sizes = [200 300 500];

opts.numepochs = 100;
opts.batchsize = 25; 
opts.momentum  =   0.5;
opts.alpha     =   0.0001;                          % 2 orders of magnitude lower learning rate
opts.vis_units = 'linear';                          % Linear visible units
opts.hid_units = 'NReLU';                           % Noisy rectified linear hidden units

dbn = dbnsetup(dbn, tx, opts);
dbn = dbntrain(dbn, tx, opts);

% train NN

nn = dbnunfoldtonn(dbn, 8);
nn.activation_function              = 'ReLU';       %  Rectified linear units
nn.learningRate                     = 0.05;         
nn.momentum                         = 0.5;          
nn.output                           = 'softmax';    %  Softmax output to get probabilities
nn.errfun                           = @nntest;      %  Error function to use with plotting
                                                    %  calculates misclassification rate
opts.numepochs = 50;
opts.batchsize = 100;
opts.plot = 1;
opts.plotfun = @nnplotnntest;                       % Plotting function

nn = nntrain(nn, tx, ty, opts,vx,vy);    



График обучения нейросети:



Средняя ошибка на проверочных данных среди 10 запусков с теми же выборками, что и для бинарных нейронов, составила 28.40%

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

Как видно, использование linear и rectified linear нейронов в ограниченной машине Больцмана позволило уменьшить ошибку распознавания на 8%, не говоря уже о том, что для последующего обучения нейронной сети потребовалось в 10 раз меньше итераций (эпох).



Ссылки


1. Neural Networks for Machine Learning (видеокурс)
2. Learning Natural Image Statistics with Gaussian-Binary Restricted Boltzmann Machines
3. Learning Multiple Layers of Features from Tiny Images
4. A Practical Guide to Training Restricted Boltzmann Machines
5. Rectified Linear Units Improve Restricted Boltzmann Machines
Поделиться публикацией

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

  • НЛО прилетело и опубликовало эту надпись здесь
      +2
      а чего это вдруг не приносит? -) это самый самый state-of-the-art нейросетей, и реально используется в распознавании образов
      • НЛО прилетело и опубликовало эту надпись здесь
          0
          Для того что бы на этом зарабатывать не обязательно создавать готовый продукт за свой счет. Можно устроиться в компанию где требуются программисты со знанием алгоритмов машинного обучения. Такая позиция обычно отлично оплачивается.
        –1
        Этим можно заниматься и после студенческих лет, особенно пока нет собственной семьи.
        0
        Замечание о графиках: поскольку нас фактически интересует способность сети правильно распознавать эмоции, а не минимизировать функцию ошибки, обучение продолжается, пока эта способность улучшается, даже после того, как функция ошибки начинает расти.

        Не понял этот момент. А как вы понимаете что способность распознавать эмоции все еще улучшается?
          0
          Там два графика: слева функция ошибки нейросети (cross entropy), справа — процент неправильно распознанных изображений. Пока процент неправильно распознанных изображений на тестовом множестве уменьшается (правый график), способность распознавать эмоции улучшается.
            0
            А, понял, ок. Подумалось, что вы попадаете на переобучение.
          0
          И еще один момент. Вроде как товарищи из той же компании что и Хинтон говорят что можно достичь хорошего обучения и без при-тренинга через один из квазиньютоновских методов. Не пробовали, случаем?

          Вот статья: Martens, J. (2010). Deep learning via hessian-free optimization

          ps я практически не занимался дип ленингом и нейросетями, просто интересно
            0
            на самом деле то способов много, я например тестил через principal component analysis habrahabr.ru/post/176257/, если проинициализировать веса слоя нейронов используя значения полученные из PCA, то сеть будет обучаться лучше нежели при рандомной инициализации; фишка именно в качестве главных компонент, rbm ищет очень компактные признаки, что можно увидеть на картинках по вышеупомянутой ссылке

            предобучение в целом, или любую умную инициализацию можно сформулировать немного по другому — это проекция исходного образа в пространство другой размерности, такое что бы минимизировать потери информации, чем по сути и занимается rbm: максимизирует вероятность того, что образ будет из пространства, размерности скрытого слоя, восстановлен верно; так что в принципе можно брать методы из en.wikipedia.org/wiki/Nonlinear_dimensionality_reduction и адаптировать их для инициализации весов
              0
              Ну смысл предварительной настройки весов вобщем-то понятен. Но я немного о другом. В той статье что я привел (да и в куче последующих у коллег Хинтона) используется не вычисление градиента как в стандартном backprop, а вычисление гессиана. Точнее, не гессиана а его приближения, т.к. сам гессиан конечно никто считать не будет. Т.е. фактически инициализация весов не важна — важен сам способ оптимизации.

              По-моему в питоновском theano все функции для этого есть. Нужно попробовать.
              +1
              Слышал, но в деталях не разбирался. Хинтон упоминал этот метод в своем видеокурсе. Если буду разбираться — обязательно сравню с предобучением и напишу на хабр, что получилось :)
                0
                Было бы интересно, спасибо.
              0
              Подскажите, не встречалась ли готовая реализация подобных алгоритмов на Java?

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

              Самое читаемое