酒店评论数据分析

2020-2021年度《数据分析与机器学习》课程期末课程设计报告

酒店评论数据分析(数据集10元一份,需要购买请私信,原代码,课程报告均可购买详情请私信)

摘要

本文主要通过机器学习的常规算法模型来对酒店评论数据进行分析预测,酒店的每一条评论都是一个文本数据,可以当作特征值,评论等级就是数据的标签。通过已有数据进行文本分析,形成一个有效的训练模型来预测英文评论文本的等级。并利用内容分析法进行酒店评论关键因素的提取,再结合词频统计绘制词云来找出影响酒店评论等级好坏的因素。
首先我们对英文文本进行分词。将文本数据的特征提取出来转换成用数字表示的词频矩阵,然后再用TF-IDF算法计算每个词在文章中的重要程度。把处理好的数据放入不同的模型中进行训练,然后根据预测准确率来判断模型的好坏。我们依次使用了,朴素贝叶斯、K近邻、决策树、随机森林、多层感知器、逻辑回归、支持向量机等算法模型。这些模型的准确率在46%~60%之间波动,其中支持向量机的预测准确率为60%,是使用的模型中预测准确率最高的。
最后利用内容分析法进行影响因素的提取。先将评论文本中的5分评论都合并到一个字符串中,再将所有的1分合并都一个字符串中,再把所有评论合并到一个字符串中,一共得到三个大型字符串,依次进行分词、提取名词、词形还原、词频统计、绘制相应的词云,分析得到影响酒店好坏的因素主要是设施、服务、环境这三大因素。

关键词:英文分词 朴素贝叶斯 支持向量机 随机森林 逻辑回归 内容分析法 词云

一、问题介绍

酒店在旅游产业中起着至关重要的作用,大多数人出行都会选在居住在酒店,随着近几年互联网的发展,酒店旅游业开始大力拓展互联网经济,在网站上消费者就可以看到琳琅满目的酒店,但是仅通过酒店单方面的信息顾客很难判断酒店是否货真价实,因此大多数消费者都喜欢去浏览已经消费过的人留下的评论,通过评论的好坏来判断酒店的质量已经成了广大消费者的主要筛选方式。这次任务就提供了大量的酒店评论信息,需要对这些评论进行处理从而分析影响酒店好坏的关键因素是什么,并根据训练得到的模型来对测试数据集进行预测。

二、分析思路

这个题目属于分类型任务。酒店的评论数据是一种文本类型的数据,所以我们将评论数据作为文本数据来进行文本分类。
首先我们对数据进行一些可视化的操作来探索数据的分布情况。文本评论的长度分布如图1所示

图 1 文本长度分布图
从图1可以看出大部分的评论数据都是在250个词以内的。评论训练集中每个评论等级的数量分布如图2所示。

图2 评论等级数量分布柱状图
5分的好评数量最多已经超过了8000条,1分的差评数量最少大概在1400条左右,从5分好评到1分差评的文本数量依次递减。
在机器学习算法模型里面朴素贝叶斯、K近邻、决策树、逻辑回归以及更高级的随机森林、支持向量机、多层感知器等模型经常拿来处理分类问题。用sklearn第三方库中的算法模型进行文本分类,简便快捷,可以先尝试着跑一下数据看看预测效果。在调用第三方函数库中的算法模型之前还需要对英文评论数据文本进行分词、特征值提取转换等操作。
将文本数据进行特征工程,采用TF-IDF算法计算每一个词的TF-IDF分数从而将文本矩阵转换成数字矩阵。就可以放入任何训练模型之中进行文本分类了。
由于我们只有带有标签的训练集数据和只包含评论文本的测试集数据,所以无法直接判断我们的预测结果准确率高低,因此先将训练集数据进行划分,只用训练集的数据来进行预测,再将预测的结果和实际标签进行对比来评估模型的好坏。最后选用预测准确率最高的模型。
针对影响酒店评价好与坏的因素,我们的想法是将所有的评论数据进行合并得到第一个大字符串,将所有的五分好评文本合并得到第二个大字符串,将所有的1分差评文本合并得到第三个大字符串,再依次分词,去除停用词,然后统计词频,绘制词云。其中词频越高相应的对酒店评论的好坏影响也就越大。

三、方法概述

在构建训练模型之前我们需要对英文文本评论数据做一个处理,因为任何训练模型都是无法直接解释文本数据的,所以需要把文本型的数据用数值表示。去掉停用词,标点符号,留下表达实际含义的词组成列表,在词库中映射稀疏向量。
在处理评论文本数据时,我们用的是TF-IDF算法。这里简单介绍一下TF-IDF算法。
TF-IDF算法
TF-IDF(term frequent-inverse document frequency,词频-逆向文件频率)是一种用于信息检索(information,retrieval)与文本挖掘(text mining)的常用加权技术。
TF-IDF是一种统计方法,用于评估字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
(1)TF是词频(Term Frequency)
词频(TF)表示词条(关键字)在文本中出现的频率。
这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。

公式:
(1)
即:
(2)
其中 是该词在文件 中出现的次数,分母则是文件 中所有词汇出现的次数总和;
(2)IDF是逆向文件频率(Inverse Document Frequency)
逆向文件频率 (IDF) :某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。
如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。
公式:
(3)
其中 是语料库中的文件总数, 表示包含词语 的文件数目(即 的文件数目)。如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用
即:
(4)
分母之所以要加一,是为了避免分母为零的计算异常情况。
(3)TF-IDF实际上是:TFIDF
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
公式:TF-IDF=TF
IDF
注:TF-IDF算法容易理解,并且容易实现,但是其简单结构并没有考虑词语的语意信息,无法处理一词多义与一义多词的情况。
朴素贝叶斯模型
朴素贝叶斯的朴素是指特征条件独立假设,贝叶斯是指贝叶斯定理。
朴素贝叶斯是用来描述两个条件概率之间的关系。
条件概率是指事件A在另外一个事件B已经发生条件下的发生概率。条件概率表示为: 读作“在B条件下A的概率”。
若只有两个事件A,B,那么:
(5)
(6)
联立(1)式和(2)式可得:
(7)
全概率公式: 表示若事件A1,A2,…,An构成一个完备事件组且都有正概率,则对任意一个事件B都有公式成立。

图 3 全概率公式示意图
(8)
(9)
贝叶斯公式是将全概率公式带入到条件概率公式当中。
对于事件 和事件B有:
(10)

对于 来说,分母 为一个固定值,因为我们只需要比较 的大小,所以可以将分母固定值去掉,并不会影响结果。因此,可以得到下面公式:
(7)
其中 是先验概率, 是后验概率, 是似然函数。
先验*似然=后验
特征条件独立假设在分类问题中,常常需要把一个事物分到某个类别中。 一个事物又有许多属性,即 。
常常类别也是多个,
即 ,表示x属于某个分类概率,那么,我们需要找出其中最大的那个概率 ,根据上一步得到的公式可得:
(11)
就样本x有n个属性: 。
所以:
。 (12)
条件独立假设,就是各条件之间互不影响
所以样本的联合概率就是连乘:
(13)
最终公式为:
(14)
朴素贝叶斯公式:

根据公式(11),即朴素贝叶斯公式,就可以做分类问题了。
随机森林算法模型
随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于机器学习的一大分支——集成学习(Ensemble Learning)方法。随机森林的名称中有两个关键词,一个是“随机”,一个就是“森林”。“森林”很好理解。一棵叫做树,成百上千棵就可以叫做森林了。这也是随机森林的主要思想–集成思想的体现。
其实从直观角度来解释,每棵决策树都是一个分类器(假设现在针对的是分类问题),那么对于一个输入样本,N棵树会有N个分类结果。而随机森林集成了所有的分类投票结果,将投票次数最多的类别指定为最终的输出,这就是一种最简单的 Bagging 思想。
随机森林的相关基础知识
  随机森林看起来是很好理解,但是要完全搞明白它的工作原理,需要很多机器学习方面相关的基础知识。这里只简单说一下,而不逐一进行赘述。
  1、信息、熵以及信息增益
  这三个基本概念是决策树的根本,是决策树利用特征来分类时,确定特征选取顺序的依据。
引用香农的话来说,信息是用来消除随机不确定性的东西。当然这句话虽然经典,但是还是很难去搞明白这种东西到底是个什么样,可能在不同的地方来说,指的东西又不一样。对于机器学习中的决策树而言,如果带分类的事物集合可以划分为多个类别当中,则某个类( )的信息可以定义如下:
(15)
用来表示随机变量信息, 指是当 发生时的概率。
熵是用来度量不确定性的。当熵越大, 的不确定性越大,反之越小对于机器学习中的分类问题而言,熵越大即这个类别的不确定性更大,反之越小。
信息增益在决策树算法中是用来选择特征的指标,信息增益越大,则这个特征的选择性越好。
决策树
决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。
集成学习
集成学习通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器/模型,各自独立地学习和做出预测。这些预测最后结合成单预测,因此优于任何一个单分类的做出预测。
  随机森林是集成学习的一个子类,它依靠于决策树的投票选择来决定最后的分类结果。
随机森林的生成
随机森林中有许多的分类树。我们要将一个输入样本进行分类,我们需要将输入样本输入到每棵树中进行分类。打个形象的比喻:森林中召开会议,讨论某个动物到底是老鼠还是松鼠,每棵树都要独立地发表自己对这个问题的看法,也就是每棵树都要投票。该动物到底是老鼠还是松鼠,要依据投票情况来确定,获得票数最多的类别就是森林的分类结果。森林中的每棵树都是独立的,99.9%不相关的树做出的预测结果涵盖所有的情况,这些预测结果将会彼此抵消。少数优秀的树的预测结果将会超脱于芸芸“噪音”,做出一个好的预测。将若干个弱分类器的分类结果进行投票选择,从而组成一个强分类器,这就是随机森林bagging的思想(关于bagging的一个有必要提及的问题:bagging的代价是不用单棵决策树来做预测,具体哪个变量起到重要作用变得未知,所以bagging改进了预测准确率但损失了解释性。)下图可以形象地描述这个情况:

图 4 随机森林示意图
有了树我们就可以分类了,每棵树按照如下规则生成:
1)如果训练集大小为N,对于每棵树而言,随机且有放回地从训练集中的抽取N训练样本(这种采样方式称为bootstrap sample方法),作为该树的训练集;
从这里我们可以知道:每棵树的训练集都是不同的,而且里面包含重复的训练样本(理解这点很重要)。
对训练集还要进行随机抽样,如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的,这样的话完全没有bagging的必要;
随机抽样还要是有放回的抽样,如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是"有偏的",都是绝对"片面的"(当然这样说可能不对),也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决,这种表决应该是"求同",因此使用完全不同的训练集来训练每棵树这样对最终分类结果是没有帮助的,这样无异于是"盲人摸象"。
2)如果每个样本的特征维度为M,指定一个常数m<<M,随机地从M个特征中选取m个特征子集,每次树进行分裂时,从这m个特征中选择最优的;
3)每棵树都尽最大程度的生长,并且没有剪枝过程。
一开始我们提到的随机森林中的“随机”就是指的这里的两个随机性。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)。
随机森林分类效果(错误率)与两个因素有关:
森林中任意两棵树的相关性:相关性越大,错误率越大;
森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。
减小特征选择个数m,树的相关性和分类能力也会相应的降低;增大m,两者也会随之增大。所以关键问题是如何选择最优的m(或者是范围),这也是随机森林唯一的一个参数。
袋外错误率(oob error)
构建随机森林的关键问题就是如何选择最优的m,要解决这个问题主要依据计算袋外错误率oob error(out-of-bag error)。
  随机森林有一个重要的优点就是,没有必要对它进行交叉验证或者用一个独立的测试集来获得误差的一个无偏估计。它可以在内部进行评估,也就是说在生成的过程中就可以对误差建立一个无偏估计。
  我们知道,在构建每棵树时,我们对训练集使用了不同的bootstrap sample(随机且有放回地抽取)。所以对于每棵树而言(假设对于第k棵树),大约有1/3的训练实例没有参与第k棵树的生成,它们称为第k棵树的oob样本。
  而这样的采样特点就允许我们进行oob估计,它的计算方式如下:
  (note:以样本为单位)
  1)对每个样本,计算它作为oob样本的树对它的分类情况(约1/3的树);
  2)然后以简单多数投票作为该样本的分类结果;
  3)最后用误分个数占样本总数的比率作为随机森林的oob误分率。
  oob误分率是随机森林泛化误差的一个无偏估计,它的结果近似于需要大量计算的k折交叉验证。
逻辑回归模型
逻辑回归假设数据服从伯努利分布,通过极大化似然函数方法,运用梯度下降来求解参数,来达到将数据二分目的。
算法推导:
对数几率函数:是一种Sigmoid函数,通过此函数来输出类别概率。
对数几率函数为:
(17)
其中y代表的是样本视为正样本的可能性,则 视为负样本的可能性。
对数几率:定义为
(18)
其中 称为比率。
决策边界:作用在n维空间,将不同样本分开的平面或曲面,在逻辑回归中,决策边界对应
(19)

图 5 逻辑回归分布函数
使用极大似然法进行参数估计。由对数几率函数对应输出正样本的概率,可得对应关系: , (20)
并令 ,对应的
(21)

(22)
现学习目标是对参数和b进行参数估计,使得逻辑回归模型能尽可能符合数据集分
布。对于给定的数据集 其中i从1到数据集大小m,来使得最大化对数似然。
首先,写出似然函数:
(23)
对数似然函数就是
(24)
化简得到
(25)
后面式子是带入 后化简得到。
现在,即对对数似然函数求极大值,即以对数似然函数为目标的最优化问题。 是关于 的高阶连续可导凸函数,根据凸优化理论,可采用梯度下降法,牛顿法等优化方法求解。
逻辑回归的损失函数
逻辑回归的损失函数是交叉熵损失函数,交叉熵主要用于度量分布的差异性。

(26)
即用 代替了 。
交叉熵损失函数:
(27)
使用交叉熵损失函数的原因: 中去掉 便是上述的对数似然函数,对 求最小,即对对数似然函数求极大。
逻辑回归的梯度下降
(28)
所以,参数迭代更新式为:
(29)
普通逻辑回归是一个二分类模型,可推广至多分类。
多分类逻辑回归
假设:离散型随机变量 的取值集合是 ,共有 类,则多分类逻辑回归模型的输出概率为:
(30)
(31)
其中,注意 是一个取1到 类其中一类, 是指第K类, 便是由1减去其他k取值的概率,就是第K类的概率。
多分类LR由来:softmax函数:
(32)

支持向量机
多分类问题:
给定含N个样本训练集 其中K维特征向量 ,类标签 , 。训练数据集共M个类。任务是找到决策函数 (或者说一个规则)用于预测新数据的类别。
内容分析法
内容分析是大众传播研究的内容和方法之一,通过对大众传播内容量和质的分析,认识和判断某一时期的传播重点,对某些问题的倾向、态度、立场,以及传播内容在某一时期的变化规律等。在进行内容分析时,研究者必须排除个人主观色彩,从现存的材料出发,追求共同的价值观;必须将所有的有关材料看成一个有机的整体,对材料进行全面、系统的研究;用数学统计方法,对所研究的材料进行量的分析。此外,内容分析也不应排除定性分析,即根据所得到的材料和数据进行一定的逻辑推理和哲学思辩。内容分析一般要经过选择、分类、统计等三个阶段,可采取以下三种作法:
(一)记录或观察某一传播媒介在某一时期的传播内容;
(二)对同一传播媒介在不同时期所报道的内容进行分析和比较;
(三)对同一时期不同传播媒介就同一事件或同一题材所报道的内容、方式、方法等进行分析和比较,找出异同。
内容分析法也是一种将不系统的、定性的符号性内容转换成系统的、定量的数据资料的研究方法,网络内容分析把网络信息和内容分析法结合起来,运用本方法进行酒店评论关键因素的提取。

四、实验过程和结果分析
实验过程
文本分类步骤:
数据集说明:一条记录为“一段酒店评论文本”,标签为”1~5”的评论等级。

  • 读取训练数据把评论文本数据列放入列表变量text
# 获取评论数据训练集
critic = pd.read_csv("Training data.csv")
text = critic["Review"].tolist()

有一些品牌是英文,有大小写的,有空格,比如(SUPER MILD),可能结巴分词直接把这两个英文拆开了,那就匹配不上了!所以需要在分词之前设定一下。

# 所以先执行这一句就可以解决了。
jieba.re_han_default = re.compile('(.+)', re.U)
  • 调用jieba分词库,将text列表中的评论文本数据进行分词。
# 对评论列表里面的评论进行分词
t = []
for i in text:
    n = jieba.lcut(str(i))
    t.append(' '.join(n))  # 将每个评论的分词结果用空格连接起来
critic["Review"] = t  # 将分词以后的结果存入到原始数据的评论列
  • 读取测试数据,把评论文本数据放入列表变量t1中
# 获取测试集
test = pd.read_csv("Testing data.csv")
text = test["Review"].tolist()
t1 = []
  • 对测试文本数据进行结巴分词
# 对每篇评论进行分词
for i in text:
    n = jieba.lcut(str(i))
    t1.append(' '.join(n))  # 将每个评论的分词结果用空格连接起来
x_test = t1
  • 对训练数据集进行分割,将特征值和标签分别放入列表x_train,y_train中
# 对训练集数据进行特征值和标签的分割
x_train = critic["Review"]
y_train = critic["Rating"]

特征工程:将原始数据集转换为用于训练机器学习模型的平坦特征(flat feature),并从现有数据特征创建新的特征。
这里我使用TF-IDF向量作为特征
TF-IDF的分数代表了词语在文档和整个语料库中的相对重要性。TF-IDF分数由两部分组成:第一部分是计算标准的词语频率(TF),第二部分是逆文档频率(IDF)。其中计算语料库中文档总数除以含有该词语的文档数量,然后再取对数就是逆文档频率。

TF-IDF向量可以由不同级别的分词产生(单个词语,词性,多个词(n-grams))
词语级别TF-IDF:矩阵代表了每个词语在不同文档中的TF-IDF分数。
N-gram级别TF-IDF: N-grams是多个词语在一起的组合,这个矩阵代表了N-grams的TF-IDF分数。
词性级别TF-IDF:矩阵代表了语料中多个词性的TF-IDF分数。

  • 调用Sklearn库中的TfidfVectorize函数进行文本特征提取
# 对数据集进行特征抽取
tf = TfidfVectorizer()

# 以训练集当中的词的列表进行每篇评论重要性统计
x_train = tf.fit_transform(x_train)
x_test = tf.transform(x_test)

因为没有测试集的标签所以直接将测试集放入各分类器模型中进行预测我们就无法判断预测结果的准确率,所以在进行测试集的预测之前先单独的将训练集进行分割,按照7:3的比例进行训练集和测试集的划分。然后使用不同的分类模型进行评估。

  • 朴素贝叶斯模型
def naviebayes(x_train,y_train,x_test,y_test):
    """
    朴素贝叶斯模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("朴素贝叶斯开始计算...")

    # 进行朴素贝叶斯算法的预测
  mlt = MultinomialNB(alpha=0.4)
    #将训练集放入模型中开始训练
  mlt.fit(x_train, y_train)

    print("朴素贝叶斯算法的准确度:",mlt.score(x_test,y_test))

在Pycharm中运行计算的准确度在46%左右,效果很差。

  • K近邻模型
def KNeighbors(x_train,y_train,x_test,y_test):
    """
    K近邻算法模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("K近邻算法开始计算...")

    #进行K近邻算法的预测
  knn = KNeighborsClassifier()
    #将训练集放入模型中开始训练
  knn.fit(x_train,y_train)

     print("K近邻的预测准确率:",knn.score(x_test,y_test))

在Pycharm中运行计算的准确度在47%左右,效果很差。

  • 决策树模型
def decision(x_train,y_train,x_test,y_test):
    """
    决策树模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("决策树开始计算...")

    # 用决策树进行预测
  dec = DecisionTreeClassifier(max_depth=8) # 传入最大深度值
  #将训练集放入模型开始训练
  dec.fit(x_train, y_train)

     print("决策树模型预测率:",dec.score(x_test,y_test))

在Pycharm中运行计算的准确度在46%左右,效果很差。

  • 随机森林模型
def RandomForest(x_train, y_train,x_test,y_test):
    """
    随机森林
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
     print("随机森林算法模型开始计算...")
    
     #用随机森林模型进行预测
  rfc = RandomForestClassifier()
  #将训练集放入模型进行训练
  rfc.fit(x_train,y_train)

      print("随机森林模型准确率:",rfc.score(x_test,y_test))

在Pycharm中运行计算的准确度在51%左右,效果差。

  • 多层感知器模型
def MLPC(x_train, y_train, x_test, y_test):
    """
    多层感知器模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("多层感知器开始计算...")
    #用多层感知器模型进行预测
    mlpc = MLPClassifier()
    #将训练集放入模型进行训练
    mlpc.fit(x_train,y_train)

    print("多层感知器预测准确率:",mlpc.score(x_test,y_test))

在Pycharm中运行计算的准确度在55%左右,效果差。

  • 逻辑回归模型
def Logitic(x_train,y_train,x_test,y_test):
    """
    逻辑回归模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("逻辑回归模型开始计算...")
    
    # 用逻辑回归进行预测
    lg = LogisticRegression(solver="sag")
    #将数据集放入模型进行训练
    lg.fit(x_train,y_train)

    print("逻辑回归的预测准确率:",lg.score(x_test,y_test))

在Pycharm中运行计算的准确度在59%左右,效果不佳。

  • 支持向量机模型
def svm_model(x_train, y_train, x_test,y_test):
    """
    支持向量机
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("支持向量机开始计算...")
    # 用支持向量机模型进行预测
    svm_clf = SVC(kernel="linear", verbose=False)
    # 将训练集数据放入模型进行训练
    svm_clf.fit(x_train, y_train)

    # 预测结果
    print("支持向量机的预测准确率:",svm_clf.score(x_test,y_test))

在Pycharm中运行计算的准确度在60%左右,效果不佳。但支持向量机的准确率是以上所有模型中运行结果最好的一个。
所以我们就使用支持向量机来模型来预测。
直接将处理好的数据放入支持向量机的函数模型中得到预测结果,然后将1500条评论数据和对应的预测结果放入列表testing中,最后把testing的内容写入文件Testingdata.csv中。

#支持向量机
svm = svm_model(x_train,y_train,x_test)
x = pd.read_csv("Testing data.csv")
x = x["Review"].tolist()
testing = []#存放效果最好的支持向量机的预测结果
for i in range(1500):
    # 预测效果最好的支持向量机模型预测结果单独放入b中
    b = []
    b.append(x[i])# 将评论数据放入列表中
    b.append(svm[i])
    testing.append(b)
print("计算完毕!开始存储每种模型的预测数据到CSV文件...")
with open("Testingdata.csv","w+",newline="") as best:
    f1 = csv.writer(best)
    f1.writerow(["Review","Rating"])
    f1.writerows(testing)

影响因素的提取:
先将文本分成三大类字符串,第一个字符串good包含所有的五分好评文本,第二个字符串包含所有的一分差评文本,第三个字符串包含所有的文本。

# 获取评论数据训练集
critic = pd.read_csv("Training data.csv")
# 获取所有评分为5的评论文本
good = ""
bad = ""
all = ""
for i in range(len(critic["Rating"])):
    all=all+critic["Review"].get(i)
    if(critic["Rating"].get(i) == 5):
        good=good+critic["Review"].get(i)
    elif(critic["Rating"].get(i) == 1):
        bad=bad+critic["Review"].get(i)
  • 对三类文本依次进行分词处理
# 有一些品牌是英文,有大小写的,有空格,比如(SUPER MILD),可能结巴分词直接把这两个英文拆开了,那就匹配不上了!
jieba.re_han_default = re.compile('(.+)', re.U)  # 所以先执行这一句就可以解决了。
# 对五分评论文本进行分词
distributegood = jieba.lcut(good)
txtgood = " ".join(distributegood)

#对1分差评进行分词
distributebad = jieba.lcut(bad)
txtbad = " ".join(distributebad)

#对所有文章进行分词
distributeall = jieba.lcut(all)
txtall = " ".join(distributeall)
  • 绘制三类文本的词云并对结果进行综合分析得出影响酒店好坏的关键因素
#绘制五分文本词云
wgood = wordcloud.WordCloud(font_path="msyh.ttc",width=1000,height=700,background_color="white",max_words=100)
wgood.generate(txtgood)
wgood.to_file("good.png")

#绘制1分文本词云
wbad = wordcloud.WordCloud(font_path="msyh.ttc", width=1000, height=700, background_color="white", max_words=100)
wbad.generate(txtbad)
wbad.to_file("bad.png")

# 绘制所有文本词云
wall = wordcloud.WordCloud(font_path="msyh.ttc", width=1000, height=700, background_color="white", max_words=100)
wall.generate(txtall)
wall.to_file("all.png")

结果分析
预测评分:
运行结果如下图所示
在这里插入图片描述

图 6 各种模型的预测准确率结果图

将训练数据集放入其中不同的分类算法模型中得出来的结果准确率在46%~60%,其中支持向量机模型的预测准确率最高,因此我们将使用支持向量机模型来预测1500条评论数据的等级。
详细代码见附录6.1各种模型对训练集的预测准确率对比代码,结果见附件Testdata.csv。

影响酒店评价的关键因素:
根据对评论信息的前期探究,先将评论分成五分好评,一分差评以及所有评论三大类,每一类为一个大型文本字符串。依次对三个文本进行分词处理,绘制词云。可以发现评论中多有出现nice,good,great等描写酒店感受的形容词,但由于我们不知道这些形容词的具体描述对象,而我们应该得到具体描述对象,所以在后期处理时我们仅选取名词。首先对三类文本进行分词处理,然后通过词性标注选取出名词,再对这些词的不同形态进行合并,比如room和rooms等。然后对处理好的词进行词频统计。
根据上述方法,整理出高频词,绘制的三类词云如下图5,图6,图7所示。
在这里插入图片描述

图 5 所有文章的词云
在这里插入图片描述

图 6 一分差评的词云
在这里插入图片描述

图 7 五分好评的词云
根据结果可以发现,评价主要分为如下几个方面:酒店入住的整体感觉(hotel,room等,以及以上提到的出现频率最高的nice,good),酒店硬件设施(restaurant,area,bathroom,bed,pool等),酒店软件服务(staff,service,time,clean等),酒店区位和周边环境(location,place,trip,station等),顾客的后期态度(restort等)。
通过对图5,图6,图7的综合分析,我们把高频词划分为3个层次,room,service,location,bathroom(设施)等为顾客提供最基本的感知,这些词出现的频率充分说明这是顾客最在意的因素,再次是restaurant,breakfast,clean,station,time(服务)等二级因子,可以看出这些是一级因子的细化,在满足了基本住宿要求以后,顾客会进一步关注酒店的居住环境,比如noise,music等辅助因素。

五、附录

5.1各种模型对训练集的预测准确率对比代码

from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
import pandas as pd
import jieba
import re
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import precision_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report


def DataDeal():
    """
    读取数据,对评论进行分词,将分词后的结果进行词频占比,特征提取的简单处理
    :return:
    x_train:训练集的特征值
    y_train:训练集的标签
    x_test:测试集
    """
    # 获取评论数据训练集
    critic = pd.read_csv("Training data.csv")
    text = critic["Review"].tolist()

    # 有一些品牌是英文,有大小写的,有空格,比如(SUPER MILD),可能结巴分词直接把这两个英文拆开了,那就匹配不上了!
    jieba.re_han_default = re.compile('(.+)', re.U)  # 所以先执行这一句就可以解决了。
    # 对评论列表里面的评论进行分词
    t = []
    for i in text:
        n = jieba.lcut(str(i))
        t.append(' '.join(n))  # 将每个评论的分词结果用空格连接起来
    critic["Review"] = t  # 将分词以后的结果存入到原始数据的评论列

    # 对训练集数据进行特征值和标签的分割
    x = critic["Review"]
    y = critic["Rating"]
    #划分训练集和测试集
    x_train,x_test, y_train, y_test = train_test_split(x, y, train_size=0.3)

    # 对数据集进行特征抽取
    tf = TfidfVectorizer()

    # 以训练集当中的词的列表进行每篇评论重要性统计
    x_train = tf.fit_transform(x_train)
    x_test = tf.transform(x_test)

    return x_train,y_train,x_test,y_test

def naviebayes(x_train,y_train,x_test,y_test):
    """
    朴素贝叶斯
    :param x_train:训练集特征值
    :param y_train:训练集
    :param x_test:
    :param y_test:
    :return:
    """

    print("朴素贝叶斯开始计算...")

    # 进行朴素贝叶斯算法的预测
    mlt = MultinomialNB(alpha=0.4)

    #将训练集放入模型中开始训练
    mlt.fit(x_train, y_train)

    print("朴素贝叶斯算法的准确度:",mlt.score(x_test,y_test))
    # 模型评估
    description("朴素贝叶斯", mlt.predict(x_test), y_test)


def description(s,y_predict,y_test):
    print(s+"模型评估:")
    print("查全率与准确率:")
    print(precision_score(y_test, y_predict, average=None))
    print(confusion_matrix(y_test, y_predict))
    print("主要分类指标的文本报告:")
    print(classification_report(y_test,y_predict))

def svm_model(x_train, y_train, x_test,y_test):
    """
    支持向量机
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("支持向量机开始计算...")
    # 用支持向量机模型进行预测
    svm_clf = SVC(kernel="linear", verbose=False)
    # 将训练集数据放入模型进行训练
    svm_clf.fit(x_train, y_train)

    # 预测结果
    print("支持向量机的预测准确率:",svm_clf.score(x_test,y_test))
    #模型评估
    description("支持向量机",svm_clf.predict(x_test),y_test)

def decision(x_train,y_train,x_test,y_test):
    """
    决策树模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("决策树开始计算...")

    # 用决策树进行预测
    dec = DecisionTreeClassifier(max_depth=8) # 传入最大深度值
    #将训练集放入模型开始训练
    dec.fit(x_train, y_train)

    print("决策树模型预测率:",dec.score(x_test,y_test))
    # 模型评估
    description("决策树", dec.predict(x_test), y_test)

def RandomForest(x_train, y_train,x_test,y_test):
    """
    随机森林
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("随机森林算法模型开始计算...")

    #用随机森林模型进行预测
    rfc = RandomForestClassifier()
    #将训练集放入模型进行训练
    rfc.fit(x_train,y_train)

    print("随机森林模型准确率:",rfc.score(x_test,y_test))
    # 模型评估
    description("随机森林", rfc.predict(x_test), y_test)

def Logitic(x_train,y_train,x_test,y_test):
    """
    逻辑回归模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("逻辑回归模型开始计算...")

    # 用逻辑回归进行预测
    lg = LogisticRegression(solver="sag")
    #将数据集放入模型进行训练
    lg.fit(x_train,y_train)

    print("逻辑回归的预测准确率:",lg.score(x_test,y_test))
    # 模型评估
    description("逻辑回归", lg.predict(x_test), y_test)

def KNeighbors(x_train,y_train,x_test,y_test):
    """
    K近邻算法
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("K近邻算法开始计算...")

    #进行K近邻算法的预测
    knn = KNeighborsClassifier()
    #将训练集放入模型中开始训练
    knn.fit(x_train,y_train)

    print("K近邻的预测准确率:",knn.score(x_test,y_test))
    # 模型评估
    description("K近邻", knn.predict(x_test), y_test)

def MLPC(x_train, y_train, x_test, y_test):
    """
    多层感知器模型
    :param x_train:训练集特征值
    :param y_train:训练集标签
    :param x_test: 测试集特征值
    :param y_test: 测试集标签
    :return:None
    """
    print("多层感知器开始计算...")
    #用多层感知器模型进行预测
    mlpc = MLPClassifier()
    #将训练集放入模型进行训练
    mlpc.fit(x_train,y_train)

    print("多层感知器预测准确率:",mlpc.score(x_test,y_test))
    # 模型评估
    description("多层感知器", mlpc.predict(x_test), y_test)


if __name__ =="__main__":
    #读取数据并进行预处理
    x_train, y_train, x_test, y_test = DataDeal()

    #朴素贝叶斯模型
    naviebayes(x_train,y_train,x_test,y_test)

    # K近邻算法模型
    KNeighbors(x_train, y_train, x_test, y_test)

    #决策树模型
    decision(x_train,y_train,x_test,y_test)

    #随机森林模型
    RandomForest(x_train,y_train,x_test,y_test)

    # 多层感知器
    MLPC(x_train, y_train, x_test, y_test)

    #逻辑回归模型
    Logitic(x_train,y_train,x_test,y_test)

    # 支持向量机模型
    svm_model(x_train, y_train, x_test, y_test)

5.2用支持向量机预测评论等级

import jieba,re,csv,pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC

def DataDeal():
    """
    读取数据,对评论进行分词,将分词后的结果进行词频占比,特征提取的简单处理
    :return:
    x_train:训练集的特征值
    y_train:训练集的标签
    x_test:测试集
    """
    # 获取评论数据训练集
    critic = pd.read_csv("Training data.csv")
    text = critic["Review"].tolist()
    # print(text)

    # 有一些品牌是英文,有大小写的,有空格,比如(SUPER MILD),可能结巴分词直接把这两个英文拆开了,那就匹配不上了!
    jieba.re_han_default = re.compile('(.+)', re.U)  # 所以先执行这一句就可以解决了。
    # 对评论列表里面的评论进行分词
    t = []
    for i in text:
        n = jieba.lcut(str(i))
        t.append(' '.join(n))  # 将每个评论的分词结果用空格连接起来
    critic["Review"] = t  # 将分词以后的结果存入到原始数据的评论列

    # 获取测试集
    test = pd.read_csv("Testing data.csv")
    text = test["Review"].tolist()
    t1 = []
    # 对每篇评论进行分词
    for i in text:
        n = jieba.lcut(str(i))
        t1.append(' '.join(n))  # 将每个评论的分词结果用空格连接起来
    x_test = t1
    # print("测试集长度:", len(x_test))

    # 对训练集数据进行特征值和标签的分割
    x_train = critic["Review"]
    y_train = critic["Rating"]

    # 对数据集进行特征抽取
    tf = TfidfVectorizer()

    # 以训练集当中的词的列表进行每篇评论重要性统计
    x_train = tf.fit_transform(x_train)
    x_test = tf.transform(x_test)

    return x_train,y_train,x_test
# 支持向量机算法
def svm_model(x_train, y_train, x_test):
    """
    支持向量机模型
    :param x_train: 训练集特征值
    :param y_train: 训练集标签
    :param x_test: 测试集特征值
    :return:
    y_predict 预测结果列表
    """
    print("支持向量机开始计算...")
    # 用支持向量机预测评论等级
    svm_clf = SVC(kernel="linear", verbose=False)
    #将训练数据放入模型进行训练
    svm_clf.fit(x_train, y_train)
    # 得到预测结果
    y_predict = svm_clf.predict(x_test)

    return y_predict

if __name__ =="__main__":
    x_train,y_train,x_test=DataDeal()

    #支持向量机
    svm = svm_model(x_train,y_train,x_test)

    x = pd.read_csv("Testing data.csv")
    x = x["Review"].tolist()
    testing = []#存放效果最好的支持向量机的预测结果
    for i in range(1500):
        # 预测效果最好的支持向量机模型预测结果单独放入b中
        b = []
        b.append(x[i])# 将评论数据放入列表中
        b.append(svm[i])
        testing.append(b)
    print("计算完毕!开始存储每种模型的预测数据到CSV文件...")
    with open("Testingdata.csv","w+",newline="") as best:
        f1 = csv.writer(best)
        f1.writerow(["Review","Rating"])
        f1.writerows(testing)

5.3绘制评论数据文本的词云

import pandas as pd
import jieba
import re
import wordcloud

def DataDeal():
    """
    绘制词云
    :return:None
       """
    # 获取评论数据训练集
    critic = pd.read_csv("Training data.csv")
    # 获取所有评分为5的评论文本
    good = ""
    bad = ""
    all = ""
    for i in range(len(critic["Rating"])):
        all=all+critic["Review"].get(i)
        if(critic["Rating"].get(i) == 5):
            good=good+critic["Review"].get(i)
        elif(critic["Rating"].get(i) == 1):
            bad=bad+critic["Review"].get(i)

    # 有一些品牌是英文,有大小写的,有空格,比如(SUPER MILD),可能结巴分词直接把这两个英文拆开了,那就匹配不上了!
    jieba.re_han_default = re.compile('(.+)', re.U)  # 所以先执行这一句就可以解决了。
    # 对五分评论文本进行分词
    distributegood = jieba.lcut(good)
    txtgood = " ".join(distributegood)

    #对1分差评进行分词
    distributebad = jieba.lcut(bad)
    txtbad = " ".join(distributebad)

    #对所有文章进行分词
    distributeall = jieba.lcut(all)
    txtall = " ".join(distributeall)

    #绘制五分文本词云
    wgood = wordcloud.WordCloud(font_path="msyh.ttc",width=1000,height=700,background_color="white",max_words=100)
    wgood.generate(txtgood)
    wgood.to_file("good.png")

    #绘制1分文本词云
    wbad = wordcloud.WordCloud(font_path="msyh.ttc", width=1000, height=700, background_color="white", max_words=100)
    wbad.generate(txtbad)
    wbad.to_file("bad.png")

    # 绘制所有文本词云
    wall = wordcloud.WordCloud(font_path="msyh.ttc", width=1000, height=700, background_color="white", max_words=100)
    wall.generate(txtall)
    wall.to_file("all.png")

if __name__ == "__main__":
    DataDeal()

  • 35
    点赞
  • 217
    收藏
    觉得还不错? 一键收藏
  • 39
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值