毕业论文案例-LDA主题模型实现文本聚类

引言

这是一篇以我毕业论文为背景的博客。由于对数据分析非常感兴趣所以选择这个主题。在论文的撰写中我参考了大量相关的CSDN博客。

时光匆匆在毕业之际我愿意分享论文过程的体会和心得给各位兄弟姐妹们做个参考。

以下内容知网查重过勿搬运

LDA主题模型的预备知识

LDA主题模型本质是文本聚类算法属于文档生成模型利用样本的相似性。它常用于对文本数据的主题挖掘中比如对用户评论的挖掘人才招聘需求信息的挖掘等。它是NLP中一个常用的算法。

1多项式分布 Multinomial Distribution

多项式分布是二项分布的推广。最常见的二项分布比如抛硬币的实验实验结果只有两个即正面和反面且每个结果发生概率为1/2。现在我们假设掷骰子的实验6个面分别对应6个不同的点数每个点数出现的概率都是1/6这就是一个典型的多项分布。但这是很理想的情况现在我们把它推广至n个情况且每个面出现的概率是不确定我们就可以认为各结果出现的概率服从一定的分布概率密度函数如下
P { X 1 = n 1 , X 2 = n 2 , … , X r = n r } = n ! n 1 ! n 2 ! … n r ! P 1 n 1 P 2 n 2 … P r n r P \lbrace X_1=n_1,X_2=n_2, \ldots ,X_r=n_r \rbrace ={n! \over n_1!n_2! \ldots n_r!} P{ ^{n_1}_1 }P{ ^{n_2}_2 } \ldots P{ ^{n_r}_r } P{X1=n1,X2=n2,,Xr=nr}=n1!n2!nr!n!P1n1P2n2Prnr
其中 n 1 + n 2 + … + n r = n n_1+n_2+ \ldots+n_r=n n1+n2++nr=n
P 1 + P 2 + … + P r = 1 P_1+P_2+ \ldots+P_r=1 P1+P2++Pr=1
本文假定主题分布和词分布均服从与多项式分布。这是由于一篇文章都有一定数量的主题且能够以一定的概率去选择某个主题。换句话说在全部的主题数中文章包含的和未包含的文章所对应的主题概率大部分都为0只有有限个主题的概率不为0我们将每篇文章对应的主题概率看作一行所有文章排列组合起来可以得到一个巨大的稀疏矩阵这与多项式的分布比较相似。所以我们用多项式分布来拟合主题分布和词分布。

2狄利克雷分布 Dirichlet Distribution

狄利克雷分布是贝塔分布推广至多变量的情形其概率密度函数如下
在这里插入图片描述

其中向量α是狄利克雷分布的参数Bα表示 Dirichlet分布的归一化常数。

3共轭分布 Conjugate Distribution

共轭分布式贝叶斯统计中的一个常用概念当后验分布的概率密度函数与先验分布的概率密度函数具有相同的形式时我们称它们为一组共轭分布。比如贝塔分布和二项分布是一组共轭分布本文所用的狄利克雷分布和多项式分布是一组共轭先验分布。

4吉普斯采样 Gibbs Sampling

吉布斯采样是一种简单且广泛适用的马尔可夫链蒙特卡洛MCMC算法可以看作是Metropolis-Hastings算法的一个特例。吉布斯采样适用于联合分布未明确知道或难以直接抽样但每个变量的条件分布是已知的并且很容易或者至少更容易从中抽样的情况。具体来说它交替的固定某一维度然后通过其他维度的值来抽样该维度的值。本文的LDA主题模型是一个文档生成模型它就是通过吉普斯采样来生成的。

LDA主题模型的代码过程

所用样本是跟研究主题相关的新闻评论。故以下处理均是针对中文文本进行的。原始数据文本如下
在这里插入图片描述

1文本预处理

这一部分较常规包括结巴库分词添加停用词。

#这里定义停用词列表
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('没有')
    jb.add_word('天津')
    jb.add_word('电商')
    jb.add_word('西安')
    
    sentence_seged = jb.cut(sentence.strip())
    stopwords = pd.read_csv("./stopwords.txt",index_col=False,quoting=3,sep=" ",names=['stopword'],encoding='UTF-8')
    stopwords = stopwordslist('./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即是读入原文本outputs即是新建一个文本然后将处理好的文本放入
inputs = open('./text.txt','r',encoding='gb18030')


outputs = open('./text03.txt','w',encoding='gb18030')
for line in inputs:
    line_seg = seg_sentence(line)  # 这里的返回值是字符串
    outputs.write(line_seg + '\n')
outputs.close()
inputs.close()

于是预处理完毕后的文本就被存放在outputs文本里下一步操作直接导入outputs即可。预处理后的文本如下
在这里插入图片描述

2建模和可视化

建立LDA主题模型的代码有很多这里给出本文所用的

#这里先导入常用库
from gensim import corpora
from gensim.models import LdaModel
from gensim.corpora import Dictionary
import codecs

train = []

fp=codecs.open(r'F:\Desktop\comments_chuli\text03.txt','r',encoding='gb18030')

data=fp.read()
fp.close()

'''
for line in data: 
    if line != '':
        line = line.split(" ")
        train.append([w for w in line])
'''
#以下几行将train列表的每个元素都生成一个列表 形成列表嵌套
train0=data.split(" ")
train=[]
for i in range(len(train0)):
    train1=[]
    train1.append(train0[i])
    train.append(train1)


dictionary = corpora.Dictionary(train)
dictionary.filter_extremes(no_below=2, no_above=0.1)

corpus = [dictionary.doc2bow(text) for text in train]

lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=10, passes=50)
#corpus是词袋dictionary是词典
# num_topics主题数目
# passes训练伦次
# num_words每个主题下输出的term的数目
#corpus指语料词典

#以下为模型的输出结果即是每个主题下的20个特征词
#可以适当注释节省运行时间

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='')
  
    
#--------------以下是代码可视化部分------------------------------

#将可视化结果保存到lda001的网页上
import gensim
from gensim import models
import pyLDAvis.gensim_models
pyLDAvis.enable_notebook()
def lda_vis():
    #dictionary = gensim.corpora.Dictionary.load('lda.dict')
    #corpus = gensim.corpora.MmCorpus('lda.mm')
    #lda = models.ldamodel.LdaModel.load('lda.lda')
    vis = pyLDAvis.gensim_models.prepare(lda, corpus, dictionary)
    pyLDAvis.save_html(vis, r'F:\Desktop\comments_chuli\lda001.html')
    return 0
 
 
if __name__ == '__main__':
    lda_vis()

根据模型运行出的结果即每个主题下的20个词语能够倒推出来它是属于哪一类的主题从而实现主题的挖掘。这里我截取主题数为5时模型运行结果如下下图是每个主题下的特征词括号里是各特征词的权重
在这里插入图片描述
这里需要注意的是

1由于LDA模型是一个文档生成模型需要不断取样生成主题和词语并多次重复建模过程比较复杂运算时间比较长。因此根据情况对部分代码加上注释减少它的运行时间

2特别需要注意的是用词袋模型处理数据类型一定是一个列表嵌套对应的上文中的train是一个列表嵌套。这里大坑我花了不少时间

3模型优化

LDA主题模型本质上是一个文本聚类模型并且它与K-means聚类算法一样是需要我们手动输入聚类个数的。

当聚类个数过多可能出现过度拟合的现象

而聚类个数过少可能使得不同的主题聚为一类聚类效果不明显

因此对于聚类算法来说确定合适的主题数至关重要。

LDA主题模型中对于主题数的选取有以下五种

A、困惑度perplexity

由Ble提出主要内容是对于训练后的模型构建似然函数并求其最大值值越大表明提取主题的质量最优此时的困惑度最小它可以理解为训练后的模型对于一篇文档C有哪些主题是不确定的所以困惑度的大小与模型质量成反比

B、一致性coherence

由LauJH2010提出的它是一种基于主题相关性的定量评价主要内容是一致性的大小与模型质量成正比

C、可视化聚类效果

类似Excel中的气泡图气泡的大小代表主题在文档出现的频率权重气泡之间的距离代表主题间的相关性。因此气泡的间隔越远说明模型的聚类效果越好。

D、距离度量

由曹娟2008提出来的利用主题间的相似度来选择最优LDA模型的方法通过计算主题向量的余弦距离和KL距离来计算主题的相似度主题平均相似度最小时模型效果最好。

E、主题方差

由关鹏2016提出的用主题方差衡量主题空间的整体差异性和稳定性当方差越大时主题间的区分度越大说明模型的效果越好。

本文采用前三种以下是相关的代码

#-------------计算困惑度-------------------------------------
import codecs
import gensim
from gensim import corpora, models
import matplotlib.pyplot as plt
import matplotlib
from nltk.tokenize import RegexpTokenizer
from nltk.stem.porter import PorterStemmer



fp = codecs.open('./text03.txt','r',encoding='gb18030')
data=fp.read()
fp.close()

#以下几行将train列表的每个元素都生成一个列表 形成列表嵌套
train0=data.split(" ")
train=[]
for i in range(len(train0)):
    train1=[]
    train1.append(train0[i])
    train.append(train1)

dictionary = corpora.Dictionary(train)  # 构建 document-term matrix
corpus = [dictionary.doc2bow(text) for text in train]
Lda = gensim.models.ldamodel.LdaModel
    
def perplexity(num_topics):
    ldamodel = Lda(corpus, num_topics=num_topics, id2word = dictionary, passes=50)  #passes为迭代次数次数越多越精准
    print(ldamodel.print_topics(num_topics=num_topics, num_words=7))  #num_words为每个主题下的词语数量
    print(ldamodel.log_perplexity(corpus))
    return ldamodel.log_perplexity(corpus)
 
# 绘制困惑度折线图
x = range(1,10)  #主题范围数量
y = [perplexity(i) for i in x]
plt.plot(x, y)
plt.xlabel('主题数目')
plt.ylabel('困惑度大小')
plt.rcParams['font.sans-serif']=['SimHei']
matplotlib.rcParams['axes.unicode_minus']=False
plt.title('主题-困惑度变化情况')
plt.show()

#----------------计算一致性-------------------------------
#计算coherence

from gensim.models.coherencemodel import CoherenceModel
 
def coherence(num_topics):
    ldamodel = Lda(corpus, num_topics=num_topics, id2word = dictionary, passes=30,random_state = 1)
    #print(ldamodel.print_topics(num_topics=num_topics, num_words=7))
    ldacm = CoherenceModel(model=ldamodel, texts=train, dictionary=dictionary, coherence='c_v')
    #print(ldacm.get_coherence())
    return ldacm.get_coherence()

x = range(1,10)
# z = [perplexity(i) for i in x]  #如果想用困惑度就选这个
y = [coherence(i) for i in x]
plt.plot(x, y)
plt.xlabel('主题数目')
plt.ylabel('一致性大小')
plt.rcParams['font.sans-serif']=['SimHei']
matplotlib.rcParams['axes.unicode_minus']=False
plt.title('主题-一致性变化情况')
plt.show()

困惑度和模型质量成反比一致性与模型质量成正比。困惑度和一致性具体的含义可查阅相关论文这里只给出代码的实现方法。

根据困惑度和一致性可以初步确定主题数目的范围再辅以可视化聚类效果能够确定合适的主题数目.

以下是可视化聚类的效果图
分别是K=1054
Alt
在这里插入图片描述
在这里插入图片描述
从上述三幅图对比可得当主题数为10时主题线条过细模型过度拟合当主题数为5时主题2、3仍有部分相交而当主题数为4是各个主题相隔较远聚类效果明显且在主题无相交的条件下模型的困惑度达到最小。故本文应设定主题数为4。

LDA主题模型的优点和不足

LDA主题模型的优点是相对于传统的主题聚类算法来说。比如PLSA算法它认为文档-主题、主题-词语的分布均是一个确定值。通俗的讲首先以一定的概率选择某个主题再在这个主题下以一定的概率选择某个词语不断的重复这两步直至生成整篇文章。

而LDA主题模型则被称为是贝叶斯版本下的PLSA。LDA模型采用了贝叶斯学派的思想把一切参数都看作随机变量都服从一定的概率先验分布。因此它为主题分布和词分布都加上了两个狄利克雷的先验分布。

LDA主题模型的结构流程图具体可参考网上很常见这里就不再给出。

由于LDA模型采用了贝叶斯学派的思想在小样本以及可靠性统计的情况下运行结果相比于经典统计学派更具优势

但是LDA模型也有它自身的局限性比如说进行文本向量化的时候用的是词袋模型。词袋模型只考虑了词数未考虑词序比如说用矩阵来描述词语当文档和词语增加会形成一个巨大的稀疏矩阵可能造成维度灾难等没有考虑一些更先进的方法如用上下文的语义来预测下文的词语等。

需要本文数据和代码可在本文评论区留言或者私信我。

希望对各位兄弟姐妹们有所帮助

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

“毕业论文案例-LDA主题模型实现文本聚类” 的相关文章