预训练语言模型小酌🍺
- 预训练原理
- 预训练模型
- Transformer介绍
- GPT (2018)
- BERT (2018)
- UniLM (2019)
- Transformer-XL (2019)
- XLNet (2019)
- MASS (2019)
- RoBERTa (2019)
- BART (2019)
- T5 (2019)
- ERNIE (Baidu, 2019)
- Albert
- BERT 拓展
本文为 Pre-trained Models for Natural Language Processing: A Survey 和相关模型的笔记
在当下的 NLP 研究领域,随着计算机算力的不断增强,越来越多的通用语言表征的预训练模型(Pre-trained Models,PTMs)逐渐涌现出来。这对下游的 NLP 任务非常有帮助,可以避免大量从零开始训练新的模型。PTM 大致可以分为两代:
- 第一代 PTM 旨在学习词嵌入。由于下游任务不在需要这些模型,因此为了计算效率,这些模型往往采用浅层模型,例如 Skip-Gram,GloVe等。尽管这些模型可以捕获词的语义,但由于未基于上下文环境,因此不能够捕捉到更深层次的概念,例如:句法结构,语义角色,指代等等。
- 第二代 PTM 专注于学习基于上下文的词嵌入,例如 CoVe 4,ELMo 5,OpenAI GPT 6 和 BERT 7 等。这些学习到的编码器在下游任务中仍会用于词在上下文中的语义表示。
预训练原理
语言表示学习
独热编码(One-hot Encoding):最简单的一种单词表示法,一个单词用长度为n的向量表示,其中只有一个位置为1,其余位置为0,n为语料中词库的大小。
马尔可夫与语言模型:人类历史上第一个对语言模型进行研究,他提出n元模型(n-gram model),假设序列上每个位置的单词只依赖于前n−1个位置的单词 $p(w1,w2,…,w_n)=\prod_{i=1}^np(w_i|w_{i-n+1},w_{i-n+2},…,w_{i-1})$
香农与语言模型:假设语言(单词序列)是由一个随机过程产生的数据,n元模型的熵的定义为:$H_n(p,q)=-\sum{p(w1,w2,…,w_n)·q(w1,w2,…,w_n)}$ ,这里p表示生成数据的真实概率分布。熵表示一个概率分布的不确定性,交叉熵表示一个概率分布相对于另一个概率分布的不确定性。熵是交叉熵的下界。如果一个语言模型比另一个语言模型能更准确地预测单词序列数据,那么它就应该有更小的交叉熵。香农的研究为语言模型学习提供了评价工具。
神经语言模型:n元模型的表示和学习能力是有限的。传统的方法是从语料中统计n元模型中的条件概率,对未见过的n元组的概率通过平滑的方法估算。模型的参数个数是指数级的 $O(V^n)$,其中 $V$ 是词表的大小。当$n$ 增大时,无法准确地学到模型的参数。Bengio等人的神经语言模型从两个方面对n元模型予以改进。一是用一个低维的实值向量表示一个单词或单词的组合;二是在使用词向量的基础上,通过神经网络来表示语言模型,大幅减少模型的参数. $p(w_i|w_{i-n+1},w_{i-n+2},…,w_{i-1})=f_\theta(w_{i-n+1},w_{i-n+2},…,w_{i-1})$
预训练语言模型:上述类型的嵌入主要有两个缺陷:一是嵌入是静态的,词在不同的上下文中的嵌入表示是相同的,因此无法处理一词多义;二是未登录词(out-of-vocabulary,OOV)问题。为了解决上述问题,基于上下文的动态词嵌入出现了。优势:可作为初始词向量、解决处理一词多义问题、能增强模型的泛化能力
神经上下文编码器
神经上下文编码器大致可以分为 3 类:
基于卷积的模型:基于卷积的模型通过卷积操作从一个词的邻居中聚合局部信息来捕获这个词的含义
基于序列的模型:基于序列的模型采用 RNNs(LSTM和 GRU) 来捕获词的上下文信息。实际中,我们采用双向的 RNNs 从词的两端收集信息,不过整体效果容易收到长期依赖问题的影响。
基于图的模型:基于图的模型将字作为图中的一个节点来学习上下文表示,这个图通常是一个词之间预定义的语言结构,例如:语法结构或语义关系。尽管基于语言学的图结构能提供有用的信息,但如何构建一个好的图结构则成为了难题。除此之外,基于语言学的图结构需要依赖专家知识和外部工具,例如:依存句法分析等。事实上,我们会采用一个更直接的方式去学习任意两个词之间的关系,通常连接的权重可以通过自注意力机制自动计算得出。Transformer是一个采用了全链接自注意力架构的实现,同时也采用了位置嵌入(positional embedding),层标准化(layer normalization)和残差连接(residual connections)等网络设计理念。
为什么预训练
对于大多数的 NLP 任务,构建一个大规模的有标签的数据集是一项很大的挑战。相反,大规模的无标签语料是相对容易构建的,为了充分利用这些无标签数据,我们可以先利用它们获取一个好的语言表示,再将这些表示用于其他任务。预训练的好处如下:
- 预训练可以从大规模语料中学习得到通用的语言表示,并用于下游任务。
- 预训练提供了更优的模型初始化方法,有助于提高模型的泛化能力和加速模型收敛。
- 预训练可以当作是在小数据集上一种避免过拟合的正则化方法。
预训练任务
预训练任务对于学习语言的通用表示来说至关重要。通常情况下,预训练任务具有挑战性,同时需要大量训练数据。我们将预训练任务划分为 3 类:
- 监督学习,即从包含输入输出对的训练数据中学习一个由输入到输出的映射函数。
- 非监督学习,即从无标签数据获取一些固有的知识,例如:聚类,密度,潜在表征等。
- 自监督学习,是监督学习和非监督学习的混合体,核心思想是对于输入的一部分利用其他部分进行预测。
下图展示了预训练模型的分类和部分代表模型:
应用于下游任务
选择合适的预训练任务,模型架构和语料
不同的 PTMs 在相同的下游任务上有着不同的效果,这是因为 PTMs 有着不同的预训练任务,模型架构和语料。
- 目前,语言模型是最流行的预训练任务,同时也可以有效地解决很多 NLP 问题。但是不同的预训练任务有着自己的侧重,在不同的任务上会有不同的效果。例如:NSP 任务使得 PTM 可以理解两句话之间的关系,因此 PTM 可以在例如问答(Question Answering,QA)和自然语言推理(Natural Language Inference,NLI)等下游任务上表现更好。
- PTM 的网络架构对下游任务也至关重要。例如:尽管 BERT 可以处理大多数自然语言理解任务,对其很难生成语言。
- 下游任务的数据分布应该和 PTM 训练所用语料相似。目前,大量现成的 PTM 仅可以快速地用于特定领域或特定语言的下游任务上。
选择合适的网络层
给定一个预训练的模型,不同的网络层捕获了不同的信息,例如:词性标记(POS tagging),语法(parsing),长期依赖(long-term dependencies),语义角色(semantic roles),指代(coreference)等。Tenney 22 等人发现 BERT 表示方式类似传统的 NLP 流程:基础的句法信息出现在浅层的网络中,高级的语义信息出现在更高的层级中。
令 H(l)(1≤l≤L) 表示共 L 层的预训练模型的第 l 层表示,g(⋅) 表示用于特定任务的的模型。一般有 3 中情况选择表示:
- Embedding Only:一种情况是仅选用预训练模型的静态嵌入,模型的其他部分仍需作为一个任务从头训练。这种情况不能够获取到一些有用的深层信息,词嵌入仅能够捕获词的语义信息。
- Top Layer:最简单有效的方式是将网络的顶层表示输入到模型中 g(H(L))。
- All Layers:另一种更灵活的方式是自动选择最合适的层,例如 ELMo:(5)rt=γ∑l=1Lαlht(l)其中 αl 是层 l 的 softmax 归一的权重,γ 是用于缩放预训练模型输出向量的一个标量值,再将不同层的混合输出输入到后续模型中 g(rt)。
是否微调
目前,主要有两种方式进行模型迁移:特征提取(预训练模型的参数是固定的)和模型微调(预训练模型的参数是经过微调的)。当采用特征提取时,预训练模型可以被看作是一个特征提取器。除此之外,我们应该采用内部层作为特征,因为他们通常是最适合迁移的特征。尽管两种不同方式都能对大多数 NLP 任务效果有显著提升,但以特征提取的方式需要更复杂的特定任务的架构。因此,微调是一种更加通用和方便的处理下游任务的方式。
开放资源
PTMs 开源实现:
项目 | 框架 | PTMs |
---|---|---|
word2vec | - | CBOW, Skip-Gram |
GloVe | - | Pre-trained word vectors |
FastText | - | Pre-trained word vectors |
Transformers | PyTorch & TF | BERT, GPT-2, RoBERTa, XLNet, etc. |
Fairseq | PyTorch | English LM, German LM, RoBERTa, etc. |
Flair | PyTorch | BERT, ELMo, GPT, RoBERTa, XLNet, etc. |
AllenNLP | PyTorch | ELMo, BERT, GPT-2, etc. |
FastNLP | PyTorch | BERT, RoBERTa, GPT, etc. |
Chinese-BERT | - | BERT, RoBERTa, etc. (for Chinese) |
BERT | TF | BERT, BERT-wwm |
RoBERTa | PyTorch | |
XLNet | TF | |
ALBERT | TF | |
T5 | TF | |
ERNIE(THU) | PyTorch | |
ERNIE(Baidu) | PaddlePaddle | |
Hugging Face | PyTorch & TF | 很多… |
论文列表和 PTMs 相关资源:
资源 | URL |
---|---|
论文列表 | https://github.com/thunlp/PLMpapers |
论文列表 | https://github.com/tomohideshibata/BERT-related-papers |
论文列表 | https://github.com/cedrickchee/awesome-bert-nlp |
Bert Lang Street | https://bertlang.unibocconi.it |
BertViz | https://github.com/jessevig/bertviz |
预训练模型
1. Transformer 💡
参考《 Attention is All You Need》浅读(简介+代码)》、Self-attenstion、 [Transformer中的Self-attenstion、Transformer实现细节、The Annotated Transformer “带注释”版本的pytorch代码详细走读
① Transformer摆脱了nlp任务对于rnn,lstm的依赖,在长距离上的建模能力更强;② 使用了 self-attention
可以并行化地对上下文进行建模,提高了训练和推理的速度;③ Transformer也是后续更强大的nlp预训练模型的基础(bert系列使用了transformer的encoder,gpt系列transformer的decoder)
为了方便学习,我将编码器分为 4
个部分,依次讲解。
1.1 Position Embedding
我们知道,文字的先后顺序,很重要。
比如吃饭没
、没吃饭
、没饭吃
、饭吃没
、饭没吃
,同样三个字,顺序颠倒,所表达的含义就不同了。
文字的位置信息很重要,Tranformer
没有类似 RNN
的循环结构,没有捕捉顺序序列的能力。
注:因为self-attention是位置无关的,无论句子的顺序是什么样的,通过self-attention计算的token的hidden embedding都是一样的,导致模型并不能捕捉序列的顺序!
为了保留这种位置信息交给 Tranformer
学习,我们需要用到位置嵌入。
加入位置信息的方式非常多,最简单的可以是直接将绝对坐标 0,1,2
编码。
Tranformer
采用的是 sin-cos
规则,使用了 sin
和 cos
函数的线性变换来提供给模型位置信息:
上式中 pos
指的是句中字的位置,取值范围是 [0, 𝑚𝑎𝑥 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛𝑔𝑡ℎ)
,i
指的是字嵌入的维度, 取值范围是 [0, 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛)
。 就是 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛
的大小。
上面有 sin
和 cos
一组公式,也就是对应着 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛
维度的一组奇数和偶数的序号的维度,从而产生不同的周期性变化。
可以用代码,简单看下效果。
1 |
|
最后,将字向量和 位置嵌入
相加,送给下一层。
绝对位置编码 vs. 相对位置编码
- 绝对位置编码是相对简单的一种方案,①可以直接将位置编码当作可训练参数,比如最大长度为512,编码维度为768,那么就初始化一个512×768的矩阵作为位置向量,让它随着训练过程更新。现在的BERT、GPT等模型所用的就是这种位置编码;②三角函数式位置编码,一般也称为Sinusoidal位置编码,Transformer所使用的方法;③先接一层RNN学习位置信息,然后再接Transformer,那么理论上就不需要加位置编码了。
- 相对位置并没有完整建模每个输入的位置信息,而是在算Attention的时候考虑当前位置与被Attention的位置的相对距离,由于自然语言一般更依赖于相对位置,所以相对位置编码通常也有着优秀的表现。对于相对位置编码来说,它的灵活性更大,更加体现出了研究人员的“天马行空”。
1.1 Attention层
Attention层的好处是能够==一步到位捕捉到全局的联系==,因为它直接把序列两两比较(代价是计算量变为 $O(n^2)$,当然由于是纯矩阵运算,这个计算量相当也不是很严重);相比之下,RNN需要一步步递推才能捕捉到,而CNN则需要通过层叠来扩大感受野,这是Attention层的明显优势。
Google给出的Attention的定义:
逐个向量来看:
其中,$\boldsymbol{Q} \in \mathbb{R}^{n \times d_{k}}, \boldsymbol{K} \in \mathbb{R}^{m \times d_{k}}, \boldsymbol{V} \in \mathbb{R}^{m \times d_{v}} , Z是归一化因子 $
过程:$q,k,v$ 分别是 $query,key,value$ 的简写,$K,V$ 是一一对应的,它们就像是key-value的关系,那么上式的意思就是==通过 $qt$ 这个query,通过与各个 $ks$ 内积的并softmax的方式,来得到 $qt$ 与各个$vs$ 的相似度,然后加权求和,得到一个 $dv$ 维的向量==。其中因子 $\sqrt{d_k}$ 起到调节作用,使得内积不至于太大(太大的话softmax后就非0即1了,不够“soft”了)。
结果:将$n×d_k$的序列$Q$编码成了一个新的$n×dv$的序列.
所谓Self Attention,其实就是$Attention(X,X,X)$,$X$ 就是前面说的输入序列。也就是说,在序列内部做Attention,寻找序列内部的联系。
1.2 Multi-Head Attention
把$Q,K,V$ 通过参数矩阵映射一下,然后再做 Attention,把这个过程重复做 $h$ 次,结果拼接起来:
最后得到一个 $n×(hd_v)$ 的序列。==所谓“多头”(Multi-Head),就是只多做几次同样的事情(参数不共享),然后把结果拼接==
Transformer Encoder 的具体流程
Inputs是经过padding的输入数据,大小是 [batch size, max seq length]
初始化embedding matrix,通过==embedding lookup==将Inputs映射成token embedding,大小是[batch size, max seq length, embedding size]
通过sin和cos函数构造位置编码,并加到token embedding中,然后dropout。
多头注意力机制
输入token embedding $X$,通过Dense生成 $Q,K,V$,大小是[batch size, max seq length, embedding size],然后按第2维split成heads的个数,并按第0维拼接,生成新的 $Q,K,V$,大小是[num heads\batch size, max seq length, embedding size/num heads]*,完成多头的操作
将 $K$ 的第1维和第2维进行转置,然后 $Q$ 和转置后的 $K$ 的进行点积,结果的大小是 [num heads \batch size, max seq length, max seq length]*
将<4.2>的结果除以hidden size的开方(在transformer中,hidden size=embedding size),完成scale的操作。
将<4.3>中padding的点积结果置成一个很小的数 $(-2^{32}+1)$,完成mask操作,后续softmax对padding的结果就可以忽略不计了 √
将经过mask的结果进行softmax操作:
- 将softmax的结果和 $V$ 进行点积,得到attention的结果,大小是 [num heads\batch size, max seq length, hidden size/num heads]*:
- 将attention的结果按第0维split成heads的个数,并按第2维concat,生成multi-head attention的结果,大小是 [batch size, max seq length, hidden size]:
- 将token embedding和multi-head attention的结果相加(残差),并进行Layer Normalization。
- 将<5>的结果经过2层Dense,其中第1层的
activation=relu
,第2层activation=None
。
transformer一问一答
Q:为什么存在Positional Embedding?在该结构中以何种形式表示?
A:Attention机制与CNN结构一样,无法表示文本的时序型,因此相比于LSTM结构,在NLP领域效果要差一些,而加入位置信息,相当于给予了时序特性。
Q:为什么<4.2>在做attention时,需要进行Scale?
QK进行点击之后,值之间的方差会较大,也就是大小差距会较大;如果直接通过Softmax操作,会导致大的更大,小的更小;进行缩放,会使参数更平滑,训练效果更好。
Q:为什么<5>要将multi-head attention的输入和输出相加?
类似于resnet中的残差学习单元,有ensemble的思想在里面,解决网络退化问题
Q:为什么multi-head attention后面要加一个ffn?
类比cnn网络中,cnn block和fc交替连接,效果更好。相比于单独的multi-head attention,在后面加一个ffn,可以提高整个block的非线性变换的能力。
2. GPT (2018)
https://www.bookstack.cn/read/huaxiaozhuan-ai/spilt.4.8c42358a11926b2f.md
gpt在bert之前就发表了,使用了transformer decoder作为预训练的框架。在看到了decoder只能get上文信息,不能get下文信息的缺点之后,bert改用了transformer encoder作为预训练的框架,能够同时get上下文信息,获得了巨大的成功。
给定一个语料 $U={u_1,…,u_n}$,使用标准的语言建模目标来最大化如下似然:
其中,k 为上下文窗口的大小,条件概率 P 通过参数为 Θ 的神经网络进行建模。GPT 中使用了一个多层的 Transformer Decoder 作为语言模型。模型首先对输入上下文词条应用多头自注意力机制,再通过按位置的前馈层产生目标词条的输出分布:
其中,$U=(u_{−k},…,u_{−1})$ 为词条的上下文向量,n 为网络层数,$W_e$ 为词条的嵌入矩阵,$W_p$ 为位置嵌入矩阵。
给定一个有标签的数据集 C,其中包含了输入词条序列 $x_1,…,x_m$ 和对应的标签 y。利用上述预训练的模型获得输入对应的最后一个 Transformer 的激活输出 $h_l^m$,之后再将其输入到一个参数为 $W_y$ 的线性输入层中预测 y:
模型通过最小化如下损失进行优化:
研究还发现将语言建模作为微调的附加目标可以帮助提高模型的泛化能力,同时可以加速模型收敛。GPT 中采用如下的优化目标:
GPT 网络架构示意图如下:
3. BERT (2018) 💡
https://zhuanlan.zhihu.com/p/46833276
- BERT BASE:L=12,H=768,A=12,参数总量为 100 M
- BERT LARGE:L=24,H=1024,A=16,参数总量为 340 M
前言
BERT 是一种语境化的词(字)嵌入模型
BERT 是基于Transformer的深度双向语言表征模型
BERT预训练过程包含两个不同的预训练任务,分别是Masked Language Model和Next Sentence Prediction任务。
Masked Language Model(MLM)
通过随机掩盖一些词(替换为统一标记符[MASK]),然后预测这些被遮盖的词来训练双向语言模型
Next Sentence Prediction(NSP)
为了训练一个理解句子间关系的模型,引入一个下一句预测任务。
模型结构
两个预训练任务
在预训练阶段,BERT 采用了两个无监督预测任务:
1. Masked Language Model
不同于一般的仅利用 [MASK]
进行遮挡,BERT 选择采用 80% 的 [MASK]
,10% 的随机词和 10% 保留原始词的方式对随机选择的 15% 的词条进行遮挡处理。由于编码器不知会预测哪个词或哪个词被随机替换了,这迫使其必须保留每个输入词条的分布式上下文表示。同时 1.5% 的随机替换也不会过多的损害模型的理解能力。
- 80%的时间中:将选中的词用[MASK]token来代替,例如
1 |
|
- 10%的时间中:将选中的词用任意的词来进行代替,例如
1 |
|
- 10%的时间中:选中的词不发生变化,例如
1 |
|
这样存在另一个问题在于在训练过程中只有15%的token被预测,正常的语言模型实际上是预测每个token的,因此Masked LM相比正常LM会收敛地慢一些,后面的实验也的确证实了这一点。
2. Next Sentence Prediction
一些重要的下游任务,例如问答(Question Answering,QA)和自然语言推断(Natural Language Inference,NLI)是基于两个句子之间关系的理解,这是语言建模无法直接捕获的。BERT 通过训练一个预测是否为下一个句子的二分类任务来实现,对于一个句子对 A 和 B,50% 的 B 是句子 A 真实的下一句,剩余 50% 为随机抽取的。
输入表示
BERT 的输入表示既可以表示一个单独的文本序列,也可以表示一对文本序列(例如:问题和答案)。对于一个给定的词条,其输入表示由对应的词条嵌入,分割嵌入和位置嵌入三部分加和构成,如下图所示:
- Token embedding 表示当前词的embedding
- Segment Embedding 表示当前词所在句子的index embedding
- Position Embedding 表示当前词所在位置的index embedding
为了一个输入能够针对两个任务,输入构造规则如下:
- 为了能够同时表示单句子和句子对,多句子(例如QA中的Q/A)需要进行拼接作为单个句子,用segment embedding和[SEG]来进行区分
- 句子第一个token总是有特殊含义,例如分类问题中是类别,如果不是分类问题那么就忽略
- 三个embedding进行sum得到输入的向量
模型训练
BERT预训练对于算力有着极大要求:
- 开启混合精度实现训练加速;
- 在通用中文语料基础上加入领域业务语料进行模型预训练,完成领域迁移;
- 预训练过程中尝试融入知识图谱中的实体信息,采用全词MASK训练策略;
通过在业务数据上进行微调,支持不同类型的业务需求。
基于Horovod的分布式训练方案…..
- 模型轻量化
- 低精度量化。在模型训练和推理中使用低精度(FP16甚至INT8、二值网络)表示取代原有精度(FP32)表示。
- 模型裁剪和剪枝。减少模型层数和参数规模。
- 模型蒸馏。通过知识蒸馏方法[22]基于原始BERT模型蒸馏出符合上线要求的小模型。
模型fine-tune
这里fine-tuning之前对模型的修改非常简单,例如针对sequence-level classification problem(例如情感分析),取第一个token的输出表示,喂给一个softmax层得到分类结果输出;对于token-level classification(例如NER),取所有token的最后层transformer输出,喂给softmax层做分类。总之不同类型的任务需要对模型做不同的修改,但是修改都是非常简单的,最多加一层神经网络即可。如下图所示
Bert和transformer在embedding上的差异
transformer的embedding由2部分构成:
- 一个是token embedding,通过embedding matrix lookup到token_ids上生成表示token的向量;
- 一个是position embedding,是通过sin和cos函数创建的定值向量。
而bert的embedding由3部分构成:
- 第一个同样是token embedding,通过embedding matrix lookup到token_ids上生成表示token的向量;
- 第二个是segment embedding,用来表达当前token是来自于第一个segment,还是第二个segment,因此segment vocab size是2;
- 第三个是position embedding,与transformer不同的是,bert创建了一个position embedding matrix,通过position embedding matrix lookup到token_ids的位置上生成表示token位置的位置向量。
transformer在embedding之后跟了一个dropout,但是bert在embedding之后先跟了一个layer normalization,再跟了一个dropout。
bert在token序列之前加了一个特定的token
“[cls]”
,这个token对应的向量后续会用在分类任务上;如果是句子对的任务,那么两个句子间使用特定的token“[seq]”来分割。
Bert和transformer在loss上的差异
transformer的loss是在decoder阶段计算的,loss的计算方式是transformer的<19>。
bert预训练的loss由2部分构成,- 一部分是==NSP==的loss,就是token“[cls]”经过1层Dense,然后接一个二分类的loss,其中0表示segment B是segment A的下一句,1表示segment A和segment B来自2篇不同的文本;
- 另一部分是==MLM==的loss,segment中每个token都有15%的概率被mask,而被mask的token有80%的概率用“
”表示,有10%的概率随机替换成某一个token,有10%的概率保留原来的token ,被mask的token经过encoder后乘以embedding matrix的转置会生成在vocab上的分布,然后计算分布和真实的token的one-hot形式的cross entropy,最后sum起来当作loss。
这两部分loss相加起来当作bert的 total loss,利用adam进行训练。bert fine-tune的loss会根据任务性质来设计,例如分类任务中就是token“[cls]”经过1层Dense,然后接了一个二分类的loss;例如问题回答任务中会在paragraph上的token中预测一个起始位置,一个终止位置,然后以起始位置和终止位置的预测分布和真实分布为基础设计loss;例如序列标注,预测每一个token的词性,然后以每一个token在词性的预测分布和真实分布为基础设计loss。
bert在encoder之后,在计算NSP和MLM的loss之前,分别对NSP和MLM的输入加了一个Dense操作,这部分参数只对预训练有用,对fine-tune没用。而transformer在decoder之后就直接计算loss了,中间没有Dense操作。
Bert的技术细节
- 为什么bert需要额外的segment embedding?
因为bert预训练的其中一个任务是判断segment A和segment B之间的关系,这就需要embedding中能包含当前token属于哪个segment的信息,然而无论是token embedding,还是position embedding都无法表示出这种信息,因此额外创建一个segment embedding matrix用来表示当前token属于哪个segment的信息,segment vocab size就是2,其中index=0表示token属于segment A,index=1表示token属于segment B。
- 为什么transformer的embedding后面接了一个dropout,而bert是先接了一个layer normalization,再接dropout?
LN是为了解决梯度消失的问题,dropout是为了解决过拟合的问题。在embedding后面加LN有利于embedding matrix的收敛。
- 为什么在multi-head attention中,bert不仅会concat<4.6>的attention的结果,还会把前N-1个encoder block中attention的结果都concat进来?
有ensemble的思路在里面,比起单纯只用第N个encoder block中的attention结果,将前N个encoder block中的attention结果concat起来显然能够get到更多的信息,而下一步的linear操作又将结果的大小重新变回[batch size, max seq length, hidden size]。该问题和transformer的问题3.4的本质是一样的,通过ensemble可以得到更多的信息。
为什么token被mask的概率是15%?为什么被mask后,还要分3种情况?
15%的概率是通过实验得到的最好的概率,xlnet也是在这个概率附近,说明在这个概率下,既能有充分的mask样本可以学习,又不至于让segment的信息损失太多,以至于影响mask样本上下文信息的表达。然而因为在下游任务中不会出现token“
”,所以预训练和fine-tune出现了不一致,为了减弱不一致性给模型带来的影响,被mask的token有80%的概率用“ ”表示,有10%的概率随机替换成某一个token,有10%的概率保留原来的token,这3个百分比也是多次实验得到的最佳组合,在这3个百分比的情况下,下游任务的fine-tune可以达到最佳的实验结果。
BERT 可以用在哪?
- Just take it as a kind of embedding (contextualized)
- Wherever you use word embedding, use BERT instead!
- Eg. Text classifacation, seqence labeling, sentence generation,machine reading comprehension, natural language inference …
细粒度情感分析
BERT的总结
- BERT采用Masked LM + Next Sentence Prediction作为pre-training tasks, 完成了真正的Bidirectional LM
- BERT模型能够很容易地Fine-tune,并且效果很好,并且BERT as additional feature效果也很好
- 模型足够泛化,覆盖了足够多的NLP tasks
4. UniLM (2019)
Q:UniLM模型介绍
A:通过不同的掩码来控制预测单词的可见上下文词语数量,实现不同的语言模型的联合训练。
单向语言模型:分为从左到右和从右向左两种,从左到右,即仅通过被掩蔽token的左侧所有本文来预测被掩蔽的token;从右到左,则是仅通过被掩蔽token的右侧所有本文来预测被掩蔽的token。
双向语言模型:与BERT模型一致,在预测被掩蔽token时,可以观察到所有的token。
序列到序列语言模型:如果被掩蔽token在第一个文本序列中,那么仅可以使用第一个文本序列中所有token,不能使用第二个文本序列的任何信息;如果被掩蔽token在第二个文本序列中,那么使用一个文本序列中所有token和第二个文本序列中被掩蔽token的左侧所有token预测被掩蔽token。
Q:UniLM模型如何掩码?
A:token掩码的概率为15%,在被掩掉的token中,有80%使用[MASK]替换,10%使用字典中随机词进行替换,10%保持原来token不变,与BERT模型一致。此外,在80%的情况下,每次随机掩掉一个token,在剩余的20%情况下,掩掉一个二元token组或三元token组。
Q:如何训练UniLM模型?
A:使用1/3的数据进行双向语言模型优化,1/3的数据进行序列到序列语言模型优化,1/6的数据进行从左向右的单向语言模型优化,1/6的数据进行从右向左的单向语言模型优化
Q:如何实现训练代码?
A:每个batch为一个任务,2个双向语言模型任务,2个序列到序列语言模型任务,1个左向右的单向语言模型任务,1个从右向左的单向语言模型,每跑一个任务进行一次累计梯度,跑完一轮所有任务,执行一次反向传播。
UniLM论文见:https://arxiv.org/abs/1905.03197
UniLM论文解读见:https://zhuanlan.zhihu.com/p/113380840
5. Transformer-XL (2019)
将 Transformer 或注意力机制应用到语言建模中的核心问题是如何训练 Transformer 使其有效地将一个任意长文本编码为一个固定长度的表示。Transformer-XL 将整个语料拆分为较短的段落,仅利用每段进行训练并忽略之前段落的上下文信息。这种方式称之为 Vanilla Model 27,如下图所示:
在这种训练模式下,无论是前向还是后向信息都不会跨越分割的段落进行传导。利用固定长度的上下文主要有两个弊端:
- 这限制了最大依赖的长度,虽然自注意力机制不会像 RNN 一样受到梯度弥散的影响,但 Vanilla Model 也不能完全利用到这个优势。
- 虽然可以利用补全操作来实现句子或其他语义的分割,但实际上通常会简单的将一个长文本截断成一个固定长度的分割,这样会产生上下文分裂破碎的问题。
为了解决这个问题,Transformer-XL 采用了一种循环机制的 Transformer。在训练阶段,在处理新的分割段落时,之前分割分部分的隐含状态序列将被固定(fixed)和缓存(cached)下来作为一个扩展的上下文被复用参与计算,如下图所示:
虽然梯度仍仅限于这个分割段落内部,但网络可以从历史中获取信息,从而实现对长期依赖的建模。令两个长度为 L 的连续分割段落为 $\mathbf{s}_{\tau} = \left[x_{\tau, 1}, \dotsc, x_{\tau, L}\right]$ 和 $\mathbf{s}_{\tau + 1} = \left[x_{\tau + 1, 1}, \dotsc, x_{\tau + 1, L}\right]$,第 τ 段分割 sτ 的第 n 层隐含状态为 $\mathbf{h}^n_{\tau} \in \mathbb{R}^{L \times d}$,其中 d 为隐含维度。则对于分割段落 $s_{τ+1}$ 的第 n 层隐含状态通过如下方式进行计算:
其中,$SG(⋅)$ 表示停止梯度,$\left[\mathbf{h}_u \circ \mathbf{h}_v\right]$ 表示将两个隐含序列按照长度维度进行拼接,W 为模型的参数。与一般的 Transformer 相比,最大的不同在于 $\mathbf{k}^n_{\tau + 1}$ 和 $\mathbf{v}^n_{\tau + 1}$ 不仅依赖于 $\tilde{\mathbf{h}}^{n-1}_{\tau - 1}$ 还依赖于之前分割段落的 $\mathbf{h}^{n-1}_{\tau}$ 缓存。
在标准的 Transformer 中,序列的顺序信息通过位置嵌入$\mathbf{U} \in \mathbb{R}^{L_{\max} \times d}$ 提供,其中第 i 行 $U_i$ 对应一个分割文本内部的第 i 个绝对位置,$L_max$ 为最大可能长度。在 Transformer-XL 中则是通过一种相对位置信息对其进行编码,构建一个相对位置嵌入 $\mathbf{R} \in \mathbb{R} ^{L_{\max} \times d}$,其中第 i 行 $R_i$ 表示两个位置之间相对距离为 i 的嵌入表示。
对于一般的 Transformer,一个分割段落内部的 qi 和 kj 之间的注意力分数可以分解为:
利用相对位置思想,变化如下:
- 首先,利用相对位置 $R_{i−j}$ 替代绝对位置嵌入 Uj,这里 R 采用的是无需学习的 sinusoid 编码矩阵 14。
- 其次,引入了一个可训练的参数 $\textcolor{red}{u} \in \mathbb{R}^d$ 用于替换 $\mathbf{U}^{\top}_i \mathbf{W}^{\top}_q$。类似的,对于$\mathbf{U}^{\top} \mathbf{W}^{\top}_q$ 使用一个可训练的 $\textcolor{red}{v} \in \mathbb{R}^d$ 替换。
- 最后,有意地划分了两个权重矩阵 $\mathbf{W}_{k, E}$ 和 $\mathbf{W}_{k, R}$ 用于生成基于内容的 Key 向量和基于位置的 Key 向量。
这样,(a) 代表了基于内容的位置信息,(b) 捕获了内容无关的位置偏置,(c) 表示了一个全局的内容偏置,(d) 捕获了一个全局的位置偏置。
利用一个自注意力头计算 N 层的 Transformer-XL 的过程如下,对于 n=1,…,N 有:
6. XLNet (2019)
https://www.bookstack.cn/read/huaxiaozhuan-ai/spilt.7.8c42358a11926b2f.md
目前语言预训练模型的模式主要有2种,第一种是像gpt这种的auto-regressive自回归模型,每个时刻都依据之前所有时刻的token来预测下一个token,auto-regressive的loss的定义如下:
第二种是像bert这种的auto-encoder自编码模型,随机mask掉句子中若干个token,然后依据上下文预测被mask掉的token,auto-encoder的loss的定义如下:
两种不同的预训练目标的优劣势如下:
- 独立假设:BERT 中联合条件概率 $p(\overline{\mathbf{x}} | \hat{\mathbf{x}})$ 假设在给定的 x^ 下,遮挡的词条 $\overline{\mathbf{x}}$ 是相关独立的,而 AR(auto-regressive) 语言模型则没有这样的假设。
- 输入噪声:BERT 在预训练是使用了特殊标记
[MASK]
,在下游任务微调时不会出现,而 AR 语言模型则不会存在这个问题。 - 上下文依赖:AR 语言模型仅考虑了词条左侧的上下文,而 BERT 则可以捕获两个方向的上下文。
为了利用 AR 语言模型和 BERT 的优点,XLNet 提出了排序语言模型。对于一个长度为 T 序列 x,共有 $T!$ 种不同的方式进行 AR 分解,如果模型共享不同分解顺序的参数,那么模型就能学习到两侧所有位置的信息。令 $Z_T$ 为长度为 T 的索引序列 $[1,2,…,T]$ 的所有可能排列,$z_t$ 和 $z_{<t}$ 分别表示一个排列 $z∈Z_T$ 第 t 个和前 t−1 个元素。则排列语言模型的优化目标为:
根据标准的 Transformer,下一个词条的分布 $p_{\theta}\left(X_{z_{t}} | \mathbf{x}_{\mathbf{z}<t}\right)$ 为:
其中,$h_{\theta}\left(\mathbf{x}_{\mathbf{z}<t}\right)$ 表示通过共享的 Transformer 产生的 $X_{Z<t}$ 的隐含表示。该表示并不依赖于所预测的位置,为了避免这个问题,我们将位置 zt 加入到模型中:
对于 $g_{\theta}\left(\mathbf{x}_{\mathbf{z}<t}, z_{t}\right)$ 进行建模需要满足如下两个要求:
- 预测 $x_{zt}$ 时,$g_{\theta}\left(\mathbf{x}_{\mathbf{z}<t}, z_{t}\right)$ 只能使用位置信息 zt 而不能使用内容信息 $x_{zt}$。
- 在预测 $x_{zt}$ 之后的词条时,$g_{\theta}\left(\mathbf{x}_{\mathbf{z}<t}, z_{t}\right)$ 又必须包含 $x_{zt}$ 的语义信息。
为了解决这个问题,XLNet 提供了两种隐含表示:
- 内容隐含表示 $h_{\theta}\left(\mathbf{x}_{\mathbf{z} \leq t}\right)$,简写为 hzt,它和标准的 Transformer 一样,既编码上下文也编码 xzt 的内容。
- 查询隐含表示 $g_{\theta}\left(\mathbf{x}_{\mathbf{z}<t}, z_{t}\right)$,简写为 gzt,它仅编码上下文信息 XZ<t 和位置信息 zt,不编码内容 xzt。
模型的整个计算过程如下图所示:
虽然排列语言模型有很多优点,但是由于计算量很大,模型很难进行优化,因此我们通过仅预测一个句子后面的一些词条解决这个问题。将 z 分为两部分:非目标子序列 $\mathbf{z}_{\leq c}$ 和目标子序列 $z_{>c}$,其中 c 为切分点。同时会设置一个超参数 K,表示仅 1/K 的词条会被预测,有 $|\mathbf{z}| /(|\mathbf{z}|-c) \approx K$。对于未被选择的词条,其查询隐状态无需被计算,从而节省计算时间和资源。
7. MASS (2019)
MASS 是一个专门针对序列到序列的自然语言任务设计的预训练方法,对于一个给定的原始句子 $x∈X$,令 $x^{\setminus u:v}$ 表示将 x 从 u 到 v 位置进行遮挡处理,$k=v−u+1$ 为被遮挡词条的个数,$x^{u:v}$ 为从 u 到 v 位置被遮挡的部分。MASS 利用被遮挡的序列 $x^{\setminus u:v}$ 预测被遮挡的部分 $x^{u:v}$ ,目标函数的对数似然如下:
对于一个具有 8 个词条的序列,$x_3 x_4 x_5 x_6$ 被遮挡的示例如下:
模型仅预测遮挡的部分 $x_3 x_4 x_5 x_6$ ,对于解码器中位置 4−6 利用 $x_3 x_4 x_5$ 作为输入,利用特殊遮挡符号 $\left[\mathbb{M}\right]$ 作为其他位置的输入。对于不同长度 k,MASS 包含了上文中提到的两种预训练模型:
长度 | 概率 | 模型 |
---|---|---|
k=1 | $P\left(x^{u} \mid x^{\setminus u} ; \theta\right)$ | masked LM in BERT |
k=m | $P\left(x^{1:m} \mid x^{\setminus 1:m} ; \theta\right)$ | masked LM in GPT |
k∈(1,m) | $P\left(x^{u:v} \mid x^{\setminus u:v} ; \theta\right)$ | 两种之间 |
对于不同 k 值,实验发现当 k 处于 m 的 50% 至 70% 之间时下游任务性能最优。
当 $k=0.5m$ 时,MASS 可以很好地平衡编码器和解码器的预训练。过度地偏向编码器(k=1,masked LM in BERT)和过度地偏向解码器(k=m,masked LM in GPT)均不能在下游的自然语言生成任务中取得很好的效果。
8. RoBERTa (2019)
Facebook的RoBERTa: A Robustly Optimized BERT Pretraining Approach,主要工作是复现 BERT
,然后对 BERT
的模型架构、训练目标、训练细节(如数据集大小、训练时间)的重要性进行探索,从而提出了改进方案,这个改进方案称为 RoBERTa
。
roberta的创新点主要有4点:
- 第1点是动态mask,之前bert使用的是静态mask,就是数据预处理的时候完成mask操作,之后训练的时候同一个样本都是相同的mask结果,动态mask就是在训练的时候每输入一个样本都要重新mask,动态mask相比静态mask有更多不同mask结果的数据用于训练,效果很好。
- 模型去掉了 NSP 任务,修改了样本的构造方式,发现可以略微提升下游任务的性能。将输入2个segment修改为从一个文本中连续sample句子直到塞满512的长度。当到达文本的末尾且未塞满512的长度时,先增加一个“[sep]”,再从另一个文本接着sample,直到塞满512的长度。
- 更大的训练数据(从16G变成了160G)和更大的 Batch 大小(256改为2K甚至8K)
- 原始 BERT 采用一个 30K 的 BPE 词表,RoBERTa 采用了一个更大的 50K 的词表 28。
9. BART (2019)
BART 采用了一个标准的 Seq2Seq Transformer 结构,类似 GPT 将 ReLU 激活函数替换为 GeLUs。对于基线模型,采用了一个 6 层的编码和解码器,对于更大模型采用了 12 层的结构。相比于 BERT 的架构主要有以下两点不同:
- 解码器的每一层叠加了对编码器最后一个隐含层的注意力。
- BERT 在预测之前采用了一个前馈的网络,而 BART 没有。
BART 采用了最小化破坏后的文档和原始文档之间的重构误差的方式进行预训练。不同于其他的一些去噪自编码器,BART 可以使用任意类型的文档破坏方式。极端情况下,当源文档的所有信息均丢失时,BART 就等价与一个语言模型。BART 中采用的文本破坏方式有:字符遮罩,字符删除,文本填充,句子重排,文档旋转,如下图所示:
10. T5 (2019)
T5(Text-to-Text Transfer Transformer) 提出了一种 text-to-text 的框架,旨在利用相同的模型,损失函数和超参数等对机器翻译,文档摘要,问答和分类(例如:情感分析)等任务进行统一建模。我们甚至可以利用 T5 通过预测一个数字的文本表示而不是数字本身来建模一个回归任务。模型及其输入输出如下图所示:
Google 的这项研究并不是提出一种新的方法,而是从全面的视角来概述当前 NLP 领域迁移学习的发展现状。T5 还公开了一个名为 C4(Colossal Clean Crawled Corpus)的数据集,该数据集是一个比 Wikipedia 大两个数量级的 Common Crawl 的清洗后版本的数据。更多模型的细节请参见源论文和 Google 的 官方博客。
11. ERNIE (Baidu, 2019)
ERNIE 1.0 29 通过建模海量数据中的词、实体及实体关系,学习真实世界的语义知识。相较于 BERT 学习原始语言信号,ERNIE 直接对先验语义知识单元进行建模,增强了模型语义表示能力。例如:
1 |
|
在 BERT 模型中,我们通过『哈』与『滨』的局部共现,即可判断出『尔』字,模型没有学习与『哈尔滨』相关的任何知识。而 ERNIE 通过学习词与实体的表达,使模型能够建模出『哈尔滨』与『黑龙江』的关系,学到『哈尔滨』是 『黑龙江』的省会以及『哈尔滨』是个冰雪城市。
训练数据方面,除百科类、资讯类中文语料外,ERNIE 还引入了论坛对话类数据,利用 DLM(Dialogue Language Model)建模 Query-Response 对话结构,将对话 Pair 对作为输入,引入 Dialogue Embedding 标识对话的角色,利用 Dialogue Response Loss 学习对话的隐式关系,进一步提升模型的语义表示能力。
ERNIE 2.0 30 是基于持续学习的语义理解预训练框架,使用多任务学习增量式构建预训练任务。ERNIE 2.0 中,新构建的预训练任务类型可以无缝的加入训练框架,持续的进行语义理解学习。 通过新增的实体预测、句子因果关系判断、文章句子结构重建等语义任务,ERNIE 2.0 语义理解预训练模型从训练数据中获取了词法、句法、语义等多个维度的自然语言信息,极大地增强了通用语义表示能力。
轻量化BERT
1. Albert
增大预训练模型的大小通常能够提高预训练模型的推理能力,但是当预训练模型增大到一定程度之后,会碰到GPU/TPU memory的限制。因此,作者==在bert中加入了2项减少参数的技术==,能够缩小bert的大小,并且修改了bert NSP的loss,在和bert有相同参数量的前提之下,有更强的推理能力。
albert的流程
词向量矩阵的分解
在bert以及诸多bert的改进版中,embedding size都是等于hidden size的,这不一定是最优的。因为bert的token embedding是上下文无关的,而经过multi-head attention+ffn后的hidden embedding是上下文相关的,bert预训练的目的是提供更准确的hidden embedding,而不是token embedding,因此token embedding没有必要和hidden embedding一样大。albert将token embedding进行了分解,首先降低embedding size的大小,然后用一个Dense操作将低维的token embedding映射回hidden size的大小。bert的 embedding size=hidden size,因此词向量的参数量是vocab size \ hidden size,进行分解后的参数量是 vocab size * embedding size + embedding size * hidden size*,==只要embedding size << hidden size,就能起到减少参数的效果==。
参数共享
bert的12层transformer encoder block是串行在一起的,每个block虽然长得一模一样,但是参数是不共享的。albert==将transformer encoder block进行了参数共享,这样可以极大地减少整个模型的参数量==。
sentence order prediction(SOP)
在auto-encoder的loss之外,bert使用了NSP的loss,用来提高bert在句对关系推理任务上的推理能力。而albert放弃了NSP的loss,使用了SOP的loss。NSP的loss是判断segment A和segment B之间的关系,其中0表示segment B是segment A的下一句,1表示segment A和segment B来自2篇不同的文本。SOP的loss是判断segment A和segment B的的顺序关系,0表示segment B是segment A的下一句,1表示segment A是segment B的下一句。
albert的技术细节
参数减少技术
albert使用了2项参数减少的技术,但是2项技术对于参数减少的贡献是不一样的,第1项是词向量矩阵的分解,当embedding size从768降到64时,可以节省21M的参数量,但是模型的推理能力也会随之下降。第2项是multi-head attention+ffn的参数共享,在embedding size=128时,可以节省77M的参数量,模型的推理能力同样会随之下降。虽然参数减少会导致了模型推理能力的下降,但是可以通过增大模型使得参数量变回和bert一个量级,这时模型的推理能力就超过了bert (🐮🍺)。
现在学术界发论文有2种常见的套路,第1种是往死里加参数加数据量,然后提高模型的推理能力;第2种是减参数,然后使模型的推理能力不怎么降。albert使用的参数减少技术看似是第2种,实则是第1种。当bert从large变到xlarge时,虽然模型变大到了1270M,但是模型出现了退化现象,推理能力下跌了一大截,说明在bert的框架下,large已经是模型推理能力的极限了。albert使用了参数减少技术,相比于bert的large是334M,albert的large只有18M,虽然推理能力比bert差,但是参数减少后的albert还有成长空间,将albert从large变到xlarge,甚至是xxlarge时,模型的推理能力又得到了提高,并且超过了bert最好的模型。
loss
在albert之前,很多bert的改进版都对NSP的loss提出了质疑。structbert在NSP的loss上进行了修改,有1/3的概率是segment B是segment A的下一句,有1/3的概率是segment A是segment B的下一句,有1/3的概率是segment A和segment B来自2篇不同的文本。roberta则是直接放弃了NSP的loss,修改了样本的构造方式,将输入2个segment修改为从一个文本中连续sample句子直到塞满512的长度。当到达文本的末尾且未塞满512的长度时,先增加一个“[sep]”,再从另一个文本接着sample,直到塞满512的长度。
albert在structbert的基础之上又抛弃了segment A和segment B来自2篇不同的文本的做法,只剩下1/2的概率是segment B是segment A的下一句,1/2的概率是segment A是segment B的下一句。论文中给出了这么做的解释,NSP的loss包含了2部分功能:topic prediction和coherence prediction,其中topic prediction要比coherence prediction更容易学习,而MLM的loss也包含了topic prediction的功能,因此bert难以学到coherence prediction的能力。albert的SOP loss抛弃了segment A和segment B来自2篇不同的文本的做法,让loss更关注于coherence prediction,这样就能提高模型在句对关系推理上的能力。
albert的总结
albert然减少参数量,但是并不会减少推理时间,推理的过程只不过是从串行计算12个transformer encoder block变成了循环计算transformer encoder block 12次。albert最大的贡献在于使模型具备了比原始的bert更强的成长性,在模型变向更大的时候,推理能力还能够得到提高。
2. DistillBert
论文:DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter
DistillBert是在bert的基础上用知识蒸馏技术训练出来的小型化bert。整体上来说这篇论文还是非常简单的,只是引入了知识蒸馏技术来训练一个小的bert。具体做法如下:
1)给定原始的bert-base作为teacher网络。
2)在bert-base的基础上将网络层数减半(也就是从原来的12层减少到6层)。
3)利用teacher的软标签和teacher的隐层参数来训练student网络。
训练时的损失函数定义为三种损失函数的线性和,三种损失函数分别为:
1)𝐿𝑐𝑒Lce。这是teacher网络softmax层输出的概率分布和student网络softmax层输出的概率分布的交叉熵(注:MLM任务的输出)。
2)𝐿𝑚𝑙𝑚Lmlm。这是student网络softmax层输出的概率分布和真实的one-hot标签的交叉熵
3)𝐿𝑐𝑜𝑠Lcos。这是student网络隐层输出和teacher网络隐层输出的余弦相似度值,在上面我们说student的网络层数只有6层,teacher网络的层数有12层,因此个人认为这里在计算该损失的时候是用student的第1层对应teacher的第2层,student的第2层对应teacher的第4层,以此类推。
作者对student的初始化也做了些工作,作者用teacher的参数来初始化student的网络参数,做法和上面类似,用teacher的第2层初始化student的第1层,teacher的第4层初始化student的第2层。
作者也解释了为什么减小网络的层数,而不减小隐层大小,作者认为在现代线性代数框架中,在张量计算中,降低最后一维(也就是隐层大小)的维度对计算效率提升不大,反倒是减小层数,也提升计算效率。
另外作者在这里移除了句子向量和pooler层,在这里也没有看到NSP任务的损失函数,因此个人认为作者也去除了NSP任务(主要是很多人证明该任务并没有什么效果)。
整体上来说虽然方法简单,但是效果还是很不错的,模型大小减小了40%(66M),推断速度提升了60%,但性能只降低了约3%。
3. TINYBERT
论文:TINYBERT: DISTILLING BERT FOR NATURAL LANGUAGE UNDERSTANDING
GitHub:暂无
TINYBERT也是采用了知识蒸馏的方法来压缩模型的,只是在设计上较distillBert做了更多的工作,作者提出了两个点:针对Transformer结构的知识蒸馏和针对pre-training和fine-tuning两阶段的知识蒸馏。
作者在这里构造了四类损失函数来对模型中各层的参数进行约束来训练模型,具体模型结构如下:
作者构造了四类损失,分别针对embedding layer,attention 权重矩阵,隐层输出,predict layer。可以将这个统一到一个损失函数中:
上面式子中𝜆𝑚λm表示每一层对应的系数,𝑆𝑚Sm表示studnet网络的第m层,𝑇𝑔(𝑚)Tg(m)表示teacher网络的第n层,其中𝑛=𝑔(𝑚)n=g(m)。并且有𝑔(0)=0g(0)=0,𝑔(𝑀+1)=𝑁+1g(M+1)=N+1,0表示embedding layer,M+1和N+1表示perdict layer。
针对上面四层具体的损失函数表达式如下:
attention 权重矩阵
h为multi attention中头数
隐层输出
因为student网络的隐层大小通常会设置的比teacher的小,因此为了在计算时维度一致,这里用一个矩阵𝑊ℎWh将student的隐层向量线性映射到和teacher同样的空间下。
embedding layer
𝑊𝑠Ws同理上。
以上三种损失函数都采用了MSE,主要是为了将模型的各项参数对齐。
predict layer
predict layer也就是softmax层,在这里的损失函数是交叉熵,t是温度参数,在这里设置为1。
以上四种损失函数是作者针对transformer提出的知识蒸馏方法。除此之外作者认为除了对pre-training蒸馏之外,在fine-tuning时也利用teacher的知识来训练模型可以取得在下游任务更好的效果。因此作者提出了两阶段知识蒸馏,如下图所示:
本质上就是在pre-training蒸馏一个general TinyBERT,然后再在general TinyBERT的基础上利用task-bert上再蒸馏出fine-tuned TinyBERT。
作者给出了TinyBERT的效果:
另外作者也给出了四种损失对最终结果的贡献:
还有就是关于𝑛=𝑔(𝑚)n=g(m)这个式子中𝑔(𝑚)g(m)怎么选择,假设student的层数为4层,这里的𝑛=𝑔(𝑚)=3𝑚n=g(m)=3m,作者将这种称为Uniform-strategy。另外作者还和其他的𝑔(𝑚)g(m)做了对比:
Top-strategy指用teacher最后4层,Bottom-strategy指用前面4层,其实这里的映射函数,我感觉可能还有更优的方案,例如取平均,或者用attention来做,可能效果会更好。
BERT 扩展 💡
https://www.bookstack.cn/read/huaxiaozhuan-ai/spilt.9.8c42358a11926b2f.md
BERT-wwm-ext
原始版本的 BERT
采用了WordPiece tokenize
来预处理,即把每个单词拆解一些 wordpiece token
。在Pretraining的时候是随机Mask这些WordPiece的,这就可能出现只Mask一个词的一部分的情况,比如下面的例子:
==为了解决这个问题,很自然的想法就是词作为一个整体要么都Mask要么都不Mask,这就是所谓的 Whole Word Masking==。这是一个很简单的想法,对于BERT的代码修改也非常少,只是修改一些Mask的那段代码。对于英文来说,分词是一个(相对)简单的问题。哈工大与科大讯飞的论文对Wiki的中文dump进行了分词,然后做了一些实验。
- Whole Word Masking
- 替换BERT的AdamWeightDecayOptimizer为LAMB优化器
Sentence-Bert
Bert的缺点:
BERT不适合语义相似度搜索,也不适合非监督任务,比如聚类。
解决聚类和语义搜索的一种常见方法是将每个句子映射到一个向量空间,使得语义相似的句子很接近。
于是,也有人尝试向BERT输入单句,得到固定大小的sentene embedding。最常用的方法是,平均BERT输出层或使用第一个token([CLS]的token)的输出。但这却产生了非常不好的sentence embedding,常常还不如averaging GloVe embeddings
语义相似度计算巨大开销。
Bert有1.1亿参数量(base版本)使得预测、推理速度明显比CNN等传统网络慢了不止一个量级,对资源要求更高,也不适合处理某些任务。例如,从10000条句子中找到最相似的一对句子,由于可能的组合众多,需要完成49,995,000次推理计算;在一块现代V00GPU上使用Bert计算,将消耗65小时。
解决方法:
Sentence-Bert模型(以下简称SBert)
SBert对预训练的BERT进行修改:使用孪生(Siamese)和三级(triplet)网络结构来获得语义上有意义的句子embedding,以此获得定长的sentence embedding,使用余弦相似度或Manhatten/Euclidean距离等进行比较找到语义相似的句子。前面所述的从10000条句子找最相似pair的任务,SBert仅需5秒就能完成!通过这样的方法得到的SBERT模型,在文本语义相似度等句子对的回归任务上吊打BERT , RoBERTa 拿到sota。
SBert的优势:
SBert充分利用了孪生网络的优点和预训练语言模型强大的特征抽取优势,在众多匹配任务上取得了最优实验结果。
SBert直接使用Bert的原始权重进行初始化,在具体数据集上微调,训练过程和传统Siamse Network差异不大。但是这种训练方式能让Bert更好的捕捉句子之间的关系,生成更优质的句向量。
- 在评估测试阶段,SBert直接使用余弦相似度来比较两个句向量之间的相似度,极大提升了推理速度;
- 同时,得益于生成的高质量句嵌入特征,SBert在语义检索、信息搜索、文本聚类、新FAQ发现等工作中预计会有不错表现。
SBert的网络结构:
SBert训练
使用SNLI和NLI数据集对Bert和Roberta进行fine-tune,得到SBERT预训练模型。
Polling策略
① mean:将句子的所有token在token维度上计算平均,这样就可以得到768(base)/1024(large)维度向量;
② max:将句子的所有token在token维度上的最大那个值,即做max_over_time,这里多解释下max_over_time,就是比如x = torch.randn(2, 10, 20),就是取x.max(1);
③ CLS:就是原始Bert做分类的方法,取句子的第一个token的向量。作者发现,使用mean的效果最好,max最差。
loss函数
① 回归任务:softmax,均方差损失函数;
② 分类任务:softmax, 交叉熵损失函数;
③ Wikipedia section triplets dataset (Dor et al., 2018)(三句分类任务):这个数据集是一些书中的句子,每一条数据有三句话,其中两句来自同一个章节的句子,另外一句是这本书的另外一个章节。
其中 ε设为1,||a, b||表示a和b的距离。求该损失函数的目的就是使得,a和p的距离小于a和n的距离
SBERT的相关资源
SBert开源地址:https://github.com/UKPLab/sentence-transformers
SBert多语预训练模型下载地址:https://public.ukp.informatik.tu-darmstadt.de/reimers/sentence-transformers/v0.2/
code adapted from https://github.com/UKPLab/sentence-transformers/blob/master/examples/applications/semantic_search.py
参考
绝对干货!NLP预训练模型:从transformer到albert