! ]* z3 y/ H& | P 了解LDA模型,我们需要先了解LDA的生成模型,LDA认为一篇文章是怎么形成的呢?) q" m( \- X1 A( a D. }/ g+ e
0 U o {& S x+ w6 U
LDA模型认为主题可以由一个词汇分布来表示,而文章可以由主题分布来表示。, }9 m2 `1 j/ F
+ w9 x+ |) o$ [; q, o 比如有两个主题,美食和美妆。LDA说两个主题可以由词汇分布表示,他们分别是:& o H% U, v) ?
8 A$ F. k" w T p% {: c9 J{面包:0.4,火锅:0.5,眉笔:0.03,腮红:0.07}( h# G; r8 f& } J: k; D7 l, ?
{眉笔:0.4,腮红:0.5,面包:0.03,火锅:0.07} - w4 w4 O1 J' Q+ Y) y 3 ?3 X d) V; c6 Y9 ? 同样,对于两篇文章,LDA认为文章可以由主题分布这么表示: & Z3 o9 C) {% c7 P. ~6 I 8 i2 @# N6 Q. ^! P《美妆日记》{美妆:0.8,美食:0.1,其他:0.1} 7 z5 _/ n5 P& j. U/ l' g ; _! k) X# q. A. l( y- a《美食探索》{美食:0.8,美妆:0.1,其他:0.1} 8 R# s% O( Q) o+ v' p 5 P, T& O6 Q. c) ?/ c9 R 所以想要生成一篇文章,可以先以一定的概率选取上述某个主题,再以一定的概率选取那个主题下的某个单词,不断重复这两步就可以生成最终文章。5 }. q+ Q- T5 M. G( f6 A$ _
- t) d% i4 X' b3 C/ @1 Z 在LDA模型中,一篇文档生成的方式如下: 0 c6 O' W$ ?/ h 7 X6 U9 D2 ~' Q7 s3 N4 z- G: F
3 n+ a% G6 A1 Z c% D
其中,类似Beta分布是二项式分布的共轭先验概率分布,而狄利克雷分布(Dirichlet分布)是多项式分布的共轭先验概率分布。' F6 |2 A6 w' |) R5 F2 R' B
# G) X4 t! Y6 z
5 L+ [; O9 S/ O; _6 s$ p/ M6 `
, k+ F5 M8 ~4 d; ^2 M! m. v% b 如果我们要生成一篇文档,它里面的每个词语出现的概率为:4 _ Y. p; q7 @5 q/ a4 S
# ]; [1 k9 H& s; M$ c9 [ 至于LDA主题模型在计算机中具体是怎么实现的,我们也可以不必细究,现在有很多可以直接用来进行LDA主题分析的包,我们直接用就行。(没错,我就是调包侠)7 S5 _. e4 Q( A5 m a
% A* l& ]& F* O+ g. z3 v. h' v* V2 s二、Python实现! ~1 K2 Z3 ^2 Z% u0 V5 c
在用Python进行LDA主题模型分析之前,我先对文档进行了分词和去停用词处理(详情可以看我之前的文章:用python对单一微博文档进行分词——jieba分词(加保留词和停用词)_阿丢是丢心心的博客-CSDN博客_jieba 停用词) . U0 p+ m9 Q: {/ G 9 o0 y. W% `$ ^3 J& G* s M) x 我下面的输入文件也是已经分好词的文件6 \3 p2 k0 c, s w2 M! I8 Y C
' N! Z k" z1 D6 [ a1.导入算法包! h3 X9 G& g( E6 n) \
import gensim . E9 N4 z8 d0 x; \/ S, v+ p6 T! mfrom gensim import corpora8 A% n6 Y& {) x! D, i# ~2 ~
import matplotlib.pyplot as plt * q2 z; N- Z' @import matplotlib, A1 U9 _9 }6 P5 F9 K
import numpy as np0 h4 J4 `6 H" x% Z! q
import warnings ; ?4 i/ s7 k. P, lwarnings.filterwarnings('ignore') # To ignore all warnings that arise here to enhance clarity " `- V- P, f* I f+ M9 ]/ S0 q2 q5 ?: D* q
from gensim.models.coherencemodel import CoherenceModel- l5 F# X- c. V
from gensim.models.ldamodel import LdaModel ! `! D& W9 ]% `. i$ g2.加载数据& u4 z4 A; y) a! x- ~3 }* J
先将文档转化为一个二元列表,其中每个子列表代表一条微博: 1 y& U& `9 f9 }$ n; ~; X* }, T' z. q/ o5 S2 G: R: E$ s+ }
PATH = "E:/data/output.csv" 4 G/ C* _9 g$ U5 Y& R 1 B* F2 r. p. u# }file_object2=open(PATH,encoding = 'utf-8',errors = 'ignore').read().split('\n') #一行行的读取内容" E# O9 H+ T6 p% O n
data_set=[] #建立存储分词的列表 / _5 Z" m. Z/ X+ [for i in range(len(file_object2)):, H/ B+ Z2 p( j, l s
result=[] 4 ^$ u; X+ }1 o( X Z! w seg_list = file_object2.split() " \8 [0 T# P$ i0 u: m% x U for w in seg_list : #读取每一行分词 8 I, p5 U2 B7 G& u, r+ N! u2 l, Y+ R result.append(w)# S+ R: H' E+ T0 e9 k: c/ c
data_set.append(result) 4 `5 y( L5 B1 w0 {5 ]- jprint(data_set) ) {1 v s" [3 Q7 y G5 h( B 构建词典,语料向量化表示: 2 i. e5 v! B6 T& p 1 [; |. y+ P7 X5 t" p0 V& z; Y) y) `; ?dictionary = corpora.Dictionary(data_set) # 构建词典 + u$ l$ q. N( K- A- ~8 ecorpus = [dictionary.doc2bow(text) for text in data_set] #表示为第几个单词出现了几次 9 T5 E0 t3 A, O% i3.构建LDA模型$ J3 t0 z, K+ h' _
ldamodel = LdaModel(corpus, num_topics=10, id2word = dictionary, passes=30,random_state = 1) #分为10个主题 H8 N9 u. p. |/ D" F0 x
print(ldamodel.print_topics(num_topics=num_topics, num_words=15)) #每个主题输出15个单词2 G0 {- S. \) z: H" m- g
这是确定主题数时LDA模型的构建方法,一般我们可以用指标来评估模型好坏,也可以用这些指标来确定最优主题数。一般用来评价LDA主题模型的指标有困惑度(perplexity)和主题一致性(coherence),困惑度越低或者一致性越高说明模型越好。一些研究表明perplexity并不是一个好的指标,所以一般我用coherence来评价模型并选择最优主题,但下面代码两种方法我都用了。 4 [7 c- x* z) @. z& a7 U! y. e$ y- p9 @3 d% {& z# o* F ?
#计算困惑度1 x5 y* w$ e- F* R
def perplexity(num_topics): 5 i7 r6 p) ^( D7 n4 X% c7 x ldamodel = LdaModel(corpus, num_topics=num_topics, id2word = dictionary, passes=30) 2 q& @: s- f- ?0 a9 d0 Y6 X print(ldamodel.print_topics(num_topics=num_topics, num_words=15)) ! Y& ?* i# `) Y* ?& }. p* `2 u print(ldamodel.log_perplexity(corpus)). m# v- y1 {( g" B
return ldamodel.log_perplexity(corpus)" x# M" G V. _0 `( t
#计算coherence - Y+ s+ m7 d. Z7 Adef coherence(num_topics):3 G M6 ?1 r, H& a# k8 y
ldamodel = LdaModel(corpus, num_topics=num_topics, id2word = dictionary, passes=30,random_state = 1)% @/ U7 w( }/ E9 W( y4 _" Y9 h
print(ldamodel.print_topics(num_topics=num_topics, num_words=10))5 d% v* S! J; d" E# K
ldacm = CoherenceModel(model=ldamodel, texts=data_set, dictionary=dictionary, coherence='c_v'): u7 q8 z* d" `' `
print(ldacm.get_coherence()) 6 k' X. h; e* C3 K; @ return ldacm.get_coherence() : f% A8 B3 f: S! L8 `/ y4.绘制主题-coherence曲线,选择最佳主题数 9 Q" C" H. R- Z; e- dx = range(1,15) - n P: C7 Q9 F# H \3 i# z = [perplexity(i) for i in x] #如果想用困惑度就选这个 . M) `4 t, W3 p* Gy = [coherence(i) for i in x]7 a: {6 }! X9 M- d$ p
plt.plot(x, y) ; N6 K8 b9 ~* Y" y; E3 R- Jplt.xlabel('主题数目') + }$ {7 V$ o! h+ u. J& mplt.ylabel('coherence大小'); ~5 h& P% d# N1 R2 L
plt.rcParams['font.sans-serif']=['SimHei']! s2 L1 D, F# V1 i
matplotlib.rcParams['axes.unicode_minus']=False 3 U& k6 b% z; Q! H& S% x/ S9 bplt.title('主题-coherence变化情况') ) @1 g3 [3 A, H, F! Iplt.show()0 K( a) C5 R# s- C, I! H# [
最终能得到各主题的词语分布和这样的图形:$ m0 b# l5 x3 R/ p
0 a" c! F1 D3 ^/ K
6 z& G0 s9 q3 m" ?) m2 Y: [ / l: c& W$ A! X% q8 ` 5.结果输出与可视化1 S) J2 X0 k3 V$ @3 Y" V
通过上述主题评估,我们发现可以选择5作为主题个数,接下来我们可以再跑一次模型,设定主题数为5,并输出每个文档最有可能对应的主题 / T; ~( D I5 o# H5 S/ u " i+ G6 s& v5 Dfrom gensim.models import LdaModel ; f/ k* G9 s/ W1 G. i& mimport pandas as pd8 r5 V" K2 n4 l, t- P+ P B
from gensim.corpora import Dictionary& S0 n& d ?( Z: ~6 K
from gensim import corpora, models& a k+ e- \; T+ k
import csv' a$ H4 w3 G/ ]3 {" A5 T" ^
+ U3 D% D7 b/ n c( W; r8 P# 准备数据 m1 @9 n. s# v" f* A3 ePATH = "E:/data/output1.csv" ' {$ z, P P7 p7 s* F, L- T8 X/ ]. P1 K# @, i! ]6 K
file_object2=open(PATH,encoding = 'utf-8',errors = 'ignore').read().split('\n') #一行行的读取内容 ! T* j9 {. P' q: sdata_set=[] #建立存储分词的列表 : ^) o# f+ |6 X6 m; P" hfor i in range(len(file_object2)): " e/ u }7 ?* N6 c" a result=[]' S+ T _! R( h" Y
seg_list = file_object2.split() * n* E t+ L" E9 o; d& b for w in seg_list :#读取每一行分词, r0 f5 k6 ^- t
result.append(w)% J. y" U& j* V* H% y. B3 h5 g
data_set.append(result) + T& |6 d0 J6 _# e$ [$ I5 J2 W" L1 u' |
dictionary = corpora.Dictionary(data_set) # 构建词典$ I1 {. X9 _' v
corpus = [dictionary.doc2bow(text) for text in data_set]8 Z7 h7 q9 x& B
+ ^( T# V5 W' j) Jlda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=5, passes = 30,random_state=1)/ ]: R. t8 b# p# m
topic_list=lda.print_topics()3 d3 H: p0 f5 f& w5 z
print(topic_list)- W8 |7 y" z6 l/ [- R+ o
/ N4 ~+ J* M0 n6 \6 N- r- t. zfor i in lda.get_document_topics(corpus)[:]: 5 C$ x* n/ I4 H listj=[]( K9 g H9 ~) f/ Z3 a, @" j
for j in i: 3 Z) v7 R/ D/ m3 M9 G listj.append(j[1])/ K2 j. _( t' L0 }" Z0 J) e. k$ Q
bz=listj.index(max(listj))" u7 B+ M. U( W3 }1 \( x( P* G4 B
print(i[bz][0]) : O) J7 @& c; [$ w& k. x% x' |6 l* H# ?: _
同时我们可以用pyLDAvis对LDA模型结果进行可视化:8 `* [2 `! p7 K9 u- `
7 L9 B3 _3 M+ j3 }# H7 N8 W d% \
import pyLDAvis.gensim , P0 y* P9 O; ipyLDAvis.enable_notebook() * W# b& ?& G% B5 D6 k8 rdata = pyLDAvis.gensim.prepare(lda, corpus, dictionary)3 H% j3 |% M# t% D
pyLDAvis.save_html(data, 'E:/data/3topic.html') $ D C# [+ w+ Z 大概能得到这样的结果: 9 G& z6 W& c c+ b( j4 t6 |# ]% ]2 {1 V" {0 i. c
1 `2 d3 O4 S- r* V& N: n8 w
?5 _. _( K5 B& R: n w" n _
左侧圆圈表示主题,右侧表示各个词语对主题的贡献度。 9 U8 U3 l/ R+ r& { n$ q5 F# t/ x5 K" D( V* C3 R
所有代码如下: 6 V0 }; q& y% Aimport gensim6 k& p% | ^+ y' T: M! r* D
from gensim import corpora 5 P2 S2 ^" L5 cimport matplotlib.pyplot as plt l. W2 y4 |! d! b0 E
import matplotlib # f- k' `7 Y) K9 J7 f( X6 Yimport numpy as np* D, z$ h8 k8 g- i/ i2 Z! W1 T
import warnings # M- H8 R4 m* Z! wwarnings.filterwarnings('ignore') # To ignore all warnings that arise here to enhance clarity 0 A$ Q% j$ N- M4 m0 l) R7 s X5 R! f# I+ ^
from gensim.models.coherencemodel import CoherenceModel * u; R4 s; b7 Z" @' @# D" xfrom gensim.models.ldamodel import LdaModel: O( y- n6 b0 ]+ }1 O+ I
, S" A1 \ h. d! U; E7 V' s 8 i/ C, E8 `/ s$ I5 [3 X 6 o# T1 c' l. D! @' ^: L. { # 准备数据 " Q) o9 i$ ?% k; y2 APATH = "E:/data/output.csv"/ o3 N* i) @' S4 n# }
2 R1 ?* Y: C5 C d# k
file_object2=open(PATH,encoding = 'utf-8',errors = 'ignore').read().split('\n') #一行行的读取内容 ) k, f' }0 }0 T0 V0 S/ W* qdata_set=[] #建立存储分词的列表& H% N. u9 o4 ^8 l
for i in range(len(file_object2)):/ U M( g8 j: I Y' v/ w4 O
result=[] 6 \; U( x% z7 B! n seg_list = file_object2.split() 0 A6 t U6 C- H for w in seg_list :#读取每一行分词5 F0 X u( m% f
result.append(w) & b: d. \: k6 d: o4 J3 t1 v data_set.append(result)+ H: J- k: B. Y
print(data_set) 9 q' B3 V7 P" X. g2 Z2 q4 U* K, E8 q+ d, z5 O l \
# m9 \3 n0 n$ O% U% `" K' B+ I
dictionary = corpora.Dictionary(data_set) # 构建 document-term matrix/ \9 S. N. |3 P- x( U
corpus = [dictionary.doc2bow(text) for text in data_set]& Y& }3 t7 t. W" L! e, O
#Lda = gensim.models.ldamodel.LdaModel # 创建LDA对象/ x) i1 R( c; G! X' C: Z X
* M. I% y8 v- |, h$ z5 D; f
#计算困惑度 + \& Z3 W1 a# x' s7 V3 ~def perplexity(num_topics): + f% T4 e! d. r8 t ldamodel = LdaModel(corpus, num_topics=num_topics, id2word = dictionary, passes=30), `2 a. l8 B! ?$ Y) s% S& \& I
print(ldamodel.print_topics(num_topics=num_topics, num_words=15)) $ E8 G t: ^7 Y# I) c! c print(ldamodel.log_perplexity(corpus)) 9 W2 [% B5 e2 G2 J' ] return ldamodel.log_perplexity(corpus)5 s8 B/ M/ \5 A4 w5 g# S! l4 w
* y4 K3 ^* \+ e# }1 o- t
#计算coherence ' l$ l3 x6 r9 l6 ^) Udef coherence(num_topics): 5 b# M: N6 T% y0 i& g( r ` ldamodel = LdaModel(corpus, num_topics=num_topics, id2word = dictionary, passes=30,random_state = 1) / V# e. k6 J) g print(ldamodel.print_topics(num_topics=num_topics, num_words=10))4 x# O# k0 S$ a- ^ I, @
ldacm = CoherenceModel(model=ldamodel, texts=data_set, dictionary=dictionary, coherence='c_v') / y6 k; Y# k# G( U C! J print(ldacm.get_coherence())+ P6 _ \) L* F% s& M( [+ h$ ^
return ldacm.get_coherence() 5 H* P8 d9 w7 E* w0 B5 K- ]5 l3 G; f4 ]" e' j! R5 S
# 绘制困惑度折线图 - V( m# V8 z$ R5 E% _) V" ex = range(1,15) " C, X: {3 m) d8 h6 ]; @' [# z = [perplexity(i) for i in x] 6 m! L) E+ B2 V' w) y1 ]3 w0 ey = [coherence(i) for i in x] . [' k& x9 R& |- V5 splt.plot(x, y) F0 m# l! G& o" o, R/ ~3 u! l+ tplt.xlabel('主题数目') # V4 |9 E; }: H, Hplt.ylabel('coherence大小') 2 q% s2 c5 G% K% gplt.rcParams['font.sans-serif']=['SimHei'] G. Z! w# u: y: Zmatplotlib.rcParams['axes.unicode_minus']=False 3 ~. b& @5 F! O/ m5 Rplt.title('主题-coherence变化情况') ) V+ \9 Z1 P. y7 L3 jplt.show()3 o8 g- J8 F1 O) X0 r6 u