资讯专栏INFORMATION COLUMN

递归神经网络不可思议的有效性

Drinkey / 1954人阅读

摘要:递归神经网络有一些不可思议的地方,有些时候,模型与你期望的相差甚远,许多人认为是非常难训练,那么究竟是什么呢就有这篇文章来带给大家。递归神经网络有一些不可思议的地方。但是我们正在不断超越自己那么究竟是什么呢递归神经网络序列。

递归神经网络有一些不可思议的地方,有些时候,模型与你期望的相差甚远,许多人认为是RNNS非常难训练,那么RNNs究竟是什么呢?就有这篇文章来带给大家。

递归神经网络(RNNs)有一些不可思议的地方。我仍然记得我训练的第一个用于 图片字幕的递归网络。从花几十分钟训练我的第一个婴儿模型(相当随意挑选的超参数)开始,到训练出能够针对图像给出有意义描述的模型。有些时候,模型对于输出结果质量的简单程度的比例,会与你的期望相差甚远,而这还仅仅是其中一点。有如此令人震惊结果,许多人认为是因为RNNs非常难训练(事实上,通过多次试验,我得出了相反的结论)。一年前:我一直在训练RNNs,我多次见证了它们的强大的功能和鲁棒性,而且它们的输出结果同样让我感到有趣。这篇文章将会给你展现它不可思议的地方。

我们将训练一个RNNs让它一个字符一个字符地生成文本,然后我们思考“这怎么可能?”

顺便说句,在讲述这篇文章的同时,我同样会将代码上传到 Github 上,这样你就可以基于多层LSTMs来训练字符级语言模型。你向它输入大量的文本,它会学习并产生类似的文本。你也可以用它来重新运行我下面的代码。但是我们正在不断超越自己;那么RNNs究竟是什么呢?

递归神经网络

序列。你可能会问:是什么让递归神经网络如此特殊?Vanilla神经网络(卷积网络也一样)较大的局限之处就是它们API的局限性:它们将固定大小的向量作为输入(比如一张图片),然后输出一个固定大小的向量(比如不同分类的概率)。还不止这些:这些模型按照固定的计算步骤来(比如模型中层的数量)实现这样的输入输出。递归网络更令人兴奋的主要原因是,它允许我们对向量序列进行操作:输入序列、输出序列、或大部分的输入输出序列。通过几个例子可以具体理解这点:

    Automated conversion

    #REDIRECT [[Christianity]]

 

这个模型完全拼凑出了timestamp,id等等。同样,注意到它以正确的嵌套顺序适当的闭合了正确的标签。如果你有兴趣了解更多,这里有 100,000 characters of sampled wikipedia

代数几何(Latex)

以上结果表明,该模型在学习复杂句法结构方面表现得相当不错。这些结果令人印象深刻,我的实验伙伴( Justin Johnson )和我打算在结构上再深入研究,我们使用这本关于代数栈/几何的 书 。我们下载了Latex的源文件(16MB),然后训练了一个多层的LSTM。令人惊讶的是,由Latex产生的样本几乎是已经汇总好了的。我们不得不介入并手动修复了一些问题,这样你就得到了合理的数学推论,这是相当惊人的:

代数几何样本(假的), 真正的PDF文件在这 。

这是另一份样本:

 

产生了更多假的代数几何,尝试处理图像(右)

正如上面你所看到的那样,有些时候这个模型试图生成LaTeX图像,但很明显它并不明白图像的具体意思。同样我很喜欢模型选择跳过证明过程的那部分(“Proof omitted”,左上角)。当然,Latex有相对困难的结构化句法格式,我自己都还没有完全掌握。为举例说明,下面是模型中的一个原始样本(未被编辑):

egin{proof}

We may assume that $mathcal{I}$ is an abelian sheaf on $mathcal{C}$.

item Given a morphism $Delta : mathcal{F} o 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.

egin{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

egin{enumerate}

item $mathcal{F}$ is an $mathcal{O}_X$-module.

end{lemma}

这份样本来自一个相对合适的模型,其中显示出了一些常见错误。比如,模型打开了一个egin{proof}环境,但是却以end{lemma}结束。这个问题我们必须手动修复,这可能是由于依赖关系太长而导致的:当这个模型做完了 proof过程,它就忘记了它是在做proof还是lemma了。同样的,当模型打开了一个egin{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 x/x, "

    "original MLL instead "),

    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许可证,然后include几个头文件,声明一些宏,然后就开始生成代码部分:

/*

 *  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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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 of sampled Linux code

了解到底发生了什么

我们可以看出,最终训练出的结果是令人印象深刻的,但是这一切都是如何工作的?让我们通过运行两个快速的实验来简要探索下。

训练中样本的演变

首先,探索模型训练过程中样本文本是如何演变的是件有趣的事,比如,我用列夫·托尔斯泰的《战争与和平》训练了一个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预测结果和神经元激活可视化

可视化有趣的另一个方面是可以按字符来观察预测分布。在下面的可视化图中,我们向Wikipedia RNN模型中输入了校验好的数据集(蓝色/绿色的行)中的字符,然后在每个字符下面,我们列出了(红色部分)模型预测会出现的前5个字符,颜色深浅根据它们概率大小决定(深红:预测准确,白色:不准确)。比如,注意到有一连串字符是模型非常有信心能够预测准确的(对http://www.序列模型置信度非常高)。

输入字符序列(蓝色/绿色)的颜色取决于RNN隐藏层中随机选择的神经元的激活情况。定义绿色=非常兴奋,蓝色=不是那么兴奋(对于那些熟悉LSTMs细节的人来说,这些就是隐藏向量中[-1,1]范围内的值,也就是经过门限操作和tanh函数的LSTM单元状态)。直观的,下图显示了在RNN“大脑”读取输入序列时一些神经元的激活情况。不同的神经元可能有不同的模式;下面我们将会看到我找到的4个不同的神经元,我认为这几个是有趣的,并且是可解释的(许多其他的并不容易解释):

此图中高亮的神经元似乎对URL极度兴奋,URL以外的地方就不那么兴奋。LSTM似乎会用这种神经元来记住它是否在URL之中。

在这张图中,当RNN在[[]]标记之中时,高亮的神经元表现极度兴奋,所以在这种标记之外就没那么兴奋,在神经元碰到字符“[”的时候不会表现得兴奋,它一定要等到出现第二个“[”才会激活。计算模型是否碰到了一个还是两个“[”的任务似乎可以用一个不同的神经元完成。

在这里,我们可以看出在[[]]环境中,神经元有着线性的变化。换句话说,它的激活函数给了RNN中[[]]范围的一种基于时间的坐标系统。RNN可以通过这些信息或多或少的依赖于字符在[[]]中出现的早/晚来生成不同的字符(有可能是这样)。

这是另外一个神经元,它有着更个性化的行为:它整体上比较平淡无常,但是碰到“www”中第一个“w”的时候突然就开始变得兴奋。RNN可能可以使用这种神经元来计算“www”序列的长度,这样它就知道是否应该再添加一个“w”还是开始添加URL。

当然,由于RNN隐藏状态的庞大性,高维度性和分布式特性,很多这样的结论都稍微要加上特别说明才能理解。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/4331.html

相关文章

  • 神经网络基础

    摘要:要学习深度学习,那么首先要熟悉神经网络,简称的一些基本概念。网络径向基函数网络是一种单隐层前馈神经网络,它使用径向基函数作为隐层神经元激活函数,而输出层则是对隐层神经元输出的线性组合。 阅读目录1. 神经元模型2. 感知机和神经网络3. 误差逆传播算法4. 常见的神经网络模型5. 深度学习6. 参考内容目前,深度学习(Deep Learning,简称DL)在算法领域可谓是大红大紫,现在不只是...

    starsfun 评论0 收藏0
  • php面试总结

    摘要:由一层函数调用进入下一层函数调用的递推。此时,中的一个称为孤儿的类就会收留这个对象。禁止访问服务器拒绝请求服务器找不到请求的页面服务器内部错误坏的网关一般是网关服务器请求后端服务时,后端服务没有按照协议正确返回结果。 持续更新。。。。 php 1. 简述 php 中的 autoload Autoload 的加载机制,当通过 new 来实例化一个类时,PHP 会通过定义的 autol...

    skinner 评论0 收藏0
  • php面试总结

    摘要:由一层函数调用进入下一层函数调用的递推。此时,中的一个称为孤儿的类就会收留这个对象。禁止访问服务器拒绝请求服务器找不到请求的页面服务器内部错误坏的网关一般是网关服务器请求后端服务时,后端服务没有按照协议正确返回结果。 持续更新。。。。 php 1. 简述 php 中的 autoload Autoload 的加载机制,当通过 new 来实例化一个类时,PHP 会通过定义的 autol...

    greatwhole 评论0 收藏0
  • php面试总结

    摘要:由一层函数调用进入下一层函数调用的递推。此时,中的一个称为孤儿的类就会收留这个对象。禁止访问服务器拒绝请求服务器找不到请求的页面服务器内部错误坏的网关一般是网关服务器请求后端服务时,后端服务没有按照协议正确返回结果。 持续更新。。。。 php 1. 简述 php 中的 autoload Autoload 的加载机制,当通过 new 来实例化一个类时,PHP 会通过定义的 autol...

    wangbjun 评论0 收藏0
  • arXiv上五篇顶尖深度学习论文都讲了些什么?

    摘要:自从年深秋,他开始在上撰写并公开分享他感兴趣的机器学习论文。本文选取了上篇阅读注释的机器学习论文笔记。希望知名专家注释的深度学习论文能使一些很复杂的概念更易于理解。主要讲述的是奥德赛因为激怒了海神波赛多而招致灾祸。 Hugo Larochelle博士是一名谢布克大学机器学习的教授,社交媒体研究科学家、知名的神经网络研究人员以及深度学习狂热爱好者。自从2015年深秋,他开始在arXiv上撰写并...

    WilsonLiu95 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<