斯坦福-CS224n-自然语言处理笔记-2017-全-

斯坦福 CS224n 自然语言处理笔记 2017(全)

CS224n 笔记 1 自然语言处理与深度学习简介

cs224n-2017-lecture1.png

这是斯坦福 CS224n 的第一篇笔记,也是第一次系统地学习用深度学习来做自然语言处理。本文还整理了 CS224n 的全部视频课件笔记,汇总下载

虽然深度学习已经席卷各大应用领域,大量开源工具让谁都能跳过理论基础速成 demo 专家。我却一直按部就班地琢磨传统的机器学习理论,实在有些落伍了。能力有限,在《统计学习方法》、老版(硬核版)CS229上断断续续挣扎了几年时间;刚刚才学完Neural Networks for Machine Learning,总算是把知识更新到 2012 年了。虽然主要兴趣在自然语言处理这个应用层上,但 NLP 公开课只看过Michael Collins 的课,以至于截止六月份博客上 ML 分类下的笔记比 NLP 还要多(这还算上了早期写的极其幼稚漏洞百出的 NLP 笔记)。

这并非是冷淡了 NLP,而是出于“根深才能叶茂”“把好吃的留到最后吃”的朴素想法。虽然我也能跳过理论基础,自顶而下照葫芦画瓢地实现一些算法和模型,但这不过是盲人摸象而已。现在稍微有了一点积累,终于可以开始这门激动人心的课程了。

正巧在 2017 年,旧版的 CS224d 与旧版 CS224n 合并为新版 CS224n,14 课升级为 18 课,加量不加价,也算是给有心人的一种特典奖励吧。

新旧 CS224 对比

这门课在 2016 年之前叫做 CS224d: Deep Learning for Natural Language Processing,之后改名为 CS224n: Natural Language Processing with Deep Learning。从两者的命名来看,旧版“为自然语言处理准备的深度学习”似乎以深度学习为中心;新版“用深度学习进行自然语言处理”似乎更贴切一些。内容上的区别,除了新版多了 4 课之外,课程表上的“for 某某 NLP 任务”也被去掉了。估计是不希望限制大家的想象力吧。实际上新版的内容是两门课程的合并:This course is a merger of Stanford's previous cs224n course (Natural Language Processing) and cs224d (Deep Learning for Natural Language Processing).

那到底应该选择哪个版本呢?亲身体会一下不就知道了。我看完了旧版的第一课和新版的第一课,直观的印象是:旧版主讲人 Richard Socher 年轻有为,刚拿到 PhD 就已是领域内的超新星;新版主讲人是泰斗 Chris Manning 和 Richard Socher,两人分别负责不同的章节。Manning 在教学上经验更丰富,章节层次感强。虽然年纪大了,但授课激情不输给年轻人。更深刻的比较现在无法给出,毕竟我也没有完整学完两版课程。但个人还是偏好新版的课程;从另一个无关紧要的角度讲,旧版的课件是投影到幕布上再录制回去的,新版是直接穿插到视频中的;新版油管上有1080P的,而旧版只有 720P 的,当然新版好了。

CS224 资料

新版 CS224n 资源(视频课件笔记)汇总:BT 种子(推荐)百度云文件夹链接 或 百度云压缩包链接(全 18 课视频+字幕+期中复习+笔记+作业更新完毕。部分视频内嵌字幕,其他外挂 srt 字幕,总之都有字幕)。

文件夹链接经常被和谐,虽然有自动补档程序,仍然需要多次点击迅速转存,手快有手慢无全看人品了。压缩包链接目前很稳定,且行且珍惜。本着西学东渐的初衷,自由传播。

hankcs.com 2017-08-22 上午 9.34.36.pnghankcs.com 2017-08-22 上午 9.36.23.png

旧版 CS224d:cs224d.stanford.edu/

新版 CS224n 官网:web.stanford.edu/class/cs224n/index.html

新版 CS224n 官方笔记:github.com/stanfordnlp/cs224n-winter17-notes

新版 CS224n 视频地址:www.youtube.com/playlist?list=PL3FW7Lu3i5Jsnh1rnUwq_TcylNr7EkRe6

新版 CS224n 国内在线观看:space.bilibili.com/34967/#!/channel/detail?cid=18192&order=1&page=1

免责声明

按惯例重申博客的宗旨:服务于个人备忘,不做任何保证。既不保证持续更新,也不保证内容一定客观正确,更不保证回复(根本没那个水平)。我会尽量用括号标记我的个人理解,但这也不做保证。水平有限,欢迎心平气和的指正。

文中术语并不会特意翻译为中文,如果我恰好知道中译,那么打字方便会打中文。

这门课的先决条件是修完CS229,有一些基本的线性代数、微积分、概率论、Python 基础。既然是备忘的笔记,当然不会浪费笔墨在已经掌握的知识点上(偶尔为了完整性会写点,比如这次第一课)。

文中所有取自课件的多媒体版权属于斯坦福大学。笔记文字以知识共享许可协议 知识共享署名-非商业性使用-相同方式共享授权。

什么是自然语言处理

这是一门计算机科学、人工智能以及语言学的交叉学科。虽然语言只是人工智能的一部分(人工智能还包括计算机视觉等),但它是非常独特的一部分。这个星球上有许多生物拥有超过人类的视觉系统,但只有人类才拥有这么高级的语言。

自然语言处理的目标是让计算机处理或说“理解”自然语言,以完成有意义的任务,比如订机票购物或 QA 等。完全理解和表达语言是极其困难的,完美的语言理解等效于实现人工智能。

自然语言处理涉及的几个层次

hankcs.com 2017-06-06 下午 2.49.04.png

作为输入一共有两个来源,语音与文本。所以第一级是语音识别和 OCR 或分词(事实上,跳过分词虽然理所当然地不能做句法分析,但字符级也可以直接做不少应用)。接下来是形态学,援引《统计自然语言处理》中的定义:

形态学(morphology):形态学(又称“词汇形态学”或“词法”)是语言学的一个分支,研究词的内部结构,包括屈折变化和构词法两个部分。由于词具有语音特征、句法特征和语义特征,形态学处于音位学、句法学和语义学的结合部位,所以形态学是每个语言学家都要关注的一门学科[Matthews,2000]。

下面的是句法分析和语义分析,最后面的在中文中似乎翻译做“对话分析”,需要根据上文语境理解下文。

这门课主要关注画圈的三个部分,其中中间的两个是重中之重,虽然深度学习在语音识别上的发力最大。

自然语言处理应用

一个小子集,从简单到复杂有:

  • 拼写检查、关键词检索……

  • 文本挖掘(产品价格、日期、时间、地点、人名、公司名)

  • 文本分类

  • 机器翻译

  • hankcs.com 2017-06-05 下午 10.33.14.png

  • hankcs.com 2017-06-06 下午 4.54.43.png

  • 客服系统

  • 复杂对话系统

在工业界从搜索到广告投放、自动\辅助翻译、情感舆情分析、语音识别、聊天机器人\管家等等五花八门。

人类语言的特殊之处

与信号处理、数据挖掘不同,自然语言的随机性小而目的性强;语言是用来传输有意义的信息的,这种传输连小孩子都能很快学会。人类语言是离散的、明确的符号系统。但又允许出现各种变种,比如颜文字,随意的错误拼写“I loooove it”。这种自由性可能是因为语言的可靠性(赘余性)。所以说语言文字绝对不是形式逻辑或传统 AI 的产物。

语言符号有多种形式(声音、手势、书写),在这些不同的形式中,其意义保持不变:

hankcs.com 2017-06-06 下午 3.37.09.png

虽然人类语言是明确的符号系统,但符号传输到大脑的过程是通过连续的声学光学信号,大脑编码似乎是连续的激活值上的模式。另外巨大的词表也导致数据稀疏,不利于机器学习。这构成一种动机,是不是应该用连续的信号而不是离散的符号去处理语言。

hankcs.com 2017-06-06 下午 3.43.53.png

什么是深度学习

这是机器学习的一个子集。传统机器学习中,人类需要对专业问题理解非常透彻,才能手工设计特征。比如地名和机构名识别的特征模板:

hankcs.com 2017-06-06 下午 3.53.16.png

然后把特征交给某个机器学习算法,比如线性分类器。机器为这些特征调整找到合适的权值,将误差优化到最小。

hankcs.com 2017-06-06 下午 3.54.21.png

在这个过程中一直在学习的其实是人类,而不是机器。机器仅仅做了一道数值优化的题目而已。

下面这张图很好地展示了这个过程中的比例:

hankcs.com 2017-06-06 下午 3.55.31.png

而深度学习是表示学习的一部分,用来学习原始输入的多层特征表示:

hankcs.com 2017-06-06 下午 3.58.56.png

“深度学习”的历史

虽然这个术语大部分时候指代利用各种各样多层的神经网络进行表示学习,有时候也有一些概率图模型参与。统计学家会说,哦,不过是一些逻辑斯谛回归单元的堆砌而已。也许的确如此,但这还是以偏概全的说法(电子计算机还是一堆半导体的堆砌呢,大脑还是一堆神经元的堆砌呢)。这门课不会回顾历史(像 Hinton 老爷子那样博古通今),而只会专注当前在 NLP 领域大放异彩的方法。

为什么需要研究深度学习

  • 手工特征耗时耗力,还不易拓展

  • 自动特征学习快,方便拓展

  • 深度学习提供了一种通用的学习框架,可用来表示世界、视觉和语言学信息

  • 深度学习既可以无监督学习,也可以监督学习

深度学习可追溯到八九十年代,但在 2010 年左右才崛起(最先是语音与图像,后来才是 NLP),那之前为什么没有呢?

与 Hinton 介绍的一样,无非是以前数据量不够,计算力太弱。当然,最近也的确有许多新模型,新算法。

语音识别中的深度学习

hankcs.com 2017-06-06 下午 4.16.48.png

突破性研究来自 Hinton 老爷子的学生,具体参考:www.hankcs.com/ml/hinton-deep-neural-nets-with-generative-pre-training.html#h3-11

计算机视觉中的深度学习

hankcs.com 2017-06-06 下午 4.19.11.png

还是来自 Hinton 的学生。

课程相关

有 4 次编程练习,会用到 TensorFlow。

为什么 NLP 难

人类语言是充满歧义的,不像编程语言那样明确。编程语言中有各种变量名,但人类语言中只有少数几个代词可以用,你得思考到底指代的是谁……

人类语言的解读依赖于现实世界、常识以及上下文。由于说话速度书写速度阅读速度的限制,人类语言非常简练,省略了大量背景知识。

接下来是几个英文的歧义例子,对 native speaker 而言很有趣。为了完整性只看一个:

The Pope’s baby steps on gays

主要歧义发生在 baby 上面,可以理解为“教皇的孩子踩了基佬”,也可以理解为“教皇在同性恋问题上裹足不前”。

旧版 CS224d 里面还有个更直观的例子,推特上关于电影明星“海瑟薇”的评论影响了保险公司哈撒韦的股价,因为两者拼写是一样的。

hankcs.com 2017-06-06 下午 4.56.02.png

说明某些“舆情系统”没做好命名实体识别。

Deep NLP = Deep Learning + NLP

将自然语言处理的思想与表示学习结合起来,用深度学习的手法解决 NLP 目标。这提高了许多方面的效果:

  • 层次:语音、词汇、语法、语义

  • 工具:词性标注、命名实体识别、句法\语义分析

  • 应用:机器翻译、情感分析、客服系统、问答系统

深度学习的一个魅力之处是,它提供了一套“宇宙通用”的框架解决了各种问题。虽然工具就那么几个,但在各行各业都适用。

word vector

老生常谈了。略过。听说接下来两课都在讲这个,希望有些更深入的收获。

NLP 表示层次:形态级别

传统方法在形态级别的表示是词素:

hankcs.com 2017-06-06 下午 6.18.14.png

深度学习中把词素也作为向量:

hankcs.com 2017-06-06 下午 6.18.44.png

多个词素向量构成相同纬度语义更丰富的词向量。

NLP 工具:句法分析

hankcs.com 2017-06-06 下午 6.24.14.png

我在《基于神经网络的高性能依存句法分析器》中分析并移植的 LTP 句法分析器,参考的就是这里介绍的 Danqi Chen 的A Fast and Accurate Dependency Parser using Neural Networks.pdf。原来她是这门课的 TA。

NLP 语义层面的表示

传统方法是手写大量的规则函数,叫做 Lambda calculus:

hankcs.com 2017-06-06 下午 6.35.01.png

在深度学习中,每个句子、短语和逻辑表述都是向量。神经网络负责它们的合并。

hankcs.com 2017-06-06 下午 6.36.38.png

情感分析

传统方法是请一两百个工人,手工搜集“情感极性词典”在词袋模型上做分类器。

深度学习复用了 RNN 来解决这个问题,它可以识别“反话”的情感极性:

hankcs.com 2017-06-06 下午 6.40.10.png

注意这只是为了方便理解的示意图,并不是 RNN 的工作流程。私以为这张图放在这里不合适,可能会误导一部分人,以为神经网络就是这样的基于规则的“决策树”模型。

QA

传统方法是手工编写大量的逻辑规则,比如正则表达式之类:

hankcs.com 2017-06-06 下午 6.43.21.png

我见过这类 QA 系统的实体,挺没意思的。

深度学习依然使用了类似的学习框架,把事实储存在向量里:

2017-06-06_18-46-19.png

客服系统

最著名的例子得数 GMail 的自动回复:

smartreply1.gif

图源

这是 Neural Language Models 的又一次成功应用,Neural Language Models 是基于 RNN 的:

hankcs.com 2017-06-06 下午 6.56.03.png

机器翻译

传统方法在许多层级上做了尝试,词语、语法、语义之类。这类方法试图找到一种世界通用的“国际语”(Interlingua)来作为原文和译文的桥梁。

hankcs.com 2017-06-07 上午 9.45.44.png

而 Neural Machine Translation 将原文映射为向量,由向量构建译文。也许可以说 Neural Machine Translation 的“国际语”是向量。

hankcs.com 2017-06-06 下午 7.02.16.png

结论:所有层级的表示都是向量

hankcs.com 2017-06-06 下午 7.11.40.png

这可能是因为向量是最灵活的形式,它的维度是自由的,它可以组合成矩阵,或者更高阶的 Tensor。事实上,在实践的时候向量和矩阵没什么本质区别,经常看到为了效率或单纯的美观而 pack 成矩阵 unroll 成向量的操作。

旧版视频中 Socher 还顺便广告了一下他的创业公司 MetaMind(已被收购,人生赢家):hankcs.com 2017-06-06 下午 7.36.47.png

这个 demo 让我非常惊讶,因为普通 NLP 演示页面都是让人手工选择要执行的任务的。而这个 demo 竟然支持用一句话表示自己要执行的意图。不光可以执行情感分析、句法分析之类的常规任务,还可以输入一段话做推理任务。更让我惊讶的是,据说后台所有任务用的都是同一种模型,真乃神机也。据说这种模型是Dynamic Memory Network。另外,他们又发了篇A Joint Many-Task Model:Growing a Neural Network for Multiple NLP Tasks,不知道两者有什么联系没有。

下面两次课会详细地讲解向量表示,希望能带来新的体会。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 1 自然语言处理与深度学习简介

CS224n 笔记 2 词的向量表示:word2vec

如何表示一个词语的意思

先来看看如何定义“意思”的意思,英文中 meaning 代表人或文字想要表达的 idea。这是个递归的定义,估计查询 idea 词典会用 meaning 去解释它。2017-06-07_15-24-56.png

中文中“意思”的意思更加有意思:

他说:“她这个人真有意思(funny)。”她说:“他这个人怪有意思的(funny)。”于是人们以为他们有了意思(wish),并让他向她意思意思(express)。他火了:“我根本没有那个意思(thought)!”她也生气了:“你们这么说是什么意思(intention)?”事后有人说:“真有意思(funny)。”也有人说:“真没意思(nonsense)”。(原文见《生活报》1994.11.13.第六版)[吴尉天,1999]

——《统计自然语言处理》

语言学中“meaning”近似于“指代、所指、符号”。

计算机如何处理词语的意思

过去几个世纪里一直用的是分类词典。计算语言学中常见的方式是 WordNet 那样的词库。比如 NLTK 中可以通过 WordNet 查询熊猫的 hypernyms (is-a,上位词),得到“食肉动物”“动物”之类的上位词。也可以查询“good”的同义词——“just 品格好”“ripe 熟了”。

discrete representation 的问题

  • 这种 discrete representation 虽然是种语言学资源,但丢失了韵味。比如这些同义词的意思实际上还是有微妙的差别:adept, expert, good, practiced, proficient, skillful

  • 缺少新词

  • 主观化

  • 需要耗费大量人力去整理

  • 无法计算准确的词语相似度

无论是规则学派,还是统计学派,绝大多数 NLP 学家都将词语作为最小单位。事实上,词语只是词表长度的 one-hot 向量,这是一种 localist representation(大概是借用 localist“局部”的意项)。

在不同的语料中,词表大小不同。Google 的 1TB 语料词汇量是 1300 万,这个向量的确太长了。

从 symbolic representations 到 distributed representations

词语在符号表示上体现不出意义的相似性,比如 Dell notebook battery size 和 Dell laptop battery capacity。而 one-hot 向量是正交的,无法通过任何运算得到相似度。

hankcs.com 2017-06-07 上午 10.58.24.png

需要找到一种用向量直接编码含义的方法。

Distributional similarity based representations

语言学家 J. R. Firth 提出,通过一个单词的上下文可以得到它的意思。J. R. Firth 甚至建议,如果你能把单词放到正确的上下文中去,才说明你掌握了它的意义。

这是现代统计自然语言处理最成功的思想之一:

hankcs.com 2017-06-07 上午 11.04.07.png

通过向量定义词语的含义

通过调整一个单词及其上下文单词的向量,使得根据两个向量可以推测两个词语的相似度;或根据向量可以预测词语的上下文。这种手法也是递归的,根据向量来调整向量,与词典中意项的定义相似。

另外,distributed representations 与 symbolic representations(localist representation、one-hot representation)相对;discrete representation 则与后者及 denotation 的意思相似。切不可搞混 distributed 和 discrete 这两个单词。

学习神经网络 word embeddings 的基本思路

定义一个以预测某个单词的上下文的模型:

损失函数定义如下:

这里的表示的上下文(负号通常表示除了某某之外),如果完美预测,损失函数为零。

然后在一个大型语料库中的不同位置得到训练实例,调整词向量,最小化损失函数。

直接学习低维词向量

这其实并不是多么新潮的主意,很早就有一些研究了:

• Learning representations by back-propagating errors (Rumelhart et al., 1986)

A neural probabilistic language model (Bengio et al., 2003)

• NLP (almost) from Scratch (Collobert & Weston, 2008)

• A recent, even simpler and faster model: word2vec (Mikolov et al. 2013)

只不过以前一直没有引起重视,直到 Bengio 展示了它的用处之大。后来研究才开始火热起来,并逐渐出现了更快更工业化的模型。

word2vec 的主要思路

通过单词和上下文彼此预测,老生常谈了。

两个算法:

  • Skip-grams (SG):预测上下文

  • Continuous Bag of Words (CBOW):预测目标单词

两种稍微高效一些的训练方法:

  • Hierarchical softmax

  • Negative sampling

但在这门课里,只会讲 Naïve softmax。好在我早就把两种算法和两种训练方法的四种组合过了一遍:www.hankcs.com/nlp/word2vec.html

Skip-gram 预测

hankcs.com 2017-06-07 下午 2.47.31.png

注意这里虽然有四条线,但模型中只有一个条件分布(因为这只是个词袋模型而已,与位置无关)。学习就是要最大化这些概率。

word2vec 细节

目标函数定义为所有位置的预测结果的乘积:

hankcs.com 2017-06-07 下午 2.55.51.png

要最大化目标函数。对其取个负对数,得到损失函数——对数似然的相反数:

hankcs.com 2017-06-07 下午 2.57.28.png

目标函数细节

这些术语都是一样的:Loss function = cost function = objective function,不用担心用错了。对于 softmax 来讲,常用的损失函数为交叉熵。

Word2Vec 细节

预测到的某个上下文条件概率可由 softmax 得到:

hankcs.com 2017-06-07 下午 3.07.22.png

o 是输出的上下文词语中的确切某一个,c 是中间的词语。u 是对应的上下文词向量,v 是词向量。

点积

复习一下课程开头所说的 baby math:

hankcs.com 2017-06-07 下午 3.09.43.png

公式这种画风这种配色真的有点幼儿园的感觉。

点积也有点像衡量两个向量相似度的方法,两个向量越相似,其点积越大。

Softmax function:从实数空间到概率分布的标准映射方法

hankcs.com 2017-06-07 下午 3.13.57.png

指数函数可以把实数映射成正数,然后归一化得到概率。

softmax 之所叫 softmax,是因为指数函数会导致较大的数变得更大,小数变得微不足道;这种选择作用类似于 max 函数。

Skipgram

2017-06-07_15-24-56.png

别看这张图有点乱,但其实条理很清晰,基本一图流地说明了问题。从左到右是 one-hot 向量,乘以 center word 的 W 于是找到词向量,乘以另一个 context word 的矩阵 W'得到对每个词语的“相似度”,对相似度取 softmax 得到概率,与答案对比计算损失。真清晰。

官方笔记里有非手写版,一样的意思:

Skip-Gram.png

这两个矩阵都含有 V 个词向量,也就是说同一个词有两个词向量,哪个作为最终的、提供给其他应用使用的 embeddings 呢?有两种策略,要么加起来,要么拼接起来。在 CS224n 的编程练习中,采取的是拼接起来的策略:

# concatenate the input and output word vectors
wordVectors = np.concatenate(
    (wordVectors[:nWords,:], wordVectors[nWords:,:]),
    axis=0)
# wordVectors = wordVectors[:nWords,:] + wordVectors[nWords:,:]

他们管 W 中的向量叫 input vector,W'中的向量叫 output vector。

训练模型:计算参数向量的梯度

把所有参数写进向量,对 d 维的词向量和大小 V 的词表来讲,有:

2017-06-07_15-34-33.png

由于上述两个矩阵的原因,所以的维度中有个

模型的学习当然是梯度法了,Manning 还耐心地推导了十几分钟:

hankcs.com 2017-06-07 下午 5.18.54.png

hankcs.com 2017-06-07 下午 5.19.15.png

hankcs.com 2017-06-07 下午 5.20.24.png

hankcs.com 2017-06-07 下午 5.20.42.png

更清晰的公式参考:www.hankcs.com/nlp/word2vec.html#h3-5

损失/目标函数

梯度有了,参数减去梯度就能朝着最小值走了。

hankcs.com 2017-06-07 下午 8.34.41.png

梯度下降、SGD

hankcs.com 2017-06-07 下午 8.42.22.png

hankcs.com 2017-06-07 下午 8.40.33.png

只有一句比较新鲜,神经网络喜欢嘈杂的算法,这可能是 SGD 成功的另一原因。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 2 词的向量表示:word2vec

CS224n 笔记 3 高级词向量表示

hankcs.com 2017-06-08 下午 9.24.05.png这节课从传统的基于计数的全局方法出发,过渡到结合两者优势的 GloVe,并介绍了词向量的调参与评测方法。

复习:word2vec 的主要思路

  • 遍历整个语料库中的每个词

  • 预测每个词的上下文:

  • hankcs.com 2017-06-08 下午 3.25.31.png

  • 然后在每个窗口中计算梯度做 SGD

SGD 与词向量

但每个窗口最多只有 2m + 1 个单词,所以会非常稀疏:

hankcs.com 2017-06-08 下午 3.33.16.png

实际上有正确答案需要去对比的只有窗口中的词语。所以每次更新只更新矩阵中的少数列,或者为每个词语建立到词向量的哈希映射:

hankcs.com 2017-06-08 下午 3.39.45.png

近似:负采样

还有一个问题亟待解决,词表 V 的量级非常大,以至于下式的分母很难计算:

hankcs.com 2017-06-08 下午 3.25.31.png

于是,练习 1 中要求用 negative sampling 实现 skip-gram。这是一种采样子集简化运算的方法。具体做法是,对每个正例(中央词语及上下文中的一个词语)采样几个负例(中央词语和其他随机词语),训练 binary logistic regression(也就是二分类器)。

negative sampling 和 skip-gram

目标函数:

这里是某个窗口,是采样个数,是一个 unigram 分布,详见:www.hankcs.com/nlp/word2vec.html#h3-12

是 sigmoid 函数:

sigmoid.png

根据上次课讲的,内积可以代表相关性。那么这个目标函数就是要最大化中央词与上下文的相关概率,最小化与其他词语的概率。

word2vec 通过把相似的词语放到同一个地方附近来增大目标函数:

hankcs.com 2017-06-08 下午 4.45.25.png

其他方法

word2vec 将窗口视作训练单位,每个窗口或者几个窗口都要进行一次参数更新。要知道,很多词串出现的频次是很高的。能不能遍历一遍语料,迅速得到结果呢?

早在 word2vec 之前,就已经出现了很多得到词向量的方法,这些方法是基于统计共现矩阵的方法。如果在窗口级别上统计词性和语义共现,可以得到相似的词。如果在文档级别上统计,则会得到相似的文档(潜在语义分析 LSA)。

基于窗口的共现矩阵

比如窗口半径为 1,在如下句子上统计共现矩阵:

  • I like deep learning.

  • I like NLP.

  • I enjoy flying.

会得到:

hankcs.com 2017-06-08 下午 5.00.43.png

朴素共现向量的问题

根据这个矩阵,的确可以得到简单的共现向量。但是它存在非常多的局限性:

  • 当出现新词的时候,以前的旧向量连维度都得改变

  • 高纬度(词表大小)

  • 高稀疏性

解决办法:低维向量

用 25 到 1000 的低维稠密向量来储存重要信息。如何降维呢?

SVD 吧:

hankcs.com 2017-06-08 下午 5.10.35.png

r 维降到 d 维,取奇异值最大的两列作为二维坐标可视化:

hankcs.com 2017-06-08 下午 5.11.36.png

改进

  • 限制高频词的频次,或者干脆停用词

  • 根据与中央词的距离衰减词频权重

  • 用皮尔逊相关系数代替词频

效果

方法虽然简单,但效果也还不错:

hankcs.com 2017-06-08 下午 5.17.13.png

hankcs.com 2017-06-08 下午 5.17.27.png

hankcs.com 2017-06-08 下午 5.17.45.png

SVD 的问题

  • 计算复杂度高:对的矩阵是

  • 不方便处理新词或新文档

  • 与其他 DL 模型训练套路不同

Count based vs direct prediction

这些基于计数的方法在中小规模语料训练很快,有效地利用了统计信息。但用途受限于捕捉词语相似度,也无法拓展到大规模语料。

而 NNLM, HLBL, RNN, Skip-gram/CBOW 这类进行预测的模型必须遍历所有的窗口训练,也无法有效利用单词的全局统计信息。但它们显著地提高了上级 NLP 任务,其捕捉的不仅限于词语相似度。

综合两者优势:GloVe

这种模型的目标函数是:

这里的是两个词共现的频次,f 是一个 max 函数:

hankcs.com 2017-06-08 下午 7.22.15.png

优点是训练快,可以拓展到大规模语料,也适用于小规模语料和小向量。

明眼人会发现这里面有两个向量,它们都捕捉了共现信息,怎么处理呢?试验证明,最佳方案是简单地加起来:

相对于 word2vec 只关注窗口内的共现,GloVe 这个命名也说明这是全局的(我觉得 word2vec 在全部语料上取窗口,也不是那么地 local,特别是负采样)。

评测方法

有两种方法:Intrinsic(内部) vs extrinsic(外部)

Intrinsic:专门设计单独的试验,由人工标注词语或句子相似度,与模型结果对比。好处是是计算速度快,但不知道对实际应用有无帮助。有人花了几年时间提高了在某个数据集上的分数,当将其词向量用于真实任务时并没有多少提高效果,想想真悲哀。

Extrinsic:通过对外部实际应用的效果提升来体现。耗时较长,不能排除是否是新的词向量与旧系统的某种契合度产生。需要至少两个 subsystems 同时证明。这类评测中,往往会用 pre-train 的向量在外部任务的语料上 retrain。

Intrinsic word vector evaluation

也就是词向量类推,或说“A 对于 B 来讲就相当于 C 对于哪个词?”。这可以通过余弦夹角得到:

hankcs.com 2017-06-08 下午 9.00.00.png

我曾经通过这些方法验证了民间很多流行的 word2vec 实现准确率比原版低几十个百分点:www.hankcs.com/nlp/word2vec.html#h2-15

这种方法可视化出来,会发现这些类推的向量都是近似平行的:

hankcs.com 2017-06-08 下午 9.03.18.png

hankcs.com 2017-06-08 下午 9.03.35.png

下面这张图说明 word2vec 还可以做语法上的类比:

hankcs.com 2017-06-08 下午 9.05.55.png

其他有趣的类比:

hankcs.com 2017-06-08 下午 9.06.35.png

这在数学上是没有证明的。

结果对比

在不同大小的语料上,训练不同维度的词向量,在语义和语法数据集上的结果如下:

hankcs.com 2017-06-08 下午 9.13.47.png

GloVe 的效果显著地更好。另外,高纬度并不一定好。而数据量越多越好。

调参

窗口是否对称(还是只考虑前面的单词),向量维度,窗口大小:

hankcs.com 2017-06-08 下午 9.20.11.png

大约 300 维,窗口大小 8 的对称窗口效果挺好的,考虑到成本。

对 GloVe 来讲,迭代次数越多越小,效果很稳定:

hankcs.com 2017-06-08 下午 9.24.05.png

维基百科语料上得到的效果比新闻语料要好:

hankcs.com 2017-06-08 下午 9.26.26.png

另一个数据集

还有直接人工标注词语相似度的:

hankcs.com 2017-06-08 下午 9.29.01.png

www.cs.technion.ac.il/~gabr/resources/data/wordsim353/

对某个单词相似度排序后,得到最相关的词语。于是可以量化评测了。

hankcs.com 2017-06-08 下午 9.31.22.png

Extrinsic word vector evaluation

做 NER 实验:

hankcs.com 2017-06-08 下午 9.38.12.png

GloVe 效果依然更好,但从数据上来看只好那么一点点。

视频中还谈了谈一些适合 word vector 的任务,比如单词分类。有些不太适合的任务,比如情感分析。课件中则多了一张谈消歧的,中心思想是通过对上下文的聚类分门别类地重新训练。

hankcs.com 2017-06-08 下午 9.47.56.png

相同颜色的是同一个单词的不同义项。

TA 也讲过这个问题。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 3 高级词向量表示

CS224n 笔记 4 Word Window 分类与神经网络

这节课介绍了根据上下文预测单词分类的问题,与常见神经网络课程套路不同,以间隔最大化为目标函数,推导了对权值矩阵和词向量的梯度;初步展示了与传统机器学习方法不一样的风格。

分类问题

给定训练集

其中  是一个 -维向量, 是一个 -维 one-hot 向量,是总数。在传统的机器学习方法中,往往通过诸如逻辑斯谛回归和 SVM 找到分类决策边界:

LinearBoundary.png

softmax 详细

softmax 分类函数:

计算方法分为两个步骤:取权值矩阵的某一行乘上输入向量,归一化得到概率。

softmax 与交叉熵误差

训练的时候,可以直接最小化正确类别的概率的负对数:

其实这个损失函数等效于交叉熵:

这是因为类别是 one-hot 向量。

对 N 个数据点来讲有:

加上正则化项有:

hankcs.com 2017-06-09 下午 4.23.18.png

红线是 test error,蓝线是 training error,横轴是模型复杂度或迭代次数。直线是方差偏差均衡点。

优化

一般的 ML 问题中,参数由权值矩阵的列组成维度不会太大。而在词向量或其他深度学习中,需要同时学习权值矩阵和词向量。参数一多,就容易过拟合:

hankcs.com 2017-06-09 下午 4.29.16.png

re-training 词向量失去泛化效果

比如有一个给单词做情感分析的小任务,在预训练的词向量中,这三个表示电视的单词都是在一起的:

pretraining.png

但由于情感分析语料中,训练集只含有 TV 和 telly,导致 re-training 之后两者跑到别处去了:

retraining.png

于是在测试集上导致 television 被误分类。

这个例子说明,如果任务的语料非常小,则不必在任务语料上重新训练词向量,否则会导致词向量过拟合。

顺便介绍一下词向量相关的术语:

词向量矩阵 L 常被称作 lookup table:

hankcs.com 2017-06-09 下午 4.44.02.png

Word vectors = word embeddings = word representations (mostly)

可能有些人认为“词向量”是个很俗的翻译,但根据这门课的课件,其实与“词嵌入”是等价的。而且 Rocher 也几乎是一直在用 word vector,而不是其他术语。

Window classification

这是一种根据上下文给单个单词分类的任务,可以用于消歧或命名实体分类。上下文 Window 的向量可以通过拼接所有窗口中的词向量得到:

hankcs.com 2017-06-09 下午 4.50.57.png

这是一个

hankcs.com 2017-06-09 下午 4.52.02.png

的列向量。

最简单的分类器:softmax

hankcs.com 2017-06-09 下午 4.56.04.png

求导,注意这里的指的是窗口所有单词的词向量拼接向量。

于是就可以更新词向量了:

另一方面,对求偏导数,将所有参数的偏导数写到一起有:

在更新过程中计算代价最高的有矩阵运算和指数函数,但矩阵运算比循环要快多了(在 CPU 上快一个数量级)。

softmax(等价于逻辑斯谛回归)效果有限

仅限于较小的数据集,能够提供一个勉强的线性分类决策边界。

hankcs.com 2017-06-09 下午 9.27.31.png

使用神经网络

神经网络可以提供非线性的决策边界:

NonlinearBoundary.png

从逻辑斯谛回归到神经网络

老生常谈了,一些术语:

hankcs.com 2017-06-09 下午 9.31.25.png

每个神经元是一个二分类逻辑斯谛回归单元:

hankcs.com 2017-06-09 下午 9.33.00.png

神经网络同时运行多个逻辑斯谛回归,但不需要提前指定它们具体预测什么:

hankcs.com 2017-06-09 下午 9.33.54.png

我们把预测结果喂给下一级逻辑斯谛回归单元,由损失函数自动决定它们预测什么:

hankcs.com 2017-06-09 下午 9.34.54.png

于是就得到了一个多层网络:

hankcs.com 2017-06-09 下午 9.35.51.png

为什么需要非线性

因为线性系统所有层等效于一层:

而非线性模型可以捕捉很复杂的数据:

hankcs.com 2017-06-09 下午 9.38.43.png

前向传播网络

一个简单的网络:

hankcs.com 2017-06-09 下午 9.40.38.png

这种红点图经常在论文里看到,大致代表单元数;中间的空格分隔开一组神经元,比如隐藏层单元数为是隐藏层到 class 的权值矩阵:

hankcs.com 2017-06-09 下午 11.34.31.png

其中是激活函数:

或:

间隔最大化目标函数

怎么设计目标函数呢,记代表误分类样本的得分,表示正确分类样本的得分。则朴素的思路是最大化 或最小化 。但有种方法只计算时的错误,也就是说我们只要求正确分类的得分高于错误分类的得分即可,并不要求错误分类的得分多么多么小。这得到间隔最大化目标函数:

但上述目标函数要求太低,风险太大了,没有留出足够的“缓冲区域”。可以指定该间隔的宽度 ,得到:

可以调整其他参数使得该间隔为 1:

这实际上是将函数间隔转换为几何间隔,参考 SVM:www.hankcs.com/ml/support-vector-machine.html#h3-3

在这个分类问题中,这两个得分的计算方式为:;通常通过负采样算法得到负例。

另外,这个目标函数的好处是,随着训练的进行,可以忽略越来越多的实例,而只专注于那些难分类的实例。

反向传播训练

依然是老生常谈,跳过链式法则推导直接贴结果:

其中是第层的误差:

可见只要控制误差的计算方式,就可以 smoothly 地过渡到间隔最大化目标函数:

hankcs.com 2017-06-09 下午 10.38.49.png

另外,对偏置的偏导数是

hankcs.com 2017-06-09 下午 10.21.13.png

最后一片拼图是对词向量的偏导数,由于连接时每个输入单元连到了多个隐藏单元,所以对某个输入单元的偏导数是求和的形式(残差来自相连的隐藏单元):

hankcs.com 2017-06-09 下午 10.24.20.png

其中,是第列,转置后得到行向量;红色部分是误差,相乘后得到一个标量,代表词向量第维的导数。那么对整个词向量的偏导数就是:

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 4 Word Window 分类与神经网络

CS224n 笔记 5 反向传播与项目指导

hankcs.com 2017-06-10 下午 8.26.24.pnghankcs.com 2017-06-10 下午 8.52.07.png最后一次数学课,其实都是些很基础的推导而已。从四个不同的层面讲解反向传播,其中电路和有向图类比还是很直观新颖的解释。

任意层的通用公式

层的残差:

其中是激活函数:

对于顶层来讲,残差就是根据某个损失函数得到的误差。对于底层来讲,激活函数不存在,或相当于

是线性函数:

是相同大小的向量之间的 element wise product()

正则化的损失函数关于第层的权值矩阵的梯度:

关于第层的偏置的梯度:

其中,是激活值:

这里偏置单元与普通神经元在数学上并无不同,只不过由于激活值,所以可以把激活值省略掉。

反向传播的电路解释

比如函数可视作如下加法器和乘法器电路:

hankcs.com 2017-06-10 下午 4.35.30.png

定义,于是有。求

我们可以从输出到输入反向计算,先得到输出关于输出自己的导数:

hankcs.com 2017-06-10 下午 4.41.14.png

然后得到关于的导数:

hankcs.com 2017-06-10 下午 7.39.38.png

另一条路,关于的导数:

hankcs.com 2017-06-10 下午 7.41.50.png

关于的导数:

hankcs.com 2017-06-10 下午 7.43.21.png

关于的导数:

hankcs.com 2017-06-10 下午 7.43.46.png

这种反向回溯的过程放到神经元中就是反向传播了:

hankcs.com 2017-06-10 下午 8.26.24.png

反向传播时每通过一级,就用链式法则乘以这一级的导数。

另一个稍微复杂一点的例子:

hankcs.com 2017-06-10 下午 8.33.36.png

其中,sigmoid 相关的元件可以合并为一个 sigmoid gate:

hankcs.com 2017-06-10 下午 8.35.43.png

第三种理解:流程图

将上述电路视作有向无环流程图去理解链式法则,比如一条路径:

hankcs.com 2017-06-10 下午 8.41.57.png

2 条路径:

hankcs.com 2017-06-10 下午 8.43.00.png

推广到多条路径:

hankcs.com 2017-06-10 下午 8.43.39.png

推广到更复杂的流程图:

hankcs.com 2017-06-10 下午 8.44.47.png

只要找到 z 的所有父节点应用链式法则并求和即可。

神经网络可以视作流程图的一个实例:

hankcs.com 2017-06-10 下午 8.47.22.png

任意流程图都可以执行反向传播:

hankcs.com 2017-06-10 下午 8.48.22.png

现在有一些软件包(TensorFlow)可以自动从前向传播的 symbolic expression(符号表达式)推导梯度,适用于快速设计原型。(其实 matlab 里也可以)

hankcs.com 2017-06-10 下午 8.52.07.png

第四种解释:实际神经网络中的误差信号

其实就是把上面这些解释综合起来的解释,对如下 2 层的网络来讲:

hankcs.com 2017-06-10 下午 8.53.58.png

假设最后一层对的误差是

hankcs.com 2017-06-10 下午 8.58.47.png

于是对的导数是

通过线性乘法器,对的导数是权值与的乘积:

hankcs.com 2017-06-10 下午 9.01.31.png

通过一个 sigmoid gate,对的导数是:

hankcs.com 2017-06-10 下午 9.05.13.png

再通过一个线性乘法器,得到对的导数:

hankcs.com 2017-06-10 下午 9.07.09.png

于是对的导数是

课程项目

接下来都是围绕着课程项目的指导与建议,就不啰嗦了。简单写写一些体会:

  • 不要想着一上来就发明个新模型搞个大新闻

  • 也不要浪费大部分时间在爬虫上面,本末倒置

  • 把旧模型用于新领域\新数据也是不错的项目

  • 先要按部就班地熟悉数据、熟悉评测标准、实现基线方法

  • 再根据基线方法的不足之处思考深度学习如何能带来改进

  • 再实现一个已有的较为前沿的模型

  • 观察该模型犯的错误,思考如何改进

  • 这时才能没准就福至心灵发明一个新方法

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 5 反向传播与项目指导

CS224n 笔记 6 句法分析

hankcs.com 2017-06-16 下午 9.34.30.png句法分析还算熟悉,就跟着复习了神经网络句法分析的动机与手法,了解一下比较前沿的动向。

语言学的两种观点

如何描述语法,有两种主流观点,其中一种是短语结构文法,英文术语是:Constituency = phrase structure grammar = context-free grammars (CFGs)。

这种短语语法用固定数量的 rule 分解句子为短语和单词、分解短语为更短的短语或单词……一个取自 WSJ 语料库的短语结构树示例:

hankcs.com 2016-12-30 下午 1.06.21.png

另一种是依存结构,用单词之间的依存关系来表达语法。如果一个单词修饰另一个单词,则称该单词依赖于另一个单词。一个由HanLP输出的依存句法树如下:

神经网络依存句法分析 51.png

歧义

通过句法树可以表达歧义,一个确定的句法树对应句子的一个确定解读,比如对介词短语依附(attachment of prepositional phrases (PPs)):

hankcs.com 2017-06-12 下午 5.08.57.png

from space 这个介词短语到底依附谁?不同的答案导致对句子不同的理解。

依附歧义

很难确定如何把一个短语(介词短语、状语短语、分词短语、不定式)依附到其他成分上去,比如下列句子:

hankcs.com 2017-06-12 下午 5.18.05.png

每个括号中都是一个短语,它们依附的对象各不相同。对于个短语来讲,组成的树形结构有。这是 Catalan 数,指数级增长,常用于树形结构的计数问题。

标注数据集的崛起:Universal Dependencies treebanks

虽然上下文无关文法中的语法集很容易写,无非是有限数量的规则而已,但人工费时费力标注的树库却茁壮成长了起来。在 1993 年首次面世的 Universal Dependencies treebanks 如今在 Google 的赞助下发布了 2.0,其授权大多是署名-相同方式共享,覆盖了全世界绝大多数语言(不包括简体中文)。

其官网是:universaldependencies.org/

GitHub 主页是:github.com/UniversalDependencies

树库示例:

hankcs.com 2017-06-12 下午 5.49.15.png

人们偏好树库多于规则的原因是显而易见的,树库虽然标注难度高,但每一份劳动都可被复用(可以用于词性标注命名实体识别等等任务);而每个人编写的规则都不同,并且死板又丑陋。树库的多用性还是得其作为评测的标杆数据,得到了越来越多的引用。

依存文法与依存结构

这节课以及练习用的都是依存句法树,而不是短语结构树。这并不是随机选择,而是由于前者的优势。90 年代的句法分析论文 99%都是短语结构树,但后来人们发现依存句法树标注简单,parser 准确率高,所以后来(特别是最近十年)基本上就是依存句法树的天下了(至少 80%)。

不标注依存弧 label 的依存句法树就是短语结构树的一种:

hankcs.com 2017-06-12 下午 6.10.12.png

一旦标上了,两者就彻底不同了:

hankcs.com 2017-06-12 下午 6.10.59.png

这里箭头的尾部是 head(被修饰的主题),箭头指向的是 dependent(修饰语)。

起源

语法依存的概念可以追溯到公元前 4 世纪印度语言学家 Panini 对语义、句法和形态依存的分类研究,但一般认为现代依存语法理论的创立者是法国语言学家 Lucien Tesnière(1893—1954)。L.Tesnière 的思想主要反映在他 1959 年出版的《结构句法基础》(Eléments de syntaxe structurale)一书中[Tesnière,1959]。

——《统计自然语言处理》

hankcs.com 2017-06-12 下午 6.23.28.png

课件中还有更详细的历史,不太感兴趣,跳过;只记录一点,第一个 computational 句法分析器也是依存句法分析器。

一些细节

人们画依存句法树的弧的方式不同,这门课是 head 指向 dependent(即箭头指向的词语是依赖者,箭头尾部的词语是被依赖者),我的偏好是反过来。

每个句子都有一个虚根,代表句子之外的开始,这样句子中的每个单词都有自己的依存对象了。

句法分析可用的特征

  • 双词汇亲和(Bilexical affinities),比如 discussion 与 issues。

  • 词语间距,因为一般相邻的词语才具有依存关系

  • 中间词语,如果中间词语是动词或标点,则两边的词语不太可能有依存

  • 词语配价,一个词语最多有几个依赖者。

依存句法分析

有几个约束条件:

  • ROOT 只能被一个词依赖。

  • 无环。

英语中大部分句子是 projective 的,少数是 non-projective 的:

hankcs.com 2017-06-15 下午 8.59.51.png

有个学生问是否可以将一个依存句法树还原成句子,答案是否定的。

文献中的依存句法分析方法有:

Dynamic programming

估计是找出以某 head 结尾的字串对应的最可能的句法树。

Graph algorithms

最小生成树。

Constraint Satisfaction

估计是在某个图上逐步删除不符合要求的边,直到成为一棵树。

“Transition-based parsing” or “deterministic dependency parsing”

主流方法,基于贪心决策动作拼装句法树。

Arc-standard transition

动作体系的 formal 描述如下:

hankcs.com 2017-06-16 下午 9.01.26.png

图解还是我自己整理的更清晰:www.hankcs.com/nlp/parsing/neural-network-based-dependency-parser.html/2#h2-6

课件上画得没有这么详细。

MaltParser

无搜索,贪婪地下转移决策,线性复杂度,只损失了一点效果。加个 beam search 会上升一点。

传统特征表示

2017-06-16_21-08-13.png

无非是栈和队列中单词、词性、依存标签的组合的特征函数,一个超长的稀疏 01 向量。

效果评估

评测指标是 UAS(不考虑标签只考虑弧)或 LAS(同时考虑标签和弧):

hankcs.com 2017-06-16 下午 9.11.38.png

课件上有一页讲依存弧可以用来判断蛋白质的相互作用:

hankcs.com 2017-06-16 下午 9.14.13.png

投射性

Manning 也跳过了这一张 PPT,大意就是 CFG 转换得到的依存树一定是投射性的,但依存理论允许非投射性的依存句法树(一些语义需要通过非投射性表达)。

hankcs.com 2017-06-16 下午 9.17.56.png

arc-standard 算法只能拼装投射性的句法树,但换个体系、加上后处理、采用 graph-based 方法就能得到非投射的句法树。

为什么需要神经网络句法分析器

传统特征表示稀疏、不完全、计算代价大(SVM 之类的线性分类器本身是很快的,而传统 parser 的 95%时间都花在拼装查询特征上了)。

神经网络依存句法分析器

从理论到代码的分析参考:www.hankcs.com/nlp/parsing/neural-network-based-dependency-parser.html ,在线演示:hanlp.hankcs.com/ 。

无非是传统方法拼接单词、词性、依存标签,新方法拼接它们的向量表示:

hankcs.com 2017-06-16 下午 9.32.16.png

然后模型是一个司空见惯的三层神经网络:

hankcs.com 2017-06-16 下午 9.34.30.png

事实上,在“深度学习”“神经网络”的喧嚣中冷静下来回顾一下,相较于传统的 graph-based 方法,花了这么多功夫得到的只是 0.1%的 LAS 提升:

hankcs.com 2017-06-16 下午 9.26.01.png

他们说速度上升很大啊,我的实际体验是取决于矩阵运算模块的加速比。如果用一些类似于 Eigen 之类高度优化的 C++并行矩阵运算库就很快,如果是手写的串行线性代数库就很慢。

为何需要非线性

以前的课上讲过,又讲了一遍,不啰嗦了。

有很多 S 形函数可选:

hankcs.com 2017-06-16 下午 9.41.54.png

tanh 是 sigmod 的缩放偏移版:

还有其他各种备选:

hankcs.com 2017-06-16 下午 9.43.32.png

其中 ReLU 成为新贵,Manning 说他简直不敢相信这种疯狂的函数竟然效果最好(也许神经网络本来就是疯狂的)。ReLU 在反向传播时要么不反馈残差,要么原样反馈残差。

未来工作

Chen&Manning 的工作被许多人继续往前推进,走在最前沿的是 Google。趋势是:

  • 更大更深调参调得更好(更昂贵)的神经网络

  • Beam Search

  • 在决策序列全局进行类似 CRF 推断的方法(CRF 宝刀未老,老当益壮啊)

Google 的 SyntaxNet 中的 Parsey McParseFace 的效果:

hankcs.com 2017-06-16 下午 9.49.18.png

财大气粗就是好。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 6 句法分析

CS224n 笔记 7 TensorFlow 入门

这节课由 TA 们介绍了 TF 里的几个基本概念(graph、variable、placeholder、session、fetch、feed),基本流程。然后现场敲代码演示如何在 TF 上跑线性回归和训练词向量。与优达学城的《深度学习公开课》不同之处在于,后者是一个浮躁的快餐培训课程,看完了只知道大象是柱状的。

TensorFlow.png

深度学习框架简介

为什么要用成熟的框架,而不是从头写一个:

  • 这些框架提供了大规模机器学习算法的成熟实现

  • 方便地计算梯度

  • 标准化机器学习应用,方便共享交流

  • 多种算法、理念、抽象、编程语言等的融合

  • 提供 GPU 并行运算的接口

TF 是什么

不是口红。

tom-ford-lip-color-2015-swatch-review.png

  • TensorFlow 是一个图计算的开源类库

  • 最初由 Google Brain 团队开发,用来进行机器学习研究

  • “TensorFlow 是一个描述机器学习算法和实现机器学习算法的接口”

图计算编程模型

中心思想是将数值运算以图的形式描述。

  • 图的节点是某种运算,支持任意数量的输入和输出

  • 图的边是 tensor(张量,n 维数组),在节点之间流动

比如 ReLU 激活函数:

计算图如下:

hankcs.com 2017-06-21 上午 10.15.56.png

其中是变量,变量通常是模型的参数。这些数据也可看做运算节点,只不过是无输出产生输出的节点。变量可以储存下来,作为模型的一部分发布。

hankcs.com 2017-06-21 上午 10.19.36.png

只是一个 placeholder(占位符),只在执行的时候填充输入,编程的时候指定大小即可。

hankcs.com 2017-06-21 上午 10.30.48.png

剩下的 3 个节点是运算单元:

hankcs.com 2017-06-21 上午 10.33.10.png

要描述这样的图运算,只需编写代码:

hankcs.com 2017-06-21 上午 10.34.20.png

这段代码只是构建了运算图,连输入都没有,自然无法马上获取的值。

图在哪里

上述代码并没有显式地声明节点和边,TensorFlow 根据数学表达式自动构造了运算图。

如何运行

到目前为止,我们只定义了一张图,如何执行它呢?我们可以通过 session 将这张图部署到某个执行环境(CPU、GPU、Google 的 TensorProcessingUnit……)上去。session 就是到某个软硬件执行环境的绑定。

在代码中只需新增三行:

hankcs.com 2017-06-21 上午 10.52.59.png

其中第一个 run 初始化了所有变量。第二个 run 中有两个概念:

sess.run(fetches, feeds)	

fetches 是一系列图节点(其实就是某个节点,因为图是连通的,给我一个节点总可以找到所有节点)或说变量。feeds 是一个从 placeholder 到其输入值的映射。

训练

前面的部分只是定义了一个图,知道如何执行它,接下来介绍如何训练模型。

定义损失

用变量定义损失,用 placeholder 定义 label:

prediction = tf.nn.softmax(...)  #Output of neural network
label = tf.placeholder(tf.float32, [100, 10])

cross_entropy = -tf.reduce_sum(label * tf.log(prediction), axis=1)

如何计算梯度

先初始化一个 optimizer,然后在图中损失函数节点后面添加一个优化操作(最小化,也是一种运算节点):

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

就像(大部分)函数都有自己的导数一样,TF 中的运算节点都附带了梯度操作。在反向传播中,TF 在计算图的逆向图上利用链式法则自动计算梯度。用户不需要编写梯度计算与参数更新的代码,而是交给 optimizer 自动完成。

在代码中是这样调用的:

hankcs.com 2017-06-21 上午 11.23.08.png

变量共享

有时候我们想要生成一张图的多个实例,或者在多机多个 GPU 上训练同一个模型,就会带来同一个变量在不同位置出现。如何在不同位置共享同一个变量呢?

一种朴素的想法是弄个字典建立变量名到 variable 的映射:

variables_dict = {
    "weights": tf.Variable(tf.random_normal([782, 100]),
                           name="weights"
                           )
)
"biases": tf.Variable(tf.zeros([100]), name="biases"
}

但这样容易导致变量名冲突,TF 支持命名空间:

with tf.variable_scope("foo"):
    v = tf.get_variable("v", shape=[
        1])  # v.name == "foo/v:0"
with tf.variable_scope("foo", reuse=True):
    v1 = tf.get_variable("v"
                         # Shared variable found!
                         ) )
with tf.variable_scope("foo", reuse=False):
    v1 = tf.get_variable("v"
# CRASH foo/v:0 already exists!

总结

TF 的用法总结如下:

  1. 创建图
    a 前向传播/预测
    b 优化操作

  2. 初始化 session

  3. 在 session 中执行

现场演示

印度小哥拿着 MBP 开始现场敲代码了,用的是 Vim,经常少敲了括号,最后跑 word2vec 死活不知道哪里敲错了,干脆把写好的 word2vec_complete.py 调出来了事。这说明:

  • 至少在学习 TF 上,MBP 还是有很多人在用的

  • 现场敲代码压力还是很大的

  • 一个有纠错提示的 IDE 还是很重要的

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 7 TensorFlow 入门

CS224n 笔记 8 RNN 和语言模型

deepbirnn.png这次课推导 RNN,介绍各种训练技巧和拓展变种。梯度消失的推导很详细,用 Python 演示很直观,也给出了用裁剪防止梯度爆炸的直观解释。笔记里还补充了用于机器翻译时的 5 项改进。cliping.pngen_decoder.png

语言模型

语言模型就是计算一个单词序列(句子)的概率()的模型。听上去很简单,做起来很难;听上去没什么用处,但用处非常多。比如在机器翻译中,判断译文序列中一种词序的自然程度高于另一种,判断一种用词选择优于另一种。

传统语言模型

为了简化问题,必须引入马尔科夫假设,句子的概率通常是通过待预测单词之前长度为的窗口建立条件概率来预测:

为了估计此条件概率,常用极大似然估计,比如对于 BiGram 和TriGram模型,有:

课件中写的 unigrams and bigrams (conditioning on one/two previous word(s)),是错误的。

在数据量足够的情况下,n-gram 中的 n 越大,模型效果越好。但实际上,数据量总是不如人意,这时候一些平滑方法就不可或缺。另外,这些 ngram 可能会占用上 G 的内存,在最新的研究中,一个 1260 亿的语料在 140G 内存的单机上花了 2.8 天才得到结果。

Bengio et al 提出了第一个大规模深度学习自然语言处理模型,只不过是用前个单词的词向量来做同样的事情(上文建模)而已,其网络结构如下:

bengio_03.png

公式如下:

这里就是前个单词词向量的线性运算,虽然这种模型名字里有“Neural”,但依然属于传统模型。

Recurrent Neural Networks

新的语言模型是利用 RNN 对序列建模,复用不同时刻的线性非线性单元及权值,理论上之前所有的单词都会影响到预测单词。

rnn.png

所需内存只与词表大小成正比,不取决于序列长度。

给定一个词向量序列:  ,在每个时间点上都有隐藏层的特征表示:

其中,

:是时间时输入的单词的词向量。

:用来 condition 输入词向量的的权值矩阵。

:用来 condition 前一个时间节点隐藏层特征表示的权值矩阵。

: 前一个时间点 的非线性激活函数的输出, 是时间点 时的隐藏层初始状态。

: 非线性激活函数(sigmoid)

:在时刻时输出的整个词表  上的概率分布,是给定上文和最近的单词 预测的下一个单词。其中

另外,这里的 与拼接两个向量乘以权值矩阵的拼接是一样的。

未 unroll 的网络:

rnn_node.png

等效于

rnn_loop.png

损失函数

分类问题中常见的交叉熵损失函数:

在大小为的整个语料上的交叉熵误差为:

如果以为底数会得到“perplexity 困惑度”,代表模型下结论时的困惑程度,越小越好:

训练 RNN 很难

观察句子 1:

"Jane walked into the room. John walked in too. Jane said hi to ___"

以及句子 2:

"Jane walked into the room. John walked in too. It was late in the day, and everyone was walking home after a long day at work. Jane said hi to ___"

人类可以轻松地在两个空中填入“John”这个答案,但 RNN 却很难做对第二个。这是因为在前向传播的时候,前面的反复乘上,导致对后面的影响很小。

反向传播时也是如此。

整个序列的预测误差是之前每个时刻的误差之和:

而每个时刻的误差又是之前每个时刻的误差之和,应用链式法则:

此处的指的是在时间区域上应用链式法则:

由于,每个  就是雅克比矩阵(导数矩阵):

将这几个式子写在一起,得到:

这个式子最中间的部分最值得关注,因为它是一个连乘的形式,长度为时间区域的长度。记分别为矩阵和向量的范数(L2),则上述雅克比矩阵的范数满足:

由于使用了 sigmoid 激活函数,所以的矩阵范数最大只能为 1,所以链式法则利用上式次后得到一个更松弛的上界:

指数项显著地大于或小于 1 的时候,经过足够多的次乘法之后就会趋近于 0 或无穷大。小于 1 更常见,会导致很长时间之前的词语无法影响对当前词语的预测。

而大于 1 时,浮点数运算会产生溢出(NaN),一般可以很快发现。这叫做梯度爆炸。小于 1,或者下溢出并不产生异常,难以发现,但会显著降低模型对较远单词的记忆效果,这叫做梯度消失。

矩阵范数可参考:1.4 向量和矩阵的范数.pdf

梯度消失实例

有个IPython Notebook专门演示梯度消失,对于如下数据:

1.png

学习非线性的决策边界:

boundry.png

用经典的三层网络结构,得到蓝色的第一层梯度的长度和绿色的第二层梯度的长度,可视化:

sigmoid 激活函数下:

2.png

ReLU 激活函数下:

3.png

在这个例子的反向传播中,相邻两层梯度是近乎减半地减小。

防止梯度爆炸

一种暴力的方法是,当梯度的长度大于某个阈值的时候,将其缩放到某个阈值。虽然在数学上非常丑陋,但实践效果挺好。

其直观解释是,在一个只有一个隐藏节点的网络中,损失函数和权值 w 偏置 b 构成 error surface,其中有一堵墙:

cliping.png

每次迭代梯度本来是正常的,一次一小步,但遇到这堵墙之后突然梯度爆炸到非常大,可能指向一个莫名其妙的地方(实线长箭头)。但缩放之后,能够把这种误导控制在可接受的范围内(虚线短箭头)。

但这种 trick 无法推广到梯度消失,因为你不想设置一个最低值硬性规定之前的单词都相同重要地影响当前单词。

减缓梯度消失

与其随机初始化参数矩阵,不如初始化为单位矩阵。这样初始效果就是上下文向量和词向量的平均。然后用 ReLU 激活函数。这样可以在 step 多了之后,依然使得模型可训练。

hankcs.com 2017-06-22 下午 8.04.04.png

困惑度结果

hankcs.com 2017-06-22 下午 8.07.09.png

相较于 NGram,RNN 的困惑度要小一些。

问题:softmax 太大了

词表太大的话,softmax 很费力。一个技巧是,先预测词语的分类(比如按词频分),然后在分类中预测词语。分类越多,困惑度越小,但速度越慢。所以存在一个平衡点:

hankcs.com 2017-06-22 下午 8.12.05.png

最后的实现技巧

记录每个的误差不要丢,反向传播的时候将其累加起来。

序列模型的应用

可以把每个词分类到 NER、实体级别的情感分析(饭菜味道不错,但环境不太卫生)、意见表达。

其中,意见挖掘任务就是将每个词语归类为:

DSE:直接主观描述(明确表达观点等)

ESE:间接主观描述(间接地表达情感等)

语料标注采用经典的 BIO 标注:

hankcs.com 2017-06-22 下午 8.23.51.png

实现这个任务的朴素网络结构就是一个裸的 RNN:

hankcs.com 2017-06-22 下午 8.29.08.png

但是这个网络无法利用当前词语的下文辅助分类决策,解决方法是使用一些更复杂的 RNN 变种。

Bidirectional RNNs

birnn.png

这里箭头表示从左到右或从右到左前向传播,对于每个时刻的预测,都需要来自双向的特征向量,拼接后进行分类。箭头虽然不同,但参数还是同一套参数(有些地方是两套参数[1] G. Lample, M. Ballesteros, S. Subramanian, K. Kawakami, and C. Dyer, “Neural Architectures for Named Entity Recognition.,” HLT-NAACL, 2016.)。

Deep Bidirectional RNNs

理解了上图之后,再加几个层,每个时刻不但接受上个时刻的特征向量,还接受来自下层的特征表示:

deepbirnn.png

评测

评测方法是标准的 F1(因为标签样本不均衡),在不同规模的语料上试验不同层数的影响:

hankcs.com 2017-06-22 下午 8.51.29.png

可见层数不是越多越好。

应用:RNN 机器翻译模型

传统机器翻译模型在不同的阶段用到大量不同的机器学习算法,这里讨论用 RNN 统一整个流水线。

比如将 3 个单词的德语翻译为 2 个单词的英语,用到如下 RNN:

rnn_translate.png

其中:

用来处理输入的叫 encoder,生成输出的叫 decoder。训练时使用交叉熵损失函数,最大化对数似然:

但预测准确率不理想,有如下拓展方法:

1、encoder 和 decoder 使用不同的权值矩阵。也就是上述两个不再相同。

2、decoder 中的隐藏层的输入来自 3 个方面:

  • 前一个时刻的隐藏层

  • encoder 的最后一个隐藏层()

  • 前一个预测结果 

    这样导致 decoder 函数变为:

  • en_decoder.png

3、使用深度 RNN

4、使用 bi-directional encoder

5、不再用作为训练实例,而是逆转原文词序:。因为 A 更可能翻译为 X,而梯度消失导致 A 无法影响输出,倒过来 A 离输出近一些。

回顾

  • RNN 是最好的 DeepNLP 模型之一

  • 因为梯度消失和梯度爆炸,训练很难

  • 可以用很多技巧来训练

  • 下次课将介绍更强大的 RNN 拓展:LSTM 和 GRU

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 8 RNN 和语言模型

CS224n 笔记 9 机器翻译和高级 LSTM 及 GRU

简单回顾了传统统计机器翻译中的难题,过渡到利用 GRU 和 LSTM 来救场,最后介绍了一些较新的改进工作。

机器翻译

对于情感分析这类还算简单的任务,你可以整理一个情感极性词典、编写一堆规则做出一个勉强能用的系统。但到了机器翻译这个高级应用,就无法完全依靠规则了。现代机器翻译手段都是基于统计的,在平行语料上学习语言知识。世界上第一个平行语料库是罗塞塔石碑:

Rosetta_Stone.JPG

图片来源:wiki

传统机器翻译系统非常复杂,因为不同阶段用到了不同的机器学习方法。hankcs.com 2017-06-23 上午 11.04.04.pnghankcs.com 2017-06-23 上午 11.15.38.pnghankcs.com 2017-06-23 下午 4.03.58.pngGRU.pngLSTM.pngLSTM3-chain.pnghankcs.com 2017-06-23 下午 5.41.49.pnghankcs.com 2017-06-23 下午 5.50.03.png

传统统计机器翻译系统

定义一些符号:

原文

译文

机器翻译定义为找到使如下条件概率最大的

这里利用了贝叶斯公式。翻译模型在平行语料上训练得到,语言模型在未对齐的原文语料上训练(是非常廉价的)。

公式描述的翻译过程如下:

hankcs.com 2017-06-23 上午 10.18.59.png

第一步:对齐

找到原文中的哪个句子或短语翻译到译文中的哪个句子或短语,这个第一步已经够难了

对齐时,原文中可能有部分词语没有对应的译文:

hankcs.com 2017-06-23 上午 10.23.00.png

也可能在译文中有部分词语没有对应的原文,根据模型不同,可能有一对多的对齐方式:

hankcs.com 2017-06-23 上午 10.24.36.png

也可能有多对一的对齐方式:

2017-06-23_10-28-01.png

还可能有多对多的对齐方式:

hankcs.com 2017-06-23 上午 10.28.49.png

所有的对齐方式数量是组合数级的。

有时候还要通过句法分析,来进行不同颗粒度的对齐:

hankcs.com 2017-06-23 上午 10.31.09.png

对齐之后

原文中每个单词都有多个备选单词,导致了许多短语的组合方式:

hankcs.com 2017-06-23 上午 10.34.13.png

解码:在海量假设中搜索最佳选择

这是一个特别复杂的搜索问题,涉及到许多语言模型。

hankcs.com 2017-06-23 上午 10.36.40.png

传统机器翻译

这还只是传统机器翻译系统的冰山一角,有许多细节没有涉及到,还需要大量的人肉特征工程,总之是非常复杂的系统。其中每个环节都是独立不同的机器学习问题。这些独立的模型各自为政,并不以一个统一的优化目标为最终目标。

而深度学习则提供了一个统一的模型,一个统一的最终目标函数。在优化目标函数的过程中,得到一个 end to end 的完整的 joint 模型。传统机器翻译系统与深度学习是截然相反的,对齐模型、词序模型、语言模型……一堆独立的模型无法联合训练。

深度学习来救场

也许可以直接用 RNN 来接受原文,预测译文“下一个单词”:

hankcs.com 2017-06-23 上午 10.50.00.png

红圈所示特征表示必须能捕捉整个原文短语的语义,但是 RNN 无法记住太久之前的事情,大概五六个单词就到极限了。所以这不是个实用的模型。

在这个最简单的模型中,

Encoder 是:

Decoder 是:

最小化所有训练实例上的交叉熵误差:

记号  一般代表接受的两个向量的权值不同。

softmax 分类器中必须有个代表句子终止的“单词”,不然模型会无休止地输出下去。

但神经网络机器翻译模型没有这么简单,必须加一些拓展。

1、编码器和解码器训练不同的权值矩阵

hankcs.com 2017-06-23 上午 11.04.04.png

红蓝代表不同的权值。

2、decoder 中的隐藏层的输入来自 3 个方面:

  • 前一个时刻的隐藏层

  • encoder 的最后一个隐藏层()

  • 前一个预测结果 

    这样导致 decoder 函数变为:

en_decoder.png

这可以辅助训练 softmax 的权值矩阵,防止模型重复生成同一个单词。

上图还有一个复杂版本,表达的是同一个意思:

hankcs.com 2017-06-23 上午 11.15.38.png

其中,带黑点的表示离散的向量表示,否则表示连续的向量空间。

3、使用深度 RNN

deepbirnn.png

4、使用 bi-directional encoder

5、不再用作为训练实例,而是逆转原文词序:。因为 A 更可能翻译为 X,而梯度消失导致 A 无法影响输出,倒过来 A 离输出近一些。逆转词序不会带来“语法语义上的改变”,因为模型学习的就是如何从逆序的原文翻译顺序的译文。但相应的,C 就离 Y 更远了

主要改进:更好的单元

引入 Gated Recurrent Units (GRU),这种单元可以让模型学习何时遗忘从而将记忆保持很久、允许误差根据输入的不同而不同。

标准的 RNN 直接计算隐藏层:

GRU 先根据当前输入的词向量和隐藏层计算一个 update gate(另一个层):

利用相同的方法不同的权值计算 reset gate

然后利用 reset gate 计算新的记忆

这里的意思是,之前的记忆由 reset gate 控制,如果 reset gate 元素都是 0,则遗忘之前的事情。比如电影评论的情感分析,“有个文艺的少男爱死了一个平凡的少女,这个平凡的少女也爱死了那个文艺的少男,可两个人就是无法相会巴拉巴拉,真是个无聊的电影”。无论前面说了多少话,起决定性作用的可能就是“无聊”这个词。那么一些 GRU 可能会说,我遇到了一个情感强烈的词语,我不想让它被之前的记忆冲淡(求和),所以我把 reset gate 设为 0,之前的记忆不起作用,把这个情感词汇写成新的记忆。

而 update gate 的作用是调节最后的更新,到底时刻的记忆多少来自之前,多少来自当前:

如果是单位向量的话,则隐藏层只是复制前一个时刻的隐藏层,梯度不发生变化(衰减)。

这些公式写在一起已经非常直观了:

有些人觉得示意图更直观:

hankcs.com 2017-06-23 下午 4.03.58.png

上图中,虚线代表某个 gate 的调节作用。隐藏层取决于上一个时刻的隐藏层和新的记忆,而 update gate 负责调节它们的比例,reset gate 和输入共同决定新的记忆……

有人问这个 reset gate 是不是多余的,是不是可以直接让。做是的确可以这么做,但永远无法把之前的记忆“挤出去”,因为是个求和的形式。

下面又是一张故弄玄虚的图:

hankcs.com 2017-06-23 下午 4.21.03.png

由于 gate 的调控作用是连续的而不是 0 或 1,所以上图中的开关并不合理。

对于短距离的依存来讲,reset gate 经常是激活的。

LSTM(Long-Short-Term-Memories)

LSTM 与 GRU 动机相似,只不过单元结构有点不同。GRU 的单元结构如下:

GRU.png

LSTM 单元结构如下:

LSTM.png

上图用公式描述如下:

LSTM 的计算可以分为如下步骤

  1. New memory generation 与 GRU 的 New memory generation 类似。使用当前词语 和之前的隐状态来生成新的记忆。于是新记忆里面就包含了当前词语的属性。

  2. Input Gate 使用当前词语和之前的隐状态决定当前词语是否值得保留来 gate 新记忆,这个“是否”是通过来体现的

  3. Forget Gate 与 input gate 类似,只不过它不是用来衡量输入单词的有用与否,而是衡量过去的记忆对计算当前记忆有用与否。它接受输入单词和上一刻的隐状态产生输出 

  4. Final memory generation 根据 forget gate 的建议来遗忘过去的记忆。类似地,根据 input gate 的建议来 gate 信的记忆 ,然后把两者加起来得到最终记忆 

  5. Output/Exposure Gate 这是 GRU 中不显式存在的门,用处是将最终记忆与隐状态分离开来。记忆  中的信息不是全部都需要存放到隐状态中,隐状态是个很重要的使用很频繁的东西,LSTM 中每个 gate 都需要隐状态的参与。Output Gate 产生 ,用来 gate 记忆的 tanh 激活值。

一些可视化

一张最清晰的图示:

LSTM3-chain.png

图源

一些把简单问题复杂化的图示:

img017.jpg

图源

lstm_memorycell.png

图源

都没有公式一目了然

LSTM 很潮

是序列标注、seq2seq 任务的首选模型,可以层叠起来形成更深的模型。在数据量特别大的时候特别有用。

比如与传统 MT 模型的比较:

hankcs.com 2017-06-23 下午 5.23.45.png

那时候的 NN 模型还是仅限于重新排序传统 MT 模型产生的结果,而最新的研究就是完全甩开了 MT 模型:

hankcs.com 2017-06-23 下午 5.25.42.png

前三用的都是 NN。

深度 LSTM 用于机器翻译

输入原文序列,将最后一个时刻的隐藏层向量 PCA 降维后可视化:

hankcs.com 2017-06-23 下午 5.32.19.png

发现无论词序如何,意义相同的句子在向量空间中更接近。

进一步改进:更多门!

hankcs.com 2017-06-23 下午 5.41.49.png

我感觉跟小孩玩乐高似的,拿着现有的单元拼拼凑凑成一个系统,跑出更高的分数就发 paper 了。至于为什么要这么拼这么改,不像理论完备的概率图模型,很少有人说得清楚吧。

RNN 的最新改进

softmax 的问题:无法出新词

对分类问题来讲,你无法指望分类模型给你分出一个训练集中不存在的类。即便是训练集中存在的类,如果样本数很少,模型也很难预测出该类。

对于预测下一个单词的语言模型来讲,也是如此。比如某某女士巴拉巴拉,然后自我介绍说我叫某某。如果某某不存在于训练集中,则模型无法预测出某某。

虽然可以用字符级的模型,但代价实在太大。

用指针来解决问题

如果把某某替换为“向前数第 10 个单词”这样的指针,问题就迎刃而解了。

hankcs.com 2017-06-23 下午 5.50.03.png

具体做法是,以前 100 个时刻的隐藏层作为输入,用一个 softmax 去计算前 100 个单词是 pointer 的概率,与原来的词表上的分布混合。

hankcs.com 2017-06-23 下午 7.35.16.png

使用了 pointer 之后,困惑度下降了零点几个百分点:

hankcs.com 2017-06-23 下午 7.37.10.png

总结

  • RNN 很强大

  • 有很多进行中的工作

  • GRU 更强大

  • LSTM 又更强大

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 9 机器翻译和高级 LSTM 及 GRU

CS224n 笔记 10 NMT 与 Attention

hankcs.com 2017-06-27 下午 3.45.05.png从 NMT 的历史谈到现代,讲解了 attention 机制的动机、原理及最新的拓展。通过实例展示谷歌翻译的变化,直言不讳指出其过度炒作。Manning 今天还换上了新 MBP,挺潮的。

机器翻译

  • 传统衡量机器对语言理解的测试之一

  • 同时涉及到语言分析与理解

  • 一个每年 400 亿美金的产业

  • 主要在欧洲,亚洲也在兴起

机器翻译的需求

  • Google 每天翻译 1000 亿单词

  • Facebook 研发了自己的翻译系统,因为通用的机器翻译系统无法适应社交领域

  • eBay 用机器翻译来促进跨境交易

什么是 NMT

用一个大型神经网络建模整个翻译过程的系统。

架构

抽象的架构就是一个 encoder 一个 decoder:

hankcs.com 2017-06-27 下午 3.17.59.png

NMT:青铜时代

80 年代神经网络是个很边缘的领域,另外计算力也很有限。当时的 NMT 系统只是个玩具:词表四五十,固定的 50 个输入(二进制编码),固定的 66 个输出,一到三层隐藏层,150 个单元……

hankcs.com 2017-06-27 下午 3.33.17.png

90 年代出现了一种类似 RNN 的更复杂的框架:

hankcs.com 2017-06-27 下午 3.35.58.png

现代 NMT 模型

之前课上也提到过,一个 RNN 做 encoder,另一个 RNN 做 decoder:

2017-06-27_15-39-23.png

实际使用的系统更加复杂:

hankcs.com 2017-06-27 下午 3.45.05.png

这里的 RNN 可视作以原文为条件的 conditional 语言模型

2017-06-27_15-47-58.png

RNN Encoder

hankcs.com 2017-06-27 下午 3.51.08.png

最后一个隐藏层的状态 Y 是整个原文的总结。

常见的 encoder 实际上加了一些 extension,比如 GRU 或 LSTM。

Decoder:递归语言模型

常见的做法是把 encoder 的最后一层(最后一个时刻)作为 decoder 的第一层,这样就必须用 LSTM 保持中期记忆。

另一种做法是将 encoder 最后一层喂给 decoder 的每一层,这样就不会有记忆丢失的后顾之忧了。

hankcs.com 2017-06-27 下午 3.59.24.png

MT 的发展

基于短语的 MT 就是 2016-11 之前的 Google 翻译所采用的系统,其发展是缓慢的。神经网络兴起之后,出现了一种基于 Syntax-based SMT(估计是换换词向量),发展也不快。但 NMT 的发展是最迅猛的:

2017-06-27_16-07-32.png

NMT 的四大优势

  1. End-to-end training
    为优化同一个损失函数调整所有参数

  2. Distributed representation
    更好地利用词语、短语之间的相似性

  3. Better exploitation of context
    利用更多上下文——原文和部分译文的上下文

  4. 生成的文本更流畅
    可能跟上述优势有关

NMT 还避免了传统 MT 中的黑盒子(reorder 之类)模型。NMT 也存在弱点

  • 无法显式利用语义或语法结构(依存句法分析完全用不上了,有些工作正在展开)

  • 无法显式利用指代相消之类的结果

可见太统一的闭环系统也有自己的烦恼啊。

统计/神经网络机器翻译

Manning 说除了英语之外,学生中第二大语种是中文,而且他亮出了简体中文的例子,真是让人激动啊。他还特意在不同年份测试了 google 翻译的效果:

hankcs.com 2017-06-27 下午 4.31.44.png

其中,13 年有所进步,14-16 年又退步了并且停滞了 3 年。直到 2017 年才有质的飞跃。

我觉得这个句子涉及到指代相消,MT 系统很难消解损兵的“他们”。MT 系统必须有这样的常识:兵力悬殊的两军对战,人数少的一方更容易失败。估计语料不足以给它这种知识。

NMT 主要由工业界促进

2016-02 微软在 Android 和 iOS 上发布了离线 NMT 系统,这对境外旅游人士特别有帮助。

2016-08 Systran 发布了 NMT 模型

2016-09 Google 发布了 NMT 系统,大肆宣传了一番,并且 overclaim 比得上人工翻译质量。Manning 真是直言不讳啊。

介绍 Attention

朴素 encoder-decoder 的问题是,只能用固定维度的最后一刻的 encoder 隐藏层来表示源语言 Y,必须将此状态一直传递下去,这是个很麻烦的事情。事实上,早期的 NMT 在稍长一点的句子上效果就骤降。

Attention 机制

hankcs.com 2017-06-27 下午 5.43.49.png

解决方法是将 encoder 的历史状态视作随机读取内存,这样不仅增加了源语言的维度,而且增加了记忆的持续时间(LSTM 只是短时记忆)。

这种机制也与人类译员的工作流程类似:不是先把长长的一个句子暗记于心再开始闭着眼睛翻译,而是草草扫一眼全文,然后一边查看原文一边翻译。这种“一边……一边……”其实类似于语料对齐的过程,即找出哪部分原文对应哪部分译文。而 NMT 中的 attention 是隐式地做对齐的。

词语对齐

传统的 SMT 中需要显式地做双语对齐:

hankcs.com 2017-06-27 下午 5.50.02.png

而 attention model 是在翻译的过程中隐式地对齐。

同时学习翻译和对齐

一个非常棒的可视化,显示 attention model 成功地对齐了法语和英语,其中一小段语序的调整也反应出来了:

hankcs.com 2017-06-27 下午 5.54.28.png

打分

在图示问号时刻,究竟应该关注哪些时刻的 encoder 状态呢?关注的强度是多少呢?

有一种打分机制,以前一刻的 decoder 状态和某个 encoder 状态为参数,输出得分:

hankcs.com 2017-06-27 下午 5.58.27.png

hankcs.com 2017-06-27 下午 5.58.52.png

hankcs.com 2017-06-27 下午 5.59.11.png

hankcs.com 2017-06-27 下午 5.59.32.png

然后 softmax 归一化分值转化为概率,这个概率称为对齐权值(alignment weights):

hankcs.com-2017-06-27-下午 6.00.23.png

这个概率也代表模型应该将多少比例的注意力放在一个历史状态上:

hankcs.com 2017-06-28 上午 9.12.56.png

加权和得到一个 context vector,作为条件之一生成 decoder 的当前状态:

hankcs.com 2017-06-28 上午 9.14.58.png

而分数的获得,是通过 attention function 进行的。attention function 有多种选择,其中流行的是中间这种。给了两个向量更复杂的 interaction,而最后一种根本没有 interaction。

2017-06-28_09-20-16.png

有一些观点认为模型不应该注意所有的事情,可能对长句子来讲比较有潜力:

hankcs.com 2017-06-28 上午 9.24.03.png

但这些观点并没有取得更好的成绩:

2017-06-28_09-27-50.png

句子特短的时候,模型们的得分都不高。这纯粹是因为语料中的短句子本来就语义隐晦,比如某个专有名词作为标题。而有 attention 的模型在句子很长的时候,效果依然没有下降,说明了 attention 的重要性。

LSTM 非常擅长生成自然的文本,但有时候译文与原文相去甚远,没有把注意力放在原文上。比如下面红色的名字不知道从哪里冒出来的:

hankcs.com 2017-06-28 上午 9.33.12.png

加了 attention 好了很多,还是比不上人类,有时候会把同一个意思的词重复两遍:

hankcs.com 2017-06-28 上午 9.33.12.png

更多 attention!覆盖范围

在图片标题生成研究中,模型通过对图片不同区域的 attention 生成了不同的词语:

hankcs.com 2017-06-28 上午 9.38.26.png

如何保证不错过任何重要的区域呢?

Doubly attention

一种思路是同时注意原文和译文:

2017-06-28_09-42-10.png

用旧模型的语言学思想拓展 attention

可以利用 IBM2 等模型中的位置或 fertility(丰富程度),因为一般而言一个词最多翻译为两三个词,如果生成了五六个词,那么模型可能在重复生成。

hankcs.com 2017-06-28 上午 9.50.27.png

decoder

模型能够在给定原文的情况下计算译文的概率之后,就来到传统的问题了,找出最可能的译文

在 decoding 的时候,朴素想法是生成所有的翻译,用语言模型打分,然后挑最大的。但译文数量是词表大小的指数函数,无法实现。

2017-06-28_10-03-00.png

Ancestral sampling

在时刻,根据之前的词语生成当前词语

可以多次 sample 取最好的。

理论上完美无缺,但实践中只会产生高方差的差效果。你也不想同一个句子每次翻译结果都不一样。

2017-06-28_21-54-06.png

不多想了,贪婪地选取当前最可能的那个单词:

缺点显而易见,不是全局最优,走错一步会影响后面的部分。

2017-06-28_21-54-54.png

老生常谈了,从不搜索到贪婪搜索到柱搜索,随处可见。

每个时刻记录个最可能的选项(剪枝),在其中进行搜索。

然后递推

其中

也就是说把词表中的词丢进入计算概率取前个。

效果对比

hankcs.com 2017-06-28 下午 10.01.15.png

采样要采 50 轮才得到比贪心搜索稍好的结果,但很小的柱搜索轻松超越了它们。另外,基于短语的 MT 常用的柱搜索大小是 100 到 150,可见 NMT 的优势。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 10 NMT 与 Attention

CS224n 笔记 11 GRU 和 NMT 的进一步话题

2017-06-30_14-14-18.png从动机层面直观地充实了 GRU 和 LSTM 的理解,介绍了 MT 的评测方法,讨论了 NMT 中棘手的大词表问题和一些常见与最新的解决办法。

深入 GRU

把 GRU 再详细讲一讲。

hankcs.com 2017-06-29 下午 5.13.08.png

RNN 的梯度消失就不赘述了,红线连乘多次下溢出。

而 GRU 额外添加了一些“捷径”红线,允许梯度直接流过去,而不是连乘的方式递减过去。

Update Gate

hankcs.com 2017-06-29 下午 5.28.17.png

用来自适应学习应该把多少注意力放到前一个隐藏层状态上。

Reset Gate

hankcs.com 2017-06-30 上午 9.14.27.png

自适应地删除不需要的连接。

RNN 寄存器

hankcs.com 2017-06-30 上午 9.16.46.png

朴素 RNN 读取所有寄存器,运算后存入所有寄存器,没有灵活性。

GRU 寄存器

hankcs.com 2017-06-30 上午 10.04.08.png

门多了之后,就可以灵活地选择读取部分寄存器,执行运算,写入部分寄存器。

Reset Gate 起到决定要读哪些寄存器的目的,而 Update Gate 决定要写的寄存器。这里的“决定”其实是“强度”的意思,不是绝对的。

GRU 和 LSTM 对比

GRU 中的隐藏状态类似于 LSTM 中的 cell,而 LSTM 中的隐藏状态其实相当于一个暴露给外部世界的“显状态”。LSTM 通过给 cell 加一个获得非线性的灵活性。

另外,观察之前的系数,这里的其实是“don't reset gate”,其实是“don't forget gate”。

2017-06-30_14-05-38.png

深入 LSTM

宏观上的 LSTM Cell:

2017-06-30_14-14-18.png

将所有操作都 gate 起来,方便遗忘甚至忽略一些信息,而不是把所有东西都塞到一起。

2017-06-30_14-18-01.png

New Memory Cell 的计算是一个非线性的过程,与朴素 RNN 一模一样:

2017-06-30_14-22-41.png

最关键之处在于,Memory Cell 的更新中有一个加法项直接来自上一刻的 Cell,也就是说建立了的直接线性连接(与 ResNet 类似):

2017-06-30_14-30-49.png

类似于 GRU 中的加法,在反向传播的时候允许原封不动地传递残差,也允许不传递残差,总之是自适应的。

有了这些改进,LSTM 的记忆可以比 RNN 持续更长的 step(大约 100):

训练技巧

  • 将递归权值矩阵初始化为正交

  • 将其他矩阵初始化为较小的值

  • 将 forget gate 偏置设为 1:默认为不遗忘

  • 使用自适应的学习率算法:Adam、AdaDelta

  • 裁剪梯度的长度为小于 1-5

  • 在 Cell 中垂直应用 Dropout 而不是水平 Dropout

  • 保持耐心,通常需要训练很长时间

Ensemble

如果想获得额外的 2 个百分点的效果提升,可以训练多个模型,平均它们的预测。

2017-06-30_15-08-31.png

MT 评测

以前人们认为交给人类译员来打分是最好的,但这太主观了,10 个译员给出的翻译可能都不相同。

BLEU

后来 IBM 发明了一种简单有效的评价策略,叫 BLEU。

hankcs.com 2017-06-30 下午 5.56.06.png

通过比较标准译文与机翻译文中 NGram 的重叠比率(0 到 1 之间)来衡量机翻质量。

Brevity Penalty

是否可以通过输出大量无意义的 the 之类来作弊?不,通过 Brevity Penalty 来防止机翻比译员译文短。

一般取 4-gram 之内参与评测,最终的分值是所有 ngram 分值的几何平均乘上一个 Brevity Penalty:

hankcs.com 2017-06-30 下午 5.57.09.png

一个具体的 BLEU 重叠例子:

hankcs.com 2017-06-30 下午 6.01.00.png

Multiple Reference Translations

为了防止某篇机翻实际上很好,可就是跟人类译文用词行文不相似的情况,IBM 的论文建议多准备几篇标准答案,这样总会撞上一个:

hankcs.com 2017-06-30 下午 6.03.04.png

当然了,虽然 IBM 建议准备 4 篇,实际测试中经常只有 1 篇。没有完美体现能力的考试,现实社会也是如此。

刚开始的时候,BLEU 和人工打分几乎是线性相关(一致)的:

hankcs.com 2017-06-30 下午 7.56.45.png

而当人们为了 BLEU 分数费尽心机之后,两者就开始脱钩了。这也是为什么 Google 翻译的效果没有宣称的那么好的原因之一。

解决大词表问题

大词表问题指的是 softmax 的计算难度:

2017-06-30_20-04-35.png

早期的 MT 系统会使用较小的词表,但这并不是解决问题,而是逃避问题。

2017-06-30_20-05-53.png

另一种思路是,hierarchical softmax,建立树形词表,但这类方法过于复杂,让模型对树形结构敏感而不是对词语本身敏感。

另外就是上几次课见过的廉价的 NCE,这些方法对 GPU 都不友好。

Large-vocab NMT

最新的方法是训练时每次只在词表的一个小子集上训练,因为 40%的词语只出现一次,如果把训练数据均分为许多份,则每一份中的稀有词可能都不会在其他语料中出现。然后测试时加一些技巧,待会儿详谈。

训练

hankcs.com 2017-06-30 下午 8.18.31.png

如何选择小词表呢?在刚才的方法上更进一步,让用词相似的文章进入同一个子集,这样每个子集的词表就更小了。

hankcs.com 2017-06-30 下午 8.19.38.png

测试

测试的时候先雷打不动将前个最常使用的单词加入备选词表,然后将原文中每个单词可能的前个翻译加进去。最后在备选词表上 softmax。

hankcs.com 2017-06-30 下午 8.25.35.png

更多技巧

上上次小讲座中提到的 pointer、用词素级别来翻译 goooooood morning 之类。至此下课时间到了,后面全部是我的理解。

Byte Pair Encoding

这种方法试图用分词的思想去找出所有有意义的“词素”,其统计方法说来也简单,就是在词频词表里统计所有的 ngram 组合作为新的更长的 ngram:

hankcs.com 2017-06-30 下午 8.32.28.png

该方法拿到了 WMT2016 的第一名,代码在:github.com/rsennrich/nematus

其他

一些字符级别的 LSTM 之类。

另有一些混合动力的 NMT,大部分情况下在词语级别做翻译,只在需要的时候从字符级去翻译。这个系统的主体是词语级别的 LSTM:

2017-06-30_20-37-02.png

在词语级别上做常规的柱搜索:

hankcs.com 2017-06-30 下午 8.38.22.png

一旦产生了 unknown 词语,则在字符级别进行柱搜索:

2017-06-30_20-39-44.png

然后提升了两个 BLEU 分值。

hankcs.com 2017-06-30 下午 8.40.31.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 11 GRU 和 NMT 的进一步话题

CS224n 笔记 12 语音识别的 end-to-end 模型

这次斯坦福请到了深度学习教父 Hinton 的弟子 Navdeep 来讲语音识别,他正在英伟达工作,怪不得 N 卡在深度学习中的地位如此之高。而他本人也在用 Dell 的搭载了 N 卡的 XPS 跑 Ubuntu,一改以往“讲台必定信仰灯”的局面。

Automatic Speech Recognition(ASR)

ASR 就是将声学信号转化为文本的系统。

hankcs.com 2017-07-01 上午 10.40.18.png

语音是一种自然的用户接口:

2017-07-01_10-42-10.png

传统 ASR

传统做法的主体是生成式语言模型,建模声学信号与文本的发音特征的联合概率,但 pipeline 的不同部分掺杂了不同的机器学习模型:

hankcs.com 2017-07-01 上午 10.49.02.png

近现代 ASR

神经网络兴起之后,人们发现传统 pipeline 中的每个模型都可以被一种对应的神经网络所替代,并且取得更好的效果:

hankcs.com 2017-07-01 上午 10.52.23.png

但是这么多混乱的目标函数各自为政,难免有顾此失彼的情况。这构成一种动机,为什么不用一个统一的大模型来取代这盘散沙呢?

end-to-end ASR

直接从音频到字符的概率模型

hankcs.com 2017-07-01 上午 10.58.05.png

(上半部分与生成式模型作对比)

Connectionist Temporal Classification

这是一种强大的概率模型,特别适用于语音识别。其主体是一个Bidirectional RNN,上面是一层 softmax。

hankcs.com 2017-07-01 上午 11.07.28.png

词表中还有一个空格,这很重要。

由于语音片段(帧)切割时的随意性,可能导致一个字符 c 被切割为多个帧,每个帧都输出 c。为了区分字符与字符的界限,所以引入空格分隔符。在解码的时候还需要限制字符只能转移到相同的字符,或者空格。

hankcs.com 2017-07-01 上午 11.15.15.png

解码时的直观演示:

2017-07-01_11-24-13.png

灰色线条表示无输出的概率。

一些效果

2017-07-01_11-28-56.png

hankcs.com 2017-07-01 下午 4.06.20.png

hankcs.com 2017-07-01 下午 4.07.03.png

可见识别结果听上去挺像那么回事,可拼写不正确。Google 通过在训练时集成语言模型进去修正了这些问题。而且不再使用字符级别,而是使用单词级别的大词表,识别出可能的单词后,用语言模型挑出最可能的句子。

2017-07-01_16-15-12.png

虽然这是个 end-to-end 模型,但还是掺杂了一个语言模型。没有语言模型的帮助,该 CTC 模型无法根据已识别的单词做条件调整下次预测。

sequence to sequence speech recognition with attention

让语言模型也成为模型天然的一部分,将音频视作 sequence,文本视作另一个 sequence,类似于 NMT 中的 encoder-decoder,LSTM 模型根据之前的 y 和全部 x 预测下一个 y:

2017-07-01_16-21-00.png

一下子把 x 都喂进去后,对于很长的序列来讲,需要做 attention,在不同的时刻关注输入的不同部分:

hankcs.com 2017-07-01 下午 4.25.45.png

由于是 RNN,所以输入 x 依然不是定长的。

Listen Attend and Spell

定义 score 函数,接受每个历史时刻的 encoder 隐藏状态和 decoder 的当前状态,得到当前应当对每个历史时刻倾注多少注意力。softmax 归一化,加权和得到最终的 context vector,参与预测。

hankcs.com 2017-07-01 下午 4.32.39.png

这里的 encoder 是树形的,因为对于较长的语音来讲,要 softmax 的 timestep 实在太多,效率不高、模型注意力被分散。通过用 softmax 把相邻的 timestep 总结一下,提高了效率和效果。

这个模型是强大的,学习到了很多 pattern:

hankcs.com 2017-07-01 下午 4.40.29.png

还可能产生一个读音的不同拼写(取决于早期的预测结果,然后导致不同的 attention):

2017-07-01_16-45-01.png

2017-07-01_16-45-28.png

效果

得到的效果虽然没有超越多年优化的旧模型,但也是一个量级的:

hankcs.com 2017-07-01 下午 4.47.51.png

LAS 的限制

  • 必须等到用户说完话之后才能开始识别

  • attention 是计算瓶颈

  • 输入的长度对准确率影响特别大

hankcs.com 2017-07-01 下午 4.50.42.png

在线 seq2seq 模型

希望能够即时产生输出,并且不需要在整个 sequence 上分配 attention。

Neural Transducer

根据一个定长的输入序列片段产生输出,不要要前一个输出,依然需要空白符,依然需要 alignment(哪些字母属于一个词):

hankcs.com 2017-07-01 下午 5.05.23.png

用空白符隔开的区块只是一个字符,究竟哪些字符成词,又回到了老生常谈的“分词”问题上来。这里采用了柱搜索找出最可能的路径。

hankcs.com 2017-07-01 下午 5.10.55.png

训练的时候理论上有一个非常复杂的对数似然的梯度,但实际上经常只取对齐,不做识别上的求和:

hankcs.com 2017-07-01 下午 5.15.10.png

对齐的过程类似 viterbi,但并不严格是,我们是在找最优路径,但路径与之前的每个选择都有关。柱搜索不太理想,如果记录到每个 block(字符)为止产生特定数量 token 的最大概率,则可以用动态规划解决:

hankcs.com 2017-07-01 下午 5.22.56.png

结果

hankcs.com 2017-07-01 下午 5.25.36.png

在有 attention 的情况下,窗口大小影响不大;而在无 attention 的情况下,窗口较小效果较好。

Encoder 中的卷积

与其简单地层叠两个 timestep,不如喂给很深的卷积网络:

2017-07-01_19-06-34.png

效果显著:

hankcs.com 2017-07-01 下午 7.08.28.png

目标颗粒度

有很多选择:

hankcs.com 2017-07-01 下午 7.10.38.png

但对语音识别来讲,更有用的是字符的 ngram(相当于音节):

hankcs.com 2017-07-01 下午 7.11.43.png

它们有指数级的组合可能,不清楚哪一种是最好的:

hankcs.com 2017-07-01 下午 7.14.31.png

对于 end-to-end 模型来讲,常用的手法是由模型自动决定 ngram 的分割:

hankcs.com 2017-07-01 下午 7.15.34.png

效果

hankcs.com 2017-07-01 下午 7.18.36.png

上表的 ngram 代表“最大产生 ngram”的意思。

hankcs.com 2017-07-01 下午 7.19.47.png

模型缺点

在句子开头和人名地名处困惑度较高:

hankcs.com 2017-07-01 下午 7.23.08.png

在词语分界处的错误自信会导致搜索时的错误,连语言模型也无力回天。

解决办法

通过惩罚 softmax 输出概率 1 来 Entropy Regularization 正则化模型,可以克服这个问题:

hankcs.com 2017-07-01 下午 7.26.50.png

与其直接 ER,不如让输出的分布尽量与 Unigram 的分布相似,这样效果更好了。

另一个缺点

另一个问题是,模型偏向于惩罚生成很长的输出,这对很长的输入来讲会出现提前终止输出的情况:

hankcs.com 2017-07-01 下午 7.30.51.png

比如:

hankcs.com 2017-07-01 下午 7.32.21.png

解决办法是在预测时惩罚那些不看输入的情况,未处理的输入越多,惩罚越大。

这种粗暴的方法还是取得了一些效果提升:

hankcs.com 2017-07-01 下午 7.36.12.png

Better Language Model Blending

标注音频-字幕数据毕竟不如海量的未标注文本多,而 end-to-end 模型是一个自治的大模型,内部隐式地存在通过标注数据学习到的语言模型。在哪里如何与外部语言模型混合呢?

答案是在 decoder 的 softmax 预测结果的对数概率上线性混合:

2017-07-01_19-41-02.png

还有很多种混合手段,也是个新的前沿课题。

Better Sequence Training

上节课提到的 ground truth 问题,除了 scheduled sampling 之外,还有一些拓展。比如 Reinforement Learning 之类(草草提了两句)。

机会

一些研究方向了。

多音源

鸡尾酒舞会上有很多人说话,能否都识别出来呢?以前的生成式模型心中有一个固定的模式去生成数据与输入对比,不适合这个任务。现在常用的判别式模型反过来,以输入特征预测结果,应该可以做出以前做不到的成绩。

hankcs.com 2017-07-01 下午 7.51.52.png

"同声传译"

接受法语音频,直接输出英文文本。相当于将上面提到的模型与 MT 模型 blend 到一起了:

hankcs.com 2017-07-01 下午 7.53.43.png

两者分别对原文和音频的对齐是非常类似的:

hankcs.com 2017-07-01 下午 7.55.27.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 12 语音识别的 end-to-end 模型

CS224n 笔记 13 卷积神经网络

hankcs.com 2017-07-04 上午 10.11.32.png

补充了 NLP 中相对冷门的 CNN,谈了谈调参与发论文的潜规则。

从 RNN 到 CNN

  • RNN 无法利用未来的特征预测当前单词,就算是 bi-RNN,也不过是双向重蹈覆辙而已。

  • 经常把过多注意力放到最后一个单词上。

    hankcs.com 2017-07-03 下午 5.11.18.png

比如,如果只想得到 my birth 的向量,RNN 就无能为力了,因为它捕捉的是从左到右的“上文”。

  • 与此对应,softmax 也是加在最后一个单词上的

CNN 的解决思路说来也很简单粗暴,那就计算相邻的 ngram,不管它到底是不是真正的短语,眉毛胡子一把抓地为它们计算向量。

什么是卷积

定义在两个矩阵上的函数,生成另一个矩阵(也许称作 tensor 会更好)。对一维矩阵(向量来讲),可以形式化地描述如下

这里是过滤器的大小。

在计算机视觉中,卷积可以提取诸如物体边缘之类的特征。一个很棒的可视化:

Convolution_schematic.gif

更多入门知识参考《理解深度学习中的卷积》

单层 CNN

假设词向量,那么个单词的句子可视作词向量(列向量)的按列拼接

对于个单词上的卷积过滤器  ,比如, , ,下图演示了 NLP 中的单层 CNN 工作过程:

single-conv.png

也就是说,每相邻 3 个词语的组合对应卷积结果中的一个标量。卷积过滤器的权值也是一个向量,最终得到每个卷积结果  ,组合得到向量

对最后两个词语,由于不够长(),常见的技巧是补零:

single-conv-complete.png

(这样得到的就不再是的向量了

一个过滤器就是一个向量,卷积运算的子运算实际上就是与某个 ngram 的内积,内积代表相似性。于是卷积核实际上在挑选与自己相似的那种 pattern,比如正面情感的 ngram。

池化

卷积不是最终目的,最终目的是将卷积结果输入到其他网络中去。在不补零的情况下,卷积结果中有个数,与有关;而我们想要一个定长的输入。一个解决办法是 max-pooling,输入是 CNN 结果,输出是一个标量,其中

当然也可以用 min,不过 ReLU 时经常结果是 0,所以没什么意义。max 的意义是,这个过滤器就是要过滤出最强烈的那个 ngram,作为整段话的中心意思。

为了得到多个卷积特征,简单地使用多个卷积核(不需要大小一致),然后把池化结果拼接起来。

另外,有一个关于词向量的技巧。如果任由梯度流入词向量,则词向量会根据分类任务目标而移动,丢失语义上泛化的相似性。解决办法是用两份相同的词向量,称作两个通道(channel)。一个通道可变,一个通道固定。将两个通道的卷积结果输入到 max-pool 中。

分类

最终个卷积核输出的特征向量是:

喂给最终的 softmax 层:

图示

一个漂亮的图示:

hankcs.com 2017-07-04 上午 10.11.32.png

双通道词向量=>多个卷积核得到的 feature map(红色是 bigram,橙色是 trigram)=>池化得到最终特征=>softmax 分类输出。

dropout

老生常谈了:

中的元素满足参数的伯努利分布,最终特征向量相当于被缩放了:

也相当于在数据上采样子集训练模型,然后平均起来(ensemble),通常可以带来 2-4%的效果提升。

直观的解释是,在情感分析任务中,并不一定要两个表示正面的 ngram 同时出现才能下结论,有时候只出现一个也可能足以说明问题。

这些训练 trick 都相当于给 GD 引入噪声,让其不再肯定地瞄准一个局部最优,而是尽可能地探索尽可能多的 landscape。以前挺有希望的高斯过程调参也不流行了,人们觉得随机采样超参数空间就已经惊人地好用了。

一般模型里面的超参数非常多,就算给定一组超参数,多数情况下也无法复现结果。往好的方向解释是作者试验了很多次,终于碰巧撞上了更好的局部最优点,而我们都没撞上去。

试验结果

hankcs.com 2017-07-04 下午 1.57.27.png

双通道不显著地带来了一点提升。这张对比表也暴露了深度学习学术界公开的“黑幕”,没有人把每个训练 trick 单独拎出来报告提升比;2014 年之前几乎没人用 Dropout,可最新发表的论文就是要跟它们比 Dropout,却不愿把它们加入 Dropout 重新实现一遍(可能费时费力还不讨好吧,万一被 baseline 反杀不就完蛋啦)。事实上,这个结果只能说明 Dropout 比不 Dropout 有 2-4%的提升,不能说明 CNN 多有效。

CNN 花样

可以变换很多花样,比如每个 feature map 是变大了还是变小了,池化技巧等等。

hankcs.com 2017-07-04 下午 2.09.13.png

CNN 应用:机器翻译

第一个神经网络机器翻译模型吧,用 CNN 做 encoder,RNN 做 decoder:

hankcs.com 2017-07-04 下午 2.10.23.png

到这里就快下课了,Richard 草草讲了几句。

模型比较

Bag of Vectors:对分类问题来讲非常棒的基线模型,接几层 ReLU 甚至可能打败 CNN。

Window Model:对单个单词的分类来讲挺好,但不适用于需要更多上下文的任务。

CNN:适合分类,不清楚如何做短语级别的标注,对短文本需要 padding,难以用 NLP 的视角解释,容易 GPU 并行化

RNN:从左读到右,最符合认知。不是分类任务的最佳选择,比 CNN 慢,但可以做序列标注。

Quasi-RNN

CNN 和 RNN 的混血儿,在时序数据上做卷积:

hankcs.com 2017-07-04 下午 2.17.04.png

比 LSTM 速度要快,效果要好:

hankcs.com 2017-07-04 下午 2.18.46.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 13 卷积神经网络

CS224n 笔记 14 Tree RNN 与短语句法分析

介绍了著名的复合性原理,由此启发得到树形 RNN,用于表示短语和句子级别的语义。从朴素树形 RNN 到 SU-RNN 和 MV-RNN,讨论了进化历史和各自优缺点,演示了效果和在图像等领域的拓展应用。

语言模型光谱

最简陋最常用的是词袋模型,或“词向量袋模型”。最复杂的可能是短语结构树,额外再标注一些诸如指代、语义等标签。

hankcs.com 2017-07-04 下午 5.33.11.png

这张图很形象,词袋中装不下所有单词,散落一地。虽然词袋模型很简陋,但它依然是一个很难击败的基线模型。它简单高效,通过一些聪明的技巧可以在一些任务中胜过深度神经网络。

语言的语义解释——并不只是词向量

词向量只是词语级别的向量,对于下列两句话,加粗部分其实意思是一致的:

hankcs.com 2017-07-04 下午 5.42.28.png

就算一个人没见过 snowboarder 这个单词,他也能明白这与下面这个短语是同一个意思。人们可以用更大颗粒度的文本来表达自己的意思,而不仅仅是词袋中的某个单词。有什么模型可以做到这一点呢?

语义合成性

这个问题触及的第一个层面就是语义的合成性,你可以组合小的单元来表示更复杂的语义。

hankcs.com 2017-07-04 下午 5.50.37.png

这不仅关乎语言,还关乎人类的其他认知活动。

对于图像而言,也有类似的组成性:

hankcs.com 2017-07-04 下午 5.52.36.png

语言能力

以前有种主流观点认为,人类生来就具备处理语言的大脑模块,使得人类区别于其他动物,成为万物灵长。但乔姆斯基最近的工作认为,人类大脑中没有这种结构,只是因为人类学会了递归而已。有了递归的思想,人类就能利用有限的器官处理无限长度的语言序列。

hankcs.com 2017-07-06 下午 2.52.01.png

语言是递归的吗

在认知科学上虽然有些争议,因为一般一个句子是有长度限制的,人们几乎从不说 300 个词以上的句子。但是递归是描述语言的最佳方式,比如

[The man from [the company that you spoke with about [the project] yesterday]]

这里面一个名词短语套一个名词短语,一级级下去。从实用的角度讲

1、通过递归地描述句子(句法树),可以有效地消歧:

hankcs.com 2017-07-06 下午 3.12.41.png

2、便于指代相消等任务。

3、便于利用语法树结构(基于短语的机器翻译)

在词向量空间模型上表示语义

不论句子多复杂,我们总是希望能在同一个向量空间中表示词语和短语的语义。为什么一定要这么做?回想这节课开头 snowboarder 的例子就明白了,有时候一个单词与一个短语表达的是同一个意思。

2017-07-06_15-38-53.png

如何将短语映射到向量空间

根据著名的复合性原理——

在数学、语义学和语言哲学中,复合性原理是指,一个复杂表达式的意义是由其各组成部分的意义以及用以结合它们的规则来决定的。

——dict.eudic.net/dicts/en/Principle_of_compositionality.html

2017-07-06_15-51-14.png

通过同时学习句法树和复合性向量表示,就可以得到短语的向量表示了。

短语结构分析:目的

如果我们能用短语结构树表示一个句子:

2017-07-06_15-56-23.png

将每个 rule 视作一个算子,即可得到每个短语乃至句子的向量表示:

hankcs.com 2017-07-06 下午 3.57.38.png

Recursive vs. recurrent neural networks

hankcs.com 2017-07-06 下午 4.01.20.png

两者都是递归神经网络,只不过前者在空间上递归,后者在时间上递归。中文有时会把后者翻译为“循环神经网络”,但这明显混淆了等级,令人误解。

它们各有各的优缺点,Recursive neural net 需要分析器来得到句法树,而 Recurrent neural net 只能捕捉“前缀”“上文”无法捕捉更小的单位。

但人们还是更倾向于用后者,LSTM 之类。因为训练 Recursive neural net 之前,你需要句法树;句法树是一个离散的决策结果,无法连续地影响损失函数,也就无法简单地利用反向传播训练 Recursive neural net。另外,复杂的结构也导致 Recursive neural net 不易在 GPU 上优化。

从 RNNs 到 CNNs

RNN 只会为满足语法的短语计算向量,而 CNN 为每个可能的短语计算向量。从语言学和认知科学的角度来讲,CNN 并不合理。甚至 recurrent neural network 也比 tree model 和 CNN 更合理。

两者的关系可以这样想象,RNN 将 CNN 捕捉的不是短语的部分删除了:

2017-07-06_16-36-07.png

得到:

2017-07-06_16-37-10.png

Recursive Neural Networks 用于结构化预测

同时得到结构树和向量表示的一种任务。

输入两个候选子节点,输出两个子节点合并后的新节点语义表示,以及新节点成立的分值。

2017-07-06_16-57-11.png

最简单的 Recursive Neural Network

利用单层的神经网络作为组合函数,向量内积作为打分函数,马上就可以得到一个最简单的 RNN:

2017-07-06_17-04-22.png

用 RNN 分析句子

计算任意两个单词合并的得分(虽然下图是相邻两个,但我觉得那只是绘图方便;就算是我第一次写的玩具级别的依存句法分析器,也是任意两个单词之间计算):

hankcs.com 2017-07-06 下午 5.19.26.png

然后贪心地选择得分最大的一对合并:

hankcs.com 2017-07-06 下午 5.20.06.png

重复这一过程

hankcs.com 2017-07-06 下午 5.20.34.png

直到得到根节点:

hankcs.com 2017-07-06 下午 5.20.58.png

最大间隔

不再仅仅根据整个句子的总分挑选,而是要求每个预测分值离错误类别要尽量远。

然后也不要贪心搜索,而是柱搜索。

结构上的反向传播

由于前向传播时每个节点的信号来自所有子节点,所以梯度也来自所有子节点。并且前向传播时父节点的信号是利用子节点信号的拼接计算的,所以梯度需要针对子节点的信号计算:

hankcs.com 2017-07-07 下午 5.06.43.png

这个问题其实在 TensorFlow 那一课已经讲过了,图计算:前向传播信号流入某节点,反向传播误差就得从某节点分流到所有源节点。树只是图的一个特例:

hankcs.com 2017-07-07 下午 5.19.53.png

简单 RNN 的缺点

hankcs.com 2017-07-08 上午 9.38.58.png

单层神经网络接受两个单词词向量的拼接,乘上权值矩阵,非线性激活。矩阵运算中可视作两个矩阵的拼接,其实没有发生互动。

另外,模型中只有一个合成函数,使用同一个权值矩阵处理 NP、VP、PP……这明显是不合理的。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 14 Tree RNN 与短语句法分析

CS224n 笔记 15 指代消解

2017-07-11_15-53-57.png从规则启发式代词回指消解出发,介绍了传统特征工程二分类模型、启发式损失的分类模型,过渡到利用强化学习自动缩放损失的神经网络模型。

什么是指代消解

找出文本中名词短语所指代的真实世界中的事物。比如:

hankcs.com 2017-07-11 上午 10.03.41.png

hankcs.com 2017-07-11 上午 10.04.57.png

不只是代词能够指代其他事物,所有格和其他名词性短语也可以。

甚至还存在大量嵌套的指代:

hankcs.com 2017-07-11 上午 10.20.22.png

应用

  • 全文理解
    完整的文章中有大量的指代

  • 机器翻译
    土耳其语不区分男他和女她,翻译到英文的时候必须做指代消解;当然了,当前工业线上的 MT 系统大多做不了

  • 文本摘要
    使用代词会使行文更加自然

  • 信息提取和 QA 系统
    比如搜索“谁娶了 Claudia Ross”,出来“He married Claudia Ross in 1971”,则系统必须消解“He”是谁。

指代消解评测

指代消解的结果很像聚类,所以一些聚类的评测手段可以借用:

hankcs.com 2017-07-11 上午 10.32.09.png

当然实际中问题可能更复杂一些,有多个类别,而且 gold cluster 如何与 produced cluster 二分图匹配也是个 NP 问题(实际应用中有一些贪婪的算法效果也还不错)。

指代的类型

hankcs.com 2017-07-11 上午 10.46.05.png

这个分类是语言学家定的,在 NLP 领域没见到过,顶多见到“回指”和“共指”。

不是所有 NP 都在指代

hankcs.com 2017-07-11 上午 10.48.34.png

绿色 NP“指代”全集或空集,描述的是一种普遍规律。红色等价于绿色,所以也不是在做指代。

Coreference, anaphors, cataphors

NLP 领域关注的其实只有上述 3 种指代类型的前 2 种,中文应当译作“共指”和“回指”,最后一种是“回指”的反义词:

hankcs.com 2017-07-11 上午 10.56.36.png

一般视作“共指”处理。

那为什么回指不视作共指处理呢?因为两者在许多时候并不等效:

hankcs.com 2017-07-11 上午 11.00.08.png

在 NLP 领域,也很少研究这种 bridging anaphora,后一个“票”视作另一种实体的指代,并不学习其与“演唱会”的关系。

共指与回指

它们是两种不同的概念:

hankcs.com 2017-07-11 上午 11.05.42.png

回指中,后一个实体指代前一个实体,但两者并非指代真实世界中同一个事物。

那是不是应该区别对待它们呢?事实上,实际应用的系统几乎无法区分它们。

传统代词消解方法:Hobbs’ naive algorithm

这是 Hobbs 手工写的规则集,在句法树上运作。有两页的规则,类似地摊杂志上的心理算命:“如果……那么跳转第几步……否则……”该算法根据英文语言上的直觉编写,可以拿到 80%的准确率,有时候也作为其他机器学习分类器的特征之一使用。

基于知识库的代词消解

Winograd 和其他旧派 AI 学者认为,要做指代消解,必须有一个外部的知识库。比如:

hankcs.com 2017-07-11 下午 2.22.46.png

根据常识,害怕暴力的更可能是女性,所以上述两个 they 的指代各不相同。这种“知识库”可能帮助系统消歧。

Winograd 等人的“知识库”连 Hobbs 的基线算法都没有达到。

几种指代消解模型

Mention Pair models

将所有的指代词(短语)与所有被指代的词(短语)视作一系列 pair,对每个 pair 二分类决策成立与否。

Mention ranking models

显式地将 mention 作为 query,对所有 candidate 做 rank

Entity-Mention models

一种更优雅的模型,找出所有的 entity 及其对话上下文。根据对话上下文聚类,在同一个类中的 mention 消解为同一个 entity。但这种方法其实也用得不多。

监督 Mention-Pair Model

hankcs.com 2017-07-11 下午 3.00.13.png

这种模型用得最多,每个 mention 与之前的 mention 做二分类决策,当然如何识别 mention 是另外一个问题。

指代消解可用特征

大量语言学特征,人肉特征工程可用:

hankcs.com 2017-07-11 下午 3.07.35.png

神经网络指代消解模型

虽然直觉上,联合学习所有 mention 之间的指代关系肯定比单个二分类要好,global 比 local 肯定要好。但实际上没有试验数据能够支撑这一点,所以虽然有 global RNN 等更强大的模型,效果最好的依然是 mention-pair 或 mention-ranking 模型。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 15 指代消解

CS224n 笔记 16 DMN 与问答系统

hankcs.com 2017-07-12 上午 10.52.33.pnghankcs.com 2017-07-12 上午 11.22.45.pnghankcs.com 2017-07-12 下午 4.35.36.png2017-07-12_16-49-43.pnghankcs.com 2017-07-12 下午 4.55.26.pnghankcs.com 2017-07-12 下午 5.09.55.png2017-07-12_17-14-50.png2017-07-12_17-17-53.png最有意思的一课,将所有 NLP 任务视作 QA 问题。模仿人类粗读文章和问题,再带着问题反复阅读文章的行为,利用 DMN 这个通用框架漂亮地解决了从词性标注、情感分析到机器翻译、QA 等一系列任务。

是否所有 NLP 任务都可视作 QA?

问答系统举例如下:

hankcs.com 2017-07-12 上午 10.08.22.png

在 old-school NLP 系统中,必须手工整理一个“知识库”;然后在这个知识库上做规则推断。这节课介绍的 DMN 完全不同于这种小作坊,它能够直接从问答语料中学习所有必要的知识表达。

DMN 还可以在问答中做情感分析、词性标注和机器翻译。

所以构建一个 joint model 用于通用 QA 成为终极目标。

要实现这个目标,有两个障碍。

前无古人

没有任何已有研究探讨过如何让单个模型学会这么多的任务。每种任务都有独特的特点,适合不同的神经网络来做:

hankcs.com 2017-07-12 上午 10.25.05.png

全才难得

第二个障碍是,Fully joint multitask learning(同一个 decoder/classifier,不仅仅共享词向量,而应该共享全部参数)非常困难。

有些不成功的研究发现,只能在低层(词向量)共享参数、如果任务之间没有直接关联则会顾此失彼。

Dynamic Memory Networks

今天介绍的 DMN 仅仅解决了第一个问题。虽然有些超参数还是得因任务而异,但总算是个通用的架构了。

回答难题

假设有个阅读理解题目:

hankcs.com 2017-07-12 上午 10.36.56.png

你无法记住全文,但看了问题之后,只要带着问题扫几眼原文,你就能找出答案。

这种现象启发了 DMN。

Dynamic Memory Networks

先来看 big picture(接下来会对每个模块单独讲解):

hankcs.com 2017-07-12 上午 10.52.33.png

左边输入 input 的每个句子每个单词的词向量,送入 input module 的 GRU 中。同样对于 Question Module,也是一个 GRU,两个 GRU 可以共享权值。

Question Module 计算出一个 Question Vector ,根据应用 attention 机制,回顾 input 的不同时刻。根据 attention 强度的不同,忽略了一些 input,而注意到另一些 input。这些 input 进入 Episodic Memory Module,注意到问题是关于足球位置的,那么所有与足球及位置的 input 被送入该模块。该模块每个隐藏状态输入 Answer module,softmax 得到答案序列。

有人问 DMN 能否学会新类型的问题,答案是否定的。Episodic Memory Module 中有两条线,分别代表带着问题第一次阅读 input 的记忆,以及带着问题第二次阅读的记忆。

The Modules: Input

开始讲解每个模块的细节了。

hankcs.com 2017-07-12 上午 11.08.20.png

输入模块接受  个输入单词,输出个“事实”的表示。如果输出是一系列词语,那么有;如果输出是一系列句子,那么约定表示句子的数量,表示句子中单词的数量。我们使用简单的 GRU 读入句子,得到隐藏状态,其中是 embedding matrix, 是时刻 的词语。

事实上,还可以将这个 Uni-GRU 升级为 Bi-GRU:

hankcs.com 2017-07-12 上午 11.22.45.png

每个 fact 被表示为双向隐藏状态的拼接。

The Modules: Question

hankcs.com 2017-07-12 上午 11.24.52.png

同样用一个标准的 GRU 读入问题,同样的 embedding matrix ,得到最后一个隐藏状态

The Modules: Episodic Memory

hankcs.com 2017-07-12 上午 11.29.57.png

DMN 与其他网络最大的不同之处在于,它会多次阅读输入句子,每次只注意句子的 fact 表示中的一个子集。

输入模块的 Bi-GRU 产生 fact 表示,Episodic Memory 模块也使用 Bi-GRU 产生 Episodic Memory 表示。记第个 pass 产生的 episodic memory representation 为  ,episode representation(由 attention mechanism 产生)为 

其中,episodic memory representation 初始化为 ,并通过 GRU 处理: 。而 episode representation 使用 input module 的隐藏状态输出更新:

其中 是 attention mechanism,有多种计算方式。在最初的 DMN paper (Kumar et al. 2016)中,验证了下列公式是最好的:

这里最重要的是的计算,通过句子向量、问题向量、上一个记忆向量之间原始形式与 element-wise 乘积(相似性)的按行拼接,得到一个超长的列向量。然后非线性激活传播一层,softmax 得到,表示每次阅读对每个时刻(句子)的关注程度。

Richard 说最初他们还尝试过,后来发现并没有什么帮助。他的忠告是,如果模型中有多余的部分,那就去掉。

这里两条线代表两个 GRU,阅读两遍。这也是个超参数。Richard 说最开始多个 pass 的 Memory 也通过 GRU 合并,后来发现只留两个 pass 直接视作两层神经网络也能解决问题,能简单就不要复杂。

The Modules: Answer

answer module 就是一个简单的 GRU decoder,接受上次输出的单词(应该是 one-hot 向量),以及 episodic memory,输出一个单词:

相关工作

有很多已有工作做了类似研究:

• Sequence to Sequence (Sutskever et al. 2014)

• Neural Turing Machines (Graves et al. 2014)

Teaching Machines to Read and Comprehend (Hermann et al. 2015)

• Learning to Transduce with Unbounded Memory (Grefenstette 2015)

• Structured Memory for Neural Turing Machines (Wei Zhang 2015)

• Memory Networks (Weston et al. 2015)

• End to end memory networks (Sukhbaatar et al. 2015)

Richard 吐槽说他不觉得机器能像加粗的论文标题所说的,跟人一样阅读理解。也许这篇论文有点标题党了。与 DMN 联系密切的是后面两篇论文,都有 Memory Component。

与 MemNets 比较

相同点

  • 都有 input, scoring, attention and response 模块

不同点

  • MemNets 主要使用词袋,然后有一些 embedding 去 encode 位置

  • MemNets 迭代运行 attention 和 response

这些不同点都是由于 MemNets 是个非 sequence 模型造成的。而 DMN 是个血统纯正的 neural sequence model,天然适合序列标注等任务,比 MemNets 应用范围更广。

DMN 的 sequence 能力来自 GRU,虽然一开始用的是 LSTM,后来发现 GRU 也能达到相同的效果,而且参数更少。(这回答了 GRU 和 LSTM 那节课有个学生的问题:哪个计算复杂度更低。Manning 当时回答应该是一样的,还不太相信 Richard 的答案。说明在工程上,还是做实验的一线博士更有经验)

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 16 DMN 与问答系统

CS224n 笔记 17 NLP 存在的问题与未来的架构

hankcs.com 2017-07-13 上午 11.32.57.png课程介绍了各种各样的深度学习网络与应用,是时候从更高层次思考自然语言处理存在的问题与展望未来了。虽然 BiLSTM 与 attention 几乎统治了 NLP,但在篇章级别的理解与推断上还不尽人意。

新时代人们正在“解决”语言

深度学习填平了领域鸿沟,许多计算机视觉的泰斗级学者也开始研究起自然语言处理的各种任务。

hankcs.com 2017-07-12 下午 10.02.33.png

这里提到的自然语言理解、机器翻译都是较高层次、更难的任务,现有系统做得并不那么好。

旧时代的热血

早期 NLP 学者拥有崇高的目标,希望让机器像人一样理解语言;但奈何数据和计算力都有限,使得成效甚微。Manning 说今天我们有了海量的数据与强大的计算力,却往往自满于跑个 LSTM,而不愿意挑战这个终极目标。

AI 的师祖 Norvig (1986)的 Ph.D.论文 The unified theory of inference 中举了个例子,希望机器从一篇文章中理解如下信息:

2017-07-12_22-12-35.png

其中 a 和 b 即便是最前沿的技术也无法自动推断,因为在文本没有提到海与岛的关系,也没提到渔网,省略了太多背景知识。

所以 Norvig 认为,必须先建立一个包罗万象的知识库,才能进行自然语言理解。但最近二十年,没有知识库我们也完成了许多 NLP 任务,并且模型学到的“知识”是连续的表示,而不是“知识库”中的离散表示。

Norvig 假想的系统中含有如下 4 种推断(inference):

  1. Elaboration:连接两个实体,表示解释说明

  2. Reference Resolution:就是指代相消

  3. View Application:比喻、活用、习语

  4. Concretization:具体化,一般化,比如 TRAVELLING is an AUTOMOBILE is an instance of DRIVING

基础 NLP:在进步

Norvig 写博士论文的时候,连像样的句法分析器都没有,所有句子都是手工分析的。现在我们有了全自动的句法分析器

hankcs.com 2017-07-13 上午 11.30.59.png

但现代 NLP 依然没有完成 Norvig 设想的宏伟目标——自动推断:

hankcs.com 2017-07-13 上午 11.32.57.png

也许是时候开始挑战这个宏伟目标了。

我们还需要什么

现在 BiLSTMs with attention 已经统治了 NLP 领域,你可以在任何任务上应用它,得到超越其他方法的好成绩(就如同若干年前的 CRF 和 SVM 一样)。

另外神经网络方法也带来了自然语言生成领域的文艺复兴(MT、QA、自动摘要……)

这些现代突破都没有采用 Norvig 假想的“知识库”。究竟是否需要一个显式的、localist(应该指的是领域相关的)语言与知识表示和推断机制,这是一个亟待探讨的科学问题。

虽然神经网络隐含了知识表达,我们也已经取得了如此多的成就,但我们建立和访问记忆或知识的手段依然十分原始。LSTM 只是短时记忆,并不能与人类经年累月的经验与记忆相比。LSTM 只是线性地扫描最近 100 个单词而已。

另外,现有模型也无法制定和执行目标或计划。这对对话系统而言非常重要,对话应当是有意义有目标的,而不是闲扯。

虽然句子级别的分析已经可以做到很清楚,句子之间的关系(顺承、原因、转折)则无法理顺。

而且现在无论多深的网络,依然缺少理解语言解释说明的常识或背景知识(虽然模型可能不够复杂,我觉得数据量不够也是很大原因,人类从小到大接受了多少文化教育,你能提供给神经网络的标注语料能有多少个句子)。

接下来介绍一些前沿的尝试“盗火”的研究。

Recursive Neural Networks 用于意识形态检测

作为语言学者,Manning 还是很喜欢树形模型(赞成)。他的学生尝试用 Recursive Neural Networks 检测人们在政治上是保守的还是自由激进的等等。

hankcs.com 2017-07-13 下午 12.05.18.png

这并不是句子级别或段落级别的分析,而是文章级别的分析。一些政治术语被复合起来检测最终的政治倾向(用不同颜色表示)。红色表示保守的:

hankcs.com 2017-07-13 下午 12.07.55.png

TreeRNN

hankcs.com 2017-07-13 下午 12.13.13.png

树形模型理论上很有吸引力,但非常慢,需要外部句法分析器(如果用内部的则更慢),而且也没有用到语言的线性结构。

recurrent NN 训练快

线性结构的模型适合 batch 训练,因为对每个训练实例,模型结构都是一样的。

hankcs.com 2017-07-13 下午 12.15.46.png

TreeRNN 结构取决于输入

所以无法并行化,一个线程在训练某种结构的模型,其他线程得等它。

hankcs.com 2017-07-13 下午 12.18.45.png

The Shift-reduce Parser-Interpreter NN (SPINN)

为了提高 tree 模型的训练效率,人们活用 Shift-reduce 依存句法分析的思想,将模型的树形机构拆分为动作序列,得到了 25 倍的速度提升。这还产生了“线性”与“树形”模型的混血,可以离开外部句法分析器独立运行。

binary trees = transition sequences

具体拆分方法如下:

hankcs.com 2017-07-13 下午 12.23.14.png

shift 入栈,reduce 合并。

架构

hankcs.com 2017-07-13 下午 12.28.48.png

模型与 parser 类似,有 stack 和 buffer 储存单词,tracking 由 LSTM 负责 track 并决策动作,TreeRNN 负责拼装成分的表达。LSTM 还将上文的表示送给 TreeRNN 拼装,这似乎解决了树形模型无法捕捉语言的线性结构的问题。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 17 NLP 存在的问题与未来的架构

CS224n 笔记 18 挑战深度学习与自然语言处理的极限

最后一课,总结了目前这两个领域中的难题,介绍了一些前沿研究:快 16 倍的 QRNN、自动设计神经网络的 NAS 等。

深度学习已经漂亮地完成了许多单项任务,但如果我们继续随机初始化模型参数,我们永远也无法得到一个可以完全理解语言的系统。模型就像蒙住眼的狗,在高原上随机游走,头撞南墙。

hankcs.com 2017-07-14 下午 4.21.53.png

Richard 说他同样不相信独立的无监督学习能够救世(同意),虽然这个问题还存在许多争议。因为语言有许多监督与反馈,要完成的目标也多种多样。

在达到下一个层次的路上,摆在 NLP 面前有许多障碍。

障碍 1:通用架构

没有单个模型能够胜任多个任务,所有模型要么结构不同,要么超参数不同。

hankcs.com 2017-07-14 上午 10.23.25.png

上次介绍的DMN带来了曙光。

hankcs.com 2017-07-14 上午 10.28.55.png

障碍 2:联合多任务学习

上次也提到过,同一个 decoder/classifier,对于不同的输入,只要替换 encoder 就能同样输出。

  • 不像计算机视觉,只能共享低层参数

  • 只在任务相关性很强的时候才会有帮助

  • 否则会影响效果

解决方案

在第一课中提到的 MetaMind 团队提出的 A Joint Many-Task Model: Growing a Neural Network for Multiple NLP Tasks,现在详细介绍。

hankcs.com 2017-07-14 上午 10.44.24.png

这是个怪兽一般的模型,多层 LSTM 并联,从下往上看在文本颗粒度上是越来越大,在任务上是越来越复杂。由底而上分别是词性标注、CHUNK、句法分析、两个句子的相关性、逻辑蕴涵关系。输入可能是词,也可能是字符 ngram。底层任务的隐藏状态有到其他层级的直接路径。相关性 encoder 的输出卷积一下,供逻辑关系分类器使用。

整个模型使用同一个目标函数。左右对称只是示意可以接受两个句子用于关联分析,其实是同一套参数。

模型细节

hankcs.com 2017-07-14 上午 10.50.02.png

词性标注是单层 LSTM,隐藏状态 softmax 一下,乘上 label 的 embedding 得到每个单词的 pos embedding。将词性标注 LSTM 的隐藏状态、pos embedding 和单词输入 chunking 的 LSTM,执行类似的流程得到 chunk 的 embedding。

依存句法分析

hankcs.com 2017-07-14 上午 11.04.03.png

依然是单层 LSTM,每个时刻同时接受下级的输入。每两个时刻的单词做一次 softmax,判断它们的依存关系。理论上讲,该方法无法保证结果一定是合法的句法树,但 Richard 说 99%的 case 都是合法的,加上一些剪枝规则后,可以拿到最高分数(虽然论文还未发表就被另一位同行超越了)。

语义联系

hankcs.com 2017-07-14 上午 11.07.57.png

依然是类似的 BiLSTM,多了个 CNN max 池化,过一层前馈神经网络,过一层 softmax 得到两个句子的语义联系。

训练

hankcs.com 2017-07-14 上午 11.17.29.png

如果每个 softmax 都用交叉熵损失函数训练的话,效果并不好。这里用到了一种新奇的技巧叫做 successive regularization,在某个上层任务的损失函数中,加入一个正则化项,限制下层任务的参数不要改变太多。

在训练的时候,从低层任务出发,先专注优化一个目标函数,假装上面的东西都不存在,逐步往上。(我觉得这并不是同一个目标函数)

结果

hankcs.com 2017-07-14 上午 11.23.33.png

联合训练提高了每项任务的效果,任务数据越小,提升越显著。这是因为迁移学习的帮助越大。

大部分任务都拿到了最高分:

hankcs.com 2017-07-14 上午 11.25.39.png

障碍 3:预测从未见过的词语

以前课程也讲过,pointer copy 机制

hankcs.com 2017-07-14 上午 11.35.29.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 笔记 18 挑战深度学习与自然语言处理的极限

CS224n 研究热点 1 一个简单但很难超越的 Sentence Embedding 基线方法

这门课会不定期地让 TA 介绍一些课程相关的最前沿研究,与课程进度并非流畅衔接,所以单独做笔记。这次是第二节课中场休息时,由 Danqi Chen 带来的五分钟小讲座:

hankcs.com 2017-06-07 下午 4.07.18.png

句子 Embedding 动机

虽然这节课一直在讲词向量可以编码词的意思,但自然语言处理真正关心的是整个句子的意思。

hankcs.com 2017-06-07 下午 4.07.53.png

如果我们能够拿到句子的向量表示,则可以方便地用内积计算相似度:

hankcs.com 2017-06-07 下午 4.08.24.png

还可以在这些句子向量之上构建分类器做情感分析:

hankcs.com 2017-06-07 下午 4.08.42.png

已有方法

具体怎么由词向量到句向量呢?有很多种方法,比如词袋模型中简单地线性运算:

hankcs.com 2017-06-07 下午 4.09.14.png

在后面的课程中,将会用 recurrent neural network、recursive neural network,CNN 来做同样的事情。

hankcs.com 2017-06-07 下午 4.09.29.png

新方法

但今天要介绍的这篇普林斯顿大学的论文却剑走偏锋,采用了一种简单的无监督方法。这种方法简单到只有两步:

hankcs.com 2017-06-07 下午 4.09.53.png

第一步,对句子中的每个词向量,乘以一个独特的权值。这个权值是一个常数除以与该词语频率的和,也就是说高频词的权值会相对下降。求和后得到暂时的句向量。

然后计算语料库所有句向量构成的矩阵的第一个主成分,让每个句向量减去它在上的投影(类似 PCA)。其中,一个向量在另一个向量上的投影定义如下:

概率论解释

其原理是,给定上下文向量,一个词的出现概率由两项决定:作为平滑项的词频,以及上下文:

hankcs.com 2017-06-07 下午 4.10.26.png

其中第二项的意思是,有一个平滑变动的上下文随机地发射单词。

效果

hankcs.com 2017-06-07 下午 4.13.14.png

在句子相似度任务上超过平均水平,甚至超过部分复杂的模型。在句子分类上效果也很明显,甚至是最好成绩。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 1 一个简单但很难超越的 Sentence Embedding 基线方法

CS224n 研究热点 2 词语义项的线性代数结构与词义消歧

hankcs.com 2017-06-08 下午 8.29.40.png

词向量本身无法解决一词多义的问题,比如:

hankcs.com 2017-06-08 下午 7.49.02.png

这里的 tie 可能表示球赛的平局,也可能表示领带,还可能表示绳子打结。那它的词向量究竟在哪里呢?

虽然相似的词被映射到邻近的位置,但该论文证明词向量是所有义项的平均:

hankcs.com 2017-06-08 下午 7.54.32.png

它被映射到这些词语的中央,这有什么用呢,可以复原它的所有义项吗?

复原

研究发现义项是由如下 sparse coding 编码:

hankcs.com 2017-06-08 下午 7.58.28.png

这里的是类似于 sports 之类的 Context vectors(或说义项的 vector),是某个 Context vector 的系数(hard sparsity constraint)。论文中说这些参数可以通过标准 k-SVD 算法求出。

复原结果

hankcs.com 2017-06-08 下午 8.03.52.png

量化评测

是请了各国的研究生与该方法一起作为实验对象,做如下的题目:

hankcs.com 2017-06-08 下午 8.06.51.png

问他们这些词语与某个词是否相关,计算 PR 值。结果证明,这种方法可以达到 non-native speaker 的水平。

hankcs.com 2017-06-08 下午 8.08.48.png

总结

词向量是多个义项的叠加。通过复原方法,可以通过 sparse coding 的 k-SVD 算法复原词向量中的所有义项。复原水平达到 non-native English speakers 的水准。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 2 词语义项的线性代数结构与词义消歧

CS224n 研究热点 3 高效文本分类的锦囊妙计

hankcs.com 2017-06-10 下午 9.24.11.png

Facebook 的 fastText

文本分类是 NLP 中常见的任务,比如情感分析:

hankcs.com 2017-06-10 下午 9.23.57.png

词袋模型

虽然词袋模型只是所有词向量的某种平均,但其维度可以做到很低:

hankcs.com 2017-06-10 下午 9.25.16.png

为了抵抗词序丢失带来的语义丢失问题,可以用 ngram 特征来代替。

简单的线性模型

这并不是神经网络,因为从输入到隐藏层只是一个 look-up table,而隐藏层到输出则是一个逻辑斯谛回归线性分类器。

hankcs.com 2017-06-10 下午 9.28.56.png

训练

用交叉熵作为损失函数:

hankcs.com 2017-06-10 下午 9.29.57.png

Hierarchical softmax

与其用一个超大的 softmax 层,不如用多个 Hierarchical softmax:

hankcs.com 2017-06-10 下午 9.31.34.png

类似于www.hankcs.com/nlp/word2vec.html#h2-3 ,可以提高效率。

效果与速度

效果与最好的神经网络模型相差无几,但训练速度非常快:

hankcs.com 2017-06-10 下午 9.34.41.png

总结

  • fastText 常常可以跟深度神经网络分类器打平。

  • 但训练速度只需几秒,而不是几天。

  • 还可以学习多种语言的词向量(效果比 word2vec 还要好)。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 3 高效文本分类的锦囊妙计

CS224n 研究热点 4 词嵌入对传统方法的启发

hankcs.com 2017-06-15 下午 9.15.54.png

主讲人是一位发音特别纯正的印度小哥,只有微量口音。这篇论文挑战了对神经网络的迷信,展示了传统模型的生命力以及调参的重要性。

词语表示方法

以前的课程中讲过两大类得到 dense 词语表示的方法,一般认为 NN 模型更好:

hankcs.com 2017-06-15 下午 9.17.09.png

这里的 PPMI 也是一种利用共现矩阵的方法。

但 Levy 指出,超参数和实现细节比算法本身更重要:

hankcs.com 2017-06-15 下午 9.18.33.png

Skip-Gram 中的超参数

有负例的采样个数,负采样算法中的平滑指数:

hankcs.com 2017-06-15 下午 9.20.31.png

对 PMI 的启发

PMI 中也有个类似负采样中的平滑指数的超参数:

hankcs.com 2017-06-15 下午 9.28.49.png

试验证明,取恰好能得到最好的效果。

另外在一种叫 Shifted PMI 的变种中,也有类似于负采样个数的超参数:

hankcs.com 2017-06-15 下午 9.30.07.png

可调超参数一览表

这些方法中,存在大量的超参数可供折腾:

hankcs.com 2017-06-15 下午 9.31.59.png

调参结果

将每种方法能调的超参数调到最佳,得到如下结果:

hankcs.com 2017-06-15 下午 9.32.23.png

结果表示,没有性能稳定的方法,时而是 count-based 方法胜出,时而是 NN 方法胜出。

结论

  • 这篇文章挑战了人们对 NN 模型的迷信,展示了 NN 模型并不一定比传统模型好。

  • 虽然模型设计很重要,要想拿到好的效果,调参也非常非常重要。

  • 不要迷信,要勇于挑战流行的论点(小哥认为词的向量表示领域还有很大的探索空间)。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 4 词嵌入对传统方法的启发

CS224n 研究热点 5 图像对话

术语 Visual Dialog,大致这么翻译(通俗理解为斗图)。你可以给聊天机器人发送图片,它能理解图片的意思,你们可以就图片内容展开对话;除了文本之外,让图片也成为交流的一部分;这可以帮助盲人理解周围或社交媒体上的信息。

这是自然语言处理与计算机视觉的交叉应用。

hankcs.com 2017-06-21 下午 1.54.35.png

相关工作

已经有很多工作在为图像和文本搭建桥梁了。

图像视频自动标题

hankcs.com 2017-06-21 下午 1.57.00.png

虽然效果有限,比如右边这只猫实际上在与一只貂戏耍,而不是一个玩具。

图像语义对齐

也就是将一个图片中的各个物体自动框出来,自动输出物体之间的语义联系:

hankcs.com 2017-06-21 下午 2.01.15.png

图像 QA

这种应用已经快要接近图像对话了,只不过 QA 系统一般是独立的一问一答,而且机器给出的答案一般很短:

hankcs.com 2017-06-21 下午 2.04.02.png

贡献

这篇文章提出了这么一种新的任务:图像对话。

然后开发了一个双人对话协议,整理了一个数据库。

介绍了一系列用于图像对话的神经网络模型。

技术细节

用于处理图像的 CNN,用于处理问题的 LSTM,和另一个用于处理历史对话内容的 LSTM:

hankcs.com 2017-06-21 下午 2.09.47.png

也没讲细节的东西。

数据集

数据集如下图左边所示:

hankcs.com 2017-06-21 下午 2.11.47.png

hankcs.com 2017-06-21 下午 2.11.44.png

右边是与 QA 数据集的对比,可见比起 QA 数据集,VD 的答案所含词语数更多,没有唯一答案的问题更多。

结果

有一些量化结果(但是没讲,应该是前多少个答案的准确率之类的标准):

hankcs.com 2017-06-21 下午 2.15.05.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 5 图像对话

CS224n 研究热点 6 基于转移的神经网络句法分析的结构化训练

介绍了依存句法分析最近的 3 次突破性工作。

什么是 SyntaxNet

这是谷歌在 16 年开源的“世界上最精准的开源句法分析器”,它基于 TF 来做句法分析,现在支持 40 种语言(不包含简体中文)。

这项工作在 Chen & Manning 的开山之作、Weiss et al 的工作上面做了一些改进:

hankcs.com 2017-06-22 下午 3.38.34.png

3 项贡献

  1. 利用未标注数据——“Tri-Training”

  2. 神经网络调参

  3. 最上层加了一层:结构化感知机与柱搜索

1 Tri-Training:利用未标注数据

其实就是两个模型分析同一个未标注句子,如果一致,则作为训练样本加入训练集中。利用得到的更大的训练集训练更精准的模型。

hankcs.com 2017-06-22 下午 3.44.53.png

2 模型改进

hankcs.com 2017-06-22 下午 3.47.37.png

在经典模型上做了如下三个改动:

  1. 加了一个隐藏层

  2. 激活函数(记得是立方函数)改为 ReLU

  3. 最上层加了一层感知机

注意上图中感知机的输入来自从前面第一层隐藏层开始的每一层的输出。

3 结构化感知机训练与柱搜索

要解决的问题是:之前转移动作是贪心的决策,无法根据接下来的动作做全局(或较为全局)的决策,也无法撤销错误的决策。

解决办法:考虑接下来的决策,搜索整棵转移树:

hankcs.com 2017-06-22 下午 5.19.31.png

具体做法是记录个得分最高的长度为的预测序列:

hankcs.com 2017-06-22 下午 5.20.51.png

结论

hankcs.com 2017-06-22 下午 5.47.07.png

用上了这 3 种改进之后,提高了 1 个百分点,然后 SyntaxNet 用 Global Normalization 替换了 Local Normalization 以解决标记偏置问题,这带来了 0.6 个百分点的提升。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 6 基于转移的神经网络句法分析的结构化训练

CS224n 研究热点 7 迈向更好的语言模型

hankcs.com 2017-06-23 上午 11.29.06.png

我们已经知道一些手段来改进语言模型,比如:

  1. 更好的输入:词→词根→字符

  2. 更好的正则化/预处理

  3. 这些手段综合起来得到了更好的语言模型

更好的输入

文本的多种颗粒度:

hankcs.com 2017-06-23 下午 2.36.49.png

更细的颗粒度相当于减小了词表,让模型更容易做对选择。试验表明的确降低了 error:

hankcs.com 2017-06-23 下午 2.39.19.png

更好的正则化和预处理

正则化就不说了。

预处理指的是,随机地将句子中的一些单词替换成另外的单词(比如把一个地名替换为另一个),或者使用 BiGram 统计信息来生成替换。

这样会得到一个更加平滑的分布,高频词将一些出场机会匀给了低频词。

hankcs.com 2017-06-23 下午 2.51.03.png

对错误率的降低效果如下(左边是正则化,右边是预处理):

hankcs.com 2017-06-23 下午 2.53.41.png

更好的模型?

hankcs.com 2017-06-23 下午 3.09.10.png

Noise Contrastive Estimation(NCE)

与其用昂贵的交叉熵损失函数,不如用一种叫 NCE 损失的近似,理论证明当 k 值足够大时,两者梯度是接近的。

更大的 LSTM 单元数

LSTM 单元数增加到 1024,k 值越大越好,直到吃满 GPU 显存。

用上了这些种种改进之后,总算是拿到如下成绩:

hankcs.com 2017-06-23 下午 3.08.50.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 7 迈向更好的语言模型

CS224n 研究热点 8 谷歌的多语种神经网络翻译系统

hankcs.com 2017-06-27 下午 4.51.19.png

双语 NMT

一般“瘦弱”的 NMT 系统只支持双语单向翻译,比如课上常见的这种:

hankcs.com 2017-06-27 下午 4.54.35.png

如果想实现一个模型支持多语种互译怎么办呢?

土办法

之前的尝试是使用多对 encoder-decoder pair、一个 encoder 多个 decoder、多个 encoder 一个 decoder:

hankcs.com 2017-06-27 下午 4.59.24.png

Google 的多语种 NMT 系统

hankcs.com 2017-06-27 下午 5.03.45.png

谷歌的 NMT 系统有什么不同呢?

  • 单个统一的模型

  • 通过对其他语种的学习可以大幅提升语料匮乏的语种的翻译效果

  • 可以直接翻译训练语料中不存在的语种组合方式(通过葡萄牙语->芬兰语、芬兰语->英语学习葡萄牙语->英语的翻译)

架构

hankcs.com 2017-06-27 下午 5.07.36.png

他们使用的架构与常见的 encoder-decoder 没有什么不同,其中 attention 会在接下来的课程中介绍。

聪明之处在于不修改网络架构,而是在输入数据上做手脚。

只需在输入平行语料 pair 中人工加入目标语种的标示符,就可以把所有语种的平行语料混合在一起训练了:

hankcs.com 2017-06-27 下午 5.09.49.png

真聪明。

效果

hankcs.com 2017-06-27 下午 5.11.44.png

Zero-Shot Translation

hankcs.com 2017-06-27 下午 5.16.13.png

啰嗦一下 Zero-Shot Translation 的细节。

在训练的时候只使用葡萄牙语到英语、英语到西班牙语的语料,就可以让模型学会葡萄牙语到西班牙语的翻译。而且效果可以与 phrase-based MT 相比。如果再加一点葡萄牙语到西班牙语的语料,该系统就可以击败上述所有模型。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 8 谷歌的多语种神经网络翻译系统

CS224n 研究热点 9 读唇术

hankcs.com 2017-06-30 下午 3.14.22.png

唇语翻译

将视频处理为以嘴唇为中心的图片序列,给或不给语音,预测正在讲的话。

hankcs.com 2017-06-30 下午 3.16.19.png

这些数据可能来自新闻直播:

hankcs.com 2017-06-30 下午 3.16.41.png

动画演示:

这里唇语和语音的识别、卡拉 OK 效果式的对齐,都是模型自动完成的。

架构

hankcs.com 2017-06-30 下午 3.40.00.png

视觉和听觉两个模块或者混合交火或者单独使用,每次输出一个字母。

视觉

取嘴唇时序上的 sliding window,先喂给 CNN,再喂给 LSTM,生成一个 output vector

hankcs.com 2017-06-30 下午 3.42.20.png

听觉

类似地,取音频上的窗口分片:

hankcs.com 2017-06-30 下午 3.44.03.png

Attention 与 Spell

将上述两个 LSTM 输出的 output states 送入一个带两个 attention 拓展的 LSTM:

hankcs.com 2017-06-30 下午 3.48.34.png

Curriculum Learning

hankcs.com 2017-06-30 下午 3.52.37.png

通常训练 seq2seq 模型时喂进去的是完整的句子,但 Curriculum Learning 的手法是,一次只喂几个单词,逐步增长。这样可以加快收敛速度,降低过拟合。

Scheduled Sampling

hankcs.com 2017-06-30 下午 5.03.54.png

通常训练递归模型的时候,使用的是前一个时刻的“标准答案”的 one-hot 向量,而这里根据前一个时刻的预测结果采样,让训练和测试统一起来。

数据集

hankcs.com 2017-06-30 下午 5.06.20.png

来自 BBC 新闻的五千个小时的视频,对齐字幕,做了嘴唇位置等预处理。

结果

hankcs.com 2017-06-30 下午 5.11.40.png

有趣之处在于,他们将模型效果与专业做唇语翻译的公司做了对比,发现比专业人士还要准,而且错误率低了 20 个百分点。(竟然还有公司专门做这个)

在同时输入语音和唇语的情况下,错误可以做到更低。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 9 读唇术

CS224n 研究热点 10 Character-Aware 神经网络语言模型

hankcs.com 2017-07-04 下午 1.10.49.png

动机

大多数神经网络语言模型其实并没有注意到结构类似的词语意义也类似这种语言现象,这使它们无法赋予低频词合适的表示。所以这个新模型的目标是:

  • 编码词素相关性:eventful, eventfully, uneventful

  • 解决低频词问题

  • 用更少的参数得到 comparable 效果

架构

输入字符,但依然在词语级别做预测。

hankcs.com 2017-07-04 上午 10.23.35.png

卷积层

读入字符的 embedding,做不同大小的卷积(捕捉不同的 ngram 与 subword),max 池化后得到特征向量。

hankcs.com 2017-07-04 上午 10.26.43.png

Highway Network

这种网络类似 LSTM 的 Memory Cell,对输入做一些变换,但同时保留原始信息。

hankcs.com 2017-07-04 上午 10.29.33.png

LSTM

将 Highway Network 的输出喂给 LSTM,应该类似 CBOW 预测中间词语:

hankcs.com 2017-07-04 上午 10.38.25.png

量化结果

与最佳结果非常接近,但参数更少:

hankcs.com 2017-07-04 上午 10.39.38.png

直观效果

可以捕捉构词类似的词语:

hankcs.com 2017-07-04 上午 11.26.16.png

比如 Richard 这个名字含有 rich,hard,ar 等词素,在 highway 之前能关联到 hard、rich 这些词语上;highway 之后能关联到 Eduard、Carl 之类的名字上。不仅名字对名字,连发音、性别都有更大的相似性。

对于 OOV 的效果也是惊人地好:

hankcs.com 2017-07-04 上午 11.27.05.png

结论

字符级别的 CNN+Highway Network 可以提取丰富的语义和结构信息。这个模型将其他网络当成积木一样,构建了更好的语言模型。

实现

github.com/mkroutikov/tf-lstm-char-cnn

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 10 Character-Aware 神经网络语言模型

CS224n 研究热点 11 深度强化学习用于对话生成

hankcs.com 2017-07-07 下午 8.14.27.png

这篇论文研究如何训练聊天机器人进行有意义的对话,常规方法是 seq2seq:

hankcs.com 2017-07-07 下午 8.19.12.png

与上几次课讲的机器翻译框架相同,encoder 与 decoder,相同的极大似然估计目标函数。

seq2seq 的缺陷

通过该方法训练出来的聊天机器人容易陷入死循环,比如让两个机器人聊天,本来聊得好好的:

2017-07-07_20-22-52.png

但一两句话之后就会重复相同的内容:

2017-07-07_20-23-52.png

其问题在于,红方“ I’m 16”缺乏指导性,没有给蓝方继续对话的话题。而“ I don’t know what you’re talking about”又是个太宽泛的大废话。虽然这些对话看起来似乎很自然,但没有内容在里面。

也就是说,一个概率大的回复并不等于好的回复,比如“我不知道你在讲什么”虽然可以应付所有的问题,在 MLE 看来概率特别高,但就是没有意义。

如何定义好的回复

一个好的回复应该是内容丰满、容易回答、又不重复的。论文作者据此编写了打分函数:

2017-07-07_20-32-15.png

强化学习

有了打分函数,也就有了奖励机制。考虑到对话其实是连续的行为,所以轮到强化学习登场了。

强化学习不再从例子中直接学习生成最大概率的回复,而是根据激励函数的指导尝试生成最大得分的回复。第一步依然是 encoder:

2017-07-07_20-36-10.png

第二步就不再是传入某个人类对话样本指导它学习语言模型之类,而是让它自顾自地生成一个回复。

2017-07-07_20-42-33.png

第三步用激励函数给模型生成的回复打分,让它知道自己做得怎么样。

量化结果

数值结果是人工评判的,这些数值都是正数,说明强化学习得到的对话更好:

2017-07-07_20-46-18.png

直观效果

回到开头的例子,现在模型能够进行更充实的对话了:

2017-07-07_20-47-49.png

虽然几句话过后又开始死循环:

2017-07-07_20-48-29.png

嘛,就算是人,尬聊一会儿也会无话可说吧。另外,这里衡量回复质量的打分函数也太主观了吧。

结论

  • 强化学习可以让模型超越模仿人类标注,做一些有意义的事情。

  • NLP 领域还有更多应用了 RL 的任务,如信息抽取、QA、task-oriented 对话、指代相消、等等。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 11 深度强化学习用于对话生成

CS224n 研究热点 12 神经网络自动代码摘要

hankcs.com 2017-07-11 上午 11.16.11.png

任务与数据集

自动生成 C#和 SQL 代码的文档描述,数据集整理自 StackOverflow 上的提问。

hankcs.com 2017-07-11 上午 11.34.30.png

子任务

hankcs.com 2017-07-11 上午 11.36.46.png

根据代码生成摘要,或根据问题检索代码。

网络架构

hankcs.com 2017-07-11 上午 11.42.19.png

一个 LSTM 处理已生成的文本 token,一个在整个代码 token 序列上做 attention 的单元,两者的输出求和,送入激活;得到 ngram 语言模型,预测下一个词语。

结果

量化评测

hankcs.com 2017-07-11 上午 11.46.04.png

文本生成评测用到了 BLEU 等自动方法,以及人工评分,检索用到了 MRR,结果都比以前的工作要好。

直观效果

hankcs.com 2017-07-11 上午 11.47.56.png

该模型知道代码在操作 TreeView,但没有捕捉到“add”这个动作。

hankcs.com 2017-07-11 上午 11.50.04.png

对这段 SQL 代码的理解,模型是完全正确的。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 12 神经网络自动代码摘要

CS224n 研究热点 13 学习代码的语义

hankcs.com 2017-07-12 下午 2.51.14.png

在培训码农的时候,教师需要给学生批改代码、写反馈。学生太多时成为机械劳动,这篇论文研究自动编码程序的语义表示。数据集来自斯坦福开发的模拟操控机器人的语言。

表示代码

hankcs.com 2017-07-12 下午 3.29.57.png

希望得到代码的简洁表示、运行后会得到什么结果,哪怕它无法编译运行。

这门课上讲了很多编码表示句子的方法,RNN/CNN 等,是否可以活用到代码上?

hankcs.com 2017-07-12 下午 3.32.15.png

编码解码状态

定义机器当前所处的状态为 precondition(P),代码执行后的状态为 postcondition(Q)。如果将代码每行下个断点,就会得到 precondition 与 postcondition 的 pair 序列。对此建模:

hankcs.com 2017-07-12 下午 3.38.22.png

中间的矩阵是待学习的 embedding,期待它能捕捉到程序代码的语义。

目标函数

hankcs.com 2017-07-12 下午 3.41.18.png

第一项衡量模型的预测能力,第二项希望模型能从 post-condition 重建 pre-condition。

利用 RecursiveNN 来生成程序 embeddings

hankcs.com 2017-07-12 下午 3.46.44.png

对于复杂的循环,则无法用单个语句的 embedding 表示。程序语言已经有确定的语法树,刚好拿过来喂给 tree structure 的 RNN。

总结

利用 recursive neural nets 可以通过解决从 precondition 到 postcondition 的映射问题,来自动编码程序的语义。通过得到的代码表示,可以用于将学生聚类;于是可以给同一类别的学生相同的反馈,还可以追踪学生多次提交代码的进步。

未来工作

还有一些应用研究,预测某次代码提交后应该给提示还是教学视频或更多练习等等,甚至预测学生未来的成绩(让一些人早日从入门到放弃?)

hankcs.com 2017-07-12 下午 3.53.56.png

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 13 学习代码的语义

CS224n 研究热点 14 自动组合神经网络做问答系统

hankcs.com 2017-07-13 下午 4.29.41.png

这是自我组装推断的 QA,可接受多种知识,包括图片和结构化知识库。问答具有复合性,很早就有人引入句法分析判断究竟在问什么,甚至脑洞大开想做自然语言编译器。但他们总是脱离不了手写规则的思维,白白糟蹋了性能卓越的句法分析器。而该模型自动组装多个神经网络用于逻辑推断,拿到了显著的好成绩。

四个 Jointly 训练的组件

hankcs.com 2017-07-13 下午 4.34.11.png

利用这四个组件可以组装分析问题的逻辑流:

hankcs.com 2017-07-13 下午 4.36.09.png

目标是训练模型自动分析 query,组装逻辑组件。

模型:在两个分布上构建

hankcs.com 2017-07-13 下午 4.40.44.png

一个 Layout Model,选择问题的 layout(应该是逻辑语句的“语法树”)。一个 Execution Model,在 world representation(应该理解为 fact 的表示)上执行 layout。

Layout Model

这个模型的训练有 3 步,首先将输入句子解析为依存句法树:

hankcs.com 2017-07-13 下午 4.42.18.png

第二步,将句法树的片段分配给合适的逻辑组件:

hankcs.com 2017-07-13 下午 4.43.57.png

最后,将逻辑片段组装为完整的 layout:

hankcs.com 2017-07-13 下午 4.45.11.png

这个 layout 的 root 是 and 逻辑,每个句子可能有多个 layout,接下来介绍如何为 layout 打分

Layout Scoring Model

得到问题的 LSTM 表示,以及特征表示,将两个表示传入多层感知机。每个时刻的梯度是 layout 的 log-probability 乘以该 layout 预测的准确率的梯度。

Execution Model

在知识库上面执行逻辑查询,输入结构化知识库中某种实体的所有 representation,流入逻辑树输出每个备选答案的分值,取最大的那个。

hankcs.com 2017-07-13 下午 4.55.03.png

Module: lookup

就是 lookup table,去结构化知识库(数据库)取数据,查出实体的向量表示:

hankcs.com 2017-07-13 下午 5.50.17.png

“把全部 attention 放到第个元素上”,真是清丽脱俗的说法呢。

Module: relate

hankcs.com 2017-07-13 下午 5.57.02.png

将 attention 从输入的一部分导向另一部分,条件中含有当前的 attention。公式没展开讲,详细看论文吧。

Module: find

hankcs.com 2017-07-13 下午 6.00.53.png

也是把输入的特征拼接起来往多层感知机里面过一下然后 softmax。这里的输入应当是遍历所有单词。

Module: and

有点像集合运算中的交集,只不过是在多个 attention 上做的乘法:

hankcs.com 2017-07-13 下午 6.03.13.png

训练 Execution Model

目标函数是给定 world representation 和 layout 下正确答案的最大似然:

hankcs.com 2017-07-13 下午 6.05.40.png

结果

VQA

直观效果不错:

hankcs.com 2017-07-13 下午 6.06.56.png

也拿到了最高分数:

hankcs.com 2017-07-13 下午 6.07.42.png

GeoQA

在这个领域知识库上表现也很出色:

hankcs.com 2017-07-13 下午 6.09.43.png

虽然在结构化知识库的手写特征利用上没有免俗,但总算自动化了“推断”这个被丑陋规则统治的部分。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 14 自动组合神经网络做问答系统

CS224n 研究热点 15 Neural Turing Machines

dnc.jpg    hankcs.com 2017-07-14 上午 11.43.28.png

这次讲座覆盖了来自 DeepMind 的两篇论文:

hankcs.com 2017-07-14 上午 11.44.00.png

第二篇是第一篇的轻微改进,这里只关注抽象思想。

问题

hankcs.com 2017-07-14 上午 11.49.57.png

目前的神经网络擅长模式识别和动态决策,但无法使用知识进行深思或推断。比如明明可以胜任电子游戏这么复杂的问题,却无法完成最短路径这样的简单问题。

任何 DFS 算法变种都要储存当前访问过的节点,是否神经网络的问题出在记忆(或说内存)上呢?

记忆是解决方案吗

可是 LSTM 不是已经有 Memory 吗?事实上那只能叫 cache,不能叫 RAM。

Neural Turing Machines

这种模型引入了可长期持续、随机读写的 RAM:

hankcs.com 2017-07-14 上午 11.53.40.png

由 RNN 充当 controller 的角色:

hankcs.com 2017-07-14 上午 11.54.50.png

决定何时读写哪一部分:

hankcs.com 2017-07-14 上午 11.55.10.png

如何读写

如果你曾经上过计算机体系课,你就会明白内存是离散的结构,无法关于要读写的地址求导,自然无法利用反向传播优化。

解决方法是对所有地址进行不同的连续的程度读写,比如 attention 机制。

读内存

就是 attention 向量与 memory 矩阵的乘积:

hankcs.com 2017-07-14 上午 11.58.54.png

写内存

类似地,将要写入的值利用 attention 得到向量,写入内存的所有位置:

hankcs.com 2017-07-14 下午 12.01.19.png

这种更新也有点像 GRU。

attention 更新

如何得到正确的 attention 向量呢?

第一步

RNN controller 生成 query vector,与 memory 的每个地址做点积,得到一个强度向量,softmax 一下得到 attention 向量:

hankcs.com 2017-07-14 下午 12.05.21.png

第二步

与前一个 attention vector 做混合:

hankcs.com 2017-07-14 下午 12.05.52.png

第三步

shift filter(允许读取 attention 集中注意的地址的邻居)一下,sharpen(锐化,使强者越强)得到最终 attention 分布,用于读写:

hankcs.com 2017-07-14 下午 12.10.37.png

效果

可以在家谱知识库上做准确的推断:

References

deepmind.com/blog/differentiable-neural-computers/

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n 研究热点 15 Neural Turing Machines

CS224n Assignment 1

这是 LaTex 答案,Python 代码开源在GitHub上。先进行大量的 SG、CBOW、负采样、交叉熵损失函数推导和证明,理论基础扎实后平滑过渡到实现;在斯坦福情感树库上做情感分析、调参、分析混淆矩阵与错误。

以前看不懂公式,总觉得代码是最重要的;现在觉得代码无关紧要了。

另需注意,公式中的向量是列向量,而代码中的向量是行向量(效率更高)。

1:Softmax

a softmax 常数不变性

证明 softmax 不受输入的常数偏移影响,即

解答:

b Python 实现

利用上述的公式实现优化版的 softmax,要求既能处理向量,也能处理矩阵(视作多个不相干的行向量集合)。

def softmax(x):
    """Compute the softmax function for each row of the input x.
    It is crucial that this function is optimized for speed because
    it will be used frequently in later code. You might find numpy
    functions np.exp, np.sum, np.reshape, np.max, and numpy
    broadcasting useful for this task.
    Numpy broadcasting documentation:
    http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
    You should also make sure that your code works for a single
    N-dimensional vector (treat the vector as a single row) and
    for M x N matrices. This may be useful for testing later. Also,
    make sure that the dimensions of the output match the input.
    You must implement the optimization in problem 1(a) of the
    written assignment!
    Arguments:
    x -- A N dimensional vector or M x N dimensional numpy matrix.
    Return:
    x -- You are allowed to modify x in-place
    """
    orig_shape = x.shape
    if len(x.shape) > 1:
        # Matrix
        ### YOUR CODE HERE
        exp_minmax = lambda x: np.exp(x - np.max(x))
        denom = lambda x: 1.0 / np.sum(x)
        x = np.apply_along_axis(exp_minmax, 1, x)
        denominator = np.apply_along_axis(denom, 1, x)
        if len(denominator.shape) == 1:
            denominator = denominator.reshape((denominator.shape[0], 1))
        x = x * denominator
        ### END YOUR CODE
    else:
        # Vector
        ### YOUR CODE HERE
        x_max = np.max(x)
        x = x - x_max
        numerator = np.exp(x)
        denominator = 1.0 / np.sum(numerator)
        x = numerator.dot(denominator)
        ### END YOUR CODE
    assert x.shape == orig_shape
    return x

为什么要费力气减掉一个最大值常量呢?参考:《计算指数函数的和的对数》

2:神经网络基础

a sigmoid 梯度

很简单的推导:

b 交叉熵损失函数的梯度

请推导交叉熵损失函数关于 softmax 输入  的梯度,记假设函数为

交叉熵损失函数为:

其中是 one-hot 向量,是概率向量。

解答:

为 softmax 函数,引入下列记号:

这里的下标表示取标量分别求导,当时:

时:

接下来正式推导:

由于概率归一化:

c 推导三层网络的梯度

hankcs.com 2017-06-17 下午 7.37.15.png

损失函数,求 ,可使用的符号有:

d 参数数量

对于上述神经网络,一共有多少个参数。假设输入维度是,隐藏单元数为,输出维度为

解答:

e 实现 sigmoid 函数

太简单了:

def sigmoid(x):
    """
    Compute the sigmoid function for the input here.
    Arguments:
    x -- A scalar or numpy array.
    Return:
    s -- sigmoid(x)
    """
    ### YOUR CODE HERE
    s = 1.0 / (1 + np.exp(-x))
    ### END YOUR CODE
    return s
def sigmoid_grad(s):
    """
    Compute the gradient for the sigmoid function here. Note that
    for this implementation, the input s should be the sigmoid
    function value of your original input x.
    Arguments:
    s -- A scalar or numpy array.
    Return:
    ds -- Your computed gradient.
    """
    ### YOUR CODE HERE
    ds = s * (1 - s)
    ### END YOUR CODE
    return ds

f 实现梯度检查

关于梯度检查的原理参考 CS229:www.hankcs.com/ml/neural-networks-learning-cs229.html#h3-7

实现如下:

def gradcheck_naive(f, x):
    """ Gradient check for a function f.
    Arguments:
    f -- a function that takes a single argument and outputs the
         cost and its gradients
    x -- the point (numpy array) to check the gradient at
    """
    rndstate = random.getstate()
    random.setstate(rndstate)
    fx, grad = f(x) # Evaluate function value at original point
    h = 1e-4        # Do not change this!
    # Iterate over all indexes in x
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        ix = it.multi_index
        # Try modifying x[ix] with h defined above to compute
        # numerical gradients. Make sure you call random.setstate(rndstate)
        # before calling f(x) each time. This will make it possible
        # to test cost functions with built in randomness later.
        ### YOUR CODE HERE:
        x[ix] += h
        random.setstate(rndstate)
        new_f1 = f(x)[0]
        x[ix] -= 2*h
        random.setstate(rndstate)
        new_f2 = f(x)[0]
        x[ix] += h
        numgrad = (new_f1 - new_f2) / (2 * h)
        ### END YOUR CODE
        # Compare gradients
        reldiff = abs(numgrad - grad[ix]) / max(1, abs(numgrad), abs(grad[ix]))
        if reldiff > 1e-5:
            print "Gradient check failed."
            print "First gradient error found at index %s" % str(ix)
            print "Your gradient: %f \t Numerical gradient: %f" % (
                grad[ix], numgrad)
            return
        it.iternext() # Step to next dimension
    print "Gradient check passed!"

g 实现前向传播和反向传播

def forward_backward_prop(data, labels, params, dimensions):
    """
    Forward and backward propagation for a two-layer sigmoidal network
    Compute the forward propagation and for the cross entropy cost,
    and backward propagation for the gradients for all parameters.
    Arguments:
    data -- M x Dx matrix, where each row is a training example.
    labels -- M x Dy matrix, where each row is a one-hot vector.
    params -- Model parameters, these are unpacked for you.
    dimensions -- A tuple of input dimension, number of hidden units
                  and output dimension
    """
    ### Unpack network parameters (do not modify)
    ofs = 0
    Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])
    W1 = np.reshape(params[ofs:ofs+ Dx * H], (Dx, H))
    ofs += Dx * H
    b1 = np.reshape(params[ofs:ofs + H], (1, H))
    ofs += H
    W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
    ofs += H * Dy
    b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))
    ### YOUR CODE HERE: forward propagation
    h = sigmoid(np.dot(data,W1) + b1)
    yhat = softmax(np.dot(h,W2) + b2)
    ### END YOUR CODE
    ### YOUR CODE HERE: backward propagation
    cost = np.sum(-np.log(yhat[labels==1])) / data.shape[0]
    d3 = (yhat - labels) / data.shape[0]
    gradW2 = np.dot(h.T, d3)
    gradb2 = np.sum(d3,0,keepdims=True)
    dh = np.dot(d3,W2.T)
    grad_h = sigmoid_grad(h) * dh
    gradW1 = np.dot(data.T,grad_h)
    gradb1 = np.sum(grad_h,0)
    ### END YOUR CODE
    ### Stack gradients (do not modify)
    grad = np.concatenate((gradW1.flatten(), gradb1.flatten(),
        gradW2.flatten(), gradb2.flatten()))
    return cost, grad

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n Assignment 1

CS224n Assignment 2

01-unrolled_rnn.png先在 TensorFlow 上实现多项逻辑斯谛回归练练手,然后增加难度实现基于神经网络的转移依存句法分析,试验 Xavier 初始化、Dropout 和 Adam 优化器。最后推导 RNN 和语言模型的困惑度、梯度、反向传播和复杂度。

Python 代码开源在GitHub上。正式开始利用 TensorFlow 了,这里采用最新的 TensorFlow-r1.2 和 Python2.7。如果在安装或升级 TF 的过程中遇到权限问题,比如:

IOError: [Errno 13] Permission denied: '/usr/local/bin/markdown_py'

不如试试:

pip install tensorflow --user

或《从源码编译安装 TensorFlow》,自行编译安装得到的库效率更高。

一些 TensorFlow 函数假定输入为行向量,所以乘上权值矩阵的时候必须右乘()而不是左乘()。

1 Tensorflow Softmax

以交叉熵损失函数

实现线性分类器。要求使用 TensorFlow 的自动微分功能将模型拟合到数据。

a softmax

手写实现 softmax,不要用直接 tf.nn.softmax。如果输入是矩阵,则视作不相干的行向量集合。

def softmax(x):
    """
    Compute the softmax function in tensorflow.
    You might find the tensorflow functions tf.exp, tf.reduce_max,
    tf.reduce_sum, tf.expand_dims useful. (Many solutions are possible, so you may
    not need to use all of these functions). Recall also that many common
    tensorflow operations are sugared (e.g. x * y does a tensor multiplication
    if x and y are both tensors). Make sure to implement the numerical stability
    fixes as in the previous homework!
    Args:
        x:   tf.Tensor with shape (n_samples, n_features). Note feature vectors are
                  represented by row-vectors. (For simplicity, no need to handle 1-d
                  input as in the previous homework)
    Returns:
        out: tf.Tensor with shape (n_sample, n_features). You need to construct this
                  tensor in this problem.
    """
    ### YOUR CODE HERE
    x_max = tf.reduce_max(x,1,keep_dims=True)          # find row-wise maximums
    x_sub = tf.sub(x,x_max)                            # subtract maximums
    x_exp = tf.exp(x_sub)                              # exponentiation
    sum_exp = tf.reduce_sum(x_exp,1,keep_dims=True)    # row-wise sums
    out = tf.div(x_exp,sum_exp)                        # divide
    ### END YOUR CODE
    return out

b 交叉熵

def cross_entropy_loss(y, yhat):
    """
    Compute the cross entropy loss in tensorflow.
    The loss should be summed over the current minibatch.
    y is a one-hot tensor of shape (n_samples, n_classes) and yhat is a tensor
    of shape (n_samples, n_classes). y should be of dtype tf.int32, and yhat should
    be of dtype tf.float32.
    The functions tf.to_float, tf.reduce_sum, and tf.log might prove useful. (Many
    solutions are possible, so you may not need to use all of these functions).
    Note: You are NOT allowed to use the tensorflow built-in cross-entropy
                functions.
    Args:
        y:    tf.Tensor with shape (n_samples, n_classes). One-hot encoded.
        yhat: tf.Tensorwith shape (n_sample, n_classes). Each row encodes a
                    probability distribution and should sum to 1.
    Returns:
        out:  tf.Tensor with shape (1,) (Scalar output). You need to construct this
                    tensor in the problem.
    """
    ### YOUR CODE HERE
    l_yhat = tf.log(yhat)                           # log yhat
    product = tf.mul(tf.to_float(y), l_yhat)        # multiply element-wise
    out = tf.neg(tf.reduce_sum(product))            # negative summation to scalar
    ### END YOUR CODE
    return out

c Placeholders & Feed Dictionaries

assignment2/model.py 这个抽象层写得还算挺优美的。

d Softmax & CE Loss

def add_prediction_op(self):
    """Adds the core transformation for this model which transforms a batch of input
    data into a batch of predictions. In this case, the transformation is a linear layer plus a
    softmax transformation:
    y = softmax(Wx + b)
    Hint: Make sure to create tf.Variables as needed.
    Hint: For this simple use-case, it's sufficient to initialize both weights W
                and biases b with zeros.
    Args:
        input_data: A tensor of shape (batch_size, n_features).
    Returns:
        pred: A tensor of shape (batch_size, n_classes)
    """
    ### YOUR CODE HERE
    with tf.variable_scope("transformation"):
        bias = tf.Variable(tf.random_uniform([self.config.n_classes]))
        W = tf.Variable(tf.random_uniform([self.config.n_features, self.config.n_classes]))
        z = tf.matmul(self.input_placeholder, W) + bias
    pred = softmax(z)
    ### END YOUR CODE
    return pred
def add_loss_op(self, pred):
    """Adds cross_entropy_loss ops to the computational graph.
    Hint: Use the cross_entropy_loss function we defined. This should be a very
                short function.
    Args:
        pred: A tensor of shape (batch_size, n_classes)
    Returns:
        loss: A 0-d tensor (scalar)
    """
    ### YOUR CODE HERE
    loss = cross_entropy_loss(self.labels_placeholder, pred)
    ### END YOUR CODE
    return loss

e Training Optimizer

def add_training_op(self, loss):
    """Sets up the training Ops.
    Creates an optimizer and applies the gradients to all trainable variables.
    The Op returned by this function is what must be passed to the
    `sess.run()` call to cause the model to train. See
    https://www.tensorflow.org/versions/r0.7/api_docs/python/train.html#Optimizer
    for more information.
    Hint: Use tf.train.GradientDescentOptimizer to get an optimizer object.
                Calling optimizer.minimize() will return a train_op object.
    Args:
        loss: Loss tensor, from cross_entropy_loss.
    Returns:
        train_op: The Op for training.
    """
    ### YOUR CODE HERE
    train_op = tf.train.GradientDescentOptimizer(self.config.lr).minimize(loss)
    ### END YOUR CODE
    return train_op

TF 会自动求偏导数,GradientDescentOptimizer 负责更新参数。

运行后输出:

Epoch 47: loss = 0.45 (0.007 sec)
Epoch 48: loss = 0.44 (0.007 sec)
Epoch 49: loss = 0.43 (0.007 sec)
Basic (non-exhaustive) classifier tests pass

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n Assignment 2

CS224n Assignment 3

hankcs.com 2017-07-02 下午 7.41.06.pngq3-0-dynamics.pngq3-clip-gru.png命名实体识别任务,先实现基于窗口的基线模型,然后进阶到 RNN 和 GRU。中间利用对自动机的模拟和推导展示 RNN 的缺点,演示梯度剪裁的作用。这是 Latex 解答,代码已提交,最后还有一个彩蛋。

命名实体识别初步

定位命名实体并将其分类到:

  • 人名 PER

  • 组织名 ORG

  • 地名 LOC

  • 其他 MISC

加上非命名实体 O 一共 5 类。连续的标注视为同一个实体。比如样本与标注以及预测结果

hankcs.com 2017-07-02 上午 11.07.41.png

系统一共识别出 3 个命名实体,在 token 级别和 entity 级别各有评测方法。

token 级别

  • P 值为预测出的正确非 O 标签比上预测出的全部非 O 标签,于是

  • R 值为预测出的正确非 O 标签比上正确答案的全部非 O 标签,于是

  • 值是两者的调和平均:

entity 级别

  • P 值为完美(不残缺不多余)识别的实体数量比上预测出的所有实体数量,于是

  • R 值为完美识别的实体数量比上正确答案中的实体数量,于是

  • 值是两者的调和平均:

1 A window into NER

基线模型使用半径窗口中的特征预测 :

hankcs.com 2017-07-02 上午 11.20.37.png

模型为一个以 ReLU 为激活函数的隐藏层的神经网络,输出层为 softmax,损失函数为交叉熵:

其中是词嵌入, 维度为 维度 , 是词表大小, 是词嵌入维度,是隐藏层维度,是分类数目(此处为 5)。

a 概念

请列举有歧义的命名实体?

太多了:

方地/nr, 的/ude1, 茶/n, 喝/vg, 个/q, 一罐/mq

baoji.png

一个亿.jpg

通常命名实体中含有低频词,为了泛化必须引入除了字符之外的特征,比如词性。这次作业为了简单,只使用字符特征。

b 维度和复杂度

如果窗口大小为,则窗口特征的维度是的行向量。的矩阵。的矩阵。

对长的句子来讲,计算复杂度是,这是因为从输入到隐藏层是计算瓶颈。

c 实现基线模型

就贴个最重要的 predict 方法吧:

def add_prediction_op(self):
    """Adds the 1-hidden-layer NN:
        h = Relu(xW + b1)
        h_drop = Dropout(h, dropout_rate)
        pred = h_dropU + b2
    Recall that we are not applying a softmax to pred. The softmax will instead be done in
    the add_loss_op function, which improves efficiency because we can use
    tf.nn.softmax_cross_entropy_with_logits
    When creating a new variable, use the tf.get_variable function
    because it lets us specify an initializer.
    Use tf.contrib.layers.xavier_initializer to initialize matrices.
    This is TensorFlow's implementation of the Xavier initialization
    trick we used in last assignment.
    Note: tf.nn.dropout takes the keep probability (1 - p_drop) as an argument.
        The keep probability should be set to the value of dropout_rate.
    Returns:
        pred: tf.Tensor of shape (batch_size, n_classes)
    """
    x = self.add_embedding()
    dropout_rate = self.dropout_placeholder
    ### YOUR CODE HERE (~10-20 lines)
    b1 = tf.get_variable(name='b1', shape = [self.config.hidden_size,], \
                         initializer=tf.contrib.layers.xavier_initializer(seed=1))
    b2 = tf.get_variable(name='b2', shape = [self.config.n_classes], \
                         initializer=tf.contrib.layers.xavier_initializer(seed=2))
    W = tf.get_variable(name='W', shape = [self.config.n_window_features * self.config.embed_size, self.config.hidden_size], \
                        initializer=tf.contrib.layers.xavier_initializer(seed=3))
    U = tf.get_variable(name='U', shape = [self.config.hidden_size, self.config.n_classes], \
                        initializer=tf.contrib.layers.xavier_initializer(seed=4))
    z1 = tf.matmul(x,W) + b1
    h = tf.nn.relu(z1)
    h_drop = tf.nn.dropout(h, dropout_rate)
    pred = tf.matmul(h_drop,U) + b2
    ### END YOUR CODE
    return pred

d 分析结果

DEBUG:Token-level confusion matrix:
go\gu   PER     ORG     LOC     MISC    O    
PER     2968    26      84      16      55   
ORG     147     1621    131     65      128  
LOC     48      88      1896    26      36   
MISC    37      40      54      1030    107  
O       42      46      18      39      42614
DEBUG:Token-level scores:
label   acc     prec    rec     f1   
PER     0.99    0.92    0.94    0.93 
ORG     0.99    0.89    0.77    0.83 
LOC     0.99    0.87    0.91    0.89 
MISC    0.99    0.88    0.81    0.84 
O       0.99    0.99    1.00    0.99 
micro   0.99    0.98    0.98    0.98 
macro   0.99    0.91    0.89    0.90 
not-O   0.99    0.89    0.87    0.88 
INFO:Entity level P/R/F1: 0.82/0.85/0.84

最拖后腿的是机构名识别,经常误识别为人名或非 NER。

由于窗口的限制,模型不擅长做完整连续的识别,如果增大窗口则会有所进步。

窗口=2

DEBUG:Token-level confusion matrix:
go\gu   PER     ORG     LOC     MISC    O    
PER     2995    23      50      10      71   
ORG     148     1679    96      52      117  
LOC     54      65      1910    24      41   
MISC    40      51      48      1029    100  
O       34      42      25      29      42629
DEBUG:Token-level scores:
label   acc     prec    rec     f1   
PER     0.99    0.92    0.95    0.93 
ORG     0.99    0.90    0.80    0.85 
LOC     0.99    0.90    0.91    0.90 
MISC    0.99    0.90    0.81    0.85 
O       0.99    0.99    1.00    0.99 
micro   0.99    0.98    0.98    0.98 
macro   0.99    0.92    0.89    0.91 
not-O   0.99    0.91    0.88    0.90 
INFO:Entity level P/R/F1: 0.85/0.87/0.86

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n Assignment 3

posted @ 2026-03-26 13:14  布客飞龙IV  阅读(4)  评论(0)    收藏  举报