写在前面

因为时间原因本文有些图片自己没有画,来自网络的图片我尽量注出原链接,但是有的链接已经记不得了,如果有使用到您的图片,请联系我,必注释。

自编码器及其变形很多,本篇博客目前主要基于普通自编码器、栈式自编码器、欠完备自编码器、稀疏自编码器和去噪自编码器,会提供理论+实践(有的理论本人没有完全理解,就先没有写上,后更)。另外,关于收缩自编码器、变分自编码器、CNN自编码器、RNN自编码器及其自编码器的应用,后更。

本文展示的所有完整代码详见:完整代码
(目前只有Keras版本,有时间会写Tensorflow版本)

文章较长,PDF版点击链接:PDF版

一、自编码器(Autoencoder, AE)

1、自编码器的结构和思想

自编码器是一种无监督的数据维度压缩和数据特征表达方法。
自编码器是神经网络的一种,经过训练后能尝试将输入复制到输出。自编码器由编码器和解码器组成,如下图所示(图片来源:深度学习浅层理解(三)— 常用模型之自编码器):

h = f ( x ) h=f(x) h=f(x)表示编码器, r = g ( h ) = g ( f ( x ) ) r=g(h)=g(f(x)) r=g(h)=g(f(x))表示解码器,自编码的目标便是优化损失函数 L ( x , g ( f ( x ) ) L(x,g(f(x)) L(x,g(f(x)),也就是减小图中的Error。

2、自编码器和前馈神经网络的比较

二者的区别和联系如下:

(1)自编码器是前馈神经网络的一种,最开始主要用于数据的降维以及特征的抽取,随着技术的不断发展,现在也被用于生成模型中,可用来生成图片等。
(2)前馈神经网络是有监督学习,其需要大量的标注数据。自编码器是无监督学习,数据不需要标注因此较容易收集。
(3)前馈神经网络在训练时主要关注的是输出层的数据以及错误率,而自编码的应用可能更多的关注中间隐层的结果。

3、自编码器和受限玻尔兹曼机的比较

关于受限玻尔兹曼机的博客请参见:[受限玻尔兹曼机] 原理、求解、深度信念网络。
二者的区别和联系如下:

(1)自编码器和受限自编码器的相同点如下:①都起到了降维的作用;②都可以用来对神经网络进行预训练;③训练都是无监督的。
(2)自编码器和受限自编码器的不同点如下:①自编码器希望通过非线性变换找到输入数据的特征表示,其重构的是输入分布与reconstruct分布的KL距离,它是某种确定论性的模型;而RBM则是围绕概率分布进行的,它通过输入数据的概率分布来提取中间层表示,它是某种概率论性的模型。②AE使用的是BP算法进行优化,而RBM是基于概率模型,使用CD算法进行优化。

4、普通自编码器存在的问题

在普通的自编码器中,输入和输出是完全相同的,因此输出对我们来说没有什么应用价值,所以我们希望利用中间隐层的结果,比如,可以将其作为特征提取的结果、利用中间隐层获取最有用的特性等。
但是如果只使用普通的自编码器会面临什么问题呢?比如,输入层和输出层的维度都是5,中间隐层的维度也是5,那么我们使用相同的输入和输出来不断优化隐层参数,最终得到的参数可能是这样: x 1 − > a 1 , x 2 − > a 2 , … x1->a1,x2->a2,… x1−>a1,x2−>a2,…的参数为1,其余参数为0,也就是说,中间隐层的参数只是完全将输入记忆下来,并在输出时将其记忆的内容完全输出即可,神经网络在做恒等映射,产生数据过拟合。如下图所示(图片来源:Introduction to autoencoders.):

上图是隐层单元数等于输入维度的情况,当然,如果是隐层单元数大于输入维度,也会发生类似的情况,即当隐层单元数大于等于输入维度时,网络可以采用完全记忆的方式,虽然这种方式在训练时精度很高,但是复制的输出对我们来说毫无意义
因此,我们会给隐层加一些约束,如限制隐藏单元数、添加正则化等,后面后介绍。

5、自编码器实现与结果分析

(1)实现框架: Keras
(2)数据集: Mnist手写数字识别
(3)关键代码:

def train(x_train):"""build autoencoder.:param x_train:  the train data:return: encoder and decoder"""# input placeholderinput_image = Input(shape=(ENCODING_DIM_INPUT, ))# encoding layerhidden_layer = Dense(ENCODING_DIM_OUTPUT, activation='relu')(input_image)# decoding layerdecode_output = Dense(ENCODING_DIM_INPUT, activation='relu')(hidden_layer)# build autoencoder, encoder, decoderautoencoder = Model(inputs=input_image, outputs=decode_output)encoder = Model(inputs=input_image, outputs=hidden_layer)# compile autoencoderautoencoder.compile(optimizer='adam', loss='mse')# trainingautoencoder.fit(x_train, x_train, epochs=EPOCHS, batch_size=BATCH_SIZE, shuffle=True)return encoder, autoencoder

(4)代码分析:
Keras封装的比较厉害,所以傻瓜式编程,这里是最简单的自编码器,其输入维度是28*28=784,中间单隐层的维度是2,使用的激活函数是Relu,返回encoder和autoencoder。encoder部分可以用于降维后的可视化,或者降维之后接分类等,autoencoder可以用来生成图片等(这部分代码git上都有)。结构见图如下:

(5)结果展示:

(i)Eencoder结果的可视化如图:

上图中不同表示表示不同的数字,由图可知,自编码器降维之后的结果并不能很好地表示10个数字。

(ii)autoencoder还原之后的图片和原图片对比如下:

上图说明,autoencoder的生成结果不是很清晰。

二、栈式自编码器(Stack Autoencoder)

1、栈式自编码器思想

栈式自编码器又称为深度自编码器,其训练过程和深度神经网络有所区别,下面是基于栈式自编码器的分类问题的训练过程(图片来自台大李宏毅老师的PPT):

即过程如下:
首先,训练784->1000->784的自编码器,而后已经固定已经训练好的参数和1000维的结果,训练第二个自编码器:1000->1000->1000,而后固定已经训练好的参数和训练的中间层结果,训练第三个自编码器:1000->500->1000,固定参数和中间隐层的结果。此时,前3层的参数已经训练完毕,此时,最后一层接一个分类器,将整体网络使用反向传播进行训练,对参数进行微调。这便是使用栈式自编码器进行分类的整体过程。
注:encoder和decoder的参数可以是对称的,也可以是非对称的。

2、栈式自编码器的特点

①增加隐层可以学到更复杂的编码,每一层可以学习到不同的信息维度。
②若层数太深,encoder过于强大,可以将学习将输入映射为任意数(然后decoder学习其逆映射)。这一编码器可以很好的重建数据,但它并没有在这一过程中学到有用的数据表示。

3、栈式自编码器和深度信念网络的异同点

①自编码器栈式自编码器;受限玻尔兹曼机深度信念网络
②栈式自编码器和深度信念网络都是逐层训练。
③栈式自编码器和深度信念网络的训练方法不同,栈式自编码器使用BP算法训练参数,深度信念网络使用的是对比散度算法。

4、栈式自编码器实现与结果分析

(1)实现框架: Keras
(2)数据集: Mnist手写数字识别
(3)关键代码:

def train(x_train):# input placeholderinput_image = Input(shape=(ENCODING_DIM_INPUT, ))# encoding layerencode_layer1 = Dense(ENCODING_DIM_LAYER1, activation='relu')(input_image)encode_layer2 = Dense(ENCODING_DIM_LAYER2, activation='relu')(encode_layer1)encode_layer3 = Dense(ENCODING_DIM_LAYER3, activation='relu')(encode_layer2)encode_output = Dense(ENCODING_DIM_OUTPUT)(encode_layer3)# decoding layerdecode_layer1 = Dense(ENCODING_DIM_LAYER3, activation='relu')(encode_output)decode_layer2 = Dense(ENCODING_DIM_LAYER2, activation='relu')(decode_layer1)decode_layer3 = Dense(ENCODING_DIM_LAYER1, activation='relu')(decode_layer2)decode_output = Dense(ENCODING_DIM_INPUT, activation='tanh')(decode_layer3)# build autoencoder, encoderautoencoder = Model(inputs=input_image, outputs=decode_output)encoder = Model(inputs=input_image, outputs=encode_output)# compile autoencoderautoencoder.compile(optimizer='adam', loss='mse')# trainingautoencoder.fit(x_train, x_train, epochs=EPOCHS, batch_size=BATCH_SIZE, shuffle=True)return encoder, autoencoder

栈式自编码器相当于深度网络的过程,主要注意维度对应即可,另外,这里设置的encoder和decoder的维度是对称的。其架构图如下:

(4)结果展示:

(i)Eencoder结果的可视化如图:

上图中不同表示表示不同的数字,由图可知,栈式自编码器的效果相比较普通自编码器好很多,这里基本能将10个分类全部分开。

(ii)autoencoder还原之后的图片和原图片对比如下:

三、欠完备自编码器(Undercomplete Autoencoder)

1、欠完备自编码器的思想

由上述自编码器的原理可知,当隐层单元数大于等于输入维度时,网络会发生完全记忆的情况,为了避免这种情况,我们限制隐层的维度一定要比输入维度小,这就是欠完备自编码器,如下图所示(图片来源:Introduction to autoencoders.)。 学习欠完备的表示将强制自编码器捕捉训练数据中最显著的特征。

2、欠完备自编码器和主成分分析(PCA)的比较

实际上,若同时满足下列条件,欠完备自编码器的网络等同于PCA,其会学习出于PCA相同的生成子空间:

  • 每两层之间的变换均为线性变换。
  • 目标函数 L ( x , g ( f ( x ) ) L(x,g(f(x)) L(x,g(f(x))为均方误差。

因此,拥有非线性编码器函数 f f f和非线性解码器函数 g g g的自编码器能够学习出比PCA更强大的知识,其是PCA的非线性推广。下图是在二维空间中PCA算法和自编码器同时作用在二维点上做映射的结果(图片来源:Introduction to autoencoders.) ,从图中可以看出,自编码器具有更好的表达能力,其可以映射到非线性函数。

3、欠完备自编码器特点

  • 防止过拟合,并且因为隐层编码维数小于输入维数,可以学习数据分布中最显著的特征。
  • 若中间隐层单元数特别少,则其表达信息有限,会导致重构过程比较困难。

四、稀疏自编码器(Sparse Autoencoder)

1、稀疏自编码器思想

稀疏自编码器是加入正则化的自编码器,其未限制网络接收数据的能力,即不限制隐藏层的单元数。
所谓稀疏性限制是指:

若激活函数是sigmoid,则当神经元的输出接近于1的时候认为神经元被激活,输出接近于0的时候认为神经元被抑制。使得大部分神经元别抑制的限制叫做稀疏性限制。若激活函数是tanh,则当神经元的输出接近于-1的时候认为神经元是被抑制的。

如上图所示(图片来源:Introduction to autoencoders. ),浅色的神经元表示被抑制的神经元,深色的神经元表示被激活的神经元。通过稀疏自编码器,我们没有限制隐藏层的单元数,但是防止了网络过度记忆的情况。
稀疏自编码器损失函数的基本表示形式如下:
L s p a r s e ( x , g ( f ( x ) ) ) = L ( x , g ( f ( x ) ) ) + Ω ( h ) L_{sparse}(x, g(f(x)))=L(x, g(f(x))) + \Omega (h) Lsparse​(x,g(f(x)))=L(x,g(f(x)))+Ω(h)
其中 g ( h ) g(h) g(h)是解码器的输出,通常 h h h是编码器的输出,即 h = f ( x ) h=f(x) h=f(x)

2、损失函数和BP函数推导

损失函数可以加入L1正则化,也可以加入KL散度,下面是对加入KL散度的损失函数的分析。
损失函数的分析如下:
假设 a j ( 2 ) ( x ) a_j^{(2)}(x) aj(2)​(x)表示在给定输入 x x x的情况下,自编码网络隐层神经元 j j j的激活度,则神经元在所有训练样本上的平均激活度为:
ρ j ^ = 1 m ∑ i = 1 m [ a j ( 2 ) ( x i ) ] \hat{\rho_j}=\frac{1}{m}\sum_{i=1}^{m}[a_j^{(2)}(x^{i})] ρj​^​=m1​i=1∑m​[aj(2)​(xi)]
其中, a j ( 2 ) = f ( w j ( 1 ) x ( i ) + b j ( 1 ) ) a_j^{(2)}=f(w_j^{(1)}x^{(i)}+b_j^{(1)}) aj(2)​=f(wj(1)​x(i)+bj(1)​),我们的目的使得网络激活神经元稀疏,所以可以引入一个稀疏性参数 ρ \rho ρ,通常 ρ \rho ρ是一个接近于0的值(即表示隐藏神经元中激活神经元的占比)。则若可以使得 ρ j ^ = ρ \hat{\rho_j}=\rho ρj​^​=ρ,则神经元在所有训练样本上的平均激活度 ρ j ^ \hat{\rho_j} ρj​^​便是稀疏的,这就是我们的目标。为了使得 ρ j ^ = ρ \hat{\rho_j}=\rho ρj​^​=ρ,我们使用KL散度衡量二者的距离,两者相差越大,KL散度的值越大,KL散度的公式如下:
∑ j = 1 s 2 K L ( ρ ∣ ∣ ρ j ^ ) = ∑ j = 1 s 2 [ ρ log ⁡ ρ ρ j ^ + ( 1 − ρ ) log ⁡ 1 − ρ 1 − ρ j ^ ] \sum_{j=1}^{s_2}KL(\rho||\hat{\rho_j})=\sum_{j=1}^{s_2} [\rho \log \frac{\rho}{\hat{\rho_j}} + (1-\rho) \log \frac{1-\rho}{1-\hat{\rho_j}}] j=1∑s2​​KL(ρ∣∣ρj​^​)=j=1∑s2​​[ρlogρj​^​ρ​+(1−ρ)log1−ρj​^​1−ρ​]
ρ \rho ρ表示平均激活度的目标值。因此损失函数如下:
J s p a r s e ( W , b ) = J ( W , b ) + β ∑ j = 1 s 2 K L ( ρ ∣ ∣ ρ j ^ ) J_{sparse}(W,b)=J(W,b)+\beta \sum_{j=1}^{s_2}KL(\rho||\hat{\rho_j}) Jsparse​(W,b)=J(W,b)+βj=1∑s2​​KL(ρ∣∣ρj​^​)
其中 J ( W , b ) J(W, b) J(W,b)便是NN网络中的普通的代价函数,可以使用均方误差等。

反向传播的分析如下:
上式代价函数,左部分就是之前BP的结果,结果如下:
∂ J ( W , b ) ∂ z i ( 2 ) = ∑ j = 1 s 2 W j i ( 2 ) ∂ J ( W , b ) ∂ z i ( 3 ) f ′ ( z i ( 2 ) ) \frac{\partial J(W,b)}{ \partial z_i^{(2)}} = \sum_{j=1}^{s_2}W_{ji}^{(2)}\frac{\partial J(W,b)}{ \partial z_i^{(3)}}{f}'(z_i^{(2)}) ∂zi(2)​∂J(W,b)​=j=1∑s2​​Wji(2)​∂zi(3)​∂J(W,b)​f′(zi(2)​)
可参考反向传导算法。

右部分的求导如下:

∂ ∑ j = 1 s 2 K L ( ρ ∣ ∣ ρ j ^ ) ∂ z i ( 2 ) = ∂ K L ( ρ ∣ ∣ ρ i ^ ) ∂ z i ( 2 ) = ∂ K L ( ρ ∣ ∣ ρ i ^ ) ∂ ρ i ^ ⋅ ∂ ρ i ^ ∂ z i ( 2 ) = ∂ ( ρ log ⁡ ρ ρ i ^ + ( 1 − ρ ) log ⁡ 1 − ρ 1 − ρ i ^ ) ∂ ρ i ^ ⋅ ∂ ρ i ^ ∂ z i ( 2 ) = ( − ρ ρ i ^ + 1 − ρ 1 − ρ i ^ ) ⋅ f ′ ( z i ( 2 ) ) \begin{aligned} \frac{\partial \sum_{j=1}^{s_2}KL(\rho||\hat{\rho_j})}{\partial z_i^{(2)}} &= \frac{\partial KL(\rho||\hat{\rho_i})}{\partial z_i^{(2)}} \newline \\ &= \frac{\partial KL(\rho||\hat{\rho_i})}{\partial \hat{\rho_i}}\cdot \frac{\partial \hat{\rho_i}}{\partial z_i^{(2)}} \\ &= \frac{\partial(\rho \log \frac{\rho}{\hat{\rho_i}}+(1-\rho) \log \frac{1-\rho}{1-\hat{\rho_i}})}{\partial\hat{\rho_i}}\cdot \frac{\partial \hat{\rho_i}}{\partial z_i^{(2)}} \\ &= (-\frac{\rho}{\hat{\rho_i}} + \frac{1-\rho}{1-\hat{\rho_i}}) \cdot {f}'(z_i^{(2)}) \end{aligned} ∂zi(2)​∂∑j=1s2​​KL(ρ∣∣ρj​^​)​​=∂zi(2)​∂KL(ρ∣∣ρi​^​)​=∂ρi​^​∂KL(ρ∣∣ρi​^​)​⋅∂zi(2)​∂ρi​^​​=∂ρi​^​∂(ρlogρi​^​ρ​+(1−ρ)log1−ρi​^​1−ρ​)​⋅∂zi(2)​∂ρi​^​​=(−ρi​^​ρ​+1−ρi​^​1−ρ​)⋅f′(zi(2)​)​
因此 J s p a r s e ( W , b ) J_{sparse}(W,b) Jsparse​(W,b)的求导结果如下:
∂ J s p a r s e ( W , b ) ∂ z i ( 2 ) = ( ( ∑ j = 1 s 2 W j i ( 2 ) ∂ J ( W , b ) ∂ z i ( 3 ) ) + β ( − ρ ρ i ^ + 1 − ρ 1 − ρ i ^ ) ) f ′ ( z i ( 2 ) ) \frac{\partial J_{sparse}(W,b)}{ \partial z_i^{(2)}}= ((\sum_{j=1}^{s_2}W_{ji}^{(2)}\frac{\partial J(W,b)}{ \partial z_i^{(3)}})+ \beta(-\frac{\rho}{\hat{\rho_i}} + \frac{1-\rho}{1-\hat{\rho_i}})){f}'(z_i^{(2)}) ∂zi(2)​∂Jsparse​(W,b)​=((j=1∑s2​​Wji(2)​∂zi(3)​∂J(W,b)​)+β(−ρi​^​ρ​+1−ρi​^​1−ρ​))f′(zi(2)​)
此即反向传播的方程,根据此方程可以参数 W W W和 b b b进行更新。

3、Mini-Batch的情况

平均激活度是根据所有样本计算出来的,所以在计算任何单元的平均激活度之前,需要对所有样本计算一下正向传播,从而获得平均激活度,所以使用小批量时计算效率很低。要解决这个问题,可采取的方法是只计算Mini-Batch中包含的训练样本的平均激活度,然后在Mini-Batch之间计算加权值
ρ ^ j t = λ ρ ^ j t − 1 + ( 1 − λ ) ρ ^ j t \hat{\rho}_j^t=\lambda\hat{\rho}_j^{t-1}+(1-\lambda)\hat{\rho}_j^t ρ^​jt​=λρ^​jt−1​+(1−λ)ρ^​jt​
其中, ρ ^ j t \hat{\rho}_j^t ρ^​jt​是时刻 t t t的Mini-Batch的平均激活度, ρ ^ j t − 1 \hat{\rho}_j^{t-1} ρ^​jt−1​是时刻t-1的Mini-Batch的平均激活度。若\lambda大,则时刻 t − 1 t-1 t−1的Mini-Batch的平均激活度所占比重大,否则,时刻t的Mini-Batch的平均激活度所占比重大。

4、稀疏限制和L1/L2正则化的关系

①稀疏限制是对激活函数的结果增加限制,使得尽量多的激活函数的结果为0(如果激活函数是tanh,则为-1)
②L2/L1是对参数增加限制,使得尽可能多的参数为0。
若自编码器编码函数 f ( w x + b ) f(wx+b) f(wx+b),若 f f f是一个线性函数,则编码器便可以写成 w x + b wx+b wx+b,限制激活函数的结果尽量为0,即是限制 w w w尽量为0,此时稀疏限制和正则化限制相同。

5、稀疏自编码器在分类中的应用

稀疏自编码器一般用来学习特征,以便用于像分类这样的任务。如下图(图片来源:为什么稀疏自编码器很少见到多层的?):

上述过程不是一次训练的,可以看到上面只有编码器没有解码器,因此其训练过程是自编码器先使用数据训练参数,然后保留编码器,将解码器删除并在后面接一个分类器,并使用损失函数来训练参数已达到最后效果。

5、稀疏编码器实现与结果分析

(1)框架: Keras
(2)数据集: Mnist手写数字识别
(3)关键代码:

def train(x_train):# input placeholderinput_image = Input(shape=(ENCODING_DIM_INPUT, ))# encoding layer# *****!!! this code is changed compared with Autoencoder, adding the activity_regularizer to make the input sparse.encode_layer1 = Dense(ENCODING_DIM_LAYER1, activation='relu', activity_regularizer=regularizers.l1(10e-6))(input_image)# *******************************************************encode_layer2 = Dense(ENCODING_DIM_LAYER2, activation='relu')(encode_layer1)encode_layer3 = Dense(ENCODING_DIM_LAYER3, activation='relu')(encode_layer2)encode_output = Dense(ENCODING_DIM_OUTPUT)(encode_layer3)# decoding layerdecode_layer1 = Dense(ENCODING_DIM_LAYER3, activation='relu')(encode_output)decode_layer2 = Dense(ENCODING_DIM_LAYER2, activation='relu')(decode_layer1)decode_layer3 = Dense(ENCODING_DIM_LAYER1, activation='relu')(decode_layer2)decode_output = Dense(ENCODING_DIM_INPUT, activation='tanh')(decode_layer3)# build autoencoder, encoderautoencoder = Model(inputs=input_image, outputs=decode_output)encoder = Model(inputs=input_image, outputs=encode_output)# compile autoencoderautoencoder.compile(optimizer='adam', loss='mse')# trainingautoencoder.fit(x_train, x_train, epochs=EPOCHS, batch_size=BATCH_SIZE, shuffle=True)return encoder, autoencoder

这里是以多层的自编码器举例,单隐层的同样适用,主要是在第一层加一个正则化项,activity_regularizer=regularizers.l1(10e-6)说明加入的是L1正则化项,10e-6是正则化项系数,完整代码可参见最开始的git。其架构如下:

(4)结果展示:
(i)Encoder结果的可视化如图:

上图中不同颜色表示不同的数字,由图可知,这个编码器的分类效果还可以,比自编码器好很多,但是看起来还是作用不大,因为大部分作用需要归功于栈式自编码器。

(ii)autoencoder还原之后的图片和原图片对比如下:

五、去噪自编码器(Denoising Autoencoder)

1、去噪自编码器思想

去噪自编码器是一类接受损失数据作为输入,并训练来预测原始未被损坏的数据作为输出的自编码器。 如下图所示(图片来自花书):

其训练过程如下:
引入一个损坏过程 C ( x ~ ∣ x ) C(\tilde{x}|x) C(x~∣x),这个条件分布代表给定数据样本 x x x产生损坏样本 x ~ \tilde{x} x~的概率。自编码器学习重构分布 p r e c o n s t r u c t ( x ∣ x ~ ) p_{reconstruct}(x|\tilde{x}) preconstruct​(x∣x~):

  • 从训练数据中采一个训练样本 x x x
  • 从 C ( x ~ ∣ X = x ) C(\tilde{x}|X=x) C(x~∣X=x)采一个损坏样本 x ~ \tilde{x} x~
  • 将 ( x ~ , x ) (\tilde{x}, x) (x~,x)作为训练样本来估计自编码器的重构分布 p r e c o n s t r u c t ( x ∣ x ~ ) = p d e c o d e r ( x ∣ h ) p_{reconstruct}(x|\tilde{x})=p_{decoder}(x|h) preconstruct​(x∣x~)=pdecoder​(x∣h),其中 h h h是编码器 f ( x ~ ) f(\tilde{x}) f(x~)的输出, p d e c o d e r p_{decoder} pdecoder​根据解码函数 g ( h ) g(h) g(h)定义。

去噪自编码器中作者给出的直观解释是:和人体感官系统类似,比如人的眼睛看物体时,如果物体的某一小部分被遮住了,人依然能够将其识别出来,所以去噪自编码器就是破坏输入后,使得算法学习到的参数仍然可以还原图片。
注: 噪声可以是添加到输入的纯高斯噪声,也可以是随机丢弃输入层的某个特性。

2、去噪自编码器和Dropout

噪声可以是添加到输入的纯高斯噪声,也可以是随机丢弃输入层的某个特性,即如果 C ( x ~ ∣ X = x ) C(\tilde{x}|X=x) C(x~∣X=x)是一个二项分布,则其表现为下图所示内容,即经过处理的 x x x的结果是保留或者舍掉,也就是说, C ( x ~ ∣ X = x ) C(\tilde{x}|X=x) C(x~∣X=x)会舍去一部分内容,保留一部分内容:

这个思想类似于Dropout,但是二者还有一些区别:

(1) 去噪自编码器操作的是输入数据,相当于对输入数据去掉一部分内容;而Dropout操作的是网络隐藏层,相当于去掉隐藏层的一部分单元。
(2) Dropout在分层预训练权值的过程中是不参与的,只是后面的微调部分会加入;而去噪自编码器是在每层预训练的过程中作为输入层被引入,在进行微调时不参与

3、去噪自编码器和PCA

去噪自编码器来源于论文[Vincent P, Larochelle H, Bengio Y, et al. Extracting and composing robust features with denoising autoencoders[C]//Proceedings of the 25th international conference on Machine learning. ACM, 2008: 1096-1103.]

上图是去噪自编码器从流形角度的原理,图中黑色的曲线表示原始的局部流型,我们通过 C ( x ~ ∣ X = x ) C(\tilde{x}|X=x) C(x~∣X=x)将其映射到一个圆的某一点, x ~ \tilde{x} x~表示添加噪声之后数据点。我们的目标是使得添加噪声之后的点能够映射到原始点,这样损失值为最小,即:图中红色箭头是噪声添加的向量场,而紫色部分是重构过程中需要不断查找的向量场。
因此,可以理解为,去噪自编码器的局部就是简化的PCA原理,其是对PCA的非线性扩展。
除此之外,文章还从信息论文、随机算子等角度理论上证明了去噪自编码器的可行性,有兴趣的读者可以参考上面提到的论文。

4、去噪自编码器效果

论文中的实验基于Minst数据集,其实验结果如下:

其中,第一个和第二个baseline是使用高斯核和多项式核的SVM,DBN-1是1层隐层单元的深度信念网络,DBN-3是3层隐层单元的深度信念网络,SAA-3是使用栈式自编码器初始化之后的3层深度网络,最后的Sda-3是3层的栈式去噪自编码器。从表中可以看出,使用去噪自编码器的结果优于其他网络。
除此之外,网站Denoising Autoencoders (dA)有去噪自编码器的代码和实验,其去噪前和去噪后过滤器的对比结果如下:

左图是去噪之前的过滤器数据,右图是去噪之后的过滤器数据,从图中可以看出,去噪自编码器学习到的特征更具代表性。。

5、去噪自编码器特点

  • 普通的自编码器的本质是学一个相等函数,即输入和输出是同一个内容,这种相等函数的缺点便是当测试样本和训练样本不符合同一个分布时,在测试集上效果不好,而去噪自编码器可以很好地解决这个问题。
  • 欠完备自编码器限制学习容量,而去噪自编码器允许学习容量很高,同时防止在编码器和解码器学习一个无用的恒等函数。
  • 经过了加入噪声并进行降噪的训练过程,能够强迫网络学习到更加鲁棒的不变性特征,获得输入的更有效的表达。

6、去噪编码器实现与结果分析

(1)框架: Keras
(2)数据集: Mnist手写数字识别
(3)关键代码:

def addNoise(x_train, x_test):"""add noise.:return:"""x_train_noisy = x_train + NOISE_FACTOR * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)x_test_noisy = x_test + NOISE_FACTOR * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)x_train_noisy = np.clip(x_train_noisy, 0., 1.)     # limit into [0, 1]x_test_noisy = np.clip(x_test_noisy, 0., 1.)   # limit into [0, 1]return x_train_noisy, x_test_noisy

去噪自编码器主要是对输入添加噪声,所以训练过程是不需要改变的,只需要改变输入和输出。上述便是对输入添加噪声的过程,NOISE_FACTOR * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)便是添加的噪声。 np.clip()是截取函数,将数值限制在0~1之间。其架构如下:

(4)结果展示:
(i)Eencoder结果的可视化如图:

上图中不同表示表示不同的数字,这里不是很直观,看下面的图片对比

(ii)autoencoder还原之后的图片和原图片对比如下:

上图是添加噪声的效果对比,第一行表示原数据,第二行表示噪声处理过后的数据。

上图根据噪声数据还原图片的对比,第一行表示噪声处理过后的数据,第二行表示去噪自编码器decoder还原之后的结果,上图可以看出,去噪自编码器的效果还是可以的。

六、卷积自编码器(convolutional Autoencoder)

1、卷积自编码器思想

卷积自编码器和普通自编码器的区别在于其encoder和decoder都是卷积神经网络,相应的,encoder使用的是卷积操作和池化操作,而decoder中使用的反卷积操作和反卷积操作,关于卷积、反卷积、池化和反池化的内容详见:[CNN] 卷积、反卷积、池化、反池化。

2、去噪编码器实现与结果分析

(1)框架: Keras
(2)数据集: Mnist手写数字识别
(3)关键代码:

def train(x_train):"""build autoencoder.:param x_train:  the train data:return: encoder and decoder"""# input placeholderinput_image = Input(shape=(28, 28, 1))# encoding layerx = Conv2D(CHANNEL_1, (3, 3), activation='relu', padding="same")(input_image)x = MaxPool2D((2, 2), padding='same')(x)x = Conv2D(CHANNEL_2, (3, 3), activation='relu', padding='same')(x)encoded = MaxPool2D((2, 2), padding='same')(x)# decoding layerx = Conv2D(CHANNEL_2, (3, 3), activation='relu', padding='same')(encoded)x = UpSampling2D((2, 2))(x)x = Conv2D(CHANNEL_1, (3, 3),activation='relu', padding='same')(x)x = UpSampling2D((2, 2))(x)decoded = Conv2D(CHANNEL_OUTPUT, (3, 3), activation='sigmoid', padding='same')(x)# build autoencoder, encoder, decoderautoencoder = Model(inputs=input_image, outputs=decoded)encoder = Model(inputs=input_image, outputs=encoded)# compile autoencoderautoencoder.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])# training# need return history, otherwise can not use history["acc"]history_record = autoencoder.fit(x_train, x_train, epochs=EPOCHS, batch_size=BATCH_SIZE, shuffle=True, )return encoder, autoencoder, history_record

注意:

i)在Keras编码中,反卷积的实现代码便是卷积操作,具体解释详见上述博客。
ii) UpSampling2D()实现的是反平均卷积的操作。

autoencoder.summary()如下:

代码架构图如下:

(4)结果展示:
(i)autoencoder还原之后的图片和原图片对比如下:

上图根据原图片和生成图片的对比,第一行表示原图片,第二行表示卷积自编码器decoder还原之后的结果,上图可以看出,效果还是不错的。

(ii)loss变化图:

由上图可以看出,虽然loss在不断降低,但是accuracy还不是非常高,一方面是和参数相关,因为性能原因,在此文章版本上对epochs设置为20,另外,网络的深度也不够,也没有加入一些其他的提高性能的小技巧。

其他参考文章:
[1] Kandeng. 自编码算法与稀疏性[EB/OL]. (2018/11/24)[ 2018/11/24] http://ufldl.stanford.edu/wiki/index.php/自编码算法与稀疏性
[2] Goodfellow I, Bengio Y, Courville A, et al. Deep learning[M]. Cambridge: MIT press, 2016.
[3] 山下隆义. 图解深度学习[M]. 人民邮电出版社,2018:68-78
[4] Francois Chollet. Building Autoencoders in Keras[EB/OL]. (2018/11/24)[ 2018/11/25] https://blog.keras.io/building-autoencoders-in-keras.html

[自编码器:理论+代码]:自编码器、栈式自编码器、欠完备自编码器、稀疏自编码器、去噪自编码器、卷积自编码器相关推荐

  1. 花书+吴恩达深度学习(二二)自编码器(欠完备,DAE,CAE,PSD)

    文章目录 0. 前言 1. 欠完备自编码器 2. 去噪自编码器 DAE 3. 收缩自编码器 CAE 4. 稀疏自编码器 5. 预测稀疏分解 PSD 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞 ...

  2. PyTorch 实现 VAE 变分自编码器 含代码

    编码器 自编码器 自编码器网络结构图 线性自编码器代码如下: 卷积自编码器代码如下: 变分自编码器 变分自编码器网络结构图 变分自编码器代码如下: Ref 自编码器 自编码器网络结构图 线性自编码器代 ...

  3. 旋钮编码器c代码_人脸合成效果媲美StyleGAN,而它是个自编码器

    机器之心报道 参与:魔王 自编码器(AE)与生成对抗网络(GAN)是复杂分布上无监督学习最具前景的两类方法,它们也经常被拿来比较.人们通常认为自编码器在图像生成上的应用范围比 GAN 窄,那么自编码器 ...

  4. 旋转编码器详解(主要讨论增量式编码器与绝对式编码器)

    参考资料:旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别? 0. 前言 我们在选电机时,非常注重电机的扭矩和尺寸,因为这直接决定了电机是否能按规定的运动模式拖动负载,能不能很好地布置在有 ...

  5. 一个很简单的基于栈式过程虚拟机的实现,它运行目标平台【x86】的原生代码。

    本文提供的 "栈式过程虚拟机" 的实现,挂在本人的 github 上面,对想要深入了解 "栈式过程虚拟机" 的人,它或许可以起到一个不错的作用,但是本人建议一般 ...

  6. 编码器杂谈 —— 关于绝对值与绝对式编码器的混淆

    绝对值编码器的"绝对"是指数据的唯一.可靠.稳定,而不是停电记忆 内容 博主后记 内容 针对目前市场上鱼目混珠的绝对值编码器产品,对于编码器 "绝对值" 的概念 ...

  7. 干货|利用卷积自编码器对图片进行降噪

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 前言: 这周工作太忙,本来想更把Attention tranlsa ...

  8. DDos攻击,使用深度学习中 栈式自编码的算法

    转自:http://www.airghc.top/2016/11/10/Dection-DDos/ 最近研究了一篇论文,关于检测DDos攻击,使用了深度学习中 栈式自编码的算法,现在简要介绍一下内容 ...

  9. Stanford UFLDL教程 栈式自编码算法

    栈式自编码算法 Contents [hide] 1概述 2训练 3具体实例 4讨论 5中英文对照 6中文译者 概述 逐层贪婪训练法依次训练网络的每一层,进而预训练整个深度神经网络.在本节中,我们将会学 ...

最新文章

  1. XML解析简介及Xerces-C++简单使用举例
  2. 总结机器学习优质学习文章Top50!
  3. 关于因为该列没有包含在聚合函数或 GROUP BY 子句中
  4. [思考]-32位的应用程序为什么不能跑64位的应用程序
  5. 数据包过滤 系统 linux,Ubuntu16.04安装libpcap开发库对pcap文件中的数据包进行过滤...
  6. Angular之RouterModule的forRoot与forChild
  7. org.activiti.engine.ActivitiOptimisticLockingException updated by another transaction concurrently
  8. error: [FabricCAClientService.js]: Failed to enroll admin, error:%o message=Calling enroll endpoint
  9. Dijkstra算法(求一点到任意一点的最短距离)
  10. python3入门代码-python3爬虫入门程序
  11. 《道德经》与 (更新中)
  12. 如何使用计算机管理员账户密码是什么意思,电脑管理员密码是什么
  13. RTMP、RTSP、HTTP协议流常用直播流地址
  14. adb 查看手机的ip地址
  15. centos7安装otrs
  16. 十六进制 转 八进制
  17. 【ELM分类】基于matlab遗传算法优化ELM神经网络数据分类【含Matlab源码 2138期】
  18. 外包程序员面试遭HR鄙视,称:外包就是程序员的“职业污点”?
  19. php翻译成中文版,php 翻译函数,可以将中文汉字转换为E文
  20. 假设银行一年整存零取的月息为1.875%(年息为12*1.875%,年息按复利计算),现在某人手头有一笔钱,他打算在今后5年中,每年年底取出1000元作为孩子来年的教育金,到第5年孩子毕业时刚好取完这

热门文章

  1. TensorFlow学习笔记1:graph、session和op
  2. 分类常用的神经网络模型,典型神经网络模型包括
  3. 【Win10 访问 Linux samba 拒绝访问】
  4. JAVA的简单数据类型和复合数据类型
  5. unity3d是干啥的
  6. 如何进行滤波器设计软件选择
  7. Proximal Point Algorithm(PPA)
  8. uva12325(分类枚举)
  9. DCOS到底是啥?看完这篇你就懂了~
  10. RK 7.1 OTA升级提示Not enough free space on /cache to apply patches