在本章的其余部分中,我们从独热表示开始。此外,我们还详细介绍了分布式词表示模型,包括布朗集群、潜在语义分析、Word2vec和GloVe。然后介绍了两个典型的词表示评价任务。最后,我们讨论了单词表示模型的各种扩展。
2.2 独热编码
本质上,独热表示将每个单词映射到词汇表的索引,这对于存储和计算非常有效。然而,它并不包含丰富的单词的语义和句法信息。因此,一热表示不能捕捉到单词之间的相关性。猫和狗的区别就像猫和床的一个热门词表示的区别一样。此外,一个热门的单词表示将每个单词嵌入到一个|V|维向量中,这只能适用于一个固定的词汇量。因此,在现实世界中处理新词是不灵活的。
2.3 Distributed Word Representation
2.3.1 Brown Cluster
布朗聚类将单词分为几个具有相似语义意义的聚类。其中,Brown聚类从一个大规模的语料库中学习一个二叉树,其中树的叶子表示单词,树的内部节点表示单词的层次聚类。这是一种硬聚类方法,因为每个单词只属于一个组。
用布朗聚类来聚类单词的想法来自于n-gram语言模型。用一个语言模型来评估一个句子出现的概率。例如,句子have a nice day应该比随机的单词序列有更高的概率。使用k-gram语言模型,一个句子的s = {w1,w2,w3,…,wn}的概率可以表示为:
P ( s ) = ∏ i = 1 n P ( w i ∣ w i − k i − 1 ) . P(s)=\prod_{i=1}^nP(w_i|\mathbf{w}_{i-k}^{i-1}). P(s)=∏i=1nP(wi∣wi−ki−1).
从一个大的语料库中估计 P ( w i ∣ w i − k i − 1 ) P\left(w_{i}|\mathbf{w}_{i-k}^{i-1}\right) P(wi∣wi−ki−1)很容易,但是模型具有 ∣ V ∣ k − 1 |V|^k−1 ∣V∣k−1独立参数,这在20世纪90年代对计算机来说是一个巨大的数字。即使k是2,参数的数量也是相当大的。此外,对罕见的单词的估计是不准确的。为了解决这些问题,[9]提出将单词分组为集群,并训练集群级的n-gram语言模型,而不是单词级的模型。通过为每个单词分配一个簇,这个概率可以被写成
P ( s ) = ∏ i = 1 n P ( c i ∣ c i − k i − 1 ) P ( w i ∣ c i ) , P(s)=\prod_{i=1}^nP(c_i|\mathbf{c}_{i-k}^{i-1})P(w_i|c_i), P(s)=∏i=1nP(ci∣ci−ki−1)P(wi∣ci),
其中ci是wi的对应簇。在集群级语言模型中,只有 ∣ C k ∣ − 1 + ∣ V ∣ − ∣ C ∣ |C^k|−1+|V|−|C| ∣Ck∣−1+∣V∣−∣C∣独立参数,其中C是集群集,通常比词汇表|V|要小得多。
2.3.2 Latent Semantic Analysis(LSA)
潜在语义分析(LSA)是一类基于向量空间模型的策略,它可以更好地捕获单词的语义。LSA旨在通过矩阵分解来探索词和文档的潜在因素,以提高词相似性的估计。参考[14]对单词-文档矩阵应用奇异值分解(SVD),并利用单词和文档的不相关因素。字文档矩阵M的支持向量值得到三个矩阵E、Σ和D
M = E Σ D ⊤ , \mathbf{M}=\mathbf{E}\Sigma\mathbf{D}^\top, M=EΣD⊤,
其中Σ为M奇异值的对角矩阵,矩阵E中的每行向量wi对应word wi,矩阵D中的每行向量di对应文档di。那么,两个单词之间的相似性就可以是
sin ( w i , w j ) = M i , ⋮ M j , : ⊤ = w i Σ 2 w j . \sin(w_i,w_j)=\mathbf{M}_{i,\vdots}\mathbf{M}_{j,:}^{\top}=\mathbf{w}_i\Sigma^2\mathbf{w}_j. sin(wi,wj)=Mi,⋮Mj,:⊤=wiΣ2wj.
在这里,Σ中包含的奇异值k的数量是一个需要进行调整的超参数。通过使用合理数量的最大奇异值,LSA可以在单词-文档矩阵中捕获许多有用的信息,并提供平滑效果,防止较大的方差。
LSA工作原理
潜语义分析(Latent Semantic Analysis)来源于信息检索(IR)中的一个问题:如何从搜索query中找到相关的文档。有的人说可以用关键词匹配文档,匹配上的就放在搜索结果前面。这明显是有问题滴,一是背景里我们讲过一词多义和同义词问题,二则10亿+的文档采用关键词匹配,实在不现实。
其实呀,在搜索中我们实际想要去比较的不是词,而是隐藏在词之后的意义和概念,用人话说:query搜索的结果文档隐含的概念要和query的概念尽可能的相近。LSA分析试图去解决这个问题,它把词和文档都映射到一个”概念”空间并在这个空间内进行比较(注:也就是一种降维技术)。
当文档的作者写作的时候,对于词语有着非常宽泛的选择。不同的作者对于词语的选择有着不同的偏好,这样会导致概念的混淆。这种对于词语的随机选择在 词-概念 的关系中引入了噪音。LSA滤除了这样的一些噪音,并且还能够从全部的文档中找到最小的概念集合(为什么是最小?)。
为了让这个难题更好解决,LSA引入一些重要的简化:
- 文档被用BOW( bags of words : 词袋 )表示,因此词在文档中出现的位置并不重要,只有一个词的出现次数。
- 概念被表示成经常出现在一起的一些词的某种模式。有点想KNN的思想,文档中概念A相关的词出现次数最多,就把这个文档映射到概念A。例如:4k屏幕、8核CPU 、4G RAM ……经常出现在手机广告文档中。
- 词被认为只有一个意思。但是这可以使得问题变得更加容易。(这个简化会有怎样的缺陷呢?)
2.3.3 word2vec2
谷歌的word2vec2工具包于2013年发布。它可以有效地从一个大型语料库中学习单词向量。该工具包有两种模型,包括连续的单词包(CBOW)和跳过克包。基于一个单词的含义可以从其上下文中学习的假设,CBOW优化了嵌入,以便它们能够预测给定其上下文单词的目标单词。相反,Skip-gram学习了可以预测给定目标单词的上下文单词的嵌入。在本节中,我们将详细介绍这两个模型。
2.3.3.1 Continuous Bag-of-Words
CBOW预测了给定一个上下文窗口的中心词。图2.1显示了一个由5个单词组成的窗口组成的CBOW的概念。在形式上,CBOW根据其上下文预测wi为:
P ( w i ∣ w j ( ∣ j − i ∣ ≤ l , j ≠ i ) ) = Softmax ( M ( ∑ ∣ j − i ∣ ≤ l , j ≠ i w j ) ) , ( 2.15 ) P(w_i|w_{j(|j-i|\leq l,j\neq i)})=\text{Softmax}\left(\text{M}\left(\sum_{|j-i|\leq l,j\neq i}\mathbf{w}_j\right)\right),\quad(2.15) P(wi∣wj(∣j−i∣≤l,j=i))=Softmax(M(∑∣j−i∣≤l,j=iwj)),(2.15)
其中 P ( w i ∣ w j ( ∣ j − i ∣ ≤ l , j ≠ i ) ) P(w_i|w_{j(|j-i|\leq l,j\neq i)}) P(wi∣wj(∣j−i∣≤l,j=i))是给定其上下文的word wi的概率, l l l是训练上下文的大小,M是 R ∣ V ∣ × m R^{|V|×m} R∣V∣×m中的权重矩阵,V表示词汇,m是单词向量的维度。
CBOW模型通过最小化负对数概率的和来进行优化: L = − ∑ i log P ( w i ∣ w j ( ∣ j − i ∣ ≤ l , j ≠ i ) ) . \mathscr{L}=-\sum_i\log P(w_i|w_{j(|j-i|\leq l,j\neq i)}). L=−∑ilogP(wi∣wj(∣j−i∣≤l,j=i)).
CBOW模型包含三层:输入层,投影层,输出层。与NNML相比,去掉了隐藏层。
CBOW是根据上下文预测中心词,有点类似于完形填空。上下文的多少是个超参数,可以自己调整。
在构建数据集时,根据CBOW的特点,一般是将上下文当作输入,中心词当作标签。
下面为示例代码:
import torch
from torch import nn, optim
from torch.autograd import Variable
import torch.nn.functional as F
CONTEXT_SIZE = 2
raw_text = "We are about to study the idea of a computational process. Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data. The evolution of a process is directed by a pattern of rules called a program. People create programs to direct processes. In effect, we conjure the spirits of the computer with our spells.".split(' ')
vocab = set(raw_text)
word_to_idx = {word: i for i, word in enumerate(vocab)}
data = []
for i in range(CONTEXT_SIZE, len(raw_text)-CONTEXT_SIZE):
context = [raw_text[i-2], raw_text[i-1], raw_text[i+1], raw_text[i+2]]
target = raw_text[i]
data.append((context, target))
class CBOW(nn.Module):
def __init__(self, n_word, n_dim, context_size):
super(CBOW, self).__init__()
self.embedding = nn.Embedding(n_word, n_dim)
self.linear1 = nn.Linear(2*context_size*n_dim, 128)
self.linear2 = nn.Linear(128, n_word)
def forward(self, x):
x = self.embedding(x)
x = x.view(1, -1)
x = self.linear1(x)
x = F.relu(x, inplace=True)
x = self.linear2(x)
x = F.log_softmax(x)
return x
model = CBOW(len(word_to_idx), 100, CONTEXT_SIZE)
if torch.cuda.is_available():
model = model.cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
for epoch in range(40):
print('epoch {}'.format(epoch))
print('*'*10)
running_loss = 0
for word in data:
context, target = word
context = Variable(torch.LongTensor([word_to_idx[i] for i in context]))
target = Variable(torch.LongTensor([word_to_idx[target]]))
if torch.cuda.is_available():
context = context.cuda()
target = target.cuda()
out = model(context)
loss = criterion(out, target)
running_loss += loss.data
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('loss: {:.6f}'.format(running_loss / len(data)))
2.3.3.2 Skip-Gram
与CBOW相反,Skip-gram预测了给定中心词的上下文。图2.2显示了该模型。形式上,给定一个单词wi,Skip-gram预测其上下文为
P ( w j ∣ w i ) = softmax ( M w i ) ( ∣ j − i ∣ ≤ l , j ≠ i ) , P(w_j|w_i)=\operatorname{softmax}(\mathbf{M}\mathbf{w}_i)\big(|j-i|\leq l,j\neq i\big), P(wj∣wi)=softmax(Mwi)(∣j−i∣≤l,j=i),
其中 P ( w j ∣ w i ) P(w_j|w_i) P(wj∣wi)为给定的上下文词wjwi的概率,M为权值矩阵。损失函数的定义类似于CBOW,其定义为
L = − ∑ i ∑ j ( ∣ j − i ∣ ≤ l , j ≠ i ) P ( w j ∣ w i ) . \mathscr{L}=-\sum_{i}\sum_{j(|j-i|\leq l,j\neq i)}P(w_j|w_i). L=−∑i∑j(∣j−i∣≤l,j=i)P(wj∣wi).
#####2.3.3.3 Hierarchical Softmax and Negative Sampling
层次化的softmax根据单词的频率生成层次化的类,即霍夫曼树。通过近似,它可以更快地计算每个单词的概率,而计算每个单词的概率的复杂性是负抽样更简单。为了计算一个单词的概率, O ( l o g ∣ V ∣ ) O(log |V|) O(log∣V∣)。
负抽样更为直接。为了计算一个单词的概率,O(log |V|)。负抽样根据单词频率直接抽取k个单词作为负样本。然后,它计算k + 1个单词上的softmax来近似目标单词的概率。
2.3.4 GloVe
诸如Skip-gram和CBOW等方法都是基于浅窗口的方法。这些方法扫描了整个语料库的上下文窗口,但没有利用一些全局信息。相反,Word表示的全局向量(GloVe)可以直接捕获语料库统计量。
GloVe的全称叫Global Vectors for Word Representation,它是一个基于全局词频统计(count-based & overall statistics)的词表征(word representation)工具,它可以把一个单词表达成一个由实数组成的向量,这些向量捕捉到了单词之间一些语义特性,比如相似性(similarity)、类比性(analogy)等。我们通过对向量的运算,比如欧几里得距离或者cosine相似度,可以计算出两个单词之间的语义相似性。
GloVe是如何实现的?
GloVe的实现分为以下三步:
-
根据语料库(corpus)构建一个共现矩阵(Co-ocurrence Matrix)X(什么是共现矩阵?),
矩阵中的每一个元素 x i j x_{ij} xij代表单词i和上下文单词j在特定大小的上下文窗口(context window)内共同出现的次数。
一般而言,这个次数的最小单位是1,但是GloVe不这么认为:它根据两个单词在上下文窗口的距离d,提出了一个衰减函数(decreasing weighting):
d e c a y = 1 / d decay=1/d decay=1/d
用于计算权重,也就是说距离越远的两个单词所占总计数(total count)的权重越小。
In all cases we use a decreasing weighting function, so that word pairs that are d words apart contribute 1/d to the total count.
-
构建词向量(Word Vector)和共现矩阵(Co-ocurrence Matrix)之间的近似关系,论文的作者提出以下的公式可以近似地表达两者之间的关系: w i T w j ~ + b i + b j ~ = log ( X i j ) w_i^T\tilde{w_j}+b_i+\tilde{b_j}=\log(X_{ij}) wiTwj~+bi+bj~=log(Xij)
其中,
w
i
T
w
j
~
w_i^T\tilde{w_j}
wiTwj~是我们最终要求解的词向量;
b
i
,
b
j
~
b_i,\tilde{b_j}
bi,bj~分别是两个词向量的bias term。
当然你对这个公式一定有非常多的疑问,比如它到底是怎么来的,为什么要使用这个公式,为什么要构造两个词向量
w
i
T
w
j
~
w_i^T\tilde{w_j}
wiTwj~文我们会详细介绍。
有了公式1之后我们就可以构造它的loss function了: J = ∑ i , j = 1 V f ( X i j ) ( w i T w ~ j + b i + b ~ j − log ( X i j ) ) 2 J=\sum_{i,j=1}^{V}f(X_{ij})(w_i^T\tilde{w}_j+b_i+\tilde{b}_j-\log(X_{ij}))^2 J=∑i,j=1Vf(Xij)(wiTw~j+bi+b~j−log(Xij))2
这个loss function的基本形式就是最简单的mean square loss,只不过在此基础上加了一个权重函数 f ( X i j ) f(X_{ij}) f(Xij)
,那么这个函数起了什么作用,为什么要添加这个函数呢?我们知道在一个语料库中,肯定存在很多单词他们在一起出现的次数是很多的(frequent co-occurrences),那么我们希望:
- 1.这些单词的权重要大于那些很少在一起出现的单词(rare co-occurrences),所以这个函数要是非递减函数(non-decreasing);
- 2.但我们也不希望这个权重过大(overweighted),当到达一定程度之后应该不再增加;
- 3.如果两个单词没有在一起出现,也就是 X i j X_{ij} Xij=0,那么他们应该不参与到loss function的计算当中去,也就是f(x)要满足f(0)=0
满足以上两个条件的函数有很多,作者采用了如下形式的分段函数:
Glove与LSA、word2vec的比较
LSA(Latent Semantic Analysis)是一种比较早的count-based的词向量表征工具,它也是基于co-occurance matrix的,只不过采用了基于奇异值分解(SVD)的矩阵分解技术对大矩阵进行降维,而我们知道SVD的复杂度是很高的,所以它的计算代价比较大。还有一点是它对所有单词的统计权重都是一致的。而这些缺点在GloVe中被一一克服了。而word2vec最大的缺点则是没有充分利用所有的语料,所以GloVe其实是把两者的优点结合了起来。从这篇论文给出的实验结果来看,GloVe的性能是远超LSA和word2vec的,但网上也有人说GloVe和word2vec实际表现其实差不多。
2.4 Contextualized Word Representation(双向lstm)
这里解决的也就是著名的多义问题,比如说bank在不同语境,会有不一样的意义。
为了解决这个问题,[48]提出了ELMo,它使用一个深度的双向LSTM模型来构建单词表示。ELMo可以根据表示每个单词
在使用它的整个上下文中。更具体地说,ELMo没有单词嵌入矩阵的查找表,而是通过将单词及其周围文本输入深度神经网络,将单词转换为低维向量。ELMo利用一个双向语言模型来进行单词表示。具体可见我的博客分析。
2.6 Evaluation
近年来,人们提出了各种将单词嵌入向量空间的方法。因此,评估不同的方法是很有必要的。对单词嵌入有两种一般的评价,包括单词相似性和单词类比。他们的目标都是检查分发这个词是否合理。这两种评价有时会产生不同的结果。例如,CBOW在单词相似度方面表现更好,而Skip-gram在单词类比方面表现优于CBOW。因此,选择哪种方法取决于高级应用程序。特定于任务的单词嵌入方法通常是为特定的高级任务而设计的.
2.6.1 Word Similarity/Relatedness
单词的动态是非常复杂和微妙的。没有一个静态的、有限的关系集可以描述两个词之间的所有相互作用。对于下游任务来说,利用不同类型的单词关系也不是一件小事。一种更实际的方法是给一对单词分配一个分数,以表示它们之间的关联程度。这种测量方法被称为单词的相似度。当谈论这个术语单词的相似性时,确切的含义在不同的情况下可能会有很大的不同。在各种不同的文献中,可以提到几种相似之处。
2.6.2 Word Analogy
s
单词的动态是非常复杂和微妙的。没有一个静态的、有限的关系集可以描述两个词之间的所有相互作用。对于下游任务来说,利用不同类型的单词关系也不是一件小事。一种更实际的方法是给一对单词分配一个分数,以表示它们之间的关联程度。这种测量方法被称为单词的相似度。当谈论这个术语单词的相似性时,确切的含义在不同的情况下可能会有很大的不同。在各种不同的文献中,可以提到几种相似之处。
2.6.2 Word Analogy
除了单词相似性外,单词类比任务也是衡量表征捕获单词语义意义的另一种方法。这个任务给出三个单词w1、w2和w3,然后要求系统预测一个单词w4,使w1和w2之间的关系与w3和w4之间的关系相同。从[43,45]开始,这个任务就被用来利用单词之间的结构关系。在这里,词的关系可以分为语义关系和句法关系两类。这是一种相对新颖的单词表示评估方法,但由于数据集发布,很快成为一个标准的评估度量。与托福同义词测试不同的是,这个数据集中的大多数单词在各种语料库中都很常见,但是第四个单词是从整个词汇表中选择的,而不是四个选项。这个测试倾向于分布式单词表示,因为它强调了单词空间的结构。在[7,56,57,61]中可以找到不同模型之间的比较。