自动编码器怎么用(自动编码器是什么)
全文5718字,预计学习时间20分钟以上。
变分自动编码器(VAE)可以说是最实用的自动编码器,但在讨论VAE之前,我们还必须了解用于数据压缩或去噪的传统自动编码器。
《变分自动编码器》的伟大之处在于,你正在开发一个开放的世界之旅,游戏中的风景设定相当复杂。你雇佣了一个平面设计团队制作一些植物和树木来装饰游戏世界,但是把这些装饰性的植物放到游戏中之后,
你发现它们看起来不自然,因为同一株植物看起来完全一样。你该怎么办?首先,你可能会建议使用一些参数化来尝试随机改变图像。
但是多少变化才够呢?需要多大的改变?还有一个重要问题:实现这种变化的计算强度是多少?这是使用可变自动编码器的理想情况。我们可以训练一个神经网络来学习植物的潜在特征。
每当我们将一株植物放入游戏世界时,我们可以从“已学习”的特征中随机选择一个样本来生成一株独特的植物。事实上,许多开放世界游戏都在以这种方式构建他们的游戏世界设置。让我们看一个更形象的例子。
假设我们是一名建筑师,想要为具有任意形状的建筑物生成平面图。自动编码器网络可以学习基于任何建筑形状的数据生成分布,并且它将从数据生成分布中提取样本以生成计划。详情请看下面的动画。
对于设计师来说,这些技术的潜力无疑是最突出的。假设我们在一家时装公司工作,需要设计一种新的时装风格。我们可以根据“时尚”的服装训练一个自动编码器,让它学习时尚数据来生成分布。然后,
从这个低维的势分布中提取样本,创造新的风格。在这一部分,我们将研究时尚MNIST数据集。
自动编码器传统的自动编码器实际上是一个非常简单的神经结构。它们基本上是一种压缩形式,类似于使用MP3压缩音频文件或jpeg压缩图像文件。
自动编码器与主成分分析密切相关。实际上,如果自动编码器使用的激活函数在每一层都是线性的,那么瓶颈处存在的势变量(网络中最小的一层,即代码)将直接对应PCA/PCA的主要成分。通常情况下,
自动编码器中使用的激活函数是非线性的,典型的激活函数有ReLU(修正线性函数)和sigmoid/S函数。网络背后的数学原理相对容易理解。本质上,网络可以分为两部分:编码器和解码器。
编码器函数表示为,它将原始数据x映射到潜在空间f(潜在空间f位于瓶颈处)。解码器函数用表示,它将瓶颈处的潜在空间f映射到输出函数。这里的输出函数和输入函数是一样的。因此,
我们基本上在一些广义非线性压缩之后重建原始图像。编码网络可以由激活函数传递的标准神经网络函数来表示,其中z是潜在维度。
同样,解码网络也可以用同样的方式表示,但需要使用不同的权重、偏差和潜在激活函数。
然后我们可以使用这些网络函数来编写损失函数,我们将使用这个损失函数通过标准的反向传播程序来训练神经网络。
由于输入和输出是同一幅图像,神经网络的训练过程不是监督学习,也不是无监督学习。我们通常称这个过程为自我监督学习。自动编码器的目的是选择编码器和解码器的功能,以便用最少的信息对图像进行编码。
这样它可以在另一边再生。如果瓶颈层中使用的节点太少,重建图像的能力将会受到限制,导致重建的图像模糊或与原始图像有很大差异。如果使用的节点太多,就没必要压缩。压缩背后的理论其实很简单。
例如,当你在网飞上下载东西时,发送给你的数据会被压缩。这些内容一旦传输到电脑上,就会通过解压算法显示在电脑屏幕上。这类似于zip文件的工作方式。
只是这里说的压缩是由流处理算法在后台完成的。有几种其他类型的自动编码器用于去噪。最常用的一种是去噪自动编码器,本教程稍后将使用Keras对其进行分析。这些自动编码器在训练前给数据添加一些白噪声,
然而,在训练期间,将误差与原始图像进行比较。这使得网络不会过度适应图像中的任何噪声。稍后,它将用于消除文档扫描图像中的折痕和暗区。与其字面意思相反,稀疏自动编码器,
稀疏自动编码器具有比输入或输出维度更大的潜在维度。然而,每次网络运行时,只有很小一部分神经元会触发,这意味着网络本质上是“稀疏”的。稀疏自动编码器也是通过一种规则化的形式来减少网络过度拟合的倾向,
这一点与去噪自动编码器相似。收缩自动编码器收缩编码器与前两个自动编码器的运行过程基本相同,但是在收缩自动编码器中,我们不改变结构,只是在丢失函数中添加一个正则化器。这可以被看作是岭回归的一种神经形式。
现在了解了自动编码器是如何运行的,接下来看看自动编码器的弱项。一些最显著的挑战包括: 潜在空间中的间隙 潜在空间中的可分性 离散潜在空间这些问题都在以下图中体现。
MNIST数据集的潜在空间表示
这张图显示了潜在空间中不同标记数字的位置。可以看到潜在空间中存在间隙,我们不知道字符在这些空间中是长什么样的。这相当于在监督学习中缺乏数据,因为网络并没有针对这些潜在空间的情况进行过训练。
另一个问题就是空间的可分性,上图中有几个数字被很好地分离,但也有一些区域被标签字符是随机分布的,这让我们很难区分字符的独特特征(在这个图中就是数字0-9)。还有一个问题是无法研究连续的潜在空间。例如,
我们没有针对任意输入而训练的统计模型(即使我们填补了潜在空间中的所有间隙也无法做到)。这些传统自动编码器的问题意味着我们还要做出更多努力来学习数据生成分布并生成新的数据与图像。
现在已经了解了传统自动编码器是如何运行的,接下来讨论变分自动编码器。变分自动编码器采用了一种从贝叶斯统计中提取的变分推理形式,因此会比前几种自动编码器稍微复杂一些。
我们会在下一节中更深入地讨论变分自动编码器。
变分自动编码器变分自动编码器延续了传统自动编码器的结构,并利用这一结构来学习数据生成分布,这让我们可以从潜在空间中随机抽取样本。然后,可以使用解码器网络对这些随机样本进行解码,以生成独特的图像,
这些图像与网络所训练的图像具有相似的特征。
对于熟悉贝叶斯统计的人来说,编码器正在学习后验分布的近似值。这种分布通常很难分析,因为它没有封闭式的解。这意味着我们要么执行计算上复杂的采样程序,如马尔可夫链蒙特卡罗(MCMC)算法,
要么采用变分方法。正如你可能猜测的那样,变分自动编码器使用变分推理来生成其后验分布的近似值。我们将会用适量的细节来讨论这一过程,但是如果你想了解更深入的分析,
建议你阅览一下Jaan Altosaar撰写的博客。变分推理是研究生机器学习课程或统计学课程的一个主题,但是了解其基本概念并不需要拥有一个统计学学位。若对背后的数学理论不感兴趣,
也可以选择跳过这篇变分自动编码器(VAE)编码教程。首先需要理解的是后验分布以及它无法被计算的原因。先看看下面的方程式:贝叶斯定理。这里的前提是要知道如何从潜变量“z”生成数据“x”。
这意味着要搞清p(z|x)。然而,该分布值是未知的,不过这并不重要,因为贝叶斯定理可以重新表达这个概率。但是这还没有解决所有的问题,因为分母(证据)通常很难解。但也不是就此束手无辞了,
还有一个挺有意思的办法可以近似这个后验分布值。那就是将这个推理问题转化为一个优化问题。
要近似后验分布值,就必须找出一个办法来评估提议分布与真实后验分布相比是否更好。而要这么做,就需要贝叶斯统计员的最佳伙伴:KL散度。KL散度是两个概率分布相似度的度量。如果它们相等,
那散度为零;而如果散度是正值,就。。这两个分布不相等。KL散度的值为非负数,但实际上它不是一个距离,因为该函数不具有对称性。可以采用下面的方式使用KL散度:
这个方程式看起来可能有点复杂,但是概念相对简单。那就是先猜测可能生成数据的方式,并提出一系列潜在分布Q,然后再找出最佳分布q*,从将提议分布和真实分布的距离最小化,然后因其难解性将其近似。
但这个公式还是有一个问题,那就是p(z|x)的未知值,所以也无法计算KL散度。那么,应该怎么解决这个问题呢?这里就需要一些内行知识了。
可以先进行一些计算上的修改并针对证据下界(ELBO)和p(x)重写KL散度:
有趣的是ELBO是这个方程中唯一取决于所选分布的变量。而后者由于不取决于q,则不受所选分布的影响。因此,可以在上述方程中通过将ELBO(负值)最大化来使KL散度最小化。这里的重点是ELBO可以被计算,
也就是说现在可以进行一个优化流程。所以现在要做的就是给Q做一个好的选择,再微分ELBO,将其设为零,然后就大功告成了。可是开始的时候就会面临一些障碍,即必须选择最好的分布系列。一般来说,
为了简化定义q的过程,会进行平均场变分推理。每个变分参数实质上是相互独立的。因此,每个数据点都有一个单独的q,可被相称以得到一个联合概率,从而获得一个“平均场”q。
实际上,可以选用任意多的场或者集群。比如在MINIST数据集中,可以选择10个集群,因为可能有10个数字存在。要做的第二件事通常被称为再参数化技巧,通过把随机变量带离导数完成,
因为从随机变量求导数的话会由于它的内在随机性而产生较大的误差。
再参数化技巧较为深奥,但简单来说就是可以将一个正态分布写成均值加标准差,再乘以误差。这样在微分时,我们不是从随机变量本身求导数,而是从它的参数求得。这个程序没有一个通用的闭型解,
所以近似后验分布的能力仍然受到一定限制。然而,指数分布族确实有一个闭型解。这意味着标准分布,如正态分布、二项分布、泊松分布、贝塔分布等。所以,就算真正的后验分布值无法被查出,
依然可以利用指数分布族得出最接近的近似值。变分推理的奥秘在于选择分布区Q,使其足够大以求得后验分布的近似值,但又不需要很长时间来计算。既然已经大致了解如何训练网络学习数据的潜在分布,
那么现在可以探讨如何使用这个分布生成数据。
数据生成过程观察下图,可以看出对数据生成过程的近似认为应生成数字‘2’,所以它从潜在变量质心生成数值2。但是也许不希望每次都生成一摸一样的数字‘2’,就好像上述端游例子所提的植物,
所以我们根据一个随机数和“已学”的数值‘2’分布范围,在潜在空间给这一过程添加了一些随机噪声。该过程通过解码器网络后,我们得到了一个和原型看起来不一样的‘2’。
这是一个非常简化的例子,抽象描述了实际自动编码器网络的体系结构。下图表示了一个真实变分自动编码器在其编码器和解码器网络使用卷积层的结构体系。从这里可以观察到,
我们正在分别学习潜在空间中生成数据分布的中心和范围,然后从这些分布“抽样”生成本质上“虚假”的数据。
该学习过程的固有性。。所有看起来很相似的参数(刺激相同的网络神经元放电)都聚集到潜在空间中,而不是随意的分散。如下图所示,可以看到数值2都聚集在一起,而数值3都逐渐地被推开。这一过程很有帮助,
因为这。。网络并不会在潜在空间随意摆放字符,从而使数值之间的转换更有真实性。
整个网络体系结构的概述如下图所示。希望读者看到这里,可以比较清晰地理解整个过程。我们使用一组图像训练自动编码器,让它学习潜在空间里均值和标准值的差,从而形成我们的数据生成分布。接下来,
当我们要生成一个类似的图像,就从潜在空间的一个质心取样,利用标准差和一些随机误差对它进行轻微的改变,然后使其通过解码器网络。从这个例子可以明显看出,最终的输出看起来与输入图像相似,但却是不一样的。
变分自动编码器编码指南本节将讨论一个简单的去噪自动编码器,用于去除文档扫描图像上的折痕和污痕,以及去除Fashion MNIST数据集中的噪声。然后,在MNIST数据集训练网络后,
就使用变分自动编码器生成新的服装。去噪自编码器Fashion MNIST在第一个练习中,在Fashion MNIST数据集添加一些随机噪声(椒盐噪声),然后使用去噪自编码器尝试移除噪声。
首先进行预处理:下载数据,调整数据大小,然后添加噪声。
## Download the data(x_train, y_train), (x_test, y_test)=datasets.fashion_mnist.load_data()## normalize and reshapex_train=x_train/255.x_test=x_test/255.x_train=x_train.reshape(-1, 28, 28, 1)x_test=x_test.reshape(-1, 28, 28, 1)# Lets add sample noise - Salt and Peppernoise=augmenters.SaltAndPepper(0.1)seq_object=augmenters.Sequential([noise])train_x_n=seq_object.augment_images(x_train * 255)/255val_x_n=seq_object.augment_images(x_test * 255)/255接着,
# input layerinput_layer=Input(shape=(28, 28, 1)) # encodingarchitectureencoded_layer1=Conv2D(64, (3, 3), activation='relu', padding='same')(input_layer)encoded_layer1=MaxPool2D( (2, 2), padding='same')(encoded_layer1)encoded_layer2=Conv2D(32, (3, 3), activation='relu', padding='same')(encoded_layer1)encoded_layer2=MaxPool2D( (2, 2), padding='same')(encoded_layer2)encoded_layer3=Conv2D(16, (3, 3), activation='relu', padding='same')(encoded_layer2)latent_view=MaxPool2D( (2, 2),padding='same')(encoded_layer3) # decodingarchitecturedecoded_layer1=Conv2D(16, (3, 3), activation='relu', padding='same')(latent_view)decoded_layer1=UpSampling2D((2, 2))(decoded_layer1)decoded_layer2=Conv2D(32, (3, 3), activation='relu', padding='same')(decoded_layer1)decoded_layer2=UpSampling2D((2, 2))(decoded_layer2)decoded_layer3=Conv2D(64, (3, 3), activation='relu')(decoded_layer2)decoded_layer3=UpSampling2D((2, 2))(decoded_layer3)output_layer=Conv2D(1, (3, 3), padding='same',activation='sigmoid')(decoded_layer3) # compile themodelmodel=Model(input_layer, output_layer)model.compile(optimizer='adam',loss='mse') # run themodelearly_stopping=EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=5,mode='auto')history=model.fit(train_x_n, x_train, epochs=20, batch_size=2048,validation_data=(val_x_n, x_test), callbacks=[early_stopping])所输入的图像,
从时尚MNIST输入的图像。
添加椒盐噪声的输入图像。
从去噪网络输出的图像。从这里可以看到,我们成功从噪声图像去除相当的噪声,但同时也失去了一定量的服装细节的分辨率。这是使用稳健网络所需付出的代价之一。可以对该网络进行调优,
使最终的输出更能。。所输入的图像。文本清理去噪自编码器的第二个例子包括清理扫描图像的折痕和暗黑区域。这是最终获得的输入和输出图像。
输入的有噪声文本数据图像。
经清理的文本图像。为此进行的数据预处理稍微复杂一些,因此就不在这里进行介绍,预处理过程和相关数据可在GitHub库里获取。网络结构如下:
input_layer=Input(shape=(258, 540, 1)) #encoderencoder=Conv2D(64, (3, 3), activation='relu', padding='same')(input_layer)encoder=MaxPooling2D((2, 2), padding='same')(encoder) #decoderdecoder=Conv2D(64, (3, 3), activation='relu', padding='same')(encoder)decoder=UpSampling2D((2, 2))(decoder)output_layer=Conv2D(1, (3, 3), activation='sigmoid', padding='same')(decoder) ae=Model(input_layer, output_layer) ae.compile(loss='mse',optimizer=Adam(lr=0.001)) batch_size=16epochs=200 early_stopping=EarlyStopping(monitor='val_loss',min_delta=0,patience=5,verbose=1,mode='auto')history=ae.fit(x_train, y_train, batch_size=batch_size, epochs=epochs,validation_data=(x_val, y_val), callbacks=[early_stopping])变分自编码器最后的压轴戏,
batch_size=16latent_dim=2 # Number of latent dimension parameters# ENCODER ARCHITECTURE: Input - Conv2D*4 - Flatten - Denseinput_img=Input(shape=(28, 28, 1))x=Conv2D(32, 3, padding='same', activation='relu')(input_img)x=Conv2D(64, 3, padding='same', activation='relu', strides=(2, 2))(x)x=Conv2D(64, 3, padding='same', activation='relu')(x)x=Conv2D(64, 3, padding='same', activation='relu')(x)# need to know the shape of the network here for the decodershape_before_flattening=K.int_shape(x)x=Flatten()(x)x=Dense(32, activation='relu')(x)# Two outputs, latent mean and (log)variancez_mu=Dense(latent_dim)(x)z_log_sigma=Dense(latent_dim)(x)## SAMPLING FUNCTIONdef sampling(args): z_mu, z_log_sigma=args epsilon=K.random_normal(shape=(K.shape(z_mu)[0], latent_dim), mean=0. stddev=1.) return z_mu + K.exp(z_log_sigma) * epsilon# sample vector from the latent distributionz=Lambda(sampling)([z_mu, z_log_sigma])## DECODER ARCHITECTURE# decoder takes the latent distribution sample as inputdecoder_input=Input(K.int_shape(z)[1:])# Expand to 784 total pixelsx=Dense(np.prod(shape_before_flattening[1:]), activation='relu')(decoder_input)# reshapex=Reshape(shape_before_flattening[1:])(x)# use Conv2DTranspose to reverse the conv layers from the encoderx=Conv2DTranspose(32, 3, padding='same', activation='relu', strides=(2, 2))(x)x=Conv2D(1, 3, padding='same', activation='sigmoid')(x)# decoder model statementdecoder=Model(decoder_input, x)# apply the decoder to the sample from the latent distributionz_decoded=decoder(z)这就是体系结构,
# construct a custom layer to calculate the lossclass CustomVariationalLayer(Layer): def vae_loss(self, x, z_decoded): x=K.flatten(x) z_decoded=K.flatten(z_decoded) # Reconstruction loss xent_loss=binary_crossentropy(x, z_decoded) # KL pergence kl_loss=-5e-4 * K.mean(1 + z_log_sigma - K.square(z_mu) - K.exp(z_log_sigma), axis=-1) return K.mean(xent_loss + kl_loss) # adds the custom loss to the class 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# apply the custom loss to the input images and the decoded latent distribution sampley=CustomVariationalLayer()([input_img, z_decoded])# VAE model statementvae=Model(input_img, y)vae.compile(optimizer='rmsprop', loss=None)vae.fit(x=train_x, y=None, shuffle=True, epochs=20, batch_size=batch_size, validation_data=(val_x, None))现在,
从这里可以清楚看到鞋子、手袋和服装之间的过渡。在此并没有标出所有使画面更清晰的潜在空间。也可以观察到Fashion MNIST数据集现有的10件服装的潜在空间和颜色代码。
可看出这些服饰分成了不同的集群。
留言点赞关注
我们一起分享AI学习与发展的干货欢迎关注全平台AI垂类自媒体“读芯术”