TensorFlow系列延伸阅读:


TensorFlow(一)——基础图模型

TensorFlow(二)——逻辑回归

TensorFlow(三)——卷积神经网络用于手写字识别





1. 什么是自动编码器

自动编码器(Autoencoder)是一种数据压缩模型,它可以将输入数据编码成另外一种较小维度的表示,而后解码器将编码后的版本还原成原始的输入形式。与主成分分析(PCA)所不同的是,PCA采取的是线性变换,而这里自动编码器采取的是非线性变换,所以相比PCA,自动编码器可以得到更复杂的编码。


相比于一般的神经网络,通常都是从数据集X去预测数据集Y,而自动编码器是从数据集X去预测数据集X。通常,自动编码器都不是完全复原输入数据,在解码的时候都会加上一点抖动噪声,也就是通常所说的Denosing Autoencoder。

1.1_.jpg


通过构造多层神经网络,解码部分的参数与编码部分的参数共享,这样做的目的是减少训练的难度。那么解码器和编码器的参数能否不共享呢?答案肯定是可以不共享的,但是那样训练起来就比较慢了。本文中,解码器与编码器只共享权重矩阵而不共享偏置向量,这样就间接为解码器添加了一些噪声。




2. 基于TensorFlow的自动编码器实现

今天将介绍如何使用TensorFlow搭建一个自动编码器,具体的应用是将此自动编码器用在MNIST手写字识别数据库中,然后使用10个测试样本来还原输入数据。


#首先,第一步还是导入基本的库函数。

import tensorflow as tf

import numpy as np

import math

  • 构建一个自动编码器,注意这里的默认参数意思是每一层自动编码器的神经元个数分别是784,512,256,64。

 
def autoencoder(dimensions=[784, 512, 256, 64]):

    #首先还是创建一个输入占位符,这里维度是batch_size*784,784=28×28。并且把此时的占位符赋值给current_input

    x = tf.placeholder(tf.float32, [None, dimensions[0]], name='x')

    current_input = x




    #构建编码器,

    encoder = []

    #对所有层进行遍历,这里的layer_i指的是层序号,而n_output指的是该层神经元的个数

    for layer_i, n_output in enumerate(dimensions[1:]):

    #获取由上一层传过来的维度,即神经元的个数

        n_input = int(current_input.get_shape()[1])

   #创建由上一层神经元连接到该层神经元之间的权重矩阵以及偏置向量

        W = tf.Variable(

            tf.random_uniform([n_input, n_output],

                              -1.0 / math.sqrt(n_input),

                              1.0 / math.sqrt(n_input)))

        b = tf.Variable(tf.zeros([n_output]))

  #将权重矩阵添加到编码器中

        encoder.append(W)

  #前馈计算得到该层的输出,并且通过tanh激活函数

        output = tf.nn.tanh(tf.matmul(current_input, W) + b)

  #将该层输出赋值给current_input,作为下一层的输入

        current_input = output



 
  • 编码器做好了,接下来开始构建解码器了,在构建解码器的时候,我们反过来使用编码器的权重,但是偏置向量是全新的。



    z = current_input

    encoder.reverse()




    #完全按照编码器的结构对称写出解码器的结构

    for layer_i, n_output in enumerate(dimensions[:-1][::-1]):

        W = tf.transpose(encoder[layer_i])

        b = tf.Variable(tf.zeros([n_output]))

        output = tf.nn.tanh(tf.matmul(current_input, W) + b)

        current_input = output




    #此时的current_input就是解码器的最终输出

    y = current_input


  • 接下来要声明误差函数了,由于这个项目是自动编码器,因此只用将还原出来的结果与最初的输入进行对照即可,所以这里采用的是将还原出来的照片像素与输入照片像素的平方差作为误差函数。


    cost = tf.reduce_sum(tf.square(y - x))

    return {'x': x, 'z': z, 'y': y, 'cost': cost}


  • 最后就是正式的训练部分了。


def test_mnist():

    import tensorflow as tf


    import tensorflow.examples.tutorials.mnist.input_data as input_data

    import matplotlib.pyplot as plt

    import matplotlib.cm as cm




    #导入MNIST数据库

    mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

    mean_img = np.mean(mnist.train.images, axis=0)

    #创建一个自动编码器,使用两层神经网络作为编码部分,神经元个数分别为256,256,64,32

    ae = autoencoder(dimensions=[784, 256, 256, 64, 32])




    #创建一个Adam优化器,优化的目标是使得cost达到最小值,而设置的学习率为0.001

    learning_rate = 0.001

    optimizer = tf.train.AdamOptimizer(learning_rate).minimize(ae['cost'])




    #创建会话,并初始化计算图中所有变量

    sess = tf.Session()

    sess.run(tf.initialize_all_variables())




    #将mnist训练数据划分成每20个样本作为一个mini-batch,并且代入训练的是每个像素图减去像素平均值之后的数据,一共训练30个周期

    batch_size = 20

    n_epochs = 30

    for epoch_i in range(n_epochs):

        for batch_i in range(mnist.train.num_examples // batch_size):

            batch_xs, _ = mnist.train.next_batch(batch_size)

            train = np.array([img - mean_img for img in batch_xs])

            sess.run(optimizer, feed_dict={ae['x']: train})

        print(epoch_i, sess.run(ae['cost'], feed_dict={ae['x']: train}))




    #找10个测试样本,代入自动编码器,得到还原后的输出,并且与原图进行对照,画在一张图上。第一行是原图,第二行是经过自动编码器后的复原图。

    n_examples = 10

    test_xs, _ = mnist.test.next_batch(n_examples)

    test_xs_norm = np.array([img - mean_img for img in test_xs])

    recon = sess.run(ae['y'], feed_dict={ae['x']: test_xs_norm})

    fig, axs = plt.subplots(2, n_examples, figsize=(28, 4))

    for example_i in range(n_examples):

        axs[0][example_i].imshow(

            np.reshape(test_xs[example_i, :], (28, 28)),cmap=cm.jet)

        axs[1][example_i].imshow(

            np.reshape([recon[example_i, :] + mean_img], (28, 28)),cmap=cm.jet)

    fig.show()

    plt.draw()

    plt.savefig('reconstruction.png',dpi=150)




if __name__ == '__main__':


    test_mnist()



通过迭代计算30个周期以后,使用了10个测试样本,得到了如下图所示的还原后的结果,其中第一行表示原始输入数字图像,而第二行表示经过自动编码器还原后的数字图像。

1.2_.jpg


本文我们使用的是简单的前馈式神经网络作为自动编码器的隐含层,这里应用的数据是图像,如果要应用到时序数据,那么则可以使用循环神经网络或者卷积神经网络代替本文中的前馈式神经网络,这样得到的编码信息更具有时间上的关联性。



自动编码器用于图像复原是可以的,但是它的复原几乎是和输入数据一致的,特征是没有什么大的变化。如果想获得一点不一样的却又满足分布的复原图像,则可以考虑使用对抗式生成网络。至于具体怎么使用GAN做图像生成,请关注此公众号接下来的几期推送。
 
 
延伸阅读:

什么是对抗式生成网络
 
 
 
 
 
来源: Pony 深度学习每日摘要
智造家