Skip to content

Configuration & presets

One object drives every ml4 run: Ml4Config. It describes the problem (what you are predicting and how success is scored) and the strategy (which models to train, how to split, how to engineer features, and what to deliver). You hand it to AutoSupervised, and the engine does the rest.

In short

  • Ml4Config holds the problem fields (ml_task, eval_metric, …) at the top level and everything else in composed sections such as engine, hpo and data.
  • data (DataConfig) maps your columns to roles: the target, numeric and categorical columns, and the domain lists that route columns to specialised encoders.
  • Leave eval_metric at None to get the task default: logloss for classification, rmse for regression.
  • Start from one of the sixteen presets (presets.default(), presets.quick_explore(), …) and override fields; ml4 re-validates the config when you call fit.
import parleml_ml4
from parleml_ml4 import AutoSupervised, Ml4Config, Tasks
from parleml_ml4.contracts.metrics import EvalMetric

cfg = Ml4Config()
cfg.ml_task = Tasks.BINARY_CLASSIFICATION
cfg.eval_metric = EvalMetric.f1
cfg.data.target_column = "churned"

model = AutoSupervised(cfg).fit(train_df)

Ml4Config is a plain Python dataclass, so it is fully introspectable, copyable, and serialisable. There is no hidden global state — a run is exactly what its config says it is.

The composite object model

Ml4Config is deliberately thin. Only the problem-definition fields live on the top-level object; everything else is a composed section dataclass with its own defaults. This keeps the surface honest: a change in one area (say, imputation) never disturbs another.

Top-level fields

These describe the problem itself and are read at every layer:

Field Type Purpose
ml_task Tasks BINARY_CLASSIFICATION (default), MULTICLASS_CLASSIFICATION, or REGRESSION.
eval_metric EvalMetric | None Optimisation + reporting metric. None resolves to the task default (see below).
beta_threshold float Threshold for the beta metric family (default 0.5).
positive_class str | None Binary only — the label to encode as class 1; asserted after fit.
user_defined_metric_ref str | None Importable "package.module:qualname" for a custom metric.
user_defined_metric_direction "minimize" | "maximize" | None Optimisation direction for the custom metric.

Composed sections

Each section is reached as an attribute of the config and holds a focused group of knobs. Set a nested field by assigning to it directly.

cfg.engine.num_bag_folds = 8                 # cross-validation folds
cfg.data.target_column = "y"                 # column roles
cfg.constraints.monotone_auto = True         # monotonic constraints
Section Type What it governs Guide
execution ExecutionConfig Seed, device, parallelism. Reproducibility, GPU
data DataConfig Target + column-role mapping. this page
engine Ml4EngineConfig Training policy (cross-validated ensemble or single model + holdout), folds, stacking, time budget. Training workflow
hpo HPOConfig Hyperparameter search — ml4's Optuna study or AutoGluon's own (provider, off by default). Hyperparameter search
roster Ml4RosterConfig Which model specs / regulated model to train. Algorithms & metrics
constraints ConstraintsConfig Monotonic constraints. Constraints
baseline BaselineConfig Constant-baseline honesty gate. Training workflow
imputation ImputationConfig Missing-value strategy. Feature engineering
balancing BalancingConfig Resampling for imbalance. (classification only) Imbalanced data
outliers OutlierRemovalConfig Training-row outlier removal. Feature engineering
stateless StatelessFEConfig Domain-aware stateless encoders. Stateless encoders
target_encoding TargetEncodingConfig Categorical target / WOE encoding. Feature engineering
target_transform TargetTransformConfig Regression y-transform tuning, plus the glass-box feature-side power / cyclical switches. (the y-transform half is regression only) Supervised learning (y-transform) · Feature engineering (feature-side switches)
stateful_fe StatefulFEConfig Interactions, clustering, GP text. Feature engineering
temporal TemporalEncodersConfig Datetime / recency encoders. Temporal encoders
feature_selection FeatureSelectionConfig Ranker-based feature pruning. Feature engineering
calibration CalibrationConfig Post-hoc probability calibration. (classification only) Training workflow
intervals IntervalsConfig Regression prediction intervals. (regression only) Prediction intervals
deployment DeploymentConfig Serving artifact delivery. Deployment
Details: two sections a shared template deliberately cannot carry

Most sections describe the recipe — what the run learns — and are portable, which is what makes a template worth sharing. Two are not, for opposite reasons, and an embedding app that offers a template editor should keep both off it:

  • data is dataset-bound. It names specific columns, so it means nothing away from the dataset it was written against. It is stripped wherever it appears, including from a stored run snapshot — some other surface owns the binding, so a copy could only go stale.
  • execution is machine-bound. The same recipe is legitimately run on a laptop and on a GPU box, under one seed and then another. It must, however, survive on an individual run's snapshot: that is where execution.seed is recorded, and it is what makes the run reproducible.

The schema publishes the union as the sections missing from x-group-order, and the two halves are queryable separately — column_referencing_config_paths() and machine_scoped_config_sections(). Treating them as one set is a tempting simplification that quietly deletes every run's seed.

Details: task-specific sections and fields

The fit path consults the sections marked above for some tasks and ignores them entirely for the rest — a classification run never fits prediction intervals, and a regression run never calibrates probabilities or rebalances classes. The schema publishes this as an x-tasks hint per section (schema._SECTION_TASKS), and the template editor hides a section that would be wholly inert for the template's ml_task rather than offering knobs no run will read. Setting one anyway is harmless: it is a no-op.

Gating is per section only when the whole section is inert. target_transform is the counter-example: its y-transform tuner is regression-only, but the two feature-side switches that live alongside it — enable_x_power_transform and enable_x_cyclical_transform — are handed to every GlassBox variant (glm / scorecard / rulefit) on any task, so a binary scorecard template needs them. Those fields carry their own per-field x-tasks (schema._FIELD_TASKS), and the section itself is not task-gated.

Validation happens twice

Cross-section consistency (for example, that a feature step whose prerequisite is switched off is not left mining zero columns) is checked in Ml4Config.__post_init__ and again at fit entry via validate_cross_section. That second pass matters because the common "build a config, then mutate it" idiom bypasses the constructor — see the tip below.

Mutating a built config re-validates on fit

Assigning to nested fields after construction (or applying a preset and then tweaking it) is fully supported. ml4 re-runs validate_cross_section when you call fit, so a post-construction change can never silently ship a conflicting combination.

Column roles: DataConfig

DataConfig describes the data itself — which column is the target, which columns are which kind, and which specialised encoders should look at which columns. The engine reads it to build AutoGluon's feature metadata and the split policy keys.

Core roles

Field Purpose
target_column The label column. Also the default for fit(label=...).
cat_features Categorical columns.
num_features Numeric columns.
key1, key2 Identification keys — never used as features.
base_time_columns Anchor datetimes for "days-since" recency features.
token_delimiters Per-column delimiter for the tokenised-text encoders.

Free text has no bucket of its own

There is no text_features field: a column becomes text by being assigned a text-family domain below (text_domain, embedding_domain, mt_domain, wt_domain, dirty_domain), which is what routes it to an encoder and keeps it out of the num/cat pool.

Group / time columns live on the split policy

The fold geometry is engine.split_policy (time_column / group_column) — see Data splitting. DataConfig has no fold, time or group column field of its own.

Domain-column lists (stateless FE routing)

Beyond the three schema buckets, DataConfig carries per-encoder domain lists. Assigning a column to a domain routes it to the matching stateless encoder, which parses structure a generic encoder would miss. See Stateless encoders for the full catalogue; the routing hints are:

Domain field Encoder / feature output
names_domains First-name / surname analysis.
time_domain Calendar parts + recency (paired with base_time_columns).
email_domain, url_domain, ip_domain Structured web / network parsing.
useragent_domain Browser / OS / device family from raw UA strings.
device_info_domain Structured device JSON blob.
address_domain, zip_domain, city_domain, state_domain, street_domain Address components.
phone_domain, creditcard_domain, iban_domain, vat_domain Checksum / validity / classification features.
text_domain Free-form text → TF-IDF / SVD.
embedding_domain Free-form text → neural sentence embeddings.
mt_domain, wt_domain Multi-token and weighted multi-token (packed-count) fields.
dirty_domain Noisy high-cardinality strings → Gamma-Poisson.
cfg.data.target_column = "fraud"
cfg.data.num_features = ["amount", "account_age_days"]
cfg.data.cat_features = ["merchant_category"]
cfg.data.email_domain = ["billing_email"]
cfg.data.useragent_domain = ["user_agent"]

Roles are declarative, not inferred blindly

ml4 only treats a column as a special domain when you place it in that domain list — the specialised encoders never sniff for datetime or email columns on their own. That keeps a run reproducible and leak-safe.

The eval_metric catalogue

eval_metric is an EvalMetric enum member. If you leave it None, ml4 resolves the task-appropriate default at fit time: binary → logloss, multiclass → logloss, regression → rmse. An explicit metric is validated against the task and rejected on mismatch.

The classification default is threshold-free on purpose: AutoGluon scores a label metric (f1, accuracy, …) at a fixed 0.5 / argmax cut that ml4 replaces with a tuned operating point, so selecting on one picks a model at a cut the product never serves. See the evaluation-metric catalogue.

auc, logloss, f1, beta, average_precision, accuracy

logloss, accuracy, and the averaged families f1_macro / f1_weighted, beta_macro / beta_weighted, precision_macro / precision_weighted, recall_macro / recall_weighted

There is no *_micro member: micro-averaging a single-label multiclass prediction reduces to accuracy identically, for every β — see Algorithms & metrics for the derivation. Pick accuracy when that is the quantity you want.

rmse, mse, mae, mape, r2, spearman, pearson

The beta* metrics and any user_defined_metric build a custom AutoGluon Scorer; the rest map to native AutoGluon metric strings. To supply your own metric:

cfg.eval_metric = EvalMetric.user_defined_metric
cfg.user_defined_metric_ref = "myproject.metrics:profit_score"
cfg.user_defined_metric_direction = "maximize"

The callable must accept (y_true, y_predicted, sample_weight=None) and return a scalar. See Algorithms & metrics for the full metric reference and the custom-scorer mechanics.

Presets

Rather than hand-tuning every knob, start from a preset — a one-line factory that returns a fully-formed Ml4Config with the strategy set (the AutoGluon budget, the roster, and the feature-engineering toggles). You still supply the dataset-specific data section.

from parleml_ml4.config import presets

cfg = presets.quick_explore()          # a named factory
cfg.data.target_column = "y"

# or resolve by name from the registry
cfg = presets.PRESETS["high_accuracy"]()
cfg.data.target_column = "y"

Presets flip only the levers they mean to change, so the difference between a preset and the default is exactly its intent.

The sixteen built-in presets

Roster entries are model specs, not families: a gradient booster names the variant it trains. The Fit column is the fit shape the preset resolves to: CV k is cross-validated bagging over k folds (+ stack when a stacking layer trains on top), holdout is the single deployed model validated on a reserved slice. See Algorithms & metrics for the full spec table.

Preset Fit Models trained What it is for
default CV 5 (unset) → GBM:gbdt Balanced defaults for most datasets — the ROI-optimal starting point. A one-model roster trains no stacking layer.
quick_explore holdout GBM:gbdt Fast first pass (heavy FE off) to validate data wiring in minutes.
high_accuracy CV 8 + stack GBM:gbdt GBM:dart GBM:rf CAT:symmetric CAT:lossguide XGB:gbtree The widest feature space and full variant roster; serves the bagged ensemble (engine.refit_full=False). Four-hour model-selection budget; potentially large artifacts.
diverse_ensemble CV 8 + stack GBM:gbdt CAT:symmetric XGB:gbtree RF XT LR Breadth of families over depth, at default feature cost.
tuned_single holdout GBM:gbdt XGB:gbtree CAT:symmetric Tuned single model — a seeded Optuna TPE search across the boosters, deploys the single best (DAI / H2O / mljar style).
tuned_ensemble CV 8 + stack GBM:gbdt XGB:gbtree CAT:symmetric Tuned ensemble — one search per roster model, then bagged and greedy-ensembled.
reliable_tuned_ensemble CV 8 + stack GBM:gbdt XGB:gbtree CAT:symmetric tuned_ensemble with parameter selection over five policy-aware folds at 20 trials per booster — at most 300 search fits before the final training.
tuned_variants CV 8 + stack GBM:gbdt GBM:dart GBM:rf CAT:symmetric CAT:lossguide XGB:gbtree The widest roster ml4 ships: every booster variant, each searched separately, then bagged and ensembled. The most expensive template here — six Optuna studies before AutoGluon starts.
real_time holdout GBM:gbdt XGB:gbtree Latency-optimised: a tuned Treelite-eligible booster, narrow feature pool, no bagging.
large_dataset holdout GBM:gbdt Big tables (~2M+ rows): one time-boxed LightGBM with lean FE, tuned on a short leash (15 trials, not the usual 60).
small_data CV 10 GBM:gbdt CAT:symmetric XGB:gbtree LR Under ~20k rows: variance control — ten folds and no stacking layer (stacking overfits at this size), feature selection off.
text_heavy CV 5 + stack GBM:gbdt LR Free-text-heavy data: neural sentence embeddings on, arithmetic crosses off.
foundation_ensemble CV 8 GBM:gbdt CAT:symmetric XGB:gbtree RF XT LR + foundation The diverse roster plus the zero-shot foundation challenger, all ensembled over 8 folds with no stacking layer.
regulated_glassbox holdout one glass-box model A single transparent model (GLM / EBM / RuleFit) with audit-friendly FE.
regulated_scorecard holdout one scorecard A weight-of-evidence logistic whose coefficients become points.
regulated_monotonic holdout GBM:gbdt XGB:gbtree CAT:symmetric → one winner A tuned monotone-constrained booster with auto-derived directions.

The default roster is one explicit LightGBM, not a portfolio

ml4 never delegates model selection to an AutoGluon portfolio — every fitted model must carry the feature recipe — so roster.models defaults visibly to one GBM:gbdt. A preset that wants breadth names its models itself, and every stock preset exposes its effective roster in the template editor. Regulated presets force a glass-box model, so their stock roster is ignored outright.

The three regulated_* presets deploy a single, non-bagged model. regulated_glassbox and regulated_scorecard use transparent estimators and disable AutoGluon's fit time limit. regulated_monotonic searches constrained boosters under a one-hour model-selection budget; it is explained through its monotone contract and SHAP. See Constraints & regulated models.

Preset time budgets

engine.time_limit is a nominal search/model-selection budget, not a hard deadline for the whole experiment. Preparation, native model completion, refit_full, post-fit evaluation and explanations can add time.

Preset Model-selection budget
default 30 minutes
quick_explore 10 minutes
high_accuracy 4 hours
diverse_ensemble, tuned_single, regulated_monotonic, real_time, large_dataset, text_heavy, foundation_ensemble 1 hour
tuned_ensemble, reliable_tuned_ensemble 2 hours
tuned_variants 3 hours
small_data 15 minutes
regulated_glassbox, regulated_scorecard No AutoGluon fit time limit

A bare Ml4Config() has engine.time_limit=None; it differs from presets.default(), which sets 30 minutes. Override a preset's limit when the dataset or host needs more time. With native ml4 Optuna search, the search gets half the budget and its actual elapsed time is deducted from AutoGluon's fit allowance, subject to a minimum fit allowance. See Hyperparameter search.

Details: no AutoGluon preset is named in the fit

AutoGluon's presets= ladder varies its model portfolio — which ml4 always replaces with recipe-carrying models — and a handful of fit levers that ml4 sets explicitly from the fit shape: folds, stacking, the stacking probe, refit, the time limit. So ml4 names no AutoGluon preset at all. engine has no preset field; a stored config that still carries one is refused at load with a message naming the field — delete the key. When you want a quality level, set the knob that carries it: roster.models for breadth, engine.num_bag_folds / engine.num_stack_levels for the ensemble, and engine.refit_full=False to serve the bagged ensemble instead of a refit single model.

Details: model type is part of the config, not a separate launch choice

Whether a run trains a bagged CV ensemble (best accuracy) or a single deployed model + reserved holdout (simplest to explain / serve / regulate) is a property of the config itself — engine.training_policy plus roster.regulated. A single-model preset sets the policy (see presets._single_model), so it stays single wherever it is used, while a bagged preset pins its fold count and stack depth explicitly. Everything else about the fit — whether it bags, stacks, builds the weighted ensemble, keeps only the best model — is derived from those knobs by parleml_ml4.config.resolve_fit_shape, never stored. The validation strategy (how rows are assigned to folds / holdout — SplitPolicy) is the data-driven axis covered in Data splitting & validation, and three pairings with the policy are refused, each with a message naming the fields: a temporal split needs chronological_train_validation, chronological_train_validation needs a temporal split, and a regulated roster cannot use cross_validation.

Composers

Two helpers transform an existing config rather than build one from scratch:

Composer Effect
with_foundation_challenger(cfg, model=None) Requires the catalogued foundation challenger (TabICL by default). A missing package or a challenger that fails to train stops the run. On a regulated glass-box roster the flag is inert and warns.
audit_friendly_fe(cfg) Disables opaque feature engineering (interactions, clustering, GP text) so a regulated model traces cleanly back to its inputs.
from parleml_ml4.config import presets

cfg = presets.with_foundation_challenger(presets.diverse_ensemble())
cfg = presets.with_foundation_challenger(Ml4Config(), model="tabicl")

Presets are just configs

A preset returns an ordinary Ml4Config, so you can apply one and then override any field. The fit-time re-validation keeps your tweaks coherent.

Where to go next