掌握主题模型:用Python实现LDA并通过pyLDAvis.gensim打造直观可视化

技术

LDA是一种用于主题建模的概率模型,它被广泛应用于文本挖掘和自然语言处理领域,其主要用途是发现文本数据中隐藏的主题结构,即在文本集合中潜在地存在的主题,并确定每个文档包含哪些主题以及每个主题中包含哪些词

1. LDA优劣势

优势:

主题发现: LDA可以用于发现文本数据中的主题结构,帮助理解大规模文本集合中隐藏的语义信息

无监督学习: LDA是一种无监督学习算法,不需要事先标注的训练数据,因此适用于处理大量未标记的文本

灵活性: LDA可以应用于不同领域的文本数据,包括新闻文章、社交媒体评论、科学文献等,具有较强的通用性

可解释性: LDA生成的主题模型对于解释文本数据的结构和内容具有一定的可解释性,可以为用户提供洞察力

劣势:

超参数选择: LDA中存在一些超参数需要手动调整,如主题数目等,选择合适的超参数可能需要一些经验和尝试

稀疏性: 在处理大规模文本数据时,由于文本的稀疏性,LDA模型可能面临词频较低的词汇难以准确建模的问题

不考虑词序: LDA是基于词袋模型的,忽略了词汇在文档中的顺序信息,可能损失一部分语境上下文的信息

计算复杂度: 在处理大规模文本集合时,LDA的计算复杂度较高,可能需要较长的训练时

2. 代码实现

2.1 LDA模型处理中文文本数据流程

  1. 中文文本数据
  • 原始中文文本数据
  • 中文分词
  • 使用分词工具(如jieba)对文本进行分词
  • 构建词典
  • 将分词后的词语建立索引,构建词典
  • 文本表示
  • 将文本数据转换为数值型表示,可以使用词袋模型或词嵌入等方法
  • 训练LDA模型
  • 使用处理后的文本数据训练LDA模型,设置主题数量(可依据困惑度、一致性确定最佳主题数)等参数
  • 获取主题分布
  • 得到文本中每个文档的主题分布,以及每个主题中的词语分布
  • 可视化工具
  • 使用可视化工具(如pyLDAvis)对LDA模型的结果进行可视化
  • 结果可视化
  • 可视化展示文本中的主题结构和单词分布

2.2 数据展示


          
import pandas as pd
          
column_names = ['文章内容']
          
df = pd.read_csv('环境监测与环境影响评价(摘要utf8).txt', sep='\t', encoding='utf-8',header=None, names=column_names)
          
print("数据维度:",df.shape)
          
df.head()
      

picture.image

2.3 定义分词去停用词函数


          
#加载停用词
          
def stopwordslist(filepath, custom_stopwords=[]):  
          
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]  
          
    stopwords.extend(custom_stopwords)  # 将自定义停用词添加到列表中
          
    return stopwords
          

          
# 加载停用词并添加自定义停用词
          
custom_stopwords = ['中', '年', '类']  
          
stopwords = stopwordslist('哈工大中文停用词.txt', custom_stopwords)
          

          
import jieba
          

          
# 定义数据清洗函数
          
def data_cleaning(content_list):
          
    # 初始化清洗后的文本列表
          
    content_seg = []
          

          
    # 初始化特殊字符、标点、数字、字母等,这些字符将被替换为空格
          
    symbols = '-\\n~%≥℃|/?``【oaicite:0】``?↓#~_「♂!?\',、:;。《》()()·—.…,0123456789abcdefghijklnmopqrstuvwxyz'
          

          
    # 循环处理输入的文本列表
          
    for content in content_list:
          
        # 去除特殊字符,将特殊字符替换为空格
          
        for con in content:
          
            if con in symbols:
          
                content = content.replace(con, ' ')
          

          
        # 使用结巴分词进行分词,cut_all=False表示采用精确模式
          
        con_list = jieba.cut(content, cut_all=False)
          
        result_list = []
          

          
        # 去除停用词,\n、\u3000(全角空格)、空格也会被去除
          
        for con in con_list:
          
            if con not in stopwords and con != '\n' and con != '\u3000' and con != ' ':
          
                result_list.append(con)
          

          
        # 将分词后的结果用空格连接成字符串,存储到content_seg列表中
          
        str1 = ' '.join(result_list)
          
        content_seg.append(str1)
          

          
    return content_seg
      

2.4 中文分词实现


          
#将评论转换为列表
          
contents = df['文章内容'].tolist()
          
participle = data_cleaning(contents)
          
df['文章内容去停用词分词结果'] = participle
          
df.head()
      

picture.image

2.5 构建词典创建词袋模型


          
from gensim import corpora
          
from gensim.models import LdaModel
          
from gensim.corpora import Dictionary
          
# 创建词典
          
train_set = df['文章内容去停用词分词结果'].apply(lambda x: x.split())  # 将文本字符串转换为列表
          
dictionary = corpora.Dictionary(train_set)
          
# 创建文档-词矩阵 词袋模型
          
corpus = [dictionary.doc2bow(text) for text in train_set]
          

          
# 查看token2id
          
print("Token to ID mapping:")
          
print(dictionary.token2id)
      

picture.image

2.6 计算 困惑度、一致性确定最佳主题数


          
from gensim.models.coherencemodel import CoherenceModel
          
import matplotlib.pyplot as plt
          

          
# 尝试不同的主题数量
          
num_topics_range = range(2, 11)
          
perplexity_scores = []
          
coherence_scores = []
          

          
for num_topics in num_topics_range:
          
    print(f"\nTrying with {num_topics} topics:")
          

          
    # 训练 LDA 模型
          
    lda_model = LdaModel(corpus, num_topics=num_topics, id2word=dictionary, passes=20, random_state=1)
          

          
    # 计算困惑度
          
    perplexity_score = lda_model.log_perplexity(corpus)
          
    perplexity_scores.append(perplexity_score)
          

          
    # 计算一致性
          
    coherence_model = CoherenceModel(model=lda_model, texts=train_set, dictionary=dictionary, coherence='c_v')
          
    coherence_score = coherence_model.get_coherence()
          
    coherence_scores.append(coherence_score)
          

          
# 绘制曲线图
          
plt.figure(figsize=(10, 5))
          

          
# 绘制困惑度曲线
          
plt.subplot(1, 2, 1)
          
plt.plot(num_topics_range, perplexity_scores, marker='o')
          
plt.title('Perplexity vs. Number of Topics')
          
plt.xlabel('Number of Topics')
          
plt.ylabel('Perplexity')
          
plt.grid(True)
          

          
# 绘制一致性曲线
          
plt.subplot(1, 2, 2)
          
plt.plot(num_topics_range, coherence_scores, marker='o', color='orange')
          
plt.title('Coherence vs. Number of Topics')
          
plt.xlabel('Number of Topics')
          
plt.ylabel('Coherence')
          
plt.grid(True)
          

          
plt.tight_layout()
          
plt.show()
      

picture.image

困惑度:困惑度值越低越好,选择困惑度最小的主题数

一致性:一致性值越高越好,选择一致性最大的主题数

在这里综合考虑计算主题数为2和主题数为10的LDA模型

2.6 训练主题数为2的LDA模型


          
# 训练LDA模型
          
num_topics = 2  # 指定主题数量
          
lda_model = LdaModel(corpus, id2word=dictionary, num_topics=num_topics, passes=20)
          

          
# 打印主题词分布
          
for topic in lda_model.print_topics(num_words=5):
          
    print(topic)
          
    
          
# 可视化主题模型结果
          
import pyLDAvis.gensim
          
pyLDAvis.enable_notebook()
          
vis = pyLDAvis.gensim.prepare(lda_model, corpus, dictionary)
          

          
# 保存可视化为HTML文件
          
pyLDAvis.save_html(vis, 'lda_2.html')
          
pyLDAvis.display(vis)
      

picture.image

主题1:

picture.image

主题2:

picture.image

2.7 训练主题数为10的LDA模型


          
# 训练LDA模型
          
num_topics = 10  # 指定主题数量
          
lda_model = LdaModel(corpus, id2word=dictionary, num_topics=num_topics, passes=20)
          

          
# 打印主题词分布
          
for topic in lda_model.print_topics(num_words=5):
          
    print(topic)
          
    
          
# 可视化主题模型结果
          
import pyLDAvis.gensim
          
pyLDAvis.enable_notebook()
          
vis = pyLDAvis.gensim.prepare(lda_model, corpus, dictionary)
          

          
# 保存可视化为HTML文件
          
pyLDAvis.save_html(vis, 'lda_10.html')
          
pyLDAvis.display(vis
      

picture.image

这里由于主题过多不一一展示,给出主题1的可视化结果

picture.image

2.8 主题数为10的LDA模型输出 保存每个文本属于各个主题的概率表


          
# 创建一个空的 DataFrame 以存储主题概率
          
topic_columns = [f"Topic_{i}" for i in range(lda_model.num_topics)]
          
result_df = pd.DataFrame(columns=topic_columns)
          

          
# 遍历文本
          
for i, doc in enumerate(corpus):
          
    doc_topics = lda_model.get_document_topics(doc)
          
    
          
    # 创建一个字典,表示主题概率
          
    topic_prob_dict = {f"Topic_{index}": prob for index, prob in doc_topics}
          
    
          
    # 如果主题不存在,将其概率设置为0
          
    missing_topics = set(topic_columns) - set(topic_prob_dict.keys())
          
    for missing_topic in missing_topics:
          
        topic_prob_dict[missing_topic] = 0.0
          
    
          
    # 将主题概率添加到 DataFrame
          
    result_df = pd.concat([result_df, pd.DataFrame(topic_prob_dict, index=[i])], ignore_index=True)
          
    
          
# 重置索引
          
result_df = result_df.reset_index(drop=True)
          
result_df['文本'] = df['文章内容']
          
result_df.to_excel('每个文本属于各个主题的概率表.xlsx',index=False)
          
result_df
      

picture.image

3**. 可视化解读**

左侧的每个气泡代表一个主题。气泡越大,主题就越普遍或具有主导地位,好的主题模型,较大的起泡将会比较均匀地分布在不同的象限,而不是聚集在一个象限,右侧对应相应主题前30词语的词频数量,其中蓝色代表总词频,红色代表所选主题内的估计术语频率,点击具体的某一个词语相应的气泡图也会发生大小变化,其占比越大气泡越大如下图选中“发展”一词后主题1的气泡变大,其余气泡均有不同程度是减小,也对应了红蓝占比

picture.image

其中 "Slide to adjust relevance metric"是一个滑动条或度量标尺,用于调整主题词云图中显示的词语的相关性,这个度量标尺对应的是主题中每个词语的贡献度,也称为词语的"lambda"值, Slide to adjust relevance metric允许用户在0到1之间滑动,调整lambda值,Lambda值用于平衡两个方面的权衡:词语在主题中的权重和词语的全局权重

当lambda值接近0时,更强调词语在主题中的权重,使得词云中显示的是主题中最能代表的词语

当lambda值接近1时,更强调词语的全局权重,使得词云中显示的是在整个语料库中更为常见的词语

如下图当两个主题(主题3和主题5)的lambda为0时

picture.image

picture.image

将lambda值调整为0,凸显主题的中心主题和最具代表性的特征词,以更清晰地理解主题的主要内容, 在lambda值接近0的情况下,词云中显示的词语更多地反映了主题内的独特性,而不受到在整个语料库中词语频率的限制。 这有助于揭示主题内部的关键特征,根据主题3和主题5设置lambda为0可以看出每个主题最具代表性的特征词存在明显差异,进一步说明这两个主题存在显著性的差异

结论

在本文中,我们研究了LDA主题模型以及其可视化展示,这是一种强大的文本分析工具,通过对语料库的建模,LDA模型能够从文本中提取主题信息,帮助我们理解数据中隐藏的结

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生机器学习系统落地和实践
机器学习在字节跳动有着丰富业务场景:推广搜、CV/NLP/Speech 等。业务规模的不断增大对机器学习系统从用户体验、训练效率、编排调度、资源利用等方面也提出了新的挑战,而 Kubernetes 云原生理念的提出正是为了应对这些挑战。本次分享将主要介绍字节跳动机器学习系统云原生化的落地和实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论