ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Word2vec词向量训练全指南:从原理到参数调优与实战应用

Word2vec词向量训练全指南:从原理到参数调优与实战应用 1. 先搞清楚word2vec到底在解决什么问题1.1 从one-hot编码说起为什么词需要用向量表示做自然语言处理的人第一天就会遇到一个绕不开的问题计算机不认识中文、不认识英文只认识数字。你想让机器处理苹果和香蕉这种词就必须先把它们变成数字。早期最朴素的办法就是one-hot编码也就是独热编码。假设你有一本词典里面有10万个词那么每个词就对应一个10万维的向量当前词的位置上是1其它位置全是0。苹果是第1个词它就是[1,0,0,...,0]香蕉是第2个词它就是[0,1,0,...,0]。这种方式简单是简单但问题非常致命。第一个问题是维度爆炸10万个词的词典每篇文章用10万维向量表示存储和计算都是灾难。第二个问题更关键它完全无法表达词与词之间的语义关系。在one-hot的世界里苹果和香蕉之间的距离跟苹果和汽车之间的距离一模一样都是(\sqrt{2})。机器完全不知道苹果和香蕉都是水果也不知道猫和狗都是宠物。这显然不符合人类对语言的理解方式。所以后来出现了分布式表示Distributed Representation核心思想是用一个低维的稠密向量来表示每个词比如300维或者500维每一维不是一个非0即1的标志位而是带有一定语义含义的数值。这样一来苹果和香蕉的向量在空间里就会离得比较近苹果和汽车就会离得远。word2vec做的就是这件事它通过海量语料训练把每个词映射成一个低维实数向量而且这个向量能很好地捕捉词的语义和句法特征。1.2 词向量的核心意义让机器理解词之间的关系你可能会问向量离得近有什么用用处太大了。我们可以直接对向量做加减法。比如经典的例子(vec(国王) - vec(男人) vec(女人))结果会非常接近(vec(女王))。这个结果不是人为设计的而是word2vec从语料里自己学出来的。因为它能学到国王和女王之间的差异和男人和女人之间的差异在向量空间里是同一种平移关系。更进一步我们可以用余弦相似度计算两个词的语义相似度。在实际项目里我经常用这个能力做三件事一是找同义词比如做搜索系统时用户搜笔记本电脑我可以召回手提电脑笔记本这些词二是做词聚类把一堆词按语义分成几组三就是作为更复杂模型比如BERT、LSTM、TextCNN的输入底座预训练好的词向量能显著提升下游任务的起点。这就是word2vec的核心价值它用无监督的方式从大规模语料里学习词的低维稠密向量表示让语义相近的词在向量空间里距离更近让词与词之间的关系变得可计算。在2013年它刚出来的时候效果是颠覆性的训练速度比当时的神经网络语言模型快了几个数量级而且在词相似度任务上效果大幅提升。哪怕放到今天如果你没有大规模GPU资源去训练BERTword2vec依然是性价比最高的词向量方案。2. 两种主流模型CBOW和Skip-gram怎么选2.1 CBOW拿上下文猜中间词word2vec提供的第一个模型叫CBOW全称是Continuous Bag-of-Words连续词袋模型。它的思路非常直观给你一个词周围的上下文词让你猜中间这个词是什么。比如有一句话我喜欢喝_茶CBOW就是拿着[我喜欢喝茶]这四个词去预测中间那个被遮住的词绿。为什么叫词袋因为在这个模型里上下文词的顺序是被忽略的它们被当成一个集合来处理。具体做法是把上下文里的每个词都映射成向量然后把这些向量取平均或者求和得到一个综合的上下文向量再拿这个向量去预测中间词。整个过程就像你听一个人说话听到前后几个词心里大概能猜出他中间要说什么。CBOW对高频词的预测效果比较好而且训练速度更快因为它一次处理多个上下文词的信息梯度更新次数相对少。这里要注意一个细节CBOW的输入是上下文词的one-hot或者索引输出是中间词的概率分布。训练时我们会拿语料里的每一个词把它当作目标词取它前后一定窗口内的词作为上下文构造训练样本。窗口大小是一个超参数通常取5也就是前后各看5个词一共最多10个上下文词。窗口太小模型看不到足够的信息窗口太大又会引入太多无关词汇反而干扰预测。2.2 Skip-gram拿中间词猜上下文Skip-gram模型正好和CBOW反过来它拿中间词去预测周围的上下文词。还是刚才那句话我喜欢喝绿茶Skip-gram会拿绿这个中间词去预测它周围的[我喜欢喝茶]。这个思路很像你记住一个词然后在脑海里回忆这个词通常会跟什么样的词一起出现。Skip-gram对低频词和生僻词更友好因为它在训练过程中会把更多注意力放在每一个单独的词上周围每个上下文词都是一个独立的预测任务。代价是训练速度比CBOW慢因为它等价于在一个样本里做了多个预测。实际使用时如果你的语料量不大、或者你特别在意罕见词的表示质量Skip-gram往往是更好的选择。Mikolov等人在原论文里的实验也表明Skip-gram在语义相似度任务上整体表现更好一些尤其是在大规模语料上效果差距会更明显。2.3 实战选型建议到底用哪个我自己的习惯是分场景选。如果做的是搜索、推荐这类需要精确处理大量实体词和低频词的项目优先用Skip-gram词向量的质量普遍更细腻。如果做的是文本分类、情感分析这类任务语料又非常大CBOW更合适它训练快、对高频词拟合好下游任务效果也够用。再补充一个判断维度训练时间。同样的语料、同样的维度CBOW通常比Skip-gram快2到3倍。如果项目迭代节奏很快需要频繁重跑模型CBOW能帮你节约不少时间。如果项目本身就离线训练、跑一次能用很久Skip-gram多花的那点时间完全值得。对比维度CBOWSkip-gram训练任务用上下文预测中间词用中间词预测上下文训练速度快慢约慢2-3倍低频词质量一般更好高频词质量好好语料较小场景谨慎使用更推荐大型语料场景推荐推荐这个表格看着简单但我建议你在项目里还是做一次小规模的A/B测试再做决定。因为不同语料的词频分布差异很大理论上的结论在具体场景里未必总是成立。3. 训练过程背后的关键细节3.1 网络结构其实很简单一个隐层的神经网络很多人一听神经网络就头大觉得word2vec的训练复杂无比。实际上word2vec的原始网络结构非常简单一个输入层、一个隐藏层、一个输出层中间没有任何激活函数和复杂的层结构。它的隐藏层本质上就是一个查找表也就是一个形状为(V \times N)的矩阵(V)是词典大小(N)是你设定的词向量维度。输入某个词的one-hot向量乘上这个矩阵得到的其实就是这个词对应的那一行向量。可以这么理解这个隐藏层的权重矩阵就是所有词的向量表。训练的目的就是不断调整这个矩阵里的数值让最终每个词对应的行向量能够携带足够丰富的语义信息。在网络训练完之后我们扔掉输出层只保留这个权重矩阵用它作为查表工具把词变成向量。要注意的是输入层用的还是one-hot但这里只是作为索引来用。实际工程实现里根本不会真的做one-hot向量和矩阵的乘法而是直接按词在词典里的索引去取矩阵的对应行这样计算效率极高。3.2 输出层的softmax问题词汇量一大就卡死如果按照最朴素的思路word2vec的输出层应该是一个标准的softmax。也就是说模型在预测目标词的时候需要计算所有词的得分再做一个归一化的概率分布。假设词典有100万个词那每个训练样本都要做100万次的乘法计算这在实际工程里是行不通的。我打个比方你在一家超市做问卷调查需要从10万个顾客里找到最符合你条件的那一个人你挨个问一遍问一次花时间不多但100万个样本每个都要问一遍时间就爆炸了。这就是为什么word2vec不能直接使用标准softmax的原因。为了解决这个问题原论文提供了两个非常经典的优化方案负采样Negative Sampling和层次SoftmaxHierarchical Softmax。这两个方案你可以理解成两种偷懒但效果很好的办法它们让word2vec在百万级词典上也能在可接受的时间内训练完成。3.3 负采样把多分类变成二分类负采样的思路很巧妙。原始的正向传播需要计算所有词的概率工作量巨大。负采样把它简化为给定上下文和中心词我只需要判断这个词是不是真的应该出现在这里。也就是说把在10万个词里挑一个正确的这个多分类问题变成了判断这组词搭配是否正确的二分类问题。具体做法是训练时对于每个正样本也就是真实出现在语料里的词对我们再随机抽出几个负样本也就是从词典里随机挑几个跟当前位置无关的词然后让模型学会给正样本打高分、给负样本打低分。负样本的数量通常取5到20个远小于词典大小计算量大幅下降。负采样时不是均匀地随机抽而是用一种基于词频的概率分布来抽这个分布叫unigram distribution的3/4次方。为什么要做3/4次方因为这样可以稍微压制那些频率极高的词给低频词更多被抽中的机会。我在实际项目中很少去调整这个参数gensim里默认的实现效果已经很好。负采样还有一个隐藏的好处它的训练过程天然会学习到词和词之间的区分性所以词向量在语义空间里的分布会更均匀相似度计算的效果也更稳定。这也是我在大多数项目里优先选择负采样的原因。3.4 层次Softmax用霍夫曼树加速归一化层次Softmax是另一个优化方案它的思路是彻底不计算所有词的得分而是把输出层的多分类问题转化成一连串的二分类问题。具体做法是先根据词频构造一棵霍夫曼树词频越高的词离根节点越近。树的每个叶子节点对应一个词每个内部节点对应一次二分类的决策。预测一个词的时候只需从根节点沿着路径走经过大约(log_2(V))个内部节点也就是大约(\log_2(1000000)\approx20)次决策而不是计算100万次。这个方案在词频分布极不均衡的语料里表现很好因为高频词的路径短训练高频词时计算很快。如果你用gensim训练word2vec默认情况下其实用的是负采样而不是层次Softmax这是实践中的一个默认取舍。层次Softmax的缺点是当词向量维度较高时它可能不如负采样灵活而且实现起来更容易出细节错误。我对层次Softmax的建议是理解原理即可除非你有明确理由否则用负采样就够了。之前有朋友问过我为什么自己实现的层次Softmax效果总比gensim差我说你检查一下霍夫曼树的构造和路径编码是否完全一致这个细节不对效果就千差万别。4. 实操过程中的参数与调优经验4.1 核心超参数窗口、维度、最小词频训练word2vec时参数设置直接决定词向量的质量这里我逐个讲讲最核心的几个参数都是我自己踩过坑总结出来的经验。第一个是窗口大小window。窗口决定了每个词能看到多大的上下文范围。窗口太小模型学到的主要是语法搭配比如我后面经常跟喜欢窗口太大模型学到的主要是主题相似性比如篮球和体育经常出现在同一段话里。一般做语义相似度任务窗口选5到10做句法、词性相关任务窗口选2到5。我在做搜索推荐项目时窗口取5通常是最稳的如果你发现词向量学出来的词和预期差距很大先把窗口调一调很多时候问题就出在这。第二个是向量维度size。这个参数决定了向量的表达能力。维度太小比如50词与词之间的差异表达不出来很多语义信息会被压缩丢失维度太大比如1000训练时间变长而且小的语料容易过拟合。常规做法是在100到300之间选。我可以给你一个参考如果你的语料有1亿词以上300维完全合理如果语料只有几千万词200维左右更稳。一个非常实用的判断方法如果你发现某些不相关词之间的相似度异常高很可能是语料不足而维度太高导致的过拟合。第三个是最小词频min_count。这个词频参数会过滤掉出现次数太少的词。为什么要过滤因为低频词本身信息量不足训练出来的向量往往不可靠而且会拖慢训练速度。一般设为5或10。但你得根据实际语料规模来灵活调整语料特别大时可以设高一点比如50语料本身很小设5可能就够。需要注意的是这个参数会直接影响词典大小你过滤掉的词越少词典越大内存占用越高。4.2 训练数据预处理这一步比调参更重要我在项目里反复跟人讲word2vec的最终效果语料质量和预处理的影响要占七成参数只占三成。很多人都把精力花在调参上结果效果还是不理想回头一看原始语料乱糟糟的分词也不对那再怎么调参都白搭。中文语料必须先分词。word2vec本身不认识中文句子这个概念它只认空格分隔的词序列。如果你把整句话直接扔进去模型学到的东西完全没有意义。我个人推荐使用jieba分词简单方便对于绝大多数场景够用。但如果你做的是专业领域比如医疗、法律建议在通用分词基础上加上自定义词典把领域内的专有名词保护起来否则心肌梗死会被切成心肌和梗死语义就歪了。另外很多人在意要不要去停用词我的建议是看场景。做情感分析时停用词确实可以去掉因为它们和情感表达关系不大但做语言模型或者词向量训练时我不建议过度清理因为的了是这些高频功能词虽然单独看没什么语义但它们的上下文搭配信息对捕捉语法结构是有帮助的。更好的做法是设置min_count让这些词自然留下来但对结果影响有限。最后还有一个容易被忽略的点样本采样参数sample。word2vec在训练时会根据词频对每个词做随机下采样也就是说高频词会以一定概率被丢弃以此来平衡低频词和高频词的比例。这个参数的默认值是0.001通常不用改。但如果你的语料里有极端高频的噪声词可以适当调低这个值比如到0.0001可以明显提升低频词的向量质量。4.3 训练过程迭代次数与训练效果的关系训练word2vec时迭代次数epochs也值得琢磨。很多人默认设5次我发现这个值并不总能保证最佳效果。在语料规模较小的情况下多迭代几次能让模型充分学习但也不是越多越好迭代次数过多词向量会逐渐过拟合到语料中的偶然共现上。怎么判断迭代次数合不合适我常用的办法是训练过程中实时检查一组验证词对比如北京和上海的相似度、中国和美国的相似度。如果随着迭代次数增加这些词对的相似度不断提高且趋于稳定说明模型还在学习如果相似度出现反弹或者开始变得不合理说明已经过拟合了。实际操作中我通常会让模型训练到验证集上的相似度指标不再明显上升为止然后在此基础上去掉一部分迭代次数作为安全余量。另外还有一点训练时设置多个线程可以显著加快速度但线程数并不是越大越好。线程过多时每个线程各自取样训练整体效果可能会略微下降因为不同线程之间的更新存在一定的竞争。如果语料特别大一般在16到32个线程之间比较合适。5. 训练完成后的应用与评估技巧5.1 词向量的评估方法不只靠肉眼观察训练完模型后如何评估词向量的好坏很多初学者喜欢用most_similar函数查一下苹果的近义词看看结果是否合理。这个方法可以做初步判断但不能作为唯一的评估手段。因为示例抽查带有很大的随机性你可能恰好选中了表现好的词。更可靠的评估方式是用标准数据集跑相似度任务。比如说SimLex-999就是一份人工标注了999对词相似度分数的数据集。你可以把word2vec算出来的余弦相似度和人工打分算相关性Spearman相关系数这个指标能比较客观地反映词向量的质量。在做中文项目时也有对应的中文词相似度评测集比如Wordsim-240和Wordsim-297。还有一个很实用的评估方法是类比推理任务。经典的问题就是国王-男人女人女王。你可以自己构建一组类比词对比如北京-中国日本东京、父亲-儿子女儿母亲等等然后用词向量做向量加减法看看模型能不能给出合理答案。这个方法操作起来非常快而且能直观地反映词向量是否学到了语义关系。5.2 词向量在下游任务里的使用方式训练好的word2vec词向量最常见的用法是作为各种深度学习模型的输入层。比如做文本分类时可以把一句话里每个词对应的向量拼起来作为卷积神经网络或者循环神经网络的输入。这种方式在你没有资源训练大规模预训练模型时是非常好的baseline方案。还有一种用法是做词向量的融合。有时候单个词向量维度过高或者信息不够充分我们可以训练多组不同参数设置下的词向量然后把它们拼接或者取平均获得更丰富的表示。我在一个文本匹配项目里试过把窗口5和窗口10训练出来的两组词向量拼接起来最终效果比单组词向量提升了大约2个百分点。这个方法成本很低值得一试。有一点必须注意词向量是静态的。一个词在任何语境下都对应同一个向量所以苹果水果和苹果手机品牌会被表示成同一个向量。因此在做下游任务时词向量只能作为特征输入不能完全替代上下文建模。如果你的任务对一词多义特别敏感还是得考虑ELMo、BERT这类动态模型。但即便如此word2vec作为理解和起步的工具依然有不可替代的价值。5.3 常见问题与排查技巧实录模型训练报错内存不足。这种情况多半是词典太大或者语料一次性加载太多造成的。先用min_count过滤低频词控制词典大小然后检查是否能用迭代读取语料的方式避免把全部语料同时放进内存。gensim本身支持流式读取词向量的内存占用约等于(词典大小 \times 维度 \times 4)字节你可以估算一下比如词典10万、维度300大概需要1.2GB内存。词向量相似度结果非常奇怪。我遇到过苹果最近似的词是华为、三星这种多半是语料里频繁出现苹果手机苹果公司这类搭配。这不是模型的bug而是语料本身的问题。这提醒你词向量学习的是语料里的共现统计而不是真实世界百科常识。想改善就要在预处理时结合语料背景做修正比如增加更多通用语料来稀释领域偏置。训练很慢如何提速检查一下是不是词典过大、样本数过多或者线程数设得太少。还有一个容易被忽略的点负采样的负样本数量不要设得过大我见过有人把negative设成50训练速度慢到怀疑人生。通常5到15就足够没有特殊情况不建议超过20。模型训练完成但个别生僻词没有词向量。这是OOVout-of-vocabulary问题。低频词被min_count过滤掉了于是训练后查不到。解决思路是降低min_count、扩大语料规模或者使用FastText这类能基于子词信息生成OOV词向量的模型。FastText本质上是word2vec的扩展版在训练时额外的字符级n-gram信息相关的做法是把它当作word2vec的备选方案效果在很多任务里都很不错。如果已经开始用新的模型做训练那也可以考虑用它来对比一下新模型与word2vec类方法的差异能帮助你更好地理解不同方法之间的差异。训练结果随机性大。相同语料、相同参数跑两次每次结果都不完全一样。这是因为训练时存在随机初始化、负采样随机抽取等因素。这不算bug但如果你需要稳定复现结果可以固定随机种子。科学实验对比时建议固定种子后再去对比不同参数的效果否则你很难判断效果差异到底来自参数调整还是随机波动。写在最后的经验和建议我在实际项目里发现很多人一上来就追求复杂的模型BERT、GPT轮着试反而忽视了word2vec这类基础工具的价值。其实在小样本、低算力的业务场景里word2vec加一个简单分类器的方案会是一个相当实用的起点接地气且能快速落地。它有近十年的历史今天依然在推荐系统召回、语义匹配、词义消歧等环节发挥着作用而且很多深度学习模型的Embedding层本质上学的也是同一种东西。最后再分享一个我自己的小习惯训练好的词向量不管后续用不用我都会存一份保留下来。因为词向量本质上是对语料的深度压缩它把海量文本蕴含的词汇关系浓缩成了一份可查询的词表。这份词表不仅能用来初始化下游模型还能在数据审计、相似词挖掘、词典构建这些工作中反复复用。花一次训练的成本换来的是一个可以长期使用的语义基础设施这个投资非常划算。
返回列表