查看原文
其他

tomotopy | 速度最快的LDA主题模型

大邓 大邓和他的Python
2024-09-09


tomotopy简介?

tomotopy 是 tomoto(主题建模工具)的 Python 扩展,它是用 C++ 编写的基于 Gibbs 采样的主题模型库。支持的主题模型包括 LDA、DMR、HDP、MG-LDA、PA 和 HPA, 利用现代 CPU 的矢量化来最大化速度。

https://github.com/bab2min/tomotopy

下图中同样的数据集, tomotopy迭代200次,gensim迭代10次的情况下, tomotopy与gensim耗时对比图,由此可见tomotopy训练主题模型速度之快。

当前版本的 tomotopy 支持的主题模型包括

  • 潜在狄利克雷分配(LDAModel)
  • 标记的 LDA(LLDA 模型)
  • 部分标记的 LDA(PLDA 模型)
  • 监督LDA(SLDA模型)
  • Dirichlet 多项回归 (DMRModel)
  • 广义狄利克雷多项回归 (GDMRModel)
  • 分层狄利克雷过程 (HDPModel)
  • 分层LDA(HLDA模型)
  • 多粒 LDA(MGLDA 模型)
  • 弹珠盘分配(PAModel)
  • 分层 PA (HPAModel)
  • 相关主题模型(CTModel)
  • 动态主题模型 (DTModel)
  • 基于伪文档的主题模型(PTModel)。

安装

!pip3 install tomotopy==0.12.2
!pip3 install pyLDAvis==3.3.1  

目前,tomotopy 可以利用 AVX2、AVX 或 SSE2 SIMD 指令集来最大程度利用PC的性能。

import tomotopy as tp

tp.isa

Run

'avx2'

如果 tp.isa 返回 None,则训练过程可能需要很长时间。


1. 导入数据

准备一个自己很熟悉的数据disaster_news.csv,一共有332条,话题数K=5,(正常情况下K是需要探索的)。

import pandas as pd

df = pd.read_csv('disaster_news.csv')
df.head()


2. 整理数据

分词、去除停用词

import re
import jieba
from cntext import STOPWORDS_zh


def segment(text):
    words = jieba.lcut(text)
    words = [w for w in words if w not in STOPWORDS_zh]
    return words

test = "云南永善县级地震已致人伤间民房受损中新网月日电据云南昭通市防震减灾局官方网站消息截至日时云南昭通永善县级地震已造成人受伤其中重伤人轻伤人已全部送医院救治民房受损户间倒塌户间个乡镇所学校不同程度受损目前被损毁电力交通通讯设施已全部抢通修复当地已调拨帐篷顶紧急转移万人月日时分云南昭通永善县发生里氏级地震震源深度公里当地震感强烈此外成都等四川多地也有明显震感"
print(segment(test))
['云南', '永善县', '级', '地震', '已致', '伤间', '民房', '受损', '中新网', '日电', '云南', '昭通市', '防震', '减灾', '局', '官方网站', '消息', '日时', '云南', '昭通', '永善县', '级', '地震', '造成', '受伤', '重伤', '轻伤', '送', '医院', '救治', '民房', '受损', '户间', '倒塌', '户间', '乡镇', '学校', '不同', '程度', '受损', '目前', '损毁', '电力', '交通', '通讯', '设施', '抢通', '修复', '调拨', '帐篷', '顶', '紧急', '转移', '万人', '时分', '云南', '昭通', '永善县', '发生', '里氏', '级', '地震', '震源', '深度', '公里', '震感', '强烈', '成都', '四川', '多地', '明显', '震感']
df['words'] = df['text'].apply(segment)
df.head()


3. 找到最佳K

正常的步骤应该认真对待这步,在一定区间范围内,根据模型得分找到合理的K。这里使用tomotopy提供的主题一致性coherence得分假装找一下。

我们期望的图应该的topic coherence随着 number of topics增加而增加,然后到某个topic值趋于平稳。

tomotopy每次运行得到的图形状不一样,为了保证运行结果具有可比性,设置了随机种子seed为555,你也可以根据需要改为自己需要的随机状态(这里有点像炼丹)。经过运行发现k=5比较合适(跑出了我的预判)。

def find_k(docs, min_k=1, max_k=20, min_df=2):
    #min_df 词语最少出现在2个文档中
    import matplotlib.pyplot as plt
    scores = []
    for k in range(min_k, max_k):
        #seed随机种子,保证在大邓这里运行结果与你运行的结果一样
        mdl = tp.LDAModel(min_df=min_df, k=k, seed=555)
        for words in docs:
            if words:
                mdl.add_doc(words)
        mdl.train(20)
        coh = tp.coherence.Coherence(mdl)
        scores.append(coh.get_score())

    #x = list(range(min_k, max_k - 1))  # 区间最右侧的值。注意:不能大于max_k
    #print(x)
    #print()
    plt.plot(range(min_k, max_k), scores)
    plt.xlabel("number of topics")
    plt.ylabel("coherence")
    plt.show()
    
    
find_k(docs=df['words'], min_k=1, max_k=10, min_df=2)

Run



4. 训练lda

使用tomotopy的LDA模型, 话题数K=5

import tomotopy as tp

#初始化LDA
mdl = tp.LDAModel(k=5, min_df=2, seed=555)
for words in df['words']:
    #确认words 是 非空词语列表
    if words:
        mdl.add_doc(words=words)

#训练
mdl.train()

#查看每个topic feature words
for k in range(mdl.k):
    print('Top 10 words of topic #{}'.format(k))
    print(mdl.get_topic_words(k, top_n=10))
    print('\n')

Run

Top 10 words of topic #0
[('一辆', 0.02751251682639122), ('事故', 0.021704642102122307), ('记者', 0.018342
189490795135), ('死亡', 0.01650812290608883), ('造成', 0.014062701724469662), ('人员', 0.013909862376749516), ('现场', 0.013451346196234226), ('受伤', 0.012687151320278645), ('相撞', 0.011922957375645638), ('货车', 0.011922957375645638)]


   Top 10 words of topic #1    [('学生', 0.02709135226905346), ('食物中毒', 0.02498047426342964), ('出现', 0.019175563007593155), ('医院', 0.016185153275728226), ('事件', 0.013546556234359741), ('调查', 0.013194743543863297), ('年月日', 0.012842929922044277), ('治疗', 0.012667023576796055), ('症状', 0.011787491850554943), ('名', 0.011259771883487701)]

   Top 10 words of topic #2    [('现场', 0.018848909065127373), ('发生', 0.01677251048386097), ('医院', 0.015015557408332825), ('起火', 0.014216942712664604), ('原因', 0.012140544131398201), ('目前', 0.012140544131398201), ('救治', 0.01150165218859911), ('进行', 0.011022482998669147), ('名', 0.009425252676010132), ('火势', 0.009265529923141003)]

   Top 10 words of topic #3    [('发生', 0.03348556533455849), ('爆炸', 0.022389251738786697), ('造成', 0.019663840532302856), ('死亡', 0.01713310182094574), ('受伤', 0.016938429325819016), ('年月日', 0.016354413703083992), ('轿车', 0.012655640952289104), ('警方', 0.012460969388484955), ('袭击', 0.012266295962035656), ('事件', 0.011487606912851334)]

   Top 10 words of topic #4    [('地震', 0.047826822847127914), ('发生', 0.03555167838931084), ('火灾', 0.03140682727098465), ('时分', 0.020885275676846504), ('级', 0.015783920884132385), ('时间', 0.013870910741388798), ('公里', 0.013711493462324142), ('人员伤亡', 0.013073823414742947), ('记者', 0.013073823414742947), ('震感', 0.012276736088097095)]


#查看话题模型信息
mdl.summary()

Run

<Basic Info>
| LDAModel (current version: 0.12.2)
| 332 docs, 29749 words
| Total Vocabs: 8428, Used Vocabs: 2984
| Entropy of words: 7.10665
| Entropy of term-weighted words: 7.10665
| Removed Vocabs: <NA>
|
<Training Info>
| Iterations: 10, Burn-in steps: 0
| Optimization Interval: 10
| Log-likelihood per word: -7.79934
|
<Initial Parameters>
| tw: TermWeight.ONE
| min_cf: 0 (minimum collection frequency of words)
| min_df: 2 (minimum document frequency of words)
| rm_top: 0 (the number of top words to be removed)
| k: 5 (the number of topics between 1 ~ 32767)
| alpha: [0.1] (hyperparameter of Dirichlet distribution for document-topic, given as a single `float` in case of symmetric prior and as a list with length `k` of `float` in case of asymmetric prior.)
| eta: 0.01 (hyperparameter of Dirichlet distribution for topic-word)
| seed: 555 (random seed)
| trained in version 0.12.2
|
<Parameters>
| alpha (Dirichlet prior on the per-document topic distributions)
| [0.7143365 0.6852513 0.75089616 0.6204677 0.7040125 ]
| eta (Dirichlet prior on the per-topic word distribution)
| 0.01
|
<Topics>
| #0 (6513) : 一辆 事故 记者 死亡 造成
| #1 (5655) : 学生 食物中毒 出现 医院 事件
| #2 (6231) : 现场 发生 医院 起火 原因
| #3 (5107) : 发生 爆炸 造成 死亡 受伤
| #4 (6243) : 地震 发生 火灾 时分 级
|

topic解读

根据每个话题top10的特征词,5个话题解读为

  • 交通事故| #0 (6513) : 一辆 事故 记者 死亡 造成
  • 食品安全| #1 (5655) : 学生 食物中毒 出现 医院 事件
  • 火灾新闻| #2 (6231) : 现场 发生 医院 起火 原因
  • 恐怖袭击| #3 (5107) : 发生 爆炸 造成 死亡 受伤
  • 地震灾害| #4 (6243) : 地震 发生 火灾 时分 级

5. 可视化

使用pyLDAvis

import pyLDAvis
import numpy as np
import warnings
warnings.filterwarnings('ignore', category=Warning)

#在notebook显示
pyLDAvis.enable_notebook()

#获取pyldavis需要的参数
topic_term_dists = np.stack([mdl.get_topic_word_dist(k) for k in range(mdl.k)])
doc_topic_dists = np.stack([doc.get_topic_dist() for doc in mdl.docs])
doc_topic_dists /= doc_topic_dists.sum(axis=1, keepdims=True)
doc_lengths = np.array([len(doc.words) for doc in mdl.docs])
vocab = list(mdl.used_vocabs)
term_frequency = mdl.used_vocab_freq


prepared_data = pyLDAvis.prepare(
    topic_term_dists, 
    doc_topic_dists, 
    doc_lengths, 
    vocab, 
    term_frequency,
    start_index=0# tomotopy话题id从0开始,pyLDAvis话题id从1开始
    sort_topics=False #注意:否则pyLDAvis与tomotopy内的话题无法一一对应。 
)


#可视化结果存到html文件中
#pyLDAvis.save_html(prepared_data, 'ldavis.html')

#notebook中显示
pyLDAvis.display(prepared_data)

Run


6. 预测

预测某文档的话题

import jieba
from cntext import STOPWORDS_zh

#预测
doc = '云南永善县级地震已致伤间民房受损中新网日电云南昭通市防震减灾局官方网站消息日时云南昭通永善县级地震造成受伤重伤轻伤送医院救治民房受损户间倒塌户间乡镇学校不同程度受损目前损毁电力交通通讯设施抢通修复调拨帐篷顶紧急转移万人时分云南昭通永善县发生里氏级地震震源深度公里震感强烈成都四川多地明显震感'
words = [w for w in jieba.lcut(doc) if w not in STOPWORDS_zh]

#构造tomotopy需要的数据
doc_inst = mdl.make_doc(words=words)
topic_dist, ll = mdl.infer(doc_inst)
print("Topic Distribution for Unseen Docs: ", topic_dist)

Run

Topic Distribution for Unseen Docs: [0.11645161 0.10240361 0.5342029 0.03622254 0.21071935]
列表长度为5, 列表第三个数值(topic #2)数值最大,该文本最大的可能性是topic #2

补充: 指定主题特征词

如果对数据比较了解,已经知道有一些主题,可以把比较明显的词语分配给指定的topic_id。

mdl = tp.LDAModel(k=5, min_df=2, seed=555)

for words in df['words']:
    if words:
        mdl.add_doc(words)

#把word相撞 分配给topic_0, 权重设置为1, 其他topic权重设置为0.1
#注意这里的range(5) 5是对应的k值
mdl.set_word_prior('相撞', [1.0 if k == 0 else 0.1 for k in range(5)])
#把word地震 分配给topic_1, 权重设置为1, 其他topic权重设置为0.1
mdl.set_word_prior('地震', [1.0 if k == 1 else 0.1 for k in range(5)])
#把word火灾 分配给topic_2, 权重设置为1, 其他topic权重设置为0.1
mdl.set_word_prior('火灾', [1.0 if k == 2 else 0.1 for k in range(5)])
#把word中毒 分配给topic_3, 权重设置为1, 其他topic权重设置为0.1
mdl.set_word_prior('中毒', [1.0 if k == 3 else 0.1 for k in range(5)])
#把word袭击 分配给topic_4, 权重设置为1, 其他topic权重设置为0.1
mdl.set_word_prior('袭击', [1.0 if k == 4 else 0.1 for k in range(5)])

mdl.train()
mdl.summary()  

Run

<Basic Info>
| LDAModel (current version: 0.12.2)
| 332 docs, 29749 words
| Total Vocabs: 8428, Used Vocabs: 2984
| Entropy of words: 7.10665
| Entropy of term-weighted words: 7.10665
| Removed Vocabs: <NA>
|
<Training Info>
| Iterations: 10, Burn-in steps: 0
| Optimization Interval: 10
| Log-likelihood per word: -7.72251
|
<Initial Parameters>
| tw: TermWeight.ONE
| min_cf: 0 (minimum collection frequency of words)
| min_df: 2 (minimum document frequency of words)
| rm_top: 0 (the number of top words to be removed)
| k: 5 (the number of topics between 1 ~ 32767)
| alpha: [0.1] (hyperparameter of Dirichlet distribution for document-topic, given as a single `float` in case of symmetric prior and as a list with length `k` of `float` in case of asymmetric prior.)
| eta: 0.01 (hyperparameter of Dirichlet distribution for topic-word)
| seed: 555 (random seed)
| trained in version 0.12.2
|
<Parameters>
| alpha (Dirichlet prior on the per-document topic distributions)
| [0.7106193 0.60264444 0.5734784 0.71375024 0.6234263 ]
| eta (Dirichlet prior on the per-topic word distribution)
| 0.01
|
<Topics>
| #0 (6599) : 一辆 事故 死亡 发生 造成
| #1 (6087) : 地震 发生 级 公里 年月日
| #2 (5892) : 火灾 发生 现场 大火 起火
| #3 (6402) : 医院 学生 食物中毒 出现 名
| #4 (4769) : 事件 发生 袭击 人员 工作

代码下载

https://textdata.cn/blog/tomotopy_is_the_fastest_topic_model/


精选内容

管理世界 | 使用文本分析词构建并测量短视主义

管理世界 | 使用 经营讨论与分析 测量 企业数字化指标

管理世界 | 用正则表达式、文本向量化、线性回归算法从md&a数据中计算 「企业融资约束指标

管理世界 | 政府与市场心理因素的经济影响及其测度

叙事经济学:揭示经济中的叙事

中文心理词典,含具体性、可成象性等指标

PNAS | 14000+篇心理学顶刊论文可复现性调研(含代码)

可视化 | 词嵌入模型用于计算社科领域刻板印象等信息(含代码)

可视化 | 绘制《三体》人物关系网络图

可视化 | 99-21年地方政府报告关键词变化趋势

可视化 | 文本数据分成n等份、词云图、情绪变化趋势、相似度变化趋势

文本分析 | 中国企业高管团队创新注意力(含代码)

文本分析 | MD&A 信息含量指标构建代码实现

金融研究 | 使用Python构建「关键审计事项信息含量」

转载 | 大数据驱动的「社会经济地位」分析研究综述

使用 Word2Vec 和 TF-IDF 计算五类企业文化

如何用「图嵌入」将企业、高管职业经历表征为向量数据

Nature | 通用中英文六维语义情感词典

采购合同数据集 | 政府采购何以牵动企业创新

96G数据集 | 2亿条中国大陆企业工商注册信息

70G数据集 | 3571万条专利申请数据集(1985-2022年)

数据集 | 3.9G全国POI地点兴趣点数据集

数据集 | 「问询函」

网络爬虫 | 使用Python采集B站弹幕和评论数据

继续滑动看下一个
大邓和他的Python
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存