11. Progress from a baseline to an advanced workflow
Your goal: complete seven practical stages, each adding one mechanism you can explain. You may stop at the simplest stage that answers your scientific question. The final exercise combines source-specific preprocessing, fusion and correction, then tests saved-model replay.
Keep an experiment notebook with inputs, recipe, split, figure and conclusion. When an example is Python-only, use its native counterpart in 10. Run the same recipe in your language for the shared operations; the tabs mark actual availability.
Recipe |
Executable guide |
What to retain |
|---|---|---|
First regression and candidate selection |
Dataset, candidates, OOF, selected refit and export |
|
Compare preprocessing and models |
Comparable validation protocol and metric direction |
|
Cross-validation and grouped observations |
Cross-Validation Examples and Sample Repetition & Aggregation |
Group/repetition/origin IDs and fold memberships |
NIR + image + series + metadata |
Encoder state, schema, fusion model and no-FIT replay |
|
Partial cohorts and late fusion |
Source/target masks and held-out meta-model inputs |
|
Native R/Octave host workflow |
Host artifact manifests, native scores and fresh-process replay |
|
Optimize, resume, refit and export |
Native PLS HPO within each outer fold and Search source subsets, preprocessing chains and Ridge versus PLS |
Search contract, parent/checkpoint identity and refit state |
Conformal intervals |
Calibration cohort, calibrator and observed coverage |
|
Reuse/deploy a selected model |
Frozen input schema and portable/host-specific artifact profile |
|
Native pipeline, SDK workspace and browser/CPU transport |
Profile/version, masks, native state, SDK snapshot and closeable sessions |
The examples index connects runnable examples with guides. Synthetic datasets illustrate behavior and support deterministic qualification; they do not substitute for evidence on a real acquisition or instrument. Label that distinction in reports and charts.
A successful producer run is only half of an interlanguage tutorial. Verify the actual exported bytes and repeat prediction in the target language with fitting disabled and no access to the training workspace.
Work through the stages in order
Complex pipelines become manageable when each added mechanism has a purpose and a check. Keep the same IDs and evaluation protocol until the lesson explicitly changes them. The following stages form a practical learning path, not a claim that a single API accepts every configuration in every language.
Build one understanding at a time. First explain the input/target and validation. Then add transforms, groups, branches and sources. Finish by reloading the complete learned predictor.
Explanation: learn how a single model is evaluated, then how transforms, identity, branches and multiple modalities change its inputs. Optimize and calibrate only after that protocol is understood. Finish by replaying the selected fitted state in the intended deployment language.
Stage 1 — Read the data contract and fit a baseline
Run 1. Run your first complete experiment in your language, then the finite Ridge recipe in
10. Run the same recipe in your language. State what one row represents, the target’s physical unit and
whether observations are independent. Identify X, y, sample IDs, source IDs,
partitions and target names before fitting. The downloadable twelve-row dataset
is synthetic; its score demonstrates software behavior.
Checkpoint: explain why a validation prediction uses a model fitted without
that row, why full refit follows selection and why the deployment predictor has
no need for labels. Inspect the selected candidate and exported artifact, then
reload it. Compare sample_ids and target_names, not just the numeric array.
Stage 2 — Compare preprocessing without changing the question
Follow Preprocessing Examples and Preprocessing Overview. Start with a fixed model and fixed folds. Add SNV to suppress sample-wise scatter effects, then try a Savitzky–Golay smoother or derivative with explicit window/order parameters. Plot raw and transformed spectra against the same wavelength coordinates. A derivative changes the signal and its interpretation; a visually smooth spectrum alone does not establish predictive improvement.
Checkpoint: distinguish a row-wise transform (SNV) from a learned feature-wise transform (standardization). Explain which values are estimated on the training fold, which parameters are predetermined and which choices are selected by held-out scores. Compare RMSE using the same folds and target unit.
The executable Python examples are
examples/user/03_preprocessing/U01_preprocessing_basics.py,
U02_feature_augmentation.py and U03_sample_augmentation.py in the same
folder. Feature augmentation creates additional feature views; sample
augmentation creates training observations whose origin IDs must remain known.
An augmented sibling of a validation sample must not enter the training fold.
Stage 3 — Preserve groups and repetitions
Follow Cross-Validation Examples and Sample Repetition & Aggregation. If several spectra come from one physical specimen, a random row split can place the same specimen in training and validation. The score then answers a different, easier question. Choose the independent unit that matches deployment and split that unit.
For the portable dense fixture, this Python exercise declares six two-row groups and three fold assignments. It illustrates contract construction; replace the artificial groups with acquisition identities in real work:
{
"dataset.groups": {
"dtype": "<U3",
"shape": [
12
],
"values": [
"g0",
"g0",
"g1",
"g1",
"g2",
"g2",
"g3",
"g3",
"g4",
"g4",
"g5",
"g5"
]
},
"fold_ids": [
"f0",
"f0",
"f1",
"f1",
"f2",
"f2",
"f0",
"f0",
"f1",
"f1",
"f2",
"f2"
]
}
dataset.groups:
dtype: <U3
shape:
- 12
values:
- g0
- g0
- g1
- g1
- g2
- g2
- g3
- g3
- g4
- g4
- g5
- g5
fold_ids:
- f0
- f0
- f1
- f1
- f2
- f2
- f0
- f0
- f1
- f1
- f2
- f2
import json
from pathlib import Path
from nirs4all_core import run_pipeline
record = json.loads(Path("dataset.json").read_text())
recipe = json.loads(Path("ridge.recipe.json").read_text())
count = len(record["origin_ids"])
record["dataset"]["groups"] = {
"dtype": "<U3", "shape": [count],
"values": [f"g{i // 2}" for i in range(count)],
}
record["fold_ids"] = [f"f{(i // 2) % 3}" for i in range(count)]
model = run_pipeline(record, recipe)
ids = record["origin_ids"]
groups = dict(zip(ids, record["dataset"]["groups"]["values"]))
for fold in model.outcome["effective_plan"]["fold_set"]["folds"]:
training = {groups[s] for s in fold["train_sample_ids"]}
validation = {groups[s] for s in fold["validation_sample_ids"]}
assert training.isdisjoint(validation)
library(nirs4all)
record <- jsonlite::fromJSON("dataset.json", simplifyVector = FALSE)
recipe <- jsonlite::fromJSON("ridge.recipe.json", simplifyVector = FALSE)
record$dataset$groups <- list(dtype = "<U3", shape = list(12L),
values = as.list(paste0("g", rep(0:5, each = 2))))
record$fold_ids <- as.list(paste0("f", rep(c(0, 1, 2, 0, 1, 2), each = 2)))
model <- nirs4all_run_pipeline(record, recipe)
print(model$outcome$effective_plan$fold_set$folds)
record = fileread('dataset.json');
recipe = fileread('ridge.recipe.json');
% Replace only these fields, preserving exact array declarations elsewhere.
groups = ['{"dtype":"<U3","shape":[12],"values":' ...
'["g0","g0","g1","g1","g2","g2","g3","g3","g4","g4","g5","g5"]}'];
folds = '["f0","f0","f1","f1","f2","f2","f0","f0","f1","f1","f2","f2"]';
record = regexprep(record, '"groups"\s*:\s*null', ['"groups":' groups], 'once');
record = regexprep(record, '"fold_ids"\s*:\s*\[[^\]]*\]', ['"fold_ids":' folds], 'once');
model = nirs4all.runPipeline(record, recipe);
disp(model.outcome.effective_plan.fold_set.folds);
import {runBrowserPipeline} from 'nirs4all';
const record = await (await fetch('./dataset.json')).json();
const pipeline = await (await fetch('./ridge.recipe.json')).json();
record.dataset.groups = {dtype: '<U3', shape: [12],
values: Array.from({length: 12}, (_, i) => `g${Math.floor(i/2)}`)};
record.fold_ids = Array.from({length: 12}, (_, i) => `f${Math.floor(i/2)%3}`);
const model = await runBrowserPipeline(record, {pipeline});
console.log(model.outcome.effective_plan.fold_set.folds);
The JSON/YAML tabs show the two changed fields, not a full dataset document: dataset.groups names the nested field and fold_ids remains at the outer level. Apply these changes to the downloaded twelve-row record. The Octave tab edits only the two declared fields in the exact JSON text so singleton shape arrays elsewhere remain arrays.
Expected result: six groups of two observations, divided into three folds. Both rows of each group have the same fold ID. The Python assertions inspect the effective plan and confirm no group appears on both sides.
Checkpoint: remove fold_ids and check that grouped native execution refuses
an implicit unsafe split. Describe whether prediction is scored per observation
or after specimen aggregation. Keep group, repetition and origin identity
separate; each describes a different relationship.
Stage 4 — Branch, merge and stack
Read Pipeline Branching, Branch Merging and Meta-Model Stacking User Guide. A branch creates parallel paths or separates observations by a declared condition. Merging features concatenates compatible feature views; merging predictions constructs model outputs for another estimator. Neither operation should quietly reorder samples.
For stacking, use the worked example
examples/user/04_models/U03_stacking_ensembles.py. Read the feature/prediction
merge before copying its pipeline. Each meta-training row must contain a
base-model prediction made without fitting on that row. Predictions from
full-data fitted base models are useful for deployment, but would leak
information if used to train the meta-model on those same observations.
Checkpoint: draw the sources of each meta-feature and locate its OOF prediction. Explain what gets refitted when the winner is selected: base models, meta-model and any fitted preprocessing. Confirm the merge carries IDs rather than relying on incidental row positions.
Stage 5 — Add typed modalities and missing sources
Run Native multimodal early fusion before
Fusion tardive avec observations partielles. The first introduces NIR,
image, time-series and metadata encoders with explicit axes and fitted state.
The second introduces partial cohorts and late fusion. Read
examples/user/02_data_handling/U08_multimodal_targets.py,
U09_multimodal_missing_sources.py and U10_multimodal_late_tuning.py alongside
their guides.
Explanation: each modality retains its own representation and encoder. Fusion joins encoder outputs or OOF model outputs by sample identity, using explicit source presence and target observation masks. Missing imagery is not a valid all-zero image, and an unobserved target is not a target value of zero.
Checkpoint: inspect raw shapes, encoded dimensions, missing-source behavior and target masks. Explain whether you are using early fusion of features or late fusion of predictions, and how a sample missing one modality is treated. For Octave native workflows, continue with Octave models in a Python multimodal workflow; keep its host artifact profile distinct from the all-Methods example.
Stage 6 — Tune structure, then calibrate
Use Native PLS HPO within each outer fold for fold-safe PLS tuning and
Search source subsets, preprocessing chains and Ridge versus PLS for changes in sources, encoders,
preprocessing chains and fusion topology. The corresponding examples include
U17_structural_hpo_ridge_pls.py, U18_structural_hpo_preprocessing_chains.py,
U19_structural_hpo_source_subsets.py, U20_structural_hpo_typed_modalities.py
and U21_structural_hpo_early_late.py under examples/user/04_models/.
Changing topology changes both the hypothesis being tested and its fitted state. Record the search space, trial budget, metric direction and seed. A resume checkpoint carries the original study identity; editing the search and calling it a resume compromises the comparison. Nested evaluation may be needed to estimate the performance of the whole selection procedure; winning CV scores alone are optimistically selected estimates.
Then follow Native tuning and conformal calibration on disjoint calibration/test cohorts. Measure coverage and interval width on held-out data, with target units and cohort provenance in the figure caption.
Checkpoint: distinguish the fitted model, the optimizer checkpoint and the calibrator. Identify which artifact supports PREDICT, which supports search resumption and which adds uncertainty output without refitting the predictor.
Stage 7 — Assemble an advanced workflow and qualify its consumer
Run the complete D08 exercise after the code boxes in 5. Build a pipeline you can explain. It performs three separate checkpoints: 62-column feature fusion, two-column prediction stacking, and 34-column two-source residual learning. Read the expected shapes on each page before running it. Record the measured validation/test scores; do not assume complexity improves them.
Your own final workflow should declare typed sources, explicit identity-safe folds, fold-fitted preprocessing/encoders, a bounded candidate search, a fusion design, selection/full refit and a separate calibration/test cohort when intervals are needed. Draw its graph before executing it. Label each node’s accepted input, output shape and fitted-state owner. Use the executable structural examples above as the starting point for the topology that matches your data.
Checkpoint: export, close the producer, start the target-language consumer and predict target-free inputs. Follow 8. Save a predictor and use it on new observations and Move a native model or a complete experiment. Change one source unit or feature order deliberately and verify rejection. Compare values within a declared tolerance and IDs exactly. A complex training run is complete only when its selected model can be replayed with the same scientific meaning.
Keep a small experiment notebook
For every stage, retain the following record with the output rather than relying on recollection:
Field |
What to write |
|---|---|
Scientific question |
Target, unit, independent unit and intended new cohort |
Data |
Source schemas, masks, IDs, provenance and exclusions |
Evaluation |
Folds/partitions, aggregation, metrics and selection rule |
Pipeline |
Ordered nodes, parameters, branch/merge semantics and runtime |
Search |
Candidates/budget, seed, study/checkpoint identity |
Result |
OOF scores, selected candidate, refit state and test result |
Deployment |
Export type, consumer cohort, replay comparison and refusals |
This record also supplies the captions and provenance for your charts. It makes an impressive graph inspectable: a reader can follow where each input went, what each fitted node learned and which unseen observations justified the reported result.
Explain the final experiment without mentioning APIs
Describe: “I align spectrum and marker rows by specimen ID; normalize spectra and scale markers within training folds; join their prepared features; compare candidate model settings on held-out specimens; refit the winner; save all learned transformations; and apply the saved predictor to raw new measurements.”
Then draw your actual graph and add each node’s name, input shape and expected output. If that description and graph agree with the executed recipe, you are ready to explain the model to a collaborator.