词向量
词向量(Word Embedding)是将词语映射到连续向量空间的技术。通过词向量,可以捕捉词语之间的语义关系,是现代 NLP 的基础技术。
为什么需要词向量
传统的词表示方法如独热编码(One-Hot Encoding)存在明显缺陷:
# 独热编码示例
vocab = ['苹果', '香蕉', '电脑', '手机']
vocab_size = len(vocab)
# 每个词用一个向量表示,只有对应位置为 1
apple = [1, 0, 0, 0]
banana = [0, 1, 0, 0]
computer = [0, 0, 1, 0]
phone = [0, 0, 0, 1]
独热编码的问题:
维度灾难:词汇表通常有几万到几十万词,导致向量维度极高。
稀疏性:每个向量只有一个位置为 1,其余全为 0,信息密度低。
无法表达语义关系:任意两个词的向量距离相等,无法体现词语之间的相似性。
词向量解决了这些问题:维度低(通常 100-300 维)、稠密、能表达语义关系。
Word2Vec
Word2Vec 是 Google 于 2013 年提出的词向量训练方法,包含两种模型:CBOW 和 Skip-gram。
核心思想
Word2Vec 的核心思想是:一个词的含义可以由它的上下文决定。
- CBOW(Continuous Bag of Words):根据上下文词预测目标词
- Skip-gram:根据目标词预测上下文词
使用 Gensim 训练 Word2Vec
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
# 准备语料(每个句子是一个词列表)
sentences = [
['我', '喜欢', '吃', '苹果'],
['苹果', '和', '香蕉', '都是', '水果'],
['我', '每天', '使用', '电脑', '工作'],
['电脑', '和', '手机', '是', '电子', '产品'],
['水果', '对', '身体', '有益'],
['电子', '产品', '更新', '很快']
]
# 训练 Word2Vec 模型
model = Word2Vec(
sentences,
vector_size=100, # 向量维度
window=5, # 上下文窗口大小
min_count=1, # 最小词频
workers=4, # 并行线程数
sg=0 # 0: CBOW, 1: Skip-gram
)
# 保存模型
model.save("word2vec.model")
# 加载模型
model = Word2Vec.load("word2vec.model")
获取词向量
# 获取单个词的向量
vector = model.wv['苹果']
print(f"'苹果' 的词向量维度: {vector.shape}")
print(f"前 10 维: {vector[:10]}")
# 获取词汇表中的所有词
vocab = list(model.wv.key_to_index.keys())
print(f"词汇表大小: {len(vocab)}")
print(f"词汇表: {vocab}")
词相似度计算
# 计算两个词的相似度
similarity = model.wv.similarity('苹果', '香蕉')
print(f"'苹果' 和 '香蕉' 的相似度: {similarity:.4f}")
similarity = model.wv.similarity('苹果', '电脑')
print(f"'苹果' 和 '电脑' 的相似度: {similarity:.4f}")
# 找出最相似的词
similar_words = model.wv.most_similar('苹果', topn=5)
print("与 '苹果' 最相似的词:")
for word, score in similar_words:
print(f" {word}: {score:.4f}")
词类比
Word2Vec 最著名的特性是可以进行词类比运算:
# 经典例子:国王 - 男人 + 女人 = 女王
# 在中文中类似的例子
# 找出满足 "A - B + C = D" 的词 D
result = model.wv.most_similar(
positive=['香蕉', '电脑'],
negative=['苹果'],
topn=3
)
print("香蕉 - 苹果 + 电脑 = ?")
for word, score in result:
print(f" {word}: {score:.4f}")
训练参数详解
model = Word2Vec(
sentences,
vector_size=100, # 词向量维度,通常 100-300
window=5, # 上下文窗口大小,通常 5
min_count=5, # 忽略词频小于此值的词
workers=4, # 并行训练线程数
sg=1, # 1: Skip-gram, 0: CBOW
hs=0, # 1: 层次 softmax, 0: 负采样
negative=5, # 负采样数量,通常 5-20
epochs=10, # 训练轮数
alpha=0.025, # 初始学习率
min_alpha=0.0001 # 最小学习率
)
参数选择建议:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| vector_size | 100-300 | 维度越高表达能力越强,但计算成本增加 |
| window | 5 | 小窗口捕捉语法关系,大窗口捕捉语义关系 |
| sg | 1 (Skip-gram) | Skip-gram 在小数据集上效果更好 |
| negative | 5-20 | 负采样数量,数据量大时可增大 |
| min_count | 5-10 | 过滤低频词,减少噪声 |
GloVe
GloVe(Global Vectors for Word Representation)是斯坦福大学提出的词向量方法,结合了全局统计信息和局部上下文信息。
GloVe 的原理
GloVe 基于词共现矩阵,利用全局词共现统计信息来训练词向量。其核心思想是:词向量之间的差异应该反映词共现概率的比值。
使用预训练 GloVe 向量
import numpy as np
def load_glove_vectors(glove_file):
"""加载预训练的 GloVe 向量"""
word_vectors = {}
with open(glove_file, 'r', encoding='utf-8') as f:
for line in f:
values = line.split()
word = values[0]
vector = np.array(values[1:], dtype='float32')
word_vectors[word] = vector
return word_vectors
# 加载 GloVe 向量(需要先下载)
# glove_vectors = load_glove_vectors('glove.6B.100d.txt')
# 使用示例
# vector = glove_vectors.get('apple')
# print(f"'apple' 的向量维度: {vector.shape}")
在 Gensim 中使用 GloVe
from gensim.scripts.glove2word2vec import glove2word2vec
from gensim.models import KeyedVectors
# 将 GloVe 格式转换为 Word2Vec 格式
# glove2word2vec('glove.6B.100d.txt', 'glove.6B.100d.word2vec.txt')
# 加载转换后的向量
# glove_model = KeyedVectors.load_word2vec_format('glove.6B.100d.word2vec.txt')
# 使用方式与 Word2Vec 相同
# similarity = glove_model.similarity('apple', 'banana')
# print(f"'apple' 和 'banana' 的相似度: {similarity:.4f}")
FastText
FastText 是 Facebook 提出的词向量方法,在 Word2Vec 的基础上引入了子词(Subword)信息。
FastText 的优势
处理未登录词(OOV):通过子词信息可以生成未见过的词的向量。
处理形态丰富的语言:对于德语、俄语等形态变化丰富的语言效果更好。
捕捉词内部结构:可以学习词根、词缀等内部结构信息。
使用 Gensim 训练 FastText
from gensim.models import FastText
# 训练 FastText 模型
model = FastText(
sentences,
vector_size=100,
window=5,
min_count=1,
workers=4,
sg=1,
min_n=3, # 最小子词长度
max_n=6 # 最大子词长度
)
# 获取已知词的向量
vector = model.wv['苹果']
print(f"'苹果' 的向量维度: {vector.shape}")
# 获取未登录词的向量(Word2Vec 做不到)
oov_vector = model.wv['苹果手机'] # 假设这个词不在训练语料中
print(f"'苹果手机' 的向量维度: {oov_vector.shape}")
# 相似度计算
similarity = model.wv.similarity('苹果', '苹果手机')
print(f"'苹果' 和 '苹果手机' 的相似度: {similarity:.4f}")
预训练词向量
使用大规模语料预训练的词向量通常效果更好,特别是对于小数据集任务。
中文预训练词向量
常用的中文预训练词向量资源:
腾讯 AI Lab 词向量:800 万中文词,200 维
Chinese-Word-Vectors:多种训练方法和语料的中文词向量
加载预训练词向量
from gensim.models import KeyedVectors
# 加载预训练词向量
model = KeyedVectors.load_word2vec_format(
'Tencent_AILab_ChineseEmbedding.txt',
binary=False
)
# 获取词向量
vector = model['自然语言处理']
print(f"向量维度: {vector.shape}")
# 找相似词
similar = model.most_similar('人工智能', topn=10)
for word, score in similar:
print(f"{word}: {score:.4f}")
词向量可视化
使用 t-SNE 或 PCA 将高维词向量降维到 2D 进行可视化。
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
import numpy as np
def plot_word_vectors(model, words):
"""可视化词向量"""
# 获取词向量
vectors = []
valid_words = []
for word in words:
if word in model.wv:
vectors.append(model.wv[word])
valid_words.append(word)
vectors = np.array(vectors)
# 使用 t-SNE 降维
tsne = TSNE(n_components=2, random_state=42, perplexity=min(5, len(vectors)-1))
vectors_2d = tsne.fit_transform(vectors)
# 绘图
plt.figure(figsize=(10, 8))
plt.scatter(vectors_2d[:, 0], vectors_2d[:, 1], c='blue', alpha=0.6)
for i, word in enumerate(valid_words):
plt.annotate(word, xy=(vectors_2d[i, 0], vectors_2d[i, 1]),
fontsize=12, ha='center')
plt.title('词向量可视化')
plt.xlabel('维度 1')
plt.ylabel('维度 2')
plt.grid(True, alpha=0.3)
plt.show()
# 可视化示例
words = ['苹果', '香蕉', '水果', '电脑', '手机', '电子', '产品', '工作', '学习']
# plot_word_vectors(model, words)
词向量在下游任务中的应用
文本分类
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
def get_sentence_vector(sentence, model):
"""计算句子向量(词向量平均)"""
vectors = []
for word in sentence:
if word in model.wv:
vectors.append(model.wv[word])
if vectors:
return np.mean(vectors, axis=0)
else:
return np.zeros(model.vector_size)
# 准备数据
texts = [
['苹果', '很', '好吃'],
['香蕉', '味道', '不错'],
['电脑', '运行', '很快'],
['手机', '屏幕', '清晰'],
]
labels = [0, 0, 1, 1] # 0: 水果, 1: 电子产品
# 获取句子向量
X = np.array([get_sentence_vector(t, model) for t in texts])
y = np.array(labels)
# 训练分类器
clf = LogisticRegression()
clf.fit(X, y)
# 预测新句子
new_sentence = ['水果', '新鲜']
new_vector = get_sentence_vector(new_sentence, model).reshape(1, -1)
prediction = clf.predict(new_vector)
print(f"预测类别: {'电子产品' if prediction[0] == 1 else '水果'}")
计算文本相似度
def cosine_similarity(v1, v2):
"""计算余弦相似度"""
return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
def text_similarity(text1, text2, model):
"""计算两段文本的相似度"""
vec1 = get_sentence_vector(text1, model)
vec2 = get_sentence_vector(text2, model)
return cosine_similarity(vec1, vec2)
text1 = ['我', '喜欢', '吃', '水果']
text2 = ['苹果', '是', '一种', '水果']
text3 = ['电脑', '是', '电子', '产品']
sim1 = text_similarity(text1, text2, model)
sim2 = text_similarity(text1, text3, model)
print(f"文本1 和 文本2 的相似度: {sim1:.4f}")
print(f"文本1 和 文本3 的相似度: {sim2:.4f}")
词向量的局限性
尽管词向量在很多任务上表现出色,但也存在一些局限性:
一词多义:传统词向量为每个词分配一个固定向量,无法处理多义词。例如"苹果"可以指水果也可以指公司。
静态表示:词向量是静态的,不随上下文变化。现代预训练模型如 BERT 使用动态词向量解决了这个问题。
需要大量数据:训练高质量的词向量需要大规模语料。
未登录词问题:Word2Vec 和 GloVe 无法处理未登录词,FastText 通过子词信息部分解决了这个问题。
词向量方法对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Word2Vec | 训练速度快,效果好 | 无法处理未登录词 | 通用场景 |
| GloVe | 利用全局统计信息 | 需要构建共现矩阵 | 需要全局语义信息 |
| FastText | 可处理未登录词 | 训练较慢 | 形态丰富语言、OOV 问题 |
总结
词向量是 NLP 的基础技术,将离散的词语映射到连续向量空间,捕捉词语之间的语义关系。本章介绍了:
- Word2Vec:基于预测的词向量方法,包括 CBOW 和 Skip-gram
- GloVe:基于全局统计的词向量方法
- FastText:引入子词信息,可处理未登录词
- 预训练词向量:使用大规模语料预训练的词向量
- 词向量应用:文本分类、相似度计算等
词向量技术为后续的预训练语言模型奠定了基础。下一章将介绍语言模型的发展和技术细节。