Skip to content

Training YAML: fields and complete examples

Use config/training.yml to choose the data, target, estimator, evaluation and promotion policy for each model. This reference explains every accepted training, model and tuning field, then supplies a complete file for each layout. The result of training is a fitted registered candidate with source/split/quality evidence; activation and scoring remain separate decisions.

Use a Core package matching your generated project; see runtime packaging. These local configuration examples do not create Delta tables, contact Databricks or prove cloud permissions. For first deployment and operator actions, follow the Bundle walkthrough.

File ownership and inheritance

File or section What belongs here
training.yml > defaults Shared data, engine, split/CV, budgets, gates and optional model settings
training.yml > models.<name> One model or candidate's explicit overrides
pre_split.yml Fixed training eligibility and normalization before splitting
preprocessing.yml Ordered fitted transformations; selected recipes fit inside training folds
inference.yml Scoring source, version selector, publication/change policy and score handoff; model-set activation/publication
deployment/variables.yml and target overrides Job compute, identities, schedules and environment bindings
src/features/ Trusted custom functions and scoring policy selected before training

The tables below apply to shared defaults and individual model entries unless their conditions say otherwise. A model entry replaces the whole value of a shared field; nested mappings are not deep-merged. For example, overriding model replaces its type and entire params mapping. Repeat the parameters you still need. Do not define another Python model declaration or workflow.json beside these YAML owners.

Competition candidates share one source, target, split, CV and selection policy. Their entries can override only model, tuning, decision_threshold, preprocessing_recipe and explainability. Competition does not accept features_path or pre_split_recipe in its defaults; use the default pre-split recipe. Independent target models can have different labels, inputs, recipes, models and gates, but must share the exact source snapshot and record keys. Never include any sibling target in a branch's inputs.

Root structure

Key Type/default Meaning
version Integer; required 1 Version of this YAML document, independent of the runtime config version below.
defaults Mapping; {} if omitted Fields inherited by every named model. Complete effective settings still need the required fields.
models Nonempty mapping; required Safe names starting with a letter, at most 64 ASCII letters/digits/underscore/hyphen. Single layout has one entry; competition needs at least two.
pipeline Optional mapping; usually omit Only empty preprocessing: [], empty modeling: {} and shared explainability are accepted. Recipe lists belong in their own files. Do not duplicate explainability here and in defaults/models.

YAML uses real numbers, booleans and null, rather than the string answers used by the init wizard. Quote dates such as "2026-01-01T00:00:00Z" so YAML does not turn them into timestamp objects. Unknown/duplicate keys, aliases, nonfinite numbers and documents larger than 64 KiB are rejected.

Training fields

Default means the generated quick-project value, or the runtime default where a field is omitted. Required fields must be present after inheritance. Inactive optional fields should be omitted or null, as specified. The last column identifies the corresponding init answer; YAML means edit the field directly. Detailed setup exposes more init answers than quick setup. Branch answers use the same question name with a branch_N_ prefix.

Identity, columns and runtime

Field Type Default/required Values and conditions What changes Init answer or owner
config_version Integer Required 1 Runtime workflow contract Keep 1; not a model version. Generated
training_layout String single_model single_model, model_competition, multi_target Chooses one model, one-target competition or independent target branches. Changing it requires the matching generated task graph. training_layout
task String regression regression, classification; must match estimator Chooses label/metric semantics. Multi-target entries can differ. task
engine String pandas pandas, polars; distributed fitted scoring requires pandas Chooses local training and fitted preprocessing engine, not compute size. engine
training_table String Required Three-part Delta name after target binding Source of labeled rows; generation/deployment does not create it. source_table_name plus catalog/input schema
training_table_version Integer or null Omitted/null Nonnegative Delta version, including 0 Null resolves latest once per invocation; an integer pins that snapshot. training_table_version
training_label_table String or null Omitted/null Separate three-part Delta table; requires availability filtering and exact record-key types Left-joins actual targets and availability into the training snapshot. External values replace embedded targets. See separate outcomes. source_layout=separate_outcomes, or YAML
training_label_table_version Integer or null Omitted/null Nonnegative Delta version; requires training_label_table Null pins latest once per invocation; saved evidence also retains the immutable table identity. New labels can make an unchanged feature snapshot eligible for retraining. YAML
record_key_columns String list Required Nonempty, nonnull, unique source identities; distinct roles Stable row membership, split evidence and output alignment. Generated publication uses string/BIGINT keys. record_key_columns
input_columns String list Required Ordered raw model inputs; excludes keys, targets, weights and time/group control roles Selects features before saved preprocessing. input_columns
target_column String Required One source label, distinct from other roles Chooses the prediction target; omit it from future scoring input. target_column
model_name String Required Three-part registry name in the active metadata schema; include deployment suffix Names the registered candidate. Multi-target branches need distinct names. Generated from project/branch name; YAML override
tracking_uri String Runtime default databricks; usually omitted Tracking store setting, credentials supplied separately Chooses experiment/run storage. YAML
registry_uri String Runtime default databricks-uc; usually omitted Registry store setting, credentials supplied separately Chooses version/alias storage independently of tracking. YAML

Final holdout, sampling and memory

Field Type Default/required Values and conditions What changes Init answer or owner
split_strategy String random random, temporal Random selects a fraction; temporal fits earlier rows and holds out the later interval. split_strategy
test_size Number or null 0.2 Random: strictly between 0 and 1; temporal: omit/null or retain inactive 0.2 Final holdout fraction, separate from CV folds. test_size
random_state Integer or null 42 Random: 0 through 2**32-1; temporal: omit/null or retain inactive 42 Random holdout seed. random_state
stratify Boolean or null false True only for random classification; group holdout requires false Retains class proportions where supported. stratify
training_sample_rows Integer or null Omitted/null At least 4 and at most max_rows Seeded bounded Spark-side selection before local transfer and splitting; does not sample predictions. training_sample_rows
training_sample_seed Integer 42 0 through 2**32-1 Reproduces requested sample membership; saved evidence pins it for approval. training_sample_seed
max_rows Integer 10000 Positive; aggregate fit/holdout input must fit Rejects a larger local training population instead of silently truncating it. max_rows
max_input_mb Integer 64 Positive whole MiB; one unit is 1,048,576 bytes Bounds decoded/local training data, not Spark wire buffers or total process memory. max_input_mb

Source windows and result availability

Field Type Default/required Values and conditions What changes Init answer or owner
training_window_mode String Quick: full_snapshot full_snapshot, fixed_window, rolling_calendar, rolling_days; init-only auto resolves before emission Selects source rows independently of final splitting and scheduling. training_window_mode
event_column String or null Omitted/null Required for window selection or temporal split; absent for full snapshot Observation clock used for selection/ordering, not label availability. event_column
event_time_parsing Mapping or null Omitted/null format, timezone, date_only; see nested table Explicitly interprets non-native event dates; no guessed string parsing. event_time_format, event_time_timezone, event_date_only
start Quoted timestamp or null Omitted/null Aware ISO instant; required for fixed window Inclusive observation-window start. Rolling modes derive it at invocation. start
cutoff Quoted timestamp or null Omitted/null Aware ISO instant, after start; required for fixed window Exclusive observation-window end; independent of source commit time. cutoff
holdout_start Quoted timestamp or null Omitted/null Temporal: start < holdout_start < cutoff; null for random Boundary between earlier fit rows and final temporal holdout. holdout_start
monthly_lookback_months Integer or null Rolling calendar: 4; otherwise omitted 1..120 for random, 2..120 for temporal; only rolling_calendar Number of complete calendar months read, including temporal holdout months. monthly_lookback_months
holdout_months Integer or null Rolling temporal: 1; otherwise omitted At least 1 and less than monthly_lookback_months Final complete months reserved for holdout. holdout_months
window_timezone String or null Rolling calendar: UTC; otherwise omitted Valid IANA zone; only rolling_calendar Defines calendar-month boundaries, including DST. Cron timezone does not set this. window_timezone
lookback_days Integer or null Rolling days: 90; otherwise omitted 1..36500; only rolling_days Number of elapsed UTC days before invocation. lookback_days
holdout_days Integer or null Rolling-days temporal: 14; otherwise omitted At least 1 and less than lookback_days; temporal only Latest elapsed days reserved for final holdout. holdout_days
filter_unavailable_results Boolean false Enable only with result column and cutoff policy Excludes unknown/not-yet-available labels from both train and holdout. filter_unavailable_results
result_available_at_column String or null Omitted/null Required when filtering; distinct from event/key/input/target roles Records when each label became available. result_available_at_column
result_cutoff Quoted timestamp or null Omitted/null Aware ISO instant; filtering only Keep labels available by this instant, including equality; null derives invocation minus lag. result_cutoff
result_availability_lag_hours Integer or null Filtering: 0; otherwise omitted 0..87600; omit/null when filtering is off Delays the derived result cutoff; an explicit result_cutoff takes precedence. result_availability_lag_hours
result_time_parsing Mapping or null Omitted/null Same nested fields as event parsing; filtering only Interprets label-availability values independently of event dates. result_time_format, result_time_timezone, result_date_only

Cross-validation on training rows

Field Type Default/required Values and conditions What changes Init answer or owner
cv_enabled Boolean false; competition forces true Competition requires true Fold-local model evaluation/search; final holdout stays outside it. cv_enabled
cv_type String k_fold k_fold, stratified_k_fold, time_series_split, shuffle_split, nested_cv, group_k_fold, stratified_group_k_fold Chooses fold membership. Stratified methods require classification; temporal/group methods require their controls. cv_type
cv_folds Integer 5 2..20 Number of folds, or outer folds for nested CV. Each fold needs at least two fit and two validation rows. cv_folds
cv_shuffle Boolean true Temporal false; shuffle_split true Controls permitted fold randomization. cv_shuffle
cv_random_state Integer 42 0 through 2**32-1 CV seed, independent of the final holdout seed. cv_random_state
cv_inner_folds Integer or null Omitted/null 2..20; nested CV Explicit inner folds; omitted selects min(3, outer-1), with minimum 2. cv_inner_folds
cv_nested_type String auto auto, k_fold, stratified_k_fold, time_series_split, group_k_fold, stratified_group_k_fold Inner/outer policy for nested CV; non-auto requires nested_cv. cv_nested_type
cv_group_column String or null Omitted/null Required for group methods; otherwise null; distinct control column Keeps entities separate; final group holdout also excludes shared groups. Not a model input. cv_group_column
cv_gap Integer 0 Nonnegative row count; temporal CV only Gap between fit and validation windows; not a duration. cv_gap
cv_test_size Integer or null Omitted/null Positive rows; temporal CV only Explicit fold validation length; different from fractional final test_size. cv_test_size
cv_max_train_size Integer or null Omitted/null Positive rows; temporal CV only Maximum training rows in each temporal fold. cv_max_train_size

Quality, weights and bounded reports

Field Type Default/required Values and conditions What changes Init answer or owner
metric String heldout_rmse / heldout_accuracy Task-compatible supported heldout_ metric; competition has a stricter shared metric subset Primary final comparison objective; error/loss minimizes, other scores maximize. regression_metric / classification_metric
min_improvement Number 0.0 Finite, nonnegative, absolute metric units Required improvement over champion; a tie never passes. min_improvement
min_scored_fraction Number or null 0.90 0 to 1; null disables Minimum share of holdout rows a model that can exclude rows must score; see models that exclude rows. min_scored_fraction
max_coverage_drop Number or null 0.05 0 to 1; null disables Largest allowed drop in scored share against the champion (0.05 = five percentage points). max_coverage_drop
quality_threshold Number or null Omitted/null Finite task-valid bound; required for automatic promotion and first-champion approval Maximum acceptable error/loss or minimum acceptable score. Set before training; later edits cannot relax a saved candidate. quality_threshold
quality_gates Mapping or null Omitted/null Additional task-compatible metric: bound pairs Every configured absolute gate must pass, including first activation. YAML; branch quality-gates answer where offered
promotion_policy String manual_approval manual_approval, automatic Individual candidate activation. Multi-target branches stay manual; the complete set's policy belongs in inference.yml. promotion_policy
champion_version String or null Omitted/null Concrete positive version string Optional expectation guard; generated lifecycle resolves current controlled champion and rejects a mismatching explicit value. Does not move an alias. YAML
weight_column String or null Omitted/null Existing numeric source column, distinct from all protected/input roles Supplies training sample weights; support depends on estimator. Holdout metrics and threshold selection remain unweighted. sample_weight_enabled, weight_column
risk_category String or null Omitted At most 256 UTF-8 bytes Metadata tag only; does not change gates or compute policy. risk_category
save_preprocessing_diagnostics Boolean false Explicit opt-in Saved/reloaded preprocessing diagnostic on at most 256 holdout rows, 8 MiB per frame; not worker admission. YAML
allow_tested_custom_steps Boolean false; automatic with batch_compute: spark or online_features Explicit opt-in for other projects, such as endpoint-only serving Seals partition evidence for eligible models, ensembles and built-in preprocessing; also lints and probes column_step/fitted_step steps and saved scoring rules. Checks use 16 to 256 holdout rows. A failing required check stops training before registration. See Built-in evidence and Custom row steps. YAML
evaluation_charts Mapping or null {enabled: false} enabled plus bounded sample/display controls below Saves an evaluation sample for the independent chart task. Enabling a previously omitted task also requires matching resources/notebook. evaluation_charts_enabled; detailed limits in YAML
competition_max_candidates Integer 8 2..32 in loader; init wizard offers up to 8 Bounds distinct candidate declarations in a one-target competition. Generated; YAML for explicit validated limits
competition_max_trials Integer Generated 1000; loader fallback 100 1..10000; competition only Aggregate search-work bound including nested/halving work, not wall-clock seconds. competition_max_trials
feature_lookup Mapping or null Omitted/null Native lookup declaration; pandas and admitted Spark/online path Adds explicit offline feature binding and source evidence; not an arbitrary join. Online opt-in creates placeholders; complete YAML
online_features Mapping or null Omitted/null Requires feature_lookup, pandas, non-temporal supported request shape Saves required-feature and freshness guards with the model; does not create a store or endpoint. include_online_feature_lookup creates placeholders; complete YAML

Class and row weights

Set classifier class weights in model.params, for example params: {class_weight: balanced}. null disables them. Keep a class weight fixed by removing its search axis; to tune it, omit the fixed parameter and retain the axis under tuning.search_space. Supported classifiers may also use row weights.

Set weight_column: importance in defaults or a target entry. Omit it or use null for unweighted fitting. Competition uses one shared column. Every active fold requires finite nonnegative values with positive total weight. Weights are not normalized. Declared weight columns remain reserved across branches and cannot be features, targets, keys, group columns or timestamps.

Weights affect estimator fitting; selection, holdout and threshold metrics remain unweighted. Built-in row changes carry explicit row positions; unsupported custom row mapping fails. Synthetic sampling needs an explicit supported weight policy. Source snapshots preserve the training column and saved evidence records its aggregate statistics and ordered digest. Logs do not print raw weight vectors. Scoring uses the saved model and needs no weight column. Weight-only edits do not count as fresh labels for automatic retraining; train explicitly after such edits.

Model and recipe fields

Field Type Default/required Choices and effect Init answer or owner
model Mapping Required here or inherited type is a registered task-compatible Core model ID; params is an optional mapping. Omit tuning to fit this model once. regression_model / classification_model and model_params; branch/candidate variants
tuning Mapping Omitted disables search; generated projects normally include it Wraps the model in the existing tuner. Fields below use native search metrics, not heldout_ names. Search questions and YAML
preprocessing_recipe String default Named list in preprocessing.yml. Each candidate/branch learns its own fitted state even when sharing a recipe. preprocessing_recipe; YAML
pre_split_recipe String default Named fixed eligibility list in pre_split.yml. Competition always uses recipes.default and rejects this field. pre_split_recipe; YAML
features_path String Generated src/features Alternative project feature package; supported only for multi-target model entries. Its source, assets and exact dependency pins are captured. YAML
decision_threshold Mapping {mode: off} Classification only: off, manual or auto. Changes hard-label decisions without changing probabilities; nested fields below. decision-threshold questions; YAML
explainability Mapping Omitted disables it SHAP method and bounded sample/display limits below; packages and report task must be present. shap_enabled and SHAP limit questions

model.params uses the selected estimator's public parameters. For example, ridge_regression accepts alpha, while a random forest accepts n_estimators and max_depth. Parameters do not transfer automatically when model type changes. Use python src/tools/preview.py --list-models and the model catalog for parameters, default search spaces, weights and ready-to-use YAML for each model. class_weight belongs in the compatible classifier's params; it is distinct from a source weight_column.

Tuning fields

These keys go inside models.<name>.tuning or a shared defaults.tuning. Deleting the entire mapping disables search. An empty mapping still requests a tuner and needs a compatible objective. Learned preprocessing is refitted within the training folds; neither search nor calibration can use the final holdout. The search and ensemble guide explains the five strategies, their additional controls, CV, competition budgets and complete examples.

Field Type Default/required Choices and effect Init answer or owner
strategy String random grid, random, optuna, halving_grid, halving_random. Optuna needs the matching optional dependencies. search_strategy
metric String Required; generated rmse / accuracy Native Core metric such as rmse, r2, accuracy or f1; not heldout_rmse. Must match task and competition objective where applicable. regression_search_metric / classification_search_metric
n_trials Integer 10 1..1000; trial/candidate budget for the selected strategy. Grid enumeration additionally respects max_candidates. search_n_trials
random_state Integer 42 0 through 2**32-1; search seed independent of final split/CV seeds. search_random_state
max_candidates Integer 1000 1..10000; rejects exhaustive grids above this bound before fitting. search_max_candidates
search_space Mapping {} Parameter to nonempty finite scalar candidate list; empty means registered default space with fixed params preserved. At most 128 axes, 100 values/axis and 64 KiB. search_space
timeout Number or null Omitted/null Positive finite seconds, optuna only; not an overall job deadline. search_timeout
strategy_params Mapping {} Optuna sampler/pruner/pruning or halving factor/resource/min/max controls below; nonempty rejected for grid/random. Advanced search questions
tune_threshold Boolean false Binary-classification nested-CV threshold search only; incompatible with an active decision_threshold. Prefer an explicit decision_threshold. search_tune_threshold

For a fixed value use model.params, for searched values use candidate lists: search_space: {alpha: [0.1, 1.0, 10.0]}. A manual search axis cannot contradict a fixed parameter. Unknown parameter names fail instead of being silently ignored. The outer job's CPU/memory limits still apply; search uses one estimator worker to avoid nested parallel fits.

Nested settings and conditional controls

Mapping Fields, defaults and valid choices Meaning
event_time_parsing / result_time_parsing format: null, timezone: null, date_only: reject (or midnight) Native timestamps need no format. Naive local clocks need an IANA timezone; offset-bearing formats must not also set one. Date-only values require midnight plus timezone. DST gaps/overlaps are rejected.
decision_threshold, mode off Only mode: off Keep the estimator's normal hard-label decision.
decision_threshold, mode manual Binary: value in [0,1] plus typed positive_class; or thresholds: [{class: ..., value: ...}, ...] covering every class with positive values Bind original class labels, not probability column indexes. Per-class decisions use the saved threshold policy.
decision_threshold, mode auto metric: balanced_accuracy (also f1, f1_macro, f1_weighted, matthews_corrcoef), validation_fraction: 0.2 strictly between 0 and 1, random_state: 42, optional positive_class Uses separate training-side calibration; temporal models retain chronological ordering.
explainability Required method: shap; max_samples 100 (1..200), max_features 30 (positive integer, no fixed upper bound), max_display_samples min(10,max_samples) (0..50 and no larger than max_samples) Training explanation and report display bounded by the configured budgets.
evaluation_charts Required boolean enabled; max_rows 2000 (2..10000), max_features 20 (1..50), max_classes 20 (2..50) Bounded saved holdout chart sample; does not change full holdout metrics.
feature_lookup lookups list, each with table_name, lookup_key and feature_names; optional timestamp fields only on supported offline paths Declare every fetched feature in input_columns. Keep lookup controls/source identities explicit. See online guide for exact native contract.
online_features required_features and freshness: [{column: ..., max_age_seconds: ...}] Cover all fetched features; each lookup needs its saved DOUBLE UTC-epoch freshness input. The age bound is an explicit domain choice, not an automatic default.
tuning.strategy_params for optuna sampler: tpe (random, cmaes); pruner: median (hyperband, none); optional boolean pruning Configures the installed Optuna integration.
tuning.strategy_params for halving factor: 3 (2..10); resource: n_samples; min_resources and max_resources depend on resource Custom estimator resource needs a compatible parameter and explicit positive maximum. Bounds and supported count strings are validated before fitting.

See online feature lookup, classification threshold policy and preprocessing context for their complete execution contracts. A valid field set does not certify a fitted recipe for Spark or serving.

Three complete training files

Start with a newly generated project of the same layout. Keep its config/inference.yml, src/features/, tool scripts and resource graph. Replace only training.yml with the chosen complete block below, and use these companion recipes. Both files require a default recipe, even if it is empty.

In config/pre_split.yml:

version: 1
recipes:
  default: []

In config/preprocessing.yml:

version: 1
recipes:
  default:
    - name: fill_numeric
      transformer: SimpleImputer
      params: {columns: [income, age], strategy: mean}
    - name: scale_numeric
      transformer: StandardScaler
      params: {columns: [income, age]}

The examples use numeric regression targets to keep estimator/metric roles easy to compare. The source is an existing Delta table with nonnull unique BIGINT customer_id, DOUBLE income/age, and DOUBLE revenue/cost labels. For local fit verification you can create the following frame; it does not upload a table:

import pandas as pd

frame = pd.DataFrame({
    "customer_id": range(1, 41),
    "income": [float(i) for i in range(1, 41)],
    "age": [float(20 + i % 10) for i in range(1, 41)],
    "revenue": [float(2 * i + 5) for i in range(1, 41)],
    "cost": [float(i + 2) for i in range(1, 41)],
})
assert frame.customer_id.is_unique and frame.shape == (40, 5)

One model for one target

Save this entire block as config/training.yml in a single_model project. The imputer/scaler are learned separately on training rows; the final 20% holdout does not fit them. The absolute RMSE gate is illustrative.

version: 1
defaults:
  config_version: 1
  training_layout: single_model
  engine: pandas
  task: regression
  training_table: "{catalog}.{input_schema}.customer_training"
  training_table_version: null             # Resolve latest once when invoked.
  record_key_columns: [customer_id]
  input_columns: [income, age]        # Keys and labels are not features.
  target_column: revenue
  model_name: "{catalog}.{metadata_schema}.revenue_model{resource_suffix}"
  split_strategy: random
  training_window_mode: full_snapshot # No event timestamps are needed.
  test_size: 0.2
  random_state: 42
  stratify: false
  filter_unavailable_results: false
  max_rows: 10000
  max_input_mb: 64
  cv_enabled: false
  metric: heldout_rmse
  min_improvement: 0.0
  quality_threshold: 5.0              # Needed for first-champion approval.
  promotion_policy: manual_approval
  preprocessing_recipe: default
  pre_split_recipe: default
models:
  revenue:
    model:
      type: ridge_regression
      params: {alpha: 1.0}

There is no tuning mapping, so this example fits the stated fixed ridge model. A generated wizard project normally includes tuning; remove that mapping deliberately if a single fixed fit is intended.

Several algorithms competing for one target

Save this in a model_competition project. Candidates share final holdout and three-fold training CV. Candidate overrides change only model/search/recipe settings. The winner alone proceeds to final holdout comparison and registration.

version: 1
defaults:
  config_version: 1
  training_layout: model_competition
  engine: pandas
  task: regression
  training_table: "{catalog}.{input_schema}.customer_training"
  training_table_version: null
  record_key_columns: [customer_id]
  input_columns: [income, age]
  target_column: revenue
  model_name: "{catalog}.{metadata_schema}.revenue_competition{resource_suffix}"
  split_strategy: random
  training_window_mode: full_snapshot
  test_size: 0.2
  random_state: 42
  stratify: false
  filter_unavailable_results: false
  max_rows: 10000
  max_input_mb: 64
  cv_enabled: true                   # Required by competition.
  cv_type: k_fold
  cv_folds: 3
  cv_shuffle: true
  cv_random_state: 42
  competition_max_candidates: 8
  competition_max_trials: 100
  metric: heldout_rmse               # Shared objective, not training accuracy.
  min_improvement: 0.0
  quality_threshold: 5.0
  promotion_policy: manual_approval
  preprocessing_recipe: default
models:
  ridge:
    model: {type: ridge_regression, params: {alpha: 1.0}}
  forest:
    model:
      type: random_forest_regressor
      params: {n_estimators: 20, max_depth: 4, random_state: 42}

To search ridge alpha instead, remove fixed alpha from that candidate's params and add tuning: {strategy: grid, metric: rmse, max_candidates: 10, search_space: {alpha: [0.1, 1.0, 10.0]}}. Search metric is rmse; the final comparison metric remains heldout_rmse. Keep aggregate work inside the competition budget. Do not tune parameters against the final heldout rows.

Independent models for two targets

Save this in a multi_target project. Both branches share one source snapshot and keys; each has its own target, model and model name. Missing labels are excluded per target. No branch uses the sibling label as a feature.

version: 1
defaults:
  config_version: 1
  training_layout: multi_target
  engine: pandas
  task: regression
  training_table: "{catalog}.{input_schema}.customer_training"
  training_table_version: null
  record_key_columns: [customer_id]
  input_columns: [income, age]
  split_strategy: random
  training_window_mode: full_snapshot
  test_size: 0.2
  random_state: 42
  stratify: false
  filter_unavailable_results: false
  max_rows: 10000
  max_input_mb: 64
  cv_enabled: false
  metric: heldout_rmse
  min_improvement: 0.0
  quality_threshold: 5.0
  promotion_policy: manual_approval    # Set activation is separate in inference.yml.
  preprocessing_recipe: default
  pre_split_recipe: default
models:
  revenue:
    target_column: revenue
    model_name: "{catalog}.{metadata_schema}.revenue_model{resource_suffix}"
    model: {type: linear_regression, params: {}}
  cost:
    target_column: cost
    model_name: "{catalog}.{metadata_schema}.cost_model{resource_suffix}"
    model: {type: ridge_regression, params: {alpha: 0.5}}

The model_set block in inference.yml owns complete-set activation and output storage; choose its output mode as described in model sets. Individual component aliases are not the set's release mechanism. Branches do not compete against each other: they predict different targets. A failed branch leaves earlier immutable candidates available but produces no complete successful set.

For the generated graph, MLflow parameters, evaluation charts and SHAP reports, see training reports. Configure classification decision thresholds separately from quality gates.

Check edits before training

Run from the generated project with the matching Core installed:

uv pip install -r deployment/train-requirements.txt
python src/tools/smoke.py
python src/tools/preview.py --action train --catalog workspace --input-schema demo --output-schema demo --metadata-schema demo
python src/tools/refresh_training_graph.py

Replace the preview bindings with the intended target's values; see the local tools reference for every flag and default. Preview does not read deployment/targets.yml for you. After changing model names, candidates or branch count, refresh the graph and validate/deploy it before running the job. Scoring source/model selector/model-set settings remain in the existing inference.yml; update them to the intended trained names and concrete versions. The training file alone is not a complete deployment or scoring configuration.

Smoke checks configuration/source syntax; preview resolves trusted recipe builders and displays each candidate/branch's effective model, inputs, window, CV and metric. Neither fits a model or reads Delta data. Expect one model preview, two competition candidates or two target branches for the examples above. Use a real fit/save/predict check on representative rows before production.

If a pre-split recipe reads the target, choose scoring behavior before preview: in config/scoring.yml set skip_target_pre_split_steps: true to retain feature-only reuse while skipping target-reading filters, or choose mode: custom for separately defined scoring eligibility. Mixed target/feature filters are skipped whole. An imputer does not override a scoring eligibility exclusion; excluded outcomes retain their keys and null predictions.

Error or surprising result Action
Unknown/duplicate key or multiple owners Correct the exact field or remove the competing declaration; values are not merged silently.
Recipe missing/default recipe absent Add a default recipe and select an existing named list in the matching phase file.
Target-reading pre-split recipe blocks preview Choose the explicit scoring switch/mode above; the examples' empty pre-split recipe needs neither change.
First-champion approval requires a bound Set quality_threshold before training and create a new candidate with saved matching evidence.
Automatic policy lacks quality_threshold Supply a domain-appropriate absolute bound before training.
Window controls active in the wrong mode Omit/null inactive calendar/day/event/result fields; choose the intended window and split independently.
No tuning intended but search runs Remove tuning mapping, rather than leaving an empty one.
Budget exceeded Deliberately reduce the eligible source/sample or review a larger limit; prediction never samples automatically.
Model/params changed but scoring is unchanged Train a new version, select/activate it deliberately, and run scoring; old artifacts keep their saved state.

Continue to preprocessing recipes for node choices, SDK workflows for Python use, and the operator walkthrough for actual run/task inspection and copyable approval/rollback fields.