Called after a GradientTape's with block has closed, gradient(target, sources) walks backward through the recorded operations, applying the chain rule automatically to compute how much target would change for a small change in each tensor in sources — passing a single tensor as sources returns a single gradient tensor, while passing a list returns a matching list of gradients, one per source. If target doesn't actually depend on a given source through any recorded operation, the returned gradient for that source is None, which is a common and important signal that something in the computation graph isn't connected the way you expected.
1Understanding tape.gradient()
Called after a GradientTape's with block has closed, gradient(target, sources) walks backward through the recorded operations, applying the chain rule automatically to compute how much target would change for a small change in each tensor in sources — passing a single tensor as sources returns a single gradient tensor, while passing a list returns a matching list of gradients, one per source. If target doesn't actually depend on a given source through any recorded operation, the returned gradient for that source is None, which is a common and important signal that something in the computation graph isn't connected the way you expected.
A gradient() result of None for a source you expected a real gradient for is a strong signal of a bug — it means target never actually depended on that source through any operation recorded on the tape, often caused by accidentally breaking the tape's tracking with a non-TensorFlow operation, like a raw NumPy call, somewhere in between.
import tensorflow as tf
x = tf.Variable(2.0)
y = tf.Variable(3.0)
with tf.GradientTape() as tape:
z = x * y + y ** 2
grads = tape.gradient(z, [x, y])
print([g.numpy() for g in grads])2Practical Example
Here is a real-world application of tape.gradient() showing how it is used in production TensorFlow code.
import tensorflow as tf
x = tf.Variable(2.0)
unrelated = tf.Variable(5.0)
with tf.GradientTape() as tape:
y = x ** 2
grad = tape.gradient(y, unrelated)
print(grad)3Best Practices
Follow these guidelines when working with tape.gradient():
1. Check for an unexpected None result from gradient() as a first debugging step whenever a custom training loop's gradients look wrong or missing
2. Pass a list of source tensors to compute multiple gradients in a single gradient() call, rather than calling it separately per tensor, which is both more efficient and the standard pattern
3. Call gradient() only once per tape by default, since a GradientTape releases its recorded resources after a single call unless persistent=True is passed to its constructor
Tip: A gradient() result of None for a source you expected a real gradient for is a strong signal of a bug — it means target never actually depended on that source through any operation recorded on the tape, often caused by accidentally breaking the tape's tracking with a non-TensorFlow operation, like a raw NumPy call, somewhere in between.
import tensorflow as tf
x = tf.Variable(2.0)
y = tf.Variable(3.0)
with tf.GradientTape() as tape:
z = x * y + y ** 2
grads = tape.gradient(z, [x, y])
print([g.numpy() for g in grads])