v1.1: CPU support — pure-PyTorch lamba_cpu.py (decode-cache, no Triton) + lamba_rag.py --device cpu + README/notebook
Browse files- LAMBA_Inference.ipynb +14 -20
- README.md +63 -24
- lamba_cpu.py +375 -0
- lamba_rag.py +37 -19
LAMBA_Inference.ipynb
CHANGED
|
@@ -3,17 +3,7 @@
|
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
"metadata": {},
|
| 6 |
-
"source":
|
| 7 |
-
"# LAMBA V1.0 — Quickstart (Colab GPU)\n",
|
| 8 |
-
"\n",
|
| 9 |
-
"A ~177M from-scratch **Mamba-3 + GQA** hybrid for **English + Turkish**.\n",
|
| 10 |
-
"\n",
|
| 11 |
-
"> ⚠️ LAMBA is small (177M). It **hallucinates** facts on its own — use it **with retrieval (RAG)**, which this notebook does by default.\n",
|
| 12 |
-
"\n",
|
| 13 |
-
"**Runtime → Change runtime type → GPU** (free T4 tier works). Then run the cells in order.\n",
|
| 14 |
-
"\n",
|
| 15 |
-
"💛 Support bigger open LAMBA models: https://www.patreon.com/c/kdirgul/membership"
|
| 16 |
-
]
|
| 17 |
},
|
| 18 |
{
|
| 19 |
"cell_type": "code",
|
|
@@ -54,16 +44,20 @@
|
|
| 54 |
},
|
| 55 |
{
|
| 56 |
"cell_type": "markdown",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
"metadata": {},
|
| 58 |
-
"
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
"Built by **Kadir Gül**. If LAMBA is useful to you, consider supporting compute for the next version 🙏"
|
| 66 |
-
]
|
| 67 |
}
|
| 68 |
],
|
| 69 |
"metadata": {
|
|
|
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
"metadata": {},
|
| 6 |
+
"source": "# LAMBA V1.0 — Quickstart (Colab: GPU or CPU)\n\nA ~177M from-scratch **Mamba-3 + GQA** hybrid for **English + Turkish**.\n\n> ⚠️ LAMBA is small (177M). It **hallucinates** facts on its own — use it **with retrieval (RAG)**, which this notebook does by default.\n\n**GPU (fastest):** Runtime → Change runtime type → GPU (free T4 works), then run cells 1–3.\n**CPU (v1.1, no GPU):** skip the wheel — jump to the **Run on CPU** cell below.\n\n💛 Support bigger open LAMBA models: https://www.patreon.com/c/kdirgul/membership"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
},
|
| 8 |
{
|
| 9 |
"cell_type": "code",
|
|
|
|
| 44 |
},
|
| 45 |
{
|
| 46 |
"cell_type": "markdown",
|
| 47 |
+
"source": "## Run on CPU (no GPU) — v1.1\n\nNo GPU? LAMBA v1.1 ships a **pure-PyTorch** path (`lamba_cpu.py`, decode-cached, no Triton/CUDA, **no wheel needed**). Set the runtime to **CPU** and run the cell below with `--device cpu`. Expect ~6 tok/s on a Colab CPU; GPU is faster for interactive use.",
|
| 48 |
+
"metadata": {}
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"cell_type": "code",
|
| 52 |
+
"source": "# ───────────── Run on CPU (no GPU / Triton / wheel) — v1.1 ─────────────\n!pip -q install torch sentencepiece \"huggingface_hub>=0.23\" sentence-transformers\nfrom huggingface_hub import snapshot_download\nREPO = \"kdirgul/Mamba3-177M-GQA-Hybrid_LAMBA_V1.0\"\nDIR = snapshot_download(REPO) # public repo, no token needed\n!cd {DIR} && python lamba_rag.py --device cpu \\\n --tokenizer tokenizer/tokenizer.model \\\n --ckpt checkpoints/lamba_v1.pt \\\n --temperature 0 --top_k 1 \\\n --query \"Türkiye'nin başkenti neresi?\"",
|
| 53 |
"metadata": {},
|
| 54 |
+
"execution_count": null,
|
| 55 |
+
"outputs": []
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"cell_type": "markdown",
|
| 59 |
+
"metadata": {},
|
| 60 |
+
"source": "## Notes\n- **Use RAG** for any factual question. Without context, LAMBA may invent answers.\n- Works in **English and Turkish**; reasoning/CoT is strongest in English.\n- **Runs on GPU** (Triton kernel, fastest) **or CPU** (`--device cpu`, pure-PyTorch, v1.1 — no wheel).\n- Full details, eval scores, and limitations: see the **model card (README)**.\n\nBuilt by **Kadir Gül**. If LAMBA is useful to you, consider supporting compute for the next version 🙏"
|
|
|
|
|
|
|
| 61 |
}
|
| 62 |
],
|
| 63 |
"metadata": {
|
README.md
CHANGED
|
@@ -18,7 +18,7 @@ pipeline_tag: text-generation
|
|
| 18 |
library_name: mamba_ssm
|
| 19 |
---
|
| 20 |
|
| 21 |
-
# Mamba-3 + GQA Hybrid (EN + TR)
|
| 22 |
|
| 23 |
**LAMBA V1.0** is a ~177M-parameter language model **trained from scratch** on a
|
| 24 |
**Mamba-3 (SISO) + Grouped-Query Attention** hybrid architecture, for **English (primary)
|
|
@@ -30,6 +30,9 @@ instruction tuning, and retrieval-augmented tuning — with no warm-start from a
|
|
| 30 |
> mimarili, **İngilizce (birincil) ve Türkçe (ikincil)** bir dil modelidir. Hiçbir hazır
|
| 31 |
> modelden başlatılmadı; tokenizer'dan eğitime tüm hat baştan kuruldu.
|
| 32 |
|
|
|
|
|
|
|
|
|
|
| 33 |
---
|
| 34 |
|
| 35 |
## ⚠️ Read this first — what LAMBA is and isn't
|
|
@@ -56,7 +59,7 @@ foundation to grow from. Treat its free-form factual claims as unreliable.
|
|
| 56 |
| MLP | GatedMLP, inner 1500 |
|
| 57 |
| Vocab | 48,000 SentencePiece BPE (EN+TR, byte-fallback, digit-split) |
|
| 58 |
| Context | 2048 |
|
| 59 |
-
| Precision | bf16 |
|
| 60 |
|
| 61 |
The hybrid puts attention layers in the depth-middle so induction heads can form, with Mamba-3
|
| 62 |
handling the bulk linear-time sequence mixing.
|
|
@@ -93,20 +96,41 @@ why LAMBA is designed to be used **with retrieval**.
|
|
| 93 |
- **Not good for:** standalone factual Q&A, math, multi-step reasoning, anything safety-critical.
|
| 94 |
- **Bias/safety:** trained on web/instruction data; may produce incorrect, biased, or unsafe
|
| 95 |
content. Always keep a human in the loop. Do not use for medical/legal/financial decisions.
|
| 96 |
-
- **Compute:**
|
| 97 |
-
(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
-
|
| 102 |
-
# 1) install the Mamba-3 fork wheel (provided in this repo under wheels/)
|
| 103 |
-
!pip -q install einops sentencepiece "huggingface_hub>=0.23"
|
| 104 |
-
!pip -q install --no-deps ./wheels/*.whl
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
#
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
```
|
| 111 |
|
| 112 |
See `LAMBA_Inference.ipynb` for a one-click Colab notebook.
|
|
@@ -115,8 +139,9 @@ See `LAMBA_Inference.ipynb` for a one-click Colab notebook.
|
|
| 115 |
|
| 116 |
- `checkpoints/` — model weights (LAMBA V1.0 = the final SFT+RAG checkpoint)
|
| 117 |
- `tokenizer/` — 48K SentencePiece model
|
| 118 |
-
- `
|
| 119 |
-
- `
|
|
|
|
| 120 |
- `LAMBA_Inference.ipynb` — Colab quickstart
|
| 121 |
|
| 122 |
## License
|
|
@@ -133,13 +158,7 @@ goes directly to GPU hours and data:
|
|
| 133 |
|
| 134 |
- **Patreon:** https://www.patreon.com/c/kdirgul/membership
|
| 135 |
|
| 136 |
-
|
| 137 |
-
0x37129fc539059dd997c4b36129033cdafd736b73
|
| 138 |
-
|
| 139 |
-
- **ETH (ERC20) ETHEREUM NETWORK**
|
| 140 |
-
0x37129fc539059dd997c4b36129033cdafd736b73
|
| 141 |
-
|
| 142 |
-
Thank you. 🙏
|
| 143 |
|
| 144 |
## Citation
|
| 145 |
|
|
@@ -152,7 +171,27 @@ goes directly to GPU hours and data:
|
|
| 152 |
}
|
| 153 |
```
|
| 154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
## Acknowledgements
|
| 156 |
|
| 157 |
-
Architecture builds on **Mamba-3** (state-spaces) and the `mamba-og` fork.
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
library_name: mamba_ssm
|
| 19 |
---
|
| 20 |
|
| 21 |
+
# LAMBA V1.0 — Mamba-3 + GQA Hybrid (EN + TR)
|
| 22 |
|
| 23 |
**LAMBA V1.0** is a ~177M-parameter language model **trained from scratch** on a
|
| 24 |
**Mamba-3 (SISO) + Grouped-Query Attention** hybrid architecture, for **English (primary)
|
|
|
|
| 30 |
> mimarili, **İngilizce (birincil) ve Türkçe (ikincil)** bir dil modelidir. Hiçbir hazır
|
| 31 |
> modelden başlatılmadı; tokenizer'dan eğitime tüm hat baştan kuruldu.
|
| 32 |
|
| 33 |
+
> **Update (v1.1): now runs on CPU** — a pure-PyTorch port (`lamba_cpu.py`, no Triton/CUDA)
|
| 34 |
+
> with a decode cache. Run anywhere with `--device cpu`; see [How to run](#how-to-run).
|
| 35 |
+
|
| 36 |
---
|
| 37 |
|
| 38 |
## ⚠️ Read this first — what LAMBA is and isn't
|
|
|
|
| 59 |
| MLP | GatedMLP, inner 1500 |
|
| 60 |
| Vocab | 48,000 SentencePiece BPE (EN+TR, byte-fallback, digit-split) |
|
| 61 |
| Context | 2048 |
|
| 62 |
+
| Precision | bf16 (GPU) / fp32 (CPU) |
|
| 63 |
|
| 64 |
The hybrid puts attention layers in the depth-middle so induction heads can form, with Mamba-3
|
| 65 |
handling the bulk linear-time sequence mixing.
|
|
|
|
| 96 |
- **Not good for:** standalone factual Q&A, math, multi-step reasoning, anything safety-critical.
|
| 97 |
- **Bias/safety:** trained on web/instruction data; may produce incorrect, biased, or unsafe
|
| 98 |
content. Always keep a human in the loop. Do not use for medical/legal/financial decisions.
|
| 99 |
+
- **Compute:** runs on **CPU** (pure-PyTorch, `--device cpu`, no GPU/Triton needed — v1.1) or
|
| 100 |
+
**GPU** (Triton Mamba-3 kernel, fastest). On a desktop CPU the decode-cached path does
|
| 101 |
+
~6 tok/s; GPU is recommended for interactive use.
|
| 102 |
+
|
| 103 |
+
## How to run
|
| 104 |
+
|
| 105 |
+
The model is designed to be used **with retrieval (RAG)** — `lamba_rag.py` does
|
| 106 |
+
document → embed → retrieve → grounded answer.
|
| 107 |
+
|
| 108 |
+
### CPU — no GPU, no Triton (v1.1)
|
| 109 |
|
| 110 |
+
```bash
|
| 111 |
+
pip install torch sentencepiece sentence-transformers "huggingface_hub>=0.23"
|
| 112 |
+
# pure-PyTorch + decode-cache; NO wheel needed
|
| 113 |
+
python lamba_rag.py --device cpu \
|
| 114 |
+
--ckpt checkpoints/lamba_v1.pt --tokenizer tokenizer/tokenizer.model \
|
| 115 |
+
--demo --query "Türkiye'nin başkenti neresi?"
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
For raw generation without retrieval deps, `lamba_cpu.py` runs standalone:
|
| 119 |
+
```bash
|
| 120 |
+
python lamba_cpu.py --ckpt checkpoints/lamba_v1.pt \
|
| 121 |
+
--tokenizer tokenizer/tokenizer.model --query "..."
|
| 122 |
+
```
|
| 123 |
|
| 124 |
+
### GPU — fastest (Triton Mamba-3 kernel)
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
+
```bash
|
| 127 |
+
pip install einops sentencepiece sentence-transformers "huggingface_hub>=0.23"
|
| 128 |
+
pip install --no-deps ./wheels/*.whl # Mamba-3 fork wheel (this repo)
|
| 129 |
+
python lamba_rag.py --device cuda \
|
| 130 |
+
--ckpt checkpoints/lamba_v1.pt --tokenizer tokenizer/tokenizer.model \
|
| 131 |
+
--demo --query "Türkiye'nin başkenti neresi?"
|
| 132 |
+
# your own documents:
|
| 133 |
+
python lamba_rag.py --device cuda --docs ./my_docs --query "..."
|
| 134 |
```
|
| 135 |
|
| 136 |
See `LAMBA_Inference.ipynb` for a one-click Colab notebook.
|
|
|
|
| 139 |
|
| 140 |
- `checkpoints/` — model weights (LAMBA V1.0 = the final SFT+RAG checkpoint)
|
| 141 |
- `tokenizer/` — 48K SentencePiece model
|
| 142 |
+
- `lamba_rag.py` — retrieval-augmented inference (`--device cpu` or `cuda`)
|
| 143 |
+
- `lamba_cpu.py` — pure-PyTorch model + decode-cache (no Triton/CUDA; powers the CPU path)
|
| 144 |
+
- `wheels/` — prebuilt Mamba-3 fork wheels (**GPU path only** — not needed for CPU)
|
| 145 |
- `LAMBA_Inference.ipynb` — Colab quickstart
|
| 146 |
|
| 147 |
## License
|
|
|
|
| 158 |
|
| 159 |
- **Patreon:** https://www.patreon.com/c/kdirgul/membership
|
| 160 |
|
| 161 |
+
(More options, including crypto, coming later.) Thank you. 🙏
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
## Citation
|
| 164 |
|
|
|
|
| 171 |
}
|
| 172 |
```
|
| 173 |
|
| 174 |
+
## Related Mamba-3 models
|
| 175 |
+
|
| 176 |
+
LAMBA is part of a recent wave of from-scratch Mamba-3 models. For context:
|
| 177 |
+
|
| 178 |
+
| Model | Size | Notes |
|
| 179 |
+
|---|---|---|
|
| 180 |
+
| [hrsvrn/mamba3-180m-finewebedu-10B](https://huggingface.co/hrsvrn/mamba3-180m-finewebedu-10B) | ~187M | EN, norm-free Mamba-3 SISO baseline — **LAMBA's recipe reference** |
|
| 181 |
+
| [ib-ssm/mamba3-370M-10BT](https://huggingface.co/ib-ssm/mamba3-370M-10BT) | 370M | EN, Mamba-3 base |
|
| 182 |
+
| [kikyo0114/nanochat-mamba3-mimo-r2](https://huggingface.co/kikyo0114/nanochat-mamba3-mimo-r2) | 113M | EN+JP, pure-PyTorch Mamba-3 **MIMO** chat |
|
| 183 |
+
| [RtaForge/Mamba3-2.7B](https://huggingface.co/RtaForge/Mamba3-2.7B) | 2.7B | Mamba-2 → Mamba-3 migrated (alpha) |
|
| 184 |
+
| [RtaForge/Mistral-Mamba3-7B](https://huggingface.co/RtaForge/Mistral-Mamba3-7B) | ~4B | Mistral → Mamba-3 transfer (alpha) |
|
| 185 |
+
| [batteryphil/mamba3-baremetal-rlf](https://huggingface.co/batteryphil/mamba3-baremetal-rlf) | ~130M | experimental bare-metal reasoning |
|
| 186 |
+
|
| 187 |
+
**What makes LAMBA different:** to our knowledge the only **bilingual English + Turkish** Mamba-3
|
| 188 |
+
model; one of the few **Mamba-3 + attention hybrids** (most are pure SSM); and it ships
|
| 189 |
+
**instruction- and RAG-tuned**, not as a base/alpha checkpoint.
|
| 190 |
+
|
| 191 |
## Acknowledgements
|
| 192 |
|
| 193 |
+
Architecture builds on **Mamba-3** (state-spaces) and the `mamba-og` fork. The training recipe
|
| 194 |
+
and the feasibility of the ~180M / 768-d / 12-layer band were directly informed by
|
| 195 |
+
**[hrsvrn/mamba3-180m](https://huggingface.co/hrsvrn/mamba3-180m-finewebedu-10B)** (the norm-free
|
| 196 |
+
Mamba-3 project) — thank you. Tokenizer, data, training recipe, and all weights are original to
|
| 197 |
+
this project.
|
lamba_cpu.py
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
LAMBA V1.0 — saf-PyTorch CPU inference (Triton/mamba_ssm YOK). [v1.1]
|
| 3 |
+
|
| 4 |
+
Fork (mamba_ssm "norm-free" Mamba-3, Triton kernel) ile EĞİTİLEN ağırlıkları (lamba_v1.pt)
|
| 5 |
+
GPU'suz çalıştırmak için fork forward matematiğinin saf-PyTorch reimplementasyonu.
|
| 6 |
+
|
| 7 |
+
Mamba-3 mixer matematiği (vendor mamba3.py + siso_step kernelinden çıkarıldı), per-head:
|
| 8 |
+
_A = -softplus(dd_A) (clamp ≤ -1e-4) # ← data-dependent A (norm-free farkı)
|
| 9 |
+
DT = softplus(dd_dt + dt_bias)
|
| 10 |
+
trap = sigmoid(trap_proj)
|
| 11 |
+
α = exp(_A·DT) # base-e ✅ (kernel exp2(ön-ölçekli adt) ile birebir)
|
| 12 |
+
β = α·DT·(1-trap) ; γ = trap·DT # trapezoidal
|
| 13 |
+
h = α·h + β·(x_prev ⊗ B_prev) + γ·(x ⊗ B) # B,x = K,V ; B üzerinde partial-RoPE
|
| 14 |
+
y = h @ C # C = Q ; C üzerinde partial-RoPE
|
| 15 |
+
y += D·x ; y *= silu(z)
|
| 16 |
+
B,C paylaşımlı (ngroups=1) → head'lere broadcast + per-head bias. RoPE: rope_fraction=0.5
|
| 17 |
+
(ilk d_state/2=64 boyut, 32 angle, INTERLEAVED [çift (2j,2j+1)]). Token-token recurrence + decode-cache (step).
|
| 18 |
+
|
| 19 |
+
✅ KALİBRE TAMAM (2026-06-27): fork'a full-logit fp32 maxdiff 0.06–0.09 (top-5/argmax birebir).
|
| 20 |
+
Kritik düzeltme MLP'deydi: GatedMLP = y·silu(gate) (1.yarı=değer, 2.yarı=gate; mixer değil).
|
| 21 |
+
Decode-cache (step) full-recompute ile birebir aynı çıktı, ~6× hızlı (O(L²)→O(L)).
|
| 22 |
+
|
| 23 |
+
Kullanım (CPU): python lamba_cpu.py --ckpt checkpoints/lamba_v1.pt --tokenizer tokenizer/tokenizer.model --query "..."
|
| 24 |
+
"""
|
| 25 |
+
import os, sys, math, argparse
|
| 26 |
+
import torch, torch.nn as nn, torch.nn.functional as F
|
| 27 |
+
|
| 28 |
+
torch.set_num_threads(max(1, os.cpu_count() or 4))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def rms_norm(x, w, eps=1e-5):
|
| 32 |
+
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + eps) * w
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# ───────────── Mamba-3 mixer (saf-PyTorch, fork matematiği) ─────────────
|
| 36 |
+
class Mamba3CPU(nn.Module):
|
| 37 |
+
# KALİBRE bayrakları (Colab grid-search ile fork'a karşı belirlenir)
|
| 38 |
+
ROPE_INTER = True # fork rotate_pairwise=True ⇒ interleaved (q[0::2], q[1::2])
|
| 39 |
+
BETA_ALPHA = True # KALİBRE: β'da alpha çarpanı (grid: True daha iyi)
|
| 40 |
+
BETA_SHIFT = False # KALİBRE: β'da dt/trap kaydırılmış mı (grid: False daha iyi)
|
| 41 |
+
EXP_E = True # ✅ ÇÖZÜLDÜ: α = exp(_A·DT) base-e (exp2 değil) — mixer kalibre (maxdiff 0.14 bf16)
|
| 42 |
+
|
| 43 |
+
def __init__(self, cfg):
|
| 44 |
+
super().__init__()
|
| 45 |
+
d = cfg["d_model"]
|
| 46 |
+
self.d_inner = cfg["expand"] * d # 1536
|
| 47 |
+
self.headdim = cfg["head_dim"] # 64
|
| 48 |
+
self.nheads = self.d_inner // self.headdim # 24
|
| 49 |
+
self.d_state = cfg["d_state"] # 128
|
| 50 |
+
self.ngroups = cfg.get("ngroups", 1) # num_bc_heads = 1
|
| 51 |
+
self.A_floor = 1e-4
|
| 52 |
+
rope_fraction = cfg.get("rope_fraction", 0.5)
|
| 53 |
+
self.rot = int(self.d_state * rope_fraction) # 64 → döner boyut
|
| 54 |
+
if self.rot % 2:
|
| 55 |
+
self.rot -= 1
|
| 56 |
+
self.n_ang = self.rot // 2 # 32 angle pair
|
| 57 |
+
bc = self.d_state * self.ngroups # 128 (SISO, rank=1)
|
| 58 |
+
d_in = 2 * self.d_inner + 2 * bc + 3 * self.nheads + self.n_ang # 3432
|
| 59 |
+
self.in_proj = nn.Linear(d, d_in, bias=False)
|
| 60 |
+
self.out_proj = nn.Linear(self.d_inner, d, bias=False)
|
| 61 |
+
self.dt_bias = nn.Parameter(torch.zeros(self.nheads))
|
| 62 |
+
self.D = nn.Parameter(torch.ones(self.nheads))
|
| 63 |
+
self.B_bias = nn.Parameter(torch.zeros(self.nheads, 1, self.d_state))
|
| 64 |
+
self.C_bias = nn.Parameter(torch.zeros(self.nheads, 1, self.d_state))
|
| 65 |
+
self.B_norm = nn.Parameter(torch.ones(bc))
|
| 66 |
+
self.C_norm = nn.Parameter(torch.ones(bc))
|
| 67 |
+
|
| 68 |
+
def _rope(self, t, cos, sin):
|
| 69 |
+
"""Partial RoPE (ilk `rot`=d_state/2 boyut döner; gerisi sabit).
|
| 70 |
+
pairwise: j ↔ j+n çiftleri | interleaved: (2j, 2j+1) çiftleri."""
|
| 71 |
+
rot, n = self.rot, self.n_ang
|
| 72 |
+
rest = t[..., rot:]
|
| 73 |
+
if Mamba3CPU.ROPE_INTER:
|
| 74 |
+
head = t[..., :rot]
|
| 75 |
+
x1, x2 = head[..., 0::2], head[..., 1::2]
|
| 76 |
+
ra, rb = x1 * cos - x2 * sin, x1 * sin + x2 * cos
|
| 77 |
+
out = torch.stack([ra, rb], dim=-1).flatten(-2)
|
| 78 |
+
return torch.cat([out, rest], dim=-1)
|
| 79 |
+
a, b = t[..., :n], t[..., n:rot]
|
| 80 |
+
ra, rb = a * cos - b * sin, a * sin + b * cos
|
| 81 |
+
return torch.cat([ra, rb, rest], dim=-1)
|
| 82 |
+
|
| 83 |
+
def forward(self, u):
|
| 84 |
+
"""u: (B, L, d_model) → (B, L, d_model). Token-token recurrence."""
|
| 85 |
+
B, L, _ = u.shape
|
| 86 |
+
H, P, S = self.nheads, self.headdim, self.d_state
|
| 87 |
+
proj = self.in_proj(u)
|
| 88 |
+
z, x, Bm, Cm, dd_dt, dd_A, trap, ang = torch.split(
|
| 89 |
+
proj, [self.d_inner, self.d_inner, S * self.ngroups, S * self.ngroups,
|
| 90 |
+
H, H, H, self.n_ang], dim=-1)
|
| 91 |
+
x = x.view(B, L, H, P)
|
| 92 |
+
z = z.view(B, L, H, P)
|
| 93 |
+
_A = (-F.softplus(dd_A.float())).clamp(max=-self.A_floor) # (B,L,H)
|
| 94 |
+
DT = F.softplus(dd_dt.float() + self.dt_bias) # (B,L,H)
|
| 95 |
+
trap = torch.sigmoid(trap.float()) # (B,L,H)
|
| 96 |
+
Bm = rms_norm(Bm.float(), self.B_norm) # (B,L,S) ngroups=1
|
| 97 |
+
Cm = rms_norm(Cm.float(), self.C_norm)
|
| 98 |
+
Bm = Bm.view(B, L, 1, S) + self.B_bias.view(1, 1, H, S) # head'e broadcast + bias
|
| 99 |
+
Cm = Cm.view(B, L, 1, S) + self.C_bias.view(1, 1, H, S)
|
| 100 |
+
alpha = torch.exp(_A * DT) if Mamba3CPU.EXP_E else torch.exp2(_A * DT) # base-e vs base-2 decay
|
| 101 |
+
bDT = torch.roll(DT, 1, dims=1) if Mamba3CPU.BETA_SHIFT else DT # β'da kaydırılmış dt/trap?
|
| 102 |
+
btrap = torch.roll(trap, 1, dims=1) if Mamba3CPU.BETA_SHIFT else trap
|
| 103 |
+
beta = (alpha if Mamba3CPU.BETA_ALPHA else 1.0) * bDT * (1 - btrap)
|
| 104 |
+
gamma = trap * DT
|
| 105 |
+
|
| 106 |
+
# KALİBRE: angle birikimi (işaret/dt-ölçeği) — ilk tahmin: cum += DT·angles
|
| 107 |
+
h = torch.zeros(B, H, P, S) # ssm_state (V,QK) = (P,S)
|
| 108 |
+
x_prev = torch.zeros(B, H, P)
|
| 109 |
+
Bk_prev = torch.zeros(B, H, S)
|
| 110 |
+
cum = torch.zeros(B, H, self.n_ang)
|
| 111 |
+
ys = []
|
| 112 |
+
for t in range(L):
|
| 113 |
+
# fork: angle = angle_state + tanh(angle_proj)·DT·π (mamba3_mimo_rotary_step referans)
|
| 114 |
+
inc = torch.tanh(ang[:, t].float()).unsqueeze(1) * DT[:, t].unsqueeze(-1) * math.pi
|
| 115 |
+
cum = cum + inc # (B,H,n_ang)
|
| 116 |
+
cos, sin = torch.cos(cum), torch.sin(cum)
|
| 117 |
+
Bk = self._rope(Bm[:, t], cos, sin) # (B,H,S)
|
| 118 |
+
Cq = self._rope(Cm[:, t], cos, sin)
|
| 119 |
+
xt = x[:, t] # (B,H,P)
|
| 120 |
+
a = alpha[:, t].view(B, H, 1, 1)
|
| 121 |
+
diff = (beta[:, t].view(B, H, 1, 1) * x_prev.unsqueeze(-1) * Bk_prev.unsqueeze(-2)
|
| 122 |
+
+ gamma[:, t].view(B, H, 1, 1) * xt.unsqueeze(-1) * Bk.unsqueeze(-2))
|
| 123 |
+
h = h * a + diff # (B,H,P,S)
|
| 124 |
+
y = (h * Cq.unsqueeze(-2)).sum(-1) # (B,H,P)
|
| 125 |
+
y = y + self.D.view(1, H, 1) * xt
|
| 126 |
+
y = y * F.silu(z[:, t])
|
| 127 |
+
ys.append(y.reshape(B, 1, H * P))
|
| 128 |
+
x_prev, Bk_prev = xt, Bk
|
| 129 |
+
return self.out_proj(torch.cat(ys, dim=1).to(self.out_proj.weight.dtype))
|
| 130 |
+
|
| 131 |
+
# ───── decode-cache (tek-token step; forward'ın bir iterasyonu, O(1)) ─────
|
| 132 |
+
def init_state(self, B, device=None, dtype=torch.float32):
|
| 133 |
+
H, P, S = self.nheads, self.headdim, self.d_state
|
| 134 |
+
z = lambda *s: torch.zeros(*s, device=device, dtype=dtype)
|
| 135 |
+
return [z(B, H, P, S), z(B, H, P), z(B, H, S), z(B, H, self.n_ang)] # h, x_prev, Bk_prev, cum
|
| 136 |
+
|
| 137 |
+
def step(self, u, state):
|
| 138 |
+
"""u:(B,d_model), state=[h,x_prev,Bk_prev,cum] → (y:(B,d_model), yeni_state)."""
|
| 139 |
+
B = u.shape[0]
|
| 140 |
+
H, P, S = self.nheads, self.headdim, self.d_state
|
| 141 |
+
h, x_prev, Bk_prev, cum = state
|
| 142 |
+
z, x, Bm, Cm, dd_dt, dd_A, trap, ang = torch.split(
|
| 143 |
+
self.in_proj(u), [self.d_inner, self.d_inner, S * self.ngroups, S * self.ngroups,
|
| 144 |
+
H, H, H, self.n_ang], dim=-1)
|
| 145 |
+
x = x.view(B, H, P); z = z.view(B, H, P)
|
| 146 |
+
_A = (-F.softplus(dd_A.float())).clamp(max=-self.A_floor)
|
| 147 |
+
DT = F.softplus(dd_dt.float() + self.dt_bias)
|
| 148 |
+
trap = torch.sigmoid(trap.float())
|
| 149 |
+
Bm = rms_norm(Bm.float(), self.B_norm).view(B, 1, S) + self.B_bias.view(1, H, S)
|
| 150 |
+
Cm = rms_norm(Cm.float(), self.C_norm).view(B, 1, S) + self.C_bias.view(1, H, S)
|
| 151 |
+
alpha = torch.exp(_A * DT) if Mamba3CPU.EXP_E else torch.exp2(_A * DT)
|
| 152 |
+
beta = (alpha if Mamba3CPU.BETA_ALPHA else 1.0) * DT * (1 - trap) # BETA_SHIFT=False
|
| 153 |
+
gamma = trap * DT
|
| 154 |
+
cum = cum + torch.tanh(ang.float()).unsqueeze(1) * DT.unsqueeze(-1) * math.pi
|
| 155 |
+
cos, sin = torch.cos(cum), torch.sin(cum)
|
| 156 |
+
Bk = self._rope(Bm, cos, sin); Cq = self._rope(Cm, cos, sin)
|
| 157 |
+
diff = (beta.view(B, H, 1, 1) * x_prev.unsqueeze(-1) * Bk_prev.unsqueeze(-2)
|
| 158 |
+
+ gamma.view(B, H, 1, 1) * x.unsqueeze(-1) * Bk.unsqueeze(-2))
|
| 159 |
+
h = h * alpha.view(B, H, 1, 1) + diff
|
| 160 |
+
y = (h * Cq.unsqueeze(-2)).sum(-1) + self.D.view(1, H, 1) * x
|
| 161 |
+
y = (y * F.silu(z)).reshape(B, H * P)
|
| 162 |
+
return self.out_proj(y.to(self.out_proj.weight.dtype)), [h, x, Bk, cum]
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
# ───────────── GQA mixer (saf-PyTorch; hybrid_mamba3 ile aynı) ─────────────
|
| 166 |
+
def _rot_half(x):
|
| 167 |
+
a, b = x.chunk(2, -1)
|
| 168 |
+
return torch.cat((-b, a), -1)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class GQACPU(nn.Module):
|
| 172 |
+
def __init__(self, cfg, base=10000.0):
|
| 173 |
+
super().__init__()
|
| 174 |
+
d = cfg["d_model"]; self.nh = cfg["n_heads"]; self.nkv = cfg["n_kv_heads"]
|
| 175 |
+
self.hd = d // self.nh; self.rep = self.nh // self.nkv
|
| 176 |
+
self.q_proj = nn.Linear(d, self.nh * self.hd, bias=False)
|
| 177 |
+
self.k_proj = nn.Linear(d, self.nkv * self.hd, bias=False)
|
| 178 |
+
self.v_proj = nn.Linear(d, self.nkv * self.hd, bias=False)
|
| 179 |
+
self.out_proj = nn.Linear(self.nh * self.hd, d, bias=False)
|
| 180 |
+
self.qn = nn.Parameter(torch.ones(self.hd))
|
| 181 |
+
self.kn = nn.Parameter(torch.ones(self.hd))
|
| 182 |
+
self.register_buffer("inv", 1.0 / (base ** (torch.arange(0, self.hd, 2).float() / self.hd)), persistent=False)
|
| 183 |
+
|
| 184 |
+
def _rope(self, x, T):
|
| 185 |
+
f = torch.outer(torch.arange(T).float(), self.inv); e = torch.cat((f, f), -1)
|
| 186 |
+
return (x * e.cos()[None, None] + _rot_half(x) * e.sin()[None, None]).to(x.dtype)
|
| 187 |
+
|
| 188 |
+
def forward(self, x):
|
| 189 |
+
B, T, _ = x.shape
|
| 190 |
+
q = self.q_proj(x).view(B, T, self.nh, self.hd).transpose(1, 2)
|
| 191 |
+
k = self.k_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 192 |
+
v = self.v_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 193 |
+
q = rms_norm(q.float(), self.qn.float()).to(x.dtype)
|
| 194 |
+
k = rms_norm(k.float(), self.kn.float()).to(x.dtype)
|
| 195 |
+
q, k = self._rope(q, T), self._rope(k, T)
|
| 196 |
+
k = k.repeat_interleave(self.rep, 1); v = v.repeat_interleave(self.rep, 1)
|
| 197 |
+
y = F.scaled_dot_product_attention(q, k, v, is_causal=True)
|
| 198 |
+
return self.out_proj(y.transpose(1, 2).reshape(B, T, -1))
|
| 199 |
+
|
| 200 |
+
# ───── decode-cache (KV-cache; q tek token vs tüm geçmiş = nedensel) ─────
|
| 201 |
+
def init_state(self, B, device=None, dtype=torch.float32):
|
| 202 |
+
return [None, None] # k_cache, v_cache (B,nkv,t,hd)
|
| 203 |
+
|
| 204 |
+
def _rope_at(self, x, pos):
|
| 205 |
+
f = (self.inv * float(pos)).unsqueeze(0) # (1, hd/2)
|
| 206 |
+
e = torch.cat((f, f), -1)
|
| 207 |
+
return (x * e.cos()[None, None] + _rot_half(x) * e.sin()[None, None]).to(x.dtype)
|
| 208 |
+
|
| 209 |
+
def step(self, x, state):
|
| 210 |
+
"""x:(B,d_model), state=[k_cache,v_cache] → (y:(B,d_model), yeni_state)."""
|
| 211 |
+
B = x.shape[0]; kc, vc = state
|
| 212 |
+
pos = 0 if kc is None else kc.shape[2]
|
| 213 |
+
xq = x.view(B, 1, -1)
|
| 214 |
+
q = self.q_proj(xq).view(B, 1, self.nh, self.hd).transpose(1, 2)
|
| 215 |
+
k = self.k_proj(xq).view(B, 1, self.nkv, self.hd).transpose(1, 2)
|
| 216 |
+
v = self.v_proj(xq).view(B, 1, self.nkv, self.hd).transpose(1, 2)
|
| 217 |
+
q = rms_norm(q.float(), self.qn.float()).to(x.dtype)
|
| 218 |
+
k = rms_norm(k.float(), self.kn.float()).to(x.dtype)
|
| 219 |
+
q = self._rope_at(q, pos); k = self._rope_at(k, pos)
|
| 220 |
+
kc = k if kc is None else torch.cat([kc, k], dim=2)
|
| 221 |
+
vc = v if vc is None else torch.cat([vc, v], dim=2)
|
| 222 |
+
kk = kc.repeat_interleave(self.rep, 1); vv = vc.repeat_interleave(self.rep, 1)
|
| 223 |
+
y = F.scaled_dot_product_attention(q, kk, vv, is_causal=False)
|
| 224 |
+
return self.out_proj(y.transpose(1, 2).reshape(B, -1)), [kc, vc]
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
class GatedMLP(nn.Module):
|
| 228 |
+
def __init__(self, cfg):
|
| 229 |
+
super().__init__()
|
| 230 |
+
d = cfg["d_model"]
|
| 231 |
+
mult = 128 # mamba_ssm GatedMLP: hidden 128'in katına yuvarlar
|
| 232 |
+
hidden = ((cfg["d_intermediate"] + mult - 1) // mult) * mult # 1500 → 1536
|
| 233 |
+
self.fc1 = nn.Linear(d, 2 * hidden, bias=False)
|
| 234 |
+
self.fc2 = nn.Linear(hidden, d, bias=False)
|
| 235 |
+
|
| 236 |
+
def forward(self, x):
|
| 237 |
+
# mamba_ssm GatedMLP: 1. yarı = değer, 2. yarı = gate → y * silu(gate)
|
| 238 |
+
y, gate = self.fc1(x).chunk(2, -1)
|
| 239 |
+
return self.fc2(y * F.silu(gate))
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
class Block(nn.Module):
|
| 243 |
+
def __init__(self, cfg, is_attn):
|
| 244 |
+
super().__init__()
|
| 245 |
+
self.norm = nn.Parameter(torch.ones(cfg["d_model"]))
|
| 246 |
+
self.norm2 = nn.Parameter(torch.ones(cfg["d_model"]))
|
| 247 |
+
self.mixer = GQACPU(cfg) if is_attn else Mamba3CPU(cfg)
|
| 248 |
+
self.mlp = GatedMLP(cfg)
|
| 249 |
+
|
| 250 |
+
def forward(self, x):
|
| 251 |
+
x = x + self.mixer(rms_norm(x, self.norm))
|
| 252 |
+
x = x + self.mlp(rms_norm(x, self.norm2))
|
| 253 |
+
return x
|
| 254 |
+
|
| 255 |
+
def init_state(self, B, **kw):
|
| 256 |
+
return self.mixer.init_state(B, **kw)
|
| 257 |
+
|
| 258 |
+
def step(self, x, mstate):
|
| 259 |
+
m_out, mstate = self.mixer.step(rms_norm(x, self.norm), mstate)
|
| 260 |
+
x = x + m_out
|
| 261 |
+
x = x + self.mlp(rms_norm(x, self.norm2))
|
| 262 |
+
return x, mstate
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
class LambaCPU(nn.Module):
|
| 266 |
+
def __init__(self, cfg):
|
| 267 |
+
super().__init__()
|
| 268 |
+
self.cfg = cfg
|
| 269 |
+
self.embedding = nn.Embedding(cfg["vocab_size"], cfg["d_model"])
|
| 270 |
+
self.layers = nn.ModuleList()
|
| 271 |
+
for i in range(cfg["n_layers"]):
|
| 272 |
+
is_attn = ((i + 1) % cfg["attn_every"] == 0) and i != 0 and i != cfg["n_layers"] - 1
|
| 273 |
+
self.layers.append(Block(cfg, is_attn))
|
| 274 |
+
self.norm_f = nn.Parameter(torch.ones(cfg["d_model"]))
|
| 275 |
+
self.lm_head = nn.Linear(cfg["d_model"], cfg["vocab_size"], bias=False)
|
| 276 |
+
self.lm_head.weight = self.embedding.weight
|
| 277 |
+
|
| 278 |
+
def forward(self, ids):
|
| 279 |
+
h = self.embedding(ids)
|
| 280 |
+
for l in self.layers:
|
| 281 |
+
h = l(h)
|
| 282 |
+
return self.lm_head(rms_norm(h, self.norm_f))
|
| 283 |
+
|
| 284 |
+
def init_states(self, B, **kw):
|
| 285 |
+
return [l.init_state(B, **kw) for l in self.layers]
|
| 286 |
+
|
| 287 |
+
@torch.no_grad()
|
| 288 |
+
def step_forward(self, ids_step, states):
|
| 289 |
+
"""ids_step:(B,1) tek token. → (logits:(B,V), yeni_states). O(1) Mamba + O(t) GQA-attn."""
|
| 290 |
+
h = self.embedding(ids_step)[:, 0] # (B,d)
|
| 291 |
+
new_states = []
|
| 292 |
+
for l, stt in zip(self.layers, states):
|
| 293 |
+
h, ns = l.step(h, stt)
|
| 294 |
+
new_states.append(ns)
|
| 295 |
+
return self.lm_head(rms_norm(h, self.norm_f)), new_states
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
# ───────────── ağırlık yükleyici (fork ckpt → CPU model) ─────────────
|
| 299 |
+
def load_lamba(ckpt_path):
|
| 300 |
+
st = torch.load(ckpt_path, map_location="cpu")
|
| 301 |
+
cfg, sd = st["cfg"], st["model"]
|
| 302 |
+
model = LambaCPU(cfg)
|
| 303 |
+
mp = {}
|
| 304 |
+
for k, v in sd.items():
|
| 305 |
+
nk = k
|
| 306 |
+
nk = nk.replace(".mixer.in_proj.", ".mixer.in_proj.").replace(".mixer.out_proj.", ".mixer.out_proj.")
|
| 307 |
+
# GQA fork→CPU isim eşlemesi zaten birebir (q_proj/k_proj/v_proj/out_proj/qn/kn)
|
| 308 |
+
# Mamba B_norm.weight/C_norm.weight → B_norm/C_norm (Parameter)
|
| 309 |
+
nk = nk.replace(".mixer.B_norm.weight", ".mixer.B_norm").replace(".mixer.C_norm.weight", ".mixer.C_norm")
|
| 310 |
+
nk = nk.replace("norm_f.weight", "norm_f")
|
| 311 |
+
nk = nk.replace(".norm.weight", ".norm").replace(".norm2.weight", ".norm2")
|
| 312 |
+
mp[nk] = v
|
| 313 |
+
# norm_f / lm_head / embedding
|
| 314 |
+
miss, unexp = model.load_state_dict(mp, strict=False)
|
| 315 |
+
print(f"[load] eksik={len(miss)} beklenmeyen={len(unexp)}", flush=True)
|
| 316 |
+
if miss:
|
| 317 |
+
print(" ilk eksikler:", miss[:8])
|
| 318 |
+
if unexp:
|
| 319 |
+
print(" ilk beklenmeyenler:", unexp[:8])
|
| 320 |
+
model.eval()
|
| 321 |
+
return model, cfg
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
@torch.no_grad()
|
| 325 |
+
def generate(model, sp, prompt, max_new=64, temperature=0.0, top_k=40, top_p=0.9,
|
| 326 |
+
rep_penalty=1.2, device=None):
|
| 327 |
+
"""Decode-cache'li O(L) üretim (step_forward). faz7_rag.generate ile signature-uyumlu."""
|
| 328 |
+
dev = device or next(model.parameters()).device
|
| 329 |
+
ids = sp.encode(prompt, out_type=int); eos = sp.eos_id()
|
| 330 |
+
states = model.init_states(1, device=dev)
|
| 331 |
+
logits = None
|
| 332 |
+
for tid in ids: # prefill: her token 1 kez (O(L))
|
| 333 |
+
logits, states = model.step_forward(torch.tensor([[tid]], device=dev), states)
|
| 334 |
+
out = []
|
| 335 |
+
for _ in range(max_new):
|
| 336 |
+
lg = logits[0].float()
|
| 337 |
+
if rep_penalty != 1.0:
|
| 338 |
+
for t in set(ids + out):
|
| 339 |
+
lg[t] = lg[t] / rep_penalty if lg[t] > 0 else lg[t] * rep_penalty
|
| 340 |
+
if temperature <= 0:
|
| 341 |
+
nxt = int(lg.argmax())
|
| 342 |
+
else:
|
| 343 |
+
lg = lg / temperature
|
| 344 |
+
if top_k:
|
| 345 |
+
kth = torch.topk(lg, min(top_k, lg.numel())).values[-1]; lg[lg < kth] = -float("inf")
|
| 346 |
+
probs = F.softmax(lg, -1)
|
| 347 |
+
if top_p < 1.0:
|
| 348 |
+
s, si = torch.sort(probs, descending=True); cut = torch.cumsum(s, -1) > top_p
|
| 349 |
+
cut[1:] = cut[:-1].clone(); cut[0] = False; s[cut] = 0
|
| 350 |
+
probs = torch.zeros_like(probs).scatter_(0, si, s); probs /= probs.sum()
|
| 351 |
+
nxt = int(torch.multinomial(probs, 1))
|
| 352 |
+
if nxt == eos:
|
| 353 |
+
break
|
| 354 |
+
out.append(nxt)
|
| 355 |
+
logits, states = model.step_forward(torch.tensor([[nxt]], device=dev), states)
|
| 356 |
+
return sp.decode(out)
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def main():
|
| 360 |
+
ap = argparse.ArgumentParser()
|
| 361 |
+
ap.add_argument("--ckpt", required=True)
|
| 362 |
+
ap.add_argument("--tokenizer", required=True)
|
| 363 |
+
ap.add_argument("--query", default="Merhaba")
|
| 364 |
+
ap.add_argument("--max_new", type=int, default=64)
|
| 365 |
+
args = ap.parse_args()
|
| 366 |
+
import sentencepiece as spm
|
| 367 |
+
sp = spm.SentencePieceProcessor(model_file=args.tokenizer)
|
| 368 |
+
model, cfg = load_lamba(args.ckpt)
|
| 369 |
+
print(f"[model] {'MIMO' if cfg.get('is_mimo') else 'SISO'} | CPU | {sum(p.numel() for p in model.parameters())/1e6:.0f}M")
|
| 370 |
+
prompt = f"### Talimat:\n{args.query}\n\n### Yanıt:\n"
|
| 371 |
+
print("CEVAP:", generate(model, sp, prompt, args.max_new))
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
if __name__ == "__main__":
|
| 375 |
+
main()
|
lamba_rag.py
CHANGED
|
@@ -18,13 +18,15 @@ import os, sys, glob, re, argparse
|
|
| 18 |
import torch, torch.nn as nn, torch.nn.functional as F
|
| 19 |
from functools import partial
|
| 20 |
|
| 21 |
-
try:
|
| 22 |
from mamba_ssm.modules.block import Block
|
| 23 |
from mamba_ssm.modules.mamba3 import Mamba3
|
| 24 |
from mamba_ssm.modules.mlp import GatedMLP
|
| 25 |
from mamba_ssm.ops.triton.layer_norm import RMSNorm
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
# ───────────── model (faz3_train ile birebir) ─────────────
|
|
@@ -131,13 +133,17 @@ def resolve_ckpt(spec, token):
|
|
| 131 |
|
| 132 |
|
| 133 |
@torch.no_grad()
|
| 134 |
-
def generate(model, sp, prompt, max_new=160, temperature=0.3, top_k=40, top_p=0.9, rep_penalty=1.2):
|
| 135 |
eos = sp.eos_id()
|
| 136 |
ids = sp.encode(prompt, out_type=int)
|
| 137 |
-
x = torch.tensor([ids], device=
|
|
|
|
| 138 |
for _ in range(max_new):
|
| 139 |
-
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
| 141 |
if rep_penalty != 1.0:
|
| 142 |
for t in set(out):
|
| 143 |
logits[t] = logits[t] / rep_penalty if logits[t] > 0 else logits[t] * rep_penalty
|
|
@@ -155,7 +161,7 @@ def generate(model, sp, prompt, max_new=160, temperature=0.3, top_k=40, top_p=0.
|
|
| 155 |
nxt = int(torch.multinomial(probs, 1))
|
| 156 |
if nxt == eos:
|
| 157 |
break
|
| 158 |
-
out.append(nxt); x = torch.cat([x, torch.tensor([[nxt]], device=
|
| 159 |
if x.shape[1] >= 2048:
|
| 160 |
x = x[:, -2048:]
|
| 161 |
return sp.decode([t for t in out[len(ids):] if t != eos])
|
|
@@ -279,10 +285,15 @@ def main():
|
|
| 279 |
ap.add_argument("--no_rag", action="store_true", help="RAG'sız (kıyas için: bağlam enjekte etme)")
|
| 280 |
ap.add_argument("--ab", action="store_true",
|
| 281 |
help="A/B: her soru için RAG vs ham cevabı yan yana üret (tek koşu, tek model yüklemesi)")
|
|
|
|
|
|
|
| 282 |
args = ap.parse_args()
|
| 283 |
|
| 284 |
-
|
|
|
|
|
|
|
| 285 |
torch.set_float32_matmul_precision("high")
|
|
|
|
| 286 |
token = os.environ.get("HF_TOKEN")
|
| 287 |
try:
|
| 288 |
from huggingface_hub import get_token
|
|
@@ -298,22 +309,29 @@ def main():
|
|
| 298 |
chunks += chunk_text(d, args.chunk_size, args.overlap)
|
| 299 |
print(f"[rag] {len(docs)} doküman → {len(chunks)} chunk | embed: {args.embed_model}", flush=True)
|
| 300 |
from sentence_transformers import SentenceTransformer
|
| 301 |
-
embedder = SentenceTransformer(args.embed_model, device=
|
| 302 |
index = build_index(embedder, chunks)
|
| 303 |
|
| 304 |
# 2) generator
|
| 305 |
sp = load_tok(token, args.tokenizer)
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
tag = f"sft epoch={st.get('epoch')}" if st.get("sft") else f"base step={st.get('step','?')}"
|
| 310 |
-
print(f"[model] {tag} | {'MIMO' if st['cfg'].get('is_mimo') else 'SISO'}\n", flush=True)
|
| 311 |
|
| 312 |
def answer(q, use_rag):
|
| 313 |
hits = retrieve(embedder, index, chunks, q, args.top_k)
|
| 314 |
prompt = (build_rag_prompt(q, hits, args.max_ctx_chars) if use_rag
|
| 315 |
else f"### Talimat:\n{q}\n\n### Yanıt:\n")
|
| 316 |
-
return hits, strip_think(
|
|
|
|
| 317 |
|
| 318 |
def show(q, use_rag):
|
| 319 |
hits, ans = answer(q, use_rag)
|
|
@@ -330,10 +348,10 @@ def main():
|
|
| 330 |
print("=" * 72)
|
| 331 |
print(f"SORU: {q}")
|
| 332 |
print(f"[getirilen] ({hits[0][1]:.2f}) {hits[0][0][:90]}")
|
| 333 |
-
a_rag = strip_think(
|
| 334 |
-
|
| 335 |
-
a_raw = strip_think(
|
| 336 |
-
|
| 337 |
print(f" [A · RAG]: {a_rag}")
|
| 338 |
print(f" [B · ham]: {a_raw}")
|
| 339 |
return
|
|
|
|
| 18 |
import torch, torch.nn as nn, torch.nn.functional as F
|
| 19 |
from functools import partial
|
| 20 |
|
| 21 |
+
try: # fork (Triton) yalnız GPU/HybridLM yolu için
|
| 22 |
from mamba_ssm.modules.block import Block
|
| 23 |
from mamba_ssm.modules.mamba3 import Mamba3
|
| 24 |
from mamba_ssm.modules.mlp import GatedMLP
|
| 25 |
from mamba_ssm.ops.triton.layer_norm import RMSNorm
|
| 26 |
+
_FORK_OK, _FORK_ERR = True, None
|
| 27 |
+
except Exception as e: # CPU: fork yok → LambaCPU (--device cpu) kullanılır
|
| 28 |
+
Block = Mamba3 = GatedMLP = RMSNorm = None
|
| 29 |
+
_FORK_OK, _FORK_ERR = False, e
|
| 30 |
|
| 31 |
|
| 32 |
# ───────────── model (faz3_train ile birebir) ─────────────
|
|
|
|
| 133 |
|
| 134 |
|
| 135 |
@torch.no_grad()
|
| 136 |
+
def generate(model, sp, prompt, max_new=160, temperature=0.3, top_k=40, top_p=0.9, rep_penalty=1.2, device="cuda"):
|
| 137 |
eos = sp.eos_id()
|
| 138 |
ids = sp.encode(prompt, out_type=int)
|
| 139 |
+
x = torch.tensor([ids], device=device); out = list(ids)
|
| 140 |
+
use_amp = (device == "cuda")
|
| 141 |
for _ in range(max_new):
|
| 142 |
+
if use_amp:
|
| 143 |
+
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
| 144 |
+
logits = model(x)[0, -1].float()
|
| 145 |
+
else:
|
| 146 |
+
logits = model(x)[0, -1].float() # CPU: saf fp32 (lamba_cpu, Triton'suz)
|
| 147 |
if rep_penalty != 1.0:
|
| 148 |
for t in set(out):
|
| 149 |
logits[t] = logits[t] / rep_penalty if logits[t] > 0 else logits[t] * rep_penalty
|
|
|
|
| 161 |
nxt = int(torch.multinomial(probs, 1))
|
| 162 |
if nxt == eos:
|
| 163 |
break
|
| 164 |
+
out.append(nxt); x = torch.cat([x, torch.tensor([[nxt]], device=device)], 1)
|
| 165 |
if x.shape[1] >= 2048:
|
| 166 |
x = x[:, -2048:]
|
| 167 |
return sp.decode([t for t in out[len(ids):] if t != eos])
|
|
|
|
| 285 |
ap.add_argument("--no_rag", action="store_true", help="RAG'sız (kıyas için: bağlam enjekte etme)")
|
| 286 |
ap.add_argument("--ab", action="store_true",
|
| 287 |
help="A/B: her soru için RAG vs ham cevabı yan yana üret (tek koşu, tek model yüklemesi)")
|
| 288 |
+
ap.add_argument("--device", default="auto", choices=["auto", "cuda", "cpu"],
|
| 289 |
+
help="cpu = saf-PyTorch lamba_cpu (GPU/Triton gerekmez); auto = cuda varsa cuda")
|
| 290 |
args = ap.parse_args()
|
| 291 |
|
| 292 |
+
device = ("cuda" if torch.cuda.is_available() else "cpu") if args.device == "auto" else args.device
|
| 293 |
+
if device == "cuda":
|
| 294 |
+
assert torch.cuda.is_available(), "CUDA yok — --device cpu kullan (saf-PyTorch lamba_cpu)."
|
| 295 |
torch.set_float32_matmul_precision("high")
|
| 296 |
+
print(f"[device] {device}", flush=True)
|
| 297 |
token = os.environ.get("HF_TOKEN")
|
| 298 |
try:
|
| 299 |
from huggingface_hub import get_token
|
|
|
|
| 309 |
chunks += chunk_text(d, args.chunk_size, args.overlap)
|
| 310 |
print(f"[rag] {len(docs)} doküman → {len(chunks)} chunk | embed: {args.embed_model}", flush=True)
|
| 311 |
from sentence_transformers import SentenceTransformer
|
| 312 |
+
embedder = SentenceTransformer(args.embed_model, device=device)
|
| 313 |
index = build_index(embedder, chunks)
|
| 314 |
|
| 315 |
# 2) generator
|
| 316 |
sp = load_tok(token, args.tokenizer)
|
| 317 |
+
ckpt_path = resolve_ckpt(args.ckpt, token)
|
| 318 |
+
st = torch.load(ckpt_path, map_location="cpu")
|
| 319 |
+
if device == "cpu": # saf-PyTorch (Triton/mamba_ssm YOK)
|
| 320 |
+
import lamba_cpu as LC
|
| 321 |
+
model, _ = LC.load_lamba(ckpt_path); gen = LC.generate # decode-cache'li O(L) üretim
|
| 322 |
+
else:
|
| 323 |
+
assert _FORK_OK, f"GPU yolu mamba-og fork ister ({_FORK_ERR!r}). Wheel kur ya da --device cpu kullan."
|
| 324 |
+
model = HybridLM(st["cfg"], device="cuda", dtype=torch.bfloat16)
|
| 325 |
+
model.load_state_dict(st["model"], strict=False); model.eval(); gen = generate
|
| 326 |
tag = f"sft epoch={st.get('epoch')}" if st.get("sft") else f"base step={st.get('step','?')}"
|
| 327 |
+
print(f"[model] {tag} | {'MIMO' if st['cfg'].get('is_mimo') else 'SISO'} | {device}\n", flush=True)
|
| 328 |
|
| 329 |
def answer(q, use_rag):
|
| 330 |
hits = retrieve(embedder, index, chunks, q, args.top_k)
|
| 331 |
prompt = (build_rag_prompt(q, hits, args.max_ctx_chars) if use_rag
|
| 332 |
else f"### Talimat:\n{q}\n\n### Yanıt:\n")
|
| 333 |
+
return hits, strip_think(gen(model, sp, prompt, max_new=args.max_new,
|
| 334 |
+
temperature=args.temperature, device=device))
|
| 335 |
|
| 336 |
def show(q, use_rag):
|
| 337 |
hits, ans = answer(q, use_rag)
|
|
|
|
| 348 |
print("=" * 72)
|
| 349 |
print(f"SORU: {q}")
|
| 350 |
print(f"[getirilen] ({hits[0][1]:.2f}) {hits[0][0][:90]}")
|
| 351 |
+
a_rag = strip_think(gen(model, sp, build_rag_prompt(q, hits, args.max_ctx_chars),
|
| 352 |
+
max_new=args.max_new, temperature=args.temperature, device=device))
|
| 353 |
+
a_raw = strip_think(gen(model, sp, f"### Talimat:\n{q}\n\n### Yanıt:\n",
|
| 354 |
+
max_new=args.max_new, temperature=args.temperature, device=device))
|
| 355 |
print(f" [A · RAG]: {a_rag}")
|
| 356 |
print(f" [B · ham]: {a_raw}")
|
| 357 |
return
|