当前位置:   article > 正文

Word2vec详解(附Gensim代码)_gensim word2vec

gensim word2vec

        是词嵌入( word embedding) 的一种, 是将人类文字符号转化为数值形式,或者说——嵌入到一个数学空间里,这种嵌入方式,就叫词嵌入(word embedding),而 Word2vec,就是词嵌入( word embedding) 的一种。

        除了Word2vec本身之外,它还有两种变体CBOWSkip-gram

                                                                        注:本篇仅为个人笔记,若认为过长可直接观看目录

原理说明:

        Word2Vec是一种用于学习词嵌入(word embeddings)的技术,其原理基于分布式假设:在大规模文本语料库中,上下文相似的词往往具有相似的语义。Word2Vec通过神经网络模型来学习单词的密集向量表示,使得相似的词在向量空间中距离更近,捕捉了单词之间的语义关系和语法信息。

        通过Word2Vec,我们可以将语言符号数学化在向量空间中,并为以后的模型学习奠定了基础。

CBOW(Continuous Bag of Words)

给定上下文预测目标词的概率分布,例如, 给定{The,cat,(),over,the,puddle} 预测中心词是jumped的概率,模型的结构如下

比如:”一只猫坐在地上” ,中间词为” 猫” , 那么上下文词为: [“一只” , “坐在” , “地上”]

这里附上个人认为讲解最好的视频,只有8分钟强烈推荐:
https://www.bilibili.com/video/BV1XH4y1D7cJ/?spm_id_from=333.337.search-card.all.click&vd_source=3fa2544ff5f05c5cb219daba51d64c60

Skip-gram

给定中心词预测上下文各个单词的概率分布, 例如,给定jumped,预测上下文单词{The, cat,(),over,the,puddle}的概率,模 型的结构如下:

用一个词语作为输入,来预测它周围的上下文

推荐学习视频:https://www.bilibili.com/video/BV1Su411c77X/?spm_id_from=333.788&vd_source=3fa2544ff5f05c5cb219daba51d64c60

Word2Vec优化:Hierarchical softmax(霍夫曼树,HS)和Negative sampling(负样本 采样)

Hierarchical softmax

可以看这位博主:https://blog.csdn.net/qq_38890412/article/details/107658406

其实通俗来讲就是将最后的softmax层变为了一个哈夫曼树用于简化计算

Negative sampling:

        负采样是用于优化Word2Vec模型训练过程中的一种方法,旨在解决传统Skip-gram模型中softmax计算量大的问题。在Skip-gram模型中,对于一个中心词,需要在整个词汇表中计算softmax概率,计算复杂度为O(V),其中V为词汇表的大小,这在大规模数据上会导致计算量过大。

        负采样通过只更新一小部分负样本(噪声词)的向量表示,从而降低了计算开销。具体来说,对于每个正样本(中心词-上下文词对),会随机采样一定数量的负样本(通常5-20个),然后将这些正负样本一起作为训练数据。在训练时,只更新正样本和少数负样本的词向量参数,其余负样本则不进行更新。

        下面通过一个简单的例子来说明负采样的过程:

        假设我们有一个训练样本:“人工智能 是 未来 的 发展 方向”,现在以“未来”为中心词,希望预测它周围的词语。

正样本:(中心词: "未来", 上下文词: "是")

负样本(假设采样两个负样本):(中心词: "未来", 负样本: "人工"), (中心词: "未来", 负样本: "发展")

        在训练过程中,我们会根据这些正负样本来更新词向量的参数,使得正样本的相似度增加,负样本的相似度减小,从而训练出更好的词向量表示。

        通过负采样,Word2Vec模型在保证训练效果的同时,显著减少了计算开销,使得在大规模语料上的训练变得更为高效。

         也就是说,负采样相当于简化了softmax运算中的分母,让其计算量减小。但是我们又该删减哪些词呢?

问:那负采样过程中的频率是如何计算的呢?

在Negative Sampling中,负采样的频率计算方式通常是根据每个词在词汇表中的频率来确定。具体来说,频率计算方式包括以下几个步骤:

  1. 统计每个词在整个语料库中出现的次数,得到每个词的词频。
  2. 计算每个词的词频占整个语料库中所有词总词频的比例,得到每个词的词频概率分布。
  3. 根据负采样的设定,从词汇表中随机采样负样本时,可以按照每个词的词频概率分布来确定采样概率。
  4. 采样时通常会设置一个采样参数k,表示每个正样本对应的负样本数量。根据每个词的词频概率分布,可以按照概率大小来进行采样,选择词频较高的词作为负样本。

        通过这种方式,词频较高的词被选中作为负样本的概率更大,而词频较低的词被选中的概率较小,从而实现了根据词频分布进行负采样的过程。这样可以更好地反映词汇在语料库中的重要程度,使得负采样更加符合实际情况。

我们了解了这两种优化啊方式,又出现了一个问题:

注意:HS和NS不能同时使用。那我们该如何选择呢?

选择使用Hierarchical Softmax还是Negative Sampling通常取决于以下几个因素:

  1. 数据集规模:对于大规模的数据集和词汇表,通常会考虑使用Negative Sampling来降低计算复杂度。Negative Sampling在这种情况下能够更好地处理大规模的负样本采样,并且训练效率更高。

  2. 计算资源:如果计算资源有限,或者需要在较短的时间内训练模型,那么可以考虑使用Negative Sampling来加快训练速度。Negative Sampling通常在这种情况下能够提供更高的效率。

  3. 模型性能要求:如果对模型的性能要求很高,希望模型能够更好地捕捉词之间的相关性并且在预测时有更高的准确性,那么可以考虑使用Hierarchical Softmax。Hierarchical Softmax相对于Negative Sampling在捕捉词之间关系上更为精确。

  4. 实验对比:最终的选择也可以通过实验对比来确定,可以在训练过程中分别尝试使用Hierarchical Softmax和Negative Sampling,然后根据模型性能、训练速度和效果等指标来进行比较,从而选择适合当前任务的方法。

需要根据具体的应用场景和需求来综合考量这些因素,以选择适合的softmax层近似计算方法。

Gensim实现代码:

  1. # 每行数据加载
  2. sentences = word2vec.LineSentence(word_file_path)
  3. # 构建Word2Vec模型
  4. model = word2vec.Word2Vec(hs = 1,min_count = 1,window = 5,vector_size = 100)
  5. # 构建词典
  6. # 构建词典的目的是为了在训练过程中对模型进行初始化,并为每个单词分配一个唯一的索引编号,
  7. 以便后续的训练和查询
  8. # 起来构建词典的过程没有显式地保存输出结果,但实际上这些信息会在内部被模型所使用,
  9. 并在训练过程中发挥重要作用。
  10. model.build_vocab(sentences)
  11. # 模型训练
  12. model.train(sentences, total_examples=model.corpus_count, epochs=5

LineSentence:按行读取数据,并按空格进行划分
这里的word_file_path是已被jieba分好的词

一些常用参数:
vector_size:最终期望提取单词维度大小
window: 窗口大小 = 周边词 + 1
min_count:单词频数小于该值的单词不参与训练(得不到这类单词对应的向量)
sg: 1(Skip-gram) 0(CBOW) 两个结构
hs: 1(hierarchical softmax) 0(negative) 两个优化
negative: 当hs为0的时候,给定负样本数目,给定为0表示不采用负采样

常用方法:

1.获取Word2Vec模型相关属性

  1. print("【词汇数目】: {}".format(len(model.wv.key_to_index)))
  2. print("【转换的稠密的特征向量维度数目,每个单词转换的向量维度大小】: {}".format(model.wv.vector_size))
  3. print("【单词到id的映射关系】: \n{}".format(model.wv.key_to_index))
model.wv.key_to_index
model.wv.vector_size

运行结果示例:
【词汇数目】: 17863
【转换的稠密的特征向量维度数目,每个单词转换的向量维度大小】: 100
【单词到id的映射关系】: 
{',': 0, '的': 1, '。': 2, '了': 3, '!': 4, ':': 5, '?': 6, '是': 7, '他': 8, '我': 9, '你': 10, '说': 11, '侯亮平': 12, '在': 13, '也': 14, '就': 15, '…': 16, '和': 17, '啊': 18, '不': 19, '李达康': 20,...}

那计算机内部的操作过程是什么样的呢(以model.wv.key_to_index为例):

当调用model.wv.key_to_index时,实际上是在请求Word2Vec模型中存储的单词到索引的映射关系。在这种情况下,我们来解释一下计算机内部是如何操作的:

  1. 访问模型对象:首先,代码会通过model.wv来访问Word2Vec模型中的词向量部分,其中包含了单词到词向量的映射以及相关的辅助函数。

  2. 获取单词索引映射:接着,通过key_to_index属性,代码请求获取存储在模型中的单词到索引的映射关系。这个映射可以帮助快速查找指定单词在词汇表中的索引位置。

  3. 内部操作:在Word2Vec模型内部,这个操作实际上是通过查询已经构建好的词典(即单词到索引的映射)来实现的。当调用key_to_index时,模型会直接返回存储在内存中的单词到索引的映射数据结构,而不需要进行额外的计算。

  4. 返回结果:最终,代码将得到一个表示单词到索引映射关系的数据结构,通常是一个字典或类似的数据类型,其中包含了每个单词对应的唯一索引值。

总的来说,通过调用model.wv.key_to_index,代码实际上是在请求Word2Vec模型内部已经存储好的单词索引映射信息,这个操作是基于模型内部数据结构的快速查询,而不需要额外的计算过程。

2.获取相似度最高的四个词:

  1. # 夹角余弦相似度
  2. req_count = 10
  3. for (word, sim) in model.wv.similar_by_word('沙瑞金', topn =100):
  4. if len(word)==3: # 为了过滤一下
  5. req_count -= 1
  6. print(word, sim)
  7. if req_count == 0:
  8. break;

model.wv.similar_by_word

 运行结果示例:

高育良 0.9471278190612793
季昌明 0.946221649646759
田国富 0.9401828646659851
祁同伟 0.9310567378997803
易学习 0.9295538067817688
侯亮平 0.9204326868057251
李达康 0.9177809953689575
吴春林 0.914404034614563
孙书记 0.9083532094955444
三百块 0.9063376784324646

3.获取单词之间的相似度

  1. # 夹角余弦相似度
  2. print(model.wv.similarity('沙瑞金', '高育良'))
model.wv.similarity

运行结果示例:
0.94712776

4.获取单词的词向量

  1. v1 = model.wv.get_vector("提拔")
  2. print(v1.shape)
  3. print(v1)
  4. ​​​​​​​model.wv['目标单词']
model.wv.get_vector
model.wv['目标单词']

(100,)
[-2.77433041e-02  5.97340837e-02 -3.09292488e-02 -1.29078086e-02
 -4.44203941e-03 -1.68389276e-01  8.30356404e-03  2.29993850e-01
 ...]
array([ 0.03580654,  0.2716976 , -0.09749199, -0.08191779, -0.17744873,
       -0.16128965,  0.11938893,  0.20923679, ... ,dtype=float32)

模型保存&模型恢复加载

方式一:保存模型数据及其加载

model.save(model_file_path1)

加载

  1. # 直接基于路径加载
  2. model1 = word2vec.Word2Vec.load(model_file_path1)
  3. print(model1)
  4. v1 = model1.wv.get_vector("提拔")
  5. print(v1.shape)
  6. print(v1)

方式二:词向量的保存及其加载

  1. model.wv.save_word2vec_format(model_file_path2, binary=True)
  2. # binary:二进制/文本格式保存

加载

  1. # 加载模型
  2. model2 = gensim.models.KeyedVectors.load_word2vec_format(model_file_path2, binary=True)
  3. print(model2)
  4. # 应用模型
  5. v1 = model2.get_vector("目标词汇")
  6. print(v1.shape)
  7. print(v1)

        看到以上两种保存方式,我想你一定和我一样有个疑问:为什么模型是model.save而词向量是model.wv.save?查阅之后得知:

        在Gensim库中,Word2Vec模型对象通常被称为model,而词向量部分则被称为model.wv。这种设计是为了更好地组织和管理代码,使得访问词向量相关的方法和属性更加清晰和直观。

        在Word2Vec模型对象(model)中,wv代表了Word Vectors,即词向量部分。因此,保存整个模型使用model.save()是为了保存包括词向量在内的完整模型参数;而单独保存词向量部分使用model.wv.save_word2vec_format()是为了专门保存词向量,并且这个方法是针对词向量部分的保存而设计的。

        通过这样的设计,可以更好地区分保存整个模型和保存词向量两种不同的操作,提高代码的可读性和可维护性。因此,虽然model.save()model.wv.save_word2vec_format()都能用来保存Word2Vec模型,但它们分别针对保存整个模型和保存词向量部分做了明确的区分,使得使用起来更加方便和清晰。

方式三:直接使用NumPy API保存词向量信息

懒得写了遇到了再说

本人水平有限,如有错误还请各位大佬斧正 谢谢

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号