tips

笔者最近在学习机器学习相关的知识,看到一些不错的文章。但都是英文版的,所以在看之余干脆翻译一下,这样也方便自己后面温习。下面这篇文章非常的长,很多地方笔者也还不是很明白。所以翻译有错误的地方,望大家斧正。当然,英文好的朋友建议看了译文之后再去看一下原文,应该会有不同的理解。
原文:The Unreasonable Effectiveness of Recurrent Neural Networks

开始

循环神经网络-RNNs 有一些神奇的地方。我还记得我训练第一个用于Image Captioning的RNN时,对我的第一个Baby模型(任意选择一些超参数)训练十几分钟之后,便开始呈现出看起来很不错的图像描述,这些描述已经处在有真正意义的边缘上了。有时,模型的简单程度与你获得结果的质量进行对比,已超乎你的预期了,这种情况只是其中一次。在当时,这个结果让我大吃一惊的原因是,大家普遍认为RNN应该是非常难训练的(实际上,在有了更多的实验之后,我得出了相反的结论)。时间向前推进一年,这一年我一直在训练RNN,也多次见证了RNN的稳定性与强大之处。但RNN神奇的输出仍然让我感到惊讶。这篇文章将和你分享这其中的奥秘。

我们将训练一个逐字生成文本的RNN,并同时思考“这怎么可能做到呢”

顺便说一句,与这篇文章一起,我也在 Github 上发布了相关代码。允许你训练基于多层LSTM的character-level 语言模型。你给它提供一大块文本,它会学习,然后生成一次一个字符的文本。你也可以使用它来重现我的实验。现在,我们来看看什么是RNN。

递归神经网络(Recurrent Neural Networks)

序列(Sequences)。我猜你可能想知道:是什么让 Recurrent Networks 如何特别?Vanilla 神经网络(以及Convolutional 网络)的一个明显的缺陷是它们的API太受限制:它们接收固定大小的矢量作为输入(比如:图像),并产生固定大小的矢量作为输出(比如:不同类别的概率)不仅如此:这些模型执行映射时使用固定数量的计算步骤(比如:模型中的层数)。recurrent nets 令人兴奋的核心原因是它允许我们对矢量序列进行操作:输入,输出或者最常见情况下的序列。下面列举一些具体例子:

图中每一个矩形代表一个向量,箭头表示函数(例如:矩阵乘法)。输入向量位红色,输出向量位蓝色,绿色向量表示RNN的状态(它会变得越来越多)。从左到右:(1) 没有RNN的Vanilla 模式,从固定大小的输入到固定大小的输出(例如:图像分类)。(2) 序列输出(例如:image captioning采用拍摄图像,并输出单词的句子)。(3) 序列输入(例如:情感分析,其中给定的句子被分类为表达正面或者负面情绪)。(4) 序列输入与序列输出(例如:机器翻译。RNN用英语读取一个句子,然后用法语输出一个句子)。(5) 同步序列输入与输出(例如:视频分类,我们希望标记视频的每一帧)。需要注意的是,在每种情况下,长度序列都没有预先指定约束,因为循环变化的部分(绿色块)是固定的,并且可以根据需要应用多次。

正如您所料,这种序列化的操作与通过固定数量的计算步骤相比更为强大,因此对于那些渴望构建更智能系统的人来说也更具吸引力。此外,正如我们稍后将要看到的,RNN将输入向量,状态向量和固定(但已学习)的函数进行组合以产生新的状态向量。在编程术语中,这可以解释为运行一个具有某些输入和一些内部变量所关联的固定程序。从这个角度来看,RNN本质上是在描述程序本身。事实上,众所周知,RNN从某种意义上来说是Turing-Complete ,它们可以模拟任意具有适当权重的程序。但与神经网络的通用逼近定理一样,你不需要理解太多。

如果说训练vanilla 神经网络是在优化函数,那么训练recurrent nets就是在优化程序。

没有序列时的顺序处理。你可能为认为将序列作为输入或输出的情况相对较少,但要意识到的重要一点是,即使你的输入/输出是固定向量,仍然可以使用这种强大的序列化形式处理它们。例如,下图展示了来自DeepMind的两篇非常好的论文的结果。1图,是一种循环网络策略的算法,它的关注点是遍历整张图像;特别是,它学会了从左到右读出门牌号(Ba et.al)。2图,是一个通过学习顺序添加颜色到canvas 来生成数字图像的循环网络(Gregor et.al )。

需要注意的是,即使你的数据不是序列形式,你仍然可以制定和训练强大的模型,让它学习按顺序处理。就像你正在学习一个有状态,可以处理有固定大小数据的程序。

RNN 计算。这些东西是如何运作的呢?核心的部分,RNN有一个看似简单的API:它们用来接收输入向量X,并为你提供输出向量Y。但是,关键的是这个输出向量的内容不仅会受到你输入的影响,还会受到你过去输入的整个历史记录的影响。如果作为一个类编写,RNN的API由一个step函数组成:

rnn = RNN()
y = rnn.step(x) # x is an input vector, y is the RNN's output vector

RNN类具有一些内部状态,每次step 函数调用它都会更新。在最简单的情况下,该状态由单个隐藏向量h 组成。下面是Vanilla RNN中 step 函数的实现:

class RNN:
  # ...
  def step(self, x):
    # update the hidden state
    self.h = np.tanh(np.dot(self.W_hh, self.h) + np.dot(self.W_xh, x))
    # compute the output vector
    y = np.dot(self.W_hy, self.h)
    return y

上面说明了vanilla RNN 的正向传递过程。该RNN的参数是三个矩阵W_hh,W_xh,W_hy。隐藏状态 self.h 用零向量初始化。函数 np.tanh 实现非线性的方式将激活单元压缩到[-1,1]区间。简要说明下它是如何工作的:tanh内部有两个部分:一个基于先前的隐藏状态,另一个基于当前的输入。Numpy 中np.dot是矩阵乘法。这两个中间体相互作用再加成,然后被tanh压缩到新的状态向量中。用数学形式来表示就是:

其中tanh被应用到向量中的每个元素上。

我们用随机数初始化RNN的矩阵,并且在训练期间的大量工作用于找到产生理想行为的矩阵,其中用一些损失函数进行衡量,该函数表示你输入序列“x”时希望看到哪种的“y”输出作为响应。
深入理解。RNN是神经网络,如果你进行深度学习,并开始把模型想煎饼一样堆叠起来,每一个部分都能独自工作的很好(如果做的正确)。例如,我们可以形成如下的2层循环网络:

y1 = rnn1.step(x)
y = rnn2.step(y1)

换句话说,我们有两个单独的RNN,一个RNN正在接收输入向量,第二个RNN正在接收第一个RNN的输出作为其输入。除非这些RNN都不知道或在意-其实这些输入输出都只是向量,在反向传播期间,一些梯度会每个模块中传递。
异想天开一下。我想简单提一下,在实践中,我们大多数人使用的模型与我上面提到的称为Long Short-Term Memory (LSTM) 网络的模型略有不同。LSTM是一种特殊类型的recurrent network,由于其更强大的更新方程和一些吸引人的反向传播特性,在实践中效果更好。我不会详细介绍,但我所说的关于RNN的所有内容都是一样的,除了计算更新的数学表达式(self.h = ... 这一行)变得更为复杂。从这里开始,我将交替使用术语“RNN / LSTM”,但本文中的所有实验都使用LSTM模型。

Character-Level 语言模型

好,我们知道RNN是什么,为什么让人激动,以及它们是如何工作的。现在,我们通过一个有趣的应用程序来看看:我们将训练RNN character-level 语言模型。意思是,我们将给RNN一大块文本,并要求它根据已有的一系列字符的序列中对下一个字符的概率分布进行建模。这将允许我们一次生成一个字符的新文本。
举一个例子,假设我们有4个可能的字母“helo”,并且想要在训练序列“hello”上训练RNN。这个训练序列实际上是4个单独的训练样例。1.“e”出现的概率来自于给出的“h”的情况下;2.“i”出现的概率应该是在“he”出现的情况下;3.“i”也应该是在“hel”的情况下。4.“o”应该是在“hell”出现情况下。
具体来说,我们将使用1-k编码将每个字符编码为矢量(即,除了词汇表中字符所对应索引处的下标是1,其他位置都是零)
并使用step函数将它们一次一个的送到RNN中。然后我们观察一系列4维输出向量(一个字符一维),我们将其解释为RNN当前分配给每一个字符在序列中下一次出现的可信度。下图:


$$ 这是一个4维输入输出层的RNN示例,隐藏层有3个单元。上图,显示了当RNN被输入字符“hell”时,正向传播的情况。输出层包含RNN为下一个字符分配的可信度(词汇表是“h,e,l,o”)。我们希望绿色数值高,红色数值低。 $$

例如,我们看到在RNN接收字符“h”的第一个时间步骤中,它将1.0的可信度分配给下一个字母为“h”,2.2给字母“e”,-3.0给“l”和4.1给“o”。在我们的训练数据(字符串“hello”)中,下一个正确的字母应是“e”,所以我们希望增加“e”(绿色)的可信度并降低其他字母(红色)的可信度。同样地,我们在4个时间步骤中的每一个都有一个期望的目标角色,我们希望神经网络为其分配更大的可信度。因为RNN完全由可微分运算组成,我们可以运行反向传播算法(来自微积分的链规则的递归应用),以确定我们应该调整每个权重的方向以增加正确目标的分数 (绿色粗体数字)。然后我们可以执行一个参数更新,它在此梯度方向上轻微推动每个向量的权重。如果我们在参数更新后将相同的输入送到RNN,我们会发现正确字符的分数(例如第一时间步中的“e”)会略高(例如2.3而不是2.2),并且分数不正确的字符会略低一些。然后,我们反复多次重复此过程,直到网络收敛并且其预测最终与训练数据一致,因为接下来始终能预测正确的字符。
更技术性的解释是们同时在每个输出矢量上使用标准Softmax分类器(通常也称为交叉熵损失)。RNN使用小批量随机梯度下降进行训练,我喜欢使用RMSProp或Adam(每参数自适应学习速率方法)来稳定更新。
还要注意,第一次输入字符“l”时,目标是“l”,但第二次是目标是“o”。因此,RNN不能单独依赖输入,必须使用其循环连接来跟踪上下文以完成此任务。
测试期间,我们将一个字符输入到RNN中,然后分析下一个可能出现的字符的分布。我们从这个分布中取样,然后将其反馈回来获取下一个字母。 重复这个过程,你正在采样文本!现在让我们在不同的数据集上训练RNN,看看会发生什么。
为了进一步说明,我还编写了 minimal character-level RNN language model in Python/numpy。它只有大约100行,如果你在阅读代码方面比文本更好,它可以给出简洁,具体和有用的上述摘要。我们现在将深入研究使用更高效的Lua / Torch代码库生成的示例结果。

Paul Graham generator

让我们首先尝试一个小的英语数据集作为健全性检查。我最喜欢的有趣数据集是Paul Graham’s 的文章集锦。基本的想法是,这些文章中有充满智慧,但不幸的是, Paul Graham是一个 慢条斯理的创作者。 如果我们能够按需提供想要的知识,那不是很好吗? 这就是RNN的用武之地。
数据集:将PG近五年的文章合成一个约1MB大的文本文件,包含了大约100万个字符(顺便说一下,这被认为是一个非常小的数据集)。模型:让我们训练一个2层LSTM,其中有512个隐藏节点(约350万个参数),每层dropout设为0.5。 我们每一次将训练100个例子,并在长度为100个字符的时间内截断并反向传播。在这样的设置下,TITAN Z GPU上的一批需要大约0.46秒(这可以用50字符BPTT减少一半,性能成本可以忽略不计)。不用多说,让我们看看来自RNN的样本:

The surprised in investors weren’t going to raise money. I’m not the company with the time there are all interesting quickly, don’t have to get off the same programmers. There’s a super-angel round fundraising, why do you can do. If you have a different physical investment are become in people who reduced in a startup with the way to argument the acquirer could see them just that you’re also the founders will part of users’ affords that and an alternation to the idea.
[2] Don’t work at first member to see the way kids will seem in advance of a bad successful startup. And if you have to act the big company too.

好吧,这个RNN模型完全不能取代PG,但要注意的是RNN是从头开始学习英语并且是在使用的小数据集上(包括你在哪里放逗号,撇号和空格)。我也喜欢它学会支持自己的论点(例如上面的[2])。有时它还能提供有那么一丝见解的东西,比如:“a company is a meeting to think to investors”。如果您想了解更多信息,这里有50K字符样本
temperature参数。我们可以调整采样过程中Softmax函数的temperature参数。将 T 从1降低到更低的数值(例如0.5)使RNN更有信心,但也意味着生成的样本更加保守。相反,较高的 T 将带来更多的多样性,但代价是更多的错误(例如拼写错误等)。将 T 设为接近0的值会产生最像PG说过的话,如:

is that they were all the same thing that was a startup is that they were all the same thing that was a startup is that they were all the same thing that was a startup is that they were all the same

译者注:temperature是Softmax函数中的一个参数,默认为1。当 T 很大时,即趋于正无穷时,所有的激活值对应的激活概率趋近于相同(激活概率差异性较小);而当 T 很低时,即趋于0时,不同的激活值对应的激活概率差异也就越大
参考:Softmax Layer

莎士比亚

经过上面实验,看起来我们可以学习拼写英语单词了。但是如果数据中有更多的结构和风格呢?为了检查这一点,我下载了莎士比亚的所有作品并将它们连接成一个(4.4MB)文件。我们现在可以负担得起培训更大的神经网络,在这种情况下,我们可以尝试使用3层的LSTM隐藏层,每层512个节点。在我们训练它几个小时后,我们获得了以下样本:

PANDARUS:
Alas, I think he shall be come approached and the day
When little srain would be attain'd into being never fed,
And who is but a chain and subjects of his death,
I should not sleep.

Second Senator:
They are away this miseries, produced upon my soul,
Breaking and strongly should be buried, when I perish
The earth and thoughts of many states.

DUKE VINCENTIO:
Well, your wit is in the care of side and that.

Second Lord:
They would be ruled after this chamber, and
my fair nues begun out of the fact, to be conveyed,
Whose noble souls I'll have the heart of the wars.

Clown:
Come, sir, I will make did behold your worship.

VIOLA:
I'll drink it.

请记住,上面这些RNN都知道是字符,但是它学会了对说话者的姓名和内容进行采样。有时我们也会得到相对较长的独白段落,比如:

VIOLA:
Why, Salisbury must find his flesh and thought
That which I am not aps, not a man and in fire,
To show the reining of the raven and the wars
To grace my hand reproach within, and not a fair are hand,
That Caesar and my goodly father's world;
When I was heaven of presence and our fleets,
We spare with hours, but cut thy council I am great,
Murdered and by thy master's ready there
My power to give thee but so much as hell:
Some service in the noble bondman here,
Would show him to her wine.

KING LEAR:
O, if you were a feeble sight, the courtesy of your law,
Your sight and several breath, will wear the gods
With his heads, and my hands are wonder'd at the deeds,
So drop upon your lordship's head, and your opinion
Shall be against your honour.

我几乎无法从实际的莎士比亚文章中认出这些样本:) 如果你喜欢莎士比亚,你可能会喜欢这100,000字符的样本。当然,您也可以使用提供的代码在不同 temperatures 下生成无限量的样本。

Wikipedia

我们看到LSTM可以学习拼写单词并复制一般的句法结构. 让我们进一步增加难度并用结构化markdown语法进行训练。让我们获取原始维基百科的Hutter Prize 100MB数据集并训练LSTM。遵循 Graves et al。我使用了第一个96MB数据进行培训,其余的用于验证,并在一夜之间运行了几个模型。我们现在可以提供维基百科文章样本。以下是一些有趣的摘录。首先,一些基markdown的输出:

Naturalism and decision for the majority of Arab countries' capitalide was grounded
by the Irish language by [[John Clair]], [[An Imperial Japanese Revolt]], associated
with Guangzham's sovereignty. His generals were the powerful ruler of the Portugal
in the [[Protestant Immineners]], which could be said to be directly in Cantonese
Communication, which followed a ceremony and set inspired prison, training. The
emperor travelled back to [[Antioch, Perth, October 25|21]] to note, the Kingdom
of Costa Rica, unsuccessful fashioned the [[Thrales]], [[Cynth's Dajoard]], known
in western [[Scotland]], near Italy to the conquest of India with the conflict.
Copyright was the succession of independence in the slop of Syrian influence that
was a famous German movement based on a more popular servicious, non-doctrinal
and sexual power post. Many governments recognize the military housing of the
[[Civil Liberalization and Infantry Resolution 265 National Party in Hungary]],
that is sympathetic to be to the [[Punjab Resolution]]
(PJS)[http://www.humah.yahoo.com/guardian.
cfm/7754800786d17551963s89.htm Official economics Adjoint for the Nazism, Montgomery
was swear to advance to the resources for those Socialism's rule,
was starting to signing a major tripad of aid exile.]]

如果你想知道,上面的雅虎网址实际上并不存在。该模型只是模仿了它。另外请注意,模型学会正确打开和关闭括号。模型学习也有很多结构化的 markdown 语法,例如有时会创建标题,列表等:

{ { cite journal | id=Cerling Nonforest Department|format=Newlymeslated|none } }
''www.e-complete''.

'''See also''': [[List of ethical consent processing]]

== See also ==
*[[Iender dome of the ED]]
*[[Anti-autism]]

===[[Religion|Religion]]===
*[[French Writings]]
*[[Maria]]
*[[Revelation]]
*[[Mount Agamul]]

== External links==
* [http://www.biblegateway.nih.gov/entrepre/ Website of the World Festival. The labour of India-county defeats at the Ripper of California Road.]

==External links==
* [http://www.romanology.com/ Constitution of the Netherlands and Hispanic Competition for Bilabial and Commonwealth Industry (Republican Constitution of the Extent of the Netherlands)]

有时,模型会进入生成随机但有效的XML的模式:

<page>
  <title>Antichrist</title>
  <id>865</id>
  <revision>
    <id>15900676</id>
    <timestamp>2002-08-03T18:14:12Z</timestamp>
    <contributor>
      <username>Paris</username>
      <id>23</id>
    </contributor>
    <minor />
    <comment>Automated conversion</comment>
    <text xml:space="preserve">#REDIRECT [[Christianity]]</text>
  </revision>
</page>

该模型完整的构成时间戳,id等。另请注意,它会以正确的嵌套顺序正确关闭标记。如果您有兴趣了解更多内容,这里有100,000个字符的采样维基百科

Algebraic Geometry (代数几何 Latex )

上面的结果表明该模型实际上非常擅长学习复杂的句法结构。我们对这些结果印象深刻。我的同事(Justin Johnson) 和我决定进一步推进结构化领域并掌握这本关于代数 堆栈/几何 的书。我们下载了原始Latex源文件(16MB文件)并训练了多层LSTM。人惊讶的是,由此产生的样本Latex 几乎可以编译。我们不得不介入并手动解决一些问题,但是这些看起来似乎很合理,这是非常令人惊讶的:

$$ 样本(假)代数几何。这是实际的pdf。$$

这是另一个例子:

$$ 更多模仿出来的代数几何。很好的尝试图(右) $$

正如您在上面所看到的,有时模型会尝试生成latex diagrams,但显然它并没有真正弄清楚它们。我也喜欢它选择跳过证明的部分(“Proof ignored。”,左上角)。当然,请记住latex 具有相对困难的结构化语法格式,我自己甚至都没有完全掌握。例如,这是来自模型(未编辑)的原始样本:

\begin{proof}
We may assume that $\mathcal{I}$ is an abelian sheaf on $\mathcal{C}$.
\item Given a morphism $\Delta : \mathcal{F} \to \mathcal{I}$
is an injective and let $\mathfrak q$ be an abelian sheaf on $X$.
Let $\mathcal{F}$ be a fibered complex. Let $\mathcal{F}$ be a category.
\begin{enumerate}
\item \hyperref[setain-construction-phantom]{Lemma}
\label{lemma-characterize-quasi-finite}
Let $\mathcal{F}$ be an abelian quasi-coherent sheaf on $\mathcal{C}$.
Let $\mathcal{F}$ be a coherent $\mathcal{O}_X$-module. Then
$\mathcal{F}$ is an abelian catenary over $\mathcal{C}$.
\item The following are equivalent
\begin{enumerate}
\item $\mathcal{F}$ is an $\mathcal{O}_X$-module.
\end{lemma}

这个样本来自相对不错的模型,它说明了一些常见的错误。例如,模型由\begin{proof}开始,却用\end{lemma}结束它。这个是我们需要手工调整的问题的一个例子,,可能是由于依赖性太长期这一事件导致:当模型完成证明时,它已经忘记了它是否正在进行证明或是进行一个引理。类似的情况,它\begin{enumerate}开始,但是忘了结束它。我们观察到这些在 更大/更好 的模型中变得不那么常见,但尽管如此,这些都是出现过的错误。

Linux 源码

我想将结构化数据推向极限,因此对于最后的挑战,我决定使用代码。我在Github上获取了Linux repo中的所有源文件和头文件,将它们连接在一个巨大的文件中(474MB的C代码)(我原本只会在内核上进行训练,但这本身就是只有~16MB)。然后我在几天的时间内训练了几个与我相同的GPU 3层LSTM。这些模型有大约1000万个参数,对于RNN模型来说仍然处于较低情况。结果是非常有趣的:

/*
 * Increment the size file of the new incorrect UI_FILTER group information
 * of the size generatively.
 */
static int indicate_policy(void)
{
  int error;
  if (fd == MARN_EPT) {
    /*
     * The kernel blank will coeld it to userspace.
     */
    if (ss->segment < mem_total)
      unblock_graph_and_set_blocked();
    else
      ret = 1;
    goto bail;
  }
  segaddr = in_SB(in.addr);
  selector = seg / 16;
  setup_works = true;
  for (i = 0; i < blocks; i++) {
    seq = buf[i++];
    bpf = bd->bd.next + i * search;
    if (fd) {
      current = blocked;
    }
  }
  rw->name = "Getjbbregs";
  bprm_self_clearl(&iv->version);
  regs->new = blocks[(BPF_STATS << info->historidac)] | PFMR_CLOBATHINC_SECONDS << 12;
  return segtable;
}

整个代码看起来非常棒。当然,我不认为它能通过编译,但是当你滚动生成代码时,它感觉非常像一个巨大的C代码库。请注意,RNN随机地在其中添加注释。它也很少的犯一些语法上的错误。例如,它正确使用字符串,指针等。它还可以{[正确打开和关闭括号,并学会很好地缩进其代码。一个常见的错误是它无法跟踪变量名称:它经常使用未定义的变量(例如上面的 rw ),声明它从不使用的变量(例如int error),或者返回不存在的变量。让我们看一些例子。这是另一个片段,显示了RNN学习的更广泛的操作:

/*
 * If this error is set, we will need anything right after that BSD.
 */
static void action_new_function(struct s_stat_info *wb)
{
  unsigned long flags;
  int lel_idx_bit = e->edd, *sys & ~((unsigned long) *FIRST_COMPAT);
  buf[0] = 0xFFFFFFFF & (bit << 4);
  min(inc, slist->bytes);
  printk(KERN_WARNING "Memory allocated %02x/%02x, "
    "original MLL instead\n"),
    min(min(multi_run - s->len, max) * num_data_in),
    frame_pos, sz + first_seg);
  div_u64_w(val, inb_p);
  spin_unlock(&disk->queue_lock);
  mutex_unlock(&s->sock->mutex);
  mutex_unlock(&func->mutex);
  return disassemble(info->pending_bh);
}

static void num_serial_settings(struct tty_struct *tty)
{
  if (tty == tty)
    disable_single_st_p(dev);
  pci_disable_spool(port);
  return 0;
}

static void do_command(struct seq_file *m, void *v)
{
  int column = 32 << (cmd[2] & 0x80);
  if (state)
    cmd = (int)(int_state ^ (in_8(&ch->ch_flags) & Cmd) ? 2 : 1);
  else
    seq = 1;
  for (i = 0; i < 16; i++) {
    if (k & (1 << 1))
      pipe = (in_use & UMXTHREAD_UNCCA) +
        ((count & 0x00000000fffffff8) & 0x000000f) << 8;
    if (count == 0)
      sub(pid, ppc_md.kexec_handle, 0x20000000);
    pipe_set_bytes(i, 0);
  }
  /* Free our user pages pointer to place camera if all dash */
  subsystem_info = &of_changes[PAGE_SIZE];
  rek_controls(offset, idx, &soffset);
  /* Now we want to deliberately put it to device */
  control_check_polarity(&context, val, 0);
  for (i = 0; i < COUNTER; i++)
    seq_puts(s, "policy ");
}

请注意,在第二个函数中,模型进行比较tty == tty,这是一个无意义的永真情况。另一方面,这次至少变量tty存在于范围内!在最后一个函数中,请注意代码不返回任何内容,因为函数签名是正确的void。但是,前两个函数也被声明void但有返回值。由于长期的相互作用,这又形成了一种常见的错误。
有时模型自己决定什么时候采样新文件。这通常是一个非常有趣的部分:该模型首先逐字逐句地叙述GNU许可证,对一些包含示例,生成一些宏,然后插入代码:

/*
 *  Copyright (c) 2006-2010, Intel Mobile Communications.  All rights reserved.
 *
 *   This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 *        This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software Foundation,
 *  Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kexec.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/multi.h>
#include <linux/ckevent.h>

#include <asm/io.h>
#include <asm/prom.h>
#include <asm/e820.h>
#include <asm/system_info.h>
#include <asm/setew.h>
#include <asm/pgproto.h>

#define REG_PG    vesa_slot_addr_pack
#define PFM_NOCOMP  AFSR(0, load)
#define STACK_DDR(type)     (func)

#define SWAP_ALLOCATE(nr)     (e)
#define emulate_sigs()  arch_get_unaligned_child()
#define access_rw(TST)  asm volatile("movd %%esp, %0, %3" : : "r" (0));   \
  if (__type & DO_READ)

static void stat_PC_SEC __read_mostly offsetof(struct seq_argsqueue, \
          pC>[1]);

static void
os_prefix(unsigned long sys)
{
#ifdef CONFIG_PREEMPT
  PUT_PARAM_RAID(2, sel) = get_state_state();
  set_pid_sum((unsigned long)state, current_state_str(),
           (unsigned long)-1->lr_full; low;
}

这里有1MB的Linux代码样本供您观看。

生成baby的名字

让我们再尝试一下。让我们为RNN提供一个大文本文件,其中包含列出的8000个婴儿名字,每行一个(从这里获得的名字)。我们将其提供给RNN,然后生成新名字。以下是一些示例,仅显示训练数据中未出现的名称(90%不显示):

Rudi Levette Berice Lussa Hany Mareanne Chrestina Carissy Marylen Hammine Janye Marlise Jacacrie Hendred Romand Charienna Nenotto Ette Dorane Wallen Marly Darine Salina Elvyn Ersia Maralena Minoria Ellia Charmin Antley Nerille Chelon Walmor Evena Jeryly Stachon Charisa Allisa Anatha Cathanie Geetra Alexie Jerin Cassen Herbett Cossie Velen Daurenge Robester Shermond Terisa Licia Roselen Ferine Jayn Lusine Charyanne Sales Sanny Resa Wallon Martine Merus Jelen Candica Wallin Tel Rachene Tarine Ozila Ketia Shanne Arnande Karella Roselina Alessia Chasty Deland Berther Geamar Jackein Mellisand Sagdy Nenc Lessie Rasemy Guen Gavi Milea Anneda Margoris Janin Rodelin Zeanna Elyne Janah Ferzina Susta Pey Castina

你可以在这里看到更多。我最喜欢的一些包括“Baby”,“Killie”,“Char”,“R”,“More”,“Mars”,“Hi”,“Saddie”,“With”和“Ahbort”。当然,你可以想象,在写小说或命名一个新创业公司时提供非常有用的灵感:)

Understanding what’s going on

我们看到训练结束时的结果可能令人印象深刻,但这有什么用呢?让我们进行两次快速实验,简要介绍一下。

训练时样本的演变

首先,看一下模型训练时采样文本是如何演变的,这很有趣。
例如,训练了Leo Tolstoy的战争与和平的LSTM,然后每100次训练生成样本。在迭代100的位置,模型对随机混乱进行采样:

tyntd-iafhatawiaoihrdemot  lytdws  e ,tfti, astai f ogoh eoase rrranbyne 'nhthnee e plia tklrgd t o idoe ns,smtt   h ne etie h,hregtrs nigtike,aoaenns lng

但是,至少它开始了解由空格分隔的单词。除了有时它插入两个空格。它也不知道逗号总是跟着一个空格。在300次迭代中,我们看到模型开始了解引号和句号。

"Tmont thithey" fomesscerliund
Keushey. Thom here
sheulke, anmerenith ol sivh I lalterthend Bleipile shuwy fil on aseterlome
coaniogennc Phe lism thond hon at. MeiDimorotion in ther thize."

这些单词现在也用空格分隔,模型开始在句子结尾处得到关于句号的用法 。500次迭代:

we counter. He stutn co des. His stanted out one ofler that concossions and was 
to gearang reay Jotrets and with fre colt otf paitt thin wall. Which das stimn

该模型现在学会拼写最短和最常见的单词,如“we”, “He”,“His”,“Which”,“and”等等。700次迭代之后,我们可以看到越来越多的类似英语的文本出现:

Aftair fall unsuch that the hall for Prince Velzonski's that me of
her hearly, and behs to so arwage fiving were to it beloge, pavu say falling misfort 
how, and Gogition is so overelical and ofter.

1200次之后,看到使用引号和问号/感叹号,也学到了更长的词汇使用:

"Kite vouch!" he repeated by her
door. "But I would be done and quarts, feeling, then, son is people...."

直到大约2000次之后,可以得到正确拼写的单词,引号,名称等等:

"Why do what that day," replied Natasha, and wishing to himself the fact the
princess, Princess Mary was easier, fed in had oftened him.
Pierre aking his soul came to the packs and drove up his father-in-law women.

RNN中的可视化预测,与“神经元”的激活

另一个有趣的可视化情况是查看字符的预测分布。在下面的可视化中,我们从验证集(沿蓝色/绿色行显示)提供维基百科RNN模型字符数据,并在我们可视化(红色)的每个字符下,模型为下一个序列分配的前5个字符的猜测情况。猜测某字符出现的概率由它们的颜色表示(暗红色=为非常可能,白色=不太可能).例如: 存在一些有很强关联性的字符,模型对这样的字符预测非常肯定(如:http://www 这样的序列)。输入字符序列(蓝色/绿色),基于在RNN的隐藏层中,随机选择的神经元的激发程度而着色。可以这样认为,绿色=这个神经元非常兴奋,激发程度高;蓝色=这个神经元不太兴奋,激发程度不高(对应熟悉LSTM细节的朋友来说,这样的兴奋状态是隐藏状态向量中[-1,1]之间的值,它只是经过gate和tanh函数后LSTM单元的状态)。直观上来说,这是在RNN的“大脑”中读取输入序列时,可视化某些神经元的激活状态。不同的神经元可能在寻找不同的模式。下面我们来看看我认为有趣或者可解释的4个不同的情况(许多不有趣或不能解释)。

上图中的神经元似乎对URL很敏感,但在URL外部则表现出不感兴趣。LSTM很可能是用这个神经元来确定是否在URL内部。

当RNN在“[[]]”markdown环境内,会表现得很兴奋,在外部则不是。有趣的是神经元看到“[”时,不是马上就能处于激活状态,它必须等到第二个“[”出现才会激活。这个计算模型是否看到1个“[”还是2个“[”,这个计算任务很可能是由不同的神经元完成的。

这里这个图,我们看到一个神经元在“[[]]”环境中看起来呈线性变化。换句话说,它的激活状态给RNN提供了一个可以“[[]]”范围的时间相对坐标。RNN可以使用该信息,然后根据“[[]]”范围出现的早晚,来制作不同的字符。

这里是一个有局部行为的神经元。它保持一个冷静的状态,且会在“www”序列出现第一个“w”之后就变成抑制的状态。RNN可能正在使用这个神经元计算它在“www”序列中的所在的位置,以便于确定是否应该发出另一个“w”,或者开始启动一个URL。

当然,由于RNN的隐藏状态是一个非常庞大,高纬度且分散表达的部分,因此很多这些结论都或多或少呈现出波浪状。这些可视化是使用自定义HTML / CSS / Javascript生成的,如果您想创建类似的东西,可以看这里
我们还可以通过排除预测的情况,只显示文本来缩小这种可视化,并且还是用颜色来显示单元格的激活状态。我们可以看到,除了大部分的神经元所做的事情无法解释之外,其中大概有5%的神经元学会了有趣且能解释的算法。

再说一次,这个方法的优点就是当你试图预测下一个字符,你不必在任何时候进行硬编码,比如:有助于跟踪你当前所在位置是否处于引用内或外。我们刚刚对原始数据进行了LSTM训练,并确定这是一个便于进行跟踪,有效的数量。换句话说,RNN的神经元在训练中逐步调整自己成为一个引用检查单元,这有助于它更好的执行最终任务。这是深度学习模型(更普遍的端到端训练)的能力,来源于一个最简洁且有吸引力的例子之一。

源码

我希望我已经说服你训练 character-level 语言模型是一个非常有趣的练习。你可以使用我在Github上发布的char-rnn代码训练你自己的模型。它需要一个大型文本文件进行训练,然后你可以从中生成样本。此外,GPU训练对此有所帮助,因为用其他CPU训练速度会慢10倍。如果你还不是很清楚 Torch / Lua 代码,请记住这里有一个100行的版本。

题外话。代码是用Torch 7编写的,它最近成为我最喜欢的深度学习框架。我在过去的几个月里才开始与Torch / LUA合作,这并不容易(我花了很多时间在Github上挖掘原始的Torch代码,并问了他们的很多问题以完成工作),但是一旦掌握了这些东西,它就会提供很大的灵活性和速度。我过去也曾与Caffe和Theano合作过,现在我相信Torch,虽然它并不完美,却能获得更好地获得抽象水平。在我看来,有效框架的理想特征是:

  • 具有许多功能的CPU / GPU transparent Tensor 库(切片,数组/矩阵运算等)
  • 脚本语言(理想情况下为Python)中完全独立的代码库,可在Tensors上运行并实现所有深度学习内容(前向传递/后向传递,计算图等)
  • 应该可以轻松地共享预训练模型(Caffe做得很好,其他人做不到),这很重要。
  • 没有编译步骤(或至少不像Theano目前所做的那样)。深度学习的趋势是朝着更大,更复杂的网络展开,这些网络在复杂的图形中被时间展开。重要的是这些不能长时间编译,不然开发时间将大大受损。其次,通过编译,可以放弃可解释性和有效 记录/调试 的能力。如果有一个选项来编译图表, 且它是为提高效率而开发的那这就没问题。

扩展Reading

在该帖子结束之前,我还想在更广泛的背景下定位RNN,并提供当前研究方向的草图。RNN最近在深度学习领域产生了大量的共鸣,引起了大家的兴趣。与Convolutional Networks类似,它们已存在数十年,但它们的全部潜力最近才开始得到广泛认可,这在很大程度上是由于我们不断增长的计算资源。这里是一些近期发展的简要草图(这不是完整的清单,很多这项工作从研究开始要回溯到20世纪90年代,参见相关工作部分):
在NLP和语音领域,RNN可以把语音转录成文本,实现机器翻译生成手写文本,当然,它们也被用作强大的语言模型 (Sutskever et al.) (Graves) (Mikolov et al.) (在字符和单词的水平上)。目前,单词级模型似乎比字符级模型更好,但这肯定是暂时的。
计算机视觉。RNN也在计算机视觉中迅速普及。例如,我们在帧级别的视频分类图像字幕(还包括我自己的工作和许多其他)中看到RNN ,视频字幕和最近的图像问答。我个人最喜欢的CV领域中关于RNN的文章: Recurrent Models of Visual Attention
归纳推理,Memories and Attention。另一个极其令人兴奋的研究方向是解决“vanilla recurrent networks”的局限性。第一个问题,RNN是不具有归纳性的:RNN可以非常好地记忆序列,但是并不一定总能以正确的方式表现出令人信服的迹象(我将提供一些指示,使其更具体)。第二个问题,他们不必要将他们的表示大小与每步的计算量相结合。比如,如果你将隐藏状态向量的大小加倍,那么由于矩阵乘法,每步的FLOPS数量将翻两番。理想情况下,我们希望维护一个巨大的 representation/memory (比如:包含所有维基百科数据或着许多中间状态变量),同时保持每个时间步长固定的计算能力。

DeepMind’s Neural Turing Machines 中提出了向这些方向发展的第一个令人信服的例子。这篇文章描述了一种模型的路径,这些模型可以在大型外部存储器阵列和较小的存储器寄存器(将其视为工作存储器)之间执行读/写操作,并在其中进行计算。至关重要的是,NTM论文还提供了非常有趣的内存寻址机制,这些机制是通过(soft, and fully-differentiable) attention 模型实现的。soft attention 的概念已经证明是一个强大的建模功能,并且还在 Neural Machine Translation by Jointly Learning to Align and Translate for Machine Translation 和 Memory Networks for (toy) Question Answering 进行了描述。事实上,我甚至可以这么说:

“attention ”的概念是近期神经网络中最有趣的创新。

现在,我不想深入了解太多细节,但内存寻址的“ soft attention”方案很方便,因为它使模型完全可以区分,但遗憾的是,牺牲了效率,因为所有可以照顾的事情都被关注的了。可以认为这是在C中声明一个指针,它不指向特定的地址,而是在整个内存中的所有地址上定义一个完整的分布,并且取消引用指针返回指向内容的加权和(这将是一个昂贵的操作!)。这促使多位成员将“soft attention”模型交换为“hard attention”,其中一个成员对特定的一块存储器进行采样(例如,某些存储器单元的读/写操作,而不是在某种程度上从所有单元读取/写入))。这个模型在理论上更具吸引力,可扩展性和高效性,但不幸的是它也是不可微分的。然后,这要求使用来自 Reinforcement Learning literature (比如: REINFORCE) 的技术,其中人们习惯于不可微分相互作用的概念。这是一个持续性的工作,但这些“hard attention”模型已被探索,例如, Inferring Algorithmic Patterns with Stack-Augmented Recurrent Nets, Reinforcement Learning Neural Turing Machines, 和 Show Attend and Tell
People。如果你想更多了解RNN,我推荐Alex GravesIlya SutskeverTomas Mikolov的论文。有关“REINFORCE”的更多信息,请看David Silver的课程,或Pieter Abbeel的课程。
Code。这篇帖子为Torch发布的代码。我刚才写的原始numpy代码,它实现了一个有效的,批量的LSTM前向和后向传递。你还可以查看我的基于numpy的NeuralTalk,它使用RNN / LSTM来标记图像,或者可能是Jeff Donahue的Caffe实现。

结论

我们已经了解了RNN,它们如何工作,为什么它们成为一个大问题, 我们在几个有趣的数据集上训练了一个RNN character-level语言模型,而且我们已经看到了RNN的发展方向。你可以放心地在RNN中进行大量创新,我相信它们将成为智能系统的一个基础和关键组成部分。
最后,为这篇文章添加一些有趣的东西。我在这篇博客文章的源文件上训练了一个RNN,遗憾的是,在大约46K字符处,我没有提供足够的正确的数据来输入RNN, 但返回的样本(用低 temperature 参数,生成以获得更典型的样本)是:

I've the RNN with and works, but the computed with program of the 
RNN with and the computed of the RNN with with and the code

Yes,帖子是关于RNN以及它的工作情况,所以很明显这是正确的:)
See you next time!

点击收藏 | 0 关注 | 1
登录 后跟帖