nifreeze.analysis.sensitivity module¶
Sensitivity of model predictions to motion of their training volumes.
Two questions are asked of a model, on an aligned dataset:
fidelity(): how well does it predict each held-out volume?sensitivity(): how far do its predictions drift from those on the aligned dataset when some volumes move by known rigid motions, or are distorted as by eddy currents (a perturbation)?
The perturbed copies are given, each with its ground truth (a
Perturbation): a simulator produces realistic ones
(nifreeze.analysis.phantoms reads them), and
nifreeze.testing.perturbations resamples a dataset for tests. A fresh
model is built on each copy. A held-out prediction never uses its own volume, so
every volume can be evaluated, the moved ones included: a moved volume’s
prediction sees the motion of the others only. Registration, the estimator and
its intensity window are not involved.
- class nifreeze.analysis.sensitivity.Distortion(self, magnitude: 'float', axis: 'int', seed: 'int | None', degree: 'int', center: 'np.ndarray', radius: 'np.ndarray', frame: 'np.ndarray', coefficients: 'np.ndarray', readout: 'np.ndarray | None' = None, readout_time: 'float | None' = None) None[source]¶
Bases:
objectDisplacements along the phase-encoding axis, polynomial in space, one per volume.
Volume
ishows at each point what lies \(d_i\) mm further along the phase-encoding axis. \(d_i\) is a polynomial of the voxel-axis coordinates, centered oncenterand divided byradiusalong each axis.Without
readout, a distorted volume is resampled at the displaced points, with its intensity scaled by the Jacobian \(1 + \partial d_i / \partial x_{PE}\) so that no signal is created or lost. With it, the volume is formed as an EPI readout forms it, which is how TRXScan simulates eddy currents: the displacement is that of an off-resonance field \(f_i = d_i / (T \, \delta)\) (Hz), with \(T\) the total readout time and \(\delta\) the voxel size along phase encoding, and each phase-encoding line accrues the field’s phase at the time it is acquired.Method generated by attrs for class Distortion.
- basis(points: ndarray) tuple[ndarray, ndarray][source]¶
Evaluate each term, and its derivative along the phase-encoding axis.
- distort(volume: ndarray, affine: ndarray, coefficients: ndarray, order: int = 3) ndarray[source]¶
Distort a volume by the displacement that some coefficients give.
- Parameters:
- Returns:
The distorted volume, on the same grid.
- Return type:
- classmethod from_sidecar(eddy: Mapping[str, Any], indices: Sequence[int], n_volumes: int) Distortion[source]¶
The eddy currents of a perturbed copy, from its sidecar.
- Parameters:
- Returns:
The distortion, formed as an EPI readout forms it.
- Return type:
- nifreeze.analysis.sensitivity.NO_PERTURBATION = Perturbation(magnitude=0.0, seed=None, moved=(), motions=(), distortion=None)¶
The ground truth of the aligned dataset.
- class nifreeze.analysis.sensitivity.Perturbation(self, magnitude: 'float', seed: 'int | None', moved: 'tuple[int, ...]' = (), motions: 'tuple[np.ndarray, ...]' = (), distortion: 'Distortion | None' = None) None[source]¶
Bases:
objectThe ground truth of a perturbed dataset.
Some volumes moved by rigid motions, and optionally every volume distorted.
Method generated by attrs for class Perturbation.
- distortion: Distortion | None¶
Distortion of every volume, applied after its motion, in the scanner’s frame.
- nifreeze.analysis.sensitivity.fidelity(model: str | ~collections.abc.Callable[[...], ~typing.Any], dataset: ~typing.Any, evaluate: ~collections.abc.Iterable[int] | None = None, mask: ~numpy.ndarray | None = None, metrics: ~collections.abc.Mapping[str, ~collections.abc.Callable[[...], float]] = {'cc': <function masked_cc>, 'mi': <function mutual_information>, 'nrmse': <function nrmse>}, **model_kwargs: ~typing.Any) list[dict[str, Any]][source]¶
Compare each held-out prediction with its volume, on an aligned dataset.
- Parameters:
model (
strorCallable) – The model (seeheld_out_predictions()).dataset (
BaseDataset) – The aligned dataset.evaluate (
Iterable, optional) – Volumes to predict (all ifNone).mask (
ndarray, optional) – Voxels compared; the dataset’s brain mask, or the whole volume, ifNone.metrics (
Mapping, optional) – Similarity metrics by name, with the signature ofmasked_cc().**model_kwargs – Arguments for the model.
- Returns:
One row per evaluated volume, with each metric.
- Return type:
- nifreeze.analysis.sensitivity.held_out_predictions(model: str | Callable[[...], Any], dataset: Any, evaluate: Iterable[int] | None = None, **model_kwargs: Any) dict[int, ndarray][source]¶
Predict each evaluated volume from all the others, with a fresh model.
- Parameters:
model (
strorCallable) – A model name forModelFactory, or a callable building a model from a dataset (such as a model class).dataset (
BaseDataset) – The dataset the model is built on.evaluate (
Iterable, optional) – Volumes to predict (all ifNone).**model_kwargs – Arguments for the model.
- Returns:
The prediction of each evaluated volume.
- Return type:
- nifreeze.analysis.sensitivity.influence(model: Any) ndarray[source]¶
Analytic influence of each volume on the predictions of a linear smoother.
The closed-form models predict volume \(i\) as \(\mathbf{S}_{i} \mathbf{Y}\), so moving volume \(j\) by \(\Delta\mathbf{y}_j\) changes every prediction by \(\mathbf{S}_{:, j} \Delta\mathbf{y}_j\).
- Parameters:
model (
ClosedFormLOVOMixin) – A closed-form model.- Returns:
The norm of each column of the smoother, \(\|\mathbf{S}_{:, j}\|_2\).
- Return type:
- nifreeze.analysis.sensitivity.sensitivity(model: str | Callable[[...], Any], reference: Any, cells: Iterable[tuple[Perturbation, Any]], evaluate: Iterable[int] | None = None, mask: ndarray | None = None, metrics: Mapping[str, Callable[[...], float]] | None = None, **model_kwargs: Any) list[dict[str, Any]][source]¶
Measure how far held-out predictions drift from those on a reference dataset.
Each cell pairs a perturbed copy of
reference(same grid, same volumes, some moved or distorted) with thePerturbationthat made it. Each prediction on a copy is compared with the prediction of the same volume on the reference. The change is reported as an NRMSE divided by the standard deviation of the actual volume, not by that of the reference prediction: a smooth prediction would otherwise inflate its own error, and models could not be compared.- Parameters:
model (
strorCallable) – The model (seeheld_out_predictions()).reference (
BaseDataset) – The aligned dataset.cells (
Iterable) – Pairs of aPerturbationand the perturbed copy ofreference, or a function without arguments that loads it (each copy is then held in memory only while it is analyzed).evaluate (
Iterable, optional) – Volumes to predict (all ifNone), moved or not.mask (
ndarray, optional) – Voxels compared; the reference’s brain mask, or the whole volume, ifNone.metrics (
Mapping, optional) – Further metrics by name, with the signature ofmasked_cc(), called asmetric(perturbed, aligned, mask).**model_kwargs – Arguments for the model.
- Returns:
One row per cell and evaluated volume, with the volume’s
shell(its b-value to the nearest 100 s/mm², NaN without gradients), thenrmse, each further metric, whether that volume was itselfmoved, and the magnitude of thedistortion(0 without one).- Return type: