1.引言
循环神经网络(Recurrent Neural Network,RNN)是一类目前非常流行的神经网络,用于处理序列数据。在许多自然语言处理问题中,如文本分类、情感分析和机器翻译等任务,RNN被广泛应用。在本文中,我们将介绍如何使用Python和Keras实现一个基于循环神经网络的文本分类器。我们将使用IMDB电影评论数据集进行实验,并讨论如何调整温度(temperature)来增加模型生成文本的多样性。
2.循环神经网络简介
循环神经网络(RNN)是一种递归式神经网络,适用于处理序列数据,例如自然语言或时间序列数据。RNN通过在不同时间步(time step)中传递信息来维持状态,这使得每个输入都可以使用以前输入的信息得到更好的理解。传统的维度为1的向量在每个时间步中被传递,记为$h_t$,并且与输入$x_t$相结合,产生的输出为$y_t$。在下一个时间步中,隐藏状态$h_t$也可以保留,以提供与以前时间步有关的数据。
2.1. LSTM
LSTM(Long Short-Term Memory,长短时记忆)是一种特殊类型的RNN,用于在长序列中避免梯度消失问题。LSTM通过使用“门”来控制当时钟信息流向和流出网络中的方式,以增强隐藏状态的相关性。
我们可以使用以下公式来表示LSTM的门控制:
$$
f_t=\sigma(W_f[h_{t-1},x_t]+b_f) \\
i_t=\sigma(W_i[h_{t-1},x_t]+b_i) \\
\hat{C_t}=\tanh(W_C[h_{t-1},x_t]+b_C) \\
C_t=f_t*C_{t-1}+i_t*\hat{C_t} \\
o_t=\sigma(W_o[h_{t-1},x_t]+b_o) \\
h_t=o_t*\tanh(C_t)
$$
其中$f_t$表示遗忘门(forget gate),$i_t$表示输入门(input gate),$\hat{C_t}$表示候选状态,$C_t$表示隐藏状态(cell state),$o_t$为输出门(output gate),$\sigma$表示sigmoid激活函数,$\tanh$表示双曲正切激活函数。
3.IMDB数据集
IMDB数据集包含来自互联网电影数据库的50,000个高度极性的评论,分为25,000个训练集和25,000个测试集。评论都被标记为正面或负面。
我们可以使用以下代码从Keras中获取IMDB数据集:
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
num_words参数设置为10,000,这意味着数据集中只包含前10,000个常见单词。这是我们要限制词汇量的原因之一。
我们还可以使用以下代码将整数序列转换回单词序列:
word_index = imdb.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
在此代码片段中,我们获取字典列表以将单词映射回其整数索引。给定一个整数序列,我们可以遍历它并使用反转字典将其转换回原始单词。请注意,由于构建字典时添加了一些额外的单词,因此我们减去3。
我们可以使用以下代码来将训练数据和测试数据向量化,并使它们具有相同的长度:
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
我们将数据向量化为大小为10,000的向量,其中每个元素表示字典中每个单词的出现频率。我们还使用神经网络的完整训练集的第一个10,000个数据点作为验证集。
4.模型设计
我们使用一个具有两个LSTM层的模型,后面跟着一个密集层,用于输出分类标签。我们还在每个LSTM层中使用了Dropout,以减少过拟合。
我们可以使用以下代码来构建模型:
from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout
model = Sequential()
model.add(LSTM(32, input_shape=(10000,), return_sequences=True))
model.add(Dropout(0.5))
model.add(LSTM(32, return_sequences=False))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(partial_x_train, partial_y_train, epochs=10, batch_size=128, validation_data=(x_val, y_val))
5.模型评估
模型的最终测试结果如下所示:
results = model.evaluate(x_test, y_test)
print(results)
我们得到了一个准确率约为88%的模型。虽然这个结果有很大的改进空间,但它已经可以通过IMDB数据集。我们还可以使用以下代码来绘制模型的准确率和损失曲线:
import matplotlib.pyplot as plt
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
我们可以清楚地看到模型在第四个周期后开始过拟合。
6.使用温度变换增加生成文本的多样性
让我们使用模型生成一些新的评论。我们可以使用以下代码来选择一个随机评论,并在特定温度下生成5个副本:
import random
import sys
def sample(preds, temperature=1.0):
preds = np.asarray(preds).astype('float64')
preds = np.log(preds) / temperature
exp_preds = np.exp(preds)
preds = exp_preds / np.sum(exp_preds)
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas)
random_index = random.randint(0, len(test_data))
text = test_data[random_index]
for temperature in [0.2, 0.5, 1.0, 1.2]:
print('temperature = ', temperature)
generated_text = ''
for i in range(200):
sampled = np.zeros((1, 10000))
for t, word_index in enumerate(text):
sampled[0, t] = word_index
preds = model.predict(sampled, verbose=0)[0]
next_index = sample(preds, temperature)
next_word = reverse_word_index[next_index]
generated_text += next_word
text = np.append(text, [next_index])
text = text[1:]
print(generated_text)
当温度较低时,模型生成的评论比较重复和平凡。但是,随着温度的升高,模型更有可能生成出新的且不同的句子组合:
temperature = 0.2
words that is great movie movie and the and the and the is a little thriller movie movie that is not great but it is a good movie that is a great movie to see
temperature = 0.5
and does a bit too cliche little surprise on realism of self awareness love these kind of movies and what the world of
temperature = 1.0
really is emotionless flat and uninteresting nothing suspenseful message movies it in the end the a too
temperature = 1.2
last year is able to provide the kind of way to the heart of a mirror is to rush the documentary from deep and unique but incredibly ugly in the bland for those who watch this film if one has too much experiences with some other preacher but then i own from the faulty from a way
我们可以看到,在高温下生成的评论显然更加具有变化性和多样性。虽然可能会生成一些不连贯的句子,但这种方法可以增加我们的模型生成更多样的文本的能力。
7.结论
在本文中,我们介绍了如何使用循环神经网络(RNN)实现基于Python的文本分类器,该分类器使用IMDB电影评论数据集进行训练和评估。我们还讨论了如何使用双向LSTM和Dropout层来减少过拟合,并进行了评估。最后,我们介绍了如何使用温度来增加生成文本的多样性,从而使模型生成更加出色的评论。