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

# Bayesian (Naive Bayes)

> Laplace-smoothed Naive Bayes. Posterior probability of positive response given customer features. Good with small samples, transparent contributions per feature.

`modelType: "bayesian"` — a Naive Bayes classifier with Laplace smoothing. Returns a calibrated posterior probability that a customer responds positively to an offer, given a set of binned predictors. The model is small, fast to train, and produces per-feature log-likelihood contributions that explain each score.

## When to use

* **You have 1k–10k labeled outcomes** — enough to estimate per-feature likelihoods, not enough to justify gradient\_boosted.
* **You need a calibrated probability** — the score IS a probability, not a relative ranking. Useful when downstream consumers (e.g. budget pacers) need to multiply by expected value.
* **Some predictors are categorical with moderate cardinality** — Naive Bayes handles `segment`, `tier`, `state` natively; logistic\_regression needs one-hot encoding.

**Skip it when** features are heavily correlated — the "naive" independence assumption hurts quality. Use logistic\_regression or gradient\_boosted instead.

## The math

```
P(positive | x₁, x₂, ..., xₙ)  ∝  P(positive) × Π_i P(xᵢ | positive)
P(negative | x₁, x₂, ..., xₙ)  ∝  P(negative) × Π_i P(xᵢ | negative)

# Laplace smoothing (α = 1 default):
P(xᵢ = v | class) = (count(xᵢ = v, class) + α) / (count(class) + α × |unique values of xᵢ|)

# Final score (probability of positive):
score = expPos / (expPos + expNeg)
```

The engine works in log space to avoid underflow:

```
logOdds_positive = log(P(positive)) + Σ_i log(P(xᵢ | positive))
logOdds_negative = log(P(negative)) + Σ_i log(P(xᵢ | negative))
score = sigmoid(logOdds_positive - logOdds_negative)
```

Numeric predictors are binned automatically — `binEdges[field]` defines bucket boundaries. Categorical predictors are used directly.

## Fixture config

```json theme={null}
{
  "modelType": "bayesian",
  "config": { "laplaceSmoothingAlpha": 1 },
  "predictors": [
    { "field": "credit_score", "selected": true, "importance": 0.40 },
    { "field": "income",       "selected": true, "importance": 0.25 },
    { "field": "age",          "selected": true, "importance": 0.10 },
    { "field": "segment",      "selected": true, "importance": 0.25 }
  ],
  "modelState": {
    "priors": { "positive": 200, "negative": 800 },
    "likelihoods": {
      "credit_score": {
        "positive": { "bin_0": 5,  "bin_1": 25,  "bin_2": 80,  "bin_3": 90 },
        "negative": { "bin_0": 200,"bin_1": 350, "bin_2": 200, "bin_3": 50 }
      },
      "income": {
        "positive": { "bin_0": 10, "bin_1": 30,  "bin_2": 70,  "bin_3": 90 },
        "negative": { "bin_0": 250,"bin_1": 300, "bin_2": 180, "bin_3": 70 }
      },
      "segment": {
        "positive": { "Bronze": 10, "Silver": 40, "Gold": 80,  "Platinum": 70 },
        "negative": { "Bronze": 200,"Silver": 300,"Gold": 200, "Platinum": 100 }
      }
    },
    "binEdges": {
      "credit_score": [600, 700, 750],
      "income":       [40000, 70000, 100000]
    }
  }
}
```

The algorithm-coverage proof script verifies this fixture produces `score = 0.812` for the standard test customer (`credit_score=760, income=95000, age=38, segment=Gold`), with `credit_score` contributing the most positive log-likelihood and `segment + income` reinforcing.

## Training

The engine's `train.ts` increments `priors[outcome]` and `likelihoods[field][outcome][bin]` for every observed interaction. No SGD, no learning rate — counts go up, smoothed estimates fall out for free.

To retrain: hit `POST /api/v1/algorithm-models/{id}/train` after a batch of new interactions. Or set the model's auto-learn cron to incrementally update from each new `respond` call.

## Score interpretation

* `score` ∈ `[0, 1]` — posterior probability of positive response.
* `confidence` ∈ `[0, 1]` — heuristic based on `sqrt(sample_count / 1000)`. A confidence of 1.0 means we've seen 1k+ outcomes for this model.
* `explanations[]` — sorted by absolute contribution. Each entry is `{field, contribution: logLik(pos) - logLik(neg)}`. Positive contributions push toward responding; negative ones away.

## Pitfalls

* **Strong predictor correlation** — Naive Bayes double-counts correlated features (`credit_score` and `income` both signal affluence). Result: overconfident scores in the extremes. If you see scores clustering at 0.01 or 0.99, this is usually why.
* **Sparse bins** — if a bin has 0 positive outcomes but `α=1`, smoothing pulls it to a tiny but non-zero rate. Acceptable as long as `α` is small relative to total samples.
* **Imbalanced priors** — if conversion rate is 1% and priors aren't reset before retrain, the prior overwhelms the per-feature likelihoods. Bayesian needs the priors to track actual class balance.
* **Categorical drift** — if a new `segment` value appears that wasn't in training data, it gets the smoothing-only count and basically falls back to the prior. Retrain when categories change.

## Lifecycle & cadence

Bayesian is the **hybrid** algorithm — it supports both incremental updates on every `/respond` (when priors are configured) AND scheduled offline retrains that recompute the full posterior over accumulated data.

* **Incremental path:** if `modelState.priors` is set, the respond handler updates posteriors on every outcome regardless of `autoLearn`. `lastLearnedAt` advances every respond; `trainingSamples` increments by 1 per call.
* **Scheduled path:** `PUT { "autoLearn": true, "learnMode": "scheduled", "learnSchedule": "24h" }` triggers a nightly re-grounding against the full history — useful for resetting drift in stale predictor distributions.
* **Both:** `learnMode: "both"` is supported and is the recommended config for production Bayesian — incremental updates keep day-to-day decisions fresh, nightly retrains keep the model anchored to the full data distribution.

See [Learning cadence](/ai-ml/learning-cadence) and [Model lifecycle](/ai-ml/model-lifecycle) for the full picture.

## Cross-reference

* [Algorithm Selection Guide](/decisioning/algorithm-selection-guide) — when to pick Bayesian over alternatives.
* [SHAP](/ai-ml/shap) — Bayesian explanations are already per-feature log-likelihood contributions; no separate SHAP pass needed.
