文章目录
前言
准备复试的时候开始看了CS224n,这是自然语言处理方面比较出名的一门课程了,刚开始学词向量的时候看的一头雾水,网上的文章越看越懵,于是便写了这篇文章,希望可以帮助看到的同学更好的了解词向量
一、词向量是什么?
计算机是个铁疙瘩,它不知道怎么读语言,因此想要让它读懂自然语言符号,必须要将语言符号数字化,那么这个数字化的手段就是词向量——将每一个语言符号用一个向量表示。
1.1离散表示(one-hot representation)
有一些机器学习基础的同学,首先的想法就是把所有符号当做一个空间,出现某个符号就把这个符号所在的位置标记为1,没出现的标记为0。
举个例子,有一个词典,仅包含8个字,我、的、宠、物、是、一、条、狗。(有序)
那么这些字的向量表示可以为:
我:[1,0,0,0,0,0,0,0]
的:[0,1,0,0,0,0,0,0]
宠:[0,0,1,0,0,0,0,0]
物:[0,0,0,1,0,0,0,0]
是:[0,0,0,0,1,0,0,0]
一:[0,0,0,0,0,1,0,0]
条:[0,0,0,0,0,0,1,0]
狗:[0,0,0,0,0,0,0,1]
这种表示方法叫做one-hot表示也叫做离散表示其表示优点是:简单易懂,鲁棒性很好。但其缺点也很明显:设想一下如果有10万个字,每一个向量都是10万维度的向量,会使得向量空间爆炸。同时,由于线性代数知识,可以知道任意两个向量是正交的,因此不能表示词与词之间的关系。
1.2分布式表示(distribution representation)
word embedding(词嵌入)是将词转化为一种分布式表示的方法。分布式表示将词表示成一个定长的连续的稠密向量。其优点是:词语之间存在相似关系(不正交)以及每一维度都有其特定含义。
二、共现矩阵生成词向量
2.1共现矩阵
通过统计一个事先指定大小的窗口内的单词共同出现的次数,此时将词划分为两种,中心词和其他词。每次仅前移一个单词。例:现在有两句话
START All that glitters isn’t gold END
START All‘’s well that ends well END
词典为:All,All’s,END,START,ends,glitters,gold,isn;t,that,well(按字典序排序)
假设窗口为1,那么共现矩阵为:
对于第一句,开始窗口状态为 [START,All],此时中心词为START,因此START和All共现,在矩阵中START为行,All为列的位置加1。然后将窗口向后移动一个词,此时状态为[START,All,that],中心词为All,因此在行为All,列为Start,列为that的位置加1。重复操作,到句尾即可,第一个句子处理后,再处理第二个句子,得到上述矩阵。
假设窗口为2,初始状态为[START,All,that],中心词为START因此在行为START,列为All,列为that的位置加1。后续操作与窗口为1类似。
共现矩阵的每一列(或行)都可以当做一个词向量。
实现共现矩阵代码如下(示例):
def compute_co_occurrence_matrix(corpus, window_size=4):
words, num_words = distinct_words(corpus)
M = None
word2Ind = {}
for (id,key) in enumerate(words):
word2Ind[key]=id
M=np.zeros((len(words),len(words)))
for sentence in corpus:
for i in range(len(sentence)):
window = list(range(max(0, i - window_size), min(i + window_size + 1, len(sentence))))
window.remove(i)
for j in window:
M[word2Ind[sentence[i]], [word2Ind[sentence[j]]]] += 1
M[word2Ind[sentence[j]], [word2Ind[sentence[i]]]] += 1
M=M/2
return M, word2Ind
def distinct_words(corpus):
#去掉语料库中重复的单词
corpus_words = []
num_corpus_words = -1
for scentence in corpus:
for word in scentence:
if word not in corpus_words:
corpus_words.append(word)
num_corpus_words = len(corpus_words)
corpus_words = sorted(corpus_words)
return corpus_words, num_corpus_words
2.2奇异值分解(SVD)
我们可以看到上述矩阵仍然为10*10的矩阵,其词向量仍然为10维,与词典长度相同,因此采用SVD来将词向量降维度。对于SVD感兴趣的话可以看看数学原理,主要还是调库来操作。
SVD降维代码如下(示例):
def reduce_to_k_dim(M, k=2):
n_iters = 10 # Use this parameter in your call to `TruncatedSVD`
M_reduced = None
print("Running Truncated SVD over %i words..." % (M.shape[0]))
svd = TruncatedSVD(n_components=k, n_iter=n_iters, random_state=42)
M_reduced=svd.fit_transform(M, y=None)
print("Done.")
return M_reduced
上述代码将词向量转化成k维向量。