深度学习算法(第26期)—-深度网络中的自编码器

  • 2019 年 10 月 7 日
  • 筆記

上期我们一起学习了机器翻译中的编码解码器网络的相关知识,

深度学习算法(第25期)—-机器翻译中的编码解码器网络 今天我们一起学一下深度网络中的自编码器.

自编码器是在无监督(训练集未标注)的情况下,能够学习有效表示数据(称为编码)的一种深度人工网络。这些编码一般跟输入数据比起来有更低的维度,这使得自编码器在数据降维方面比较有用。更重要的是,自编码器可以作为强大的特征检测器,它可以在深度网络中用于无监督的预训练。最后,它可以随机产生和训练数据相似的新数据,这叫做生成模型。例如,我们可以训练一个人脸图像上的自编码器,那么它能够产生新的人脸图像。

咋一看,自编码器的工作就是学会简单的把输入值copy到输出,然而,训练网络的时候,可以从各个角度进行约束网络,使它不能够做简单的复制,进而能够训练出有效的网络。比方说,我们可以限制内部呈现的大小,或者在输入图像上加些噪声来训练网络恢复原始输入。这些约束可以防止网络玩障眼法,或者说防止网络去简单的复制输入到输出。这就强制它去学习表示数据的有效方法。简而言之,编码是自编码器在一些约束下尝试学习恒等方程而得到的副产品。 接下来,我们将更进一步的学习自编码器的工作原理,不管是在降维,特征提取,无监督预训练,还是生成模型,以及可以实施哪些约束,以及如何在tensorflow中实现。

有效的数据表示

我们来看两组数字,下面哪一组数更好记呢?

• 40, 27, 25, 36, 81, 57, 10, 73, 19, 68  • 50, 25, 76, 38, 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20  

咋一看,感觉应该是第一组数据更好记一些,因为第一组短一点嘛。然而仔细看第二组数据就会发现,这条数据是有规律的,即:如果是偶数的话,那么它后面跟的是该偶数的一半,如果是奇数的话,后面跟的是该数的3倍加1(其实该序列为希尔顿序列)。一旦我们发现了这个规律,那么第二组数据就更为好记了。

关于记忆,感知和模式匹配的关系在 20 世纪 70 年代早期由 William Chase 和 Herbert Simon 研究。他们注意到,专业的棋手能够通过观察棋盘5秒钟就记住所有棋子的位置,这个任务,大多数人都会认为不可思议。然而也只是在正常比赛中才会是这种情况,而不是随机放置位置。这也就说明了,专业棋手并不是记忆力比我们好很多,而是由于丰富的经验,他们更看到棋子背后的模式,也就是说模式能够帮他们更为高效的存储信息。

就像上面说的专业棋手一样,自编码器将输入转换成一种高效的内部模式呈现,并且输出一些跟输入很似接近的东西。一个自编码器通常由两部分组成:编码(识别网络)用来将输入转换成一个内部表示,解码(生成网络)用来将内部表示转换为输出,如下图:

正如我们上图看到的一样,除了自编码器的输入输出必须一致以外,一个自编码器基本上跟多层感知机(MLP)有着相同的架构。在上面的例子中,仅仅有一个包含两个神经元的隐藏层(编码),和一个由三个神经元组成的输出层(解码)。由于自编码器在尽力的恢复输入数据,所以输出通常也被称为重构。损失函数为重构损失,往往会在跟输入不一致的时候惩罚模型。

由于这个内部呈现跟输入数据比起来维度更低,所以自编码器被认为是不完整的,一个不完整的自编码器不能简单的复制输入到编码,也就是说,它必须找到一个模式来输出输入的近似。这就强制它去学习输入数据的一些重要的特征,而抛弃不重要的特征。

现在,我们一起看一下,如何用不完整的自编码器来实现降维的。

用不完整的线性编码器实现PCA

如果自编码器仅仅使用线性激活函数,并且用MSE作为损失函数的话,则它将最终收敛为我们之前学过的PCA(主成分分析)模型(见下链接)。 机器学习三人行(系列十)—-机器学习降压神器(附代码)

我们一起看下如何创建一个简单的线性自编码器来实现PCA,将一个三维数据映射到两维:

import tensorflow as tf  from tensorflow.contrib.layers import fully_connected  n_inputs = 3 # 3D inputs  n_hidden = 2 # 2D codings  n_outputs = n_inputs    learning_rate = 0.01    X = tf.placeholder(tf.float32, shape=[None, n_inputs])  hidden = fully_connected(X, n_hidden, activation_fn=None)  outputs = fully_connected(hidden, n_outputs, activation_fn=None)    reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE    optimizer = tf.train.AdamOptimizer(learning_rate)  training_op = optimizer.minimize(reconstruction_loss)    init = tf.global_variables_initializer()  

这段代码跟我们之前学的MLP基本一致,需要注意的是:

  1. 神经元的输入数量和输出数量一致。
  2. 为了实现PCA,这里设置activation_fn=None,并且损失函数为MSE。

现在我们将加载数据,在训练集上训练模型,并且用模型去编码测试数据(投影到2D):

X_train, X_test = [...] # load the dataset    n_iterations = 1000  codings = hidden # the output of the hidden layer provides the codings    with tf.Session() as sess:      init.run()      for iteration in range(n_iterations):          training_op.run(feed_dict={X: X_train}) # no labels (unsupervised)      codings_val = codings.eval(feed_dict={X: X_test})  

下图显示了原始数据(左侧),和自编码器的隐藏层的输出(编码层,右图),正如我们看到的,自编码器找到了将数据投影到数据上的最佳二维平面,保留了数据的尽可能多的差异(就像 PCA 一样)。