下面我们来分别介绍复位门和更新门的原理与功能。
[1] J. Westhuizen 和 J. Lasenby, “The unreasonable effectiveness of the forget gate,” CoRR, 卷 abs/1804.04849, 2018.
复位门用于控制上一个时间戳的状态 h t − 1 \boldsymbol h_{t-1} ht−1进入GRU的量。门控向量 g r \boldsymbol g_r gr由当前时间戳输入 x t \boldsymbol x_t xt和上一时间戳状态 h t − 1 \boldsymbol h_{t-1} ht−1变换得到,关系如下:
g r = σ ( W r [ h t − 1 , x t ] + b r ) \boldsymbol g_r=σ(\boldsymbol W_r [\boldsymbol h_{t-1},\boldsymbol x_t ]+\boldsymbol b_r) gr=σ(Wr[ht−1,xt]+br)
其中 W r \boldsymbol W_r Wr和 b r \boldsymbol b_r br为复位门的参数,由反向传播算法自动优化, σ σ σ为激活函数,一般使用Sigmoid函数。门控向量 g r = 0 \boldsymbol g_r=0 gr=0时,新输入 h ~ t \tilde \boldsymbol h_t h~t全部来自于输入 x t \boldsymbol x_t xt,不接受 h t − 1 \boldsymbol h_{t-1} ht−1,此时相当于复位 h t − 1 \boldsymbol h_{t-1} ht−1。当 g r = 1 \boldsymbol g_r=1 gr=1时, h t − 1 h_{t-1} ht−1和输入 x t \boldsymbol x_t xt共同产生新输入 h ~ t \tilde\boldsymbol h_t h~t,如下图所示:
更新门用控制上一时间戳状态 h t − 1 \boldsymbol h_{t-1} ht−1和新输入 h ~ t \tilde\boldsymbol h_t h~t对新状态向量 h t \boldsymbol h_t ht的影响程度。更新门控向量 g z \boldsymbol g_z gz由
g z = σ ( W z [ h t − 1 , x t ] + b z ) \boldsymbol g_z=σ(\boldsymbol W_z [\boldsymbol h_{t-1},\boldsymbol x_t ]+\boldsymbol b_z) gz=σ(Wz[ht−1,xt]+bz)
得到,其中 W z \boldsymbol W_z Wz和 b z \boldsymbol b_z bz为更新门的参数,由反向传播算法自动优化, σ σ σ为激活函数,一般使用Sigmoid函数。 g z \boldsymbol g_z gz用于控制新输入 h ~ t \tilde\boldsymbol h_t h~t信号, 1 − g z 1-\boldsymbol g_z 1−gz用于控制状态 h t − 1 \boldsymbol h_{t-1} ht−1信号:
h t = ( 1 − g z ) h t − 1 + g z h ~ t \boldsymbol h_t=(1-\boldsymbol g_z ) \boldsymbol h_{t-1}+\boldsymbol g_z \tilde\boldsymbol h_t ht=(1−gz)ht−1+gzh~t
可以看到, h ~ t \tilde\boldsymbol h_t h~t和 h t − 1 \boldsymbol h_{t-1} ht−1的更新量处于相互竞争、此消彼长的状态。当更新门 g z = 0 \boldsymbol g_z=0 gz=0时, h t \boldsymbol h_t ht全部来自上一时间戳状态 h t − 1 \boldsymbol h_{t-1} ht−1;当更新门 g z = 1 \boldsymbol g_z=1 gz=1时, h t \boldsymbol h_t ht全部来自新输入 h ~ t \tilde\boldsymbol h_t h~t。
同样地,在TensorFlow中,也有Cell方式和层方式实现GRU网络。GRUCell和GRU层的使用方法和之前的SimpleRNNCell、LSTMCell、SimpleRNN和LSTM非常类似。首先是GRUCell的使用,创建GRU Cell对象,并在时间轴上循环展开运算。例如:
import tensorflow as tf
from tensorflow.keras import layers
x = tf.random.normal([2, 80, 100])
xt = x[:, 0, :] # 得到一个时间戳的输入
# 初始化状态向量,GRU只有一个
h = [tf.zeros([2, 64])]
cell = layers.GRUCell(64) # 新建GRU Cell,向量长度为64
# 在时间戳维度上解开,循环通过cell
for xt in tf.unstack(x, axis=1):
out, h = cell(xt, h)
# 输出形状
print(out.shape)
运行结果如下所示:
(2, 64)
通过layers.GRU类可以方便创建一层GRU网络层,通过Sequential容器可以堆叠多层GRU层的网络。例如:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential
x = tf.random.normal([2, 80, 100])
xt = x[:, 0, :] # 得到一个时间戳的输入
# 初始化状态向量,GRU只有一个
h = [tf.zeros([2, 64])]
net = keras.Sequential([
layers.GRU(64, return_sequences=True),
layers.GRU(64)
])
out = net(x)
# 输出形状
print(out.shape)
运行结果如下所示:
(2, 64)