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)
修改说明
- 替换中间变量类型:将
y_last和Ys从tf.Variable改为普通Tensor,确保梯度计算图能追踪每一步的依赖关系。 - 用列表堆叠替代assign:用列表存储每个时间步的输出,最后通过
tf.stack合并成张量,避免原地修改操作。 - 修正形状获取方式:用
get_shape().as_list()将TensorShape对象转为普通列表,避免后续循环中的潜在问题。 - 简化目标变量:
outputs作为目标值不需要是tf.Variable,普通张量即可。
运行修改后的代码,你就能得到正常的梯度值,而不是None了。
内容的提问来源于stack exchange,提问作者Borun Chowdhury
相关产品推荐
相关产品推荐

