【Datawhale学习笔记】基于Gensim的词向量实战

举报
JeffDing 发表于 2026/01/09 10:48:21 2026/01/09
【摘要】 Gensim 简介Gensim (Generate Similar) 是一个功能强大且高效的Python库,专门用于处理原始的、非结构化的纯文本文档。它内置了多种主流的词向量和主题模型算法,如 Word2Vec、TF-IDF、LSA、LDA 等。 核心概念语料库:这是 Gensim 处理的主要对象,可以简单理解为训练数据集。分词后的文档通常表示为 list[list[str]];用于 TF...

Gensim 简介

Gensim (Generate Similar) 是一个功能强大且高效的Python库,专门用于处理原始的、非结构化的纯文本文档。它内置了多种主流的词向量和主题模型算法,如 Word2Vec、TF-IDF、LSA、LDA 等。

核心概念

  1. 语料库:这是 Gensim 处理的主要对象,可以简单理解为训练数据集。分词后的文档通常表示为 list[list[str]];用于 TF-IDF、LDA 等模型的标准 BoW 语料库是包含稀疏向量的可迭代对象,每篇文档表示为 [(token_id, frequency), …]。例如 [[“我”, “爱”, “吃”, “海参”], [“国足”, “惨败”, “泰国”]] 中每个子列表代表一篇独立的文档。

  2. 词典:这是一个将词语(token)映射到唯一整数ID的词汇表。在使用词袋模型之前,必须先根据整个语料库构建一个词典。

  3. 向量:在Gensim中,一篇文档最终会被转换成一个数学向量。例如,使用词袋模型时,一篇文档 [“我”, “爱”, “我”] 可能会被表示为 [(0, 2), (1, 1)]。

  4. 稀疏向量:这是 Gensim 为了节省内存而采用的一种高效表示法。对于像 One-Hot 或词袋模型这样维度巨大且绝大多数值为0的向量,Gensim 不会存储所有0。例如,一个词袋向量 [2, 1, 0, 0, … , 0] 会被表示成 [(0, 2), (1, 1)],仅记录非零项的索引和值,极大地减少了存储开销。

  5. 模型:在 Gensim 中,模型是一个用于实现向量转换的算法。例如,TfidfModel 可以将一个由词频构成的词袋向量,转换为一个由TF-IDF权重构成的向量。

内置模型

TF-IDF

models.TfidfModel

主题模型 / 矩阵分解

LSA (Latent Semantic Analysis): models.LsiModel
LDA (Latent Dirichlet Allocation): models.LdaModel
NMF (Non-negative Matrix Factorization): models.Nmf

神经网络词向量

Word2Vec: models.Word2Vec
FastText: models.FastText
Doc2Vec: models.Doc2Vec

安装方法

pip install gensim

工作流

安装依赖

pip install jieba

流程

  1. 准备语料:将原始的文本文档进行分词,并整理成Gensim要求的格式——一个由列表构成的列表 list[list[str]],其中每个子列表代表一篇独立的文档。
  2. 创建词典:遍历所有分词后的文档,创建一个词典,将每个唯一的词元(Token)映射到一个从 0 开始的整数 ID。
  3. 词袋化:使用创建好的词典,将每一篇文档转换为其稀疏的词袋(BoW)向量。这个向量只记录文档中出现的词的 ID 及其频次,格式为 [(token_id, frequency), …]。

这个最终生成的 BoW语料库,就是训练 TF-IDF、LDA 等模型的标准输入。

import jieba
from gensim import corpora

# Step 1: 准备分词后的语料 (新闻标题)
raw_headlines = [
    "央行降息,刺激股市反弹",
    "球队赢得总决赛冠军,球员表现出色"
]
tokenized_headlines = [jieba.lcut(doc) for doc in raw_headlines]
print(f"分词后语料: {tokenized_headlines}")

# Step 2: 创建词典
dictionary = corpora.Dictionary(tokenized_headlines)
print(f"词典: {dictionary.token2id}")

# Step 3: 转换为BoW向量语料库
corpus_bow = [dictionary.doc2bow(doc) for doc in tokenized_headlines]
print(f"BoW语料库: {corpus_bow}")

输出结果

分词后语料: [['央行', '降息', ',', '刺激', '股市', '反弹'], ['球队', '赢得', '总决赛', '冠军', ',', '球员', '表现出色']]
词典: {'刺激': 0, '反弹': 1, '央行': 2, '股市': 3, '降息': 4, ',': 5, '冠军': 6, '总决赛': 7, '球员': 8, '球队': 9, '表现出色': 10, '赢得': 11}
BoW语料库: [[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1)], [(5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1)]]

TF-IDF与关键词权重

TF-IDF 是衡量一个词在文档中重要性的经典加权方法。下面将继续使用新闻标题的例子,演示如何计算其 TF-IDF 向量。

import jieba
from gensim import corpora, models

# 1. 准备语料 (新闻标题,包含财经和体育两个明显主题)
headlines = [
    "央行降息,刺激股市反弹",
    "球队赢得总决赛冠军,球员表现出色",
    "国家队公布最新一期足球集训名单",
    "A股市场持续震荡,投资者需谨慎",
    "篮球巨星刷新历史得分记录",
    "理财产品收益率创下新高"
]
tokenized_headlines = [jieba.lcut(title) for title in headlines]

# 2. 创建词典和BoW语料库
dictionary = corpora.Dictionary(tokenized_headlines)
corpus_bow = [dictionary.doc2bow(doc) for doc in tokenized_headlines]

# 3. 训练TF-IDF模型
tfidf_model = models.TfidfModel(corpus_bow)

# 4. 将BoW语料库转换为TF-IDF向量表示
corpus_tfidf = tfidf_model[corpus_bow]

# 辅助函数:把 (token_id, weight) 转成 (token, weight),并按权重降序展示
def tfidf_with_words(tfidf_vec, id2word):
    pairs = [(id2word[token_id], weight) for token_id, weight in tfidf_vec]
    return sorted(pairs, key=lambda x: x[1], reverse=True)

# 打印第一篇标题的TF-IDF向量
first_tfidf = list(corpus_tfidf)[0]
print("第一篇标题的TF-IDF向量:")
print(first_tfidf)
print("第一篇标题的TF-IDF向量(带词语):")
print(tfidf_with_words(first_tfidf, dictionary))

# 5. 对新标题应用模型
new_headline = "股市大涨,牛市来了"
new_headline_bow = dictionary.doc2bow(list(jieba.cut(new_headline)))
new_headline_tfidf = tfidf_model[new_headline_bow]
print("\n新标题的TF-IDF向量:")
print(new_headline_tfidf)

输出结果

第一篇标题的TF-IDF向量:
[(0, 0.44066740566370055), (1, 0.44066740566370055), (2, 0.44066740566370055), (3, 0.44066740566370055), (4, 0.44066740566370055), (5, 0.1704734229377651)]
第一篇标题的TF-IDF向量(带词语):
[('刺激', 0.44066740566370055), ('反弹', 0.44066740566370055), ('央行', 0.44066740566370055), ('股市', 0.44066740566370055), ('降息', 0.44066740566370055), (',', 0.1704734229377651)]

新标题的TF-IDF向量:
[(3, 0.9326446771245245), (5, 0.360796211497975)]

结果分析

  • 原始的BoW向量只包含词频(整数),而 TF-IDF 向量则包含浮点数权重。
  • 像“,”这样在多篇文档中都出现的词,其 TF-IDF 权重会较低;而在特定财经新闻中出现的“股市”、“降息”等词,权重会相对较高。
  • 这个 TF-IDF 向量后续可用于计算文档相似度或作为机器学习模型的输入特征。
  • 词典外的新词会被忽略,新文本的向量仅由词典中已有词构成。
  • 本示例中标点“,”进入了词典并具有非零权重;如不希望其影响权重或相似度,建议在构建词典前移除标点/停用词。
  • 你会看到新标题的TF-IDF仅包含“股市”和“,”两项,这是因为其他词为OOV被忽略。

LDA 与文档主题挖掘

主题模型(如 LDA)能从大量文档中自动发现隐藏的、无监督的主题。它的输入同样是词典和 BoW 语料库。

from gensim import corpora, models

# 1. 准备语料
headlines = [
    "央行降息,刺激股市反弹",
    "球队赢得总决赛冠军,球员表现出色",
    "国家队公布最新一期足球集训名单",
    "A股市场持续震荡,投资者需谨慎",
    "篮球巨星刷新历史得分记录",
    "理财产品收益率创下新高"
]
tokenized_headlines = [jieba.lcut(title) for title in headlines]

# 2. 创建词典和BoW语料库
dictionary = corpora.Dictionary(tokenized_headlines)
corpus_bow = [dictionary.doc2bow(doc) for doc in tokenized_headlines]

# 3. 训练LDA模型 (假设需要发现2个主题)
lda_model = models.LdaModel(corpus=corpus_bow, id2word=dictionary, num_topics=2, random_state=100)

# 4. 查看模型发现的主题
print("模型发现的2个主题及其关键词:")
for topic in lda_model.print_topics():
    print(topic)

# 5. 推断新文档的主题分布
new_headline = "詹姆斯获得常规赛MVP"
new_headline_bow = dictionary.doc2bow(jieba.lcut(new_headline))
topic_distribution = lda_model[new_headline_bow]
print(f"\n新标题 '{new_headline}' 的主题分布:")
print(topic_distribution)

输出结果

模型发现的2个主题及其关键词:
(0, '0.045*"," + 0.040*"公布" + 0.039*"一期" + 0.039*"名单" + 0.039*"足球" + 0.039*"最新" + 0.038*"集训" + 0.038*"国家队" + 0.037*"A股" + 0.037*"市场"')
(1, '0.066*"," + 0.039*"篮球" + 0.039*"刷新" + 0.039*"历史" + 0.039*"记录" + 0.038*"得分" + 0.038*"巨星" + 0.037*"刺激" + 0.036*"降息" + 0.036*"反弹"')

新标题 '詹姆斯获得常规赛MVP' 的主题分布:
[(0, 0.5), (1, 0.5)]

Word2Vec 模型实战

Word2Vec 的输入直接是 分词后的句子列表 。它的目标不是加权或寻找主题,而是根据上下文学习每个词语本身内在的、稠密的语义向量

模型训练与核心参数

from gensim.models import Word2Vec

# 1. 准备语料
headlines = [
    # 财经
    "央行降息,刺激股市反弹",
    "A股市场持续震荡,投资者需谨慎",
    "理财产品收益率创下新高",
    "证监会发布新规,规范市场交易",
    "创业板指数上涨,科技股领涨大盘",
    "房价调控政策出台,房地产市场降温",
    "全球股市动荡,影响资本市场信心",
    "分析师认为,当前股市风险与机遇并存,市场情绪复杂",

    # 体育
    "球队赢得总决赛冠军,球员表现出色",
    "国家队公布最新一期足球集训名单",
    "篮球巨星刷新历史得分记录",
    "奥运会开幕,中国代表团旗手确定",
    "马拉松比赛圆满结束,选手创造佳绩",
    "电子竞技联赛吸引大量年轻观众",
    "这支球队的每位球员都表现出色",
    "球员转会市场活跃,多支球队积极引援"
]
tokenized_headlines = [jieba.lcut(title) for title in headlines]


# 2. 训练Word2Vec模型
model = Word2Vec(tokenized_headlines, vector_size=50, window=3, min_count=1, sg=1)

# 训练完成后,所有词向量都存储在 model.wv 对象中
# model.wv 是一个 KeyedVectors 实例

参数解析

  • sentences: 输入的语料库,必须是 list[list[str]] 格式。
  • vector_size: 词向量的维度。维度越高,能表达的语义信息越丰富,但计算量也越大。通常在50-300之间选择。
  • window: 上下文窗口大小。表示在预测一个词时,需要考虑其前后多少个词。
  • min_count: 最小词频过滤。任何在整个语料库中出现次数低于此值的词将被直接忽略。这是非常关键的一步,可以过滤掉大量噪音(如错别字、罕见词),并显著减小模型体积。
  • sg: 选择训练算法。0 表示 CBOW (根据上下文预测中心词);1 表示 Skip-gram (根据中心词预测上下文)。
  • hs: 选择优化策略。0 表示使用 Negative Sampling (负采样);1 表示使用 Hierarchical Softmax。当 hs=0 时,下面的 negative 参数才会生效。
  • negative: 当使用负采样时,为每个正样本随机选择多少个负样本。通常在5-20之间。
  • sample: 高频词二次重采样阈值。这是一个控制高频词(如“的”、“是”)被随机跳过的机制,目的是减少它们对模型训练的过多影响,并加快训练速度。值越小,高频词被跳过的概率越大。

使用词向量

# 1. 寻找最相似的词
# 在小语料上,结果可能不完美,但能体现出模型学习到了主题内的关联
similar_to_market = model.wv.most_similar('股市')
print(f"与 '股市' 最相似的词: {similar_to_market}")

# 2. 计算两个词的余弦相似度
similarity = model.wv.similarity('球队', '球员')
print(f"\n'球队' 和 '球员' 的相似度: {similarity:.4f}")

# 3. 获取一个词的向量
market_vector = model.wv['市场']
print(f"\n'市场' 的向量维度: {market_vector.shape}")

输出结果

# 1. 寻找最相似的词
# 在小语料上,结果可能不完美,但能体现出模型学习到了主题内的关联
similar_to_market = model.wv.most_similar('股市')
print(f"与 '股市' 最相似的词: {similar_to_market}")

# 2. 计算两个词的余弦相似度
similarity = model.wv.similarity('球队', '球员')
print(f"\n'球队' 和 '球员' 的相似度: {similarity:.4f}")

# 3. 获取一个词的向量
market_vector = model.wv['市场']
print(f"\n'市场' 的向量维度: {market_vector.shape}")

输出结果

'股市' 最相似的词: [('赢得', 0.32000917196273804), ('的', 0.30753469467163086), ('出台', 0.28610461950302124), ('领涨', 0.27674591541290283), ('大量', 0.2701166272163391), ('房价', 0.2357291877269745), ('指数', 0.23326583206653595), ('交易', 0.23140685260295868), ('活跃', 0.2246733456850052), ('反弹', 0.21687227487564087)]

'球队''球员' 的相似度: -0.1547

'市场' 的向量维度: (50,)

模型的持久化

from gensim.models import KeyedVectors

# 保存词向量到文件
model.wv.save("news_vectors.kv")

# 从文件加载词向量
loaded_wv = KeyedVectors.load("news_vectors.kv")

# 加载后可以执行同样的操作
print(f"\n加载后,'球队' 和 '球员' 的相似度: {loaded_wv.similarity('球队', '球员'):.4f}")

输出结果

加载后,'球队''球员' 的相似度: -0.1547

参考资料

https://github.com/FutureUnreal/base-nlp/blob/main/code/C2/04_gensim.ipynb

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。