自然语言处理之PyTorch实现词袋CBOW模型

发布于:2025-09-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

在自然语言处理(NLP)领域,词向量(Word Embedding)是将文本转换为数值向量的核心技术。它能让计算机“理解”词语的语义关联,例如“国王”和“女王”的向量差可能与“男人”和“女人”的向量差相似。而Word2Vec作为经典的词向量训练模型,其核心思想是通过上下文预测目标词(或反之)。本文将以 --CBOW(连续词袋模型)为例,带你从代码到原理,一步步实现一个简单的词向量训练过程。

一、CBOW模型简介

CBOW(Continuous Bag-of-Words)是Word2Vec的两种核心模型之一。其核心思想是:给定目标词的上下文窗口内的所有词,预测目标词本身。例如,对于句子“We are about to study”,若上下文窗口大小为2(即目标词左右各取2个词),则当目标词是“about”时,上下文是“We, are, to, study”,模型需要根据这4个词预测出“about”。

CBOW的优势在于通过平均上下文词向量来预测目标词,计算效率高;缺点是对低频词不友好。本文将实现的CBOW模型包含词嵌入层、投影层和输出层,最终输出目标词的概率分布。

二、环境准备与数据预处理

2.1 进度条库安装

pip install torch numpy tqdm

2.2 语料库与基础设置

我们使用一段英文文本作为语料库,并定义上下文窗口大小(CONTEXT_SIZE=2,即目标词左右各取2个词):

CONTEXT_SIZE = 2  # 上下文窗口大小(左右各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()  # 按空格分割成单词列表

2.3 构建词汇表与映射

为了将文本转换为模型可处理的数值,需要先构建词汇表(所有唯一词),并为每个词分配唯一索引:

vocab = set(raw_text)  # 去重后的词汇表(集合)
vocab_size = len(vocab)  # 词汇表大小(本文示例中为49)

# 词到索引的映射(如:"We"→0,"are"→1)
word_to_idx = {word: i for i, word in enumerate(vocab)}
# 索引到词的反向映射(如:0→"We",1→"are")
idx_to_word = {i: word for i, word in enumerate(vocab)}

三、生成训练数据:上下文-目标词对

CBOW的训练数据是“上下文词列表”与“目标词”的配对。例如,若目标词是raw_text[i],则上下文是[raw_text[i-2], raw_text[i-1], raw_text[i+1], raw_text[i+2]](假设窗口大小为2)。

3.1 数据生成逻辑

通过遍历语料库,跳过前CONTEXT_SIZE和后CONTEXT_SIZE个词(避免越界),生成上下文-目标词对:

data = []
for i in range(CONTEXT_SIZE, len(raw_text) - CONTEXT_SIZE):
    # 左上下文:取i-2, i-1(j从0到1,2-j对应2,1)
    left_context = [raw_text[i - (2 - j)] for j in range(CONTEXT_SIZE)]
    # 右上下文:取i+1, i+2(j从0到1,i+j+1对应i+1, i+2)
    right_context = [raw_text[i + j + 1] for j in range(CONTEXT_SIZE)]
    context = left_context + right_context  # 合并上下文(共4个词)
    target = raw_text[i]  # 目标词(当前中心词)
    data.append((context, target))

3.2 示例验证

i=2为例:

  • 左上下文:i-2=0(“We”),i-1=1(“are”)→ ["We", "are"]
  • 右上下文:i+1=3(“to”),i+2=4(“study”)→ ["to", "study"]
  • 上下文合并:["We", "are", "to", "study"]
  • 目标词:raw_text[2](“about”)

四、CBOW模型实现(PyTorch)

4.1 模型结构设计

CBOW模型的核心是通过上下文词的词向量预测目标词。模型结构包含三层:

  1. 词嵌入层(Embedding):将词的索引映射为低维稠密向量(如10维)。
  2. 投影层(Linear):将拼接后的词向量投影到更高维度(如128维),增加非线性表达能力。
  3. 输出层(Linear):将投影后的向量映射回词汇表大小,通过Softmax输出目标词的概率分布。
import torch
import torch.nn as nn
import torch.nn.functional as F

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)  # softmax交叉熵
        return nll_prob

五、模型训练与优化

5.1 初始化模型与超参数

设置词向量维度(embedding_dim=10)、学习率(lr=0.001)、训练轮数(epochs=200),并初始化模型、优化器和损失函数:

vocab_size = 49
model = CBOW(vocab_size, 10).to(device)
optimizer=torch.optim.Adam(model.parameters(),lr=0.001)
losses = []# 存储损失的集合  losses: []
loss_function = nn.NLLLoss()        #NLLLoss损失函数(当分类列表非常多的情况),将多个类

5.2 训练循环逻辑

遍历每个训练轮次(epoch),对每个上下文-目标词对进行前向传播、损失计算、反向传播和参数更新:

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)  # 可以不写forward,torch的内置功能,
        loss = loss_function(train_predict, target)  # 计算损失
        # 反向传播
        optimizer.zero_grad()  # 梯度值清零
        loss.backward()  # 反向传播计算得到每个参数的梯度
        optimizer.step()  # 根据梯度更新网络参数

        total_loss += loss.item()

六、词向量提取与应用

训练完成后,模型的词嵌入层(model.embeddings.weight)中存储了每个词的向量表示。我们可以将其提取并保存,用于后续任务(如文本分类、相似度计算)。

6.1 提取词向量

# 将词向量从GPU移至CPU,并转换为NumPy数组
W = model.embeddings.weight.cpu().detach().numpy()
print("词向量矩阵形状:", W.shape)  # (vocab_size, embedding_dim) → (49, 10)

6.2 生成词-向量映射字典

word_2_vec = {}
for word, idx in word_to_idx.items():
    word_2_vec[word] = W[idx]  # 每个词对应词向量矩阵中的一行
print("示例词向量('process'):", word_2_vec["process"])

6.3 保存词向量

使用np.savez保存词向量矩阵,方便后续加载使用:

import numpy as np
np.savez('word2vec实现.npz', word_vectors=W)  # 保存为npz文件

# 加载验证
data = np.load('word2vec实现.npz')
loaded_vectors = data['word_vectors']
print("加载的词向量形状:", loaded_vectors.shape)  # 应与原始矩阵一致

网站公告

今日签到

点亮在社区的每一天
去签到