文本建模:主题模型和LDA(Latent Dirichlet A
@[toc]
LDA概念原理
The Problem
有许多事先未知主题的文本,我们想要根据主题对文本进行筛选,使用LDA.
两个基本假设:
- Each topic is a mixture of an underlying set of words. 每个主题都由一组词构成
- Each document is a mixture of a set of topics. 每个文本含有几个主题(但不会特别多)
狄利克雷分布Dirichlet Distribution——分布的分布
LDA原理
LDA模型中包括了两个狄利克雷分布,分别是基于主题的文本分布和基于词语的主题分布。以及两个多项式分布,分别是文本的主题分布和主题的词语分布。
把LDA想象成一个文件生成机器,它的数学公式为:
结合之前狄利克雷分布多项式分布的例子,该公式由以下成分构成:
-
- The probability that a document will appear. LDA生成的某个组合的文件概率。把它理解为文件生成的概率即可。对于每个文件,都是由主题分布的组合和词语分布的组合构成的,所以生成某个组合的文件的概率都是非常小的。
-
- The Dirichlet distribution of documents over topics. For each document j in our corpus of size M , the probability of distribution of topics for j . 文件中主题的狄利克雷分布。对于每个文件来说,它的主题构成概率分布情况。
-
- The Dirichlet distribution of topics over terms. For each topic i amoangst K topics, the probability distribution of words for i . 主题中词语的狄利克雷分布。对于每个主题来说,它的词语构成概率分布。
-
- The probability of a topic appearing given a document j . How likely is it that certain topics Z appear in this document. 对于某个文件,不同主题的概率多项式分布。
-
- The probability of a word appearing in a given topic. How likely is it that certain words W appear given those topics. 对于某个主题,其里面词语的概率多项式分布。
根据公式,LDA最终计算的是一个机器生成某个组合的fake document的概率。
LDA分析大学选择意向文本
学生大学选择意向除了应用问卷调查的手段之外,还采用大数据爬虫的方式在百度、必应等搜索引擎搜集到大量公开的文章和帖子,以数据挖掘和文本建模的形式分析海量信息,从中为合理分析准大学生在选择大学时的考虑因素,为结论提供数据支持。
首先利用python爬虫以“选择大学考虑因素”为关键词请求网页链接并写入文件,之后利用多线程方式批量爬取网页并解析出文字,将每个网页的文字写入单独的文件。在爬取过程中发现,当爬取的链接过多时,爬取的网页出现重复,初步判断是搜索引擎机制问题,因此将重复的文件筛去,最终得到160个包含网页文字的文本文档,得到的文本文档均是与大学选择因素相关的文本内容。
进一步分析文本发现,160个文档中存在部分因网络连接和安全证书问题未能成功爬取的空文档,部分文档存在乱码,部分文档与主题不够相关,为增强分析结果的准确性,排除存在问题的文本文档,最终选择40个与大学选择高度相关,内容丰富的文本文档,之后进行数据清洗。
数据清洗主要用于去除文字中的标点符号、换行符和个别特殊字符,批量读入文件之后用停用词表和正则表达式去除上述干扰因素,最后将40个文本文档转化为一个符合文本建模要求的文本文档,该文档每行为前面文档中的分词结果,共40行,即上述40个文本文档。将数据转化为标准形式后进行文本建模。
文本建模适用于寻找文本主题和关键词,本次我们采用常用的文本语义分析方法:隐含狄利克雷分布——Latent Dirichlet Allocation(LDA),LDA方法可以找到文本中的主题和主题包含的词语,并确定最优主题数。
首先将分完词的文档加载成符合python第三方库gensim
格式的输入,之后构造词典,并基于词典将分词列表集转换成向量集,形成语料库(corpus
),使用TF-IDF模型
处理语料库并存储。之后使用一致性分数(Coherence Score
)指标来衡量不同主题个数的优劣。主题一致性通过测量主题中得分高的单词之间的语义相似度来衡量单个主题的得分,它提供了一种简便的方法来判断一个给定的主题模型的优劣。测试结果显示以5个主题构建主题模型有最高的主题一致性。
利用LDA模型对语料库进行建模,设置模型的主题的个数为5,得到建模结果,将文档-主题分布与主题-词分布存储到本地。使用pyLDAvis
库进行可视化分析。
当时,
relevance
全部由该词在主题下出现的频率决定;当 时,
relevance
与该词语出现在整个语料中的频率有关,在整体语料中出现得越高频的词,会有越低的relevance
。由此可见,主题一的关键词多与专业和未来职业有关,表明影响准大学生选择学校的最重要因素还是专业和未来职业规划;主题二的关键词不够明晰,主题三表明影响因素是大学的投调档和退档政策,主题四表明影响因素是办学理念和录取人数等,主题五表明影响因素有学校环境和保研率等。
综上所述,我们用文本建模的LDA方法对准大学生选择学校的因素进行数据分析,并得到初步结果,因最终得到的有效信息仍然相对较少,以及存在难以去除的干扰信息(如出国信息和考研信息),对结果造成一定的干扰,不过仍然能看出主要结论与我们的日常认识一致。
# coding=utf-8
from gensim import corpora, models
# 将分完词的文档加载成符合gensim文格式的输入
with open('document.txt', 'r', encoding='utf-8') as f:
train = []
for line in f.readlines():
line = [word.strip() for word in line.split()]
train.append(line)
# 构造词典
dictionary = corpora.Dictionary(train)
feature_cnt = len(dictionary.token2id) # 词典中词的数量
dictionary.save('dict.txt') # 保存生成的词典,用于以后加载
# dictionary=Dictionary.load('dict.txt')#加载词典
# 基于词典,将【分词列表集】转换成【向量集】,形成【语料库】
corpus = [dictionary.doc2bow(text) for text in train]
# 使用【TF-IDF模型】处理语料库
tfidf_model = models.TfidfModel(corpus)
# 打印模型参数:文档数量与语料库单词数
print(tfidf_model)
# 存储通过tf-idf转化过的文档
with open('tfidf_doc.txt', 'w', encoding='utf-8') as fr:
for doc in tfidf_model[corpus]:
fr.write(doc.__str__() + '\n')
TfidfModel(num_docs=40, num_nnz=13770)
import gensim
import matplotlib.pyplot as plt
from gensim.models import CoherenceModel
coherence = []
# 假设主题数量在1-20中进行选择
for n in range(1, 21):
# Multi-core implementation:
lda = gensim.models.LdaMulticore(corpus, num_topics = n, id2word = dictionary, passes = 10, workers = 2)
# ompute coherence for each lda model with different number of topics
cohm = CoherenceModel(model = lda, corpus = corpus, dictionary = dictionary, coherence = 'u_mass')
coh = cohm.get_coherence()
coherence.append(coh)
print(coherence)
topic = [i for i in range(1,21)]
plt.plot(topic,coherence)
plt.show()
[-0.25352388741134857, -0.38094825806867716, -0.8669676411059538, -0.6401822895010143, -0.6463415108432043, -0.6322069824275579, -0.7797726449271771, -0.5853272249024961, -0.8358462179649898, -0.7995276836977411, -0.7320991395961808, -0.6199301798139824, -0.586875736946976, -0.7317883536812847, -0.7402079074170832, -0.5593792205764136, -0.5684482221878336, -0.6476912488541839, -0.589463914276295, -0.7276000475137212]
# 利用lda模型对语料库进行建模,设置模型的主题的个数、迭代次数
lda = models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=5, chunksize=1000, iterations=2000)
# 打印LDA模型参数
print(lda)
# 存储LDA文档结果:存储文档-主题分布
with open('doc_dis.txt', 'w') as f1:
# for doc in lda_corpus:
for doc in lda[corpus]:
f1.write(doc.__str__() + '\n')
f1.flush()
# 存储LDA文档结果:存储主题-词分布
with open('topic_dis.txt', 'w') as f2:
for topic in lda.get_topics():
f2.write(topic.__str__() + '\n')
f2.flush()
LdaModel(num_terms=5601, num_topics=5, decay=0.5, chunksize=1000)
for topic in lda.print_topics(num_topics=5, num_words=15):
print(topic)
(0, '0.019*"大学" + 0.011*"学校" + 0.011*"志愿" + 0.010*"专业" + 0.009*"选择" + 0.008*"就业" + 0.007*"大学生" + 0.006*"高校" + 0.006*"学生" + 0.006*"会" + 0.006*"不" + 0.006*"录取" + 0.006*"因素" + 0.005*"都" + 0.005*"招生"')
(1, '0.032*"专业" + 0.013*"选择" + 0.012*"大学" + 0.008*"就业" + 0.008*"学校" + 0.008*"考生" + 0.008*"高校" + 0.007*"因素" + 0.006*"学生" + 0.006*"影响" + 0.006*"中" + 0.006*"志愿" + 0.005*"都" + 0.005*"大学生" + 0.005*"招生"')
(2, '0.022*"专业" + 0.016*"选择" + 0.014*"大学" + 0.014*"学校" + 0.009*"美国" + 0.009*"学科" + 0.008*"因素" + 0.008*"学生" + 0.007*"都" + 0.006*"会" + 0.006*"不" + 0.006*"职业" + 0.005*"排名" + 0.005*"就业" + 0.005*"一个"')
(3, '0.019*"专业" + 0.014*"选择" + 0.011*"志愿" + 0.011*"学校" + 0.010*"考生" + 0.009*"因素" + 0.008*"大学" + 0.008*"高校" + 0.006*"填报" + 0.005*"发展" + 0.005*"时" + 0.005*"更" + 0.005*"一个" + 0.005*"潜能" + 0.004*"都"')
(4, '0.045*"专业" + 0.018*"选择" + 0.011*"大学生" + 0.010*"考生" + 0.010*"大学" + 0.010*"学校" + 0.010*"职业" + 0.009*"就业" + 0.008*"都" + 0.007*"学生" + 0.007*"因素" + 0.007*"不" + 0.007*"社会" + 0.006*"学习" + 0.006*"会"')
# Visualize the topics
import pyLDAvis
import pyLDAvis.gensim
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
pyLDAvis.enable_notebook()
vis = pyLDAvis.gensim.prepare(lda, corpus, dictionary,mds='mmds')
vis