案例4:中文商品评论情感判定
案例背景
本案例将基于电商平台的商品评论数据,利用SVM对评论的情感倾向进行预测。任何行业领域,用户对产品的评价都显得尤为重要。通过用户评论,可以对用户情感倾向进行判定。例如目前最为普遍的网购行为,对于用户来说,参考评论可以做出更优的购买决策;对于商家来说,对商品评论按照情感倾向进行分类,并通过文本聚类得到普遍提及的商品优缺点,可以进一步改良产品。本案例主要讨论如何对商品评论进行情感倾向判定。
本案例中,我们从互联网某电商平台抓取手机的中文评论内容。然后对中文评论进行分词处理。为了区分评论中的好评和差评,我们使用支持向量机模型,并分析模型的输出结果。这份某款手机的商品评论信息数据集,共计8186个样本,数据集包括两个属性。其中,Comment字段是用户对该款手机的评论,类型为String;Class代表标注好的该评论的情感倾向,-1代表差评,0代表中评,1代表好评。
数据读取与划分
这份某款手机的商品评论信息数据集,包含2个属性,共计8186个样本。
| 列名 | 说明 | 类型 | 示例 |
|---|---|---|---|
| Comment | 对该款手机的评论 | String | 客服特别不负责,明明备注了也不看,发错了东西。 |
| Class | 该评论的情感倾向: -1 ----- 差评 0 ----- 中评 1 ------ 好评 | Int | -1 |
使用Pandas中的read_excel函数读取xls格式的数据集文件,注意文件的编码设置为gb18030。
python
import pandas as pd
#读入数据集
data = pd.read_excel("./data.xls")
data = data.dropna(subset=["Comment"])
data.head()
| | Comment | Class |
| 0 | 快就是手感满意也好喜欢也流畅很服务态度实用超快挺快用着速度礼品也不错非常好挺好感觉才来还行好... | 1 |
| 1 | 差评,说好的返现返现都是骗人。东西很差 很垃圾 | -1 |
| 2 | 售后真是差 买了不到15天锁屏键出现故障,申请换货过了审核说上门取件了 等了几天没人来 ... | -1 |
| 3 | 郁闷啊多等2天多无线充 充电宝和贴膜 和京东沟通没补发 心里那个郁闷啊不摆了 失败 ... | -1 |
| 4 | 今天去贴膜时才看到在卡槽右面有一处很小的刻痕,很是郁闷 | -1 |
|---|
查看数据集的相关信息,包括行列数,列名,以及各个类别的样本数。
python
# 数据集的大小
data.shape
(8185, 2)
python
# 数据集的列名
data.columns.values
array(['Comment', 'Class'], dtype=object)
python
# 不同类别数据记录的统计
data['Class'].value_counts()
1 3042
-1 2657
0 2486
Name: Class, dtype: int64
现在,我们要将Comment列的文本信息,转化成数值矩阵表示,也就是将文本映射到特征空间。首先,通过jieba对文本进行中文分词。
python
# 导入中文分词库jieba
import jieba
import numpy as np
python
# 对数据集的每个样本的文本进行中文分词
cutted = []
for row in data.values:
raw_words = (" ".join(jieba.cut(row[0])))
cutted.append(raw_words)
# 生成新DataFrame,Comment字段为分词后的内容
data_cutted = pd.DataFrame({
'Comment': cutted,
'Class': data['Class']
})
data_cutted.head(5)
Building prefix dict from the default dictionary ...
(8185, 2)
Dumping model to file cache C:\Users\ggq\AppData\Local\Temp\jieba.cache
Loading model cost 0.514 seconds.
Prefix dict has been built successfully.
| | Comment | Class |
| 0 | 快 就是 手感 满意 也好 喜欢 也 流畅 很 服务态度 实用 超快 挺快 用 着 速度 礼... | 1 |
| 1 | 差评 , 说好 的 返现 返现 都 是 骗人 。 东西 很差 很 垃圾 | -1 |
| 2 | 售后 真是 差 买 了 不到 15 天锁 屏键 出现 故障 , 申请 换货 过 了 审核... | -1 |
| 3 | 郁闷 啊 多 等 2 天多 无线 充 充电 宝 和 贴膜 和 京东 沟通 没 补发 ... | -1 |
| 4 | 今天 去 贴膜 时才 看到 在 卡槽 右面 有 一处 很小 的 刻痕 , 很 是 郁闷 | -1 |
|---|
绘制词云图
为了更直观地观察词频高的词语,我们使用第三方库wordcloud进行文本的可视化。
python
# 导入第三方库wordcloud
from wordcloud import WordCloud
针对好评,中评和差评的文本,建立WordCloud对象,绘制词云。
python
# 好评
wc = WordCloud(font_path='msyh.ttf')
wc.generate(''.join(data_cutted['Comment'][data_cutted['Class'] == 1])) #取出好评文本
wc.to_image()

python
# 中评
wc = WordCloud(font_path='msyh.ttf')
wc.generate(''.join(data_cutted['Comment'][data_cutted['Class'] == 0])) #取出中评文本
wc.to_image()

python
# 差评
wc = WordCloud(font_path='msyh.ttf')
wc.generate(''.join(data_cutted['Comment'][data_cutted['Class'] == -1]))
wc.to_image()

文本向量化
从词云展现的词频统计图来看,"手机","就是","屏幕","收到"等词对于区分毫无帮助而且会造成偏差。因此,需要把这些对区分类没有意义的词语筛选出来,放到停用词文件stopwords.txt中。
python
# 写入停用词文件
import codecs
with codecs.open('./stopwords.txt', 'a', encoding='utf-8') as f:
for items in ['ain', 'al', 'couldn', 'didn', 'doesn', 'don', 'hadn', 'hasn', 'haven', 'isn', 'll', 'mon', 'shouldn', 've', 'wasn', 'weren', 'won', 'wouldn', '几天', '上去', '手机', '就是', '屏幕', '收到', '感觉', '机子']:
f.write(items+'\n')
f.close()
使用jieba库的extract_tags函数,统计好评,中评,差评文本中的top 20关键词
python
#设定停用词文件,在统计关键词的时候,过滤停用词
import jieba.analyse
jieba.analyse.set_stop_words('./stopwords.txt')
python
# 好评关键词
# extract_tags是用tf-idf做的
keywords_pos = jieba.analyse.extract_tags(''.join(data_cutted['Comment'][data_cutted['Class'] == 1]), topK=20)
for item in keywords_pos:
print (item, end=' ')
不错 正品 赠品 发货 五分 东西 很漂亮 喜欢 满意 评价 好评 很快 速度 充电 卖家 物流 漂亮 快递 流畅 手感
经过以上步骤的处理,整个数据集的预处理工作"告一段落"。在中文文本分析和情感分析的工作中,数据预处理的内容主要是分词。只有经过分词处理后的文本数据集才可以进行下一步的向量化操作,满足输入模型的条件。
模型搭建与训练
经过分词之后的文本数据集要先进行向量化之后才能输入到分类模型中进行运算。
我们使用sklearn库实现向量化方法,去掉停用词,并将其映射到特征空间。
我们使用sklearn库中的函数直接实现SVM算法(SVC)。
为了方便,创建文本情感分析类CommentClassifier,来实现建模过程:
-
__init__为类的初始化函数。 -
fit()函数,来实现向量化与模型建立的过程。
python
# 实现向量化方法
from sklearn.feature_extraction.text import CountVectorizer
#实现svm模型
from sklearn.svm import SVC
# 实现交叉验证
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
# 实现评价指标
from sklearn import metrics
with open("./stopwords.txt",encoding="utf-8") as f:
stopwords = f.read().split("\n")
python
# 文本情感分类的类:CommentClassifier
class CommentClassifier:
def __init__(self):
pass
def fit(self, train_x, train_y, max_df):
list_text = list(train_x)
self.vectorizer = CountVectorizer(max_df=max_df, stop_words = stopwords, ngram_range=(1, 3)).fit(list_text)
self.array_trainx = self.vectorizer.transform(list_text)
self.array_trainy = train_y
self.model = SVC(kernel='linear', gamma=10 ** -5, C=1,class_weight="balanced").fit(self.array_trainx, self.array_trainy)
def predict_value(self, test_x):
list_text = list(test_x)
self.array_testx = self.vectorizer.transform(list_text)
array_predict = self.model.predict(self.array_testx)
return array_predict
-
使用
train_test_split()函数划分训练集和测试集。训练集:80%;测试集:20%。 -
输出分类评价结果,内容包括混淆矩阵以及含
Precision、Recall和F1-score三个指标的评分矩阵
python
#划分训练集,测试集
train_x, test_x, train_y, test_y = train_test_split(data_cutted['Comment'].ravel().astype('U'), data_cutted['Class'].ravel(),
test_size=0.2, random_state=4)
commentCls = CommentClassifier()
#max_df 设置为0.98
commentCls.fit(train_x, train_y, 0.98)
value_result = commentCls.predict_value(test_x)
print ('classification report')
print (metrics.classification_report(test_y, value_result, labels=[-1, 0, 1]))
print ('confusion matrix')
print (metrics.confusion_matrix(test_y, value_result, labels=[-1, 0, 1]))
c:\environment\python38\lib\site-packages\sklearn\feature_extraction\text.py:409: UserWarning: Your stop_words may be inconsistent with your preprocessing. Tokenizing the stop words generated tokens ['zz'] not in stop_words.
warnings.warn(
classification report
precision recall f1-score support
-1 0.63 0.57 0.60 519
0 0.51 0.46 0.48 489
1 0.72 0.84 0.78 629
accuracy 0.64 1637
macro avg 0.62 0.62 0.62 1637
weighted avg 0.63 0.64 0.63 1637
confusion matrix
[[297 139 83]
[147 223 119]
[ 27 75 527]]