> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Online Learner

> Online gradient descent on numeric features. Updates weights on every outcome. Cheap inference, lower ceiling than batch-trained models, no retrain step.

`modelType: "online_learner"` — a perceptron / online logistic regression that updates its weight vector on every observed outcome. No batch retrain phase; the model is always current as of the most recent reward.

## When to use

* **You can't afford a retrain pipeline** — limited engineering capacity, fast-changing data.
* **You want a model that adapts to drift** — seasonal patterns, campaign changes, news-driven shifts in customer behavior.
* **Numeric features only** — the engine version handles numeric inputs natively (categoricals need preprocessing).

**Skip it when** you need calibrated probabilities (online updates can leave the model uncalibrated between adjustments) or when features are highly non-linear (use `gradient_boosted`).

## The math

```
# Inference (same as logistic_regression):
z      = bias + Σ_i (weights[xᵢ] × xᵢ)
score  = sigmoid(z)

# After observing outcome:
error  = (observed_reward - score)
η_t    = learningRate / (1 + decayRate × step)   # decayed step size
weights[xᵢ] += η_t × error × xᵢ
bias        += η_t × error
step        += 1
```

The base learning rate `learningRate` controls how aggressively each new outcome moves the weights. The engine **decays** it as `η_t = learningRate / (1 + decayRate × step)` (default `decayRate = 0.001`), so updates shrink as the model accumulates outcomes. Common base values: `0.01` (slow, stable) to `0.1` (fast, noisy).

## Fixture config

```json theme={null}
{
  "modelType": "online_learner",
  "modelState": {
    "weights": { "credit_score": 0.004, "income": 0.000015, "age": 0.005 },
    "bias": -3.2,
    "learningRate": 0.01,
    "decayRate": 0.001,
    "step": 0
  }
}
```

The proof script verifies this scores `0.8108` for the standard test customer — close to logistic\_regression's `0.93` (different weights, same shape). The slightly lower score reflects the more conservative weights you'd expect from an online learner that hasn't seen as many outcomes as a batch logistic regression.

## Training

Updates happen via `POST /api/v1/respond` — `auto-learn.ts` applies the gradient update for each incoming outcome. No separate train endpoint.

To bootstrap: seed `weights = {}` and `bias = 0` (cold start, every candidate scores 0.5). Or seed with weights from a batch-trained logistic\_regression model — the online learner can then refine them as outcomes arrive.

## Score interpretation

Same as logistic\_regression — calibrated probability in `[0, 1]`, conditional on the weight vector being stable. During rapid drift, the score is more "current estimate" than "calibrated probability".

## Pitfalls

* **High learning rate → instability** — at `η = 0.5`, a single bad outcome can flip the sign of a weight. Stick to `0.01–0.1`.
* **Feature scaling** — same issue as logistic\_regression. Standardize features before training.
* **No regularization** — the engine's online learner has no L2 by default; weights can drift unboundedly on rare features. If you see exploding magnitudes, add a decay step in `auto-learn`.
* **Lost-update on concurrent writes** — two parallel `respond` calls can race on the same weight vector. The engine serializes writes to ModelAdaptation per (modelId, scopeId); if you customize the update path, preserve that contract.
* **No held-out evaluation** — there's no train/test split; you're updating against the same stream that's being scored. Watch for self-confirming loops (the model believes "premium customers respond" → only shows offers to premium → only learns from premium → over-confident on premium). Mix in [exploration via `shadowModelKeys`](/decisioning/scoring-strategies) if you suspect this.

## Lifecycle & cadence

Online learner is **always continuous** — every `/respond` performs one SGD step on the weight vector against the current outcome's features. `lastLearnedAt` advances on every respond; `trainingSamples` increments by 1. The `autoLearn` / `learnMode` / `learnSchedule` toggles are **ignored** for incremental updates.

You CAN additionally schedule periodic full re-grounding via `learnMode: "scheduled"`, which runs a full SGD pass over the last N interactions to dampen drift from any single bad batch — useful when you suspect a feedback-loop has poisoned the weights. The Model Health dashboard's "Online Learner Weights" panel renders top-K features by absolute weight (green = positive, red = negative) so you can sanity-check the learned signal. See [Learning cadence](/ai-ml/learning-cadence) and [Model lifecycle](/ai-ml/model-lifecycle).

## Cross-reference

* [Algorithm Selection Guide](/decisioning/algorithm-selection-guide).
* [Logistic Regression](/ai-ml/algorithms/logistic-regression) — batch-trained equivalent.
