PixelRNN и PixelCNN — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Архитектура: - Обновление знаков умножения)
м (rollbackEdits.php mass rollback)
 
(не показано 66 промежуточных версий 4 участников)
Строка 1: Строка 1:
 
[[File:pixel-1.png|450px|thumb|Рисунок 1. Пример использования PixelRNN/PixelCNN сетей]]
 
[[File:pixel-1.png|450px|thumb|Рисунок 1. Пример использования PixelRNN/PixelCNN сетей]]
  
'''PixelRNN/PixelCNN''' - алгоритмы машинного обучения, входящие в семейство авторегрессивных моделей. Используются для генерации и дополнения изображений. Алгоритмы были представлены в 2016 году компанией DeepMind<ref name=PixelNet>[https://arxiv.org/abs/1601.06759 Pixel Recurrent Neural Networks]</ref> и являются предшественниками алгоритма WaveNet<ref name=WaveNet>[https://deepmind.com/blog/article/wavenet-generative-model-raw-audio WaveNet: A generative model for raw audio]</ref>, который используется в голосовом помощнике Google.  
+
'''''PixelRNN''''' и '''''PixelCNN''''' {{---}} алгоритмы машинного обучения, входящие в семейство авторегрессивных моделей и использующиеся для генерации и дополнения изображений. Алгоритмы были представлены в 2016 году компанией ''DeepMind''<ref name=PixelNet>[https://arxiv.org/abs/1601.06759 Pixel Recurrent Neural Networks]</ref> и являются предшественниками алгоритма ''WaveNet''<ref name=WaveNet>[https://deepmind.com/blog/article/wavenet-generative-model-raw-audio WaveNet: A generative model for raw audio]</ref>, который используется в голосовом помощнике ''Google''.  
  
Основным преимуществом PixelRNN/PixelCNN является уменьшение времени обучения, по сравнению с наивными способами попиксельной генерации изображений.
+
Основным преимуществом ''PixelRNN'' и ''PixelCNN'' является уменьшение времени обучения, по сравнению с наивными способами попиксельной генерации изображений.
 
== Постановка задачи ==
 
== Постановка задачи ==
Пусть дано черно-белое изображение <tex>X</tex> размером <tex>N\times N</tex>. Построчно преобразуем картинку в вектор <tex>V_X = \{x_1, x_2, \dots, x_{N^2} \}</tex>, соединяя конец текущей строки с началом следующей. В таком представлении изображения можно предположить, что значение любого пикселя <tex>x_i\in V_X</tex> может зависеть от значений предыдущих пикселей <tex>x_j, j = 1,2,\dots i-1</tex>.  
+
Пусть дано черно-белое изображение <tex>X</tex> размером <tex>N\times N</tex>. Построчно преобразуем картинку в вектор <tex>V_X = \{x_1, x_2, \dots, x_{N^2} \}</tex>, соединяя конец текущей строки с началом следующей. При таком представлении изображения можно предположить, что значение любого пикселя <tex>x_i\in V_X</tex> может зависеть от значений предыдущих пикселей <tex>x_j, j = 1,2,\dots i-1</tex>.  
  
Тогда значение пикселя <tex>x_i\in V_X</tex> можно выразить через условную вероятность <tex>p(x_i|x_1, x_2, \dots x_{i-1})</tex>, и, используя цепное правило для вероятностей<ref name=ChainRule>[https://en.wikipedia.org/wiki/Chain_rule_(probability) Chain rule (probability)]</ref>, оценка совместного распределения всех пикселей будет записываться в следующем виде: <tex>p(X)=\prod_{i=1}^{N^2}p(x_i|x_1, x_2, \dots x_{i-1})</tex>.
+
Тогда значение пикселя <tex>x_i\in V_X</tex> можно выразить через условную вероятность <tex>p(x_i|x_1, x_2, \dots x_{i-1})</tex> и цепное правило для вероятностей<ref name=ChainRule>[https://en.wikipedia.org/wiki/Chain_rule_(probability) Chain rule (probability)]</ref>. Оценка совместного распределения всех пикселей будет записываться в следующем виде: <tex>p(X)=\prod_{i=1}^{N^2}p(x_i|x_1, x_2, \dots x_{i-1})</tex>.
  
Задача алгоритма - восстановить данное распределение. Учитывая тот факт, что любой пиксель принимает значение <tex>0<=x_i<=255</tex>, необходимо восстановить лишь дискретное распределение.
+
Задача алгоритма {{---}} восстановить данное распределение. Учитывая тот факт, что любой пиксель принимает значение <tex>0<=x_i<=255</tex>, необходимо восстановить лишь дискретное распределение.
  
 
== Идея ==
 
== Идея ==
  
Т.к. утверждается, что значение текущего пикселя зависит от значений предыдущего, то уместно использовать [[:Рекуррентные_нейронные_сети|RNN]], а точнее [[Долгая краткосрочная память|LSTM]]. В ранних работах<ref name=SpatialLSTM>[https://arxiv.org/abs/1506.03478 Generative Image Modeling Using Spatial LSTMs]</ref> уже использовался данный подход, и вычисление скрытого состояния происходило следующим образом: <tex>h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})</tex>, т.е. для того, чтобы вычислить текущее скрытое состояние, нужно было подсчитать все предыдущие, что занимает достаточно много времени.  
+
Так как утверждается, что значение текущего пикселя зависит от значения предыдущего, то уместно использовать [[:Рекуррентные_нейронные_сети|''рекуррентные нейронные сети (RNN)'']], а точнее [[Долгая краткосрочная память|''долгую краткосрочную память (LSTM)'']]. В ранних работах<ref name=SpatialLSTM>[https://arxiv.org/abs/1506.03478 Generative Image Modeling Using Spatial LSTMs]</ref> уже использовался данный подход, и вычисление скрытого состояния происходило следующим образом: <tex>h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})</tex>, т.е. для того, чтобы вычислить текущее скрытое состояние, нужно было подсчитать все предыдущие, что занимает достаточно много времени.  
  
Авторы алгоритма модернизировали [[Долгая краткосрочная память|LSTM]] в '''RowLSTM''' и '''Diagonal BiLSTM''' таким образом, чтобы стало возможным распараллеливание вычислений, что в итоге положительно сказывается на времени обучения модели.
+
У алгоритма [[Долгая краткосрочная память|''LSTM'']] существует две модификации: '''''RowLSTM''''' и '''''Diagonal BiLSTM'''''. Основным преимуществом модификаций является возможность проводить вычисления параллельно, что ускоряет общее время обучения модели.
  
 
=== RowLSTM ===
 
=== RowLSTM ===
[[File:pixel-2.png|350px|thumb|Рисунок 2. Визуализация работы модификаций LSTM. Снизу кружками обозначены пиксели, сверху - состояния на каждом пикселе. Синим обозначено то, что влияет на текущее скрытое состояние. Пустые кружки не принимают участие в вычислениях для данного скрытого состояния]]
+
[[File:pixel-2.png|350px|thumb|Рисунок 2. Визуализация работы модификаций ''LSTM''. Снизу кружками обозначены пиксели, сверху {{---}} состояния на каждом пикселе. Синим обозначено то, что влияет на текущее скрытое состояние. Пустые кружки не принимают участие в вычислениях для данного скрытого состояния]]
В данной модификации [[Долгая краткосрочная память|LSTM]] предлагается рассчитывать скрытое состояние следующим образом: <tex>h_{i,j}=f(h_{i-1,j-1}, h_{i-1,j}, h_{i-1,j+1}, x_{i,j})</tex>.
+
В данной модификации [[Долгая краткосрочная память|''LSTM'']] скрытое состояние считается по формуле: <tex>h_{i,j}=f(h_{i-1,j-1}, h_{i-1,j}, h_{i-1,j+1}, x_{i,j})</tex>.
  
Как видно из формулы и Рисунка 2, значение текущего скрытого состояния не зависит от предыдущего слева, а зависит от предыдущих сверху, которые можно параллельно рассчитать.  
+
Как видно из формулы и Рисунка 2, значение текущего скрытого состояния не зависит от предыдущего слева, а зависит только от предыдущих сверху, которые считаются параллельно.  
  
Из плюсов данного алгоритма можно отметить его быстродействие - модель обучается быстрее, нежели наивный [[Долгая краткосрочная память|LSTM]]. Из минусов - относительно плохое качество получаемых изображений. Это связанно как минимум с тем, что мы используем контекст пикселей с предыдущей строки, но никак не используем контекст соседнего слева пикселя, которые является достаточно важным, т.к. является ближайшим с точки зрения построчной генерации изображения.  
+
Таким образом, главным преимуществом алгоритма перед наивным [[Долгая краткосрочная память|''LSTM'']] является более быстрое обучение модели, однако качество получаемых изображений ухудшается. Это связанно как минимум с тем, что мы используем контекст пикселей с предыдущей строки, но никак не используем контекст соседнего слева пикселя, которые является достаточно важным, т.к. является ближайшим с точки зрения построчной генерации изображения. Значит надо научиться находить скрытое состояние слева, но делать это эффективно.
 
 
Отсюда напрашивается идея каким-то образом найти скрытое состояние пикселя слева, но при этом не потерять в производительности.
 
  
 
=== Diagonal BiLSTM ===
 
=== Diagonal BiLSTM ===
[[File:pixel-3.png|350px|thumb|Рисунок 3. Операция сдвига в Diagonal BiLSTM. Параллелизация происходит по диагоналям.]]
+
[[File:pixel-3.png|350px|thumb|Рисунок 3. Операция сдвига в ''Diagonal BiLSTM''. Параллелизация происходит по диагоналям.]]
В данной версии скрытое состояние считается таким же образом, как и в наивном подходе: <tex>h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})</tex>, но при этом есть хитрость в самом вычислении. Построчно сдвинем строки вправо на один пиксель относительно предыдущей, а затем вычислим скрытые состояния в каждом столбце, как показано на рисунке 3.
+
В данной версии скрытое состояние считается таким же образом, как и в наивном подходе: <tex>h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})</tex>, но использует следующую хитрость в самом вычислении {{---}} построчно сдвинем строки вправо на один пиксель относительно предыдущей, а затем вычислим скрытые состояния в каждом столбце, как показано на Рисунке 3. Как следствие, контекст учитывается более качественно, что повышает качество изображения, однако такая модификация замедляет модель по сравнению с подходом ''RowLSTM''.
 
 
Данная версия позволяет учитывать контекст более качественно, но при этом занимает больше времени, чем RowLSTM.
 
  
 
=== PixelCNN ===
 
=== PixelCNN ===
Идея в том, что обычно соседние пиксели (в рамках ядра 9x9) хранят самый важный контекст для пикселя. Поэтому предлагается просто использовать известные пиксели для вычисления нового, как показано на рисунке 2.
+
Идея в том, что наиболее важные данные для пикселя содержатся в соседних пикселях (в рамках ядра 9x9), поэтому предлагается просто использовать известные пиксели для вычисления нового, как показано на рисунке 2.
  
 +
== Архитектура ==
 +
В алгоритмах ''PixelRNN'' и ''PixelCNN'' используются несколько архитектурных трюков, позволяющих производить вычисления быстро и надежно.
  
== Архитектура ==
+
[[File:0_UbiGmm8uyZ-GKhv5_.jpg|350px|thumb|Рисунок 4. MaskA и MaskB.]]
В PixelRNN/PixelCNN используются множество архитектурных трюков, позволяющих сделать вычисления быстрыми и надежными.
 
  
 
=== Маскированные сверточные слои ===
 
=== Маскированные сверточные слои ===
В описаниях алгоритмов фигурируют два типа маскированных сверточных слоя - '''MaskA''', '''MaskB'''. Они необходимы для сокрытия от алгоритма лишней информации и учета контекста - чтобы не обрабатывать изображение после каждого подсчета, удаляя значения пикселей, можно применить маску к изображению, что является более быстрой операцией.  
+
В описаниях алгоритмов фигурируют два типа маскированных сверточных слоя {{---}} '''''MaskA''''', '''''MaskB'''''. Они необходимы для сокрытия от алгоритма лишней информации и учета контекста {{---}} чтобы ускорить обработку изображения после каждого подсчета, предлагается вместо удаления значения пикселей применять маску к изображению, что является более быстрой операцией.  
  
Для каждого пикселя в цветном изображении в порядке очереди существуют 3 контекста: красный канал, зеленый и синий. В данном алгоритме очередь важна, т.е. если сейчас обрабатывается красный канал, то контекст только от предыдущих значений красного канала, если зеленый - то от всех значений на красном канале и предыдущих значениях на зеленом и т.д.
+
Для каждого пикселя в цветном изображении в порядке очереди существуют три контекста: красный канал, зеленый и синий. В данном алгоритме очередь важна, т.е. если сейчас обрабатывается красный канал, то контекст только от предыдущих значений красного канала, если зеленый {{---}} то от всех значений на красном канале и предыдущих значениях на зеленом и т.д.
  
'''MaskA''' используется для того, чтобы учитывать контекст предыдущих каналов, но при этом не учитывать контекст от предыдущих значений текущего канала и следующих каналов.  
+
'''''MaskA''''' используется для того, чтобы учитывать контекст предыдущих каналов, но при этом не учитывать контекст от предыдущих значений текущего канала и следующих каналов.  
'''MaskB''' выполняет ту же функцию, что и '''MaskA''', но при этом учитывает контекст от предыдущих значений текущего канала.
+
'''''MaskB''''' выполняет ту же функцию, что и '''''MaskA''''', но при этом учитывает контекст от предыдущих значений текущего канала.
  
 
=== Уменьшение размерности ===  
 
=== Уменьшение размерности ===  
[[File:pixel-4.png|350px|thumb|Рисунок 4. Блоки уменьшения размерности. Слева - блок для PixelCNN, справа - PixelRNN. ]]
+
[[File:pixel-4.png|320px|thumb|Рисунок 5. Блоки уменьшения размерности. Слева {{---}} блок для ''PixelCNN'', справа {{---}} ''PixelRNN''. ]]
На вход в любой их указанных выше алгоритмов (PixelCNN, Row LSTM, Diagonal BiLSTM) подается большое количество объектов. Поэтому внутри каждого из них сначала происходит уменьшение их количества в 2 раза, а затем обратное увеличение в 2 раза. Структура алгоритма с учетом уменьшения размерности показана на рисунке 4.
+
На вход в любой их указанных выше алгоритмов (''PixelCNN'', ''RowLSTM'', ''Diagonal BiLSTM'') подается большое количество объектов, поэтому внутри каждого из них сначала происходит уменьшение их количества в два раза, а затем обратное увеличение до исходного размера. Структура алгоритма с учетом уменьшения размерности показана на рисунке 4.
 +
 
 +
[[File:pixel-5.png|320px|thumb|Рисунок 6. Простой LSTM-блок с тремя вентилями: входным, выходным и забывания.]]
  
=== Внутреннее устройства LSTM ===  
+
=== Внутреннее устройство LSTM ===  
Модификация Row LSTM, Diagonal BiLSTM одинакова с архитектурной точки зрения. Отличия Diagonal BiLSTM только в том, что добавляется операция сдвига в начале и возврат к исходной структуре изображения в конце.  
+
Внутреннее устройство ''RowLSTM'' и ''Diagonal BiLSTM'' блоков одинаково, за исключением того, что во втором случае добавляется операция сдвига в начале и возврат к исходной структуре изображения в конце.  
  
Структура LSTM блока:  
+
Структура ''LSTM'' блока:  
1. MaskB слой input-to-state <tex>K_{is}</tex> учитывает контекст из входа.  
+
# ''MaskB'' слой ''input-to-state'' <tex>K_{is}</tex> учитывает контекст из входа.  
2. Сверточный слой state-to-state <tex>K_{ss}</tex> учитывает контекст из предыдущих скрытых слоев.  
+
# Сверточный слой ''state-to-state'' <tex>K_{ss}</tex> учитывает контекст из предыдущих скрытых слоев.  
  
Используя эти два сверточных слоя формально вычисление LSTM блока можно записать следующим образом:  
+
Используя эти два сверточных слоя формально вычисление ''LSTM'' блока можно записать следующим образом:  
  
 
<tex>
 
<tex>
[o_i, f_i, i_i, g_i] = \sigma (K_{ss}\circledast h_{i-1} + K_{is}\circledast x_{i}) \\
+
[o_i, f_i, i_i, g_i] = \sigma (K_{ss}\circledast h_{i-1} + K_{is}\circledast x_{i}), \\
c_i=f_i\odot c_{i-1} + i_i\odot g_i\\
+
c_i=f_i\odot c_{i-1} + i_i\odot g_i,\\
h_i = o_i\odot tanh(c_i)
+
h_i = o_i\odot tanh(c_i),
 
</tex>
 
</tex>
  
где <tex>\sigma</tex> - функция активации, <tex>\circledast</tex> - операция свертки, <tex>\odot</tex> - поэлементное умножение.  
+
где <tex>\sigma</tex> {{---}} функция активации,  
 +
 
 +
<tex>\circledast</tex> {{---}} операция свертки,  
 +
 
 +
<tex>\odot</tex> {{---}} поэлементное умножение,
 +
 
 +
<tex>f_i</tex> {{---}} вектор вентиля забывания, вес запоминания старой информации,
 +
 
 +
<tex>i_i</tex> {{---}} вектор входного вентиля, вес получения новой информации,
 +
 
 +
<tex>o_i</tex> {{---}} вектор выходного вентиля, кандидат на выход,
 +
 
 +
<tex>g_i</tex> {{---}} вектор вентиля данных,
 +
 
 +
<tex>x_i</tex> {{---}} строка <tex>i</tex> входных данных,
 +
 
 +
<tex>h_i</tex> {{---}} вектор краткосрочной памяти,
 +
 
 +
<tex>c_i</tex> {{---}} вектор долгосрочной памяти,
 +
 
 +
<tex>K_{is}</tex> и <tex>K_{ss}</tex> {{---}} ядерные веса компонент ''input-to-state'' и ''state-to-state'' соответственно.
  
 
=== Архитектура PixelRNN ===
 
=== Архитектура PixelRNN ===
# MaskA размером <tex>7\times 7</tex>  
+
# ''MaskA'' размером <tex>7\times 7</tex>.
# Блоки уменьшения размеренности с Row LSTM блоком, в котором <tex>K_{is}</tex> имеет размер <tex>3\times 1</tex>, <tex>K_{ss}</tex> - <tex>3\times 2</tex>. Для Diagonal BiLSTM <tex>K_{is}</tex> имеет размер <tex>1\times 1</tex>, <tex>K_{ss}</tex> - <tex>1\times 2</tex>. Количество блоков варьируется.  
+
# Блоки уменьшения размеренности с ''RowLSTM'' блоком, в котором <tex>K_{is}</tex> имеет размер <tex>3\times 1</tex>, <tex>K_{ss}</tex> {{---}} <tex>3\times 2</tex>. Для ''Diagonal BiLSTM'' <tex>K_{is}</tex> имеет размер.<tex>1\times 1</tex>, <tex>K_{ss}</tex> {{---}} <tex>1\times 2</tex>. Количество блоков варьируется.  
# ReLU активация   
+
# ''ReLU'' активация.  
# Сверточный слой размером <tex>1\times 1</tex>  
+
# Сверточный слой размером <tex>1\times 1</tex>.
# Softmax слой
+
# ''Softmax'' слой.
 +
 
  
 +
[[File:exampleCNN.png|450px|thumb|Рисунок 7. Лица, сгенерированные с помощью PixelCNN сетей <ref name=NaturalModeling>[https://arxiv.org/pdf/1612.08185v4.pdf#page=8 Natural Modeling]</ref>]]
 
=== Архитектура PixelCNN ===
 
=== Архитектура PixelCNN ===
# MaskA размером <tex>7\times 7</tex>  
+
# ''MaskA'' размером <tex>7\times 7</tex>.
# Блоки уменьшения размеренности для PixelCNN.  
+
# Блоки уменьшения размеренности для ''PixelCNN''.  
# ReLU активация
+
# ''ReLU'' активация.
# Сверточный слой размером <tex>1\times 1</tex>  
+
# Сверточный слой размером <tex>1\times 1</tex>.
# Softmax слой
+
# ''Softmax'' слой.
  
 
== Сравнение подходов ==
 
== Сравнение подходов ==
 +
[[File:exampleGAN.png|450px|thumb|Рисунок 8. Лица, сгенерированные с помощью GAN <ref name=ForwardScience>[https://towardsdatascience.com/how-to-train-stylegan-to-generate-realistic-faces-d4afca48e705 Towards data science]</ref>]]
 +
 +
Если сравнивать [[Generative Adversarial Nets (GAN) | GAN]] с PixelCNN/PixelRNN, то можно отметить более хорошее качество получаемых изображений у генеративно-состязательного метода. Однако у метода GAN время обучения медленнее, чем у PixelCNN и PixelRNN. Для реализации GAN требуется найти равновесие Нэша, но в настоящее время нет алгоритма делающего это. Поэтому обучение GAN более нестабильное, если сравнивать с другими методами<ref name=Reg>[https://towardsdatascience.com/auto-regressive-generative-models-pixelrnn-pixelcnn-32d192911173 Auto-Regressive Generative Models]</ref>. В настоящее время многие мировые компании используют GAN для генерации изображений, например: [https://neurohive.io/ru/papers/pggan-progressivnaja-generativnaja-nejroset-ot-nvidia/ PGGAN] от ''Nvidia'', [https://bdol.github.io/exemplar_gans/ Exemplar GAN] от ''Facebook'' и другие.
 +
 
{| class="wikitable"
 
{| class="wikitable"
 
! style="font-weight:bold;" | Критерий\название
 
! style="font-weight:bold;" | Критерий\название
Строка 89: Строка 114:
 
! style="font-weight:bold;" | PixelRNN(Row LSTM)
 
! style="font-weight:bold;" | PixelRNN(Row LSTM)
 
! style="font-weight:bold;" | PixelRNN(Diagonal BiLSTM)
 
! style="font-weight:bold;" | PixelRNN(Diagonal BiLSTM)
 +
! style="font-weight:bold;" | GAN
 
|-
 
|-
 
| Время обучения
 
| Время обучения
 
| Быстрый
 
| Быстрый
 
| Средний
 
| Средний
 +
| Медленный
 
| Медленный
 
| Медленный
 
|-
 
|-
Строка 99: Строка 126:
 
| Средне-низкое
 
| Средне-низкое
 
| Средне-высокое
 
| Средне-высокое
 +
| Высокое
 
|}
 
|}
  
Строка 105: Строка 133:
 
* [https://github.com/ardapekis/pixel-rnn PixelRNN на Pytorch]
 
* [https://github.com/ardapekis/pixel-rnn PixelRNN на Pytorch]
 
* [https://github.com/shirgur/PixelRNN PixelRNN на Keras]
 
* [https://github.com/shirgur/PixelRNN PixelRNN на Keras]
 +
 +
==См. также==
 +
* [[Рекуррентные нейронные сети]]
 +
* [[Долгая краткосрочная память]]
 +
* [[Нейронные сети, перцептрон]]
 +
* [[Генерация объектов]]
  
 
==Примечания==
 
==Примечания==
Строка 113: Строка 147:
 
* [https://github.com/tensorflow/magenta/blob/master/magenta/reviews/pixelrnn.md Pixel Recurrent Neural Networks]
 
* [https://github.com/tensorflow/magenta/blob/master/magenta/reviews/pixelrnn.md Pixel Recurrent Neural Networks]
 
* [http://bjlkeng.github.io/posts/pixelcnn/ PixelCNN]
 
* [http://bjlkeng.github.io/posts/pixelcnn/ PixelCNN]
 +
* [https://arxiv.org/pdf/1612.08185v4.pdf#page=8 Natural Modeling]
 +
* [https://towardsdatascience.com/how-to-train-stylegan-to-generate-realistic-faces-d4afca48e705 Towards data science]
 
[[Категория: Машинное обучение]]
 
[[Категория: Машинное обучение]]
 +
[[Категория: Генерация объектов]]

Текущая версия на 19:13, 4 сентября 2022

Рисунок 1. Пример использования PixelRNN/PixelCNN сетей

PixelRNN и PixelCNN — алгоритмы машинного обучения, входящие в семейство авторегрессивных моделей и использующиеся для генерации и дополнения изображений. Алгоритмы были представлены в 2016 году компанией DeepMind[1] и являются предшественниками алгоритма WaveNet[2], который используется в голосовом помощнике Google.

Основным преимуществом PixelRNN и PixelCNN является уменьшение времени обучения, по сравнению с наивными способами попиксельной генерации изображений.

Постановка задачи

Пусть дано черно-белое изображение [math]X[/math] размером [math]N\times N[/math]. Построчно преобразуем картинку в вектор [math]V_X = \{x_1, x_2, \dots, x_{N^2} \}[/math], соединяя конец текущей строки с началом следующей. При таком представлении изображения можно предположить, что значение любого пикселя [math]x_i\in V_X[/math] может зависеть от значений предыдущих пикселей [math]x_j, j = 1,2,\dots i-1[/math].

Тогда значение пикселя [math]x_i\in V_X[/math] можно выразить через условную вероятность [math]p(x_i|x_1, x_2, \dots x_{i-1})[/math] и цепное правило для вероятностей[3]. Оценка совместного распределения всех пикселей будет записываться в следующем виде: [math]p(X)=\prod_{i=1}^{N^2}p(x_i|x_1, x_2, \dots x_{i-1})[/math].

Задача алгоритма — восстановить данное распределение. Учитывая тот факт, что любой пиксель принимает значение [math]0\lt =x_i\lt =255[/math], необходимо восстановить лишь дискретное распределение.

Идея

Так как утверждается, что значение текущего пикселя зависит от значения предыдущего, то уместно использовать рекуррентные нейронные сети (RNN), а точнее долгую краткосрочную память (LSTM). В ранних работах[4] уже использовался данный подход, и вычисление скрытого состояния происходило следующим образом: [math]h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})[/math], т.е. для того, чтобы вычислить текущее скрытое состояние, нужно было подсчитать все предыдущие, что занимает достаточно много времени.

У алгоритма LSTM существует две модификации: RowLSTM и Diagonal BiLSTM. Основным преимуществом модификаций является возможность проводить вычисления параллельно, что ускоряет общее время обучения модели.

RowLSTM

Рисунок 2. Визуализация работы модификаций LSTM. Снизу кружками обозначены пиксели, сверху — состояния на каждом пикселе. Синим обозначено то, что влияет на текущее скрытое состояние. Пустые кружки не принимают участие в вычислениях для данного скрытого состояния

В данной модификации LSTM скрытое состояние считается по формуле: [math]h_{i,j}=f(h_{i-1,j-1}, h_{i-1,j}, h_{i-1,j+1}, x_{i,j})[/math].

Как видно из формулы и Рисунка 2, значение текущего скрытого состояния не зависит от предыдущего слева, а зависит только от предыдущих сверху, которые считаются параллельно.

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

Diagonal BiLSTM

Рисунок 3. Операция сдвига в Diagonal BiLSTM. Параллелизация происходит по диагоналям.

В данной версии скрытое состояние считается таким же образом, как и в наивном подходе: [math]h_{i,j}=f(h_{i-1,j}, h_{i,j-1}, x_{i,j})[/math], но использует следующую хитрость в самом вычислении — построчно сдвинем строки вправо на один пиксель относительно предыдущей, а затем вычислим скрытые состояния в каждом столбце, как показано на Рисунке 3. Как следствие, контекст учитывается более качественно, что повышает качество изображения, однако такая модификация замедляет модель по сравнению с подходом RowLSTM.

PixelCNN

Идея в том, что наиболее важные данные для пикселя содержатся в соседних пикселях (в рамках ядра 9x9), поэтому предлагается просто использовать известные пиксели для вычисления нового, как показано на рисунке 2.

Архитектура

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

Рисунок 4. MaskA и MaskB.

Маскированные сверточные слои

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

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

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

Уменьшение размерности

Рисунок 5. Блоки уменьшения размерности. Слева — блок для PixelCNN, справа — PixelRNN.

На вход в любой их указанных выше алгоритмов (PixelCNN, RowLSTM, Diagonal BiLSTM) подается большое количество объектов, поэтому внутри каждого из них сначала происходит уменьшение их количества в два раза, а затем обратное увеличение до исходного размера. Структура алгоритма с учетом уменьшения размерности показана на рисунке 4.

Рисунок 6. Простой LSTM-блок с тремя вентилями: входным, выходным и забывания.

Внутреннее устройство LSTM

Внутреннее устройство RowLSTM и Diagonal BiLSTM блоков одинаково, за исключением того, что во втором случае добавляется операция сдвига в начале и возврат к исходной структуре изображения в конце.

Структура LSTM блока:

  1. MaskB слой input-to-state [math]K_{is}[/math] учитывает контекст из входа.
  2. Сверточный слой state-to-state [math]K_{ss}[/math] учитывает контекст из предыдущих скрытых слоев.

Используя эти два сверточных слоя формально вычисление LSTM блока можно записать следующим образом:

[math] [o_i, f_i, i_i, g_i] = \sigma (K_{ss}\circledast h_{i-1} + K_{is}\circledast x_{i}), \\ c_i=f_i\odot c_{i-1} + i_i\odot g_i,\\ h_i = o_i\odot tanh(c_i), [/math]

где [math]\sigma[/math] — функция активации,

[math]\circledast[/math] — операция свертки,

[math]\odot[/math] — поэлементное умножение,

[math]f_i[/math] — вектор вентиля забывания, вес запоминания старой информации,

[math]i_i[/math] — вектор входного вентиля, вес получения новой информации,

[math]o_i[/math] — вектор выходного вентиля, кандидат на выход,

[math]g_i[/math] — вектор вентиля данных,

[math]x_i[/math] — строка [math]i[/math] входных данных,

[math]h_i[/math] — вектор краткосрочной памяти,

[math]c_i[/math] — вектор долгосрочной памяти,

[math]K_{is}[/math] и [math]K_{ss}[/math] — ядерные веса компонент input-to-state и state-to-state соответственно.

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

  1. MaskA размером [math]7\times 7[/math].
  2. Блоки уменьшения размеренности с RowLSTM блоком, в котором [math]K_{is}[/math] имеет размер [math]3\times 1[/math], [math]K_{ss}[/math][math]3\times 2[/math]. Для Diagonal BiLSTM [math]K_{is}[/math] имеет размер.[math]1\times 1[/math], [math]K_{ss}[/math][math]1\times 2[/math]. Количество блоков варьируется.
  3. ReLU активация.
  4. Сверточный слой размером [math]1\times 1[/math].
  5. Softmax слой.


Рисунок 7. Лица, сгенерированные с помощью PixelCNN сетей [5]

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

  1. MaskA размером [math]7\times 7[/math].
  2. Блоки уменьшения размеренности для PixelCNN.
  3. ReLU активация.
  4. Сверточный слой размером [math]1\times 1[/math].
  5. Softmax слой.

Сравнение подходов

Рисунок 8. Лица, сгенерированные с помощью GAN [6]

Если сравнивать GAN с PixelCNN/PixelRNN, то можно отметить более хорошее качество получаемых изображений у генеративно-состязательного метода. Однако у метода GAN время обучения медленнее, чем у PixelCNN и PixelRNN. Для реализации GAN требуется найти равновесие Нэша, но в настоящее время нет алгоритма делающего это. Поэтому обучение GAN более нестабильное, если сравнивать с другими методами[7]. В настоящее время многие мировые компании используют GAN для генерации изображений, например: PGGAN от Nvidia, Exemplar GAN от Facebook и другие.

Критерий\название PixelCNN PixelRNN(Row LSTM) PixelRNN(Diagonal BiLSTM) GAN
Время обучения Быстрый Средний Медленный Медленный
Качество генерируемых изображений Наихудшее Средне-низкое Средне-высокое Высокое

Примеры реализации

См. также

Примечания

Источники информации