本文主要是介绍lecture8-RNN的训练方法之二三,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
HInton第8课,之所以说之二三,是因为训练RNN的四种方法之一:长短时记忆在lecture7中介绍过了,这里介绍的是第二和第三种方法:HF优化和Echo (这个字觉得翻译成回声是不是欠妥,所以保留着,不过个人觉得“回显”不错)状态网络。这课有两个论文作为背景可以看《Generating Text with Recurrent Neural Networks》和《Echo state network - Scholarpedia》。
一、“Hessian-Free”优化的简单的介绍
在这部分中,会简短的介绍Hessian-free优化,这可以用在训练RNN中,这也是一个非常复杂的优化,HInton并不期望从这部分中就能知道所有的细节(要记得这是给本科生上课的,所以不同级别的 不同对待),只希望能有个大致的感觉,他是如何工作的,在第二部分中才会举个例子,它是如何完成一个很有趣的问题的。
当我们对NN训练权重的时候,会让误差尽可能的在误差表面上达到最低。所以问题来了,一个就是如果我们选择了一个给定的方向,那么在正确的方向上(真正的方向)到底能下降多少,在误差重新上涨之前那个方向上误差到底下降了多少(也就是求局部最小值或者全局最小值),这里我们假设曲率是常数
,这里假设它就是个二次误差表面(类似一个碗的纵切)。我们可以合理的假设当我们沿着梯度向下移动的时候,梯度的大小是减少的,这就是说我们认为误差表面其实是个上凸形状(就是碗型)。
我们通过某个具体的方向上的误差中得到的减少的最大值是由梯度的曲率比决定的。所以其实我们希望在移动的方向上有个好的比率,就算是梯度已经相当小了,我们还是希望曲率变得更小。
上图中就是个例子(图中下面的小图):垂直轴是表示误差,水平轴表示移动方向上的权值,蓝色箭头对应关于从红点开始的减少量。(上图中的上面大图):这里演示的是比上面那个更好的梯度曲率比,所以这次我们可以得到更大的误差虾酱值,所以更贴近最小值,问题在于如何找到第二个这种下降方向,即找到的方向上梯度是很小的,而且曲率甚至要更小。
这里以牛顿方法来开始吧,牛顿方法是为了处理在最速下降法上的基本问题的,也就是但梯度不在想要下降的方向上的问题。如果这个误差表面有圆形横截面,那么而且是二次的,那么梯度就是一个好的方向的选择,它将会引导直至最小值,所以牛顿方法的想法是通过一个线性变换使得椭圆变成圆,如果将这个想法应用于梯度向量的转换,那么就好像是在一个圆形误差表面上下山(形象的称呼)一样。为了实现这一的效果,需要将dE关于dW的梯度乘以曲率矩阵的逆矩阵,所以H是曲率矩阵,有时候也被称为Hessian,这就是我们拥有的权重的函数,而且我们需要对它取逆和与梯度相乘,然后我们就需要在那个方向上前进一段距离了。如果它真的是一个二次表面,而且我们选择的epsilon是正确的,那么剩下的事情就好办了,我们就可以在一个单步上达到表面的最小值,当然这里的单步是包含了很多复杂的东西的,也就是Hessian矩阵的逆矩阵。
这里伴随的问题是即使我们只有1百万个权重,这里的曲率矩阵,Hessian将会有1兆的项,这就是完全的不可能去求逆了。
所以曲率矩阵像上图这样,对于每个权重,Wi或者Wj,它们会告诉你在一个方向上和另一个方向上梯度的变化,换句话说,正如现在改变权重 i ,那么误差关于权重 j 的梯度的变化是多少,也就是非对角线上的项所表示的;而在对角线上的项说明的是误差梯度在这个权重方向上的这个权重的改变。所以曲率矩阵中非对角线项就是对应于误差表面中的曲折(twists),这里的曲折意思是当你在一个方向上尝试的时候,在另个方向上梯度会改变。如果我们有个很好的圆碗,所有的这些非对角线项就是0了,正如我们在一个方向上使用梯度,在其他方向上的梯度就不会受改变。
所以在最速下降法中的问题就是当你有个椭圆误差表面,如上述一样,在一个方向上的梯度改变会影响到其他方向上的梯度的改变,所以如果更新所有权重中的一个,那么在同一时刻就是也同时更新所有其他的权重,所有其他的权重上的更新又会导致第一个权重上梯度的改变,也就是说当更新的时候,有可能会让事情变得更糟。梯度也许会因为其他权重上的改变而事实上会是个反正弦(这句话不知道什么意思)。所以当我们得到越来越多的权重的时候,我们就需要在每个权重上都要越来越注意,因为在所有权重上的同时的改变有可能会改变我们梯度的范围。曲率矩阵决定着这些交互的尺寸(就是这个Hessian矩阵决定着所有权重的更新影响)。
所以我们不得不需要处理曲率,我们不可能忽略它。而且我们也希望处理的时候不需要进行对一个巨大的矩阵求逆,因为这个矩阵在一个大NN中将会有超多的项。我们所能做的就是通过观察曲率矩阵的主对角线,然后以此得到步长尺寸,这个的确有所帮助,他会让我们在不同的权重上拥有不同的步长尺寸,但是对角线也只是交互中的微小的一部分(这里是对于整个矩阵来说),所以我们这么干的时候其实是忽略了曲率矩阵的大部分信息的,事实上,我们差不多忽略了所有的信息;另一个我们所能做的就是将曲率矩阵用能抓住曲率矩阵主要方面的更低的矩阵秩来逼近(有点拗口,不过要是懂下面的几个方法的原理,相信就应该知道说的是什么意思),方法有:Hessian-Free,LBFGS和许多其他方法,去试图逼近最小化误差的二阶值。
在Hessian-Free方法中,我们通过对曲率矩阵进行逼近,然后假设逼近是正确的,所以我们假设知道曲率是多少,而且误差表面也的确是二次的。然后现在,引入一个高效的方法来最小化误差,叫做共轭梯度,一旦我们完成了,一旦我们逼近了曲率上的最小值,我们就随后就对曲率矩阵进行另一次的逼近,然后使用共轭梯度来再次进行最小化。在RNN中加上一个关于其他隐藏激活值改变太多的惩罚项,这可以阻止我们过早的改变权值而导致在序列中后期的巨大影响。我们不想让(RNN中的)影响很大,如果我们观察隐藏激活值中的变化,那么就能通过惩罚这些变化来阻止事情的发生,如果我们在这些变化上使用的是二次惩罚项,那么就能将它们与Hessian-free方法中剩下的部分结合起来。
这里最后需要解释的就是共轭梯度,这里将会简短的说下。共轭梯度是个非常聪明的方法:不是和牛顿方法中直接试图得到最小值,而是每次在一个方向上达到最小,所以它是以某个方向上的最速下降法然后在那个方向上达到最小值。这包含了对梯度的重新评估,和重新评估误差去找到这个方向上的最小值。一旦这么做了,然后就可以找另一个方向,然后在这个方向上找最小值。这个方法聪明的地方在于它以这样的方法选择的第二个方向并不会影响在第一个方向上得到的最小值的结果,这也被称之为共轭方向,共轭就是说在选择新的方向上,不会改变之前方向上的梯度。这是个很有趣的想法,就像之前误差表面上曲折(twists)的想法一样,曲折就是说当你在一个方向上的时候,就会改变另一个方向上的梯度;而共轭方向是一旦到了一个方向上,那就不会有曲折发生,即一旦到了一个方向上,在第一个方向上的梯度就不会发生。
这里是个椭圆图形,红线是椭圆的主轴,我们通过采用沿着所有方向上执行一步最速下降法来作为开始(上图中从椭圆外面进入的黑色箭头),如果想想的话,就能发现其实最小值不是沿着红线的,在红线的正确角度上梯度是为0的,因为这是山谷的底部了,但是我们跟进的方向事实上不是这个点的正确角度。我们可以多做一些处理来使得在红线上朝着正确角度前进一小步,然后沿着红线前进一小步。因为红线是朝着椭圆的中部坡度下降的,这也指导着我们一些处理上的想法,所以当在第一个方向上达到了最小值,我们将会笔直的横跨椭圆的底部(第一个大黑箭头),然后当我们达到了那个点,也就是那个方向上的最小值,对于沿着上图中绿色线来说,黑色箭头(第一个)的方向上的梯度都是0,所以我们可以沿着绿色线去任何地方而且不会破坏之前在黑色箭头上得到最小值的事实。(个人:这大段意思就是开始的大黑色箭头就是在第一个方向上得到的最小值,而且通过计算在绿色线上运动,第一个方向上的梯度都不会变化,那么就可以沿着这条线来计算第二个方向上的梯度并计算最小值了)
如果我们能在高维度误差表面上徐哦多方向上这么干,我们最终就能得到在许多不同方向上的最小值,而且如果我们在我们空间上所有维度方向上都达到了局部最小值,那么也就会最后处在全局最小值的位置了。所以我们先执行第一步最速下降,然后找出绿色线方向,然后在这个方向上搜索我们可以走多远以至于沿着这条线都能达到这个方向的误差最小化;然后采取第二步,如图中第二个黑色箭头,在这个2D空间中,我们可以得到最小值(黑色箭头顶端,也就是和绿线和红线交叉的地方),是因为我们在第一步方向上得到的最小值而且现在第二个方向上也都达到了最小值,那么两个方向上的最小值,就是全局最小了。
共轭梯度所能达到的它达到了在N-D二次表面上的全局最小,而这只需要N步,这非常的高效。它成功的在N个不同方向上使得梯度为0。它们虽然不是正交方向,但是他们都是各自独立的,所以这是很有效率的达到全局最小的方法。更重要的是,在二次表面上跨出许多步后(就是只运行了小于N步的时候)将会让误差非常的接近最小值,而这就是为什么用它的理由,我们不需要完整的运行N步,因为当N很大的时候,计算代价比较大,有可能和将整个矩阵求逆一样大,我们只需要执行小于N步,那么就能接近最小值。
可以直接将共轭梯度方法应用在非二次误差表面上,比如来自多层非线性NN的误差表面,而且通常效果还很不错。它本质上也是批量的方法,但是也能将它应用在大mini批量上,当这么做了之后,在同一个大mini批量上执行了许多步共轭梯度,然后在下一个大mini批量上操作,这也叫做非线性共轭梯度。
Hessian-Free优化使用的是共轭梯度方法,在真正的二次表面上达到最小值,这也是共轭梯度最擅长的。在二次表面上的工作效果还是比非线性表面上的效果要好。这里HF方法是通过对真正的表面进行二次逼近得到的真正的二次表面。所以在得到逼近之后,使用共轭梯度在第一个逼近上接近最小值,然后在曲率上进行新的逼近,然后再次采用同样的操作。
二、使用乘法连接来对字符串进行建模
这部分将介绍使用Hessian-Free优化去对字符串数据进行建模,这里的字符串都来自于维基。所以这里的想法就是从维基上阅读大量的语句,然后让模型试着去预测下一个单词。在介绍模型学习之前,先说明下为什么我们需要乘法连接和我们怎样在一个RNN中高效的执行这些乘法连接
这里先解释下为什么使用字符串而不是单词串(个人觉得翻译成单词流更好,想法来自c++语法)来进行模型的训练,而单词串是通常用来在对语言进行建模的时候使用的。
web网页都是由字符串组成的;任何足够强大的学习方法都能够通过阅读这些网页理解知道到底发生了什么,这些方法都需要最基本的知道哪些字符组成单词,正如我们肉眼看到的,这些都是正确的道理。所以在这里我们对接下来做的事情很有信心,我们希望某些模型能够阅读维基然后理解这个世界。
如果我们对维基文本预处理成单词,那么这将会导致一个巨大的争论,那么就充满了问题了。比如第一个问题就是语素(相对于声音也有音素),也就是语言学家认为的意思的最小单元。所以如果想要明智的去处理的话,我们就需要将每一个单词拆成语素(如上图中pre,es),但是问题就是还不清楚语素到底是什么。这些(上图中的例子)都有点像语素,但是语言学家却不会这么认为这些是语素。
所以在英语中,如果你以sn开头的任何单词,那么有可能有会存在嘴唇或鼻子所导致的意思的高度变化,特别是上嘴唇和鼻子,所以例如单词snarl、sneeze、snot、snog、snort等。有着太多这样的单词是恰好一致的(个人:就是这里分出来的语素没法用来表达单词的意思,一个sn看起来像语素,但是同一个语素却能存在多个单词中,所以才觉得按照单词训练不太好,想按照字符训练)。许多人也许会说是的,那么现在该干嘛呢?这完全不影响上嘴唇和鼻子啊,但是问问自己吧,为什么雪(snow,觉得是字幕错误,这里就当是某个单词吧)是针对可卡因的一个好的单词,然后这些单词都来自于好几个部分。所以正常的来说,我们想要将New York视为一个词汇,但是如果我们想谈论纽约的大教堂屋顶,那么我们就需要将new 和york视为两个独立的单词(就是有些词汇在某些情况是可以视为词汇,但是某些情况却需要分开看待)。
还有像Finnish(芬兰语)这样的语言,Finnish(芬兰语)是一个胶合的语言,所以它是将许多语素放在一起来形成一个大单词。所以这里就有个例子是将5个英语单词组合成一个单词的芬兰语(上图红色最长的单词),这时候就没法知道单词的意思了,但是不管是理解的缺失(就是水平不够),这就是为了说明语素的划分其实很难。
上图就是一个公认的RNN,可以用来对字符进行建模。本例子中,它有着一个隐状态,然后Hinton使用了1500个隐藏单元,这里的隐状态动态就是在时刻T 隐状态提供的输入去决定在时刻T+1时的隐状态,同样字符也提供着输入,所以我们将当前字符和之前隐状态的影响结合起来去得到一个新的隐状态,然后当我们获得了一个新的隐状态,那么就试图去预测下一个字符。所以我们这里有着在86个字符上(86这个数字目测应该就是ascii中可打印出的字符吧)的一个单一的softmax,然后通过得到的隐状态去指定高度概率的下一个正确的字符(softmax就是选定输出层上概率最大的那个作为结果的),然后将低概率的作为其他部分。然后从softmax开始使用BP来训练整个系统的得到正确字符的低概率情况(就是使误差最小化,让整个系统能够最大概率的识别正确的字符,当然是将正确字符的低概率事件作为误差考虑了)。
(看上图结构说明)我们用BP方法先穿过从隐藏到输出之间的连接(就是输出层与倒数第二层之间的权重)往回穿过隐藏到字符之间的连接,然后往回穿过隐藏到隐藏之间的连接等等,从所有的路径返回到字符串的开始部分(也就是输入层)。
预测86个字符比100,000个单词简单的多,所以在输出层使用softmax也是很容易的,这里没有前面课程说的那种大softmax的问题。
现在,来解释为什么不使用那种RNN吧,而是使用另一种不同的网络,而且工作的效率更好。在我们的例子中,你可能将所有的字符安排成一个有着86个分支率的树。这里展示的,就是那个大树的一个微型子树,事实上,这个子树将会出现很多次,但是每次代表着不同的事物(这里由fix前面的 三个点来表示)
,所以这里表示我们已经有了整个字符树,然后我们就有了f,然后是i ,然后是 x。现在如果我们得到了一个 i ,我们就顺着根到叶子节点部分往左下方走;如果我们得到了一个 e ,那么我们就往右下方走。所以每次我们得到了一个字符,我们往下移动一层去得到一个新的节点。在长度为n的所有字符串中有着指数级别的节点,所以我们会得到一个超大型树以至于我们没法将它们进行存储起来,如果我们将它存储起来了,我们所要做的就是在每个箭头上加上概率了,这就是在给定节点的上下文的情况下得到字母的概率了。
在RNN中,我们试图处理的事实是这整个树是很巨大的,是通过一个隐状态向量来表示每个节点的,所以现在下一个字符所要做的就是:考虑用来表示字符串而且后面跟着fix的一个隐状态向量,然后在这个隐状态向量上进行处理去生成一个合适的新隐状态向量。如果下一个字符是 i 的话,那么就需要将这个隐状态向量转化成一个新的隐状态向量。
关于处理这个(使用RNN的隐状态)字符树中的这些节点的一个很好的方法就是通过共享一些结构。例如,在我们到达那个节点的时候,也就是f,i,x ,我们就能决定他可能是个动词,如果他是一个动词,那么下一个就多半是 i 了,因为考虑可以用 i,n,g来后缀。而这时候就可以将那些没有 f i x 的但是具有i,n,g 的子树进行共享,而且可以在所有动词之间共享。
注意到,是我们所处的当前状态的连接和这个字符决定了我们所要走的路径,我们不想 i 给我们一个状态说下一个期望的字符是 n 而它却不是一个动词,所以我们不需要说i 可能会导致下一个是 n 了。我们想要说的就是,如果已经认为这是一个动词了,那么当看见一个 i ,那么就期望下一个是 n 。这是实际生活中的联系:我们认为他是一个动词,然后我们看见个 i ,这使得我们进入这个状态被标记为 f,i,x,i,那么就期望遇见下一个是 n。(这里说的比较罗嗦,其实就是这个树结构可以有组织的搜索,而不是已经匹配了半个单词的时候,下个字符还需要26个字母的去瞎匹配。就可以通过实际的单词表等来很好的预测下个字符了)
所以我们通过乘法连接去试图去实现上述的结果。不使用字符输入到RNN中去得到额外的输入,然后输入给隐藏单元,Hinton这里使用这些字符去在一个完整的隐藏到隐藏的权重矩阵中进行交换,这里字符决定了转换矩阵。现在,如果使用一个简单的方法,那么就是让这86个字符中的每个一都能找到一个1500×1500的矩阵,而这就是有着很多的参数需要处理了,如果我们有了这么多的参数,那么很有可能会过拟合,除非我们在一个超巨大数量的文本上运行,而这也没那么多时间。
所以解决的方法就是如何达到使用乘法的相互作用的结果,就是在字符决定隐藏到隐藏权重的矩阵上使用更少的参数。通过考虑一个事实:字符在很多地方是通用的。例如,所有的数字在使得隐状态进化的方式中都是相互之间相似的,所以我们希望对于86个字符中的每一个都得到一个不同的转换矩阵,但是这86个字符具体权重矩阵能够共享参数,这是合理的,因为我们知道字符8和9有着非常相似的转换矩阵。
这里就是我们将要做的事情,这里先引入一个被称为因子的东西,他们被上图中有着F的一个三角形表示。因子的意思是组a 和组 b相互之间相乘去提供输入给组 c。所以每个因子所作的就是首先计算它的两个输入组的权重化的和,所以这里先将组a的向量状态称之为 a,然后通过一个与因子连接的权重与之相乘,换句话说,就是基于向量a和权重向量u的标量积,就可以得到上图中三角形左边顶点的一个值,相似的,我们考虑组b就能得到因子的底部的顶点的值。现在我们将这两个数字相乘,然后得到一个数字或者标量。然后我们使用这个标量与输出到组c的权值v相乘去得到组c。就是如上图中的公式。(中间省略一段废话)然后,我们就得到了整群因子了。
这里是对因子的另一种理解方式,来说明具体的过程。每个因子实际上定义了一种非常简单的转换矩阵,这是一个有着的秩为1的转换矩阵,所以之前得到的式子中(上图中最上面那个式子)是将因子视为两个标量的乘积乘以即将输出的向量 v (也就是权值)。我们可以重新排列下这个式子(上图中第二个式子),所以我们得到了一个标量乘积,然后重新安排最后一位,所以现在,我们考虑权重向量 u 和 v,然后得到一个外积,就得到了一个矩阵。这里的标量乘积是由b乘以w得到的,只是这个外积矩阵的系数而已,就是一个标量系数,然后乘以得到的外积矩阵得到一个标量矩阵,然后用当前隐状态 a 乘以这个矩阵去决定因子 f 的输入去得到下一个隐状态。如果我们将所有的因子相加(上图第三个式子),那么到组 c 的所有输入就是一个标量的所有因子的和乘以一个秩为 1 的矩阵,这个和得到的是一个大矩阵,这就是所谓的转换矩阵,然后将它乘以当前的 隐状态 a 去得到一个新的隐状态,所以我们能发现我们综合了这个转换矩阵,实际上,这个秩为1 的矩阵是由每个因子提供的,组 b 中当前字符所做的就是决定这些秩为 1 的矩阵的每个权值,b 乘以 w 就决定了每个矩阵的标量权值,标量系数。我们这里要做的就是将这个大字符指定正确的矩阵进行组合。
这里就是整个系统的示意图了。这里有一群因子,实际上会有大约1500个因子,而且字符输入在这里面是不相同的,只有他们中的一个是激活的(一次只输入一个字符)所以每一次只有一个相关的权值,这个权值来自于当前的字符 k (上图中举的例子),也被称为Wkf,是用在秩为1 的矩阵上的增益,这个矩阵是 由 u 和 v 的外积得到的。所以这个字符决定了一个增益 ,Wkf, 然后用u和v 得到的矩阵乘以这个增益,接着对所有不同的因子上加上所有的标量矩阵并得到一个转换矩阵。
三、使用HF去学习并预测下一个字符
这部分将介绍在当Hessian-Free优化被用在包含乘法连接的RNN上和这个网络在维基上训练并预测下一个字符时到底发生了什么。这个网络是在百万级别的字符上训练的,而且结果显示它工作相当的出色。它学习了许多英语然后变得非常擅长以很有趣的方式来完成句子(就是补完句子)。
Ilya Sutskever使用了5百万个字符串,每个字符串有100个字符,这些都是来自于英语版的维基。对于每个字符串来说,他就开始预测第11个字符后面的字符
,所以这个RNN是以一个默认的状态开始的,它读取前11个字符,然后每个时间步改变它的隐状态,然后它就开始预测了,然后在基于预测上的误差来使用BP去进行训练。
他使用了Hessian-free优化,然后在一个非常快的GPU板上训练了一个月并得到了一个很好的模型。
他的针对字符预测的最好的RNN有可能是当前用来预测字符的最好的单一模型。你可以通过结合不同的模型来比这个模型做的更好,比如决定使用哪个不同的NN,但是如果是单一的模型的话,他的模型也是差不多最好的了。
它与其他模型相比以非常不同的方式工作,所以Ilya的模型能够平衡引号和括号很长的距离,而任何依赖于匹配指定的之前上下文的模型却不能做到。例如,如果有一个括号,然后差不多想最多在35个字符后发现右括号,为了合理的完成这个任务,一个依赖于匹配以前上下文的模型就不得不匹配所有的介于括号中的35个字符,而且它也不像已经存储了整个字符串。
一旦这个模型学习完成,那么就可以看到通过从模型中生成的字符串来观察它到底知道了什么,当然也不需要过分解读它所说的。生成字符串的方式是以隐状态的默认状态开始的;然后给它一个“燃烧中”的字符串流,所以我们是通过给他馈送字符,然后让他在每个字符后进行更新它的隐状态;然后我们让它停止预测,观察它预测下一个字符的概率分布;然后随机从这个分布中挑选出一个字符,所以如果他预测Q的概率是1/1k,那么我们就每1k次挑选一个Q,然后我们告诉这个网络我们挑选的字符是真实发生的字符,然后叫模型预测下一个字符,换句话说,我们告诉它它猜测的什么都是对的;然后让它接着预测直到我们得到我们所要的的时候;然后观察它输出的字符来观察他到底知道什么。
所以这里就有个由Ilya的网络在被输送一些“燃烧中”的字符串后预测的字符串。这“燃烧中”的字符串是从一个更大段的文本中选择出来的,但是它也是一个连续的短文,结果显示这个模型工作的很好。你有可能发现他有一些奇怪的语义联想,例如“opus paul at rome”没有人会这么说,但是我们可以理解opus和paul和rome是高度相互关联的。你也可能注意到它不是真正的拥有很长范围的主题结构,它在每个句号之后主题后就有了很大的改变。一个令人惊讶的事情是它几乎不会生成奇怪的单词(非单词,就是它生成的都是我们可看过的单词),意思就是即使他预测字符的概率,一旦你拥有了足够的字符,那么让它作为一个英语单词来完成的唯一方法就是它会近乎完美的预测下一个字符,如果这不会发生的话,它就会生成一个非单词。即使当它生成了一个非单词,如上图中红色标注的,它们都是一个很好的非单词。我们不会完全的肯定或者当看见它第一眼的时候,都不认为“ephemerable”不是个英语单词;
你也许会发现它生成了一个右括号而没有生成一个左括号,所以它没有总是很好的平衡括号,它这样的行为也是挺频繁的,因为你有可能在文章的最后发现一个左括号然后在很久之后才有一个右括号。这在它的部分一致的行为(这里是解释为什么上图只有有括号,有可能在之前就有了左括号),它真的生成了这个右括号,是因为他在之前就有了一个左括号;
如果观察这个文本,那么就能发现,里面还是有很多很好的局部语法的,所以三、四个单词组成的单词串看起来也很有合理性;同样它有着许多的语义知识。
所以我们能做的一件事就是可以通过给它一些精心设计的字符串来测试这个模型去观察它所知道的。所以Hinton试图给它一些非单词,“Thrunge”这不是一个英语单词,但是大多数说英语的在但他们看到这个这个单词的时候,都因为它的形式而猜测这是一个动词(就是拿一个大多数人都会分错的单词给这个模型)。所以给它一个公开的上下文环境然后观察下一个预测的将会是什么。
所以如果给它一个"Sheila thrunge"然后让它预测下一个字符,最可能的字符就是 s ,这表明它认为(只是从维基上去读起来)sheila是一个单数;如果给它“people thrunge”那么下一个最有可能的字符就是一个空格,而不是一个 s ,这表明它知道people是个复数;然后试图给它一个名字列表,所以这里使用了名字首字母大写,并在之间用了一个逗号,然后给“thrunge”的首字母大写让这个单词看起来也像个名字,去观察这个模型会用它们怎么做,然后事实上它真的把它当成个名字了,而且看起来还不是那么糟糕,这也表示它知道很多语言中的一大堆名字;同样你也可以给他“the meaning of life is”然后看接下来是什么,如果结果是42,那么事情就不会那么有趣了,因为Hinton相当确信这是存在于维基的某个地方的语句,它随机生成事物,但是在前10次的实验中,它生成的都是“the meaning of ief is literally recognition”,这在句法上和语义上都是正确的;然后我们试了很多次,然后在上面的测试的10次之后它又得出了一个很有意思的语句,它生成的完整的“the meaning of life is”的语句,从在维基上读取的结果看来它真的开始知道“the meaning of life”的真正意思了,尽管这可能看起来是奇异的过度解释了。就是上图最长的那句。
所以这个模型在读完了这些维基上得到的字符之后知道的就是:当然它知道单词,它几乎总是生成英语单词,它还知道在字符串的开头通常都是大写,它能生成数字和日期和类似的东西,不过它不是经常生成非单词的,它几乎很少会犯错,而且当就算生成了非单词,那也是具有可以理解的非单词。它同样知道很多名字,例如“Frangelini Del Key”,它知道日期和数字和上下文中发生的东西;
它也擅长平衡引号和括号,事实上它能对括号计数。如果你给它的不是一个左括号,它也不怎么生成一个右括号,如果你给它一个左括号,它差不多会在接下来的20或者更多的字符后面生成一个右括号。如果你给它两个左括号,它会比较快速的生成一个右括号,但是如果给它三个的话不是意味着会更快;
它也清楚的知道一些有关语法的知识,因为它可以生成一些具有意义的简短的英语单词串,但是也很难一针见血的说形成这些知识的是什么,它不像三元组模型(之前课程说的)那样是学习很短的单词串,或者说他们有个包含简短单词串的表。它实际上是综合了单词串的,而且它通过有意义的语法来进行综合的。也很难说是什么形成了语法知识,它也不是像一个语言学一样有着一大堆的规则,它看起来更像是当一个人说一门语言的时候它脑袋中的语法一样(传说中的语感);
它也知道许多的弱语义联想,所以例如:它甚至只生成一次“Wittgenstein”这个单词,然后就很快的生成单词“Plato”,所以它知道Plato和Wittgenstein都是有联系的,这是一个很好的假设。它清楚的知道“cabbage”是与“vegetable”之间有着联系。它不知道这些事物联系的准确方式,同样人也是这样,如果你让他们快速的反应:问你一个问题,然后想要你大声的喊出答案。即你坐在这里看这个课程,实验马上就要开始了,最好如果你知道了这个答案,那么就需要快速大声的喊出来,你就能很快的得到奖励,对你说的什么内容不关心,关心的是你的快速反应。那么问题来了:奶牛喝什么?大多数人会立马喊出来牛奶,但是大多数奶牛在大多数时间上都不喝牛奶。我们说牛奶是因为它有着喝和奶牛上的关系才直接脱口而出的,但是在逻辑上这不符合
最近,Thomas Mikolov和他的同僚已经训练了一个相当大的RNN去预测在大量的数据集中的下一个单词,他们使用和前馈NN一样的技术,即首先将单词转换成一个实值特征向量,然后使用这些特征作为网络剩余部分的输入,他们做的比前馈NN好很多;他们同样做的也比最好的其他模型要好;当你将它们与其他最好的模型平均后相比,他们仍然计胜一凑,所以这也是当前最好的语言模型了;
RNN的一个有趣的特性是它与其他达到通级别效果的模型相比需要更少的训练数据;
更重要的是,正如这些数据集变得越来越大,RNN提升的比其他方法要快,所以其他方法例如三元组,在遇到更大的数据集的时候要想要得到更好的结果,但是却是更慢的处理速度,你还需要将数据集的尺寸翻倍才能得到一个小的提升结果。而RNN,他们能使用这些数据做的更好,也就是说在随着数据变得越来越大的情况下,打败他们将会变得更加困难。Hinton认为这与使用大的,深的NN来做对象识别也是同样的道理,但是一旦NN领先了,它们就能在更快的计算机和更大的数据集上做的更好,也会让其他的方法更加的望尘莫及。
四、Echo状态网络(echo state network)
在这部分将会介绍echo状态网络。通过使用一个聪明的技巧去世的学习RNN变得更容易。他们在RNN中线通过一种方法进行初始化连接,即这种方法中有一个大的耦合振荡器的保留。所以如果你提供输入给它,它就会将输入转换成这些振荡器的阻焊台,然后你就能从这些振荡器的状态中预测你想要的输出。唯一你所要学习的就是怎么耦合这些输出到振荡器。这个方法是完全避免了关于学习隐藏到隐藏连接或者输入到隐藏的连接的问题。然而,为了让这些网络在复杂的任务中表现良好,你就需要一个非常大的隐状态,正如这部分将要介绍的,没有原因不使用(就是应该用)给echo状态网络精心设计的初始化,然后使用带有动量的BP through time去训练这个网络使它在当前执行的任务上完成的更好。
一个有趣和最近的想法去训练RNN是完全不要训练隐藏到隐藏的连接,而是随机固定他们,并期望你能通过对输出的影响进行训练来学习序列。这与老想法的感知机强烈的相似。所以一个简单的想法去训练一个前馈NN的方法是去将网络前面结构上特征检测层的层随机赋值固定,就是将这些权值以有意义的尺寸(个人:这里说的就是权重的大小的范围)随机赋值,然后所有你要学习的就是最后一层,也就是从最后一层隐藏单元上的激活值到输出层上去学习一个线性模型。当然学习一个线性模型是可以更快的,这依赖于一个想法:一个输入向量的大的随机扩展可以通常使得线性模型能够容易的拟合数据,当不能很好的拟合数据的时候,那么就观察下原始的输入。
虽然这里是个小型nn,这些红色权值都是随机固定的,它们会将输入向量进行扩展,然后使用这些扩展的表征,然后试图去拟合出一个线性模型。这和SVM很大的相似。所以这些相同的想法,在许多年之后,又重新循环到RNN上被RNN所用,想法就是将输入到隐藏的连接和隐藏到隐藏的连接给精心选择的值固定了,只是学习隐藏的最后一层到输出的连接,然后当你使用的是线性输出单元的时候,学习是相当简单的,而且可以做的超级快。这个方法只在你精心安排随机连接的时候才会工作,所以RNN不会没有激活值的消失或者爆炸(之前课上讲的两个极端)。
所以在echo状态网络中设置随机连接的方法就是设置隐藏到隐藏的权值使得激活向量的长度能够保持在每个迭代之后还是相同的。如果使用线性系统和矩阵的话,就设置他们的谱半径为 1 .,即隐藏到隐藏权值矩阵的最大特征值为 1 ,或者当他是线性系统的时候它就是 1 。如果你想在非线性系统中获得同样的特性,如果你设置这些权值都在正确的大小,那么一个输入就能在递归状态的周围回显很长一段时间(这里就是echo的来源)。
同样的使用稀疏连接也很重要,所以不使用许多中等尺寸的权重,而是有一些相当大的权重,而且在隐藏到隐藏连接上靠近所有这些权重的权重都是为0,这也使得有了很多松散的耦合振荡,所以信息就能在网络的一部分上挂起,而不会太快的传递给网络的其他部分。
选择输入到隐藏连接的尺度同样也是很重要的,这些连接需要去驱动这些松散的耦合振荡器,但是他们有不能抹去这些信息,也就是这些振荡器需要包括最近的历史信息。
幸运的是在echo状态网络中这些学习是可以很快的,所以我们可以提供实验去寻找合适的重要连接的尺寸,你可能认为那就是个小的学习循环而已,只是为了学习这些连接的尺寸,而它是通过这些实验来进行反馈的排序。它同样有助于学习在隐藏到隐藏连接上的稀疏,同样的因为学习可以很快,所以可以提供实验来找到好的解。这很重要是因为通常来说做实验去使得系统很好的工作是很有必要的。
这里介绍的是一个简单的例子,从一个echo状态网络的web上得到的。它有一个输入序列,这个序列的实值是随着时间变化的,并指定了一个echo状态网络输出的sin波的频率,所以你会希望这个模型能够生成sin波,这里的输入是指定这个频率的。
目标输出序列是同样也是用输出来指定同样的频率的波。
而且可以通过设置一个线性模型来简单的进行学习,这个现象模型考虑隐藏单元的状态然后用这些来预测正确的标量输出值。
所以这里就是一张从echo状态网络的学术百科上下的图,为了完成这个程序,输入信号是sin波的期望频率,在学习之后得到输出信号(或者说是监督(teacher)信号),当学习的时候,输出信号是一个被输入指定频率的sin波。在中部的填充是一个大的动态存储器,所以从输入信号得到的输入可以驱动这些松散的耦合振荡器,并引发复杂的动态持续很长一段时间。这些输出权值可以将这些复杂的动态映射到你想要的输出的具体的动态。所有的其他图片都是为了说明事实上包含在动态存储器中的独立单元的动态。所需要注意的一件事是从输出回向到存储器的连接,这部分不是总是需要的,但是他们有助于告诉存储器到目前为止到底生成了什么。
这里就是一个在系统学习完之后实际生成的例子,你可以发现在开始部分,他生成一个sin波的相,在最后,他生成一个正确频率的sin波,但是这个相却是错的,这是因为我们没有告诉它里面应该是什么样的相的sin波,所以他是满足生成一个合适频率要求的。
这里是echo状态网络许多好的方面。它们可以被很快的训练是因为它们只是去拟合一个线性模型;他们同样证明聪明的隐藏到隐藏权值初始化是很重要的;他们可以令人印象深刻的建立基于一维时间序列模型,这是它们优异的地方,它们可以通过观察一段时间的时间序列,然后很好的预测未来很长一段时间上的结果,他们不擅长的地方是对高维数据进行建模,例如声音系数帧,或者视频帧;为了对像这样的数据进行建模,它们需要在隐藏到隐藏连接上比RNN更多的隐藏单元;最近,Ilya Sutskever试图去初始化一个RNN,并使用了所有人们在echo状态网络上提出的技巧,一旦你也这么做了,你会发现只是学习隐藏到输出的连接就可以学习的相当好。但是假定你同样学着去使隐藏到隐藏的权值更好(就是这里也考虑隐藏到隐藏的权值,Ilya是只对隐藏到输出的权值进行学习了),那么模型就可以学的更好。所以Ilya试图是echo状态网络初始化,但是随后使用BP through time 来进行训练。他使用带有动量的rmsprop方法,然后发现,这实际上是一个非常高效的方法去训练RNN。
这篇关于lecture8-RNN的训练方法之二三的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!