畅游人工智能之海--Keras教程之循环神经网络(四)

畅游人工智能之海--Keras教程之循环神经网络(四) | 自制循环神经网络

写在开篇

什么是Cell呀?Cell可以理解为循环神经网络的一个单元,如图所示。如果一个网络被定义为 \[ \begin{equation} h_t = \mathcal{F}(h_{t - 1},X_t) \end{equation} \] 那么\(\mathcal{F}\)就是这篇文章要讲的Cell。

1587168739629

换言之,Keras中提供了自定义循环神经网络中的每个计算单元的接口,接下来就结合官方文档给出的一个例子进行分析。

API

自定义下面的代码已经调试过,可直接运行

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
from keras.layers import *
from keras import backend as K
class MinimalRNNCell(Layer):
def __init__(self, units, **kwargs):
self.units = units # output size
self.state_size = units
# 使用方法可以看这个博客 https://blog.csdn.net/qq_38787214/article/details/87902291
super(MinimalRNNCell, self).__init__(**kwargs) # 调用父类的构造函数

def build(self, input_shape):
self.kernel = self.add_weight(shape=(input_shape[-1], self.units),
initializer='uniform',
name='kernel')
self.recurrent_kernel = self.add_weight(
shape=(self.units, self.units),
initializer='uniform',
name='recurrent_kernel')
self.built = True

def call(self, inputs, states):
prev_output = states[0]
h = K.dot(inputs, self.kernel)
output = h + K.dot(prev_output, self.recurrent_kernel)
return output, [output]


import keras
cell = MinimalRNNCell(32)
x = keras.Input((None, 5))
layer = RNN(cell)
y = layer(x)

from keras.models import Model
model = Model(inputs=x,outputs=y)
model.summary()

其中MinimalRNNCell继承Layer类,是一个实现的Cell,按照官网的说法,call函数是必须实现的,也就是\(\mathcal{F}\) 真正做的事情,其中输入输出分别是 (input_at_t, states_at_t)(output_at_t, states_at_t_plus_1)state_sizeoutput_size也必须指明,在实现时units通常代表output_sizebuild函数在这里不是必须的,它本来是Layer类的函数,在这里override了。

封装Cell类

除了可以自定义之外,keras也提供了已经封装好了的Cell类,比如:ConvLSTM2DCellSimpleRNNCellGRUCellLSTMCell。这些类的参数就不再显示在这里了,因为他们跟自己对应的Layer的参数一摸一样,举一个例子,如下所示。

1
2
3
4
# 先是卷积LSTM的层的声明
keras.layers.ConvLSTM2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, return_sequences=False, go_backwards=False, stateful=False, dropout=0.0, recurrent_dropout=0.0)
# 然后是卷积LSTMCell的声明
keras.layers.ConvLSTM2DCell(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0)

既然参数一样,为什么还要制作两个接口呢?其实在使用cell创建RNN层时,可以一次传入多个cell,这样的Layer叫stacked RNN,比如:

1
2
3
4
cells = [MinimalRNNCell(32), MinimalRNNCell(64)]
x = keras.Input((None, 5))
layer = RNN(cells)
y = layer(x)

而通常封装的API都是一个Cell的。因此,分别给出来有一定的意义。

最后

在循环神经网络部分,总共有三种神经网络,分别是RNN,GRU,LSTM,这些都用来处理时间序列的数据。除此之外还有在gpu下使用的版本,在keras中的名字就是CuDNN**。