word2vec

Posted by xcTorres on May 25, 2020

当刚开始学习自然语言处理深度学习课程的时候,第一步就是向量化,即如何把一篇文章,一个句子转换成数据输入。而文章段落句子都是由词语单词构成的,如果能把这些最小单元结构向量化,句子自然就可以作为输入了。

One Hot表达

最简单的方式就是One-Hot独热编码表达。用词向量来表示词并不是word2vec的首创,在很久之前就出现了。最早的词向量是很冗长的,它使用是词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。比如我们有下面的5个词组成的词汇表,词”Queen”的序号为2, 那么它的词向量就是(0,1,0,0,0)。同样的道理,词”Woman”的词向量就是(0,0,0,1,0)。这种词向量的编码方式我们一般叫做1-of-N representation或者one hot representation。其最大的缺点是当词汇量达到百万级的时候,每个词语都需要百万维度的向量去表达词语,且各个词语之间是正交的,很难直接描述各个词汇之间的相关性。

Word2Vec

然后有了神经网络词向量语言模型,其一般有三层,输入层即词向量,隐藏层和输出层(softmax层),其中最大的问题在于从隐藏层到输出的softmax层的计算量很大,因为要计算所有词的softmax概率,再去找概率最大的值,并通过反向传播来不断更新隐藏层的参数。这个模型如下图所示。其中𝑉是词汇表的大小。即假设输入词向量的维度为M,则输入层可以看成VM,隐藏层参数大小应该为MP, 如此一来得到的中间结果是V*P,可以理解讲词向量从M降维到P。

Word2vec原理也是如此,只是为了避免如此大的计算量,原作者做了一些优化来提升速率。第一个优化叫做Hierarchical Softmax。第二个优化叫做Negative Sampling。

Hierarchical Softmax

首先我们来看一下Hierarchical Softmax。其目的是将这个多分类问题转化为多个2分类问题,从而降低计算成本。第一步即把词汇表按照词频建立一颗霍夫曼二叉树。霍夫曼树的好处是词频越高的词汇层数越浅,越容易被遍历到,越早做分类判断。和之前的神经网络语言模型相比,我霍夫曼树的所有内部节点就类似之前神经网络隐藏层的神经元,其中,根节点的词向量对应我们的投影后的词向量,而所有叶子节点就类似于之前神经网络softmax输出层的神经元,叶子节点的个数就是词汇表的大小。在霍夫曼树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着霍夫曼树一步步完成的,因此这种softmax取名为”Hierarchical Softmax”。

Negative Sampling

既然名字叫Negative Sampling(负采样),那么肯定使用了采样的方法。采样的方法有很多种,比如之前讲到的大名鼎鼎的MCMC。我们这里的Negative Sampling采样方法并没有MCMC那么复杂。

比如我们有一个训练样本,中心词是𝑤,它周围上下文共有2𝑐个词,记为𝑐𝑜𝑛𝑡𝑒𝑥𝑡(𝑤)。由于这个中心词𝑤,的确和𝑐𝑜𝑛𝑡𝑒𝑥𝑡(𝑤)相关存在,因此它是一个真实的正例。通过Negative Sampling采样,我们得到neg个和𝑤不同的中心词𝑤𝑖,𝑖=1,2,..𝑛𝑒𝑔,这样𝑐𝑜𝑛𝑡𝑒𝑥𝑡(𝑤)和𝑤𝑖就组成了neg个并不真实存在的负例。利用这一个正例和neg个负例,我们进行二元逻辑回归,得到负采样对应每个词𝑤𝑖对应的模型参数𝜃𝑖,和每个词的词向量。
从上面的描述可以看出,Negative Sampling由于没有采用霍夫曼树,每次只是通过采样neg个不同的中心词做负例,就可以训练模型,因此整个过程要比Hierarchical Softmax简单。

Genism

Gensim是一个用于机器学习和深度学习的开源库,其也有Word2vec的支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def read_input(input_file):
    """This method reads the input file which is in gzip format"""
    
    logging.info("reading file {0}...this may take a while".format(input_file))
    
    with gzip.open (input_file, 'rb') as f:
        for i, line in enumerate (f): 

            if (i%10000==0):
                logging.info ("read {0} reviews".format (i))
            # do some pre-processing and return a list of words for each review text
            yield gensim.utils.simple_preprocess (line)

# read the tokenized reviews into a list
# each review item becomes a serries of words
# so this becomes a list of lists
documents = list (read_input (data_file))
logging.info ("Done reading data file")

model = gensim.models.Word2Vec(documents, vector_size=150, window=10, min_count=2, workers=10)
model.train(documents,total_examples=len(documents),epochs=10)

源码分析

https://github.com/chenbjin/RepresentationLearning/blob/master/Word2vec/word2vec.c

参考

刘建平老师 nlp-in-practice genium-word2vec



-->