LDA文档主题生成模型
LDA是一种文档主题生成模型,包含词、主题和文档三层结构。
所谓生成模型,就是说,我们认为一篇文章的每个词都是通过“以一定概率选择了某个主题,并从这个主题中以一定概率选择某个词语”这样一个过程得到。文档到主题服从多项式分布,主题到词服从多项式分布。
LDA是一种非监督机器学习技术,可以用来识别大规模文档集或语料库中潜藏的主题信息。它采用了词袋的方法,这种方法将每一篇文档视为一个词频向量,从而将文本信息转化为了易于建模的数字信息。
但是词袋方法没有考虑词与词之间的顺序,这简化了问题的复杂性,同时也为模型的改进提供了契机。每一篇文档代表了一些主题所构成的一个概率分布,而每一个主题又代表了很多单词所构成的一个概率分布。
理解LDA的五个步骤:
一个函数:gamma函数
四个分布:二项分布、多项分布、beta分布、Dirichlet分布
一个概念和一个理念:共轭先验、贝叶斯框架
两个模型:pLSA、LDA
一个采样:Gibbs采样
LDA的贝叶斯模型
LDA是基于贝叶斯模型的,涉及到贝叶斯模型离不开“先验分布”,“数据(似然)”和"后验分布"三块。
先验分布 + 数据(似然)= 后验分布
例如:P(好工程师)P(简历|好工程师)= P(好工程师|简历)
二项分布与Beta分布
二项分布:
其中:p我们可以理解为好的工程师概率,k为好工程师的个数,n为工程师的总数
我们希望这个先验分布和数据(似然)对应的二项分布集合后,得到的后验分布在后面还可以作为先验分布。也即是说,我们希望先验分布和后验分布的形式应该是一样的,这样的分布我们一般叫共轭分布。在我们的例子里,我们希望找到和二项分布共轭的分布。
和二项分布共轭的分布其实就是Beta分布。Beta分布的表达式为:
其中Γ是Gamma函数,满足Γx=x-1!
多项分布与Dirichlet 分布
假如数据有三维,由于二维我们使用了Beta分布和二项分布来表达这个模型,则在三维时,以此类推,我们可以用三维的Beta分布来表达先验后验分布,三项的多项分布来表达数据(似然)。(超过二维的Beta分布我们一般称之为狄利克雷(以下称为Dirichlet )分布)
假设是三维,则多项式分布为:
三维的Dirichlet分布:
一般意义上的K维Dirichlet 分布表达式为:
LDA主题模型
LDA假设文档主题的先验分布是Dirichlet分布,即对于任一文档d,其主题分布θd 为:
其中,α 为分布的超参数,是一个K维向量,K表示主题数目
LDA假设主题中词的先验分布是Dirichlet分布,即对于任一主题k, 其词分布βk为:
其中,η为分布的超参数,是一个V维向量。V代表词汇表里所有词的个数。
对于数据中任一一篇文档d中的第n个词,我们可以从主题分布θd中得到它的主题编号zdn的分布为:
而对于该主题编号,得到我们看到的词wdn的概率分布为:
基于这个LDA模型的求解方法:
- 基于Gibbs采样算法求解
- 基于变分推断EM算法求解
LDA主题模型的python实现
- 导入文档
- 清洗文档
- 分词
- 停用词处理
- 词干提取
- 构建描述文档词频的矩阵
- 应用LDA模型
- 检查结果
from stop_words import get_stop_words
from nltk.stem.porter import PorterStemmer
from nltk.tokenize import RegexpTokenizer
from gensim import corpora,models
import gensim
tokenizer=RegexpTokenizer(r'\w+')
texts=[]
doc_a = "Brocolli is good to eat. My brother likes to eat good brocolli, but not my mother."
doc_b = "My mother spends a lot of time driving my brother around to baseball practice."
doc_c = "Some health experts suggest that driving may cause increased tension and blood pressure."
doc_d = "I often feel pressure to perform well at school, but my mother never seems to drive my brother to do better."
doc_e = "Health professionals say that brocolli is good for your health."
doc_set = [doc_a, doc_b, doc_c, doc_d, doc_e]
en_stop=get_stop_words('en')
# compile sample documents into a list
#分词
for i in doc_set:
raw=i.lower()
tokens=tokenizer.tokenize(raw)
print(tokens)
#创建停用词列表
stopped_tokens=[i for i in tokens if i not in en_stop]
print(stopped_tokens)
#词干提取(将相似的单词去除词缀得到词根)
p_stemmer=PorterStemmer()#p_stemmer 要求所有单词的类型都是 str。p_stemmer 以词干的形式返回字符串参数
stemmed_tokens=[p_stemmer.stem(i) for i in stopped_tokens]
texts.append(stemmed_tokens)
dictionary=corpora.Dictionary(texts)#Dictionary() 方法遍历所有的文本,为每个不重复的单词分配一个单独的整数ID
corpus=[dictionary.doc2bow(text)for text in texts]#doc2bow() 方法将 dictionary 转化为一个词袋。
# 得到的结果 corpus 是一个向量的列表,向量的个数就是文档数。在每个文档向量中都包含一系列元组。
ldamodel=gensim.models.ldamodel.LdaModel(corpus,num_topics=2,id2word=dictionary,passes=20)
#num_topics: 必须。LDA 模型要求用户决定应该生成多少个主题。由于我们的文档集很小,所以我们只生成三个主题。
#id2word:必须。LdaModel 类要求我们之前的 dictionary 把 id 都映射成为字符串。
#passes:可选。模型遍历语料库的次数。遍历的次数越多,模型越精确。但是对于非常大的语料库,遍历太多次会花费很长的时间。
#检查结果
print(ldamodel.print_topics(num_topics=2,num_words=4))
#每一个生成的主题都用逗号分隔开。每个主题当中有三个该主题当中最可能出现的单词