Keras深度学习实战——使用循环神经网络构建情感分析模型

时间:2022-10-05 10:52:20

0. 前言

《循环神经详解与实现》一节中,我们已经了解循环神经网络 (Recurrent neural networks, RNN) 的基本原理,并且在 Keras 中实现了 RNN 模型,在本节中,我们构建 RNN 模型进行航空推文情感分类。

1. 使用循环神经网络构建情感分析模型

1.1 数据集分析

接下来,我们将实现 RNN 构建情感分析模型,所用的数据集与在《从零开始构建单词向量》一节中使用的数据集相同,即航空公司 Twitter 数据集,模型的目标是预测用户对于航空公司的评价属于正面、负面或者中立。

1.2 构建 RNN 模型进行情感分析

本节中,我们将实现在 RNN 情感分析模型。

(1) 导入相关的库和数据集:

from keras.layers import Dense
from keras.layers.recurrent import SimpleRNN
from keras.models import Sequential
from keras.layers.embeddings import Embedding
from sklearn.model_selection import train_test_split
import numpy as np
import nltk
from nltk.corpus import stopwords
import re
import pandas as pd

data=pd.read_csv('archive/Tweets.csv')
print(data.head())

(2) 预处理文本,删除标点符号、将所有单词转换为小写并删除停用词:

import nltk

stop = nltk.corpus.stopwords.words('english')
def preprocess(text):
    text=text.lower()
    text=re.sub('[^0-9a-zA-Z]+',' ',text)
    words = text.split()
    words2=[w for w in words if (w not in stop)]
    #words3=[ps.stem(w) for w in words]
    words4=' '.join(words2)
    return(words4)
data['text'] = data['text'].apply(preprocess)

(3) 提取数据集中的所有单词,并为每个单词分配一个索引:

from collections import Counter
counts = Counter()
for i,review in enumerate(data['text']):
    counts.update(review.split())
words = sorted(counts, key=counts.get, reverse=True)

nb_chars = len(words)
print(nb_chars)
word_to_int = {word: i for i, word in enumerate(words, 1)}
int_to_word = {i: word for i, word in enumerate(words, 1)}

提取到的单词示例如下:

{'whitterbug', 'funnycaptain', 'referencing', 'pnbajfkmhg', 'vacatinn', 'devalue', ...}

整数到单词的映射字典的示例如下:

{..., 9966: 'intrusive', 9967: 'pockets', 9968: 'goingtovegas', 9969: 'getconnected', 9970: 'achieving', ...}

(4) 将给定句子中的每个单词映射到与其关联的单词 ID 上,将文本评论转换为单词列表,其中每个列表均包含构成句子的单词 ID

mapped_reviews = []
for review in data['text']:
mapped_reviews.append([word_to_int[word] for word in review.split()])

# 打印示例
print('Original text:',data.loc[0]['text'])
print('Mapped text:',mapped_reviews[0])

原始评论文本和映射为单词 ID 的评论示例如下:

Original text: virginamerica dhepburn said
Mapped text: [31, 6369, 137]

(5) 提取句子的最大长度,并通过填充所有句子将其标准化为相同的长度。接下来,遍历所有评论文本,并存储每个评论的长度。此外,我们还需要计算评论的最大长度,用于将所有句子标准化为最大长度:

length_sent = []
for i in range(len(mapped_reviews)):
    length_sent.append(len(mapped_reviews[i]))
sequence_length = max(length_sent)

我们可以看到,不同的推文具有不同的长度。但是,RNN 接收的每个输入应具有相同长度。如果评论的长度小于数据集中最大的评论长度,则使用 0 值填充的评论,使所有输入具有相同的长度。

from keras.preprocessing.sequence import pad_sequences
x = pad_sequences(maxlen=sequence_length, sequences=mapped_reviews, padding="post", value=0)

(6) 构造训练和测试数据集,将目标输出转换为独热编码,并将原始数据拆分为训练数据集和测试数据集:

y = data['airline_sentiment'].values
y = np.array(pd.get_dummies(y))
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

(7) 构建 RNN 架构并编译模型:

embedding_vecor_length=32
max_review_length=26
model = Sequential()
model.add(Embedding(input_dim=nb_chars+1, output_dim=32, input_length = 26))

model.add(SimpleRNN(50, return_sequences=False))

model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
model.summary()

Embedding 层的 input_dim 参数为数据集中不重复单词的总数加 1,其会为每个单词创建一个单词向量,其中 output_dim 表示要表示创建单词维数,input_length 表示每个句子中的单词数。在 RNN 层中,如果要提取每个时间步的输出,则 return_sequences 参数为 True,在本例中,我们仅需要在处理所有输入后才会提取输出,因此 return_sequences = False
模型简要信息输出如下:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, 26, 32)            477920    
_________________________________________________________________
simple_rnn (SimpleRNN)       (None, 50)                4150      
_________________________________________________________________
dense (Dense)                (None, 3)                 153       
=================================================================
Total params: 482,223
Trainable params: 482,223
Non-trainable params: 0
_________________________________________________________________

Embedding 层中,由于共计有 14934 个不重复单词,且我们使用索引 0 作为填充词,因此共有 14934 个可能的单词,每个单词都以 32 维表示,因此共有 14934 x 32 = 48223个 参数。
simpleRNN 层中,有一组权重将输入连接到具有 50 个输出的 RNN 层,由于每个时刻有 32 个输入,每个时时刻具有相同的权重 W x h W_{xh} Wxh,则总共使用 32 x 50=1600 个权重将输入连接到 RNN 中,每个输入对应的输出尺寸为 1 x 50
此外,计算每个时刻的网络中间状态,需要计算 X ∗ W x h X * W_{xh} XWxh h ( t − 1 ) ∗ W h h h^{(t-1)}* W_{hh} h(t1)Whh,并求和,其中 X X X 是输入值, W x h W_{xh} Wxh 是将输入层连接到 RNN 层的权重, W h h W_{hh} Whh 是将上一个时刻网络状态连接到当前时刻网络状态的权重, h ( t − 1 ) h^{(t-1)} h(t1) 是上一个时刻的网络状态,由于 X ∗ W x h X*W_{xh} XWxh 的输出为 1 x 50,则 h ( t − 1 ) ∗ W h h h^{(t-1)}* W_{hh} h(t1)Whh 也为 1 x 50。由于 h ( t − 1 ) h^{(t-1)} h(t1) 的尺寸为 1 x 40,因此 W h h W_{hh} Whh 矩阵的尺寸为 50 x 50。除权重外,我们还需要 50 个偏置项与 50 个输出相关联,因此共有 (32 x 50 + 50 x 50 + 50 = 4150) 个权重参数。
最后一层总共有 153 个权重参数,因为 RNN 最后时刻的 50 个输出连接到全连接层的 3 个节点上,因此具有 50 x 3 个权重和 3 个偏置,因此总共有 153 个权重参数。

(8) 拟合模型:

history = model.fit(x_train, y_train,
                validation_data=(x_test, y_test),
                epochs=10,
                batch_size=32)

训练过程中,训练、测试数据集中的准确率和损失值变化情况如下:

Keras深度学习实战——使用循环神经网络构建情感分析模型
此模型的测试准确率约为 76%,与我们使用词向量构建情感分析中构建的基于词向量的网络相比,没有任何明显的改进。但是,通过使用更多数据样本数量的增加,该模型将具有更高的准确率。

相关链接

Keras深度学习实战(1)——神经网络基础与模型训练过程详解
Keras深度学习实战(2)——使用Keras构建神经网络
Keras深度学习实战(7)——卷积神经网络详解与实现
Keras深度学习实战(24)——从零开始构建单词向量
Keras深度学习实战(25)——使用skip-gram和CBOW模型构建单词向量
Keras深度学习实战(26)——文档向量详解
Keras深度学习实战(27)——循环神经详解与实现
Keras深度学习实战(28)——利用单词向量构建情感分析模型