快速实现民宿整体的意见挖掘
数据准备
对于采集后的在线评论,本次实验采用的主要的处理步骤包含:数据转换、数据清洗、数据划分、数据建模和数据可视化,在线源数据如下所示。
使用 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)
数据属性如下表所示
数据 EDA
查看数据描述,可以选择数据进行统计描述,注意 describe 函数只对浮点和字符型有效,我们发现除了自增字段 order_id,其余不能进行数据描述,其余不能进行数据描述,用户打分列表的数据出现异常。
data.describe()
查看数据结构,Non-Null Count 显示是否有空值存在,Dtype 显示数据属性,user_score 显示为 object,证实用户打分列表的数据出现异常,里面出现了脏数据。
data.info()
数据清洗
针对用户打分出现的问题进行处理,首先打印用户打分的去重数据
data['user_score'].unique()
我们发现用户打分出现了字符型数据,需要进行清理。
# 复制一个pandas的处理副本
data_clean = data[data.user_score != '信息不存在'].copy()
# 数据检查
data_clean['user_score'].unique()
对清理之后的用户打分进行可视化,发现大部分的用户打分在 4 分以上,原始用户打分整体呈现满意趋势。
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
sns.countplot(data=data_clean, x='user_score')
plt.show()
为了和模型打分能进行一致性评价,我们对用户打分数据进行归一化操作。
data_clean['user_score_normal'] = data_clean['user_score'].apply(
lambda x: round(float(x) / 5, 3))
data_clean.head(1)
用户打分数据标注
假设用户打分和用户情感极性一致,满足用户评论情感建模需求,可以对用户高于 3 分的评价标注为 1,否则为 0。
data_clean['label'] = data_clean['user_score'].apply(
lambda x: 1 if float(x) > 3 else 0)
data_clean['label'].unique()
针对语料标签出现了失衡的问题,课程设置在后续实验中研究数据失衡对模型的影响和对应的解决标签失衡的解决方法,如上采样/下采样/ smote 采样等等。
# 查看标签的分布情况
sns.countplot(data=data_clean, x='label')
plt.show()
情感分析模型训练
模型介绍
依据统计学模型假设,假设用户评论中的词语之间相互独立,用户评价中的每一个词语都是一个特征,当用户好评中常常出现某一个词语的时候,通过概率模型可以得知含有该词语的评价大概率是好评,基于此我们使用 scikit-learn 库的 MultinomialNB 对用户评论进行训练和情感预测。
# 加载用户评论向量化模块
from sklearn.feature_extraction.text import CountVectorizer
# 对测试集和训练集进行划分
from sklearn.model_selection import train_test_split
import numpy as np
import jieba
jieba 分词器预热,第一次使用需要加载字典和缓存,通过结果看出返回的是分词的列表。
' '.join(jieba.lcut(str('使用分词器进行分词')))
批量对用户评价进行分词。
%time data_clean['text_cut'] = data_clean['content'].apply(lambda x: " ".join(jieba.lcut(str(x))))
data_clean.head(1)
用户评论向量化
用户评论向量化的目的就是将用户评论转化为计算机可以处理的数字信息,通过建立向量化词典对用户评论进行向量化。
# 调整低词频带来的影响
vectorizer = CountVectorizer(token_pattern='\[?\w+\]?', max_features=5000)
vectorizer.fit_transform(data_clean['text_cut'])
查看词典中的词频信息。
vectorizer.vocabulary_
数据集合划分
按照训练集 8 成和测试集 2 成的比例对数据集进行划分,并检查划分之后的数据集数量。
x_train_, y_train_, x_test, y_test = train_test_split(
data_clean['text_cut'], data_clean['label'], test_size=0.2, random_state=1)
for data_set in [x_train_, y_train_, x_test, y_test]:
print(data_set.shape)
通过刚刚建立起来的词典,开始对训练集和测试集合进行向量化。
x_train = vectorizer.transform(x_train_)
y_train = vectorizer.transform(y_train_)
for data_set in [x_train, y_train, x_test, y_test]:
print(data_set.shape)
模型加载和训练
# 模型加载贝叶斯分类模型
from sklearn.naive_bayes import MultinomialNB
own_model = MultinomialNB()
# 模型训练
%time own_model.fit(x_train, x_test)
own_model_result = own_model.predict(y_train)
模型评价
"classification_report" 函数通过传入原始的标签和预测的标签可以直接将分类器性能进行度量,利用常用的分类模型评价指标对训练好的模型进行模型评价,acc 评价被正确预测的样本占总样本的比例,Precision 是衡量模型精确率的指标,它是指模型识别出的文档数与识别的文档总数的比率,衡量的是模型的查准率。Recall 召回率也称为敏感度,它是指模型识别出的相关文档数和文档库中所有的相关文档数的比率,衡量的是检索系统的查全率,表示正样本在被正确划分样本中所占的比例,F 值是精确率与召回率的调和平均数。
# 打印测试报告
from sklearn import metrics
print(metrics.classification_report(y_test, own_model_result))
用户评论情感极性推理
基于模型的情感极性预测可以很好的对情感极性进行预测,直接输出用户评论中积极情感对应的概率值作为用户情感极性映射,预测结果根据其中的情感副词不同给出不同的情感极性判断。对照自己输入的测试数据来看,模型受到标签数据不平衡问题的影响,对积极评论识别比较好,但是对消极评论的情感极性预测不合理,后续可以继续改进。
predict_text = ['这个民宿环境不错啊', '这家民宿环境真的垃圾']
test_text = vectorizer.transform(
[" ".join(jieba.lcut(i)) for i in predict_text])
test_pro = own_model.predict_proba(test_text)
# 直接输出用户评论对应的积极情感对应的概率值作为用户情感极性映射
[i[1] for i in test_pro]
快速使用模型进行批量的情感分析
使用训练好的模型对用户评论进行情感极性预测,并输出情感为积极的概率来表示用户评论的情感趋势。
# 用户评论向量化
data_text = vectorizer.transform(data_clean['text_cut'])
# 批量对用户评论进行情感推理
%time data_clean['sa_model_score'] = [round(i[1], 5) for i in own_model.predict_proba(data_text)]
情感可视化
首先对用户自己的原始打分进行可视化,显示原始的用户满意度趋势,横坐标表示用户情感极性,越靠近 1 用户情感越积极,纵坐标表示用户数量。
model_score = data_clean['sa_model_score']
user_score = data_clean['user_score_normal']
# 定义画布大小
plt.rcParams['figure.figsize'] = (8.0, 8.0)
plt.hist(user_score, bins=np.arange(0, 1, 0.01))
plt.xlabel("user_sa_score")
plt.ylabel("count")
plt.title('Customer_Satisfaction_Analysis')
plt.show()
对模型依据用户评价进行建模而预测出来的用户满意度进行可视化。
plt.hist(model_score, bins=np.arange(0, 1, 0.01))
plt.xlabel("model_sa_score")
plt.ylabel("count")
plt.title('Customer_Satisfaction_Analysis')
plt.show()
对两种情感极性进行对比可视化,模型在情感分布上较用户打分更加均匀,通过对用户打分进行建模可以使得将用户评价在同一个标准中进行评价,减少顾客个人的喜好差异对评价的影响。
# 使用红色描述模型情感极性
plt.hist(model_score, bins=np.arange(0, 1, 0.01),
color='red', label='model_sa_score')
# 使用蓝色描述用户原始打分
plt.hist(user_score, bins=np.arange(0, 1, 0.01),
color='blue', label='user_sa_score')
plt.legend()
plt.xlabel('count')
plt.ylabel('sa_score')
plt.title('Double_Customer_Satisfaction_Analysis')
plt.show()
使用 Matplotlib 将用户自主打分(归一化之后的分数)和情感模型分析的结果进行对比,通过数据的描述分析得知,模型标签和用户实际打分表现接近,证实使用用户打分映射用户情感极性的可行性。
# 挑选两列数据进行描述统计
static = data_clean[['sa_model_score', 'user_score_normal']]
static.describe()
在实验的开始,我们了解到实验数据中存在缺失的情况,暂时在数据的建模中不进行使用,使用在最开始定义的 data_all,在最后我们使用模型对缺失用户打分的评价数据进行批量的情感极性预测,以此来填补数据不完整的问题。全量用户评价使用模型预测,耐心等候,需要花费一点时间。
# 全量用户评论分词
data['text_cut'] = data['content'].apply(
lambda x: " ".join(jieba.lcut(str(x))))
# 全量用户评论向量化
data_text = vectorizer.transform(data['text_cut'])
# 全量对用户评论情感推理
data['sa_model_score'] = [round(i[1], 5)
for i in own_model.predict_proba(data_text)]
plt.hist(data['sa_model_score'], bins=np.arange(0, 1, 0.01))
plt.xlabel("model_sa_score")
plt.ylabel("count")
plt.title('Final_Customer_Satisfaction_Analysis')
plt.show()
使用 Pandas 的 describe 函数进行满意度描述,从 5677 条用户评价中,通过平均值得出整体民宿满意度为较好,超过一半的用户满意度达到 0.9 以上,2-8 分布情况在整体较为满意,但是关注到用户情感峰值出现在消极的部分,所以仍旧存在改进的地方,后续我们会进行细粒度的用户意见挖掘。
# 对用户情感极性进行描述统计
static = data[['sa_model_score']]
static.describe()
预览最终的数据文件:
data.head()