How to register a custom gradient for a operation composed of tf operations
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If an operation is built from ordinary TensorFlow ops, you usually do not need to register a low-level gradient override in the graph. The modern and simplest approach is to wrap the forward computation with @tf.custom_gradient and return a backward function that defines how gradients should propagate.
Why Use a Custom Gradient
TensorFlow's automatic differentiation is often enough, but there are cases where you want to define the backward pass yourself:
- the default gradient is numerically unstable
- the backward formula can be made cheaper than the automatic one
- the operation is mathematically correct but you want a special training-time surrogate gradient
- you want to stop or reshape gradient flow deliberately
When the operation is composed of TensorFlow ops rather than a custom C++ kernel, @tf.custom_gradient is usually the right abstraction.
A Minimal @tf.custom_gradient Example
The decorated function returns the forward result and a nested gradient function.
The forward pass still computes x * x, but the backward pass is now whatever grad returns.
Apply It to a Composite TensorFlow Operation
Suppose your forward computation is built from several TensorFlow ops and you want to keep the forward logic readable while defining a custom backward rule.
This is a good example of a composite op whose gradient you may want to control explicitly for stability or clarity.
Multiple Inputs Are Supported
If the custom operation takes several inputs, the gradient function must return one gradient per input.
The order matters. The returned gradients must line up with the order of the original inputs.
Use It with GradientTape
The easiest way to test a custom gradient is with tf.GradientTape. That gives immediate confirmation that the backward rule is being used.
If the result does not match your intended derivative, fix the gradient function before embedding the op in a larger model.
When to Reach for Lower-Level Gradient Registration
Older TensorFlow 1 style code sometimes used graph-level gradient overrides and registration hooks. Those still exist in some legacy workflows, but they are usually not necessary when the operation is just a Python function composed of TensorFlow ops.
The practical rule is:
- composite Python op: prefer
@tf.custom_gradient - true custom kernel or lower-level graph integration: use the lower-level registration tools if needed
For most TensorFlow 2 code, @tf.custom_gradient is the clean answer.
Be Careful with Correctness
A custom gradient can intentionally differ from the mathematical derivative, but if you do that, it should be a deliberate modeling decision. Otherwise you can silently train a model on incorrect gradients.
That is why custom-gradient code should be small, tested, and well named. It is one of the easiest places to create a model that runs fine and still learns the wrong thing.
Common Pitfalls
- Reaching for low-level registration when
@tf.custom_gradientis enough. - Returning the wrong number of gradients for multi-input functions.
- Writing a custom backward rule without testing it under
GradientTape. - Forgetting that the gradient function receives upstream gradient
dy. - Using a surrogate gradient without documenting that it intentionally differs from the true derivative.
Summary
- For a TensorFlow operation composed of TensorFlow ops,
@tf.custom_gradientis usually the right tool. - Return the forward result plus a nested function that defines the backward pass.
- The gradient function must account for upstream gradient flow through
dy. - Multi-input functions must return one gradient per input.
- Test the custom backward rule explicitly before using it in model training.

