《ELMo 原始论文:Deep contextualized word representations》
2018年3月份,ELMo出世。
在之前2013年的word2vec及2014年的GloVe的工作中,每个词对应一个vector,对于多义词无能为力。
比如我们使用Word2Vec也可以得到词”bank”的Embedding,我们可以认为这个Embedding包含了bank的语义。但是bank有很多意思,可以是银行也可以是水边,使用普通的Word2Vec作为Pretraining的Embedding,只能同时把这两种语义都编码进向量里,然后靠后面的模型比如RNN来根据上下文选择合适的语义——比如上下文有money,那么它更可能是银行;而如果上下文是river,那么更可能是水边的意思。但是RNN要学到这种上下文的关系,需要这个任务有大量相关的标注数据,这在很多时候是没有的。
ELMo的工作对于此,提出了一个较好的解决方案。不同于以往的一个词对应一个向量,是固定的。在ELMo世界里,预训练好的模型不再只是向量对应关系,而是一个训练好的模型。使用时,将一句话或一段话输入模型,模型会根据上线文来推断每个词对应的词向量。
这样做之后明显的好处之一就是对于多义词,可以结合前后语境对多义词进行理解。比如appele,可以根据前后文语境理解为公司或水果。
一、Word2vec/GloVe 缺点【无法解决一词多义问题】
词向量是自然语言处理任务中非常重要的一个部分,词向量的表征能力很大程度上影响了自然语言处理模型的效果。如论文中所述,词向量需要解决两个问题:
(1)词使用的复杂特性,如句法和语法;
(2)如何在具体的语境下使用词,比如多义词的问题;
传统的词向量比如word2vec能够解决第一类问题,但是无法解决第二类问题。比如:“12号地铁线马上就要开通了,以后我们出行就更加方便了。”和“你什么时候方便,我们一起吃个饭。”这两个句子中的“方便”用word2vec学习到的词向量就无法区分,因为word2vec学习的是一个固定的词向量,它只能用同一个词向量来表示一个词不同的语义,而elmo就能处理这种多义词的问题。
对于要由机器学习模型处理的单词,它们需要以某种数字形式表示,以便模型可以在其计算中使用。Word2Vec表明我们可以用一个向量(一个数字列表)以捕捉语义或意义关系(如判断单词的近义、反义关系)、以及语法或语法关系(例如, “had”和“has” 、“was” and “is”有同样的语法关系)的方式恰当地表示单词。
研究人员很快发现,使用经过大量文本数据进行预训练的嵌入(embeddings)是一个好主意,而不是与小数据集的模型一起训练。因此,通过使用Word2Vec或GloVe进行预训练,可以下载单词列表及其嵌入。如下图是单词 “stick” 的GloVe 嵌入示例(嵌入向量大小为200)
因为这些向量很大,并且数字很多,所以本文后面用下面这个基本图形来表示向量:
如果我们使用GloVe表示,那么不管上下文是什么,“stick”这个词都会由这个向量表示。
很多研究人员就发现不对劲了。“stick”“有多种含义,取决于它的上下文是什么。那么,为什么不根据它的上下文给它一个嵌入呢——既要捕捉该上下文中的单词含义,又要捕捉其他上下文信息?因此,语境化的词嵌入(contextualized word-embeddings)就出现了。(这也是将词向量和词嵌入分开阐述的原因)
在RNNs模型中,每一个Word Embedding的输出是要参考前面已经输入过的数据。所以叫做:语境化的词嵌入(contextualized word-embeddings)
作者认为好的词表征模型应该同时兼顾两个问题:
- 一是词语用法在语义和语法上的复杂特点;
- 二是随着语言环境的改变,这些用法也应该随之改变。
作者提出了deep contextualized word representation 方法来解决以上两个问题。
二、ELMO概述(Embeddings from Language Model )
ELMo算法的特点是:每一个词语的表征都是整个输入语句的函数。
具体做法就是先在大语料上以language model为目标训练出bidirectional LSTM模型,然后利用LSTM产生词语的表征。
ELMo故而得名(Embeddings from Language Models)。
为了应用在下游的NLP任务中,一般先利用下游任务的语料库(注意这里忽略掉label)进行language model的微调,这种微调相当于一种 domain transfer; 然后才利用label的信息进行supervised learning。
ELMo表征是“深”的,就是说它们是biLM的所有层的内部表征的函数。这样做的好处是能够产生丰富的词语表征。
- 高层的LSTM的状态可以捕捉词语意义中和语境相关的那方面的特征(比如可以用来做语义的消歧),
- 低层的LSTM可以找到语法方面的特征(比如可以做词性标注)。
如果把它们结合在一起,在下游的NLP任务中会体现优势。
ELMo不是对每个单词使用固定的嵌入,而是在为每个单词分配词嵌入之前查看整个句子。
它使用针对特定任务的双向LSTM来创建嵌入。
ELMo为NLP中的预训练提供了重要的一步。
ELMo LSTM在大型数据集上进行训练,然后我们可以将其用作所处理语言的其他模型中的组件使用。
1、ELMo的历史意义
自从ELMo 模型提出来后,预训练模型正式被NLP各项 下游任务中采用,下游任务中的各种模型都被 “预训练模型+” 给替代。
2、ELMO缺点
那么站在现在这个时间节点(Bert已有)看,ELMO 有什么值得改进的缺点呢?
- 首先,一个非常明显的缺点在特征抽取器选择方面,ELMO 使用了 LSTM 而不是新贵 Transformer,Transformer 是谷歌在 17 年做机器翻译任务的“Attention is all you need”的论文中提出的,引起了相当大的反响,很多研究已经证明了 Transformer 提取特征的能力是要远强于 LSTM 的。如果 ELMO 采取 Transformer 作为特征提取器,那么估计 Bert 的反响远不如现在的这种火爆场面。
- 另外一点,ELMO模型在语言模型这个任务上同BERT一样使用了双向的方法,但是它是通过双向的两层RNN结构对两个方向进行建模,两个方向的loss计算相互独立。BERT的作者指出这种两个方向相互独立或只有单层的双向编码可能没有发挥最好的效果,我们可能不仅需要双向编码,还应该要加深网络的层数。但加深双向编码网络却会引入一个问题,导致模型最终可以间接地“窥探”到需要预测的词,“窥探”的过程可以用下面的图来表示:
从图中可以看到经过两层的双向操作,每个位置上的输出就已经带有了原本这个位置上的词的信息了。这样的“窥探”会导致模型预测词的任务变得失去意义,因为模型已经看到每个位置上是什么词了。
3、ELMo模型为什么用 2个 “单向LSTM” 代替 1个 “双向LSTM” ?
Bi-LSTM即双向LSTM, 它没有改变LSTM本身任何的内部结构, 只是将同一个LSTM应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出。
用双向模型结构 BiLSTM去训练语言模型会导致“看到自己”或“看到答案”的问题。
后来发展的其他预训练语言模型(GPT、Bert、XLNet)也都在避免或解决这个问题,解决的程度也影响着模型效果。
三、ELMo模型结构
1、前向LSTM语言模型结构
给定一串长度为
N
N
N 的词条
(
t
1
,
t
2
,
.
.
.
,
t
N
)
(t_1, t_2,...,t_N)
(t1,t2,...,tN),前向语言模型通过对给定历史
(
t
1
,
t
2
,
.
.
.
,
t
k
−
1
)
(t_1, t_2,...,t_{k-1})
(t1,t2,...,tk−1) 预测
t
k
t_k
tk 进行建模,如下图6(值得注意的是,图6不是ELMo的最终图,只是解释LSTM语言模型的图),
对应的公式如下图
p ( t 1 , t 2 , . . . , t N ) = ∏ k = 1 N p ( t k ∣ t 1 , t 2 , . . . , t k − 1 ) p(t_1,t_2,...,t_N)=\prod_{k=1}^{N}p(t_k|t_1,t_2,...,t_{k-1}) p(t1,t2,...,tN)=k=1∏Np(tk∣t1,t2,...,tk−1)
这个前向LSTM网络的输入输出是什么?具体的流程是什么?这个问题很基础又关键。以“the cat sat on the mat”这句话为例,在某一个时刻 k k k 时,输入为the,输出cat的概率。过程是这里面包含了几步:
- 将 the 转换成word embedding:所谓word embedding就是一个
n
∗
1
n * 1
n∗1 维的列向量,这个很好理解。那单词怎么转成word embedding的呢?如果大家用过word2vec,glove就知道,就是简单的查表。在本篇ELMo的paper中,用的不是word2vec,glove,毕竟2018年了。作者用的是cnn-big-lstm生成的word embedding,其实跟word2vec等也差不多,就是提前单独训练好的模型,模型喂入单词就能得到单词的word embedding。总之,在这第一步里,就是简单将单词转换成了
n
∗
1
n * 1
n∗1 的列向量,而这个列向量,对应于我们普通LSTM中的输入
x
t
x_t
xt,如下图所示。
- 将上一时刻的隐状态 h k − 1 h_{k−1} hk−1 及第一步中的word embedding一并输入进LSTM中,并得到输出及隐状态 h k h_k hk 。其中,隐状态 h k − 1 h_{k−1} hk−1 是一个 m ∗ 1 m * 1 m∗1 的列向量。在这一步里,LSTM的输出及隐状态都是一个 m ∗ 1 m * 1 m∗1 维的列向量。请大家务必注意 h k h_k hk,这个 h k h_k hk 与我们后文提到ELMo向量有着直接的关系。
- 将LSTM的输出 h k h_k hk 与上下文矩阵 W ′ W' W′ 相乘,即 W ′ ∗ h k W'*h_k W′∗hk 得到一个列向量,再将该列向量经过softmax归一化。 其中,假定数据集有 V V V 个单词, W ′ W' W′ 是 ∣ V ∣ ∗ m |V|*m ∣V∣∗m的矩阵, h k h_k hk 是 m ∗ 1 m∗1 m∗1 的列向量,于是最终结果是 ∣ V ∣ ∗ 1 |V|*1 ∣V∣∗1 的归一化后向量,即从输入单词得到的针对每个单词的概率。
从上面三步,就可以明白这个前向LSTM语言模型的工作流程了。其实很多神经网络语言模型都很类似,除了LSTM,还可以用RNN及前馈神经网络,差不多的。
2、后向LSTM语言模型结构
对于后向语言模型,跟前向语言模型类似,除了它是给定后文来预测前文。后向LSTM语言模型的公式如下图所示,
p ( t 1 , t 2 , . . . , t N ) = ∏ k = 1 N p ( t k ∣ t k + 1 , t k + 2 , . . . , t N ) p(t_1,t_2,...,t_N)=\prod_{k=1}^{N}p(t_k|t_{k+1},t_{k+2},...,t_N) p(t1,t2,...,tN)=k=1∏Np(tk∣tk+1,tk+2,...,tN)
可以对照着前向语言LSTM语言模型的公式来看。还是非常好理解的。
3、ELMo的双向LSTM语言模型结构
有了前面的基础,ELMo的双向LSTM语言模型就很好解释了。ELMo的整体图如下图。
相对于上面的前向LSTM语言模型,有两个改进:
- 第一个是使用了多层LSTM;
- 第二个是增加了后向语言模型(backward LM);
对于多层LSTM,每层的输出都是隐向量 h t h_t ht,在ELMo里,为了区分,
- 前向LSTM语言模型的第 j j j 层第 k k k 时刻的输出向量命名为 h → k , j L M \overrightarrow{h}^{LM}_{k,j} hk,jLM
- 后向LSTM语言模型的第 j j j 层第 k k k 时刻的输出向量命名为 h ← k , j L M \overleftarrow{h}^{LM}_{k,j} hk,jLM
前向、后向LSTM语言模型所要学习的目标函数(注意此处是追求似然概率最大化的,跟通常的目标函数追求最小化有所不同,要是追求最小化,前面加负号即可)
ELMo使用的双向lstm语言模型,论文中简称biLM。作者将前向、后向LSTM语言模型所要学习的目标函数公式结合起来,得到所要优化的目标:最大化对数前向和后向的似然概率:
P = ∑ k = 1 N ( l o g p ( t k ∣ t 1 , . . . , t k − 1 ; Θ x , Θ → L S T M , Θ s ) + l o g p ( t k ∣ t k + 1 , . . . , t N ; Θ x , Θ ← L S T M , Θ s ) P=\sum_{k=1}^{N}(logp(t_k|t_1,...,t_{k-1};\Theta_x,\overrightarrow{\Theta}_{LSTM},\Theta_s) + logp(t_k|t_{k+1},...,t_N;\Theta_x,\overleftarrow{\Theta}_{LSTM},\Theta_s) P=k=1∑N(logp(tk∣t1,...,tk−1;Θx,ΘLSTM,Θs)+logp(tk∣tk+1,...,tN;Θx,ΘLSTM,Θs)
- 这两个LSTM有各自的参数 Θ → L S T M \overrightarrow{\Theta}_{LSTM} ΘLSTM和 Θ ← L S T M \overleftarrow{\Theta}_{LSTM} ΘLSTM,但是word embedding参数 Θ x \Theta_x Θx和softmax参数 Θ s \Theta_s Θs是共享的。
- Θ x Θ_x Θx 代表的是字符集的 Embedding(映射层的共享参数),将单词映射为word embedding的共享,就是说同一个单词,映射为同一个word embedding;
- Θ s Θ_s Θs 代表的是 Softmax Layer(上下文矩阵的参数,这个参数在前向和后向lstm中也是相同的);
- Θ → L S T M \overrightarrow{Θ}_{LSTM} ΘLSTM 代表的是前向LSTM的参数;
- Θ ← L S T M \overleftarrow{Θ}_{LSTM} ΘLSTM 代表的是后向LSTM的参数;
论文的作者提供了预训练好的ELMo,其训练使用的模型结构为:
- 映射层(单词到word embedding)使用的Jozefowicz的CNN-BIG-LSTM,即输入为512维的列向量。
- LSTM的层数L,最终使用的是 2 2 2,即 L = 2 L=2 L=2。
- 每个LSTM的输出也是 512 512 512 维列向量。
- 每层LSTM(含前、向后向两个)的单元个数是 4096 4096 4096( 2048 × 2 2048×2 2048×2)个。也就是每层的单个LSTM的输入是512维,输出也是512维 ( 512 × 4 = 2048 512×4=2048 512×4=2048, LSTM模型有 4 个非线性变换(3 个 门 + 1 个 tanh),每一个非线性变换说白了就是一个两层的全连接网络)。
一旦模型预训练完成,便可以用于NLP其他任务。
在一些领域,可以对biLM(双向lstm语言模型)进行微调,对任务的表现会有所提高,这种可以认为是一种迁移学习(transfer learning)。
三、ELMo(Embeddings from Language Model)是什么?是 Embeddings
所谓ELMo不过是一些网络层的Embeddings的组合,即下面式子中的 R R R。
对于每个单词(token)
t
k
t_k
tk,对于
L
L
L 层的双向LSTM语言模型,一共有
2
L
+
1
2L+1
2L+1 个表征(representations)(加的1是指初始化时的词向量),放一个集合里用
R
k
R_k
Rk 表示,如下图所示:
- k k k 表示一个文本序列中单词的下标;
- 对于每一个单词 w k w_k wk 而言,在一个 L L L 层 Layer 的双向语言模型中有 2 L + 1 2L+1 2L+1 个Embedding表示(加的1是指初始化时的词向量),分别是 { x k L M , h → k j L M , h ← k j L M , j = 1 , 2 , . . . , L } \{x_k^{LM}, \overrightarrow{h}_{kj}^{LM}, \overleftarrow{h}_{kj}^{LM}, j=1,2,...,L\} {xkLM,hkjLM,hkjLM,j=1,2,...,L},我们把它记作 R k = { h k j L M , j = 0 , 1 , . . . , L } R_k=\{h_{kj}^{LM}, j=0,1,...,L\} Rk={hkjLM,j=0,1,...,L};
- R k R_k Rk表示在一个文本序列中,单词 w k w_k wk在双向语言模型中所有 2 L + 1 2L+1 2L+1 个Embedding表示的组合后最终的ELMo表示;
- j j j 表示第 j j j 层 Layer;
- L M LM LM 表示 Language Model;
- x k L M x^{LM}_{k} xkLM 表示第一层Embedding(token layer)
- h k , 0 L M h^{LM}_{k,0} hk,0LM 表示 x k L M x^{LM}_{k} xkLM,即第一层Embedding(token layer),也就是LSTM的输入,是词的Embedding,它与上下文无关;
- h k , j L M = [ h → k , j L M ; h ← k , j L M ] h^{LM}_{k,j}=[\overrightarrow{h}^{LM}_{k,j};\overleftarrow{h}^{LM}_{k,j}] hk,jLM=[hk,jLM;hk,jLM],每一层有一个前向LSTM的输出,一个后向LSTM的输出,两者就是简单的拼接起来的。也就是如果分别都是 m ∗ 1 m*1 m∗1 维的列向量,拼完之后就是 2 m ∗ 1 2m*1 2m∗1 的列向量,就这么简单。
- 在实际的任务中,参数 h k j L M h_{kj}^{LM} hkjLM都是固定的,可以调的参数只是 γ t a s k \gamma^{task} γtask和 s j t a s k s_j^{task} sjtask。
ELMo通过训练预测单词序列中的下一个单词来获得语言理解能力——这项任务被称为语言建模。这很方便,因为我们有大量的文本数据,这样的模型可以从这些数据中学习,不需要标签。
我们可以看到每个展开的LSTM步骤的隐藏状态从ELMo的头部后面突出来。这些在预训练结束后的嵌入过程中会派上用场。
ELMo实际上更进一步,训练了双向LSTM——这样它的语言模型不仅考虑下一个单词,而且考虑前一个单词。
当ELMo预训练完毕之后,在下游任务中使用时:
- ELMo模型中的各个层的 Embedding不再变化;
- 在下游任务中变化的是ELMo模型各个层 Embedding组合时的权重系数 s j s_j sj,模型进行Fine-tuning 时学习得到 ,通过权重系数的变化来实现一词多义。
四、ELMo如何用于有监督的NLP下游任务?
既然ELMo有这么多向量了,那怎么使用呢?最简单的方法就是使用最顶层的LSTM输出,但是我们有更好的方法使用这些向量。
ELMo通过将隐藏状态(和初始嵌入)以某种方式组合在一起(连接后加权求和),得到语境化词嵌入。
使用多层Hidder Layer后,每个词最终的 Word Embedding = 该词所有层的 Word Embedding 进行加权平均
- Θ t a s k Θ_{task} Θtask 代表具体的下游任务;
- 对于每层向量,我们加一个权重 s j t a s k s_j^{task} sjtask(一个实数),将每层的向量与权重相乘,最后再乘以一个权重 γ t a s k γ^{task} γtask。
- 每层LSTM学到的东西是不一样的,针对每个任务,每层的向量重要性也不一样,所以对于L层LSTM,有 L + 1 L+1 L+1 个权重系数 s j t a s k s_j^{task} sjtask 需要学习(初始化词向量也有权重,所以+1),加上前面的 γ t a s k γ^{task} γtask,一共有 L + 2 L+2 L+2 个参数需要学习。
上图就是我们所说的ELMo向量了。它是多个输出层及输入层,按照一定权重相乘得到的。
1、EMLo用于下游任务后,EMLo预训练模型需要微调(Fine-tuning)的参数有哪些?
EMLo预训练模型用于下游任务时,ELMo模型中的各个层的 Embedding不再变化,需要微调(Fine-tuning)的参数为:
- s j t a s k s_j^{task} sjtask ,代表每层Embedding在加权平均时的 w e i g h t weight weight (权重系数)【在EMLo预训练模型与下游任务一同训练(Fine-tuning)时学习】;
- γ γ γ ,代表加权之前的 Layer Normalization;
2、这个权重 s j t a s k s_j^{task} sjtask 怎么来的?
针对具体的NLP任务,我们用的时候,需要再次训练去得到的这个权重。最简单的方法,就是权重都设为一样。
3、ELMo Embeddings如何用于下游任务
在生成了ELMo向量 E L M o k t a s k ELMo_k^{task} ELMoktask 之后,使用方法依然是拼接。
将单词/词条的表征 x k x_k xk 与 E L M o k t a s k ELMo_k^{task} ELMoktask 拼接起来就可以了,即一个单词的最终向量是这样的 [ x k ; E L M o k t a s k ] [x_k;ELMo_k^{task}] [xk;ELMoktask]。 这就是最终的使用方法。【To add ELMo to the supervised model, we first freeze the weights of the biLM and then concatenate the ELMo vector E L M o k t a s k ELMo_k^{task} ELMoktask with x k x_k xk and pass the ELMo enhanced representation [ x k ; E L M o k t a s k ] [x_k;ELMo_k^{task}] [xk;ELMoktask] into the task RNN.】
-
我们将 E L M o k t a s k ELMo_k^{task} ELMoktask 与 x k x_k xk 要进行拼接,所以 E L M o k t a s k ELMo_k^{task} ELMoktask 会事先有一个 γ t a s k γ^{task} γtask 缩放系数。
-
s j t a s k s_j^{task} sjtask 可以直接取各层平均得到,也可以让模型自己去学习得到。
五、ELMo使用方法总结
对于预训练好的双向LSTM语言模型,我们可以送入一段话,然后模型会得到下图的各个向量
然后我们加上一定的权重(可训练)即可得到下图的ELMo向量。
最终将 E L M o k t a s k ELMo_k^{task} ELMoktask 向量与 x k x_k xk 拼接得到 [ x k ; E L M o k t a s k ] [x_k;ELMo_k^{task}] [xk;ELMoktask] 作为单词的特征,用于后续的处理。
对于部分任务,可以对双向LSTM语言模型微调,可能有所提升。
至于ELMo的效果,总之是很多任务都提升就对了。
六、ELMo学到了什么?ELMo学到了单词在当前上下文环境中的含义(一词多义)
-
从上图可以看到,加入ELMo之后,可以明显将play的两种含义区分开来,而GloVe则不能。
-
Word sense disambiguation(词义消歧)实验,第一层、第二层分别使用的结果显示:
- 越高层对语义的理解越好,表示对词义消歧做的越好;
- 越高层越能捕获词义信息;
-
POS tagging(词性标注)实验,第一层效果好于第二层,表明:底层更能学到词的句法信息、词性信息。
参考资料:
The Illustrated BERT, ELMo, and co. (How NLP Cracked Transfer Learning)
[NLP]高级词向量表达之ELMo详解
ELMo详解
ELMo原理解析及简单上手使用
ELMo讲解