Введение
Вариационный автоэнкодер(Variational Autoencoder, VAE) — разновидность генеративной модели (Generative Model), другая распространенная генеративная модель —Генеративно-состязательные сети(генеративно-состязательная сеть, GAN)
Здесь мы вводим принцип VAE и реализуем его с помощью Keras.
принцип
У нас часто возникает необходимость научиться генерировать новые образцы на основе множества образцов.
Взяв в качестве примера MNIST, просмотрев тысячи изображений рукописных цифр, мы можем сымитировать и сгенерировать некоторые похожие изображения, которых нет в исходных данных, которые имеют некоторые изменения, но выглядят одинаково.
Другими словами, необходимо изучить распределение данных x, чтобы можно было легко создавать новые выборки на основе распределения данных.
Но оценка распределения данных — непростая задача, особенно когда объем данных недостаточен.
Вы можете использовать скрытую переменную z, получить x из z с помощью комплексного отображения и предположить, что z подчиняется распределению Гаусса.
Поэтому необходимо только узнать параметры распределения Гаусса, которым подчиняются скрытые переменные, и функцию отображения для получения распределения исходных данных.
Чтобы узнать параметры гауссовского распределения скрытых переменных, необходимо получить достаточно выборок z
Однако выборки z не могут быть получены напрямую, поэтому функция отображения (условное распределение вероятностей) также требуется для получения соответствующих выборок z из существующих выборок x.
Это очень похоже на автокодировщик: из самих данных путем кодирования получается представление скрытого слоя, а декодирование восстанавливается.
Но разница между VAE и AE заключается в следующем:
- Распределение представления скрытого слоя в AE неизвестно, в то время как скрытые переменные в VAE подчиняются распределению Гаусса.
- Кодировщик и декодер изучаются в AE, а распределение скрытых переменных, включая среднее значение и дисперсию распределения Гаусса, также изучается в VAE.
- AE может получить соответствующий реконструированный x только из одного x
- VAE может сгенерировать новый z и, таким образом, получить новый x, т.е. сгенерировать новые выборки
функция потерь
Помимо ошибки реконструкции, поскольку мы предполагаем, что скрытая переменная z подчиняется распределению Гаусса в ВАЭ, соответствующее кодеру условное распределение вероятностей должно быть максимально похоже на распределение Гаусса
Относительная энтропия, также известная как дивергенция KL (расхождение Кульбака-Лейблера), может использоваться для измерения разницы или расстояния между двумя распределениями, но относительная энтропияасимметричныйиз
выполнить
Взяв здесь в качестве примера MNIST, мы изучаем два параметра, среднее значение и дисперсию гауссовского распределения, которым подчиняется скрытая переменная z, так что новое z можно использовать для генерации x, которого нет в исходных данных.
Кодер и декодер используют два полностью связанных уровня, которые проще, в основном для иллюстрации реализации VAE.
загрузить библиотеку
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from keras.layers import Input, Dense, Lambda
from keras.models import Model
from keras import backend as K
from keras import objectives
from keras.datasets import mnist
определить некоторые константы
batch_size = 100
original_dim = 784
intermediate_dim = 256
latent_dim = 2
epochs = 50
Часть кодировщика, два полностью связанных слоя, представление скрытого слоя включает среднее значение и дисперсию
x = Input(shape=(original_dim,))
h = Dense(intermediate_dim, activation='relu')(x)
z_mean = Dense(latent_dim)(h)
z_log_var = Dense(latent_dim)(h)
Lambda
Слой не участвует в обучении, а только в расчете, который позже используется для генерации новых значений z.
def sampling(args):
z_mean, z_log_var = args
epsilon = K.random_normal(shape=(batch_size, latent_dim), mean=0.)
return z_mean + K.exp(z_log_var / 2) * epsilon
z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, z_log_var])
Часть декодера, два полносвязных слоя,x_decoded_mean
за реконструированный выпуск
decoder_h = Dense(intermediate_dim, activation='relu')
decoder_mean = Dense(original_dim, activation='sigmoid')
h_decoded = decoder_h(z)
x_decoded_mean = decoder_mean(h_decoded)
Настройте функцию общих потерь и скомпилируйте модель
def vae_loss(x, x_decoded_mean):
xent_loss = original_dim * objectives.binary_crossentropy(x, x_decoded_mean)
kl_loss = -0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
return xent_loss + kl_loss
vae = Model(x, x_decoded_mean)
vae.compile(optimizer='rmsprop', loss=vae_loss)
Загружайте данные и тренируйтесь, скорость обучения процессора может быть приемлемой.
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
vae.fit(x_train, x_train,
shuffle=True,
epochs=epochs,
batch_size=batch_size,
validation_data=(x_test, x_test))
Определите кодировщик и посмотрите, как данные в MNIST выглядят в скрытом слое.
encoder = Model(x, z_mean)
x_test_encoded = encoder.predict(x_test, batch_size=batch_size)
plt.figure(figsize=(6, 6))
plt.scatter(x_test_encoded[:, 0], x_test_encoded[:, 1], c=y_test)
plt.colorbar()
plt.show()
Результаты следующие, указывающие на то, что в двумерном скрытом слое разные числа хорошо разделены.
Определите другой генератор, от скрытого слоя до вывода, для создания новых выборок.
decoder_input = Input(shape=(latent_dim,))
_h_decoded = decoder_h(decoder_input)
_x_decoded_mean = decoder_mean(_h_decoded)
generator = Model(decoder_input, _x_decoded_mean)
Сгенерируйте некоторые 2D-данные с помощью сетки, введите их в генератор как новый z и отобразите сгенерированный x
n = 20
digit_size = 28
figure = np.zeros((digit_size * n, digit_size * n))
grid_x = np.linspace(-4, 4, n)
grid_y = np.linspace(-4, 4, n)
for i, xi in enumerate(grid_x):
for j, yi in enumerate(grid_y):
z_sample = np.array([[yi, xi]])
x_decoded = generator.predict(z_sample)
digit = x_decoded[0].reshape(digit_size, digit_size)
figure[(n - i - 1) * digit_size: (n - i) * digit_size,
j * digit_size: (j + 1) * digit_size] = digit
plt.figure(figsize=(10, 10))
plt.imshow(figure)
plt.show()
Результаты следующие, что согласуется с графиком скрытого слоя, показанным ранее, и даже можно увидеть переходные состояния между некоторыми числами.
Из-за включения некоторых случайных факторов результаты будут отличаться каждый раз.
Если вы замените полностью связанный слой на CNN, вы должны получить лучшие результаты представления.
расширять
После освоения вышеуказанного контента, используя тот же метод, вы можете снова запустить его на наборе данных FashionMNIST.Масштаб набора данных точно такой же, как у MNIST.
Просто измените четыре строки
from keras.datasets import fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
grid_x = np.linspace(-3, 3, n)
grid_y = np.linspace(-3, 3, n)
Полный код выглядит следующим образом
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from keras.layers import Input, Dense, Lambda
from keras.models import Model
from keras import backend as K
from keras import objectives
from keras.datasets import fashion_mnist
batch_size = 100
original_dim = 784
intermediate_dim = 256
latent_dim = 2
epochs = 50
x = Input(shape=(original_dim,))
h = Dense(intermediate_dim, activation='relu')(x)
z_mean = Dense(latent_dim)(h)
z_log_var = Dense(latent_dim)(h)
def sampling(args):
z_mean, z_log_var = args
epsilon = K.random_normal(shape=(batch_size, latent_dim), mean=0.)
return z_mean + K.exp(z_log_var / 2) * epsilon
z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, z_log_var])
decoder_h = Dense(intermediate_dim, activation='relu')
decoder_mean = Dense(original_dim, activation='sigmoid')
h_decoded = decoder_h(z)
x_decoded_mean = decoder_mean(h_decoded)
def vae_loss(x, x_decoded_mean):
xent_loss = original_dim * objectives.binary_crossentropy(x, x_decoded_mean)
kl_loss = -0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
return xent_loss + kl_loss
vae = Model(x, x_decoded_mean)
vae.compile(optimizer='rmsprop', loss=vae_loss)
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
vae.fit(x_train, x_train,
shuffle=True,
epochs=epochs,
batch_size=batch_size,
validation_data=(x_test, x_test))
encoder = Model(x, z_mean)
x_test_encoded = encoder.predict(x_test, batch_size=batch_size)
plt.figure(figsize=(6, 6))
plt.scatter(x_test_encoded[:, 0], x_test_encoded[:, 1], c=y_test)
plt.colorbar()
plt.show()
decoder_input = Input(shape=(latent_dim,))
_h_decoded = decoder_h(decoder_input)
_x_decoded_mean = decoder_mean(_h_decoded)
generator = Model(decoder_input, _x_decoded_mean)
n = 20
digit_size = 28
figure = np.zeros((digit_size * n, digit_size * n))
grid_x = np.linspace(-3, 3, n)
grid_y = np.linspace(-3, 3, n)
for i, xi in enumerate(grid_x):
for j, yi in enumerate(grid_y):
z_sample = np.array([[yi, xi]])
x_decoded = generator.predict(z_sample)
digit = x_decoded[0].reshape(digit_size, digit_size)
figure[(n - i - 1) * digit_size: (n - i) * digit_size,
j * digit_size: (j + 1) * digit_size] = digit
plt.figure(figsize=(10, 10))
plt.imshow(figure)
plt.show()
Давайте взглянем на представление скрытого слоя, который также имеет хороший эффект классификации.
Затем сгенерируйте немного графики, вы можете увидеть переход между разными видами одежды.
Ссылаться на
- Автоматическое кодирование вариационного Байеса:АР Вест V.org/PDF/1312.61…
- Учебник по вариационным автоэнкодерам:АР Вест V.org/PDF/1606.05…
- Создание автоэнкодеров в Keras:блог Может ли RAS.IO/building-AU…
- База модных товаров Fashion-MNIST:Доступно в RAS.IO/datasets/#…
- [Клевка риса ежедневно] Вариационный энкодер VAE:zhuanlan.zhihu.com/p/25269592