虽然Latent Dirichlet allocation 和Hierarchical Dirichlet Process 通常用于在文本语料库中派生主题,然后使用这些主题对单个条目进行分类,但也可以开发一种为整个语料库派生关键字的方法。这种方法受益于不依赖另一个文本语料库。一个基本的工作流程是将这些 Dirichlet 关键字与最常用的词进行比较,以查看 LDA 或 HDP 是否能够识别出不包含在最常用词中的重要词。
在使用以下代码之前,一般建议先对文本进行如下预处理:
- 从文本中删除标点符号(参见string.punctuation)
- 将字符串文本转换为“标记”(str.split(‘ ’).lower() 为单个单词)
- 删除数字和停用词(请参阅 stopwordsiso 或 stop_words)
- 创建二元组 - 文本中经常一起出现的单词组合(参见 gensim.Phrases)
- 词形化标记 - 将单词转换为其基本形式(参见 spacy 或 NLTK)
- 删除不够频繁(或过于频繁,但在这种情况下跳过删除过于频繁的令牌,因为这些是很好的关键字)的标记
这些步骤将在下面创建变量corpus。可以在 here 找到对所有这一切的一个很好的概述以及对 LDA 的解释。
现在使用 gensim 用于 LDA 和 HDP:
from gensim.models import LdaModel, HdpModel
from gensim import corpora
首先创建一个将corpus 中的单词映射到索引的dirichlet 字典,然后使用它创建一个单词包,其中corpus 中的标记被它们的索引替换。这是通过:
dirichlet_dict = corpora.Dictionary(corpus)
bow_corpus = [dirichlet_dict.doc2bow(text) for text in corpus]
对于LDA,需要推导最优的主题数量,可以通过this answer中的方法启发式地完成。假设我们的最佳主题数是 10,根据问题我们想要 300 个关键字:
num_topics = 10
num_keywords = 300
创建一个 LDA 模型:
dirichlet_model = LdaModel(corpus=bow_corpus,
id2word=dirichlet_dict,
num_topics=num_topics,
update_every=1,
chunksize=len(bow_corpus),
passes=20,
alpha='auto')
接下来是一个函数,可以根据整个语料库的平均连贯性得出最佳主题。首先将生成每个主题最重要单词的有序列表;然后找到每个主题对整个语料库的平均连贯性;最后,主题根据这个平均连贯性进行排序,并与稍后使用的平均值列表一起返回。所有这些的代码如下(包括从下面使用 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.type_of_model
bow_corpus : list of lists (contains (id, freq) tuples)
num_topics : int (default=10)
num_keywords : int (default=10)
Returns
-------
ordered_topics, ordered_topic_averages: list of lists and list
"""
if type(dirichlet_model) == gensim.models.ldamodel.LdaModel:
shown_topics = dirichlet_model.show_topics(num_topics=num_topics,
num_words=num_keywords,
formatted=False)
elif type(dirichlet_model) == gensim.models.hdpmodel.HdpModel:
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] # limit for HDP
ordered_topic_averages = [topic_averages[i] for i in topic_indexes_by_avg_coherence][:num_topics] # limit for HDP
ordered_topic_averages = [a/sum(ordered_topic_averages) for a in ordered_topic_averages] # normalize HDP values
return ordered_topics, ordered_topic_averages
现在获取关键字列表 - 主题中最重要的单词。这是通过基于它们与整体的平均连贯性从每个有序主题中对单词(默认情况下再次按重要性排序)进行子集化来完成的。为了明确解释,假设只有两个主题,文本与第一个主题的连贯性为 70%,与第二个主题的连贯性为 30%。然后关键字可以是第一个主题中前 70% 的词,以及第二个主题中尚未选择的前 30% 的词。这是通过以下方式实现的:
ordered_topics, ordered_topic_averages = \
order_subset_by_coherence(dirichlet_model=dirichlet_model,
bow_corpus=bow_corpus,
num_topics=num_topics,
num_keywords=num_keywords)
keywords = []
for i in range(num_topics):
# Find the number of indexes to select, which can later be extended if the word has already been selected
selection_indexes = list(range(int(round(num_keywords * ordered_topic_averages[i]))))
if selection_indexes == [] and len(keywords) < num_keywords:
# Fix potential rounding error by giving this topic one selection
selection_indexes = [0]
for s_i in selection_indexes:
if ordered_topics[i][s_i] not in keywords and ordered_topics[i][s_i] not in ignore_words:
keywords.append(ordered_topics[i][s_i])
else:
selection_indexes.append(selection_indexes[-1] + 1)
# Fix for if too many were selected
keywords = keywords[:num_keywords]
上面还包括变量ignore_words,它是一个不应该包含在结果中的单词列表。
对于 HDP,模型遵循与上述类似的过程,除了在模型创建中不需要传递 num_topics 和其他参数。 HDP 自己推导出最佳主题,但随后需要使用order_subset_by_coherence 对这些主题进行排序和子集化,以确保将最佳主题用于有限选择。通过以下方式创建模型:
dirichlet_model = HdpModel(corpus=bow_corpus,
id2word=dirichlet_dict,
chunksize=len(bow_corpus))
最好同时测试 LDA 和 HDP,因为如果能够找到合适数量的主题,LDA 可以根据问题的需要表现出色(这仍然是 HDP 的标准)。仅将 Dirichlet 关键字与词频进行比较,希望生成的是与文本的整体主题更相关的关键字列表,而不仅仅是最常见的词。
显然,根据文本连贯性百分比从主题中选择有序词并不能按重要性对关键字进行整体排序,因为稍后会选择一些在整体连贯性较低的主题中非常重要的词。
使用LDA为语料库中的单个文本生成关键字的过程可以在this answer找到。