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

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

安装方法

bash 复制代码
pip install gensim

工作流

安装依赖

bash 复制代码
pip install jieba

流程

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

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

python 复制代码
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 向量。

python 复制代码
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 语料库。

python 复制代码
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 的输入直接是 分词后的句子列表 。它的目标不是加权或寻找主题,而是根据上下文学习每个词语本身内在的、稠密的语义向量

模型训练与核心参数

python 复制代码
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: 高频词二次重采样阈值。这是一个控制高频词(如"的"、"是")被随机跳过的机制,目的是减少它们对模型训练的过多影响,并加快训练速度。值越小,高频词被跳过的概率越大。

使用词向量

python 复制代码
# 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,)

模型的持久化

python 复制代码
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

相关推荐
冬奇Lab1 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab1 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP5 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年5 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼5 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS5 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区6 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈6 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang7 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk18 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能