ELMo词向量出自于论文《Deep contextualized word representations》
什么是ELMo?
ELMo的全称是Embeddings from Language Models,中文意思是基于语言模型的词向量,它是一个上下文词向量/动态词向量,同一单词在不同的上下文中具有不同的含义,而ELMo词向量能表示出这种不同。
实现过程可以简单描述为:将不同的句子输入模型,模型会实时产生上下文词向量。
ELMo是如何实现的?
-
构建一个双向语言模型,给定一个包含N个tokens的序列
前向语言模型,给定前面的tokens求下一个token是的概率:
反向语言模型,与前向语言模型相反,知道后边的token,求前面的token:
那么,双向语言模型可以表示为:
其中,是输入层单词表示的参数, 是LSTM参数,是Softmax层的参数。 -
结合网络各层的双向语言模型的表达
对于token,通过一个由层双向语言模型构成的网络,可以得到个表示:
是对token直接编码的结果(可以使用word2vec,Glove,或者将字符通过CNN进行编码),是每个biLSTM层输出的结果。
在下游任务中,可以组合不同层的单词的表示进行使用:
其中,是任务相关的scale参数,不同的任务要设置不同的数值,是一个softmax的结果,可以在训练的过程中学习。
ELMo是如何训练的?
网络结构共三层
- 第一层是普通的word embedding 可以用wrod2vec或者glove来得到,或者使用character level得到token embedding。 这部分是general embedding,上下文无关。文中使用的是character level的CNN+Highway。
- 后面两层是两个biLSTM 去encode 输入(同时也有残差连接), 每一层LSTM得到的输出(隐状态) 作为每个词的上下文相关的word vectors。
ELMo和Glove的区别
-
Glove 词向量是固定的词向量。同一单词在不同的语句中具有相同的表示。
-
ELMo的基本输入单元为句子,每个词没有固定的词向量,是根据词的上下文环境来动态产生当前词的词向量,可以较好地解决一词多义的问题。
-
ELMo 的输入是字母而不是单词。因此,他们可以利用子字词单元来计算有意义的表示,即使对于词典外的词(如 FastText这个词)也是如此。
-
ELMo 是 biLMs 几层激活的串联。语言模型的不同层对单词上的不同类型的信息进行编码(如在双向LSTM神经网络中,词性标注在较低层编码好,而词义消歧义用上层编码更好)。连接所有层可以自由组合各种文字表示,以提高下游任务的性能。
实践参考代码
哈工大改写的多国语言版:https://github.com/HIT-SCIR/ELMoForManyLangs
Pytorch版ELMo(Allennlp)
from allennlp.commands.elmo import ElmoEmbedder
elmo = ElmoEmbedder(options_file='../data/elmo_options.json', weight_file='../data/elmo_weights.hdf5', cuda_device=0)
context_tokens = [['I', 'love', 'you', '.'], ['Sorry', ',', 'I', 'don', "'t", 'love', 'you', '.']]
elmo_embedding, elmo_mask = elmo.batch_to_embeddings(context_tokens)
print(elmo_embedding)
print(elmo_mask)
1. 导入ElmoEmbedder类
2. 实例化ElmoEmbedder. 3个参数分别为参数配置文件, 预训练的权值文件, 想要用的gpu编号, 这里两个文件我是直接下载好的, 如果指定系统默认自动下载会花费一定的时间, 下载地址
DEFAULT_OPTIONS_FILE = "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_options.json"
DEFAULT_WEIGHT_FILE = "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_weights.hdf5"
3. 输入是一个list的token序列, 其中外层list的size即内层list的个数就是我们平时说的batch_size, 内层每个list包含一个你想要处理的序列(这里是一句话, 你可以一篇文章或输入任意的序列, 因为这里预训练的模型是在英文wikipidia上训的, 所以输入非英文的序列肯定得到的结果没什么意义).
4. 通过batch_to_embeddings对输入进行计算的到tokens的embedding结果以及我们输入的batch的mask信息(自动求mask)
Variable containing:
( 0 , 0 ,.,.) =
0.6923 -0.3261 0.2283 ... 0.1757 0.2660 -0.1013
-0.7348 -0.0965 -0.1411 ... -0.3411 0.3681 0.5445
0.3645 -0.1415 -0.0662 ... 0.1163 0.1783 -0.7290
... ⋱ ...
0.0000 0.0000 0.0000 ... 0.0000 0.0000 0.0000
0.0000 0.0000 0.0000 ... 0.0000 0.0000 0.0000
0.0000 0.0000 0.0000 ... 0.0000 0.0000 0.0000
⋮
( 1 , 2 ,.,.) =
-0.0830 -1.5891 -0.2576 ... -1.2944 0.1082 0.6745
-0.0724 -0.7200 0.1463 ... 0.6919 0.9144 -0.1260
-2.3460 -1.1714 -0.7065 ... -1.2885 0.4679 0.3800
... ⋱ ...
0.1246 -0.6929 0.6330 ... 0.6294 1.6869 -0.6655
-0.5757 -1.0845 0.5794 ... 0.0825 0.5020 0.2765
-1.2392 -0.6155 -0.9032 ... 0.0524 -0.0852 0.0805
[torch.cuda.FloatTensor of size 2x3x8x1024 (GPU 0)]
Variable containing:
1 1 1 1 0 0 0 0
1 1 1 1 1 1 1 1
[torch.cuda.LongTensor of size 2x8 (GPU 0)]
输出两个Variable, 第一个是2*3*8*1024的embedding信息, 第二个是mask.
其中2是batch_size, 3是两层biLM的输出加一层CNN对character编码的输出, 8是最长list的长度(对齐), 1024是每层输出的维度
mask的输出2是batch_size, 8实在最长list的长度, 第一个list有4个tokens, 第二个list有8个tokens, 所以对应位置输出1.
参考资料
http://shomy.top/2019/01/01/elmo-1/
https://cstsunfu.github.io/2018/06/ELMo/
https://blog.csdn.net/sinat_26917383/article/details/81913790
网友评论