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做文本分类,词表只有5000个词,结果模型死活不收敛。后来才发现,每个向量5000维,大部分位置都是0,计算效率极低,而且完全无法表达词与词之间的语义关系。

One-hot有两个致命缺陷:

  1. 维度灾难:词表多大,向量就多长。中文词表动辄几十万,算起来要命。
  2. 语义孤岛:任意两个词的内积都是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做的就是:

  1. 把上下文词的One-hot向量加起来(或取平均)
  2. 通过一个隐藏层(就是我们要学的词向量)
  3. 输出层预测目标词的概率分布

说白了,就是「猜词游戏」——给你前后文,猜中间是什么词。

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解决这个问题。

先消化一下这一章的内容,动手跑跑代码。有任何问题,欢迎交流。