使用Gensim获得LDA模型的最佳主题数的最佳方法是什么?

Aka*_*tor 6 python text-mining lda gensim topic-modeling

我试图在Gensim中获得LDA模型的最佳主题数.我发现的一种方法是计算每个模型的对数似然,并将每个模型相互比较,例如,使用潜在Dirichlet分配的输入参数

因此,我研究了使用Gensim计算LDA模型的对数似然性,并得出以下文章:您如何估计潜在Dirichlet分配模型的α参数?

这基本上说明update_alpha()方法实现了黄,乔纳森所描述的方法.Dirichlet分布参数的最大似然估计.我仍然不知道如何在不更改代码的情况下使用libary获取此参数.

如何使用Gensim从LDA模型获取对数似然?

有没有更好的方法来获得Gensim的最佳主题数量?

小智 13

一般的经验法则是创建跨不同主题编号的 LDA 模型,然后检查每个主题的Jaccard 相似性和连贯性。在这种情况下,一致性通过主题中高分词之间的语义相似度来衡量单个主题(这些词是否在文本语料库中共同出现)。下面将对最佳主题数给出强烈的直觉。这应该是跳转到分层狄利克雷过程之前的基线,因为该技术已被发现在实际应用中存在问题。

首先为您要考虑的各种主题编号的模型和主题词创建字典,在这种情况下corpus是清理的标记,num_topics是您要考虑的主题列表,是您想要的num_words每个主题的热门词数要考虑的指标:

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from gensim.models import LdaModel, CoherenceModel
from gensim import corpora
Run Code Online (Sandbox Code Playgroud)
dirichlet_dict = corpora.Dictionary(corpus)
bow_corpus = [dirichlet_dict.doc2bow(text) for text in corpus]

# Considering 1-15 topics, as the last is cut off
num_topics = list(range(16)[1:])
num_keywords = 15

LDA_models = {}
LDA_topics = {}
for i in num_topics:
    LDA_models[i] = LdaModel(corpus=bow_corpus,
                             id2word=dirichlet_dict,
                             num_topics=i,
                             update_every=1,
                             chunksize=len(bow_corpus),
                             passes=20,
                             alpha='auto',
                             random_state=42)

    shown_topics = LDA_models[i].show_topics(num_topics=i, 
                                             num_words=num_keywords,
                                             formatted=False)
    LDA_topics[i] = [[word[0] for word in topic[1]] for topic in shown_topics]
Run Code Online (Sandbox Code Playgroud)

现在创建一个函数来导出两个主题的 Jaccard 相似度:

def jaccard_similarity(topic_1, topic_2):
    """
    Derives the Jaccard similarity of two topics

    Jaccard similarity:
    - A statistic used for comparing the similarity and diversity of sample sets
    - J(A,B) = (A ? B)/(A ? B)
    - Goal is low Jaccard scores for coverage of the diverse elements
    """
    intersection = set(topic_1).intersection(set(topic_2))
    union = set(topic_1).union(set(topic_2))
                    
    return float(len(intersection))/float(len(union))
Run Code Online (Sandbox Code Playgroud)

通过考虑下一个主题,使用以上内容推导出主题的平均稳定性:

LDA_stability = {}
for i in range(0, len(num_topics)-1):
    jaccard_sims = []
    for t1, topic1 in enumerate(LDA_topics[num_topics[i]]): # pylint: disable=unused-variable
        sims = []
        for t2, topic2 in enumerate(LDA_topics[num_topics[i+1]]): # pylint: disable=unused-variable
            sims.append(jaccard_similarity(topic1, topic2))    
        
        jaccard_sims.append(sims)    
    
    LDA_stability[num_topics[i]] = jaccard_sims
                
mean_stabilities = [np.array(LDA_stability[i]).mean() for i in num_topics[:-1]]
Run Code Online (Sandbox Code Playgroud)

gensim有一个内置的主题一致性模型(这使用了该'c_v'选项):

coherences = [CoherenceModel(model=LDA_models[i], texts=corpus, dictionary=dirichlet_dict, coherence='c_v').get_coherence()\
              for i in num_topics[:-1]]
Run Code Online (Sandbox Code Playgroud)

从这里大致通过每个主题数量的一致性和稳定性之间的差异得出理想的主题数量:

coh_sta_diffs = [coherences[i] - mean_stabilities[i] for i in range(num_keywords)[:-1]] # limit topic numbers to the number of keywords
coh_sta_max = max(coh_sta_diffs)
coh_sta_max_idxs = [i for i, j in enumerate(coh_sta_diffs) if j == coh_sta_max]
ideal_topic_num_index = coh_sta_max_idxs[0] # choose less topics in case there's more than one max
ideal_topic_num = num_topics[ideal_topic_num_index]
Run Code Online (Sandbox Code Playgroud)

最后在主题编号上绘制这些指标:

plt.figure(figsize=(20,10))
ax = sns.lineplot(x=num_topics[:-1], y=mean_stabilities, label='Average Topic Overlap')
ax = sns.lineplot(x=num_topics[:-1], y=coherences, label='Topic Coherence')

ax.axvline(x=ideal_topic_num, label='Ideal Number of Topics', color='black')
ax.axvspan(xmin=ideal_topic_num - 1, xmax=ideal_topic_num + 1, alpha=0.5, facecolor='grey')

y_max = max(max(mean_stabilities), max(coherences)) + (0.10 * max(max(mean_stabilities), max(coherences)))
ax.set_ylim([0, y_max])
ax.set_xlim([1, num_topics[-1]-1])
                
ax.axes.set_title('Model Metrics per Number of Topics', fontsize=25)
ax.set_ylabel('Metric Level', fontsize=20)
ax.set_xlabel('Number of Topics', fontsize=20)
plt.legend(fontsize=20)
plt.show()   
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

您理想的主题数量将根据 Jaccard 相似度最大化连贯性并最小化主题重叠。在这种情况下,看起来我们可以安全地选择 14 左右的主题编号。


SJB*_*SJB 6

虽然我不能特别评论 Gensim,但我可以权衡一些优化主题的一般建议。

正如您所说,使用对数似然是一种方法。另一种选择是在模型生成过程中保留一组文档,并在模型完成时推断它们的主题并检查它是否有意义。

您可以尝试一种完全不同的方法是分层狄利克雷过程,该方法可以在不指定的情况下动态查找语料库中的主题数。

有很多关于如何最好地指定参数和评估主题模型的论文,根据您的经验水平,这些可能适合也可能不适合您:

Rethinking LDA: Why Priors Matter , Wallach, HM, Mimno, D. 和 McCallum, A.

主题模型的评估方法, Wallach HM, Murray, I., Salakhutdinov, R. 和 Mimno, D.

此外,这是关于分层狄利克雷过程的论文:

分层狄利克雷过程,Teh,YW,Jordan,MI,Beal,MJ 和 Blei,DM