virgil.fitting
fit finds maximum a posteriori parameters, taking the same arguments as
numpyro_model, plus optional regularizers. gauss_newton_mass turns a fit into a dense
mass matrix for numpyro's NUTS, for sampling large images.
Angles with an AngleVector prior are fitted as 2-D vectors,
with no wrap boundary at 0°/360°.
Maximum a posteriori fits of models, including images.
fit takes the same arguments as
numpyro_model: a model (a template,
or a function of the parameters), a dict of numpyro priors whose keys are the
free parameters, and the data, plus optional regularizers (see
virgil.imaging). It finds the maximum a
posteriori parameters with Levenberg–Marquardt, L-BFGS or Adam, optimizing
each parameter in unconstrained coordinates through the bijection to its
prior's support, in float64 by default. A parameter whose prior is uniform in
some coordinate (a log-uniform scale, an isotropic inclination) is fitted in
that flat coordinate, where its prior adds nothing to the loss, so that
Levenberg–Marquardt works with the Jeffreys priors. To sample the same
posterior, pass the same arguments to numpyro_model.
FitResult
dataclass
The result of fit.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
SourceModel or list
|
The fitted model, or models (one per dataset) if the model function returned a list. |
values |
dict
|
The fitted parameter values, keyed by path, in the model's own
parameters (not the flat coordinates some are fitted in). An angle with an
|
info |
dict
|
|
model
instance-attribute
values
instance-attribute
info
instance-attribute
__init__(model, values, info)
fit(model, priors, data, regularizers=(), *, noise=None, init=None, method=None, max_steps=None, gtol=0.0001, max_step_size=2.0, lbfgs_memory=50, learning_rate=0.01, cg_steps=50, dtype='float64', likelihoods=(), time_limit=None, progress=None)
Find the maximum a posteriori parameters of a model given data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or callable
|
A template model whose leaves at the paths in |
required |
priors
|
dict[str, Distribution]
|
A prior for each free parameter, keyed by its path (e.g.
A prior that is uniform in some coordinate of its parameter has a
flat coordinate, and |
required |
data
|
OIData or sequence of OIData
|
The data, fitted jointly. May be empty ( |
required |
regularizers
|
sequence
|
Penalties added to the loss, e.g. from
|
()
|
noise
|
dict or list of dict
|
Priors on error-inflation terms to fit with the parameters:
|
None
|
init
|
dict
|
Starting values by path (or |
None
|
method
|
(lm, lbfgs, adam)
|
|
"lm"
|
max_steps
|
int
|
Step limit (defaults: 1000 for LM, 20000 for L-BFGS, 2000 for Adam). It sets the length of LM's and Adam's loops, so a new value recompiles them (not L-BFGS); other numbers do not. |
None
|
gtol
|
float
|
LM and L-BFGS stop when no component of the gradient of the loss
per data point exceeds |
0.0001
|
max_step_size
|
float
|
L-BFGS moves no unconstrained coordinate by more than this per step
(for a log-brightness pixel, a factor |
2.0
|
lbfgs_memory
|
int
|
Number of past steps L-BFGS keeps to model the curvature (default 50; optax's own default is 10). On regularized images, 10 left the fits short of their optimum at many weights, and weakly regularized ones running to the step limit; 50 found lower losses and converged in fewer steps, at a higher cost per step. |
50
|
learning_rate
|
float
|
Adam's learning rate, in unconstrained coordinates. |
0.01
|
cg_steps
|
int
|
Conjugate-gradient steps per LM step, for more than 200 coordinates. The inner solve runs for exactly this many steps, or as many as there are coordinates if fewer: its tolerances are zero, because an inner solve that stops at a step limit would abort the outer one. |
50
|
dtype
|
(float64, float32)
|
Precision of the fit. The default runs in float64 inside a local
|
"float64"
|
likelihoods
|
sequence
|
Further Gaussian likelihood terms that are not visibilities: each is
a callable of the fitted values (a dict, by path or keyword) that
returns whitened residuals, such as
|
()
|
time_limit
|
float
|
Wall-clock budget (seconds) for the optimizer, for LM and L-BFGS.
The optimizer then runs in chunks of steps, and stops after the
first chunk that ends past the limit, unconverged, with
|
None
|
progress
|
callable or bool
|
Report progress between chunks (as for |
None
|
Returns:
| Type | Description |
|---|---|
FitResult
|
The fitted model, parameter values and diagnostics. A warning is
raised if LM or L-BFGS did not converge; for L-BFGS it says whether
the fit reached
|
gauss_newton_mass(model, priors, data, values, *, likelihoods=())
A dense NUTS mass matrix from the Gauss–Newton curvature at a fit.
Near the maximum a posteriori, the posterior is close to a Gaussian whose precision, in the unconstrained coordinates z that numpyro samples, is the Gauss–Newton matrix JᵀJ. Here J is the Jacobian of the whitened residuals with respect to z, including the residuals of the priors (so a standard-normal prior adds the identity). Giving NUTS the inverse, (JᵀJ)⁻¹, as its inverse mass matrix whitens that Gaussian. Directions the data fix tightly then take the same step size as those left to the prior. Without it, the step size shrinks to suit the tightest direction, and NUTS needs its full tree depth (1023 leapfrog steps) per draw. On a 62² Gaussian-field image fitted to 588 AMI observables, it cut the cost to 63 steps per draw.
Use it at fixed field hyperparameters (σ and ℓ, chosen for example by
log_evidence). The curvature
depends on them, so a matrix computed at one σ and ℓ is wrong when
they move. Every sampled parameter must be in priors: a
tightly constrained one left out (such as an image's flux) keeps its
identity mass and its tiny step size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
As for |
required | |
priors
|
As for |
required | |
data
|
As for |
required | |
values
|
dict
|
The parameter values at which to take the curvature, normally
|
required |
likelihoods
|
sequence
|
Further likelihood terms, as for |
()
|
Returns:
| Type | Description |
|---|---|
dict
|
Keyword arguments for |
Examples:
>>> result = fit(scene, priors, data)
>>> kernel = NUTS(numpyro_model(result.model, priors, data),
... init_strategy=init_to_value(values=result.values),
... **gauss_newton_mass(scene, priors, data, result.values))