这个模型具体介绍、应用场景什么的就不谈了,你知道它能从一堆的文本中给你反馈出规定主题数的词语聚类就可以了。
由于是无监督算法,意味着你不需要去准备训练集,不用去苦逼地打标签,在数据准备阶段任务量至少省掉了一半啊。
而作为数学小白,其中具体原理如无必要我一向是不愿深究的,只要有人证明可行,那我拿来就用。聪明的脑袋要各尽其用嘛(笑
但也许我之后始终逃不掉要学习原理吧,先把原理解释放在这里,之后需要时再看。
故而本篇主要介绍的是如何上手实现及如何分析结果,这才是Data Scientist该干的事嘛
书归正传。以本次工作为例,我需要分析爬取到500名用户每人50页微博,大概500* 50* 10=250000条微博文本的主题情况。也就是说想看看这些人的微博主要是在关心些什么,他们共同关注哪些方面。
编程环境:
python 3.6 + pycharm 2018,
LDA实现使用的包是gensim,
分词还是用我们的老朋朋朋朋友jieba.
数据准备:
爬到的原始数据存在csv中,我想的是将所有的微博文本分行读到一个txt中,然后分词去停用词,就达到gensim的输入标准了。
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
return stopwords
# 对句子进行分词
def seg_sentence(sentence):
sentence = re.sub(u'[0-9\.]+', u'', sentence)
jb.add_word('光线摄影学院') # 这里是加入用户自定义的词来补充jieba词典。
jb.add_word('曾兰老师') # 同样,如果你想删除哪个特定的未登录词,就先把它加上然后放进停用词表里。
jb.add_word('网页链接')
jb.add_word('微博视频')
jb.add_word('发布了头条文章')
jb.add_word('青春有你')
jb.add_word('青你')
sentence_seged = jb.cut(sentence.strip())
stopwords = stopwordslist('stopWords/stopwords.txt') # 这里加载停用词的路径
outstr = ''
for word in sentence_seged:
if word not in stopwords and word.__len__()>1:
if word != '\t':
outstr += word
outstr += " "
return outstr
inputs = open('input/like_mi10_user_all_retweet.txt', 'r', encoding='utf-8')
outputs = open('output1/mi10_user_retweet_fc.txt', 'w',encoding='utf-8')
for line in inputs:
line_seg = seg_sentence(line) # 这里的返回值是字符串
outputs.write(line_seg + '\n')
outputs.close()
inputs.close()
准备数据两小时,接口调用三分钟,等待结果两小时。
gensim很友好,词典、词袋模型、lda模型都是一句话搞定。
code
from gensim import corpora
from gensim.models import LdaModel
from gensim.corpora import Dictionary
train = []
fp = codecs.open('output1/mi10_user_retweet_fc.txt','r',encoding='utf8')
for line in fp:
if line != '':
line = line.split()
train.append([w for w in line])
dictionary = corpora.Dictionary(train)
corpus = [dictionary.doc2bow(text) for text in train]
lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=20, passes=60)
# num_topics:主题数目
# passes:训练伦次
# num_words:每个主题下输出的term的数目
for topic in lda.print_topics(num_words = 20):
termNumber = topic[0]
print(topic[0], ':', sep='')
listOfTerms = topic[1].split('+')
for term in listOfTerms:
listItems = term.split('*')
print(' ', listItems[1], '(', listItems[0], ')', sep='')
最后的输出是
0:
"北京" (0.024)
"疫情" ( 0.021)
"中国联通" ( 0.019)
"领券" ( 0.019)
"购物" ( 0.016)
"新冠" ( 0.016)
"专享" ( 0.012)
"元券" ( 0.012)
"确诊" ( 0.012)
"上海" ( 0.011)
"优惠" ( 0.011)
"肺炎" ( 0.010)
"新闻" ( 0.010)
"病例" ( 0.010)
"汽车"( 0.009)
1:
"小米" (0.133)
"Redmi" ( 0.019)
"新浪" ( 0.019)
"智慧" ( 0.018)
"雷军" ( 0.014)
"众测" ( 0.012)
"体验" ( 0.012)
"智能" ( 0.012)
"MIUI" ( 0.012)
"电视" ( 0.012)
"红米" ( 0.011)
"空调" ( 0.009)
"产品" ( 0.009)
"品牌" ( 0.009)
"价格"( 0.008)
2:
"抽奖" (0.056)
"平台" ( 0.032)
"评测" ( 0.022)
"生活" ( 0.013)
"红包" ( 0.013)
"关注" ( 0.012)
"这条" ( 0.012)
"视频" ( 0.012)
"工具" ( 0.011)
"获得" ( 0.011)
"有效" ( 0.011)
"进行" ( 0.010)
"恭喜" ( 0.010)
"用户" ( 0.010)
"公正"( 0.010)
.....
这种。他只会返回给你规定主题数下的多少词,每个词后面的小数可认为是这个词属于这个主题的概率,主题下所有词的概率和为1;而这个主题应该是什么,就要靠你人工后面分析来定义了。
那,盯着这一堆冷冰冰的词语和数字,也许你能通过概率衡量出这个词和这个主题的关系,但你能看出不同主题之间的关系吗?你能看出一个词和其他主题的关系吗?
有点困难。这时就要引出我们的LDA可视化分析工具了。
> pip install pyldavis
会安装很多依赖包。包括:
源码地址及文档
直接上手的使用指南
pyLDAvis支持三种包内lda模型的直接传入:sklearn、gensim、graphlab,好像也可以自己算。这里当然紧接上文直接用gensim得到的lda模型了。
pyLDAvis也很友好,同样一句话完成实现:
import pyLDAvis.gensim
'''插入之前的代码片段'''
d=pyLDAvis.gensim.prepare(lda, corpus, dictionary)
'''
lda: 计算好的话题模型
corpus: 文档词频矩阵
dictionary: 词语空间
'''
pyLDAvis.show(d) #展示在浏览器
# pyLDAvis.displace(d) #展示在notebook的output cell中
数据量很大,运行时间稍长。
同时,如果你希望能够将这个结果保存为独立网页以便分享或放到web系统中,那么可以这样:
d=pyLDAvis.gensim.prepare(lda, corpus, dictionary)
pyLDAvis.save_html(d, 'lda_pass10.html') # 将结果保存为该html文件
就不用每次都等好久时间跑结果了。。
是的,这个可视化过程真的很慢。。我用time来计时了,测试时为节省时间,gensim只训练一遍,耗时58s,然后等待pyLDAvis的渲染。。等了足足一个多小时吧?4200s…才终于出来了。
然后保存为网页很快。
期间电脑CPU利用率一直保持在100%, pycharm占比90%左右。结果出来气得我也没截图,马上试了试查到的新方案:
d=pyLDAvis.gensim.prepare(lda, corpus, dictionary, mds='mmds')
酱紫。
实际测试了,可以说,没有效果。。。。。
同时这个选择算法的参数还可以为tsne
,不同算法的区别还是看文档吧。
出来的这个页面,说复杂也不复杂,左边的气泡分布是不同主题,右边就是主题内前30个特征词了。浅蓝色的表示这个词在整个文档中出现的频率(权重),深红色的表示这个词在这个主题中所占的权重。右上角可以调节一个参数λ
,其作用接着往下看。
于是我们最后回答LDAvis作者开发这个工具所要解决的三个问题:
通过鼠标悬浮在左边的气泡上,我们可以选择查看具体的某个主题。选定后,右侧面板会相应地显示出跟这个主题相关的词汇,通过总结这些词汇表达的意义,我们就可以归纳出该主题的意义。
同时,哪个词对主题的权重更高呢?某个词语主题的相关性,就由λ
参数来调节。
如果
λ
接近1,那么在该主题下更频繁出现的词,跟主题更相关;
如果λ
越接近0,那么该主题下更特殊、更独有(exclusive)的词,跟主题更相关(有点TF-IDF的意思了)。
所以我们可以通过调节λ
的大小来改变词语跟主题的相关性,探索更加sense的主题意义。
在跑完主题建模之后,我们就可以知道每个主题出现的频率。LDAvis的作者将这个数字,用圆圈的大小来表示,同时也按顺序标号为1~n。所以气泡的大小及编号即表示主题出现的频率。
这里作者用多维尺度分析,提取出主成分做维度,将主题分布到这两个维度上,主题相互之间的位置远近,就表达了主题之间的接近性。气泡距离采用的是JSD距离,(应该)可认为是主题间的差异度,气泡有重叠说明这两个话题里的特征词有交叉。
知道这些后,就是看词说话了。看看这些词应该是在说些什么东西,提炼出不同的topic来,这才是有实际应用价值的结果。若无最后的结果输出,前面所做的一切都是废纸一张。
先总结到这里吧,抛去理论层面,应该是够用了。