tf.gradients输出维度不符:为何未得到预期的3×3矩阵?
问题分析:TensorFlow tf.gradients 返回结果不符合预期
先看你的代码(补全了缺失的numpy导入):
import tensorflow as tf import numpy as np tf.reset_default_graph() x = tf.placeholder(tf.float32, [None, 3],name='x') W_1 = tf.get_variable('W_1', [3,3], dtype = tf.float32, initializer=tf.constant_initializer(1.0)) layer_out = tf.matmul(x, W_1, name = 'layer_out') sess = tf.Session() sess.run(tf.global_variables_initializer()) sess.run([tf.gradients(layer_out, [x])], feed_dict = {x: np.array([[1,7,5]])} )
运行返回结果:
[[array([[3., 3., 3.]], dtype=float32)]]
你预期得到3×3矩阵或对应维度的列表,但实际结果是1×3向量——核心原因在于你没注意到tf.gradients的设计逻辑:
为什么会自动求和?
文档里提到的sum(dy/dx)是关键:tf.gradients本质是为标量损失函数设计的。当你传入的输出是一个张量(比如这里的layer_out是[1,3]的向量)时,TensorFlow会自动对这个输出张量的所有元素求和,再计算这个总和对输入x的梯度。
拆解你的例子:
W_1全为1,所以layer_out的每个元素都是x[0]+x[1]+x[2](也就是1+7+5=13,三个元素都是13)- 对三个13求和得到39,求39对
x每个元素的导数:39 = 3*(x₀+x₁+x₂),所以每个导数都是3,最终得到[[3,3,3]]
如何获取完整的雅可比矩阵?
如果想要输出张量每个元素对输入张量每个元素的偏导数(也就是雅可比矩阵),不能直接用tf.gradients,推荐两种方法:
方法1:循环计算每个输出元素的梯度(兼容TF1.x)
手动遍历layer_out的每个元素,分别计算它对x的梯度,最后拼接结果:
import tensorflow as tf import numpy as np tf.reset_default_graph() x = tf.placeholder(tf.float32, [None, 3],name='x') W_1 = tf.get_variable('W_1', [3,3], dtype = tf.float32, initializer=tf.constant_initializer(1.0)) layer_out = tf.matmul(x, W_1, name = 'layer_out') # 逐个计算输出元素对x的梯度 jacobian_components = [] for idx in range(layer_out.shape[1]): grad = tf.gradients(layer_out[:, idx], x)[0] jacobian_components.append(grad) # 把列表转为张量,形状是[3, 1, 3],对应3个输出元素各自的梯度 jacobian = tf.stack(jacobian_components) sess = tf.Session() sess.run(tf.global_variables_initializer()) result = sess.run(jacobian, feed_dict={x: np.array([[1,7,5]])}) print(result)
运行后你会得到每个输出元素对应的梯度,调整维度就能得到你想要的3×3矩阵。
方法2:用tf.jacobian(TF1.15+或TF2.x更方便)
如果你的TensorFlow版本支持,直接用tf.jacobian可以一步到位计算完整的雅可比矩阵,比如TF2.x的示例:
import tensorflow as tf import numpy as np x = tf.constant([[1,7,5]], dtype=tf.float32) W_1 = tf.Variable(tf.ones([3,3], dtype=tf.float32)) with tf.GradientTape() as tape: layer_out = tf.matmul(x, W_1) # 计算雅可比矩阵,形状为[输出batch, 输出维度, 输入batch, 输入维度] jacobian = tape.jacobian(layer_out, x) print(jacobian.numpy())
这个结果包含了所有偏导数,完全符合你想要的3×3矩阵结构。
内容的提问来源于stack exchange,提问作者user1700890
相关产品推荐
相关产品推荐

