初识自然语言处理——从CBOW词嵌入到单词预测

一、引言:让计算机理解词语

在之前的博客中,我们学习了CNN处理图像、迁移学习复用预训练模型。但有一类数据比图像更贴近人类的日常——文本

然而,计算机不认识苹果、香蕉这些词,它只认识数字。如何把词语变成计算机能处理的数字?如何让计算机理解苹果和香蕉都是水果,而苹果和汽车关系较远?

这就是自然语言处理(NLP) 的核心问题之一——词嵌入(Word Embedding)

词嵌入是将词语映射到低维稠密向量空间的技术。在这个空间里,语义相近的词距离更近。比如,猫和狗的向量会很接近,而"猫"和"汽车"的向量则相距较远。

本篇博客将基于PyTorch,从零实现一个CBOW(Continuous Bag of Words) 模型,学习词嵌入,并完成一个单词预测系统。同时,我们还会学习如何使用NumPy保存和加载词向量文件。

二、什么是CBOW

2.1 CBOW的核心思想

CBOW是Word2Vec的两种训练方式之一(另一种是Skip-gram)。它的核心思想非常直观:

用上下文预测中心词

给定一句话:"We are about to study the idea of a computational process"

如果中心词是"study",上下文窗口大小为2,那么:

  • 上下文词:["about", "to", "the", "idea"]

  • 目标词:"study"

CBOW模型接收上下文词作为输入,输出对中心词的预测。训练完成后,模型学到的词向量(embedding权重)就包含了词语的语义信息。

2.2 CBOW vs Skip-gram

对比项CBOWSkip-gram
输入上下文词中心词
输出中心词上下文词
训练速度
小数据集表现一般
罕见词表现一般

三、数据准备

3.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)}
idx_to_word = {i: word for i, word in enumerate(vocab)}

关键点

  • raw_text.split():按空格分词,得到单词列表

  • set(raw_text):去重,得到词汇表

  • word_to_idxidx_to_word:实现词与索引的相互转换

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

逻辑解析

  • 遍历每个可以作为中心词的位置(从第2个到倒数第2个)

  • context:取中心词前后各2个词,共4个上下文词

  • target:中心词

  • 每个样本为 (context, target) 的形式

示例:对于 "We are about to study the idea..."

i=4("study"):context = ["about", "to", "the", "idea"],target = "study"

3.3 上下文向量转换

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

将上下文单词列表转为索引张量,作为模型输入。

四、CBOW模型定义

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.embedding = nn.Embedding(vocab_size, embedding_dim)  # 词嵌入层
        self.proj = nn.Linear(embedding_dim, 120)                  # 投影层
        self.output = nn.Linear(120, vocab_size)                   # 输出层

    def forward(self, inputs):
        # 1. 获取上下文词的嵌入向量并求平均
        embeds = sum(self.embedding(inputs)).view(1, -1) / len(inputs)
        # 2. 投影 + ReLU激活
        out = F.relu(self.proj(embeds))
        # 3. 输出层
        out = self.output(out)
        # 4. log_softmax(配合NLLLoss使用)
        nll_prob = F.log_softmax(out, dim=-1)
        return nll_prob

逐层解析

作用输入维度输出维度
embedding将词索引映射为稠密向量vocab_sizeembedding_dim
proj投影到隐藏层embedding_dim120
output输出每个词的概率120vocab_size

关键操作

  • self.embedding(inputs):输入4个上下文词的索引,得到4个嵌入向量

  • sum(...) / len(inputs):对4个向量求平均,得到上下文表示

  • .view(1, -1):调整形状为 (1, embedding_dim)

  • F.log_softmax(out, dim=-1):将输出转为对数概率分布

五、模型训练

device = "cuda" if torch.cuda.is_available() else "cpu"
model = CBOW(vocab_size, 10).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)

losses = []
loss_fn = nn.NLLLoss()
model.train()

for epoch in tqdm(range(200)):
    total_loss = 0
    for context, target in data:
        context_vec = make_context_vector(context, word_to_idx).to(device)
        target = torch.tensor([word_to_idx[target]]).to(device)

        train_pred = model(context_vec)
        loss = loss_fn(train_pred, target)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    losses.append(total_loss)

训练流程

  1. 将上下文转为索引张量

  2. 前向传播,得到预测分布

  3. 计算NLL损失

  4. 梯度清零、反向传播、更新参数

  5. 累加损失,记录每个epoch的总损失

tqdm的作用:显示训练进度条,方便观察训练状态。

六、词嵌入提取与保存

6.1 提取词向量

训练完成后,model.embedding.weight 就是所有词的嵌入矩阵:

model.eval()
W = model.embedding.weight.cpu().detach().numpy()

word_2_vec = {}
for word in word_to_idx.keys():
    word_2_vec[word] = W[word_to_idx[word], :]

W的形状(vocab_size, embedding_dim),每行对应一个词的向量。

6.2 保存为NPZ文件

np.savez("word_2_vec.npz", file1=W)
data = np.load("word_2_vec.npz")
print(data)

七、NumPy文件保存与加载

代码中还包含了两个演示NumPy文件操作的示例。

7.1 保存单个数组(.npy)

import numpy as np

a = np.random.randint(5, size=(2, 4))
np.save("test.npy", a)

b = np.load("test.npy")
print(b)

特点

  • .npy 格式保存单个数组

  • 加载后直接得到数组

7.2 保存多个数组(.npz)

a = np.random.randint(0, 10, (3,), dtype="int")
b = np.random.randint(0, 10, (3,), dtype="int")
c = np.random.randint(0, 10, (3,), dtype="int")

np.savez("test.npz", a=a, b=b, c=c)

data = np.load("test.npz")
print(data.files)          # ['a', 'b', 'c']
aa = data[data.files[0]]
bb = data[data.files[1]]
cc = data[data.files[2]]

特点

  • .npz 格式可保存多个命名数组

  • 加载后类似字典,通过 data.files 查看所有键

  • 通过 data['key'] 获取对应数组

7.3 加载预训练词向量

d = np.load("../embedding_Tencent.npz")
e = d["embeddings"]
print("Tencent的词向量为:", e)

八、实战:单词预测系统

nlp_work.py 将CBOW模型应用到一个实际任务——给定连续4个单词,预测第5个单词

8.1 任务描述

读取 AI报道.txt,训练模型后:

  • 输入:"the rise of AI"

  • 输出:"also"

8.2 代码要点

CONTEXT_SIZE = 5

with open('AI报道.txt', 'r') as f:
    raw_text = f.readline().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-(5-j)] for j in range(CONTEXT_SIZE)]
    target = raw_text[i]
    data.append((context, target))

8.3 预测

context = ["the", "rise", "of", "AI"]
context_vector = make_context_vector(context, word_to_idx).to(device)

model.eval()
pred = model(context_vector)
max_idx = pred.argmax(1)
print(idx_to_word[int(max_idx)])

argmax(1):取输出概率最大的索引,即模型认为最可能的下一个词。

九、总结

本篇博客围绕CBOW词嵌入,系统讲解了:

知识点核心内容
词嵌入将词语映射为稠密向量
CBOW模型用上下文预测中心词
Embedding层nn.Embedding 实现词向量查表
NLLLoss配合 log_softmax 计算分类损失
词向量提取model.embedding.weight
NumPy保存.npy 单数组,.npz 多数组
单词预测输入4个词,输出第5个词
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值