当前位置: 首页 > 科技观察

一文读懂自编码器的前世今生

时间:2023-03-18 18:05:23 科技观察

变分自编码器(VAE)可以说是最实用的自编码器,但是在讨论VAE之前,有必要先了解一下传统的自动编码器,用于数据压缩或去噪。编码器。变分自动编码器的强大功能假设您正在开发一款开放世界客户端游戏,并且游戏中的景观设置相当复杂。你聘请了一个平面设计团队制作了一些植物和树木来装饰游戏世界,但是把这些装饰植物放到游戏中之后,你发现它们看起来很不自然,因为同一种植物的外观看起来完全一样,此时你该怎么办?首先,您可能会建议使用一些参数化来尝试随机更改图像,但更改多少就足够了?需要多少改变?还有一个重要的问题:实现这样的改变需要多大的计算量?这是使用变分自动编码器的理想情况。我们可以训练一个神经网络来学习植物的潜在特征,每次我们在游戏世界中放置植物时,我们都可以从“学习到”的特征中随机抽取一个样本来生成一个独特的植物。事实上,许多开放世界游戏都是以这种方式构建它们的游戏世界。让我们看一个更形象的例子。假设我们是一名建筑师,想要为任意形状的建筑物生成平面图。可以使自动编码器网络学习基于任意建筑物形状的数据生成分布,它将从数据生成分布中抽取样本以生成平面图。详情请看下面的动画。对于设计师来说,这些技术的潜力无疑是最突出的。假设我们在一家时装公司工作,需要设计一种新款式的服装。我们可以训练一个基于“时尚”服装的自动编码器,让它学习时尚的数据生成分布。随后,从这个低维潜在分布中提取样本并用于创建新样式。在本节中,我们将研究时尚MNIST数据集。自动编码器传统自动编码器自动编码器实际上是非常简单的神经架构。它们主要是一种压缩形式,类似于使用MP3压缩音频文件或使用jpeg压缩图像文件。自动编码器与主成分分析(PCA)密切相关。事实上,如果自编码器使用的激活函数在每一层都是线性的,那么瓶颈(网络中最小层,即代码)存在的潜在变量将直接对应于主体(PCA/PrincipalComponent分析)成分。通常,自编码器中使用的激活函数是非线性的,典型的激活函数是ReLU(整流线性函数)和sigmoid/S函数。网络背后的数学原理相对容易理解。本质上,网络可以分为两部分:编码器和解码器。用?表示,编码器函数将原始数据X映射到潜在空间F(潜在空间F处于瓶颈)。用ψ表示,解码器函数将瓶颈处的潜在空间F映射到输出函数。这里的输出函数与输入函数相同。所以我们基本上是在一些广义的非线性压缩之后重建原始图像。编码网络可以由传递给激活函数的标准神经网络函数表示,其中z是潜在维度。类似地,解码网络可以用相同的方式表示,但具有不同的权重、偏差和底层激活函数。然后可以使用这些网络函数来编写我们将用于通过标准反向传播程序训练神经网络的损失函数。由于输入和输出是相同的图像,因此神经网络的训练过程不是监督学习或非监督学习。我们通常称这个过程为自监督学习。自动编码器的目的是选择编码器和解码器功能,以便用最少的信息对图像进行编码,以便在另一侧再现。如果瓶颈层使用的节点太少,重建图像的能力将受到限制,导致重建图像模糊或与原始图像有很大差异。如果使用太多节点,则不需要压缩。压缩背后的理论其实很简单,例如,每当您在Netflix上下载内容时,发送给您的数据都会被压缩。内容传输到电脑后,通过解压算法显示在电脑屏幕上。这类似于zip文件的操作方式,只是压缩是通过流处理算法在后台完成的。去噪自动编码器还有其他几种类型的自动编码器。其中最常用的是去噪自动编码器,本教程稍后将使用Keras对其进行分析。这些自动编码器在训练前向数据中添加一些白噪声,但在训练时将误差与原始图像进行比较。这可以防止网络过度拟合图像中存在的任何噪声。稍后,它将用于清理文档扫描图像中的折痕和暗区。稀疏自动编码器与其字面意思相反,稀疏自动编码器具有比输入或输出维度更大的潜在维度。然而,每次网络运行时,只有一小部分神经元会激活,这意味着网络本质上是“稀疏的”。稀疏自动编码器还通过一种正则化形式减少网络过度拟合的趋势,类似于去噪自动编码器。ShrinkageAutoencoderShrinkageencoder的工作原理和前面两个autoencoder基本一样,但是在shrinkageautoencoder中,我们没有改变结构,只是在lossfunction中加了一个regularizer。这可以看作是岭回归的一种神经形式。现在您了解了自动编码器的工作原理,让我们看看它们的弱点。一些最显着的挑战包括:·潜在空间中的差距·潜在空间中的可分离性·离散潜在空间这些问题如下图所示。MNIST数据集的潜在空间表示该图显示了潜在空间中不同标记数字的位置。可以看到潜在空间有空隙,我们不知道这些空间里的字符长什么样。这相当于监督学习中缺少数据,因为网络没有针对这些潜在空间进行训练。另一个问题是空间的可分离性,上图中有几个数字很好地分离,但也有一些区域的字符是随机分布的,这让我们很难区分字符的独特特征(在此图像中这是数字0-9)。还有无法研究连续潜在空间的问题。例如,我们没有针对任意输入训练的统计模型(即使我们填补了潜在空间中的所有空白,我们也无法做到)。传统自动编码器的这些问题意味着我们必须付出更多努力来学习数据生成分布并生成新数据和图像。现在您了解了传统自动编码器的工作原理,让我们来谈谈变分自动编码器。变分自动编码器采用一种从贝叶斯统计得出的变分推理形式,因此比以前的自动编码器稍微复杂一些。我们将在下一节中更深入地讨论变分自动编码器。变分自动编码器变分自动编码器延续了传统自动编码器的结构,并使用这种结构来学习数据生成分布,这使我们能够从潜在空间中随机抽取样本。然后可以使用解码器网络对这些随机样本进行解码,以生成独特的图像,这些图像具有与训练网络的图像相似的特征。对于那些熟悉贝叶斯统计的人来说,编码器正在学习后验分布的近似值。这种分布通常很难分析,因为它没有封闭形式的解决方案。这意味着我们要么实施计算复杂的采样程序,例如马尔可夫链蒙特卡罗(MCMC)算法,要么采用变分方法。您可能会猜到,变分自动编码器使用变分推理来生成其后验分布的近似值。我们将适度详细地讨论这个过程,但如果您想进行更深入的分析,我建议您查看JaanAltosaar的这篇博文。变分推理是研究生机器学习课程或统计学课程中的一个主题,但你不需要统计学学位也能理解它的基本概念。如果您对其背后的数学理论不感兴趣,您也可以选择跳过此变分自编码器(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个数。要做的第二件事通常称为重新参数化技巧,它是通过从导数中取出随机变量来完成的,因为从随机变量中取出导数会由于其固有的随机性而产生很大的误差。重新参数化技术比较深奥,但简单来说,正态分布可以写成均值加标准差,然后乘以误差。这样,我们在微分时,不是从随机变量本身求导数,而是从它的参数中求导数。这个过程没有一般的封闭形式的解决方案,因此近似后验分布的能力仍然有些有限。然而,指数分布族确实有一个封闭形式的解决方案。这意味着像Normal、Binomial、Poisson、Beta等标准分布。因此,即使无法找到真实的后验分布值,使用指数分布族仍然可以得到最接近的近似值。变分推理的秘诀在于选择分布的区域Q,使其足够大以获得后验分布的近似值,但计算时间不长。现在我们对如何训练网络学习数据的底层分布有了一个大概的了解,现在我们可以探索如何使用这种分布来生成数据。数据生成过程看下图,可以看出数据生成过程的近似认为应该生成数字'2',所以它从隐变量质心生成值2。但是也许你不想每次都生成完全相同的数字'2',就像上面提到的结束游戏示例中提到的植物一样,所以我们分配一个随机数和“学习”值'2'在latentspace过程中添加了一些随机噪声。该过程通过解码器网络后,我们得到一个看起来与原型不同的“2”。这是一个非常简化的示例,它抽象了实际自动编码器网络的架构。下图表示在其编码器和解码器网络中使用卷积层的真正变分自动编码器的架构。从这里可以看出,我们是分别学习潜在空间中生成数据分布的中心和范围,然后从这些分布中“采样”生成本质上是“假”的数据。这种学习过程的固有性质意味着所有看起来相似的参数(刺激同一网络中神经元的放电)都聚集到潜在空间中,而不是随机分散。如下图所示,可以看出2的值都是聚在一起的,而3的值是逐渐推开的。这个过程很有帮助,因为它意味着网络不会将字符随机放置在潜在空间中,从而使值之间的转换更加真实。整个网络架构的概览如下图所示。希望读者看到这里能更清楚地了解整个过程。我们在一组图像上训练自动编码器,以了解潜在空间中均值和范数之间的差异,这形成了我们的数据生成分布。接下来,当我们想要生成相似的图像时,我们对潜在空间的质心进行采样,用标准偏差和一些随机误差对其进行轻微改变,然后将其传递给解码器网络。从这个例子中可以清楚地看出,最终输出看起来与输入图像相似,但并不相同。变分自动编码器的编码指南本节讨论一个简单的去噪自动编码器,用于从文档扫描图像中去除折痕和污迹,并去除FashionMNIST数据集中的噪声。然后,在MNIST数据集上训练网络后,使用变分自动编码器生成新衣服。DenoisingAutoencoderFashionMNIST在第一个练习中,向FashionMNIST数据集添加一些随机噪声(椒盐噪声),然后使用去噪自动编码器尝试去除噪声。第一步预处理:下载数据,调整大小,加噪声。##下载数据(x_train,y_train),(x_test,y_test)=datasets.fashion_mnist.load_data()##normalizeandreshapeex_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)#Letsaddsamplenoise-SaltandPeppernoise=augmenters.SaltAndPepper(0.1)seq_object=augmenters.Sequential([噪声])train_x_n=seq_object.augment_images(x_train*255)/255val_x_n=seq_object.augment_images(x_test*255)/255接下来,为自动编码器网络创建一个结构。这包括多层卷积神经网络、编码器网络上的最大池化层和解码器网络上的放大层。#inputlayerinput_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='相同')(encoded_layer2)encoded_layer3=Conv2D(16,(3,3),activation='relu',padding='相同')(encoded_layer2)latent_view=MaxPool2D((2,2),padding='相同')(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)#compilethemodelmodel=Model(input_layer,output_layer)model.compile(optimizer='adam',loss='mse')#runthemodelearly_stopping=EarlyStopping(monitor='val_loss',min_delta=0,patience=10,verbose=5,模式='auto')history=model.fit(train_x_n,x_train,epochs=20,batch_size=2048,validation_data=(val_x_n,x_test),callbacks=[early_stopping])输入图像,添加噪声图像,输出图像fashionImagefromMNIST输入。向输入图像添加椒盐噪声。去噪网络输出的图像。从这里可以看出,我们成功地从嘈杂的图像中去除了相当多的噪声,但同时丢失了一定量的服装细节分辨率。这是使用强大网络的成本之一。可以调整该网络,使最终输出更能代表输入图像。文本清理去噪自动编码器的第二个示例涉及清理扫描图像中的折痕和暗区。下面是最终得到的输入输出图像。嘈杂文本数据的输入图像。清理后的文字图片。这方面的数据预处理比较复杂,这里不做介绍,预处理过程和相关数据可以在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))(解码器)output_layer=Conv2D(1,(3,3),activation='sigmoid',padding='same')(解码器)ae=Model(input_layer,output_layer)ae.compile(loss='mse',optimizer=Adam(lr=0.001))batch_size=16epochs=200early_stopping=EarlyStopping(monitor='val_loss',min_delta=0,patient=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])变分自动编码器的最终结局是尝试从FashionMNIST数据集中的现有服装生成新图像。神经结构更为复杂,包括一个称为“Lambda”层的采样层。batch_size=16latent_dim=2#Numberoflatentdimensionparameters#ENCODERARCHITECTURE:Input->Conv2D*4->Flatten->Denseinput_img=Input(shape=(28,28,1))x=Conv2D(32,3,padding='same',激活='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)#needtoknowtheshapeofthenetworkhereforthedecodershape_before_flattening=K.int_shape(x)x=Flatten()(x)x=Dense(32,activation='relu')(x)#Twooutputs,latentmeanand(log)variancez_mu=Dense(latent_dim)(x)z_log_sigma=Dense(latent_dim)(x)##SAMPLINGFUNCTIONdefsampling(args):z_mu,z_log_sigma=argsepsilon=K.random_normal(shape=(K.shape(z_mu)[0],latent_dim),mean=0.,stddev=1.)returnz_mu+K.exp(z_log_sigma)*epsilon#samplevectorfromthelatentdistributionz=Lambda(采样)([z_mu,z_log_sigma])##DECODERARCHITECTURE#decodertakesthelatentdistributionsampleasinputdecoder_input=Input(K.int_shape(z)[1:])#Expandto784totalpixelsx=Dense(np.prod(shape_before_flattening[1:]),activation='relu')(decoder_input)#reshapex=Reshape(shape_before_flattening[1:])(x)#useConv2DTransposetoreversetheconvlayersfromtheencoderx=Conv2DTranspose(32,3,填充相同',activation='relu',strides=(2,2))(x)x=Conv2D(1,3,padding='same',activation='sigmoid')(x)#decodermodelstatementdecoder=模型(decoder_input,x)#applythedecodertothesamplefromthelatentdistributionz_decoded=decoder(z)就是这个架构,但是还是需要插入损失函数,然后合并KL散度#constructacustomlayertocalculatethelossclassCustomVariationalLayer(Layer):defvae_loss(self,x,z_decoded):x=K.flatten(x)z_decoded=K.flatten(z_decoded)#重建lossxent_loss=binary_crossentropy(x,z_decoded)#KLdivergencekl_loss=KLdivergencekl_loss=KLdivergence-kl_4*K5均值(1+z_log_sigma-K.square(z_mu)-K.exp(z_log_sigma),axis=-1)returnK.mean(xent_loss+kl_loss)#addsthecustomlosstotheclassdefcall(self,inputs):x=inputs[0]z_decoded=输入[1]loss=self.vae_loss(x,z_decoded)self.add_loss(loss,inputs=inputs)returnx#applythecustomlosstotheinputimagesandthedecodedlatentdistributionsampley=CustomVariationalLayer()([input_img,z_decoded])#VAEmodelstatementvae=Model(input)vae.imgiley,optimizer='rmsprop',loss=None)vae.fit(x=train_x,y=None,shuffle=True,epochs=20,batch_size=batch_size,validation_data=(val_x,None))现在可以查看重构后的样本,看看网络能学到什么。鞋子、手袋和服装之间的过渡从这里可以清楚地看到。此处并未标记所有可能使图片更清晰的空间。还可以观察到FashionMNIST数据集中现有10件服装的潜在空间和颜色代码。可以看出,这些服装被分成了不同的簇。