基于CBOW模型的词向量训练实战:从原理到PyTorch实现
在自然语言处理(NLP)领域,词向量是将单词映射为计算机可处理的数值向量的重要方式。通过词向量,单词之间的语义关系能够以数学形式表达,为后续的文本分析、机器翻译、情感分析等任务奠定基础。本文将结合连续词袋模型(CBOW),详细介绍如何使用PyTorch训练词向量,并通过具体代码实现和分析训练过程。
一、CBOW模型原理简介
CBOW(Continuous Bag-of-Words)模型是一种用于生成词向量的神经网络模型,它基于上下文预测目标词。其核心思想是:给定一个目标词的上下文单词,通过模型预测该目标词。在训练过程中,模型会不断调整参数,使得预测结果尽可能接近真实的目标词,最终训练得到的词向量能够捕捉单词之间的语义关系。
例如,在句子 “People create programs to direct processes” 中,如果目标词是 “programs”,CBOW模型会利用其上下文单词 “People”、“create”、“to”、“direct” 来预测 “programs”。通过大量类似样本的训练,模型能够学习到单词之间的语义关联,从而生成有效的词向量。
二、代码实现与详细解析
下面我会逐行解释你提供的代码,此代码借助 PyTorch 实现了一个连续词袋模型(CBOW)来学习词向量。
1. 导入必要的库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from tqdm import tqdm, trange # 显示进度条
import numpy as np
torch
:PyTorch 深度学习框架的核心库。torch.nn
:用于构建神经网络的模块。torch.nn.functional
:提供了许多常用的函数,像激活函数等。torch.optim
:包含各种优化算法。tqdm
和trange
:用于在训练过程中显示进度条。numpy
:用于处理数值计算和数组操作。
2. 定义上下文窗口大小和原始文本
CONTEXT_SIZE = 2
raw_text = """We are about to study the idea of a computational process.
Computational processes are abstract beings that inhabit computers.
As they evolve, processes manipulate other abstract things called data.
The evolution of a process is directed by a pattern of rules called a program.
People create programs to direct processes.
In effect,we conjure the spirits of the computer with our spells.""".split()
CONTEXT_SIZE
:上下文窗口的大小,意味着在预测目标词时,会考虑其前后各CONTEXT_SIZE
个单词。raw_text
:原始文本,将其按空格分割成单词列表。
3. 构建词汇表和索引映射
vocab = set(raw_text) # 集合,词库,里面的内容独一无二(将文本中所有单词去重后得到的词汇表)
vocab_size = len(vocab) # 词汇表的大小
word_to_idx = {word: i for i, word in enumerate(vocab)} # 单词到索引的映射字典
idx_to_word = {i: word for i, word in enumerate(vocab)} # 索引到单词的映射字典
vocab
:把原始文本中的所有单词去重后得到的词汇表。vocab_size
:词汇表的大小。word_to_idx
:将单词映射为对应的索引。idx_to_word
:将索引映射为对应的单词。
4. 构建训练数据集
data = [] # 获取上下文词,将上下文词作为输入,目标词作为输出,构建训练数据集(用于存储训练数据,每个元素是一个元组,包含上下文词列表和目标词)
for i in range(CONTEXT_SIZE, len(raw_text) - CONTEXT_SIZE):
context = (
[raw_text[i - (2 - j)] for j in range(CONTEXT_SIZE)]
+ [raw_text[i + j + 1] for j in range(CONTEXT_SIZE)]
) # 获取上下文词
target = raw_text[i] # 获取目标词
data.append((context, target)) # 将上下文词和目标词保存到 data 中
data
:用于存储训练数据,每个元素是一个元组,包含上下文词列表和目标词。- 通过循环遍历原始文本,提取每个目标词及其上下文词,然后将它们添加到
data
中。
5. 定义将上下文词转换为张量的函数
def make_context_vector(context, word_to_ix): # 将上下词转换为 one - hot
idxs = [word_to_ix[w] for w in context]
return torch.tensor(idxs, dtype=torch.long)
make_context_vector
:把上下文词列表转换为对应的索引张量。
6. 打印第一个上下文词的索引张量
print(make_context_vector(data[0][0], word_to_idx))
- 打印第一个训练样本的上下文词对应的索引张量。
7. 定义 CBOW 模型
class CBOW(nn.Module): # 神经网络
def __init__(self, vocab_size, embedding_dim):
super(CBOW, self).__init__()
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
self.proj = nn.Linear(embedding_dim, 128)
self.output = nn.Linear(128, vocab_size)
def forward(self, inputs):
embeds = sum(self.embeddings(inputs)).view(1, -1)
out = F.relu(self.proj(embeds)) # nn.relu() 激活层
out = self.output(out)
nll_prob = F.log_softmax(out, dim=1)
return nll_prob
CBOW
:继承自nn.Module
,定义了 CBOW 模型的结构。__init__
:初始化模型的层,包含一个嵌入层、一个线性层和另一个线性层。forward
:定义了前向传播过程,将输入的上下文词索引转换为嵌入向量,求和后经过线性层和激活函数,最后输出对数概率。
8. 选择设备并创建模型实例
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device") # 字符串的格式化
model = CBOW(vocab_size, 10).to(device)
device
:检查当前设备是否支持 GPU(CUDA 或 MPS),若支持则使用 GPU,否则使用 CPU。model
:创建 CBOW 模型的实例,并将其移动到指定设备上。
9. 定义优化器、损失函数和损失列表
optimizer = optim.Adam(model.parameters(), lr=0.001) # 创建一个优化器,
losses = [] # 存储损失的集合
loss_function = nn.NLLLoss()
optimizer
:使用 Adam 优化器来更新模型的参数。losses
:用于存储每个 epoch 的损失值。loss_function
:使用负对数似然损失函数。
10. 训练模型
model.train()
for epoch in tqdm(range(200)):
total_loss = 0
for context, target in data:
context_vector = make_context_vector(context, word_to_idx).to(device)
target = torch.tensor([word_to_idx[target]]).to(device)
# 开始向前传播
train_predict = model(context_vector)
loss = loss_function(train_predict, target)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
losses.append(total_loss)
print(losses)
model.train()
:将模型设置为训练模式。- 通过循环进行 200 个 epoch 的训练,每个 epoch 遍历所有训练数据。
- 将上下文词和目标词转换为张量并移动到指定设备上。
- 进行前向传播得到预测结果。
- 计算损失。
- 进行反向传播并更新模型参数。
- 累加每个 epoch 的损失值。
11. 进行预测
context = ['People', 'create', 'to', 'direct']
context_vector = make_context_vector(context, word_to_idx).to(device)
model.eval() # 进入到测试模式
predict = model(context_vector)
max_idx = predict.argmax(1) # dim = 1 表示每一行中的最大值对应的索引号, dim = 0 表示每一列中的最大值对应的索引号
print("CBOW embedding weight =", model.embeddings.weight) # GPU
W = model.embeddings.weight.cpu().detach().numpy()
print(W)
- 选择一个上下文词列表进行预测。
model.eval()
:将模型设置为评估模式。- 进行预测并获取预测结果中概率最大的索引。
- 打印嵌入层的权重,并将其转换为 NumPy 数组。
12. 构建词向量字典
word_2_vec = {}
for word in word_to_idx.keys():
word_2_vec[word] = W[word_to_idx[word], :]
print('jiesu')
word_2_vec
:将每个单词映射到其对应的词向量。
13. 保存和加载词向量
np.savez('word2vec实现.npz', file_1 = W)
data = np.load('word2vec实现.npz')
print(data.files)
np.savez
:将词向量保存为.npz
文件。np.load
:加载保存的.npz
文件,并打印文件中的数组名称。
综上所述,这段代码实现了一个简单的 CBOW 模型来学习词向量,并将学习到的词向量保存到文件中。 。运行结果
三、总结
通过上述代码的实现和分析,我们成功地使用CBOW模型在PyTorch框架下完成了词向量的训练。从数据准备、模型定义,到训练和测试,再到词向量的保存,每一个步骤都紧密相连,共同构建了一个完整的词向量训练流程。
CBOW模型通过上下文预测目标词的方式,能够有效地学习到单词之间的语义关系,生成的词向量可以应用于各种自然语言处理任务。在实际应用中,我们还可以通过调整模型的超参数(如词向量维度、上下文窗口大小、训练轮数等),以及使用更大规模的数据集,进一步优化词向量的质量和模型的性能。希望本文的内容能够帮助读者更好地理解CBOW模型和词向量训练的原理与实践。