资讯详情

深度学习系列27:VAE生成模型

1. AE

AE(Autoencoder),自动编码器。自编码器的初衷是降低数据维度。假设原始特征x维度过高,我们希望通过编码器E将其编码成低维特征向量z=E(x),编码的原则是尽可能保留原始信息,所以我们训练另一个解码器D,希望通过z重构原始信息,即x≈D(E(x)),其优化目标一般为 在这里插入图片描述 我们常用的encoder-decoder就是最简单的一个AE。在训练过程中添加一些干扰可以成为去噪自编码器(DAE):

或者用遮盖(MIM,mask image modeling)添加扰动的方法:

2. VAE

损失是重构误差 KL散度。 用神经网络拟合每个样本的平均值 u u u和方差 δ 2 \delta^2 δ2,然后用标准正态分布取样Z,然后恢复X。方差是核心,是对抗生成的关键。重构部分误差会使 u u u尽量接近真实值KL散度将确保生成器具有标准的随机性。 我们来看下keras版本代码:

from keras.layers import Input, Dense, Lambda from keras.models import Model from keras import backend as K # 输入 x = Input(shape=(original_dim,)) h = Dense(intermediate_dim, activation='relu')(x)  # 计算全连接层p(Z|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=K.shape(z_mean))     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)  # 建立模型 vae = Model(x, x_decoded_mean)  # xent_loss是重构loss,kl_loss是KL loss xent_loss = K.sum(K.binary_crossentropy(x, x_decoded_mean), axis=-1) kl_loss = - 0.5 * K.sum(1   z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1) vae_loss = K.mean(xent_loss   kl_loss) vae.add_loss(vae_loss) vae.compile(optimizer='rmsprop') vae.summary()  # 开始训练 vae.fit(x_train,         shuffle=True,         epochs=epochs,         batch_size=batch_size,         validation_data=(x_test, None)) 

3. CVAE

CVAE是标签下的条件VAE,最简单的方法是通过控制平均值来控制只需修改KL散度,从

改成

4. 直观解释

参见:https://spaces.ac.cn/archives/7725 首先是AE说明样本点在降维后应能够充分表达原始信息,因此必须在降维空间中非常规则,无冗余。显然,下图中的第四个是最好的。 加入后验分布自编码器p(z|x),编码空间可以更加规则。 现在每个样本x对应一个椭圆,确定一个椭圆需要两个信息:椭圆中心和椭圆轴长,它们形成一个向量,这取决于样本x,我们把它记为μ(x),σ(x)。既然整个椭圆对应样本x,我们要求椭圆可以重构任何一点x,因此,训练目标是:

这样一来,VAE它可以达到两个效果:1。从标准高斯分布(单元圆)随机取样一个向量,解码器可以获得真实样本,即生成模型;2。由于编码空间的紧凑形状和训练中添加到编码向量的噪声,编码向量的每个分量都可以在一定程度上解耦,并赋予编码向量一定的线性操作性质。

5. VQ-VAE

可参考本文:https://zhuanlan.zhihu.com/p/91434658 VQ-VAE基础是逐点回归生成图像模型。由于图像像素很多,需要先降维,再用PixelCNN建模。降维过程如下: 具体来说, n ? n ? 3 n*n*3 n?n?3的图片被encode成 m ∗ m ∗ d m*m*d m∗m∗d的矩阵,然后每个d维向量进行最近邻搜素,得到一个m*m的整数矩阵。一般来说,现在的m×m比原来的n×n×3要小得多,比如用CelebA数据做实验的时候,原来128×128×3的图可以编码为32×32的编码而基本不失真。 优化目标函数为: 前向计算的时候用的是 z q z_q zq​,计算梯度时用的是 z z z

下面是使用keras的示例代码:

# 编码器
x_in = Input(shape=(img_dim, img_dim, 3))
......# 这里加入了一堆Conv2D, resnet层
e_model = Model(x_in, x)

# 解码器
z_in = Input(shape=K.int_shape(x)[1:])
......# 这里加入了一堆Conv2D, resnet层
g_model = Model(z_in, z)

# 硬编码模型(寻找最近邻)
z_in = Input(shape=K.int_shape(x)[1:])
z = z_in
vq_layer = VectorQuantizer(num_codes)
codes, code_vecs = vq_layer(z)
q_model = Model(z_in, [codes, code_vecs])

# 训练流程
z = e_model(x)
_, e = q_model(z) # 注意这里仅使用code_vec,没有用code。
ze = Lambda(lambda x: x[0] + K.stop_gradient(x[1] - x[0]))([z, e])
x = g_model(ze)

train_model = Model(x_in, [x, _])
mse_x = K.mean((x_in - x)**2)
mse_e = K.mean((K.stop_gradient(z) - e)**2) # 这里的e就是上文中的q_z.
mse_z = K.mean((K.stop_gradient(e) - z)**2)
loss = mse_x + mse_e + 0.25 * mse_z
train_model.add_loss(loss)
train_model.compile(optimizer=Adam(1e-3))

这里生成模型e_model使用了8个卷积层,其中前面2个缩小尺寸,后面6个加残差。尺寸由(None,128,128,3)变为(None, 32, 32, 128) 离散化的硬编码模型q_model是一个VectorQuantizer,需要特别看一下。计算inputs和embeddings之间的距离矩阵distance,尺寸为[2, 32, 32, 64],然后用argmin得到下标,然后再用gather函数得到实际的向量。 解码模型g_model使用了3个卷积层(都有残差)+2个反卷积层(用于扩大尺寸)。尺寸由(None, 32, 32, 128)变为(None, 128, 128, 3) 总的模型是e_model->q_model(要加stop_gradient,即反向时消失)->g_model

class VectorQuantizer(Layer):
    """量化层
    """
    def __init__(self, num_codes, **kwargs):
        super(VectorQuantizer, self).__init__(**kwargs)
        self.num_codes = num_codes
        
    def build(self, input_shape): # 定义权重
        super(VectorQuantizer, self).build(input_shape)
        dim = input_shape[-1]
        self.embeddings = self.add_weight(
            name='embeddings',
            shape=(self.num_codes, dim),
            initializer='uniform'
        ) #使用add_weight函数创建权重矩阵
        
    def call(self, inputs): # 定义计算图
        """inputs.shape=[None, m, m, dim]
        """
        l2_inputs = K.sum(inputs**2, -1, keepdims=True)
        l2_embeddings = K.sum(self.embeddings**2, -1)
        for _ in range(K.ndim(inputs) - 1):
            l2_embeddings = K.expand_dims(l2_embeddings, 0)
        embeddings = K.transpose(self.embeddings)
        dot = K.dot(inputs, embeddings)
        distance = l2_inputs + l2_embeddings - 2 * dot
        codes = K.cast(K.argmin(distance, -1), 'int32')
        code_vecs = K.gather(self.embeddings, codes)
        return [codes, code_vecs]
    def compute_output_shape(self, input_shape):
        return [input_shape[:-1], input_shape]

标签: 连接器fit系列

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台