《Measuring Word Significance using Distributed Representations of Words》论文解读

这篇论文严格来说是一个实验报告(report),作者分析了使用word2vec训练得到的词向量的特点,提出使用词频和词向量的模长来衡量词的重要性。 整篇论文的核心就是上面这张图。作者将arXiv上理论高能物理范围内的论文都下载下来,提取所有论文摘要,并使用word2vec默认参数进行训练,得到所有词的词向量。使用词向量的模长和词频绘制了上图。 由图可知,当词频小于30时,随着词频的增大,词向量的模长也增加;但当词频大于30后,词频继续增大时,词向量的模长呈减小趋势。作者分析发现,对于词频比较小的词,这些词所在的上下文相对固定,而word2vec正是通过词的上下文来学习词向量的,因此在word2vec训练的时候,这些词的词向量的更新方向相对固定,所以随着词频的增大,这些词的词向量在某个固定方向走得越远,故向量模长越大。但是对于词频很大的词,这些词很可能是多义词(比如may即可以做名词也可以做助动词),则在word2vec训练的时候,词向量会频繁往不同方向上更新,虽然词频很大更新了很多步,但由于分散在了多个不同方向上,故离初始点的距离并不远,即模长并不长。常见的停用词就是后者的典型代表。 因此,作者提出同时使用向量模长和词频来衡量词的重要性,如果这两个值都很大,则说明这个词很重要,而且很可能是某个子领域的专用词,只出现在特定的上下文中,类似于IDF很大。

March 13, 2022 · 1 min

和我一起构建搜索引擎(八)更新爬虫&修改打分&线上部署

时隔多年,我发现我写的和我一起构建搜索引擎系列是我的博客中访问量最高的内容,截至目前该搜索引擎的GitHub项目已经收获了上百个Star和Fork。这几天我又回顾了一下这个项目,并进行了部分的更新和修改,以及将该搜索引擎部署到了我的个人网站上,以便展示和外网访问。以下是我将搜索引擎部署到个人网站上的截图,大家可以访问 http://news.bitjoy.net/ 进行测试。 下面分别介绍新增的爬虫,对打分的修改和部署到服务器上的过程。 新增爬虫。我们之前是抓取搜狐新闻的数据,但是这个页面自从2018年之后就没再更新过,难道是被很多爬虫爬了之后停更了吗。经过多方查找,我发现中国新闻网的滚动新闻上的新闻数据特别全,从2008年至今,每天的新闻都有,而且新闻类型丰富、格式规整。于是,我重现写了一个爬虫程序:spider.chinanews.com.py,运行该爬虫会自动从网站上爬取最近5天的新闻数据,并整理成xml格式。 在编写爬虫的过程中,有两点建议。一是一定要捕获异常,因为爬虫在运行的过程中会遇到各种异常情况,比如突然断网了、某个URL无效、连接超时等等,如果不捕获异常,爬虫很容易崩溃停止,有可能爬了一整天都前功尽弃了。二是要控制抓取频率,宁愿让爬虫抓取频率慢一点,也不要让网站封了你的爬虫,一旦被封,要解禁估计要等好几个小时,还不如爬的时候慢一点,可以一直爬;我的策略是,遇到异常时,无条件睡眠10分钟,因为异常很可能是服务器察觉到爬虫然后拒绝响应了;还有就是每抓取到500条新闻后睡眠3分钟。在这两条策略下,我的爬虫运行一天一夜都没问题,可以连续抓取几万条新闻。 修改打分。之前在介绍检索模型时,我设计了一个基于新闻热度的打分公式,该公式的热度是综合了BM25相关性打分和新闻发布时间的一个函数。后来我在测试时发现由于BM25打分可能是负数(在df很大时),会导致计算热度打分时对数异常。于是,我把热度公式中的对数函数更换为了sigmoid函数,新的热度公式如下: $$hot_{score}=k_1*sigmoid(BM25_{score})+\frac{k_2}{t_{now}-t_{news}}$$ 线上部署。Flask是一个基于python的很轻量的web服务,之前我们通过本地http://127.0.0.1:5000/访问该搜索引擎服务,现在我把它部署到线上,和我的这个博客在一个VPS中。线上部署的步骤如下。 首先是准备新闻数据。由于我的VPS性能很弱,所以我只爬了约2500条新闻,并构建好了数据库等文件。 然后是准备服务器环境,如果是性能比较弱的VPS,建议安装Minicoda,否则直接安装正常版本的Anaconda。有了conda环境之后,就按之前的步骤安装lxml、jieba、Flask。此时,在web文件夹运行main.py后,可以通过服务器的内网访问,但外网还不能访问。 为了让外网也能访问,需要在运行Flask应用时指定host=”0.0.0.0″,此外还需要指定端口,如port=5000。此时,可通过服务器的ip:port的方式访问到Flask服务。 你也可以申请一个域名,将域名指向你的服务器,并设置port=80,此时就可以通过域名直接访问Flask服务了。 由于我的VPS上已经运行了Nginx服务,搭建了两个wordpress博客,所以我希望能够通过子域名的方式访问我的搜索引擎服务。比如直接访问 http://news.bitjoy.net/ 就可访问搜索引擎。此时,我们需要在DNS设置上将news.bitjoy.net这个二级域名指向服务器IP(我用的是DNSPOD),然后我们可以用通过news.bitjoy.net:5000的方式访问搜索引擎。但我希望只输入域名访问,省掉输入端口的操作。如果只输入域名news.bitjoy.net,默认访问的是服务器的80端口,但80端口已经给了Nginx的wordpress。为了实现输入news.bitjoy.net,访问的是5000端口,需要对Nginx进行设置,告诉它从 news.bitjoy.net:80进来的流量都转给 news.bitjoy.net:5000。为此,需要在Nginx的配置文件中增加一个虚拟主机,我的VPS上的文件路径是/usr/local/nginx/conf/vhost,这个文件夹下存放了所有网站的Nginx配置文件。新增一个配置文件news.bitjoy.net.conf,表示需要对访问 news.bitjoy.net 的网站的设置。内容如下,就是告诉Nginx,从news.bitjoy.net:80进来的流量,都交给localhost:5000处理。这里的localhost:5000就是ip:5000,前面我们已经对Flask进行了设置,所以访问ip:5000就可以访问我们的搜索引擎服务了。 server { listen 80; server_name news.bitjoy.net; location / { proxy_pass http://localhost:5000; } } 综上所述,流量走过的路径是这样的: 访问news.bitjoy.net 等价于访问news.bitjoy.net:80 Nginx将流量转发给localhost:5000 localhost:5000等价于ip:5000 Flask收到流量,开始处理 新增了Nginx的配置文件后,使用命令service nginx restart重启Nginx,此时就可直接通过二级域名访问Flask了。最后,可以使用nohup命令运行Flask并将其置于后台,这样关于服务器的远程连接后,Flask依然会运行。

April 5, 2020 · 1 min

CS224N(3.14)Future of NLP + Deep Learning

今天是该课程的最后一节课,介绍了使用未标注数据集进行NLP学习的方法,以及谈了谈NLP未来的发展方向。下面主要介绍使用未标注数据集进行NLP学习的方法。 我们知道,在机器翻译领域,特别缺少标注好的语料集。目前世界上有上千种语言,但用得最多的只有十几种。对于那些使用人数很少的语言,它们和其他语言之间标注好的翻译句子就更少了。如何使用少量标注集,甚至不用标注集,就能实现机器翻译功能,是NLP领域一个很有前景的发展方向。 之前的很多工作使用pre-training来提高机器翻译模型的性能。具体方法是,先在源语言和目标语言的语料集上分别训练一个语言模型,这是无监督的,这个语言模型可以学到不同词的含义。然后在翻译模型中,用源语言的语言模型初始化Encoder权重,用目标语言的语言模型初始化Decoder权重。使用pre-training的模型相比于不使用pre-training的模型的BLEU大概有2分的提高。 Pre-training的问题是,由于预训练是在两种语言上独立进行的,两种语言在预训练期间没有交互过程。 下面的Back-Translation比较有意思。比如我们要训练一个英语到法语的翻译器,初始化的时候让模型随便从英语翻译成法语。同时,训练一个法语到英语的翻译器,把上一个翻译器输出的法语翻译成英语。有点像练功的时候左右互搏,也有点像AlphaGo自己教自己下棋,随着训练的进行,两个模型的翻译能力都得到了提升。 当然这有一些问题,如果两个模型一开始都一无所知,则可能前一个模型的输出是随机的,后一个模型的输入是随机的,模型根本学不到任何知识,无法收敛。所以更好的做法是,有少量的标注数据,两个模型先在标注数据上学到一个比较差的模型;然后用这个比较差的模型左右互搏,相当于可以粗略的标注一部分新数据;然后又在标注数据上训练;如此循环往复。实验结果表明,使用Back-Translation和大量无标注数据集之后,翻译模型的性能有大幅提升。 Back-Translation要求我们还是需要少量的标注数据,用来启动左右互搏的过程。那么如果有两种语言X和Y,我们没有X和Y的任何翻译好的句子pair,但依然想翻译它们,怎么办。这时候,可以先从简单的单词翻译做起。在训练X和Y各自的词向量时,可以把它们的词向量映射到同一个空间,则空间中相近的词的含义也相近。对于X中的一个词x,只需要在词向量空间中选与x最接近的Y中的词y,则y可以作为x的翻译。这种把多种语言的词向量统一对齐到一个空间之后的词向量称为跨语言的词向量,也就是说从这个空间中取一个词向量,虽然它的含义是固定的,但可以转换成任意一种语言的具体的词。 那么,关键问题就是怎样把X和Y的词向量对齐。我们知道word2vec有一个很好的特点就是,它训练出来的词向量能够保持比较好的空间结构。举例来说,对于X中的词x和Y中的词y,即使它们的含义很接近,但如果直接把x和y放到同一个空间中,它们的距离可能还是比较大,因为X和Y的词向量坐标系可能就不一样。但是,对于X中的两个词x1和x2,如果它们分别对应Y中的两个词y1和y2,则在X空间中,x1和x2的距离应该与Y空间中y1和y2的距离相近,也就是说两个空间的整体结构是一样的。如下图所示,X和Y对齐的过程就是找出它们的变换矩阵W。由于我们只希望将它们的坐标系进行变换对齐,并不想改变里面的数据分布,所以变换矩阵W最好是正交的。 学习矩阵W的过程也很有意思,这里介绍了一种对抗学习的方式。有一个生成器,用来生成矩阵W。有一个判别器,它想要区分一个词向量是Y中的词向量,还是X中的词向量经过W变换得到的。起初,由于X和Y的空间分布不同,W也是随机的,判别器可以很容易地区分Y和WX。但是随着对抗的进行,W越来越准,最后WX和Y重合了,此时判别器傻傻分不清楚,它只能随机猜,有50%的几率猜对。所以学习矩阵W的过程就是让判别器懵圈的过程。 上述是非监督的词与词的翻译,怎样由此得到非监督的句子与句子的翻译呢? 我们首先使用上述的跨语言的词向量,然后使用相同的encoder-decoder来编码和解码两种语言。解码的时候,设置一个标志位,告诉decoder要把目标含义用哪种语言表达出来,就是下图的<Fr>标签。 然后有两个训练目标,第一个目标是对源句子进行微小的打乱,然后让encoder恢复原来的句子。第二个目标是使用上文提到的back translation进行左右互搏(所以好像还是需要少量标注集?)。 这种方法有效是因为输入的词向量是跨语言词向量,输入一个英文句子就相当于输入了一个法文句子。又因为使用的encoder是相同的,所以英文句子在进行第一个目标训练时,隐含学到了将法语翻译为英语的能力。 上述的非监督词与词、句子与句子的翻译,只有在源语言和目标语言比较像的情况下才能取得比较好的效果,比如英语、法语、德语比较像,可以用。但英语和土耳其语差别比较大,这种非监督方法的效果就比较差。语言像不像涉及到很多方面,比如语法结构、句子结构、用词顺序等等。

March 26, 2020 · 1 min

CS224N(3.12)Bias in AI

今天介绍AI的偏见,本质上,AI的偏见由人类的偏见导致。 看到成熟的黄颜色的香蕉,我们不会说这是黄颜色的香蕉,我们只会说这是香蕉,而如果看到未成熟的青色的香蕉,我们会特别说明这是未成熟的香蕉,或者说这是青色的香蕉。所以我们通常说香蕉时,其实暗含了它是黄颜色这个属性。 原型理论(Prototype Theory)说的就是这个意思,原型理论认为,我们在定义事物,并且给事物分类时,把这类事物中最常见的状态或实体定义为这一类的原型(Prototype)。同属于这一类,但具有不同特性的实物,只需要对原型增加修饰词来说明即可。比如香蕉这个例子,成熟的黄色的香蕉就是香蕉这一类别中的原型,它具有常见香蕉最典型的特征,而未成熟的香蕉最大的区别是颜色,所以我们可以用青色的香蕉来表示这是未成熟的香蕉的意思。 那么原型理论会对AI造成什么影响呢?由于原型是一类事物中的典型代表,人们在描述这类事物时,往往会省略原型的基本特征,而会重点描述某个具体事物的有差别的特征。这就导致不同事物出现在人类报道中的频率并不能反应事物真实发生的频率。 比如下面的例子,对于凶杀案,只要出现了,人们很可能就会报道,但对于眨眼睛,人们却很少报道。主要是因为眨眼睛是人类这个原型的基本特征,是众所周知的,而谋杀往往是少数人的特征,这个特征和人类原型是有显著差别的,所以人们在报道时反而更倾向于报道谋杀案,而不是眨眼睛。但事实上,每个人几乎每分钟都会眨眼睛,眨眼睛的真实频率远远大于凶杀案出现的频率,但相比于眨眼睛,人们更关注凶杀案,所以报道的凶杀案的数量远多于眨眼睛的数量。这就是人类在报道中出现的偏差,这种偏差反应了人类处理信息的方式以及人类对不同事物的关注程度。而AI模型是从数据中学习特征,由于人类报道的偏差,AI很可能会学到凶杀案比眨眼睛更常见这种荒唐的知识。 下图是常见的机器学习流程,在最开始的收集数据并建立标注集的过程中,会引入很多Human biases,包括上面提到的Reporting bias,这里就不具体介绍其他bias了。由于在整个pipeline的输入端就引入了bias,导致bias也跟着网络前向和反向传播,导致整个网络也是bias的,这个效应被称为Bias Network Effect。 一些可能的解决方法。比如去掉带有偏见的数据;评价模型在不同子类上的性能,有点类似于分开过滤的思想;对一批数据集,使用多任务学习等。

March 24, 2020 · 1 min

CS224N(3.7)Constituency Parsing, TreeRNNs

在开始今天的内容之前,给所有AI炼丹师的建议: 简化模型,使用最简单的词袋模型 使用很少的数据集进行训练和调试,比如就用10个样本,确保模型能够在10个样本上过拟合 确认模型没问题之后,增加模型的复杂度 画出训练集和测试集上的错误率变化曲线 如果一切都符合预期的话,增加L2正则和Dropout 搜索更优的超参数组合 今天要介绍的内容是成分句法分析(Constituency Parsing)。如下图是CS理解语言的两个极端,左边是bag of words,即词袋模型,把句子中的词完全独立出来,不管词与词之间的关系。右边就是对句子进行严格的成分句法分析,明确每个词在句子中充当的成分和依赖关系。本节课要讲的内容就是成分句法分析。 我们知道,我们的语言可以通过递归的方式表达含义,比如多个单词可以组成一个短语,多个短语又可以组成更长的短语,如此递归进行下去。类似的,单词在组成句子成分时也可以是递归的,比如短的名词短语NP可以组成长的NP,一级一级往上抽象,形成一个树形的句子成分结构。把一个句子转换为这样一个树形结构的过程就是成分句法分析的过程。 一个很简单的方法就是贪心的方法。对于一个句子,如下图所示的“The cat sat on the mat.”,对于任意相邻的两个词,它们可能组合成一个新的短语表达一个更完整的含义。那么,组合哪两个相邻的词呢?我们可以对所有相邻的两个词的词向量,输入到一个神经网络中,算出它们能组合成一个新的含义(新词)的概率(打分),以及这个新词的词向量。在所有组合中取打分最高的组合,把这个组合确定下来。比如下图中,The和cat组合后新的词的打分是最高分3.1,所以第一步把The cat组合起来,形成的新词的词向量是[5,2]。于是,就用[5,2]代替原来的The和cat,成为一个新词,和剩余的sat、on、the、mat.组成一个新的句子,再重复刚才的操作,直到建成一棵树,得到根节点。通过这种方法构建了一棵TreeRNN,训练的过程也是误差反向传播,各种求导。 贪心方法是最简单的成分句法分析的方法,后续针对这个方法有诸多改进,由于本人对这方面不感兴趣,本博客就不展开介绍了,感兴趣的可以去看这门课的视频。

March 22, 2020 · 1 min

CS224N(3.5)Multitask Learning

今天介绍NLP中的多任务学习。 我们知道预训练和参数共享在CV中很重要,很多模型都会在ImageNet上预训练,然后迁移到具体的任务中进行微调。这种方式能够成功的原因是很多CV任务几乎都是以分类为基础任务,分类相当于CV的积木(building block),所以在ImageNet上训练的CNN分类模型迁移到其他CV任务中能起到很好的提升效果。 NLP中虽然也有一些预训练模块,比如预训练词向量,然后用到具体的NLP任务中,但也仅仅是将词向量作为下游模型的输入。在NLP中,并没有一个基础模型(包括模型的结构、权重等),能把整个基础模型迁移到下游任务进行微调,现在都是针对不同的问题设计专门的网络结构,比如POS、NER、NMT等,处于不同任务各自为政的局面。 在NLP中,一个统一的多任务基础模型可以很方便地进行模型迁移、模型部署,并且对这个基础模型的不断改进,可以不断提高下游模型的性能,有可能达到持续学习、持续提升的目的,而如果每个新的子模型都重新学习的话,相当于利用不到基础模型长期学习到的知识。 但是,NLP领域有很多任务,比如序列标注、命名实体识别是一类;对整个句子的分类是一类;还有就是seq2seq的机器翻译、自动摘要等,要怎样将这些不同的任务统一到一个模型中呢? 可以把所有NLP任务统一成QA任务。如下左图所示,机器翻译转换为QA任务就是问这个句子翻译成另一种语言是什么句子,句子情感分类就是问这个句子表达的是积极还是消极的含义等等,所以所有NLP任务都可以转换为QA任务。PPT中列出了10个NLP任务都可以转换为QA任务,所以这个统一的基础任务相当于十项全能选手(decaNLP)。 设计这样一个十项全能的NLP模型,需要满足如下三个条件。1. 必须对十项任务一视同仁,也就是喂给模型的数据不能包含这条数据具体是哪个任务,不能告诉模型这条数据是要做NMT,另一条数据要做QA等。2. 模型也不能包含针对不同任务的特殊模块,模型必须学会自己辨别不同的任务类型,并且进行内部切换,执行不同的操作。3. 此外,模型还应具备执行这10个任务之外的任务的能力,即zero shot learning。 如下图是这个多任务QA模型的一个全貌。首先给一个上下文Context,然后给一个问题Question,最后模型输出答案。输出答案的时候,每次产生一个词,这个词可能来自Question、Context、或者Vocabulary,所以有一个指针开关,指向这个词可能的来源分布。 模型细节如下图所示,基本上是以前学过的模块,这里只是把它们组合起来了。 输入:固定词向量GloVe和charCNN,然后用bi-LSTM进行编码(Initial Encoding),注意这个bi-LSTM是Question和Context共享的,共享一套参数。输出就是Question和Context的每个词的隐藏层特征向量,图中Question有3个词,所以Initial Encoding后面有一个3*4的矩阵,3行就表示Question中的3个词,Context有4个词,也是类似的。 Coattention:以Question为例,Question自己的bi-LSTM输出经过一个attention,然后拼上Context的bi-LSTM输出再过一个attention,所以相当于Question和Context进行了co-attention。Context的也类似。 Transformer:Coattention出来后经过一层bi-LSTM、两层Transformer、再经过一层bi-LSTM,得到Question和Context最终的编码。这里的bi-LSTM就是Question和Context独立的了。 输出:Question和Context的Final Encoding输出又经过Attention,综合Question、Context和Vocabulary得到输出词的分布。 在训练阶段,PPT底部还提供了Answer,供误差反传。 收集的10个数据集,虽然不同任务的评价指标不同,但他们的值理论上都在0~100之间,所以模型总的得分是10个子任务各自得分的累加和。 性能表如下,看起来在大多数任务上,针对该任务单独训练的模型比Multitask模型的性能要好,但是在QA Zero shot relation extraction(QA-ZRE)任务上,Multitask具有比较大的优势。 训练技巧。不同的训练方式可能对模型的性能产生影响,这里介绍两种方式,一种是Fully joint,另一种是Anti-Curriculum。Fully joint,有10个不同的数据集需要训练,训练的时候,从每个数据集中抽一部分数据出来,组成一个batch进行训练,而不是依次训练第一个、第二个数据集这样子。Anti-Curriculum,训练的时候先训练难的任务,比如先在机器翻译上训练,再在句子情感分类上训练,因为情感分类任务相对简单,如果先在这上面训练的话,很容易达到局部最优,到时候就很难爬出来再针对机器翻译训练了;而如果先在更难的机器翻译上训练的话,得到泛化能力更强的模型后再在情感分类上训练就容易得多了。仅仅是训练方式的不同,Anti-Curriculum比Fully joint的测试得分会有所提高。 但是多任务模型的性能依然弱于单任务模型,发现主要在机器翻译数据集上,多任务模型的性能较大地差于单任务模型,可能是因为机器翻译的输出词大多数不在Question和Context中,而是在Vocabulary中。作者近期又有很多改进,使得多任务一个模型的总性能已经很接近多个单任务模型的性能总和了。 最后,愿景,希望把decaNLP变成NLP的ImageNet-CNN模型,以后大家的模型都可以基于decaNLP进行fine-tune和改进。

March 14, 2020 · 1 min

CS224N(2.26)Coreference Resolution

今天介绍的内容是指代消解(Coreference Resolution)。指代(mention)是指句子中出现的名词、名词短语、代词等,指代消解就是把指向同一实体(entity)的指代聚类到一起的过程。比如下面的两句话,蓝色的词就是很多的指代,需要找出来哪些指代是指向同一个实体。比如Barack Obama、his、He都是指奥巴马。 下面的例子是比较简单的情况,事实上,在真实的语言中,情况更加复杂,比如有些指代可能表示多个实体。比如“他们”,可能同时指代了“小明”和“小王”,这种指代对于现在的NLP模型来说比较难,暂时不考虑。 指代消解有很多应用场景,比如知道代词的含义之后,能更好地理解全文,进而能更准确的进行机器翻译,另外,在对话系统中进行指代消解能产生更正确的回答句子。 指代消解主要有两个步骤。第一步是指代识别(mention detection),即找出句子中所有的指代,这一步相对简单。第二步才是进行真正的指代消解(coreference resolution),这一步比较难,也是本节课的主要内容。 指代(Mention)是指句子中的一个短语(span),它可以是代词、也可以是命名实体、还可以是名词短语。指代识别的方法有多种,比如词性标注(POS)、命名实体识别(NER)、语法分析器(parser)等。 指代识别虽然相比于指代消解更简单,但也不是那么简单,它也有它自己的一些难题。比如下面列出的几个例子其实就不是指代,但用POS、NER等方法有可能把它们找出来,导致找出过多的指代(over-generates)。 一个简单的方法是,指代识别阶段尽量保召回率,保留所有找到的可能是指代的词,都参与后期的指代消解。如果一个指代没有找到它的共同指代(coreference),则说明这个指代是孤立的(singleton mention),有可能是指代识别阶段找到的不是指代的词,直接把它扔掉即可。 指代消解发展至今,经历了四种不同的方法,分别是Rule-based、Mention pair、Mention Ranking和Clustering。下面分别介绍每一种方法。 1976年Hobbs提出了基于规则的朴素算法,被后人称为Hobbs算法。该方法有9个步骤,包含了很多规则,非常繁琐,这里就不具体介绍了。Hobbs算法虽然是基于规则的,但在当时取得了不错的效果,现在也常常作为该领域的baseline模型。但是因为该方法是基于规则的,有很多指代消解没法解决。比如下图的两组例子,它们的句子结构完全相同,但指代不同,基于规则的Hobbs算法如果正确消解了第一个句子的指代,则肯定无法正确消解第二个句子的指代。 很明显,要想同时正确消解两个句子的指代,必须像人一样理解句子的含义。所以,指代消解也可以作为图灵测试之外的实验,用来测试AI的智力。如果AI能100%准确完成指代消解,AI的智力也就达到了人类的水平。 比较简单的Mention pair的方法。该方法把指代消解问题转化为一个二分类问题。从左到右遍历句子,每找到一个指代,就把它和前面找到的每个指代作为一个pair,问分类器这个pair是否指代同一个实体,如果是的话,就把它们连起来。二分类的损失就是交叉熵。很简单的一个模型。 该模型在测试的时候,设定一个概率阈值,比如把所有概率超过0.5的指代pair都连起来,然后所有连在一起的指代作为一个聚类。如果一个指代不与任何其他指代连接,则它可能不是一个指代。这种方法有over-cluster的风险,即万一有一根线连错了,则会导致原本属于两个cluster的所有指代归为一个类。 Mention pair模型的不足:对于很长的句子,可能会找到很多指代,而一个指代的先行词(antecedent)往往只有一个,但mention pair方法却把它和它前面的所有指代都pair进行指代消解。比如下面的例子,最后的he明确的指代是紧邻它的前一个Nader,但却需要把he和前面的4个指代都pair进行消解,无形中增加了计算量。可能的解决方法是只让模型预测一个先行词,也就是mention ranking方法。 Mention ranking的方法。每个指代同时和前面所有指代打分,用softmax归一化,找出概率最大的先行词,添加一条连边。注意需要添加一个NA节点,因为有的指代可能第一次出现,前面没有先行词,或者这个指代根本就不是一个真正的指代。 Mention ranking在训练时的损失函数如下:i:对于每一个指代;j:看看其前面的所有指代,最大化i所指代的j的概率\(1(y_{ij}=1)p(m_j,m_i)\)。 测试的时候与mention pair类似,把有边连的指代聚类到一起。 前面的内容都是假设我们计算好了任意两个指代是coreference的概率,那么,如何来计算这个概率呢?主要有三种方法,分别是Non-neural statistical classifier、Simple neural network和More advanced model using LSTMs, attention。下面分别介绍这三种方法。 A. Non-neural statistical classifier。统计机器学习方法,抽取每个指代的各种特征,然后用机器学习分类器来计算两个指代是coreference的概率。这里面的特征包括人称、性别一致性,语义相容性等等,如下图所示。 B. Neural Coref Model。输入是候选先行词和当前指代词的词向量,还需要加入一些额外的特征(Additional Feature),也就是上面统计机器学习方法里用到的一些特征。中间是FFNN,即全连接网络,最后输出两个指代是coreference的概率。 C. End-to-end Model。end2end模型是目前指代消解的SOTA模型,它把指代识别和指代消解两个任务融合到一起,用一个模型来解决。 它的网络结构如下左图所示。由于该模型同时完成了指代识别和指代消解两个任务,所以它需要枚举句子中任意两个词之间的span是否是一个指代,以及这个指代与其他指代是否是coreference的关系。 具体来说,对于每个词i,输入网络中的是这个词的词向量以及charCNN词向量拼接起来的向量。然后送入bi-LSTM中,提取bi-LSTM两个LSTM的隐藏层向量,拼接起来作为词i的特征向量\(x^*\)。枚举包含词i的起始位置START(i)和终止位置END(i),由起始位置到终止位置构成一个span,假设这个span可能是一个指代。一个span由向量\(g_i\)表示,它包含4个部分,分别是:span的起始和终止位置的特征向量\(x^*_{START(i)}\)和\(x^*_{END(i)}\),包含这个span的起始和终止位置的特征;这个span的attention表示向量\(\hat{x_i}\),提取这个span表示的核心信息,比如the black cat中cat是核心信息;以及一些额外的特征\(\phi(i)\)。 一个span的attention表示向量\(\hat{x_i}\)计算公式如下右图。span中每个词的特征向量经过全连接网络FFNN,得到一个attention打分,softmax归一化得到归一化的attention打分分布,最后加权平均起来。 ...

March 8, 2020 · 1 min

CS224N(2.26)Natural Language Generation

今天要介绍的内容比较多,但都是概述性的内容,主要了解自然语言生成领域的进展。 Section 1: Recap LMs and decoding algorithms 之前已经讲过什么是语言模型,语言模型就是给定句子中的一部分词,要求预测下一个词是什么。形式化表述就是预测\(P(y_t|y_1,…,y_{t-1})\),其中的\(y_1,…,y_{t-1}\)就是目前已知的词,\(y_t\)就是要预测的下一个词。 条件语言模型是指除了已知\(y_1,…,y_{t-1}\),还给定了\(x\),这个\(x\)就是提供给语言模型的额外的信息。比如机器翻译的\(x\)就是源语言的句子信息;自动摘要的\(x\)就是输入的长文;对话系统的\(x\)就是历史对话内容等。 需要提醒的是,语言模型在训练阶段,输入Decoder的是正确的词,这种方法被称为Teacher Forcing,即不论上一步的输出是什么,都强制给这一步输入正确的词。而如果在测试阶段,Decoder的输入是上一步的输出。 在机器翻译那期的博客中(CS224N(1.31)Translation, Seq2Seq, Attention),我们曾提到过在测试阶段,语言模型的Decoder有两种策略,一种是Greedy search,另一种是Beam search。Greedy search是指当前步的输入是上一步输出中概率最大的词。而Beam searh是指不但保留概率最大的词,还要保留概率在Top-k的词,那么在每一个时间步,都会有k条路径,最后选一条概率最大的路径。 那么,Beam search中保留的Top-k到底取多少合适呢?当k=1时,Beam search退化为Greedy search,产生的句子可能会不够自然,甚至难以理解。增大k会使得产生的句子的全局概率更大,读起来会更通顺一些。但是k的增大也会带来一些问题,比如:计算量增大;对于NMT任务,k的增大会导致翻译出来的句子更短,BLEU得分更低;对于对话系统,k的增大会导致产生的句子过于“安全”和“通用”,也就是和当前对话没有太大关系但又比较通顺的句子。 比如下面右图的例子,k=1时,回答和对话有点关系,但句子语法有问题;k=6时,句子很同时,但感觉答非所问。 事实上,除了Greedy search和Beam search,还有其他的decoding方法。下面是两种基于采样的decoding方法。 Pure sampling:每个时间步t,根据softmax输出的概率分布进行采样,来决定t时刻最终输出的词。Top-n sampling:只在概率top-n的词中采样,相当于对Pure sampling的概率分布进行truncate。两种方法都是单路径的,不像Beam search那样保留多条路径。由于两种方法都是通过采样决定输出,属于随机算法,所以每次运行算法输出都不一样,可增加句子的多样性,比较适合于对话系统。 还有一种可以改变语言模型输出概率的方法,就是Softmax temperature,带温度的Softmax,如下图所示。当t越大,分布变得越扁平,削弱了大和小的差别;t越小,则分布变得越尖,大的越大,小的越小。配合不同的decoding算法,可以控制产生句子的流畅度、平庸度、或者新奇度等。 以下是语言模型的decoding算法小结。 Section 2: NLG tasks and neural approaches to them 自然语言生成是一类生成新文本的任务,包括机器翻译、自动摘要、对话系统(聊天机器人)、写作机器人、看图写作等。下面对其中几个任务进行简单的介绍。 自动摘要的定义是,给定长文本x,要求生成短文本y,y能概括x的主要内容。自动摘要又分为x是单文档或多文档两类。 (左图)自动摘要的一些数据集。Summarization:根据长文本写一个短句子作为长文本的摘要,就是常规意义的自动摘要。Sentence simplification:把复杂的句子转换为简单的句子,通常转换后的句子比源句子短,主要是换成更简单易懂的词,用简单的句子结构替代复杂的句子结构等,比如把新闻转换为儿童容易读懂的新闻。 (右图)两种自动摘要的策略对比。Extractive summarization是指摘要的句子从原文中提取;Abstractive summarization是指使用语言模型生成新的句子作为摘要。前者类似于从原文中高亮某些关键句子,后者相当于用笔创作出新的句子作为摘要。 前神经网络时代的自动摘要大多数是Extractive summarization,是一个pipeline,很复杂,可能会用到各种不同的算法。 自动摘要的评价指标ROUGE。ROUGE和BLEU类似,都是基于模型输出和标准答案之间的n-gram的overlap,但是ROUGE不会对太短的摘要进行惩罚,而且ROUGE对不同的n-gram打分是分开的,而BLEU综合了n=1,2,3,4的n-gram结果。BLEU更关注precision,所以对太短(可能没有包含源句子的意思)的翻译有惩罚。ROUGE更关注recall,从其名称就可以看出来,在某个长度限制下,尽量包含长文的信息。) 2015年开始,基于神经网络的自动摘要方法蓬勃发展。基本思路是把自动摘要看成从长句子到短句子的翻译任务,使用seq2seq+attention的方法解决,效果还不错。下图是一些技巧,感兴趣的可以搜索原文阅读。 对话系统的分类:assistive:个人助手型;co-operative:协作型;adversarial:对抗型,辩论?chit-chat:聊天机器人;therapy:心理咨询师? 神经网络之前的做法是,事先设计一些问答模板,根据场景,给模板填充不同的内容。或者使用信息检索的方式,从问答库中检索问答。 2015年后,很多人开始用seq2seq+attention的方法做问答系统,但存在一些问题,如下。 ...

March 6, 2020 · 1 min

CS224N(2.21)Transformers and Self-Attention For Generative Models

今天介绍大名鼎鼎的Transformer,它于2017年出自谷歌的论文《Attention Is All You Need》(https://arxiv.org/pdf/1706.03762.pdf),用Attention实现机器翻译模型,并取得了新的SOTA性能。 传统的机器翻译模型一般是结合RNN和Attention,可以看我之前的博客介绍:CS224N(1.31)Translation, Seq2Seq, Attention。虽然RNN+Attention的组合取得了不错的效果,但依然存在一些问题。由于RNN是序列依赖的模型,难以并行化,训练时间较长;且当句子很长时由于梯度消失难以捕捉长距离依赖关系。虽然相继推出的LSTM和GRU能一定程度上缓解梯度消失的问题,但这个问题依然存在。而且LSTM和GRU难以解释,我们根本不知道当前timestep依赖远的词多一点还是近的词多一点。 Transformer的思想很激进,它完全抛弃了RNN,只保留Attention,从其论文标题可见一斑。RNN无法并行化的根本原因是它的正向和反向传播是沿着句子方向(即水平方向),要想实现并行化,肯定不能再走水平方向了。于是,Transformer完全抛弃水平方向的RNN,而是在垂直方向上不断叠加Attention。由于每一层的Attention计算只和其前一层的Attention输出有关,所以当前层的所有词的Attention可以并行计算,互不干扰,这就使得Transformer可以利用GPU进行并行训练。 具体来说,Transformer的结构如下图所示。我们知道end2end的机器翻译模型一般都是Encoder+Decoder的组合,Encoder对源句子进行编码,将编码信息传给Decoder,Decoder翻译出目标句子。Transformer也不例外,下图左边即为Encoder,右边即为Decoder。 Encoder的每一层有两个子层组成,包括Self-Attention和Feed-forward neural network (FFNN)。FFNN就是常规的全连接网络,没什么可说的,下面重点介绍Self-Attention。 Encoder Self-Attention的结构如下图所示,由于此时是Encoder阶段,对于每个词,都能看到句子中所有其他的词(对应到RNN里面就是可以用双向的RNN)。假设我们想要抽取第二个词”represent”的特征表示。首先,对第二个词的词向量\(e_2\)进行线性变换,即乘以矩阵\(matmul_Q\),得到Query,这就是标准Attention中的Query。其次,对周围所有的其他词,比如\(e_1\),也进行线性变换,变换矩阵为\(matmul_K\),得到很多的Key。然后,Query和所有Key做点积,并用softmax归一化,得到了Query在周围词上的Attention score distribution。接着,周围词乘以另一个线性变换矩阵\(matmul_V\),变换为Value。最后,Value和Attention score distribution进行加权求和,并加上\(e_2\)自己,送给FFNN。图中右下角的公式中的分母只是个缩放因子。 回顾一下,一个标准的Attention包括三个向量:Q、K、V,其中Q为用来查询的Query,K表示被查询的Key,V表示被查询的Value。其中的K和V来源相同,只是经过了不同的变换。形象描述就是:计算Q在K上分配的注意力\(QK^T\),然后从V中取出这部分注意力的值\(softmax(\frac{QK^T}{\sqrt{d_k}})V\)。 Self-Attention的优点。因为每个词都和周围所有词做attention,所以任意两个位置都相当于有直连线路,可捕获长距离依赖。而且Attention的可解释性更好,根据Attention score可以知道一个词和哪些词的关系比较大。易于并行化,当前层的Attention计算只和前一层的值有关,所以一层的所有节点可并行执行self-attention操作。计算效率高,一次Self-Attention只需要两次矩阵运算,速度很快。 Transformer的Decoder部分每一层有三个子层组成,包括Self-Attention、Encoder-Decoder Attention和FFNN。Decoder的Self-Attention如下图所示,和Encoder的Self-Attentoin非常像,只不过当要Decoder第二个词时,用黑框蒙住了第三、四个词的运算(设置值为-1e9)。因为对于机器翻译来说,Encoder时能看到源句子所有的词,但是翻译成目标句子的过程中,Decoder只能看到当前要翻译的词之前的所有词,看不到之后的所有词,所以要把之后的所有词都遮住。所以这个Attention也叫Masked Self-Attention。这也说明Transformer只是在Encoder阶段可以并行化,Decoder阶段依然要一个个词顺序翻译,依然是串行的。 不要忘了我们的任务是机器翻译,Decoder Self-Attention只用到了翻译出来的目标句子的前缀信息,还没用到源句子的信息,这部分就在Encoder-Decoder Attention中。前面说了对于源句子,通过Encoder的Self-Attention+FFNN,源句子的每个词都有一个输出向量,这些输出向量作为Encoder-Decoder Attention的Keys和Values,而从目标句子当前要翻译的词的Decoder Self-Attention出来的向量就是Encoder-Decoder Attention的Query。从下图可以看到,Encoder上面出来指向右边Multi-Head Attention的两个箭头就是Keys和Values,而从下面出来指向Multi-Head Attention的一个箭头就是Query。Encoder-Decoder Attention的作用就是看看当前要翻译的词在源句子中各个词上的注意力情况。 我们知道Attention机制是位置无关的,因为对于每个词,它都和句子中的所有词直连求Attention score,跟词在句子中的位置没有关系。但是句子作为一种线性结构,词在句子中的顺序对句子的含义至关重要。为了考虑词的位置信息,词在输入Attention前,把词向量和词在句子中的位置Positional Encoding加起来,得到一个新的向量,输入到Attention中,如上图所示。这个Positional Encoding可通过公式计算得到,这里不展开。 上图的Attention前面都有一个修饰词Multi-Head,也就是下图的Parallel attention heads。前面提到一个标准的Attention包括三个向量Q、K、V,它们分别由原始的查询向量和特征向量乘以矩阵\(matmul_Q\)、\(matmul_K\)、\(matmul_V\)得到。如果一个词在计算Attention时,选用多个不同的\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),得到的Attention输出向量也就不同了,这正好可以用来表示一个词在句子中的不同作用。比如句子“华为是一家中国的公司”中的“华为”,它的语义是一家公司,它在句子中的成分是主语,也就是说一个词至少有其语义信息和句法信息,如果只用一套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),则只能得到一种含义,如果设置多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),则能提取到这个词更多的信息。于是,在对每个词进行Attention时,都会设置多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),提取多个Attention输出向量,然后拼接起来,这就是Multi-Head Attention,或者说Parallel attention heads。我个人理解多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\)相当于CNN中不同的kernal,相当于不同的特征提取器。 课上还介绍了利用Transformer生成图片和音乐的应用,感兴趣的同学可以搜索相关论文看一看。 有关Transformer的介绍,还可参考如下三个链接: http://jalammar.github.io/illustrated-transformer/ https://zhuanlan.zhihu.com/p/48508221 https://zhuanlan.zhihu.com/p/44121378

March 4, 2020 · 1 min

CS224N(2.19)Contextual Word Embeddings

今天介绍几种新的词向量学习方法,在此之前,建议大家看看我关于word2vec或GloVe等传统词向量的介绍:CS224N(1.8)Introduction and Word Vectors。 传统词向量,比如word2vec,它在训练阶段学习到一个词的向量表示之后,在下游的各种NLP任务中,这个词向量不再变动了。也就是说传统词向量的特点是,对一个词只学习一个词向量,且在具体任务中固定不变。传统词向量有两个主要的不足: 难以表达一词多义。一个词在不同的上下文语境中可能表示不同的含义,比如“苹果”在“苹果真好吃”和“苹果手机很好用”这两个句子中表示不同的含义,但word2vec学习到的“苹果”词向量只有一个,也就是说下游任务对于这两个句子用的是同一个词向量。虽然word2vec的词向量可能同时包含了这两个含义,但它把这两个含义糅合到一个向量中了,导致在“苹果真好吃”中可能引入了“苹果手机”的干扰因素,在“苹果手机很好用”中引入了“吃的苹果”的干扰因素。总之就是,word2vec学习到的词向量粒度较粗,向量固定不变,无法根据具体的上下文语境进行改变。 难以表达不同的语法或语义信息。一个词,即使是同一个意思,在语法或语义上也可能充当不同的角色,比如“活动”这个词,既可以做名词、也可以做动词,既可以做主语、也可以做谓语等。但word2vec对一个词只给出一个词向量,无论这个词在句子中充当什么角色,词向量都是一样的。虽然word2vec训练时可能已经学到了一个词的不同语法或语义特征,但它把这些信息糅合到一个向量中了,也就是粒度较粗的问题。 其实上述两点暴露出来的word2vec的不足,本质上是同样的两个原因:1. 词向量是静态的,无法根据上下文进行调整;2. 词向量表示只有一个向量,糅合了太多信息,粒度较粗。 下图中的word type表示一个相同的词,比如“苹果”;word token表示同一个word type在不同上下文的具体实例,比如“苹果真好吃”和“苹果手机很好用”中的“苹果”就是两个不同的word token。word type和word token有点类似于类和实例的关系。 2018年艾伦人工智能研究所提出了ELMo: Embeddings from Language Models,即从语言模型中学习词向量的方法,它的原文标题为Deep contextualized word representations(https://www.aclweb.org/anthology/N18-1202.pdf),即深度的上下文词向量表示。ELMo很好地解决了上述传统词向量的两个不足,ELMo对一个词的表示由多个向量组成,并且每个向量的权重在具体的上下文中动态更新,由此不但粒度更细,而且能根据上下文动态调整一个词的最终词向量。使用ELMo词向量,作者在很多NLP任务上刷新了SOTA,在当年引起了很大的轰动。 ELMo有三个特点: 学习的是word token的词向量,根据上面的定义,word token与具体的上下文有关,不再是静态的word type的词向量; 使用很长的上下文进行学习,而不像word2vec一样使用较小的滑动窗口,所以ELMo能学到长距离词的依赖关系; 使用双向的语言模型进行学习,并使用网络的所有隐藏层作为这个词的特征表示。 下面介绍ELMo具体的训练过程,以下截图来自台湾大学李宏毅老师的教学视频。 首先,既然word2vec使用滑动窗口只能学习到局部特征,那么ELMo就用RNN来建模长距离的依赖关系。如下图所示,ELMo使用双向语言模型bi-LM学习每个词的特征。我们知道,语言模型是给定一个句子前缀,预测下一个可能出现的词,也就是说常规的语言模型是只知道t时刻前的信息,不知道t时刻之后的信息,即通常都是单向的。而ELMo则使用了bi-LM,比如下图我们要学习“退了”这个词的特征,在前向网络中,模型学到了在给定上文的情况下,“退了”的隐藏层特征;在反向网络中,模型学到了在给定下文的情况下,“退了”的隐藏层特征。最后,ELMo把两个方向的隐藏层特征拼起来,作为bi-LM学到的“退了”这个词的特征表示。 为了学到更多的特征,ELMo对双向RNN进行堆叠,每增加一层就能多学习到2个特征表示(一正一反,下图把这两个向量拼接起来作为一个整体向量h)。 ELMo文中只使用了两层的双向LSTM抽取特征,所以对一个词能抽取到4个特征表示,即下图中的h1和h2(每个h包含一正一反特征向量组合)。 在使用ELMo词向量时,每个词的最终词向量是所有隐藏层特征向量h的加权求和,系数是α。这个系数是根据词在不同的上下文中学习得来的。ELMo文章分析发现,不同的NLP任务学到的系数不尽相同,比如在Coref和SQuAD任务中,第一层的系数更大。有可能ELMo在第一层学到的是词的句法特征,第二次学到的是更高级的语义特征。有点类似于CNN中在浅层学到点、线、转角,在高层学到轮廓等高级特征。 ELMo的形式化表示如下图所示。假设堆叠的bi-LSTM有L层,则每一层都能学到前向特征\(\underset{h_{k,j}^{LM}}{\rightarrow}\)和反向特征\(\underset{h_{k,j}^{LM}}{\leftarrow}\),这两个组合起来就是\(h_{k,j}^{LM}\)(也就是上图的\(h_i\))。下图的ELMo还有个\(x_k^{LM}\),它是bi-LM的词向量输入,这相当于词最原始的输入向量,ELMo使用char-CNN抽取一个词的原始输入向量,和上一课提到的Subword model类似,这里不展开。下图j=0时的特征就是\(x_k^{LM}\) 。 所以,对于词\(k\),ELMo得到的完整词向量是\(R_k\),它其实是这个词的一系列特征向量的组合,它还不是这个词最终的词向量,因为最终的词向量要在具体的NLP任务中根据上下文来定。词\(k\)在某个任务task中的词向量\(ELMo_k^{task}\)如下图所示,它等于对所有层的特征向量的加权求和,权重\(s_j^{task}\)(即上图的α)根据当前任务动态学习得到。最终还会有一个参数\(\gamma^{task}\),控制ELMo词向量对任务的贡献程度。 不同层抽取的特征不一样,ELMo只使用了两层bi-LSTM,如果堆叠更多层的话,估计能学到更多有意思的特征。 ELMo词向量的使用方法。在下游的NLP任务中,既可以把ELMo词向量作为输入层特征,也可以将其拼接到隐藏层,反正都需要学习参数\(s_j^{task}\)和\(\gamma^{task}\)。 性能方面,ELMo刷新了很多NLP任务的SOTA,大概能提高3个百分点。 个人理解word2vec和ELMo的本质区别。其实两者在训练阶段都考虑了上下文,只不过word2vec使用的是滑动窗口,只能学习局部特征,而ELMo使用RNN,能学到长距离特征。word2vec学到embedding之后固定成一个向量了,在下游NLP任务中不再变动;而ELMo训练完之后得到这个词的多个特征向量(词的隐含特征表示,粒度更细),且在下游NLP任务中会根据上下文动态组合这些特征向量,得到在这一上下文中的词向量表示(针对性更强)。 其实,仔细想想的话,这个本质区别也没有那么大,虽然word2vec训练到的词向量只有一个,且固定了,但词向量的维度如果够大的话,可以用不同维度来表示一词多义呀。而且用到下游NLP任务时,词向量不同维度的权重也可以不一样啊,不就相当于ELMo的不同\(s_j^{task}\)吗?虽然如此,可能还是不如ELMo的多个隐藏层向量,一方面是经过隐藏层的特征抽取,另一方面是毕竟ELMo出来的特征向量有2L+1个,仅维度上就比word2vec多很多,效果好是理所应当的。 ELMo的作者在2017年发过TagLM的工作,和ELMo类似,被称为Pre-ELMo,感兴趣的可以阅读原文(https://arxiv.org/abs/1705.00108)。ELMo是把语言模型的隐藏层抽取出来作为词的特征向量,2017年的CoVe词向量是把机器翻译过程中的隐藏层提取出来作为词的特征向量,但效果没ELMo好,感兴趣的可以阅读原文(https://arxiv.org/abs/1708.00107)。 同样在2018年,OpenAI和GoogleAI借助Transformer模型,提出了更加强大的上下文词向量学习方法GPT和BERT。Transformer是为了解决RNN无法并行化的问题,提出Attention is all you need(https://arxiv.org/abs/1706.03762)。以前我们提到Attention用来提升RNN的性能,Transformer更加激进,直接不要RNN,只留下Attention,然后堆叠网络深度,使得其可以和CNN一样并行化,使用GPU之后可以大大加速训练过程。下节课将详细介绍Transformer的内容,这里我们只需要知道Transformer是一个强大的seq2seq(Encoder-Decoder)模型。 BERT就是借助Transformer学习上下文词向量的方法,它的全称是BERT (Bidirectional Encoder Representations from Transformers): Pre-training of Deep Bidirectional Transformers for Language Understanding。 ...

February 24, 2020 · 1 min