How to use from the
Use from the
Transformers library
# Use a pipeline as a high-level helper
from transformers import pipeline

pipe = pipeline("text-classification", model="MergenAI/SIMURG")
# Load model directly
from transformers import AutoModel
model = AutoModel.from_pretrained("MergenAI/SIMURG", device_map="auto")
Quick Links

SIMURG

SIMURG is a streaming hallucination and output-degradation detector for LLMs. It watches a live model response as tokens stream in and raises a calibrated alarm the moment the output degenerates: repetition loops, cross-lingual drift, regurgitation, and structural collapse β€” the failure modes that ship hallucinated or garbage text to users. This checkpoint is the learned deep tier of the SIMURG ensemble; it runs in single-digit milliseconds on Apple Silicon and never blocks the stream it guards.

Can you train SIMURG on your own hallucination types?

Yes. The detector learns from any (clean, corrupted) pairs you can produce. If your workload has a characteristic failure mode β€” fabricated citations, number drift, prompt echo, domain-specific garbage β€” collect or synthesize examples of it, point the trainer at your endpoint, and the ensemble picks up the new checkpoint automatically. See the Fine-Tuning Tutorial below.

Architecture

Component Specification
Backbone 2x TransformerEncoder (norm-first, dropout 0.05)
Hidden dimension 64
Attention heads 4
Feedforward 256 (4x hidden), ReLU
Vocabulary 4096 trigram-hash buckets (index 0 reserved for left padding)
Sequence length 256 trigrams (~600-character tail window)
Positional encoding learnable parameters
Pooling masked mean over non-padding positions
Head LayerNorm -> Linear(64, 1), single logit
Parameters 345,665
Checkpoint size ~1.3 MB (safetensors)

Formulation

A checkpoint observes the stream prefix $x_1\dots x_t$ and reads only the recent window $w_t = x_{\max(1,,t-600)}\dots x_t$. The window is tokenized into character trigrams and mapped deterministically into a 4096-bucket vocabulary:

Ο„i=blake2b(xixi+1xi+2)β€Šβ€Š(Vβˆ’1)+1,V=4096 \tau_i = \mathrm{blake2b}(x_i x_{i+1} x_{i+2}) \bmod (V - 1) + 1, \qquad V = 4096

After embedding $E \in \mathbb{R}^{V \times 64}$ and a learnable positional bias $P$, two norm-first transformer layers with masked self-attention produce the representation

H=Transformer(E[Ο„]+P),h=βˆ‘imi Hiβˆ‘imi, H = \mathrm{Transformer}\big(E[\tau] + P\big), \qquad h = \frac{\sum_i m_i\, H_i}{\sum_i m_i},

where $m_i \in {0,1}$ masks padding positions. The head outputs a logit

z=W LN(h)+b, z = W\,\mathrm{LN}(h) + b,

and the sigmoid score is linearly calibrated to a probability using two anchors stored in the checkpoint metadata:

p^corrupt(wt)=clip ⁣(Οƒ(z)βˆ’β„“ohiβˆ’β„“o,β€…β€Š0,β€…β€Š1),β„“o=Q0.95[clean scores],β€…β€Šhi=median[corrupt scores]. \hat{p}_\mathrm{corrupt}(w_t) = \mathrm{clip}\!\left( \frac{\sigma(z) - \ell_o}{h_i - \ell_o},\; 0,\; 1\right), \qquad \ell_o = Q_{0.95}\big[\text{clean scores}\big],\; h_i = \mathrm{median}\big[\text{corrupt scores}\big].

Calibration anchors for this checkpoint: $\ell_o = 0.168$, $h_i = 0.367$.

Training objective

Training minimizes class-weighted binary cross-entropy over onset-aware windows:

L=βˆ’1Nβˆ‘n=1N[yn α log⁑σ(zn)+(1βˆ’yn) log⁑(1βˆ’Οƒ(zn))],Ξ±=Nβˆ’N+ \mathcal{L} = -\frac{1}{N}\sum_{n=1}^{N} \Big[ y_n \, \alpha \, \log \sigma(z_n) + (1-y_n)\, \log\big(1 - \sigma(z_n)\big) \Big], \qquad \alpha = \frac{N_-}{N_+}

with $N_-$ and $N_+$ the counts of clean and corrupt windows. A window is labeled corrupt only when its right edge lies at least $\Delta = 300$ characters past the true corruption onset; windows before the onset are labeled clean. Optimizer: AdamW ($\eta = 5\times10^{-4}$, weight decay $10^{-4}$), batch 64, 8 epochs; the best validation checkpoint is kept.

Training Data

Split Source Count
Clean (train) live answers from the guarded endpoint (wahoo-1.5-preview via vLLM) plus a bundled clean corpus 40 live + pooled
Corrupt (train) CorruptBench synthetic corruptions: repetition loops, cross-lingual drift, table echo, structural garbage 240 streams
Validation held-out clean streams + 40 held-out corruptions onset-aware windows

Results

ROC curve

Figure: ROC re-evaluated on a held-out split at release time (292 onset-aware windows, 9 held-out live clean documents). The headline training-time held-out AUROC reported by the trainer is 0.925; per-split variation is expected with a small clean set.

Metric Value
Held-out AUROC 0.925
Calibration anchors $\ell_o = 0.168$, $h_i = 0.367$
Inference latency ~4 ms per window on MPS
CPU fallback supported

Score distribution

Per-class response

Input Pulse probability
Clean prose 0.000
Repetition loop 1.000
Cross-lingual drift 1.000
Structural table echo 1.000

Training curves

Production Readiness

  • Deterministic: tokenization uses a stable blake2b hash, so training and live inference over the same text produce identical tensors.
  • Graceful degradation: the detector is optional. Without torch, safetensors, or the weights file, the ensemble behaves exactly as the numpy-only core.
  • Lightweight: 1.3 MB checkpoint, single-digit millisecond inference, far below the token rate of any guarded model.

Usage Inside SIMURG

The weights ship with the SIMURG package and auto-register as the sixth detector in the ensemble. Install with:

pip install "simurg[deep]"

To point SIMURG at a custom or retrained checkpoint:

export SIMURG_PULSE_WEIGHTS=/path/to/simurg_pulse.safetensors

Standalone Inference

from safetensors.torch import load_file

tensors = load_file("simurg_pulse.safetensors")

The full inference plumbing (tokenization, windowing, calibration) lives in SIMURG under src/simurg/deep/pulse.py; this repository ships the raw checkpoint and the training recipe.

Fine-Tuning Tutorial β€” train SIMURG on your own hallucinations

Retrain the checkpoint against any OpenAI-compatible endpoint in one command. The trainer samples live answers (clean labels), synthesizes corruptions (label 1 with known onset), trains on CPU in seconds, computes held-out AUROC and calibration anchors, and writes a new safetensors file.

Step 1: Install dependencies

pip install "simurg[deep]"

Step 2: Configure the target endpoint

export SIMURG_LIVE_URL=http://your-host:port/v1/chat/completions
export SIMURG_LIVE_MODEL=your-model-name

For custom prompts, prepare a JSONL file where each line is {"text": "your prompt"} and set:

export SIMURG_LIVE_PROMPTS_JSONL=prompts.jsonl

Step 3: Run the trainer

python -m simurg.deep.train_pulse --clean 40 --corrupt 240 --epochs 8 --out ./simurg_pulse.safetensors

Useful flags:

Flag Default Meaning
--clean 40 number of live clean answers to sample
--corrupt 240 number of synthetic corruption streams
--epochs 8 training epochs
--batch 64 batch size
--seed 7 RNG seed (reproducible runs)
--device cpu cpu, mps, or cuda
--out bundled path output safetensors path

Step 4: Verify the new checkpoint

The trainer prints held-out AUROC and calibration anchors before saving. A strong fine-tune typically reaches held-out AUROC above 0.90. If AUROC drops below ~0.75, increase --clean or add more diverse prompts to the JSONL.

Step 5: Ship it

Replace the bundled file (src/simurg/weights/simurg_pulse.safetensors) or keep the new file external and export SIMURG_PULSE_WEIGHTS in production. The ensemble picks it up automatically on the next process start.

Links

License

Apache-2.0, matching the parent SIMURG project.

Attribution

Trained as part of SIMURG v1.0.4 (2026-09-21) by MergenAI.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Evaluation results

  • held-out AUROC on CorruptBench + live wahoo-1.5-preview
    self-reported
    0.925