ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入解析Word2Vec Skip-gram模型:从原理、负采样优化到实战指南

深入解析Word2Vec Skip-gram模型:从原理、负采样优化到实战指南 1. 从“词袋”到“词向量”为什么我们需要word2vec如果你做过文本分类或者情感分析大概率用过“词袋”模型。简单来说就是把一篇文章看成一个个独立的单词然后统计每个词出现的次数。这种方法直白有效但有个致命缺陷它认为“苹果”和“香蕉”的距离与“苹果”和“公司”的距离是一样的。显然在语义上“苹果”和“香蕉”都是水果关系更近。词袋模型丢失了词语之间的语义关联这就像把一本字典里的所有词都打散然后告诉你每个词出现了几次但你完全不知道这些词之间有什么关系。word2vec的出现就是为了解决这个问题。它的核心思想非常巧妙一个词的语义可以由它经常和哪些词一起出现来定义。这句话听起来有点绕但想想我们学语言的过程。一个孩子第一次听到“美味”这个词可能是在“美味的苹果”、“美味的蛋糕”这样的语境里。听多了他就能模糊地感觉到“美味”和“好吃的东西”有关。word2vec做的就是类似的事它通过海量的文本数据让模型自己去学习这种“上下文”关系最终为每个词生成一个固定长度的稠密向量也就是我们常说的“词向量”或“词嵌入”。这些向量有多神奇呢经过训练后向量空间中的几何关系会反映语义关系。比如“国王”的向量减去“男人”的向量再加上“女人”的向量结果会非常接近“女王”的向量。再比如“北京”和“中国”的关系会类似于“巴黎”和“法国”的关系。这种从离散符号到连续向量的转变是自然语言处理领域一个里程碑式的进步为后续的深度学习模型提供了高质量的输入基础。word2vec主要提出了两种模型架构来实现这个目标CBOW和Skip-gram。CBOW是用上下文词来预测中心词而Skip-gram恰恰相反是用中心词来预测它周围的上下文词。今天我们就来深入拆解一下Skip-gram模型看看这个用中心词预测上下文的思路具体是怎么运作的背后又有哪些精妙的设计和不得不提的工程优化。2. Skip-gram模型的核心思想与网络结构Skip-gram模型的全称是“Skip-gram with Negative Sampling”但它的基本思想比这个名字更早。我们可以把它理解成一个“完形填空”游戏不过这个游戏规则是给你一个中心词让你猜猜它周围可能出现的词是什么。2.1 模型的基本设定假设我们有一句话“我 喜欢 吃 美味 的 苹果”。我们设定一个窗口大小比如为2。那么对于中心词“吃”它的上下文窗口就是前后各两个词[我 喜欢 美味 的]。Skip-gram模型的任务就是给定中心词“吃”模型需要最大化它预测出上下文词“我”、“喜欢”、“美味”、“的”的概率。注意这里有一个关键点在基础的Skip-gram中模型认为给定中心词后各个上下文词的出现是相互独立的。也就是说预测“我”和预测“喜欢”这两个事件是独立的。这个假设显然不完全符合语言事实“喜欢”后面接“吃”的概率和“我”后面接“吃”的概率显然不同但它极大地简化了模型设计和计算被实践证明是行之有效的。2.2 网络结构拆解Skip-gram的神经网络结构其实非常简单是一个仅含输入层、一个隐藏层和输出层的浅层网络。输入层 (Input Layer)这是一个1 x V的one-hot向量其中V是整个词汇表的大小。比如词汇表有1万个词“吃”这个词是词汇表中的第500个那么输入向量就是在第500个位置为1其余位置为0的向量。[0, 0, ..., 1, ..., 0]。隐藏层 (Hidden Layer)隐藏层的神经元数量就是我们想要的词向量的维度比如300维。连接输入层和隐藏层的是一个V x D的权重矩阵W通常称为输入权重矩阵或嵌入矩阵。这个矩阵的每一行就对应一个词的词向量当我们把“吃”的one-hot向量输入网络时与矩阵W相乘实际上就是一次查找操作得到的正是矩阵W的第500行也就是“吃”这个词的D维词向量。所以训练完成后我们真正要拿走的就是这个输入权重矩阵W它就是所有词的词向量查找表。输出层 (Output Layer)输出层的神经元数量也是V对应词汇表中的每一个词。连接隐藏层和输出层的是另一个D x V的权重矩阵W‘称为输出权重矩阵。隐藏层的词向量与W‘相乘会得到一个1 x V的分数向量。然后我们通常对这个分数向量应用softmax函数将其转换为一个概率分布。这个概率分布就表示给定中心词“吃”词汇表中每一个词作为其上下文词出现的概率。整个前向传播过程可以概括为输入中心词的one-hot向量 - 通过W矩阵查到其词向量 - 该词向量与W‘矩阵相乘得到分数 - softmax得到概率分布 - 我们希望正确上下文词如“我”、“喜欢”对应的概率尽可能大。注意这里有一个容易混淆的点。最终我们使用的词向量是输入矩阵W的行向量。输出矩阵W‘在训练中起到辅助作用它也可以被视为每个词作为“上下文词”时的另一种向量表示。有些实践中会将W和W‘的平均或拼接作为最终词向量效果可能更好。2.3 目标函数最大化对数似然模型的目标很明确对于训练语料中的每一个中心词让它正确预测出其上下文窗口中所有词的概率最大。用数学公式表达就是最大化下面的平均对数概率$$ \frac{1}{T} \sum_{t1}^{T} \sum_{-c \leq j \leq c, j \neq 0} \log p(w_{tj} | w_t) $$其中T是语料总词数c是上下文窗口大小w_t是第t个中心词w_{tj}是其上下文词。p(w_O | w_I)就是给定中心词w_I出现上下文词w_O的条件概率由上述网络经过softmax计算得出$$ p(w_O | w_I) \frac{\exp({v{w_O}}^T v{w_I})}{\sum_{w1}^{V} \exp({v{w}}^T v{w_I})} $$这里v_w是词w在输入矩阵W中的向量中心词表示v‘_w是词w在输出矩阵W‘中的向量上下文词表示。3. 训练中的巨大挑战与关键优化负采样如果你仔细看上面的softmax公式会发现分母需要对词汇表V中的每一个词都计算一次指数内积并求和。当V很大时比如达到百万级别这个计算成本是灾难性的几乎无法完成训练。这就是原始Skip-gram模型面临的核心挑战计算归一化分母的代价太高。为了解决这个问题Mikolov等人提出了两种革命性的优化技术层次Softmax和负采样。其中负采样因其简单高效成为了最流行的方法。3.1 负采样的核心思想负采样彻底改变了训练的目标。它不再去计算那个庞大的、完整的softmax概率分布而是把问题转化成了一个二分类任务。对于每一个训练样本中心词上下文词对比如“吃” “美味”我们把它看作一个正样本目标是要让模型判断这个对是“相关的”。同时我们随机从词汇表中抽取k个词比如k5这些词通常不是“吃”的上下文词。我们构造k个负样本如“吃” “足球”、“吃” “编程”等。目标是要让模型判断这些对是“不相关的”。这样原来一个需要计算V次百万次的softmax问题就变成了k1次如6次的二分类sigmoid问题计算量骤降。3.2 新的目标函数在新的设定下目标函数变成了最大化正样本的概率同时最小化负样本的概率。对于一对中心词w_I和上下文词w_O其损失函数负对数似然为$$ \log \sigma({v{w_O}}^T v{w_I}) \sum_{i1}^{k} \mathbb{E}{w_i \sim P_n(w)} [\log \sigma(-{v{w_i}}^T v_{w_I})] $$其中σ是sigmoid函数。第一项是正样本的得分我们希望v‘_w_O和v_w_I的内积越大越好sigmoid越接近1。第二项是对k个负样本的期望我们希望负样本词w_i的向量v‘_w_i与中心词向量v_w_I的内积越小越好sigmoid越接近0。3.3 负样本如何采样—— 一个影响重大的细节负样本不是均匀随机采样的。一个直觉是像“的”、“了”、“是”这样的高频词它们出现在任何词附近的概率都很高作为负样本提供的信息量就很小。相反像“饕餮”这样的低频词如果它出现在负样本中模型能更明确地学到“吃”和“饕餮”不常共现。因此word2vec采用了一种加权采样策略一个词被采样的概率与其在语料中出现的频率的3/4次方成正比$$ P(w_i) \frac{f(w_i)^{3/4}}{\sum_{j1}^{V} f(w_j)^{3/4}} $$这个3/4次方是一个经验性的魔法数字。它既降低了高频词的采样概率又避免了低频词被过度提升实践效果非常好。在实际自己实现时这个采样分布需要预先计算好通常使用“别名采样”算法来高效实现。实操心得负采样的数量k是一个重要超参数。论文中建议对于小数据集k取5-20对于大数据集k取2-5就足够了。k越大模型训练越稳定但每个样本的计算量也越大。我个人的经验是在一般规模的语料上如中文维基百科k5是一个不错的起点。你可以观察到增加k会使训练速度变慢但最终得到的词向量在相似度任务上可能略有提升。4. 从理论到实践训练Skip-gram的完整流程与参数剖析理解了原理和优化我们来看看如何从头开始训练一个Skip-gram模型。这个过程充满了工程细节每一步的选择都会影响最终词向量的质量。4.1 数据预处理比想象中更重要很多人拿到文本数据就直接开始训练这是不对的。预处理对word2vec的效果影响巨大。分词/分字对于英文需要处理大小写、缩写如cant对于中文则需要可靠的分词工具。值得注意的是word2vec对分词错误比较敏感因为错误的切分会制造出根本不存在的“词”。去除低频词出现次数少于min_count如5-10次的词由于没有足够的上下文信息学到的向量不可靠应该从词汇表中剔除。这能大幅减小模型规模提升训练速度和稳定性。下采样高频词这是word2vec论文中一个非常关键的技巧。像“的”、“是”、“在”这样的高频词几乎和所有词共现提供的语义信息很少但出现的次数极多。如果不处理它们会主导整个训练过程。下采样的概率公式为 $$ P(w_i) 1 - \sqrt{\frac{t}{f(w_i)}} $$ 其中f(w_i)是词频t是一个阈值如10^-5。词频越高被丢弃的概率越大。这能有效平衡高频词和低频词的影响并加速训练。构建训练样本滑动窗口遍历整个语料。对于每个中心词将其与窗口内每一个上下文词组成一个正样本对(center_word,context_word)。窗口大小window是一个超参数通常取5或10。论文中还提到可以使用动态窗口即每次采样一个1到window之间的随机数作为实际窗口大小这可以让模型看到更多样化的上下文。4.2 超参数调优指南训练Skip-gram就是与这些超参数打交道。下面是一个详细的参考表格超参数典型值/范围作用与影响调整建议向量维度 (size)100 - 300词向量的长度。维度越高表达能力越强但也更容易过拟合需要更多数据。小语料1亿词用100-200维大语料10亿词可用300维甚至更高。通常256是一个安全的选择。窗口大小 (window)5 - 10考虑多远的上下文。小窗口捕捉句法信息如动词宾语大窗口捕捉语义/话题信息同一话题的词。默认用5。如果任务更关注语义相似性如文档分类可以尝试10。可以使用动态窗口增加随机性。最小词频 (min_count)5 - 10过滤低频词。对于大数据集可以设小点如5小数据集设大点如10-20以保证词向量质量。负采样数 (negative)5 - 15每个正样本对应的负样本数量。影响训练速度和稳定性。小数据集用10-15大数据集用2-5。常用值为5。下采样阈值 (sample)1e-5控制高频词下采样概率的阈值t。值越大下采样越激进。常用1e-5或1e-4。如果语料中高频词停用词特别多可以尝试1e-3。初始学习率 (alpha)0.025训练开始时的学习率。通常从0.025开始随着训练线性递减至一个很小的值如0.0001。迭代次数 (epochs)5 - 15在整个语料上训练的轮数。不是越多越好word2vec训练很快会收敛通常5-10个epoch足够。太多会导致过拟合。4.3 训练过程模拟假设我们有一个经过预处理的语料库词汇表大小为1万我们设定size100,window5,negative5。初始化随机初始化两个矩阵W10000 x 100和W‘100 x 10000。取一个批次从语料中取出一批中心词比如batch_size32。前向传播对每个中心词查找其在W中的向量100维。生成样本对于每个中心词从其上下文窗口中选取一个正样本上下文词并从噪声分布中采样5个负样本词。计算损失对于正样本计算σ(v_center · v‘_positive)对于每个负样本计算σ(-v_center · v‘_negative)。求和得到这个样本的总损失。反向传播计算损失关于v_center、v‘_positive和5个v‘_negative的梯度。参数更新使用随机梯度下降SGD更新这些被涉及到的向量。注意这里有一个巨大的优化我们只更新了1中心词 1正样本词 5负样本词 7个词的向量而不是更新整个百万维的矩阵。这被称为“稀疏更新”是训练高效的关键。循环重复步骤2-7直到遍历完所有语料并完成设定的epoch数。训练完成后丢弃W‘矩阵W矩阵就是我们需要的词向量表。5. 评估、应用与避坑指南训练完成后我们怎么知道得到的词向量好不好又该怎么用这里分享一些实用的评估方法和应用场景以及我踩过的一些坑。5.1 如何评估词向量质量评估通常分为内部任务和下游任务。内部任务直接评估向量本身词相似度计算计算两个词向量的余弦相似度与人工标注的相似度分数如WordSim-353, SimLex-999计算相关系数如斯皮尔曼等级相关系数。这是最直接的评估方式。类比任务经典的“国王-男人女人女王”任务。给定三个词a, b, c如北京、中国、法国让模型找到词d使得vec(a)-vec(b) ≈ vec(d)-vec(c)。通过模型答对类比问题的比例来评估。下游任务评估在实际应用中的效果将训练好的词向量作为特征输入到文本分类、命名实体识别、情感分析等具体任务的模型中观察任务指标如准确率、F1值的提升。这是终极检验。个人体会内部任务和下游任务的结果不一定完全正相关。我遇到过在类比任务上得分很高的词向量在具体的文本分类任务上表现平平。这可能是因为类比任务更关注向量空间的线性关系而分类任务可能更关注整体的分布特征。因此最可靠的评估方式还是在你自己的目标任务上进行验证。5.2 经典应用场景作为深度学习模型的初始化在训练RNN、CNN、Transformer等模型处理文本时使用预训练的word2vec词向量来初始化嵌入层相比随机初始化通常能带来显著的性能提升和更快的收敛速度。计算词语/短文本相似度直接用于搜索引擎、推荐系统的语义匹配或者社群发现、关键词扩展等。特征工程对文本中的词向量进行聚合如平均、加权平均、TF-IDF加权平均得到句子或文档的向量表示用于聚类、分类等任务。词义发现与消歧通过分析一个词在不同语境下的上下文向量将上下文词向量平均可以发现一词多义现象。5.3 实战中的常见“坑”与解决方案坑1语料质量差噪声大现象训练出的词向量语义混乱“苹果”和“公司”的相似度可能比和“香蕉”还高。排查检查预处理步骤。是否做了有效的清洗是否包含了大量乱码、广告、无关符号中文是否分词准确解决投入足够精力在数据清洗上。对于中文使用高质量的分词器如jieba的精确模式、pkuseg、HanLP等。建立领域相关的停用词表和保留词表。坑2超参数设置不合理现象模型不收敛或者词向量性能很差。排查对照上文的超参数表格检查最关键的几个size是否相对于语料过大导致过拟合window是否适合你的任务sample下采样阈值是否太小导致高频词没被抑制解决进行小规模的超参数网格搜索。固定其他参数每次只调整1-2个在一个小型的验证集如类比任务上观察效果。学习率的衰减策略非常重要很多开源实现如gensim已经内置了良好的衰减策略直接使用即可。坑3生僻词或领域专有词效果差现象在医疗、法律等垂直领域专业术语学不到好的向量表示。排查这些词在通用语料中出现的频率min_count是否达标领域语料是否足够解决使用领域内的大规模语料进行训练。如果数据量有限可以考虑采用“继续训练”的方式用通用语料预训练一个模型然后在领域语料上用较小的学习率进行增量训练让模型在通用语义的基础上适应领域特性。坑4内存或速度问题现象词汇表巨大百万级以上时内存消耗大训练慢。排查是否使用了原始的softmax是否没有过滤低频词解决确保使用负采样或层次softmax。严格使用min_count过滤。如果使用Python的gensim库它内部采用了C语言优化和多线程训练能高效处理大规模数据。对于超大规模语料可以考虑使用分布式训练。word2vec的Skip-gram模型以其简洁的架构和深刻的思想开启了词向量预训练的时代。虽然如今Transformer和BERT等模型已经成为了主流但理解Skip-gram仍然是进入NLP世界的一把关键钥匙。它教会我们如何从数据中无监督地学习语义表示这种思想至今仍在深刻影响着深度学习的发展。当你下次使用一个现成的词向量模型时不妨想想背后这个用中心词预测上下文的简单网络以及它为了高效训练而做的那些精妙绝伦的优化。
返回列表