Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Learning a factor parameter with Gradients

Gradients helps learn factor parameters from a loss on GTSAM’s optimized estimates. This tutorial calibrates an odometry scale using two planar positions and a supervised endpoint. It implements a factor’s parameter derivative in Python, computes the loss gradient, and takes one training step.

The scale is a NumPy vector owned by the application. A ParameterTag links it to the factor’s derivative callback, and parameterGradient handles the graph solve and noise weighting. The class guide describes the other queries and the differentiation model.

The PyTorch tutorial extends this workflow to a small MLP with shared weights, automatic factor VJPs, and held-out evaluation.

Open In Colab

1. The calibration parameter and estimated positions

The learnable parameter is a scale θ\theta multiplying the odometry sensor’s reported displacement, stored in the vector step. The corrected measurement is z(θ)=θ stepz(\theta)=\theta\,\mathrm{step}.

GTSAM estimates two planar positions, x0x_0 and x1x_1, for each fixed scale. A prior factor attracts the start position to a noisy start anchor, another prior attracts the endpoint to a noisy end anchor, and the between factor compares x1−x0x_1-x_0 with the scaled odometry measurement.

The positions are optimization variables stored in Values, while the scale is an application parameter used to construct a factor. The supervised endpoint target is a separate position used only in the training loss; it is not one of the graph’s anchor measurements.

Figure 1. The estimation graph. Round nodes are variables: blue circles are the estimated positions and the gold circle is the learnable scale. Square nodes are factors: 0 is the start anchor, 1 is odometry, and 2 is the end anchor. The dashed arrow shows how the scale changes the odometry measurement.

Reported displacement: [1.  0.2]
Displacement used by the factor: [0.9  0.18]

2. The factor and its parameter derivative

The odometry factor compares the displacement between two Point2 states with the scaled measurement. Its raw factor residual is e=x1−x0−θ stepe=x_1-x_0-\theta\,\mathrm{step}, with state Jacobians −I-I and II. The first callback below returns that raw factor residual and fills in the Jacobians requested by CustomFactor.

The parameter callback supplies a vector–Jacobian product (VJP): the transpose of the raw factor residual’s parameter Jacobian multiplied by an input vector. That input is a factor residual cotangent, which expresses how the loss changes with a raw factor residual change. Here the parameter Jacobian is −step-\mathrm{step}, so the VJP returns −stepT-\mathrm{step}^T times the input, as a one-coordinate vector.

The shared scale_tag selects this parameter without storing its value or registering factors. The callback returns None for other tags. GTSAM supplies the solved states and already accounts for noise weighting, so the callback only differentiates the raw factor residual. It holds both the solved states and the cotangent fixed.

Number of factors: 3

The noise models weight each factor’s coordinate errors. The anchors have standard deviation 0.4 in each coordinate, and odometry has standard deviations [0.3,0.5][0.3,0.5], so horizontal odometry errors receive more weight than vertical errors. Both optimization and parameter differentiation apply these weights automatically.

Ordinary prior factors can coexist with parameterized custom factors. They have no scale callback, yet they influence the fitted positions and therefore the scale gradient. The query needs neither their indices nor an explicit list of parameterized factors.

3. The optimized positions

The optimizer fits the two positions while the scale remains fixed at 0.9. It starts from the positions in initial and minimizes the sum of squared whitened factor residuals from the three factors. The result is stored in solution.

The fitted positions balance the anchors against the odometry measurement, so they generally differ from the anchor positions. At an optimized solution, the estimation objective’s gradient should be close to zero, a condition called stationarity. The reported gradient norm indicates how close the solution is to that condition.

Estimated start: [ 0.11707317 -0.02245614]
Estimated endpoint: [1.08292683 0.12245614]
Inner objective gradient norm: 1.44e-15

4. The training loss

The training loss combines squared endpoint error with a penalty on the scale. Write X⋆=(x0⋆,x1⋆)X^\star=(x_0^\star,x_1^\star) for the optimized positions and α\alpha (penalty_weight) for the penalty coefficient. With the supervised position target, the loss is

L(X⋆,θ)=12∥x1⋆−target∥2⏟endpoint loss+12αθ2⏟scale penalty.L(X^\star,\theta) =\underbrace{\tfrac12\|x_1^\star-\mathrm{target}\|^2}_{\text{endpoint loss}} +\underbrace{\tfrac12\alpha\theta^2}_{\text{scale penalty}}.

The scale affects this loss both through the optimized positions and through the penalty. The parameter derivative must include both contributions.

Endpoint error: [0.08292683 0.12245614]
Endpoint loss: 0.01093618
Scale penalty: 0.02025000
Total loss: 0.03118618

5. The gradient through optimized positions

The endpoint loss has derivative x1⋆−targetx_1^\star-\mathrm{target} with respect to the optimized endpoint. Place it in a VectorValues under X(1). These keyed loss derivatives are called state cotangents. Omitting X(0) gives it zero direct loss derivative, since the training loss does not compare the start position to a target.

parameterGradient propagates this loss derivative through the graph and calls the factors’ VJPs for scale_tag. It sums their contributions and returns the loss gradient through the optimized states. The direct scale penalty is added in the next step.

Figure 2. Computing the scale gradient. parameterGradient invokes the factor callback and accumulates its contribution. The application adds the direct loss derivative.

Scale gradient through the optimized positions: [0.03923642]

6. The total scale gradient

The scale penalty contributes the direct derivative αθ\alpha\theta, where α\alpha is penalty_weight. Adding it to the derivative through the positions gives the total gradient of the training loss:

∇θL=∇θLthrough estimates+αθ.\nabla_\theta L=\nabla_\theta L_{\mathrm{through\ estimates}}+\alpha\theta.

Both terms use the same one-coordinate parameter vector. A larger parameter block would return a correspondingly larger vector, with every participating factor agreeing on its coordinate order.

Through optimized positions: 0.0392364202
Direct scale penalty:        0.0450000000
Total scale gradient:        0.0842364202

7. Comparing with finite differences

A central finite difference estimates the total derivative using losses at θ+ϵ\theta+\epsilon and θ−ϵ\theta-\epsilon. Each trial builds factors with the new scale and reoptimizes the positions. The optimizer’s state gradient must be near zero before comparing derivatives; otherwise optimization error can dominate the finite difference.

The output compares the calculated gradient with finite differences at three step sizes. This example’s raw factor residuals are linear in both the positions and scale, so the derivative model agrees with reoptimization.

Step 1e-04: gradient 0.0842364202, finite difference 0.0842364202
Step 1e-05: gradient 0.0842364202, finite difference 0.0842364202
Step 1e-06: gradient 0.0842364202, finite difference 0.0842364202

8. A gradient-descent update

A gradient-descent update subtracts the loss derivative multiplied by a positive learning rate, η\eta, from the parameter:

θnew=θ−η dLdθ.\theta_{\mathrm{new}}=\theta-\eta\,\frac{dL}{d\theta}.

The updated parameter needs a new position estimate before its loss can be evaluated. With the default values and learning rate 0.5, the update lowers the scale from 0.9 to about 0.858 and reduces the training loss. A new Gradients object is then constructed for a derivative calculation at the updated scale and solution.

Scale: 0.90000000 -> 0.85788179
Loss:  0.03118618 -> 0.02782052
Next scale gradient: [0.07558357]

Training with more parameters

Each parameter vector needs one tag shared between its gradient queries and participating factors. A factor can recognize several tags and return a different VJP for each; several factors can recognize the same tag and their contributions are summed. Query only the tags being trained. Creating another tag with the same name produces a different identity, so keep the original tags across training steps.

An automatic-differentiation library can evaluate the raw factor VJP inside the callback. Differentiate the dot product of the supplied cotangent and raw factor residual with respect to the selected parameter vector, holding the solved states and cotangent fixed. Return a NumPy vector to GTSAM. This keeps the parameter values and their derivative graph entirely in Python.

The graph’s factors and captured parameters must stay fixed during queries. This factory copies each parameter vector into its factor, and a training update constructs a new graph. Noise models remain fixed throughout training; learning their parameters is outside this API.

Advanced: the derivative model

Gradients uses a Gauss-Newton model to differentiate the optimized estimates. The factor residuals here are linear functions plus constants in the combined positions and scale, called jointly affine factor residuals. In this case the model agrees with exact differentiation.

Nonlinear factor residuals can contribute additional second derivatives to the solution derivative. These terms affect both the state Hessian, the estimation objective’s second derivative with respect to the positions, and the mixed state-parameter derivative, which describes how its state gradient changes with the parameter. Gauss-Newton omits the parts of both weighted by factor residuals, so exact reoptimization can give a different derivative even at a converged solution.

The implicit-function theorem obtains the solution derivative from the estimation objective’s zero-gradient equation. The guide’s advanced section derives this equation and the terms used by Gradients.