LDA主题模型主题数的确定

通过折肘法+困惑度折线确定lda模型的主题个数

前言

如题,LDA(Latent Dirichlet Allocation)是主题模型中极具代表性的一种,常用于文本分类,推测文本(文档)的主题分布。简而言之:LDA算法可以将文档集中的每篇文章所对应的主题以概率分布的形式给出。

  • 给定一些文档集,可通过LDA算法获得这些文档的主题分布,后续可根据主题分布做文本聚类文本分类
  • LDA 模型涉及很多数学先验知识,也是LDA晦涩难懂的主要原因,本文不会推导LDA涉及的数学公式,感兴趣的朋友可以参考:rickjin的《LDA数学八卦》;http://www.flickering.cn/tag/lda/
  • LDA模型通常需要确定从每篇文章中提取多少个主题(关键词),本文重点介绍通过折肘法+困惑度法确定LDA主题模型主题数

理论

对于主题模型而言,选定的算法一旦确定, 需要人为确定的选项(超参)通常是主题数量,LDA算法也不例外:主题数量通常视不同场景进行调整,简单点儿说,通过评估不同主题数模型的困惑度来选择最优的模型主题数。在本文中,采用计算困惑度Perplexity)的方法来衡量选取的主题数量的优劣:
在这里插入图片描述
其中,M是测试语料的大小(文档的数量),Nd是第d篇文本大小(wordtoken个数)
在这里插入图片描述
其中,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)

备注

  • 第4步中的pwd,要求将给定的语料集合成一个文本文件。原因是在测试使用LDA模型提取单篇文档时报bug,至今未解决[2022.04.28];
  • 不会写latex公式,后面的学习内容;

图像

使用随机抽取语料采用本文方法的结果
主题数-困惑度折线图

结论

  • 在上图中,随着KNumber of Topic)值的增大,困惑度(Perplexity)逐渐减小。根据手肘法,并且当K约为5的时候,存在一个显著的拐点:

    1. K属于(1, 5)时,曲线急剧下降;
    2. K属于(5,10)时,曲线基本趋于平稳;
  • 故拐点5即为K的最佳值,因此在本文所选定的语料集中,LDA主题的生成数量选定为5。

  • 值得注意的是,当K>10时,训练困惑度出现小幅度的上升,这与样本空间和算法本身都有关联,属正常现象。

  • 在使用LDA算法提取关键词时,通常会采样如下方案:使用tf-idf对数据集中的每个词进行加权,得到加权后的向量表示,通过词空间构建和向量化方法,得到数据集的主题-词分布,最后计算词的分布和文档的分布的相似度,取相似度最高的keyword num个词作为关键词。具体见下一篇文章:LDA主题模型提取文本中的关键词

完结撒花 🎉🎉

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐