一、数学上的“嵌入”(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)
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}
[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变成向量的设想,在共现矩阵每一行(或每一列)都是对应单词的一个向量表示。
- “国王” – “王后” ≈ “男” – “女”
- “英国” – “伦敦” ≈ “法国” – “巴黎” ≈ “首都”
import gensim, logging, os
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
import nltk
corpus = nltk.corpus.brown.sents()
fname = 'brown_skipgram.model'
if os.path.exists(fname):
# load the file if it has already been trained, to save repeating the slow training step below
model = gensim.models.Word2Vec.load(fname)
else:
# can take a few minutes, grab a cuppa
model = gensim.models.Word2Vec(corpus, size=100, min_count=5, workers=2, iter=50)
model.save(fname)
现在已经得到模型了,接下来我们来评估一下这个模型的质量。我们要评估一下下面这几个词之间的相近(或伴随)程度。
words = "woman women man girl boy green blue did".split()
for w1 in words:
for w2 in words:
print(w1, w2, model.similarity(w1, w2))
输出的结果如下:
woman woman 1.0
woman women 0.3451595268
woman man 0.607956254336
woman girl 0.761190251497
woman boy 0.558522930154
woman green 0.24118403927
woman blue 0.178044251325
woman did 0.0751838683173
women woman 0.3451595268
women women 1.0
women man 0.126646555737
women girl 0.292825346454
women boy 0.298552943639
women green 0.104096393379
women blue 0.0930137564485
women did 0.152766770859
注意:输出内容较长,这里不全部列出,读者可以执行尝试并观察输出结果... ...
当然,你也可能希望有一个更直观的表示,所以不妨试试图形化的结果显示:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
M = np.zeros((len(words), len(words)))
for i, w1 in enumerate(words):
for j, w2 in enumerate(words):
M[i,j] = model.similarity(w1, w2)
plt.imshow(M, interpolation='nearest')
plt.colorbar()
ax = plt.gca()
ax.set_xticklabels([''] + words, rotation=45)
ax.set_yticklabels([''] + words)
所绘制的结果如下:
model.most_similar(positive=['woman'], topn=10)
输出之结果如下:
[('girl', 0.7611901760101318),
('man', 0.6079562902450562),
('lady', 0.6069421768188477),
('boy', 0.5585228800773621),
('child', 0.5556907653808594),
('person', 0.5444432497024536),
('young', 0.5219132900238037),
('pair', 0.5211296081542969),
('she', 0.5175711512565613),
('fellow', 0.5115353465080261)]
现在你也可以试着来玩一下Word2Vec啦!