Hierarchical Dirichlet Process Gensim 主题数与语料库大小无关

Hierarchical Dirichlet Process Gensim topic number independent of corpus size

我在一组文档上使用 Gensim HDP 模块。

>>> hdp = models.HdpModel(corpusB, id2word=dictionaryB)
>>> topics = hdp.print_topics(topics=-1, topn=20)
>>> len(topics)
150
>>> hdp = models.HdpModel(corpusA, id2word=dictionaryA)
>>> topics = hdp.print_topics(topics=-1, topn=20)
>>> len(topics)
150
>>> len(corpusA)
1113
>>> len(corpusB)
17

为什么主题数量与语料库长度无关?

我没有用过 gensim 做 HDP,但有没有可能小语料库中的大部分主题出现的概率都极低?您可以尝试打印主题概率吗?也许,主题数组的长度并不一定意味着所有这些主题实际上都在语料库中找到。

我想你误解了被调用方法执行的操作。直接从文档中可以看到:

Alias for show_topics() that prints the top n most probable words for topics number of topics to log. Set topics=-1 to print all topics.

您训练模型时未指定主题数量的截断级别,默认级别为 150。使用 topics=-1 调用 print_topics 您将获得每个主题的前 20 个单词,在您的案例中有 150 个主题。

我还是图书馆的新手,所以我可能错了

@user3907335 在这里完全正确:HDP 将计算与分配的截断级别一样多的主题。 但是,可能很多这样的话题出现的概率基本为零。为了在我自己的工作中解决这个问题,我编写了一个方便的小函数来粗略估计与每个主题相关的概率权重。请注意,这只是一个粗略的指标:它没有考虑与每个词相关的概率。即便如此,它还是提供了一个很好的指标来衡量哪些主题有意义,哪些主题没有意义:

import pandas as pd
import numpy as np 

def topic_prob_extractor(hdp=None, topn=None):
    topic_list = hdp.show_topics(topics=-1, topn=topn)
    topics = [int(x.split(':')[0].split(' ')[1]) for x in topic_list]
    split_list = [x.split(' ') for x in topic_list]
    weights = []
    for lst in split_list:
        sub_list = []
        for entry in lst: 
            if '*' in entry: 
                sub_list.append(float(entry.split('*')[0]))
        weights.append(np.asarray(sub_list))
    sums = [np.sum(x) for x in weights]
    return pd.DataFrame({'topic_id' : topics, 'weight' : sums})

我假设您已经知道如何计算 HDP 模型。一旦你有了一个由 gensim 计算的 hdp 模型,你就可以按如下方式调用该函数:

topic_weights = topic_prob_extractor(hdp, 500)

@Aaron 上面的代码由于 gensim API 更改而损坏。我重写并简化如下。截至 2017 年 6 月与 gensim v2.1.0

一起使用
import pandas as pd

def topic_prob_extractor(gensim_hdp):
    shown_topics = gensim_hdp.show_topics(num_topics=-1, formatted=False)
    topics_nos = [x[0] for x in shown_topics ]
    weights = [ sum([item[1] for item in shown_topics[topicN][1]]) for topicN in topics_nos ]

    return pd.DataFrame({'topic_id' : topics_nos, 'weight' : weights})

@Aron 和@Roko Mijic 的方法忽略了这样一个事实,即函数 show_topics returns 默认情况下仅显示每个主题的前 20 个单词。如果一个 returns 组成一个主题的所有单词,那么在这种情况下所有近似的主题概率将是 1(或 0.999999)。我试验了以下代码,它改编自@Roko Mijic:

def topic_prob_extractor(gensim_hdp, t=-1, w=25, isSorted=True):
    """
    Input the gensim model to get the rough topics' probabilities
    """
    shown_topics = gensim_hdp.show_topics(num_topics=t, num_words=w ,formatted=False)
    topics_nos = [x[0] for x in shown_topics ]
    weights = [ sum([item[1] for item in shown_topics[topicN][1]]) for topicN in topics_nos ]
    if (isSorted):
        return pd.DataFrame({'topic_id' : topics_nos, 'weight' : weights}).sort_values(by = "weight", ascending=False);
    else:
        return pd.DataFrame({'topic_id' : topics_nos, 'weight' : weights});

一种更好但我不确定是否 100% 有效的方法是提到的方法 here。您可以获得 HDP 模型的主题真实权重(alpha 向量):

alpha = hdpModel.hdp_to_lda()[0];

检查主题的等效 alpha 值比计算每个主题的前 20 个单词的权重来估计其在数据中的使用概率更合乎逻辑。

Gensim(版本 3.8.3)中显然存在一个错误,其中将 -1 赋给 show_topics 根本不会 return 任何东西。所以我调整了 Roko Mijicaaron 的答案。

def topic_prob_extractor(gensim_hdp):
    shown_topics = gensim_hdp.show_topics(num_topics=gensim_hdp.m_T, formatted=False)
    topics_nos = [x[0] for x in shown_topics ]
    weights = [ sum([item[1] for item in shown_topics[topicN][1]]) for topicN in topics_nos ]
    return pd.DataFrame({'topic_id' : topics_nos, 'weight' : weights})

从单个文本级别的连贯性推导 HDP 主题的平均连贯性是一种对它们进行排序(并可能截断)的方法。以下函数就是这样做的:

def order_subset_by_coherence(dirichlet_model, bow_corpus, num_topics=10, num_keywords=10):
    """
    Orders topics based on their average coherence across the corpus

    Parameters
    ----------
        dirichlet_model : gensim.models.hdpmodel.HdpModel
        bow_corpus : list of lists (contains (id, freq) tuples)
        num_topics : int (default=10)
        num_keywords : int (default=10)

    Returns
    -------
        ordered_topics: list of lists containing topic tokens
    """
    shown_topics = dirichlet_model.show_topics(num_topics=150, # return all topics
                                               num_words=num_keywords,
                                               formatted=False)
    model_topics = [[word[0] for word in topic[1]] for topic in shown_topics]
    topic_corpus = dirichlet_model.__getitem__(bow=bow_corpus, eps=0) # cutoff probability to 0 

    topics_per_response = [response for response in topic_corpus]
    flat_topic_coherences = [item for sublist in topics_per_response for item in sublist]

    significant_topics = list(set([t_c[0] for t_c in flat_topic_coherences])) # those that appear
    topic_averages = [sum([t_c[1] for t_c in flat_topic_coherences if t_c[0] == topic_num]) / len(bow_corpus) \
                      for topic_num in significant_topics]

    topic_indexes_by_avg_coherence = [tup[0] for tup in sorted(enumerate(topic_averages), key=lambda i:i[1])[::-1]]
    significant_topics_by_avg_coherence = [significant_topics[i] for i in topic_indexes_by_avg_coherence]
    ordered_topics = [model_topics[i] for i in significant_topics_by_avg_coherence][:num_topics] # truncate if desired

    return ordered_topics

此函数的一个版本包含与语料库的关键字(标签)生成主题相关联的平均连贯性输出,可在 this answer. A similar process for keywords for individual texts can further be found in this answer.

中找到