如果需要对基础概念不了解,可以参考这里。我汇总了论文中涉及的大部分概念,以便更好的理解论文。
NLP十大Baseline论文简述(一) - Word2vec
NLP十大Baseline论文简述(二) - GloVe
NLP十大Baseline论文简述(三) - C2W
NLP十大Baseline论文简述(四) -textcnn
NLP十大Baseline论文简述(五) - chartextcnn
NLP十大Baseline论文简述(六) -fasttext
NLP十大Baseline论文简述(七) - deep_nmt
NLP十大Baseline论文简述(八) - attention_nmt
NLP十大Baseline论文简述(九) - han_attention
NLP十大Baseline论文简述(十) - sgm
Efficient Estimation of Word Representations in Vector Space 向量空间中词表示的有效估计
Distributed representations ofwords and phrases and their compositionality单词和短语的分布式表示及其组成
Word2vec是Google的Mikolov等人提出来的一种文本分布式表示的方法,这种方法是对神经网络语言模型的“瘦身”, 巧妙地运用层次softmax(hierarchical softmax )和负采样(Negative sampling )两种技巧,使得原本参数繁多、计算量巨大的神经网络语言模型变得容易计算。
We propose two novel model architectures for computing continuous vector repre-sentations of words from very large data sets. 我们提出了两种新的模型体系结构来计算从非常大的数据集的词的连续向量表示。
The quality of these representationsis measured in a word similarity task, and the results are compared to the previ-ously best performing techniques based on different types of neural networks. 在一个词相似度任务中测量了这些表征的质量,并将结果与以往基于不同类型神经网络的最佳表现技术进行了比较。
Weobserve large improvements in accuracy at much lower computational cost, i.e. ittakes less than a day to learn high quality word vectors from a 1.6 billion wordsdata set. 我们观察到,在计算成本更低的情况下,准确率有了很大的提高,也就是说,从16亿个单词数据集中学习高质量的单词向量只需不到一天的时间。
Furthermore, we show that these vectors provide state-of-the-art perfor-mance on our test set for measuring syntactic and semantic word similarities.此外,我们还表明,这些向量在度量语法和语义词相似度的测试集上提供了最先进的性能。
1)传统NLP把词当成最小单元处理,并且能够在大语料上得到很好的结果,其中一个例子是N-grams模型
2)然而很多自然语言处理任务只能提供很小的语料,如语音识别,机器翻译,所以简单的扩大数据规模来提升简单模型的表现在这些任务不再适用,所以必须寻找更加先进的模型。
3) 分布式表示可以在大语料上训练得到很好的语言模型,并且能超过N-Grams模型, 这是一个很好的可以作为改进的技术。
首先语言模型的基本思想:句子中下一个词的出现和前面的词是有关系的,所以可以使用前面的词预测下一个词。
Word2vec的基本思想:句子中相近的词之间是有联系的,比如今天后面经常出现上午,下午和晚上。所以Word2vec就是用词来预测词,skip-gram使用中心词预测周围词,cbow使用周围词预测中心词。
其实在word2vec出现之前,已经有用神经网络DNN来用训练词向量进而处理词与词之间的关系了。采用的方法一般是一个三层的神经网络结构(当然也可以多层),分为输入层,隐藏层和输出层(softmax层)。这个模型是如何定义数据的输入和输出呢?一般分为CBOW(Continuous Bag-of-Words )与Skip-Gram两种模型。
cbow使用周围词预测中心词,即输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量。
根据图片例子:我们的输入是4个词向量,输出是所有词的softmax概率(训练的目标是期望训练样本特定词对应的softmax概率最大),对应的CBOW神经网络模型输入层有4个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某8个词对应的最可能的输出中心词时,我们可以通过一次DNN前向传播算法并通过softmax激活函数找到概率最大的词对应的神经元即可。
skip-gram使用中心词预测周围词, 用‘下午’来预测其余的词。即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。
具体过程: 即输入特定词 W i W_i Wi‘下午’, 输出时softmax概率前4的4个词,对应的Skip-Gram神经网络模型输入层有1个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某1个词对应的最可能的8个上下文词时,我们可以通过一次DNN前向传播算法得到概率大小排前8的softmax概率对应的神经元所对应的词即可。
层次化softmax是为了解决用softmax进行V分类时(V是词典大小),由于词典巨大导致计算目标词的似然概率的低效问题。
层次化softmax改变了原来的模型结构。原来是1V(输入one-hot),经过VD的矩阵(input-embedding) ,再经过DV矩阵(output-embedding),即一系列矩阵乘法(1,V)(V,D)*(D,V)=(1,V)【其中的第一次乘法,一般用查表的方式直接读取,不用相乘】,再softmax,得到目标词是词典中每个词的概率。使用层次化softmax时,output-embedding被取消了,查表后直接用(1,D)的向量与每个中间路径节点对应的一个向量γn【形状为(D,1)】,进行相乘,得到一个scalar,再经过sigmoid,转为一个0到1之间的小数,此数从就是从树(树是二叉树,树的每个叶子节点都对应一个单词)的根节点到某个叶子节点的路径上每次在中间节点选择向左(也可定为向右)走的概率。
层次化softmax的目标函数就是最大化目标词的路径概率,换句话说,我们只需要关注目标词的路径概率即可,而目标词是已知的,完全不需要计算其他的词的路径概率。这点是后续优化措施的关键。
那层次化softmax的优点体现在哪里?体现在计算预测输出值为每个单词的概率时,由于概率是从根节点到叶子节点的路径上的边进行连乘,也就是计算量与目标词(叶子节点)的路径长度成正比,原来的softmax的目标函数在反向传播时需要求导,而求导时,由于分母是e的指数的累加和,导致必须算出所有单词的概率,计算量是与词表大小V成正比。【目标函数为交叉熵,loss=-ΣYilogPi, 其中i代表某个类别,而只有目标词的Yi为1,其余词的Yi为0,因此loss=-logP(target)。而Pi=softmax(w1w2),w1,w2分别是input-embedding、output-embedding矩阵中的参数, 先求对w2的梯度:d(loss) /d(w2)=-1/P(target)* d(P(target))/d(w2),可见梯度中包括P(target)的计算,因此必须包含V个e的指数计算。】
而层次化softmax的树是二叉树,路径长度也就是树的高度,假设是满二叉树,则树高=V取2为底的对数值,远远小于V,节省了很多计算过程。比如一个大小为1024的词典,原来softmax是1024个e的指数计算,现在是log1024=10次sigmoid值再连乘,共节省了1024-10=1014次计算。
是否还可以进一步优化?可以。以上表述中层次化的softmax用到的树,一般认为是满的二叉树,也就是树的高度为logV,其实还有优化的空间。因为目标词的概率仅与根节点到目标词的路径长度有关,如果我们能够优化根节点到目标词的路径长度,就可以进一步节省计算。什么二叉树的根节点到叶子节点的路径长度最短?答案是哈夫曼树。
因此可以把树做成哈夫曼树。mikolov的论文就是采用了哈夫曼树,且构建哈夫曼树时,比较节点的权重大小是依据该节点(也就是单词)的词频,词频大的放在离根节点近的地方,词频小的远离根节点。如此词频大的词只需要很少的比较次数,路径就结束了,计算次数比较少。
由于是根据词频来构建哈夫曼树,所以一开始这课树的结构就是已经确定的,每个叶子节点对应的单词也是已知的,换言之根节点到目标节点的路径包括中间节点、向左还是向右也是已知的,每个中间节点对应的向量的形状已经定好了,并不是真的需要每次都要判断向左走还是向右走。后续优化时只需要更新中间节点对应的向量、词向量即可。
应用Hierarchical Softmax就是把 N 分类问题变成 log(N)次二分类。
舍弃多分类,提升速度。
自然语言处理共识:文档或者数据集中出现频率高的词往往携带信息较少,比如the, is, a, and 而出现频率低的词往往携带信息多。
为什么用重采样:
方法:
P ( w i ) = 1 − t / f ( w i ) P(w_i)=1-\sqrt{t/f(w_i)} P(wi)=1−t/f(wi)
其中 f ( w i ) f(w_i) f(wi)为词 w i w_i wi在数据集中出现的频率。文中t选取为 1 0 − 5 10^{-5} 10−5,训练集中的词 w i w_i wi会以 P ( w i ) P(w_i) P(wi)的概率被删除。
分析上面式子,词频越大, f ( w i ) f(w_i) f(wi)越大, P ( w i ) P(w_i) P(wi)越大,那么词 w i w_i wi就有更大概率被删除,反之亦然。如果词 w i w_i wi的词频小于等于t,那么 w i w_i wi则不会被剔除。
优点: 加速训练,能够得到更好的词向量。
O = E ∗ T ∗ Q O = E * T *Q O=E∗T∗Q
O O O 是训练复杂度Training complexity
E E E 是训练迭代次数Number of the training epochs
T T T 是数据集大小Number of the words in the training set
Q Q Q 是模型计算复杂度Model computational complexity
Q = V ∗ H + N ∗ D ∗ H + N ∗ D Q=V * H + N * D * H + N * D Q=V∗H+N∗D∗H+N∗D
Q = H ∗ H + H ∗ V Q = H * H + H * V Q=H∗H+H∗V
层次Softmax:
Q = C ( D + D ∗ l o g 2 V ) Q = C ( D + D * log_2 V ) Q=C(D+D∗log2V)
负样本:
Q = C ( D + D ∗ ( K + 1 ) ) Q = C ( D + D * (K + 1)) Q=C(D+D∗(K+1))
Q = N ∗ D + D ∗ V Q = N*D + D*V Q=N∗D+D∗V
层次Softmax:
Q = N ∗ D + D ∗ l o g 2 V Q = N*D + D*log_2 V Q=N∗D+D∗log2V
负样本:
Q = N ∗ D + D ∗ ( K + 1 ) ) Q = N*D + D*(K + 1)) Q=N∗D+D∗(K+1))
用小数据集调参,选择最好的参数。
维度,以及训练数据量,是2个需要寻找的参数。
The main innovation fo the paper:
关键点:
更简单的预测模型 - Word2vec
更快的分类方案 - HS和NEG
创新点:
使用词对的预测来代替语言模型的预测
使用HS和NEG降低分类复杂度
使用subsampling加快训练
新的词对推理数据集来评估词向量的质量
启发点:
大数据集上的简单模型往往强于小数据集上的复杂模型
King的词向量减去man的词向量加上women的词向量和Queen的词向量最接近
我们设计简单的模型来训练词向量,虽然简答的模型无法像神经网络那么准确的表示数据,但是可以在更多的数据上更快的训练
我们相信在更大的数据集上使用更大的词向量维度能够训练得到更好的词向量。
以下命令直接在服务器命令行以及vim 文件中操作,命令与Jupyter notebook和pycharm不一样。
这里使用英语维基百科的部分网页信息
首先创建一个存储数据的文件夹data
$ mkdir data
# 使用wget下载数据的zip压缩包, 它将存储在data目录中
$ wget -c http://mattmahoney.net/dc/enwik9.zip -P data
# 使用unzip解压, 如果你的服务器中还没有unzip命令, 请使用: yum install unzip -y
# 解压后在data目录下会出现enwik9的文件夹
$ unzip data/enwik9.zip -d data
查看原始数据
$ head -10 data/enwik9
# 原始数据将输出很多包含XML/HTML格式的内容, 这些内容并不是我们需要的
<mediawiki xmlns=" http://www.mediawiki.org/xml/export-0.3/ " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance " xsi:schemaLocation=" http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd " version="0.3" xml:lang="en">
<siteinfo>
<sitename>Wikipedia</sitename>
<base> http://en.wikipedia.org/wiki/Main_Page </base>
<generator>MediaWiki 1.6alpha</generator>
<case>first-letter</case>
<namespaces>
<namespace key="-2">Media</namespace>
<namespace key="-1">Special</namespace>
<namespace key="0" />
原始数据处理
wikifil.pl 可以从此处下载https://gitee.com/mirrors/fasttext/blob/master/wikifil.pl
# 使用wikifil.pl文件处理脚本来清除XML/HTML格式的内容
$ perl wikifil.pl data/enwik9 > data/fil9
# 查看前80个字符
head -c 80 data/fil9
# 输出结果为由空格分割的单词
anarchism originated as a term of abuse first used against early working class
fasttext安装有点点复杂:(Ubuntu 20.04 Server)
$ wget https://github.com/facebookresearch/fastText/archive/v0.9.2.zip
$ unzip v0.9.2.zip
$ cd fastText-0.9.2
$ make
国内的关系wget经常不成功,可以转战gitee,https://gitee.com/mirrors/fasttext
直接克隆下载然后unzip也可以make
# 代码运行在python解释器中
# 导入fasttext
>>> import fasttext
# 使用fasttext的train_unsupervised(无监督训练方法)进行词向量的训练
# 它的参数是数据集的持久化文件路径'data/fil9'
>>> model = fasttext.train_unsupervised('data/fil9')
# 有效训练词汇量为124M, 共218316个单词
Read 124M words
Number of words: 218316
Number of labels: 0
Progress: 100.0% words/sec/thread: 53996 lr: 0.000000 loss: 0.734999 ETA: 0h 0m
查看单词对应的词向量:
# 通过get_word_vector方法来获得指定词汇的词向量
>>> model.get_word_vector("the")
array([-0.03087516, 0.09221972, 0.17660329, 0.17308897, 0.12863874,
0.13912526, -0.09851588, 0.00739991, 0.37038437, -0.00845221,
...
-0.21184735, -0.05048715, -0.34571868, 0.23765688, 0.23726143],
dtype=float32)
# 在训练词向量过程中, 我们可以设定很多常用超参数来调节我们的模型效果, 如:
# 无监督训练模式: 'skipgram' 或者 'cbow', 默认为'skipgram', 在实践中,skipgram模
式在利用子词方面比cbow更好.
# 词嵌入维度dim: 默认为100, 但随着语料库的增大, 词嵌入的维度往往也要更大.
# 数据循环次数epoch: 默认为5, 但当你的数据集足够大, 可能不需要那么多次.
# 学习率lr: 默认为0.05, 根据经验, 建议选择[0.01,1]范围内.
# 使用的线程数thread: 默认为12个线程, 一般建议和你的cpu核数相同.
>>> model = fasttext.train_unsupervised('../data/fil9', "cbow", dim=300, epoch=10, lr=0.1, thread=1)
Read 124M words
Number of words: 218316
Number of labels: 0
Progress: 100.0% words/sec/thread: 49523 lr: 0.000000 avg.loss: 1.777205 ETA: 0h 0m 0s
# 检查单词向量质量的一种简单方法就是查看其邻近单词, 通过我们主观来判断这些邻近单词是否与目标单词相关来粗略评定模型效果好坏.
# 查找"运动"的邻近单词, 我们可以发现"体育网", "运动汽车", "运动服"等.
>>> model.get_nearest_neighbors('sports')
[(0.8414610624313354, 'sportsnet'), (0.8134572505950928, 'sport'), (0.8100415468215942, 'sportscars'), (0.8021156787872314, 'sportsground'), (0.7889881134033203, 'sportswomen'), (0.7863013744354248, 'sportsplex'), (0.7786710262298584, 'sporty'), (0.7696356177330017, 'sportscar'), (0.7619683146476746, 'sportswear'), (0.7600985765457153, 'sportin')]
# 查找"音乐"的邻近单词, 我们可以发现与音乐有关的词汇.
>>> model.get_nearest_neighbors('music')
[(0.8908010125160217, 'emusic'), (0.8464668393135071, 'musicmoz'), (0.8444250822067261, 'musics'), (0.8113634586334229, 'allmusic'), (0.8106718063354492, 'musices'), (0.8049437999725342, 'musicam'), (0.8004694581031799, 'musicom'), (0.7952923774719238, 'muchmusic'), (0.7852965593338013, 'musicweb'), (0.7767147421836853, 'musico')]
# 查找"小狗"的邻近单词, 我们可以发现与小狗有关的词汇.
>>> model.get_nearest_neighbors('dog')
[(0.8456876873970032, 'catdog'), (0.7480780482292175, 'dogcow'), (0.7289096117019653, 'sleddog'), (0.7269964218139648, 'hotdog'), (0.7114801406860352, 'sheepdog'), (0.6947550773620605, 'dogo'), (0.6897546648979187, 'bodog'), (0.6621081829071045, 'maddog'), (0.6605004072189331, 'dogs'), (0.6398137211799622, 'dogpile')]
# 使用save_model保存模型
>>> model.save_model("fil9.bin")
# 使用fasttext.load_model加载模型
>>> model = fasttext.load_model("fil9.bin")
>>> model.get_word_vector("the")
array([-0.03087516, 0.09221972, 0.17660329, 0.17308897, 0.12863874,
0.13912526, -0.09851588, 0.00739991, 0.37038437, -0.00845221,
...
-0.21184735, -0.05048715, -0.34571868, 0.23765688, 0.23726143],
dtype=float32)