Theano 梯度扫描操作失败

Posted

技术标签:

【中文标题】Theano 梯度扫描操作失败【英文标题】:Theano gradients failing over scan operation 【发布时间】:2020-06-27 01:30:08 【问题描述】:

在 theano 中实现深度网络让我可以从头开始精确控制我的层。我知道它不再受支持,但在我看来仍然在积极使用(至少从我这里:->)。无论如何,我注意到一些与通过扫描操作计算梯度有关的行为。

我有以下内部函数(RNN 内部迭代步骤的一部分,如果需要,我可以提供):

    def iter_step(x_step, h):
        ...

        return hidden, obj, est

我有一组用于计算梯度的参数h_paramso_paramse_params

h, o, e = iter_step(x_step, h)
hidden_grads = T.grad(T.sum(h), h_params)
obj_grads = T.grad(T.sum(o), o_params)
est_grads = T.grad(T.sum(est), e_params)

一切都很好。我添加一个扫描操作

[h_n, obj_n, x_n], _ = theano.scan(
    fn=iter_step,
    sequences=[x],
    outputs_info=[T.as_tensor_variable(np.zeros(model.h_shape), model.h.dtype),
                  None,
                  None],
    )

评估很好,但是在相同参数上计算梯度现在是个问题:

# Works
h_n0 = theano.function([], h_n)()
# Fails
h_n_grads = T.grad(T.sum(h_n), h_params)

---------------------------------------------------------------------------
NullTypeGradError                         Traceback (most recent call last)
<ipython-input-32-0d7c8a65d265> in <module>
----> 1 h_n_grads = T.grad(T.sum(h_n), h_params)

/usr/local/lib/python3.6/dist-packages/theano/gradient.py in grad(cost, wrt, consider_constant, disconnected_inputs, add_names, known_grads, return_disconnected, null_gradients)
    609             if null_gradients == 'raise':
    610                 raise NullTypeGradError("tensor.grad encountered a NaN. " +
--> 611                                         rval[i].type.why_null)
    612             else:
    613                 assert null_gradients == 'return'

NullTypeGradError: tensor.grad encountered a NaN. This variable is Null because the grad method for input 4 (Subtensorint64.0) of the forcpu,scan_fn op is mathematically undefined. Depends on a shared variable

这是为什么?我还无法调试 - 图形未断开连接,手动展开扫描可提供良好的渐变。梯度计算应该通过扫描操作进行。如果可以计算 h(iter_step 的第一个输出)上的梯度,为什么不能在扫描的模拟输出上计算?

【问题讨论】:

【参考方案1】:

问题解决了。上面的iter_step 包含一个采样步骤,类似于

def sample(self, mu, logSigma):
    global SEED
    srng = T.shared_randomstreams.RandomStreams(seed=SEED)
    dev = srng.normal((self.batch_size, self.n_latent[-1]))
    z = mu + T.exp(0.5 * logSigma) * dev
    return z

通过此计算得出的梯度无法通过scan 操作保留下来。分离样本中的所有参数依赖项仍然不起作用。最终奏效的是在dev 中创建偏差并将其作为non_sequences 之一在扫描中传递,如

    [h_n, obj, x],inner_updates = theano.scan(
        fn=iter_step,
        sequences=[x_in],
        outputs_info=[T.as_tensor_variable(np.zeros(self.h_shape), self.h.dtype),
                      None,
                      None],
        non_sequences=[T.as_tensor_variable(self.srng.normal((self.batch_size, self.n_latent[-1])), self.h.dtype)],
        )

我知道为什么这个scan 上的渐变会失败,而即使使用randomstreams.RandomStreams 对象也可以进行简单的扫描操作。我会深入研究一下。

总结:如果您的发行版允许,请使用重新参数化技巧,传入白化偏差,预先参数化为 non_sequences。这似乎适用于所有情况。

【讨论】:

以上是关于Theano 梯度扫描操作失败的主要内容,如果未能解决你的问题,请参考以下文章

Theano / Pytorch / Tensorflow 可以自动计算以下梯度吗?

theano-windows学习笔记二十——LSTM理论及实现

Theano中的导数

Android 颜色渲染 SweepGradient扫描/梯度渲染

使用 Keras 获取模型输出 w.r.t 权重的梯度

深度学习之六,基于RNN(GRU,LSTM)的语言模型分析与theano代码实现