You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TensorFlow2手动实现RNN时GradientTape返回None的问题排查

解决TensorFlow2手动实现RNN时梯度为None的问题

你遇到的问题核心在于RNN前向传播中错误地使用了tf.Variable和assign操作,这会打断TensorFlow的梯度计算图追踪,导致无法生成有效梯度。

问题原因分析

在ModelRNN的__call__方法里,你用tf.Variable定义了y_last和Ys,并通过assign方法更新它们的值:

y_last = tf.Variable(tf.zeros([batch_size, self.n_neurons], dtype=tf.float32))
Ys = tf.Variable(tf.zeros([batch_size, num_time_steps, self.n_neurons], dtype=tf.float32))
# ...
y_last.assign(yt)
Ys[:, t, :].assign(yt)

TensorFlow的GradientTape默认不会追踪tf.Variable的原地赋值(assign)操作带来的梯度依赖——因为这些操作是直接修改变量的存储,而不是构建计算图中的张量依赖关系。y_last和Ys作为前向传播的中间状态,完全不需要是可训练的Variable,用普通张量即可。

修改后的代码方案

把y_last和Ys改成普通Tensor,用常规的张量赋值替代assign操作:

import numpy as np
import tensorflow as tf

def loss(predicted_y, target_y):
    return tf.reduce_mean(tf.square(predicted_y - target_y))

class ModelRNN(object):
    def __init__(self, n_inputs, n_neurons):
        self.n_inputs = n_inputs
        self.n_neurons = n_neurons
        # weights for new input
        self.Wx = tf.Variable(tf.random.normal(shape=[self.n_inputs, self.n_neurons], dtype=tf.float32))
        # weights for previous output
        self.Wy = tf.Variable(tf.random.normal(shape=[self.n_neurons, self.n_neurons], dtype=tf.float32))
        # bias weights
        self.b = tf.Variable(tf.zeros([1, self.n_neurons], dtype=tf.float32))
    def __call__(self, X_batch):
        # get shape of input,转为普通列表避免TensorShape对象问题
        batch_size, num_time_steps, _ = X_batch.get_shape().as_list()
        # 用普通Tensor初始化中间状态,而非tf.Variable
        y_last = tf.zeros([batch_size, self.n_neurons], dtype=tf.float32)
        # 用列表存储每个时间步输出,最后堆叠成张量
        Ys = []
        for t in range(num_time_steps):
            Xt = X_batch[:, t, :]
            yt = tf.tanh(tf.matmul(y_last, self.Wy) + tf.matmul(Xt, self.Wx) + self.b)
            Ys.append(yt)
            y_last = yt  # 直接赋值构建计算图依赖
        return tf.stack(Ys, axis=1)

# 测试代码
inputs = tf.convert_to_tensor(np.array([
    # t = 0 t = 1
    [[0, 1, 2], [9, 8, 7]], # instance 1
    [[3, 4, 5], [0, 0, 0]], # instance 2
    [[6, 7, 8], [6, 5, 4]], # instance 3
    [[9, 0, 1], [3, 2, 1]], # instance 4
],dtype=np.float32))
outputs=tf.zeros((4,2,5),dtype=np.float32)  # 目标值无需用tf.Variable
model = ModelRNN(3, 5)
with tf.GradientTape() as t:
    current_loss = loss(model(inputs), outputs)
dWx,dWy,db = t.gradient(current_loss, [model.Wx, model.Wy,model.b])
print(dWx,dWy,db)

修改说明

  1. 替换中间变量类型:将y_last和Ys从tf.Variable改为普通Tensor,确保梯度计算图能追踪每一步的依赖关系。
  2. 用列表堆叠替代assign:用列表存储每个时间步的输出,最后通过tf.stack合并成张量,避免原地修改操作。
  3. 修正形状获取方式:用get_shape().as_list()将TensorShape对象转为普通列表,避免后续循环中的潜在问题。
  4. 简化目标变量:outputs作为目标值不需要是tf.Variable,普通张量即可。

运行修改后的代码,你就能得到正常的梯度值,而不是None了。

内容的提问来源于stack exchange,提问作者Borun Chowdhury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:32:30