================================================================================
NLP 词向量与句向量 完整笔记
================================================================================

一、NLP 概述与发展


深度学习的核配
应用上:NLP 的词向量与句向量
Manus 作简历
开发者社区:Hugging Face

实际解决问题的思路:任务与所学知识相结合
任务
├─ 分类
└─ 回归

⇒ 数据
├─ 有label
└─ 无label

数据周期长不长 → 长 → deep learning

NLP → 语义理解的突破 → 是 AI 的里程碑
符号计算 → 语义计算的转换
经典例子:king - man + woman → queen(女王)
应用价值:主要和文本打交道

NLP 发展阶段:
1950 年:基于规则的系统
依赖人工编码
缺乏理解能力

2000 年前 NLP:
泛化能力差
语义缺失

2010 后 → 深度学习
深度学习
├─ 词袋model
└─ 向量

[深度补充]
Hugging Face 是目前全球最大的 NLP 开源社区,提供了海量预训练模型、数据集和工具库,是工业界 NLP 开发的事实标准。
数据周期判断:如果你的任务数据积累超过 10 万条,且持续有新数据产生,优先用深度学习;如果数据量小于 1 万条,优先用传统机器学习方法(如 SVM、逻辑回归)。

================================================================================

二、学习目标


理论基础
掌握词向量空间距离度量,语义组合性等数学原理
理解 word2Vec 的负采样优化等关键技术细节
建立完整的分布式表示知识框架
捕捉词之间的语义关系
Doc2Vec → 捕捉文档之间的关系

实践能力
主题建模,文件索引
可视化
以图像、表格展示出来

应用创新
NLP 怎么适配相应的推荐系统的

[深度补充]
语义组合性:指句子的语义可以由组成它的词的语义组合而成,这是词向量能扩展到句向量的理论基础。
Doc2Vec 是 Word2Vec 的扩展,专门用于生成文档级别的向量表示,解决了 Word2Vec 只能生成词向量的问题。

================================================================================

三、词向量基础


余弦相似度
cosθ = |A·B| / (|A|·|B|)

向量的方向和大小决定他们的相似性
不同角度的含义:
|A|·|B|·cosθ = 0 → θ=90° → 正交 → 语义无关
cosθ = 负值 → A与B不相似 ≈ 相反
θ=180°时,A与B完全相反
θ=30°,cosθ=√3/2 → A与B相似

余弦相似度几何示意图:

1
2
3
4
5
6
7
8
9
10
11
     y
|
| B
| /
| /
| / θ
| /
------+------> x
|
| A

词语表示的演变
初期
用字母序列和词典 ID 表示
缺点:不能捕捉语义关系
例子:apple和fruit无法建立关联;run和running无法建立关联

统计时代
统计时代
├─ one-hot → 二进制向量
└─ 词袋mode → 词频统计

one-hot 编码例子:
苹果:[0, 0, 0, 0, 1]
瓜:[0, 0, 0, 1, 0]
狗:[0, 0, 0, 0, 0]
通过编码看相似度

[深度补充]
词袋模型(Bag of Words):将文本看作是词的集合,不考虑词的顺序和语法结构,只统计每个词出现的次数。
one-hot 编码的本质是将词映射到正交空间,每个词都是一个独立的维度,因此任意两个词的内积都是 0。

================================================================================

四、分布式词向量与 One-Hot 局限性


现代:分布式词向量
分布式将词语映射到低维连续空间,通过向量距离反映语义相似度
例子:A ↔ B、cat ↔ dog
属于无监督学习

独热编码的局限性
用 0,1 表示对象

  1. 维度灾难
    比例表示2^6=64中的一个类,区别可能仅在最后几位,但前面的大部分占位都得写出来 [0, 0, …, 0, 1] → 计算资源会指数级增长
    例子:
    词汇表:[apple, fruit, computer, phone]

        1      2       3        4
    

    对应 one-hot 向量:
    apple: [1, 0, 0, 0]
    fruit: [0, 1, 0, 0]
    computer: [0, 0, 1, 0]
    phone: [0, 0, 0, 1]

  2. 语义隔离
    任意两个词向量点积恒为零,无法区分 “手机” 与 “电话” 的词义相似性

  3. 泛化缺陷
    出现新词必须扩展向量维度,无法用已有词向量推导表示
    例子:
    初始词汇表:[cat, dog]
    cat: [1, 0]
    dog: [0, 1]
    加入新词man后:
    词汇表:[man, cat, dog]
    man: [1, 0, 0]
    cat: [0, 1, 0]
    dog: [0, 0, 1]
    输入新词要重写编码。

[深度补充]
分布式表示的核心思想是:每个词的语义由它周围的词共同决定,因此相似的词会有相似的向量表示。
维度灾难的具体表现:10 万词的词汇表,one-hot 向量需要 10 万维,存储 100 万个词向量需要 400GB 的存储空间,完全无法工程化。

================================================================================

五、Word2Vec 两大核心架构


模型架构对比
设计
├─ CBOW → 通过上下文预测中心词,适合处理高频词汇
└─ Skip-gram → 通过中心词预测上下文,在低频词表现更好

效率
├─ CBOW训练速度比Skip-gram快2-3倍
└─ 但小数据集上Skip-gram能学到更丰富的语义表示

选择
├─ 通用NLP → 选CBOW
└─ 处理专业术语(罕见词)用Skip-gram架构

两大技术框架
CBOW(Continue-Bag of word)连续词袋 model
Skip-gram:中心词 → 上下文

例子:
Who ← jump → high

She really | hate you,但稀有词不好推断
love

[深度补充]
CBOW 适合高频词的原因:它是用多个上下文词预测一个中心词,高频词有更多的上下文样本,因此学习得更充分。
Skip-gram 适合低频词的原因:它是用一个中心词预测多个上下文词,即使是低频词,只要出现一次,就能产生多个训练样本。

================================================================================

六、CBOW 与 Skip-gram 模型详解


CBOW 模型详解
是三层神经网络:输入层 → 隐藏层 → 输出层
但不关心上下文的顺序

数学原理:
数学原理
├─ 交叉熵误差
└─ 最大似然函数

模型流程:
输入层:接收多个上下文词的 one-hot 向量
投影层:进行向量平均
输出层:用 Softmax 预测中心词
窗口设置:典型窗口大小为 5-15 个词

实践技巧:建议设初始学习率为η=0.025,采用线性衰减策略;配合层次 softmax 加速训练过程。

Skip-gram 模型详解
反向预测,输入单个中心词的 one-hot 向量,输出层同时预测多个上下文词的位置分布

优化技巧:
层次 softmax:哈夫曼树,将序列问题 → 构建分类
例子:[果, 梨, 瓜] → 梨 瓜 果
负采样:多分类变成二分类
本来是:中心词 → 上下文词
变成:下一个词是什么? → 词有上下文关系吗?(是/否)
子采样:动态丢弃高频词如 “是”“的” 这样的词,平衡词频分布

[深度补充]
CBOW 的隐藏层是对上下文词向量的平均,因此它对上下文词的顺序不敏感。
Skip-gram 的训练样本更多,因此在小数据集上能学到更丰富的语义表示。
负采样是目前工业界最常用的优化方法,因为它实现简单,训练速度快,效果好。

================================================================================

七、Softmax 函数与哈夫曼树


Softmax 函数
激活函数 → 非线性
映射过去 → 概率向量[0, 1]
Softmax = e^x / Σi e^xi

Softmax 函数图像:

1
2
3
4
5
6
7
8
9
10
11
12
      y
| 1
|_____
/|
/ |
/ |
--+---+----> x
0

x→∞, y→1
x→0, y→0

哈夫曼树
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Score → [不及格, 及格, 良好, 优秀]
[0,60] [60,70] [70,85] [85,100]
D C B A

哈夫曼树对比:
树1(深度=4):
root
/ \
Q<60? N
/ \
N Q<70?
/ \
N Q<85?
/ \
A B
总步数:Dx1, Cx2, Ax3, Bx3 → 共9步

树2(深度=3):
root
/ \
Q<70? Q<85?
/ \ / \
D C B A
总步数:Dx2, Cx2, Bx2, Ax2 → 共8步

这个树更好点,找最短路径
权重:1×w1 + 1×w2 + 1×w3

[深度补充]
Softmax 函数的作用是将任意实数向量映射为概率分布,所有元素的和为 1。
哈夫曼树是一种带权路径最短的二叉树,它让高频词的路径更短,从而减少 Softmax 的计算量。
层次 Softmax 的计算复杂度从O(V)降到了O(log V),其中V是词汇表大小。

================================================================================

八、GloVe 模型原理


GloVe model 原理(Global Vectors of word Representation)全局词向量
设计理念:融合全局词共现统计与局部上下文预测的优势

例子 2:
词汇:[ice, gas, water, steam]
共现概率:

  • [ice, gas] 小
  • [gas, steam] 大
  • [ice, water] 大
  • [ice, steam] 小

[深度补充]
GloVe 的核心思想是:词与词之间的共现概率比包含了丰富的语义信息。
例如,ice和water的共现概率比ice和gas的共现概率大很多,说明ice和water的语义更相似。
GloVe 在大多数通用任务上的效果略优于 Word2Vec,而且训练速度更快。

================================================================================

九、四大词向量模型对比


┌─────────────┬──────────────────┬──────────────┬──────────────────┬──────────────────┐
│ 模型 │ Word2Vec(CBOW) │ Skip-gram │ GloVe │ FastText │
├─────────────┼──────────────────┼──────────────┼──────────────────┼──────────────────┤
│ 思想 │ 上下文→中心词 │ 中心→上下文 │ 局部+全局信息 │ 子词 + 词向量 │
├─────────────┼──────────────────┼──────────────┼──────────────────┼──────────────────┤
│ 训练快慢 │ 较快 │ 慢 │ 极快 │ 快 │
├─────────────┼──────────────────┼──────────────┼──────────────────┼──────────────────┤
│ 内存消耗 │ 大 │ 大 │ 小 │ 小 │
├─────────────┼──────────────────┼──────────────┼──────────────────┼──────────────────┤
│ 语义捕捉 │ 60 分 │ 80 分 │ 70-90 分 │ 65-80 分 │
├─────────────┼──────────────────┼──────────────┼──────────────────┼──────────────────┤
│ 优势 │ 通用NLP、高频词 │ 低频词、罕见词│ 全局语义建模 │ 未登录词、文本分类│
└─────────────┴──────────────────┴──────────────┴──────────────────┴──────────────────┘

[深度补充]
FastText 的核心优势是能处理未登录词,它将词拆分成子词,即使一个词不在训练语料中,只要它的子词在,就能生成它的向量。
工业界选择指南:
通用任务、追求速度:GloVe
专业领域、罕见词多:Skip-gram + 负采样
有很多新词、拼写纠错:FastText

================================================================================

十、词向量评估与可视化


词向量评估
词向量(75%-85% 以上准确率)
以 acc → 准确率为主

评估方法:
外在评估:将词向量作为特征输入下游任务
内在评估:通过词类比(如:国王 - 男人 + 女人 = 女王)和相似度计算验证词向量质量

可视化
t-SNE 可视化
降维原理:高维望远镜,更擅长保留高维的局部特征 → 2D 或 3D
分析技巧:方差越大,data 波动越大
PCA → 这个扩展下

[深度补充]
PCA(主成分分析)是一种线性降维方法,它通过正交变换将一组可能相关的变量转换为一组线性不相关的变量,保留数据的全局结构。
t-SNE 是一种非线性降维方法,它通过最小化高维空间和低维空间的概率分布差异,保留数据的局部结构。
可视化技巧:先使用 PCA 将维度降到 50 维,再用 t-SNE 降到 2 维,这样可以减少噪声,加快计算速度。

================================================================================

十一、句向量生成方法


句向量生成方法:词 +(排列顺序)⇒ 句子

数据结构:
数据结构
├─ Queue(队列):先进先出
└─ Stack(栈):先进后出

1
2
3
4
5
6
7
8
9
10
队列示意图:
输入:C B A → [C][B][A] → 输出:A B C
先来先出

栈示意图:
输入:C B A → [C]
[B]
[A]
→ 输出:C B A
先来后出

Doc2vec model
句子:有结构性的一段文字
PV-DM → 通过上下文预测中心词
PV-DBOW → 【原笔记标注:帮我补充】

BERT 句向量(神经网络)
比如有 A B C → 变成 vector → 成为自己相应的 Token
Token 不仅包含自己的信息,还有前、后一个词的含义,称为双向编码
难点:BERT 对 model 的要求比较高
每个词向量前加一个标记,整合句子的信息

[深度补充(PV-DBOW)]
PV-DBOW(Distributed Bag of Words)是 Doc2Vec 的另一种模式。
它不使用上下文词预测中心词,而是直接用文档向量预测文档中的所有词。
它不考虑词的顺序,训练速度比 PV-DM 快,但效果略差。
实际应用中,通常将 PV-DM 和 PV-DBOW 的向量拼接起来使用,效果最好。

================================================================================

十二、文本相似度计算


用向量来量化语义文本相似度的问题 → 语义匹配的应用

为什么不用欧氏距离?
欧氏距离公式:
|AB| = √[(x1-x2)² + (y1-y2)²]
两个点之间的绝对距离

例子:
A:———— 机器学习 ———— (长)
B:机器学习 (短)

→ 距离并不能代表语义不相关

更多研究的是文本的方向是否相似

几何示意图:

1
2
3
4
5
6
7
8
9
10
      y
| B
| /
| / C
| /
| / θ
------+------> x
|
| A

对A来说,与C是相似的

TF-IDF 的加权
3 的 ML Transformer
0.1 0.1 0.4 0.4

→ 分词加权,关注真正重要的关键词

评估
准确率:Precision
召回率:Recall
F1-score = (Precision × Recall / (R+P)) × 2

预训练词向量应用
迁移学习
多任务学习
领域适配
对抗训练

[深度补充]
TF-IDF 的全称是词频 - 逆文档频率,它的核心思想是:一个词在当前文档中出现的频率越高,在所有文档中出现的频率越低,它对当前文档的重要性就越高。
工业界文本相似度标准流程:

  1. 文本预处理:分词、去停用词、去特殊字符
  2. 生成句向量:使用 Sentence-BERT
  3. 建立向量索引:使用 FAISS 或 Milvus 向量数据库
  4. 检索 Top-K 相似文本
  5. 结果重排序

================================================================================

十三、对应代码实践要点


原笔记对应代码
环境导入:matplotlib、os、gensim、sklearn、numpy
准备训练数据:5 个分好词的英文句子
训练 Word2Vec 和 FastText 模型
模型保存与信息打印
词向量可视化函数(支持 PCA/t-SNE)
GloVe 预训练模型加载函数(解决格式不兼容问题)
词类比测试函数
可视化展示

关键代码补充说明
result_pca, _ = visualize_vectors(…):函数返回两个值(降维结果 + 标题),_是 Python 通用的占位符,表示接收但不使用第二个返回值。
GloVe 格式转换:Gensim 无法直接加载原生 GloVe 格式,需通过glove2word2vec工具转换为 Word2Vec 格式。
t-SNE 参数perplexity=min(5, len(words)-1):防止当词的数量少于 5 个时,perplexity 参数过大导致报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# ==========================================
# 第一块:导入所有需要的工具包
# 【定位】:整个代码的“工具箱”,后面所有功能都靠这里导入的库实现
# ==========================================

# 1. 解决 matplotlib 在某些环境下的显示问题
# 【关系】:必须放在最前面,否则后面 plt.show() 可能报错
import matplotlib

matplotlib.use('TkAgg')

# 2. 操作系统接口库
# 【作用】:用来找文件路径、拼接路径
# 【关系】:后面加载 GloVe 模型时,要靠它找到模型文件在哪
import os

# 3. Gensim:NLP领域最常用的词向量工具库
# 【作用】:训练 Word2Vec、训练 FastText、加载预训练 GloVe
# 【关系】:这是整个代码的核心库,第3、6、7块全靠它
import gensim
from gensim.models import Word2Vec, FastText, KeyedVectors

# 4. 降维工具
# 【作用】:把100维的词向量压成2维,才能在纸上画出来
# 【关系】:第4块可视化函数会用到
from sklearn.decomposition import PCA # 线性降维,保留全局结构
from sklearn.manifold import TSNE # 非线性降维,保留局部相似性

# 5. 数值计算 + 画图
# 【作用】:numpy处理向量计算,matplotlib画图
# 【关系】:所有画图、向量运算都靠它们
import numpy as np
import matplotlib.pyplot as plt

# ==========================================
# 第二块:设置 matplotlib 中文显示
# 【定位】:为了让画出来的图能显示中文,而不是乱码
# 【关系】:必须在 plt.show() 之前设置,否则中文显示成方框
# ==========================================
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题

# ==========================================
# 第三块:准备训练数据
# 【定位】:给 Word2Vec 和 FastText 提供“教材”
# 【关系】:下一块训练模型时,会把这个 data 传进去
# ==========================================
# 数据格式:列表套列表,每个子列表是一个分好词的句子
# 注意:这是一个极小的演示数据集,实际训练需要百万级以上的句子
data = [
["natural", "language", "processing", "is", "fascinating"],
["word", "embeddings", "capture", "semantic", "meaning"],
["king", "queen", "man", "woman", "royalty"],
["similar", "words", "have", "close", "vectors"],
["machine", "learning", "models", "learn", "patterns"]
]

# ==========================================
# 第四块:训练 Word2Vec 模型
# 【定位】:用上面的 data 训练第一个词向量模型
# 【关系】:
# - 输入:第三块的 data
# - 输出:训练好的 word2vec_model,后面会用来画图
# ==========================================
word2vec_model = Word2Vec(
sentences=data, # 【输入】:训练用的句子列表
vector_size=100, # 【核心参数】:每个词生成100维的向量
# 维度越高,语义表达越强,但计算越慢
window=5, # 【核心参数】:上下文窗口大小
# 意思:学习当前词时,看它前后各5个词
min_count=1, # 【过滤参数】:词至少出现1次才会被训练
# 实际项目中一般设为5,过滤掉生僻词
workers=4, # 【加速参数】:用4个CPU核心并行训练
epochs=50 # 【训练轮次】:把整个数据集反复训练50遍
)

# ==========================================
# 第五块:训练 FastText 模型
# 【定位】:训练第二个词向量模型,和 Word2Vec 做对比
# 【核心区别】:FastText 会学习“子词”,能处理未登录词
# 【关系】:
# - 输入:同样是第三块的 data
# - 输出:fasttext_model,这里演示暂不画图,只保存
# ==========================================
fasttext_model = FastText(
sentences=data,
vector_size=100,
window=5,
min_count=1,
workers=4,
epochs=50,
min_n=3, # 【FastText特有】:子词最小长度为3
max_n=6 # 【FastText特有】:子词最大长度为6
# 例如:"apple" 会拆成 "app", "ppl", "ple" 等
)

# ==========================================
# 第六块:保存模型到本地
# 【定位】:把训练好的模型存成文件,下次直接加载,不用重训
# 【关系】:
# - 输入:第四、五块训练好的模型对象
# - 输出:本地生成两个文件:word2vec_model.model 和 fasttext_model.model
# ==========================================
word2vec_model.save("word2vec_model.model")
fasttext_model.save("fasttext_model.model")

# ==========================================
# 第七块:打印模型信息(方便调试)
# 【定位】:确认模型训练成功,看看参数对不对
# 【关系】:只是打印信息,不影响后续逻辑
# ==========================================
print("\n===== Word2Vec 模型信息 =====")
print(f"词汇量:{len(word2vec_model.wv)}") # .wv 是词向量存储对象
print(f"向量维度:{word2vec_model.vector_size}")
print(f"训练轮次:{word2vec_model.epochs}")

print("\n===== FastText 模型信息 =====")
print(f"词汇量:{len(fasttext_model.wv)}")
print(f"向量维度:{fasttext_model.vector_size}")
print(f"子词长度范围:{fasttext_model.wv.min_n} - {fasttext_model.wv.max_n}")


# ==========================================
# 第八块:定义可视化核心函数
# 【定位】:这是一个“工具函数”,后面第九块和最后一块都会调用它
# 【功能】:把100维的词向量降到2维,方便画图
# 【输入】:模型对象、要可视化的词列表、降维方法('pca'或'tsne')
# 【输出】:降维后的2维坐标 + 标题
# ==========================================
def visualize_vectors(model, words, method='pca'):
# --------------------------
# 第一步:兼容性处理
# 【原因】:
# - 自己训练的 Word2Vec/FastText 模型,词向量存在 .wv 属性里
# - 直接加载的 GloVe 模型,本身就是词向量对象,没有 .wv
# 【作用】:统一接口,不管传什么模型进来都能处理
# --------------------------
vectors_model = model.wv if hasattr(model, 'wv') else model

# --------------------------
# 第二步:提取词向量
# 【逻辑】:遍历 words 列表,把每个词对应的向量取出来
# 【关系】:输入是词列表,输出是向量列表
# --------------------------
vectors = [vectors_model[word] for word in words]

# --------------------------
# 第三步:转成 numpy 数组
# 【原因】:sklearn 的降维函数只接受 numpy 数组,不接受 Python 列表
# --------------------------
vectors = np.array(vectors)

# --------------------------
# 第四步:选择降维方法
# 【区别】:
# - PCA:线性降维,保留词与词之间的“全局关系”
# - t-SNE:非线性降维,保留“局部相似性”,相似的词会挨得更近
# --------------------------
if method == 'pca':
reducer = PCA(n_components=2) # n_components=2 表示降到2维
title = "PCA 降维(全局结构)"
else:
# t-SNE 参数设置
reducer = TSNE(
n_components=2,
# perplexity:困惑度,可以理解为“考虑几个邻居”
# 这里用 min(5, len(words)-1) 是为了防止报错:
# 如果词总数少于5个,就取“总数-1”
perplexity=min(5, len(words) - 1),
learning_rate=200, # 学习率,步长大小
random_state=42 # 随机种子,保证每次运行结果一样
)
title = "t-SNE 降维(局部结构)"

# --------------------------
# 第五步:执行降维
# 【作用】:把 (N, 100) 的向量变成 (N, 2)
# --------------------------
result = reducer.fit_transform(vectors)

# --------------------------
# 返回结果
# 【关系】:返回给调用者,用来画图
# --------------------------
return result, title


# ==========================================
# 第九块:可视化 Word2Vec 词向量
# 【定位】:调用上面的 visualize_vectors 函数,画出词向量图
# 【关系】:
# - 输入:第四块训练好的 word2vec_model
# - 输出:弹出一个 matplotlib 窗口,显示两张图
# ==========================================

# 定义要可视化的词(选了语义关系明显的几个词)
test_words = ['king', 'queen', 'man', 'woman', 'royalty']

# 创建画布:1行2列,大小 16x6
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
fig.suptitle("Word2Vec 词向量可视化对比", fontsize=16) # 总标题

# --------------------------
# 左边画 PCA
# --------------------------
result_pca, _ = visualize_vectors(word2vec_model, test_words, method='pca')
ax1.scatter(result_pca[:, 0], result_pca[:, 1]) # 画散点
# 在每个点旁边写上词
for i, word in enumerate(test_words):
ax1.annotate(word, xy=(result_pca[i, 0], result_pca[i, 1]), fontsize=14)
ax1.set_title("PCA 降维(保持全局结构)")

# --------------------------
# 右边画 t-SNE
# --------------------------
result_tsne, _ = visualize_vectors(word2vec_model, test_words, method='t-sne')
ax2.scatter(result_tsne[:, 0], result_tsne[:, 1])
for i, word in enumerate(test_words):
ax2.annotate(word, xy=(result_tsne[i, 0], result_tsne[i, 1]), fontsize=14)
ax2.set_title("t-SNE 降维(保持局部结构)")

# 显示图片
plt.show()


# ==========================================
# 第十块:词类比测试函数(备用,演示用)
# 【定位】:测试经典的 "king - man + woman = queen"
# 【关系】:这里只是定义,后面可以选择性调用
# ==========================================
def word_analogy_test(model, a, b, c):
"""
测试逻辑:a 对应 b,那么 c 对应什么?
公式:b - a + c
"""
try:
vm = model.wv if hasattr(model, 'wv') else model
res = vm.most_similar(positive=[b, c], negative=[a], topn=3)
print(f"\n{a}:{b} = {c}:? → 结果:{res}")
except Exception as e:
print("测试失败:", e)


# ==========================================
# 第十一块:【核心难点】GloVe 模型加载函数
# 【定位】:这是你最关心的部分
# 【为什么这么复杂】:
# - GloVe 是斯坦福发布的格式
# - Gensim 只认 Word2Vec 格式
# - 所以必须先“转格式”,再加载
# 【关系】:
# - 输入:GloVe 文件名(需要你自己先下载)
# - 输出:可以直接用的词向量模型对象
# ==========================================
def load_glove_model(glove_file='glove.6B.100d.txt', convert=True):
# --------------------------
# 第一步:找到当前文件所在的文件夹
# 【原因】:
# - 如果你把代码和 GloVe 文件放在同一个文件夹
# - 不管你在哪运行这段代码,都能准确定位到 GloVe 文件
# --------------------------
script_dir = os.path.dirname(os.path.abspath(__file__))

# --------------------------
# 第二步:拼接完整路径
# 【例子】:
# script_dir = "C:/Users/xxx/Desktop"
# glove_file = "glove.6B.100d.txt"
# 拼接后:"C:/Users/xxx/Desktop/glove.6B.100d.txt"
# --------------------------
glove_path = os.path.join(script_dir, glove_file)

# --------------------------
# 第三步:检查文件是否存在
# 【原因】:GloVe 文件很大(几百MB),代码不会自带
# 【作用】:如果文件不存在,友好提示下载命令,而不是直接报错
# --------------------------
if not os.path.exists(glove_path):
print(f"❌ 未找到模型文件:{glove_path}")
print("请先下载 GloVe 模型:")
print(" 下载命令:wget https://nlp.stanford.edu/data/glove.6B.zip")
print(" 解压命令:unzip glove.6B.zip")
print(" 然后把解压后的 glove.6B.100d.txt 放在和代码同级的文件夹里")
return None

# --------------------------
# 第四步:定义转换后的文件名
# 【逻辑】:在原文件名后面加 ".w2v.txt"
# 【例子】:
# 原文件:glove.6B.100d.txt
# 新文件:glove.6B.100d.txt.w2v.txt
# --------------------------
w2v_path = f"{glove_path}.w2v.txt"

# --------------------------
# 第五步:执行格式转换(GloVe → Word2Vec)
# 【条件判断】:
# - convert=True:需要转换
# - not os.path.exists(w2v_path):且转换后的文件还不存在
# (避免每次运行都重新转换,浪费时间)
# --------------------------
if convert and not os.path.exists(w2v_path):
print("🔄 正在把 GloVe 格式转换为 Word2Vec 格式...")
# 导入转换工具(放在这里是为了避免不需要转换时也导入)
from gensim.scripts.glove2word2vec import glove2word2vec
# 【核心转换】:一行代码搞定格式转换
glove2word2vec(glove_path, w2v_path)
print("✅ 转换完成!")

# --------------------------
# 第六步:加载最终模型
# --------------------------
try:
# 决定用哪个路径:
# - 如果转换了,就用转换后的 w2v_path
# - 如果没转换,就用原路径(一般不建议)
use_path = w2v_path if convert else glove_path

print(f"📥 正在加载模型:{use_path}")

# 【核心加载】:
# KeyedVectors 是 Gensim 专门用来加载预训练词向量的类
# binary=False:表示文件是文本格式,不是二进制
model = KeyedVectors.load_word2vec_format(use_path, binary=False)

print("✅ 模型加载成功!")
return model
except Exception as e:
print("❌ 加载失败:", e)
return None


# ==========================================
# 第十二块:加载 GloVe 并可视化
# 【定位】:调用上面的 load_glove_model 函数,加载并画图
# 【关系】:
# - 输入:需要你先下载好 glove.6B.100d.txt
# - 输出:弹出 GloVe 的词向量图
# ==========================================

# 调用加载函数
glove_model = load_glove_model()

# 只有加载成功了才画图
if glove_model:
# 过滤一下:只保留在 GloVe 词汇表中存在的词
glove_words = [w for w in test_words if w in glove_model]

# 创建画布
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
fig.suptitle("GloVe 预训练词向量可视化", fontsize=16)

# 画 PCA
res_pca, _ = visualize_vectors(glove_model, glove_words, 'pca')
ax1.scatter(res_pca[:, 0], res_pca[:, 1])
for i, w in enumerate(glove_words):
ax1.annotate(w, (res_pca[i, 0], res_pca[i, 1]))
ax1.set_title("GloVe PCA 降维")

# 画 t-SNE
res_tsne, _ = visualize_vectors(glove_model, glove_words, 'tsne')
ax2.scatter(res_tsne[:, 0], res_tsne[:, 1])
for i, w in enumerate(glove_words):
ax2.annotate(w, (res_tsne[i, 0], res_tsne[i, 1]))
ax2.set_title("GloVe t-SNE 降维")

# 调整布局,防止标题重叠
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()

================================================================================
\033[94m 学习心得 \033[0m
================================================================================
本周系统学习了词向量与句向量的完整知识体系,从最基础的 one-hot 编码,到 Word2Vec、GloVe、FastText 等经典模型,再到句向量生成与文本相似度计算,建立了完整的分布式表示知识框架。
词向量的核心思想是“语义由上下文决定”,这一思想彻底改变了 NLP 的发展,让计算机真正具备了理解语义的能力。
这些基础是后续学习 BERT、GPT 等大语言模型的核心基石,只有把底层原理搞懂,才能真正理解现代 NLP 的工作机制。
================================================================================