Skip to navigation

MLOps Reference

View as Markdown

Look up the exact layout, fields, and definitions behind Kumo Engine MLOps. The task pages, starting with Turn On MLOps, show how to use these features; this page describes what they write and how their numbers are computed.

Artifact Root Layout

Each stored artifact has its own directory under the artifact root, with a _provenance.json manifest at the place the tree shows. Manifests and Lineage describes the manifests.

<artifact root>/
├── snapshots/<graph id>/<data version>/
│ ├── kumo_snapshot.json
│ └── <generation>/
│ ├── index/
│ └── _provenance.json
├── training_tables/tt-<id>/
│ ├── part-*.parquet
│ ├── _provenance.json
│ └── _SUCCESS
├── models/<model id>/
│ ├── model/
│ ├── holdout.parquet
│ ├── metrics.json
│ ├── training-spec.json
│ └── _provenance.json
├── predictions/<prediction id>/
│ ├── predictions/
│ │ ├── part-*.parquet
│ │ ├── _provenance.json
│ │ └── _SUCCESS
│ └── skipped/
└── assets/<asset kind>/<sha256>/
└── _provenance.json
DirectoryContents
snapshots/One directory per graph and data version. kumo_snapshot.json names the current generation, which holds the graph index
training_tables/The training table’s rows as Parquet parts
models/The model bundle: weights and encoders in model/, the holdout predictions in holdout.parquet, the holdout metrics, and the training specification. The bundle holds only the model’s own state; its manifest records what else it needs to predict
predictions/One directory per prediction run, never overwritten. predictions/ holds the scores. skipped/ exists when the run skipped rows, and lists each skipped row with the reason it was skipped. A run that also wrote embeddings has an embeddings/ directory
assets/Files that models need besides their bundle, such as word vectors for text columns. Each is stored once, by its SHA-256, and shared by every model that uses it

Identifiers

IdentifierNamesWhere to find it
snapshot.id, fit_result.snapshot_idA graph snapshot, as <graph id>@<data version>snapshots/<graph id>/<data version>/
training_table.idA training table, starting with tt-training_tables/<id>/
fit_result.idA model, by its bundle id: 64 hexadecimal charactersmodels/<id>/; the kumo.bundle_id run tag; kumo_bundle_id on the registered version; the BUNDLE_ID column of an appended table
prediction.idA prediction runpredictions/<id>/; the kumo.prediction_id run tag; the PREDICTION_ID column of an appended table
fit_result.registered_model.versionA registered versionThe registry
fit_result.tracking, prediction.trackingThe tracker’s run, with platform, experiment, run_id, run_name, and urlThe tracker

A training table’s id is the same each time the same snapshot, query, generation plan, split seed, and engine build produce it. A model’s bundle id is a hash of the bundle’s contents.

Manifests and Lineage

Each _provenance.json is a JSON manifest that records what the artifact is and where it came from. It contains metadata only. The same manifest is available in Python as snapshot.manifest, training_table.manifest, fit_result.manifest, and prediction.manifest.

Every manifest has these fields:

FieldMeaning
kindsnapshot, training_table, model, prediction, or asset
schema_versionThe manifest format version
idThe artifact’s identifier
created_atWhen the artifact was created, in UTC
environmentThe Kumo Engine, DiskGraph, PyTorch, and Python versions and the engine build that created it
locationWhere the artifact is stored
parentsThe manifests of the artifacts it was made from, embedded in full, so one manifest traces the whole lineage

The parents and other fields of each kind:

KindparentsOther fields
Snapshotsource_tables: each table’s connector, location, and versionbuild: when the snapshot was built, how long it took, and its size on disk
Training tablesnapshot, and for a table read from a source, source_tablequery, generation_plan, task_type, split_seed, build
Modeltraining_table, and warm_start for a model trained from another modelfit (start, end, and seconds), fit_mode, tracking (the fit run), dependencies, holdout
Prediction runmodel and the scoring snapshotprediction_table, scoring, outputs, status, timings, stats

The model and prediction run fields in more detail:

FieldMeaning
dependenciesWhat predict needs besides the bundle and a snapshot: the predictor entry point, the kumo-engine package with any extras such as text, and each asset with its kind, SHA-256, and location
holdoutThe holdout scores: rows, whether they were truncated to fit a size limit, their exact counts in fixed bins, and the label window. Score drift compares against these counts
prediction_tableThe query’s SHA-256, the anchors (prediction times), requested_rows, scored_rows, skipped_rows, a sample of up to 20 skipped ids, and skipped_location
scoringThe binary classification threshold and the number of classes returned, each with where it came from, such as bundle
outputsEach destination the run wrote: the output_config destination, and with MLOps on, the copy under the artifact root
statsPer output, the row count, and with MLOps on, the scores’ exact counts in fixed bins

An embedded parent leaves out its mutable fields and statistics: a model’s tracking and holdout, and a prediction run’s outputs, stats, status, and timings. A parent that has no manifest, such as a bundle from a release before MLOps, appears as a stub with its id only.

graph.snapshot(record_sources=False) records each source table without its location: only the connector, the table name, and a SHA-256 of the location.

Drift Statistics

Monitor Predictions describes how to run score_drift. These are the definitions behind its numbers.

Score Drift

Score drift compares the distribution of a prediction run’s scores with a reference distribution. It is computed twice:

  • Against the holdout: the model’s own holdout scores at training. Logged on the prediction run as drift/psi, drift/ks, and drift/wasserstein.
  • Against a previous run: with score_drift(run, previous=earlier). Logged as drift/psi_vs_prev, drift/ks_vs_prev, and drift/wasserstein_vs_prev.
StatisticDefinitionRange
psiPopulation stability index: the sum of (p − q) × ln(p / q) over up to 10 groups of bins, where p and q are the run’s and the reference’s shares. The groups are cut where the reference’s cumulative share reaches each tenth. Shares are floored at 0.0000010 for identical distributions. Rule of thumb: below 0.1 is a small shift, 0.1 to 0.25 moderate, above 0.25 large
ksKolmogorov Smirnov (KS) statistic: the largest gap between the two cumulative distributions, at the bin edges0 for the same distribution to 1 for disjoint ones
wassersteinWasserstein-1 distance: how far the scores moved on average, computed over the bin midpoints, with the end bins closed at each side’s recorded minimum and maximumIn score units

The score each statistic uses depends on the task:

TaskScoreBins
Binary classificationTrue_PROB100 bins of width 0.01, plus one for a score of exactly 1
Multiclass classificationThe top class’s probabilityThe same probability bins
Multilabel classificationEach entity’s top scoreThe same probability bins
Ranking, link predictionEach entity’s top scoreThe holdout’s top score percentiles, from the 0th to the 100th
Regression, forecastingThe predictionThe training target’s histogram bins, or 50 equal bins from its minimum to its maximum, with its quartiles as extra edges

Each statistic is computed exactly from the counts that both sides stored on the same bins. A statistic is absent, with a note, when the two sides were counted on different bins. Probability bins are the same for every model, but the bins of a ranking, link prediction, regression, or forecasting model come from that model, so a previous run of another version of such a model cannot be compared. Link prediction holdout scores are partial, so its numbers against the holdout carry no shift verdict.

Input Columns

score_drift also compares each column the model encodes with the training data, using the column statistics stored with the training and scoring snapshots. It reads no data again. A tracked score_drift writes one table per source table, drift/inputs/<table>.json, with one row per column, the most shifted first:

ColumnColumn typesMeaning
psiNumerical, categoricalPSI against the training snapshot. Numerical: over up to 10 groups of fixed value bins, exact. Categorical: over the categories, approximate when the category counts were capped or sampled
shiftNumerical, categoricalThe PSI rule of thumb: small, moderate, or large
mean Δ sdNumericalThe change in the mean, in training standard deviations
null Δ ppAllThe change in the share of nulls, in percentage points
distinct Δ≈ %Numerical, categorical, textThe approximate change in distinct values, accurate to within a few percent
newest Δ daysTimestampHow much later the newest value is now
tokens Δ≈ %TextThe change in the average number of tokens per value, in percent, from a sample of about 10,000 rows

A table leaves out measures that none of its columns has, and a column whose type changed shows only the old and new type. drift/inputs_max_psi is the largest column PSI, which you can use in alerts, for example metrics.`drift/inputs_max_psi` > 0.25. These statistics describe whole snapshots, so a slow change in a large event table is diluted.

Input Sizes

drift/table_rows lists each source table whose row count changed since the training snapshot, with its rows at training, rows now, and the change in percent. drift/link_edges lists each edge, such as orders.USER_ID → users, whose edge count changed. DriftReport.input_changes holds the same counts in Python.

Evaluation Columns

evaluation.export() writes one row per scored entity whose outcome is known. Warehouse Monitors uses these columns. For binary classification:

ColumnMeaning
ENTITYThe entity’s primary key
ANCHOR_TIMESTAMPThe prediction time
TARGETThe realized outcome
EVALUATED_ATWhen the evaluation ran, in UTC
True_PROBThe predicted probability
TARGET_PREDThe predicted label, which needs a threshold
PREDICTION_ID, BUNDLE_IDThe prediction run and the model, added only when you export to a table with write_mode=WriteMode.APPEND

Custom Trackers

To record runs in another system, pass any object with three methods as tracking= to kumo.MLOps. Kumo builds a RunRecord for each run, with the same run name, parameters, metrics, tags, and summaries every tracker receives:

import kumo.engine as kumo
from kumo.engine.tracking import Event, RunRecord
class PrintTracker:
def start(self, record: RunRecord) -> None:
print("start", record.name)
def on_trial(self, record: RunRecord, event: Event) -> None:
print("trial", event.trial, event.name)
def finish(self, record: RunRecord | None, error: BaseException | None) -> None:
if record is not None:
print("finish", record.name, record.metrics)
cfg = kumo.MLOps("s3://<bucket>/kumo", tracking=PrintTracker())

A fit inside with kumo.mlops(cfg): prints start fit-users-binary_classification-<time>, then finish with the fit’s holdout metrics, such as test/auroc.

MethodCalled
start(record)When the fit starts, with the fit run’s name, parameters, and tags
on_trial(record, event)During a hyperparameter search with more than one trial, with each trial’s record after each of its events
finish(record, error)When the fit ends, with the final record, or None when tracking stopped early, and the error if the fit failed

record.summaries holds the fit’s tables, notes, and datasets. Each dataset has a name, a source (connector, location, version, and version_kind), a schema, a profile, and a context, its role. Skip summary types your tracker does not handle; new types may be added.

A tracker can define these optional members:

MemberPurpose
prepare()Called when a fit starts, before start
check(registry)Called when kumo.MLOps is created, to fail early on a bad configuration
tagsTags added to every run, except names that start with kumo. or match a parameter name
log_metrics(metrics, step)Called with the fit run’s per-epoch metrics
log_model(bundle_dir, manifest, snapshot, registry)Logs and registers the model, and returns the published version
connectorA connector whose model registry registry= registers in, instead of log_model
load_model(location), register(result, location)Load a model from the registry, and register an existing model with trainer.export(cfg)
platform, run_id, run_urlRecorded as fit_result.tracking
log_prediction_run(record), log_monitoring(record)Called after each prediction run, and after each score_drift or evaluate

If a tracker method raises, Kumo warns and stops calling the tracker for that fit, except for one final finish(None, None).

Progress Events

trainer.fit(callbacks=[...]) calls each callback with every kumo.engine.tracking.Event. Each event has a name, a time, and, where they apply, a trial and epoch counted from 1, metrics, and params:

EventWhen
fit_startedThe fit starts
trial_started, epoch_ended, trial_endedFor each trial, and each epoch in it
fit_endedThe fit ends

trial_ended and fit_ended carry params["status"] and, on failure, params["error"]. A callback that raises is logged and not called again. To write each event as a JSON line, pass callbacks=[lambda event: print(json.dumps(event.to_dict()))].

graph.snapshot(callbacks=[...]) reports snapshot_started, then table_staged for each table, then snapshot_ended with snapshot_id and reused. Each carries params["seconds"], the time since the snapshot started. With callbacks=[print], each event prints as its name and fields:

snapshot_started graph=<graph id> tables=['users', 'items', 'orders'] seconds=0.0
table_staged table=users seconds=0.0
table_staged table=items seconds=0.0
table_staged table=orders seconds=0.0
snapshot_ended snapshot_id=<graph id>@<data version> reused=False seconds=1.3

Limits

  • Tracking time. At the end of a fit, Kumo waits at most 30 seconds for tracking to finish, and up to one hour for model logging and registration.
  • MLflow HTTP calls. During a fit, MLflow calls use 2 retries and a 30 second timeout, and model uploads a 600 second timeout. Set MLFLOW_HTTP_REQUEST_MAX_RETRIES and MLFLOW_HTTP_REQUEST_TIMEOUT to change them.
  • Metric names. Names with @ are logged with _at_, so map@10 becomes test/map_at_10, and acc is logged as accuracy. Parameters that MLflow rejects are counted in the kumo.dropped_params tag.
  • Snowflake (preview). A run keeps at most 1,000 parameters, and the metric name limit listed in Track Runs, where a per-epoch series counts as one name. Parameters and metrics over the limits are counted in kumo.dropped_params and kumo.dropped_metrics.

Next Steps