apply_gradients() takes a list of (gradient, variable) pairs, typically constructed with zip() from a gradients list and a matching variables list, and updates each variable according to the optimizer's specific algorithm, such as Adam's adaptive per-parameter scaling or SGD's simple fixed-size step. This is the manual step that model.fit() performs automatically and invisibly on your behalf every single training step; calling it directly, together with GradientTape and tape.gradient(), is exactly what a fully custom training loop looks like, giving complete, explicit control over every part of the training step.
1Understanding optimizer.apply_gradients()
apply_gradients() takes a list of (gradient, variable) pairs, typically constructed with zip() from a gradients list and a matching variables list, and updates each variable according to the optimizer's specific algorithm, such as Adam's adaptive per-parameter scaling or SGD's simple fixed-size step. This is the manual step that model.fit() performs automatically and invisibly on your behalf every single training step; calling it directly, together with GradientTape and tape.gradient(), is exactly what a fully custom training loop looks like, giving complete, explicit control over every part of the training step.
The full custom-training-loop pattern is always the same three steps in sequence: record the forward pass and loss inside tf.GradientTape(), compute gradients with tape.gradient(), then apply them with optimizer.apply_gradients() — this exact sequence is precisely what model.fit() does internally on every single batch, just hidden behind a simple, automatic interface.
import tensorflow as tf
from tensorflow.keras import layers, Sequential
model = Sequential([layers.Dense(1, input_shape=(1,))])
optimizer = tf.keras.optimizers.SGD(learning_rate=0.1)
x, y_true = tf.constant([[1.0]]), tf.constant([[2.0]])
with tf.GradientTape() as tape:
y_pred = model(x)
loss = tf.reduce_mean((y_true - y_pred) ** 2)
grads = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
print(len(grads))2Practical Example
Here is a real-world application of optimizer.apply_gradients() showing how it is used in production TensorFlow code.
import tensorflow as tf
from tensorflow.keras import layers, Sequential
model = Sequential([layers.Dense(1, input_shape=(1,))])
optimizer = tf.keras.optimizers.SGD(learning_rate=0.1)
weight_before = model.trainable_variables[0].numpy().copy()
x, y_true = tf.constant([[1.0]]), tf.constant([[5.0]])
with tf.GradientTape() as tape:
y_pred = model(x)
loss = tf.reduce_mean((y_true - y_pred) ** 2)
grads = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
weight_after = model.trainable_variables[0].numpy()
print((weight_before != weight_after).any())3Best Practices
Follow these guidelines when working with optimizer.apply_gradients():
1. Use apply_gradients() together with GradientTape and tape.gradient() specifically when a custom training loop needs full manual control that model.fit() doesn't offer
2. Pass zip(gradients, model.trainable_variables) to apply_gradients(), ensuring gradients and variables are matched up in the exact same order
3. Prefer model.fit() for standard training scenarios, reserving a manual apply_gradients() loop for genuinely custom needs like multiple loss terms, custom gradient clipping, or non-standard training procedures
Tip: The full custom-training-loop pattern is always the same three steps in sequence: record the forward pass and loss inside tf.GradientTape(), compute gradients with tape.gradient(), then apply them with optimizer.apply_gradients() — this exact sequence is precisely what model.fit() does internally on every single batch, just hidden behind a simple, automatic interface.
import tensorflow as tf
from tensorflow.keras import layers, Sequential
model = Sequential([layers.Dense(1, input_shape=(1,))])
optimizer = tf.keras.optimizers.SGD(learning_rate=0.1)
x, y_true = tf.constant([[1.0]]), tf.constant([[2.0]])
with tf.GradientTape() as tape:
y_pred = model(x)
loss = tf.reduce_mean((y_true - y_pred) ** 2)
grads = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
print(len(grads))