自编码器和深度学习

本文详细阐述了自编码神经网络在无监督学习中的应用,特别关注了自编码算法与稀疏性的关系。介绍了自编码网络如何通过反向传播学习恒等函数,并在隐藏层数量较少时,如何从输入数据中发现有趣的结构。此外,文章还讨论了通过加入稀疏性限制,即使在隐藏层数量较多的情况下也能发现输入数据中的结构。最后,文章讲解了如何将自编码器“栈化”到逐层贪婪训练法中,用于预训练深度神经网络。
摘要由CSDN通过智能技术生成

自编码算法与稀疏性

目前为止,我们已经讨论了神经网络在有监督学习中的应用。在有监督学习中,训练样本是有类别标签的。现在假设我们只有一个没有带类别标签的训练样本集合 \textstyle \{x^{(1)}, x^{(2)}, x^{(3)}, \ldots\} ,其中 \textstyle x^{(i)} \in \Re^{n} 。自编码神经网络是一种无监督学习算法,它使用了反向传播算法,并让目标值等于输入值,比如 \textstyle y^{(i)} = x^{(i)} 。下图是一个自编码神经网络的示例。Autoencoder636.png


自编码神经网络尝试学习一个 \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) 做梯度下降。使用梯度验证方法,你可以自己来验证梯度下降算法是否正确。。

栈式自编码算法

逐层贪婪训练法依次训练网络的每一层,进而预训练整个深度神经网络。在本节中,我们将会学习如何将自编码器“栈化”到逐层贪婪训练法中,从而预训练(或者说初始化)深度神经网络的权重。概述


栈式自编码神经网络是一个由多层稀疏自编码器组成的神经网络,其前一层自编码器的输出作为其后一层自编码器的输入。对于一个 \textstyle n 层栈式自编码神经网络,我们沿用自编码器一章的各种符号,假定用 \textstyle W^{(k, 1)}, W^{(k, 2)}, b^{(k, 1)}, b^{(k, 2)} 表示第 \textstyle k 个自编码器对应的 \textstyle W^{(1)}, W^{(2)}, b^{(1)}, b^{(2)} 参数,那么该栈式自编码神经网络的编码过程就是,按照从前向后的顺序执行每一层自编码器的编码步骤:


\begin{align}a^{(l)} = f(z^{(l)}) \\z^{(l + 1)} = W^{(l, 1)}a^{(l)} + b^{(l, 1)}\end{align}


同理,栈式神经网络的解码过程就是,按照从后向前的顺序执行每一层自编码器的解码步骤:


\begin{align}a^{(n + l)} = f(z^{(n + l)}) \\z^{(n + l + 1)} = W^{(n - l, 2)}a^{(n + l)} + b^{(n - l, 2)}\end{align}


其中,\textstyle a^{(n)} 是最深层隐藏单元的激活值,其包含了我们感兴趣的信息,这个向量也是对输入值的更高阶的表示。


通过将 \textstyle a^{(n)} 作为softmax分类器的输入特征,可以将栈式自编码神经网络中学到的特征用于分类问题。


训练

一种比较好的获取栈式自编码神经网络参数的方法是采用逐层贪婪训练法进行训练。即先利用原始输入来训练网络的第一层,得到其参数 \textstyle W^{(1,1)}, W^{(1,2)}, b^{(1,1)}, b^{(1,2)};然后网络第一层将原始输入转化成为由隐藏单元激活值组成的向量(假设该向量为A),接着把A作为第二层的输入,继续训练得到第二层的参数 \textstyle W^{(2,1)}, W^{(2,2)}, b^{(2,1)}, b^{(2,2)};最后,对后面的各层同样采用的策略,即将前层的输出作为下一层输入的方式依次训练。


对于上述训练方式,在训练每一层参数的时候,会固定其它各层参数保持不变。所以,如果想得到更好的结果,在上述预训练过程完成之后,可以通过反向传播算法同时调整所有层的参数以改善结果,这个过程一般被称作“微调(fine-tuning)”。


实际上,使用逐层贪婪训练方法将参数训练到快要收敛时,应该使用微调。反之,如果直接在随机化的初始权重上使用微调,那么会得到不好的结果,因为参数会收敛到局部最优。


如果你只对以分类为目的的微调感兴趣,那么惯用的做法是丢掉栈式自编码网络的“解码”层,直接把最后一个隐藏层的 \textstyle a^{(n)} 作为特征输入到softmax分类器进行分类,这样,分类器(softmax)的分类错误的梯度值就可以直接反向传播给编码层了。


具体实例

让我们来看个具体的例子,假设你想要训练一个包含两个隐含层的栈式自编码网络,用来进行MNIST手写数字分类(这将会是你的下一个练习)。 首先,你需要用原始输入 \textstyle x^{(k)} 训练第一个自编码器,它能够学习得到原始输入的一阶特征表示\textstyle h^{(1)(k)}(如下图所示)。


Stacked SparseAE Features1.png


接着,你需要把原始数据输入到上述训练好的稀疏自编码器中,对于每一个输入\textstyle x^{(k)},都可以得到它对应的一阶特征表示\textstyle h^{(1)(k)}。然后你再用这些一阶特征作为另一个稀疏自编码器的输入,使用它们来学习二阶特征 \textstyle h^{(2)(k)}。(如下图所示)

Stacked SparseAE Features2.png


同样,再把一阶特征输入到刚训练好的第二层稀疏自编码器中,得到每个 \textstyle h^{(1)(k)} 对应的二阶特征激活值 \textstyle h^{(2)(k)}。接下来,你可以把这些二阶特征作为softmax分类器的输入,训练得到一个能将二阶特征映射到数字标签的模型。

Stacked Softmax Classifier.png


如下图所示,最终,你可以将这三层结合起来构建一个包含两个隐藏层和一个最终softmax分类器层的栈式自编码网络,这个网络能够如你所愿地对MNIST数字进行分类。

Stacked Combined.png


讨论

栈式自编码神经网络具有强大的表达能力及深度神经网络的所有优点。

更进一步,它通常能够获取到输入的“层次型分组”或者“部分-整体分解”结构。为了弄清这一点,回顾一下,自编码器倾向于学习得到能更好地表示输入数据的特征。因此,栈式自编码神经网络的第一层会学习得到原始输入的一阶特征(比如图片里的边缘),第二层会学习得到二阶特征,该特征对应一阶特征里包含的一些模式(比如在构成轮廓或者角点时,什么样的边缘会共现)。栈式自编码神经网络的更高层还会学到更高阶的特征。


举个例子,如果网络的输入数据是图像,网络的第一层会学习如何去识别边,第二层一般会学习如何去组合边,从而构成轮廓、角等。更高层会学习如何去组合更形象且有意义的特征。例如,如果输入数据集包含人脸图像,更高层会学习如何识别或组合眼睛、鼻子、嘴等人脸器官。



中英文对照

自编码算法 Autoencoders
稀疏性 Sparsity
神经网络 neural networks
监督学习 supervised learning
无监督学习 unsupervised learning
反向传播算法 backpropagation
隐藏神经元 hidden units
像素灰度值 the pixel intensity value
独立同分布 IID
主元分析 PCA
激活 active
抑制 inactive
激活函数 activation function
激活度 activation
平均活跃度 the average activation
稀疏性参数 sparsity parameter
惩罚因子 penalty term
相对熵 KL divergence
伯努利随机变量 Bernoulli random variable
总体代价函数 overall cost function
后向传播 backpropagation
前向传播 forward pass
梯度下降 gradient descent
目标函数 the objective
梯度验证方法 the derivative checking method
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值