1. Skip-Gram介绍
Skip-gram模型是Word2Vec模型的一种训练方法,它的目标是通过目标词预测上下文词。Skip-gram模型通过神经网络结构来学习每个单词的向量表示。
在Skip-gram模型中,每个单词被表示为一个固定维度的向量,该向量称为嵌入向量或词向量。模型通过对训练语料中的每个中心词进行预测,来学习得到这些词向量。
训练过程中,Skip-gram模型的输入是一个中心词,目标是预测该词周围窗口内的上下文词。例如,给定一个句子"the cat sat on the mat"和窗口大小为1,中心词"sat"会被输入到模型中,而目标是预测"the"和"on"这两个上下文词。为了完成这个预测任务,模型通过调整词向量的参数来优化预测结果。
Skip-gram模型的训练目标是最大化预测上下文词的概率。具体而言,模型通过在中心词和上下文词之间计算余弦相似度,将预测问题转化为一个二分类问题。通过多次迭代训练,模型会学习到每个单词的向量表示,使得上下文语境相似的单词在向量空间中的距离更近。
Skip-gram模型具有以下优势:能够处理大规模的语料库、能捕捉到词之间的语义关系、词向量具有相对较低的维度、可以处理生僻词等。因此,Skip-gram模型被广泛应用于自然语言处理领域的词嵌入任务中。
2. Skip-Gram代码实战
2.1 定义一个句子列表,后面会用这些句子来训练 CBOW 和 Skip-Gram 模型
这个其实和CBOW的处理过程一样的
# 定义一个句子列表,后面会用这些句子来训练 CBOW 和 Skip-Gram 模型
sentences = ["Kage is Teacher", "Mazong is Boss", "Niuzong is Boss",
"Xiaobing is Student", "Xiaoxue is Student",]
# 将所有句子连接在一起,然后用空格分隔成多个单词
words = ' '.join(sentences).split()
# 构建词汇表,去除重复的词
word_list = list(set(words))
# 创建一个字典,将每个词映射到一个唯一的索引
word_to_idx = {word: idx for idx, word in enumerate(word_list)}
# 创建一个字典,将每个索引映射到对应的词
idx_to_word = {idx: word for idx, word in enumerate(word_list)}
voc_size = len(word_list) # 计算词汇表的大小
print(" 词汇表:", word_list) # 输出词汇表
print(" 词汇到索引的字典:", word_to_idx) # 输出词汇到索引的字典
print(" 索引到词汇的字典:", idx_to_word) # 输出索引到词汇的字典
print(" 词汇表大小:", voc_size) # 输出词汇表大小
2.2 生成 Skip-Gram 训练数据
# 生成 Skip-Gram 训练数据
def create_skipgram_dataset(sentences, window_size=2):
data = [] # 初始化数据
for sentence in sentences: # 遍历句子
sentence = sentence.split() # 将句子分割成单词列表
for idx, word in enumerate(sentence): # 遍历单词及其索引
# 获取相邻的单词,将当前单词前后各 N 个单词作为相邻单词
for neighbor in sentence[max(idx - window_size, 0):
min(idx + window_size + 1, len(sentence))]:
if neighbor != word: # 排除当前单词本身
# 将相邻单词与当前单词作为一组训练数据
data.append((neighbor, word))
return data
# 使用函数创建 Skip-Gram 训练数据
skipgram_data = create_skipgram_dataset(sentences)
# 打印未编码的 Skip-Gram 数据样例(前 3 个)
print("Skip-Gram 数据样例(未编码):", skipgram_data[:3])
2.3 定义 One-Hot 编码函数
# 定义 One-Hot 编码函数
import torch # 导入 torch 库
def one_hot_encoding(word, word_to_idx):
tensor = torch.zeros(len(word_to_idx)) # 创建一个长度与词汇表相同的全 0 张量
tensor[word_to_idx[word]] = 1 # 将对应词的索引设为 1
return tensor # 返回生成的 One-Hot 向量
# 展示 One-Hot 编码前后的数据
word_example = "Teacher"
print("One-Hot 编码前的单词:", word_example)
print("One-Hot 编码后的向量:", one_hot_encoding(word_example, word_to_idx))
# 展示编码后的 Skip-Gram 训练数据样例
print("Skip-Gram 数据样例(已编码):", [(one_hot_encoding(context, word_to_idx),
word_to_idx[target]) for context, target in skipgram_data[:3]])
2.4 定义 Skip-Gram 类
# 定义 Skip-Gram 类
import torch.nn as nn # 导入 neural network
class SkipGram(nn.Module):
def __init__(self, voc_size, embedding_size):
super(SkipGram, self).__init__()
# 从词汇表大小到嵌入层大小(维度)的线性层(权重矩阵)
self.input_to_hidden = nn.Linear(voc_size, embedding_size, bias=False)
# 从嵌入层大小(维度)到词汇表大小的线性层(权重矩阵)
self.hidden_to_output = nn.Linear(embedding_size, voc_size, bias=False)
def forward(self, X): # 前向传播的方式,X 形状为 (batch_size, voc_size)
# 通过隐藏层,hidden 形状为 (batch_size, embedding_size)
hidden = self.input_to_hidden(X)
# 通过输出层,output_layer 形状为 (batch_size, voc_size)
output = self.hidden_to_output(hidden)
return output
embedding_size = 2 # 设定嵌入层的大小,这里选择 2 是为了方便展示
skipgram_model = SkipGram(voc_size, embedding_size) # 实例化 Skip-Gram 模型
print("Skip-Gram 模型:", skipgram_model)
2.5 训练 Skip-Gram 类
# 训练 Skip-Gram 类
learning_rate = 0.001 # 设置学习速率
epochs = 1000 # 设置训练轮次
criterion = nn.CrossEntropyLoss() # 定义交叉熵损失函数
import torch.optim as optim # 导入随机梯度下降优化器
optimizer = optim.SGD(skipgram_model.parameters(), lr=learning_rate)
# 开始训练循环
loss_values = [] # 用于存储每轮的平均损失值
for epoch in range(epochs):
loss_sum = 0 # 初始化损失值
for context, target in skipgram_data:
X = one_hot_encoding(target, word_to_idx).float().unsqueeze(0) # 将中心词转换为 One-Hot 向量
y_true = torch.tensor([word_to_idx[context]], dtype=torch.long) # 将周围词转换为索引值
y_pred = skipgram_model(X) # 计算预测值
loss = criterion(y_pred, y_true) # 计算损失
loss_sum += loss.item() # 累积损失
optimizer.zero_grad() # 清空梯度
loss.backward() # 反向传播
optimizer.step() # 更新参数
if (epoch+1) % 100 == 0: # 输出每 100 轮的损失,并记录损失
print(f"Epoch: {epoch+1}, Loss: {loss_sum/len(skipgram_data)}")
loss_values.append(loss_sum / len(skipgram_data))
# 绘制训练损失曲线
import matplotlib.pyplot as plt # 导入 matplotlib
# 绘制二维词向量图
plt.rcParams["font.family"]=['SimHei'] # 用来设定字体样式
plt.rcParams['font.sans-serif']=['SimHei'] # 用来设定无衬线字体样式
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.plot(range(1, epochs//100 + 1), loss_values) # 绘图
plt.title(' 训练损失曲线 ') # 图题
plt.xlabel(' 轮次 ') # X 轴 Label
plt.ylabel(' 损失 ') # Y 轴 Label
plt.show() # 显示图
2.6 输出 Skip-Gram 习得的词嵌入
# 输出 Skip-Gram 习得的词嵌入
print("Skip-Gram 词嵌入:")
for word, idx in word_to_idx.items(): # 输出每个词的嵌入向量
print(f"{word}: {skipgram_model.input_to_hidden.weight[:,idx].detach().numpy()}")
2.7 向量可视化
fig, ax = plt.subplots()
for word, idx in word_to_idx.items():
# 获取每个单词的嵌入向量
vec = skipgram_model.input_to_hidden.weight[:,idx].detach().numpy()
ax.scatter(vec[0], vec[1]) # 在图中绘制嵌入向量的点
ax.annotate(word, (vec[0], vec[1]), fontsize=12) # 点旁添加单词标签
plt.title(' 二维词嵌入 ') # 图题
plt.xlabel(' 向量维度 1') # X 轴 Label
plt.ylabel(' 向量维度 2') # Y 轴 Label
plt.show() # 显示图
3. 总结
Word2Vec是一种用于学习词向量的算法模型,它能够将单词转换为密集的向量表示,并捕捉单词之间的语义关系。Word2Vec模型由Google于2013年提出,是一种基于神经网络的词嵌入技术。
Word2Vec模型包括两种主要的训练方法:Skip-gram和CBOW。Skip-gram模型的目标是通过目标词预测上下文词,而CBOW模型的目标是通过上下文词预测目标词。这两种模型均采用神经网络结构,在大规模文本语料上进行训练,学习得到每个单词的向量表示。
Word2Vec的核心思想是通过单词在上下文中的分布来学习单词的语义信息。具体而言,相似上下文中的单词会拥有相似的词向量表示,这样就能够捕捉到单词之间的语义关系。通过将单词表示为稠密的向量,Word2Vec模型可以表示单词之间的相似度,进而应用于词义相似度计算、文本分类、语言建模等多个自然语言处理任务中。
Word2Vec模型的训练速度快、效果好,因此在自然语言处理领域得到了广泛的应用。它为计算机更好地理解和处理自然语言提供了有效的工具,被认为是自然语言处理领域的重要突破之一。