添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接



1.tomotopy简介

lDA困惑度是负的 lda 困惑度_笔记

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

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

lDA困惑度是负的 lda 困惑度_lDA困惑度是负的_02

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

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

2.安装

!pip3 install tomotopy==0.12.2
!pip3 install pyLDAvis==3.3.1  
#目前,tomotopy 可以利用 AVX2、AVX 或 SSE2 SIMD 指令集来最大程度利用PC的
import tomotopy as tp
tp.isa
#将会输出'avx2'

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

3.数据的导入

import pandas as pd
df = pd.read_csv('disaster_news.csv')
df.head()

lDA困惑度是负的 lda 困惑度_lDA困惑度是负的_03

4.数据预处理

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可以设置为读取一个文件什么的,按自己需要的情况修改
test = "云南永善县级地震已致人伤间民房受损中新网月日电据云南昭通市防震减灾局官方网站消息截至日时云南昭通永善县级地震已造成人受伤其中重伤人轻伤人已全部送医院救治民房受损户间倒塌户间个乡镇所学校不同程度受损目前被损毁电力交通通讯设施已全部抢通修复当地已调拨帐篷顶紧急转移万人月日时分云南昭通永善县发生里氏级地震震源深度公里当地震感强烈此外成都等四川多地也有明显震感"
print(segment(test))
#####这句应该是说写成一个列表,就是第一列是原本的text内容后边追加的第二列是做好预处理的文本
df['words'] = df['text'].apply(segment)
df.head()
####贴了原文的图,对比一下上下的图片

lDA困惑度是负的 lda 困惑度_python_04

5.找最佳主题数

正常的步骤应该认真对待这步,在一定区间范围内,根据模型得分找到合理的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)

lDA困惑度是负的 lda 困惑度_机器学习_05

也可以计算困惑度perplexity,困惑度越小越好

6. 训练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')
输出内容(我是原文贴过来的,格式会怪怪的)
Top 10 words of topic #0
[('一辆', 0.02751251682639122), ('事故', 0.021704642102122307), ('记者', 0.018342
189490795135), ('死亡', 0.01650812290608883), ('造成', 0.014062701724469662), ('人
员', 0.013909862376749516), ('现场', 0.013451346196234226), ('受伤', 0.0126871513
20278645), ('相撞', 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)]

7.可视化

可以做一个看着很高级的图啊哈哈哈哈

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)

lDA困惑度是负的 lda 困惑度_lDA困惑度是负的_06

8.某文档主题预测

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)
输出内容
Topic Distribution for Unseen Docs:  [0.11645161 0.10240361 0.5342029  0.03622254 0.21071935]

列表长度为5, 列表第三个数值(topic #2)数值最大,该文本最大的可能性是topic #2

9.指定主题特征词

如果对数据比较了解,已经知道有一些主题,可以把比较明显的词语分配给指定的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()

lDA困惑度是负的 lda 困惑度_机器学习_07