稀疏自动编码之自动编码器和稀疏性

到目前为止,已经叙述了神经网络的监督学习,即学习的样本都是有标签的。现在假设我们有一个没有标签的训练集\textstyle \{x^{(1)}, x^{(2)}, x^{(3)}, \ldots\},其中\textstyle x^{(i)} \in \Re^{n}. 自动编码器就是一个运用了反向传播进行无监督学习的神经网络,学习的目的就是为了让输出值和输入值相等,即\textstyle y^{(i)} = x^{(i)}.下面就是一个自动编码器:

自动编码器试图学习一个函数\textstyle h_{W,b}(x) \approx x. 换句话说,它试图逼近一个等式函数,使得该函数的输出 \textstyle \hat{x} 和输入 \textstyle x 很近似。举一个具体的例子,假设输入 \textstyle x 是来自一个 \textstyle 10 \times 10 图像(共100个像素点)像素点的灰度值,在 \textstyle L_2 层有 \textstyle s_2=50 个隐层节点. 注意输出 \textstyle y \in \Re^{100}. 由于隐层节点只有50个,所以网络必须学习出输入的压缩表示,即给出以隐层节点激活值作为元素的向量\textstyle a^{(2)} \in \Re^{50},它需要重构出100个像素点灰度值的输入 \textstyle x. 如果输入是完全随机的,那么这种压缩学习的任务将会很难,但是数据具有一定的结构,例如一些输入特征彼此关联,那么这个算法就可以发现这些关联关系。事实上,自动编码器往往最终就是学习出一个较低维度的表示。

我们认为,上面讨论是依赖于隐层节点数很少。但是,即使隐层节点数很多(也许比输入的像素点个数还多),通过对网络加上某些约束,我们仍然能够发现感兴趣的结构。特别是,对隐层节点加上稀疏约束

简答来说,如果一个神经元的输出为1,我们认为这个神经元是被激活的,如果某个神经元的输出是0,就认为这个神经元是抑制的。我们想约束神经元,使得神经元在大多数时候是处于抑制状态。假设激活函数是sigmoid函数。

在上面的网络中,\textstyle a^{(2)}_j 表示隐层(第二层)节点 \textstyle j 的激活值。但是这种表示并没有显示给出哪一个输入 \textstyle x 导致了这个激活值。因此,用 \textstyle a^{(2)}_j(x) 去表示当输入为 \textstyle x 时这个隐层节点的激活值。进一步,令

\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 \rho 偏差很大的 \textstyle \hat\rho_j  加以惩罚,我们使用下面的惩罚项:

\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 是隐层神经元的数目。这个约束想也可写为:

\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 \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 变化而变化的情况:

由上图可以看出,在\textstyle \hat\rho_j = \rho时,函数到达最小值0.但是当\textstyle \hat\rho_j无论从左边还是右边远离\textstyle \rho(0.2)时,函数值显著增大。

于是,我们总体的代价函数(损失函数)就变为:

\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) 是在前面的博文

稀疏自动编码之反向传播算法(BP)

中定义的,\textstyle \beta 是用来控制稀疏惩罚项的权重。\textstyle \hat\rho_j 其实也依赖于\textstyle W,b ,因为\textstyle \hat\rho_j 是隐层节点的平均激活值,而计算平均激活值,首先需要计算该节点所有激活值,激活值是多少是取决于\textstyle W,b的(训练集一定)。

下面把惩罚项加入代价函数,再计算导数。只需在原来的代码中作很小的改动,例如在反向传播的第2层,前面博文中已经计算出:

现在用下面的式子代替:

\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_j,因此,在前馈传播过程中首先把隐层所有神经元的平均激活值计算出来。当训练集很小的时候,在前馈传播的时候,把所有神经元的激活值和平均值都存储在内存里,然后在反向传播过程中可以利用这些提取计算好的激活值。如果训练集太大,没有那么多内存存储中间结果,可以在前馈传播过程中先计算每个节点针对所有的样本的激活值,从而计算出该节点的平均激活值\textstyle \hat\rho_j,在计算下一个节点的平均激活值的时候就可以丢掉前一个节点的针对所有样本的激活值,值存储它的平均激活值,这样之后,在反向传播之前,就需要再一次前馈传播得到神经元的激活值,这样会让计算效率有些降低,但是保证内存的够用。

整个用梯度下降求解过程的伪代码在

稀疏自动编码之反向传播算法(BP)

中已经给出,只不过目标函数改为这里给出的\textstyle J_{\rm sparse}(W,b). 然后用

稀疏自动编码之梯度检验

中介绍的导数检验的方法,可以验证代码的正确与否。

 

学习来源:http://deeplearning.stanford.edu/wiki/index.php/Autoencoders_and_Sparsity

 

posted @ 2014-10-14 10:55  90Zeng  阅读(4514)  评论(0编辑  收藏  举报