一、Word2Vec
1.1简介
Word2Vec:一种基于神经网络的词嵌入技术,它的目标是将单词映射到实数向量空间中,使得语义相似的单词在向量空间中的距离较近。这种映射是通过在大规模文本语料库上训练神经网络模型来实现的。Word2Vec通过学习单词的共现信息,能够在向量空间中模拟出有意义的语义关系,如同义词、反义词等。
本质是一个神经网络模型,目的是通过模型训练学习得到一个矩阵(Matrix C),一个表示词与词之间关联关系的矩阵。

二、CBOW
2.1简介
CBOW(连续词袋模型):
CBOW(Continuous Bag of Words,连续词袋模型)与Skip-Gram相反,CBOW模型通过给定的上下文单词来预测中心词。具体来说,对于文本中的每一个中心词,CBOW模型会将其周围一定窗口大小内的其他单词(即上下文单词)作为输入,并尝试预测该中心词。
CBOW模型类似于一个高级的完型填空游戏,其中上下文中的词汇(已知选项)被用来"填空"预测出缺失的中心词(答案),从而揭示词汇间的语义联系和模式。
2.2结构
输入层:输入上下文单词的one-hot编码。
嵌入层:将上下文单词的one-hot编码转换为词向量,并将这些词向量进行平均或求和,得到上下文向量。
输出层:输出层同样使用softmax函数输出中心词的概率分布。

2.3 one-hot编码
one-hot 编码是一种常用于将类别数据(如单词)转换为机器学习模型可以处理的数值形式的方法。one-hot 编码会为每个类别分配一个全为零的向量,除了该类别的索引位置为1之外。因为单词通常被视为离散的类别,所以这种方法在处理文本数据时特别有用。
2.4词嵌入
假设现在统计词料库中的所有词的个数围殴4960个,按顺序依次给每个词进行one-hot编码,例如第1个词为:1,0,0,0,0,0,0,....,0,最后1个词为: 0,0,0,0,0,0,0,....,1
此时出现了一个问题:矩阵为非常稀疏,出现维度灾难
例如有一句话为"我爱北京天安门",传入神经网络输入层的数据为:
1,0,0,0,0,0,0,....,0
0,1,0,0,0,0,0,....,0
0,0,1,0,0,0,0,....,0
0,0,0,1,0,0,0,....,0(4 * 4960)
那么该怎么解决这个问题呢?
方案:通过神经网络训练,将每个词都映射到一个较短的词向量上来。
这个较短的词向量维度是多大呢? 一般需要在训练时自己来指定。现在很常见的例如300维。
例如有一句话为"我爱北京天安门",通过神经网络训练后的数据为:
0.62,0.12,0.01,0,0,0,0,....,0
0.1,0.12,0.001,0,0,0,0,....,0
0,0,0.01,0.392,0.39, 0,....,0
0,0,0,1,0,0.01,0.123,....,0.11(4 * 300)
这种将高维度的词表示转换为低维度的词表示的方法,我们称之为词嵌入(word embedding)
2.5 CBOW原理
BOW模型的核心思想是通过上下文中的词来预测目标词。具体而言,CBOW模型通过将上下文中的词的词向量相加,得到一个上下文向量,然后通过一个隐藏层将上下文向量映射到目标词的概率分布。在训练过程中,CBOW模型通过最大化预测目标词的概率来学习到每个词的词向量表示。
2.6 模型训练步骤
1、当前词的上下文词语的one-hot编码输入到输入层。
2、这些词分别乘以同一个矩阵WV*N后分别得到各自的1*N 向量。
3、将多个这些1*N 向量取平均为一个1*N 向量。
4、将这个1*N 向量乘矩阵 W'V*N ,变成一个1*V 向量。
5、将1*V 向量softmax归一化后输出取每个词的概率向量1*V
6、将概率值最大的数对应的词作为预测词。
7、将预测的结果1*V 向量和真实标签1*V 向量(真实标签中的V个值中有一个是1,其他是0)计算误差
8、在每次前向传播之后反向传播误差,不断调整 WV*N和 W'V*N矩阵的值。
示例:
假定语料库中一共有4960个词,则词编码为4960个01组合 现在压缩为300维
依据下图



即4960 * 300
2.7 代码示例
python
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from tqdm import tqdm
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 effecte,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循环的复合写法,第一次循环,i得到索引号,word得到第一个单词
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))
#在cuda中训练
device= "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
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, 128)
self.output = nn.Linear(128, vocab_size)
def forward(self,inputs):
embeds = sum(self.embedding(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
model = CBOW(vocab_size, 10).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
losses = []
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) # 可以不写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)
#测试
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表示每一列中的最大值对应的索引号
# 获取词向量,这个Embedding就是我们需要的词向量,他只是一个模型的一个中间过程
print('CBOW embedding weights:',model.embedding.weight)
w = model.embedding.weight.cpu().detach().numpy() #.detach(:这个法会创建一个新Tensor,原来的Tensor共享数#这意味着这个新的Tensor不会参与梯度的反向传播,这对于防止在计算梯度时意外修改某些参数很有用。
print(w)
#生成词嵌入字典,即{单词1:词向量1,单词2:词向量2...}的格式
word_2_vec = {}
for word in word_to_idx.keys():
# 词向量矩阵中某个词的索引所对应的那一列即为所该词的词向量
word_2_vec[word] = w[word_to_idx[word],:]
print('over')
##1保存训练后的词向量为npz文件''numpy w 处理矩阵的速度非常快,方便后期其他人项目,要继续使用
np.savez('word2vec.npz',file_1 = w)
data = np.load('word2vec.npz')
print(data.files)
对于这段代码:实现了一个简单的CBOW模型,用于学习词向量表示。代码首先准备数据,构建词汇表并生成训练数据集。然后定义了CBOW模型类,包括Embedding层、线性映射层和输出层。接着使用Adam优化器 和负对数似然损失函数进行模型训练,迭代多个epoch。最后,给定上下文词列表,模型预测目标词的词向量表示,并将训练得到的词向量保存为npz文件。整体而言,这段代码展示了如何使用PyTorch实现CBOW模型进行词向量表示学习。