1. 词向量基础:从One-hot到Word2Vec,理解分布式表示的核心思想
大家好,欢迎来到这门课的第一章。
说实话,每次带新人入门NLP,我都是从词向量开始讲起。为什么?因为这是整个自然语言处理的基石。你想想看,计算机不认识汉字,也不懂英文,它只认数字。那怎么让计算机理解「苹果」和「香蕉」的关系?怎么让它知道「北京」是「中国」的首都?
答案就是——词向量。
1.1 从One-hot说起:最朴素的表示法
最早期的做法,叫One-hot编码。说白了就是:给每个词分配一个独一无二的「身份证号」。
假设我们有一个词表:["我", "爱", "自然语言", "处理"]。那么:
- "我" → [1, 0, 0, 0]
- "爱" → [0, 1, 0, 0]
- "自然语言" → [0, 0, 1, 0]
- "处理" → [0, 0, 0, 1]
看起来挺简单对吧?但问题很快就来了。
One-hot有两个致命缺陷:
- 维度灾难:词表多大,向量就多长。中文词表动辄几十万,算起来要命。
- 语义孤岛:任意两个词的内积都是0,计算机眼里「苹果」和「香蕉」毫无关系。
嗯,这里要注意:One-hot不是不能用,但只适合词表极小(比如几十个词)的场景。但凡有点规模,就得换思路。
1.2 分布式表示:核心思想来了
那怎么解决语义相似度的问题?
分布式表示(Distributed Representation)的核心思想其实很简单:用一组稠密的实数向量来表示一个词,每个维度都承载一部分语义信息。
举个例子:
- "国王" → [0.8, 0.3, -0.1, 0.6, ...]
- "王后" → [0.7, 0.4, -0.2, 0.5, ...]
- "男人" → [0.9, 0.1, 0.0, 0.2, ...]
- "女人" → [0.8, 0.2, -0.1, 0.3, ...]
你看,「国王」和「王后」的向量很接近,「男人」和「女人」也很接近。而且有意思的是:国王 - 男人 + 女人 ≈ 王后。这就是分布式表示的魅力——语义关系可以通过向量运算来捕捉。
💡 核心洞察: 分布式表示的本质是「一个词的语义由它周围的词共同决定」。说白了,就是那句老话——"You shall know a word by the company it keeps."(观其伴,知其意)
1.3 Word2Vec:把思想变成现实
2013年,Google的Mikolov团队提出了Word2Vec,一下子把词向量技术推向了实用化。我个人习惯把Word2Vec理解为「用神经网络学词向量」的经典方法。
Word2Vec有两种模型:
| 模型 | 核心思路 | 我常用的场景 |
|---|---|---|
| CBOW(连续词袋模型) | 用上下文词预测当前词 | 小数据集、高频词多的场景 |
| Skip-gram(跳字模型) | 用当前词预测上下文词 | 大数据集、低频词多的场景 |
我建议你记住一个原则:CBOW训练快,适合高频词;Skip-gram对低频词更友好,但训练慢一些。
CBOW 原理速览
假设句子是「我 爱 自然语言 处理」。窗口大小为2,那么对于目标词「自然语言」,它的上下文是「我」「爱」「处理」。CBOW做的就是:
- 把上下文词的One-hot向量加起来(或取平均)
- 通过一个隐藏层(就是我们要学的词向量)
- 输出层预测目标词的概率分布
说白了,就是「猜词游戏」——给你前后文,猜中间是什么词。
Skip-gram 原理速览
反过来,Skip-gram是:给你一个词,猜它周围可能出现哪些词。
比如看到「自然语言」,模型要预测它前后可能出现的词是「我」「爱」「处理」。
我在项目中遇到过这样一个情况:用Skip-gram训练一个中文新闻语料,结果「特朗普」和「拜登」的向量距离非常近,而且「总统」这个词出现在它们周围。这说明模型确实学到了语义关系。
1.4 训练技巧与避坑指南
Word2Vec虽然好用,但有几个坑我得提醒你:
✅ 实用技巧:
- 负采样(Negative Sampling):别用完整的softmax,太慢了。每次只采样几个负样本,效率提升几十倍。
- 高频词下采样:像「的」「了」「是」这种词,出现太频繁反而没信息量。可以按概率丢弃一部分。
- 向量维度:我一般从100维开始试,效果不好再往上加。300维是个常见选择。
⚠️ 我曾经踩过的坑:
- 直接用原始文本训练,没做分词。中文必须分词!否则「自然语言处理」会被当成一个词,学不到「自然」「语言」「处理」之间的关系。
- 窗口大小设太大。窗口=5通常够用,设到10以上反而引入噪声。
- 训练轮数太少。我建议至少跑5轮,观察loss是否还在下降。
1.5 代码实战:用Gensim训练词向量
理论说完了,咱们动手试试。Gensim库封装了Word2Vec,用起来很方便。
from gensim.models import Word2Vec
from gensim.utils import simple_preprocess
# 准备语料(这里用简单示例)
sentences = [
"我 爱 自然语言 处理",
"自然语言 处理 是 人工智能 的 重要 分支",
"我 喜欢 学习 新 技术",
"人工智能 正在 改变 世界"
]
# 分词(实际项目中用jieba等工具)
tokenized_sentences = [s.split() for s in sentences]
# 训练模型
model = Word2Vec(
sentences=tokenized_sentences,
vector_size=100, # 向量维度
window=5, # 上下文窗口
min_count=1, # 忽略出现次数少于1的词
sg=0, # 0=CBOW, 1=Skip-gram
negative=5, # 负采样数量
epochs=10 # 训练轮数
)
# 查看词向量
print(model.wv['自然语言']) # 输出100维向量
# 找相似词
print(model.wv.most_similar('自然语言', topn=3))
# 输出:[('处理', 0.85), ('人工智能', 0.72), ('重要', 0.68)]
# 向量运算
result = model.wv.most_similar(
positive=['国王', '女人'],
negative=['男人'],
topn=1
)
print(result) # 理论上接近「王后」
嗯,这里要注意:小语料训练出来的词向量质量有限。实际项目中,我建议用大规模预训练好的词向量(比如Google News的300维向量),或者在自己的领域语料上微调。
1.6 小结与思考
这一章我们走完了从One-hot到Word2Vec的进化之路。核心就三句话:
- One-hot:简单但稀疏,无法表达语义相似度
- 分布式表示:用稠密向量编码语义,让相似词距离更近
- Word2Vec:通过上下文预测来学习词向量,CBOW和Skip-gram各有千秋
你可能会问:Word2Vec这么好,为什么后来还有BERT、GPT这些?
好问题。Word2Vec有个硬伤——它是静态的。一个词只有一个向量,无法处理一词多义。比如「苹果」在「吃苹果」和「苹果公司」里意思完全不同,但Word2Vec给的是同一个向量。
这就是我们后面章节要讲的内容。下一章,我会带你看看如何用ELMo和BERT解决这个问题。
先消化一下这一章的内容,动手跑跑代码。有任何问题,欢迎交流。