跳转到主要内容

深度学习概述:从感知机到深度网络(下)

editor 提交于

<br>接上文《<a href="http://intel.eetrend.com/content/2016/100003341.html">深度学习概述:从感知机到深度网络(上)</a>》</br>

<font color="#7a0e6b"><strong>受限波尔兹曼机</strong> </font>
  
下一步来看下受限波尔兹曼机(<a href="http://en.wikipedia.org/wiki/Restricted_Boltzmann_machine">Restricted Boltzmann machines</a>  RBM),一种可以在输入数据集上学习概率分布的生成随机神经网络。

<center><img src="http://imgtec.eetrend.com/sites/imgtec.eetrend.com/files/201610/blog/87…; alt=""></center>

<br>RBM由隐含层、可见层、偏置层组成。和前馈神经网络不同,可见层和隐含层之间的连接是无方向性(值可以从可见层->隐含层或隐含层->可见层任意传输)且全连接的(每一个当前层的神经元与下一层的每个神经元都有连接——如果允许任意层的任意神经元连接到任意层去,我们就得到了一个波尔兹曼机(非受限的))。</br>

标准的RBM中,隐含和可见层的神经元都是二态的(即神经元的激活值只能是服从<a href="http://en.wikipedia.org/wiki/Bernoulli_distribution">伯努力分布</a>的0或1),不过也存在其它非线性的变种。

虽然学者们已经研究RBM很长时间了,最近出现的对比差异无监督训练算法使这个领域复兴。

<font color="#7a0e6b"><strong>对比差异
  
单步对比差异算法原理:

1、正向过程:

<li>输入样本 v 输入至输入层中。</li>

<li>v 通过一种与前馈网络相似的方法传播到隐含层中,隐含层的激活值为 h。</li>
  
<br>2、反向过程:</br>

<li>将 h 传回可见层得到 v’ (可见层和隐含层的连接是无方向的,可以这样传)。</li>

<li>再将 v’ 传到隐含层中,得到 h’。</li>
  
<br>3、权重更新:</br>

<center><img src="http://imgtec.eetrend.com/sites/imgtec.eetrend.com/files/201610/blog/87…; alt=""></center>
      
<br>其中 a 是学习速率, v, v’, h, h’ 和 w 都是向量。</br>

算法的思想就是在正向过程中影响了网络的内部对于真实数据的表示。同时,反向过程中尝试通过这个被影响过的表示方法重建数据。主要目的是可以使生成的数据与原数据尽可能相似,这个差异影响了权重更新。

换句话说,这样的网络具有了感知对输入数据表示的程度的能力,而且尝试通过这个感知能力重建数据。如果重建出来的数据与原数据差异很大,那么进行调整并再次重建。

<font color="#7a0e6b"><strong>再看流行感冒的例子
  
为了说明对比差异,我们使用与上例相同的流感症状的数据集。测试网络是一个包含6个可见层神经元、2个隐含层神经元的RBM。我们用对比差异的方法对网络进行训练,将症状 v 赋到可见层中。在测试中,这些症状值被重新传到可见层;然后再被传到隐含层。隐含层的神经元表示健康/生病的状态,与自编码器相似。

在进行过几百次迭代后,我们得到了与自编码器相同的结果:输入一个生病样本,其中一个隐含层神经元具有更高激活值;输入健康的样本,则另一个神经元更兴奋。

例子的代码在<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;。

<font color="#7a0e6b"><strong>深度网络</strong> </font>
  
到现在为止,我们已经学习了隐含层中强大的特征探测器——自编码器和RBM,但现在还没有办法有效的去利用这些功能。实际上,上面所用到的这些数据集都是特定的。而我们要找到一些方法来间接的使用这些探测出的特征。

好消息是,已经发现这些结构可以通过栈式叠加来实现深度网络。这些网络可以通过贪心法的思想训练,每次训练一层,以克服之前提到在反向传播中梯度消失及过度拟合的问题。

这样的算法架构十分强大,可以产生很好的结果。如Google著名的<a href="http://static.googleusercontent.com/media/research.google.com/en/us/pub…;,在实验中通过使用特定的深度自编码器,在无标记的图片库中学习到人和猫脸的识别。

下面我们将更深入。

<font color="#7a0e6b"><strong>栈式自编码器</strong> </font>
  
和名字一样,这种网络由多个栈式结合的自编码器组成。

<center><img src="http://imgtec.eetrend.com/sites/imgtec.eetrend.com/files/201610/blog/87…; alt=""></center>       

<br>自编码器的隐含层 t 会作为 t + 1 层的输入层。第一个输入层就是整个网络的输入层。利用贪心法训练每一层的步骤如下:</br>

1、通过反向传播的方法利用所有数据对第一层的自编码器进行训练(t=1,上图中的红色连接部分)。

2、训练第二层的自编码器 t=2 (绿色连接部分)。由于 t=2 的输入层是 t=1 的隐含层,我们已经不再关心 t=1 的输入层,可以从整个网络中移除。整个训练开始于将输入样本数据赋到 t=1 的输入层,通过前向传播至 t = 2 的输出层。下面t = 2的权重(输入->隐含和隐含->输出)使用反向传播的方法进行更新。t = 2的层和 t=1 的层一样,都要通过所有样本的训练。

3、对所有层重复步骤1-2(即移除前面自编码器的输出层,用另一个自编码器替代,再用反向传播进行训练)。

4、步骤1-3被称为预训练,这将网络里的权重值初始化至一个合适的位置。但是通过这个训练并没有得到一个输入数据到输出标记的映射。例如,一个网络的目标是被训练用来识别手写数字,经过这样的训练后还不能将最后的特征探测器的输出(即隐含层中最后的自编码器)对应到图片的标记上去。这样,一个通常的办法是在网络的最后一层(即蓝色连接部分)后面再加一个或多个全连接层。整个网络可以被看作是一个多层的感知机,并使用反向传播的方法进行训练(这步也被称为微调)。

栈式自编码器,提供了一种有效的预训练方法来初始化网络的权重,这样你得到了一个可以用来训练的复杂、多层的感知机。

<font color="#7a0e6b"><strong>深度信度网络</strong> </font>
  
和自编码器一样,我也可以将波尔兹曼机进行栈式叠加来构建深度信度网络(DBN)。

<center><img src="http://imgtec.eetrend.com/sites/imgtec.eetrend.com/files/201610/blog/87…; alt=""></center>       

<br>在本例中,隐含层 RBM t 可以看作是 RBM t+1 的可见层。第一个RBM的输入层即是整个网络的输入层,层间贪心式的预训练的工作模式如下:</br>

1. 通过对比差异法对所有训练样本训练第一个RBM t=1

2. 训练第二个RBM t=1。由于 t=2 的可见层是 t=1 的隐含层,训练开始于将数据赋至 t=1 的可见层,通过前向传播的方法传至 t=1 的隐含层。然后作为 t=2 的对比差异训练的初始数据。

3. 对所有层重复前面的过程。

4. 和栈式自编码器一样,通过预训练后,网络可以通过连接到一个或多个层间全连接的 RBM 隐含层进行扩展。这构成了一个可以通过反向传僠进行微调的多层感知机。

本过程和栈式自编码器很相似,只是用RBM将自编码器进行替换,并用对比差异算法将反向传播进行替换。

(注: 例中的源码可以从<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/d2bbc296eca926d07d0….)

<font color="#7a0e6b"><strong>卷积网络</strong> </font>
  
这个是本文最后一个软件架构——卷积网络,一类特殊的对图像识别非常有效的前馈网络。

<center><img src="http://imgtec.eetrend.com/sites/imgtec.eetrend.com/files/201610/blog/87…; alt=""></center>       

<br>在我们深入看实际的卷积网络之臆,我们先定义一个图像滤波器,或者称为一个赋有相关权重的方阵。一个滤波器可以应用到整个图片上,通常可以应用多个滤波器。比如,你可以应用四个6x6的滤波器在一张图片上。然后,输出中坐标(1,1)的像素值就是输入图像左上角一个6x6区域的加权和,其它像素也是如此。</br>

有了上面的基础,我们来介绍定义出卷积网络的属性:

<li>卷积层 对输入数据应用若干滤波器。比如图像的第一卷积层使用4个6x6滤波器。对图像应用一个滤波器之后的得到的结果被称为特征图谱(feature map, FM),特征图谱的数目和滤波器的数目相等。如果前驱层也是一个卷积层,那么滤波器应用在FM上,相当于输入一个FM,输出另外一个FM。从直觉上来讲,如果将一个权重分布到整个图像上后,那么这个特征就和位置无关了,同时多个滤波器可以分别探测出不同的特征。</li>

<li>下采样层 缩减输入数据的规模。例如输入一个32x32的图像,并且通过一个2x2的下采样,那么可以得到一个16x16的输出图像,这意味着原图像上的四个像素合并成为输出图像中的一个像素。实现下采样的方法有很多种,最常见的是最大值合并、平均值合并以及随机合并。</li>

<li>最后一个下采样层(或卷积层)通常连接到一个或多个全连层,全连层的输出就是最终的输出。</li>

<li>训练过程通过改进的反向传播实现,将下采样层作为考虑的因素并基于所有值来更新卷积滤波器的权重。</li>
  
<br>可以<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/9e569aa7c9a4d724cf3… <a href="http://en.wikipedia.org/wiki/MNIST_database">MNIST</a&gt; 数据集上的卷积网络的例子,<a href="http://cs.stanford.edu/people/karpathy/convnetjs/demo/mnist.html">在这</a…。</br>

<font color="#7a0e6b"><strong>实现</strong> </font>

目前为止,我们已经学会了常见神经网络中最主要的元素了,但是我只写了很少的在实现过程中所遇到的挑战。

概括来讲,我的目标是实现一个<a href="https://github.com/ivan-vasilev/neuralnetworks">深度学习的库</a&gt;,即一个基于神经网络且满足如下条件的框架:    

<li>一个可以表示多种模型的通用架构(比如所有上文提到的神经网络中的元素)</li>

<li>可以使用多种训练算法(反向传播,对比差异等等)。</li>

<li>体面的性能</li>

<br>为了满足这些要求,我在软件的设计中使用了分层的思想。</br>

<font color="#7a0e6b"><strong>结构</strong> </font>
  
我们从如下的基础部分开始:

<li><a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;  是所有神经网络模型实现的基类。</li>

<li>每个网络都包含有一个<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…; 的集合。</li>

<li>每一层中有一个的链表, <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…; 指的是两个层之间的连接,将整个网络构成一个有向无环图。</li>
  
<br>这个结构对于经典的反馈网络、<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;  及更复杂的如 <a href="http://www.cs.toronto.edu/~hinton/absps/imagenet.pdf">ImageNet</a&gt; 都已经足够灵活。</br>

这个结构也允许一个 layer 成为多个网络的元素。比如,在 <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/… Belief Network</a>(深度信度网络)中的layer也可以用在其 RBM 中。

另外,通过这个架构可以将DBN的预训练阶段显示为一个栈式RBM的列表,微调阶段显示为一个前馈网络,这些都非常直观而且程序实现的很好。

<font color="#7a0e6b"><strong>数据流</strong> </font>

下个部分介绍网络中的数据流,一个两步过程:

1、定义出层间的序列。例如,为了得到一个多层感知机的结果,输入数据被赋到输入层(因此,这也是首先被计算的层),然后再将数据通过不同的方法流向输出层。为了在反向传播中更新权重,输出的误差通过广度优先的方法从输出层传回每一层。这部分通过<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…,应用到了网络图结构的优势,使用了不同的图遍历方法。其中一些样例包含了<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…; 和<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;。层的序列实际上由层间的连接进行决定,所以策略部分都是返回一个连接的有序列表。

2、计算激活值。每一层都有一个关联的<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;,包含有连接的列表(从上一步得来)和输入值(从其它层得到)并计算得到结果的激活值。例如,在一个简单的S形前馈网络中,隐含层的 ConnectionCalculator 接受输入层和偏置层的值(分别为输入值和一个值全为1的数组)和神经元之间的权重值(如果是全连接层,权重值实际上以一个矩阵的形式存储在一个<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;  结构中,计算加权和,然后将结果传给S函数。ConnectionCalculator 中实现了一些转移函数(如加权求和、卷积)和激活函数(如对应多层感知机的对数函数和双曲正切函数,对应RBM的二态函数)。其中的大部分都可以通过 <a href="https://code.google.com/p/aparapi/">Aparapi</a&gt; 在GPU上进行计算,可以利用迷你批次训练。

<font color="#7a0e6b"><strong>通过 Aparapi 进行 GPU 计算</strong> </font>
  
像我之前提到的,神经网络在近些年复兴的一个重要原因是其训练的方法可以高度并行化,允许我们通过GPGPU高效的加速训练。本文中,我选择 <a href="https://code.google.com/p/aparapi/">Aparapi</a&gt; 库来进行GPU的支持。

Aparapi 在连接计算上强加了一些重要的限制:

<li>只允许使用原始数据类型的一维数组(变量)。</li>

<li>在GPU上运行的程序只能调用 Aparapi Kernel 类本身的成员函数。</li>
  
<br>这样,大部分的数据(权重、输入和输出数据)都要保存在 <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…; 实例里面,其内部是一个一维浮点数组。所有Aparapi 连接计算都是使用 <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;(应用在全连接层和加权求和函数上)、<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;(应用在下采样层)或 <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…; (应用在卷积层)。这些限制可以通过 <a href="http://developer.amd.com/resources/heterogeneous-computing/what-is-hete… System Architecture</a> 里介绍的内容解决一些。而且Aparapi 允许相同的代码运行在CPU和GPU上。</br>

<font color="#7a0e6b"><strong>训练</strong> </font>
  
<a href="https://github.com/ivan-vasilev/neuralnetworks/tree/master/nn-core/src/…; 的模块实现了多种训练算法。这个模块依赖于上文提到的两个模块。比如,<a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;(所有的训练算法都以 <a href="https://github.com/ivan-vasilev/neuralnetworks/blob/master/nn-core/src/…;  为基类)在前馈阶段使用前馈层计算,在误差传播和权重更新时使用特殊的广度优先层计算。

我最新的工作是在Java8环境下开发,其它一些更新的功能可以在这个<a href="https://github.com/ivan-vasilev/neuralnetworks/tree/java8">branch</a&gt;  下获得,这部分的工作很快会merge到主干上。

<font color="#7a0e6b"><strong>结论</strong> </font>
  
本文的目标是提供一个深度学习算法领域的一个简明介绍,由最基本的组成元素开始(感知机)并逐渐深入到多种当前流行且有效的架构上,比如受限波尔兹曼机。

神经网络的思想已经出现了很长时间,但是今天,你如果身处机器学习领域而不知道深度学习或其它相关知识是不应该的。不应该过度宣传,但不可否认随着GPGPU提供的计算能力、包括Geoffrey Hinton, Yoshua Bengio, Yann LeCun and Andrew Ng在内的研究学者们提出的高效算法,这个领域已经表现出了很大的希望。现在正是最佳的时间深入这些方面的学习。

本文译自http://www.toptal.com/machine-learning/an-introduction-to-deep-learning…

来源:深度学习世界