Skip to content

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
fm.create_binary_classifier(target_column="churned")
# intent="balanced" is the default

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
fm.create_binary_classifier(
    target_column="is_fraud",
    intent="catch_everything_aggressive",
)

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 dialsrecall_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
predictor = fm.create_binary_classifier(target_column="will_pay", intent="predict_probabilities")

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.

fm.create_multi_classifier(
    target_column="next_action",
    intent="top_k_hit",
    k=3,
)

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
fm.create_regressor(
    target_column="delivery_minutes",
    intent="minimize_typical_error",
)

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).

fm.create_regressor(
    target_column="forecast_revenue",
    intent="minimize_relative_error",
)

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 — balanced will give you the same recall with better precision. Pick catch_everything only when positives are ≤ 30% of the dataset.

  • intent="catch_everything_aggressive" with < 20 validation positives. Recall is granular at 1/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. Use catch_everything (0.85) or catch_everything_aggressive with recall_floor=0.85 for stability.

  • Mixing intent="balanced" (or any non-cost intent) with cost_false_* kwargs. Raises. Pick one path: name an intent OR pass costs (which auto-picks minimize_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 pick minimize_typical_error.

  • intent="rank" followed by calling predict(). Raises by design. Use predict_proba() and threshold yourself.

  • Passing precision_floor= (the old kwarg name) to catch_everything. The SDK accepts it as a legacy alias and silently coerces to recall_floor=, but the semantics flipped: today the floor binds on recall, not precision. New code should use recall_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 resolved TrainingComponents (loss, threshold, calibration, report_also).
  • training_optimization.checkpoint_metric — the metric the BestCheckpointTracker actually 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.