kdirgul commited on
Commit
b3f62e8
·
verified ·
1 Parent(s): 02a0421

v1.1: CPU support — pure-PyTorch lamba_cpu.py (decode-cache, no Triton) + lamba_rag.py --device cpu + README/notebook

Browse files
Files changed (4) hide show
  1. LAMBA_Inference.ipynb +14 -20
  2. README.md +63 -24
  3. lamba_cpu.py +375 -0
  4. 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
- "source": [
59
- "## Notes\n",
60
- "- **Use RAG** for any factual question. Without context, LAMBA may invent answers.\n",
61
- "- Works in **English and Turkish**; reasoning/CoT is strongest in English.\n",
62
- "- GPU is required for now (Mamba-3 Triton kernel). A CPU build is planned for **v1.1**.\n",
63
- "- Full details, eval scores, and limitations: see the **model card (README)**.\n",
64
- "\n",
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) - LAMBA V1.0
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:** the Mamba-3 forward uses a **Triton GPU kernel** currently **requires a GPU**
97
- (Google Colab works, free tier included). A pure-PyTorch CPU port is planned for **v1.1**.
 
 
 
 
 
 
 
 
98
 
99
- ## How to run (Colab GPU)
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- ```python
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
- # 2) run retrieval-augmented inference (recommended)
107
- !python faz7_rag.py --demo --query "Türkiye'nin başkenti neresi?"
108
- # or your own documents:
109
- !python faz7_rag.py --docs /content/my_docs --query "..."
 
 
 
 
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
- - `wheels/` — prebuilt Mamba-3 fork wheels (GPU)
119
- - `*.py` — inference + RAG code
 
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
- - **BSC (BEP20) BNB SMART CHAIN**
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. Tokenizer/data/
158
- training recipe and all weights are original to this project.
 
 
 
 
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
- except Exception as e:
27
- sys.exit(f"[hata] mamba-og fork yok ({e!r}). Önce wheel kurulum hücresini çalıştır (CUDA).")
 
 
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="cuda"); out = list(ids)
 
138
  for _ in range(max_new):
139
- with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
140
- logits = model(x)[0, -1].float()
 
 
 
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="cuda")], 1)
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
- assert torch.cuda.is_available(), "CUDA yok (Colab GPU gerekir)."
 
 
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="cuda")
302
  index = build_index(embedder, chunks)
303
 
304
  # 2) generator
305
  sp = load_tok(token, args.tokenizer)
306
- st = torch.load(resolve_ckpt(args.ckpt, token), map_location="cpu")
307
- model = HybridLM(st["cfg"], device="cuda", dtype=torch.bfloat16)
308
- model.load_state_dict(st["model"], strict=False); model.eval()
 
 
 
 
 
 
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(generate(model, sp, prompt, max_new=args.max_new, temperature=args.temperature))
 
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(generate(model, sp, build_rag_prompt(q, hits, args.max_ctx_chars),
334
- max_new=args.max_new, temperature=args.temperature))
335
- a_raw = strip_think(generate(model, sp, f"### Talimat:\n{q}\n\n### Yanıt:\n",
336
- max_new=args.max_new, temperature=args.temperature))
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