电商评论情感分类-基于CNN-Keras

6 篇文章 0 订阅
3 篇文章 0 订阅

1.导包

import re
import jieba
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# from keras.preprocessing.text import Tokenizer
# from keras.preprocessing.sequence import pad_sequences
# from keras.models import Sequential
# from keras.layers import *
# from keras.utils.np_utils import to_categorical
# from keras.callbacks import EarlyStopping

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import *
from tensorflow.python.keras.utils.np_utils import to_categorical
from tensorflow.keras.callbacks import EarlyStopping

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score,classification_report

import warnings
warnings.filterwarnings('ignore')
2023-08-16 09:28:02.684741: I tensorflow/core/util/port.cc:110] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2023-08-16 09:28:02.686545: I tensorflow/tsl/cuda/cudart_stub.cc:28] Could not find cuda drivers on your machine, GPU will not be used.
2023-08-16 09:28:02.724128: I tensorflow/tsl/cuda/cudart_stub.cc:28] Could not find cuda drivers on your machine, GPU will not be used.
2023-08-16 09:28:02.725814: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2023-08-16 09:28:03.400648: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT

2.数据读取

#读取数据
data = pd.read_csv('./data/dataset.csv')
#数据预览
data.head()
text\tlabel
0酒店设计有特色,房间很舒服亦很美,位置于南门很方便出入,而且又有得免费上网。前台服务员不错,...
1地理位置不错,闹中取静。房间比较干净,布局合理。但是隔音效果太差了,有住简易客栈的感觉。临水...
2不错,下次还考虑入住。交通也方便,在餐厅吃的也不错。\t正向
3地理位置比较便捷,逛街、旅游、办事都比较方便。老的宾馆新装修改的,房间内的设施简洁、干净,但...
4因为只住了一晚,所以没什么感觉,差不多四星吧。大堂的地砖很漂亮。房间小了一点。\t正向
data.iloc[0,0]
'酒店设计有特色,房间很舒服亦很美,位置于南门很方便出入,而且又有得免费上网。前台服务员不错,唯退房时出了点问题,令大打折扣。事缘我们有饮用酒店雪柜内的汽水,但临退房前已经买回一样的饮品放进去,但酒店说汽水的包装不一样,所以必须收我们钱,最终由查房、收钱、拿回我们的汽水花了我二十分钟,刚刚我又要赶车,很气愤!我们一共住了三天,花了千多元,那几元都要和我们收足,很讨厌!\t正向'

3.数据预处理

# 分割text和label
data['text'], data['label'] = zip(*data['text\tlabel'].str.split('\t'))
del data['text\tlabel']
data.head()
textlabel
0酒店设计有特色,房间很舒服亦很美,位置于南门很方便出入,而且又有得免费上网。前台服务员不错,...正向
1地理位置不错,闹中取静。房间比较干净,布局合理。但是隔音效果太差了,有住简易客栈的感觉。临水...正向
2不错,下次还考虑入住。交通也方便,在餐厅吃的也不错。正向
3地理位置比较便捷,逛街、旅游、办事都比较方便。老的宾馆新装修改的,房间内的设施简洁、干净,但...正向
4因为只住了一晚,所以没什么感觉,差不多四星吧。大堂的地砖很漂亮。房间小了一点。正向
# 标签数值化
cate = pd.Categorical(data['label'])
data['label'] = cate.codes
data.head()
textlabel
0酒店设计有特色,房间很舒服亦很美,位置于南门很方便出入,而且又有得免费上网。前台服务员不错,...0
1地理位置不错,闹中取静。房间比较干净,布局合理。但是隔音效果太差了,有住简易客栈的感觉。临水...0
2不错,下次还考虑入住。交通也方便,在餐厅吃的也不错。0
3地理位置比较便捷,逛街、旅游、办事都比较方便。老的宾馆新装修改的,房间内的设施简洁、干净,但...0
4因为只住了一晚,所以没什么感觉,差不多四星吧。大堂的地砖很漂亮。房间小了一点。0
# from sklearn import preprocessing
# preprocessing.LabelEncoder().fit_transform(data['label'])
array([0, 0, 0, ..., 1, 1, 1])
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000 entries, 0 to 9999
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   text    10000 non-null  object
 1   label   10000 non-null  int8  
dtypes: int8(1), object(1)
memory usage: 88.0+ KB
data.dtypes
text     object
label      int8
dtype: object
# 缺失值检查
data.isna().sum()
text     0
label    0
dtype: int64
# 重复值检查
data.duplicated().sum()
0
#标签分布
data['label'].value_counts()
0    5000
1    5000
Name: label, dtype: int64
# 经检查,数据集中无缺失值与重复值,且标签分布平均,无不平衡情况
#定义删除除字母,数字,汉字以外的所有符号的函数
def remove_pun(line):
    rule = re.compile(u"[^a-zA-Z0-9\u4E00-\u9FA5]")
    line = rule.sub('',line)
    return line

#读取停用词表
stopwords = [line.strip() for line in open('./data/中文停用词库.txt', 'r', encoding='utf-8').readlines()] 
#字符清理
data['clean_text'] = data['text'].map(lambda x:remove_pun(x))
#结合停用词表进行分词
data['cut_text'] = data['clean_text'].apply(lambda x: " ".join(w for w in jieba.lcut(x) if w not in stopwords))
Building prefix dict from the default dictionary ...
Dumping model to file cache /tmp/jieba.cache
Loading model cost 0.536 seconds.
Prefix dict has been built successfully.
data.head()
textlabelclean_textcut_text
0酒店设计有特色,房间很舒服亦很美,位置于南门很方便出入,而且又有得免费上网。前台服务员不错,...0酒店设计有特色房间很舒服亦很美位置于南门很方便出入而且又有得免费上网前台服务员不错唯退房时出...酒店设计 特色 房间 舒服 美 位置 南门 出入 免费 上网 前台 服务员 不错 唯 退房 ...
1地理位置不错,闹中取静。房间比较干净,布局合理。但是隔音效果太差了,有住简易客栈的感觉。临水...0地理位置不错闹中取静房间比较干净布局合理但是隔音效果太差了有住简易客栈的感觉临水的房间风景不...地理位置 不错 闹中取静 房间 干净 布局合理 隔音 效果 太差 住 简易 客栈 感觉 临水...
2不错,下次还考虑入住。交通也方便,在餐厅吃的也不错。0不错下次还考虑入住交通也方便在餐厅吃的也不错不错 下次 入住 交通 餐厅 吃 不错
3地理位置比较便捷,逛街、旅游、办事都比较方便。老的宾馆新装修改的,房间内的设施简洁、干净,但...0地理位置比较便捷逛街旅游办事都比较方便老的宾馆新装修改的房间内的设施简洁干净但宾馆整体建筑设...地理位置 便捷 逛街 旅游 办事 宾馆 新装 修改 房间内 设施 简洁 干净 宾馆 整体 建...
4因为只住了一晚,所以没什么感觉,差不多四星吧。大堂的地砖很漂亮。房间小了一点。0因为只住了一晚所以没什么感觉差不多四星吧大堂的地砖很漂亮房间小了一点只住 一晚 没什么 感觉 四星 大堂 地砖 很漂亮 房间 一点

4.建模数据预处理

#查看cut_text的长度分布
import seaborn as sns

len_list = data['cut_text'].map(lambda x:len(x)).tolist()
sns.distplot(len_list)
plt.show()

图1

# 通过上图,较多的cut_text的文本长度集中在0-250之间,绝大多数cut_text的文本长度集中在0-500
# 设置最频繁使用的50000个词
MAX_USED_WORDS = 50000
# 每条cut_text最大的长度
MAX_SEQUENCE_LENGTH = 500
# 设置Embeddingceng层的维度
EMBEDDING_DIM = 64

#对文本进行token
tokenizer = Tokenizer(num_words=MAX_USED_WORDS, filters='!"#$%&()*+,-./:;<=>?@[\]^_`{|}~')
#从数据集中出现的词汇构建索引词汇表
tokenizer.fit_on_texts(data['cut_text'].values) 
#将句子转换成表示词汇的数字列表
X = tokenizer.texts_to_sequences(data['cut_text'].values)
#填充X,让X的各个列的长度统一,前文所提到绝大多数cut_text文本长度集中在500以内,故此处将maxlen设置为500
X = pad_sequences(X, maxlen=MAX_SEQUENCE_LENGTH)
#多类标签的onehot展开
Y = pd.get_dummies(data['label']).values
X
array([[    0,     0,     0, ...,  9526, 13914,  3007],
       [    0,     0,     0, ...,  3243,  3815,    22],
       [    0,     0,     0, ...,    42,    37,     3],
       ...,
       [    0,     0,     0, ...,    46,   101,  4001],
       [    0,     0,     0, ...,    58,   104,    54],
       [    0,     0,     0, ...,   137,   582,    29]], dtype=int32)
Y
array([[1, 0],
       [1, 0],
       [1, 0],
       ...,
       [0, 1],
       [0, 1],
       [0, 1]], dtype=uint8)

5.数据集划分

#拆分训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.10, random_state=42)
print(X_train.shape, Y_train.shape)
print(X_test.shape, Y_test.shape)
(9000, 500) (9000, 2)
(1000, 500) (1000, 2)

6.模型构建

model = Sequential()
model.add(Embedding(MAX_USED_WORDS, EMBEDDING_DIM, input_length=X.shape[1]))  #输入层
model.add(Conv1D(64, 3, padding='same', activation='relu'))                   #卷积层
model.add(MaxPool1D(32, padding='same'))                                      #池化层
model.add(Flatten())                                                          #拉直化
model.add(Dense(16, activation='relu'))                                       #全连接层
model.add(Dropout(0.2))                                                       #防止过拟合
model.add(Dense(units=2, activation='softmax'))                               #输出层
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])  ##模型配置(损失函数、优化器、评估标准)
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding (Embedding)       (None, 500, 64)           3200000   
                                                                 
 conv1d (Conv1D)             (None, 500, 64)           12352     
                                                                 
 max_pooling1d (MaxPooling1  (None, 16, 64)            0         
 D)                                                              
                                                                 
 flatten (Flatten)           (None, 1024)              0         
                                                                 
 dense (Dense)               (None, 16)                16400     
                                                                 
 dropout (Dropout)           (None, 16)                0         
                                                                 
 dense_1 (Dense)             (None, 2)                 34        
                                                                 
=================================================================
Total params: 3228786 (12.32 MB)
Trainable params: 3228786 (12.32 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________

7.模型训练

epochs = 5    #训练周期
batch_size = 256 #批大小
history = model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size, validation_split=0.1,
                    callbacks=[EarlyStopping(monitor='val_loss', patience=3, min_delta=0.0001)])
Epoch 1/5
32/32 [==============================] - 2s 39ms/step - loss: 0.6827 - accuracy: 0.5540 - val_loss: 0.6567 - val_accuracy: 0.7133
Epoch 2/5
32/32 [==============================] - 1s 37ms/step - loss: 0.5410 - accuracy: 0.7985 - val_loss: 0.4150 - val_accuracy: 0.8344
Epoch 3/5
32/32 [==============================] - 1s 34ms/step - loss: 0.3262 - accuracy: 0.8753 - val_loss: 0.3058 - val_accuracy: 0.8700
Epoch 4/5
32/32 [==============================] - 1s 36ms/step - loss: 0.1923 - accuracy: 0.9336 - val_loss: 0.2838 - val_accuracy: 0.8878
Epoch 5/5
32/32 [==============================] - 1s 34ms/step - loss: 0.1200 - accuracy: 0.9646 - val_loss: 0.2949 - val_accuracy: 0.8867

8.模型评估

#预测测试集
y_pred = model.predict(X_test)
y_pred_pos = y_pred.argmin(axis=1)
32/32 [==============================] - 0s 4ms/step
#正向的测试集label
Y_test_pos = Y_test.argmin(axis=1)
#使用准确率(acc)进行评估
accuracy_score(y_pred_pos, Y_test_pos)
0.889
#打印分类报告
print(classification_report(y_pred_pos, Y_test_pos))
              precision    recall  f1-score   support

           0       0.87      0.90      0.89       491
           1       0.90      0.87      0.89       509

    accuracy                           0.89      1000
   macro avg       0.89      0.89      0.89      1000
weighted avg       0.89      0.89      0.89      1000
#查看5个训练周期的Loss值变化趋势
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(range(epochs), history.history['loss'], label='Training Loss')
plt.plot(range(epochs), history.history['val_loss'], label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

图2

# 由上图所示,Training Loss在后期仍不断下降,而Validation Loss趋于平稳,
# 出现这种情况大概率是出现了过拟合现象,后期工作也将在降低过拟合方面进行研究

9.模型保存

#保存
model.save('./data/电商评论情感分析数据集/model/model.h5')
#读取
# from keras.models import load_model
from tensorflow.keras.models import load_model
model_loaded = load_model('./data/电商评论情感分析数据集/model/model.h5')
model_loaded
<keras.src.engine.sequential.Sequential at 0x7fd6ec6ee310>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值