《python深度学习》 第八章
8.4 用变分自编码器(VAE)生成图像
变分自动编码器也是一个生成模型,与GAN是一样的
VAE的工作原理如下
1.一个编码器模块将输入样本转换为表示潜在空间中的两个参数,均值和方差
2.假设潜在正态分布能够生成输入图像,并从这个分布中随机采样一个点,z = z_mean + exp(z_log_variance) * epsilon, epsilon是取值很小的随机张量
3.一个解码器将潜在空间的这个点映射回原始图像
VAE的参数通过两个损失函数来进行训练,一个是重构损失,它迫使解码后的样本匹配初始输入:另一个是正则化损失,它有助于学习具有良好结构的潜在空间,并可以降低在训练数据上的过拟合
# VAE编码器网络
import keras
from keras import layers
from keras import backend as K
from keras.models import Model
import numpy as np
# 编码器
img_shape = (28,28,1)
batch_size = 16
latent_dim = 2
input_img = keras.Input(shape = img_shape)
x = layers.Conv2D(32,3,padding = 'same',activation = 'relu')(input_img)
x = layers.Conv2D(64,3,padding = 'same',activation = 'relu',strides = (2,2))(x)
x = layers.Conv2D(64,3,padding = 'same',activation = 'relu')(x)
x = layers.Conv2D(64,3,padding = 'same',activation = 'relu')(x)
shape_before_flattening= K.int_shape(x)
x = layers.Flatten()(x)
x = layers.Dense(32,activation = 'relu')(x)
# 输入图像最终被编码为这两个参数
z_mean = layers.Dense(latent_dim)(x)
z_log_var = layers.Dense(latent_dim)(x)
# 使用均值和方差来生成一个潜在空间点,使用lambda层
def sampling(args):
z_mean,z_log_var = args
epsilon = K.random_normal(shape = (K.shape(z_mean)[0],latent_dim),mean = 0.,stddev = 1.)
return z_mean + K.exp(0.5*z_log_var) * epsilon
z = layers.Lambda(sampling)([z_mean,z_log_var])
# VAE解码器网络,将潜在空间点映射为图像
# 将z输入到这里
decoder_input = layers.Input(K.int_shape(z)[1:])
# 对输入进行上采样
x = layers.Dense(np.prod(shape_before_flattening[1:]),activation = 'relu')(decoder_input)
# 将Z映射为特征图.使其形状与编码器模型最后一个Flatten层之前的特征图的形状相同
x = layers.Reshape(shape_before_flattening[1:])(x)
# 使用以下两层,将z解码为与原始图像具有相同尺寸的特征图
x = layers.Conv2DTranspose(32,3,padding = 'same',activation = 'relu',strides = (2,2))(x)
x = layers.Conv2D(1,3,padding = 'same',activation = 'sigmoid')(x)
# 将解码器模型实例化,它将decoder_input转换为解码后的图像
decoder = Model(decoder_input,x)
# 将这个实例应用于z,已得到解码后的z
z_decoded = decoder(z)
# 损失的设置方法:编写一个自定义层,并在其内部使用内置的add_loss层方法来创建一个你想要的损失
class CustomVariationalLayer(keras.layers.Layer):
def vae_loss(self,x,z_decoded)