【人工智能项目】- 卷积神经网络实现游客评价情绪鉴别
本次主要任务是对游客发表的评价内容的情绪进行鉴别,积极还是消极。
读取数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
plt.rcParams['font.family'] = "SimHei"
plt.rcParams['axes.unicode_minus'] = "False"
plt.rcParams['font.size'] = 15
data = pd.read_csv('data.csv')
data
Unnamed: 0 | content | sentiment | |
---|---|---|---|
0 | 0 | 第一天,去了土楼,路上车程比较长,我自己带了个小枕头,让脖子舒服点,还有建议大家穿比较合脚的... | 正 |
1 | 1 | 导游王艺芳带的很好,我们很慢,总是让他操心,但他很负责。耐心详细介绍带领😊经历了好多风景… ... | 正 |
2 | 2 | 整个行程轻松自由。第一天在中山路步行街,有很多厦门小吃和海鲜店,土笋冻、 海蛎煎、五香卷,很... | 正 |
3 | 3 | 第一天早点到这 可以骑小黄车或者坐地铁去中山路玩一圈(奢侈品和各种小吃)。第二天去土楼。第二... | 正 |
4 | 4 | 这个冬天,我路过了厦门的满树繁花、万家灯火和四海长风。厦门,是一座骨子里透着浪漫与精致的城市... | 正 |
... | ... | ... | ... |
3995 | 3995 | 同程真是越来越**,说好的看到海,结果根本看不到,不如自己去定 | 负 |
3996 | 3996 | 硬件还行,软件**。寄个快递礼品盒全压坏!硬塞的能再恶心点?!!!真**** | 负 |
3997 | 3997 | 非常差的一次服务,因为和产品经理熟了所以愿意相信他。对他的服务非常满意。对此次三亚地接导游非... | 负 |
3998 | 3998 | 不满意 | 负 |
3999 | 3999 | 导游蔡文佳非常不错,很热情,必须赞!最不满意就是酒店,我住的是阳光大酒店,说是五星级,进去一... | 负 |
4000 rows × 3 columns
数据EDA
# 查看数据的NAN情况
#data.info()
data.isnull().sum()
Unnamed: 0 0
content 0
sentiment 0
dtype: int64
# 去除多余索引列
data = data.drop(labels='Unnamed: 0',axis=1)
data
content | sentiment | |
---|---|---|
0 | 第一天,去了土楼,路上车程比较长,我自己带了个小枕头,让脖子舒服点,还有建议大家穿比较合脚的... | 正 |
1 | 导游王艺芳带的很好,我们很慢,总是让他操心,但他很负责。耐心详细介绍带领😊经历了好多风景… ... | 正 |
2 | 整个行程轻松自由。第一天在中山路步行街,有很多厦门小吃和海鲜店,土笋冻、 海蛎煎、五香卷,很... | 正 |
3 | 第一天早点到这 可以骑小黄车或者坐地铁去中山路玩一圈(奢侈品和各种小吃)。第二天去土楼。第二... | 正 |
4 | 这个冬天,我路过了厦门的满树繁花、万家灯火和四海长风。厦门,是一座骨子里透着浪漫与精致的城市... | 正 |
... | ... | ... |
3995 | 同程真是越来越**,说好的看到海,结果根本看不到,不如自己去定 | 负 |
3996 | 硬件还行,软件**。寄个快递礼品盒全压坏!硬塞的能再恶心点?!!!真**** | 负 |
3997 | 非常差的一次服务,因为和产品经理熟了所以愿意相信他。对他的服务非常满意。对此次三亚地接导游非... | 负 |
3998 | 不满意 | 负 |
3999 | 导游蔡文佳非常不错,很热情,必须赞!最不满意就是酒店,我住的是阳光大酒店,说是五星级,进去一... | 负 |
4000 rows × 2 columns
数据清洗
data.drop_duplicates(inplace=True)#查找重复值并删除
print(data.duplicated().sum())
0
import re#文本内容清洗
import string
def remove_punctuation(s):
# print(s)
delEStr = string.punctuation + ''+ string.digits + string.ascii_letters# 标点符号、数字、字母
identify = str.maketrans(","," ",delEStr)# 这里感觉并没有用到映射的功能,而是着重使用到了 删除指定字符串 的功能
s = s.translate(identify)
# print(s,type(s))
s = re.sub(r"[!\"#$%&'()*+,-./:;<=>?@[\\\]¯~_`{|}~·—!,。?、¥…():;【】《》‘’“”\s]+",'',s) # 正则匹配并替换
return s
data['content'] = data['content'].apply(remove_punctuation)
data.sample(5)
#data["content"].count()
content | sentiment | |
---|---|---|
2249 | 第一次自由行出发前还很忐忑怕安排的不好结果客服的服务很到位有什么问题问她立马就给我电话回复过... | 正 |
3760 | 导游凶神恶煞一脸恶相吐个槽要被他两小时 | 负 |
773 | 这次出行总体还不错出发前一天我的专属客服帮忙定的行程很专业人也很好由于是不同旅行套餐的人组团... | 正 |
3142 | 去的时候飞机整整延误了三个多小时好心情都给破坏了还好泛太平洋酒店还不错第二天孩子在他们的露天... | 中 |
3772 | 服务很差的回程最后一天遇到台风是航空公司先打电话给我的然后打电话给同程时候她们都不知道航班取... | 负 |
# print(string.punctuation)
for i in data.iloc[:,0]:
clean_data = remove_punctuation(i)
print(clean_data)
# break
第一天去了土楼路上车程比较长我自己带了个小枕头让脖子舒服点还有建议大家穿比较合脚的运动鞋因为土楼的路高低不平坑坑洼洼容易脚疼一定要穿上舒服的鞋子这点很重要鼓浪屿的路虽然不会像土楼那样但是上坡下坡很多走的比较辛苦所以不需要的东西不要带轻松上岛龙头路的美食很多但是卫生很重要一定要选择干净卫生的店海鲜也比较多价格也不贵喜欢吃海鲜的有口福了厦大开放时间有规定不过我们的导游林珊珊安排的很合理在厦大开放前先带我们去了南普陀参观讲解的很详细听的津津有味然后让我们自由活动去参观厦前的提醒服务非常被动滞后
通过同程旅游纪天宇订的厦门日自由行第一次通过同程跑远地方旅游感觉各方面都是非常满意唯一一点遗憾就是春秋航空去厦门那天晚点个小时还好意外险有买不知道有没有理赔酒店住的泛太平洋星酒店含日双人早饭酒店服务相当满意行程方面第一天因为飞机延误直接入住酒店睡觉第二天满满的行程都是坐的公交车先中山路步行街看看厦门特产东西有些贵–厦门大学就
import jieba
def cut_word(text):
return jieba.cut(text)
data['content']=data['content'].apply(cut_word)
data.sample(5)
content | sentiment | |
---|---|---|
2912 | <generator object Tokenizer.cut at 0x000001F67... | 正 |
2219 | <generator object Tokenizer.cut at 0x000001F67... | 正 |
2640 | <generator object Tokenizer.cut at 0x000001F67... | 正 |
37 | <generator object Tokenizer.cut at 0x000001F67... | 正 |
3593 | <generator object Tokenizer.cut at 0x000001F67... | 负 |
def get_stopword():
s = set()
with open('stop_words.txt','r',encoding='utf-8') as f:
for line in f:
s.add(line.strip())
return s
def remove_stopword(words):
return [word for word in words if word not in stopword]
stopword = get_stopword()
data['content'] = data['content'].apply(remove_stopword)
data.sample(5)
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\LVCHAO~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.896 seconds.
Prefix dict has been built successfully.
content | sentiment | |
---|---|---|
2677 | [这次, 预定, 自由, 行, 产品, 全程, 入住, 厦门, 泛太平洋, 大酒店, 地处,... | 正 |
2248 | [第二次, 同程, 出游, 服务, 一如既往, 好, 此次, 厦门, 选择, 自由, 行在,... | 正 |
3264 | [这次, 买, 机票, 过去, 住宿, 餐饮, 认为, 厦门, 不好玩, 接送, 服务] | 中 |
3436 | [厦门, 旅游, 热, 走路, 两分钟, 流汗, 两, 小时, 捕蟹, 只, 半小时, 受不... | 中 |
2482 | [住, 好, 唯一, 航班, 太, 不靠, 谱, 加钱, 机票, 划不来, 下午, 飞机, ... | 正 |
t = data['sentiment'].value_counts()
print(t)
t.plot(kind="bar")
正 2852
负 507
中 455
Name: sentiment, dtype: int64
data["content"]
0 [第一天, 土楼, 路上, 车程, 比较, 长, 小, 枕头, 脖子, 舒服, 点, 建议,...
1 [导游, 王艺芳, 好, 慢, 总是, 操心, 负责, 耐心, 详细, 介绍, 带领, 😊,...
2 [整个, 行程, 轻松自由, 第一天, 中山路, 步行街, 厦门, 小吃, 海鲜店, 土笋,...
3 [第一天, 早点, 骑小, 黄车, 坐地铁, 中山路, 玩, 一圈, 奢侈品, 小吃, 第二...
4 [冬天, 路过, 厦门, 满树, 繁花, 万家灯火, 四海, 长风, 厦门, 一座, 骨子里...
...
3994 [这次, 旅程, 真的, 太, 失望, 要求, 大床, 房, 变成, 标准间, 导游, 不负...
3995 [同程, 真是, 越来越, 说好, 看到, 海, 根本, 看不到, 去定]
3996 [硬件, 行, 软件, 寄个, 快递, 礼品盒, 全, 压坏, 硬塞, 再, 恶心, 点真]
3997 [差, 一次, 服务, 产品, 经理, 熟, 愿意, 相信, 服务, 满意, 此次, 三亚,...
3999 [导游, 蔡文佳, 不错, 热情, 赞, 最, 满意, 酒店, 住, 阳光, 大酒店, 说,...
Name: content, Length: 3814, dtype: object
from itertools import chain
from collections import Counter
li_2d = data['content'].tolist()
li_1d = list(chain.from_iterable(li_2d))
#二位列表转换为一维列表
print(f'总词汇量:{len(li_1d)}')
c = Counter(li_1d)
print(f'不重复词汇量:{len(c)}')
common = c.most_common(15)
print(common)
总词汇量:172537
不重复词汇量:16695
[('酒店', 4271), ('好', 2843), ('厦门', 2344), ('不错', 2135), ('同程', 1895), ('鼓浪屿', 1656), ('导游', 1649), ('服务', 1564), ('行程', 1446), ('说', 1322), ('安排', 1161), ('吃', 961), ('住', 909), ('这次', 889), ('比较', 880)]
d = dict(common)
plt.figure(figsize=(15,5))
plt.bar(d.keys(),d.values())
total = len(li_1d)
percentage = [v*100/total for v in d.values()]
plt.figure(figsize=(15,5))
plt.bar(d.keys(),percentage)
from wordcloud import WordCloud#词云
from PIL import Image
#wc = WordCloud(font_path=r'C:\Users\dell\Desktop\旅游\font.ttf',width=600,height=400)
#bg = WordCloud(mask = plt.imread(r'C:\Users\dell\Desktop\旅游\bgf.jpg'))
img = Image.open(r'123.jpg') # 打开背景图片
img_array = np.array(img) # 将图片转换为数组
wc = WordCloud(
background_color="black", # 将背景颜色设置为黑色,也可根据个人喜好更改
mask=img_array,#背景图
max_words=100,
font_path = r"font.ttf", #字体设置
width=1000).generate_from_frequencies(c)
plt.imshow(wc)
plt.axis('off')
wc.to_file("pic.png")
#构建训练与测试集
def join(text_list):
return " ".join(text_list)
data["content"] = data["content"].apply(join)
data.sample(5)
content | sentiment | |
---|---|---|
468 | 总体 满分 导游 人 好 | 正 |
490 | 林珊珊 美女 导游 | 正 |
1466 | 厦门 自由 行 安排 不错 鼓浪屿 风景 好 旅游社 服务 下次 出行 一定 找 贵 | 正 |
3792 | 飞机 晚点 小时 酒店 服务 不错 酒店 硬件 不行 酒店 私有 海滩 公共 沙滩 | 负 |
125 | 好 尤其 最后 一天 导游 小张 张立 导游 细心 专业 | 正 |
#标签列转换为离散值
data["sentiment"] = data["sentiment"].map({"正":0,"中":1,"负":2})
data["sentiment"].value_counts()
0 2852
2 507
1 455
Name: sentiment, dtype: int64
data
content | sentiment | |
---|---|---|
0 | 第一天 土楼 路上 车程 比较 长 小 枕头 脖子 舒服 点 建议 穿 比较 合脚 运动鞋 ... | 0 |
1 | 导游 王艺芳 好 慢 总是 操心 负责 耐心 详细 介绍 带领 😊 经历 好多 风景 海边 ... | 0 |
2 | 整个 行程 轻松自由 第一天 中山路 步行街 厦门 小吃 海鲜店 土笋 冻 海蛎 煎 五香 ... | 0 |
3 | 第一天 早点 骑小 黄车 坐地铁 中山路 玩 一圈 奢侈品 小吃 第二天 土楼 第二天 庄常... | 0 |
4 | 冬天 路过 厦门 满树 繁花 万家灯火 四海 长风 厦门 一座 骨子里 透着 浪漫 精致 城... | 0 |
... | ... | ... |
3994 | 这次 旅程 真的 太 失望 要求 大床 房 变成 标准间 导游 不负责任 尽到 应 义务 导... | 2 |
3995 | 同程 真是 越来越 说好 看到 海 根本 看不到 去定 | 2 |
3996 | 硬件 行 软件 寄个 快递 礼品盒 全 压坏 硬塞 再 恶心 点真 | 2 |
3997 | 差 一次 服务 产品 经理 熟 愿意 相信 服务 满意 此次 三亚 地接 导游 一味 说 金... | 2 |
3999 | 导游 蔡文佳 不错 热情 赞 最 满意 酒店 住 阳光 大酒店 说 五星级 一看 旅店 舒服... | 2 |
3814 rows × 2 columns
对数据打乱顺序并保存好训练和测试数据
# 打乱顺序
data = data.sample(frac=1).reset_index(drop=True)
data
content | sentiment | |
---|---|---|
0 | 年前 老公 夏威夷 选择 同程 这次 国内游 满意 厦门 当地 导游 比较 热情 接机 准时... | 0 |
1 | 想 如实 写出 感受 第一天 自由 行 晚上 六点 第二天 云水 谣 领队 郑 导游 联系 ... | 2 |
2 | 厦门 这次 游玩 满意 游玩 开心 安排 星 酒店 不错 住 舒适 出门 公交 出行 方便 ... | 0 |
3 | 这次 自由 行 比较满意 住 五星级 酒店 泛太平洋 地理位置 不错 市区 这次 坐 动车 ... | 0 |
4 | 买 经停 飞机 商量 信誉 大打折扣 | 2 |
... | ... | ... |
3809 | 出游 安排 合理 导游 服务 棒棒 哒 | 0 |
3810 | 鼓浪屿 导游 服务 好 | 0 |
3811 | 玩 开心 好评 | 0 |
3812 | 回来 航班 延误 小时 机上 空乘 个个 帅哥 哈哈哈哈 第二次 厦门 总体 不错 八市 海... | 0 |
3813 | 房间 大门口 修 地铁 出行 方便 | 1 |
3814 rows × 2 columns
data.shape[0]*0.8
3051.2000000000003
train_data = data[:3051]
test_data = data[3051:]
train_data
content | sentiment | |
---|---|---|
0 | 年前 老公 夏威夷 选择 同程 这次 国内游 满意 厦门 当地 导游 比较 热情 接机 准时... | 0 |
1 | 想 如实 写出 感受 第一天 自由 行 晚上 六点 第二天 云水 谣 领队 郑 导游 联系 ... | 2 |
2 | 厦门 这次 游玩 满意 游玩 开心 安排 星 酒店 不错 住 舒适 出门 公交 出行 方便 ... | 0 |
3 | 这次 自由 行 比较满意 住 五星级 酒店 泛太平洋 地理位置 不错 市区 这次 坐 动车 ... | 0 |
4 | 买 经停 飞机 商量 信誉 大打折扣 | 2 |
... | ... | ... |
3046 | 四天 行程 满满的 机场 泛太平洋 酒店 打个 滴滴 方便 二十多块 就够 酒店 服务 好 ... | 0 |
3047 | 这次 厦门 之旅 同伴 鼓浪屿 日光岩 点 意外 得到 同程 客服 人员 积极 配合 特别 ... | 0 |
3048 | 满意 一次 旅行 同程 服务 好 每次 提前 提醒 告知 酒店 不错 服务 好 设施 一流 | 0 |
3049 | 这次 厦门 旅让 感到 满意 何导 何仙姑 服务 贴心 周到 满足 要求 接送 机 小张 师... | 0 |
3050 | 酒店 不错 交通 方便 建议 中山 街 不去 厝 垵 鼓浪屿 必去 厦大 芙蓉 隧道 比较 ... | 0 |
3051 rows × 2 columns
test_data
content | sentiment | |
---|---|---|
3051 | 想 朋友 厦门 玩 先后 换 三四次 线路 最后 选 自由 行机 加酒 套餐 整体 不错 酒... | 0 |
3052 | 玩 不错 | 0 |
3053 | 同程 老客户 这次 帮贵社 推荐 位 朋友 想 感受 下夏门 金 钻 会议 之后 变化 顺便... | 1 |
3054 | 玩过 之后 觉得 鼓浪屿 住 一晚 这样的话 鼓浪屿 玩会 担心 晚上 再 返回 市里 酒店... | 0 |
3055 | 总体 这次 旅行 休闲 娱乐 找 愉快 天 行程 每天 投诉 电话 关键 投诉 解决问题 速... | 2 |
... | ... | ... |
3809 | 出游 安排 合理 导游 服务 棒棒 哒 | 0 |
3810 | 鼓浪屿 导游 服务 好 | 0 |
3811 | 玩 开心 好评 | 0 |
3812 | 回来 航班 延误 小时 机上 空乘 个个 帅哥 哈哈哈哈 第二次 厦门 总体 不错 八市 海... | 0 |
3813 | 房间 大门口 修 地铁 出行 方便 | 1 |
763 rows × 2 columns
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3051 entries, 0 to 3050
Data columns (total 2 columns):
content 3051 non-null object
sentiment 3051 non-null int64
dtypes: int64(1), object(1)
memory usage: 47.8+ KB
test_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 763 entries, 3051 to 3813
Data columns (total 2 columns):
content 763 non-null object
sentiment 763 non-null int64
dtypes: int64(1), object(1)
memory usage: 12.1+ KB
x_train = train_data["content"]
y_train = train_data["sentiment"]
x_test = test_data["content"]
y_test = test_data["sentiment"]
# from sklearn.model_selection import train_test_split
# x_train,x_test,y_train,y_test = train_test_split(data["content"],data["sentiment"],test_size=0.2,random_state=10)
# print("训练集样本数:",y_train.shape[0],"测试集样本数:",y_test.shape[0])
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer()
x_train_tran = vec.fit_transform(x_train)
x_test_tran = vec.transform(x_test)
display(x_train_tran,x_test_tran)
#c = CountVectorizer()
#bag = c.fit_transform(x_train)
#print(c.get_feature_names()) # 注意,在中文中也是不会对单个的汉字进行特征值化,即单个字符被省去了
#print(bag.toarray())
#print(c.vocabulary_)
<3051x13568 sparse matrix of type '<class 'numpy.float64'>'
with 96220 stored elements in Compressed Sparse Row format>
<763x13568 sparse matrix of type '<class 'numpy.float64'>'
with 21996 stored elements in Compressed Sparse Row format>
from sklearn.feature_selection import f_classif
f_classif(x_train_tran,y_train)
(array([0.1713417 , 3.18935482, 0.1713417 , ..., 0.1713417 , 0.1713417 ,
0.1713417 ]),
array([0.84254174, 0.04133597, 0.84254174, ..., 0.84254174, 0.84254174,
0.84254174]))
#df=pd.DataFrame({'content':data['content'],'label':data['sentiment']})
#print(df.tail())
#df.dtypes
from sklearn.feature_selection import SelectKBest
x_train_tran = x_train_tran.astype(np.float64)
x_test_tran = x_test_tran.astype(np.float64)
selector = SelectKBest(f_classif,k=min(1000,x_train_tran.shape[1]))
selector.fit(x_train_tran,y_train)
x_train_tran = selector.transform(x_train_tran)
x_test_tran = selector.transform(x_test_tran)
print(x_train_tran.shape,x_test_tran.shape)
(3051, 1000) (763, 1000)
模型
#KNN
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score, recall_score, f1_score
#param = {'n_neighbors':[5,7],
# 'weights':['uniform','distance']}
#gs = GridSearchCV(estimator=KNeighborsClassifier(),param_grid=param,
# cv=2,scoring="f1",n_jobs=-1,verbose=10)
#gs.fit(x_train_tran,y_train)
#print(gs.best_params_)
#y_hat = gs.best_estimator_.predict(x_test_tran)
#print(classification_report(y_test,y_hat))
classifier = KNeighborsClassifier()
classifier.fit(x_train_tran, y_train)
# print(classifier.score(x_test_tran, y_test))
y_pred = classifier.predict(x_test_tran)
p = precision_score(y_test, y_pred, average=None)
r = recall_score(y_test, y_pred, average=None)
f1score = f1_score(y_test, y_pred, average=None)
score = accuracy_score(y_test,y_pred)
print("precision:",p)
print("recall:",r)
print("fscore:",f1score)
print("accuracy:",score)
precision: [0.7661823 0.2 1. ]
recall: [1. 0.01111111 0.01075269]
fscore: [0.86761406 0.02105263 0.0212766 ]
accuracy: 0.762778505897772
from sklearn.ensemble import RandomForestClassifier
classifier = RandomForestClassifier()
classifier.fit(x_train_tran, y_train)
# print(classifier.score(x_test_tran, y_test))
y_pred = classifier.predict(x_test_tran)
p = precision_score(y_test, y_pred, average=None)
r = recall_score(y_test, y_pred, average=None)
f1score = f1_score(y_test, y_pred, average=None)
score = accuracy_score(y_test,y_pred)
print("precision:",p)
print("recall:",r)
print("fscore:",f1score)
print("accuracy:",score)
precision: [0.81883024 0.125 0.7962963 ]
recall: [0.98965517 0.01111111 0.46236559]
fscore: [0.89617486 0.02040816 0.58503401]
accuracy: 0.8099606815203145
#决策树
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(x_train_tran,y_train)
# print(classifier.score(x_test_tran, y_test))
y_pred = clf.predict(x_test_tran)
p = precision_score(y_test, y_pred, average=None)
r = recall_score(y_test, y_pred, average=None)
f1score = f1_score(y_test, y_pred, average=None)
score = accuracy_score(y_test,y_pred)
print("precision:",p)
print("recall:",r)
print("fscore:",f1score)
print("accuracy:",score)
precision: [0.84271523 0.18055556 0.54022989]
recall: [0.87758621 0.14444444 0.50537634]
fscore: [0.8597973 0.16049383 0.52222222]
accuracy: 0.745740498034076
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
import time
import datetime
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.utils import class_weight as cw
from keras import Sequential
from keras.models import Model
from keras.layers import LSTM,Activation,Dense,Dropout,Input,Embedding,BatchNormalization,Add,concatenate,Flatten
from keras.layers import Conv1D,Conv2D,Convolution1D,MaxPool1D,SeparableConv1D,SpatialDropout1D,GlobalAvgPool1D,GlobalMaxPool1D,GlobalMaxPooling1D
from keras.layers.pooling import _GlobalPooling1D
from keras.layers import MaxPooling2D,GlobalMaxPooling2D,GlobalAveragePooling2D
from keras.models import load_model
from keras.optimizers import RMSprop,Adam
from keras.preprocessing.text import Tokenizer
from keras.preprocessing import sequence
from keras.utils import to_categorical
from keras.callbacks import EarlyStopping
from keras.callbacks import ModelCheckpoint
from keras.callbacks import ReduceLROnPlateau
%matplotlib inline
import warnings
warnings.filterwarnings("ignore")
Using TensorFlow backend.
x_train = train_data["content"]
y_train = train_data["sentiment"]
x_test = test_data["content"]
y_test = test_data["sentiment"]
x_train
0 年前 老公 夏威夷 选择 同程 这次 国内游 满意 厦门 当地 导游 比较 热情 接机 准时...
1 想 如实 写出 感受 第一天 自由 行 晚上 六点 第二天 云水 谣 领队 郑 导游 联系 ...
2 厦门 这次 游玩 满意 游玩 开心 安排 星 酒店 不错 住 舒适 出门 公交 出行 方便 ...
3 这次 自由 行 比较满意 住 五星级 酒店 泛太平洋 地理位置 不错 市区 这次 坐 动车 ...
4 买 经停 飞机 商量 信誉 大打折扣
...
3046 四天 行程 满满的 机场 泛太平洋 酒店 打个 滴滴 方便 二十多块 就够 酒店 服务 好 ...
3047 这次 厦门 之旅 同伴 鼓浪屿 日光岩 点 意外 得到 同程 客服 人员 积极 配合 特别 ...
3048 满意 一次 旅行 同程 服务 好 每次 提前 提醒 告知 酒店 不错 服务 好 设施 一流
3049 这次 厦门 旅让 感到 满意 何导 何仙姑 服务 贴心 周到 满足 要求 接送 机 小张 师...
3050 酒店 不错 交通 方便 建议 中山 街 不去 厝 垵 鼓浪屿 必去 厦大 芙蓉 隧道 比较 ...
Name: content, Length: 3051, dtype: object
y_train
0 0
1 2
2 0
3 0
4 2
..
3046 0
3047 0
3048 0
3049 0
3050 0
Name: sentiment, Length: 3051, dtype: int64
# 分词器Tokenizer Tokenizer是一个用于向量化文本,或将文本转换为序列(即单词在字典中的下标构成的列表,从1算起)的类
# 类方法
# fit_on_texts(texts) :texts用于训练的文本列表
# texts_to_sequences(texts):texts待转为序列的文本列表 返回值:序列的列表,列表中的每个序列对应于一段输入文本
# 填充序列pad_sequences 将长为nb_smaples的序列转换为(nb_samples,nb_timesteps)2Dnumpy attay.如果提供maxlen,nb_timesteps=maxlen,否则其值为最长序列的长度。
# 其它短于该长度的序列都会在后部填充0以达到该长度。长与nb_timesteps的序列会被阶段,以使其匹配该目标长度。
#max_words = 1000
#max_len = 150
max_words = len(set(" ".join(x_train).split()))
max_len = x_train.apply(lambda x:len(x)).max()
tok = Tokenizer(num_words=max_words)
tok.fit_on_texts(x_train)
sequences = tok.texts_to_sequences(x_train)
sequences_matrix = sequence.pad_sequences(sequences,maxlen=max_len)
# one-hot编码
from keras.utils import to_categorical
y_train = to_categorical(y_train)
y_train
array([[1., 0., 0.],
[0., 0., 1.],
[1., 0., 0.],
...,
[1., 0., 0.],
[1., 0., 0.],
[0., 1., 0.]], dtype=float32)
# 计算各个类别的weights
def get_weight(y):
class_weight_current = cw.compute_class_weight("balanced",np.unique(y),y)
return class_weight_current
class_weight = get_weight(y_train.flatten())
-
sklearn.utils.class_weight 样本均衡
- 当我们的数据,有多个类别,每个类别的数据量有很大差距时,这时需要对每个类别的样本做一次均衡,这样会让每个类别的特征都在一定程度上被模型学习
-
ModelCheckpoint:
- 作用:该回调函数将在每个epoch后保存模型到filepath
- 参数:
- filename:字符串,保存模型的路径,filepath可以是格式化的字符串,里面的
- monitor:需要监视的值,通常为:val_acc或val_loss或acc或loss
- verbose:信息展示模型,0或1。默认为0表示不输出该信息,为1表示输出epoch模型保存信息。
- save_best_only:当设置为Trur时,将只保存在验证集上性能最好的模型
- mode:“auto”,“min”,"max"之一,在save_best_only=True时决定性能最佳模型的评判准则。
- save_weights_only:若设置为True时,则只保存模型权重,否则将保存整个模型(包括模型结构,配置信息等)
- period:CheckPoint之间的间隔的epoch数
-
EarlyStopping:
- 作用:当监测值不再改善时,该回调函数将中止训练
- 参数:
- monitor:需要监视的量,通常为val_acc或val_loss或acc或loss
- patience:当early stop被激活(如发现loss相比上patience个epoch训练没有下降),则经过patience个epoch后停止训练。
- verbose:信息展示模型
- mode:“auto”,“min”,"max"之一,在min模式下,如果检测值停止下降则中止训练。在max模式下,当检测值不再上升则停止训练。
-
ReduceLROnPlateau:
- 作用:当评价指标不再提升时,减少学习率。当学习停滞时,减少2倍或10倍的学习率通常能够获得较好的效果。该回调函数检测指标的情况,如果在patience个epoch中看不到模型性能提升,则减少学习率。
- 参数:
- monitor:被监测的量
- factor:每次减少学习率的因子,学习率将以lr=lr*factor的形式被技术那好
- patience:当patience个epoch过去而模型性能不提升时,学习率减少的动作会被触发
- mode:“auto”,“min”,"max"之一,在min模式下,如果检测值触发学习率减少。在max模式下,当检测值不再上升则触发学习率减少
- epsilon:阈值,用来确定是否进入检测值的“平原区”
- cooldown:学习率减少后,会经过cooldown个epoch才重新进行正常操作
- min_lr:学习率的下限。
print("Setting Callbacks")
checkpoint = ModelCheckpoint("model.h5",
monitor="val_acc",
save_best_only=True,
mode="max")
early_stopping = EarlyStopping(monitor="val_loss",
patience=6,
verbose=1,
restore_best_weights=True,
mode="min")
reduce_lr = ReduceLROnPlateau(monitor="val_loss",
factor=0.6,
patience=2,
verbose=1,
mode="min")
callbacks=[checkpoint,early_stopping,reduce_lr]
Setting Callbacks
# 定义CNN模型
def CNN():
model=Sequential()
model.add(Embedding(max_words,100,input_length=max_len))
model.add(Conv1D(64,3,padding="valid",activation="relu",strides=1))
model.add(Conv1D(128,3,padding="valid",activation="relu",strides=1))
model.add(Conv1D(256,3,padding="valid",activation="relu",strides=1))
model.add(GlobalMaxPooling1D())
model.add(Dense(128,activation="relu"))
model.add(Dropout(0.25))
model.add(Dense(64,activation="relu"))
model.add(Dense(16,activation="relu"))
model.add(Dropout(0.25))
model.add(Dense(3,activation="softmax"))
model.summary()
return model
loss = "categorical_crossentropy"
metrics=["accuracy"]
cnn_model = CNN()
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 1017, 100) 1669500
_________________________________________________________________
conv1d_4 (Conv1D) (None, 1015, 64) 19264
_________________________________________________________________
conv1d_5 (Conv1D) (None, 1013, 128) 24704
_________________________________________________________________
conv1d_6 (Conv1D) (None, 1011, 256) 98560
_________________________________________________________________
global_max_pooling1d_2 (Glob (None, 256) 0
_________________________________________________________________
dense_5 (Dense) (None, 128) 32896
_________________________________________________________________
dropout_2 (Dropout) (None, 128) 0
_________________________________________________________________
dense_6 (Dense) (None, 64) 8256
_________________________________________________________________
dense_7 (Dense) (None, 16) 1040
_________________________________________________________________
dropout_3 (Dropout) (None, 16) 0
_________________________________________________________________
dense_8 (Dense) (None, 3) 51
=================================================================
Total params: 1,854,271
Trainable params: 1,854,271
Non-trainable params: 0
_________________________________________________________________
# CNN模型训练
print("Starting...\n")
print("\n\nCompliling Model...\n")
learning_rate=0.001
optimizer=Adam(learning_rate)
cnn_model.compile(optimizer=optimizer,
loss=loss,
metrics=metrics)
verbose = 1
epochs=100
batch_size=8
validation_split=0.1
print("Trainning Model...\n")
cnn_model = load_model("cnn_model1.h5")
cnn_history=cnn_model.fit(sequences_matrix,
y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
callbacks=callbacks,
validation_split=validation_split,
class_weight=class_weight)
Starting...
Compliling Model...
Trainning Model...
Train on 3432 samples, validate on 382 samples
Epoch 1/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.5437 - accuracy: 0.8033 - val_loss: 0.4385 - val_accuracy: 0.8377
Epoch 2/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.3397 - accuracy: 0.8601 - val_loss: 0.5063 - val_accuracy: 0.8246
Epoch 3/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.2460 - accuracy: 0.9024 - val_loss: 0.6464 - val_accuracy: 0.8115
Epoch 00003: ReduceLROnPlateau reducing learning rate to 0.0003600000170990825.
Epoch 4/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.1551 - accuracy: 0.9464 - val_loss: 0.7178 - val_accuracy: 0.8037
Epoch 5/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.0879 - accuracy: 0.9729 - val_loss: 0.8973 - val_accuracy: 0.7958
Epoch 00005: ReduceLROnPlateau reducing learning rate to 0.00021600000327453016.
Epoch 6/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.0572 - accuracy: 0.9843 - val_loss: 1.1212 - val_accuracy: 0.8089
Epoch 7/100
3432/3432 [==============================] - 8s 2ms/step - loss: 0.0346 - accuracy: 0.9898 - val_loss: 1.3203 - val_accuracy: 0.8141
Restoring model weights from the end of the best epoch
Epoch 00007: ReduceLROnPlateau reducing learning rate to 0.00012960000021848827.
Epoch 00007: early stopping
# 将带预测数据转为序列
predict_sequences = tok.texts_to_sequences(x_test)
predict_sequences_matrix = sequence.pad_sequences(predict_sequences,maxlen=max_len)
from keras.models import load_model
y_pred = cnn_model.predict_classes(predict_sequences_matrix)
p = precision_score(y_test, y_pred, average=None)
r = recall_score(y_test, y_pred, average=None)
f1score = f1_score(y_test, y_pred, average=None)
score = accuracy_score(y_test,y_pred)
print("precision:",p)
print("recall:",r)
print("fscore:",f1score)
print("accuracy:",score)
precision: [0.88135593 0. 0.64912281]
recall: [0.9862069 0. 0.79569892]
fscore: [0.93083808 0. 0.71497585]
accuracy: 0.8466579292267365