使用Word2Vec模型训练词向量
摘要
使用 Word2Vec
模型训练一个词向量模型,并进行一些基本的词向量操作,比如查找相似词和进行词向量的类比。
代码的每个部分有如下解释:
1. 导入库
import jieba
from gensim.models import Word2Vec
jieba
:一个中文分词库,用于将中文句子分割成词语。gensim.models.Word2Vec
:用于训练和使用Word2Vec
词向量模型。
2. 训练词向量模型
def train_word2vec_model(corpus, dim):
model = Word2Vec(corpus, size=dim, sg=1, min_count=1,workers=4, iter=15,compute_loss=True, callbacks=[PlotCallback()])
model.save("model.w2v")
return model
train_word2vec_model(corpus, dim)
:该函数用来训练一个Word2Vec
模型。corpus
:传入的语料是一个列表,列表的每个元素都是一个由词语构成的子列表(例如["今天", "天气", "不错"]
)。这些子列表是已经分好词的句子。dim
:词向量的维度,决定了词向量的大小(例如 128)。sg=1
:使用 Skip-Gram 模型(如果设置为 0 则使用 CBOW 模型)。在 Skip-Gram 模型中,通过当前词来预测上下文词。min_count=1
:这是词频的最小阈值,任何出现次数小于此值的词都会被忽略。在这个代码中,注释掉的部分其实原本是希望使用min_count=3
。然而,这里硬编码为min_count=1
,意味着即使是非常稀有的单词也会被包括进训练。workers=4
:设置用于训练的线程数。4
表示使用 4 个线程进行训练,加速训练过程。iter=15
:指定模型迭代次数,模型会根据训练数据进行 15 轮的优化。compute_loss=True
:启用计算训练过程中的损失函数,这对于调试和分析模型训练的效果很有用。callbacks=[PlotCallback()]
:使用一个回调函数PlotCallback
,这个回调函数通常用于在训练过程中绘制损失曲线等图表,帮助监控训练的进度和效果。model.save("model.w2v")
:将训练好的模型保存为文件model.w2v
,方便后续加载使用。return model
:返回训练好的Word2Vec
模型。
3.训练的回调函数
# 启用 compute_loss=True 后,通过回调函数监控
class PlotCallback(CallbackAny2Vec):
def __init__(self):
super().__init__()
self.epoch_losses = [] # 存储每个epoch的损失值
self.epoches = 0 # 精确记录实际epoch数
def on_epoch_end(self, model):
"""每epoch结束时记录损失并重置计数器"""
loss = model.get_latest_training_loss()
model.running_training_loss = 0.0 # 关键!防止损失值累积
self.epoch_losses.append(loss)
self.epoches += 1
def on_train_end(self, model):
"""训练完全结束后生成最终可视化图表"""
plt.figure(figsize=(12, 6))
# 生成正确的x轴范围(从1到总epoch数)
x_values = range(1, self.epoches + 1)
plt.plot(x_values, self.epoch_losses, 'b-', marker='o', markersize=8,
linewidth=2, alpha=0.7, label='Training Loss')
# 标注最低损失点
min_loss = min(self.epoch_losses)
min_epoch = self.epoch_losses.index(min_loss) + 1
plt.scatter( min_epoch, min_loss,
s=200, facecolors='none', edgecolors='r',
label=f'Min Loss: {min_loss:.2f} (Epoch {min_epoch})'
)
# 可视化增强配置
plt.title(f"Word2Vec Training Loss Progression ({self.epoches} Epochs)", fontsize=14)
plt.xlabel("Epoch", fontsize=12)
plt.ylabel("Loss", fontsize=12)
# plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
# 自适应Y轴范围
y_min = min(self.epoch_losses) * 0.9
y_max = max(self.epoch_losses) * 1.1
plt.ylim(y_min, y_max)
plt.tight_layout()
plt.show()
time.sleep(3)
plt.close()
实现了回调类, PlotCallback
,并且用于监控和记录模型训练过程中的损失值,并在训练结束时生成损失曲线图。下面是对代码的详细解释:
PlotCallback类
这个类继承自 CallbackAny2Vec
,主要用于在每个 epoch 结束时记录损失,并在训练结束时生成损失曲线图。此回调类的目的是提供更直观的训练过程损失的可视化效果。
-
__init__
方法:- 初始化
epoch_losses
,用来存储每个 epoch 的损失值。 - 初始化
epoches
,用来记录实际训练的 epoch 数量。
- 初始化
-
on_epoch_end
方法:- 每当一个 epoch 结束时,获取并记录该 epoch 的训练损失。
- 重置
model.running_training_loss
为 0.0,防止损失值的累积。 - 将当前损失值添加到
epoch_losses
列表中。 - 增加
epoches
计数器,记录总的 epoch 数量。
-
on_train_end
方法:- 在训练结束后,生成一个损失曲线图,展示训练过程中的损失变化。
- 使用
matplotlib
来绘制损失曲线。 x_values
是横坐标,表示 epoch 数量,从 1 到总的 epoch 数量。- 绘制蓝色的折线图,表示每个 epoch 的损失值。
- 标注最低损失值的点,红色圆圈标记最低损失点的位置,显示最小损失值及其对应的 epoch。
- 设置图表的标题、x轴和y轴标签、图例等。
- 自适应调整 y 轴的范围,确保图表显示完整。
- 最后,显示图表并暂停 3 秒,之后关闭图表窗口。
训练结束后,输出训练过程中loss变化情况图,
4. 加载已训练的模型
def load_word2vec_model(path):
model = Word2Vec.load(path)
return model
load_word2vec_model(path)
:该函数加载已经训练好的Word2Vec
模型文件,path
是模型文件的路径。Word2Vec.load(path)
:从文件中加载模型。return model
:返回加载的模型对象。
示例文本预料词向量训练语料文件
5. 主函数:读取语料并训练模型
def main():
sentences = []
with open("corpus.txt", encoding="utf8") as f:
for line in f:
sentences.append(jieba.lcut(line))
model = train_word2vec_model(sentences, 128)
return model
main()
:主函数,负责读取语料文件并训练模型。- 打开文件
corpus.txt
,逐行读取每一行文本。 jieba.lcut(line)
:将每行文本通过jieba
分词,将句子分解成词语列表(例如"今天 天气 不错"
会被分割成["今天", "天气", "不错"]
)。- 将所有分词后的句子存储在
sentences
列表中。 - 调用
train_word2vec_model(sentences, 128)
来训练Word2Vec
模型,词向量的维度设为 128。 - 返回训练好的模型。
- 打开文件
6. 运行脚本
if __name__ == "__main__":
model = main() #训练
model = load_word2vec_model("model.w2v") #加载
print(model.wv.most_similar(positive=["男人", "母亲"], negative=["女人"])) #类比
if __name__ == "__main__":
:检查是否是直接运行该脚本,而不是作为模块导入。model = main()
:调用main()
函数训练模型。model = load_word2vec_model("model.w2v")
:加载保存的Word2Vec
模型文件model.w2v
。model.wv.most_similar(positive=["男人", "母亲"], negative=["女人"])
:进行词向量类比计算,类似于在数学上进行向量加减。positive=["男人", "母亲"]
:正向输入,表示方向上的两个词男人
和母亲
。negative=["女人"]
:负向输入,表示从正向词中减去女人
。- 这个计算类似于“男人 + 母亲 - 女人”,模型会返回与结果最相似的词。
7. 查找相似词的交互式部分
while True: #找相似
string = input("input:")
try:
print(model.wv.most_similar(string))
except KeyError:
print("输入词不存在")
- 交互式查询:
- 进入一个无限循环,等待用户输入一个词语。
input("input:")
:提示用户输入一个词语。model.wv.most_similar(string)
:根据输入的词查找与其最相似的词。- 如果输入的词在模型中存在,返回最相似的词;如果词不在词汇表中,抛出
KeyError
异常并输出"输入词不存在"
。
总结
这段代码的主要功能是:
- 训练一个基于
Word2Vec
的中文词向量模型,使用jieba
进行中文分词。 - 训练好模型后,保存为文件并可以加载来进行使用。
- 提供一个功能来进行词向量类比(例如“男人 + 母亲 - 女人”)并输出与之最相似的词。
- 提供一个交互式命令行界面,允许用户输入词语并查找最相似的词。
使用流程:
- 提供一个包含中文句子的文本文件
corpus.txt
。 - 运行代码,模型会根据文本文件进行训练。
- 训练完成后,模型可以用来查找词语的相似词或执行类比任务。
全部代码:
import jieba
from gensim.models import Word2Vec
from gensim.models.callbacks import CallbackAny2Vec
from collections import defaultdict
import matplotlib.pyplot as plt
import time
'''
词向量模型的简单实现
'''
# 启用 compute_loss=True 后,通过回调函数监控
class PlotCallback(CallbackAny2Vec):
def __init__(self):
super().__init__()
self.epoch_losses = [] # 存储每个epoch的损失值
self.epoches = 0 # 精确记录实际epoch数
def on_epoch_end(self, model):
"""每epoch结束时记录损失并重置计数器"""
loss = model.get_latest_training_loss()
model.running_training_loss = 0.0 # 关键!防止损失值累积
self.epoch_losses.append(loss)
self.epoches += 1
def on_train_end(self, model):
"""训练完全结束后生成最终可视化图表"""
plt.figure(figsize=(12, 6))
# 生成正确的x轴范围(从1到总epoch数)
x_values = range(1, self.epoches + 1)
plt.plot(x_values, self.epoch_losses, 'b-', marker='o', markersize=8,
linewidth=2, alpha=0.7, label='Training Loss')
# 标注最低损失点
min_loss = min(self.epoch_losses)
min_epoch = self.epoch_losses.index(min_loss) + 1
plt.scatter( min_epoch, min_loss,
s=200, facecolors='none', edgecolors='r',
label=f'Min Loss: {min_loss:.2f} (Epoch {min_epoch})'
)
# 可视化增强配置
plt.title(f"Word2Vec Training Loss Progression ({self.epoches} Epochs)", fontsize=14)
plt.xlabel("Epoch", fontsize=12)
plt.ylabel("Loss", fontsize=12)
# plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
# 自适应Y轴范围
y_min = min(self.epoch_losses) * 0.9
y_max = max(self.epoch_losses) * 1.1
plt.ylim(y_min, y_max)
plt.tight_layout()
plt.show()
time.sleep(3)
plt.close()
#训练模型
#corpus: [["cat", "say", "meow"], ["dog", "say", "woof"]]
#corpus: [["今天", "天气", "不错"], ["你", "好", "吗"]]
#dim指定词向量的维度,如100
def train_word2vec_model(sentences, dim, min_count=3):
# # 统计词频
# word_freq = defaultdict(int)
# for sentence in sentences:
# for word in sentence:
# word_freq[word] += 1
#
# # 将低频词替换为UNK
# sentence_words = [
# [word if word_freq[word] >= min_count else "<UNK>" for word in sentence] for sentence in sentences
# ]
model = Word2Vec(sentences, size=dim, sg=1, min_count=1,workers=4, iter=15,
compute_loss=True, callbacks=[PlotCallback()])
model.save("model.w2v")
return model
#输入模型文件路径
#加载训练好的模型
def load_word2vec_model(path):
model = Word2Vec.load(path)
return model
def main():
sentences = []
with open("corpus.txt", encoding="utf8") as f:
for line in f:
sentences.append(jieba.lcut(line))
model = train_word2vec_model(sentences, 128)
return model
if __name__ == "__main__":
model = main() #训练
model = load_word2vec_model("model.w2v") #加载
print(model.wv.most_similar(positive=["男人", "母亲"], negative=["女人"])) #类比
while True: #找相似
string = input("input:")
try:
print(model.wv.most_similar(string))
except KeyError:
print("输入词不存在")