卷积神经网络处理文本序列

Posted by RAIS on 2020-03-19

​我们之前讨论了卷积神经网络,从局部可以提取出特征,用于小猫小狗的图片识别处理,非常有效;也讨论了循环神经网络进行文本的处理,因为文本的顺序是顺序相关的。基于以上特点,我们把时间或者说文本的前后看做一个维度,那一段文本就是一个一维空间,相比图片的二维空间,变得更加简单了,那卷积神经网络是否可以处理这样的情况呢?

先亮出结论,答案是肯定的。图片是二维的数据,文本是一维的数据,因此我们可以对训练的神经网络进行简化,让网络去提取一维的特征。既然如此,一维神经网络与二维的神经网络是类似的,因此我们可以把二维卷积神经网络的特性迁移到一维的卷积神经网络上。

我们在讨论神经网络的时候,我们知道了卷积神经网络具有平移不变性,非常好的特性,这个特性可以抓住问题的关键,识别出特殊的特征。在一维的卷积神经网络(用到的方法:Conv1D)中,一小段文本所具有的特性,进行学习后,在文本的其他位置仍然可以被识别出来。可以这样理解,一句话在开头说与在结束的时候说,表达的感情应该是一致的,效果也是一样的(绝大多数情况下),这就是文本的平移不变性。

同样的,在处理图片的时候,为了防止数据量过大,防止过拟合,我们需要进行池化的操作,在一维的神经网络中,我们也需要进行池化操作(用到的方法:MaxPooling1D)。

说到这里,大概的主要内容我们就说完了,比较简单,我们该举一个例子了,我们还用之前用到的 IMDB 评论情感分析的数据集,看看效果怎么样,老规矩,看看结果然后最后给出完整代码:

验证

损失

我不知道你是否实践了我前面所有的代码,如果你实践了,你可能知道在最近的循环神经网络的执行时间有的超过了一小时(之前的 LSTM),确实太慢了,这是循环神经网络的弊端。这个网络几分钟就完成了,并且我们看到在第四次左右就开始过拟合了,因此其实只需要训练四次这个网络就可以了,并且我们发现相比 LSTM 的准确率下降的幅度非常小,是可以接受的范围,因此这个网络有其独特的优越性。下面看代码,注意,代码后面还有非常重要的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python3

import time

import matplotlib.pyplot as plt
from keras import layers
from keras.datasets import imdb
from keras.models import Sequential
from keras.optimizers import RMSprop
from keras.preprocessing import sequence


def imdb_run():
max_features = 10000
max_len = 500
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
x_train = sequence.pad_sequences(x_train, maxlen=max_len)
x_test = sequence.pad_sequences(x_test, maxlen=max_len)

model = Sequential()
model.add(layers.Embedding(max_features, 128, input_length=max_len))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.MaxPooling1D(5))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(1))
model.summary()
model.compile(optimizer=RMSprop(lr=1e-4), loss='binary_crossentropy', metrics=['acc'])
history = model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2)

# 画图
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 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.show()
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()


if __name__ == "__main__":
time_start = time.time()
imdb_run()
time_end = time.time()
print('Time Used: ', time_end - time_start)

我们知道卷积神经网络是用于提取特征的,准确点说是用于提取局部特征的,因此对于这种文本序列,能提取一小段序列的特征,范围无法过长,这是它的一点局限性,循环神经网络的优势是能获取较长范围内的关联性,那如何将这两者的优势结合起来,即可以在较长范围内将其关联起来,又可以有一个比较好的训练速度呢?

没错,就是将两者结合起来,这是个大招,首先用一维卷积神经网络去提取特征,然后将提取出的特征用循环神经网络将前后相关联,这既能减少数据量,又能兼顾前后的联系,非常高级的用法。

image

网络结构类似于这种:

1
2
3
4
5
6
model = Sequential()
model.add(layers.Conv1D(32, 5, activation='relu',input_shape=(None, float_data.shape[-1])))
model.add(layers.MaxPooling1D(3))
​model.add(layers.Conv1D(32, 5, activation='relu'))
model.add(layers.GRU(32, dropout=0.1, recurrent_dropout=0.5))
model.add(layers.Dense(1))

本篇讨论的问题是将一维卷积神经网络用于文本序列的处理,就到这里,文本的处理也将算是告一段落,接下来后续的文章将讨论一些其他的问题。