捋一捋XLNet

最近花了几天时间研究了下XLNet的源码和论文,它于2019年6月问世,是NLP(Natural Language Processing,自然语言处理)领域中的一个语言模型,通俗一点理解它就是一个让机器做阅读理解的算法。

看了一下介绍,阅读论文时需要熟悉AR(autoregressive,自回归)、AE(autoencoding,自编码)、BERT(Bidirectional Encoder Representations from Transformers)模型、transformer-XL等,因为XLNet是在它们的基础上进行了一些借鉴和方法的改善。

不过这些我暂时还不太了解,我的目的很简单,我只需要知道:

为什么会有XLNet?

XlNet跟他们的区别是什么?

XLNet做了什么?

作为一个实战派,先理解当前的项目,代码跑通,把项目转为己用。成为一个有用的调包侠之后,再慢慢搞懂它的原理(当然,这只是我天真的想法,哈哈).

回想了下我去年6月在做什么?

好像还沉浸在OCR(OpticalCharacter Recognition,光学字符识别)的海洋里无法自拔!

先从github把源码下载下来

git clone https://github.com/zihangdai/xlnet.git

大致浏览一下整个项目分布,再点开README.md边读边跟着操作。

README里先是对XlNet做了基本介绍,同BERT一样,XLNet也分为Pre-train(预训练)和Fine-tune(微调)两阶段。

于19年6月和7月分别发行了XLNet-Large和XLNet-Base两个预训练模型版本,同其他模型相比,在相同的数据集上XLNet表现出了较好的效果。

阅读理解结果

文字分类结果(错误率)

GLUE上的结果

先下载其中一个预训练模型:

Wget https://storage.googleapis.com/xlnet/released_models/cased_L-24_H-1024_A-16.zip

(这里下载的是XLNet-Large:24层,16个注意力头,1024个隐藏单元)

里面包含三个项目,分别是:

  • 一个TensorFlow检查点(xlnet_model.ckpt),其中包含预先训练的权重(实际上是3个文件)。

  • 一个句子件模型(spiece.model)用于(DE)标记化。

  • 一个配置文件(xlnet_config.json),用于指定模型的超参数。

由于XLNet需要做wordpiece切分和分词,需要装一个sentencepiece库,给它装上:

pip install sentencepiece

(注:sentencepiece需要编译工具Bazel依赖)

论文作者的项目环境为Tensorflow1.13.1+Python2,训练XLNet-Large是在512核心的TPUv3芯片上,看了一些帖子说是跑这个模型用了2.5天大概花了40多万人民币,还不包括调参期间的花费。

同时,XLNet只提供了英语的模型,在模型越来越大的情况下,如果要提供中文版的支持,可能需要近百万的预算去做pre-train,我等宵小只能坐等大厂放出基于中文语料pre-train的模型了。

                                                           实战:数据预处理

虽然不能从头开始训练,但我们可以先搭一个小demo试试,模型层数少建几层,目的是先把代码跑通,掌握整个运作流程。

这里先用python3+Tensorflow1.13.1的环境试一下.

继续往下读README.md.

要做训练,先要有数据,有了数据还要进行预处理,把它处理成机器能够理解的形式。随便找篇英文文档(test.txt)作为数据来源,如下:

It was a cold night.

The taxi driver didn't take even one passenger all day.
When he went by the railway station, he saw a young man coming out with twobags in his hands.
So he drove to him and asked, " where are you going, sir?"
"To the Red Hotel," the young man answered.<eop>

When the taxi driver heard this, he didn't feel happy any more.
........

​​​​这里对输入的文本文件有一些格式要求:

  • 每行是一个句子。

  • 空行表示文档末尾。

  • (可选)如果还想对段落结构建模,<eop>可以将其插入某些行的末尾(没有任何空格)以指示相应的句子在段落的结尾。

XLNet提供了一个数据预处理的脚本data_utils.py,在运行之前可以调整以下参数适配你自己的环境

python data_utils.py \
  --bsz_per_host=32 \
  --num_core_per_host=16 \
  --seq_len=512 \
  --reuse_len=256 \
  --input_glob=test.txt \ 
  --save_dir= traindata \ 
  --num_passes=20 \
  --bi_data=True \
  --sp_path=/data/xlnet_cased_L-24_H-1024_A-16/spiece.model \
  --mask_alpha=6 \
  --mask_beta=1 \
  --num_predict=85

有几个需要注意的参数:

  • Input_glob 输入的训练数据,写你自己的路径,如果有很多的话,可以用通配符* .txt。

  • save_dir 数据处理后的保存目录

  • sp_path  sentencepiece的模型,预训练模型下载后自带了一个,写你自己保存的路径。

预处理后的数据使用<eop>和<eod>表示段落结尾和文档结尾。

                                                   实战:预训练(pre-train)

接下来开始进行预训练(pre-train),这里提供了两个python脚本,train.py和train_gpu.py,两个的区别是train.py是在TPU上训练的代码,train_gpu.py在GPU上使用。

下面列出了一些重要的用于预训练XLNet-Large超参数信息:

python train_gpu.py 
 --record_info_dir = traindata/tfrecords \ 
 --train_batch_size = 1024 \ 
 --seq_len = 512 \ 
 --reuse_len = 256 \ 
 --mem_len = 384 \ 
 --perm_size = 256 \ 
 --n_layer = 24 \ 
 --d_model = 1024 \ 
 --d_embed = 1024 \ 
 --n_head = 16 \ 
 --d_head = 64 \ 
 --d_inner = 4096 \ 
 --untie_r = True \ 
 --mask_alpha = 6 \ 
 --mask_beta = 1 \ 
 --num_predict = 85

train_gpu.py导入的包可以看出,训练时调用了data_utils.py、model_utils.py、gpu_utils.py、function_builder.py这些脚本中大大小小的模块和函数。

                                                 实战:微调(fine-tuning)

训练结束后,开始进入微调(fine-tuning)环节,这里遇到最大的阻碍可能就是内存问题,一般普通的GPU(8GB)很难跑得起来大模型,即使每次batch=1,用XLNet-Large模型进行Fine-tuning也需要至少16GB的内存。

因此,作者推荐了减少batch大小(train_batch_size)或者减少最大序列长度(max_seq_length)等方法来缓解内存不够的情况,并附上了单个GPU(16GB)可以训练的模型的序列长度和batch大小对应关系。

这里写了三个微调(fine-tuning)脚本代码供你选择:run_classifier.py、run_race.py、run_squad.py.

如果你是分类/回归问题,可选择run_classifier.py、run_squad.py.

如果要做阅读理解问题,可选用run_race.py.

先来跑一下run_classifier.py,下载一个GLUE数据集放进data_dir,更改几个参数,开始进行微调。

python run_classifier.py \ 
  --do_train = False \ 
  --do_eval = True \ 
  --task_name = sts-b \ 
  --data_dir = /data/glue_data/STS-B \ 
  --output_dir = proc_data / sts-b \ 
  --model_dir = exp / sts-b \ 
  --uncased = False \ 
  --spiece_model_file = /data/xlnet_cased_L-24_H-1024_A-16/spiece.model \
  --model_config_path =/data/xlnet_cased_L-24_H-1024_A16/xlnet_config.json \ 
  --max_seq_length = 128 \ 
  --eval_batch_size = 8 \ 
  --num_hosts = 1 \ 
  --num_core_per_host = 1 \   # 使用的GPU数量。
  --eval_all_ckpt = True \   #允许save_steps在训练结束后评估所有保存的检查点(保存频率由控制)
  --is_regression = True

不同的脚本可下载不同的数据集来进行微调,要跑run_squad.py可以下载SQuAD数据集.

要执行run_race.py可下载RACE数据集。

最后,你还可以根据自己情况,做一些自定义的改变,as you like.

以上,就是所有从README.md中我们获取到的信息,整个项目的流程马马虎虎过了一遍,按照这个流程执行代码也马马虎虎可以跑通.

但是!

具体到数据预处理是怎样的一个处理,做了哪些操作?

数据训练时又经历了哪些过程,发生了怎么样的转变?

模型搭建是怎样的一个形式?

还是不了解。

现在需要着手做两件事:

    1.  根据执行顺序,一行行去读代码,搞懂它中间经历了什么操作;

    2.  通读论文,把原理搞懂,明白为什么。

想了想,还是读论文来的快些,先读一下吧.

打开论文,往下一滑,映入眼帘的就是AR(自回归)和AE(自编码)模型的俩长串公式

真是………

辣眼睛!

赶紧截个图发我姐妹,让她帮我搞定。

不到20秒,就发来解析。

这么一看,原来就是条件概率的思想嘛,只是多了几个数,最后求个最大似然概率的事。

我的姐妹真是太优秀了!

论文大致可分四个模块:

    一、基础知识回顾与现存问题介绍

        1.1 AR与AE介绍

        1.2 Bert 的痛点

    二、提出新方法

        2.1 排列组合获取上下文信息

        2.2 双流注意力

        2.3 部分预测

        2.4 借鉴Transformer-XL思想

        2.5 多段建模

    三、与其他语言模型进行对比

    四、实验部分及总结

                                           论文:(1)基础知识回顾与现存问题介绍

1.1 AR与AE介绍

在我们传统的无监督表征学习(Unsupervised representation learning)中,AR(autoregressive,自回归)与AE(autoencoding,自编码)是最成功的两种预训练语言模型。

AR方法是学习文本序列的条件概率,即学习已知上文预测下一个词或已知下文预测上一个词。

现在有一个文本序列.

它的推演公式如下:

X<t表示t时刻之前的所有x,也就是X1:t−1;

hθ(x1:t−1)是RNN编码的t时刻之前的隐状态;

e(x)是词x的embedding

很显然,AR语言模型的缺点:

  • 上下文依赖问题。只能参考一个方向的上下文,根据概率分布生成第一个词,然后根据第一个词生成第二个词,然后根据前两个词生成第三个词,……,直到生成整个句子。

而AE方法是一种无监督学习输入特征的方法,重构数据,把一部分训练数据[token]先隐藏[mask]掉,通过上下文的信息来预测被隐藏[mask]的数据[token]。

参考了双向整个句子的上下文,比AR方法好一点点。

假设这个文本序列还是.

它的推演公式如下:

表示输入里被隐藏[mask]的数据(添加了噪声);

表示被mask的原始值;

=1表示t时刻是一个Mask,需要恢复;

是一个Transformer,它把长度为T的序列X映射为隐状态的序列

1.2  Bert 的痛点

Bert作为AE的代表性模型,由于数据在pre-train时,会按一定比例引入[mask]标记,然而在实际生产数据中却并不可能做[mask],这直接导致了两个问题:

  • 独立性假设的风险。在AE的推演公式中,我们可以看到取最大似然概率时是约等号≈,也就是它做了一个独立性假设,假设每个被[mask]的数据[token]之间相互独立,这个显然并不成立,忽略了[mask]之间的相关性。

  • Pre-train阶段和fine-tune阶段数据分布不一致。同样由于引入[mask]的原因(还会输入噪声),两个阶段的训练数据分布不一致,将会影响fine-tune的效果。

现在的问题是,是否有一种模型,既能结合上下文,同时又能避免Bert由于[mask]标记而导致的独立性和数据分布不一致?

作者构造出了能够结合上下文的AR模型——XLNet!

                                                      论文:(2)提出新方法

2.1 排列组合获取上下文信息

排列组合的思想大概是我们高中时候就知道的,给定一个长度为T的序列,那么它的排列方法共有T!(T的阶乘)种排列方法。

现在假设序列T=x1x2x3,那么它共有3!=6种分解方法。

p(x2|x1x3)指的是已知第一个词是x1并且第三个词是x3的条件下第二个词是x2的概率,也就是说原来词的顺序是保持的。

对于传统的AR语言模型只能从左到右或从右往左学习一种方向的依赖关系,由于排列顺序的不同,这种排列语言模型会学习各种顺序的猜测方法,而且这个模型的参数是共享的,只要我们把序列T!全给遍历了,再学习下语言模型的参数,看起来关于上下文依赖的问题就迎刃而解了!

一个显而易见的问题又出现了!

我们算一下10!是多少? 

10!=10x9x8x7x6x5x4x3x2x1=362880010

这个计算量太大了,还只是10个词就组成了这么多组合。因此实际情况下只能随机采样部分排列,这里引入了数学符号来表示这个操作:

表示长度为T的序列的所有排列组成的集合,则z∈是一种排列方法;

表示排列的第t个元素;

z<t表示z的第1到第t-1个元素;

把公式解读一下就是:从所有的排列中采样一种,然后根据这个排列来分解联合概率成条件概率的乘积,然后加起来。

2.2 双流注意力

关于上下文依赖解决了,现在的关键问题是模型并不知道要预测的那个词(target)在原始序列中的位置。

为了解决这个问题,必须引入位置信息。

作者杨植麟说:新任务希望在预测下一个词时只能提供位置信息,不能提供内容相关的信息。因此模型希望同时做两件事,首先它希望预测自己到底是哪个字符,其次还要能预测后面的字符是哪个。

对于某个需要预测的目标词,我们既需要得到包含它信息以及位置的表征 h (用来进一步计算其他词的表征),又需要得到不包含它信息,只包含它位置的表征 g (用来做语境的表征)。

XLNet便提出了双通道自注意力(Two Stream Self-Attention),引入了两个Stream,同时计算内容表征通道 (ContentStream) hθ和语境表征通道 (Query Stream) gθ.

论文中也放了个图来直观的描述其计算过程:

左上图(a)表示content流的计算,左下图(b)是query流的计算;

右图(c)表示完整的计算过程。

2.3 部分预测

这里是一个很小的点,因为我们选用排列语言模型,虽然前面随机采样了,但它的计算量仍然不可小觑(排列很多),很难优化。

XLNet这里是把一个序列 Z划分为两段,只对一个句子的后一段进行预测。

具体的,就是取一个位置c,作为切分点,然后预测z>c后面的词。关于 c值的选取,作者给出了一个超参 K ,满足

从而有效的节省计算时间,作者设置 K=6 。

2.4 借鉴Transformer-XL思想

Transformer-XL是一个语言模型,可将其归类为AR(自回归)模型。在我们实际生产应用中,对于一段超长的序列(如长对话),我们会选择将其切分成一个个短的序列(sentence),然而这样却切断了它们之间的联系。

Transformer-XL的做法是把上一个短序列各层输入的一部分作为memory缓存,和当前各层的输入拼接,以获取上文的信息。

XLNet借鉴使用了相对位置编码和片段复发机制分别解决绝对位置编码无法处理的2个以上文本对应输入的task和算法效率问题,如果想深入了解,可能还需要取通读一下transformer-XL 的文章。

2.5 多段建模

这个部分主要阐述多段建模(Multiple Segments)无法用绝对位置编码解决的问题,其他好像没啥了。

到此为止,XLNet的核心思想已经比较清楚了:还是使用语言模型,但是为了解决双向上下文的问题,引入了排列语言模型。排列语言模型在预测时需要target的位置信息,因此通过引入Two-Stream,Content流编码到当前时刻的所有内容,而Query流只能参考之前的历史以及当前要预测的位置。最后为了解决计算量过大的问题,对于一个句子,我们只预测后面的1/K的词,再借鉴transformer-XL的思想,来处理下绝对位置编码问题。

综合来说,就是提出问题,解决问题,在解决问题的过程中又遇到了各种各样的问题,最后又用各种各样的方法解决问题,最后,成功。

                                               论文:(3)与其他模型对比

这章主要做了些讨论分析的工作,先将XLNet与Bert的异同点进行了比较:

  • 都是一个句子预测部分词,但原因不同。BERT使用的是Mask语言模型,因此只能预测部分词。而XLNet预测部分词是出于性能考虑,而BERT是随机的选择一些词来预测。

  • 关于条件独立的假设。BERT是约等号,假设被[mask]的词与非[mask]的词相互独立,这个假设并不总是成立,前面已介绍过。

再与其他语言模型比较一番,XLNet最大的优势就是通过输入序列的各种排列,同时学习到上下文的信息。

                                               论文:(4)实验部分及总结

XLNet用了各种数据集来进行pre-training,有BooksCorpus和英文的维基百科(两者共13GB),Ciga(16GB),ClueWeb2012-B和Common Crawl。

分别预训练了XLNet-large和XLnet-base,但模型仍然欠拟合(underfitting),如果继续训练的话loss还可以再下降。

然后还做了一个Ablation实验,为了发现哪些改进是有效的。

总结一下,XLNet是一种广义的AR预训练方法,它利用排列语言的建模目标,结合AR和AE方法的优点。开发了XLNet的神经网络结构,实现了与AR目标的无缝工作,包括对transformer-XL的集成和双流注意力机制的精心设计。XLNet在各项任务上比以往的预训练目标有了实质性的改进。

最近几年,以BERT 为代表的预训练算法得到了快速的发展,后续基本上所有的算法都采用了pre-train+Fine tune 的方法.

BERT 在 2018 年年底通过预训练打败了 NLP 上 11 个任务的经典算法;XLNet 在 2019 年提出来通过双向网络的方法超过了 BERT,再后来,ALBERT 又超过了 XLNet 和原始的 BERT.

清华大学计算机系唐杰教授在《人工智能下一个十年》的主题报告中说:从未来的趋势来看,人工智能将会有一个从感知到认知逐步发展的基本趋势。人工智能在感知方面取得的重要成果,得益于这些取得快速进展的算法,让人工智能一次又一次掀起浪潮.

 写在最后

如果你觉得自己单独研究论文太麻烦的话,有一个偷懒的办法,就是找前人。

毕竟XLNet出了已有一段时间,肯定有乐于分享见解的大佬,我们要做的就是动动手指,网上一搜,完事。

当然,这期间我自己也看了很多优秀大佬的分析指南,知乎上的见解、微信订阅号上科技大号的讨论、简书和CSDN上关于XLNet的研讨,还看了两个讲解视频。

对我帮助比较大的是博客上叫李理的博主,他共写了5篇关于XLNet的分析,一篇原理,四篇代码分析,那个代码分析细致到真的是逐行解读,看得出是一个认真且很有耐心的人。让我产生了因为阅读文章从而想要认识写文章的人这样强烈的好奇心,把他的博客翻了翻,试图寻找关于这个人的蛛丝马迹,可惜,哈哈,没找到。

如果要做代码分析,可直接查看他的。(真心推荐)

Anyway,向优秀的人学习!

相关链接

XLNet论文   https://arxiv.org/pdf/1906.08237.pdf

XLNet源码  https://github.com/zihangdai/xlnet

李理的博客  http://fancyerii.github.io/2019/06/30/xlnet-theory/

Transformer-XL论文  https://arxiv.org/abs/1901.02860

参考资料

XLNet原理解读,sliderSun:

https://blog.csdn.net/weixin_37947156/article/details/93035607

NLP论文解读,漆黑的羽毛:

https://zhuanlan.zhihu.com/p/74922547

XLNet原理,李理:

http://fancyerii.github.io/2019/06/30/xlnet-theory/

NLP论文解读:XLNet

https://www.jianshu.com/p/4b96dc092fb7

XLNet作者杨植麟:超越BERT,用AI赋能企业销售

https://www.infoq.cn/article/4Q_rH1X6zk6O2oCvjc1T?utm_source=related_read&utm_medium=article

认知推理:人工智能的下一波浪潮

https://baijiahao.baidu.com/s?id=1662605514651429471&wfr=spider&for=pc

备注

(该文章首发于个人维护微信公众号,一只小coder,一个女程序员的知识屋,欢迎和我一起交流学习,一起变得更厉害吧!)

猜你喜欢

转载自blog.csdn.net/qq_41127332/article/details/106090171