深度学习入门(六)自编码器Auto Encoder

Apr 7, 2018


参考这篇:https://www.zhihu.com/question/41490383

顺带一说,最高票答主的回答质量都挺高的,值得关注。

环境:macOS-10.13.4, Python-3.6, Tensorflow-1.4.1

自编码器AE

先直接看一下它的网络结构吧:

image

它可以简单的分为两个结构,左边的叫做Encoder,俗称编码器,右边的叫做Decoder,俗称解码器。

我们需要的不是它的输出层,而是中间的那一层,所以它到底是怎么工作的呢?

首先它是用来干嘛的?

它是一种【无监督学习】,不需要标签的,比如上面那个网络,直接从输入层输入一亿个四元向量,然后进入隐含层的两个节点进行计算,再输出四个输出节点,我们希望,【输出与输入越接近越好】,那么,中间的两个节点所表示的,其实就可以描述为特征。

所以它就是用来提取特征用的。

再举一个跟神经网络无关的例子,LED屏上的数字,七根火柴棍就可以描述出来0-9十个数字。换句话说就是十个数字可以拆成七个具有【0,1】状态的特征,这些特征通过一定的顺序又可以组合成原来的十个数字。

而现实中的很多东西也一样,比如小猫的脸的照片,有眼睛,鼻子,嘴巴,脸上的毛等等等,如果我们构造一个三层神经网络,让输出的图片尽量的和输入的相同,隐藏的层的节点数小于输入层,那么我们拿到隐藏层后,在很大程度上是可以复现出原来的照片的,这是不是很神奇呢。

提取特征换句话说类似于降维或者提取主成分,但又不相同的,AE是一种非常好的模型去完成这个。

那么回到之前的网络结构,Encoder就是输入层到隐含层的过程,Decoder就是隐含层到输出层的过程,是不是现在大致就能够理解了。编码指的是提取输入的东西的特征,看能不能找到一些可以共同描述这些输入的东西,解码就是通过找到的特征再重新组合,看能不能尝试把输入的东西再拼回来。

根据《Tensorflow实战》中的讲解,这个思路最原始是来自于摄影。有人发现把一张风景照片剪成8*8的64份后,再换个顺序或者和别的同类型的照片的碎片进行拼一拼,又能拼出来一些比较好看的照片。

它用的也是bp算法进行反向传播从而使用optimizer降低loss。

稀疏自编码器Sparse Auto Encoder

我们希望的是自编码器的隐藏节点不要太多,同时在一次运算里,被激活的节点也要远远小于被抑制的节点,否则就会出现一些直接拷贝输入层的内容呀等等的奇怪的现象。这个可以使用相对熵来控制,在最上面的参考中有比较详细的说明,这里不再赘述。

降噪自编码器Denosing Auto Encoder

神经网络都是对输入比较敏感的,如果有噪声的话,AE要尽量去掉噪声增强鲁棒性,一般我们可以梯度下降来解决。同样最上面的参考中有详细的说明,这里不再赘述。

其他

别的关于AE也没有什么好说的,看了我的第二篇深度学习的文章的话,并且下了Tensorflow和matplotlib,就直接运行一下下面的代码试试把,直接对MNIST手写数字集上做的一个AE,最后显示的是在二维坐标上的聚类效果,想做科研的朋友可以多试试这段代码。代码来源:https://blog.csdn.net/marsjhao/article/details/68950697

import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=False)

learning_rate = 0.01
training_epochs = 10
batch_size = 256
display_step = 1
n_input = 784
X = tf.placeholder("float", [None, n_input])

n_hidden_1 = 128
n_hidden_2 = 64
n_hidden_3 = 10
n_hidden_4 = 2
weights = {
    'encoder_h1': tf.Variable(tf.truncated_normal([n_input, n_hidden_1],)),
    'encoder_h2': tf.Variable(tf.truncated_normal([n_hidden_1, n_hidden_2],)),
    'encoder_h3': tf.Variable(tf.truncated_normal([n_hidden_2, n_hidden_3],)),
    'encoder_h4': tf.Variable(tf.truncated_normal([n_hidden_3, n_hidden_4],)),
    'decoder_h1': tf.Variable(tf.truncated_normal([n_hidden_4, n_hidden_3],)),
    'decoder_h2': tf.Variable(tf.truncated_normal([n_hidden_3, n_hidden_2],)),
    'decoder_h3': tf.Variable(tf.truncated_normal([n_hidden_2, n_hidden_1],)),
    'decoder_h4': tf.Variable(tf.truncated_normal([n_hidden_1, n_input],)),
}
biases = {
    'encoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
    'encoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
    'encoder_b3': tf.Variable(tf.random_normal([n_hidden_3])),
    'encoder_b4': tf.Variable(tf.random_normal([n_hidden_4])),
    'decoder_b1': tf.Variable(tf.random_normal([n_hidden_3])),
    'decoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
    'decoder_b3': tf.Variable(tf.random_normal([n_hidden_1])),
    'decoder_b4': tf.Variable(tf.random_normal([n_input])),
}
def encoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['encoder_h1']),
                                   biases['encoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['encoder_h2']),
                                   biases['encoder_b2']))
    layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, weights['encoder_h3']),
                                   biases['encoder_b3']))
    # 为了便于编码层的输出,编码层随后一层不使用激活函数
    layer_4 = tf.add(tf.matmul(layer_3, weights['encoder_h4']),
                                    biases['encoder_b4'])
    return layer_4

def decoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['decoder_h1']),
                                   biases['decoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['decoder_h2']),
                                   biases['decoder_b2']))
    layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, weights['decoder_h3']),
                                biases['decoder_b3']))
    layer_4 = tf.nn.sigmoid(tf.add(tf.matmul(layer_3, weights['decoder_h4']),
                                biases['decoder_b4']))
    return layer_4

encoder_op = encoder(X)
decoder_op = decoder(encoder_op)

y_pred = decoder_op
y_true = X

cost = tf.reduce_mean(tf.pow(y_true - y_pred, 2))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)

with tf.Session() as sess:
    # tf.initialize_all_variables() no long valid from
    # 2017-03-02 if using tensorflow >= 0.12
    if int((tf.__version__).split('.')[1]) < 12 and int((tf.__version__).split('.')[0]) < 1:
        init = tf.initialize_all_variables()
    else:
        init = tf.global_variables_initializer()
    sess.run(init)
    total_batch = int(mnist.train.num_examples/batch_size)
    for epoch in range(training_epochs):
        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)  # max(x) = 1, min(x) = 0
            _, c = sess.run([optimizer, cost], feed_dict={X: batch_xs})
        if epoch % display_step == 0:
            print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(c))
    print("Optimization Finished!")

    encoder_result = sess.run(encoder_op, feed_dict={X: mnist.test.images})
    plt.scatter(encoder_result[:, 0], encoder_result[:, 1], c=mnist.test.labels)
    plt.colorbar()
    plt.show()