RNN:为什么是 Wx + Uh 而不是 W[x,h]

数据挖掘 机器学习 线性代数 rnn
2022-02-05 15:09:43

传统上,RNN 的状态计算为

ht=σ(Wx+Uht1+b)

对于 RNN,为什么要将术语加起来(Wx+Uht1)而不是只有一个矩阵乘以一个级联向量:

Wm[x,ht1]

在哪里[...]是串联。


换句话说,我们最终会得到一个长向量{x1,x2,x3,h1,t1,h2,t1,h3,t1}乘以Wm.

似乎第二种方法会有一个更大的矩阵,它的元素比WU结合。

这是否意味着WU是一种简化,使用它们并把结果加起来我们会失去什么?

1个回答

从理论上讲,两个矩阵的公式更清晰,不言而喻,我认为这就是它被更多使用的原因。在实践中,这两种方法实际上都在生产中使用,因此是等效的。这只是一个偏好问题。

张量流

例如,Tensorflow 通常针对性能进行优化。以下是基本 RNN 单元的实现方式(tensorflow/python/ops/rnn_cell_impl.py):

def call(self, inputs, state):
    """Most basic RNN: output = new_state = act(W * input + U * state + B)."""

    gate_inputs = math_ops.matmul(
        array_ops.concat([inputs, state], 1), self._kernel)
    gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)
    output = self._activation(gate_inputs)
    return output, output

单个矩阵乘法更有效,因此它被应用,即使注释描述了具有两个矩阵的扩展公式。

喀拉斯

另一方面,Keras 经常选择简单和清晰而不是性能。这是它的实现(keras/layers/recurrent.py):

def call(self, inputs, states, training=None):
    prev_output = states[0]

    ...

    if dp_mask is not None:
        h = K.dot(inputs * dp_mask, self.kernel)
    else:
        h = K.dot(inputs, self.kernel)
    if self.bias is not None:
        h = K.bias_add(h, self.bias)

    ...
    output = h + K.dot(prev_output, self.recurrent_kernel)
    if self.activation is not None:
        output = self.activation(output)

因此,该类可以轻松地分别访问两个矩阵(self.kernelself.recurrent_kernel)。

火炬

Pytorch 方法更接近于 keras。此外,它们不仅使用单独的内核矩阵,还具有两个偏置向量,并且客户端代码可以访问这四个数组。

尽管存在这些差异,所有三个库在功能上都是等效的。