第11天:NLP补充——主题模式(LDA)

时间:2022-07-24
本文章向大家介绍第11天:NLP补充——主题模式(LDA),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

前言

  主题模式是在自然语言处理中是较为常用的一个环节,常用于文本分类中的主题提取。接下来给大家详细介绍主题模型。   首先给大家介绍什么是主题模型,也就是主题模型的定义.我们在解释之前首先看几张图片让大家更好的、直观的理解主题模型的作用: 1、这个是比较经典的:

2、接下来是一些网上找到一些补充主题模型的图片:

  主题模型是机器语言在自然语言处理中的一个应用,主要做的是无监督的任务。一般情况下,如果我们拿到一篇文章,这篇文章是无标记的文章。我希望通过机器学习的相关算法,帮助我们得到文章的分类结果。具体来说:假设某企业想要招聘一个工程师,他们收到了许多的简历,他们想直接通过简历来看哪些是比较优秀的,哪些是比较差一点的。我们往往就会用到主题模型来自动识别。然而简历里一般会有这些特征:

  当然也有三个要素构成了一般企业的HR对应聘者的整体印象以及录取原则:

  也就是说这位HR拿到应聘者的简历只需要记录下每份简历包含的特征,然而,他并不知道,这一切代表着什么。于是他开始猜拿起一份简历A,他看到里面说A参加过七月课程他就猜这位应聘者的水平应该很nice,大概应该是位好的工程师。当然,这个猜,只是猜,没有任何证据可以证实。经过一系列的统计,总结成公式就是:

  这个例子就是大概介绍了主题模型的一般情况。一般情况下,主题模型应用在语义分析和文本挖掘。例如按主题对文本进行收集、分类和降维;也被用于生物信息学(bioinfomatics)研究 。隐含狄利克雷分布Latent Dirichlet Allocation, LDA)是常见的主题模型。在文本的主题分类中,我们的例子和实际之间的联系如下:

LDA主题模型

1、什么是LDA   LDA(Latent Dirichlet Allocation)是一种无监督的贝叶斯模型,是一种主题模型,它可以将文档集中每篇文档的主题按照概率分布的形式给出。同时它是一种无监督学习算法,在训练时不需要手工标注的训练集,需要的仅仅是文档集以及指定主题的数量k即可。此外LDA的另一个优点则是,对于每一个主题均可找出一些词语来描述它。是一种典型的词袋模型,即它认为一篇文档是由一组词构成的一个集合,词与词之间间没有顺序以及先后的关系。一篇文档可以包含多个主题,文档中每一个词都由其中的一个主题生成。我们可以通过用LDA找寻的就是之前例子里总监统计出来的经验:一份简历的每个特征都是因为本人有一定概率是好/坏程序员,并从好/坏这个分类中以一定概率选择某些特征而组成的。一篇文章的每个词都是以一定概率选择了某个主题,并从这个主题中以一定概率选择某个词语而组成的。这里有一个很重要的公式就是:

2、LDA生成过程   对于语料库中的每篇文档,LDA定义了如下生成过程(generative process): 1、对每篇篇文档,从主题分布中抽取一个主题; 2、从上述被抽到的主题所对应的单词分布中抽取一个单词; 3、重复上述过程直至遍历文档中的每一个单词。   也就是说:(w代表单词;d代表文档;t代表主题;大写代表总集合,小写代表个体),D中每个文档d看作一个单词序列<w1,w2,…,wn>,wi表示第i个单词。D中涉及的所有不同单词组成一个词汇表大集合V (vocabulary),LDA以文档集合D作为输入,希望训练出的两个结果向量(假设形成k个topic,V中⼀共m个词): 对每个D中的文档d,对应到不同Topic的概率θd<pt1,…,ptk>,其中,pti表示d对应T中第i个topic的概率。计算方法是直观的,pti=nti/n,其中nti表示d中对应第i个topic的词的数量,n是d中所有词的总数。对每个T中的topict,生成不同单词的概率φt<pw1,…,pwm>,其中,pwi表示t生成V中第i个单词的概率。计算方法同样很直观,pwi=Nwi/N,其中Nwi表示对应到topict的V中第i个单词的数量,N表示所有对应到topict的单词总数。所以,LDA的核心公式如下:

  直观的看这个公式,就是以Topic作为中间层,可以通过当前的θd和φt给出了文档d中出现单词w的概率。其中p(t|d)利用θd计算得到,p(w|t)利用φt计算得到。实际上,利用当前的θd和φt,我们可以为一个文档中的一个单词计算它对应任意一个Topic时的p(w|d),然后根据这些结果来更新这个词应该对应的topic。然后,如果这个更新改变了这个单词所对应的Topic,就会反过来影响θd和φt。 3、LDA学习过程   LDA算法开始时,先随机地给θd和φt赋值,(对所有的d和t),然后: 1、针对一个特定的文档ds中的第i单词wi,如果令该单词对应的topic为tj,可以把上述公式改写为:

Pj(wi | ds)=P(wi | tj)*P(tj | ds)

2、现在我们可以枚举T中的topic,得到所有的pj(wi | ds)。然后可以根据这些概率值结果为ds中的第i个单词wi选择一个topic。最简单的想法是取令pj(wi | ds)最大的tj(注意,这个式子里只有j是变量) 3、然后,如果ds中的第i个单词wi在这里选择了一个与原先不同的topic(也就是说,这个时候i在遍历ds中所有的单词,而tj理当不变),就会对θd和φt有影响了。它们的影响⼜会反过来影响对上面提到的p(w | d)的计算。对D中所有的d中的所有w进行一次p(w | d)的计算并重新选择topic看作一次迭代。这样进行n次循环迭代之后,就会收敛到LDA所需要的结果了。 4、LDA公式推导:   我们要首先从宏观上把握公式,主要包括:

一个函数: gamma函数 四个分布: 二项分布、多项分布、beta分布、Dirichlet分布 一个概念和一个理念: 共轭先验和贝叶斯框架 两个模型: pLSA、LDA 一个采样: Gibbs采样

  在正式介绍公式推导之前,我们首先给大家介绍共轭分布共轭先验: 在贝叶斯统计中,如果后验分布与先验分布属于同类,则先验分布与后验分布被称为共轭分布,而先验分布被称为似然函数的共轭先验。这里给出了两篇文章,大家可以详细的去了解。具体来说就是:

后验概率(posterior probability)∝ 似然函数(likelyhood function)* 先验概率(prior probability)

  首先让我们推导一个函数(Gamma函数)。阶乘函数在实数上的推广。我们知道,对于整数而言:

对于实数:

二项分布(Binomial distribution)   二项分布是从伯努利分布推进的。伯努利分布,对称两点分布或0-1分布,是一个离散型的随机分布,其中的随机变量只有两类取值,非正即负{+,-}。而二项分布即重复n次的伯努利试验,记为 。简记之,只做一次实验,是伯努利分布,重复做了n次,是二项分布。二项分布的概率密度函数为:

多项分布,是二项分布扩展到多维的情况:   多项分布是指单次试验中的随机变量的取值不再是0-1的,而是有多种离散值可能(1,2,3…,k)。比如投掷6个面的骰子实验,N次实验结果服从K=6的多项分布。当然啦,他们加起来的P应该是等于1的。多项分布的概率密度函数为:

Beta分布,二项分布的共轭先验分布:给定参数a>0和b>0,取值范围为[0,1]的随机变量 x 的概率密度函数:

其中:

Dirichlet分布,是beta分布在高维度上的推广:

其中:

贝叶斯派的思考公式:

几个主题模型 1、独立模型(Unigram model)

2、Mixture of unigrams model   该模型的生成过程是:给某个文档先选择一个主题z,再根据该主题生成文档,该文档中的所有词都来自一个主题。假设主题有z1,z2,z3,…zk,生成文档的概率为:

LDA模型应用——希拉里邮件主题提取

  正如前面文章提到的一样,我们还是用的同样的环境,如果不清楚的话大家可以看一下这篇文章。首先我们用的数据集就是西希拉里的邮件(验证码:vqaj)。下载下来放在你通缉目录下,具体如下:

然后该数据集的内容如下:

接下来就是展示真正技术的时候了: 1、首先让我们导入一些库以及数据

import numpy as np
import pandas as pd
import re
df = pd.read_csv(r"F:南昌大学自然语言处理学习自然语言处理资料课件资料4主题模型课件与资料inputHillaryEmails.csv")
df = df[['Id','ExtractedBodyText']].dropna()

2、文本预处理:   我们这里用到的是正则表达式,如果有不熟悉的同学,可以看看之前的文章。具体如下:

def clean_email_text(text):
    text = text.replace('n'," ") 
    text = re.sub(r"-", " ", text) 
    text = re.sub(r"d+/d+/d+", "", text) 
    text = re.sub(r"[0-2]?[0-9]:[0-6][0-9]", "", text) 
    text = re.sub(r"[w]+@[.w]+", "", text) 
    text = re.sub(r"/[a-zA-Z]*[://]*[A-Za-z0-9-_]+.+[A-Za-z0-9./%&=?-_]+/i", "", text)
    pure_text = ''
    for letter in text:
        if letter.isalpha() or letter==' ':
            pure_text += letter
    text = ' '.join(word for word in pure_text.split() if len(word)>1)
    return text
docs = df['ExtractedBodyText']
docs = docs.apply(lambda s: clean_email_text(s)) 
docs.head(1).values
doclist = docs.values

2、LDA模型构建:   我们用Gensim来做一次模型构建。

from gensim import corpora, models, similarities
import gensim
stoplist = ['very', 'ourselves', 'am', 'doesn', 'through', 'me', 'against', 'up', 'just', 'her', 'ours', 
            'couldn', 'because', 'is', 'isn', 'it', 'only', 'in', 'such', 'too', 'mustn', 'under', 'their', 
            'if', 'to', 'my', 'himself', 'after', 'why', 'while', 'can', 'each', 'itself', 'his', 'all', 'once', 
            'herself', 'more', 'our', 'they', 'hasn', 'on', 'ma', 'them', 'its', 'where', 'did', 'll', 'you', 
            'didn', 'nor', 'as', 'now', 'before', 'those', 'yours', 'from', 'who', 'was', 'm', 'been', 'will', 
            'into', 'same', 'how', 'some', 'of', 'out', 'with', 's', 'being', 't', 'mightn', 'she', 'again', 'be', 
            'by', 'shan', 'have', 'yourselves', 'needn', 'and', 'are', 'o', 'these', 'further', 'most', 'yourself', 
            'having', 'aren', 'here', 'he', 'were', 'but', 'this', 'myself', 'own', 'we', 'so', 'i', 'does', 'both', 
            'when', 'between', 'd', 'had', 'the', 'y', 'has', 'down', 'off', 'than', 'haven', 'whom', 'wouldn', 
            'should', 've', 'over', 'themselves', 'few', 'then', 'hadn', 'what', 'until', 'won', 'no', 'about', 
            'any', 'that', 'for', 'shouldn', 'don', 'do', 'there', 'doing', 'an', 'or', 'ain', 'hers', 'wasn', 
            'weren', 'above', 'a', 'at', 'your', 'theirs', 'below', 'other', 'not', 're', 'him', 'during', 'which']
texts = [[word for word in doc.lower().split() if word not in stoplist] for doc in doclist]
texts[0]

3、建立语料库   用词袋的方法,把每个单词用一个数字index指代,并把我们的原文本变成一条长长的数组:

dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]
corpus[13]

  这个列表告诉我们,第14(从0开始是第一)个邮件中,一共6个有意义的单词(经过我们的文本预处理,并去除了停止词后)其中,36号单词出现1次,505号单词出现1次,以此类推。。。接着,我们终于可以建立模型了:

lda = gensim.models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=20)
lda.print_topic(10, topn=5)

我们把所有的主题打印出来看看

lda.print_topics(num_topics=20, num_words=5)

  接下来就是通过

lda.get_document_topics(bow)

或者

lda.get_term_topics(word_id)

两个方法,我们可以把新鲜的文本/单词,分类成20个主题中的一个。这里需要我们注意的是:我们这里的文本和单词,都必须得经过同样步骤的文本预处理+词袋化,也就是说,变成数字表示每个单词的形式。 大家有兴趣的话可以继续完善。

总结

  本文主要介绍自然语言处理中最为常用的一种主题抽取模型——LDA,并且详细给大家从概念入手到最后的公式推导以及最后用一个案例来为大家讲解了LDA模型的应用,感兴趣的话可以将数据集下载下来,当然你也可以选取自己喜欢的数据集进行跑一遍代码,对LDA的相关应用有一个更深的了解。大家也可以看看这篇文章,对主题模型这一块介绍的相当清楚,相信大家看了该文章会有很大的帮助。最后我们一起努力,共同进步。