Word Embedding与Word2Vec

一、数学上的“嵌入”(Embedding)


Embed这个词,英文的释义为, fix (an object) firmly and deeply in a surrounding mass, 也就是“嵌入”之意。例如:One of the bullets passed through Andrea's chest before embedding itself in a wall.


另外,这个词(的分词形式)在数学上也是一个专有名词,Embedding,它广泛存在于包括代数、拓扑与几何等诸多数学领域。它主要表征某个数学结构中的一个实例被包含在另外一个实例中,例如一个group它同时又是一个subgroup。


当我们说某个对象 X 被嵌入到另外一个对象 Y 中, 那么 embedding 就由一个单射的、结构保持的(structure-preserving)映射 f : X → Y 来给定的。此处的结构保持的具体含义要依赖于X 和 Y 是哪种数学结构的实例而定。


举个例子:我们可以把整数“嵌入”进有理数之中。显然,整数是一个group,同时它又是有理数的一个subgroup。整数集合中的每个整数,在有理数集合中都能找到一个唯一的对应(其实就是它本身)。同时,整数集合中的每个整数所具有的性质,在有理数中同样得到了保持。同理,我们也可以把有理数“嵌入”到实数中去。


二、词嵌入(Word Embedding)

扫描二维码关注公众号,回复: 932018 查看本文章


前面我们之所以要讨论Embedding在数学上的意思,就是因为这名称其实特别具有一种误导性,尤其是在翻译成中文的时候会令汉语思维下的我们更加难于把握住它的本质。

Word Embedding并不是要把单词像贴瓷砖那样镶嵌进什么地方。更重要的是,我们在把单词嵌入进另外一个空间时,要做到单射和structure-preserving,或者说我们更专注的是映射关系,而最终得到每个单词在另外一个空间中的表达也仅仅是之前设计好的映射关系的很自然的表达。

Word embedding 是NLP中一组语言模型(language modeling)和特征学习技术(feature learning techniques)的总称,这些技术会把词汇表中的单词或者短语(words or phrases)映射成由实数构成的向量上。 

最简单的一种Word Embedding方法,就是基于词袋(BOW)的One-Hot表示。这种方法,把词汇表中的词排成一列,对于某个单词 A,如果它出现在上述词汇序列中的位置为 k,那么它的向量表示就是“第 k 位为1,其他位置都为0 ”的一个向量。

例如,有语料库如下:

John likes to watch movies. Mary likes movies too.

John also likes to watch football games.

把上述语料中的词汇表整理出来并排序(具体的排序原则可以有很多,例如可以根据字母表顺序,也可以根据出现在语料库中的先后顺序)

假设我们的词汇表排序结果如下:

{"John": 1, "likes": 2, "to": 3, "watch": 4, "movies": 5, "also":6, "football": 7, "games": 8, "Mary": 9, "too": 10}

那么则有如下word的向量表示:
John: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] 
likes: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
……
此时,你也可以进一步地把文档也表示成向量。方法就是直接将各词的词向量表示加和,于是则有原来的两句话的向量表示如下:

[1, 2, 1, 1, 2, 0, 0, 0, 1, 1]

[1, 1, 1, 1, 0, 1, 1, 1, 0, 0]


One-hot方法很简单,但是它的问题也很明显:

1)它没有考虑单词之间相对位置的关系;

2)词向量可能非常非常长!

针对第一个问题,你可能会想到n-gram方法,这确实是一个策略,但是它可能会导致计算量的急剧增长。因为n-gram已经在之前的文章中解释过了,下面我们来看另外一个方法:共现矩阵 (Cocurrence matrix)。


一个非常重要的思想是,我们认为某个词的意思跟它临近的单词是紧密相关的。这是我们可以设定一个窗口(大小一般是5~10),如下窗口大小是2,那么在这个窗口内,与rests 共同出现的单词就有life、he、in、peace。然后我们就利用这种共现关系来生成词向量。


例如,现在我们的语料库包括下面三份文档资料:

I like deep learning. 

I like NLP. 

I enjoy flying.

作为示例,我们设定的窗口大小为1,也就是只看某个单词周围紧邻着的那个单词。此时,将得到一个对称矩阵——共现矩阵。因为在我们的语料库中,I 和 like做为邻居同时出现在窗口中的次数是2,所以下表中I 和like相交的位置其值就是2。这样我们也实现了将word变成向量的设想,在共现矩阵每一行(或每一列)都是对应单词的一个向量表示。

虽然Cocurrence matrix一定程度上解决了单词间相对位置也应予以重视这个问题。但是它仍然面对维度灾难。也即是说一个word的向量表示长度太长了。这时,很自然地会想到SVD或者PCA等一些常用的降维方法。当然,这也会带来其他的一些问题,例如,我们的词汇表中有新词加入,那么就很难为他分配一个新的向量。但这并非本文要讨论的重点,我们不再赘述。


三、Word2Vec

我们已经见识了两种词嵌入的方式。而现在最常用、最流行的方法,就是Word2Vec。这是Tomas Mikolov在谷歌工作时发明的一类方法,也是由谷歌开源的一个工具包的名称。具体来说,Word2Vec中涉及到了两种算法,一个是CBOW一个是Skip-Gram。这也是因为深度学习流行起来之后,基于神经网络来完成的Word Embedding方法。

Word2Vec之所以现在这么流行,不同于之前的一些Word Embedding方法,它能够自动实现:1)单词语义相似性的度量;2)词汇的语义的类比。此处,语义的类比,反应的是类似下面这种关系:
  • “国王” – “王后” ≈ “男” – “女”
  • “英国” – “伦敦” ≈ “法国”  “巴黎”  “首都” 
如果用图形来表示,即如下图所示:

对于Skip-Gram模型来说,它是要Generates each word in context given centre word。如下图所示:

所以总概率定义为:

其中下标denotes position in running text. 对于每个单词而言,则有


对于CBOW模型来说,Condition on context, and generate centre word。如下图所示:

要细抠Skip-GramCBOW的话,恐怕还需要很长篇幅和太多细节上的讨论,这一点留待后续文章中再来另行讨论。下面我们将在Python中实际使用一下Word2Vec,这就要简单许多了,因为我们可以直接使用gensim [1]。注意我们用来训练模型的语料库是NLTK中的Brown语料库。实际中要获得更高质量的模型,往往意味着需要更大的语料库,当然这也意味着更多的训练时间。
[python]  view plain  copy
  1. import gensim, logging, os  
  2. logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)  
  3.   
  4. import nltk  
  5. corpus = nltk.corpus.brown.sents()  
  6.   
  7. fname = 'brown_skipgram.model'  
  8. if os.path.exists(fname):  
  9.     # load the file if it has already been trained, to save repeating the slow training step below  
  10.     model = gensim.models.Word2Vec.load(fname)  
  11. else:  
  12.     # can take a few minutes, grab a cuppa  
  13.     model = gensim.models.Word2Vec(corpus, size=100, min_count=5, workers=2, iter=50)   
  14.     model.save(fname)  
现在已经得到模型了,接下来我们来评估一下这个模型的质量。我们要评估一下下面这几个词之间的相近(或伴随)程度。
[python]  view plain  copy
  1. words = "woman women man girl boy green blue did".split()  
  2. for w1 in words:  
  3.     for w2 in words:  
  4.         print(w1, w2, model.similarity(w1, w2))  
输出的结果如下:
[plain]  view plain  copy
  1. woman woman 1.0  
  2. woman women 0.3451595268  
  3. woman man 0.607956254336  
  4. woman girl 0.761190251497  
  5. woman boy 0.558522930154  
  6. woman green 0.24118403927  
  7. woman blue 0.178044251325  
  8. woman did 0.0751838683173  
  9. women woman 0.3451595268  
  10. women women 1.0  
  11. women man 0.126646555737  
  12. women girl 0.292825346454  
  13. women boy 0.298552943639  
  14. women green 0.104096393379  
  15. women blue 0.0930137564485  
  16. women did 0.152766770859  
  17. 注意:输出内容较长,这里不全部列出,读者可以执行尝试并观察输出结果... ...  
当然,你也可能希望有一个更直观的表示,所以不妨试试图形化的结果显示:
[python]  view plain  copy
  1. import numpy as np  
  2. import matplotlib.pyplot as plt  
  3. %matplotlib inline  
  4.   
  5. M = np.zeros((len(words), len(words)))  
  6. for i, w1 in enumerate(words):  
  7.     for j, w2 in enumerate(words):  
  8.         M[i,j] = model.similarity(w1, w2)  
  9.           
  10. plt.imshow(M, interpolation='nearest')  
  11. plt.colorbar()  
  12.   
  13. ax = plt.gca()  
  14. ax.set_xticklabels([''] + words, rotation=45)  
  15. ax.set_yticklabels([''] + words)  
所绘制的结果如下:

你也可以从词汇表中提取出跟某个单词(例如woman)最相关的k个词:
[python]  view plain  copy
  1. model.most_similar(positive=['woman'], topn=10)  
输出之结果如下:
[python]  view plain  copy
  1. [('girl'0.7611901760101318),  
  2.  ('man'0.6079562902450562),  
  3.  ('lady'0.6069421768188477),  
  4.  ('boy'0.5585228800773621),  
  5.  ('child'0.5556907653808594),  
  6.  ('person'0.5444432497024536),  
  7.  ('young'0.5219132900238037),  
  8.  ('pair'0.5211296081542969),  
  9.  ('she'0.5175711512565613),  
  10.  ('fellow'0.5115353465080261)]  

现在你也可以试着来玩一下Word2Vec啦!


参考:
[1]   https://radimrehurek.com/gensim/

猜你喜欢

转载自blog.csdn.net/sjyttkl/article/details/80324656