暂无说说

LDA文档主题生成模型

案例研究 jiajun 5个月前 (05-24) 148次浏览 0个评论 扫描二维码

LDA 简单介绍

LDA 是一种文档主题生成模型,也称为一个三层贝叶斯概率模型,包含词、主题和文档三层结构。我们认为一篇文章的每个词都是通过以一定概率选择了某个主题,并从这个主题中以一定概率选择某个词语。文档到主题服从多项式分布,主题到词服从多项式分布。

LDA 是一种非监督机器学习技术,可以用来识别大规模文档集或语料库中潜藏的主题信息。它采用了词袋(bag of words)的方法,这种方法将每一篇文档视为一个词频向量,从而将文本信息转化为易于建模的数字信息。但是词袋方法没有考虑词与词之间的顺序,这简化了问题的复杂性,同时也为模型的改进提供了契机。每一篇文档代表了一些主题所构成的一个概率分布,而每一个主题又代表了很多单词所构成的一个概率分布。

LDA 生成过程

对于语料库中的每篇文档,LDA 定义了如下生成过程:
(1)对每一篇文档,从主题分布中抽取一个主题;
(2)从上述被抽到的主题所对应的单词分布中抽取一个单词;
(3)重复上述过程直至遍历文档中的每一个单词。

LDA 整体流程

文档集合 D,主题集合 T
D 中每个文档 d 看作一个单词序列<w1, w2, …… ,wn>,wi 表示第 i 个单词,设 d 有 n 个单词。(LDA 里面称之为 wordbag,实际上每个单词的出现位置对 LDA 算法无影响)
文档集合 D 中的所有单词组成一个大集合 VOCABULARY(简称 VOC)。

LDA 以文档集合 D 作为输入,希望训练出两个结果向量(设聚成 k 个 topic,VOC 中共包含 m 个词)。

对每个 D 中的文档 d,对应到不同 Topic 的概率θd<pt1,…,ptk>,其中,pti 表示 d 对应 T 中第 i 个 topic 的概率。计算方法是直观的,pti=nti/n,其中 nti 表示 d 中对应第 i 个 topic 的词的数目,n 是 d 中所有词的总数。

对每个 T 中的 topic,生成不同单词的概率φt<pw1,…,pwm>,其中,pwi 表示 t 生成 VOC 中第 i 个单词的概率。计算方法同样很直观,pwi=Nwi/N,其中 Nwi 表示对应到 topict 的 VOC 中第 i 个单词的数目,N 表示所有对应到 topict 的单词总数。
LDA 的核心公式如下:

p(w|d)=p(w|t)*p(t|d)

直观的看这个公式,就是以 Topic 作为中间层,可以通过当前的θd 和φt 给出了文档 d 中出现单词 w 的概率。其中 p(t|d)利用θd 计算得到,p(w|t)利用φt 计算得到。

实际上,利用当前的θd 和φt,我们可以为一个文档中的一个单词计算它对应任意一个 Topic 时的 p(w|d),然后根据这些结果来更新这个词应该对应的 topic。然后,如果这个更新改变了这个单词所对应的 Topic,就会反过来影响θd 和φt。

LDA 操作过程

上手过程

from gensim import corpora, models
import jieba.posseg as jp, jieba
# 文本集
texts = [
    '美国教练坦言,没输给中国女排,是输给了郎平',
    '美国无缘四强,听听主教练的评价',
    '中国女排晋级世锦赛四强,全面解析主教练郎平的执教艺术',
    '为什么越来越多的人买 MPV,而放弃 SUV?跑一趟长途就知道了',
    '跑了长途才知道,SUV 和轿车之间的差距',
    '家用的轿车买什么好']
jieba.add_word('四强', 9,  'n')
flags = ('n', 'nr', 'ns', 'nt', 'eng', 'v', 'd')  # 词性
stopwords = ('没', '就', '知道', '是', '才', '听听', '坦言', '全面', '越来越', '评价', '放弃', '人')  # 停用词

分词

# 分词
words_list = []
for text in texts:
    words = [w.word for w in jp.cut(text) if w.flag in flags and w.word not in stopwords]
    words_list.append(words)
print(words_list)
>>[['美国', '输给', '中国女排', '输给', '郎平'], ['美国', '无缘', '四强', '主教练'], ['中国女排', '晋级', '世锦赛', '四强', '主教练', '郎平', '执教', '艺术'], ['买', 'MPV', 'SUV', '跑', '长途'], ['跑', '长途', 'SUV', '轿车', '差距'], ['家用', '轿车', '买']]

构造词典

# 构造词典
dictionary = corpora.Dictionary(words_list)
# 基于词典,使【词】→【稀疏向量】,并将向量放入列表,形成【稀疏向量集】
corpus = [dictionary.doc2bow(words) for words in words_list]
print(dictionary.token2id)
>>{'中国女排': 0, '美国': 1, '输给': 2, '郎平': 3, '主教练': 4, '四强': 5, '无缘': 6, '世锦赛': 7, '执教': 8, '晋级': 9, '艺术': 10, 'MPV': 11, 'SUV': 12, '买': 13, '跑': 14, '长途': 15, '差距': 16, '轿车': 17, '家用': 18}
print(corpus)
>>[[(0, 1), (1, 1), (2, 2), (3, 1)], [(1, 1), (4, 1), (5, 1), (6, 1)], [(0, 1), (3, 1), (4, 1), (5, 1), (7, 1), (8, 1), (9, 1), (10, 1)], [(11, 1), (12, 1), (13, 1), (14, 1), (15, 1)], [(12, 1), (14, 1), (15, 1), (16, 1), (17, 1)], [(13, 1), (17, 1), (18, 1)]]

LDA 模型

# LDA 模型,num_topics 设置主题的个数
lda = models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=2)
print(lda)
LdaModel(num_terms=19, num_topics=2, decay=0.5, chunksize=2000)

打印主题

# 打印所有主题,每个主题显示 5 个词
for topic in lda.print_topics(num_words=5):
    print(topic)
>>(0, '0.082*"郎平" + 0.080*"中国女排" + 0.077*"输给" + 0.075*"四强" + 0.075*"主教练"')
(1, '0.100*"买" + 0.086*"轿车" + 0.086*"长途" + 0.082*"跑" + 0.080*"SUV"')

doc2bow 函数

主题推断

for e, values in enumerate(lda.inference(corpus)[0]):
    print(texts[e])
    for ee, value in enumerate(values):
        print('\t 主题%d 推断值%.2f' % (ee, value))
>>
美国教练坦言,没输给中国女排,是输给了郎平
	主题 0 推断值 5.45
	主题 1 推断值 0.55
美国无缘四强,听听主教练的评价
	主题 0 推断值 4.39
	主题 1 推断值 0.61
中国女排晋级世锦赛四强,全面解析主教练郎平的执教艺术
	主题 0 推断值 8.45
	主题 1 推断值 0.55
为什么越来越多的人买 MPV,而放弃 SUV?跑一趟长途就知道了
	主题 0 推断值 0.58
	主题 1 推断值 5.42
跑了长途才知道,SUV 和轿车之间的差距
	主题 0 推断值 0.66
	主题 1 推断值 5.34
家用的轿车买什么好
	主题 0 推断值 0.56
	主题 1 推断值 3.44
newtext = '中国女排将在郎平的率领下向世界女排三大赛的三连冠发起冲击'
bow = dictionary.doc2bow([word.word for word in jp.cut(newtext) if word.flag in flags and word.word not in stopwords])
ndarray = lda.inference([bow])[0]
print(newtext)
for e, value in enumerate(ndarray[0]):
    print('\t 主题%d 推断值%.2f' % (e, value))
中国女排将在郎平的率领下向世界女排三大赛的三连冠发起冲击
	主题 0 推断值 2.47
	主题 1 推断值 0.53

词和主题的关系

单个词和主题的关系

#单个词和主题的关系
word_id = dictionary.doc2idx(['长途'])[0]
for i in lda.get_term_topics(word_id):
    print('【长途】与【主题%d】的关系值:%.2f%%' % (i[0], i[1]*100))

>>【长途】与【主题 0】的关系值:2.94%
【长途】与【主题 1】的关系值:6.39%

全部词和主题的关系

#全部词与主题的关系(minimum_probability 设置概率阈值)
for word, word_id in dictionary.token2id.items():
    print(word, lda.get_term_topics(word_id, minimum_probability=1e-8))

>>
中国女排 [(0, 0.06442815), (1, 0.014080671)]
美国 [(0, 0.05490445), (1, 0.026663143)]
输给 [(0, 0.061345495), (1, 0.017999072)]
郎平 [(0, 0.06625165), (1, 0.011863526)]
主教练 [(0, 0.05975414), (1, 0.020088725)]
四强 [(0, 0.059979346), (1, 0.019790726)]
无缘 [(0, 0.025368767), (1, 0.020737274)]
世锦赛 [(0, 0.03366431), (1, 0.01001923)]
执教 [(0, 0.034435168), (1, 0.009128721)]
晋级 [(0, 0.03374774), (1, 0.009921753)]
艺术 [(0, 0.033762135), (1, 0.0099049155)]
MPV [(0, 0.008605375), (1, 0.046699677)]
SUV [(0, 0.033518188), (1, 0.057664976)]
买 [(0, 0.020289622), (1, 0.07797615)]
跑 [(0, 0.031679653), (1, 0.060431425)]
长途 [(0, 0.029418245), (1, 0.0638557)]
差距 [(0, 0.024054041), (1, 0.022578618)]
轿车 [(0, 0.029404663), (1, 0.063876286)]
家用 [(0, 0.010735649), (1, 0.043013442)]

每个主题,所有词概率和为 1

# 对于每个主题,所有词对应的概率,求和=1
print(lda.show_topic(0, 9999))
print('概率总和', sum(i[1] for i in lda.show_topic(0, 9999)))

[('郎平', 0.081504114), ('中国女排', 0.07969216), ('输给', 0.07662659), ('四强', 0.07526694), ('主教练', 0.07504273), ('美国', 0.07020949), ('执教', 0.049633656), ('艺术', 0.04894922), ('晋级', 0.048934568), ('世锦赛', 0.04884967), ('SUV', 0.04870095), ('跑', 0.04682636), ('长途', 0.044511233), ('轿车', 0.044497292), ('无缘', 0.040333282), ('差距', 0.038965795), ('买', 0.035010517), ('家用', 0.024490705), ('MPV', 0.02195474)]
概率总和 1.0000000149011612

参考资料

朴素贝叶斯
用 LDA 处理文本
LDA 简洁模型

喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址