精华内容
下载资源
问答
  • 也经常使用卷积自动编码器去解决图像编码问题,而且非常有效。 下面通过**keras**完成简单的卷积自动编码。 编码器有堆叠的卷积层和池化层(max pooling用于空间降采样)组成。 对应的解码器由卷积层和上采样层组成。 ...
  • LSTM异常检测自动编码器 使用LSTM自动编码器检测异常并对稀有事件进行分类。 如此多次,实际上是现实生活中的大多数数据,我们都有不平衡的数据。 数据是我们最感兴趣的事件,很少见,不像正常情况下那么频繁。 ...
  • TensorFlow 2.0中的自动编码器 以下自动编码器的实现; 香草自动编码器(AE) 去噪自动编码器 备用自动编码器(进行中) 压缩式自动编码器(进行中) 可变自动编码器(VAE) 条件变分自动编码器(CVAE) 节拍...
  • Tensorflow中的LSTM自动编码器和LSTM未来预测器。 这是基于本文的简单实现: : 要求 Tensorflow 1.4.0 的Python 3.5.4 Python软件包:numpy,matplotlib,os,argparse,scipy 用法 数据生成后实施重建或未来预测...
  • 3D自动编码器-源码

    2021-02-14 13:03:14
    3D自动编码器 该项目旨在开发具有3D卷积层的自动编码器。 图书馆Tensorflow 2
  • 自动编码器异常检测 使用自动编码器检测mnist数据集中的异常 说明 异常被定义为偏离标准,很少发生并且不遵循其余“模式”的事件。只有在我们的类标签中存在巨大的不平衡这一事实,问题才会复杂化。要完成此任务,...
  • 将节点相似度矩阵,作为深度稀疏自动编码器的输入,并通过不断迭代,作为输出低维特征矩阵。(matlab编写)
  • NNSAE:非负稀疏自动编码器(NNSAE)。 自动编码器神经网络中非负和稀疏编码的有效在线学习方案
  • 基于自动编码器的域自适应网络
  • 最先提出深度学习算法hinton的自动编码器matlab代码,内容是:利用多层rbm进行自动编码的多层特征训练,然后使用梯度算法进行fine turn。可以进行特征提取,也可以进行分类。
  • ImageNet预训练的自动编码器 使用Keras的ImageNet预训练自动编码器。 我使用在Imagenet上经过预训练的VGG16网络来构建编码器。 由于无法直接从编码器派生解码器,因此在玩具Imagenet数据集中训练网络的其余部分。 该...
  • 本资源是3层的自编码器加上稀疏正则项约束的matlab代码。隐层激活函数选sigmoid函数,输出层选线性函数,程序中以一个标准数据集sonar为例,使用该方法可以做无监督表征学习,数据压缩,多任务学习等
  • PyTorch中的分子自动编码器
  • 深度计数自动编码器可对scRNA-seq数据进行消噪 深度计数自动编码器网络使用具有零膨胀负二项式(ZINB)损失功能的深度自动编码器,通过考虑计数结构,数据的过度分散的性质和稀疏性来对scRNA-seq数据进行去噪并消除...
  • 自动编码器讲义

    2018-09-12 12:02:10
    自动编码器是一种数据的压缩算法,其中数据的压缩和解压缩函数是数据相关的、有损的、从样本中自动学习的。在大部分提到自动编码器的场合,压缩和解压缩的函数是通过神经网络实现的。
  • 详解自动编码器(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) 变分...

    自动编码器(AE,DAE,CAE,SAE)的方法介绍与简单实现(附代码)

    自动编码器的发展简述

    自动编码器(Auto-Encoders,AE)

    传统的自动编码器是一种数据的压缩算法
    其算法包括编码阶段解码阶段,且拥有对称的结构。

    目前自编码器的应用主要有两个方面,第一是数据去噪,第二是为进行可视化而降维。配合适当的维度和稀疏约束,自编码器可以学习到比PCA等技术更有意思的数据投影。

    传统编码器的编解码过程描述如图:
    在这里插入图片描述
    在这里插入图片描述
    评价:
    传统自编码器的目的是使输出与输入尽量相同,这完全可以通过学习两个恒等函数来完成,但是这样的变换没有任何意义,因为我们真正关心的是隐层表达,而不是实际输出。因此,针对自编码器的很多改进方法都是对隐层表达增加一定的约束,迫使隐层表达与输入不同。

    降噪自编码(Denoising Auto-Encoders, DAE)(2008)

    自编码器真正关心的是隐藏层的特征表达,一个好的表达能够捕获输入信号的稳定结构,以该目的为出发出现了降噪自动编码器。

    降噪自动编码器,首先对干净的输入信号加入噪声产生一个受损的信号。然后将受损信号送入传统的自动编码器中,使其重建回原来的无损信号。

    降噪自编码器的编解码过程描述如图:
    在这里插入图片描述
    在这里插入图片描述
    降噪自编码器与传统的自动编码器的主要区别在于:

    1.降噪自编码器通过人为的增加噪声使模型获得鲁棒性的特征表达

    2.避免使隐层单元学习一个传统自编码器中没有意义的恒等函数

    评价:
    降噪自编码器通过对输入信号人为地进行损坏,主要是为了达到两个目的,首先是为了避免使隐层单元学习一个传统自编码器中没有实际意义的恒等函数,其次就是为了使隐层单元可以学习到一个更加具有鲁棒性的特征表达。
    降噪自编码器最大的优点在于,重建信号对输入中的噪声具有一定的鲁棒性,而最大的缺陷在于每次进行网络训练之前,都需要对干净输入信号人为地添加噪声,以获得它的损坏信号,这无形中就增加了该模型的处理时间。

    堆叠降燥自动编码器 (Stacked Denoising Auto-Encoders, SAE)(2008)

    降噪自编码器的编解码过程描述如图:
    在这里插入图片描述
    堆叠降噪自编码器与降噪自编码器的区别在于:

    1.堆叠降噪自编码器采用了降噪编码器的编码器作为基础单元,并且使用其训练方法进行预训练

    2.降噪自动编码器是无监督学习(自监督)的一种方法,而降噪自编码器是一种有监督方法.
    评价:
    堆叠降噪自编码器是降噪自编码器的一个应用方法.

    卷积自动编码器(Convolution Auto-Encoders, CAE)(2011)

    全卷积网络是一种面向特定应用(图像语义分割)的卷积神经网络,其结构图如下图所示:
    在这里插入图片描述
    与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。

    与全卷积网络类似的一种无监督方法称为卷机自动编码器。

    由于卷积神经网络所取得的各种优异表现,直接推动了卷积自编码器的产生。

    卷积自编码器属于传统自编码器的一个特例,它使用卷积层和池化层替代了原来的全连接层,卷积自编码器能很好的保留二维信号的空间信息。

    评价:
    其主要差别在于卷积自编码器采用卷积方式对输入信号进行线性变换,并且其权重是共享的,这点与卷积神经网络一样。因此,重建过程就是基于隐藏编码的基本图像块的线性组合。

    变分自动编码器(Variational Auto-Encoders, VAE)(Kingma, 2014)

    变分自编码器是一种主要用于数据生成的自编码器的变体.当作为生成模型时,首先利用数据训练变分自编码器,然后只使用变分自编码器的解码部分,自动生成与训练数据类似的输出.

    其结构图如图所示:
    在这里插入图片描述
    整个结构可以分成三个部分,分别是编码部分,解码部分和生成部分.编码部分和解码部分同时进行训练,目标函数是从KL散度的概念中推倒得到的.
    在这里插入图片描述
    loss函数的推导过程:
    在这里插入图片描述

    几种算法的改进点表格

    编码器名称提出时间改进点目的
    传统自编码器1986
    降噪自编码器2008将带有噪声的损坏信息作为输入信号使重建信号鲁棒性更强
    堆叠自编码器2008将多层结构和栈式训练引入自编码器使自编码器可以训练更高层数
    卷积自编码器2011将卷积层引入自编码器更好的处理图片数据,得到更好的效果
    变分自编码器2014相当于在传统自编码器的隐层表达上增加一个对隐变量的约束,提出了一种将概率模型和神经网络结构的方法使编码器产生的隐层表达满足正态分布,能够更好的生成图像模型

    实现与Python实现

    传统的自动编码器实验结果

    模型结构与实现代码

    传统的自动编码器分为编码器部分和解码器部分,整体模型结构如图所示:
    在这里插入图片描述
    模型分为三个子模块,由上至下分别为输入层,编码器层和解码器层,编码器将输入维度为784(28 28)的mnint灰度值转化为一个维度为2的值.编码器将维度为2的值解码回维度为784(28 28)的mnint灰度值.

    python-keras代码实现关键代码如下

    def __init__(self, ENCODING_DIM_INPUT=784, ENCODING_DIM_OUTPUT=2, Name = "ae"):
      
            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', metrics=['accuracy'])
      
            self.autoencoder = autoencoder
            self.encoder = encoder
    

    训练过程

    本实验讨论使用relu和tanh两种激活函数的自监督训练的情况.训练的loss函数选择为方均根误差.
    训练过程的loss变化图像如下.

    使用tanh作为激活函数时,loss变化情况如下:
    在这里插入图片描述
    可以观察到,loss收敛到0.0685,效果较好.使用relu作为激活函数同样能够有效收敛,不过误差数值相对较大.由于篇幅原因不将图片在此进行展示.

    编码器输出的可视化结果

    本节将从重建图像和编码器输出层的二维可视化图像两部分进行展示,分别展示使用tanh和relu两种损失函数的训练结果.

    训练好的自动编码器重建图像(使用relu激活函数)如下图:
    在这里插入图片描述
    训练好的自动编码器重建图像(使用tanh激活函数)如下图:
    在这里插入图片描述
    两者对比可以发现relu函数训练出的模型存在一些像素颗粒,这也验证了上一节loss函数较大的实验结果.为了解释该问题,展示编码器输出层的二维可视化图片.

    训练好的编码器输出图像(使用relu激活函数)如下图:
    在这里插入图片描述
    训练好的编码器输出图像(使用tanh激活函数)如下图:
    在这里插入图片描述
    以上两张图片是 编码器-解码器 结构中编码器部分的输出绘制成的二维可视化图片,不同的颜色代表了不同的数字,对应的数字在右边的图例中进行了显示.从以上两张图片中可以得到:

    1.由于relu函数对负数的截断性质,使用relu激活函数训练的模型中有一部分点被限制在x=0,y=0两条边缘线上,这也是上文中提到的训练误差较大和出现像素颗粒的原因.

    2.自动编码器虽然能够对mnist数据集进行编码和解码,但是效果并没有其改进的其他方法理想,这一观点可以从解码图片较为模糊和编码器可视化后各个类别的分类相重合得到验证.

    说明与讨论

    传统自编码器有很大的改进空间,改进空间的可以从几个方面阐述:

    1.解码器输出较为模糊
    2.编码器可视化的类别间的界限不明显
    

    堆叠降噪自编码器

    模型结构与实现代码

    传统的自动编码器分为编码器部分和解码器部分,整体模型结构如图所示:
    在这里插入图片描述
    模型分为三个子模块,由上至下分别为输入层,多层编码器层和多层解码器层,编码器将输入维度为784(28 28)的mnint灰度值转化为一个维度为2的值.编码器将维度为2的值解码回维度为784(28 28)的mnint灰度值

    python-keras代码实现关键代码如下

    class DAE(ae.AE):
      
        def __init__(
            self, ENCODING_DIM_INPUT=784, ENCODING_DIM_LAYER1=128,
            ENCODING_DIM_LAYER2=64, ENCODING_DIM_LAYER3=10,
            ENCODING_DIM_OUTPUT=2,Name="dae" ):
            # 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 surprised learning model
            SL_output = Dense(10, activation='softmax')(encode_output)
      
            # build autoencoder, encoder
            autoencoder = Model(inputs=input_image, outputs=decode_output)
            encoder = Model(inputs=input_image, outputs=encode_output)
            SL_model = Model(inputs=input_image, outputs=SL_output)
      
            # compile autoencoder
            autoencoder.compile(optimizer='adam', loss='mse', metrics=['accuracy'])
            SL_model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])
    

    代码定义了三个模型,分别是用于自监督训练的 autoencoder, 用于二维化的编码器 encoder 和用于进行有监督训练的 SL_model.

    训练过程

    堆叠降噪自动编码器分为无监督的预训练过程和有监督的训练过程两部分.
    本部分分别进行说明.

    自监督的预训练过程loss变化情况如下.
    在这里插入图片描述
    无监督的训练过程正确率acc变化情况如下.
    在这里插入图片描述
    可以看到,在两个训练阶段,方法可以有效的达到收敛.

    以下几点需要指出:

    1.多层编码器的训练没有使用栈式编码器的训练方式.
    2.预训练次数20epoch,并不足够模型充分收敛,但是作为预训练部分,已经充分.
    3.预训练部分的误差与传统自编码器相比较大,是因为在初始层加入了噪声的原因.
    4.训练时间与传统编码器相比更长,是其3倍左右.
    5.在有监督学习的开始阶段,分类的正确率并不高,这也印证了上一部分二位可视化的结果,很多点的界限不清晰.

    编码器输出的可视化结果

    本节将从重建图像和编码器输出层的二维可视化图像两部分进行展示

    预训练部分

    重建图像

    下图展示了添加噪声的效果(第一行原图,第二行增加噪声的图).

    image

    下图展示了,对添加噪声的图片进行重构的结果(第一行增加噪声的图,第二行重构图)

    image

    编码器输出层的二维可视化图像

    下图展示了添加噪声的效果(第一行原图,第二行增加噪声的图).

    image

    以下几点需要指出:

    1.本方法可以有效的对随机噪声进行去除
    2.恢复图与原图相比虽然能够识别但是效果更模糊
    3.与传统自动编码器相比,本方法得到的二维图的界限更加清晰

    有监督训练部分
    经过有监督的训练

    重建图像(因为不是目标,所以必然走样)
    下图展示了,对添加噪声的图片进行重构的结果(第一行增加噪声的图,第二行重构图)

    image

    编码器输出层的二维可视化图像

    image

    经过有监督学习,二维可视化图中各个组的界限更加清晰.

    说明与讨论

    堆叠降噪自编码器的改进有以下启发:

    1.使用自监督预训练与有监督训练方式相结合的形式获得更加优秀的效果
    2.使用增加噪声的形式迫使模型学习更加有效的特征
    3.将深度玻尔兹曼姬的思想迁移到自动编码器中

    卷积自编码器

    模型结构与实现代码

    卷积自编码器自动编码器分为编码器部分和解码器部分,整体模型结构如图所示:
    在这里插入图片描述
    python-keras代码实现关键代码如下:

    def __init__(self,CHANNEL_1 = 16,CHANNEL_2 = 8,CHANNEL_OUTPUT = 1,   Name="cae"):
            # 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)
            encode_output = MaxPool2D((2, 2), padding='same')(x)
      
            # decoding layer
            x = Conv2D(CHANNEL_2, (3, 3), activation='relu', padding='same')(encode_output)
            x = UpSampling2D((2, 2))(x)
            x = Conv2D(CHANNEL_1, (3, 3),activation='relu', padding='same')(x)
            x = UpSampling2D((2, 2))(x)
            decode_output = Conv2D(CHANNEL_OUTPUT, (3, 3), activation='sigmoid', padding='same')(x)
      
            # build surprised learning model
            encode_output_flatten = Flatten()(decode_output)
            SL_output = Dense(10, activation='softmax')(encode_output_flatten)
      
            # build autoencoder, encoder
            autoencoder = Model(inputs=input_image, outputs=decode_output)
            encoder = Model(inputs=input_image, outputs=encode_output)
            SL_model = Model(inputs=input_image, outputs=SL_output)
      
            # compile autoencoder
            autoencoder.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
            SL_model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])
    

    训练过程

    堆叠降噪自动编码器分为无监督的预训练过程和有监督的训练过程两部分.
    本部分分别进行说明.

    在自监督的预训练过程loss变化情况如下:
    在这里插入图片描述
    图像显示,自监督的训练loss收敛于0.07左右,该误差比降噪自编码器的0.09要小.与传统自编码器的训练误差相差不多.但是从下文可知其训练效果明显优于传统自动编码器.

    在有监督的训练过程正确率acc变化情况如下:
    在这里插入图片描述
    图像显示,有监督训练过程的正确率上升到0.99,比降噪自动编码器的正确(0.95)率更高.
    因此在mnist数据集中的重建任务和分类任务中,卷积自动编码器有一定优势.

    不过以下几点需要指出:

    1.与sDAE相比,CAE有更高的准确率和更低的损失
    2.更好的效果除了卷积单元能够更好的处理图像数据外,可能与CAE方法的复杂度更高和其瓶颈处通道更宽有关

    编码器输出的可视化结果

    可以看到和stacked AE的主要区别在于局部卷积连接,而不所有层都是全连接。对图像和某些其他数据影响在空间局部的问题,比全连接更合适.因此效果更加优秀.

    于降噪自动编码器相同,首先对图片增加噪声:
    在这里插入图片描述
    然后对增加噪声的图片进行去噪:

    image

    去噪结果比较优秀,与上文中所有的结果相比是最优秀的.

    变分自编码器

    模型结构与实现代码

    变分自动编码器的结构最为复杂,并且在模型中引入了隐变量,和KL散度等概率论概念.对模型的实现造成了一定的影响.
    自动编码器分为编码器部分和解码器部分,整体模型结构如图所示:

    image

    上图中并没有展开编码器和解码器的结构,编码器(encoder) 与 解码器(decoder)的形式分别如下:
    encoder:
    在这里插入图片描述
    decoder:
    在这里插入图片描述

    python-keras代码实现关键代码如下:

    class VAE(ae.AE):
      
        def __init__(
            self,
            ENCODING_DIM_INPUT = 784, 
            intermediate_dim = 512,
            batch_size = 128,
            latent_dim = 2,
            mse_loss = True,
            Name="vae"
        ):
      
            self.name = Name
      
            # input placeholder
            input_image = Input(shape=(ENCODING_DIM_INPUT,), name='encoder_input')
            # VAE model = encoder + decoder
            # encoding layer
            x = Dense(intermediate_dim, activation='relu')(input_image)
            z_mean = Dense(latent_dim, name='z_mean')(x)
            z_log_var = Dense(latent_dim, name='z_log_var')(x)
            z = Lambda(sampling, output_shape=(latent_dim,), name='z')([z_mean, z_log_var])
            # build decoder model
            latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
            x = Dense(intermediate_dim, activation='relu')(latent_inputs)
            outputs = Dense(ENCODING_DIM_INPUT, activation='sigmoid')(x)
      
            # # build surprised learning model
            # SL_output = Dense(10, activation='softmax')()
      
            # build autoencoder, encoder
            encoder = Model(input_image, [z_mean, z_log_var, z], name='encoder')
            decoder = Model(latent_inputs, outputs, name='decoder')
            # SL_model = Model(inputs=input_image, outputs=SL_output)
      
            outputs = decoder(encoder(input_image)[2])
            autoencoder = Model(input_image, outputs, name='vae_mlp')
      
            # compile autoencoder
            # VAE loss = mse_loss or xent_loss + kl_loss
            if mse_loss:
                reconstruction_loss = mse(input_image, outputs)
            else:
                reconstruction_loss = binary_crossentropy(input_image,outputs)
      
            reconstruction_loss *= ENCODING_DIM_INPUT
            kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
            kl_loss = K.sum(kl_loss, axis=-1)
            kl_loss *= -0.5
            vae_loss = K.mean(reconstruction_loss + kl_loss)
            autoencoder.add_loss(vae_loss)
            autoencoder.compile(optimizer='adam')
            autoencoder.summary()
      
            # SL_model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])           
            self.autoencoder = autoencoder
            self.encoder = encoder
            self.decoder = decoder
    

    训练过程

    由于,变分自动编码器主要应用于图像生成,而并非是提取与分类,因此变分降噪自动编码器只有自监督的训练过程…

    在自监督的训练过程,使用 KL散度+交叉熵 作为loss函数,loss变化情况如下.

    image

    可以看散度可以收敛到145的情况,能够有效进行收敛.

    在自监督的训练过程,使用 KL散度+方均根 作为loss函数,loss变化情况如下.

    image

    对于两种损失函数效果的讨论在下文中进行.

    自编码器输出的可视化结果

    使用 KL散度+交叉熵 作为损失函数

    基于kl散度的loss训练结果二维可视化如下

    image

    使用生成器对图像进行生成可以得到如下结果.

    image

    使用 KL散度+方均根 作为损失函数

    基于kl散度的loss训练结果二维可视化如下

    image

    使用生成器对图像进行生成可以得到如下结果.

    image

    由于方均根与交叉熵的区别在于 解码器重现误差上面的区别,在编码器部分同样使用kl散度作为loss,因此两者的可视化结果类似.

    以下几点需要指出:

    1.二维可视化的结果中各个类别的界限较为明显,且其分布十分集中方便生成模型的图像生成.
    2.KL散度作为一种新的损失函数无法与其他方法的误差进行对比.

    讨论

    1.自动编码器可能的应用有特征提取,图像分类,图像去燥,图像生成等

    2.在特征提取领域和图像分类领域使用SAE有较优秀的效果

    3.在图像去噪领域可以使用cae方法,CAE方法对二维图片的去燥效果十分优秀,但是由于中间部分是比较复杂的卷机核结构,无法进行有效的可视化

    4.VAE方法在图像生成领域有出色的表现,将中间的隐变量约束为正太分布的形式,十分方便的通过生成器完成图像生成.

    5.在研究角度,VAE方法将概率方法引入神经网络的计算体系中,通过网络完成对概率分布的映射,使用散度的概念构造损失函数,对其他研究有些启发.

    完成代码

    https://github.com/zangzelin/Auto-encoder-AE-SAE-DAE-CAE-DAE-with-keras-in-Mnist-and-report

    展开全文
  • 自动编码器这是本文所述的(可变)图自动编码器模型的TensorFlow实现:TN Kipf,M.Welling,变分图自动编码器,关于贝叶斯深度学习的NIPS研讨会(2图自动编码器)这是(可变)图自动编码器模型的TensorFlow实现,...
  • 基于堆叠稀疏自动编码器的数字调制自动识别
  • 自动编码器,主要作用: 数据去噪, 可视化降维度, 生成数据。 模型结构: 缺点:在inference时只能通过输入一个输入得到一个隐含变量Z,然后在通过Decoder生成,无法任意去构造隐含变量。 1.2 VAE

    1 VAEs

    1.1 AE: AutoEncoder

    自动编码器,主要作用:

    1. 数据去噪,
    2. 可视化降维度,
    3. 生成数据

    模型结构:

    在这里插入图片描述

    缺点:在inference时只能通过输入一个输入得到一个隐含变量Z,然后在通过Decoder生成,无法任意去构造隐含变量。

    在这里插入图片描述

    1.2 VAE: Variational AutoEncoder

    变分自编码器(VAE)是一个生成模型,将生成一个隐空间(latent space),从中可以对点进行采样。这些点中的任何一个都可以解码为看起来合理的target。

    AE存在的问题就由VAE来解决,只需要在编码过程给它增加一些限制,迫使其生成的隐含向量能够粗略的遵循一个标准正态分布, encoder在隐空间中产生概率分布,这就是其与一般的自动编码器最大的不同,因此在Inference时,我们只需要给它一个标准正态分布的随机隐含向量,这样通过解码器就能够生成我们想要的target。

    实际情况中,需要在模型的准确率上与隐含向量服从标准正态分布之间做一个权衡。

    1. 模型准确率:input和output之间求loss;

    2. 两种分布的相似程度: 使用KL divergence来表示隐含向量与标准正态分布之间差异的loss。

    KL divergence 的公式: D K L ( P ∥ Q ) = ∫ − ∞ ∞ p ( x ) log ⁡ p ( x ) q ( x ) d x D K L(P \| Q)=\int_{-\infty}^{\infty} p(x) \log \frac{p(x)}{q(x)} d x DKL(PQ)=p(x)logq(x)p(x)dx,这里变分编码器使用了一个技巧“重新参数化”来解决KL divergence的计算问题。

    在这里插入图片描述

    这时不再是每次产生一个隐含向量,而是生成两个向量,一个表示均值,一个表示标准差,然后通过这两个统计量来合成隐含向量,这也非常简单,用一个标准正态分布先乘上标准差再加上均值就行了,这里我们默认编码之后的隐含向量是服从一个正态分布的。这个时候我们是想让均值尽可能接近0,标准差尽可能接近1。

    N ( u , σ 2 ) N(u,\sigma^2) N(u,σ2)采样隐含变量Z等效于,采样 ϵ ∼ N ( 0 , 1 ) \epsilon\sim N(0,1) ϵN(0,1),然后计算 Z = u + σ ⋅ ϵ Z = u+ \sigma \cdot \epsilon Z=u+σϵ

    上述VAE中,网络中的输入和输出之间存在一个随机变量。由于无法通过随机变量反向传播梯度,这带来了一个明显的问题,即无法训练编码器。为了解决该问题,以不同的方式来表示VAE,以使隐分布的参数从随机变量的参数中被分解出来,从而可以通过隐分布的参数进行反向传播。(说白了就是采样这个过程是随机的,不能进行反向传播,所以就想办法改变VAE的表现形式使它可以被训练),具体地说,当协方差矩阵Σ是对角矩阵时, N ( u , Σ ) = u + Σ N ( 0 , 1 ) N(u, \Sigma)=u+\Sigma N(0,1) N(u,Σ)=u+ΣN(0,1)。重要的一点是,可以使用反向传播对VAE进行端到端训练。但是,由于仍然存在随机性因素,因此训练过程称为随机梯度变化贝叶斯(stochastic gradient variational Bayes,SGVB),而不是被称为随机梯度下降。

    缺点:VAE创建了可以重现其输入的自编码器,以及可以生成合理的target的解码器,但是解码器无法按需生成特定想要的target。

    对比:在传统的自编码器中,编码器从数据中获取一个样本,并返回隐空间中的单个点,然后将其传递到解码器中。在变分自编码器中,编码器会在隐空间中产生概率分布!(注意这个区别)。它输出的隐分布是与隐空间维数相同的高斯分布。编码器产生这些高斯分布的参数(均值,方差)。

    1.3 CVAE: Conditional Variational Autoencoder

    VAE的缺陷产生了条件变分自编码器(CVAE),它的编码器和解码器都有额外的输入

    在这里插入图片描述

    在训练时,数字(标签信息)被提供给编码器和解码器。在这种情况下,它将表示为一个one-hot向量。要生成特定数字的图像,只需将该数字与从标准正态分布中采样的隐空间中的随机点一起馈入解码器即可。即使输入相同的点以产生两个不同的数字,该过程也将正确运行,因为系统不再依赖于隐空间来编码要处理的数字(标签信息)。相反,潜在空间对其他信息进行编码,例如笔划宽度或数字被写入的角度(此处我的理解是:隐空间原来编码所有输入信息,输出完全依赖于隐空间提供的采样点信息,现在在解码器处提供了标签信息,那么隐空间就编码其他信息了,相当于把标签信息从隐空间分离表征(disentangled)了)。

    在这里插入图片描述

    References:

    https://zhuanlan.zhihu.com/p/27549418

    https://zhuanlan.zhihu.com/p/88750084

    https://zhuanlan.zhihu.com/p/34998569

    欢迎进群交流~

    展开全文
  • 自动编码器

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

    自动编码器

    Deep Learning最简单的一种方法是利用人工神经网络的特点,人工神经网络(ANN)本身就是具有层次结构的系统,如果给定一个神经网络,我们假设其输出与输入是相同的,然后训练调整其参数,得到每一层中的权重。自然地,我们就得到了输入I的几种不同表示(每一层代表一种表示),这些表示就是特征。自动编码器就是一种尽可能复现输入信号的神经网络。为了实现这种复现,自动编码器就必须捕捉可以代表输入数据的最重要的因素,就像PCA那样,找到可以代表原信息的主要成分。

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

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

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

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


    • 隐层维度小于输入数据维度。也就是说从输入层隐含层x→h的变换是一种降维的操作,网络试图以更小的维度去描述原始数据而尽量不损失数据信息。实际上,当每两层之间的变换均为线性,且监督训练的误差是二次型误差时,该网络等价于PCA!
    • 隐层维度大于输入数据维度。这又有什么用呢?其实不好说,但比如我们同时约束hh的表达尽量稀疏(有大量维度为0,未被激活),此时的编码器便是大名鼎鼎的“稀疏自编码器”。可为什么稀疏的表达就是好的?这就说来话长了,有人试图从人脑机理对比,即人类神经系统在某一刺激下,大部分神经元是被抑制的。个人觉得,从特征的角度来看更直观些,稀疏的表达意味着系统在尝试去 特征选择 ,找出大量维度中真正重要的若干维。


           具体过程简单的说明如下:

    1)给定无标签数据,用非监督学习学习特征:

     

           在我们之前的神经网络中,如第一个图,我们输入的样本是有标签的,即(input, target),这样我们根据当前输出和target(label)之间的差去改变前面各层的参数,直到收敛。但现在我们只有无标签数据,也就是右边的图。那么这个误差怎么得到呢?

            如上图,我们将input输入一个encoder编码器,就会得到一个code,这个code也就是输入的一个表示,那么我们怎么知道这个code表示的就是input呢?我们加一个decoder解码器,这时候decoder就会输出一个信息,如果输出的这个信息和一开始的输入信号input是很像的(理想情况下就是一样的),那很明显,我们就有理由相信这个code是靠谱的。所以,我们就通过调整encoder和decoder的参数,使得重构误差最小,这时候我们就得到了输入input信号的第一个表示了,也就是编码code了。因为是无标签数据,所以误差的来源就是直接重构后与原输入相比得到。

    2)通过编码器产生特征,然后训练下一层。这样逐层训练:

           那上面我们就得到第一层的code,我们的重构误差最小让我们相信这个code就是原输入信号的良好表达了,或者牵强点说,它和原信号是一模一样的。接着,我们将第一层输出的code当成第二层的输入信号,同样最小化重构误差,就会得到第二层的参数,并且得到第二层输出的code,也就是原输入信息的第二个表达了。其他层就同样的方法炮制就行了(训练这一层,前面层的参数都是固定的,并且他们的decoder已经没用了,都不需要了)。

    需要注意的是,整个网络的训练不是一蹴而就的,而是逐层进行。如果按nmkn,m,k结构,实际上我们是先训练网络nmnn→m→n得到nmn→m的变换,然后再训练mkmm→k→m得到mkm→k的变换。最终堆叠成SAE,即为nmkn→m→k的结果,整个过程就像一层层往上盖房子,这便是大名鼎鼎的 layer-wise unsuperwised pre-training (逐层非监督预训练),正是导致深度学习(神经网络)在2006年第3次兴起的核心技术。

    3)有监督微调:

          经过上面的方法,我们就可以得到很多层了。至于需要多少层(或者深度需要多少,这个目前本身就没有一个科学的评价方法)需要自己试验调了。每一层都会得到原始输入的不同的表达。当然了,我们觉得它是越抽象越好了,就像人的视觉系统一样。

           到这里,这个AutoEncoder还不能用来分类数据,因为它还没有学习如何去连结一个输入和一个类。它只是学会了如何去重构或者复现它的输入而已。或者说,它只是学习获得了一个可以良好代表输入的特征,这个特征可以最大程度上代表原输入信号。那么,为了实现分类,我们就可以在AutoEncoder的最顶的编码层添加一个分类器(例如罗杰斯特回归、SVM等),然后通过标准的多层神经网络的监督训练方法(梯度下降法)去训练。

            也就是说,这时候,我们需要将最后层的特征code输入到最后的分类器,通过有标签样本,通过监督学习进行微调,这也分两种,一个是只调整分类器(黑色部分):

           另一种:通过有标签样本,微调整个系统:(如果有足够多的数据,这个是最好的。end-to-end learning端对端学习)

           一旦监督训练完成,这个网络就可以用来分类了。神经网络的最顶层可以作为一个线性分类器,然后我们可以用一个更好性能的分类器去取代它。

           在研究中可以发现,如果在原有的特征中加入这些自动学习得到的特征可以大大提高精确度,甚至在分类问题中比目前最好的分类算法效果还要好!

     

    一、稀疏编码器

    Sparse AutoEncoder稀疏自动编码器:

          

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

    那么,怎么从数学模型上做到这点呢?思路也不复杂,既然要求平均激活度为ρρ,那么只要引入一个度量,来衡量神经元ii的实际激活度^ρiρ^i与期望激活度ρρ之间的差异即可,然后将这个度量添加到目标函数作为正则,训练整个网络即可。那么,什么样的度量适合这个任务呢?有过概率论、信息论基础的同学应该很容易想到它——相对熵,也就是KL散度(KL divergence) 在信息论中,KL(P||Q)表示当用概率分布Q来拟合真实分布P时,产生的信息损耗,其中P表示真实分布,Q表示P的拟合分布。

    这一惩罚因子有如下性质,当 \textstyle \hat\rho_j = \rho 时 \textstyle {\rm KL}(\rho || \hat\rho_j) = 0 ,并且随着 \textstyle \hat\rho_j 与 \textstyle \rho 之间的差异增大而单调递增。举例来说,在下图中假设平均激活度ρ=0.2ρ=0.2

    我们可以看出,相对熵在  \textstyle \hat\rho_j = \rho  时达到它的最小值0,而当  \textstyle \hat\rho_j  靠近0或者1的时候,相对熵则变得非常大(其实是趋向于 \textstyle \infty )。所以,最小化这一惩罚因子具有使得  \textstyle \hat\rho_j  靠近  \textstyle \rho  的效果。 


    如果在AutoEncoder的基础上加上L1的Regularity限制(L1主要是约束每一层中的节点中大部分都要为0,只有少数不为0,这就是Sparse名字的来源),我们就可以得到Sparse AutoEncoder法。

           如上图,其实就是限制每次得到的表达code尽量稀疏。因为稀疏的表达往往比其他的表达要有效(人脑好像也是这样的,某个输入只是刺激某些神经元,其他的大部分的神经元是受到抑制的)。

    Sparse autoencoder的一个网络结构图如下所示:

       

     

      

    自编码神经网络尝试学习一个 \textstyle h_{W,b}(x) \approx x 的函数。换句话说,它尝试逼近一个恒等函数,从而使得输出 \textstyle \hat{x} 接近于输入 \textstyle x 。恒等函数虽然看上去不太有学习的意义,但是当我们为自编码神经网络加入某些限制,比如限定隐藏神经元的数量,我们就可以从输入数据中发现一些有趣的结构。举例来说,假设某个自编码神经网络的输入 \textstyle x 是一张 \textstyle 10 \times 10 图像(共100个像素)的像素灰度值,于是 \textstyle n=100 ,其隐藏层 \textstyle L_2 中有50个隐藏神经元。注意,输出也是100维的 \textstyle y \in \Re^{100} 。由于只有50个隐藏神经元,我们迫使自编码神经网络去学习输入数据的压缩表示,也就是说,它必须从50维的隐藏神经元激活度向量 \textstyle a^{(2)} \in \Re^{50} 中重构出100维的像素灰度值输入 \textstyle x 。如果网络的输入数据是完全随机的,比如每一个输入 \textstyle x_i 都是一个跟其它特征完全无关的独立同分布高斯随机变量,那么这一压缩表示将会非常难学习。但是如果输入数据中隐含着一些特定的结构,比如某些输入特征是彼此相关的,那么这一算法就可以发现输入数据中的这些相关性。事实上,这一简单的自编码神经网络通常可以学习出一个跟主元分析(PCA)结果非常相似的输入数据的低维表示。


    我们刚才的论述是基于隐藏神经元数量较小的假设。但是即使隐藏神经元的数量较大(可能比输入像素的个数还要多),我们仍然通过给自编码神经网络施加一些其他的限制条件来发现输入数据中的结构。具体来说,如果我们给隐藏神经元加入稀疏性限制,那么自编码神经网络即使在隐藏神经元数量较多的情况下仍然可以发现输入数据中一些有趣的结构。

    稀疏性可以被简单地解释如下。如果当神经元的输出接近于1的时候我们认为它被激活,而输出接近于0的时候认为它被抑制,那么使得神经元大部分的时间都是被抑制的限制则被称作稀疏性限制。这里我们假设的神经元的激活函数是sigmoid函数。如果你使用tanh作为激活函数的话,当神经元输出为-1的时候,我们认为神经元是被抑制的。

    注意到 \textstyle a^{(2)}_j 表示隐藏神经元 \textstyle j 的激活度,但是这一表示方法中并未明确指出哪一个输入 \textstyle x 带来了这一激活度。所以我们将使用 \textstyle a^{(2)}_j(x) 来表示在给定输入为 \textstyle x 情况下,自编码神经网络隐藏神经元 \textstyle j 的激活度。 进一步,让

    \begin{align}\hat\rho_j = \frac{1}{m} \sum_{i=1}^m \left[ a^{(2)}_j(x^{(i)}) \right]\end{align}

    表示隐藏神经元 \textstyle j 的平均活跃度(在训练集上取平均)。我们可以近似的加入一条限制

    \begin{align}\hat\rho_j = \rho,\end{align}

    其中, \textstyle \rho 是稀疏性参数,通常是一个接近于0的较小的值(比如 \textstyle \rho = 0.05 )。换句话说,我们想要让隐藏神经元 \textstyle j 的平均活跃度接近0.05。为了满足这一条件,隐藏神经元的活跃度必须接近于0。

    为了实现这一限制,我们将会在我们的优化目标函数中加入一个额外的惩罚因子,而这一惩罚因子将惩罚那些 \textstyle \hat\rho_j 和 \textstyle \rho 有显著不同的情况从而使得隐藏神经元的平均活跃度保持在较小范围内。惩罚因子的具体形式有很多种合理的选择,我们将会选择以下这一种:

    \begin{align}\sum_{j=1}^{s_2} \rho \log \frac{\rho}{\hat\rho_j} + (1-\rho) \log \frac{1-\rho}{1-\hat\rho_j}.\end{align}

    这里, \textstyle s_2 是隐藏层中隐藏神经元的数量,而索引 \textstyle j 依次代表隐藏层中的每一个神经元。如果你对相对熵(KL divergence)比较熟悉,这一惩罚因子实际上是基于它的。于是惩罚因子也可以被表示为

    \begin{align}\sum_{j=1}^{s_2} {\rm KL}(\rho || \hat\rho_j),\end{align}

    其中 \textstyle {\rm KL}(\rho || \hat\rho_j) = \rho \log \frac{\rho}{\hat\rho_j} + (1-\rho) \log \frac{1-\rho}{1-\hat\rho_j} 是一个以 \textstyle \rho 为均值和一个以 \textstyle \hat\rho_j 为均值的两个伯努利随机变量之间的相对熵。相对熵是一种标准的用来测量两个分布之间差异的方法。(如果你没有见过相对熵,不用担心,所有你需要知道的内容都会被包含在这份笔记之中。)


    这一惩罚因子有如下性质,当 \textstyle \hat\rho_j = \rho 时 \textstyle {\rm KL}(\rho || \hat\rho_j) = 0 ,并且随着 \textstyle \hat\rho_j 与 \textstyle \rho 之间的差异增大而单调递增。举例来说,在下图中,我们设定 \textstyle \rho = 0.2 并且画出了相对熵值 \textstyle {\rm KL}(\rho || \hat\rho_j) 随着 \textstyle \hat\rho_j 变化的变化。

                                                          KLPenaltyExample.png

      
    我们可以看出,相对熵在 \textstyle \hat\rho_j = \rho 时达到它的最小值0,而当 \textstyle \hat\rho_j 靠近0或者1的时候,相对熵则变得非常大(其实是趋向于\textstyle \infty)。所以,最小化这一惩罚因子具有使得 \textstyle \hat\rho_j 靠近 \textstyle \rho 的效果。 现在,我们的总体代价函数可以表示为

    \begin{align}J_{\rm sparse}(W,b) = J(W,b) + \beta \sum_{j=1}^{s_2} {\rm KL}(\rho || \hat\rho_j),\end{align}

    其中 \textstyle J(W,b) 如之前所定义,而 \textstyle \beta 控制稀疏性惩罚因子的权重。 \textstyle \hat\rho_j 项则也(间接地)取决于 \textstyle W,b ,因为它是隐藏神经元 \textstyle j 的平均激活度,而隐藏层神经元的激活度取决于 \textstyle W,b 。


    为了对相对熵进行导数计算,我们可以使用一个易于实现的技巧,这只需要在你的程序中稍作改动即可。具体来说,前面在后向传播算法中计算第二层( \textstyle l=2 )更新的时候我们已经计算了

    \begin{align}\delta^{(2)}_i = \left( \sum_{j=1}^{s_{2}} W^{(2)}_{ji} \delta^{(3)}_j \right) f'(z^{(2)}_i),\end{align}

    现在我们将其换成

    \begin{align}\delta^{(2)}_i =  \left( \left( \sum_{j=1}^{s_{2}} W^{(2)}_{ji} \delta^{(3)}_j \right)+ \beta \left( - \frac{\rho}{\hat\rho_i} + \frac{1-\rho}{1-\hat\rho_i} \right) \right) f'(z^{(2)}_i) .\end{align}

    就可以了。


    有一个需要注意的地方就是我们需要知道 \textstyle \hat\rho_i 来计算这一项更新。所以在计算任何神经元的后向传播之前,你需要对所有的训练样本计算一遍前向传播,从而获取平均激活度。如果你的训练样本可以小到被整个存到内存之中(对于编程作业来说,通常如此),你可以方便地在你所有的样本上计算前向传播并将得到的激活度存入内存并且计算平均激活度 。然后你就可以使用事先计算好的激活度来对所有的训练样本进行后向传播的计算。如果你的数据量太大,无法全部存入内存,你就可以扫过你的训练样本并计算一次前向传播,然后将获得的结果累积起来并计算平均激活度 \textstyle \hat\rho_i (当某一个前向传播的结果中的激活度 \textstyle a^{(2)}_i 被用于计算平均激活度 \textstyle \hat\rho_i 之后就可以将此结果删除)。然后当你完成平均激活度 \textstyle \hat\rho_i 的计算之后,你需要重新对每一个训练样本做一次前向传播从而可以对其进行后向传播的计算。对于后一种情况,你对每一个训练样本需要计算两次前向传播,所以在计算上的效率会稍低一些。

    证明上面算法能达到梯度下降效果的完整推导过程不再本教程的范围之内。不过如果你想要使用经过以上修改的后向传播来实现自编码神经网络,那么你就会对目标函数 \textstyle J_{\rm sparse}(W,b) 做梯度下降。使用梯度验证方法,你可以自己来验证梯度下降算法是否正确。。

     

      损失函数的偏导数的求法:

      如果不加入稀疏规则,则正常情况下由损失函数求损失函数偏导数的过程如下:

       

      而加入了稀疏性后,神经元节点的误差表达式由公式:

       

      变成公式:

       

      

      梯度下降法求解:

      有了损失函数及其偏导数后就可以采用梯度下降法来求网络最优化的参数了,整个流程如下所示:

      

      从上面的公式可以看出,损失函数的偏导其实是个累加过程,每来一个样本数据就累加一次。这是因为损失函数本身就是由每个训练样本的损失叠加而成的,而按照加法的求导法则,损失函数的偏导也应该是由各个训练样本所损失的偏导叠加而成。从这里可以看出,训练样本输入网络的顺序并不重要,因为每个训练样本所进行的操作是等价的,后面样本的输入所产生的结果并不依靠前一次输入结果(只是简单的累加而已,而这里的累加是顺序无关的)。

     

    二、降噪编码器

    当采用无监督的方法分层预训练深度网络的权值时,为了学习到较鲁棒的特征,可以在网络的可视层(即数据的输入层)引入随机噪声,这种方法称为Denoise Autoencoder(简称dAE),由Bengio在08年提出,见其文章Extracting and composing robust features with denoising autoencoders.使用dAE时,可以用被破坏的输入数据重构出原始的数据(指没被破坏的数据),所以它训练出来的特征会更鲁棒。本篇博文主要是根据Benigio的那篇文章简单介绍下dAE,然后通过2个简单的实验来说明实际编程中该怎样应用dAE。这2个实验都是网络上现成的工具稍加改变而成,其中一个就是matlab的Deep Learning toolbox,见https://github.com/rasmusbergpalm/DeepLearnToolbox,另一个是与python相关的theano,参考:http://deeplearning.net/tutorial/dA.html.

     

      基础知识:

      首先来看看Bengio论文中关于dAE的示意图,如下:

      

      由上图可知,样本x按照qD分布加入随机噪声后变为 ,按照文章的意思,这里并不是加入高斯噪声,而是以一定概率使输入层节点的值清为0,这点与上篇博文介绍的dropoutDeep learning:四十一(Dropout简单理解))很类似,只不过dropout作用在隐含层。此时输入到可视层的数据变为,隐含层输出为y,然后由y重构x的输出z,注意此时这里不是重构 ,而是x.

      Bengio对dAE的直观解释为:1.dAE有点类似人体的感官系统,比如人眼看物体时,如果物体某一小部分被遮住了,人依然能够将其识别出来,2.多模态信息输入人体时(比如声音,图像等),少了其中某些模态的信息有时影响也不大。3.普通的autoencoder的本质是学习一个相等函数,即输入和重构后的输出相等,这种相等函数的表示有个缺点就是当测试样本和训练样本不符合同一分布,即相差较大时,效果不好,明显,dAE在这方面的处理有所进步。

      当然作者也从数学上给出了一定的解释。

      1. 流形学习的观点。一般情况下,高维的数据都处于一个较低维的流形曲面上,而使用dAE得到的特征就基本处于这个曲面上,如下图所示。而普通的autoencoder,即使是加入了稀疏约束,其提取出的特征也不是都在这个低维曲面上(虽然这样也能提取出原始数据的主要信息)。

      

      2.自顶向下的生成模型观点的解释。3.信息论观点的解释。4.随机法观点的解释。这几个观点的解释数学有一部分数学公式,大家具体去仔细看他的paper

      当在训练深度网络时,且采用了无监督方法预训练权值,通常,Dropout和Denoise Autoencoder在使用时有一个小地方不同:Dropout在分层预训练权值的过程中是不参与的,只是后面的微调部分引入;而Denoise Autoencoder是在每层预训练的过程中作为输入层被引入,在进行微调时不参与。另外,一般的重构误差可以采用均方误差的形式,但是如果输入和输出的向量元素都是位变量,则一般采用交叉熵来表示两者的差异。

    三、收缩自动编码器

    Contractive autoencoder是autoencoder的一个变种,其实就是在autoencoder上加入了一个规则项,它简称CAE(对应中文翻译为?)。通常情况下,对权值进行惩罚后的autoencoder数学表达形式为:

       

      这是直接对W的值进行惩罚的,而今天要讲的CAE其数学表达式同样非常简单,如下:

       

      其中的 是隐含层输出值关于权重的雅克比矩阵,而   表示的是该雅克比矩阵的F范数的平方,即雅克比矩阵中每个元素求平方

      

      然后求和,更具体的数学表达式为:

       

      关于雅克比矩阵的介绍可参考雅克比矩阵&行列式——单纯的矩阵和算子,关于F范数可参考我前面的博文Sparse coding中关于矩阵的范数求导中的内容。

      有了loss函数的表达式,采用常见的mini-batch随机梯度下降法训练即可。

      关于为什么contrative autoencoder效果这么好?paper中作者解释了好几页,好吧,我真没完全明白,希望懂的朋友能简单通俗的介绍下。下面是读完文章中的一些理解:

      好的特征表示大致有2个衡量标准:1. 可以很好的重构出输入数据; 2.对输入数据一定程度下的扰动具有不变形。普通的autoencoder和sparse autoencoder主要是符合第一个标准。而deniose autoencoder和contractive autoencoder则主要体现在第二个。而作为分类任务来说,第二个标准显得更重要。

      雅克比矩阵包含数据在各种方向上的信息,可以对雅克比矩阵进行奇异值分解,同时画出奇异值数目和奇异值的曲线图,大的奇异值对应着学习到的局部方向可允许的变化量,并且曲线越抖越好(这个图没看明白,所以这里的解释基本上是直接翻译原文中某些观点)。

      另一个曲线图是contractive ratio图,contractive ratio定义为:原空间中2个样本直接的距离比上特征空间(指映射后的空间)中对应2个样本点之间的距离。某个点x处局部映射的contraction值是指该点处雅克比矩阵的F范数。按照作者的观点,contractive ration曲线呈上升趋势的话更好(why?),而CAE刚好符合。

      总之Contractive autoencoder主要是抑制训练样本(处在低维流形曲面上)在所有方向上的扰动。

     

    四、栈式自动编码器

    单自动编码器,充其量也就是个强化补丁版PCA,只用一次好不过瘾。

    于是Bengio等人在2007年的  Greedy Layer-Wise Training of Deep Networks 中,

    仿照stacked RBM构成的DBN,提出Stacked AutoEncoder,为非监督学习在深度网络的应用又添了猛将。

    这里就不得不提  “逐层初始化”(Layer-wise Pre-training),目的是通过逐层非监督学习的预训练,

    来初始化深度网络的参数,替代传统的随机小值方法。预训练完毕后,利用训练参数,再进行监督学习训练。

     

    Part I  原理

    非监督学习网络训练方式和监督学习网络的方式是相反的。

    监督学习网络当中,各个Layer的参数W受制于输出层的误差函数,因而Layeri参数的梯度依赖于Layeri+1的梯度,形成了"一次迭代-更新全网络"反向传播。

    但是在非监督学习中,各个Encoder的参数W只受制于当前层的输入,因而可以训练完Encoderi,把参数转给Layeri,利用优势参数传播到Layeri+1,再开始训练

    形成"全部迭代-更新单层"的新训练方式。这样,Layeri+1效益非常高,因为它吸收的是Layeri完全训练奉献出的精华Input。

     

     

    Part II  代码与实现

    主要参考  http://deeplearning.net/tutorial/SdA.html

    栈式机在构造函数中,构造出各个Layer、Encoder,并且存起来。

    Theano在构建栈式机中,易错点是Encoder、Layer的参数转移。

    我们知道,Python的列表有深浅拷贝一说。Theano所有被shared标记的变量都是浅拷贝。

    因而首先有这样的错误写法:

    def __init__(self,rng,input,n_in,n_out,layerSize):
          ......
          for i in xrange(len(layerSize)):
                ......
                da.W=hidenlayer.W
                da.bout=hidenlayer.b

    然后你在外部为da做grad求梯度的时候就报错了,提示说params和cost函数不符合。

    这是因为cost函数的Tensor表达式在写cost函数时就确定了,这时候da这个对象刚好构造完,因而Tensor表达式中的da.W是构造随机值。

    然后我们在da构造完了之后,手贱把da.W指向的内存改变了(浅拷贝相当于引用),这样算出的grad根本就不对。

    其实这样写反了,又改成了这样

    def __init__(self,rng,input,n_in,n_out,layerSize):
          ......
          for i in xrange(len(layerSize)):
                ......
                hidenlayer.W=da.W
                hidenlayer.b=da.bout

    好吧,这样不会报错了,而且每训练一个Encoder,用get_value查看Layer的值确实改变了。但是,训练Encoderi+1的时候,怎么感觉没效果?

    其实是真的没效果,因为Layeri的参数根本没有传播到Layeri+1去。

    Theano采用Python、C双内存区设计,在C代码中训练完Encoderi时,参数并没有转到Layeri中。但是我们明明建立了浅拷贝啊?

    原来updates函数在C内存区中,根本没有觉察到浅拷贝关系,因为它在Python内存区中。

    正确做法是像教程这样,在da构造时建立浅拷贝关系,当编译成C代码之后,所有Python对象要在C内存区重新构造,自然就在C内存区触发了浅拷贝。

     da=dA(rng,layerInput,InputSize,self.layerSize[i],hidenlayer.W,hidenlayer.b)

     或者训练完Encoderi,强制把Encoderi参数注入到C内存区的Layeri里。

    updateModel=function(inputs=[],outputs=[],updates=[(....)],
    updateModel()

    Theano的写法风格近似于函数式语言,对象、函数中全是数学模型。一旦构造完了之后,就无法显式赋值。

    所以,在Python非构造函数里为对象赋值是愚蠢的,效果仅限于Python内存区。但是大部分计算都在C内存区,所以需要updates手动把值打进C内存区。

    updates是沟通两区的桥梁,一旦发现Python内存区中有建立浅拷贝关系,就会把C内存区中值更新到Python内存区。(有利于Python中保存参数)

    但是绝对不会自动把Python内存区值,更新到C内存区当中。(这点必须小心)

    这种做法可以扩展到,监督训练完之后,参数的保存与导入。

    五、变分自动编码器

    1. 神秘变量与数据集

    现在有一个数据集DX(dataset, 也可以叫datapoints),每个数据也称为数据点。
    X是一个实际的样本集合,我们假定这个样本受某种神秘力量操控,但是我们也无从知道这些神秘力量是什么?那么我们假定这股神秘力量有n个,起名字叫power1,power2,,powern吧,他们的大小分别是z1,z2,,zn,称之为神秘变量表示成一个向量就是

    z=⎛⎝⎜⎜⎜⎜z1z2zn⎞⎠⎟⎟⎟⎟

    z也起个名字叫神秘组合

    一言以蔽之:神秘变量代表了神秘力量神秘组合关系。
    用正经的话说就是:隐变量(latent variable)代表了隐因子(latent factor)的组合关系。

    这里我们澄清一下隶属空间,假设数据集DX是m个点,这m个点也应该隶属于一个空间,比如一维的情况,假如每个点是一个实数,那么他的隶属空间就是实数集,所以我们这里定义一个DX每个点都属于的空间称为XS,我们在后面提到的时候,你就不再感到陌生了。

    神秘变量z可以肯定他们也有一个归属空间称为ZS。

    下面我们就要形式化地构造X与Z的神秘关系了,这个关系就是我们前面说的神秘力量,直观上我们已经非常清楚,假设我们的数据集就是完全由这n个神秘变量全权操控的,那么对于X中每一个点都应该有一个n个神秘变量的神秘组合zj来神秘决定。

    接下来我们要将这个关系再简化一下,我们假设这n个神秘变量不是能够操控X的全部,还有一些其他的神秘力量,我们暂时不考虑,那么就可以用概率来弥补这个缺失,为什么呢?举个例子,假设我们制造了一个机器可以向一个固定的目标发射子弹,我们精确的计算好了打击的力量和角度,但由于某些难以控制的因素,比如空气的流动,地球的转动导致命中的目标无法达到精准的目的,而这些因素可能十分巨大和繁多,但是他们并不是形成DX的主因素,根据大数定理,这些所有因素产生的影响可以用高斯分布的概率密度函数来表示。它长这样:
    p(x|μ,σ2)=12π√σe12(xμσ)2

    μ=0时,就变成了这样:
    p(x|σ2)=12π√σex22σ2
    这是一维高斯分布的公式,那么多维的呢?比较复杂,推导过程见知乎,长这样:

    不管怎样,你只要记住我们现在没有能力关注全部的神秘变量,我们只关心若干个可能重要的因素,这些因素的分布状况可以有各种假设,我们回头再讨论他们的概率分布问题,我们现在假定我们对他们的具体分布情况也是一无所知,我们只是知道他们处于ZS空间内。
    前面说到了一个神秘组合,如果一个数据集X对应的神秘组合完全一样,那么这个数据集就是一个单一的分类数据集,如果是多个,那么就是多分类数据集,但如果是一个连续的组合数据,那么就是一个有点分不清界限的复杂数据集,就好比,我们这个数据集是一条线段的集合,线段的长度是唯一的神秘变量,那么只要长度在一个范围内连续变化,那么这个集合里的线段你就会发现分散的很均匀,你几乎没有办法区分开他们,也没法给他们分成几类,但如果这个长度值只能选择1,3,5,那么当你观察这个数据集的时候,你会发现他们会聚在三堆儿里。如果这个线段的生成完全依靠的是计算机,那么每一堆儿都是完全重合的,但如果是人画的,就可能因为误差,没法完全重合,这没法重合的部分就是我们说的其他复杂因素,我们通常用一个高斯分布来把它代表了。好,我们已经基本清晰了,我们该给这个神秘组合一个形式化的描述了。
    假设有两个变量,zZS 和 xXS,存在一个确定性函数族f(z;θ),族中的每个函数由θΘ唯一确定,f:ZS×ΘXS,当θ固定,z是一个随机变量(概率密度函数为Pz(z))时,那么f(z;θ)就是定义在XS上的随机变量x,对应的概率密度函数可以写成g(x)。
    那么我们的目标就是优化θ从而寻找到一个f,能够是随机变量x的采样和X非常的像。这里需要注意一下,x是一个变量,DX是已经现成的数据集,x不属于DX,我特意将名字起的有区分度。
    这样,f就是那个神秘力量通道,他把这些神秘力量的力度,通过f变成了x变量,而这个x变量就是与数据集DX具有直接关系的随机变量。

    设一个数据集为DX,那么这个数据集存在的概率为Pt(DX),则根据贝叶斯公式有:

    Pt(DX)=Pxz(DX|z;θ)Pz(z)dz; (1)

    其中,Pxz(DX|z;θ)是我们新定义的概率密度函数,我们前面知道f是将z映射成x,而x又与DX有某种直接的关系,这个直接关系可以表示成Px(DX|x),那么Pt(DX)=Px(DX|x)g(x)dx

    这样我们就直接定义个Pxz(DX|z;θ) 来替换Px(DX|x)g(x),从而表示z与DX的关系了。

    好了,其实公式(1)就是我们的神秘力量与观察到的数据集之间的神秘关系,这个关系的意思我们直白的说就是:当隐秘变量按照某种规律存在时,就非常容易产生现在我们看到的这个数据集。那么,我们要做的工作就是当我们假定有n个神秘力量时,我们能够找到一个神奇的函数f,将神秘力量的变化转化成神奇的x的变化,这个x能够轻而易举地生成数据集DX。
    从上面的描述里面我们看到,f是生成转换函数,公式(1)不表示这种转换关系,而是这种关系的最大似然估计(maximum likelihood),它的意思是找到最有可能生成DX这个数据集的主导函数f。

    接下来我们回到讨论Pxz(DX|z;θ)这个概率密度函数上来,我们前面说过,如果z是全部的神秘力量,那么它产生的变量x就一定固定的,即当z取值固定时,x取值固定,但是现实中还有很多其他的因素,因而x的取值还与他们有关,他们的影响力,最终反映成了高斯函数,所以我们大胆假定Pxz是一个高斯分布的概率密度函数,即Pxz(DX|z;θ)=N(DX|f(x;θ),σ2I)

    注意z的分布我们依然是未知的。

    假定我们知道z现在取某一个或几个特定值,那么我们就可以通过Gradient Descent来找到一个θ尽量满足z能够以极高的概率生成我们希望的数据集DX。再一推广,就变成了,z取值某一范围,但去几个特定值或某一取值范围是就面临z各种取值的概率问题,我们回头再讨论这个棘手的问题,你现在只要知道冥冥之中,我们似乎可以通过学习参数θ寻找最优解就行了。

    OK,我们还要说一个关键问题,就是我们确信f是存在的,我们认为变量与神秘变量之间的关系一定可以用一个函数来表示。

    2. 变分自编码器(VAE)

    本节,我们探讨如何最大化公式(1)。首先,我们要讨论怎样确定神秘变量z,即z应该有几个维度,每个维度的作用域是什么?更为较真的,我们可能甚至要追究每一维度都代表什么?他们之间是不是独立的?每个维度的概率分布是什么样的?

    如果我们沿着这个思路进行下去,就会陷入泥潭,我们可以巧妙地避开这些问题,关键就在于让他们继续保持“神秘”!

    我们不关心每一个维度代表什么含义,我们只假定存在这么一群相互独立的变量,维度我们也回到之前的讨论,我们虽然不知道有多少,我们可以假定有n个主要因素,n可以定的大一点,比如假设有4个主因素,而我们假定有10个,那么最后训练出来,可能有6个长期是0。最后的问题需要详细讨论一下,比较复杂,就是z的概率分布和取值问题。

    既然z是什么都不知道,我们是不是可以寻找一组新的神秘变量w,让这个w服从标准正态分布N(0,I)。I是单位矩阵,然后这个w可以通过n个复杂函数,转换成z呢?有了神经网络这些也是可行的,假设这些复杂函数分别是h1,h2,,hn,那么有z1=h1(w1),,zn=hn(wn)。而z的具体分布是什么,取值范围是多少我们也不用关心了,反正由一个神经网络去算。回想一下P(DX|z;θ)=N(DX|f(z;θ),σ2×I),我们可以想象,如果f(z;θ)是一个多层神经网络,那么前几层就用来将标准正态分布的w变成真正的隐变量z,后面几层才是将z映射成x,但由于w和z是一一对应关系,所以w某种意义上说也是一股神秘力量。就演化成w和x的关系了,既然w也是神秘变量,我们就还是叫回z,把那个之前我们认为的神秘变量z忘掉吧。

    好,更加波澜壮阔的历程要开始了,请坐好。

    我们现在已经有了

    Pz(z)=N(0,I),

    Pxz(DX|z;θ)=N(DX|f(x;θ),σ2I),

    Pt(DX)=Pxz(DX|z;θ)Pz(z)dz,

    我们现在就可以专心攻击f了,由于f是一个神经网络,我们就可以梯度下降了。但是另一个关键点在于我们怎么知道这个f生成的样本,和DX更加像呢?如果这个问题解决不了,我们根本都不知道我们的目标函数是什么。

    3. 设定目标函数

    我们先来定义个函数 Q(z|DX),数据集DX的发生,z的概率密度函数,即如果DX发生,Q(z|DX)就是z的概率密度函数,比如一个数字图像0,z隐式代表0的概率就很大,而那些代表1的概率就很小。如果我们有办法搞到这个Q的函数表示,我们就可以直接使用DX算出z的最佳值了。为什么会引入Q呢?其实道理很简单,如果DX是x这个变量直接生成的,要想找回x的模型,就要引入一个概率密度函数T(x|DX),亦即针对DX,我们要找到一个x的最佳概率密度函数。
    现在的问题就变成了,我们可以根据DX计算出Q(z|DX)来让他尽量与理想的Pz(z|DX)尽量的趋同,这就要引入更加高深的功夫了——相对熵,也叫KL散度(Kullback-Leibler divergence,用 D表示)。

    离散概率分布的KL公式

    KL(pq)=p(x)logp(x)q(x)

    连续概率分布的KL公式

    KL(pq)=p(x)logp(x)q(x)dx

    Pz(z|DX)和Q(z|DX)的KL散度为

    D[Q(z|DX)Pz(z|DX)]=Q(z|DX)[logQ(z|DX)logPz(z|DX)]
    也可写成
    D[Q(z|DX)Pz(z|DX)]=EzQ[logQ(z|DX)logPz(z|DX)]

    通过贝叶斯公式

    Pz(z|DX)=P(DX|z)P(z)P(DX)
    这里不再给P起名,其实Pz(z)直接写成P(z)也是没有任何问题的,前面只是为了区分概念,括号中的内容已经足以表意。

    D[Q(z|DX)Pz(z|DX)]=EzQ[logQ(z|DX)logP(DX|z)logP(z)]+logP(DX)

    因为logP(DX)与z变量无关,直接就可以提出来了,进而得到闪闪发光的公式(2):

    logP(DX)D[Q(z|DX)P(z|DX)]=EzQ[logP(DX|z)]D[Q(z|DX)P(z)]; (2)

    公式(2)是VAE的核心公式,我们接下来分析一个这个公式。
    公式的左边有我们的优化目标P(DX),同时携带了一个误差项,这个误差项反映了给定DX的情况下的真实分布Q与理想分布P的相对熵,当Q完全符合理想分布时,这个误差项就为0,而等式右边就是我们可以使用梯度下降进行优化的,这里面的Q(z|DX)特别像一个DX->z的编码器,P(DX|z)特别像z->DX的解码器,这就是VAE架构也被称为自编码器的原因。

    由于DX早已不再有分歧,我们在这里把所有的DX都换成了X。

    我们现在有公式(2)的拆分:
    – 左侧第一项:logP(X)
    – 左侧第二项:D(Q(z|XP(z|X))
    – 右边第一项:EzQ[logP(X|z)]
    – 右边第二项:D[Q(z|X)P(z)]

    还有下面这些:
    – P(z)=N(0,I),
    – P(X|z)=N(X|f(z),σ2I),
    – Q(z|X)=N(z|μ(X),Σ(X))

    我们再明确一下每个概率的含义:
    – P(X)——当前这个数据集发生的概率,但是他的概率分布我们是不知道,比如,X的空间是一个一维有限空间,比如只能取值0-9的整数,而我们的 X = { 0, 1, 2, 3, 4 },那么当概率分布是均匀的时候,P(X)就是0.5,但是如果不是这个分布,就不好说是什么了,没准是0.1, 0.01,都有可能。P(X)是一个函数,就好像是一个人,当你问他X=某个值的时候,他能告诉发生的概率。
    – P(z) —— 这个z是我们后来引入的那个w,还记得吗?他们都已经归顺了正态分布,如果z是一维的,那他就是标准正态分布N(0, I)。
    – P(X|z) —— 这个函数的含义是如果z给定一个取值,那么就知道X取某个值的概率,还是举个例子,z是一个神奇的变量,可以控制在计算机屏幕上出现整个屏幕的红色并且控制其灰度,z服从N(0,1)分布,当z=0时代表纯正的红色,z越偏离0,屏幕的红色就越深,那么P(X|z)就表示z等于某个值时X=另一值的概率,由于计算机是精确控制的,没有额外的随机因素,所以如果z=0能够导致X取一个固定色值0xFF0000,那么P(X=0xFF0000|z=0)=1,P(x!=0xFF0000|z=0) = 0,但如果现实世界比较复杂附加其他的随机因素,那么就可能在z确定出来的X基础值之上做随机了。这就是我们之前讨论的,大数定理,P(X|z)=N(X|f(x),σ2I)。f(z)就是X与z直接关系的写照。
    – P(z|X) —— 当X发生时,z的概率是多少呢?回到刚才计算机屏幕的例子,就非常简单了P(z=0|X=0xFF0000) = 1, P(z!=0|X=0xFF0000) = 0,但是由于概率的引入,X|z可以简化成高斯关系,相反,也可以简化高斯关系。这个解释对下面的Q同样适用。
    – Q(z) —— 对于Q的分析和P的分析是一样的,只不过Q和P的不同时,我们假定P是那个理想中的分布,是真正决定X的最终构成的背后真实力量,而Q是我们的亲儿子,试着弄出来的赝品,并且希望在现实世界通过神经网络,让这个赝品能够尝试控制产生X。当这个Q真的行为和我们理想中的P一模一样的时候,Q就是上等的赝品了,甚至可以打出如假包换的招牌。我们的P已经简化成N(0,I),就意味着Q只能向N(0, I)靠拢。
    – Q(z|X) —— 根据现实中X和Q的关系推导出的概率函数, 当X发生时,对应的z取值的概率分布情况。
    – Q(X|z) —— 现实中z发生时,取值X的概率。

    我们的目标是优化P(X),但是我们不知道他的分布,所以根本没法优化,这就是我们没有任何先验知识。所以有了公式(2),左边第二项是P(z|X)和Q(z|X)的相对熵,意味着X发生时现实的分布应该与我们理想的分布趋同才对,所以整个左边都是我们的优化目标,只要左边越大就越好,那么右边的目标就是越大越好。

    右边第一项:EzQ[logP(X|z)]就是针对面对真实的z的分布情况(依赖Q(z|X),由X->z的映射关系决定),算出来的X的分布,类似于根据z重建X的过程。
    右边第二项:D[Q(z|X)P(z)] 就是让根据X重建的z与真实的z尽量趋近,由于P(z)是明确的N(0, I),而Q(z|X)是也是正态分布,其实就是要让Q(z|X)趋近与标准正态分布。

    现在我们对这个公式的理解更加深入了。接下来,我们要进行实现的工作。

    4. 实现

    针对右边两项分别实现
    第二项是Q(z|X)与N(0, I)的相对熵,X->z构成了编码器部分。
    Q(z|x)是正态分布,两个正态分布的KL计算公式如下(太复杂了,我也推不出来,感兴趣的看[1]):

    KL(N(μ,Σ)N(0,I))=12[log[det(Σ)]d+tr(Σ)+μTμ]

    det是行列式,tr是算矩阵的秩,d是I的秩即d=tr(I)。

    变成具体的神经网络和矩阵运算,还需要进一步变化该式:

    KL(N(μ,Σ)N(0,I))=12i[log(Σi)+Σi+μ2i1]
    OK,这个KL我们也会计算了,还有一个事情就是编码器网络,μ(X)和Σ(X)都使用神经网络来编码就可以了。

    第一项是EzQ[logP(X|z)]代表依赖z重建出来的数据与X尽量地相同,z->X重建X构成了解码器部分,整个重建的关键就是f函数,对我们来说就是建立一个解码器神经网络。

    到此,整个实现的细节就全都展现在下面这张图里了

    由于这个网络传递结构的一个环节是随机采样,导致无法反向传播,所以聪明的前辈又将这个结构优化成了这样:

    这样就可以对整个网络进行反向传播训练了。

    具体的实现代码,我实现在了这里:

    https://github.com/vaxin/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/variational_autoencoder.py

    里面的每一步,都有配合本文章的对照解释。

    5. 延伸思考

    之所以关注VAE,是从文献[4]引发的,由于视觉早期的概念形成对于之后的视觉认知起了十分关键的作用,我们有理由相信,在神经网络训练时,利用这种递进关系,先构建具有基础认知能力的神经网络,再做高级认知任务时会有极大的效果提升。但通过前面神秘变量的分析,我们发现,为了充分利用高斯分布,我们将w替换成了z,也就是说真正的隐变量隐藏在f的神经网络里面,而现在的z反而容易变成说不清楚的东西,这一不利于后续的时候,二来我们需要思考,是否应该还原真实的z,从而在层次化递进上有更大的发挥空间。

    展开全文
  • 一些特征转换技术有主成分分析(PCA)、矩阵分解、自动编码器(Autoencoders)、t-Sne、UMAP等。 本文主要介绍了主成分分析以及自动编码器两种方法,具体分析两者的优缺点,并且通过一个生动的示例进行详解。 主成分...

    降维是一种减少特征空间维度以获得稳定的、统计上可靠的机器学习模型的技术。降维主要有两种途径:特征选择和特征变换。

    特征选择通过选择重要程度最高的若干特征,移除共性的或者重要程度较低的特征。

    特征转换也称为特征提取,试图将高维数据投影到低维空间。一些特征转换技术有主成分分析(PCA)、矩阵分解、自动编码器(Autoencoders)、t-Sne、UMAP等。

    本文主要介绍了主成分分析以及自动编码器两种方法,具体分析两者的优缺点,并且通过一个生动的示例进行详解。

    主成分分析

    主成分分析是一种无监督技术,将原始数据投影到若干高方差方向(维度)。这些高方差方向彼此正交,因此投影数据的相关性非常低或几乎接近于 0。这些特征转换是线性的,具体方法是:

    步骤一:计算相关矩阵数据,相关矩阵的大小为 n*n。

    步骤二:计算矩阵的特征向量和特征值。

    步骤三:选取特征值较高的 k 个特征向量作为主方向。

    步骤四:将原始数据集投影到这 k 个特征向量方向,得到 k 维数据,其中 k≤n。

    自动编码器

    自动编码器是一种无监督的人工神经网络,它将数据压缩到较低的维数,然后重新构造输入。自动编码器通过消除重要特征上的噪声和冗余,找到数据在较低维度的表征。它基于编解码结构,编码器将高维数据编码到低维,解码器接收低维数据并尝试重建原始高维数据。

    自动编码器基本结构示意图

    深层自动编码器结构示意图

    上图中, X 是输入数据,z 是 X 在低维空间的数据表征,X’ 是重构得到的数据。根据激活函数的不同,数据从高纬度到低纬度的映射可以是线性的,也可以是非线性的。

    性能对比:主成分分析 VS 自动编码器

    1. PCA 只能做线性变换;而自动编码器既可以做线性变换,也可以做非线性变换。
    2. 由于既有的 PCA 算法是十分成熟的,所以计算很快;而自动编码器需要通过梯度下降算法进行训练,所以需要花费更长的时间。
    3. PCA 将数据投影到若干正交的方向;而自动编码器降维后数据维度并不一定是正交的。
    4. PCA 是输入空间向最大变化方向的简单线性变换;而自动编码器是一种更复杂的技术,可以对相对复杂的非线性关系进行建模。
    5. 依据经验来看,PCA 适用于数据量较小的场景;而自动编码器可以用于复杂的大型数据集。
    6. PCA 唯一的超参数是正交向量的数量;而自动编码器的超参数则是神经网络的结构参数。
    7. 单层的并且采用线性函数作为激活函数的自动编码器与 PCA 性能一致;但是多层的以非线性函数作为激活函数的自动编码器(深度自动编码器)能够具有很好的性能,虽然可能会存在过拟合,但是可以通过正则化等方式进行解决。

    降维示例:图像数据

    示例图片

    该示例图片的数据维度为 360*460。我们将尝试通过 PCA 和自动编码器将数据规模降低为原有的 10%。

    PCA 方法

    pct_reduction = 0.10
    reduced_pixel  = int( pct_reduction* original_dimensions[1])
    #Applying PCA
    pca = PCA(n_components=reduced_pixel)
    pca.fit(image_matrix)
    #Transforming the input matrix
    X_transformed = pca.transform(image_matrix)
    print("Original Input dimesnions {}".format(original_dimensions))
    print("New Reduced dimensions {}".format(X_transformed.shape))
    

    输出如下:

    Original Input dimesnions (360, 460)
    New Reduced dimensions (360, 46)
    

    检查各维度的相关性:

    df_pca = pd.DataFrame(data = X_transformed,columns=list(range(X_transformed.shape[1])))
    figure = plt.figure(figsize=(10,6))
    corrMatrix = df_pca.corr()
    sns.heatmap(corrMatrix, annot=False)
    plt.show()
    

    PCA降维后各维度相关性

    从上图可以看出,PCA 降维后各个维度都是不相关的,也就是完全正交。

    接下来,我们通过降维后的数据来重构原始数据:

    reconstructed_matrix = pca.inverse_transform(X_transformed)
    reconstructed_image_pca = Image.fromarray(np.uint8(reconstructed_matrix))
    plt.figure(figsize=(8,12))
    plt.imshow(reconstructed_image_pca,cmap = plt.cm.gray)
    

    PCA 图像重构

    计算重构后图像的均方根误差:

    def my_rmse(np_arr1,np_arr2):
        dim = np_arr1.shape
        tot_loss = 0
        for i in range(dim[0]):
            for j in range(dim[1]):
                tot_loss += math.pow((np_arr1[i,j] - np_arr2[i,j]),2)
        return round(math.sqrt(tot_loss/(dim[0]* dim[1]*1.0)),2)
    error_pca = my_rmse(image_matrix,reconstructed_matrix)
    

    计算可知,均方根误差为11.84。

    单层的以线性函数作为激活函数的自动编码器

    # Standarise the Data
    X_org = image_matrix.copy()
    sc = StandardScaler()
    X = sc.fit_transform(X_org)
    # this is the size of our encoded representations
    encoding_dim = reduced_pixel 
    # this is our input placeholder
    input_img = Input(shape=(img.width,))
    # "encoded" is the encoded representation of the input
    encoded = Dense(encoding_dim, activation='linear')(input_img)
    # "decoded" is the lossy reconstruction of the input
    decoded = Dense(img.width, activation=None)(encoded)
    # this model maps an input to its reconstruction
    autoencoder = Model(input_img, decoded)
    #Encoder
    encoder = Model(input_img, encoded)
    # create a placeholder for an encoded (32-dimensional) input
    encoded_input = Input(shape=(encoding_dim,))
    # retrieve the last layer of the autoencoder model
    decoder_layer = autoencoder.layers[-1]
    # create the decoder model
    decoder = Model(encoded_input, decoder_layer(encoded_input))
    autoencoder.compile(optimizer='adadelta', loss='mean_squared_error')
    autoencoder.fit(X, X,
                    epochs=500,
                    batch_size=16,
                    shuffle=True)
    encoded_imgs = encoder.predict(X)
    decoded_imgs = decoder.predict(encoded_imgs)
    

    自动编码器结构

    检查各维度的相关性:

    df_ae = pd.DataFrame(data = encoded_imgs,columns=list(range(encoded_imgs.shape[1])))
    figure = plt.figure(figsize=(10,6))
    corrMatrix = df_ae.corr()
    sns.heatmap(corrMatrix, annot=False)
    plt.show()
    

    自动编码器降维后各维度相关性

    相关矩阵表明新的变换特征具有一定的相关性。皮尔逊相关系数与0有很大的偏差。

    接下来,我们通过降维后的数据来重构原始数据:

    X_decoded_ae = sc.inverse_transform(decoded_imgs)
    reconstructed_image_ae = Image.fromarray(np.uint8(X_decoded_ae))
    plt.figure(figsize=(8,12))
    plt.imshow(reconstructed_image_ae,cmap = plt.cm.gray)
    

    自动编码器重构后的图像

    计算重构后图像的均方根误差:

    error_ae = my_rmse(image_matrix,X_decoded_ae)
    

    计算可知,均方根误差为12.15。单层线性激活的自动编码器和 PCA 性能几乎一致。

    三层的以非线性函数为激活函数的自动编码器

    input_img = Input(shape=(img.width,))
    encoded1 = Dense(128, activation='relu')(input_img)
    encoded2 = Dense(reduced_pixel, activation='relu')(encoded1)
    decoded1 = Dense(128, activation='relu')(encoded2)
    decoded2 = Dense(img.width, activation=None)(decoded1)
    autoencoder = Model(input_img, decoded2)
    autoencoder.compile(optimizer='adadelta', loss='mean_squared_error')
    autoencoder.fit(X,X,
                    epochs=500,
                    batch_size=16,
                    shuffle=True)
    # Encoder
    encoder = Model(input_img, encoded2)
    # Decoder
    decoder = Model(input_img, decoded2)
    encoded_imgs = encoder.predict(X)
    decoded_imgs = decoder.predict(X)
    

    自动编码器模型结构

    图像重构:

    X_decoded_deep_ae = sc.inverse_transform(decoded_imgs)
    reconstructed_image_deep_ae = Image.fromarray(np.uint8(X_decoded_deep_ae))
    plt.figure(figsize=(8,12))
    plt.imshow(reconstructed_image_deep_ae,cmap = plt.cm.gray)
    

    计算均方误差:

    error_dae = my_rmse(image_matrix,X_decoded_deep_ae)
    

    多层自动编码器的均方误差为 8.57,性能优于 PCA,提升了 28%。

    具有非线性激活的附加层的自动编码器能够更好地捕获图像中的非线性特征。它能够比PCA更好地捕捉复杂的模式和像素值的突然变化。但是它需要花费相对较高的训练时间和资源。

    总结

    本文主要介绍了主成分分析以及自动编码器两种方法,具体分析两者的优缺点,并且通过一个生动的示例进行详解。

    完整代码 https://github.com/samread81/PCA-versus-AE

    作者:Abhishek Mungoli

    deephub翻译组:Oliver Lee

    展开全文
  • 自动编码器是一种神经网络,可用于学习原始数据的压缩表示。自动编码器由编码器和解码器子模型组成。编码器压缩输入,而解码器尝试根据编码器提供的压缩版本重新创建输入。训练后,将保存编码器模型,并丢弃解码...
  • 对抗式自动编码器的行为类似于,迫使自动编码器的潜在空间遵循预先定义的先验。 在对抗自动编码器的情况下,可以任意定义此潜在空间,并轻松对其进行采样并将其馈入网络中的鉴别器。 左图显示了在经过2D高斯先验后...
  • 量子自动编码器:经典量子(变分)自动编码器的实现
  • 图像自动编码器-源码

    2021-02-18 15:42:28
    图像自动编码
  • 深度学习:自动编码器基础和类型

    千次阅读 2018-12-21 14:37:34
    本文转载自《机器之心》,原文链接:...Mobibit 创始人兼 CEO Pramod Chandrayan 近日在 codeburst.io 上发文对自动编码器的基础知识和类型进行了介绍并给出了代码实例。机器之心对本文进行了...
  • 使用MovieLens1M数据集(数据可以从下载),实现自动编码器(AE),可变自动编码器(VAE),BERT提取电影名特征3种方法,对评分矩阵进行耦合,继而对用户做出推荐。 代码建议在Google Colab环境下运行,代码中的目录...
  • PyTorch 学习笔记(九):自动编码器(AutoEncoder)

    万次阅读 多人点赞 2019-06-01 16:16:39
    跟数据相关程度很高,这意味着自动编码器只能压缩与训练数据相似的数据,因为使用神经网络提取的特征一般是高度相关于原始的训练集,使用人脸训练出的自动编码器在压缩自然界动物的图片时就会表现的很差,因为它只...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 479,724
精华内容 191,889
关键字:

自动编码器