1. Word2Vec 概述
Word2Vec 是 Google 在 2013 年推出的一个 NLP 工具,其核心特点是将单词转化为向量来表示。这样词与词之间就可以定量地度量它们之间的关系,挖掘词之间的联系。
Word2Vec 是 Google 提出的将单词转化为向量的工具,通过分布式表示解决 One-Hot 编码高维稀疏问题。文章介绍了其核心原理,包括 CBOW 和 Skip-gram 两种神经网络模型结构及区别。CBOW 利用上下文预测目标词,Skip-gram 利用目标词预测上下文。针对训练效率,还讲解了层次 Softmax 和负采样优化方法。最后补充了基于 Python Gensim 库的实际代码实现示例,帮助读者理解词向量训练流程及应用场景。

Word2Vec 是 Google 在 2013 年推出的一个 NLP 工具,其核心特点是将单词转化为向量来表示。这样词与词之间就可以定量地度量它们之间的关系,挖掘词之间的联系。
用词向量来表示词并不是 Word2Vec 的首创。在很久之前就已经出现了。最早的词向量采用 One-Hot 编码(One-Hot Encoding),又称为一位有效编码。每个词向量的维度大小为整个词汇表的大小,对于词汇表中的每个具体词汇,将对应的位置置为 1。
例如,假设有 5 个词组成的词汇表:
采用 One-Hot 编码方式来表示词向量非常简单,但缺点也是显而易见的:
Distributed Representation(分布式表示)可以解决 One-Hot 编码存在的问题。它的思路是通过训练,将原来 One-Hot 编码的每个词都映射到一个较短的词向量上来。这个较短的词向量的维度可以由自己在训练时根据任务需要来指定。
下图是采用 Distributed Representation 的一个例子,将词汇表里的词用 "Royalty", "Masculinity", "Femininity" 和 "Age" 4 个维度来表示。King 这个词对应的词向量可能是 (0.99, 0.99, 0.05, 0.7)。当然在实际情况中,并不能对词向量的每个维度做一个很好的解释。
有了用 Distributed Representation 表示的较短的词向量,就可以较容易地分析词之间的关系了。比如将词的维度降维到 2 维,有一个有趣的研究表明,用下图的词向量表示词时,可以发现 King - Man + Woman ≈ Queen 这样的语义规律。
可见只要得到了词汇表里所有词对应的词向量,那么就可以做很多有趣的事情了。不过,怎么训练才能得到合适的词向量呢?针对这个问题,Google 的 Tomas Mikolov 在他的论文中提出了 CBOW 和 Skip-gram 两种神经网络模型。
Word2Vec 的训练模型本质上是只具有一个隐含层的神经元网络。
它的输入是采用 One-Hot 编码的词汇表向量,它的输出也是 One-Hot 编码的词汇表向量。
使用所有的样本,训练这个神经元网络,等到收敛之后,从输入层到隐含层的那些权重,便是每一个词的采用 Distributed Representation 的词向量。比如,上图中单词的 Word Embedding 后的向量便是矩阵 $W_{V\times N}$ 的第 i 行的转置。这样就把原本维数为 V 的词向量变成了维数为 N 的词向量(N 远小于 V),并且词向量间保留了一定的相关关系。
Google 的 Mikolov 在关于 Word2Vec 的论文中提出了 CBOW 和 Skip-gram 两种模型:
其中 CBOW 使用围绕目标单词的其他单词(语境)作为输入,在映射层做加权处理后输出目标单词。 与 CBOW 根据语境预测目标单词不同,Skip-gram 根据当前单词预测语境。
假如有一个句子 "There is an apple on the table" 作为训练数据,CBOW 的输入为 (is, an, on, the),输出为 apple。而 Skip-gram 的输入为 apple,输出为 (is, an, on, the)。
在训练前需要定义好损失函数(一般为交叉熵代价函数),采用梯度下降算法更新 W 和 W'。
训练完毕后,输入层的每个单词与矩阵 W 相乘得到的向量的就是 Distributed Representation 表示的词向量,也叫做 Word Embedding。因为 One-Hot 编码词向量中只有一个元素为 1,其他都为 0,所以第 i 个词向量乘以矩阵 W 得到的就是矩阵的第 i 行,所以这个矩阵也叫做 Look Up Table。有了 Look Up Table 就可以免去训练过程,直接查表得到单词的词向量了。
Skip-Gram 是给定 input word 来预测上下文,其模型结构与 CBOW 类似。
它的做法是,将一个词所在的上下文中的词作为输出,而那个词本身作为输入。也就是说,给出一个词,希望预测可能出现的上下文的词。通过在一个大的语料库训练,得到一个从输入层到隐含层的权重模型。
"apple" 的上下文词是 ('there', 'is', 'an', 'on', 'the', 'table')。那么以 apple 的 One-Hot 词向量作为输入,输出则是这些上下文词的 One-Hot 词向量。训练完成后,就得到了每个词到隐含层的每个维度的权重,就是每个词的向量(和 CBOW 中一样)。
具体训练步骤如下: 假如有一个句子 "There is an apple on the table"。
skip_window 的参数,它代表着从当前 input word 的一侧(左边或右边)选取词的数量。如果设置 skip_window=2,那么最终获得窗口中的词(包括 input word 在内)就是 ['is', 'an', 'apple', 'on', 'the']。skip_window=2 代表着选取 left input word 左侧 2 个词和右侧 2 个词进入窗口,所以整个窗口大小 span=2*2=4。另一个参数叫 num_skips,它代表着从整个窗口中选取多少个不同的词作为 output word。当 skip_window=2,num_skips=2 时,将会得到两组 (input word, output word) 形式的训练数据,即 ('apple', 'an'),('apple', 'one')。在前面两节中介绍了 CBOW 和 Skip-gram 最理想情况下的实现,即训练迭代两个矩阵 W 和 W',之后在输出层采用 Softmax 函数来计算输出各个词的概率。但在实际应用中这种方法的训练开销很大,不具有很强的实用性。为了使得模型便于训练,有学者提出了 Hierarchical Softmax 和 Negative Sampling 两种改进方法。
Hierarchical Softmax 对原模型的改进主要有两点:
具体来说,这棵哈夫曼树除了根结点以外的所有非叶节点中都含有一个由参数 θ 确定的 Sigmoid 函数,不同节点中的 θ 不一样。训练时隐藏层的向量与这个 Sigmoid 函数进行运算,根据结果进行分类,若分类为负类则沿左子树向下传递,编码为 0;若分类为正类则沿右子树向下传递,编码为 1。
尽管哈夫曼树的引入为模型的训练缩短了许多开销,但对于一些不常见、较生僻的词汇,哈夫曼树在计算它们的词向量时仍然需要做大量的运算。
负采样是另一种用来提高 Word2Vec 效率的方法,它是基于这样的观察:训练一个神经网络意味着使用一个训练样本就要稍微调整一下神经网络中所有的权重,这样才能够确保预测训练样本更加精确。如果能设计一种方法每次只更新一部分权重,那么计算复杂度将大大降低。
将以上观察引入 Word2Vec 就是:当通过 ("fox", "quick") 词对来训练神经网络时,回想起这个神经网络的'标签'或者是'正确的输出'是一个 one-hot 向量。也就是说,对于神经网络中对应于 "quick" 这个单词的神经元对应为 1,而其他上千个的输出神经元则对应为 0。
使用负采样,通过随机选择一个较少数目(比如说 5 个)的'负'样本来更新对应的权重。(在这个条件下,'负'单词就是希望神经网络输出为 0 的神经元对应的单词)。并且仍然为'正'单词更新对应的权重(也就是当前样本下 "quick" 对应的神经元仍然输出为 1)。
在实际开发中,通常使用 Python 的 Gensim 库来实现 Word2Vec。以下是一个简单的示例代码,展示如何加载语料、训练模型以及获取词向量。
from gensim.models import Word2Vec
from gensim.test.utils import common_texts
# 1. 准备语料数据
# 每一行是一个列表,包含分词后的单词
sentences = [
["i", "love", "deep", "learning"],
["deep", "learning", "is", "fun"],
["i", "like", "programming"]
]
# 2. 训练模型
# vector_size: 词向量的维度
# window: 上下文窗口大小
# min_count: 忽略低于此频率的词
# workers: 并行训练线程数
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4)
# 3. 查看模型信息
print(model.wv.index_to_key) # 查看词汇表
# 4. 获取词向量
vector = model.wv['learning']
print(f"learning 的词向量维度:{len(vector)}")
# 5. 查找相似词
similar_words = model.wv.most_similar('learning', topn=5)
print("与 learning 最相似的词:", similar_words)
# 6. 保存模型
model.save("word2vec.model")
# 7. 加载模型
loaded_model = Word2Vec.load("word2vec.model")
通过上述代码,可以快速构建基础的词向量模型。在实际生产环境中,通常会使用更大的语料库(如维基百科、新闻语料等),并调整超参数以获得更高质量的词向量表示。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online