遵义市网站建设_网站建设公司_MySQL_seo优化
2026/1/8 21:12:01 网站建设 项目流程

文章目录

  • 一. CBOW模型详解
    • 1.1 Word2Vec与分布式表示
    • 1.2 CBOW模型原理
      • 数学表达
    • 1.3 网络架构详解
      • 代码中的网络层说明:
    • 1.4 训练目标与优化
    • 1.5 CBOW 与 Skip-gram 比较
    • 1.6 词向量的应用与提取
  • 二. 数据准备与预处理
    • 2.1 语料库与基本参数设置
    • 2.2 构建词汇表
    • 2.3 构建训练数据集
    • 2.4 上下文向量化函数
  • 三. CBOW模型构建
    • 3.1 模型类定义
  • 四. 模型训练
    • 4.1 设备配置与模型初始化
    • 4.2 训练循环
  • 五. 模型测试与词向量提取
    • 5.1 模型测试
    • 5.2 词向量提取与保存

一. CBOW模型详解

1.1 Word2Vec与分布式表示

Word2Vec是Google在2013年提出的一种高效学习词向量的模型,它包含两种主要架构:连续词袋模型(CBOW)和跳字模型(Skip-gram)。这两种模型都基于一个核心思想:“一个词的含义可以通过它周围出现的词来定义”,这被称为分布式假设。

在代码实现中,我们通过构建(context, target)训练对来体现这一思想:

foriinrange(CONTEXT_SIZE,len(raw_text)-CONTEXT_SIZE):context=([raw_text[i-(2-j)]forjinrange(CONTEXT_SIZE)]+[raw_text[i+j+1]forjinrange(CONTEXT_SIZE)])# 获取上下文词target=raw_text[i]# 获取目标词data.append((context,target))# 构建训练样本

与传统one-hot编码相比,Word2Vec生成的词向量具有以下优势:

  • 低维稠密:代码中设置embedding_dim=10,将49维的稀疏one-hot压缩为10维稠密向量
  • 语义信息:相似的词在向量空间中距离较近
  • 数学运算:支持向量运算,如"国王 - 男人 + 女人 ≈ 女王"

1.2 CBOW模型原理

CBOW模型的核心思想是通过上下文词汇预测中心词。具体来说:

输入:上下文窗口中所有词的one-hot表示(或索引)
输出:中心词的概率分布
目标:最大化正确中心词的对数似然

数学表达

给定上下文词序列C = { w t − 2 , w t − 1 , w t + 1 , w t + 2 } C = \{w_{t-2}, w_{t-1}, w_{t+1}, w_{t+2}\}C={wt2,wt1,wt+1,wt+2},CBOW模型试图预测中心词w t w_twt

P ( w t ∣ C ) = exp ⁡ ( v w t T ⋅ v ˉ C ) ∑ w ∈ V exp ⁡ ( v w T ⋅ v ˉ C ) P(w_t | C) = \frac{\exp(v_{w_t}^T \cdot \bar{v}_C)}{\sum_{w \in V} \exp(v_w^T \cdot \bar{v}_C)}P(wtC)=wVexp(vwTvˉC)exp(vwtTvˉC)

其中:

  • v ˉ C \bar{v}_CvˉC是上下文词向量的平均(或求和)
  • v w v_{w}vw是词w ww的输出向量
  • V VV是词汇表

在代码中,这一原理体现在CBOW模型的forward方法:

defforward(self,inputs):embeds=sum(self.embeddings(inputs)).view(1,-1)# 对上下文词向量求和out=F.relu(self.proj(embeds))out=self.output(out)nl_l_prob=F.log_softmax(out,dim=-1)# 计算对数概率returnnl_l_prob

1.3 网络架构详解

CBOW模型通常包含以下三层:

  1. 输入层:上下文词的索引表示,代码中使用make_context_vector函数转换
  2. 投影层(隐藏层):共享的嵌入矩阵,将索引映射为稠密词向量
  3. 输出层:线性层将隐藏层表示映射回词汇表空间

代码中的网络层说明:

classCBOW(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)# 输出层

1.4 训练目标与优化

CBOW的训练目标是最大化给定上下文时正确中心词的条件概率:

L = ∑ t = 1 T log ⁡ P ( w t ∣ w t − m , . . . , w t − 1 , w t + 1 , . . . , w t + m ) \mathcal{L} = \sum_{t=1}^{T} \log P(w_t | w_{t-m}, ..., w_{t-1}, w_{t+1}, ..., w_{t+m})L=t=1TlogP(wtwtm,...,wt1,wt+1,...,wt+m)

在代码中,我们使用负对数似然损失(NLLLoss):

loss_function=nn.NLLLoss()# 负对数似然损失# 在训练循环中train_predict=model(context_vector)loss=loss_function(train_predict,target)# 计算损失

训练过程采用反向传播和Adam优化器:

optimizer=optim.Adam(model.parameters(),lr=0.001)# Adam优化器# 反向传播optimizer.zero_grad()# 梯度清零loss.backward()# 反向传播optimizer.step()# 参数更新

1.5 CBOW 与 Skip-gram 比较

特性CBOWSkip-gram代码体现
训练速度更快较慢forward中求和操作计算效率高
输入输出多对一一对多多个上下文词预测一个中心词
上下文利用求和/平均独立处理使用sum()聚合上下文信息

1.6 词向量的应用与提取

训练完成后,可以提取词向量用于各种NLP任务。代码中通过以下方式提取和保存词向量:

# 获取Embedding层的权重矩阵W=model.embeddings.weight.cpu().detach().numpy()# 构建词向量字典word_2vec={}forwordinword_to_idx.keys():word_2vec[word]=W[word_to_idx[word],:]# 保存词向量np.savez(r'word2vec实现.npz',file_1=W)

这些词向量可以应用于:

  1. 语义相似度计算:通过余弦相似度比较词向量
  2. 文本分类:作为特征输入分类器
  3. 命名实体识别:提供上下文语义信息

二. 数据准备与预处理

2.1 语料库与基本参数设置

首先,需要定义上下文窗口的大小并准备训练语料:

importtorchimporttorch.nnasnn# 神经网络importtorch.nn.functionalasFimporttorch.optimasoptim#fromtqdmimporttqdm,trange# 显示进度条importnumpyasnp# 设置词左边和右边选择的个数(即上下文词汇个数)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()# 中文的语句,你可以选择分词,也可以选择分字

代码分析

  • CONTEXT_SIZE = 2:定义上下文窗口大小为2,即每个中心词考虑左右各2个上下文词
  • raw_text:示例英文语料库,使用.split()方法按空格分词
  • 注释提示对于中文语料,可以选择分词或分字处理

2.2 构建词汇表

vocab=set(raw_text)# 集合、词库,里面内容去重vocab_size=len(vocab)word_to_idx={word:ifori,wordinenumerate(vocab)}# for循环的复合写法,第1次循环,i得到的索引号,word 第1个单词idx_to_word={i:wordfori,wordinenumerate(vocab)}

代码分析

  • vocab = set(raw_text):创建词汇集合,自动去除重复单词
  • vocab_size = len(vocab):获取词汇表大小
  • word_to_idx:创建单词到索引的映射字典
  • idx_to_word:创建索引到单词的映射字典,用于后续的反向查找

2.3 构建训练数据集

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

代码分析

  • 循环从第3个词开始到倒数第3个词结束(索引从0开始),确保每个中心词都有完整的上下文
  • context列表推导式:前部分获取左侧上下文词,后部分获取右侧上下文词
  • target:当前中心词
  • 最终data列表包含多个(context, target)元组

2.4 上下文向量化函数

defmake_context_vector(context,word_to_ix):# 将上下文词转换为one-hotidxs=[word_to_ix[w]forwincontext]returntorch.tensor(idxs,dtype=torch.long)# 强制类型的转换,将列表print(make_context_vector(data[0][0],word_to_idx))# 示例

代码分析

  • make_context_vector函数:将单词列表转换为对应的索引列表
  • 返回PyTorch张量,数据类型为torch.long,适合作为Embedding层的输入
  • 打印示例:展示如何将上下文词转换为索引张量

三. CBOW模型构建

3.1 模型类定义

classCBOW(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)defforward(self,inputs):embeds=sum(self.embeddings(inputs)).view(1,-1)# cnnout=F.relu(self.proj(embeds))# nn.relu()激活层out=self.output(out)nl_l_prob=F.log_softmax(out,dim=-1)# softmax交叉熵。returnnl_l_prob

代码分析

初始化方法__init__参数

  • vocab_size:词汇表大小,即one-hot向量的维度
  • embedding_dim:词嵌入的维度,将高维one-hot向量压缩到此维度

网络层说明

  • self.embeddings:Embedding层,将单词索引映射为稠密向量
  • self.proj:线性投影层,将词向量维度从embedding_dim映射到128维
  • self.output:输出层,将128维特征映射回词汇表大小维度

前向传播forward方法

  • inputs:上下文词的索引张量
  • sum(self.embeddings(inputs)):对上下文词的词向量求和,体现CBOW的核心思想
  • .view(1, -1):调整张量形状为[1, embedding_dim]
  • F.relu:ReLU激活函数,引入非线性
  • F.log_softmax:log_softmax函数,计算对数概率,与NLLLoss配合使用

四. 模型训练

4.1 设备配置与模型初始化

# 模型在cuda训练device="cuda"iftorch.cuda.is_available()else"mps"iftorch.backends.mps.is_available()else"cpu"print(device)model=CBOW(vocab_size,embedding_dim=10).to(device)# 语料库中一共有49个单词,[000000...1]49->[ ... ]300optimizer=optim.Adam(model.parameters(),lr=0.001)# 优化器

代码分析

  • device:自动检测可用设备(CUDA、MPS或CPU)
  • model:创建CBOW模型实例,词嵌入维度设为10
  • optimizer:使用Adam优化器,学习率为0.001

4.2 训练循环

losses=[]# 存储损失的集合loss_function=nn.NLLLoss()# NLLLoss损失函数(当分类列表非常多的情况),将多个类别分别分成0、1两个类别。这里和log_softmax合在一起就是一个model.train()# 不代表开始训练,模型具备训练的能力,设置一个可写的权限??forepochintqdm(range(200)):# 开始训练total_loss=0forcontext,targetindata: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()losses.append(total_loss)print(losses)

代码分析

  • losses:记录每个epoch的总损失
  • loss_function:负对数似然损失,与log_softmax配合使用
  • model.train():设置模型为训练模式,启用dropout和batch normalization
  • tqdm(range(200)):使用tqdm包装循环,显示训练进度条
  • 内部循环:遍历所有训练样本
  • make_context_vector:将上下文转换为索引张量
  • torch.tensor([word_to_idx[target]]):将目标词转换为索引张量
  • model(context_vector):前向传播获取预测值
  • loss.backward():反向传播计算梯度
  • optimizer.step():更新模型参数

五. 模型测试与词向量提取

5.1 模型测试

# 测试# context = ['People', 'create', 'to', 'direct'] # People create programs to directcontext=["spirits","of","the","computer"]# spirits of the computercontext_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表示每一列中的最大值对应的索引号

代码分析

  • model.eval():设置模型为评估模式,禁用dropout和batch normalization
  • predict.argmax(1):获取预测概率最大值的索引,即预测的中心词

5.2 词向量提取与保存

# 获取词向量,这个Embedding就是我们需要的问题。他只是一个模型的一个中间过程print("CBOW embedding weight=",model.embeddings.weight)# GPUW=model.embeddings.weight.cpu().detach().numpy()# .detach(); 这个方法会创建一个新的Tensor,它和原来的Tensor共享数据,但是不会追踪梯度。# 这意味着这个新的Tensor不会参与梯度的反向传播,这对于防止在计算梯度时意外修改某些参数很有用。print(W)# 生成词嵌入字典,即{单词1:词向量1,单词2:词向量2...}的格式word_2vec={}forwordinword_to_idx.keys():# 词向量矩阵中某个词的索引所对应的那一列即为所该词的词向量word_2vec[word]=W[word_to_idx[word],:]print("end")# 保存训练后的词向量为npz文件'''numpy W 处理矩阵的速度非常快,方便后期其他人项目,要继续使用np.savez(r'word2vec实现.npz',file_1=W)data=np.load(r'word2vec实现.npz')print(data.files)

代码分析

  • model.embeddings.weight:获取Embedding层的权重矩阵,即所有词的词向量
  • .cpu().detach().numpy():将张量转移到CPU,脱离计算图,转换为numpy数组
  • word_2vec字典:构建{单词: 词向量}的映射关系
  • np.savez:将词向量矩阵保存为npz格式,便于后续加载和使用

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询