核心代码如下:
# 从零实现 GRU,其实与 RNN 的主要区别在于有门的设计
# 确定哪些是需要初始化模型参数:
# 核心公式就是一下 四个
# Rt=σ(XtWxr+Ht−1Whr+br), Zt=σ(XtWxz+Ht−1Whz+bz), H~t=tanh(XtWxh+(Rt⊙Ht−1)Whh+bh), Ht=Zt⊙Ht−1+(1−Zt)⊙H~t
# 初始化参数有:Ht-1 的维度
vocab_size = 1027
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
class GRU(object):
def __init__(self, hiden_dim, params=None):
"""
初始化待训练的参数 variabel
params = (Wxr, Whr, br, Wxz, Whz, bz, Wxh, Whh, bh)
"""
# 是否加载预训练参数
if params:
self.Wxr, self.Whr, self.br, self.Wxz, self.Whz, self.bz, self.Wxh, self.Whh, self.bh = params
else:
self.Wxr = self._ones(shape=(vocab_size, num_hiddens))
self.Whr = self._ones(shape=(num_hiddens, num_hiddens))
self.Wxz = self._ones(shape=(vocab_size, num_hiddens))
self.Whz = self._ones(shape=(num_hiddens, num_hiddens))
self.Wxh = self._ones(shape=(vocab_size, num_hiddens))
self.Whh = self._ones(shape=(num_hiddens, num_hiddens))
self.br = tf.Variable(tf.zeros([1,num_hiddens]), dtype=tf.float32)
self.bz = tf.Variable(tf.zeros([1,num_hiddens]), dtype=tf.float32)
self.bh = tf.Variable(tf.zeros([1,num_hiddens]), dtype=tf.float32)
self.Whq = self._ones(shape=(num_hiddens, vocab_size))
self.bq = tf.Variable(tf.zeros([1,vocab_size]), dtype=tf.float32)
def _ones(self,shape):
return tf.Variable(tf.random.normal(shape=shape,stddev=0.01,mean=0,dtype=tf.float32))
def net(self, inputs, Ht):
# 展开做循环计算
outputs = []
for X in inputs:
x = tf.reshape(X,(-1, vocab_size))
# Rt=σ(XtWxr+Ht−1Whr+br)
Rt = tf.sigmoid(tf.matmul(x, self.Wxr) + tf.matmul(Ht, self.Whr) + self.br)
# Zt=σ(XtWxz+Ht−1Whz+bz)
# Zt = tf.sigmoid(tf.matmul(x, self.Wxz) + tf.matmul(Ht, self.Whz) + self.bz) 也开始用@用作矩阵乘法
Zt = tf.sigmoid(x @ self.Wxz + Ht @ self.Whz + self.bz)
# 计算 H~t=tanh(XtWxh+(Rt⊙Ht−1)Whh+bh),注意这里的Rt和Zt都是 num_hiddens*num_hiddens 维,才能这样操作
# H_hat = tf.tanh(tf.matmul(x,self.Wxh) + tf.multiply(Rt,Ht)@self.Whh + self.bh)
H_hat = tf.tanh(x@self.Wxh + (Rt * Ht)@self.Whh + self.bh)
# Ht=Zt⊙Ht−1+(1−Zt)⊙H~t
Ht = Zt * Ht + (1-Zt) * H_hat
Y = tf.matmul(Ht, self.Whq) + self.bq
outputs.append(Y)
return outputs, Ht
注意:‘for X in inputs’此类写法其实有些问题,容易造成误解在 batch_size 维度上做循环,实则是在 time_step 上做得拆解,建议使用:¶
for x in tf.unstack(inputs, axis=1)
在时间维度上做循环更加合理,不过此处不做更改了