漫话Word2Vec:从离散符号到语义向量空间

🐱小可 & 🐶博士 · Word2Vec 科普漫画

从离散符号到语义向量空间------一场跨越25幕的知识冒险!


小可(好奇地探头):计算机看到「苹果」这个词,到底在想什么?

博士(推了推眼镜):对人类来说,看到「苹果」会联想到水果、红色、甜美、甚至iPhone......但对计算机而言,它只是一个毫无意义的离散符号!

这就是自然语言处理(NLP)面临的第一个根本性问题:如何让计算机理解人类语言中的词?

人看到"苹果"这个词,脑海中会自然浮现出一幅丰富的画面------红润的果实、清脆的口感、水果店的货架、甚至是某个科技品牌。这种联想能力是人类语言理解的基石。但计算机完全不同。对计算机来说,"苹果"只是一个字符串,一个由两个汉字组成的离散符号。计算机最擅长的是数学运算------加减乘除、矩阵乘法、梯度下降------但这些都要求输入是数字,而词不是数字。

这就引出了NLP最核心的挑战:如何把离散的词变成计算机可以处理的数学表示? 如果不能解决这个基础问题,后续所有的NLP技术------从简单的文本分类到复杂的Transformer大模型------都无从谈起。今天,小可和博士就带领大家踏上一段从整数编号到Word2Vec的进化之旅,看看科学家们是如何一步步破解这个难题的。


深入思考:什么是"离散符号"?

在计算机科学中,离散符号是指那些没有连续性、无法进行数学运算的标识符。比如"苹果"和"香蕉"是两个完全不同的字符串,它们之间没有中间状态,也不能说"苹果"加0.1等于某个词。这与数字完全不同------1和2之间有1.5、1.25、1.125等无穷多个值,而且1+1=2是有意义的数学运算。

神经网络------包括我们现在用ChatGPT时接触到的大语言模型------本质上是做数学运算的机器。它们的每一层都在做矩阵乘法、激活函数计算、梯度下降等数学操作。所以输入必须是数字(张量),而不是文本字符串。这就是为什么我们需要Embedding层作为"翻译器":把离散的文本符号翻译成连续的数学向量。


小可(歪着脑袋):那给每个词编个号不就行了?苹果=1,香蕉=2,汽车=3......

博士(摇头摆手):不行不行!模型会以为火车(4)比苹果(1)大,这是虚假的大小关系!

给词编号看起来是最直观的办法。我们建立一个词表,给每个词分配一个唯一的整数ID:苹果→1,香蕉→2,汽车→3,火车→4。当模型看到数字1,就去查表找到对应的词。这个方案叫整数编码(Integer Encoding),它确实在一些简单的场景中被使用,比如Keras的Tokenizer就提供了这种功能。

但整数编码有一个致命的缺陷:它引入了虚假的数学关系。在数学上,4 > 1,3 - 1 = 2,这些运算对整数来说是有意义的。但把这些运算映射到词上就荒谬了------"火车大于苹果"?"汽车减去苹果等于香蕉"?这显然没有意义。

更严重的是,模型在训练过程中会"学习"到这些虚假的关系。如果使用整数编码的词作为神经网络的输入,模型会隐式地学到:词ID越大,某种"程度"就越高。但词之间根本不存在这种程度关系!苹果和香蕉之间没有谁"大"谁"小",它们只是不同的水果而已。

所以整数编码虽然简单,但它给模型注入了错误的先验知识,导致模型学到错误的模式。我们需要一种更好的方法。


补充知识:什么时候可以用整数编码?

整数编码并非完全没有用处。在某些特定场景下,它是合适的:

  1. 序列编号任务:比如给句子中的词编号位置(第1个词、第2个词......),这里的数字确实有大小意义
  2. 类别编号但后续做Embedding:Keras等框架中,先把文本转为整数序列,然后通过Embedding层转为向量。这里的整数编码只是中间步骤,不是最终表示
  3. 简单的词汇表映射:当你只需要给每个词一个唯一标识,且模型会学习自己的表示时

但关键是:整数编码不能直接作为神经网络的输入表示,必须经过Embedding层或One-Hot编码转换后才有意义。


小可(眼睛一亮):那就用One-Hot编码!每个词一个位置是1,其余全是0!

博士(点头):对,这样消除了假顺序。但问题更大------维度太高、极度稀疏、所有词都正交!

One-Hot编码是机器学习中最经典的表示方法之一。它的思路很巧妙:假设有V个不同的词,我们就创建一个V维的向量。每个词对应向量中的一个维度,在那个维度上是1,其余维度全是0。

比如我们的词表是{苹果, 香蕉, 汽车, 火车},那么:

  • 苹果 = 1, 0, 0, 0
  • 香蕉 = 0, 1, 0, 0
  • 汽车 = 0, 0, 1, 0
  • 火车 = 0, 0, 0, 1

One-Hot编码确实解决了整数编码的问题:不再有虚假的大小关系了。1和0之间没有"谁大谁小"的含义,每个词都在自己的维度上"发光",其他维度保持沉默。

但One-Hot编码带来了三个更严重的问题:

第一,维度灾难。 现实中的词表不是4个词,而是几十万甚至上百万个词!英文维基百科的词表大约有200万,中文的更大。这意味着每个词的向量都有百万维度------这太浪费存储空间和计算资源了。

第二,极度稀疏。 百万维的向量中,只有一个位置是1,其余999,999个位置都是0。信息密度低得可怜,绝大部分存储空间都在浪费。

第三,所有词都正交。 这是最致命的问题。任意两个不同的One-Hot向量做点积(内积),结果永远是0。这意味着在One-Hot表示下,"苹果"和"香蕉"的距离,与"苹果"和"汽车"的距离完全一样!但从语义上来说,苹果和香蕉显然更接近------它们都是水果,都可以吃,都有甜味。One-Hot编码完全无法表达这种语义上的远近关系。


小可(困惑地挠头):正交是什么意思?所有词的余弦相似度都是0?

博士(拿出粉笔在黑板上画起来):对!夹角90度,cos(θ)=0,完全没关系。但苹果和香蕉明明更接近啊!

让我们来认真理解一下"正交"这个概念。在向量空间中,两个向量的关系可以用它们的夹角来衡量。夹角的余弦值(cosine)就是我们常说的余弦相似度

cosine_similarity(A,B)=A⋅B∣∣A∣∣×∣∣B∣∣=cos⁡(θ)\text{cosine\_similarity}(A, B) = \frac{A \cdot B}{||A|| \times ||B||} = \cos(\theta)cosine_similarity(A,B)=∣∣A∣∣×∣∣B∣∣A⋅B=cos(θ)

当两个向量完全相同时,夹角为0°,cos(0°) = 1,相似度为1。当两个向量完全相反时,夹角为180°,cos(180°) = -1,相似度为-1。当两个向量正交时,夹角为90°,cos(90°) = 0,相似度为0。

在One-Hot编码中,任意两个不同的词向量,它们非零的维度不同,所以点积永远是0,余弦相似度永远是0。这意味着在One-Hot的世界里,所有的词都是毫无关系的------苹果和香蕉的关系,与苹果和汽车的关系,完全一样。

但从人类的直觉来看,这显然是不对的。苹果和香蕉都是水果,都能吃,都有甜味,颜色鲜艳------它们在语义上非常接近。而苹果和汽车之间,除了都是名词之外,几乎没有任何共同点。一个好的词表示方法应该能反映出这种差异。


小可(兴奋地跳起来):那怎么才能让计算机理解词之间的关系呢?

博士 (微笑):看上下文!相似的词,上下文也很相似。这就是分布式假设(Distributional Hypothesis)

英国语言学家J.R. Firth在1957年说过一句名言:

"You shall know a word by the company it keeps."

(你可以通过一个词的"朋友圈"来认识它。)

这就是分布式假设 的核心思想:一个词的含义,由它经常出现的上下文决定。 如果两个词经常出现在相似的上下文环境中,那么它们的含义也应该相似。

让我们用简单的例子来说明:

  • "我吃了一个苹果"
  • "我吃了一个香蕉"

这两句话的结构几乎一模一样,"苹果"和"香蕉"出现在完全相同的句法位置------都是"吃"的宾语,都在"一个"之后。这说明它们在语法功能上是等价的。

再看:

  • "苹果很甜"
  • "香蕉很甜"

同样,"苹果"和"香蕉"都能和"很甜"搭配,说明它们在语义属性上也很接近------都是甜的、都是可以食用的。

而"汽车"这个词,它常出现的上下文完全不同:

  • "这辆汽车很漂亮"
  • "汽车的轮毂非常亮"

"汽车"和"漂亮""轮毂""行驶"等词经常一起出现,这些是"苹果"和"香蕉"几乎不会搭配的。

通过观察大量文本中词的共现情况,我们就能发现:苹果和香蕉的上下文分布非常相似,而它们与汽车的上下文分布差异很大。这种上下文分布的相似性,就是语义相似性的可靠指标。


小可(认真地做笔记):怎么统计上下文关系呢?

博士 :用共现矩阵(Co-occurrence Matrix)!统计每对词在同一窗口中出现的次数。

共现矩阵是一个经典的统计学方法。它的做法是:给定一个窗口大小(比如前后各2个词),遍历整个语料库,统计每对词在同一个窗口中共同出现的次数。

构建一个共现矩阵,行和列都是词表中的所有词,矩阵中的每个元素Mij表示词i和词j在窗口中共同出现的次数。

以苹果为例,统计它和各个词的共现次数:

  • 苹果 ↔ 吃:12次
  • 苹果 ↔ 甜:8次
  • 苹果 ↔ 香蕉:5次
  • 苹果 ↔ 汽车:0次
  • 苹果 ↔ 发动机:0次

而火车和各个词的共现:

  • 火车 ↔ 铁轨:15次
  • 火车 ↔ 发动机:10次
  • 火车 ↔ 道路:12次
  • 火车 ↔ 苹果:0次
  • 火车 ↔ 甜:0次

从这个共现矩阵中,我们可以提取出每个词的"共现向量"------它和所有其他词的共现次数组成的向量。苹果和香蕉的共现向量非常相似(都和"吃""甜"共现频繁),而苹果和火车的共现向量差异巨大。

这样,词不再是孤立的符号,而是由它和整个语言环境中其他词的关系来定义的。每个词的含义,就是它和所有其他词的关系的总和。

但共现矩阵也有严重的问题。首先是规模问题 ------矩阵大小是V×V,V是词表大小。如果词表有100万,矩阵就有1万亿个元素!其次是泛化能力差 ------"apple"和"苹果"如果都出现在语料中,它们会被当成完全不同的词,各自有独立的行,无法被合并。最后是数据稀疏------绝大多数词对的共现次数是0或很小的数字,统计噪声很大。


小可(翻着自己的笔记):这几种方法各有什么优缺点?

博士:让我们做一个完整的对比。整数编号→One-Hot→共现矩阵→Embedding,这是一个一步步进化的过程!

让我们用表格来系统地对比这四种方法:

方法 表示形式 维度 语义关系 主要问题
整数编号 1, 2, 3, 4 1 ✗ 虚假大小关系 引入虚假顺序
One-Hot 1,0,0,0 V ✗ 全部正交 维度灾难+无语义
共现矩阵 12,10,3... V ✓ 有统计语义 O(V²)+稀疏+无法泛化
Embedding 0.31,-0.82... d (d≪V) ✓ 密集语义 需要训练

这个进化路径非常清晰:

  1. 整数编号解决了"把词变成数字"的问题,但引入了虚假的数学关系
  2. One-Hot编码消除了虚假关系,但维度太高且完全没有语义
  3. 共现矩阵引入了语义关系(通过统计上下文),但规模太大且无法泛化
  4. Embedding最终解决了所有问题:低维、密集、可学习、保留语义

每一步都在前一步的基础上解决了一个核心问题,同时又引入了新的挑战。这就是科学进步的经典模式。


小可(满脸期待):Embedding到底是什么?就是把词变成向量?

博士 (竖起大拇指):对!Embedding就是把离散词嵌入(embed)到连续的向量空间中,让词变成可以计算的坐标!

Embedding这个词的英文原意就是"嵌入"------把一些原本离散、孤立的东西,放进一个连续的空间中,让它们有坐标、有距离、有方向。

在NLP中,Embedding层的作用是把每个词映射为一个固定维度的实数向量。假设我们选择100维,那么每个词都被表示为一个包含100个实数的向量,比如:

  • 苹果 → 0.31, -0.82, 0.15, 0.47, ...(100个数字)
  • 香蕉 → 0.28, -0.79, 0.22, 0.43, ...
  • 汽车 → -0.65, 0.41, -0.33, 0.18, ...
  • 火车 → -0.62, 0.38, -0.28, 0.15, ...

这些数字不是人工指定的,而是通过训练自动学习到的。训练过程中,模型不断调整这些数字,使得语义相近的词在向量空间中靠得更近,语义不同的词离得更远。

Embedding的关键性质:

  1. 低维:通常100~300维,远小于词表大小(百万级)
  2. 密集:每个维度都是非零的实数,信息密度高
  3. 可学习:通过梯度下降自动优化,不需要人工设计
  4. 语义保留:相似的词在空间中距离近,可以用余弦相似度衡量

从本质上说,Embedding层是离散符号空间和连续向量空间之间的桥梁。它把人类语言中离散的、不可计算的词,变成了数学上连续的、可计算的向量。这就是为什么Embedding被称为"现代AI的通用接口"------任何离散对象(文字、图片、用户、商品......)都可以通过Embedding变成连续的表示,然后交给神经网络处理。


小可(惊叹地睁大眼睛):向量空间里,语义关系变成了几何关系?

博士(满意地点头):完全正确!相似的词在空间中靠得近,可以用余弦相似度来衡量距离!

这是Embedding最迷人的特性之一:语义相似性被编码为几何距离

想象一个二维平面(实际上Embedding空间是100~300维的,但二维方便理解)。在这个平面上:

  • 苹果、香蕉、梨、橘子聚在一起,形成"水果簇"
  • 汽车、火车、飞机聚在一起,形成"车辆簇"
  • 两个簇之间有明显的距离

两个词之间的距离越近,说明它们的语义越相似。我们可以用余弦相似度来精确量化这种相似性:

  • cosine(苹果, 香蕉) ≈ 0.85 --- 非常相似
  • cosine(苹果, 汽车) ≈ 0.12 --- 几乎不相似
  • cosine(汽车, 火车) ≈ 0.78 --- 比较相似

这种几何化的语义表示有很多强大的应用。比如我们可以做词类比推理:找出与C的关系类似于A与B的关系的词D。最著名的例子就是"国王-男人+女人=女王"------这在Word2Vec的论文中被广泛引用,展示了词向量不仅能编码语义,还能编码语法关系。


小可(兴奋地拍手):Word2Vec为什么这么厉害?

博士(激动地站起来):因为它不再构建完整语言模型,目标就是学词向量!训练快、效果好,震惊了整个NLP领域!

在Word2Vec出现之前(2013年),获取高质量词向量主要有两条路:

第一条路:共现矩阵 + SVD降维。 先构建庞大的共现矩阵,然后用奇异值分解(SVD)等矩阵分解方法降维。这种方法的问题在于:构建共现矩阵需要遍历整个语料库,SVD的计算复杂度很高,而且降维过程会有信息损失。

第二条路:神经语言模型。 Bengio等人在2003年提出了神经概率语言模型,在预测下一个词的过程中顺便学到了词向量。但这种方法的问题是:它的核心目标是语言建模(预测下一个词),词向量只是训练的"副产品"。为了得到词向量,你需要训练一个完整的语言模型------这太昂贵了!

Word2Vec的革命性在于:它把目标直接定为学习词向量本身。 不再需要完整的语言模型,不再需要庞大的共现矩阵。它用一个极简的神经网络架构------只有一层隐藏层------就能高效地学习到高质量的词向量。

Word2Vec提供了两种经典的训练方式:

  1. CBOW(Continuous Bag of Words):用上下文预测中心词
  2. Skip-gram:用中心词预测上下文

这两种方式都极其简单高效,在大规模语料上训练只需几小时,就能得到质量极高的词向量。Word2Vec的论文《Efficient Estimation of Word Representations in Vector Space》由Tomas Mikolov等人发表于2013年,至今已被引用数万次,是NLP领域最有影响力的论文之一。


小可(震惊地张大嘴巴):vec(king) - vec(man) + vec(woman) ≈ vec(queen)?向量居然能做加减运算?

博士(自豪地微笑):对!词与词之间的关系变成了向量空间中的方向!语义和语法都被编码在向量中了!

这是Word2Vec最令人震惊的发现之一。训练好的词向量不仅能表达语义相似度,还能表达语义关系------而且这种关系可以用简单的向量加减法来操作!

最经典的例子:

king⃗−man⃗+woman⃗≈queen⃗\vec{\text{king}} - \vec{\text{man}} + \vec{\text{woman}} \approx \vec{\text{queen}}king −man +woman ≈queen

这个等式的含义是:从"国王"的向量中减去"男人"的向量(去掉"男性君主"中的"男性"属性),再加上"女人"的向量(添加"女性"属性),得到的结果非常接近"女王"的向量。

这意味着什么呢?意味着向量空间中的方向和距离编码了语义关系。"男人→女人"这个语义变化,对应着向量空间中的一个特定方向。把这个方向应用到"国王"上,就得到了"女王"。

这种类比推理在词向量空间中大量存在:

类比 语义关系
北京 - 中国 + 法国 ≈ 巴黎 首都关系
走 - 现在 + 过去 ≈ 走了 时态关系
大 - 好 + 坏 ≈ 小 反义关系
猫 - 公 + 母 ≈ 母猫 性别关系

这些例子展示了词向量不仅编码了词的"含义",还编码了词与词之间的"关系"。这是传统方法(One-Hot、共现矩阵)完全无法做到的。


小可(好奇地凑近黑板):CBOW是怎么工作的?用上下文猜中心词?

博士(耐心讲解):对!取上下文词向量求平均,用简单神经网络预测中心词。

CBOW(Continuous Bag of Words)的工作原理如下:

给定一个句子:"我今天想吃苹果",设定窗口大小为2。当中心词是"想"时,它的上下文是左右各2个词:"今天", "我", "吃", "苹果"

CBOW的训练步骤:

  1. 取上下文词向量:从Embedding矩阵中查找"今天""我""吃""苹果"对应的词向量
  2. 求平均:将这4个词向量相加后除以4,得到一个综合的上下文表示
  3. 前向传播:将这个平均向量输入到一个简单的神经网络(输入层→隐藏层→输出层)
  4. 预测:输出层使用softmax,预测中心词是什么
  5. 计算损失:用交叉熵损失比较预测结果和真实的中心词("想")
  6. 反向传播:计算梯度,更新所有词向量

整个过程非常简洁。神经网络的结构只有三层:输入层(词向量查表)、隐藏层(线性投影)、输出层(softmax分类)。没有复杂的激活函数,没有深层网络,就是最简单的MLP。

正是因为结构简单,CBOW的训练速度极快。而且因为它是用多个上下文词来预测一个中心词,所以对高频词的建模效果特别好------上下文词的平均操作起到了平滑作用。


小可(恍然大悟):Skip-gram和CBOW正好相反?用中心词猜周围词?

博士(竖起食指):对!一个中心词拆成多个训练样本,对低频词更友好!

Skip-gram几乎是CBOW的反方向:

CBOW是:上下文词1, 上下文词2, 上下文词3, 上下文词4 → 预测中心词

Skip-gram是:中心词 → 预测上下文词1, 预测上下文词2, 预测上下文词3, 预测上下文词4

同样以"我今天想吃苹果"为例,窗口大小为2,中心词是"想":

  • CBOW产生1个训练样本:("今天","我","吃","苹果", "想")
  • Skip-gram产生4个训练样本:("想","今天"), ("想","我"), ("想","吃"), ("想","苹果")

这意味着Skip-gram的训练样本数量是CBOW的窗口大小倍。窗口为2时是4倍,窗口为5时是10倍!

这对低频词非常友好。即使一个词在语料中只出现了一次,Skip-gram也能通过它产生多个训练样本(每个上下文词一个),从而让这个低频词的向量得到充分的训练。而CBOW中,低频词作为中心词出现时只有一个样本,作为上下文词时又被平均掉了,训练效果不如Skip-gram。

Skip-gram的训练过程:

  1. 取中心词的向量
  2. 分别预测每个上下文词(每个上下文词是一个独立的softmax分类任务)
  3. 计算每个预测的交叉熵损失
  4. 反向传播更新词向量

小可(歪着脑袋思考):两种方法该怎么选?

博士(推了推眼镜):高频词多用CBOW,低频词多用Skip-gram,根据数据特征决定。

CBOW和Skip-gram各有优劣,选择时需要考虑以下因素:

选择CBOW的场景:

  • 数据集很大,词频分布相对均匀
  • 更关注训练速度
  • 语料中低频词不多
  • 需要快速得到一个可用的词向量模型

选择Skip-gram的场景:

  • 数据集较小
  • 语料中有大量低频词、专有名词
  • 更关注词向量质量(特别是低频词的表示)
  • 不介意多花一些训练时间

在实际应用中,gensim的Word2Vec实现提供了一个sg参数:

  • sg=0:使用CBOW
  • sg=1:使用Skip-gram

大多数情况下,如果你不确定选哪个,用Skip-gram(sg=1)通常能得到更好的结果,特别是在中文这种低频词较多的语言中。


小可(搓搓手):训练Word2Vec需要什么步骤?

博士(拿出清单):读取文本→分词→配置参数→训练→验证结果,五步搞定!

使用gensim训练Word2Vec模型非常简单,主要分为五个步骤:

第一步:读取文本

python 复制代码
text = open('西游记.txt', encoding='utf-8').read()

第二步:分词

python 复制代码
import jieba
sentences = [list(jieba.cut(s)) for s in text.split('。')]

第三步:配置参数并训练

python 复制代码
from gensim.models import Word2Vec
model = Word2Vec(
    sentences,          # 分词后的句子列表
    vector_size=100,    # 词向量维度
    window=5,           # 上下文窗口大小
    min_count=3,        # 最低词频
    sg=1,               # 1=Skip-gram, 0=CBOW
    epochs=10,          # 训练轮数
    negative=5,         # 负采样数量
)

第四步:验证结果

python 复制代码
# 查看最相似的词
model.wv.most_similar('孙悟空', topn=10)
# 计算两个词的相似度
model.wv.similarity('孙悟空', '唐僧')

第五步:保存模型

python 复制代码
model.save('word2vec.model')
# 后续可以直接加载使用
model = Word2Vec.load('word2vec.model')

关键参数说明:

  • vector_size:词向量维度,通常100~300
  • window:上下文窗口大小,通常3~10
  • min_count:过滤低频词的阈值
  • sg:选择CBOW(0)还是Skip-gram(1)
  • negative:负采样数量,通常5~20
  • epochs:训练轮数

小可(皱眉思考):分词质量会影响训练效果?

博士(严肃地点头):对!把「孙悟空」拆成「孙」「悟空」就没意义了,要保护专有名词!

分词是中文NLP中最关键的预处理步骤之一,对词向量训练的质量影响极大。

好的分词示例:

复制代码
孙悟空 / 唐僧 / 齐天大圣 / 三藏 / 沙悟净

坏的分词示例:

复制代码
孙 / 悟空 / 唐 / 僧 / 齐 / 天 / 大 / 圣

如果"孙悟空"被拆成"孙"和"悟空",那么模型学到的"孙"这个词会和"孙膑""孙中山""孙悟空"等等完全无关的词产生联系,而"悟空"又会和"领悟""空空如也"等词混淆。"孙悟空"作为一个完整的专有名词,它的语义意义完全丢失了。

jieba分词默认就能识别大部分常见的专有名词,但对于特殊的领域词汇,可以通过添加自定义词典来改进:

python 复制代码
jieba.add_word('齐天大圣')
jieba.add_word('金箍棒')
jieba.add_word('花果山')

顺便提一下,"Token"是更标准的说法。 我们常说的"词"其实不太准确。Token可以是一个字、一个词、甚至一个词的一部分(比如"review"可能被拆成"re"和"view")。现代大模型使用的BPE(Byte Pair Encoding)和WordPiece等Subword Tokenization方法,就是自动学习最优的Token切分方式。


小可(迫不及待地):训练完怎么检查模型有没有学到语义?

博士(打开终端):计算余弦相似度!孙悟空和唐僧0.17,和唐太宗只有0.05,很合理!

训练完成后,我们需要验证模型是否真的学到了有意义的语义关系。有几种方法:

方法一:most_similar --- 检查近义词质量

python 复制代码
model.wv.most_similar('孙悟空', topn=5)
# 可能输出: [('齐天大圣', 0.89), ('沙悟净', 0.76), ('猪悟能', 0.72), ...]

如果结果符合直觉(和孙悟空最相似的确实是西游记中的人物),说明语义编码是合理的。

方法二:计算特定词的相似度

python 复制代码
model.wv.similarity('孙悟空', '唐僧')    # 0.17 --- 同团队,有一定关联
model.wv.similarity('孙悟空', '唐太宗')   # 0.05 --- 几乎无关

这些结果看起来很合理:孙悟空和唐僧是同团队的,有一定的上下文关联;而唐太宗虽然也是"唐"字开头,但在西游记中和孙悟空几乎没有交集,相似度很低。

方法三:词类比测试

python 复制代码
model.wv.most_similar(positive=['北京', '法国'], negative=['中国'], topn=1)
# 如果输出是'巴黎',说明模型学到了首都关系

需要注意的是,如果训练语料较小(比如只用了一本西游记)、训练轮数较少(比如只有10轮),结果可能不够精确。实际应用中,通常需要更大的语料(维基百科、新闻语料等)和更多的训练轮数(50~100轮)。


小可(看着屏幕上密密麻麻的数字):100个数字,每个是什么意思?

博士(摇头):单个数字没有独立含义,只有100个合起来才有意义,类似PCA!

这是理解词向量时最常见的困惑。当我们看到孙悟空的100维向量:

复制代码
[0.31, -0.82, 0.15, 0.47, -0.63, 0.91, -0.22, 0.08, ...]

人们很自然地会想问:"第3维的0.15是什么意思?代表'战斗力'吗?第6维的0.91代表'知名度'吗?"

答案是:单个维度没有可解释的含义。

这和我们熟悉的One-Hot编码完全不同。在One-Hot中,第3维是1就代表"这个词是词表中的第3个词",意义很明确。但在Embedding中,没有任何一个维度对应某个具体的特征。

这其实和PCA(主成分分析)很像。PCA降维后的每个主成分都是原始特征的线性组合,单个主成分的含义很难解释,但所有主成分合起来就能很好地表示数据。

词向量也是如此:

  • 每个维度本身没有独立可解释的含义
  • 单个数字(比如0.31)不代表某个具体特征
  • 只有整个100维向量合起来才有意义
  • 语义关系隐藏在整体向量模式中

词向量是通过训练自动学习到的分布式表示。训练过程中,100个数字被同时优化,使得相似词的向量在空间中靠近。每个维度的值是多少不重要,重要的是它们组合起来形成的"方向"和"位置"。


小可(双手一摊):100维超出人的理解范围了,怎么展示?

博士(拿出一张图):用PCA降到2维画在图上!相似的词会聚在一起!

人类最多只能理解三维空间,100维的词向量远远超出了我们的直觉范围。但我们可以通过降维技术把高维向量投影到二维平面上,从而直观地看到词之间的远近关系。

最常用的方法是PCA(主成分分析)

python 复制代码
from sklearn.decomposition import PCA
import numpy as np

# 获取所有词的向量
words = list(model.wv.key_to_index.keys())
vectors = np.array([model.wv[w] for w in words])

# PCA降到2维
pca = PCA(n_components=2)
reduced = pca.fit_transform(vectors)

# 画图
import matplotlib.pyplot as plt
for i, word in enumerate(words):
    plt.scatter(reduced[i, 0], reduced[i, 1])
    plt.annotate(word, (reduced[i, 0], reduced[i, 1]))
plt.show()

在2D图上,我们可以看到:

  • 神仙类的词(如来、玉帝、观音、菩萨)聚在一起
  • 取经团的词(唐僧、行者、三藏、沙僧)聚在一起
  • 妖怪类的词(妖精、魔王、小妖)聚在一起
  • 地名类的词(花果山、火焰山、龙宫)聚在一起

不同类别的簇之间有明显的距离,同一类簇内的词彼此靠近。即使PCA从100维降到2维有大量信息损失,这种聚类结构仍然清晰可见。

更先进的降维方法如t-SNEUMAP能更好地保持高维空间中的局部结构,可视化效果通常比PCA更好。


小可(看着散点图惊叹):在语义空间里,词之间有什么关系?

博士(指着图):水果类聚在一起,车辆类聚在一起,不同类之间有明显的距离!

降维可视化最直观地展示了语义空间的本质特征:

1. 同类词聚类

语义相近的词会自动聚集成簇。在我们的西游记语料中:

  • 菩萨、如来佛祖、玉帝、观音聚在一起(神仙类)
  • 孙悟空、东海、花果山、火焰山、龙宫、金箍棒聚在一起(相关事物)
  • 妖怪、妖精、魔王、大王、小妖聚在一起(反派类)
  • 沙僧、八戒、师傅、唐僧、行者、三藏聚在一起(取经团队)

2. 不同类之间有距离

不同语义类别的簇之间有明显的距离。神仙类和妖怪类相距甚远,取经团队类和相关事物类距离较近(因为在故事中经常一起出现)。

3. 语义关系被编码为向量关系

更重要的是,不仅是"相似"被编码,"关系"也被编码了。"孙悟空"到"齐天大圣"的向量方向,可能和"猪八戒"到"猪悟能"的向量方向相似------因为都是"本名→法号"的关系。

这种结构化的语义空间,是之前所有方法(整数编号、One-Hot、共现矩阵)都无法实现的。它不是手工设计的规则,而是从数据中自动学习到的。


小可(好奇地歪着头):训练这么快,有什么技巧?

博士 (竖起大拇指):负采样(Negative Sampling)!只计算正样本+少量负样本,从O(V)降到O(k),提速数百倍!

Word2Vec训练速度极快的一个关键原因是使用了**负采样(Negative Sampling)**技术。

在没有负采样的情况下,每次训练都需要对整个词表计算softmax概率:

P(wt∣wcontext)=exp⁡(vwt⋅vcontext)∑w∈Vexp⁡(vw⋅vcontext)P(w_t | w_{context}) = \frac{\exp(v_{w_t} \cdot v_{context})}{\sum_{w \in V} \exp(v_w \cdot v_{context})}P(wt∣wcontext)=∑w∈Vexp(vw⋅vcontext)exp(vwt⋅vcontext)

分母需要对词表中所有V个词计算,当V = 10万时,每次训练要做10万次指数运算------这太慢了!

负采样的思路极其巧妙:把多分类问题转化为二分类问题。

对于每个训练样本(中心词, 真实上下文词),我们:

  1. 构造正样本:(中心词, 真实上下文词) → 标签为1
  2. 构造负样本:随机选取k个词作为"假"的上下文词 → 标签为0

比如,真实的上下文词是"苹果",我们随机采样5个词作为负样本(如"轨道""发动机""轨道""法律""哲学"),那么训练目标就变成了:

  • 让模型判断"苹果"确实是上下文(输出接近1)
  • 让模型判断"轨道""发动机"等不是上下文(输出接近0)

每次只需要计算1个正样本 + k个负样本 = k+1次sigmoid运算,而不是V次softmax运算。当k=5,V=10万时,计算量从10万降到6,提速超过16000倍!

负采样的另一个关键是采样策略:不是均匀随机采样,而是按词频的3/4次方来采样。这样高频词不会被过度采样,低频词有足够的机会被选为负样本。


小可(若有所思):有些词出现特别多,有些特别少,怎么办?

博士(认真地说):高频词会主导训练,需要降采样;低频词需要更多关注。

在自然语言中,词频分布遵循Zipf定律:少数词出现极其频繁,大多数词很少出现。

在语料中,"的""了""是""在"这样的虚词可能各出现数十万次,而专有名词如"红孩儿""白骨精"可能只出现几十次。这种极端的词频不平衡给训练带来了两个问题:

问题一:高频词主导训练

"的""了"这些词信息量很低(几乎每个句子都有),但它们的训练样本数量极多,会主导梯度更新,导致模型花大量时间在无意义的词上。

解决方案:降采样(Subsampling)

Word2Vec使用以下概率公式随机丢弃高频词:

P(discardw)=1−tf(w)P(\text{discard}_w) = 1 - \sqrt{\frac{t}{f(w)}}P(discardw)=1−f(w)t

其中t是阈值(通常设为1e-5),f(w)是词w的频率。对于频率极高的词,被丢弃的概率就大。这样既保留了足够的训练样本,又减少了高频词的负面影响。

问题二:低频词训练不充分

专有名词、生僻词出现次数少,向量的学习不够充分。

解决方案:Skip-gram + 降低min_count

Skip-gram每个中心词产生多个训练样本,对低频词更友好。同时可以降低min_count参数(比如从5降到2),让更多低频词进入词表。


小可(翻着评测报告):怎么评价训练出来的词向量好不好?

博士:看三个维度:most_similar结果、类比准确率、降维可视化!

评估词向量质量有三个层次:

层次一:定性检查(most_similar)

python 复制代码
model.wv.most_similar('美丽', topn=10)
# 期望输出: 漂亮, 好看, 俊美, 优美, 秀丽...

如果输出符合直觉,说明语义编码基本到位。这是最简单也最直观的检查方法。

层次二:定量评测(类比准确率)

使用标准的词类比评测集,如Google Analogy Dataset(包含19550条语义和语法类比题):

python 复制代码
# 语义类比: 巴黎是法国的首都,东京是__的首都?
model.wv.most_similar(positive=['东京', '法国'], negative=['巴黎'], topn=1)

计算模型在语义类比(如"首都关系")和语法类比(如"时态关系")上的准确率,准确率越高,词向量质量越好。

层次三:降维可视化

用PCA/t-SNE将词向量降到2维,在图上直观检查同类词是否聚在一起,不同类是否有明显距离。这是定性检查的可视化工具。

层次四:下游任务测试

最终的检验标准是:用这些词向量作为特征,在下游NLP任务(文本分类、命名实体识别、情感分析等)上的表现如何。好词向量应该能提升下游任务的性能。

此外还有标准的词相似度评测集,如WordSim-353(353对词的相似度标注)和SimLex-999(999对词的语义相似度标注),可以计算模型预测的相似度与人工标注的相关系数。


小可(激动地转圈):Word2Vec训练出来的词向量有什么用?

博士(掰着手指头数):推荐系统、搜索引擎、文本分类、情感分析、机器翻译......用途太多了!

Word2Vec词向量的应用极其广泛,几乎涵盖了所有需要"理解"文本的场景:

推荐系统:

  • 将用户和商品都embedding到同一向量空间
  • 计算用户向量和商品向量的相似度来推荐
  • Item-to-Item推荐:买了A商品的用户也买了B,本质就是item embedding的相似度

搜索引擎:

  • 查询词和文档都用词向量表示
  • 通过向量相似度匹配查询和文档
  • 解决词汇不匹配问题:搜"手机"也能找到包含"智能手机"的文档

文本分类:

  • 将文档中所有词的向量取平均(或加权平均),得到文档向量
  • 用文档向量训练分类器(SVM、神经网络等)
  • 比传统的TF-IDF + 分类器效果更好

情感分析:

  • "好"和"坏"的词向量方向相反
  • 通过词向量的组合可以推断文本的情感倾向
  • 对未知词汇的情感极性也能做出合理推测

机器翻译:

  • 跨语言词向量对齐:将英文和中文的词向量映射到同一空间
  • "apple"和"苹果"的向量在跨语言空间中应该很近
  • 为神经机器翻译提供初始词向量

RAG(检索增强生成):

  • 向量相似度召回是RAG系统的核心之一
  • 先将文档分词、embedding
  • 查询时也embedding,然后计算相似度召回最相关的文档
  • 这就是为什么分词质量对RAG很重要

更广泛的应用:

  • 图像:图片也可以embedding到向量空间(Image Embedding)
  • 生物信息学:氨基酸序列embedding
  • 音乐:音符序列embedding
  • 任何离散对象都可以embedding到连续空间!

小可(双手合十,满眼崇拜):今天学到了很多!从离散符号到语义向量空间!

博士(欣慰地笑):完美!从Word2Vec到Transformer,Embedding思想贯穿了整个NLP的发展史!

让我们回顾一下今天学到的核心内容:

📚 词表示方法的进化之路

  1. 整数编号 --- 把词变成数字,但引入了虚假的大小关系
  2. One-Hot编码 --- 消除了虚假关系,但维度太高、全部正交、无语义
  3. 共现矩阵 --- 引入了基于统计的语义,但规模太大、稀疏、无法泛化
  4. Word2Vec (Embedding) --- 低维、密集、可学习、保留语义的终极方案

🔑 Word2Vec的核心要点

  • 思想:通过预测上下文的轻量任务来学习词向量
  • CBOW:用上下文预测中心词,对高频词友好,训练快
  • Skip-gram:用中心词预测上下文,对低频词友好,质量好
  • 负采样:从O(V)降到O(k),训练加速数百倍
  • 结果:语义和语法关系都被编码在向量空间中

🌟 Embedding的深远影响

Word2Vec只是一个开始。它开创的Embedding思想贯穿了整个AI发展史:

  • GloVe:结合全局统计信息和局部上下文
  • FastText:处理词内结构和未登录词
  • BERT:上下文相关的动态词向量
  • GPT系列:从词级别到Token级别的全面Embedding
  • 多模态Embedding:文字、图片、音频统一向量空间

从Word2Vec到Transformer,从静态词向量到动态上下文表示,Embedding的核心思想从未改变:

离散对象 → 连续向量空间 → 可计算、可学习、可比较

这是所有现代AI模型的基石。无论是ChatGPT、Claude、还是Gemini,它们的第一层都是Embedding------把输入的文字变成向量。没有Embedding,就没有深度学习,就没有大语言模型,就没有今天的AI革命。


小可(合上笔记本):博士,下次我们学什么?

博士(神秘一笑):RNN?LSTM?还是直接上Transformer?敬请期待!


🐱 小可 & 🐶博士的科普漫画,用轻松的方式讲硬核的知识。如果觉得有用,欢迎分享给更多人!

学习建议

  1. 动手用gensim训练一个Word2Vec模型,试试西游记、红楼梦等语料
  2. 用t-SNE可视化你的词向量,观察聚类效果
  3. 尝试词类比推理,看看模型学到了哪些有趣的语义关系
  4. 对比CBOW和Skip-gram的结果差异
  5. 探索负采样数量对训练速度和词向量质量的影响
相关推荐
大龄码农有梦想6 小时前
Codex、Claude Code 等 AI 编程工具对软件工程的启发
人工智能·软件工程·agent·ai编程·ai agent·智能体·智能体平台
风痕天际6 小时前
Pytorch开发教程1——CUDA安装
人工智能·pytorch·python
心运软件6 小时前
基于深度学习的IMDB电影评论情感分析完整实现
人工智能·pytorch·深度学习·数据分析
xian_wwq6 小时前
【学习笔记】什么是Harness Engineering- 01/15
人工智能·笔记·学习
老猿AI洞察6 小时前
三万Token的底牌:Claude Opus 5提示词泄露,揭开AI“灵魂“的每一道锁
人工智能·ai
俊哥V6 小时前
每日 AI 研究简报 · 2026-07-26
人工智能·ai
延凡科技6 小时前
告别粗放灌溉!让灌区管理智能化、节水化、精细化
大数据·人工智能·科技·物联网·信息可视化
魔镜er6 小时前
04-pytorch构建线性回归
人工智能·pytorch·线性回归
Summer-Bright6 小时前
AI 芯片简报 07.21-07.24:NVIDIA Vera 亮剑、AMD 2nm GPU、Google 叛逃 CoWoS
大数据·人工智能·ai·自然语言处理·芯片·agi
程序员果子6 小时前
CrewAI :当 Agent 学会团队协作
人工智能·git·python·多智能体·agent框架