精华内容
下载资源
问答
  • 自编码器及其变形很多,本篇博客目前主要基于普通自编码器、欠完备自编码器、稀疏自编码器和去噪自编码器,会提供理论+实践(有的理论本人没有完全理解,就先没有写上,后更)。另外,关于收缩自编码器、变分自编码...

    写在前面

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

    自编码器及其变形很多,本篇博客目前主要基于普通自编码器、栈式自编码器、欠完备自编码器、稀疏自编码器和去噪自编码器,会提供理论+实践(有的理论本人没有完全理解,就先没有写上,后更)。另外,关于收缩自编码器、变分自编码器、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>a1x2>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 placeholder
        input_image = Input(shape=(ENCODING_DIM_INPUT, ))
    
        # encoding layer
        hidden_layer = Dense(ENCODING_DIM_OUTPUT, activation='relu')(input_image)
        # decoding layer
        decode_output = Dense(ENCODING_DIM_INPUT, activation='relu')(hidden_layer)
    
        # build autoencoder, encoder, decoder
        autoencoder = Model(inputs=input_image, outputs=decode_output)
        encoder = Model(inputs=input_image, outputs=hidden_layer)
    
        # compile autoencoder
        autoencoder.compile(optimizer='adam', loss='mse')
    
        # training
        autoencoder.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 placeholder
        input_image = Input(shape=(ENCODING_DIM_INPUT, ))
    
        # encoding layer
        encode_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 layer
        decode_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, encoder
        autoencoder = Model(inputs=input_image, outputs=decode_output)
        encoder = Model(inputs=input_image, outputs=encode_output)
    
        # compile autoencoder
        autoencoder.compile(optimizer='adam', loss='mse')
    
        # training
        autoencoder.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^=m1i=1m[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=1s2KL(ρρj^)=j=1s2[ρ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=1s2KL(ρρ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=1s2Wji(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=1s2KL(ρρ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=1s2Wji(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=λρ^jt1+(1λ)ρ^jt
    其中, ρ ^ j t \hat{\rho}_j^t ρ^jt是时刻 t t t的Mini-Batch的平均激活度, ρ ^ j t − 1 \hat{\rho}_j^{t-1} ρ^jt1是时刻t-1的Mini-Batch的平均激活度。若\lambda大,则时刻 t − 1 t-1 t1的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 placeholder
        input_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 layer
        decode_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, encoder
        autoencoder = Model(inputs=input_image, outputs=decode_output)
        encoder = Model(inputs=input_image, outputs=encode_output)
    
        # compile autoencoder
        autoencoder.compile(optimizer='adam', loss='mse')
    
        # training
        autoencoder.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(xx~):

    • 从训练数据中采一个训练样本 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(xx~)=pdecoder(xh),其中 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 placeholder
        input_image = Input(shape=(28, 28, 1))
    
        # encoding layer
        x = 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 layer
        x = 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, decoder
        autoencoder = Model(inputs=input_image, outputs=decoded)
        encoder = Model(inputs=input_image, outputs=encoded)
    
        # compile autoencoder
        autoencoder.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

    展开全文
  • 自从Hinton 2006年的工作之后,越来越多的研究者开始关注各种自编码器模型相应的堆叠模型。实际上,自编码器(Auto-Encoder)是一个较早的概念了,比如Hinton等人在1986, 1989年的工作。(说来说去都是这些人呐。。...

    自从Hinton 2006年的工作之后,越来越多的研究者开始关注各种自编码器模型相应的堆叠模型。实际上,自编码器(Auto-Encoder)是一个较早的概念了,比如Hinton等人在1986, 1989年的工作。(说来说去都是这些人呐。。。)

    自编码器简介

    先暂且不谈神经网络、深度学习,仅是自编码器的话,其原理很简单。自编码器可以理解为一个试图去还原其原始输入的系统。如下图所示:

    图中,虚线蓝色框内就是一个自编码器模型,它由编码器(Encoder)和解码器(Decoder)两部分组成,本质上都是对输入信号做某种变换。编码器将输入信号x变换成编码信号y,而解码器将编码y转换成输出信号\widetilde{x},即:

    y=f(x)

    \widetilde{x} = g(y) = g(f(x))

    而自编码器的目的是,让输出\widetilde{x}尽可能复现输入x,即tries to copy its input to its output。但是,这样问题就来了——如果f和g都是恒等映射,那不就恒有\widetilde{x}=x了?不错,确实如此,但这样的变换——没有任何卵用啊!因此,我们经常对中间信号y(也叫作“编码”)做一定的约束,这样,系统往往能学出很有趣的编码变换f和编码y。

    这里强调一点,对于自编码器,我们往往并不关系输出是啥(反正只是复现输入),我们真正关心的是中间层的编码,或者说是从输入到编码的映射。可以这么想,在我们强迫编码y和输入x不同的情况下,系统还能够去复原原始信号x,那么说明编码y已经承载了原始数据的所有信息,但以一种不同的形式!这就是特征提取啊,而且是自动学出来的!实际上,自动学习原始数据的特征表达也是神经网络和深度学习的核心目的之一。

    为了更好的理解自编码器,下面结合神经网络加以介绍。

    自编码器与神经网络

    神经网络的知识不再详细介绍,相信了解自编码器的读者或多或少会了解一些。简单来讲,神经网络就是在对原始信号逐层地做非线性变换,如下图所示:

    该网络把输入层数据x∈Rn转换到中间层(隐层)h∈Rp,再转换到输出层y∈Rm。图中的每个节点代表数据的一个维度(偏置项图中未标出)。每两层之间的变换都是“线性变化”+“非线性激活”,用公式表示即为:

    h=f(W(1)x+b(1))
    y=f(W(2)h+b(2)) 

    神经网络往往用于分类,其目的是去逼近从输入层到输出层的变换函数。因此,我们会定义一个目标函数来衡量当前的输出和真实结果的差异,利用该函数去逐步调整(如梯度下降)系统的参数(W(1),b(1),W(2),b(2)),以使得整个网络尽可能去拟合训练数据。如果有正则约束的话,还同时要求模型尽量简单(防止过拟合)。

    那么,自编码器怎么表示呢?前面已说过,自编码器试图复现其原始输入,因此,在训练中,网络中的输出应与输入相同,即y=x,因此,一个自编码器的输入、输出应有相同的结构,即:

    我们利用训练数据训练这个网络,等训练结束后,这个网络即学习出了x→h→x的能力。对我们来说,此时的h是至关重要的,因为它是在尽量不损失信息量的情况下,对原始数据的另一种表达。结合神经网络的惯例,我们再将自编码器的公式表示如下:(假设激活函数是sigmoid,用s表示)

    y = f\theta (x) = s(Wx+b)

    \widetilde{x} = g\theta '(y) = s(W'y+b')

    L(x,\widetilde{x}) = L(x,g(f(x)))

    其中,L表示损失函数,结合数据的不同形式,可以是二次误差(squared error loss)或交叉熵误差(cross entropy loss)。如果W' = W^{T},一般称为tied weights。

    为了尽量学到有意义的表达,我们会给隐层加入一定的约束。从数据维度来看,常见以下两种情况:

    • n>p,即隐层维度小于输入数据维度。也就是说从x→h的变换是一种降维的操作,网络试图以更小的维度去描述原始数据而尽量不损失数据信息。实际上,当每两层之间的变换均为线性,且监督训练的误差是二次型误差时,该网络等价于PCA!没反应过来的童鞋可以反思下PCA是在做什么事情。

     

    • n<p,即隐层维度大于输入数据维度。这又有什么用呢?其实不好说,但比如我们同时约束h的表达尽量稀疏(有大量维度为0,未被激活),此时的编码器便是大名鼎鼎的“稀疏自编码器”。可为什么稀疏的表达就是好的?这就说来话长了,有人试图从人脑机理对比,即人类神经系统在某一刺激下,大部分神经元是被抑制的。个人觉得,从特征的角度来看更直观些,稀疏的表达意味着系统在尝试去特征选择,找出大量维度中真正重要的若干维。

    堆叠自编码器

    有过深度学习基础的童鞋想必了解,深层网络的威力在于其能够逐层地学习原始数据的多种表达。每一层的都以底一层的表达为基础,但往往更抽象,更加适合复杂的分类等任务。

    堆叠自编码器实际上就在做这样的事情,如前所述,单个自编码器通过虚构x→h→x的三层网络,能够学习出一种特征变化h=fθ(x)(这里用θ表示变换的参数,包括W,b和激活函数)。实际上,当训练结束后,输出层已经没什么意义了,我们一般将其去掉,即将自编码器表示为:

    之前之所以将自编码器模型表示为3层的神经网络,那是因为训练的需要,我们将原始数据作为假想的目标输出,以此构建监督误差来训练整个网络。等训练结束后,输出层就可以去掉了,我们关心的只是从x到h的变换。

    接下来的思路就很自然了——我们已经得到特征表达h,那么我们可不可以将h再当做原始信息,训练一个新的自编码器,得到新的特征表达呢?当然可以!这就是所谓的堆叠自编码器(Stacked Auto-Encoder, SAE)。Stacked就是逐层垒叠的意思,跟“栈”有点像。UFLDL教程将其翻译为“栈式自编码”,anyway,不管怎么称呼,都是这个东东,别被花里胡哨的专业术语吓到就行。当把多个自编码器Stack起来之后,这个系统看起来就像这样:

    亦可赛艇!这个系统实际上已经有点深度学习的味道了,即learning multiple levels of representation and abstraction(Hinton, Bengio, LeCun, 2015)。需要注意的是,整个网络的训练不是一蹴而就的,而是逐层进行。按题主提到的结构n,m,k结构,实际上我们是先训练网络n→m→n,得到n→m的变换,然后再训练m→k→m,得到m→k的变换。最终堆叠成SAE,即为n→m→k的结果,整个过程就像一层层往上盖房子,这便是大名鼎鼎的layer-wise unsuperwised pre-training(逐层非监督预训练),正是导致深度学习(神经网络)在2006年第3次兴起的核心技术。

    关于逐层预训练与深度学习,将在本文最后探讨。

    自编码器的变种形式

    上述介绍的自编码器是最基本的形式。善于思考的童鞋可能已经意识到了这个问题:隐层的维度到底怎么确定?为什么稀疏的特征比较好?或者更准确的说,怎么才能称得上是一个好的表达(What defines a good representation)?

    事实上,这个问题回答并不唯一,也正是从不同的角度去思考这个问题,导致了自编码器的各种变种形式出现。目前常见的几种模型总结如下(有些术语实在不好翻译,看英文就好。。。)

    下面简介下其中两种模型,以对这些变种模型有个直观感受。

    稀疏自编码器

    UFLDL-自编码算法与稀疏性对该模型有着比较详细的介绍。如前所示,这种模型背后的思想是,高维而稀疏的表达是好的。一般而言,我们不会指定隐层表达h中哪些节点是被抑制的(对于sigmoid单元即输出为0),而是指定一个稀疏性参数ρ,代表隐藏神经元的平均活跃程度(在训练集上取平均)。比如,当ρ=0.05时,可以认为隐层节点在95%的时间里都是被一直的,只有5%的机会被激活。实际上,为了满足这一条件,隐层神经元的活跃度需要接近于0。

    那么,怎么从数学模型上做到这点呢?思路也不复杂,既然要求平均激活度为ρ,那么只要引入一个度量,来衡量神经元ii的实际激活度\widetilde{\rho _{a}}与期望激活度ρ之间的差异即可,然后将这个度量添加到目标函数作为正则,训练整个网络即可。那么,什么样的度量适合这个任务呢?有过概率论、信息论基础的同学应该很容易想到它——相对熵,也就是KL散度(KL divergence)。因此,整个网络所添加的惩罚项即为:

    \sum_{i\in h}KL(\rho ||\widetilde{\rho _{a}} )

    具体的公式不再展开,可以从下图(摘自UFLDL)中直观理解KL散度作为惩罚项的含义。图中假设平均激活度ρ=0.2。

    可以看出,当^ρiρ^i一旦偏离期望激活度ρρ,这种误差便急剧增大,从而作为惩罚项添加到目标函数,指导整个网络学习出稀疏的特征表达。

    降噪自编码器

    关于降噪自编码器,强烈推荐其作者Pascal Vincent的论文Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion。DAE的核心思想是,一个能够从中恢复出原始信号的表达未必是最好的,能够对“被污染/破坏”的原始数据编码、解码,然后还能恢复真正的原始数据,这样的特征才是好的。

    稍微数学一点,假设原始数据x被我们“故意破坏”,比如加入高斯白噪,或者把某些维度数据抹掉,变成了\overline{x},然后再对\overline{x}编码、解码,得到恢复信号\widetilde{x} = g(f(x)),该恢复信号尽可能逼近未被污染的数据xx。此时,监督训练的误差从L(x,g(f(x)))变成了L(x,g(f(\widetilde{x}))。

    直观上理解,DAE希望学到的特征变换尽可能鲁棒,能够在一定程度上对抗原始数据的污染、缺失。Vincent论文里也对DAE提出了基于流行的解释,并且在图像数据上进行测试,发现DAE能够学出类似Gabor边缘提取的特征变换。注意,这一切都是在我们定义好规则、误差后,系统自动学出来的!从而避免了领域专家费尽心力去设计这些性能良好的特征。

    DAE的系统结构如下图(摘自Vincent论文)所示:

    现在使用比较多的noise主要是mask noise,即原始数据中部分数据缺失,这是有着很强的实际意义的,比如图像部分像素被遮挡、文本因记录原因漏掉了一些单词等等。

    其他的模型就不再展开了,总之,每遇到一个自编码器的一个变种模型时,搞清楚其背后的思想(什么样的表达才是好的),就很容易掌握了。套用V的”Behind this mask is a man, and behind this man is an idea, and ideas are bulletproof”,我们可以说,”Behind this auto-encoder is a model, and behind this model is an idea, and ideas are bulletproof”。

    转自:https://www.zhihu.com/question/41490383/answer/103006793

    展开全文
  • 编码器分频输出

    2018-07-06 17:07:01
    用VHDL语言实现伺服电机编码器分频输出,由发出脉冲数计算模块、分频脉冲发生器模块、发出脉冲计数器模块、正交脉冲输出状态机模块和原点信号输出计数器模块构成
  • ![图片说明]... MATLAB2017a中稀疏自动编码器工具包的案例仅给出编码器部分的结构体和解码部分的输出,没有给出编码部分的输出,求问如何获得隐藏层编码器输出
  • 对抗自编码器指南之一:自编码器

    千次阅读 2017-12-09 19:21:41
    自编码器是一种特殊的神经网络(`neural network`),它的输出目标(`target`)就是输入(所以它基本上就是试图将输出重构为输入),由于它不需要任何人工标注,所以可以采用无监督的方式进行训练。 自编码器包括两...

    如果你知道如何编写Tensorflow代码来对MNIST数字进行分类,那么阅读本文就不会有太多障碍,否则我会强烈建议你先阅读Tensorflow网站上的这篇 文章

    我们不再需要任何重大的新突破就可以获得真正的AI!!!???

    这是非常离谱、极其可笑的错误观点。 正如我之前所说:人类和动物的学习大部分都是无监督的。 如果把智能比做一个蛋糕,那么无监督(unsupervised)学习才是真正的蛋糕,有监督(supervised)学习只是蛋糕上的糖霜,而强化(reinforcement)学习则是蛋糕上那些作为点缀的樱桃。

    现在我们已经知道了如何制作糖霜和樱桃,但是还不知道如何做蛋糕。 在我们可以开始考虑实现真正的AI之前,首先要先解决无监督学习的问题。 而这只是我们实现真正AI的已知的障碍之一。 还有那些我们不知道的事情呢?

    这是AlphaGo胜利后FacebookAI研究主管Yan Lecun的一句话。

    我们知道卷积神经网络(CNN)或者全连接网络(也被称为MLP,即多层感知器)可以用来进行图像识别。 但是,单纯使用CNNMLP并不能胜任所有的任务,例如:分离图像的内容和风格、生成真实的图像(生成式模型)、使用非常少量的标注数据进行分类、执行数据压缩(例如文件压缩)等。

    现在要实现上述这些任务,每一个可能都需要专有的网络架构和训练算法。 但是,如果我们能够使用同一种架构来实现上述所有的任务,是不是很酷?对抗自编码器(Adversarial Autoencoder)就是这样一种架构,它可以执行所有这些任务,甚至更多。

    在这个系列教程中,我们将利用MNIST数据集来构造这么一个对抗自编码器,它可以对数字(0~9)的图像执行(有损)压缩、自动分离数字图像的风格和内容(来生成不同风格的数字)、利用少量(1000个)标注数据可以获得95%的分类准确度,而且它是一个生成式模型、可以生成以假乱真的数字。

    在介绍对抗自编码器的理论和实现之前,让我们先后退一步,从自编码器(Autoencoder)开始,先利用tensorflow实现一个简单的自编码器。

    mnist autoencoder

    自编码器是一种特殊的神经网络(neural network),它的输出目标(target)就是输入(所以它基本上就是试图将输出重构为输入),由于它不需要任何人工标注,所以可以采用无监督的方式进行训练。

    自编码器包括两个组成部分:编码器和解码器。

    编码器(Encoder)负责把输入x(可以是图像、单词嵌入向量、视频或音频数据)转换为中间输出hh的维度通常比x低,即降维)。 例如,编码器可以把100×100大小的图像x转换为100×1(当然可以是任何尺寸)的输出hh常被称为隐编码(lantent code)。 在这种情况下,编码器只是对图像进行压缩,使其占用较小的空间,正如我们看到的, h 只需要x 百分之一的存储空间(这会导致某些信息丢失,即有损压缩)。

    让我们来考虑一下像WinRAR这样的压缩软件(你还在免费试用吗?),它可以用来压缩文件以获得占用更少空间的zip(或rar...)文件。 编码器在自编码器架构中就是执行类似的操作。

    如果编码器使用函数q来表示,那么:

    encoder function

    解码器(Decoder)接收编码器的输出,并试图在其输出端重构编码器的输入。 在前面编码器的例子中,h的大小是100×1,解码器的目标是将h转换为初始的100×100大小的图像。 我们训练解码器以便从隐编码h中获取尽可能多的信息来重构x

    因此,解码器的操作与在WinRAR上执行解压缩类似。

    如果函数p代表我们的解码器,那么重建图像x_对应于:

    decoder function

    维度压缩(dimension reduction)只有在输入中存在某种相关(correlate)时才起作用(如来自同一领域的多个图像)。 如果我们训练自编码器时,每次都传入完全随机的输入,训练就会失败。 最终一个自编码器可以在编码器的输出端产生给定输入的降维输出,这非常类似于主成分分析( PCA: Primary Component Analysis )。 而且由于我们在训练过程中并不需要使用任何人工标注,所以它也是一个无监督模型。

    但是,除了降维以外,自编码器还有什么用?

    • 图像去噪:输入有噪声的图像,输出清晰的无噪声图像。下图是手写数字去噪自编码器的例子:
      denoise
    • 语义散列技术通过降维使信息检索更快(我发现这非常有趣!)。
    • 以对抗方式训练出的自编码器可以用作生成式(generative)模型,这一点我们将在后面进一步深入。

    我把这篇文章分成四个部分:

    • 第1部分:自编码器

    我们将从一个简单自编码器的Tensorflow实现开始,使用它来压缩MNIST(你肯定听说过这个数据集)图像的维度。

    • 第2部分:探索对抗自编码器的隐空间

    我们将使用对抗学习手段,在隐编码(编码器的输出)上引入约束条件。

    • 第3部分:分离样式与内容

    在这里,我们将以相同的写作风格生成不同的样本图像。

    • 第4部分:用1000个标签分类MNIST数据。

    我们将训练一个自编码器来对MNIST数字进行分类,只用1000个标注过的输入来获得大约95%的准确度(令人印象深刻,对吧?)。

    现在进入第一部分,我们先看看需要实现的网络结构, 。

    如前所述,自编码器(AE)包括一个编码器和一个解码器,让我们从一个简单的全连接编码器架构开始:
    encoder

    输入层有784个神经元(将图像拉平到单一维度),两个隐层分别包含1000个使用relu激活函数的神经元,输出层使用2个神经元,都不使用激活函数以便获得隐编码。

    如果你想直接试试代码,可以查看这个链接

    为了在Tensorflow中实现上述架构,我们将从一个dense()函数开始,它将基于给定的样本输入x、输入神经元数量n1和输出神经元数量n2,自动创建一个全连接层。 name参数用于设置变量域(variable_scope)的名称。 关于共享变量和变量域的更多内容可以查看这里(我强烈建议你看一下):

    def dense(x, n1, n2, name):
        """
        Used to create a dense layer.
        :param x: input tensor to the dense layer
        :param n1: no. of input neurons
        :param n2: no. of output neurons
        :param name: name of the entire dense layer.i.e, variable scope name.
        :return: tensor with shape [batch_size, n2]
        """
        with tf.variable_scope(name, reuse=None):
            weights = tf.get_variable("weights", shape=[n1, n2],
                                      initializer=tf.random_normal_initializer(mean=0., stddev=0.01))
            bias = tf.get_variable("bias", shape=[n2], initializer=tf.constant_initializer(0.0))
            out = tf.add(tf.matmul(x, weights), bias, name='matmul')
    return out

    我使用了tf.get_variable()而不是tf.Variable()来创建权重和偏差变量,以便后续可以在训练好的模型(编码器或解码器)尝试不同的变量并查看其输出。

    接下来,我们将使用这个dense()函数来实现编码器体系结构。 代码很简单,但请注意,我们没有在编码器的输出中使用任何激活函数:

    def encoder(x, reuse=False):
        """
        Encode part of the autoencoder
        :param x: input to the autoencoder
        :param reuse: True -> Reuse the encoder variables, False -> Create or search of variables before creating
        :return: tensor which is the hidden latent variable of the autoencoder.
        """
        if reuse:
            tf.get_variable_scope().reuse_variables()
        with tf.name_scope('Encoder'):
            e_dense_1 = tf.nn.relu(dense(x, input_dim, n_l1, 'e_dense_1'))
            e_dense_2 = tf.nn.relu(dense(e_dense_1, n_l1, n_l2, 'e_dense_2'))
            latent_variable = dense(e_dense_2, n_l2, z_dim, 'e_latent_variable')
    return latent_variable
    • reuse标志用于重用训练好的编码器。
    • input_dim = 784, n_l1 = 1000, n_l2 = 1000, z_dim = 2

    解码器是以类似的方式实现的,我们期望的架构如下:

    decoder

    再次使用dense()函数来构建我们的解码器。 不过这里我在输出层使用了sigmoid激活函数,以确保输出值介于01之间(与输入的值范围相同)。

    • z_dim = 2, n_l2 = 1000, n_l1 = 1000, input_dim = 784,与编码器相同。

    使用编码器的输出作为解码器的输入:

    encoder_output = encoder(x_input)
    decoder_output = decoder(encoder_output)

    现在已经实现了前面示意图中描述的自编码器架构。 我们将利用占位符x_inputsizebatch_size784)提供输入,将target(目标)设置为与x_input(输入)相同, 然后对x_input(输入)与decoder_output(重构的输入)进行比较 。

    所使用的损失函数是均方误差(MSE:Mean Squared Error),它找出输入( x_input)和输出图像( decoder_output)中像素之间的距离。 我们称之为重构损失(reconstruction loss),因为我们的主要目标就是重构输入。

    loss function

    上面的公式用来计算输入和输出(重构的输入)的平方差。 在Tensorflow中实现这个计算很容易:

    loss = tf.reduce_mean(tf.square(x_target - decoder_output))

    我使用的优化器是AdamOptimizer(你可以随意试试新的优化器,我还没有尝试过其他的),学习率为0.01beta10.9。 在Tensorflow中预置了AdamOptimizer的实现,你可以直接拿来使用:

    optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate, beta1=beta1).minimize(loss)

    请注意,我们使用同一个损失函数让误差反向传播过编码器和解码器。

    最后,我们加载MNIST图像集,使用100大小的批次来训练我们的模型,每个批次的100张图像同时也作为网络要学习的输出目标。

    step = 0
    with tf.Session() as sess:
        sess.run(init)
        if train_model:
            tensorboard_path, saved_model_path, log_path = form_results()
            writer = tf.summary.FileWriter(logdir=tensorboard_path, graph=sess.graph)
            for i in range(n_epochs):
                n_batches = int(mnist.train.num_examples / batch_size)
                for b in range(n_batches):
                    batch_x, _ = mnist.train.next_batch(batch_size)
                    sess.run(optimizer, feed_dict={x_input: batch_x, x_target: batch_x})
                    if b % 50 == 0:
                        batch_loss, summary = sess.run([loss, summary_op], feed_dict={x_input: batch_x, x_target: batch_x})
                        writer.add_summary(summary, global_step=step)
                        print("Loss: {}".format(batch_loss))
                        print("Epoch: {}, iteration: {}".format(i, b))
                        with open(log_path + '/log.txt', 'a') as log:
                            log.write("Epoch: {}, iteration: {}\n".format(i, b))
                            log.write("Loss: {}\n".format(batch_loss))
                    step += 1
                saver.save(sess, save_path=saved_model_path, global_step=step)
        else:
            all_results = os.listdir(results_path)
            all_results.sort()
            saver.restore(sess,
                          save_path=tf.train.latest_checkpoint(results_path + '/' + all_results[-1] + '/Saved_models/'))
    generate_image_grid(sess, op=decoder_image)

    完整的代码都放到github上了。

    注意事项

    • generate_image_grid()函数将一组数字输入给训练好的解码器来生成一组图像,这也是get_variable派上用场的地方。
    • 每次运行代码都将在下面目录生成所需的tensorboard文件:
    /Results/<model>/<time_stamp_and_parameters>/Tensorboard
    • 训练日志文件为:
    ./Results/<model>/<time_stamp_and_parameters>/log/log.txt
    • train标志设置为True来训练模型,将其设置为False则显示某个随机输入的解码器输出。

    下面是200个周期(eopch)的训练过程中,网络重构损失(reconstruction loss)的变化曲线:

    loss

    重构损失在逐步减少,这正是我们想要的。

    训练过程中重构图像的变化:

    generated image

    注意解码器是如何通过消除输入数字3上面的小的不规则性来实现泛化的。

    现在,如果我们向训练好的解码器输入一些随机数(因为隐编码是二维的,所以我输入了0,0),我们会得到一个对应的数字图像吗?

    random input mapped image

    但是上面的图看起来还不是一个清晰的数字(至少对我来说是这样)。

    原因在于,编码器的输出不能覆盖整个二维的隐空间(它的输出分布中有很多空白区域)。 因此,如果我们输入了编码器在训练过程中没有生成过的值,可能就会看到解码器输出奇怪的图像。 要解决这个问题,我们可以在产生隐编码时,将编码器的输出限制为具有指定的随机分布(比如一个具有0.0均值和2.0标准偏差的正态分布)。 这正是对抗自编码器所能做到的,我们将在第二部分学习它。

    原文:A wizard’s guide to Adversarial Autoencoders: Part 1, Autoencoder?

    展开全文
  • DL入门(2):自编码器(AutoEncoder)

    万次阅读 2021-01-17 13:02:49
    简单叙述普通自编码器、堆叠自编码器、稀疏自编码器、降噪自编码器的原理及应用。

      写在前面:看预测论文综述时,面临这样一个问题:很多DL的方法只是会简单运用,却不是特别了解其详细原理,故针对CNN、RNN、LSTM、AutoEncoder、RBM、DBN以及DBM分别做一些简单总结,以达到了解的目的,此篇为AutoEncoder。

    1.大致了解

    1.1 原理

      自动编码器是一种无监督的数据维度压缩和数据特征表达方法。在大部分提到自动编码器的场合,压缩和解压缩的函数是通过神经网络实现的。
      一个简单的自编码器结构如下所示:
    在这里插入图片描述
    可以看到上述结构只有一个隐藏层,从输入到隐藏层即为Encoder(编码器),从隐藏层到输出即为Decoder(解码器)。
      一提到降维,首先想到的肯定是主成分分析(PCA),PCA具体原理可以参考:降维基础知识(样本均值、样本方差、中心矩阵)与PCA(最大投影方差,最小重构代价,SVD分解)
      那么自编码器这么简单的结构如何达到降维的目的呢?我们知道,一个最简单的自编码器(上图所示)只有三层结构,编码器将输入进行编码,变成中间结果,中间结果再经过解码器还原,这种输入等于输出的结构没有什么实际意义。对于上述结构,如果我们只看左边部分,即编码器的部分,就很容易理解降维的原理:隐藏层神经元的数目远低于输入层,那么我们就可以用更少的特征(神经元)去表征输入数据,从而到降维目的。

    1.2 结构

    在这里插入图片描述
    对于样本x,自编码器的中间隐藏层的活性值为x的编码,即:
    在这里插入图片描述
    自编码器的输出为重构的数据:
    在这里插入图片描述
    其中, W ( 1 ) , W ( 2 ) , b ( 1 ) , b ( 2 ) W^{(1)}, W^{(2)}, b^{(1)}, b^{(2)} W(1),W(2),b(1),b(2)是自编码器网络的参数,参数经梯度下降训练得到。
      因此,搭建一个自编码器需要以下几个步骤:

    1. 搭建编码器
    2. 搭建解码器
    3. 设定一个损失函数
    4. 训练

    2.自编码器分类

    2.1 普通自编码器

    输入和输出完全相同,即上面提到的这种。

    2.2 堆叠自编码器

      对于很多数据来说, 仅使用两层神经网络的自编码器还不足以获取一种好的数据表示。为了获取更好的数据表示, 我们可以使用更深层的神经网络。深层神经网络作为自编码器提取的数据表示一般会更加抽象, 能够更好地捕捉到数据的语义信息。
      在实践中经常使用逐层堆叠的方式来训练一个深层的自编码器,称为堆叠自编码器(Stacked Auto-Encoder, SAE)。堆叠自编码器一般可以采用逐层训练(Layer-Wise Training)来学习网络参数。
      举一个具体的例子:
    在这里插入图片描述
      可以看到,相比于普通的自编码器,我们将隐藏层的个数从1增加到3,其实就是三个普通自编码器堆叠而成。
    训练过程如下:

    1. 第一个自编码器:784->1000->784。训练完毕后,固定参数和中间隐层的结果,去掉输出层和相应的权值偏置,然后将隐藏层1000作为第二个自编码器的输入。
    2. 第二个自编码器:1000->1000->1000。训练完毕后,固定参数和中间隐层的结果,去掉输出层和相应的权值偏置,然后再将隐藏层1000作为第三个自编码器的输入。
    3. 第三个自编码器:1000->500->1000。训练完毕后,固定参数和中间隐层的结果,去掉输出层和相应的权值偏置。
    4. 在第三个自编码器隐藏层后面加一个分类器(softmax),进行解码恢复。

      三个自编码器训练完毕后,最后要进行的是整体的反向调优训练,即整体网络使用反向传播进行训练,对参数进行微调。

    2.3 降噪自编码器

      我们使用自编码器是为了得到有效的数据表示, 而有效的数据表示除了具有最小重构错误或稀疏性等性质之外,还可以要求其具备其他性质,比如对数据部分损坏(Partial Destruction)的鲁棒性。高维数据(比如图像)一般都具有一定的信息冗余,比如我们可以根据一张部分破损的图像联想出其完整内容。因此, 我们希望自编码器也能够从部分损坏的数据中得到有效的数据表示, 并能够恢复出完整的原始信息,降噪自编码器便应运而生。
      降噪自编码器(Denoising Auto-Encoder)就是一种通过引入噪声来增加编码鲁棒性的自编码器。

    工作原理
      对于一个向量 x x x,我们首先根据一个比例 μ \mu μ将x的某些维度设置为0,得到一个被损坏的向量 x ^ \hat{x} x^,要注意,损坏比例一般不超过0.5,另外,也可通过引入高斯噪声来损坏数据。
      我们把损坏的数据 x ^ \hat{x} x^送给自编码器的输入端,并要求它通过编码+解码两个步骤重构出无损的原始输入。那么当输入一个没有经过损坏的数据时,我们就能将其恢复到更理想的状态。
      因此,降噪自编码器的思想十分简单, 通过引入噪声来学习更鲁棒性的数据编码,并提高模型的泛化能力。
    在这里插入图片描述

    2.3 稀疏自编码器

      一般来说,自编码器的隐层节点数小于输入层的节点数,比如前面所述,为了达到降维的目的,一般使隐藏层神经元个数小于输入层神经元个数。但假设我们并不限制隐藏层神经元个数,而是限制了其中一部分神经元的活性,如下所示:
    在这里插入图片描述
    隐藏层中,浅蓝色的神经元被抑制了活性。
      稀疏自编码器的目标函数中我们加入了一个正则化项,用于防止过拟合,如下所示:
    在这里插入图片描述
      其中Z= [z(1), ⋯ , z(N)]表示所有训练样本的编码, ρ ( Z ) \rho(Z) ρ(Z)为稀疏性度量函数,𝑾表示自编码器中的参数。
      一般而言,我们不会指定隐层表达h中哪些节点是被抑制的,而是指定一个稀疏性参数ρ,代表隐藏神经元的平均活跃程度(在训练集上取平均)。比如,当ρ=0.05时,可以认为隐层节点在95%的时间里都是被抑制的,只有5%的机会被激活。

    2.4 欠完备自编码器

      欠完备自编码器为了得到有用信息,会限制编码维度小于输入维度,因为隐层编码维数小于输入维数,可以学习数据分布中最显著的特征。
      但是,若中间隐层单元数特别少,则其表达信息有限,会导致重构过程比较困难。

    3 损失函数的设计

    3.1 普通自编码器

      普通自编码器训练目的就是为了使得输入等于输出,因此,损失函数loss可以设计如下:
    在这里插入图片描述
    公式中各项意义一目了然,不再解释。

    3.2 稀疏自编码器

      给自编码器中隐藏层单元加上稀疏性限制,在学习的过程中,均方误差可能变得很小,这样会导过拟合,而我们期望的是一个泛化能力很强的编码器,所以我们加入L1正则化来抑制过拟合。
      目标函数设计如下:
    在这里插入图片描述
    最后一项为正则化项,中间为稀疏操作。

    展开全文
  • 自编码器(Auto Encoder)神经网络常常用于分类,通过定义一个目标函数衡量输出与目标值之间的差异,然后通过调整系统的参数使系统尽量拟合训练数据. 而对每一层神经网络来说,前一层的输出都是可看做未加工的初始...
  •   在非监督学习中,最典型的一类神经网络莫过于autoencoder(自编码器),它的目的是基于输入的unlabeled数据X={x(1),x(2),x(3),...}X={x(1),x(2),x(3),...}X=\{x^{(1)}, x^{(2)}, x^{(3)}, ...\},通过训练得到数据...
  • 衡量一个自编码器模型的效果可以从两个标准入手:1.模型是否可以很好的重建输入信号;2.模型对输入数据在一定程度下的扰动是否具有不变性。 为了使自编码器在标准2下具有更好的效果,Rifai等人提出了收缩自编码器,...
  • 在实际的操作中,也经常使用卷积自动编码器去解决图像编码问题,而且非常有效。 下面通过**keras**完成简单的卷积自动编码。 编码器有堆叠的卷积层和池化层(max pooling用于空间降采样)组成。对应的解码器由卷积层和...
  • 自编码器与堆叠自编码器简述

    千次阅读 2018-06-20 15:45:45
    商业转载请联系作者获得授权,非商业转载请注明出处。本文是对知乎问题为什么稀疏自编码器很少见到...目录自编码器自编码器简介自编码器与神经网络堆叠自编码器自编码器的变种形式稀疏自编码器降噪自编码器关于预训...
  • 自编码器原理概述

    千次阅读 2020-01-14 16:47:58
    自编码器(Auto-Encoder),是一种利用反向传播算法使得输出值等于输入值的神经网络,它先将输入压缩成潜在空间表征,然后通过这种表征来重构输出自编码器由两部分组成: 编码器:这部分能将输入压缩成潜在空...
  • 自编码器、变分自编码器(VAE)简介以及Python实现

    千次阅读 多人点赞 2019-06-25 14:50:00
    本篇博客简单介绍了自编码器(AutoEncoder, AE)以及近几年比较火的变分自编码器(Variational AutoEncoder, VAE),并用Python实现。 自编码器(AE) 自编码器是一种无监督学习模型,可用于数据降维,数据可视化,深度...
  • TensorFlow实现自编码器

    千次阅读 热门讨论 2017-07-27 19:48:23
    TensorFlow实现自编码器,具体一点是实现去噪自编码器自编码器属于非监督学习的方法。自编码器作为深度学习在无监督领域的尝试是非常成功的,使用TensorFlow实现自编码器,能认识到其作用和领悟当中的原理。
  • 变分自编码器是很火的生成网络,数学推导很复杂,我喜欢望眼欲穿的感觉,一眼就看到本质,所以就记录自己的理解。 1.自编码器(AE): 自编码器分成两个部分,第一个部分是encoder,一般是多层网络,将输入的数据压缩...
  • 深度通信网络专栏|自编码器: 整理2018-2019年使用神经网络实现通信系统自编码器的论文,一点拙见,如有偏颇,望不吝赐教,顺颂时祺。 文章中心思想 OFDM系统的优点是单径均衡简单,对采样同步误差的鲁棒性更强。...
  • 详解自动编码器(AE)

    千次阅读 多人点赞 2019-11-21 11:57:34
    自动编码器(Auto-Encoders,AE) 降噪编码(Denoising Auto-Encoders, DAE)(2008) 堆叠降燥自动编码器 (Stacked Denoising Auto-Encoders, SAE)(2008) 卷积自动编码器(Convolution Auto-Encoders, CAE)(2011) 变分...
  • 自动编码器

    万次阅读 2017-05-24 09:32:53
    自动编码器 Deep Learning最简单的一种方法是利用人工神经网络的特点,人工神经网络(ANN)本身就是具有层次结构的系统,如果给定一个神经网络,我们假设其输出与输入是相同的,然后训练调整其参数,得到每一层...
  • 自编码器(AutoEncoder)入门及TensorFlow实现

    万次阅读 多人点赞 2018-03-03 23:01:19
    自编码器(Autoencoder,AE),是一种利用反向传播算法使得输出值等于输入值的神经网络,它先将输入压缩成潜在空间表征,然后通过这种表征来重构输出自编码器由两部分组成:编码器(encoder):这部分能将输入压缩成...
  • 编码器输出:HTL、TTL

    万次阅读 2015-08-06 15:03:26
    电路简单,好用,工作电压较宽,编码器常用5到30V均可(注:HTL输出信号电平一般等同工作电压减三极管压降)。有较强的抗干扰能力,它的主要缺点是工作速度比较低,所以多用在对工作速度要求不高而对抗干扰能力要求...
  • 深度学习之卷积自编码器

    万次阅读 多人点赞 2018-10-31 10:49:48
    自编码器(Autoencoder)是一种旨在将它们的输入复制到的输出的神经网络。他们通过将输入压缩成一种隐藏空间表示(latent-space representation),然后这种重构这种表示的输出进行工作。这种网络由两部分组成,如下...
  • %单元数组的每个单元都可以直接转换为图片输出 end 运行结果 还给出了重构前后图片的对比 其他 训练设置通过,该网页中Name-Value Pair Arguments部分的介绍见前一篇博客MATLAB实现堆栈自编码器(一)——...
  • 深度自编码器原理

    千次阅读 2017-08-22 14:05:09
    自编码器的目标:使用少量高阶特征重构输入定义:使用自身的高阶特征编码自己思想:自编码器其实也是一种神经网络,他的输入和输出一致的,借助稀疏编码的思想,目标是使用高阶特征重新组合来重构自己。特点:期望...
  • 一、自编码器介绍 自编码器:用于图像压缩和去噪。 自编码器会造成一些信息丢失,因为压缩的实质是因为单元减少了。自编码器的压缩性能:比jpeg, Mp3, Mpeg或者视频的效果差。而且泛化能力差。但最近发现,...
  • 自编码器python实现

    千次阅读 2019-04-15 11:53:51
    自编码器 自编码器是一种非常通用的神经网络工具。主要思想是通过一个编码器,将原始信息编码为一组向量,然后通过一个解码器,将向量解码为原始数据。通过衡量输入与输出的差别,来对网络参数进行训练。主要可以...
  • 自编码器中的重参数技巧

    千次阅读 2020-07-07 20:32:34
    自编码器中的重参数技巧可变自编码器:编码,采样,解码和重复 可变自编码器:编码,采样,解码和重复 在vanilla 自编码器中,encoder网络不知道如何压缩输入数据到隐码。在可变自编码器中,将输入数据映射到多元...
  • 自动编码器重建图像及Python实现

    千次阅读 2019-03-14 18:54:32
    自动编码器简介 自动编码器(一下简称AE)属于生成模型的一种,目前主流的生成模型有AE及其变种和生成对抗网络(GANs)及其变种。随着深度学习的出现,AE可以通过网络层堆叠形成深度自动编码器来实现数据降维。通过...
  • MATLAB给出了堆栈自编码器的实现Train Stacked Autoencoders for Image Classification ,本文对其进行分析 堆栈自编码器Stacked Autoencoders 堆栈自编码器是 具有多个隐藏层的神经网络可用于解决图像等复杂数据的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 420,981
精华内容 168,392
关键字:

自编码器输出