🎯 学习目标
- 理解离散表示(One-Hot/词袋/TF-IDF)的根本缺陷
- 理解分布式语义假设与词向量的几何意义
- 理解 word2vec 的 CBOW 与 Skip-gram 两种结构
- 会用 gensim 训练、加载、查询词向量并做词语类比
- 掌握余弦相似度与句向量平均池化,实现简易语义检索
- 了解静态向量局限、预训练中文词向量与 BERT 上下文向量
1离散表示:从符号到数字的第一道桥
前面章节我们把文本转成了 TF-IDF 向量,回顾这条演进线的起点与问题:
One-Hot(独热编码)
词表:[计算机, 电脑, 汽车]
计算机 = [1, 0, 0]
电脑 = [0, 1, 0]
汽车 = [0, 0, 1]
问题一目了然:
- 语义鸿沟:"计算机"和"电脑"是同义词,但向量夹角 90°、点积为 0,系统认为它们毫无关系。
- 维度灾难:词表 10 万词 → 每个向量 10 万维,且几乎全是 0(稀疏)。
- 无法表达类比、蕴含等任何语义关系。
词袋 / TF-IDF(文档向量)
把词的 One-Hot 按计数/权重累加,得到文档向量(第 6 章)。比 One-Hot 实用,但文档向量同样稀疏、无语义,且语序信息仍缺失。
2分布式语义:词的含义藏在邻居里
"You shall know a word by the company it keeps." —— J. R. Firth, 1957
一个词的含义,由它周围经常出现的词决定。
"喝"附近总是水、茶、咖啡;"驾驶"附近总是车、司机、马路。于是"喝 ___"和"饮 ___"的上下文高度相似——即使两个词从未同句出现,模型也能推断它们意思相近。这就是分布式表示(Distributional Representation)。
词向量(Word Embedding):为每个词学习一个低维稠密实数向量(如 100~300 维),让上下文相似的词在向量空间中距离也近。
One-Hot:10万维,99.99% 是 0,词之间等距无关
词向量:300维,全是稠密实数,语义相近的词在空间中成簇
电脑 ● ●计算机
╲
手机 ● ● 卡车
● 汽车
3word2vec:词向量的开山之作
2013 年 Google 开源的 word2vec 用极简神经网络从大规模无标注语料中学词向量,有两种结构:
| CBOW 连续词袋 | Skip-gram 跳字 | |
|---|---|---|
| 输入→预测 | 上下文词 → 中心词 | 中心词 → 上下文词 |
| 训练速度 | 快 | 慢 |
| 低频词效果 | 一般 | 更好(每个低频词都要预测多次) |
| 适用 | 大语料、求快 | 中小语料、重视稀有词 |
句子:我 爱 自然 语言 处理 窗口大小=2
CBOW:用 [我,爱,语言,处理] 预测中间的 [自然]
Skip-gram:用 [自然] 预测 [我,爱,语言,处理]
训练目标本质:让"真实共现"的词对向量点积高(softmax + 负采样加速)
训练完成后,取隐藏层权重矩阵作为词向量表 —— 副产品成了主角
关键直觉:模型其实不需要"预测得很准",为了完成预测任务,它被迫把语义压缩进那几百维向量——向量本身才是我们要的财富。
神奇的向量运算:词的类比
语义关系在向量空间中表现为方向,可以做加减法:
vec(国王) - vec(男人) + vec(女人) ≈ vec(女王)
vec(北京) - vec(中国) + vec(日本) ≈ vec(东京)
vec(走路) - vec(走) + vec(游) ≈ vec(游泳)
4gensim 训练与使用词向量
# pip install gensim
from gensim.models import Word2Vec
import jieba
# 1) 准备语料:分好词的句子列表(列表的列表)
raw_docs = [
"深度学习是人工智能的核心技术",
"机器学习推动人工智能快速发展",
"自然语言处理让计算机理解人类语言",
"计算机视觉让机器识别图像和视频",
"大语言模型显著提升自然语言理解能力",
"神经网络是深度学习的基础模型",
]
sentences = [jieba.lcut(d) for d in raw_docs]
# 真实项目:语料越大越好(千万~十亿字),且应做成迭代器逐行读,避免爆内存
# 2) 训练
model = Word2Vec(
sentences=sentences,
vector_size=100, # 向量维度(常用 100~300)
window=5, # 上下文窗口大小
min_count=1, # 词频低于此值忽略(大语料设 5~10 去噪声)
sg=1, # 1=Skip-gram,0=CBOW
epochs=20, # 训练轮数
workers=4, # 线程数
negative=5) # 负采样数量
# 3) 保存 / 加载
model.save("w2v.model")
model = Word2Vec.load("w2v.model")
# 只导出词向量本体(占用更小)
model.wv.save_word2vec_format("vectors.txt", binary=False)
# 4) 使用词向量
wv = model.wv
wv["人工智能"] # shape=(100,) 的 numpy 向量
wv.similarity("人工智能", "机器学习") # 余弦相似度 -1~1
wv.most_similar("自然语言", topn=3) # 最相近的词(带分数)
wv.most_similar(positive=["中国", "东京"], negative=["北京"]) # 类比运算
wv.similar_by_vector(wv["人工智能"] + wv["神经网络"], topn=3)
# 词表外的词会 KeyError,先判断
word = "区块链"
if word in wv:
print(wv[word])
# 注意:玩具语料太小,相似度结果没有统计意义;
# 真实效果需千万字以上语料,或直接加载预训练向量(下一节)
5余弦相似度与句向量
余弦相似度:用两个向量夹角的余弦衡量方向相似性,只看方向不看长度,是文本向量的标准度量:
import numpy as np
def cosine(a, b):
a, b = np.asarray(a), np.asarray(b)
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9)
# 句向量最简单的做法:词向量平均池化(average pooling)
def sentence_vector(text, wv):
vecs = [wv[w] for w in jieba.lcut(text) if w in wv]
if not vecs:
return np.zeros(wv.vector_size)
return np.mean(vecs, axis=0)
def doc_similarity(t1, t2, wv):
return cosine(sentence_vector(t1, wv), sentence_vector(t2, wv))
# print(doc_similarity("人工智能发展很快", "机器学习进步迅速", wv))
# print(doc_similarity("人工智能发展很快", "今天中午吃了面条", wv))
# 前者余弦高,后者低 —— 即使没有一个相同词,语义近的句子也能被识别
平均池化朴素但有效,工程上改进:① 用 TF-IDF 加权平均(重要词权重大);② 去停用词后再平均;③ SIF(平滑逆频率加权 + 去主成分);④ 直接用 BERT 句向量。gensim 也内置 TF-IDF/LSI/LDA 等主题向量,可在无 w2v 时构建文档向量。
6预训练中文词向量(不必从零训练)
自己训练需要海量语料与时间。推荐直接使用大厂发布的预训练词向量(通用语料训练,覆盖百万词):
| 资源 | 规模/特点 | 获取 |
|---|---|---|
| 腾讯 AI Lab 中文词向量 | 800万+词/短语,200维,覆盖新词短语,质量高 | 搜索 "Tencent AI Lab Embedding" |
| Chinese-Word-Vectors(哈工大 SCIR) | 多领域(百科/新闻/微博),300维 | GitHub: Embedding/Chinese-Word-Vectors |
| fastText 中文向量 | Facebook,subword 机制对未登录词友好 | fastText 官网 vectors 页 |
# 加载 text 格式预训练向量(腾讯/哈工大均提供 txt/gz)
from gensim.models import KeyedVectors
wv = KeyedVectors.load_word2vec_format(
"tencent-ailab-embedding-zh-d200-v0.2.0-s.txt",
binary=False, unicode_errors="ignore")
print(wv.most_similar("碳中和", topn=5))
# 大文件建议转二进制加速加载:
# wv.save_word2vec_format("tencent.bin", binary=True)
# KeyedVectors.load_word2vec_format("tencent.bin", binary=True)
7静态向量的局限 → 上下文向量
word2vec 一个词只有一个向量,无法处理多义词:
"苹果发布了新手机" 苹果 = 公司
"苹果多少钱一斤" 苹果 = 水果
word2vec 中两处"苹果"共享同一个向量,语义被强行平均
演进路线:
- ELMo(2018):基于 LSTM 的上下文相关词向量,同一个词在不同句子中向量不同。
- BERT(2018,Google):基于 Transformer 的双向预训练模型,通过 MLM 掩码语言模型任务学习深度上下文表示,横扫 11 项 NLP 榜单。中文模型:
bert-base-chinese。 - GPT 系列 / 大语言模型:自回归生成式预训练 + 指令对齐,不仅能理解还能生成。
用 transformers 获取 BERT 句向量(选学)
pip install transformers torch
# 首次运行会自动下载 bert-base-chinese(约 400MB),可设镜像:
# set HF_ENDPOINT=https://hf-mirror.com (Windows 命令行)
import torch
from transformers import AutoTokenizer, AutoModel
model_name = "bert-base-chinese"
tok = AutoTokenizer.from_pretrained(model_name)
bert = AutoModel.from_pretrained(model_name)
@torch.no_grad()
def bert_sentence_vector(text):
inputs = tok(text, return_tensors="pt", truncation=True, max_length=128)
outputs = bert(**inputs)
# 取 <[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]> 位置向量作为整句表示(简单基线;更推荐 mean pooling)
cls = outputs.last_hidden_state[0, 0]
return torch.nn.functional.normalize(cls, dim=0).numpy()
import numpy as np
v1 = bert_sentence_vector("这家餐厅味道很好")
v2 = bert_sentence_vector("菜品不错,值得推荐")
v3 = bert_sentence_vector("明天可能会下雨")
print(v1 @ v2, v1 @ v3) # v1-v2 相似度明显更高
生产级语义检索/聚类推荐使用专门训练过的句向量模型(如 text2vec、bge-small-zh、m3e、sentence-transformers 系列),余弦相似度质量显著高于直接取 BERT 的 <[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>。
8案例:语义相似度检索
# 思路:FAQ 文档全部编码成向量入库,新问题向量化后取余弦最近邻
import numpy as np
faqs = [
"如何申请退货退款",
"订单几天可以到货",
"优惠券怎么使用",
"账号密码忘记怎么办",
]
query = "买的东西不想要了能退吗"
# 方案一:TF-IDF + 余弦(无需训练,传统基线)
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
tv = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b")
mat = tv.fit_transform([" ".join(jieba.lcut(x)) for x in faqs + [query]])
sims = cosine_similarity(mat[-1], mat[:-1])[0]
print(faqs[sims.argmax()], sims.round(3))
# TF-IDF 缺陷:query 与 FAQ 没有共同词(退货/退款 vs 不想要/退)时可能失配
# 方案二:词向量/句向量(语义泛化,方案见第8章搜索引擎项目)
# v_q = sentence_vector(query, wv)
# 对每条 FAQ 向量算余弦,取 TopK —— 没共同词也能匹配
9本章小结与练习
📌 关键结论
- One-Hot/词袋稀疏且无语义;分布式表示让"上下文相近 → 向量相近"。
- word2vec:CBOW(上下文预测中心,快)/ Skip-gram(反之,低频词好),gensim 训练。
- 句向量基线:词向量平均(可 TF-IDF 加权);相似度用余弦。
- word2vec 一词一向量,不懂多义;BERT 提供上下文向量,生产检索用 bge 等句向量模型。