网站新闻详细页面设计,一个网站通常包含多个网页,湖南建设人力资源网和报考平台,个人网站建设实验心得电影评论的情感分析 案例背景
很多同学对电影系列的数据都比较喜欢#xff0c;那我就补充一下这个最经典的文本分类数据集#xff0c;电影情感评论分析。用神经网络做。对国外的英文评论文本进行分类#xff0c;看是正面还是负面情感。 数据集介绍
数据集#xff1a;IMDb…电影评论的情感分析 案例背景
很多同学对电影系列的数据都比较喜欢那我就补充一下这个最经典的文本分类数据集电影情感评论分析。用神经网络做。对国外的英文评论文本进行分类看是正面还是负面情感。 数据集介绍
数据集IMDb网站的电影评论数据集.
这个数据集包含评论文本和标签标签表示该评论是“正面的”(positive)还是“负面的”(negative).
数据集包括两个独立文件夹一个是训练数据train, 另一个是测试数据test. 每个文件夹又都有两个子文件夹一个叫做pos, 另一个叫做neg。pos文件夹包含所有正面的评论, 每条评论都是一个单独的文本文件, neg文件夹与之类似。
说实话搞这么多文件夹和这么多txt文件看着麻烦我还是一样自己清洗和整理后直接用excel装起来了如下 两个excel文件一个训练集一个测试集每个里面有25000条评论其中又分为12500条正面和12500条负面是一个很平衡的样本。
需要这个数据的全部代码的同学可以参考电影数据 代码实现
预处理
导入包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams[font.sans-serif] [KaiTi] #指定默认字体 SimHei黑体
plt.rcParams[axes.unicode_minus] False #解决保存图像是负号
读取数据合并展示前五行
df_trainpd.read_csv(train.csv)
df_testpd.read_csv(test.csv)
dfpd.concat([df_train,df_test]).reset_index(dropTrue)
df.head() 可以看到里面有一些“b”这种二进制留下的解码前缀我们需要简单处理一下
import re
def preprocess_text(s):# 解码字节字符串if isinstance(s, bytes):s s.decode(utf-8)# 替换转义字符s re.sub(r\\n, , s)s re.sub(r\\, , s)return s.strip().replace(b,).replace(b,)
#应用预处理
df[text] df[text].apply(preprocess_text)
df.head() 查看种类分布
df[label].value_counts().plot(kindbar) 0是负面文本评论1是正面可以看到很均衡。
词向量转化
英文词汇自带空格所以不需要向中文一样的去分词直接构建词表然后词向量化
from os import listdir
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split
# 将文件分割成单字, 建立词索引字典
tok Tokenizer(num_words10000)
tok.fit_on_texts(df[text].to_numpy())
print(样本数 : , tok.document_count) 查看词表前10 的词汇
print({k: tok.word_index[k] for k in list(tok.word_index)[:10]})
将其变为数组然后查看其长度分布
X tok.texts_to_sequences(df[text].to_numpy())
#查看x的长度的分布
v_cpd.Series([len(i) for i in X]).value_counts()
print(v_c[v_c150])
v_c[v_c150].plot(kindbar,figsize(12,5)) 可以看到大部分评论长度都在120左右我们选择将X统一到长度为200多的去掉少的用0补起来。
# 将序列数据填充成相同长度
X sequence.pad_sequences(X, maxlen200)
Ydf[label].to_numpy()
print(X.shape: , X.shape)
print(Y.shape: , Y.shape)然后我们再将训练集和测试集分开前面是直接拼接的那这里我们直接顺序分割就行。
X_train, X_test, Y_train, Y_test train_test_split(X, Y, test_size0.5, shuffleFalse)
X_train.shape,X_test.shape,Y_train.shape, Y_test.shape y进行独立热编码
Y_test_originalY_test.copy()
Y_train to_categorical(Y_train)
Y_test to_categorical(Y_test)
Y to_categorical(Y)
查看X和y的其中三个
print(X_train[100:103])
print(Y_test[:3])
Y_test_original[:3] 数据没什么问题我们可以构建神经网络进行训练了。 构建神经网络
导入包自定义Transformer层和位置编码层。
from tensorflow.keras import layers
import tensorflow as tf
from tensorflow import keras
class TransformerEncoder(layers.Layer):def __init__(self, embed_dim, dense_dim, num_heads, **kwargs):super().__init__(**kwargs)self.embed_dim embed_dimself.dense_dim dense_dimself.num_heads num_headsself.attention layers.MultiHeadAttention(num_headsnum_heads, key_dimembed_dim)self.dense_proj keras.Sequential([layers.Dense(dense_dim, activationrelu),layers.Dense(embed_dim),] )self.layernorm_1 layers.LayerNormalization()self.layernorm_2 layers.LayerNormalization()def call(self, inputs, maskNone):if mask is not None:mask mask[:, tf.newaxis, :]attention_output self.attention(inputs, inputs, attention_maskmask)proj_input self.layernorm_1(inputs attention_output)proj_output self.dense_proj(proj_input)return self.layernorm_2(proj_input proj_output)def get_config(self):config super().get_config()config.update({embed_dim: self.embed_dim,num_heads: self.num_heads,dense_dim: self.dense_dim, })return config
class PositionalEmbedding(layers.Layer):def __init__(self, sequence_length, input_dim, output_dim, **kwargs):super().__init__(**kwargs)self.token_embeddings layers.Embedding(input_diminput_dim, output_dimoutput_dim)self.position_embeddings layers.Embedding(input_dimsequence_length, output_dimoutput_dim)self.sequence_length sequence_lengthself.input_dim input_dimself.output_dim output_dimdef call(self, inputs):length tf.shape(inputs)[-1]positions tf.range(start0, limitlength, delta1)embedded_tokens self.token_embeddings(inputs)embedded_positions self.position_embeddings(positions)return embedded_tokens embedded_positionsdef compute_mask(self, inputs, maskNone):return tf.math.not_equal(inputs, 0)def get_config(self):config super().get_config()config.update({output_dim: self.output_dim,sequence_length: self.sequence_length,input_dim: self.input_dim,})return config
导入层和定义一些参数
from keras.preprocessing import sequence
from keras.models import Sequential,Model
from keras.layers import Dense,Input, Dropout, Embedding, Flatten,MaxPooling1D,Conv1D,SimpleRNN,LSTM,GRU,Multiply,GlobalMaxPooling1D
from keras.layers import Bidirectional,Activation,BatchNormalization,GlobalAveragePooling1D,MultiHeadAttention
from keras.callbacks import EarlyStopping
from keras.layers.merge import concatenate
np.random.seed(0) # 指定随机数种子
#单词索引的最大个数10000单句话最大长度200
top_words10000
max_words200 #序列长度
embed_dim128 #嵌入维度
num_labels2 #2分类
构建模型构建了14种模型 def build_model(top_wordstop_words,max_wordsmax_words,num_labelsnum_labels,modeLSTM,hidden_dim[64]):if modeRNN:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim, mask_zeroTrue))model.add(Dropout(0.25))model.add(SimpleRNN(hidden_dim[0])) model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeMLP:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim))#, mask_zeroTruemodel.add(Flatten())model.add(Dropout(0.25))model.add(Dense(hidden_dim[0])) model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeLSTM:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim))model.add(Dropout(0.25))model.add(LSTM(hidden_dim[0]))model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeGRU:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim))model.add(Dropout(0.25))model.add(GRU(hidden_dim[0]))model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeCNN: #一维卷积model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim, mask_zeroTrue))model.add(Dropout(0.25))model.add(Conv1D(filters32, kernel_size3, paddingsame,activationrelu))model.add(MaxPooling1D(pool_size2))model.add(Flatten())model.add(Dense(hidden_dim[0], activationrelu))model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeCNNLSTM:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim))model.add(Dropout(0.25)) model.add(Conv1D(filters32, kernel_size3, paddingsame,activationrelu))model.add(MaxPooling1D(pool_size2))model.add(LSTM(hidden_dim[0]))model.add(Dropout(0.25)) model.add(Dense(num_labels, activationsoftmax))elif modeBiLSTM:model Sequential()model.add(Embedding(top_words, input_lengthmax_words, output_dimembed_dim))model.add(Bidirectional(LSTM(64)))model.add(Dense(hidden_dim[0], activationrelu))model.add(Dropout(0.25))model.add(Dense(num_labels, activationsoftmax))#下面的网络采用Funcional API实现elif modeTextCNN:inputs Input(nameinputs,shape[max_words,], dtypefloat64)## 词嵌入使用预训练的词向量layer Embedding(top_words, input_lengthmax_words, output_dimembed_dim)(inputs)## 词窗大小分别为3,4,5cnn1 Conv1D(32, 3, paddingsame, strides 1, activationrelu)(layer)cnn1 MaxPooling1D(pool_size2)(cnn1)cnn2 Conv1D(32, 4, paddingsame, strides 1, activationrelu)(layer)cnn2 MaxPooling1D(pool_size2)(cnn2)cnn3 Conv1D(32, 5, paddingsame, strides 1, activationrelu)(layer)cnn3 MaxPooling1D(pool_size2)(cnn3)# 合并三个模型的输出向量cnn concatenate([cnn1,cnn2,cnn3], axis-1)x Flatten()(cnn) x Dense(hidden_dim[0], activationrelu)(x)output Dense(num_labels, activationsoftmax)(x)model Model(inputsinputs, outputsoutput)elif modeAttention:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x Embedding(top_words, input_lengthmax_words, output_dimembed_dim, mask_zeroTrue)(inputs)x MultiHeadAttention(1, key_dimembed_dim)(x, x,x)x GlobalAveragePooling1D()(x)x Dropout(0.2)(x) x Dense(32, activationrelu)(x)output Dense(num_labels, activationsoftmax)(x)model Model(inputs[inputs], outputsoutput) elif modeMultiHeadAttention:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x Embedding(top_words, input_lengthmax_words, output_dimembed_dim, mask_zeroTrue)(inputs)x MultiHeadAttention(8, key_dimembed_dim)(x, x,x)x GlobalAveragePooling1D()(x)x Dropout(0.2)(x) x Dense(32, activationrelu)(x)output Dense(num_labels, activationsoftmax)(x)model Model(inputs[inputs], outputsoutput) elif modeAttentionBiLSTM:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x Embedding(top_words, input_lengthmax_words, output_dimembed_dim)(inputs)x MultiHeadAttention(2, key_dimembed_dim)(x, x,x)x Bidirectional(LSTM(hidden_dim[0]))(x) x Dense(64, activationrelu)(x)x Dropout(0.2)(x)output Dense(num_labels, activationsoftmax)(x)model Model(inputsinputs, outputsoutput) elif modeBiGRUAttention:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x Embedding(top_words, input_lengthmax_words, output_dimembed_dim)(inputs)x Bidirectional(GRU(32,return_sequencesTrue))(x)x MultiHeadAttention(2, key_dimembed_dim)(x,x,x)x Bidirectional(GRU(32))(x)x Dropout(0.2)(x)output Dense(num_labels, activationsoftmax)(x)model Model(inputs[inputs], outputsoutput) elif modeTransformer:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x Embedding(top_words, input_lengthmax_words, output_dimembed_dim, mask_zeroTrue)(inputs)x TransformerEncoder(embed_dim, 32, 4)(x)x GlobalMaxPooling1D()(x)x Dropout(0.25)(x)outputs Dense(num_labels, activationsoftmax)(x)model Model(inputs, outputs)elif modePositionalEmbeddingTransformer:inputs Input(nameinputs,shape[max_words,], dtypefloat64)x PositionalEmbedding(sequence_lengthmax_words, input_dimtop_words, output_dimembed_dim)(inputs)x TransformerEncoder(embed_dim, 32, 4)(x)x GlobalMaxPooling1D()(x)x Dropout(0.5)(x)outputs Dense(num_labels, activationsoftmax)(x)model Model(inputs, outputs)model.compile(losscategorical_crossentropy, optimizeradam, metrics[accuracy])return model
这里构建了“[MLP, CNN, RNN, LSTM, GRU, CNNLSTM, BiLSTM, TextCNN, Attention, MultiHeadAttention, AttentionBiLSTM, BiGRUAttention, Transformer, PositionalEmbeddingTransformer]” 14种模型大家还可以任意组合自己想要的模型。
定义评价函数和画损失图函数
#定义损失和精度的图,和混淆矩阵指标等等
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from sklearn.metrics import cohen_kappa_score
def plot_loss(history):# 显示训练和验证损失图表plt.subplots(1,2,figsize(10,3))plt.subplot(121)loss history.history[loss]epochs range(1, len(loss)1)val_loss history.history[val_loss]plt.plot(epochs, loss, bo, labelTraining Loss)plt.plot(epochs, val_loss, r, labelValidation Loss)plt.title(Training and Validation Loss)plt.xlabel(Epochs)plt.ylabel(Loss)plt.legend() plt.subplot(122)acc history.history[accuracy]val_acc history.history[val_accuracy]plt.plot(epochs, acc, b-, labelTraining Acc)plt.plot(epochs, val_acc, r--, labelValidation Acc)plt.title(Training and Validation Accuracy)plt.xlabel(Epochs)plt.ylabel(Accuracy)plt.legend()plt.tight_layout()plt.show()
def plot_confusion_matrix(model,X_test,Y_test_original):#预测概率probmodel.predict(X_test) #预测类别prednp.argmax(prob,axis1)#数据透视表混淆矩阵predpd.Series(pred)Y_test_originalpd.Series(Y_test_original)table pd.crosstab(Y_test_original, pred, rownames[Actual], colnames[Predicted])#print(table)sns.heatmap(table,cmapBlues,fmt.20g, annotTrue)plt.tight_layout()plt.show()#计算混淆矩阵的各项指标print(classification_report(Y_test_original, pred))#科恩Kappa指标print(科恩Kappastr(cohen_kappa_score(Y_test_original, pred)))def evaluation(y_test, y_predict):accuracyclassification_report(y_test, y_predict,output_dictTrue)[accuracy]sclassification_report(y_test, y_predict,output_dictTrue)[weighted avg]precisions[precision]recalls[recall]f1_scores[f1-score]#kappacohen_kappa_score(y_test, y_predict)return accuracy,precision,recall,f1_score #, kappa
定义训练函数
#定义训练函数
df_evalpd.DataFrame(columns[Accuracy,Precision,Recall,F1_score])
def train_fuc(max_wordsmax_words,modeBiLSTMAttention,batch_size64,epochs10,hidden_dim[64],show_lossTrue,show_confusion_matrixTrue):#构建模型modelbuild_model(max_wordsmax_words,modemode,hidden_dimhidden_dim)print(model.summary())es EarlyStopping(patience3)historymodel.fit(X_train, Y_train,batch_sizebatch_size,epochsepochs,validation_split0.1, verbose1,callbacks[es])print(——————————-----------------——训练完毕—————-----------------------------———————)# 评估模型#loss, accuracy model.evaluate(X_test, Y_test) ; print(测试数据集的准确度 {:.4f}.format(accuracy))probmodel.predict(X_test) ; prednp.argmax(prob,axis1)scorelist(evaluation(Y_test_original, pred))df_eval.loc[mode,:]scoreif show_loss:plot_loss(history)if show_confusion_matrix:plot_confusion_matrix(modelmodel,X_testX_test,Y_test_originalY_test_original)
我首先命名了一个df_eval的数据框用来存放模型预测的效果的评价指标。我们采用准确率精确值召回率F1值四个分类问题常用的指标来进行评价。
我这个训练函数里面包括了很多东西可以打印模型的信息然后展示模型的训练对模型训练过程的训练集和验证集的损失变化都画了图然后对于预测结果的混淆矩阵和其热力图都进行了展示还储存了评价指标。
初始化参数
top_words10000
max_words200
batch_size64
epochs10
hidden_dim[64]
show_confusion_matrixTrue
show_lossTrue 上面构建这么多函数是方便下面进行训练的下面的训练代码就很简单了一行代码就全有。
train_fuc(modeMLP,batch_sizebatch_size,epochsepochs) 训练其他模型只需要改mode参数就行
train_fuc(modeCNN,batch_sizebatch_size,epochsepochs) 这里太多了就不一一展示了直接一起训练然后看看所有模型的效果对比
train_fuc(modeRNN,batch_sizebatch_size,epochs8)
train_fuc(modeLSTM,epochsepochs)
train_fuc(modeGRU,epochsepochs)
train_fuc(modeCNNLSTM,epochsepochs)
train_fuc(modeBiLSTM,epochsepochs)
train_fuc(modeTextCNN,epochs3)
train_fuc(modeAttention,epochs4)
train_fuc(modeMultiHeadAttention,epochs3)
train_fuc(modeAttentionBiLSTM,epochs8)
train_fuc(modeBiGRUAttention,epochs4)
train_fuc(modeTransformer,epochs3)
train_fuc(modePositionalEmbeddingTransformer,batch_sizebatch_size,epochs3) 模型评价
df_eval.assign(sdf_eval.sum(axis1))#[s].idxmax() 可以看到这个数据集上TextCNN模型效果较为优良。
多层感知机 (MLP) 的表现在所有指标上都是相对均衡的。卷积神经网络 (CNN) 在精确度和F1分数上稍微高于其他指标。递归神经网络 (RNN) 在所有指标上的表现都不是很好尤其是在准确度和召回率上。长短期记忆网络 (LSTM) 在准确度和F1分数上表现良好。门控循环单元 (GRU) 在所有指标上的表现都很均衡是性能较好的模型之一。CNNLSTM 组合模型在准确度和F1分数上表现较好。双向LSTM (BiLSTM) 的表现和单向的LSTM类似但是在所有指标上略有下降。TextCNN 在精确度和F1分数上表现出色说明它在分类正确的同时也保持了较高的相关性。Attention 机制的加入提供了相对较高的精确度但在其他指标上的提升不是很显著。MultiHeadAttention 相比单一的Attention机制在准确度和召回率上略有下降。AttentionBiLSTM 的组合模型在所有指标上均有较好的表现尤其是在F1分数上这表明它在精确度和召回率之间取得了很好的平衡。BiGRUAttention 在所有指标上也显示出良好的性能尤其是在准确度和F1分数上这表明它有效地结合了GRU的时序处理能力和Attention机制的聚焦能力。Transformer 模型在所有指标上都有不错的表现尤其是在F1分数上表明它在精确度和召回率之间取得了良好的平衡。PositionalEmbeddingTransformer 在所有指标上有较好的表现这可能表明它在捕捉长距离依赖和上下文信息方面非常有效。
画出对应的柱状图
bar_width 0.4
colors[c, b, g, tomato, m, y, lime, k,orange,pink,grey,tan,gold,r]
fig, ax plt.subplots(2,2,figsize(10,8),dpi128)
for i,col in enumerate(df_eval.columns):nint(str(22)str(i1))plt.subplot(n)df_coldf_eval[col]m np.arange(len(df_col))plt.bar(xm,heightdf_col.to_numpy(),widthbar_width,colorcolors)#plt.xlabel(Methods,fontsize12)namesdf_col.indexplt.xticks(range(len(df_col)),names,fontsize10)plt.xticks(rotation40)plt.ylabel(col,fontsize14)plt.tight_layout()
#plt.savefig(柱状图.jpg,dpi512)
plt.show() 其实模型们都差不了太多大概都在85%左右就RNN离谱一些。。。 模型预测
我们单独拿Transformer模型进行训练我们选择使用它单独拿出来在所有的数据集上进行训练然后对新来的新闻进行预测看看效果。然后去预测新文本。
modelbuild_model(max_wordsmax_words,modePositionalEmbeddingTransformer,hidden_dimhidden_dim)
historymodel.fit(X,Y,batch_sizebatch_size,epochs3,verbose0)
再来一个新的电影评论的文本怎么预测呢
new_txtThis latest movie is a cinematic masterpiece! It brilliantly blends stunning visuals with a captivating storyline.
The performances are exceptional, capturing the essence of each character beautifully.
Its a rare film that not only entertains but also provokes deep thought and emotional engagement.
A must-see for movie lovers! 这么多感叹号和love一看就是正面情感。。
我们来试试要先转化为和前面一样的向量。
自定义一个类别处理函数
def predict_newkind(new_txt,tokentok):dic{0: 负面, 1: 正面}new_text_seq tok.texts_to_sequences([new_txt])new_text_seq_padded sequence.pad_sequences(new_text_seq, maxlen200)predictions model.predict(new_text_seq_padded)predicted_class np.argmax(predictions, axis1)return dic[predicted_class[0]]
预测看看
predict_newkind(new_txt) 没问题很准确
再来个负面评论 看看
new_txtUnfortunately, this recent film falls short of expectations. Despite a promising premise, the plot is poorly executed and lacks
depth. The performances feel forced and fail to connect with the audience. Additionally, the over-reliance on special effects over substance
makes it a forgettable experience. Its a disappointing entry in what could have been an exciting film series.predict_newkind(new_txt) 不错 模型储存
可以把构建的模型和词表进行保存下次就不用再训练了可以直接用。
import pickle
from tensorflow.keras.models import save_model
# 保存Tokenizer
with open(tokenizer.pickle, wb) as handle:pickle.dump(tok, handle, protocolpickle.HIGHEST_PROTOCOL)
model.save(my_model.h5) # 保存模型到HDF5文件
下次要用的话直接载入
from tensorflow.keras.models import load_model
import pickle
with open(tokenizer.pickle, rb) as handle:tok pickle.load(handle)
model load_model(my_model.h5, custom_objects{PositionalEmbedding: PositionalEmbedding,TransformerEncoder:TransformerEncoder})
大家还可以用自己的想法构建更多的模型说不定可以得到更好的准确率。
我本人也测试过 KNN, 决策树逻辑回归这种传统机器学习学的方法效果比神经网络差多了....而且训练时间也长很多。
当然这是英文的案例不用分词什么的想做中文的评论情感分类就可以参考我上一篇文章新闻文本主题多分类 创作不易看官觉得写得还不错的话点个关注和赞吧本人会持续更新python数据分析领域的代码文章~(需要定制代码可私信)