Training large model with your GPU

In the last post, I shared my story of the Kaggle Jigsaw Multilingual Toxic Comment Classification competition. At that time, I only had a 1080Ti with 11G VRAM, and this made it impossible for me to train the SOTA Roberta-XLM large model which requires larger VRAM than what I had. In this post, I want to share some tips about how to reduce the VRAM usage so that you can train larger deep neural networks with your GPU....

April 15, 2021 · 4 min · Yuanhao

面向电商场景的语言模型E-BERT

最近跟不少做电商NLP的朋友们聊天,有不少收获。我之前从来没想过【搜索】在电商里的地位是如此重要,可能GMV的50%以上都是从搜索来的。巨大的经济价值也极大地推动了技术的发展,他们的工作做得很细致,毕竟一个百分点的点击率后购买率提升也许对应的就是几百亿的成交额。 其实之前做的汽车领域NLP工作跟电商有很多相似的地方,场景先验都非常重要。直接使用开放域语料预训练的语言模型效果并不好。我们也尝试过一些方法,例如用本领域语料训练语言模型,结合一些词库词典等等。今天介绍最近看到的一篇针对电商场景调优BERT的论文《E-BERT: Adapting BERT to E-commerce with Adaptive Hybrid Masking and Neighbor Product Reconstruction》,其中的一些方法应该对细分领域NLP都有一些启发。 方法 论文的创新方法主要有两个:Adaptive Hybrid Masking(AHM,自适应混合掩码)和Neighbor Product Reconstruction(NPR,相似商品重构)。 E-BERT总览 {: .align-caption style=“text-align:center;font-size:smaller”} AHM 第一个方法AHM其实是对已有掩码方式的改进。原始版本的BERT采用的是随机mask,这个大家应该都比较清楚。这种mask方式针对的是token,而众所周知token是由单词通过wordpiece tokenizer分割而来。所以这种方式遮盖住的可能是单词的一个部分,学习这种类似看三个字母猜剩下四个字母的任务不是很符合大家的直觉。随后就诞生了更加符合人类认知的Whole Word Masking,这个方法就是说要遮就遮整个词。这里用一个网上的例子帮大家理解 Input Text: the man jumped up , put his basket on phil ##am ##mon ' s head Original Masked Input: [MASK] man [MASK] up , put his [MASK] on phil [MASK] ##mon ' s head Whole Word Masked Input: the man [MASK] up , put his basket on [MASK] [MASK] [MASK] ' s head philammon是一个词,他会被tokenizer分解成三个token,这时就体现了普通mask和WWM的区别。...

September 16, 2020 · 1 min · Yuanhao

受控文本生成2

昨天的文章介绍了在输出空间对文本生成进行控制的两篇论文,今天介绍一篇在隐空间控制的论文。 隐空间方法也分为两个流派,一种认为在隐空间里可以把内容和特性的向量表示分开(disentangled),然后通过修改特性向量来实现对特性的控制;另一种则无须这种假设。下面分别介绍一篇相关论文。 Style Transfer from Non-Parallel Text by Cross-Alignment disentangled representation的代表作之一,发表在2017年的NIPS上,如今引用已经接近300多。 Style Transformer: Unpaired Text Style Transfer without Disentangled Latent Representation 复旦大学NLP组发表于2019年的文章,已经被引用20次,文章标题中明确写了他们的方法without Disentangled Latent Representation。 在文章的introduction部分列举了一些Disentangled表示的问题,我觉得比较重要的两条是: 难以评价Disentangled表示的质量,纯粹的Disentangled表示也很难获得; 分离不是必要的,有文章已经论证了靠解码器就可以overwrite风格。 这篇文章总的来说是对抗生成网络的思路。模型分两大块,一块是典型的encoder-decoder结构的transformers,用来作为风格迁移器,另一块是判别器,用来解决由于没有平行语料带来的训练问题。 判别器训练 文中提出了两种判别器,从结果上看多分类判别器对BLEU指标更友好,而条件判别器对迁移后的风格更友好。 多分类判别器 这种方法比较好理解,即采用K+1类的多类分类器作为判别器,输入只有句子。后K类对应K种风格,第0类对应$f_\theta(\rm{x},\rm{\hat{s}})$,即假样本。在训练时判别器时,将原始语料和用原风格重建后的句子都标为对应的风格,假样本标为第0类。在训练风格转换器的时候,我们希望转换器能尽量提高$f_\theta(\rm{x},\rm{\hat{s}})$被判别为$\rm{\hat{s}}$类的概率,即能骗过判别器,使判别器不认为生成的是个假样本。 条件判别器 输入包括句子和风格,判别器需要判断句子是否含有风格(二分类)。训练判别器时将原始语料和重建句子$f_\theta(\rm{x},\rm{s})$标注为正样本,将变换后的句子$f_\theta(\rm{x},\rm{\hat{s}})$标注为负样本。 判别器训练算法 {: .align-caption style=“text-align:center;font-size:smaller”} 风格迁移器训练 风格迁移器有三个重要的任务,一个是自重建(Self Reconstruction),一个是循环重建(Cycle Reconstruction),以及风格控制(Style Controlling)。 自重建就是输入句子$\rm{x}$以及其原本风格的控制变量$\rm{s}$,让他生成自己。这个任务是可以使用监督学习来完成的,loss计算公式如下 $$ L_{\rm{self}}=-p_\theta(\rm y=\rm x|\rm x,\rm s) $$ 循环重建是先输入$\rm{x}$和一个其他的风格控制变量$\rm{\hat{s}}$,生成$\rm\hat y$,再用$\rm\hat y$和$\rm s$生成一个$\rm y$。此时$\rm y$应该和$\rm{x}$无论内容及风格都一致,并且可以使用监督学习来计算loss: $$ L_{\rm{cycle}}=-p_\theta(\rm y=\rm x|f_\theta(\rm x,\rm\hat s), \rm s) $$ 前面两个任务虽然解决了没有平行语料带来的训练问题,但思考一下就会发现这两个任务并不会起效果。模型可以完全只学习返回原句子就可以“蒙混过关”。解决的办法就是检验一下循环的中间结果$f_\theta(\rm x,\rm\hat s)$,这个中间结果应该尽可能含有$\rm\hat s$风格。因此引入了第三个风格控制任务,这个任务根据判别器的不同也分成两种情况:...

July 23, 2020 · 1 min · Yuanhao

受控文本生成1

关于文本生成的话题聊得比较少,印象中我们之前只有一期多轮问句改写涉及到了文本生成,受控文本生成就更少了。 受控文本生成是在保证内容的前提下对文本的特性,例如情绪、文风等,进行控制。典型的任务有文本风格迁移。图片和声音的风格迁移都已经商用落地了,例如之前很火的几个应用例如Prisma和FaceApp,相比起来文本风格迁移的发展要慢一些。 名噪一时的Prisma是图像风格迁移的代表性应用 {: .align-caption style=“text-align:center;font-size:smaller”} 文本风格迁移很像翻译任务(语言也可以认为是文本特性的一种),但相比于机器翻译,风格迁移任务几乎没有平行语料,所以要困难一些。如果你对这个方向有兴趣,强烈推荐北大付振新同学整理的这个Repo。 受控文本生成因为是文本生成的高级版,通过学习相关技术可以很好地加深对文本生成的理解。受控文本生成从技法上来讲主要有两类,第一类在隐空间做文章,第二类在生成器的输出空间上做文章。 相比于在隐空间施加控制,我感觉在输出空间施加控制在方法上更简单一些。今天先从这个流派开始。我认为在输出空间进行控制又可以细分出两种形式,一种是在概率空间,一种是在离散空间,下面分别用一篇文章来举例。 Delete, Retrieve, Generate: A Simple Approach to Sentiment and Style Transfer 来自斯坦福NLP组,发表在2018年的NAACL,目前引用150,可以说是这个方向的经典论文了。 这篇其实包含了四种方法,但我感觉最有代表性的是在token空间进行的方法(即后面的template based方法),可解释性强,效率也高。 DRG的四种迁移方法 {: .align-caption style=“text-align:center;font-size:smaller”} 这篇文章的思路很简单,因为它基于一个假设:通常文本特征迁移可以通过改变一些标志词或短语(attribute markers)来完成。 在做风格迁移时,首先要找到这些attribute markers。找的方法也很简单,就是考虑某个n-gram在不同风格语料中出现的概率。如果有显著(salience, s)差异,那它就很可能是个attribute marker,显著性的计算公式如下,$u$是某个term,$v$是某种风格,$\mathcal{D}_v$是某种风格的所有语料,$\lambda$是个平滑系数。公式简单,大家一看便懂,计算出数值后根据阈值最终确定所有的attribute marker。 $$ s(u,v)=\frac{\text{count}(u, \mathcal{D}v)+\lambda}{(\sum{v’\in \mathcal{V},v’\neq v}\text{count}(u, \mathcal{D}_{v’}))+\lambda} $$ 围绕这些attribute marker(后文简称AM),后续将进行如文字标题所写的三种核心操作:delete, retrieve和generate。 Delete Delete的目的是要删除句子中的AM,留下内容。用$a(x, v^{\text{src}})$表示源句子x中所有的AM,删除AM后的x表示为$c(x, v^{\text{src}})$,即不含AM的句子内容。 Retrieve 这一步是要在源句子中插入目标特性的AM。论文的策略是先使用$c(x, v^{\text{src}})$在目标特性句子集合中检索一个内容最接近的句子$x^{\text{tgt}}$。内容接近程度的评价可以使用任意的距离函数来完成。 Generate 这是最后一步,即获得最终的结果。文章里有四种策略 Retrieve Only 直接返回第二步的结果。这么做生成的句子在语言角度应该是正确的且带有目标特性,但可能在内容上和源句有出入。 Template Based 直接把$a(x, v^{\text{src}})$替换成$a(x^{\text{tgt}}, v^{\text{tgt}})$。这么做简单粗暴,可能产生不通顺的句子。 Delete Only 把$c(x, v^{\text{src}})$交给一个RNN进行编码,再拼上特性$v^{\text{tgt}}$的embedding,最后交由一个解码器解码。 Delete And Retrieve 和上一种相似,但不是拼上特性$v^{\text{tgt}}$的嵌入,而是用另一个RNN编码得到的$a(x^{\text{tgt}}, v^{\text{tgt}})$的表示向量。 前两种方法是不需要训练的,后两种则需要训练。对于Delete Only,使用重建句子任务(即训练一个自编码器)来训练。对于Delete And Retrieve则复杂一些,为了防止特征迁移的能力退化成句子拼接(c+a)的能力,作者在这里训练一个降噪自编码器,具体地说就是随机替换a里的AM。...

July 22, 2020 · 1 min · Yuanhao

Tweet Sentiment Extraction比赛总结

这是前段时间结束的Kaggle比赛,之前在文档问答的文章中我也有提到过,最终我们队获得了第七名,这篇文章分享一下我的参赛收获和感受。 首先感谢队友,特别是曹老师,如果没有曹老师我肯定中途就弃赛了。至于弃赛的原因,在感受部分会详细介绍。我的代码已经传到Github上了,感兴趣的朋友可以看一看,里面包含了我所有的commits,可以完整看到我方案的演进。 Repo: https://github.com/thuwyh/Tweet-Sentiment-Extraction 赛题回顾 比赛叫做Tweet Sentiment Extraction,对于给定的tweet和情感极性,需要选手从文本中找出支撑情感的部分。例如下面这条数据 "My ridiculous dog is amazing." [sentiment: positive] 模型应该返回amazing这个词。比赛的评价指标是word-level Jaccard score,它的含义看下面的实现就一目了然了。 def jaccard(str1, str2): a = set(str1.lower().split()) b = set(str2.lower().split()) c = a.intersection(b) return float(len(c)) / (len(a) + len(b) - len(c)) Baseline及一些改进 在比赛的初期讨论区和kernel分享区基本就定下了解题思路的基调,即用机器阅读理解(MRC)的方法来做span prediction。具体的说,就是把数据提供的情感词作为question,把tweet作为context,把预测对象作为answer。 模型也很简单,在RoBERTa后面接一个questionAnswering head预测start和end位置就可以了。这道题一个比较神奇的地方就是RoBERTa的效果比普通的BERT要好一些。 在这个框架下,大家也都做了一些改进,例如: 在语言模型输出后面加dropout; concat语言模型的多层输出结果; 引入FGM等对抗训练方法 以上都是一些比较常规的操作,也比较容易实现,类似FGM是比较稳定能提分的。还有一些稍微复杂一点的trick,例如: 在词级别进行数据增强,例如同义词替换,随机删词 在token级别的增强 label smoothing 蒸馏 因为是span prediction任务,数据增强如果做成随机动态的,需要考虑到改词后对label的影响,这是实现的一个小难点。英文的同义词替换可以使用wordnet来做,相比中文的一些同义词库来讲质量是比较高的。 label smoothing和蒸馏是很相关的两个技术,因为他们都需要用到KL散度作为损失函数。我也是趁这个比赛补了一补相关的知识,感觉还蛮有趣的,感兴趣的朋友可以参考这篇文章。做QA任务通常是对位置用CrossEntropyLoss,但是如果label不是一个确定的位置而是平滑过或者是teacher model预测得到的分布,就需要使用KLDivLoss。 这里在做标签平滑的时候遇到了一个小问题,蛮值得思考的。最开始是Google在Imagenet上用这个技巧,对于这个分类问题标签的种类是确定的K=1000类,所以在Inception论文里直接用一个系数来控制平滑的强度,即 $$ q’(k) = (1-\epsilon)\delta_{k,y}+\frac{\epsilon}{K} $$ 但是如果用同样方法在这些长短不一的句子上做平滑,其实是不合适的。每个位置的平滑概率反比于句子的长度,也就是K,所以我认为更好的确定平滑强度的方法是先确定一个单位平滑强度,再根据句子总长来确定原标签的权重。 针对数据特点的方法 这次的数据总体质量很差,噪声(其实是错误)很多,给参赛者带来了很多困扰。主要的噪声模式有两种,一种是把整个句子都标注成了支撑情感的selected_text,第二种是数据中有大量“断头词”出现在标签中。下图给出了一些例子。 对于第一种整句都是标签的情况,早期很多参赛者就发现了对于neutral类型的情感,绝大部分selected_text都和text一样;但对于其他情感,我们在人工审阅数据之后没有发现什么规律。我只好设计了一个辅助的分类任务让模型自己学习,实测下来有些微的提升,但并不明显。 对于“断头词”的情况,我们在比赛的末期终于发现了其规律。这种情况应该是由于标注环境不一致导致的。例如Twitter数据里有很多@用户的情况,这份比赛数据集会把相关的文本删除,但由于删除脚本的问题会导致文本中多出一个空格。我们猜测标注者看到的数据应该是没有多余空格的,类似于是使用' '.join(text.split())处理过的。这就会导致标出来的span相对于原text的位置产生了位移。且位移的大小就等于多余空格的数量。...

July 8, 2020 · 1 min · Yuanhao

跨语种语言模型

在著名的科幻电影《银河系漫游指南》里有一种叫巴别鱼的神奇生物。将它塞进耳朵里你就能听懂任何语言。多语种语言模型做得事情和巴别鱼很像,人们希望这个模型能用来处理所有的语言。举个例子,大家常用的中文bert有很强的中文处理能力以及一定的英文处理能力,但基本也就只能处理这两种语言;而目前的SOTA多语种模型XLM-RoBERTa能够处理104种语言。 巴别鱼,体型很小,黄色,外形像水蛭,很可能是宇宙中最奇异的事物。它靠接收脑电波的能量为生,并且不是从其携带者身上接收,而是从周围的人身上。它从这些脑电波能量中吸收所有未被人察觉的精神频率,转化成营养。然后它向携带者的思想中排泄一种由被察觉到的精神频率和大脑语言中枢提供的神经信号混合而成的心灵感应矩阵。所有这些过程的实际效果就是,如果你把一条巴别鱼塞进耳朵,你就能立刻理解以任何形式的语言对你说的任何事情。 数据集 训练跨语种语言模型会用到两种语料。一种是单语种(monolingual)语料,另一种是平行(parallel)语料。所谓平行语料就是源语言与译文“对齐”的语料。所谓对齐也有好几种级别,最常见的是句子级对齐,也有按词进行对齐的文本。可想而知,平行语料的获取相比于单语种语料要困难许多。如何充分借助单语种语料来提升模型能力是XLM研究的一个重点。 跨语种语言模型的评价一般有两个大方向,一个是其语义理解能力,另一个是文本生成能力。语义理解能力通常借助XNLI数据集,它提供了15种语言的平行文本,每种语言7500对的NLI语料。文本生成通常用翻译任务来评估,感兴趣的朋友可以自己查阅相关资料。 模型 下表列出了常见的单语种和多语种预训练语言模型。接下来我们将分析其中的mBERT、XLM和XLM-R三个模型。 Multilingual Bert(mBERT) 模型来自于这论文《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》,你没有看错,就是发表于2018年大名鼎鼎的BERT论文。 2018年11谷歌就放出了支持104种语言的多语种版本预训练模型,规格是BERT base。这个模型的较新版本是uncased版,即没有对输入文本进行规范化。使用WordPiece算法进行tokenization,词典大小是110k。其他的训练方法和普通的BERT一样,采用的是MLM和NSP两个loss,语料是Wikipedia。 XLM 模型来自于论文《Cross-lingual lan- guage model pretraining》,来自于FAIR,发表在NIPS2019。 XLM使用BPE算法进行tokenization,并且词典大小比mBERT更大,达到200k。论文指出Shared sub-word vocabulary对模型性能有很大的影响,在训练BPE算法的过程中他们使用了特殊的采样方式来避免低资源语种被进行字符集切分。 模型训练使用了三种不同的目标函数,在单语种语料上使用非监督的CLM和MLM。MLM就是masked language modeling,大家比较熟悉,在此就不再赘述了。CLM全称是Causal Language Modeling,简单的说就是用前面的词预测当前词,更详细的介绍大家可以参考我们之前UniLM和MASS的文章。在平行语料上使用的目标称为Translation Language Modeling (TLM)。其训练方式如下图所示,是将平行句子拼接后随机mask,希望让模型能借助另一种语言的信息来还原出被遮蔽的词。从图中可以看出模型用language embedding替换了BERT里的type embedding,并且在做TLM任务时position embedding在两个语言间是对应的。 我们来看一下XLM在XNLI上的表现。这张表很有意思,首先对这个数据集有3种处理方式:translate-train,translate-test和直接测试,即zeroshot。第一种是把英语的MNLI数据集机器翻译成XNLI内的15种语言用于训练,在XNLI测试集上测试;第二种是把XNLI测试集的15种语言翻译成英文。本文的对照组就是上面的mBERT。 可以看到效果最好的是翻译训练集,平均精度达到了76.7%,zero-shot次之,最差的是翻译测试集。在相同的实验设定下XLM稳定优于mBERT,甚至在zero-shot下的XLM也比finetune过的mBERT强。另外MLM+TLM也稳定优于只用MLM的方式。 XLM-RoBERTa 模型来自于论文《Unsupervised Cross-lingual Representation Learning at Scale》,和上文一样来自FAIR,已经被ACL 2020接收。 XLM-R使用了比XLM更大的词典,达到了250k。它也没有辜负RoBERTa的血统,使用了比Wikipedia大得多的cc100数据集。XLM-R只使用单语种语料,训练目标也只有MLM一个。 Tokenizer换成了sentence piece算法,在构建时也进行了采样,并且调整了系数使得各语言更加平衡。模型层面去掉了language embedding,变得更加简洁。我感觉用“重剑无锋”来形容XLM-R再合适不过了。 这篇论文总结了几个影响多语种模型的重要因素,可能会对大家有所启发: 当处理的语种变多的时候模型的能力会下降(嗯,符合常识)。增大模型可以一定程度对抗这种效应。 模型能力主要受词典大小、训练集大小、语种的采样频率影响 增大词典规模可以提高模型性能 sentence piece可以提高模型的通用性 下面这种图可以让大家对这些结论有更直观的印象 最后来看一下XLM-RoBERTa的实力。下表是在XNLI数据集上的结果对比,设定和XLM论文中差不多,其中Devlin et al.指的是mBERT,Lample and Conneau指的是XLM。可以看出XLM-R相比于XLM又前进了一大步。 顺便再提一嘴,论文作者还在GLUE数据集上对比了XLM-R和XLNET、RoBERTa等单语种语言模型,XLM-R超过了BERT-large,略低于XLNET和RoBERTa。也就是说XLM-R不仅获得了多语种能力,而且没有牺牲英文上的水平。 总结一下,从2018年的mBERT到2020年的XLM-R,跨语种预训练语言模型获得了长足的发展,地球语言范围内的巴别鱼指日可待。最近在Kaggle上正在进行一场跨语种文本分类的比赛,如果有想体验XLM最新进展的朋友可以去试试身手。 今天的文章就到这里,下期再见👋

May 11, 2020 · 1 min · Yuanhao

十分钟读懂beam search-2

在上一篇文章中我们介绍了基础版的beam search,这篇文章是对它的一个扩展,可以在模型不改的情况下获得更好的生成结果。今天的介绍围绕的也是一篇蛮新的论文,《The Curious Case of Neural Text Degeneration》,根据这篇论文的版面内容,它应该已经被ICLR 2020接收了。 Beam Search的问题 先解释以下什么要对Beam Search进行改进。因为Beam Search虽然比贪心有所改进,但还是会生成出空洞、重复、前后矛盾的文本。如果你有文本生成经验,一定对这些现象并不陌生。在语言模型还不像如今的BERT、GPT这么厉害的时候,这种现象更加明显。 没有经验也没关系,我们来看一个论文里面的例子。输入模型的引文(context) “The study, published in the Proceedings of the They were cattle called Bolivian Cavalleros; they live in a National Academy of Sciences of the United States of remote desert uninterrupted by town, and they speak huge, America (PNAS), was conducted by researchers from the beautiful, paradisiacal Bolivian linguistic thing. They say, Universidad Nacional Autónoma de México (UNAM) and...

March 23, 2020 · 4 min · Yuanhao

十分钟读懂beam search-1

最近研究了一下用基于BERT的encoder-decoder结构做文本生成任务,碰巧管老师昨天的文章也介绍了以生成任务见长的GPT模型,于是决定用两篇文章大家介绍一下在文本生成任务中常用的解码策略Beam Search(集束搜索)。 解码及贪心搜索 生成式任务相比普通的分类、tagging等NLP任务会复杂不少。在生成的时候,模型的输出是一个时间步一个时间步依次获得的,而且前面时间步的结果还会影响后面时间步的结果。也就是说,每一个时间步,模型给出的都是基于历史生成结果的条件概率。为了生成完整的句子,需要一个称为解码的额外动作来融合模型多个时间步的输出,而且使得最终得到的序列的每一步条件概率连乘起来最大。 在文本生成任务中,每一个时间步可能的输出种类称为字典大小(vocabulary size,我们用$v$表示),进行T步随机的生成可能获得的结果总共有$v^T$种。拿中文文本生成来说,$v$的值大约是5000-6000,即常用汉字的个数。在如此大的基数下,遍历整个生成空间是不现实的。 最容易想到的策略是贪心搜索,即每一个时间步都取出一个条件概率最大的输出,再将从开始到当前步的结果作为输入去获得下一个时间步的输出,直到模型给出生成结束的标志。例如下图,每一个时间步都取出了条件概率最大一个结果,生成了序列[A,B,C]。 很明显,这样做将原来指数级别的求解空间直接压缩到了与长度线性相关的大小。由于丢弃了绝大多数的可能解,这种关注当下的策略无法保证最终得到的序列概率是最优的。 Beam Search 而beam search是对贪心策略一个改进。思路也很简单,就是稍微放宽一些考察的范围。在每一个时间步,不再只保留当前分数最高的1个输出,而是保留num_beams个。当num_beams=1时集束搜索就退化成了贪心搜索。 下图是一个实际的例子,每个时间步有ABCDE共5种可能的输出,即$v=5$,图中的num_beams=2,也就是说每个时间步都会保留到当前步为止条件概率最优的2个序列。 在第一个时间步,A和C是最优的两个,因此得到了两个结果[A],[C],其他三个就被抛弃了; 第二步会基于这两个结果继续进行生成,在A这个分支可以得到5个候选人,[AA],[AB],[AC],[AD],[AE],C也同理得到5个,此时会对这10个进行统一排名,再保留最优的两个,即图中的[AB]和[CE]; 第三步同理,也会从新的10个候选人里再保留最好的两个,最后得到了[ABD],[CED]两个结果。 可以发现,beam search在每一步需要考察的候选人数量是贪心搜索的num_beams倍,因此是一种牺牲时间换性能的方法。 以上就是Beam Search的基本概念,下面我们解析一种高效率实现方式。 Beam Search代码解析 Beam Search的原理虽然简单,但实际实现的时候却有很多细节要考虑。下面要解析这个实现出自于NLP界著名Python包Transformers,我为了说明方便做了一些改动。 一个正确且高效的算法需要处理的问题大概有两个: 充分利用硬件,可以处理批量数据,且尽量使用并行计算少用循环 处理好长短不同的生成结果 下面是基础版的beam search函数定义。其中context是编码器编码获得的向量,batch_size是每批数据中包含的样本量,bos_token_id是句子开头标志的token id,pad_token_id是用于填充的token id,eos_token_id是句子结束标志的token id。这里给参数填上的默认值和我们后面讲解时使用的例子是一致的。 def beam_search_generate(context, batch_size=3, max_length=20, min_length=2, num_beams=2, bos_token_id=101, pad_token_id=0, eos_token_id=102, ): pass 在函数中主要执行以下三个步骤: 准备初始输入 在当前生成的序列长度未达到max_length时扩展生成序列 准备最终输出的序列 下面我们分别解析。 准备初始输入 # 建立beam容器,每个样本一个 generated_hyps = [ BeamHypotheses(num_beams, max_length, length_penalty, early_stopping=early_stopping) for _ in range(batch_size) ] # 每个beam容器的得分,共batch_size*num_beams个 beam_scores = torch.zeros((batch_size, num_beams), dtype=torch.float, device=encoder_input_ids....

March 20, 2020 · 4 min · Yuanhao

最近邻搜索,MIPS,LSH和ALSH

上一篇介绍REALM的文章对文中提到的Maximum Inner Product Search没有作充分的介绍。发出去的标题已经没法改了,这篇文章介绍一下MIPS和最近邻搜索问题,以及两个相关的算法。 问题定义 MIPS的定义很简单,假设你有一堆d维向量,组成集合X,现在输入了一个同样维度的查询向量q(query),请从X中找出一个p,使得p和q的点积在集合X是最大的。用公式写出来就是 $$p=\mathop{\arg\max}_{x \in X}x^Tq$$ 这个问题和最近邻问题很像,最近邻问题只要把上面的定义改成找一个p使得p和q的距离最小,假设这个距离是欧氏距离,则 $$p = \mathop{\arg\min}_{x \in X}\left |q-x\right|^2=(\left | x \right |^2 – 2q^Tx)$$ 如果X中的向量模长都一样,那两个问题其实是等价的。然而在很多实际场景例如BERT编码后的句向量、推荐系统里的各种Embedding等,这个约束是不满足的。 最近邻搜索其实应用非常广泛,如图片检索、推荐系统、问答等等。以问答匹配为例,虽然我们可以用BERT这样的大型模型获得很好的准确度,但如果用BERT直接对语料库中的所有问题进行计算,将耗费大量的时间。所以可以先用关键词检索或者向量检索从语料库里召回一些候选语料后再做高精度匹配。 朴素的算法 对于MIPS问题,一个直观的蛮力算法就是计算出所有相关的内积,然后将内积排序,找到最大的那个。对于最近邻问题其实也类似,即使X中向量模长各不相同,也可以提前计算出来,并不会增加排序的时间复杂度。 内积的计算可以转换成一个矩阵乘法,在CPU和GPU上都有大量的高效实现。当X中有N个向量时,时间复杂度是O(Nd),当N不大的时候是可以接受的,但是通常在工业界的大规模系统中,X的规模往往很大,朴素算法就显得力不从心。 Locality-sensitive hashing 对于某些距离度量(例如欧式距离,cosine距离)下的最近邻问题,可以使用LSH算法来解决。LSH的思路就像下图示意的那样,用hash函数把高维空间的点分到几个桶里去,从而减少距离的计算量。 跟普通的哈希函数不同,这个哈希函数是Locality-sensitive的。具体地说就是它有一个神奇的特点:在空间中离得近的点被分到同一个桶的概率大,离得远的点则大概率被分到不同的桶里去。或者说对于两个点x和y,他们被哈希函数分到同一个桶的概率随着距离的增大单调递减。 这样在查询的时候,只需要精确地比较和查询向量q处在同一个桶里的那些x。如果桶足够多,那便可以将N大大降低,从而提高查询速度。但需要注意的是,LSH是一个近似算法,有可能产生桶内的向量其实都不是最优解的情况,不同哈希函数发生这种情况的概率都不一样,也是作为评价哈希函数好坏的重要依据之一,对这部分感兴趣的朋友可以读参考文献。 下面举一个具体的例子来解释一下LSH。假设某个最近邻问题考虑的距离度量是cosine距离,有一个满足要求的LSH函数(变换),称为Random Projection。 如上图所示,其过程很好理解: 随机取一个空间中的超平面将空间分为两半,X内位于某一半的点标为0,其他标为1; 重复第一步K次。 完成之后,X中的每个点便得到了一个由K个0,1组成的表示(signature)。例如重复了K=32次,那每个点都被分到了一个用一个int32类型的整数编号的桶里。如果这些点在空间中分布足够均匀,那么我们将可以期望每个桶里只有N/2^K个点,当K~logN,则查询的时间复杂度就约为O(dlogN)。整个过程构建出了一张哈希表,由于LSH可能会错过最优解,一个可行的增强鲁棒性的做法是用同样的方法多构造几张哈希表,借助随机的力量来降低犯错的概率。这里有一个讲解LSH的视频,可谓短小精悍,直观易懂,推荐给大家。 LSH看上去相对于朴素算法确实前进了一大步。但别高兴得太早,要达到O(dlogN)的效果必须服从那个很强的假设。而点在空间中分布足够均匀往往是不太现实的。除此之外,一个LSH只能适用于某些距离度量,对于MIPS,找不到符合要求的LSH。 Asymmetric LSH(ALSH) 论文里证明了找不到可用于MIPS问题的LSH函数,但他们发现对LSH稍作点改动即可将MIPS问题转变为欧式距离下的最近邻搜索问题。改动的关键就在于Asymmetric这个词。在LSH算法中,对查询向量q和X中的向量做的是同样(对称) 的变换,而在ALSH中作者对两者使用了 不同(非对称) 的变换。简单起见,假设查询向量q的模长是1。对于X,先做一个放缩变换使得X中所有向量x的所有元素都小于1。然后对X中的向量进行变换P(x),对查询向量q做变换Q(x),P和Q的定义如下: $$P(x) = [x; \left | x \right |_2^{2^1}; \left | x \right |_2^{2^2},…,\left | x \right |_2^{2^m}]\ Q(x) = [x;\frac{1}{2};\frac{1}{2},…,\frac{1}{2}]$$ 可以发现,P和Q虽然变换不同,但都会使输入向量增加m维。进一步观察可以得到 $$Q(q)^TP(x)=q^Tx+\frac{1}{2}(\left | x \right |_2^{2^1}+ \left | x \right |_2^{2^2}]+…+\left | x \right |_2^{2^m})\ \left | P(x) \right |_2^2=\left | x \right |_2^{2^1}+ \left | x \right |_2^{2^2}]+…+\left | x \right |_2^{2^{m+1}}$$...

March 7, 2020 · 1 min · Yuanhao

REALM: Retrieval-Augmented Language Model Pre Training

知识就是力量 ——培根 背景 去年可以说是语言模型快速发展的一年,BERT、XLNET、Albert等等模型不断刷新各个NLP榜单。在NLP榜单中比较引人注目的应该属于阅读理解型的任务,例如SQuAD等等。以SQuAD为例,模型需要阅读一段给定的文本,然后回答几个问题,问题如果存在答案,答案一定可以在文章中找到。所以说虽然叫阅读理解,但其实和序列标注有点相像,是在给定序列中标出答案段。而这篇论文针对的问题叫开放领域问答(Open-domain QA),对于一个问题Q,模型需要从包含大量文档的知识库中找到答案,而不是像SQuAD数据集一样从一篇文章中寻找。 大部分的语言模型都采用一种称为masked language model,简称MLM的任务来训练,让模型学会类似完形填空一样的能力。通过在大规模语料上的训练,预训练语言模型如BERT实际上已经隐含了一些知识。例如输入一句“The is the currency of the United Kingdom”,BERT很有可能会填入单词"pound"。虽然他还是根据词的共现信息学习和推理的,但看上去就像具有所谓的知识一样。从去年开始就有越来越多的研究从单纯语言模型转换为带有知识嵌入的语言模型,例如清华和百度提出的两个同名模型ERNIE。 但上面说的这种隐含知识不好把握,也难以扩展。这篇论文则提出了一种更加模块化且可解释性更强的知识嵌入方法。总的来说,他的方法是训练一个独立的“语境知识抽取器”(contextual knowledge retriever),通过这个抽取器来决定应该在推理时使用哪些知识。而且这个抽取器和语言模型一起进行非监督预训练大大提高模型性能。 方法 如上图所示,整篇论文涉及两个任务,左边是语言模型预训练任务MLM,右边是QA任务。下图是预训练任务一个更加完整的流程图,我们由此切入进行介绍。 整个过程分为两个关键步骤。先看第一步,即neural knowledge retriever,它负责计算p(z|x)。要实现这个过程首先需要对z和x进行编码。论文采用的是BERT,对于问题x,直接输入BERT,取[CLS] token的输出作为编码向量,而对于文档z,则将标题和正文用[SEP]连接后输入BERT,同样去[CLS] token的输出。论文中还对BERT的输出向量进行了降维处理。即 对于文档库中的某一个z,则 其中f是问题和文档的相关性, 以上部分就称为neural knowledge retriever,通过他每篇z都会得到一个p。现在可以进行第二步,综合x和z求y。上图是一个预训练的例子,y是抠掉的词。利用z的方式是将z的正文和x拼在一起来提供上下文信息,然后优化下面的目标 其中j指第j个masked token。 在做QA的时候稍有不同。由于此时是针对某个具体的z,所以作者将开放域问答任务退化成了像SQuAD一样在文档中找答案的阅读理解任务。 这一部分就是knowledge-augmented encoder。 训练 上面已经描述了预训练阶段和QA finetune阶段的任务。训练的过程都是最大化正确y对应的logp(y|z,x),而且以上描述的两个任务都是可以端到端优化的。 但这里面对一个问题,上面有个公式需要对整个知识库中所有的文档z的相关概率求和,这是很困难的。作者提出将这一步用只对概率最高的k个文档计算来近似,因为绝大部分文档由于与问题不相关,p(z|x)都非常小。但问题还没有解决,如何找到概率最高的k个文档呢。 观察公式可以发现p(z|x)是正比于两个编码后的内积的,由于大家的分母都一样,分子的顺序就是整个分数的顺序。所以可以用最大内积搜索算法(Maximum Inner Product Search, MIPS,并不知道是什么,维基百科都没有)来解决。但要构建一个快速检索的索引又要求两个编码后的向量是确定的,而由于编码器是不断训练的,所以这个条件无法满足。为了追求一个平衡,作者决定每隔几百步才更新一下编码器,并重新构建索引。而且这只发生在预训练语言模型的时候,在finetune QA任务的时候只使用语言模型得到的编码器编码一次所有的z和x并构建索引。 额外策略 在研究过程中作者发现了一些能让模型更好训练的策略。 只训练真正需要知识的词(通常是实体和日期)来训练MLM 在topk文档外添加一个虚拟的null document 避免让x出现在z中(因为x被mask过,如果它来源于z,那答案就暴露了!) 避免冷启动的retriever太渣导致的恶性循环,他们用了一个以ICT作为任务的模型来初始化retriever 结果对比 这篇论文的对手主要是原来sparse retriever+神经阅读理解模型的组合,例如大名鼎鼎的DrQA。所谓sparse retriever就是指用例如TFIDF之类的特征进行检索的模型。还有一些跟本文很像的neural retriever+neural reader的组合。其中提到了一个ORQA,跟这篇非常像,只是这篇增加了预训练的步骤。最后是一些生成式模型,例如finetune后的T5(可怕!) 在Natural Questions-Open(NQ)、Web Questions(WQ)和Curated Trec(CT)三个数据集上的结果如下 总之一句话,非常牛逼!而且这里模型只取了top 5文档,其他模型可能取了20-80篇,还是打不过他。注意到ours的括号内有两个数据,Z是知识库,很好理解,X是指预训练用的语料。而且通过文章的Ablation Analysis部分可以知道预训练其实是非常关键的一个步骤,对performance的贡献非常大。 后记 我感觉这篇论文和他提到的ORQA还是很厉害的,知识嵌入也从去年的实体粒度的嵌入发展到了如今句子、篇章级别的嵌入。试想一下,这项技术发展起来之后,如今基于词的Sparse搜索引擎应该很快会发展成对NN更友好的Dense搜索引擎,所有的内容也许都会被映射到一个向量空间。各路神经网络将在这个向量空间尽情驰骋~莫非得encoder者得天下?! 论文链接:https://kentonl.com/pub/gltpc.2020.pdf

March 6, 2020 · 1 min · Yuanhao