基于字典的评论主题挖掘
数据准备
此次使用基于主题词典的方式解决主题句提取的冷启动问题。启发式规则主要依据简单结构特点抽取主题词和主题句,比如使用《旅游民宿基本要求与评价》标准中的评级指标,通过具体的指标属性词对民宿进行细粒度的评估,具体的评价参考指标如下图所示。
使用 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('垃圾饭店'))
对待处理的数据进行整合。
comment_process = zip(data['order_id'].tolist(), data['content'].tolist())
comment_process
开始对评论进行分句,利用词性标注技术通过找到用户评价中的词性为标点符号的利用换行符进行替代的方法,此方法解决了一句评论中出现多主题的评论,可以达到了降维且更加突出主题的目的,大幅减少无关评论的影响,为后续的主题评论的查询和情感分析做铺垫。例如:“环境很不错!装修古朴,喜欢”这句评论就可以被拆分为:“环境很不错”、“装修古朴”、“喜欢”三句评论,其中的主题就会直接通过主题词典查询出来,使得总体语料进一步扩充,对于主题属性词构建来说,主题词本身可以直接匹配到相应的主题,批量的对用户评论进行处理,需要一些时间进行处理。
# 添加可视化模块
from tqdm.notebook import tqdm
sentence_cut_list = list()
order_id_list = list()
for order_id, sentence in tqdm(list(comment_process)):
# 剔除长度小于 1 的主题句
if len(sentence) > 1:
sentence_ = str()
for word, flag in pseg.cut(sentence):
if flag != 'x':
sentence_ += word
else:
# 使用统一的分隔符号代替所有标点符号
sentence_ += '\\\\'
# 去除空格
comment_cut_split = sentence_.split('\\\\')
sentence_cut_list += comment_cut_split
order_id_list += [order_id] * len(comment_cut_split)
对分句后的的数据进行聚合,此目的就是将用户评论中的多个评价主题尽心切分,分散到子句中去,通过 oder_id 字段可以与 data 数据表格进行链接。
comment_cut_data = pd.DataFrame(
{'comment_cut': sentence_cut_list, 'order_id': order_id_list})
comment_cut_data.head(5)
打印数据行数。
comment_cut_data.shape
针对用户评论中的空值进行清理,清除空字符串,并打印处理后的前 5 行数据。
import numpy as np
# 将空字符串替换为 nan
comment_cut_data.replace(to_replace='', value=np.nan, regex=True, inplace=True)
# 去除 nan 所在的行
comment_cut_data.dropna(axis=0, how='any', inplace=True)
comment_cut_data.head(5)
通过对用户评论进行清理,我们发现有不少空评论,统计最后的数据量。
comment_cut_data.shape
主题词提取
主题词提取一般不需要人工标注的语料,利用某些方法发现文本中比较重要的词作为主题词,进行提取。首先抽取出候选词,然后对各个候选词进行打分,然后输出 topK 个分值最高的候选词作为主题词。根据打分的策略不同,有不同的算法,例如 TF-IDF、TextRank 等算法。
基于 TF-IDF 的主题词提取
原理简介:TF-IDF(Term Frequency/Inverse Document Frequency)是信息检索领域非常重要的搜索词重要性度量,当一个词在某个文档中频率高并且在其他文档出现少的时候,其 TF-IDF 值越高,此方法能过滤一些常见词,并保留能提供更多信息的重要词,速度极快。
加载 jieba 分文本分析模块。
from jieba import analyse
jieba 内部存在 TF-IDF 的实现,我们直接批量对用户评论进行基于 TF-IDF 主题词抽取,处理过程需要一些时间。
# 对用户评论进行处理,只保留名词,显示 TF-IDF 的权重并求得前 topK 个主题词
topK = 200
sentence = str(comment_cut_data['comment_cut'].tolist())
%time keywords_tfidf = analyse.extract_tags(sentence, topK=topK,withWeight=True,allowPOS=('n'))
打印 topn 的 TF-IDF 提取结果,我们发现使用抽取用户评论中的名词生成的 TF-IDF 的结果能直接反映出顾客的话题倾向,诸如“服务”、“性价比”等之类的词语,TF-IDF 处理的输出的值是小于 1 ,第一位属于中心词,数值依次减小。
topn = 10
# 对处理好的 TF-IDF 数据进行数据转换
keywords_tfidf_pair = [[i[0], i[1]] for i in keywords_tfidf]
keywords_tfidf_pair[:topn]
安装可视化工具 pyecharts。
!pip install pyecharts==1.6.2
通过对提取的用户评论中的主题词进行可视化,并按照 TF-IDF 的大小进行展示,我们发现用户对当前的民宿和酒店并未完全区分清楚,重庆市区的民宿较多,在大部分的顾客心中仍旧是以酒店来对待,用户评论中占比较大的“景区”、“性价比”、“环境”等名词显示出顾客的关注倾向,民宿当前多在景区附近,环境明显区别于酒店,价格也是一大优势。
# 加载词云模块
from pyecharts.charts import WordCloud
# 加载 pyecharts 配置模块配置词云显示
from pyecharts import options as opts
# 初始化词云模块
comment_wordcloud = WordCloud()
# 设置词云中的文字大小,可以根据需要改变
comment_wordcloud.add(
series_name="",
data_pair=keywords_tfidf_pair,
word_size_range=[10, 100]
)
# 设置词云的全局配置
comment_wordcloud.set_global_opts(
title_opts=opts.TitleOpts(title="TF-IDF 主题词词云分析",
# 设置标题文字大小
title_textstyle_opts=opts.TextStyleOpts(font_size=15)),
# 显示词云中的词频数字
tooltip_opts=opts.TooltipOpts(is_show=True),
)
# 直接在 notebook 中显示词云
comment_wordcloud.render_notebook()
基于 TextRank 的主题词提取
原理简介:TextRank 算法是通过谷歌的 PageRank 网页排名算法演变过来的,原理保持一致。通过词之间的相邻关系构建网络,然后用算法公式迭代计算每个节点的 rank 值,通过排序 rank 值即可得到主题词。
jieba 内部存在 TextRank 的实现,我们直接批量对用户评论进行基于 TextRank 主题词抽取,需要一些时间处理。
# 设置全语料抽取的 topk、TextRank 结果的权重显示、提取词性设置 n 为名词
sentence = str(comment_cut_data['comment_cut'].tolist())
%time keywords_textrank = analyse.textrank(sentence, topK=topK, withWeight=True, allowPOS=('n'))
打印 topn 的 TextRank 主题词排序,TextRank 处理的输出最大值是 1 ,属于话题中心词,数值依次减小。
# 对处理好的 TextRank 数据进行数据转换
keywords_textrank_pair = [[i[0], i[1]] for i in keywords_textrank]
keywords_textrank_pair[:topn]
使用词云模块对主题词进行可视化处理,我们发现在此用户评价语料下,两种方式产生出来的顾客话题分布保持一致。
# 加载词云模块
tr_wordcloud = WordCloud()
# 设置词云中的文字大小,可以根据需要改变
tr_wordcloud.add(
series_name="",
data_pair=keywords_textrank_pair,
word_size_range=[10, 100]
)
# 设置词云的全局配置
tr_wordcloud.set_global_opts(
title_opts=opts.TitleOpts(title="TextRank 主题词词云分析",
# 设置标题文字大小
title_textstyle_opts=opts.TextStyleOpts(font_size=15)),
# 显示词云中的词频数字
tooltip_opts=opts.TooltipOpts(is_show=True),
)
# 直接在 notebook 中显示词云
tr_wordcloud.render_notebook()
建立话题词典
通过上述提取的主题词,利用人工选取的方式建立主题词典,通过建立主题字典的方式对切分后的用户评论进行提取,此字典可以暂时对用户评论中的主题句进行提取,后续利用扩增字典即可,此方式具有速度极快和易于搭建的优点,缺点是精度和召回度受到字典覆盖度的影响,严格定义每个主题词只属于一个主题,避免引发歧义。后续大家可以利用此方式打下的标签进行深度学习训练,以提高精度和召回。
topic_words_list = {
'环境': ['环境', '周边', '风景', '空气', '江景', '小区', '景点', '夜景', '街', '周围', '景区', '声音', '景色'],
'价格': ['价格', '房价', '性价比', '价位', '单价', '价钱'],
'特色': ['特色', '装潢', '布置', '建筑', '结构', '格调', '装修', '设计', '风格', '隔音'],
'设施': ['设施', '设备', '条件', '硬件', '房间', '热水', '马桶', '电梯', '阳台', '卫生间', '洗手间', '空调', '被子', '床', '大厅', '电话', '电', '摆设'],
'餐饮': ['餐饮', '早餐', '咖啡', '味道', '饭', '菜', '水果'],
'交通': ['交通', '车程', '地段', '路程', '停车', '机场'],
'服务': ['服务', '态度', '前台', '服务员', '老板', '掌柜', '店家', '工作人员'],
'体验': ['体验', '整体', '感觉'],
}
提取需要处理的内容,转化为列表,并打印数据统计量。
batch_comment_process = comment_cut_data['comment_cut'].tolist()
len(batch_comment_process)
开始批量处理,执行每一句都进行字符串过滤,得出来的句子进行主题划分,得不出来的按照 “其他” 进行标记。
# 定义话题收集列表
comment_topic = []
for comment_cut_list in tqdm(batch_comment_process):
topic_wait_insert = str()
for topic, topic_word_list in topic_words_list.items():
for topics_words in topic_word_list:
if topics_words in comment_cut_list:
topic_wait_insert = topic
# 匹配到即停止
break
# 如果找不到主题,则标记为“其他”
if len(topic_wait_insert):
comment_topic.append(topic_wait_insert)
else:
topic_wait_insert = '其他'
comment_topic.append(topic_wait_insert)
len(comment_topic)
最后产生主题句,按照每个主题进行匹配找出,打印前 5 行进行查看,由于不存在对应的主题词,所以标注为“其他”。
comment_cut_data['comment_topic'] = comment_topic
comment_cut_data.head()
对主题句进行统计,查看主题的分布情况,我们发现标记为“其他”的占大多数,服务、设施、环境是用户比较关心的三个主要方面。
# 聚合顾客话题的统计信息
user_topic, user_topic_count = [], []
for i in comment_cut_data['comment_topic'].groupby(
comment_cut_data['comment_topic']).count().items():
# 取出顾客话题标签
user_topic.append(i[0])
# 取出计数信息
user_topic_count.append(i[1])
开始对顾客话题分布进行统计可视化。
from pyecharts.charts import Bar
# 初始化饼图
bar = Bar(init_opts=opts.InitOpts(width='660px', height='350px'))
# 横坐标为用户真实打分
bar.add_xaxis(user_topic)
# 纵坐标为顾客情感极性的统计量,不设置纵坐标的名字
bar.add_yaxis("", user_topic_count)
# 设置图表的名字
bar.set_global_opts(title_opts=opts.TitleOpts(title='Comment_Topic'))
# 设置数据展示,并设置显示数据
bar.set_series_opts(label_opts=opts.LabelOpts(is_show=True), )
# 直接在 jupyter 上进行显示
bar.render_notebook()
随机抽查一个主题进行检查,查看对应的主题句和主题词的情况,我们发现通过主题词匹配能很快的对用户评论进行过滤和筛选。但是准确度受到所构建词典的制约,后续试验将尝试更多的方法进行扩充主题属性词典。
comment_cut_data[comment_cut_data['comment_topic'] == '环境'].head(5)