简介
这是一个使用深度学习处理文本序列的示例。
我们已经尝试使用密集连接层处理过IMDB数据集,回顾请参考Imdb影评分类。
本文使用循环神经网络来处理文本序列。
文本处理
文本可以理解为单词序列或字符序列。
现在已经广泛使用的自然语言理解、文档分类、自动问答等都可以看作是深度学习在文本识别领域的应用。
本文介绍的深度学习模型并没有像人类一样真正地理解了文本,而只是映射出书面语言的统计结构,但这也足以解决许多简单的文本处理任务。
深度学习用于自然语言处理是将模式识别应用于单词、句子和段落,这与计算机视觉是将模式识别应用于像素大致相同。
识别文本的关键一步是文本向量化,有两种方法可以实现,一种是one-hot编码,另一种是使用密集的词向量(词嵌入)。
one-hot编码得到的向量是二进制、稀疏的、维度很高(维度等于单词个数)。而词嵌入是从数据中学习得到的,它是低维的浮点数向量。
在keras中可以使用Embedding层学习词嵌入。
RNN
循环神经网络与之前使用的密集连接网络和卷积神经网络不同:
- 密集连接网络和卷积神经网络都没有记忆,它们单独处理每个输入,输入与输入之间没有任何状态
- 对于这种网络,要想处理数据点的时间序列,需要向网络展示整个序列
- 如IMDB,需要将所有电影评论转换为一个大向量,然后一次性处理
- 这种网络也叫前馈网络
- 循环神经网络是有记忆的,它会保存状态,其中包含已处理内容相关的信息
- RNN是一类具有内部环的神经网络
- 在处理两个不同的独立序列之间,RNN的状态会被重置
- 仍可以将一个序列看作是单个数据点
- 真正改变的是,数据点不是在单个步骤中处理,网络内部会对整个序列进行遍历
如下图所示,展示了RNN的一种形式:
keras内置了循环层,如SimpleRNN,它接收形状为(batch_size, timesteps, input_features)的输入可以在两种模式下运行:
- 一种是返回每个时间步连续输出的完整序列,即形状为(batch_size, timesteps, output_features)的三维张量
- 另一种只返回每个输入序列的最终输出,形状为(batch_size, output_features)的二维张量,默认模式
- 这两种模式由return_sequences这个构造函数参数控制,为true时输出为三维张量
处理IMDB数据
直接上代码:
# -*- coding: utf-8 -*-
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
from keras.datasets import imdb
from keras.preprocessing import sequence
max_features = 10000
maxlen = 500
batch_size = 32
print('load data...')
path = r'E:\practice\tf2\imdb\imdb.npz'
(input_train, y_train), (input_test, y_test) = imdb.load_data(path, num_words = max_features)
print('pad sequences (smaples x time)')
input_train = sequence.pad_sequences(input_train, maxlen = maxlen)
input_test = sequence.pad_sequences(input_test, maxlen = maxlen)
print('input_train shape: ', input_train.shape)
print('input_test shape: ', input_test.shape)
# 训练网络
from keras import models
from keras import layers
from keras.layers import Dense
from keras.layers import Embedding
from keras.layers import SimpleRNN
# 使用一个Embedding层和一个SimpleRNN层
model = models.Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation = 'sigmoid'))
model.compile(optimizer = 'rmsprop', loss = 'binary_crossentropy', metrics = ['acc'])
history = model.fit(input_train, y_train, epochs = 10, batch_size = 128, validation_split = 0.2)
# 绘图
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
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 = 'Balidation 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()
结果如下图:
LSTM
SimpleRNN并非keras中唯一可用的循环层,还有LSTM和GRU。
SimpleRNN的问题在于它并不能记住许多时间步之前的信息,这是由于梯度消失问题引起的。
LSTM(long short-term memory)长短期记忆是SimpleRNN的一种变体,它增加了一种携带信息跨越多个时间步的方法。
它能够保存信息以便以后使用,从而防止较早期的信号在处理过程中逐渐消失。
使用LSTM训练IMDB数据的方法与使用SimpleRNN类似,只需要在训练时修改使用SimpleRNN的地方如下:
from keras.layers import LSTM
# 只需要指定LSTM层的输出维度,其他所有参数都使用Keras的默认值
model.add(LSTM(32))
训练结果绘图如下所示:
小节
总体而言,使用RNN训练的处理IMDB影评分类的神经网络,在最终精确度上并不比密集连接层高多少,可能的原因如下:
- 训练RNN使用的数据较少,500个时间步之后就截断了序列,而Dense层模型则读取了整个序列
- 没有精细地调用LSTM的超参数,如嵌入维度、输出维度等
- 缺少正则化,可能会过拟合
- 对于情感分析问题,LSTM并不是最擅长的。对于这样的基本问题,观察每条评论中出现了哪些词以及它们出现的频率就可以很好地解决
- 对于更加困难的自然语言理解问题,如问答和机器翻译等,LSTM应该会表现更突出