前言

准备复试的时候开始看了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,那么共现矩阵为:
图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维向量。


三、总结

本文仅仅简单介绍了词向量的概念和共现矩阵的方法来表示词向量,由于词向量的内容比较多,下一篇博客打算写一下word2vec,感兴趣的小伙伴可以关注。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐