使用pee-phole时3个gate会考虑到Ct-1和Ct,使用三种权重去倍乘,input门和forget门对Ct-1进行倍乘,而输出门对Ct进行倍乘;
常规的LSTM的gate只考虑ht-1和Xt,并且两者拼接(concat)在一起;而peephole中的Ct-1和Ct不是和ht-1,Xt拼接,而是和它们叠加(+)在一起,代码如下
if self._use_peepholes:
c = (sigmoid(f + self._forget_bias + self._w_f_diag * c_prev) * c_prev +
sigmoid(i + self._w_i_diag * c_prev) * self._activation(j))
if self._use_peepholes:
m = sigmoid(o + self._w_o_diag * c) * self._activation(c)
常规的LSTM(BasicLSTMCell)其输出和state使用的节点数是一样的,都是num_units参数,但是这里会将ht经过一层神经网络转换为输出m,节点数为num_proj参数,代码如下
self._proj_kernel = self.add_variable(
"projection/%s" % _WEIGHTS_VARIABLE_NAME,
shape=[self._num_units, self._num_proj],
initializer=self._initializer,
partitioner=maybe_proj_partitioner)
if self._num_proj is not None:
m = math_ops.matmul(m, self._proj_kernel)
而由于输出m和状态c的宽度不同,因此总状态不能按列平分,而是分成num_units列+num_proj列
if self._state_is_tuple:
(c_prev, m_prev) = state
else:
c_prev = array_ops.slice(state, [0, 0], [-1, self._num_units])
m_prev = array_ops.slice(state, [0, self._num_units], [-1, num_proj])
new_state = (LSTMStateTuple(c, m) if self._state_is_tuple else
array_ops.concat([c, m], 1))
另外c在激活之前进行clip操作,把值限制在一定范围之内,然后再激活形成输出
if self._cell_clip is not None:
# pylint: disable=invalid-unary-operand-type
c = clip_ops.clip_by_value(c, -self._cell_clip, self._cell_clip)
# pylint: enable=invalid-unary-operand-type
if self._use_peepholes:
m = sigmoid(o + self._w_o_diag * c) * self._activation(c)
else:
m = sigmoid(o) * self._activation(c)