语言模型串烧(BERT前)

语言模型的发展实在是太快了,怎么才赶上NLP快车呢?此文包含在BERT及其之前提出的一些语言模型。

Word2Vec(2013年1月)

CBOW用上下文训练目标词,而Skip-gram用目标词去预测上下文。Word2Vec采用三层神经网络就可以训练,最后一层的输出是Softmax,因为Softmax需要计算全局的token,因此速度慢。为了提高训练速度,衍生出用Huffman树和负采样代替最简单的Softmax的训练方法。

GloVe(2014年1月)

Global Vectors for Word Representation。

Word2Vec的目的是预测,而GloVe的目的是将共现矩阵降维。不过共现矩阵的维度太大不可能直接构造出来用传统方法降维,所以GloVe的工作实际上可以理解为重构共现矩阵。

假设词i的出现次数为$X_i$,词i和词j的共现次数为$x_{i,j}$,那么词i和词j的共现频率就是$P_{ij} = X_{i,j} / X_i$。作者认为 __两个词的相关程度是可以通过共现次数做比较的__,就行下面这个表格:

$P_{ik}/P_{jk}$ 单词j和单词k相关 单词j和单词k不相关
单词i和单词k相关 趋近于1 很大
单词i和单词k不相关 很小 趋近于1

而用词向量表示词义的时候,衡量两者近似程度就是用内积。用$v_i$、$v_j$、$v_k$分别表示词i、j、k的词向量,并用函数g(i,j,k)表示词向量i和词向量j谁更接近于词向量k,那么结合共现次数,就可以推知:

$$
\frac{P_{i,k}}{P_{j,k}} = g(i,j,k)
$$

代价函数为
$$
J = \sum_{i,j,k}(P_{i,k}/P_{j,k} − g(i,j,k))^2
$$

不过目前,计算所有g的代价是$O(n^3)$,作者将函数g展开,并最终得到了函数g的等价形式。首先是定义函数g,
$$
g(i,j,k) = exp⁡(v_i^T v_k − v_j^T v_k) = \frac{exp⁡(v_i^T v_k)}{exp⁡(v_j^T v_k)}
$$

因此
$$
\frac{P_{i,k}}{P_{j,k}} = \frac{exp⁡(v_i^T v_k)}{exp⁡(v_j^T v_k)}
$$

那么只要等式上下对应相等,整个等式就成立了
$$ P_{i,k} = exp⁡(v_i^T v_k) $$

进一步,两边同时取对数
$$ log⁡(P_{i,k}) = v_i^T v_k $$

这样代价函数化简为时间复杂度为$O(n^2)$的
$$ J = \sum_{i,k}(log⁡(P_{i,k}) − v_i^T v_k)^2 $$

但是实际上这里存在一个bug:__$log⁡(P_{i,k})$不具备对称性,而$v_i^T v_k$却具备对称性__!为了弥补这个bug,作者将概率$P_{i,k}$展开、移项并得到
$$ log⁡(X_{i,k}) = v_i^T v_k − log⁡(X_i) $$

这样等式左边具备对称性,而等式右边的对称性却被打破了,为了弥补这一点,在等式右边打上一个补丁
$$ log⁡(X_{i,k}) = v_i^T v_k − log⁡(X_i) + b_k = v_i^T v_k + b_i + b_k $$

于是等式就构造出来了,另代价函数为:
$$ J = \sum_{i,k}{v_i^T v_k + b_i + b_k − log⁡(X_{i,k}))^2} $$

细心的作者还意识到,这样的代价函数是等权重的,也就是对所有的ik组合,其权重是相等的,但是实际上出现频率更高的组合更具有话语权,因此手动加一个权重函数f:

$$
f(x) =
\begin{aligned}
\begin{cases}
(\frac{x}{xmax})^{0.75}, &if \ x < xmax \
1, &if \ x \ge xmax
\end{cases}
\end{aligned}
$$

加上权重后,最终的代价函数为:

$$
J = \sum_{i,k}{f(X_{i,k})(v_i^T v_k + b_i + b_k − log⁡(X_{i,k}))^2}
$$

FastText(2016年7月)

fastText是Facebook于2016年开源的一个词向量计算和文本分类工具,在学术上并没有太大创新。但是它的优点也非常明显,在文本分类任务中,fastText(浅层网络)往往能取得和深度网络相媲美的精度,却在训练时间上比深度网络快许多数量级。在标准的多核CPU上, 能够训练10亿词级别语料库的词向量在10分钟之内,能够分类有着30万多类别的50多万句子在1分钟之内。

FastText的模型架构和CBOW非常相似,不同之处在于,FastText预测标签,而CBOW模型预测中间词。

FastText有以下特点:

  1. 利用了subword特性,比如“English-born”和“China-born”可以共享“born”信息。__这对于英语等西语来说是十分有效的,但是放在中文下可能不那么奏效__。关于subword的具体实现在“_Enriching Word Vectors with Subword Information_”中。
  2. subword在文本分类任务中的output是文本标签,并且 __输入是一篇文章中所有的词__。
  3. 使用了Hierarchy Softmax或Negative sampling加速训练。
  4. 融合Ngram特性。因为FastText将整篇文章的所有单词作为输入,并且hidden层是所有词的平均,所以词序变得不那么重要了。为了解决解决这个问题,FastText将每一个n-gram看成一个词,在计算hidden向量的时候把这些n-gram也加进去。但是n-gram的数量是非常大的,完全存下这些embedding也不现实,因此作者把这些n-gram的哈希到N个桶中,同一个桶中的所有n-gram共享同一个embedding

从工业界的角度来看,Fasttext因为其优秀的性能,不错的分类效果,使用起来也非常简单,__因此非常适合大规模的文本分类问题__。实际上Facebook已经将Fasttext应用于实际的大规模文本分类的场景中了。

Transformer(2017年6月)

这张能在很多地方看到的图片就是Transformer的架构,出自google brain的“_attention is all you need_”。

而将N展开之后就是这样的encoder-decoder框架

Positional Encoding

Transformer架构的一个特点就是用全连接层代替了RNN结构,以此大幅度提升训练的速度,但是用全连接层会丢弃位置信息,也就相当于将句子打散成词袋模型。为了弥补这个缺陷,位置信息编码就被引入了。一种方法是用不同频率的sine和cosine函数直接代入计算,另一种是学习出一份positional embedding。但其实两者的结果一样,那么更高效的第一种就被采用了。

$$
\begin{aligned}
PE_{(pos, 2i)} &= \sin(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) \
PE_{(pos, 2i+1)} &= \cos(\frac{pos}{10000^{\frac{2i}{d_{model}}}})
\end{aligned}
$$

其中pos是位置,i代表第i个维度。这个公式可视化结果如下,其中横坐标表示维度,纵坐标表示pos。

Multi-head self attention

Self-attention的作用是将每个输入分成Q(query)、K(key)、V(value)三部分,并完成下面的运算:

$$ Attention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})V $$

多头就是实现h个self-attention的输出拼接起来,再做一个线性变换。

$$
\begin{aligned}
head_i &= Attention(QW_i^Q, KW_i^K, VW_i^V) \
MultiHead(Q, K, V) &= Concat(head_1, \dots, head_h)W
\end{aligned}
$$

Fully connected feed forward

这一层就是非线性映射。

Transformer的不足之处:self-attention的计算复杂度为$O(n^2)$,当句子很长的时候,时间消耗相当大。另外长度为n的句子也并不一定需要全局attention,一些局部attention也能起作用,而局部attention甚至可以用CNN代替。

ELMo(2018年2月)

前面所有的模型使用的词向量均是静态的,也就是说一份词向量在不同的场合作用相同,而语义消歧(WSD)任务丢给下游任务。而ELMo的设计初衷是让词向量拥有上下文相关的能力,也就是 __动态词向量__。

ELMo是从深层双向语言模型中的 内部状态 学习而来的。首先定义双向语言模型的似然函数:

$$
\sum_{k=1}^N{\left(
\log ⁡p(t_k |t_{1:k−1}; \Theta_x, \stackrel{\rightarrow}{\Theta}{LSTM}, \Theta_s) +
\log ⁡p(t_k |t
{k+1:N}; \Theta_x, \stackrel{\leftarrow}{\Theta}_{LSTM}, \Theta_s) \right)}
$$

其中$\Theta_x$和$\Theta_s$分别是训练的(上下文无关的)词向量和softmax层的参数,$\stackrel{\rightarrow}{\Theta}{LSTM}$和$\stackrel{\leftarrow}{\Theta}{LSTM}$则是双向语言模型的参数。

对于某一个词语$t_k$,__ELMo将L层双向语言模型的词向量$X^{LM}$和每一层的隐含状态$\stackrel{\rightarrow}{h}_k^j$和$\stackrel{\leftarrow}{h}_k^j$整合成一个向量__:

$$
R_k = {X^{LM}, \stackrel{\rightarrow}{h}_k^j, \stackrel{\leftarrow}{h}_k^j }, j = 1, \dots, L
$$

于是,这个复杂的向量在不同的语境下就具有了不同的含义。不过ELMo的亮点当然不在于模型层,而是其通过实验间接说明了在多层的RNN中,__不同层学到的特征其实是有差异的__,因此ELMo提出在预训练完成并迁移到下游NLP任务中时,要为原始词向量层和每一层RNN的隐层都设置一个可训练参数,这些参数通过softmax层归一化后乘到其相应的层上并求和便起到了weighting的作用,然后对“加权和”得到的词向量再通过一个参数γ来进行词向量整体的scaling以更好的适应下游任务。

$$
\begin{aligned}
s &= softmax(w) \
E(R_k) &= \gamma \sum_{j=0}^{L}{s_j h_k^j}
\end{aligned}
$$

另外插一句,简单的LSTM实际上并不可能堆很深,比如ELMo默认L=2,也就是三层,更深的LSTM组合就很难训练了。

BERT(2018年10月)

ELMo相比word2vec会有这么大的提升,可以看出预训练词向量的重要性。而BERT则是训练了一个龙骨级的语言模型,可以为下游任务提供词级、句子级的特征。

传统的语言模型是单向的,不论是从左往右还是从右往左,即使是用了双向LSTM的ELMo,两个方向的RNN实际上也是独立训练的,两者之间并没有交集。这样的模型被称为autoregressive(AR)模型。如果想在预训练模型上使用真正的双向encoding,那么在传统的语言模型假设下是行不通的,因为如果做了双向encoding,那么就意味着要预测的词已经看到了,那么这种预测就没有意义了……为此,在BERT中,一种新的模型被提出来了,那就是 __Masked LM__。

具体来说,Masked LM不是像传统LM那样给定已经出现过的词,去预测下一个词,而是直接把整个句子的一部分词(随机选择)盖住(make it masked),这样模型就可以放心的去做双向encoding了,并让模型去预测这些盖住的词。这个任务其实最开始叫做 __cloze test__(完形填空测验)。因为[mask]标记在训练过程中会被encoding进句子里,而作者通过:

  • 80%用[mask]替换
  • 10%随机替换
  • 10%不做替换

的操作告诉模型忽略这些噪声。这种已经知道了上下文,然后预测中间某个单词的模型被称为autoencoding(AE)模型,而BERT更是加上了随机替换[mask]这样的手段,因此带上了降噪的功能,被称为denoising autoencoding(DAE)模型。

另外,因为深层LSTM十分难训练,所以作者用了 __Transformer__,因此BERT的架构图如下:

前面提到了BERT是一个可以学到多层次特征的模型,那么怎么体现字符级和句子级呢?

BERT采用了“__句子级负采样__”,首先给定一个句子,它的下一句话为正例,而随机采样的另一个句子就是负例,将两个句子拼起来作为一个例子,然后在sentence-level做二分类。二分类的做法也是很脑洞的,因为考虑到Transformer可以无视空间距离做encoding,因此在句首添加了一个[cls]标记,让encoder对[cls]进行深度encoding,深度encoding的最高隐层即为整个句子对的表示。

另外,为了能让模型区分句子对中的每个词是来自“左句子”还是“右句子”,作者引入了 __segment embedding__,embedding A表示左边的句子,而embedding B表示右边的句子。

最终每个token由词向量embedding、segment embedding、position embedding组成。

训练完后的BERT怎么使用呢?接下来的部分可以看作是对pre-trained BERT进行fine-tuning。

对于文本分类任务来说,只需要在[cls]的输出上加一层简单的全连接层就好了。

对于问答系统而言,只要给定question和paragraph,最后输出span的起点和终点即可。

对于序列标注任务来说,只需要对输出加上softmax层就好了。

其实个人感觉BERT最大的成功之处在于真正利用了语言的上下文,这是AE模型的天生优势。

参考

Word2Vec

  • Mikolov, T., Chen, K., Corrado, G., & Dean, J. (2013). Efficient estimation of word representations in vector space. 1st International Conference on Learning Representations, ICLR 2013 - Workshop Track Proceedings, 1–12.

GloVe

FastText

  • Joulin, A., Grave, E., Bojanowski, P., & Mikolov, T. (2017). Bag of tricks for efficient text classification. 15th Conference of the European Chapter of the Association for Computational Linguistics, EACL 2017 - Proceedings of Conference, 2, 427–431. https://doi.org/10.18653/v1/e17-2068
  • Bojanowski, P., Grave, E., Joulin, A., & Mikolov, T. (2017). Enriching Word Vectors with Subword Information. Transactions of the Association for Computational Linguistics, 5, 135–146. https://doi.org/10.1162/tacl_a_00051
  • Joulin, A., Grave, E., Bojanowski, P., Douze, M., Jégou, H., & Mikolov, T. (2016). FastText.zip: Compressing text classification models. 1–13. Retrieved from http://arxiv.org/abs/1612.03651
  • 玩转Fasttext

Transformer

  • Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., … Polosukhin, I. (2017). Attention is all you need. Advances in Neural Information Processing Systems, 2017-Decem(Nips), 5999–6009.
  • 图解什么是 Transformer

ELMo

  • Peters, M., Neumann, M., Iyyer, M., Gardner, M., Clark, C., Lee, K., & Zettlemoyer, L. (2018). Deep Contextualized Word Representations. 2227–2237. https://doi.org/10.18653/v1/n18-1202

GPT

BERT

其他