word2vec原理&代码 详细全面总结
阅读原文时间:2021年04月22日阅读:1

目录

本文介绍word2vec必备基础知识,原理,结构,模型训练及代码。

表示学习:将研究对象的语义信息表示为稠密低维实值向量。在该低维向量空间中,2个对象距离越近则说明其语义相似度越高。
word2vec:2013年,由Google团队提出。word2vec 是一种词嵌入(word embedding)技术,即把一个词语转换成其对应的向量表达,进而方便计算机处理。word2vec是词嵌入技术发展的重要里程碑。

一、统计语言模型—必备基础知识

统计语言模型(Statistical Language Model)是自然语言处理的基础模型,是从概率统计角度出发,解决自然语言上下文相关的特性的数学模型。统计语言模型的核心就是判断一个句子在文本中出现的概率

假定 S S S表示某个有意义的句子,由一连串特定顺序排列的词 ( ω 1 , ω 2 , … , ω n ) (\omega_{1}, \omega _{2}, \ldots, \omega_{n}) (ω1​,ω2​,…,ωn​)组成, n n n是句子的长度。将 S S S在文本中出现概率表示为 P ( S ) P(S) P(S),则 P ( S ) = P ( ω 1 , ω 2 , … , ω n ) P(S)=P(\omega_{1}, \omega _{2}, \ldots, \omega_{n}) P(S)=P(ω1​,ω2​,…,ωn​)

利用条件概率公式:

P ( ω 1 , ω 2 , … , ω n ) = P ( ω 1 ) ⋅ P ( ω 1 ∣ ω 2 ) ⋅ P ( ω 3 ∣ ω 1 , ω 2 ) … P ( ω n ∣ ω 1 , ω 2 … , ω n − 1 ) P(\omega_{1}, \omega _{2}, \ldots, \omega_{n})=P(\omega_{1})\cdot P(\omega_{1}|\omega_{2})\cdot P(\omega_{3}|\omega_{1},\omega_{2}) \ldots P(\omega_{n}|\omega_{1},\omega_{2} \ldots, \omega_{n-1}) P(ω1​,ω2​,…,ωn​)=P(ω1​)⋅P(ω1​∣ω2​)⋅P(ω3​∣ω1​,ω2​)…P(ωn​∣ω1​,ω2​…,ωn−1​)
将序列的联合概率转化为一系列条件概率的乘积。那么问题变成了如何去预测这些给定previous words下的条件概率 P ( ω 1 , ω 2 … , ω n − 1 ) P(\omega_{1},\omega_{2} \ldots, \omega_{n-1}) P(ω1​,ω2​…,ωn−1​)。

当 n n n太大时,上式难以计算。因此在此基础上马尔可夫提出了,一种马尔可夫假设:假设一个词出现的概率只与前面N-1个词相关。当N=2时(只考虑前一个词),就是简单的二元模型(Bigram Model),N=3(只考虑前2个词),三元模型(Trigram Model),当N=N时(考虑前N-1个词),就是常说的N元模型(N-gram Model)。

二、词的表示学习类型

1. 独热编码 one hot

one hot 向量,维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置设置为1,其他位置为0。如,句子*(今年,夏天,超级,热),词语夏天*的one hot向量为(0,1,0,0).
独热编码的缺点:稀疏(很多0)、孤立(无法表示出在语义层面上词语之间的相关信息)、高维(词汇表很大时,词向量高维)。

2. 分布式表示学习 distributed representation

这里分布式可以理解为单看向量的某个维度没有意义,但综合起来就能表示词的意义,解决了one hot存在的问题。例:某个词的向量表示 [0.31343242, 0.65464122, 0.12343425, …, -1.324344]

词的分布式表示主要可以分为三类:基于矩阵的分布表示、基于聚类的分布表示和基于神经网络的分布表示。
接下来只介绍word2vec,一种基于神经网络的分布表示。

三、 word2vec

word2vec 包含两个模型:跳字模型(skip-gram)和连续词袋模型(continuous bag of words,简称CBOW);
两种高效训练的方法:负采样(negative sampling)和层序softmax(hierarchical softmax)。

1. CBOW

CBOW 是一个三层神经网络(所以word2vec称为浅层模型,浅层向量表示)。如下图所示,该模型的特点是输入已知上下文,输出对当前单词的预测。


输入层
假设所有训练数据所构成的词典大小为 V V V。现在输入数据为“今年夏天超级热”,分词后将每个词表示为 V V V维的one hot向量,设某个词的输入向量/one hot向量表示为 x 1 ∗ V x_{1*V} x1∗V​。现假设根据上下文*“今年,超级,热”来预测中心词“夏天”*。

然后网络初始化权重,该权重为一个矩阵表示为 W V ∗ N W_{V*N} WV∗N​,其中 N N N为词向量的维度。该权重矩阵也称为投影矩阵,投影到词空间的矩阵。(多次迭代后的结果也就是我们要的词向量矩阵)

隐藏层
分别将输入层的4个词的向量*投影矩阵,即 x 1 ∗ V x_{1*V} x1∗V​ * W V ∗ N W_{V*N} WV∗N​,将这4个结果相加后求平均,即得到隐藏层的结果 h 1 ∗ N h_{1*N} h1∗N​。
然后将 h 1 ∗ N h_{1*N} h1∗N​乘以另一个投影矩阵 W N ∗ V ′ W'_{N*V} WN∗V′​,将结果传播到输出层。

输出层
使用激活函数softmax,输出结果one hot 向量 O 1 ∗ V O_{1*V} O1∗V​。将该one hot 向量与true label“夏天”的向量做比较,然后进行误差反向传播,不断迭代优化权重矩阵,最终即得到我们要的词向量矩阵(每行为一个词所对应的词向量,维度为N,V个词就有V行)。

注意:这里有两个矩阵 W V ∗ N W_{V*N} WV∗N​和 W N ∗ V ′ W'_{N*V} WN∗V′​,一般使用前者作为我们的词向量矩阵,也可以使用后者,或两者相加求平均。


目标为最大化对数似然函数
目标为使得每个 P ( ω i ∣ C o n t e x t ( ω i ) ) P(\omega_{i}|Context(\omega_{i})) P(ωi​∣Context(ωi​))最大,根据以上统计语言模型,可知道是相乘。这里取对数可转化为相加,方便计算。

2. skip gram

CBOW 是一个三层神经网络。如下图所示,该模型的特点是已知当前词语,预测上下文。训练过程类似,这里不再赘述。

训练优化方法

注意到,实际中词汇表一般很大,那么Word2Vec模型是一个超级大的神经网络(权重矩阵规模非常大)。
两种方法优化训练过程:负采样(negative sampling)和层序softmax(hierarchical softmax)

(1)负采样
负采样核心思想:不考虑出现概率低的词,保证频次越高的样本越容易被采样出来
(softmax分出来的类别数目是整个词汇表的大小,那么是否可以减小?)详见word2vec的负采样

(2)hierarchical softmax
hierarchical softmax的核心内容是将词汇表中的每个词使用哈夫曼树(Huffman Tree)进行编码,出现概率越高的符号使用较短的编码(层次越浅),出现概率低的符号则使用较长的编码(层次越深)。这样做,可以使得最终是在做一层层的二分类。详见基于hierarchical softmax的word2vec

四、word2vec代码实现

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:使用word2vec将分词后的数据进行文本向量化,保存词语的索引字典、词向量,然后保存为pkl文件
word2vec:
    1.预训练语料:已分词的语料,输入格式为txt(一行为一个样本)或list of list
    2.建立一个空的模型对象;遍历一次语料库建立词典;第二次遍历语料库建立并训练模型(初始化词向量,逐句的读取一系列的词,用梯度下降法更新词向量)
    3.可保存的:模型;根据语料得到的索引字典{索引数字: 单词},词向量(后两者通过pickle库保存)
    4.模型评估:词的向量表示,词与词之间的相似度,与某个词最相近的前N个词
"""

import pickle
import logging
import gensim
from gensim.models import Word2Vec, word2vec
from gensim.corpora.dictionary import Dictionary

addr = 'E:/word2vec/'

# 将日志输出到控制台
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) 


#==========================1.读取预训练语料=======================================
print("选择分词后的文本作为训练语料...")
#这里输入数据格式为txt,一行为一个样本
sentences = word2vec.LineSentence(r"E:/word2vec/seg_sent.txt")

#=====================2.训练Word2vec模型(可修改参数)...====================
print('训练Word2vec模型(可自定义参数)...')
model = Word2Vec(sentences,
                 size=100,  # 词向量维度
                 min_count=5,  # 词频阈值
                 window=5)  # 窗口大小
'''
参数解读
#sg=1是skip—gram算法,对低频词敏感,默认sg=0为CBOW算法
#size是特征向量的维度。
#window是句子中当前词与目标词之间的最大距离,3表示在目标词前看3-b个词,后面看b个词(b在0-3之间随机)
#min_count是对词进行过滤,频率小于min-count的单词则会被忽视,默认值为5。
#negative和sample可根据训练结果进行微调,sample表示更高频率的词被随机下采样到所设置的阈值,默认值为1e-3,
#negative: 如果>0,则会采用negativesamping,用于设置多少个noise words
#hs=1表示层级softmax将会被使用,默认hs=0且negative不为0,则负采样将会被选择使用。
#workers是线程数,此参数只有在安装了Cpython后才有效,否则只能使用单核
'''
print(u"保存w2v模型...")
model.save(addr + 'w2v_100.model')  # 保存模型
print("保存w2v模型的位置: ", addr + 'w2v_100.model', '\n')


'''
模型评估
print('计算与词A的最近似的(前10个)词:')
model.most_similar("wordA",top n=10))#计算与该词最近似的词,top n指定排名前n的词

print('计算词A和词B的相似度:')
model.similarity("wordA","wordB") 

print('获取词A的词向量:')
model ['wordA']
'''
#===================3.创建词语字典,并返回word2vec模型中词语的索引,词向量================
def create_dictionaries(p_model):
    gensim_dict = Dictionary()  # 创建词语词典
    gensim_dict.doc2bow(p_model.wv.vocab.keys(), allow_update=True)
    w2dict_index = {v: k + 1 for k, v in gensim_dict.items()}  # 词语+索引。词语的索引,从1开始编号
    w2index_dict = {k + 1: v for k, v in gensim_dict.items()}  # 索引+词语。词语的索引,从1开始编号
    w2vec = {word: p_model[word] for word in w2dict_index.keys()}  # 词语的词向量
    return w2dict_index, w2index_dict, w2vec


#====================4.从训练好的模型中提取出索引字典、词向量字典index_dict,==========================
dict_index, index_dict, word_vectors= create_dictionaries(model)
#注意,后续将词向量输入到模型中(如CNN)训练时,实际输入为每个词/词向量的索引

#===========================5.使用 pickle 存储序列化数据 ====================================
#pickle是一个非常方便的库 可以将py的字典、列表等等程序运行过程中的对象存储为实体数据存储为pkl文件
print(u"保存x_dict_indexpkl.pkl文件...")
output = open(addr + "w2v_100.pkl", 'wb')
pickle.dump(dict_index, output)  # 索引字典,{单词: 索引数字}
pickle.dump(index_dict, output)  #索引字典,{索引数字: 单词}
pickle.dump(word_vectors, output)  # 词向量字典
output.close()
print("保存pkl文件的位置: ", addr + "w2v_100.pkl", '\n')


if __name__ == "__main__":
    pass

词嵌入技术的发展

word2vec之后其他词嵌入技术:fastText,GloVe,BERT等

参考文献

https://blog.csdn.net/yu5064/article/details/79601683
https://www.jianshu.com/p/d8bfaae28fa9
https://zhuanlan.zhihu.com/p/53194407

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章