Process reference: ml

Generated from the docstrings of vaft.process.ml: 24 public functions. Each entry is what vaft.process.describe("ml.<name>") prints. All process categories

Overview

Training is a VAFT processing stage like any other. This package owns the whole path – dataset construction, group-aware splitting, windowing, training, evaluation, threshold calibration, artifact packaging, export, resolution of published versions and inference – while trained weights are distributed through the separate VEST-Tokamak/vaft-nn registry.

Importing it loads NumPy only. Frameworks are backends chosen by name in a ModelSpec and imported on first use: "numpy" (ridge, PCA autoencoder, k-NN distance – no dependency), "sklearn" (one-class SVM, stored as ONNX) and "torch" (MLP, autoencoder, and any architecture task code registers), the last two installed with pip install 'vaft[ml]'. No public signature takes or returns a framework object. Published models come from VEST-Tokamak/vaft-nn, an external dependency configured with $VAFT_NN_HOME like the external codes’ $XHOME.

A split assigns groups – the independent scientific unit, a shot or a simulation case – to partitions, and is made before windowing or augmentation; it then applies unchanged to every derived dataset. Normalisation moments come from the train partition only. Every inference result carries the immutable identity of the model that produced it: exact version and manifest SHA-256, never only a stage alias.

Functions

build_datasetAssemble a fingerprinted dataset from arrays.
build_structured_datasetValidate and fingerprint named scientific samples without retaining a corpus. Convention
calibrate_thresholdSet a decision threshold on a per-sample model output from one partition.
dataset_fingerprintSHA-256 content fingerprint of a dataset.
evaluate_modelStandard ML metrics of a model on one partition, overall and per group.
export_modelExport the network to an inference format and prove it agrees.
fetch_modelDownload a published model’s release assets into the cache, verified.
import_data_rolesImport exact group roles without recomputing a scientific split. Convention
load_datasetLoad a dataset written by save_dataset, verifying its fingerprint.
load_modelResolve a published model and load it, ready for predict.
load_model_artifactLoad an artifact directory, verifying every file against its manifest.
load_model_bundleLoad a bundle directory, verifying every file and member manifest.
predictRun a model on new data and stamp the result with its exact identity.
record_data_useCheck a declared use and return its provenance receipt. Convention
register_architectureMake a task-defined network available to a backend by name.
register_augmentationMake a train-time augmentation available by name.
register_lossMake a task-defined training loss available by name.
resolve_modelResolve a published model to one exact, hash-verified local version. Convention
save_datasetMaterialise a dataset on disk so training can run elsewhere.
save_model_artifactWrite a model as an immutable, hash-pinned artifact directory.
save_model_bundleWrite a multi-model bundle as one immutable, hash-pinned release.
split_groupsAssign independent groups to train/validation/test partitions.
train_modelTrain a model on the train partition and package it with its provenance. Convention
window_datasetCut sliding windows that never cross a group boundary.

build_dataset

Machine-independent source
build_dataset(inputs, groups, *, spec, targets=None, sample_meta=None, feature_spec=None, available=None)

Assemble a fingerprinted dataset from arrays.

The generic entry point for every ML task: an engineered-feature table (pass feature_spec), a waveform or image tensor, or paired input-target supervision (pass targets).

NameTypeUnitDescription
inputsarray_likeany

Samples along the first axis, shape (n, *input_shape).

groupsarray_like-

Independent unit of each sample (shot number, simulation case), shape (n,).

specDatasetSpec-

Name, task, group key and column names of the dataset.

targetsarray_like or None, optionalany

Paired targets, shape (n, *target_shape); None for an unsupervised dataset.

sample_metamapping of str to array_like or None, optionalany

Per-sample arrays that travel with every subset, such as window times.

feature_specFeatureSpec or None, optional-

Column schema of a 2-D engineered-feature table; returns a FeatureDataset when given.

availablearray_like of bool or None, optional-

Per-sample availability: False where the inputs do not exist (a diagnostic absent for that shot). Such samples may hold non-finite inputs; they are stored as zeros and excluded from training, calibration and evaluation, and predict reports them as unavailable. None means every sample is available.

Returns
—DatasetArtifact-

The dataset with its fingerprint filled in.

Raises

  • ModelContractError On mismatched lengths, a non-finite input in an available sample, or a feature table whose width differs from feature_spec.
Applicability · Limitations

Applicability

Machine-independent.

Limitations

Available inputs must be finite: imputation is a scientific choice (train-partition medians, a flag, a drop) that belongs to the caller. Unavailability is per sample, not per feature: a two-stage model whose second stage lacks a diagnostic builds that stage’s dataset with its own available.

build_structured_dataset

Machine-independentConvention-sensitive source
build_structured_dataset(source, groups, *, input_specs, target_specs, spec, sample_meta=None, available=None)
Show source vaft/process/ml/structured.py, lines 199–261
def build_structured_dataset(source, groups, *, input_specs, target_specs, spec, sample_meta=None, available=None):
    """Validate and fingerprint named scientific samples without retaining a corpus.

    Parameters
    ----------
    source : sequence
        Stable provider of StructuredSample objects; lazy file access is allowed [-].
    groups : array_like
        Independent scientific group identity for every sample [-].
    input_specs : mapping
        Named input TensorSpec records with shape, dtype, units and masks [-].
    target_specs : mapping
        Named fixed-shape target TensorSpec records [-].
    spec : DatasetSpec
        Task, group convention and source-role/preprocessing provenance [-].
    sample_meta : mapping or None, optional
        Per-sample machine, time or identity arrays [any].
    available : array_like or None, optional
        Boolean sample-level input availability; True means available [-].

    Returns
    -------
    StructuredDatasetArtifact
        Validated source and schema with a content SHA-256 fingerprint [-].

    Processing steps
    ----------------
    1. Validate names, shapes, mask broadcasts and finite valid elements.
    2. Stream metadata and every input, target and validity array into SHA-256.
    3. Retain the stable source and fingerprint, without retaining sampled tensors.

    Convention
    ----------
    True means valid. Missing observations differ from physical zero. Only a
    leading input axis may be variable; target availability uses separate masks.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    A source must have stable contents. DataLoader workers require a picklable
    source. Fixed output shapes are required; this is not an IMAS data model.

    Provenance
    ----------
    .. [vaft2016] VEST-Tokamak/vaft#2016, structured observation and provenance
       extension of the existing #669 grouped ML lifecycle.
    """
    data = StructuredDatasetArtifact(source=source, groups=groups, input_specs=input_specs,
        target_specs=target_specs, spec=spec, sample_meta=sample_meta or {}, available=available)
    digest = hashlib.sha256(canonical_json({"spec": spec.to_dict(), "schema": data.schema(),
        "groups": data.group_labels.tolist(), "metadata": dict(data.sample_meta),
        "available": data.available_mask}).encode())
    for i in range(data.n_samples):
        sample = data.sample(i)
        for values in (sample.inputs, sample.targets, sample.target_masks):
            for name, a in sorted(values.items()):
                digest.update(canonical_json([name, a.shape, a.dtype.str]).encode())
                digest.update(np.ascontiguousarray(a).tobytes())
    from dataclasses import replace
    return replace(data, fingerprint=digest.hexdigest())

Validate and fingerprint named scientific samples without retaining a corpus.

NameTypeUnitDescription
sourcesequence-

Stable provider of StructuredSample objects; lazy file access is allowed.

groupsarray_like-

Independent scientific group identity for every sample.

input_specsmapping-

Named input TensorSpec records with shape, dtype, units and masks.

target_specsmapping-

Named fixed-shape target TensorSpec records.

specDatasetSpec-

Task, group convention and source-role/preprocessing provenance.

sample_metamapping or None, optionalany

Per-sample machine, time or identity arrays.

availablearray_like or None, optional-

Boolean sample-level input availability; True means available.

Returns
—StructuredDatasetArtifact-

Validated source and schema with a content SHA-256 fingerprint.

Convention

True means valid. Missing observations differ from physical zero. Only a leading input axis may be variable; target availability uses separate masks.

Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Validate names, shapes, mask broadcasts and finite valid elements.
  2. Stream metadata and every input, target and validity array into SHA-256.
  3. Retain the stable source and fingerprint, without retaining sampled tensors.

Applicability

Machine-independent.

Limitations

A source must have stable contents. DataLoader workers require a picklable source. Fixed output shapes are required; this is not an IMAS data model.

Provenance

  1. VEST-Tokamak/vaft#2016, structured observation and provenance extension of the existing #669 grouped ML lifecycle.

calibrate_threshold

Machine-independent source
calibrate_threshold(model, dataset, split, *, partition='validation', output='score', quantile=0.99, name=None, mask=None, population=None)

Set a decision threshold on a per-sample model output from one partition.

The second half of a two-stage detector: a score is only an event candidate once a threshold, fixed on data the network did not train on, has been applied. Attach the result with model.with_calibration(result); predict then reports <name>_exceeds for every sample.

NameTypeUnitDescription
modelModelArtifact-

Trained or loaded model.

datasetDatasetArtifact-

Data with the model’s input shape.

splitSplit-

Group assignment selecting the partition.

partitionstr, optional-

Partition the quantile is taken over.

outputstr, optional-

Per-sample scalar output to threshold.

quantilefloat, optional-

Quantile of that output on the partition, in (0, 1).

namestr or None, optional-

Calibration name; output when None.

maskarray_like of bool or None, optional-

Further restricts the population within the partition – a second stage calibrated only on the first stage’s candidates.

populationstr or None, optional-

Description of mask stored with the result, e.g. "stage1_exceeds".

Returns
—CalibrationResultany

Threshold with the partition, population, quantile, sample count and groups it was set on.

Raises

  • ModelContractError When the output is not a per-sample scalar, the population is empty, quantile is outside (0, 1), mask is given without a population description, the dataset’s columns differ from the model’s, or the partition holds groups the model was fitted on – a model scores its own training samples in-sample (a k-NN against themselves), so that threshold would be too low; use TrainingResult.train_scores for a training-population statistic.
Defaults · Applicability

Defaults

quantile = 0.99 is numerical convenience; a detector’s quantile is a task decision (the VEST IRE prototype used 0.85-0.90).

Applicability

Machine-independent.

dataset_fingerprint

Machine-independent source
dataset_fingerprint(dataset)
Show source vaft/process/ml/dataset.py, lines 46–87
def dataset_fingerprint(dataset):
    """SHA-256 content fingerprint of a dataset.

    Two datasets share a fingerprint only when their spec, feature schema,
    inputs, targets, groups and per-sample metadata are identical in value,
    dtype and shape.  A model records this fingerprint as part of its identity,
    so "which data trained it" is answerable from the artifact alone.

    Parameters
    ----------
    dataset : DatasetArtifact
        The dataset to fingerprint; its own ``fingerprint`` field is ignored [-].

    Returns
    -------
    str
        64-character lowercase hex digest [-].

    Processing steps
    ----------------
    1. Hash the canonical JSON of the spec (and the feature spec, if any).
    2. Hash inputs, targets, string group labels, the availability mask (when
       one is set) and each ``sample_meta`` array in sorted key order, each
       prefixed by its name, dtype and shape.

    Applicability
    -------------
    Machine-independent.
    """
    digest = hashlib.sha256()
    digest.update(canonical_json(dataset.spec.to_dict()).encode())
    if isinstance(dataset, FeatureDataset):
        digest.update(canonical_json(dataset.feature_spec.to_dict()).encode())
    _hash_array(digest, "inputs", np.asarray(dataset.inputs))
    if dataset.targets is not None:
        _hash_array(digest, "targets", np.asarray(dataset.targets))
    _hash_array(digest, "groups", dataset.group_labels.astype(str))
    if dataset.available is not None:
        _hash_array(digest, "available", np.asarray(dataset.available))
    for key in sorted(dataset.sample_meta):
        _hash_array(digest, f"meta:{key}", np.asarray(dataset.sample_meta[key]))
    return digest.hexdigest()

SHA-256 content fingerprint of a dataset.

Two datasets share a fingerprint only when their spec, feature schema, inputs, targets, groups and per-sample metadata are identical in value, dtype and shape. A model records this fingerprint as part of its identity, so “which data trained it” is answerable from the artifact alone.

NameTypeUnitDescription
datasetDatasetArtifact-

The dataset to fingerprint; its own fingerprint field is ignored.

Returns
—str-

64-character lowercase hex digest.

Processing steps · Applicability

Processing steps

  1. Hash the canonical JSON of the spec (and the feature spec, if any).
  2. Hash inputs, targets, string group labels, the availability mask (when one is set) and each sample_meta array in sorted key order, each prefixed by its name, dtype and shape.

Applicability

Machine-independent.

evaluate_model

Machine-independent source
evaluate_model(model, dataset, split, partition='test', *, metrics=None)

Standard ML metrics of a model on one partition, overall and per group.

This is training evaluation. Whether a model is scientifically valid – campaign robustness, negative controls, cross-diagnostic timing – is a task-specific verdict for vaft.validation, not a number here.

NameTypeUnitDescription
modelModelArtifact-

Trained or loaded model.

datasetDatasetArtifact-

Data with the model’s input shape.

splitSplit-

Group assignment selecting the partition.

partitionstr, optional-

Partition to evaluate.

metricscallable or None, optional-

Task metrics, metrics(outputs, subset) returning a mapping of names to floats, where outputs are the model outputs and subset the DatasetArtifact of the evaluated samples; evaluated overall and per group, and merged into the built-in metrics.

Returns
—EvaluationResultany

Aggregate and per-group metrics over the available samples: mse, rmse, mae and r2 in target units for a supervised model; score_mean, score_median, score_p95 (normalised units for an autoencoder, the detector’s own for a score model) and, for an autoencoder, reconstruction_mse in input units.

Raises

  • ModelContractError When the partition holds groups the model was fitted on (other than evaluating the training partition itself, which reports training metrics), or the dataset’s columns differ from the model’s.
Applicability

Applicability

Machine-independent.

export_model

Machine-independent source
export_model(model, directory, *, format='onnx')
Show source vaft/process/ml/artifact.py, lines 295–356
def export_model(model, directory, *, format="onnx"):
    """Export the network to an inference format and prove it agrees.

    The training artifact and the deployment artifact need not be the same
    thing: a model trained in PyTorch can be served by ONNX Runtime.  The
    export is accepted only after the exported graph reproduces the source
    model on a probe batch.

    Parameters
    ----------
    model : ModelArtifact
        Trained or loaded model [-].
    directory : str or path-like
        Output directory; must not exist or be empty [-].
    format : str, optional
        Target format; ``"onnx"`` is the only one implemented [-].

    Returns
    -------
    dict
        Export record written to ``export.json``: format, opset, parity
        difference and tolerance, runtimes, and the source model identity [-].

    Raises
    ------
    ModelContractError
        When the backend cannot export to ``format`` or the parity check fails.
    BackendUnavailableError
        When the exporter or runtime is not installed.

    Output semantics
    ----------------
    The exported graph maps *normalised* inputs to *normalised* outputs;
    ``normalization.npz`` is written beside it and must be applied around it,
    exactly as :func:`predict` does.  A ``sklearn`` one-class SVM's graph
    returns ``scores``, the estimator's decision function; the model's
    ``score`` is its negative.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [vaft669] VEST-Tokamak/vaft#669, section 12: training and inference
       artifacts are separate, with numerical validation between them.
    """
    if model.tensor_schema:
        raise ModelContractError("structured ONNX export is not qualified; use the native artifact")
    directory = Path(directory)
    if directory.exists() and any(directory.iterdir()):
        raise ModelContractError(f"{directory} is not empty")
    directory.mkdir(parents=True, exist_ok=True)
    backend = get_backend(model.model_spec.backend)
    output_shape = model.target_shape if model.supervised else model.input_shape
    record = backend.export(
        model.model_spec, model.state, model.input_shape, output_shape, directory / f"model.{format}", format
    )
    (directory / NORMALIZATION).write_bytes(_npz(model.normalization))
    record = {**record, "source": model.resolved_identity().to_dict()}
    (directory / "export.json").write_text(json.dumps(_jsonable(record), indent=2, sort_keys=True) + "\n")
    return record

Export the network to an inference format and prove it agrees.

The training artifact and the deployment artifact need not be the same thing: a model trained in PyTorch can be served by ONNX Runtime. The export is accepted only after the exported graph reproduces the source model on a probe batch.

NameTypeUnitDescription
modelModelArtifact-

Trained or loaded model.

directorystr or path-like-

Output directory; must not exist or be empty.

formatstr, optional-

Target format; "onnx" is the only one implemented.

Returns
—dict-

Export record written to export.json: format, opset, parity difference and tolerance, runtimes, and the source model identity.

Raises

  • ModelContractError When the backend cannot export to format or the parity check fails.
  • BackendUnavailableError When the exporter or runtime is not installed.
Output semantics · Applicability · Provenance

Output semantics

The exported graph maps normalised inputs to normalised outputs; normalization.npz is written beside it and must be applied around it, exactly as predict does. A sklearn one-class SVM’s graph returns scores, the estimator’s decision function; the model’s score is its negative.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#669, section 12: training and inference artifacts are separate, with numerical validation between them.

fetch_model

Machine-independent source
fetch_model(name, *, version=None, stage=None, registry=None, cache=None, env=None)

Download a published model’s release assets into the cache, verified.

The registry checkout says which files a version is (their hashes); the GitHub Release holds the bytes. This copies the bytes into the cache and accepts them only if they are exactly the files the registry pins.

NameTypeUnitDescription
namestr-

Registry model name.

versionstr or None, optional-

Exact version; give this or stage.

stagestr or None, optional-

Lifecycle alias, resolved to an exact version first.

registrystr or path-like or None, optional-

vaft-nn checkout; $VAFT_NN_HOME when None.

cachestr or path-like or None, optional-

Artifact cache root; $VAFT_NN_CACHE, else the platform cache directory.

envmapping or None, optional-

Environment to read the variables from; os.environ when None.

Returns
—ModelIdentity-

The identity resolve_model returns for the fetched version.

Raises

  • ModelResolutionError When gh is missing or cannot read the release, or a downloaded file is missing or differs from the registry manifest; the cache is then left as it was.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Resolve the exact version and its manifest from the registry.
  2. Download the release <name>-v<version> (or the tag the registry records) with gh release download into a temporary directory inside the cache root; the repository is the registry checkout’s GitHub origin.
  3. Verify every file the registry manifest lists, then replace <cache>/<name>/<version> with the verified directory.

Applicability

Machine-independent.

Limitations

Needs the GitHub CLI logged in to an account that can read the private repository; VAFT reads no token itself.

Provenance

  1. VEST-Tokamak/vaft#669, sections 10 and 13: weights are release artifacts outside git history, verified against the registry on resolution.

import_data_roles

Machine-independentConvention-sensitive source
import_data_roles(dataset, assignments, allowed_operations, *, role_to_partition=None, provenance=None)
Show source vaft/process/ml/usage.py, lines 47–113
def import_data_roles(dataset, assignments, allowed_operations, *, role_to_partition=None, provenance=None):
    """Import exact group roles without recomputing a scientific split.

    Parameters
    ----------
    dataset : DatasetArtifact or StructuredDatasetArtifact
        Existing stable, fingerprinted samples [-].
    assignments : mapping of str to str
        Actual independent group-to-role assignments, including absent groups [-].
    allowed_operations : mapping of str to sequence of str
        Explicit role permissions: train, preprocess, calibrate, select,
        evaluate or infer [-].
    role_to_partition : mapping or None, optional
        Explicit mapping to execution partitions; None preserves role names [-].
    provenance : mapping or None, optional
        Split/protocol revisions, origins, label methods and dependencies [-].

    Returns
    -------
    tuple
        Re-fingerprinted dataset and exact Split, without random reassignment [-].

    Processing steps
    ----------------
    1. Copy the declared assignments, permissions, mapping and provenance.
    2. Validate role coverage and record the full assignment digest.
    3. Recompute dataset identity and construct a fixed split for its groups.

    Convention
    ----------
    Roles are owner-defined; neither machine restrictions nor salts are inferred.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    Re-fingerprinting streams structured samples. Sources must remain stable.
    Supplied historical provenance is a declaration, not retrospective proof.

    Provenance
    ----------
    .. [vaft2047] VEST-Tokamak/vaft#2047, exact assignment and usage provenance.
    """
    roles = {str(k): str(v) for k, v in assignments.items()}
    mapping = dict(role_to_partition) if role_to_partition is not None else {v: v for v in roles.values()}
    policy = {"schema_version": 1, "group_roles": roles,
        "allowed_operations": {str(k): sorted(set(v)) for k, v in allowed_operations.items()},
        "role_to_partition": mapping, "assignment_sha256": _hash(roles),
        "provenance": dict(provenance or {}), "history_basis": "owner_declaration"}
    spec = replace(dataset.spec, metadata={**dataset.spec.metadata, "data_use_policy": policy, "data_use_policy_sha256": _hash(policy)})
    data = replace(dataset, spec=spec)
    _policy(data)
    from .structured import StructuredDatasetArtifact, build_structured_dataset
    if isinstance(data, StructuredDatasetArtifact):
        data = build_structured_dataset(data.source, data.groups, input_specs=data.input_specs,
            target_specs=data.target_specs, spec=spec, sample_meta=data.sample_meta, available=data.available)
    else:
        from .dataset import dataset_fingerprint
        data = replace(data, fingerprint=dataset_fingerprint(data))
    fixed = {g: mapping[roles[g]] for g in sorted(set(data.group_labels))}
    if not fixed or any(not isinstance(v, str) or not v for v in fixed.values()):
        raise ModelContractError("execution partitions must be nonempty strings")
    names = sorted(set(fixed.values()))
    split = Split(spec=SplitSpec(fractions={p: 1 / len(names) for p in names}, fixed=fixed), assignment=fixed)
    return data, split

Import exact group roles without recomputing a scientific split.

NameTypeUnitDescription
datasetDatasetArtifact or StructuredDatasetArtifact-

Existing stable, fingerprinted samples.

assignmentsmapping of str to str-

Actual independent group-to-role assignments, including absent groups.

allowed_operationsmapping of str to sequence of str-

Explicit role permissions: train, preprocess, calibrate, select, evaluate or infer.

role_to_partitionmapping or None, optional-

Explicit mapping to execution partitions; None preserves role names.

provenancemapping or None, optional-

Split/protocol revisions, origins, label methods and dependencies.

Returns
—tuple-

Re-fingerprinted dataset and exact Split, without random reassignment.

Convention

Roles are owner-defined; neither machine restrictions nor salts are inferred.

Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Copy the declared assignments, permissions, mapping and provenance.
  2. Validate role coverage and record the full assignment digest.
  3. Recompute dataset identity and construct a fixed split for its groups.

Applicability

Machine-independent.

Limitations

Re-fingerprinting streams structured samples. Sources must remain stable. Supplied historical provenance is a declaration, not retrospective proof.

Provenance

  1. VEST-Tokamak/vaft#2047, exact assignment and usage provenance.

load_dataset

Machine-independent source
load_dataset(directory)
Show source vaft/process/ml/dataset.py, lines 447–515
def load_dataset(directory):
    """Load a dataset written by :func:`save_dataset`, verifying its fingerprint.

    Parameters
    ----------
    directory : str or path-like
        Directory holding ``arrays.npz`` and ``dataset.json`` [-].

    Returns
    -------
    DatasetArtifact
        The dataset, a :class:`FeatureDataset` when one was saved [-].

    Raises
    ------
    ModelContractError
        When the files are missing or the recomputed fingerprint differs from
        the recorded one -- the arrays changed after they were saved.

    Applicability
    -------------
    Machine-independent.
    """
    directory = Path(directory)
    record_path = directory / _DATASET_RECORD
    if not record_path.is_file() or not (directory / _DATASET_ARRAYS).is_file():
        raise ModelContractError(f"{directory} does not hold a saved dataset")
    record = json.loads(record_path.read_text(encoding="utf-8"))
    with np.load(directory / _DATASET_ARRAYS, allow_pickle=False) as payload:
        arrays = {key: payload[key] for key in payload.files}
    spec_record = record["spec"]
    spec = DatasetSpec(
        name=spec_record["name"],
        task=spec_record["task"],
        group_key=spec_record.get("group_key", "group"),
        input_names=tuple(spec_record.get("input_names", ())),
        target_names=tuple(spec_record.get("target_names", ())),
        description=spec_record.get("description", ""),
        metadata=spec_record.get("metadata", {}),
    )
    groups = arrays["groups"]
    if np.dtype(record.get("group_dtype", groups.dtype)).kind in "iu":
        groups = groups.astype(record["group_dtype"])
    fields = dict(
        spec=spec,
        inputs=arrays["inputs"],
        groups=groups,
        targets=arrays.get("targets"),
        sample_meta={k[len("meta:"):]: v for k, v in arrays.items() if k.startswith("meta:")},
        available=arrays.get("available"),
    )
    fs = record.get("feature_spec")
    if fs is not None:
        dataset = FeatureDataset(
            feature_spec=FeatureSpec(
                feature_names=tuple(fs["feature_names"]),
                blocks={k: tuple(v) for k, v in fs.get("blocks", {}).items()},
                version=fs.get("version", ""),
            ),
            **fields,
        )
    else:
        dataset = DatasetArtifact(**fields)
    fingerprint = dataset_fingerprint(dataset)
    if fingerprint != record["fingerprint"]:
        raise ModelContractError(
            f"dataset in {directory} has fingerprint {fingerprint}, but was saved as {record['fingerprint']}"
        )
    return dataset.replace(fingerprint=fingerprint)

Load a dataset written by save_dataset, verifying its fingerprint.

NameTypeUnitDescription
directorystr or path-like-

Directory holding arrays.npz and dataset.json.

Returns
—DatasetArtifact-

The dataset, a FeatureDataset when one was saved.

Raises

  • ModelContractError When the files are missing or the recomputed fingerprint differs from the recorded one – the arrays changed after they were saved.
Applicability

Applicability

Machine-independent.

load_model

Machine-independent source
load_model(name, *, version=None, stage=None, registry=None, cache=None, env=None, fetch=False)
Show source vaft/process/ml/resolver.py, lines 353–409
def load_model(name, *, version=None, stage=None, registry=None, cache=None, env=None, fetch=False):
    """Resolve a published model and load it, ready for :func:`predict`.

    Parameters
    ----------
    name : str
        Registry model name [-].
    version : str or None, optional
        Exact version; give this or ``stage`` [-].
    stage : str or None, optional
        Lifecycle alias such as ``"production"`` [-].
    registry : str or path-like or None, optional
        ``vaft-nn`` checkout; ``$VAFT_NN_HOME`` when ``None`` [-].
    cache : str or path-like or None, optional
        Artifact cache root; ``$VAFT_NN_CACHE``, else the platform cache
        directory [-].
    env : mapping or None, optional
        Environment to read the variables from [-].
    fetch : bool, optional
        On a cache miss or mismatch, run :func:`fetch_model` and retry [-].

    Returns
    -------
    ModelArtifact or ModelBundle
        The model (or bundle of models), carrying the identity
        :func:`resolve_model` returned [-].

    Applicability
    -------------
    Machine-independent.
    """
    from .artifact import load_model_artifact
    from .bundle import _with_identity, load_model_bundle

    env = os.environ if env is None else env
    try:
        identity = resolve_model(name, version=version, stage=stage, registry=registry, cache=cache, env=env)
    except ModelResolutionError:
        if not fetch:
            raise
        fetched = fetch_model(name, version=version, stage=stage, registry=registry, cache=cache, env=env)
        identity = ModelIdentity(**{**fetched.to_dict(), "stage": stage})
    root = _registry_root(registry, env)
    manifest = root / "models" / name / "versions" / identity.version / "manifest.json"
    kind = json.loads(manifest.read_bytes()).get("kind", "model")
    # verify=False skips only the size/presence pass resolve_model just made;
    # every byte loaded is still hashed, and the manifest re-read here must be
    # the one whose hash the registry pins.
    if kind == "bundle":
        loaded = load_model_bundle(identity.source, manifest=manifest, verify=False)
    else:
        loaded = load_model_artifact(identity.source, manifest=manifest, verify=False)
    if loaded.identity.manifest_sha256 != identity.manifest_sha256:
        raise ModelResolutionError(f"the registry manifest of {name} {identity.version} changed after verification")
    if kind == "bundle":
        return _with_identity(loaded, identity)
    return loaded.replace(identity=identity)

Resolve a published model and load it, ready for predict.

NameTypeUnitDescription
namestr-

Registry model name.

versionstr or None, optional-

Exact version; give this or stage.

stagestr or None, optional-

Lifecycle alias such as "production".

registrystr or path-like or None, optional-

vaft-nn checkout; $VAFT_NN_HOME when None.

cachestr or path-like or None, optional-

Artifact cache root; $VAFT_NN_CACHE, else the platform cache directory.

envmapping or None, optional-

Environment to read the variables from.

fetchbool, optional-

On a cache miss or mismatch, run fetch_model and retry.

Returns
—ModelArtifact or ModelBundle-

The model (or bundle of models), carrying the identity resolve_model returned.

Applicability

Applicability

Machine-independent.

load_model_artifact

Machine-independent source
load_model_artifact(directory, *, manifest=None, verify=True)
Show source vaft/process/ml/artifact.py, lines 190–243
def load_model_artifact(directory, *, manifest=None, verify=True):
    """Load an artifact directory, verifying every file against its manifest.

    Parameters
    ----------
    directory : str or path-like
        Directory holding the weights and normalisation files [-].
    manifest : str or path-like or None, optional
        Manifest to trust; ``directory/manifest.json`` when ``None``.  The
        resolver passes the registry's reviewed copy here, so a tampered cache
        cannot vouch for itself [-].
    verify : bool, optional
        Check the SHA-256 and size of every file before loading [-].

    Returns
    -------
    ModelArtifact
        The model with an identity naming the directory, version and manifest
        hash [-].

    Raises
    ------
    ModelResolutionError
        When the manifest is missing or a file is missing or differs.
    ModelContractError
        When the manifest's schema version is not one this VAFT reads.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [vaft669] VEST-Tokamak/vaft#669, section 13: resolution verifies identity,
       version and artifact hash.
    """
    directory = Path(directory)
    manifest_path = Path(manifest) if manifest is not None else directory / MANIFEST
    if not manifest_path.is_file():
        raise ModelResolutionError(f"no manifest at {manifest_path}")
    raw = manifest_path.read_bytes()
    record = json.loads(raw)
    if record.get("kind", "model") != "model":
        raise ModelContractError(f"{manifest_path} describes a {record.get('kind')!r}; use load_model_bundle")
    model = _load_artifact(directory, record, verify=verify)
    identity = ModelIdentity(
        name=model.name,
        state_sha256=model.state_sha256,
        version=record["version"],
        manifest_sha256=sha256_bytes(raw),
        resolved_by="path",
        source=str(directory),
    )
    return model.replace(identity=identity)

Load an artifact directory, verifying every file against its manifest.

NameTypeUnitDescription
directorystr or path-like-

Directory holding the weights and normalisation files.

manifeststr or path-like or None, optional-

Manifest to trust; directory/manifest.json when None. The resolver passes the registry’s reviewed copy here, so a tampered cache cannot vouch for itself.

verifybool, optional-

Check the SHA-256 and size of every file before loading.

Returns
—ModelArtifact-

The model with an identity naming the directory, version and manifest hash.

Raises

  • ModelResolutionError When the manifest is missing or a file is missing or differs.
  • ModelContractError When the manifest’s schema version is not one this VAFT reads.
Applicability · Provenance

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#669, section 13: resolution verifies identity, version and artifact hash.

load_model_bundle

Machine-independent source
load_model_bundle(directory, *, manifest=None, verify=True)
Show source vaft/process/ml/bundle.py, lines 137–195
def load_model_bundle(directory, *, manifest=None, verify=True):
    """Load a bundle directory, verifying every file and member manifest.

    Parameters
    ----------
    directory : str or path-like
        Directory holding the bundle's files [-].
    manifest : str or path-like or None, optional
        Bundle manifest to trust; ``directory/manifest.json`` when ``None``.
        The resolver passes the registry's reviewed copy [-].
    verify : bool, optional
        Check every file's SHA-256 and size first [-].

    Returns
    -------
    ModelBundle
        The members, each with an identity naming the bundle, and the
        composition record [-].

    Raises
    ------
    ModelResolutionError
        When a file is missing or differs, or a member manifest differs from
        the hash the bundle pins.
    ModelContractError
        When the manifest is not a bundle manifest this VAFT reads.

    Applicability
    -------------
    Machine-independent.
    """
    directory = Path(directory)
    manifest_path = Path(manifest) if manifest is not None else directory / MANIFEST
    if not manifest_path.is_file():
        raise ModelResolutionError(f"no manifest at {manifest_path}")
    raw = manifest_path.read_bytes()
    record = json.loads(raw)
    if record.get("schema_version") != MANIFEST_SCHEMA_VERSION or record.get("kind") != "bundle":
        raise ModelContractError(f"{manifest_path} is not a schema-{MANIFEST_SCHEMA_VERSION} bundle manifest")
    if verify:
        _verify_files(record, directory)
    identity = ModelIdentity(
        name=record["model"],
        state_sha256=sha256_bytes(raw),
        version=record["version"],
        manifest_sha256=sha256_bytes(raw),
        resolved_by="path",
        source=str(directory),
    )
    members = {}
    for role, entry in record["members"].items():
        member_raw = (directory / entry["manifest"]).read_bytes()
        if sha256_bytes(member_raw) != entry["manifest_sha256"]:
            raise ModelResolutionError(f"member {role!r} manifest differs from the hash the bundle pins")
        member = _load_artifact(directory, json.loads(member_raw), verify=verify, prefix=f"{role}.")
        members[role] = member.replace(identity=_member_identity(member, role, entry["manifest_sha256"], identity))
    return ModelBundle(
        name=record["model"], members=members, composition=record.get("composition") or {}, identity=identity
    )

Load a bundle directory, verifying every file and member manifest.

NameTypeUnitDescription
directorystr or path-like-

Directory holding the bundle’s files.

manifeststr or path-like or None, optional-

Bundle manifest to trust; directory/manifest.json when None. The resolver passes the registry’s reviewed copy.

verifybool, optional-

Check every file’s SHA-256 and size first.

Returns
—ModelBundle-

The members, each with an identity naming the bundle, and the composition record.

Raises

  • ModelResolutionError When a file is missing or differs, or a member manifest differs from the hash the bundle pins.
  • ModelContractError When the manifest is not a bundle manifest this VAFT reads.
Applicability

Applicability

Machine-independent.

predict

Machine-independent source
predict(model, data)
Show source vaft/process/ml/inference.py, lines 69–146
def predict(model, data):
    """Run a model on new data and stamp the result with its exact identity.

    Accepts a dataset (checked against the model's feature schema) or a bare
    array.  The result records the model's immutable identity -- name, exact
    version, manifest and weight hashes -- never an alias such as
    ``production``, so a stored result keeps meaning what it meant when it was
    produced.

    Parameters
    ----------
    model : ModelArtifact
        Trained, loaded or resolved model [-].
    data : DatasetArtifact or array_like
        Inputs of shape ``(n, *model.input_shape)`` [any].

    Returns
    -------
    InferenceResult
        Outputs aligned with the samples (``prediction``; ``reconstruction``
        and a per-sample ``score``; or ``score`` alone; plus
        ``<name>_exceeds`` for every calibration and, for a dataset with an
        availability mask, ``available``), the model identity, and the groups
        and per-sample metadata of a dataset input [any].

    Raises
    ------
    ModelContractError
        When the input shape, or a dataset's column names, differ from the
        model's.

    Output semantics
    ----------------
    ``prediction`` and ``reconstruction`` are in the units of the targets and
    inputs; an autoencoder's ``score`` is the mean squared reconstruction
    error in the model's normalised units, and a score model's is the
    detector's own anomaly measure (larger is more anomalous).  An
    unavailable sample has NaN outputs and exceeds no threshold: missing
    data is not "no anomaly".  A score or an exceedance is a model output, not a
    physical event: an ``IRE-like candidate``, never a reconnection onset
    (#669 section 16).

    Applicability
    -------------
    Machine-independent.
    """
    from .usage import _inference_use
    usage = _inference_use(data) if hasattr(data, "spec") else None
    from .structured import StructuredDatasetArtifact
    if isinstance(data, StructuredDatasetArtifact):
        from ._structured_lifecycle import predict_structured
        result = predict_structured(model, data)
        from dataclasses import replace
        return replace(result, provenance={**result.provenance, "data_use": usage}) if usage else result
    if model.tensor_schema:
        raise ModelContractError("structured models require a StructuredDatasetArtifact")
    if isinstance(data, DatasetArtifact):
        _check_columns(model, data)
        x = np.asarray(data.inputs)
        available = data.available
        groups, meta = data.groups, dict(data.sample_meta)
        provenance = {"input": "dataset", "dataset_fingerprint": data.fingerprint}
    else:
        x = np.asarray(data)
        available = None
        groups, meta = None, {}
        digest = hashlib.sha256(np.ascontiguousarray(x).tobytes()).hexdigest()
        provenance = {"input": "array", "input_sha256": digest}
    if usage:
        provenance["data_use"] = usage
    outputs = _forward(model, x, available)
    return InferenceResult(
        outputs=outputs,
        model_identity=model.resolved_identity(),
        groups=groups,
        sample_meta=meta,
        provenance=provenance,
    )

Run a model on new data and stamp the result with its exact identity.

Accepts a dataset (checked against the model’s feature schema) or a bare array. The result records the model’s immutable identity – name, exact version, manifest and weight hashes – never an alias such as production, so a stored result keeps meaning what it meant when it was produced.

NameTypeUnitDescription
modelModelArtifact-

Trained, loaded or resolved model.

dataDatasetArtifact or array_likeany

Inputs of shape (n, *model.input_shape).

Returns
—InferenceResultany

Outputs aligned with the samples (prediction; reconstruction and a per-sample score; or score alone; plus <name>_exceeds for every calibration and, for a dataset with an availability mask, available), the model identity, and the groups and per-sample metadata of a dataset input.

Raises

  • ModelContractError When the input shape, or a dataset’s column names, differ from the model’s.
Output semantics · Applicability

Output semantics

prediction and reconstruction are in the units of the targets and inputs; an autoencoder’s score is the mean squared reconstruction error in the model’s normalised units, and a score model’s is the detector’s own anomaly measure (larger is more anomalous). An unavailable sample has NaN outputs and exceeds no threshold: missing data is not “no anomaly”. A score or an exceedance is a model output, not a physical event: an IRE-like candidate, never a reconnection onset (#669 section 16).

Applicability

Machine-independent.

record_data_use

Machine-independentConvention-sensitive source
record_data_use(dataset, split, operation, *, groups=None)
Show source vaft/process/ml/usage.py, lines 116–173
def record_data_use(dataset, split, operation, *, groups=None):
    """Check a declared use and return its provenance receipt.

    Parameters
    ----------
    dataset : DatasetArtifact or StructuredDatasetArtifact
        Dataset with an optional data_use_policy [-].
    split : Split
        Realized execution assignment to verify against the declaration [-].
    operation : str
        Requested train, preprocess, calibrate, select, evaluate or infer [-].
    groups : iterable of str or None, optional
        Groups actually consumed; None selects every dataset group [-].

    Returns
    -------
    dict or None
        Usage receipt; None means no policy was supplied, not validated history [-].

    Processing steps
    ----------------
    1. Verify the dataset's execution assignment matches its declared mapping.
    2. Check explicit permissions for every selected group.
    3. Return dataset, policy and assignment identities with observed group roles.

    Convention
    ----------
    Calling this reports a requested entrypoint use; it cannot inspect external
    training history or intercept scientific operations outside this API.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [vaft2047] VEST-Tokamak/vaft#2047, opt-in operation-role checks.
    """
    policy = _policy(dataset)
    if policy is None:
        return None
    if operation not in _OPERATIONS:
        raise ModelContractError("unknown data-use operation")
    roles = policy["group_roles"]
    if _hash(roles) != policy["assignment_sha256"]:
        raise ModelContractError("data assignment digest does not match declaration")
    for g in set(dataset.group_labels):
        if split.assignment.get(g) != policy["role_to_partition"][roles[g]]:
            raise ModelContractError("split differs from declared data roles")
    selected = sorted(set(dataset.group_labels if groups is None else map(str, groups)))
    if set(selected) - set(dataset.group_labels):
        raise ModelContractError("requested use contains groups outside the dataset")
    for g in selected:
        if operation not in policy["allowed_operations"][roles[g]]:
            raise ModelContractError(f"{operation} is forbidden for role {roles[g]!r}, group {g!r}")
    return {"operation": operation, "groups": selected, "group_roles": {g: roles[g] for g in selected},
        "dataset_fingerprint": dataset.fingerprint, "assignment_sha256": policy["assignment_sha256"],
        "policy_sha256": _hash(policy), "history_basis": "declared_use_at_entrypoint"}

Check a declared use and return its provenance receipt.

NameTypeUnitDescription
datasetDatasetArtifact or StructuredDatasetArtifact-

Dataset with an optional data_use_policy.

splitSplit-

Realized execution assignment to verify against the declaration.

operationstr-

Requested train, preprocess, calibrate, select, evaluate or infer.

groupsiterable of str or None, optional-

Groups actually consumed; None selects every dataset group.

Returns
—dict or None-

Usage receipt; None means no policy was supplied, not validated history.

Convention

Calling this reports a requested entrypoint use; it cannot inspect external training history or intercept scientific operations outside this API.

Processing steps · Applicability · Provenance

Processing steps

  1. Verify the dataset’s execution assignment matches its declared mapping.
  2. Check explicit permissions for every selected group.
  3. Return dataset, policy and assignment identities with observed group roles.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#2047, opt-in operation-role checks.

register_architecture

Machine-independent source
register_architecture(name, factory, *, kind, backend='torch')
Show source vaft/process/ml/_backends/__init__.py, lines 142–201
def register_architecture(name, factory, *, kind, backend="torch"):
    """Make a task-defined network available to a backend by name.

    Task code (the SXR reconstruction transformer, a sequence model) defines
    its network next to its science and registers it here; a
    :class:`ModelSpec` then selects it with ``architecture=name``.  The
    factory is called only when a model is built, so registering imports no
    ML framework.

    Parameters
    ----------
    name : str
        Architecture name a ``ModelSpec`` refers to [-].
    factory : callable
        ``factory(spec, input_shape, output_shape)`` returning a network that
        maps a batch of shape ``(n, *input_shape)`` to ``(n, *output_shape)``
        without flattening it first [-].
    kind : {"supervised", "reconstruction", "score"}
        What the network learns [-].
    backend : str, optional
        Backend the factory builds for; only ``"torch"`` accepts registrations [-].

    Returns
    -------
    None
        The architecture is registered for this process [-].

    Raises
    ------
    ModelContractError
        On an unknown kind, a backend that does not accept registrations, a
        name that shadows a built-in, or a second registration of one name
        with a different factory.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    The registry is per process.  A published model records its
    architecture's name, not its code; loading it needs the same task module
    imported, and the code version is traced through the VAFT revision in the
    manifest.
    """
    if kind not in KINDS:
        raise ModelContractError(f"kind must be one of {KINDS}; got {kind!r}")
    _check_name("architecture", name)
    target = get_backend(backend)
    if not target.extensible:
        raise ModelContractError(f"backend {backend!r} does not accept registered architectures")
    if kind not in target.trainable_kinds:
        raise ModelContractError(f"backend {backend!r} trains {target.trainable_kinds}, not {kind!r}")
    if name in target.builtin:
        raise ModelContractError(f"{name!r} is a built-in {backend} architecture")
    registered = _REGISTERED.setdefault(backend, {})
    existing = registered.get(name)
    if existing is not None and (existing.factory is not factory or existing.kind != kind):
        raise ModelContractError(f"architecture {name!r} is already registered for {backend!r}")
    registered[name] = Architecture(name=name, kind=kind, factory=factory)

Make a task-defined network available to a backend by name.

Task code (the SXR reconstruction transformer, a sequence model) defines its network next to its science and registers it here; a ModelSpec then selects it with architecture=name. The factory is called only when a model is built, so registering imports no ML framework.

NameTypeUnitDescription
namestr-

Architecture name a ModelSpec refers to.

factorycallable-

factory(spec, input_shape, output_shape) returning a network that maps a batch of shape (n, *input_shape) to (n, *output_shape) without flattening it first.

kind{"supervised", "reconstruction", "score"}-

What the network learns.

backendstr, optional-

Backend the factory builds for; only "torch" accepts registrations.

Returns
—None-

The architecture is registered for this process.

Raises

  • ModelContractError On an unknown kind, a backend that does not accept registrations, a name that shadows a built-in, or a second registration of one name with a different factory.
Applicability · Limitations

Applicability

Machine-independent.

Limitations

The registry is per process. A published model records its architecture’s name, not its code; loading it needs the same task module imported, and the code version is traced through the VAFT revision in the manifest.

register_augmentation

Machine-independent source
register_augmentation(name, function)
Show source vaft/process/ml/_backends/__init__.py, lines 253–287
def register_augmentation(name, function):
    """Make a train-time augmentation available by name.

    Selected with ``TrainingConfig(augmentation=name)``.  It is applied to the
    **train partition only**, once per epoch, after the group split -- so an
    augmented copy of a sample can never reach validation or test.

    Parameters
    ----------
    name : str
        Augmentation name [-].
    function : callable
        ``function(inputs, targets, rng)`` returning ``(inputs, targets)`` of
        the same shapes, in physical (un-normalised) units; ``targets`` is
        ``None`` for an unsupervised model and ``rng`` is a seeded
        ``numpy.random.Generator`` [-].

    Returns
    -------
    None
        The augmentation is registered for this process [-].

    Raises
    ------
    ModelContractError
        On a name already registered with a different function.

    Applicability
    -------------
    Machine-independent.
    """
    _check_name("augmentation", name)
    if AUGMENTATIONS.get(name, function) is not function:
        raise ModelContractError(f"augmentation {name!r} is already registered")
    AUGMENTATIONS[name] = function

Make a train-time augmentation available by name.

Selected with TrainingConfig(augmentation=name). It is applied to the train partition only, once per epoch, after the group split – so an augmented copy of a sample can never reach validation or test.

NameTypeUnitDescription
namestr-

Augmentation name.

functioncallable-

function(inputs, targets, rng) returning (inputs, targets) of the same shapes, in physical (un-normalised) units; targets is None for an unsupervised model and rng is a seeded numpy.random.Generator.

Returns
—None-

The augmentation is registered for this process.

Raises

  • ModelContractError On a name already registered with a different function.
Applicability

Applicability

Machine-independent.

register_loss

Machine-independent source
register_loss(name, factory)
Show source vaft/process/ml/_backends/__init__.py, lines 204–250
def register_loss(name, factory):
    """Make a task-defined training loss available by name.

    Selected with ``ModelSpec(hyperparameters={"loss": name})``.  The factory
    receives the model spec and the train-partition normalisation, so a
    physics loss -- a line-of-sight residual through a projector acting on
    emissivity in physical units -- can undo the normalisation the network
    works in.

    Parameters
    ----------
    name : str
        Loss name [-].
    factory : callable
        ``factory(spec, normalization)`` returning ``loss(prediction, target,
        inputs)``, a differentiable scalar in the backend's tensor type.
        ``prediction``, ``target`` and ``inputs`` are normalised and keep
        their sample shapes; ``normalization`` maps ``input_mean``,
        ``input_std`` and, for a supervised model, ``target_mean`` and
        ``target_std`` to NumPy arrays that broadcast against one sample [-].

    Returns
    -------
    None
        The loss is registered for this process [-].

    Raises
    ------
    ModelContractError
        On a name that is already registered with a different factory, or
        that shadows the built-in ``"mse"``.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    Only the iterative ``torch`` backend trains with a loss; the closed-form
    and scikit-learn backends refuse a ``loss`` hyperparameter.
    """
    _check_name("loss", name)
    if name == "mse":
        raise ModelContractError("'mse' is the built-in loss")
    if LOSSES.get(name, factory) is not factory:
        raise ModelContractError(f"loss {name!r} is already registered")
    LOSSES[name] = factory

Make a task-defined training loss available by name.

Selected with ModelSpec(hyperparameters={"loss": name}). The factory receives the model spec and the train-partition normalisation, so a physics loss – a line-of-sight residual through a projector acting on emissivity in physical units – can undo the normalisation the network works in.

NameTypeUnitDescription
namestr-

Loss name.

factorycallable-

factory(spec, normalization) returning loss(prediction, target, inputs), a differentiable scalar in the backend’s tensor type. prediction, target and inputs are normalised and keep their sample shapes; normalization maps input_mean, input_std and, for a supervised model, target_mean and target_std to NumPy arrays that broadcast against one sample.

Returns
—None-

The loss is registered for this process.

Raises

  • ModelContractError On a name that is already registered with a different factory, or that shadows the built-in "mse".
Applicability · Limitations

Applicability

Machine-independent.

Limitations

Only the iterative torch backend trains with a loss; the closed-form and scikit-learn backends refuse a loss hyperparameter.

resolve_model

Machine-independentConvention-sensitive source
resolve_model(name, *, version=None, stage=None, registry=None, cache=None, env=None)
Show source vaft/process/ml/resolver.py, lines 139–207
def resolve_model(name, *, version=None, stage=None, registry=None, cache=None, env=None):
    """Resolve a published model to one exact, hash-verified local version.

    Parameters
    ----------
    name : str
        Registry model name, e.g. ``"ire_ae_vaft_v1"`` [-].
    version : str or None, optional
        Exact version; give this or ``stage`` [-].
    stage : str or None, optional
        Lifecycle alias such as ``"production"``, resolved to an exact version
        before anything is loaded [-].
    registry : str or path-like or None, optional
        ``vaft-nn`` checkout; ``$VAFT_NN_HOME`` when ``None`` [-].
    cache : str or path-like or None, optional
        Artifact cache root; ``$VAFT_NN_CACHE``, else the platform cache
        directory [-].
    env : mapping or None, optional
        Environment to read the two variables from; ``os.environ`` when ``None`` [-].

    Returns
    -------
    ModelIdentity
        Name, exact version, manifest SHA-256, requested stage, lifecycle status
        and the verified artifact directory (``source``) [-].

    Raises
    ------
    ModelResolutionError
        When the registry, model, version, stage, manifest or a cached file is
        missing, or a hash differs from the one the registry pins.
    ModelContractError
        When both or neither of ``version`` and ``stage`` are given.

    Processing steps
    ----------------
    1. Read ``models/<name>/releases.yaml`` from the registry.
    2. Map ``stage`` to its version, or take ``version``; refuse a stage that
       points at a deprecated version.
    3. Check the registry manifest's SHA-256 against the pinned one.
    4. Check every file the manifest lists in ``<cache>/<name>/<version>/``
       for presence, size and SHA-256.

    Convention
    ----------
    A stage alias is mutable and a version is not.  The returned identity
    always carries the exact version and manifest hash; ``stage`` only records
    what was asked for.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [vaft669] VEST-Tokamak/vaft#669, sections 9 and 13: lifecycle aliases
       resolve to immutable artifacts, and resolution verifies the hash.
    """
    env = os.environ if env is None else env
    _, version, entry, _, manifest, manifest_sha = _registry_entry(name, version, stage, registry, env)
    directory = _cache_root(cache, env) / name / version
    problems = _cache_problems(directory, manifest)
    if problems:
        raise ModelResolutionError(
            f"cached artifact {directory} is not {name} {version}: " + "; ".join(problems)
            + f". Fetch it with vaft.process.ml.fetch_model({name!r}, version={version!r}), or: "
            + f"gh release download {entry.get('release') or f'{name}-v{version}'} -R {DEFAULT_REPOSITORY} -D {directory}"
        )
    return _identity(name, version, stage, entry, manifest, manifest_sha, directory)

Resolve a published model to one exact, hash-verified local version.

NameTypeUnitDescription
namestr-

Registry model name, e.g. "ire_ae_vaft_v1".

versionstr or None, optional-

Exact version; give this or stage.

stagestr or None, optional-

Lifecycle alias such as "production", resolved to an exact version before anything is loaded.

registrystr or path-like or None, optional-

vaft-nn checkout; $VAFT_NN_HOME when None.

cachestr or path-like or None, optional-

Artifact cache root; $VAFT_NN_CACHE, else the platform cache directory.

envmapping or None, optional-

Environment to read the two variables from; os.environ when None.

Returns
—ModelIdentity-

Name, exact version, manifest SHA-256, requested stage, lifecycle status and the verified artifact directory (source).

Convention

A stage alias is mutable and a version is not. The returned identity always carries the exact version and manifest hash; stage only records what was asked for.

Raises

  • ModelResolutionError When the registry, model, version, stage, manifest or a cached file is missing, or a hash differs from the one the registry pins.
  • ModelContractError When both or neither of version and stage are given.
Processing steps · Applicability · Provenance

Processing steps

  1. Read models/<name>/releases.yaml from the registry.
  2. Map stage to its version, or take version; refuse a stage that points at a deprecated version.
  3. Check the registry manifest’s SHA-256 against the pinned one.
  4. Check every file the manifest lists in <cache>/<name>/<version>/ for presence, size and SHA-256.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#669, sections 9 and 13: lifecycle aliases resolve to immutable artifacts, and resolution verifies the hash.

save_dataset

Machine-independent source
save_dataset(dataset, directory)
Show source vaft/process/ml/dataset.py, lines 392–444
def save_dataset(dataset, directory):
    """Materialise a dataset on disk so training can run elsewhere.

    Building a corpus-scale dataset (reading thousands of shots) and training
    on it are separate steps, often on separate machines; this is the hand-off.

    Parameters
    ----------
    dataset : DatasetArtifact
        Fingerprinted dataset from :func:`build_dataset` [-].
    directory : str or path-like
        Target directory; must not exist or be empty [-].

    Returns
    -------
    str
        The dataset fingerprint, which :func:`load_dataset` re-checks [-].

    Raises
    ------
    ModelContractError
        When the dataset has no fingerprint, the directory is not empty, or an
        array has dtype ``object``.

    Applicability
    -------------
    Machine-independent.
    """
    if not dataset.fingerprint:
        raise ModelContractError("dataset has no fingerprint; build it with build_dataset")
    directory = Path(directory)
    if directory.exists() and any(directory.iterdir()):
        raise ModelContractError(f"{directory} is not empty")
    directory.mkdir(parents=True, exist_ok=True)
    arrays = {"inputs": np.asarray(dataset.inputs), "groups": dataset.group_labels.astype(str)}
    if dataset.targets is not None:
        arrays["targets"] = np.asarray(dataset.targets)
    if dataset.available is not None:
        arrays["available"] = np.asarray(dataset.available)
    for key, value in dataset.sample_meta.items():
        arrays[f"meta:{key}"] = np.asarray(value)
    for key, value in arrays.items():
        if value.dtype == object:
            raise ModelContractError(f"{key} has dtype object and cannot be stored without pickle")
    np.savez(directory / _DATASET_ARRAYS, **arrays)
    record = {
        "fingerprint": dataset.fingerprint,
        "spec": dataset.spec.to_dict(),
        "feature_spec": dataset.feature_spec.to_dict() if isinstance(dataset, FeatureDataset) else None,
        "group_dtype": str(np.asarray(dataset.groups).dtype),
    }
    (directory / _DATASET_RECORD).write_text(json.dumps(record, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    return dataset.fingerprint

Materialise a dataset on disk so training can run elsewhere.

Building a corpus-scale dataset (reading thousands of shots) and training on it are separate steps, often on separate machines; this is the hand-off.

NameTypeUnitDescription
datasetDatasetArtifact-

Fingerprinted dataset from build_dataset.

directorystr or path-like-

Target directory; must not exist or be empty.

Returns
—str-

The dataset fingerprint, which load_dataset re-checks.

Raises

  • ModelContractError When the dataset has no fingerprint, the directory is not empty, or an array has dtype object.
Applicability

Applicability

Machine-independent.

save_model_artifact

Machine-independent source
save_model_artifact(model, directory, *, version, model_card=None, applicability=None)
Show source vaft/process/ml/artifact.py, lines 114–173
def save_model_artifact(model, directory, *, version, model_card=None, applicability=None):
    """Write a model as an immutable, hash-pinned artifact directory.

    The directory is what gets attached to a ``vaft-nn`` release; its
    ``manifest.json`` is what gets committed to the registry.

    Parameters
    ----------
    model : ModelArtifact
        Trained model; ``model.name`` must be lower_snake_case [-].
    directory : str or path-like
        Target directory; must not exist or be empty [-].
    version : str
        Semantic version of this artifact, e.g. ``"0.1.0"`` [-].
    model_card : mapping or None, optional
        Intended use, training population, validation evidence and known
        limits, stored in the manifest [-].
    applicability : mapping or None, optional
        The input domain the model was trained and validated on (parameter
        ranges, shot ranges, diagnostics), stored in the manifest [-].

    Returns
    -------
    ModelArtifact
        The same model carrying its saved identity: version, manifest SHA-256
        and weight SHA-256 [-].

    Raises
    ------
    ModelContractError
        On an invalid name or version, or a non-empty target directory.

    Processing steps
    ----------------
    1. Write the backend weights and the normalisation moments.
    2. Hash each file and write ``manifest.json`` (sorted keys, two-space
       indent) pinning every hash and size.
    3. Hash the manifest; that hash is the artifact's identity.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [vaft669] VEST-Tokamak/vaft#669, sections 10-11: the ``vaft-nn`` release
       layout and manifest contract.
    """
    _check_name_version(model.name, version)
    directory = _empty_directory(directory)
    raw = _write_artifact(model, directory, str(version), "", model_card, applicability)
    identity = ModelIdentity(
        name=model.name,
        state_sha256=model.state_sha256,
        version=str(version),
        manifest_sha256=sha256_bytes(raw),
        resolved_by="saved",
        source=str(directory),
    )
    return model.replace(identity=identity)

Write a model as an immutable, hash-pinned artifact directory.

The directory is what gets attached to a vaft-nn release; its manifest.json is what gets committed to the registry.

NameTypeUnitDescription
modelModelArtifact-

Trained model; model.name must be lower_snake_case.

directorystr or path-like-

Target directory; must not exist or be empty.

versionstr-

Semantic version of this artifact, e.g. "0.1.0".

model_cardmapping or None, optional-

Intended use, training population, validation evidence and known limits, stored in the manifest.

applicabilitymapping or None, optional-

The input domain the model was trained and validated on (parameter ranges, shot ranges, diagnostics), stored in the manifest.

Returns
—ModelArtifact-

The same model carrying its saved identity: version, manifest SHA-256 and weight SHA-256.

Raises

  • ModelContractError On an invalid name or version, or a non-empty target directory.
Processing steps · Applicability · Provenance

Processing steps

  1. Write the backend weights and the normalisation moments.
  2. Hash each file and write manifest.json (sorted keys, two-space indent) pinning every hash and size.
  3. Hash the manifest; that hash is the artifact’s identity.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#669, sections 10-11: the vaft-nn release layout and manifest contract.

save_model_bundle

Machine-independent source
save_model_bundle(bundle, directory, *, version, model_card=None, applicability=None)

Write a multi-model bundle as one immutable, hash-pinned release.

NameTypeUnitDescription
bundleModelBundle-

Members by role and the composition record.

directorystr or path-like-

Target directory; must not exist or be empty.

versionstr-

Semantic version shared by the bundle and every member.

model_cardmapping or None, optional-

Intended use, population, validation evidence and limits.

applicabilitymapping or None, optional-

Input domain the bundle was trained and validated on.

Returns
—ModelBundle-

The bundle carrying its saved identity; every member carries its own identity and the bundle’s.

Raises

  • ModelContractError On an invalid bundle or member name or version, or a non-empty directory.
Processing steps · Applicability · Provenance

Processing steps

  1. Write each member as an ordinary artifact whose file names are prefixed by its role.
  2. Write manifest.json with kind: "bundle", every file’s SHA-256 and size, each member manifest’s SHA-256 and the composition record.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#973, section 24: one logical model release may contain two fitted stage artifacts.

split_groups

Machine-independent source
split_groups(groups, spec=None)

Assign independent groups to train/validation/test partitions.

Splits the groups – never the samples – so correlated samples of one shot or one simulation case can never sit on both sides of a holdout. Do this on the record-level data, before any windowing or augmentation; the returned Split then applies unchanged to everything derived.

NameTypeUnitDescription
groupsarray_like or DatasetArtifact-

Group label of every sample, or a dataset whose groups are used.

specSplitSpec or None, optional-

Partition fractions (of groups), seed and pinned groups; None uses 70/15/15 with seed 0.

Returns
—Split-

Group-to-partition assignment.

Raises

  • ModelContractError When a pinned group is absent, or when there are fewer groups than partitions with a positive fraction.
Processing steps · Defaults · Applicability · Provenance

Processing steps

  1. Take the unique groups as strings, sorted, so the result depends on the set of groups and the seed only – not on sample order.
  2. Assign the groups spec.fixed pins.
  3. With method="permutation", permute the remaining groups with numpy.random.default_rng(seed) and cut the permutation at the cumulative fractions, rounding so every partition with a positive fraction receives at least one group. With method="hash", map each group to u = int(sha256("seed:group")) / 2**256 in [0, 1) and place it in the partition whose cumulative-fraction interval holds u; no partition is guaranteed a group.

Defaults

The 70/15/15 fractions are numerical convenience, the same shares the VEST IRE prototype used [ire].

Applicability

Machine-independent.

Provenance

  1. S. Kaufman, S. Rosset, C. Perlich, O. Stitelman, “Leakage in data mining: formulation, detection, and avoidance”, ACM TKDD 6, 15 (2012).
  2. VEST IRE two-stage anomaly-detection notebook (v4), shot-level split; reviewed in issue #669 section 15A.

train_model

Machine-independentConvention-sensitive source
train_model(dataset, split, model_spec, config=None, *, name=None, train_mask=None, train_mask_record=None)

Train a model on the train partition and package it with its provenance.

The one training entry point for every backend and architecture. What comes back is a ModelArtifact, not a framework object: its trained state is opaque bytes and everything that identifies it – data, split, configuration, software – travels with it.

NameTypeUnitDescription
datasetDatasetArtifact-

Fingerprinted training data from build_dataset.

splitSplit-

Group assignment from split_groups, made before any windowing.

model_specModelSpec-

Backend, architecture, task and hyperparameters.

configTrainingConfig or None, optional-

Optimisation, normalisation and augmentation settings; None uses the defaults.

namestr or None, optional-

Model name used when publishing; model_spec.task when None.

train_maskarray_like of bool or None, optional-

Per-sample mask further restricting the fitted samples within the train partition – training-set cleaning; None keeps them all.

train_mask_recordmapping or None, optional-

How train_mask was made (method, threshold, source model), stored in the provenance next to the kept and removed counts.

Returns
—TrainingResult-

The artifact, the per-epoch history, the split, the epoch whose weights were kept, the fitted sample indices and, for a score model that can leave a sample out (k-NN), the leave-self-out training scores.

Convention

Normalisation is a z-score, (x - mean) / std, with a constant element given std = 1; the reconstruction score of an autoencoder is measured in these normalised units. A score model’s score is larger for more anomalous samples.

Raises

  • ModelContractError When the dataset has no fingerprint, a group is missing from the split, no sample is left to fit, a supervised architecture has no targets, or a named augmentation or loss is not registered.
  • BackendUnavailableError When the backend’s framework is not installed.
Processing steps · Defaults · Applicability · Provenance

Processing steps

  1. Select the fitted samples: train partition, available, and train_mask; select the available validation samples.
  2. Compute input (and target) normalisation moments on the fitted samples only, per element or pooled over config.normalize_axes.
  3. Fit the backend on the normalised arrays; a reconstruction architecture learns to reproduce its input. A registered augmentation is applied to the fitted samples only, once per epoch. The validation loss drives early stopping and the best epoch’s weights are kept.
  4. Record dataset fingerprint and spec, split groups, the train-mask record, model spec, configuration, software revision and runtimes, and the loss history.

Defaults

The TrainingConfig defaults (Adam, learning rate 1e-3, batch 64, patience 10, per-element normalisation) are numerical convenience, not tuned for any task.

Applicability

Machine-independent.

Provenance

  1. VEST-Tokamak/vaft#669, sections 6 and 7: training as a VAFT processing stage, dataset provenance as part of model identity.

window_dataset

Machine-independent source
window_dataset(dataset, window, step, *, target_mode=None)

Cut sliding windows that never cross a group boundary.

Turns a record-level dataset – samples in time order, one row per time point – into a window dataset for a sequence model or a feature extractor.

NameTypeUnitDescription
datasetDatasetArtifact-

Record-level samples in time order within each group.

windowint-

Samples per window.

stepint-

Samples between consecutive window starts.

target_mode{"window", "last", "none"} or None, optional-

What each window’s target is: the target rows over the window, the target at its last sample, or none; None is "window" for a dataset with targets and "none" otherwise.

Returns
—DatasetArtifact-

Inputs of shape (n_windows, window, *input_shape); groups repeated per window; sample_meta carries each window’s window_start and window_stop source indices and, for every per-sample array of the source, its value at the window’s first sample.

Raises

  • ModelContractError When window or step is not a positive integer, or a target mode asks for targets the dataset does not have.
Processing steps · Input semantics · Output semantics · Applicability · Provenance

Processing steps

  1. Find the maximal contiguous runs of one group label.
  2. Within each run, start a window every step samples while a full window fits; a shorter tail is dropped.
  3. Recompute the fingerprint of the result.

Input semantics

One row per sample time, grouped and time-ordered within each group.

Output semantics

One row per window. The group of every window is the group of all of its samples, so a Split made on the source applies unchanged. A window is available only when every sample in it is.

Applicability

Machine-independent.

Provenance

  1. S. Kaufman et al., “Leakage in data mining”, ACM TKDD 6, 15 (2012): windows from one unit on both sides of a holdout leak.

results matching ""

    No results matching ""