«Это 14-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."
Авторегрессионные модели
Алгоритмы генерации глубоких нейронных сетей в основном делятся на три категории:
- Генеративно-состязательная сеть (GAN)
- Вариационный автоэнкодер (VAE)
- Авторегрессионные модели
VAE уже в«Подробное объяснение и реализация вариационного автоэнкодера (VAE)»введено в. Справочник по подробной информации GAN«Принцип и реализация глубокой сверточной генеративно-состязательной сети (DCGAN)». В этой статье будет представлена менее известная авторегрессионная модель, которая все еще является активной областью исследований, несмотря на то, что она редко используется для создания изображений. WaveNet от DeepMind использует авторегрессию для создания реалистичного звука. В этой статье мы представим авторегрессионную модель и построим модель PixelCNN.
Введение
«Авто» в авторегрессии означает «я» (self
), а регрессия в терминах машинного обучения (regress
) означает прогнозирование нового значения. Собрав все вместе, авторегрессия означает, что мы используем модель для прогнозирования новых точек данных на основе прошлых точек данных модели.
Пусть распределение вероятностей изображения равноэто совместное распределение вероятностей пикселей, что сложно моделировать из-за высокой размерности. Здесь мы предполагаем, что значение пикселя зависит только от значения пикселя перед ним. Другими словами, текущий пиксель обусловлен только своим предыдущим пикселем, т.е., мы можем аппроксимировать совместную вероятность как произведение условных вероятностей:
В качестве конкретного примера предположим, что красное яблоко находится рядом с центром изображения, а яблоко окружено зелеными листьями, в этом случае предположим, что есть только два возможных цвета: красный и зеленый.это верхний левый пиксель, поэтомуУказывает вероятность того, что верхний левый пиксель будет зеленым или красным. еслизеленый, то правильнотакже может быть зеленым, так как на нем может быть больше листьев. Но, хотя и менее вероятно, он также может быть красным.
Продолжая вычисления, мы получим красные пиксели. От этого пикселя следующие несколько пикселей также, вероятно, тоже будут красными, что намного проще, чем рассматривать их все одновременно.
PixelRNN
PixelRNN был предложен DeepMind в 2016 году. Как следует из названия RNN (рекуррентная нейронная сеть), модель использует тип RNN, называемый долговременной памятью (LSTM), для изучения распределения изображений. Он считывает изображение по одной строке за один шаг в LSTM, обрабатывает его, используя одномерный сверточный слой, а затем передает информацию об активации в последующие слои для прогнозирования пикселей для этой строки.
Поскольку LSTM медленные, для обучения и создания образцов требуется много времени. Поэтому не будем слишком его изучать и обратим внимание на вариант, предложенный в той же статье — PixelCNN.
Создание модели PixelCNN с помощью TensorFlow 2
PixelCNN состоит только из сверточных слоев, что делает его намного быстрее, чем PixelRNN. Здесь мы обучим простую модель PixelCNN, используя набор данных MNIST.
ввод и метки
MNIST состоит из 28 x 28 x 1 цифровых рукописных цифр в оттенках серого. Он имеет только один канал:
В этом эксперименте задача упрощается за счет преобразования изображения в двоичные данные: 0 для черного и 1 для белого:
def binarize(image, label):
image = tf.cast(image, tf.float32)
image = tf.math.round(image/255.)
return image, tf.cast(image, tf.int32)
Функция требует два входа — изображение и метку. Первые две строки функции преобразуют изображение в двоичный формат float32, либо 0,0, либо 1,0. И мы конвертируем двоичное изображение в целое число и обратно, просто чтобы следовать соглашению об использовании целых чисел в качестве меток. Возвращенные данные, которые будут служить входными данными и метками для обучения сети, представляют собой двоичные изображения MNIST размером 28 x 28 x 1, которые отличаются только типом данных.
маска
В отличие от PixelRNN, который считывает строку за строкой, PixelCNN перемещает ядра свертки слева направо и сверху вниз на изображении. При выполнении свертки для прогнозирования текущего пикселя традиционные ядра свертки могут видеть текущий входной пиксель и окружающие его пиксели, включая информацию о пикселях после текущего пикселя, что противоречит предположению об условной вероятности во вводном разделе.
Чтобы избежать этого, нам нужно убедиться, что CNN предсказывает выходные пиксели.не будет видеть входные пиксели, когда.
Это достигается с помощью маскированной свертки, когда маска применяется к весам ядра перед выполнением свертки. На изображении ниже показана маска ядра свертки 7 x 7 с нулевым весом от центра. Это не позволяет CNN видеть пиксель, который он предсказывает (центр ядра свертки), и все пиксели после него. это называетсяA型掩膜
, применяется только к входному слою.
Поскольку центральный пиксель закрыт в первом слое, нам больше не нужно скрывать центральный элемент в последующих слоях. На самом деле нам нужно установить центр ядра свертки равным 1, чтобы он мог считывать признаки предыдущих слоев, что называетсяB型掩膜
.
Внедрение пользовательских слоев
Теперь мы создадим собственный слой для свертки маски. Мы можем использовать подкласс, который наследуется от базового класса tf.keras.layers.Layer вTensorFlow2.x
Создайте пользовательский слой в , чтобы его можно было использовать как любой другой слой Keras. Ниже приведена базовая структура класса пользовательского слоя:
class MaskedConv2D(tf.keras.layers.Layer):
def __init__(self):
...
def build(self, input_shape):
...
def call(self, inputs):
...
return output
build() принимает форму входного тензора в качестве аргумента, и мы будем использовать эту информацию, чтобы обеспечить создание правильной формы переменной. Эта функция запускается только один раз при построении слоя. Мы можем создавать маски, объявляя необучаемые переменные или константы, чтобы сообщить TensorFlow, что ему не нужны градиенты для обратного распространения:
def build(self, input_shape):
self.w = self.add_weight(shape=[self.kernel,
self.kernel,
input_shape[-1],
self.filters],
initializer='glorot_normal',
trainable=True)
self.b = self.add_weight(shape=(self.filters,),
initializer='zeros',
trainable=True)
mask = np.ones(self.kernel**2, dtype=np.float32)
center = len(mask)//2
mask[center+1:] = 0
if self.mask_type == 'A':
mask[center] = 0
mask = mask.reshape((self.kernel, self.kernel, 1, 1))
self.mask = tf.constant(mask, dtype='float32')
call() используется для выполнения прямого прохода. В сверточном слое маски, используя низкоуровневыйtf.nn API
Перед выполнением свертки мы умножаем веса на маску и устанавливаем младшую половину значения в ноль:
def call(self, inputs):
masked_w = tf.math.multiply(self.w, self.mask)
output=tf.nn.conv2d(inputs, masked_w, 1, "SAME") + self.b
return output
Сетевая архитектура
Архитектура PixelCNN очень проста. в настоящее время используюA型掩膜
После первого слоя conv2d 7 x 7 есть несколько слоев сB型掩膜
Остаточный блок:
На следующем рисунке показана остаточная блочная архитектура, используемая в PixelCNN:
перекрестная потеря энтропии
Перекрестная энтропийная потеря, также известная как логарифмическая потеря, измеряет производительность модели, в которой вероятность выхода находится в диапазоне от 0 до 1. Ниже приведено уравнение для бинарной кросс-энтропийной потери, где есть только два класса, а метка y может быть 0 или 1,предсказания модели:
В PixelCNN в качестве меток используются отдельные пиксели изображения. В бинарном MNIST мы хотим предсказать, является ли выходной пиксель 0 или 1, что делает проблему классификации с использованием кросс-энтропии в качестве функции потерь.
Наконец, для компиляции и обучения нейронной сети мы используем бинарную кросс-энтропию как для потерь, так и для метрик, а также используем RMSprop в качестве оптимизатора. Доступно множество различных оптимизаторов, и их основное отличие заключается в том, как они регулируют скорость обучения на основе прошлой статистики. Не существует единого оптимального оптимизатора, который можно было бы использовать во всех случаях, поэтому рекомендуется пробовать разные оптимизаторы.
Скомпилируйте и обучите модель pixelcnn:
pixelcnn = SimplePixelCnn()
pixelcnn.compile(
loss = tf.keras.losses.BinaryCrossentropy(),
optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.001),
metrics=[ tf.keras.metrics.BinaryCrossentropy()])
pixelcnn.fit(ds_train, epochs = 10, validation_data=ds_test)
Далее мы создадим новое изображение на основе предыдущей модели.
Выборка для создания изображений
После обучения мы можем использовать модель для создания новых изображений, выполнив следующие шаги:
- Создайте пустой тензор той же формы, что и входное изображение, и используйте
1
заполнение. пустить его в сеть и получить, вероятность первого пикселя. - отвзять выборки и присвоить выборочные значения пикселям во входном тензоре.
- Снова подайте вход в сеть и выполните шаг 2 для следующего пикселя.
- Повторяйте шаги 2 и 3, пока не будет сгенерировано.
Основным недостатком авторегрессионной модели является то, что она медленная, потому что ее нужно генерировать пиксель за пикселем, и ее нельзя распараллелить. Следующие изображения генерируются нашей моделью PixelCNN после 100 эпох обучения. Они пока не совсем похожи на правильные числа, но теперь мы можем генерировать новые изображения из воздуха. Лучшие числа могут быть получены путем обучения более длинной модели с некоторой настройкой гиперпараметров.