如题,LDA(Latent Dirichlet Allocation)是主题模型中极具代表性的一种,常用于文本分类,推测文本(文档)的主题分布。简而言之说:LDA算法可以将文档集中的每篇文章所对应的主题以概率分布的形式给出。
对于主题模型而言,选定的算法一旦确定, 需要人为确定的选项(超参)通常是主题数量,LDA算法也不例外:主题数量通常视不同场景进行调整,简单点儿说,通过评估不同主题数模型的困惑度来选择最优的模型主题数。在本文中,采用计算困惑度(Perplexity)的方法来衡量选取的主题数量的优劣:
其中,M是测试语料的大小(文档的数量),Nd是第d篇文本大小(word或token个数)
其中,z是主题,w是文档,r是基于训练集学习的文本-主题分布,简而言之,Perplexity对数函数的分子部分是生成整个文档集的似然估计(表示训练集训练出的参数的生成能力)的负数,由于概率取值范围为[0,1],按照对数函数的定义,分子值是一个正值且与文本生成能力正相关;而分母是整个文档集的的单词数目。那么,也就是说,模型生成能力越强,Perplexity值越小。
1. 加载第三方gensim.LdaModel
from gensim import corpora, models
import math
import matplotlib.pyplot as plt
def ldamodel(num_topics, pwd):
cop = open(pwd, 'r', encoding='UTF-8')
train = []
for line in cop.readlines():
line = [word.strip() for word in line.split(' ')]
train.append(line)
dictionary = corpora.Dictionary(train)
corpus = [dictionary.doc2bow(text) for text in train]
corpora.MmCorpus.serialize('corpus.mm', corpus)
lda = models.LdaModel(corpus=corpus, id2word=dictionary, random_state=1,
num_topics=num_topics)
topic_list = lda.print_topics(num_topics, 10)
return lda, dictionary
2. 计算给定文档集的Perplexity
def perplexity(ldamodel, testset, dictionary, size_dictionary, num_topics):
print('the info of this ldamodel: \n')
print('num of topics: %s' % num_topics)
prep = 0.0
prob_doc_sum = 0.0
topic_word_list = []
for topic_id in range(num_topics):
topic_word = ldamodel.show_topic(topic_id, size_dictionary)
dic = {}
for word, probability in topic_word:
dic[word] = probability
topic_word_list.append(dic)
doc_topics_ist = []
for doc in testset:
doc_topics_ist.append(ldamodel.get_document_topics(doc, minimum_probability=0))
testset_word_num = 0
for i in range(len(testset)):
prob_doc = 0.0
doc = testset[i]
doc_word_num = 0
for word_id, num in dict(doc).items():
prob_word = 0.0
doc_word_num += num
word = dictionary[word_id]
for topic_id in range(num_topics):
# cal p(w) : p(w) = sumz(p(z)*p(w|z))
prob_topic = doc_topics_ist[i][topic_id][1]
prob_topic_word = topic_word_list[topic_id][word]
prob_word += prob_topic * prob_topic_word
prob_doc += math.log(prob_word) # p(d) = sum(log(p(w)))
prob_doc_sum += prob_doc
testset_word_num += doc_word_num
prep = math.exp(-prob_doc_sum / testset_word_num) # perplexity = exp(-sum(p(d)/sum(Nd))
print("模型困惑度为 : %s" % prep)
return prep
3. 绘制主题数与困惑度的折线图
def graph_draw(topic, perplexity):
x = topic
y = perplexity
plt.plot(x, y, color="red", linewidth=2)
plt.xlabel("Number of Topic")
plt.ylabel("Perplexity")
plt.savefig("Perplexity-Topics")
plt.show()
4. 主函数入口
if __name__ == '__main__':
pwd = '~/*_summary.txt'
for i in range(20,21,1):
print("抽样为"+str(i)+"时的perplexity")
a=range(1,50,1) # 主题个数
p=[]
for num_topics in a:
lda, dictionary = ldamodel(num_topics, pwd)
corpus = corpora.MmCorpus('corpus.mm')
testset = []
for c in range(int(corpus.num_docs/i)):
testset.append(corpus[c*i])
prep = perplexity(lda, testset, dictionary, len(dictionary.keys()), num_topics)
p.append(prep)
graph_draw(a,p)
备注
在上图中,随着K(Number of Topic)值的增大,困惑度(Perplexity)逐渐减小。根据手肘法,并且当K约为5的时候,存在一个显著的拐点:
故拐点5即为K的最佳值,因此在本文所选定的语料集中,LDA主题的生成数量选定为5。
值得注意的是,当K>10时,训练困惑度出现小幅度的上升,这与样本空间和算法本身都有关联,属正常现象。
在使用LDA算法提取关键词时,通常会采样如下方案:使用tf-idf对数据集中的每个词进行加权,得到加权后的向量表示,通过词空间构建和向量化方法,得到数据集的主题-词分布,最后计算词的分布和文档的分布的相似度,取相似度最高的keyword num个词作为关键词。具体见下一篇文章:LDA主题模型提取文本中的关键词
完结撒花