PyTorch 实战:从零搭建 CBOW 模型,轻松生成高质量词向量

在自然语言处理(NLP)领域,词向量是非常基础且重要的概念,它能将文本中的词语转化为计算机可理解的数值向量。而 CBOW(Continuous Bag-of-Words)模型是获取词向量的经典方法之一,今天我们就来详细介绍如何使用 PyTorch 框架实现 CBOW 模型,并训练得到词向量。​

一、CBOW 模型原理简介​

CBOW 模型的核心思想是根据上下文词语来预测中间的目标词语。比如在句子 “People create programs to direct processes” 中,如果我们把 “programs” 作为目标词,那么它的上下文可以是 “People”“create”“to”“direct”(这里我们设置上下文窗口大小为 2,即目标词左右各 2 个词)。模型通过学习这种上下文与目标词之间的关系,最终得到每个词语对应的词向量。​

二、代码实现步骤详解​

1. 导入所需库​

首先,我们需要导入实现模型所需的各种库,包括 PyTorch 相关库用于构建和训练模型、numpy 用于数据处理、tqdm 用于显示训练进度等。​

import torch.optim as optim
import numpy as np
import torch
import torch.nn.functional as F
from torch import nn
from tqdm import tqdm

2. 数据预处理​

数据预处理是模型训练的基础,这一步我们需要准备训练数据,包括确定上下文窗口大小、处理原始文本、构建词汇表以及创建词语与索引之间的映射关系。​

(1)设置上下文窗口大小和原始文本​

我们设置上下文窗口大小CONTEXT_SIZE = 2,意味着每个目标词的上下文是其左右各 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()

这里使用split()方法将原始文本按空格分割成单词列表。​

(2)构建词汇表和映射关系​

词汇表是所有不重复单词的集合,我们通过set(raw_text)获取。然后分别创建单词到索引(word_to_idx)和索引到单词(idx_to_word)的映射字典,方便后续将单词转换为模型可处理的数值形式。​

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)}​

(3)生成训练数据​

根据上下文窗口大小,我们遍历原始文本单词列表,为每个可能的目标词构建对应的上下文和目标词对,组成训练数据data。​

data = []​

for i in range(CONTEXT_SIZE, len(raw_text) - CONTEXT_SIZE):​

# 构建上下文:左边2个词 + 右边2个词​

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)) # 添加到训练数据​

例如,当i取合适的值时,context就是目标词raw_text[i]左右各 2 个词组成的列表,target就是中间的目标词。​

(4)定义上下文向量转换函数​

该函数用于将上下文单词列表转换为对应的索引张量,以便输入到模型中。​


def make_context_vector(context, word_to_idx): ​

idxs = [word_to_idx[w] for w in context]​

return torch.tensor(idxs, dtype=torch.long)​

​

我们可以通过以下代码测试该函数是否正常工作:​

print(make_context_vector(data[0][0], word_to_idx))​

3. 配置训练设备​

为了提高模型训练速度,我们优先使用 GPU 进行训练,如果没有 GPU 则使用 CPU(这里也考虑了苹果设备的 MPS 加速)。​

device = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'​

print(device)​

​

执行该代码后,会输出当前使用的训练设备。​

4. 构建 CBOW 模型​

使用 PyTorch 的nn.Module类来构建 CBOW 模型,模型主要包括嵌入层(embeddings)、投影层(proj)和输出层(output)。​

(1)模型初始化​

在__init__方法中定义模型的各个层:​

  • 嵌入层:将单词的索引转换为指定维度的词向量,nn.Embedding(vocab_size, embedding_dim)中vocab_size是词汇表大小,embedding_dim是词向量的维度。​
  • 投影层:对嵌入层的输出进行线性变换,这里设置输出维度为 128。​
  • 输出层:将投影层的输出转换为词汇表大小维度的向量,用于后续计算每个单词作为目标词的概率。​
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)  # 输出层

(2)前向传播​

在forward方法中定义模型的前向传播过程:​

  • 首先,通过嵌入层获取上下文单词的词向量,并对这些词向量求和。​
  • 然后,将求和后的结果通过投影层,并使用 ReLU 激活函数进行非线性变换。​
  • 最后,将投影层的输出通过输出层,再经过log_softmax函数计算每个单词的对数概率,方便后续计算损失。​
def forward(self, inputs):​

embeds = sum(self.embeddings(inputs)).view(1, -1) # 对上下文词嵌入求和​

out = F.relu(self.proj(embeds)) # 通过投影层并激活​

out = self.output(out) # 输出层​

nll_probs = F.log_softmax(out, dim=-1) # 对数softmax​

return nll_probs​

(3)初始化模型​

指定词向量维度为 10,并将模型移动到之前配置好的训练设备上。​

model = CBOW(vocab_size, 10).to(device)​

5. 配置优化器和损失函数​

  • 优化器:这里使用 Adam 优化器,学习率设置为 0.001,用于更新模型参数以最小化损失。​
  • 损失函数:由于模型输出的是对数概率,我们使用负对数似然损失函数(nn.NLLLoss())来计算模型的损失。​
optimizer = optim.Adam(model.parameters(), lr=0.001)​

loss_function = nn.NLLLoss()​

​

6. 模型训练​

设置训练轮数为 200,使用tqdm显示训练进度。在每一轮训练中,遍历所有训练数据,进行前向传播计算损失,然后通过反向传播更新模型参数,并累加每一轮的总损失,最后将总损失添加到losses列表中,以便后续观察损失变化情况。​

losses = []​

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)​

在训练过程中,我们可以观察到losses列表中的损失值逐渐减小,这表明模型在不断学习并改善预测效果。​

7. 模型预测​

训练完成后,我们可以使用模型进行预测。这里选择上下文['People', 'create', 'to', 'direct'],将其转换为上下文向量并输入到模型中,模型会输出每个单词作为目标词的对数概率,我们通过argmax(1)获取概率最大的单词对应的索引,从而得到模型预测的目标词。​

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)​

# 可以通过以下代码查看预测的目标词​

# print(idx_to_word[max_idx.item()])​

8. 获取和保存词向量​

(1)获取词向量​

模型训练完成后,嵌入层的权重就是我们需要的词向量。我们可以通过model.embeddings.weight获取嵌入层权重,并将其转换为 numpy 数组,方便后续处理和使用。​

print("CBOW embedding weight=", model.embeddings.weight)​

W = model.embeddings.weight.cpu().detach().numpy() # 将权重从GPU(如果使用)转移到CPU并转换为numpy数组​

print(W)​

​

为了更方便地查看每个单词对应的词向量,我们创建word_2_vec字典,将单词与对应的词向量关联起来。​

​



word_2_vec = {}​

for word in word_to_idx.keys():​

word_2_vec[word] = W[word_to_idx[word], :]​

print('词向量获取完成') # 修正原代码中“jiesu”为有意义的提示信息​

(2)保存词向量​

使用 numpy 的savez方法将词向量保存为 npz 文件,以便后续在其他任务中使用。保存后,我们可以通过load方法加载该文件,并查看文件中的数据名称。​

# 保存训练后的词向量为npz文件​

np.savez('word2vec实现.npz', file_1=W)​

# 加载保存的npz文件​

data = np.load('word2vec实现.npz')​

# 查看文件中的数据名称​

print(data.files)​

# 可以通过以下代码获取保存的词向量​

# saved_W = data['file_1']​

​

三、总结​

通过以上步骤,我们成功使用 PyTorch 实现了 CBOW 模型,并训练得到了词向量。整个过程包括数据预处理、模型构建、模型训练、模型预测以及词向量的获取和保存。CBOW 模型作为获取词向量的经典方法,虽然结构相对简单,但在很多 NLP 任务中都有着广泛的应用。通过本次实践,相信大家对 CBOW 模型的原理和实现有了更深入的理解。​

在实际应用中,我们可以根据具体任务需求调整模型的参数,如词向量维度、上下文窗口大小、学习率、训练轮数等,以获得更好的词向量效果。同时,训练得到的词向量可以用于文本分类、情感分析、机器翻译等多种 NLP 任务,为后续的模型训练提供有力的支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值