使用 PyTorch 实现 CBOW 词向量模型


在自然语言处理 (NLP) 领域,词向量表示是一项基础而关键的技术。通过将文本中的词语映射到低维向量空间,我们可以让计算机更好地理解和处理人类语言。今天,我们将深入探讨并实现 CBOW (Continuous Bag-of-Words) 模型,这是一种经典的词向量训练方法。
什么是 CBOW 模型?
CBOW 模型是一种基于上下文预测目标词的神经网络模型,由 Mikolov 等人在 2013 年提出。与 Skip-gram 模型相反,CBOW 使用周围的上下文词来预测中间的目标词。例如,对于句子 "We are about to study",如果我们以 "about" 为目标词,上下文可以是 ["We", "are", "to", "study"]。CBOW 模型的核心思想是:相似上下文的词应该具有相似的语义表示。
PyTorch 实现 CBOW 模型
让我们来分析一下实现 CBOW 模型的 PyTorch 代码。这段代码从数据准备到模型训练,再到词向量的保存和使用,完整地展示了 CBOW 模型的实现过程。
数据准备与预处理
 

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

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

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

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

def make_context_vector(context, word_to_ix):
    idxs = [word_to_ix[w] for w in context]
    return torch.tensor(idxs, dtype=torch.long)

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


在这段代码中,我们首先定义了上下文窗口大小CONTEXT_SIZE为 2,这意味着我们将使用目标词前后各两个词作为上下文。然后,我们将一段示例文本分割成单词列表,并创建了词汇表vocab。接着,我们构建了单词到索引和索引到单词的映射字典,这对后续的数据处理非常重要。
数据准备阶段的核心是构建 (context, target) 对。对于每个目标词,我们提取其前后的上下文词,形成训练样本。函数make_context_vector将上下文词转换为对应的索引张量,方便后续输入到模型中。
模型定义
 

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))
        out = self.output(out)
        nll_prob = F.log_softmax(out, dim=-1)
        return nll_prob


CBOW 模型的结构非常简洁。在初始化函数中,我们定义了三个主要层:
nn.Embedding:将单词索引转换为词向量
第一个线性层proj:将词向量映射到隐藏层
第二个线性层output:将隐藏层表示映射到词汇表大小的输出层
在前向传播过程中,我们首先获取上下文词的嵌入表示,将它们相加得到一个综合表示,然后通过 ReLU 激活函数和线性变换,最后使用 log_softmax 函数得到每个单词的概率分布。
模型训练
 

device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(device)

model = CBOW(vocab_size, 10).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
losses = []
loss_function = nn.NLLLoss()

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)


在训练阶段,我们首先选择合适的计算设备(GPU 或 CPU),然后实例化 CBOW 模型和 Adam 优化器。损失函数使用负对数似然损失NLLLoss,这与我们模型输出的 log_softmax 概率分布是匹配的。
训练循环中,我们遍历每个训练样本,将上下文和目标词转换为张量并移至计算设备,然后进行前向传播、计算损失、反向传播和参数更新。每个 epoch 结束后,我们记录总损失并打印出来,以便监控训练过程。
词向量的使用与保存
 

device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(device)

model = CBOW(vocab_size, 10).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
losses = []
loss_function = nn.NLLLoss()

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)


训练完成后,我们可以使用模型进行预测。给定一个上下文,模型会预测最可能的目标词。此外,我们还可以提取训练得到的词向量矩阵。词向量矩阵保存在模型的embeddings.weight中,我们将其转换为 NumPy 数组,并构建一个单词到向量的映射字典。
最后,我们将词向量矩阵保存到文件中,并演示了如何加载这些词向量数据,以便后续在其他 NLP 任务中使用。
CBOW 模型的应用与意义
CBOW 模型不仅为我们提供了一种获取词向量的方法,更重要的是,它揭示了语言中词与词之间的语义关系。通过训练得到的词向量,我们可以进行诸如词义相似度计算、类比推理等任务。例如,"king - man + woman = queen" 这样的经典类比就是词向量强大语义表示能力的体现。
在实际应用中,预训练的词向量通常作为下游 NLP 任务(如文本分类、命名实体识别、情感分析等)的基础输入,能够显著提高模型的性能和训练效率。
通过这篇博客,我们详细了解了 CBOW 模型的原理和 PyTorch 实现方法。希望这对你理解词向量技术和神经网络在 NLP 中的应用有所帮助。如果你对代码有任何疑问或想要进一步探索相关内容,欢迎留言讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值