A Better Score on Which Dataset?
Which Experiment Produced This Model?
BOTIE
Databricks introduced MLflow as an open-source project on June 5, 2018. Its announcement described the difficulty of tracking experiments and reproducing them across different machine-learning tools. MLflow Tracking groups a training attempt’s settings and results under a run ID. Code can log those details through its programming interface, and a person can compare the runs on a screen. It also keeps output files, such as the trained model, with the run that produced them.
A managed MLflow service is available in Databricks, and the open-source software can also be used elsewhere. In this lesson we use the tracking idea: keep a complete record for each attempt, then compare only attempts measured against the same validation evidence. Recording a score makes it inspectable; choosing a fair comparison is still our responsibility.
Sources for this history
The Winning Run Used a Different Validation Table
Botie lists several training runs and chooses the smallest error. The new run looks excellent until we notice that it used a different validation table. Both tables happen to be at version four. Another score was measured on the final test set. Sorting those numbers together does not tell us which model improved on the same evidence.
We need a run record that connects the model to its inputs and assessment. Then we can decide which records belong in this particular comparison before choosing an artifact.
The floor selects among supplied experiment records. Training and loading the saved models happen elsewhere, and a small observed error difference still needs appropriate assessment before a release decision.
Step 1 of 1
A version number belongs to a particular table
Mean absolute error (MAE) is the average size of a prediction’s errors. Lower is better here, but the assessment data must still match.
| Run | MAE | Validation table | Version | Split |
|---|---|---|---|---|
| A | 2.0 | lab.validation.sensor_readings | 4 | shift-S |
| B | 0.5 | lab.validation.easy_readings | 4 | shift-S |
| C | 1.5 | lab.validation.sensor_readings | 4 | shift-S |
A
- MAE
- 2.0
- Validation table
- lab.validation.sensor_readings
- Version
- 4
- Split
- shift-S
B
- MAE
- 0.5
- Validation table
- lab.validation.easy_readings
- Version
- 4
- Split
- shift-S
C
- MAE
- 1.5
- Validation table
- lab.validation.sensor_readings
- Version
- 4
- Split
- shift-S
Make the run record readable before sorting it
The full assessment identity is checked before a metric can choose an artifact.
Choose the run that improved on the declared evidence
A tempting low score came from a different validation table.
BOTIE
Manifest of the selected comparable run
{
"runId": "C",
"modelUri": "models:/C",
"trainingTable": "lab.training.calibrated_features",
"trainingVersion": 4,
"validationTable": "lab.validation.sensor_readings",
"validationVersion": 4,
"split": "S"
}Follow the operations
1. Experiment run records
[ { "runId": "A", "modelUri": "models:/A", "trainingTable": "lab.training.raw_features", "trainingVersion": 3, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S", "role": "validation", "mae": 2 }, { "runId": "B", "modelUri": "models:/B", "trainingTable": "lab.training.raw_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 5, "split": "S", "role": "validation", "mae": 0.5 }, { "runId": "C", "modelUri": "models:/C", "trainingTable": "lab.training.calibrated_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S", "role": "validation", "mae": 1.5 } ]2. Required validation-table version
4
3. Required validation split
S
4. Required validation table identity
lab.validation.sensor_readings
5. Keep validation runs using the same evidence
[ { "runId": "A", "modelUri": "models:/A", "trainingTable": "lab.training.raw_features", "trainingVersion": 3, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S", "role": "validation", "mae": 2 }, { "runId": "C", "modelUri": "models:/C", "trainingTable": "lab.training.calibrated_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S", "role": "validation", "mae": 1.5 } ]6. Choose the smallest validation MAE
{ "runId": "C", "modelUri": "models:/C", "trainingTable": "lab.training.calibrated_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S", "role": "validation", "mae": 1.5 }7. Keep the artifact attached to its data identities
{ "runId": "C", "modelUri": "models:/C", "trainingTable": "lab.training.calibrated_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S" }8. Result
{ "runId": "C", "modelUri": "models:/C", "trainingTable": "lab.training.calibrated_features", "trainingVersion": 4, "validationTable": "lab.validation.sensor_readings", "validationVersion": 4, "split": "S" }
The floor selects among supplied experiment records. Training and loading the saved models happen elsewhere, and a small observed error difference still needs appropriate assessment before a release decision.
1. Make the run record readable before sorting it
Think of a run as the record of one execution of our experiment. It keeps what we tried beside what happened. The run ID names that execution. The saved model file is an artifact, meaning an output we keep, and its URI is the address another program can use to request it.
The input needs an address too: record the training table identity beside its version, and do the same for validation. Another table can have its own unrelated version four. The split says which assessment rows were used inside the selected validation snapshot. A dictionary keeps those meanings attached to the score and model.
Throughout this example the metric is validation mean absolute error in the same temperature unit, and a smaller value is better.
2. Choose a comparison before choosing a winner
Declare the validation table identity, its version and the split used for this selection. Exclude records with different assessment evidence and records marked as final test results. Only then rank the remaining candidates by validation MAE.
Candidates may deliberately use different training tables, training versions or parameter settings. That can be the experiment we want to run. Hold the assessment evidence fixed while recording those training differences so we can investigate what changed.
Repeatedly selecting on the validation set can still overfit that selection process. The final untouched assessment remains separate.
A table-local version needs its table identity
Both rows have version four and the same split. Keep the row from the declared assessment table.
runs = [{"runId":"a","validationTable":"lab.validation.easy_readings","validationVersion":4,"split":"S"}, {"runId":"b","validationTable":"lab.validation.sensor_readings","validationVersion":4,"split":"S"}]
print([row["runId"] for row in runs if row["validationTable"] == "lab.validation.sensor_readings" and row["validationVersion"] == 4 and row["split"] == "S"])['b']The full selector also checks assessment role, ranks the eligible errors and preserves both input table identities in the manifest.
Check yourself
Questions on Parts 1 to 2
Question 1 of 2
Which run is selected by the declared rule?
Use the information below.
Every score has role validation and split S. A: MAE 2 on lab.validation.sensor_readings version 4. B: MAE 0.5 on lab.validation.easy_readings version 4. C: MAE 1.5 on lab.validation.sensor_readings version 4. Selection requires lab.validation.sensor_readings version 4 and S.3. A dataset digest does not replace a retained input
An MLflow dataset record can describe where rows came from and include a compact digest, a summary used to recognise that dataset. The record is useful for inspection, but it does not contain all the source files. Keep the named Delta table and version, with a retention plan that allows the needed read.
Training may transform those rows before the model sees them. Record the transformation code and settings as well as any preprocessing learned from training data. Otherwise two runs could name the same raw table while training on different representations.
This is why the earlier lineage work matters: we want to follow a model back through the actual operations that produced its input.
4. Log the input that the run actually read
The reference constructs dataset records from explicitly versioned training and validation Delta reads. It selects the named validation split before logging that input. The supplied fitted model and measured validation MAE must come from that exact declared procedure.
The input example omits the label and must match the model’s actual input representation. Use the returned model_uri rather than inventing an artifact path from a displayed metric. Keep the runtime and dependency versions with the project.
The tracking run now names both tables, their versions and the assessment split beside the model artifact.
Run in your own environment
Attach both dataset identities and the artifact to one MLflow run
This optional reference runs in your own Databricks environment. The browser exercise below uses ordinary Python and does not contact Databricks.
Use an optional Databricks MLflow environment supporting the documented MLflow 3 name parameter, with both retained Delta tables and permission to read them. Each has target; the validation table also has split_id. Replace the example identities and retained versions with the inputs your procedure actually used.
Before running, supply fitted_model, input_example and validation_mae from training on the declared training read and evaluating on the declared validation read after its split filter. This reference logs those results; it does not train or evaluate a model.
Logging writes experiment metadata and a model artifact. Use an approved tracking location; never log service credentials or unnecessary personal example records.
import mlflow
import mlflow.sklearn
training_table, training_version = "lab.training.sensor_features", 4
validation_table, validation_version = "lab.validation.sensor_readings", 8
validation_split = "S"
training = spark.read.option("versionAsOf", training_version).table(training_table)
validation = spark.read.option("versionAsOf", validation_version).table(validation_table)
validation = validation.filter(validation["split_id"] == validation_split)
training_dataset = mlflow.data.from_spark(
training, table_name=training_table, version=str(training_version), targets="target",
)
validation_dataset = mlflow.data.from_spark(
validation, table_name=validation_table, version=str(validation_version), targets="target",
)
with mlflow.start_run() as run:
mlflow.log_input(training_dataset, context="training")
mlflow.log_input(validation_dataset, context="validation")
mlflow.log_param("training_table", training_table)
mlflow.log_param("training_table_version", training_version)
mlflow.log_param("validation_table", validation_table)
mlflow.log_param("validation_table_version", validation_version)
mlflow.log_param("validation_split", validation_split)
mlflow.log_metric("validation_mae", float(validation_mae))
model_info = mlflow.sklearn.log_model(
fitted_model, name="model", input_example=input_example,
)
print(run.info.run_id, model_info.model_uri)The dataset records and explicit parameters preserve table identity as well as version. The split and role identify which assessment a metric describes.
Official interface documentation. Documentation checked .
5. Return the artifact identity with the decision
Once a comparable run is selected, return a manifest containing its run ID and model URI together with both training and validation table identities, their versions and the split. Keeping version numbers without their table identities would leave the next consumer guessing which histories to read.
A real release also needs input/output contracts, runtime dependencies and approval evidence appropriate to the application. Loading an artifact must follow the model-file security boundary from the other course area.
The manifest should let another piece of software identify the artifact and its declared evidence.
6. Handle missing comparisons and exact ties deliberately
If no run used the required validation table, version and split, the result is no eligible comparison. Raise a clear error instead of choosing the smallest number from another dataset. In our challenge, exact metric ties use run ID order only to make the output deterministic.
That tie-breaker is not evidence of statistical superiority. Small observed differences may need paired analysis and uncertainty estimates before a practical conclusion can be defended.
Keep that complete chosen record on the wire all the way to the manifest.
7. Try a tempting but invalid winner
A useful fresh case gives a run on another validation table an extremely good number while leaving its version and split unchanged. If it still wins, part of the evidence identity was lost. Other cases change the version or split.
Keep final-test rows out of this selection altogether. Looking at a test score to choose a candidate spends the independence that test set was meant to provide.
Can the job identity read the table named in this manifest?
Check yourself
Questions on Parts 1 to 7
Question 1 of 5
Which question remains unanswered?
Use the information below.
A run records a model URI and training version 4, but omits the training table identity.Try it with Botie
Build with Botie
Write and run Python