[Deep-Learning-with-Python]使用VAEs生成新图片

变分自动编码器生成图片

从隐图像空间进行采样以创建全新的图像或编辑现有图像是目前创作AI最受欢迎和最成功的应用方式。

图像隐空间取样

图像生成的关键思想是开发表示的低维潜在空间(自然是矢量空间),其中任何点都可以映射到逼真的图像上。 能够实现该映射的模块,将潜在点作为输入并输出图像(像素网格),被称为生成器(在GAN的情况下)或解码器(在VAE的情况下)。一旦开发出这样的潜在空间,可以有意或无意地从中采样点,并通过将它们映射到图像空间,生成以前从未见过的图像。

image

GAN和VAE是用于学习图像表示的潜在空间的两种不同策略,每种都具有其自身的特征。VAE非常适合学习结构良好的潜在空间,其中特定方向编码数据能产生有意义的变化轴。GAN生成的图像可能非常逼真,但它们来自潜在的空间可能没有那么多的结构和连续性

image

图片编辑的概念向量

给定潜在的表示空间或嵌入空间,空间中的某些方向可以编码原始数据中有趣的变化轴。例如,在面部图像的潜在空间中,可能存在微笑矢量s,使得如果潜在点z是某个面部的嵌入表示,则潜在点z+s是同一面部的嵌入表示,面带微笑。一旦确定了这样的矢量,就可以通过将图像投影到潜在空间中来编辑图像,以有意义的方式移动它们的表示,然后将它们解码回图像空间。

变分自动编码器

变分自动编码器,是一种生成模型,特别适用于通过概念向量进行图像编辑的任务。它们是自动编码器的现代版本 - 一种旨在将输入编码到低维潜在空间然后将其解码回来的网络 - 将来自深度学习的想法与贝叶斯推理混合在一起.
经典图像自动编码器通过编码器模块拍摄图像,将其映射到潜在的矢量空间,然后通过解码器模块将其解码回与原始图像具有相同尺寸的输出。然后通过使用与输入图像相同的图像作为目标数据来训练,这意味着自动编码器学习重建原始输入。通过对代码(编码器的输出)施加各种约束,可以使自动编码器学习或多或少有趣的数据潜在表示。最常见的是,将限制代码为低维和稀疏(大多数为零),在这种情况下,编码器可以将输入数据压缩为更少的信息位。

image

在实践中,这种经典的自动编码器不会导致特别有用或结构良好的潜在空间,也不太擅长数据压缩。由于这些原因,他们已经基本上不再流行。然而,VAE用统计方法增强了自动编码器,迫使他们学习连续的,高度结构化的潜在空间。它们已成为图像生成的强大工具

VAE不是将其输入图像压缩为潜在空间中的固定代码,而是将图像转换为统计分布的参数:均值和方差。从本质上讲,这意味着假设输入图像是由统计过程生成的,并且此过程的随机性应在编码和解码期间用于计算。然后,VAE使用均值和方差参数随机采样分布的一个元素,并将该元素解码回原始输入。该过程的随机性提高了鲁棒性并迫使潜在空间在任何地方编码有意义的表示:在潜在空间中采样的每个点被解码为有效输出

image

数学描述,VAE工作过程:

  1. 编码器模块将输入样本input_img转换为表示的隐空间的z_mean和z_log_variance两个参数;
  2. 通过z=z_mean + exp(z_log_variance)epsilon 从假定生成输入图像的潜在正态分布*中随机采样点z,其中epsilon是小值的随机张量;
  3. 解码器模块将隐空间中的z点映射回原始输入图像。

因为epsilon是随机的,所以该过程确保接近编码input_img(z-mean)的潜在位置的每个点都可以被解码为类似于input_img的东西,从而迫使潜在空间持续有意义。潜在空间中的任何两个闭合点将解码为高度相似的图像。连续性与潜在空间的低维度相结合,迫使潜在空间中的每个方向编码有意义的数据变化轴,使得潜在空间非常结构化,因此非常适合通过概念向量进行操纵

VAE的参数通过两个损失函数进行训练:强制解码样本与初始输入匹配的重建损失函数,以及有助于学习良好的隐空间并减少过度拟合训练数据的正则化损失函数。让我们快速了解一下VAE的Keras实现。原理上,它看起来像这样:

z_mean,z_log_variance = encoder(input_img)#输入编码成均值、方法参数

z = z_mean + exp(z_log_variance)*epsilon#隐空间通过epsilon取样

reconstructed_img = decoder(z)#取样点生成新图片

model = Model(input_img,reconstructed_img)#实例化模型:输入图片映射到新建图片上,之后训练

模型定义后,使用重建损失函数和正则损失训练模型。
使用一个简单的convnet将输入图片映射到隐空间的概率分布上,得到两个向量z_mean,z_log_var。

VAE Encoder网络

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',stride=(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)

之后使用输入图片的假设空间分布特征z_mean和z_log_var得到隐空间取样点z。在这里,将一些任意代码(构建在Keras后端基元之上)包装到Lambda层中。在Keras中,一切都需要是一个层,因此不属于内置层的代码应该包装在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(z_log_var)*epsilon

z = layers.Lambda(sampling)([z_mean,z_log_var])

解码器部分实现。将向量z reshape到图片尺寸,最后经过几个卷积层得到最终的图片输出。
VAE decoder网络:隐变量空间到图片

decoder_input = layers.Input(K.int_shape(z)[1:])#输入z向量

x = layers.Dense(np.prod(shape_before_flattening[1:]),activation='relu')(decoder_input)
x = layers.Reshape(shape_before_flattening[1:])(x)
x = layers.Conv2DTranspose(32,3,padding='same',activation='relu',strides=(2,2))(x)
x = layers.Conv2D(1,3,padding='same',activation='sigmoid')(x)

decoder = Model(decoder_input, x)#实例化模型,模型将输入decoder_input转换成图片
z_decoded = decoder(z)#输入z,得到最终转换后的输出图片

VAE的双重损失函数不符合传统形式损失函数(输入,目标)的预期。因此,将通过编写内部使用内置add_loss图层方法来创建任意损失的自定义图层来设置损失函数。
定义图层计算损失函数

class CustomVariationalLayer(keras.layers.Layer):
    def vae_loss(self, x, z_decoded):
        x = K.flatten(x)
        z_decoded = K.flatten(z_decoded)
        xent_loss = keras.metrics.binary_crossentropy(x, z_decoded)#重构损失
        kl_loss = -5e-4 * K.mean(1+z_log_var-K.square(z_mean)-  
            K.exp(z_log_var), axis=-1)#encoder损失
        return K.mean(xent_loss + kl_loss)
    
    def call(self,inputs):
        x = inputs[0]
        z_decoded = inputs[1]
        loss = self.vae_loss(x,z_decoded)
        self.add_loss(loss,inputs=inputs)
        return x
    
y = CustomVariationalLayer()([input_img, z_decoded])

最后,实例化模型并训练。由于损失函数是在自定义层中处理的,因此不会在编译时指定外部损失(loss=None),这反过来意味着不会在训练期间传递目标数据(如所见,只能将x_train传递给模型在fit函数中)。
VAE训练

from keras.datasets import mnist

vae = Model(input_img,y)#通过定义输入和输出 Model模型
vae.compile(optimizer='rmsprop', loss=None)
vae.summary()
(x_train, _), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_train = x_train.reshape(x_train.shape + (1,))
x_test = x_test.astype('float32') / 255.
x_test = x_test.reshape(x_test.shape + (1,))
vae.fit(x=x_train, y=None,shuffle=True,epochs=10,batch_size=batch_size,
    validation_data=(x_test, None))

模型训练完成后,可以使用decoder模块将任意隐变量空间点转换生成图片。
2D隐变量空间点取样,生成图片

import matplotlib.pyplot as plt
from scipy.stats import norm

n = 15#15*15 225个数字图片
digit_size = 28
figure = np.zeros((digit_size*n,digit_sie*n))#最终图片
grid_x = norm.ppf(np.linspace(0.05,0.95,n))#假设隐变量空间符合高斯分布
grid_y = norm.ppf(np.linspace(0.05,0.95,n))#ppf随机取样

for i,yi in enumerate(grid_x):
    for j, xi in enumerate(grid_y):
        z_sample = np.array([[xi, yi]])
        #重复z_sample多次,形成一个完整的batch
        z_sample = np.tile(z_sample, batch_size).reshape(batch_size, 2)
        x_decoded = decoder.predict(z_sample, batch_size=batch_size)
        digit=x_decoded[0].reshape(digit_size, digit_size)#28*28*1->28*28
        figure[i*digit_size:(i+1)*digit_size,j*digit_size:(j+1)*digit_size] = digit

plt.figure(figsize=(10, 10))
plt.imshow(figure, cmap='Greys_r')
plt.show()
image

小结

  • 深度学习的图像生成是通过学习捕获有关图像数据集的统计信息的潜在空间来完成的。通过对潜在空间中的点进行采样和解码,可以生成前所未见的图像。有两个主要工具:VAE和GAN
  • VAE导致高度结构化,连续的潜在表征。出于这个原因,它们适用于在潜在空间中进行各种图像编辑:面部交换,将皱眉脸变成笑脸,等等。它们也可以很好地用于基于潜在空间的动画,例如沿着潜在空间的横截面动画制作动画,显示起始图像以连续的方式慢慢变形为不同的图像。
  • GAN可以生成逼真的单帧图像,但可能不会引入具有坚固结构和高连续性的潜在空间。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容