跳到主要内容

词向量

词向量(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_size100-300维度越高表达能力越强,但计算成本增加
window5小窗口捕捉语法关系,大窗口捕捉语义关系
sg1 (Skip-gram)Skip-gram 在小数据集上效果更好
negative5-20负采样数量,数据量大时可增大
min_count5-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:引入子词信息,可处理未登录词
  • 预训练词向量:使用大规模语料预训练的词向量
  • 词向量应用:文本分类、相似度计算等

词向量技术为后续的预训练语言模型奠定了基础。下一章将介绍语言模型的发展和技术细节。