基于 LDA 模型的评论主题挖掘
数据准备
本次实验使用基于 LDA 主题聚类和主题分布可视化的方式研究顾客评论中的主题分布情况,并参考 《旅游民宿基本要求与评价》 标准中的评级指标辅助定义用户评价主题,具体的评价参考指标如下图所示。
使用 Pandas 加载在线数据表格,并查看数据维度和第一行数据。
import pandas as pd
data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/2628/1-1.csv')
print(data.shape)
data.head(1)
数据属性如下表所示
数据预处理
分词器预热,加快 jieba 分词。
import jieba.posseg as pseg
jieba 词性标注预热,使用 jieba 中的词性标注模块对输入的句子进行处理,产生每个词和对应的词性。
list(pseg.cut('垃圾饭店'))
批量提取每句用户评论中的长度大于 1 的名词,需要一些时间,请耐心等待。
# 加载进度条模块,实时显示处理进度
from tqdm.notebook import tqdm
# 定义处理函数
def get_n_filter(sentence_list):
# 提取语料提取后的名词
n_filter_list = list()
for sentence_ in tqdm(sentence_list):
n_filter = list()
words = pseg.cut(sentence_)
for word, flag in words:
# 过滤长度大于 1 的名词
if 'n' in flag and len(word) > 1:
n_filter.append(word)
n_filter_list.append(n_filter)
return n_filter_list
# 将处理的结果写入 comment_n 列
data['comment_n'] = get_n_filter(data['content'].tolist())
data.head(1)
LDA 模型是一个统计模型,所以我们需要进行词频统计和建立词典。CountVectorizer 是属于常见的特征数值计算类,是一个文本特征提取方法,对于每一个训练文本,它只考虑每种词汇在该训练文本中出现的频率,CountVectorizer 会将文本中的词语转换为词频矩阵,它通过 fit_transform 函数计算各个词语出现的次数。
# 加载词频统计模块
from sklearn.feature_extraction.text import CountVectorizer
# 设置词典大小为 n_features
n_features = 3000
n_count_vectorizer = CountVectorizer(max_features=n_features)
# 对用户评价中的名词进行字典转换
n_comment_count_vec = n_count_vectorizer.fit_transform(
[str(i) for i in data['comment_n']])
LDA 主题聚类
LDA(Latent Dirichlet Allocation)称为隐含狄利克雷分布,是一种以词、主题和文档三层贝叶斯概率为核心结构的主题模型,我们在 LDA 模型训练前不需要进行任何手工标注,LDA 在文本挖掘领域有广泛应用。本次实验我们使用 LDA 主题模型进行用户评论的主题提取。LDA 模型是一个概率模型,直观来讲,如果一篇文章有一个中心思想,那么一些特定词语会更频繁的出现。例如一篇文章中出现很多体育类的词,比如,篮球,足球之类的,那么主题模型就会把它划分为体育类的文章。
# 加载对语料进行处理的字典提取库
from gensim.corpora.dictionary import Dictionary
# 加载 LDA 模型库
from gensim.models.ldamodel import LdaModel
# 加载计算 LDA 模型的评估库
from gensim.models.coherencemodel import CoherenceModel
将数据处理成 LDA 模型要求的输入格式。
# 生成统计词典
dictionary = Dictionary(data['comment_n'].tolist())
# 利用词典对生成每个词进行字典向量化
corpus = [dictionary.doc2bow(text) for text in data['comment_n'].tolist()]
聚类个数选取
因为 LDA 算法需要一个初始的聚类个数,我们可以通过控制变量下的一致性分数进行定量评估来寻找最佳的主题数。Coherence Score 简称一致性分数,就是评估 LDA 主题模型在生成的各个主题下的主题词相关程度指标,该指标越大越好,说明主题内的主题词越相关,主题内部的歧义越少。gensim 提供了 CoherenceModel 模块进行 Coherence Score 的计算,本实验不讨论参数调节,使用默认参数进行模型评估和训练,参数寻找需要耗费一些时间,请耐心等待。
# 设初始的主题数 start_topic_number
start_topic_number = 1
# 设置最大的主题数 topic_numbers_max
topic_numbers_max = 10
# 收集模型分数
error_list = list()
# 定义主题数搜索列表,每次计算差距 2 个 topic
topics_list = list(range(start_topic_number, topic_numbers_max + 1, 2))
# 开始搜素最佳主题数
for topic_numbers in tqdm(topics_list):
lda_model = LdaModel(corpus=corpus,
id2word=dictionary,
num_topics=topic_numbers)
# 计算主题模型的分数
CM = CoherenceModel(
model=lda_model,
texts=data['comment_n'].tolist(),
dictionary=dictionary,)
# 收集不同主题数下的一致性分数
error_list.append(CM.get_coherence())
画出主题数对 LDA 模型一致性分数的影响,并选择在展平之前得出最高一致性分数下的主题数为当前语料下最佳的主题数。
import matplotlib.pyplot as plt
%matplotlib inline
# 横坐标表示聚类点
plt.xlabel('topic_number')
# 纵坐标表示一致性分数
plt.ylabel('coherence_score')
# 对主题数进行标注
plt.plot(topics_list, error_list, '*-')
plt.show()
通过趋势图观察最佳的主题个数不适合自动化的聚类,因为评价指标较为简单,只需要求得最大的一致性分数下的主题数即可自动化的确定最佳主题个数。
import numpy as np
# 求得最大一致性分数所在的索引
max_score_index = np.argmax(error_list)
# 根据最大的值的索引找到最佳的主题数
best_topic_numbers = topics_list[max_score_index]
best_topic_numbers
训练主题聚类模型
将上述步骤求得的 best_topic_numbers 作为 LDA 模型初始化的模型主题数,并开始我们的主题模型训练。计算需要一些时间,请耐心等待。
# 加载 LDA 主题模型
from sklearn.decomposition import LatentDirichletAllocation
# 使用 best_topic_numbers 作为主题数,其余参数均使用默认即可
best_lda_model = LatentDirichletAllocation(n_components=best_topic_numbers)
# 开始训练 LDA 模型
%time best_lda_model.fit(n_comment_count_vec)
将主题词通过 LDA 算法进行抽象的主题归纳,真正的主题词需要人工进行命名。从 LDA 模型分析结果看,每个主题中的每个词语都分配了相应的权重,我们可以粗略的从主题和对应的主题词进行抽象,主题模型就是从大量语料中找出附属的关键词,并依照关键词之间的相似度提炼成主题,下面我们下面打印各个主题下的主题词,看一下各个主题下的主题词情况。
# 设置每个主题下关联的主题词数量
topic_words_numbers = 30
# 获取所有的词典中的词语
feature_names = n_count_vectorizer.get_feature_names()
# 打印每个主题和附属的主题词
for topic_idx, topic in enumerate(best_lda_model.components_):
print("Topic # {}: ".format(int(topic_idx)))
print(" ".join(
[feature_names[i] for i in topic.argsort()[:-topic_words_numbers - 1:-1]]))
主题可视化
本实验对 LDA 主题分布进行可视化,通过图像的方式直观查看主题和主题词的分布情况。pyLDAvis 是主题模型交互式可视化库,我们可以直接在 notebook 内部使用主题可视化模块 pyLDAvis 对处理之后的 LDA 用户评论进行可视化,使用 pyLDAvis 可以交互式的显示不同主题,及每个主题的相关词语。
!pip install pyLDAvis==2.1.2
开始对主题分布进行可视化,需要一些时间,请耐心等待。
# 加载主题可视化组件
import pyLDAvis.sklearn
# best_lda_model 为以上训练好的主题模型,n_comment_count_vec 是向量化之后的名词语料
%time data_vis = pyLDAvis.sklearn.prepare(best_lda_model, n_comment_count_vec, n_count_vectorizer)
开启 notebook 中支持画图的控件。
!jupyter nbextension enable --py --sys-prefix widgetsnbextension
主题模型的可视化结果给出了包括所有主题在内的全局视图,可以看到,输出结果分为左右两部分,左侧为“主题距离地图”,展示各个主题之间的差异,图中带有数字编号的圆形即代表各个主题,圆形的面积与该主题出现的可能性成正比,并且按照面积大小自动进行编号,右侧为各个主题前 30 个最为相关的词汇,对各个主题进行解释说明,以水平柱状图的形式展示,蓝色表示整体词频,红色表示主题词频,当将鼠标光标移至某个主题圆形上方时,右侧将会显示该主题对应的词汇,也可以在左上角 “Selected Topic” 输入框中输入主题编号得到同样的效果。我们可以在可视化上进行参数微调,实时的显示不同主题和每个主题下的相关词语,通过对图中的特征词分布进行理解,将抽象的主题词进行具体化,根据其中的主题词进行命名主题,主题各自包含的词数以及它们之间距离的远近,使聚类效果具有可解释性,通过观察 LDA 主题可视化下的主题词,可以通过 LDA 去扩充主题词典。
# 加载可视化模块
import ipywidgets
# 允许在 notebook 中显示
pyLDAvis.enable_notebook()
# 直接在notebook 的 cell 中显示
pyLDAvis.display(data_vis)