Combining gradients from multiple objectives#
This tutorial uses aidge_learning.GradientAccumulator to combine gradients already computed for a model output or intermediate tensor. It needs only aidge_core, aidge_learning, and NumPy; the data is synthetic and nothing is downloaded.
This is different from accumulating gradients across training iterations: here, several objectives contribute to the same tensor within one step. The accumulator does not evaluate losses, run backward, or update parameters.
Weighted sum on one target#
Stage a zero base, then add two precomputed gradients. The regularizer contributes one quarter of its raw gradient. Nothing is written to prediction.grad until finalize().
[ ]:
import numpy as np
import aidge_core as core
import aidge_learning as learning
prediction = core.Tensor(np.zeros(2, dtype=np.float32))
task_gradient = core.Tensor(np.array([1.0, 2.0], dtype=np.float32))
regularizer_gradient = core.Tensor(np.array([2.0, -2.0], dtype=np.float32))
accumulator = learning.GradientAccumulator()
accumulator.zero(prediction)
accumulator.add(prediction, task_gradient)
accumulator.add(prediction, regularizer_gradient, weight=0.25)
assert not prediction.has_grad()
accumulator.finalize()
np.testing.assert_allclose(np.asarray(prediction.grad), [1.5, 1.5])
assert accumulator.empty
print("Combined gradient:", np.asarray(prediction.grad))
Failed transaction#
All targets are validated before any gradient is committed. A wrong-shaped contribution for one target leaves every target unchanged, and the staged operations remain available until clear(). This example can also be run independently of the previous cell.
[ ]:
import numpy as np
import aidge_core as core
import aidge_learning as learning
prediction = core.Tensor(np.zeros(2, dtype=np.float32))
prediction.grad = core.Tensor(np.array([1.5, 1.5], dtype=np.float32))
other = core.Tensor(np.zeros(2, dtype=np.float32))
other.grad = core.Tensor(np.array([9.0, 9.0], dtype=np.float32))
invalid = learning.GradientAccumulator()
invalid.set(other, core.Tensor(np.array([1.0, 1.0], dtype=np.float32)))
invalid.add(prediction, core.Tensor(np.array([1.0, 2.0, 3.0], dtype=np.float32)))
try:
invalid.finalize()
except ValueError as exc:
print("Invalid contribution rejected:", exc)
else:
raise AssertionError("An invalid gradient should fail before commit")
np.testing.assert_allclose(np.asarray(other.grad), [9.0, 9.0])
np.testing.assert_allclose(np.asarray(prediction.grad), [1.5, 1.5])
assert invalid.pending_operations == 2
print("Other gradient unchanged:", np.asarray(other.grad))
print("Prediction gradient unchanged:", np.asarray(prediction.grad))
print("Pending operations before clear:", invalid.pending_operations)
invalid.clear()
assert invalid.empty
In a training loop, calculate each objective’s gradient for the relevant output or feature tensor, stage all contributions, and call finalize() before the model’s backward pass. Run backward and the optimizer update separately. If you want to preserve an existing target gradient, omit zero() or set(); add() then extends it.