引言

自然语言处理(NLP)是现代人工智能的重要领域之一,而词嵌入(word embedding)是NLP的基础技术。本文将介绍如何使用PyTorch实现连续词袋模型(CBOW)来预测文本中的单词。

一、什么是CBOW模型?

连续词袋模型(Continuous Bag-of-Words, CBOW)是一种用于学习词嵌入的神经网络模型。与Skip-gram模型不同,CBOW通过上下文单词来预测目标单词。它的特点是:

  1. 输入是上下文单词(通常取目标单词前后各n个单词)
  2. 输出是预测的目标单词
  3. 训练目标是最大化正确预测目标单词的概率

二、代码实现

1. 准备数据

首先我们需要准备语料库并构建词汇表:

CONTEXT_SIZE = 2    # 设置词左边和右边选择的个数
raw_text = """We are about to study the idea of a computational process...""".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)}

2. 构建训练数据

我们需要将原始文本转换为模型可以理解的训练数据:

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

3. 定义CBOW模型

我们使用PyTorch的nn.Module来定义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))
        out = self.output(out)
        nll_prob = F.log_softmax(out, dim=-1)
        return nll_prob

4. 训练模型

设置好优化器和损失函数后,我们就可以开始训练模型了:

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

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)

5. 使用模型进行预测

训练完成后,我们可以使用模型来预测单词:

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)
predicted_word = idx_to_word[max_idx.item()]
print(f"根据这些词:{context}")
print(f"预测到下一个词是: {predicted_word}")
  • 预测结果如下:

在这里插入图片描述

三、完整代码

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

#任务:已经有了语料库,1、构造训练数据集,(单词,词库)
#真实的单词模型,每一个单词的词性,你训练大量的输入文本
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)}#for循环的复合写法,第1次循环,i得到的索引号,word 第1个单词
idx_to_word = {i:word for i,word in enumerate(vocab)}

data = []   #获取上下文词,将上下文词作为输入,目标词作为输出。构建训练数据集
for i in range(CONTEXT_SIZE,len(raw_text) - CONTEXT_SIZE):  #(2,60)
    context = (
        [raw_text[i - (2-j)] for j in range(CONTEXT_SIZE)]
        + [raw_text[i + j + 1] for j in range(CONTEXT_SIZE)]
            #获取上下文词(['we','are','to','study'])
        )
    target = raw_text[i] #获取目标词'about'
    data.append((context,target))#将上下文词和目标词保存到data中

def make_context_vector(context,word_tp_ix):#将上下文词转换为one-hot
    idxs = [word_tp_ix[w] for w in context]
    return  torch.tensor(idxs,dtype=torch.long)

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

class CBOW(nn.Module):  #神经网络
    def __init__(self,vocab_size,embedding_dim):
        super(CBOW,self).__init__()
        self.embeddings = nn.Embedding(vocab_size,embedding_dim) #vocab_size:词嵌入的one-hot大小。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

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)  #语料库中一共有49个单词

optimizer = optim.Adam(model.parameters(),lr=0.001)

losses = [] #存储损失的集合
loss_function = nn.NLLLoss()        #NLLLoss损失函数(当分类列表非常多的情况),将多个类别分成0、1两个类别
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)

#测试
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表示每一列中的最大值对应的索引号
predicted_word = idx_to_word[max_idx.item()]  # 用 .item() 从张量中提取标量值
print(f"根据这些词:{context}")
print(f"预测到下一个词是: {predicted_word}")

四、总结

通过这个简单的CBOW模型实现,我们能够:

  1. 理解词嵌入的基本概念
  2. 学习如何使用PyTorch构建神经网络模型
  3. 掌握自然语言处理中的上下文预测方法

CBOW模型虽然简单,但它是理解现代NLP技术的重要基础。在实际应用中,我们可以通过调整上下文窗口大小、嵌入维度等参数,或者使用更大的语料库来获得更好的效果。

希望这篇博客能帮助你理解CBOW模型的原理和实现方法!

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐