这篇论文是Facebook在BERT的基础上发展出来的Cross-Lingual版本,即多语的。BERT的github上实际上也有一个多语版本的,但却没有提到是怎么训练的,也没有任何的信息。这里的XLM提出了一些策略用于多语言学习,并与multi-lingual的BERT进行了对比,效果确实会好。

文章目录

  • 一. 前言
  • 二. XLM原理
    • 1. 多语词表构建
    • 2. 预训练任务
    • 3. 预训练流程
  • 三. 实验
  • 四. PyTorch实现
  • 五. 总结
    • 优势
    • 不足
  • 传送门

一. 前言

一开始BERT出来的时候,只有英语的,这对于各个国家的广大AI爱好者,是十分不便的,大家都希望能有自己国家语言的版本。这不,后面BERT又出了多语言版本,FB也紧跟着出了一个更好的多语言版本(不过貌似语言比较少?主要还是针对翻译和XNLI任务而定制的,不像BERT的那个那么多语言,而且很通用)

这里复述一下作者在第一章总结的他们的贡献:

  1. 引入了一个新的无监督方法,用于训练多语的表征,并且提出两个单语的预训练LM目标
  2. 提出了一种新的有监督方法,使用平行语料,来增强多语预训练的表现
  3. 在跨语言分类、有/无监督机器翻译任务上,达到了新的SoTA
  4. 对于resource比较少的语言,用这种预训练方式很有帮助
  5. 重点来了!有源码和预训练模型

下面来看看XLM具体是怎么操作的~

PS:不知道有没有读者和笔者一样,一看到这种多语的啊,pair的啊,parallel的啊这类的词眼,就觉得整个逻辑非常的晕,这篇论文,尤其是实验部分,真的是结合源码看了好久,大概率是笔者太菜了吧。。。

二. XLM原理

1. 多语词表构建

既然是多语的模型嘛,总不可能是一个语言一个model,然后封装在一起,假装是多语模型吧,肯定是只有一个模型的,那么就要求这个模型能接收各个语言的句子作为输入,因此这里就需要构建包含各个语言词的多语词表。

与BERT一样,这里也是使用BPE,但不是简单地把各个语言的bpe词表进行拼接,那样也太大了,,这里是先对多语语料按照如下的概率进行采样,然后将多语语料进行拼接,最后进行正常的BPE统计。采样的目的是对大语种的语料和小语种的语料进行一下平滑,省的全采到大语种上面了,小语种连词表都没有了(或者小语种都被按照char拆分了)。。这里取 α = 0.5 \alpha=0.5 α=0.5

q i = p i α ∑ j = 1 N p j α w i t h p i = n i ∑ k = 1 N n k q_i = \frac{p_i^ \alpha}{\sum_{j=1}^N p_j^ \alpha}\ with\ p_i = \frac{n_i}{\sum_{k=1}^N n_k} qi=j=1Npjαpiαwithpi=k=1Nnkni

2. 预训练任务

这里作者提出了三种预训练任务:CLM、MLM和TLM,下面分别介绍:

  1. CLM:Causal Language Model,无监督单语单向LM训练任务,就是用Transformer进行LM的单向训练。
  2. MLM:Masked Language Model,无监督单语双向LM训练任务,与BERT一样。
  3. TLM:Translation Language Model,有监督翻译LM训练,拼接平行双语语料,然后执行MLM,以期这样能学到翻译的对齐信息?

对于MLM和TLM的形象化表示可以看下图:

这里MLM/TLM的输入构造时与BERT的不同主要在于:

  • BERT在预训练构造输入的时候,用的都是pair的输入方式,其实就是先构建NSP的数据,然后再mask并构造MLM的数据。输入会规定一个最大长度,然后选择两个句子组(句子组的概念就是把物理上相邻的多个句子当成一整个句子,中间不加入任何句子的分隔符),满足在这个长度内即可。
  • XLM在预训练的时候,对于CLM和MLM都是用的stream的方式,将多个物理上的句子(不一定相邻?)通过分隔符连接起来作为输入,对于TLM的构造与前者一样,只不过又拼接了一个平行语料。同时,去掉了BERT里面的句子id标识,改成了语言的id标识。

3. 预训练流程

简而言之就是:CLM/MLM (+TLM),也即从CLM或MLM中选一个进行单语LM的预训练,然后再根据需求和数据情况,决定要不要加入TLM进行训练,加入的话就是和前面的CLM/MLM进行交替训练。

三. 实验

论文里面主要验证了XNLI、无监督机器翻译和有监督机器翻译任务的效果,下面分别说:

  1. XNLI

这个任务其实笔者也是去扒拉了FB之前的论文,才知道是个什么任务。。其实还是文本蕴含任务,但只不过训练集里面都是英文的,验证集里面是多语的(15种语言,获取方法就是把英文的验证集翻译过来,同一个pair里面是同一种语言的)。

先用MLM在各个语言的单语语料上进行训练(也有加上额外的平行语料进行TLM训练的部分),然后再用英文训练集进行finetune,最后在多个语种上评估。结果如下:

  1. 无监督MT

无监督MT的任务,笔者也是特意去查阅了相关的资料,知道大概是怎么回事。用大白话说就是,给出两个语言各自的语料(不一定要平行),机器应该就能学会翻译,就像人类一样,在学会了中文和英语之后,应该能进行翻译,因为中间的连接是语义,而不是词表的对应。

无监督MT就是基于这么一个设定,一般在不考虑pretrain的时候,用的比较多的方法是用去噪自编码器+循环翻译,具体来说,对于英译德这个任务,搭建起encoder-decoder这个模型之后,可以用英文语料加上噪声,输入encoder,然后decoder出来原始的英文语料,同理也可以用德文语料加上噪声,输入encoder,然后decoder出来原始的德文语料,这个就叫去噪自编码器,目的其实是在于让encoder学到语义信息;循环翻译是个啥?比如en->de->en,就是先让英文经过encoder和decoder,得到翻译的德文伪数据,然后将这个德文伪数据,再输入encoer和decoder,得到原来的英文数据,这样进行训练。

那么在这里,其实就是用CLM或MLM去初始化encoder和decoder,decoder就初始化那些encoder有的部分,然后后面用上面的套路进行正常流程的训练即可,这里对比不同的初始化方法的结果:

  1. 有监督MT

这里对比了几种方法用不同的预训练方式,结果如下:

  • Sennich:这个是之前的SoTA,好像还是用了back-translation+ensemble的方法,也是一个强baseline
  • ro->en:这个是用单向的数据进行finetune
  • ro<->en:这个使用双向的数据进行finetune
  • ro<->en + BT:用双向的数据进行finetune,同时进行back-translation(这个好像又是那种先从A->B生成B的伪数据,然后再翻译回到A)
  1. 小语种LM

这里主要是验证多语训练对小语种语言模型建模的影响,结果如下:

  1. 无监督多语embedding

这里主要是验证无监督情况下生成的多语embedding的优秀程度,通过验证各种源单词和其翻译对应的词之间的距离,结果如下:

四. PyTorch实现

这里主要是分析XLM源码中关于模型和训练的部分,因笔者对于论文中的这些任务(如翻译等)不是特别熟悉,所以全凭README的内容和代码一步一步摸索,如果有理解错误的地方,还请指正~

下面我将按照源码中README给出的思路顺序进行剖析:

  1. 有/无监督机器翻译

在机器翻译这个场景下,论文首先用CLM/MLM对MT的encoder和decoder进行预训练。其实这里就是用的多种语言的单语语料,输入词表是多语的,然后用CLM/MLM训练语言模型,并将其参数作为后续MT的encoder和decoder的初始参数,对decoder的初始化是只初始化其中与encoder相同的部分,即不初始化encoder-decoder-attention的部分。感觉这样也是一种思路啊,一般都认为decoder是没法初始化的,这里却可以这样初始化??

其预训练的代码如下:

model = build_model(params, data['dico'])# CLM steps
for lang1, lang2 in shuf_order(params.clm_steps, params):trainer.clm_step(lang1, lang2, params.lambda_clm)# MLM steps (also includes TLM if lang2 is not None)
for lang1, lang2 in shuf_order(params.mlm_steps, params):trainer.mlm_step(lang1, lang2, params.lambda_mlm)

这里首先就是定义模型,其实就是Transformer的encoder,这里就不再赘述。紧接着是有两种训练方式,一种是CLM,一种是MLM,分别与论文里面是对应的。

下面来看clm_stepmlm_step各自的实现:

def clm_step(self, lang1, lang2, lambda_coeff):"""Next word prediction step (causal prediction).CLM objective."""# generate batch / select words to predictx, lengths, positions, langs, _ = self.generate_batch(lang1, lang2, 'causal')x, lengths, positions, langs, _ = self.round_batch(x, lengths, positions, langs)alen = torch.arange(lengths.max(), dtype=torch.long, device=lengths.device)pred_mask = alen[:, None] < lengths[None] - 1y = x[1:].masked_select(pred_mask[:-1])# forward / losstensor = model('fwd', x=x, lengths=lengths, langs=langs, causal=True)_, loss = model('predict', tensor=tensor, pred_mask=pred_mask, y=y, get_scores=False)def mlm_step(self, lang1, lang2, lambda_coeff):"""Masked word prediction step.MLM objective is lang2 is None, TLM objective otherwise."""# generate batch / select words to predictx, lengths, positions, langs, _ = self.generate_batch(lang1, lang2, 'pred')x, lengths, positions, langs, _ = self.round_batch(x, lengths, positions, langs)x, y, pred_mask = self.mask_out(x, lengths)# forward / losstensor = model('fwd', x=x, lengths=lengths, positions=positions, langs=langs, causal=False)_, loss = model('predict', tensor=tensor, pred_mask=pred_mask, y=y, get_scores=False)

仔细看这两者的实现,其实只在generate batch上不同,CLM只需要生成正常的序列即可,而MLM则需要进行mask_out的操作,这里与BERT一致,也不再赘述。

在预训练完Encoder和Decoder之后,就开始用task-specific的方法进行finetune,比如对于无监督机器翻译来说,就是用去噪自编码器+循环翻译的方式,比如对于en-fr这种翻译,去噪自编码器就是noise_en->en和noise_fr->fr,循环翻译就是en->fr->en和fr->en->fr;对于有监督机器翻译来说,目前较好的方式就是比如对于en->fr,就是同时学习en->fr和fr->en(是用同一个MT模型学习en->fr和fr->en?),而后用en->fr的数据为fr->en进行数据增广(back-translation,不知道理解是否有误?)以及fr->en的数据为en->fr进行数据增广,这样来进行finetune。

这里源码里面分别给出了这些方法的训练方式:

# denoising auto-encoder steps
for lang in shuf_order(params.ae_steps):trainer.mt_step(lang, lang, params.lambda_ae)# machine translation steps
for lang1, lang2 in shuf_order(params.mt_steps, params):trainer.mt_step(lang1, lang2, params.lambda_mt)# back-translation steps
for lang1, lang2, lang3 in shuf_order(params.bt_steps):trainer.bt_step(lang1, lang2, lang3, params.lambda_bt)

其中的mt_step是翻译训练,可以是A->B的翻译,也可以是noise_A->A的翻译;bt_step是back-translation训练,主要是A->B->A的这种训练。其实现方式如下:

def mt_step(self, lang1, lang2, lambda_coeff):"""Machine translation step.Can also be used for denoising auto-encoding."""# generate batchif lang1 == lang2:(x1, len1) = self.get_batch('ae', lang1)(x2, len2) = (x1, len1)(x1, len1) = self.add_noise(x1, len1)else:(x1, len1), (x2, len2) = self.get_batch('mt', lang1, lang2)langs1 = x1.clone().fill_(lang1_id)langs2 = x2.clone().fill_(lang2_id)# target words to predictalen = torch.arange(len2.max(), dtype=torch.long, device=len2.device)pred_mask = alen[:, None] < len2[None] - 1  # do not predict anything given the last target wordy = x2[1:].masked_select(pred_mask[:-1])# encode source sentenceenc1 = self.encoder('fwd', x=x1, lengths=len1, langs=langs1, causal=False)enc1 = enc1.transpose(0, 1)# decode target sentencedec2 = self.decoder('fwd', x=x2, lengths=len2, langs=langs2, causal=True, src_enc=enc1, src_len=len1)# loss_, loss = self.decoder('predict', tensor=dec2, pred_mask=pred_mask, y=y, get_scores=False)def bt_step(self, lang1, lang2, lang3, lambda_coeff):"""Back-translation step for machine translation."""# generate source batchx1, len1 = self.get_batch('bt', lang1)langs1 = x1.clone().fill_(lang1_id)# generate a translationwith torch.no_grad():# evaluation modeself.encoder.eval()self.decoder.eval()# encode source sentence and translate itenc1 = _encoder('fwd', x=x1, lengths=len1, langs=langs1, causal=False)enc1 = enc1.transpose(0, 1)x2, len2 = _decoder.generate(enc1, len1, lang2_id, max_len=int(1.3 * len1.max().item() + 5))langs2 = x2.clone().fill_(lang2_id)# free CUDA memorydel enc1# training modeself.encoder.train()self.decoder.train()# encode generate sentenceenc2 = self.encoder('fwd', x=x2, lengths=len2, langs=langs2, causal=False)enc2 = enc2.transpose(0, 1)# words to predictalen = torch.arange(len1.max(), dtype=torch.long, device=len1.device)pred_mask = alen[:, None] < len1[None] - 1  # do not predict anything given the last target wordy1 = x1[1:].masked_select(pred_mask[:-1])# decode original sentencedec3 = self.decoder('fwd', x=x1, lengths=len1, langs=langs1, causal=True, src_enc=enc2, src_len=len2)# loss_, loss = self.decoder('predict', tensor=dec3, pred_mask=pred_mask, y=y1, get_scores=False)

代码还是比较清晰的,对于mt_step,就是直接调用encoder和decoder进行正常的MT训练;而对于bt_step,则首先在eval模式下离线生成A->B’,而后再进行B’->A的正常MT训练。

  1. XNLI分类任务

这部分是多语言的分类任务,这里主要看不用翻译系统的方法,即先用MLM+TLM和多语言的单语语料及平行语料进行encoder的预训练,而后用纯英文的语料进行finetune。

预训练的部分和前面那个MT任务中的预训练一样,都是使用mlm_step这个函数,只不过在构建语料的时候,加上了使用平行语料进行mask的部分。

在finetune部分,是在顶层加入了一层Linear,用于三分类;而后将输入的两个句子进行拼接,进入分类层,代码如下:

self.proj = nn.Sequential(*[nn.Dropout(params.dropout),nn.Linear(self.embedder.out_dim, 3)]).cuda()(sent1, len1), (sent2, len2), idx = batch
sent1, len1 = truncate(sent1, len1, params.max_len, params.eos_index)
sent2, len2 = truncate(sent2, len2, params.max_len, params.eos_index)
x, lengths, positions, langs = concat_batches(sent1, len1, lang_id,sent2, len2, lang_id,params.pad_index,params.eos_index,reset_positions=False
)
y = self.data['en']['train']['y'][idx]# loss
output = self.proj(self.embedder.get_embeddings(x, lengths, positions, langs))
loss = F.cross_entropy(output, y)

五. 总结

优势

  1. 提供了多语预训练的思路,并且确实效果很好
  2. 几个预训练任务的设计和训练,都非常巧妙
  3. 对小语种的训练很有帮助,并且可以提供无监督的多语embedding
  4. 提供了源码和所有预训练模型

不足

  1. 语言比较少,而且基本都是针对下游任务的,是否不太通用?
  2. 论文整体思路比较不够clean,而且对于特定任务的介绍不够充分,导致理解起来比较困难(至少对于笔者这样的小白来说很困难~),有些需要看代码甚至要查阅资料才能知道如何处理的

传送门

论文:https://arxiv.org/pdf/1901.07291.pdf
源码:https://github.com/facebookresearch/XLM
博客:https://www.lyrn.ai/2019/02/11/xlm-cross-lingual-language-model/

XLM解读(论文 + PyTorch源码)相关推荐

  1. Transformer-XL解读(论文 + PyTorch源码)

    前言 目前在NLP领域中,处理语言建模问题有两种最先进的架构:RNN和Transformer.RNN按照序列顺序逐个学习输入的单词或字符之间的关系,而Transformer则接收一整段序列,然后使用s ...

  2. ELMo解读(论文 + PyTorch源码)

    ELMo的概念也是很早就出了,应该是18年初的事情了.但我仍然是后知后觉,居然还是等BERT出来很久之后,才知道有这么个东西.这两天才仔细看了下论文和源码,在这里做一些记录,如果有不详实的地方,欢迎指 ...

  3. 小白学习pytorch源码(二):setup.py最详细解读

    小白学习pytorch源码(二) pytorch setup.py最全解析 setup.py与setuptools setup.py最详细解读 setup.py 环境检查 setup.py setup ...

  4. PyTorch 源码解读之 torch.utils.data:解析数据处理全流程

    目录 0 前言 1 Dataset 1.1 Map-style dataset 1.2 Iterable-style dataset 1.3 其他 dataset 2 Sampler 3 DataLo ...

  5. pytorch源码解析2——数据处理torch.utils.data

    迭代器 理解 Python 的迭代器是解读 PyTorch 中 torch.utils.data 模块的关键. 在 Dataset, Sampler 和 DataLoader 这三个类中都会用到 py ...

  6. pytorch 测试每一类_DeepFM全方面解析(附pytorch源码)

    写在前面 最近看了DeepFM这个模型.把我学习的思路和总结放上来给大家和未来的自己做个参考和借鉴.文章主要希望能串起学习DeepFM的各个环节,梳理整个学习思路.以"我"的角度浅 ...

  7. 基于深度学习的文本分类6大算法-原理、结构、论文、源码打包分享

    导读:文本分类是NLP领域一项基础工作,在工业界拥有大量且丰富的应用场景.传统的文本分类需要依赖很多词法.句法相关的human-extracted feature,自2012年深度学习技术快速发展之后 ...

  8. 多智能体系统——竞争网络下异构多智能体系统的分组一致性问题 Group consensus of heterogeneous multi-agent system (附论文链接+源码Matlab)

    多智能体系统--竞争网络下异构多智能体系统的分组一致性问题 (附论文链接+源码Matlab) Yu F, Ji L, Yang S. Group consensus for a class of he ...

  9. 基于北斗导航定位系统的设计与实现(论文+程序设计源码+数据库文件)

    摘 要 改革开放新时代的到来,人们的生活发生了翻天覆地的变化,人们的娱乐方式变得更加丰富多彩,人们的活动圈子在不断的扩大,不仅仅局限于眼前的苟且,更向往着诗与远方,人们的活动圈子的扩大就意味着在特殊时 ...

最新文章

  1. 算法编程Algos Programming
  2. 【无人驾驶年度盘点】2018年15大单点技术机会,5大创业机遇,投资走向上游,行业极速细分
  3. 最新Golang安装教程(Linux环境 Ubuntu + Go) 附centos安装视频链接
  4. thinkphp extend.php,【ThinkPHP5.1】如何引用extend的类库
  5. jenkins slave problem :Unsupported major.minor version 52.0
  6. HDU1480 钥匙计数之二【递推】
  7. RAC-DG 安装总结
  8. 拓端tecdat|R语言分解商业周期时间序列数据:线性滤波器、HP滤波器、Baxter King滤波器、Beveridge Nelson分解等去趋势方法
  9. dtu连接mysql_Azure SQL的DTU和eDTU到底是个什么鬼
  10. 学习随笔——2.仿苹果官网效果,滚动缩放图片
  11. 2022年北京购房攻略一 (常识篇)
  12. 申请德国农工大学计算机案例,德州农工大学本科案例
  13. 如何批量重命名为不同文件名?
  14. Trophy Skin大排灯嫩肤仪是个小宝藏没错了
  15. ubuntu编译工程出现: fatal error: error writing to /tmp/cc1ezU8m.s: No space left on device
  16. 05、Python中转义字符与字符串
  17. 管理者的人品的重要性
  18. commons-codec使用简介
  19. 拉勾Java工程师P7训练营 高薪笔记
  20. 嵌入式行业怎么样,有什么好的就业方向?

热门文章

  1. 自媒体账号的流量推荐减少了很多?
  2. 软件公司的绩效管理和内部消耗
  3. 微信小程序全局生命周期和页面生命周期
  4. linux内核版本不一致,内核模块版本和内核版本不一致的处理方法
  5. 300左右蓝牙耳机推荐,口碑好的平价蓝牙耳机选购指南
  6. noj 1142 F 踩气球
  7. 怎么查看电脑bios启动类型
  8. 20个优秀的 Photoshop 网页制作教程
  9. java 数组怎么求和_java数组求和怎么解决?有哪些方式?
  10. 业务理解:深入业务是做好架构的前提