Intents — pick the right operating point for your model¶
intent= is the single highest-leverage knob on a Featrix predictor. One word
on the SDK call decides the loss function, threshold policy, and
best-epoch metric the trainer optimizes against — together. Default
behavior changes with this knob; do not omit it.
This page is the reference. For narrative, see the blog posts under "Why Featrix". For SDK signatures, see the API Reference.
TL;DR — pick by question¶
binary classifier?
├── "I want a calibrated probability" .................... predict_probabilities
├── "Rank items, no threshold" ........................... rank
├── "Catch ≥85% of positives, accept FPs" ................ catch_everything ← default for skewed data
├── "Catch ≥95% — missing one is catastrophic" ........... catch_everything_aggressive ← heavy skew, fraud, safety
├── "Don't fire unless precision ≥ 0.7" .................. only_alert_when_confident
├── "FN costs $5000, FP costs $100" ...................... minimize_cost (+ cost_false_positive=, cost_false_negative=)
└── "Just give me the standard tradeoff" ................. balanced
multi-class classifier?
├── "Just predict the right class" ....................... balanced (macro-F1)
├── "Optimize raw accuracy, ignore class imbalance" ...... accuracy
├── "Top-3 hit is enough" ................................ top_k_hit (+ k=3)
└── "Rank classes for me, no argmax" ..................... rank_classes
regressor?
├── "Standard tradeoff" .................................. balanced (MSE / R²)
├── "Outliers exist, optimize for typical case" .......... minimize_typical_error (Huber / MAE)
├── "Big misses are catastrophic, no tolerance" .......... minimize_worst_case (tail-weighted / RMSE)
└── "Off by 10% > off by $10" ............................ minimize_relative_error (log-MSE / sMAPE; targets ≥ 0)
Binary intents¶
Every binary intent listed below resolves to one specific
(loss, threshold_policy, best_epoch_metric, calibration) quadruple. None
of these are user-tunable except where recall_floor=, precision_target=,
or cost_* is documented as overrides.
balanced — the default¶
Use when: you want today's "no surprises" behavior.
| Knob | Value |
|---|---|
| Loss | Class-weighted CE (symmetric) |
| Threshold | max_f1 (sweep the F1-optimal cut) |
| Best-epoch metric | auc (ROC-AUC) |
| Calibration | Temperature scaling |
catch_everything — recall-first, default tier (recall ≥ 85%)¶
Use when: missing positives is more expensive than false alarms (fraud, safety, churn detection on a moderately imbalanced dataset). Sweep finds the lowest threshold at which recall ≥ 0.85, then maximizes precision.
| Knob | Value |
|---|---|
| Loss | Cost-sensitive focal, positives upweighted |
| Threshold | max_precision_s.t._recall_ge_0.85 |
| Best-epoch metric | pr_auc (PR-AUC; F2 in a future release) |
| Calibration | Temperature scaling |
fm.create_binary_classifier(
target_column="is_fraud",
rare_label_value="fraud",
intent="catch_everything",
)
Override the floor:
fm.create_binary_classifier(
target_column="is_fraud",
intent="catch_everything",
recall_floor=0.92, # catch ≥92% instead of the 85% default
)
recall_floor valid range: [0.05, 0.99].
catch_everything_aggressive — recall-first, heavy tier (recall ≥ 95%)¶
Use when: the positive class is extremely rare and missing one is
unacceptable (small-cohort fraud, regulatory triage, life-safety). Same loss
shape and threshold formulation as catch_everything, just a tighter
recall floor (0.95 instead of 0.85).
| Knob | Value |
|---|---|
| Loss | Cost-sensitive focal, positives upweighted (same as catch_everything) |
| Threshold | max_precision_s.t._recall_ge_0.95 |
| Best-epoch metric | pr_auc |
| Calibration | Temperature scaling |
When NOT to use: if you don't have many positives per validation epoch
(rule of thumb: < 20 positives in val), the threshold sweep at 0.95 will be
noisy. Prefer catch_everything (0.85) and rely on selective-prediction
demur for the high-confidence subset.
only_alert_when_confident — precision-first¶
Use when: every alert costs human attention and you'd rather miss some positives than swamp the queue with false alarms. Sweep finds the highest threshold that still keeps recall above the floor, then the autotuner pushes the loss toward the precision target.
| Knob | Value |
|---|---|
| Loss | Cost-sensitive focal, negatives upweighted |
| Threshold | max_precision_s.t._recall_ge_0.5 |
| Autotuner objective | precision ≥ 0.7 (configurable) |
| Best-epoch metric | pr_auc |
| Calibration | Temperature scaling |
fm.create_binary_classifier(
target_column="should_review",
intent="only_alert_when_confident",
recall_floor=0.4, # accept recall as low as 40%
precision_target=0.85, # but push the model toward 85% precision
)
precision_target valid range: [0.05, 0.99]. Default 0.7. recall_floor
and precision_target are independent dials — recall_floor is the
threshold-sweep constraint; precision_target is the autotuner's objective.
minimize_cost — explicit cost optimization¶
Use when: you have a real dollar cost for a false positive and a false negative. The Bayes-optimal threshold falls out of the cost ratio.
| Knob | Value |
|---|---|
| Loss | Cost-weighted CE |
| Threshold | bayes_optimal[c_fp, c_fn] |
| Best-epoch metric | composite_score |
fm.create_binary_classifier(
target_column="is_fraud",
intent="minimize_cost",
cost_false_positive=100, # $100 per false alarm
cost_false_negative=5000, # $5000 per missed fraud
)
Both costs are required when intent="minimize_cost". Passing them without
this intent raises (intents are coherent — pick one, don't blend).
rank — AUC only, no threshold¶
Use when: you'll consume the predicted scores for ranking and apply your own threshold downstream. The model trains for ROC-AUC and never selects a threshold.
| Knob | Value |
|---|---|
| Loss | PR-AUC loss with class-weighted alpha |
| Threshold | None — predict() raises; use predict_proba() |
| Best-epoch metric | auc |
| Calibration | None |
predictor = fm.create_binary_classifier(target_column="lead_quality", intent="rank")
scores = predictor.predict_proba({...}) # predict() would raise
predict_probabilities — calibrated probability output¶
Use when: the consumer of your model expects calibrated P(y=1|x)
probabilities (e.g., feeding into a downstream Bayesian step). Threshold
fixed at 0.5; isotonic calibration applied.
| Knob | Value |
|---|---|
| Loss | Plain CE (no focal) |
| Threshold | fixed_0.5 |
| Best-epoch metric | val_loss |
| Calibration | Isotonic regression |
Multi-class intents¶
balanced — macro-F1 default¶
Use when: classes have unequal support and you want each class weighted the same. Today's "no surprises" multi-class default.
| Knob | Value |
|---|---|
| Loss | Multi-class focal |
| Threshold | argmax |
| Best-epoch metric | macro_f1 |
accuracy — raw accuracy¶
Use when: the dataset's class distribution matches production and you want plain top-1 accuracy as the headline.
| Knob | Value |
|---|---|
| Loss | CE |
| Best-epoch metric | accuracy |
top_k_hit — top-K accuracy¶
Use when: the user picks from a short list (search, recommendation), and a hit anywhere in the top-K counts as success.
k is required. Best-epoch metric = top_k_accuracy.
rank_classes — per-class scores, no argmax¶
Use when: you want the predicted distribution over classes and will
consume it raw (e.g., entropy gating, downstream ensembling).
predict() raises; use predict_proba(). Best-epoch metric = auc
(macro one-vs-rest).
Regression intents¶
Each regression intent pins a primary metric AND a coherent loss family. The resolver wires them together; the customer just picks the intent.
balanced — MSE / R² (default)¶
Use when: you want today's regression default (MSE training, R² for checkpoint selection).
| Knob | Value |
|---|---|
| Loss | MSE |
| Best-epoch metric | r2 |
| Reports | r2, rmse, mae |
minimize_typical_error — Huber / MAE¶
Use when: outliers exist in your training data but you care about how well the model predicts the typical case. Huber loss is linear past 0.5σ error so outliers can't dominate the gradient.
| Knob | Value |
|---|---|
| Loss | Huber |
| Best-epoch metric | mae |
| Reports | mae, rmse, r2 |
minimize_worst_case — tail-weighted MSE / RMSE¶
Use when: big misses are catastrophic (safety bounds, capacity planning). Pure MSE everywhere with extra weight on the tails so the network spans the full range; checkpoint selection on RMSE which is quadratically sensitive to outliers.
| Knob | Value |
|---|---|
| Loss | Tail-weighted MSE |
| Best-epoch metric | rmse |
| Reports | rmse, max_error, r2 |
minimize_relative_error — log-MSE / sMAPE¶
Use when: the cost of a prediction error scales with the target's magnitude — being off by 10% on a $100,000 forecast is the same severity as being off by 10% on $1,000. Loss operates in log-target space; gradient is in relative-error units.
| Knob | Value |
|---|---|
| Loss | Log-target MSE |
| Best-epoch metric | smape |
| Reports | smape, mae, r2 |
Constraint: target values must be non-negative. Signed-target datasets cannot use this intent (the log transform isn't defined).
Anti-patterns¶
-
intent="catch_everything"on roughly balanced data (positive rate ≥ 40%). The recall floor of 0.85 doesn't add value when half the rows are positive —balancedwill give you the same recall with better precision. Pickcatch_everythingonly when positives are ≤ 30% of the dataset. -
intent="catch_everything_aggressive"with < 20 validation positives. Recall is granular at1/n_pos, so with 15 positives every threshold step changes recall by 6.7 points — the sweep is noisy and the chosen operating point will move epoch-to-epoch. Usecatch_everything(0.85) orcatch_everything_aggressivewithrecall_floor=0.85for stability. -
Mixing
intent="balanced"(or any non-cost intent) withcost_false_*kwargs. Raises. Pick one path: name an intent OR pass costs (which auto-picksminimize_cost). The SDK refuses to silently merge them. -
intent="minimize_relative_error"with negative or zero target values. The log transform isn't defined. Either shift the target to be strictly positive or pickminimize_typical_error. -
intent="rank"followed by callingpredict(). Raises by design. Usepredict_proba()and threshold yourself. -
Passing
precision_floor=(the old kwarg name) tocatch_everything. The SDK accepts it as a legacy alias and silently coerces torecall_floor=, but the semantics flipped: today the floor binds on recall, not precision. New code should userecall_floor=directly.
Defaults: what happens when intent= is not passed¶
| Task | Default intent | Default best-epoch metric |
|---|---|---|
| Binary classifier (no costs) | balanced |
auc |
Binary classifier (with cost_*) |
minimize_cost |
composite_score |
| Multi-class classifier | balanced |
macro_f1 |
| Regressor | balanced |
r2 |
These defaults preserve the behavior callers had before intents shipped.
Customers who already ship to production without intent= see no
behavior change.
How intents flow through training¶
SDK call ─→ from_*_kwargs() builds UserIntent(task, objective, params)
│
▼
resolve(intent) # intent_resolver.py
│
▼
TrainingComponents (loss_spec, threshold_policy, best_epoch_metric, ...)
│
├─→ BinaryClassifierLoss / MulticlassFocalLoss / RegressionLoss
├─→ Threshold sweep (threshold_policies.py)
├─→ BestCheckpointTracker (which epoch we save)
├─→ SPLossAutotuner (for catch_everything*, only_alert)
└─→ model_card.json under "user_intent" + "training_optimization"
The resolved intent is persisted in the model card:
model_identification.user_intent—{task, objective, params, source}plus the resolvedTrainingComponents(loss,threshold,calibration,report_also).training_optimization.checkpoint_metric— the metric theBestCheckpointTrackeractually used to pick the saved epoch (pr_auc,auc,composite_score, etc.).
Together they let a customer audit exactly which operating point a deployed model was trained for, without having to re-resolve the intent.
Programmatic introspection¶
from featrixsphere.api.foundational_model import _BINARY_INTENT_VOCAB
from featrixsphere.api.foundational_model import _MULTICLASS_INTENT_VOCAB
from featrixsphere.api.foundational_model import _REGRESSION_INTENT_VOCAB
print(sorted(_BINARY_INTENT_VOCAB))
# ['balanced', 'catch_everything', 'catch_everything_aggressive',
# 'minimize_cost', 'only_alert_when_confident', 'predict_probabilities', 'rank']
For agents: these vocab sets are the source of truth. If your generation includes an intent not in the vocab, the SDK rejects at the call site — the resolver never sees it.
Related¶
- Cheatsheet — one-page intent picker
- API Reference — full SDK signatures
- Model Card Spec — where the resolved intent gets persisted