首页 文章详情

NLP主题挖掘必读:从LDA到ATM、DTM变体模型看应用场景、关键问题与开源实现总结

大邓和他的Python | 1689 2022-05-28 00:51 0 0 0
UniSMS (合一短信)

主题分析与挖掘是当前NLP处理的一个典型范式,广泛应用于文本聚类、文本表示、文本分类等场景当中。

在实践环节,强大的主题建模工具Gensim: Topic modelling for humans更是提供了十分方便的调用接口,根据约定好的数据格式,进行分词、cbow转换、tfidf转换,然后送入模型,可以快速训练得到适配业务的主题模型。

在理论环节,LDA(Latent Dirichlet Allocation),是Blei等人于2003年提出的基于概率模型的主题模型算法,用来识别大规模文档集或语料库中的潜在隐藏的主题信息,该方法假设每个词是由背后的一个潜在隐藏的主题中抽取出来,每篇文章是由多个主题混合混合而成,并且每个主题可以由多个词的概率表征。

因此,对于我们看到的每篇文档,LDA 定义了如下生成过程: 

首先,对每一篇文档,从主题分布中抽取一个主题;

其次,从上述被抽到的主题所对应的单词分布中抽取一个单词;

最后,重复上述过程直至遍历文档中的每一个单词;

通过吉布斯采样和狄利克雷分布,分别估计出文档-主题概率,主题-概率后,既可以产出多种有意义的结果。

而随着具体业务的变化,LDA后续陆续出现了变体应用类型,包括适用于作者主题分析的ATM模型(Author-Topic Model)、 加入时许的动态主题模型DTM(Dynamic Topic Models)等。

模型类型功能含义应用场景
LDA模型(Latent Dirichlet Allocation)一般主题模型,用于主题分析文章主题偏好、单词的主题偏好、主题内容展示、主题内容矩阵
ATM模型(Author-Topic Model)加入监督的’作者’,每个作者对不同主题的偏好作者主题偏好、词语主题偏好、相似作者推荐、可视化
DTM模型(Dynamic Topic Models)加入时间因素,不同主题会随着时间变动时间-主题词条矩阵、主题-时间词条矩阵、文档主题偏好、新文档预测、跨时间+主题属性的文档相似性

这是一个比较有意思的话题,在舆情监控领域使用较多,本文主要介绍LDA主题模型的几个典型的变体,并对其应用场景、具体功能、代码实现以及开源工具进行论述。


一、使用LDA进行文本主题建模与聚类

LDA(Latent Dirichlet Allocation),通过对文本进行分词,并进行LDA训练,可以得到指定k个主题下的文档聚类结果,产生文档的主题分布、词语的主题分布等概率统计数据,基于这些数据,可以支撑多种应用。

论文地址:https://www.researchgate.net/publication/2917336_Journal_of_Machine_Learning_Research_3_2003_993-1022_Submitted_202_Published_103_Latent_Dirichlet_Allocation

例如,下图展示了文献1在基于Sogou新闻语料的LDA实验结果,得到了20个主题下的词语分布。

1、应用场景

功能代表例子
每个主题对应的主题词(0, '0.143*"苹果" + 0.077*"水果" + 0.070*"树上" + 0.058*"电脑" + 0.048*"叶子"')
每个主题对应的文档集合[1234,456,756,4545,1245]
某个词汇属于特定主题的几率[('水果', 0.03219079), ('公司', 0.056897775)]
新旧文档主题类别,取概率最大主题[('水果', 0.80287796), ('公司', 0.19712202)]
文档主题表示(维度大小为主题数目,值为对应概率),可额用于文本分类[0,12323, 0.4564565,0.45645,......]
词语对应的主题embedding:Topic Word Embedding训练文档中每个词会分配一个主题,根据主题权重,生成向量,可以一定程度上解决一词多义问题。
2、具体实现
1)关键输入

2)代码实现

针对需要建模的文本,进行分词、cbow转换、tfidf转换,设定需要聚类的主题数目,调用gensim提供的接口,即可完成训练。

from gensim import corpora, models
def train_ldamodel(num_topics, data):
    train = data
    dictionary = corpora.Dictionary(train)
    corpus = [dictionary.doc2bow(text) for text in train] 
    # corpus里面的存储格式(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)
    corpus_tfidf=models.TfidfModel(corpus)[corpus]
    # corpus_tfidf将corpus表示为tfidf形式,可以有效提升性能;
    lda = models.LdaModel(corpus=corpus_tfidf, id2word=dictionary, num_topics=num_topics, passes=10)
    return lda, dictionary

二、使用ATM模型进行作者写作主题分析

ATM模型(author-topic model)是LDA主题模型(Latent Dirichlet Allocation )的拓展,能对某个语料库中作者的写作主题进行分析,找出某个作家的写作主题倾向,以及找到具有同样写作倾向的作家。

在传统LDA模型的基础上,加入author的概念。传统LDA模型,是描述文档和词(文档组成元素)之前的关系,这种关系用主题(topic)来衔接和描述。这篇文章加入author的概念,即一篇文章可能有多个author,一个author可能有多个文章,词是文章的组成元素,那么,ATM模型旧通过topic描述了author和词之间的关系。


论文原文:https://arxiv.org/abs/1207.4169


例如,下图展示了文献3中使用虎嗅网4W+文章得到的ATM建模结果。

1、应用场景

功能代表例子
主题对应的主题词(0, '0.143*"苹果" + 0.077*"水果" + 0.070*"树上" + 0.058*"电脑" + 0.048*"叶子"')
作者对应的主题分布[1234,456,756,4545,1245]
作者最相似的作者集合[('水果', 0.03219079), ('公司', 0.056897775)]
作者的向量表示[('水果', 0.80287796), ('公司', 0.19712202)]

2、具体实现

1)关键输入

2)代码实现

def train_atmmodel(datas):
   for data in datas:
        author = data[0]
        text = data[1]
        wds = cut_text(text)
        clean_txt = ' '.join(wds)
        docs.add(clean_txt)
        doc = clean_txt
        if doc not in doc_url_dict:
            doc_url_dict[doc] = [url]
        else:
            doc_url_dict[doc].append(url)
        if author not in author_doc_dict:
            author_doc_dict[author] = [clean_txt]
        else:
            author_doc_dict[author].append(clean_txt)
    print("building dictionary......")
    docs = list(docs)
    doc_id_dict = {}
    for idx, doc in enumerate(docs):
        doc_id_dict[idx] = doc + '@' + ';'.join(doc_url_dict.get(doc)) 
    ## 构建dictionary
    doc_wds = [doc.split(' 'for doc in docs]
    dictionary = Dictionary(doc_wds)
    print("building corpus......")
    corpus = [dictionary.doc2bow(doc) for doc in doc_wds]
    print("building author2doc......")
    author2doc = dict()
    doc_dict = {doc:i for i, doc in enumerate(docs)}
    for author, docs in author_doc_dict.items():
        author2doc[author] = [doc_dict.get(i) for i in docs]
    corpus, dictionary, author2doc, doc_id_dict = prepare_data()
    corpus_tfidf=models.TfidfModel(corpus)[corpus]
    model = AuthorTopicModel(corpus=corpus_tfidf, num_topics=self.num_topics, id2word=dict(dictionary.items()), author2doc=author2doc, passes=10, eval_every=0, chunksize=1000, iterations=1, random_state=1)
    return model

三、使用DTM模型进行主题时序演化分析

DTM模型,Dynamic Topic Model,来源于Blei于2006发表在第23届机器学习国际会议上的论文与先前的Latent Dirichlate Allocation(LDA)模型有所不同,DTM引入了时间因素,从而刻画语料库主题随时间的动态演化。

在LDA模型中,给定语料库中的所有文档,并无时间先后的差别,与词袋模型(bag-of-words)中的词无先后之分类似,在建模的过程中认为整个语料库中的K个主题是固定的,在DTM模型中,文档有了时间属性,具有先后之分。DTM认为,在不同时期,主题是随时间动态演化的。


论文原文:https://dl.acm.org/doi/10.1145/1143844.1143859


例如,通过分析由Ed Edi-son于1880年创立的Jour-nal Science的100多年的OCR文章来证明其适用性,在实现上,文章按年份分组,每年的艺术作品都来自于去年主题演变而来的一系列主题,如下图所示:

1、应用场景


功能代表例子
不同时期的各个主题的情况(u'blair', 0.0062483544617615841),(u'labour', 0.0059223974769828398)]
每个主题的各个时期主题重要词[(u'blair', 0.0061528696567048772), (u'labour', 0.0054905202853533239)]
不同文档主题偏好[ 5.46298825e-05 2.18468637e-01 5.46298825e-05 5.46298825e-05 7.81367474e-01]
新文档主题预测[ 0.00110497 0.00110497 0.00110497 0.00110497 0.99558011]
跨时间+主题属性的文档相似性先把文档dictionary.doc2bow矢量化,然后变成文档主题偏好向量(1*5),然后根据两个文档的主题偏好向量用hellinger距离进行求相似。

2、具体实现

1)关键输入:

2)代码实现:

def train_dtmmodel():
    corpus, dictionary = prepare_data()
    a = [i for i in range(len(corpus))]
    ## 每个均分为1000个文档,因为没有时间信息,调用time_slice方法进行处理
    step = 1000
    time_slice = [len(a[i:i+step]) for i in range(0,len(a),step)]
    model = LdaSeqModel(corpus=corpus, time_slice=time_slice, num_topics=self.num_topics, chunksize=1)
    return model

四、基于困惑度的最佳主题数确定

但我们看到最后,会发现,无论是LDA,还是ATM,都会遇到一个最佳主题数topic_number的设定问题。

例如,援引知乎中的一个问题:https://www.zhihu.com/question/32286630

怎么确定LDA的topic个数?面试时,由于之前用过LDA做推荐,面试官就问怎么确定LDA的topic个数,我就实话实说是自己拍的,面试官就一个劲问“你觉得合理吗?你难道就这么草率吗?”搞得我无所适从,请问有哪些方法确定LDA的topic个数呢?

困惑度perplexity是常用的一个手段。

其中,D表示语料库中的测试集,M偏文档,Nd表示每篇文档d中的单词数,Wd表示文档d中的词,p(Wd)即文档中Wd的产生概率。

import math
def perplexity(ldamodel, testset, dictionary, size_dictionary, 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  # the probablity of the doc
        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))
    return prep

五、开源主题模型可视化工具

实际上,主题结果与可视化工具进行搭配能够最大化的发挥出其效果,pyLDAvis是python中的一个对LDA主题模型进行交互可视化的库,可以将主题模型建模后的结果,制作成一个网页交互版的结果分析工具。

pyLDAvis在可视化呈现中以可视化的方式,逐步展示每个主题的意义、每个主题在总语料库的比重以及主题之间的关联信息。

地址:https://github.com/bmabey/pyLDAvis

总结

本文主要介绍LDA主题模型的几个典型的变体,并对其应用场景、具体功能、代码实现以及开源工具进行论述。

lda最大的意义在于从统计的视角,给出了一个从文档、主题、词语之间的概率计算方法,从而为文本的表示、语义建模奠定了基础。正如文中所说的,可以支撑多种业务下的场景落地可能。

不过,主题数的确定、基于主题,再进行聚类、主题名称生成的工作依旧必不可少。

本文受到如下参考文献的启发,并做了参考,感谢前人的整理。

参考文献

1、https://www.cnblogs.com/chenbjin/p/5638904.html

2、https://www.heywhale.com/mw/project/621bb7c17e72dd00175e25e5

3、https://zhuanlan.zhihu.com/p/51556982

4、https://cloud.tencent.com/developer/article/1435976

5、https://blog.csdn.net/Lyric1/article/details/96331805

6、https://blog.csdn.net/xceman1997/article/details/50334853

关于我们

老刘,刘焕勇,NLP开源爱好者与践行者,主页:https://liuhuanyong.github.io。

就职于360人工智能研究院、曾就职于中国科学院软件研究所。

老刘说NLP,将定期发布语言资源、工程实践、技术总结等内容,欢迎关注。

对于想加入更优质的知识图谱、事件图谱、NLP实践相关分享的,可关注公众号,在后台菜单栏中点击会员社区->会员入群加入。




精选文章

从符号到嵌入:计算社会科学的两种文本表示

推荐 | 社科(经管)文本分析快速指南

使用cntext训练Glove词嵌入模型

认知的测量 | 向量距离vs语义投影

Wordify | 发现和区分消费者词汇的工具

karateclub库 | 计算社交网络中节点的向量

视频专栏课 | Python网络爬虫与文本分析

PNAS | 文本网络分析&文化桥梁Python代码实现

Wordify | 发现和区分消费者词汇的工具

BERTopic库 | 使用预训练模型做话题建模

tomotopy | 速度最快的LDA主题模型

文本分析方法在《管理世界》(2021.5)中的应用

Wow~70G上市公司定期报告数据集

doccano|为机器学习建模做数据标注

使用WeasyPrint自动生成pdf报告文件

100min视频 | Python文本分析与会计

good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter