word2vec原理及gensim中word2vec的使用
概述
在NLP中,对于一个词,我们用一个词向量来表示,最常见的一个方式是one hot representation,这种词向量的编码方式就是用一个很长的向量来表示一个词,向量的长度为词典的大小N,向量的分量只有一个1,其他全为0,1的位置对应该词在词典中的索引。这种表示方法不需要繁琐的计算,简单易得,但是缺点也不少:
- 我们的词汇表一般都非常大,比如达到百万级别,这样每个词都用百万维的向量来表示,简直是内存的灾难。
- 这样表示数据,数据非常稀疏,稀疏数据的训练效率比较低,我们通常需要更多地数据来训练。
- 维度很高,很容易造成维数灾难。
- 无法体现出词之间的关系,比如“面条”和“方便面”显然有非常紧密的关系,但转化成向量[1,0,0]和[0,1,0]以后,就看不出两者有什么关系了,因为这两个向量相互正交。
- 这样的向量其实除了一个位置是1,其余的位置全部都是0,表达的效率不高。
另一个方式是分布式表示(Distributed Representation),基本思想是将每个词表达成一个固定长度的、稠密、连续的、实数、短向量,此时向量长度可以自由选择,与词典规模无关。与之相对的one-hot encoding向量空间只有一个维度是1,其余都是0。分布式表示最大的优点是具备非常powerful的特征表达能力,比如 n 维向量每维 k 个值,可以表征 \(k^n\) 个概念。
向量空间模型(Vector Space Models)可以将字词转化为连续值的向量表示,并且其中意思相近的词将被映射到向量空间中相近的位置。向量空间模型在NLP中主要依赖的假设为Distributional Hypothesis,即在相同语境中出现的词其语义也相近。向量空间模型可以分为两类,
- 一类是计数模型,比如LSA;计数模型统计在语料库中,相邻出现的词的频率,再把这些计数统计结果转为小而稠密的矩阵;
- 另一类是预测模型(比如Neural Probabilisitc Language Models)。而预测模型则根据一个词周围相邻的词推测出这个词,以及它的向量空间。
我们今天要要介绍的word2vec和Bengio在2003年发表的论文A neural probabilistic language model中提出的模型很相似,可以看做是在此基础上的改进,下图是Bengio在论文中提出的网络结构:
这篇文章提出的神经网络语言模型(NNLM,Neural Probabilistic Language Model)采用的是文本分布式表示,即每个词表示为稠密的实数向量。NNLM模型的目标是构建语言模型:
词的分布式表示即word embedding,是训练语言模型的一个附加产物,即图中的Matrix C。
尽管Bengio 2003年便提出了NNLM,但是由于它的局限性,word embedding真正火起来是google Mikolov 2013年发表的两篇word2vec的文章 Efficient Estimation of Word Representations in Vector Space 和 Distributed Representations of Words and Phrases and their Compositionality,更重要的是发布了简单好用的word2vec工具包。值得说明的一点是,word2vec是一个获得word embeding的工具,该工具得到的训练结果——word embedding,可以很好地度量词与词之间的相似性,挖掘词之间的联系。里面的模型只是很浅的神经网络,算不得深度学习算法,除了word2vec之外,我们还有其他的获得word embeding的方法,如bert等。
下面我们会详细的介绍word2vec中的两个主要语言模型训练方案:CBOW和Skip-Gram,以及Hierarchical Softmax和Negative Sampling这两个两个提速手段。这两个方法很好的解决了计算有效性,事实上这两个方法都没有严格的理论证明,有些trick之处,非常的实用主义。然后我们利用gensim的python版来介绍word2vec的使用。
word2vec原理
CBOW模型和Skip-gram模型是神经概率语言模型的两种变形形式,下面我们将会一一介绍。
CBOW模型
CBOW(Continuous Bag-of-Words)的意思就是用上下文来预测当前词,如下图所示:
在上图中,通过词\(w_t\)的前后词\(w_{t-2}\),\(w_{t-1}\),\(w_{t+1}\), \(w_{t+2}\)来预测当前词\(w_t\)。此处的窗口的大小window为2。
其中,在CBOW模型中包含三层,即输入层,映射层和输出层。输入层是当前词周围每个词对应的词向量,在映射层将这些词的词向量相加求平均,在输出层求出为当前词的概率。
注:
如何根据一个词的one-hot编码,得到它对应的词向量,可以看下图所示:
在word2vec实际的实现的网络里,上式中左边的式子是输入词的one-hot编码和隐藏层参数矩阵的乘积,在做这个乘法时是不会进行矩阵的运算的,而是直接通过输入值中1的位置索引来寻找隐藏层中的参数矩阵中对应的索引的行。word2vec训练的目的就是得到这个隐藏层参数矩阵,这个矩阵也可以叫做embeding 矩阵,由于这一步很简单,我们上面的讲解里忽略掉这个隐藏层,输入层已经是之后词得到它所对应的词向量了,当然这个输入的词向量也是要求解的参数,下面的Skip-gram模型里面也按照这个设定,不再详述。
从数学上看,CBOW模型等价于一个词袋模型的向量乘以一个embedding矩阵,从而得到一个连续的embedding向量。这也是CBOW模型名称的由来。
Skip-gram模型
而Skip-gram(Continuous Skip-gram)模型则与CBOW模型正好相反,在Skip-gram模型中,是用当前词来预测上下文,如下图所示:
在上图中,通过词\(w_t\)来预测它的前后词\(w_{t-2}\),\(w_{t-1}\),\(w_{t+1}\), \(w_{t+2}\)。此处的窗口的大小window为2。
这里输入层是当前词对应的词向量,映射层什么也不做,在输出层求出当前词上下文窗口中词的概率。
Skip-gram名称来源于该模型在训练时会对上下文环境里的word进行采样,然后预测我们选到这个附近词的概率.
gensim中word2vec的使用
首先我们先介绍一下gensim中的word2vec API,官方API介绍如下:
class gensim.models.word2vec.Word2Vec(sentences=None,
corpus_file=None, vector_size=100, alpha=0.025,
window=5, min_count=5, max_vocab_size=None, sample=0.001,
seed=1, workers=3, min_alpha=0.0001, sg=0, hs=0, negative=5,
ns_exponent=0.75, cbow_mean=1, hashfxn=<built-in function hash>,
epochs=5, null_word=0, trim_rule=None, sorted_vocab=1,
batch_words=10000, compute_loss=False, callbacks=(),
comment=None, max_final_vocab=None)
在gensim
中,word2vec
相关的API
都在包gensim.models.word2vec
中。和算法有关的参数都在类gensim.models.word2vec.Word2Vec
中。算法需要注意的参数有:
- sentences: 我们要分析的语料,可以是一个列表,或者从文件中遍历读出(通过
word2vec
提供的LineSentence
类来读文件,word2vec.LineSentence(filename)
)。 - vector_size: 词向量的维度,默认值是
100
。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M
的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。 - window:即词向量上下文最大距离,
window
越大,则和某一词较远的词也会产生上下文关系。默认值为5
。在实际使用中,可以根据实际的需求来动态调整这个window
的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]
之间。 - sg: 即我们的
word2vec
两个模型的选择了。如果是0
, 则是CBOW
模型,是1
,则是Skip-Gram
模型,默认是0
,即CBOW
模型。 - hs: 即我们的
word2vec
两个解法的选择了,如果是1
, 则是Hierarchical Softmax
,是0
的话并且负采样个数negative
大于0
, 则是Negative Sampling
。默认是0
即Negative Sampling
。 - negative:即使用
Negative Sampling
时负采样的个数,默认是5
。推荐在[3,10]
之间。 - cbow_mean: 仅用于
CBOW
在做投影的时候,为0
,则算法中的\(x_w\)为上下文的词向量之和,为1
则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示\(x_w\),默认值也是1
,不推荐修改默认值。 - min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是
5
。如果是小语料,可以调低这个值。 - iter: 随机梯度下降法中迭代的最大次数,默认是
5
。对于大语料,可以增大这个值。 - alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为
η
,默认是0.025
。 - min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,
min_alpha
给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。这部分由于不是word2vec算法的核心内容,因此在原理篇我们没有提到。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。
常用的导入中文训练数据的方式,有以下几种:
使用Python的内置列表,
# 导入gensim库
from gensim.models import word2vec
# 第一种输入方式:Python内置列表
sentences = [['第', '一', '个', '句子'], ['第', '二', '个', '句子']]
# 调用函数训练模型
model = word2vec.Word2Vec(sentences)
sentences是一个列表的列表,它的每个元素是一个句子所构成的列表。
当数据集特别大的时候,使用上述第一种方法就需要消耗特别多的内存,gensim还支持使用迭代生成器的方式输入,
class MySentences(object):
def __init__(self, dirname):
self.dirname = dirname
def __iter__(self):
for fname in os.listdir(self.dirname):
for line in open(os.path.join(self.dirname, fname)):
yield line.split()
sentences = MySentences('/some/directory') # 第二种输入方式:占用内存少的迭代器
model = Word2Vec(sentences)
这个输入方式会依次打开“/some/directory”文件夹中的每个数据文件,对于每个文件一行一行的读取数据进模型,这样就避免了一次性读入而占用太多内存。注意这里的每个文件里的每一行都是经过分词的一个句子,单词之间用空格分隔。
对于一个语料文件,里面的每一行都是经过分词的一个句子,单词之间用空格分隔,那么我们还可以使用
sentences = word2vec.LineSentence('corpus.txt')的方式进行读取,而sentences = word2vec.PathLineSentences(dir_name)则是针对dir_name文件夹下的所有的这样的文件进行读取。
from gensim.models import word2vec
#训练
model = word2vec.Word2Vec(sentences, hs=1,min_count=1,window=3,size=100)
model.save('model') # 保存模型
model = word2vec.Word2Vec.load('model') # 加载模型
model.wv['man']# 得到词的词向量
#找出某一个词向量最相近的词集合
for val in model.wv.similar_by_word("酒店", topn=10):
print(val[0], val[1])
#查看两个词的相似性
#找出不同类的词
参考
word2vec前世今生
Word2Vec Tutorial - The Skip-Gram Model
word2vec原理(二) 基于Hierarchical Softmax的模型
用深度学习(CNN RNN Attention)解决大规模文本分类问题 - 综述和实践
word2vector的原理,结构,训练过程
Word2vec 入门(skip-gram部分)
源码解析——word2vec源码解析
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)