Text Classification
PEFT
Safetensors
Turkish
English
decision
decision-model
typed-decisions
calibrated-probabilities
system-one
jev
jevbench
noul
choice
score
turkish
lora
multiple-choice
Instructions to use berkay123001/turnike-jev-4b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use berkay123001/turnike-jev-4b with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-4B") model = PeftModel.from_pretrained(base_model, "berkay123001/turnike-jev-4b") - Notebooks
- Google Colab
- Kaggle
Ilk yayin: LoRA adaptoru + model karti + karar.py
Browse files- .gitattributes +5 -0
- README.md +287 -83
- assets/arch.png +3 -0
- assets/banner_turnike.png +3 -0
- assets/fig_compare.png +3 -0
- assets/fig_jevbench_delta.png +3 -0
- assets/fig_speed.png +3 -0
- birlestir.py +7 -4
- karar.py +24 -7
.gitattributes
CHANGED
|
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/arch.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
assets/banner_turnike.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
assets/fig_compare.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/fig_jevbench_delta.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/fig_speed.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -15,15 +15,30 @@ tags:
|
|
| 15 |
- multiple-choice
|
| 16 |
---
|
| 17 |
|
| 18 |
-
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
```python
|
| 24 |
-
# pip install torch "transformers>=5.17" peft accelerate ; karar.py: hf download berkay123001/
|
| 25 |
from karar import Karar
|
| 26 |
-
k = Karar("berkay123001/
|
| 27 |
|
| 28 |
# TR
|
| 29 |
k.sor("Kulaklık iki günde bozuldu, paramı geri istiyorum.",
|
|
@@ -43,25 +58,37 @@ k.sor("The production database has been down since 9am. Please escalate now.",
|
|
| 43 |
> - AI assistance: the card and code were written with Claude coding agents under the author's direction ([details](#ai-assistance)). / Kart ve kod, yazarın yönlendirmesiyle Claude kodlama ajanlarıyla yazıldı.
|
| 44 |
> - No web/browser data in this release; the browser version will be published separately. / Bu sürümde web verisi yok; tarayıcı sürümü ayrı yayınlanacak.
|
| 45 |
|
| 46 |
-
|
| 47 |
|
| 48 |
- Author: Berkay Hasret (independent researcher)
|
| 49 |
-
- Languages:
|
| 50 |
- Base model: Qwen/Qwen3.5-4B (Apache-2.0, Copyright 2026 Alibaba Cloud), frozen during training; only the adapter is trained
|
| 51 |
- License of the adapter, the merged weights, `karar.py` and this card: Apache-2.0 (see [License](#license))
|
| 52 |
-
- Merged bf16 weights (no PEFT needed) are published as a separate repo: `berkay123001/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
## Quick start
|
| 55 |
|
| 56 |
```bash
|
| 57 |
-
hf download berkay123001/
|
| 58 |
pip install -r requirements.txt # torch, transformers 5.x, peft, safetensors, accelerate
|
| 59 |
# optional: pip install torchao (FP8), causal-conv1d and flash-linear-attention (faster linear-attention layers)
|
| 60 |
```
|
| 61 |
|
| 62 |
```python
|
| 63 |
from karar import Karar # karar.py from this repo
|
| 64 |
-
k = Karar("berkay123001/
|
| 65 |
print(k.sor(
|
| 66 |
"Hocam merhaba, BIL204 proje ödevi bu cuma teslim ama geçen hafta ameliyat oldum, raporum ekte. "
|
| 67 |
"Teslimi salıya uzatabilir misiniz?",
|
|
@@ -71,7 +98,7 @@ print(k.sor(
|
|
| 71 |
"nötr": "duygu belirtmiyor, yalnızca bilgi aktarıyor"}}))
|
| 72 |
```
|
| 73 |
|
| 74 |
-
`sor(text, questions)` returns `{question: {label: probability}}`
|
| 75 |
|
| 76 |
| form | meaning |
|
| 77 |
|---|---|
|
|
@@ -80,24 +107,79 @@ print(k.sor(
|
|
| 80 |
| `{"type": "noul"}` | yes/no; returns `{"false": p, "true": p}` |
|
| 81 |
| `{"type": "choice" \| "score" \| "noul", "instructions": "...", "criteria": {...}}` | full form, as used in our evaluations |
|
| 82 |
|
| 83 |
-
If every key is a short identifier (`"urgent"`, `"iade_istiyor"`), it
|
| 84 |
|
| 85 |
Command line:
|
| 86 |
|
| 87 |
```bash
|
| 88 |
-
python karar.py --adapter berkay123001/
|
| 89 |
--soru "Mesajın tonu nedir?|olumlu|olumsuz|nötr" --soru "Cevap bekliyor mu?|evet|hayır" [--fp8]
|
| 90 |
```
|
| 91 |
|
| 92 |
-
Merged weights (no PEFT): `Karar(None, taban="berkay123001/
|
|
|
|
|
|
|
| 93 |
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
Tips:
|
| 97 |
- Options work best as full candidate answers or with a short description ("olumsuz": "genel olarak olumsuz"), not bare codes.
|
| 98 |
- The question wording matters; an explicit rule with examples beats a vague question.
|
| 99 |
- Yes/no questions: either `{"type": "noul"}` (the form used in training for most yes/no data) or two descriptive options.
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
## Prompt format
|
| 102 |
|
| 103 |
The prompt is the Qwen3.5 chat template with an empty thinking block. The system message and the user-message layout are fixed and must not be changed:
|
|
@@ -115,68 +197,187 @@ user: Return one answer for every field using the supplied answer symbols.
|
|
| 115 |
B = <label>: <description>
|
| 116 |
q2: ...
|
| 117 |
assistant: {
|
| 118 |
-
"q1": ?, <- probabilities are read at the token before each placeholder
|
| 119 |
-
"q2": ?
|
| 120 |
}
|
| 121 |
```
|
| 122 |
|
| 123 |
-
Yes/no questions are written as `A = no: The answer is no (...)`, `B = yes: The answer is yes (...)`.
|
| 124 |
|
| 125 |
## Results
|
| 126 |
|
| 127 |
-
All numbers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|---|---|---|---|---|---|---|
|
| 131 |
-
| **
|
| 132 |
-
|
|
| 133 |
-
|
|
| 134 |
-
|
|
| 135 |
-
|
|
| 136 |
-
|
|
| 137 |
-
|
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
| 144 |
-
|
|
| 145 |
-
|
|
| 146 |
-
|
|
| 147 |
-
|
|
| 148 |
-
|
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|---|---|---|---|
|
| 169 |
-
|
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
-
FP8 kept accuracy on tr_insan (0.7505 → 0.7513, McNemar p = 1.0)
|
| 173 |
|
| 174 |
## Training
|
| 175 |
|
| 176 |
-
- **Objective:** KL divergence between the
|
| 177 |
-
- **Items:** each training item is one text with 8–16 questions packed into one prompt
|
| 178 |
-
- **Adapter:** LoRA r=16, alpha=32, dropout 0.05 on all attention, MLP and linear-attention projections of the language model
|
| 179 |
-
- **Shortcut history:**
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
<a id="training-data"></a>
|
| 182 |
## Training data
|
|
@@ -189,20 +390,23 @@ This release contains no web/browser data; the browser version will be published
|
|
| 189 |
|
| 190 |
## Limitations
|
| 191 |
|
| 192 |
-
- **"None of the above"
|
| 193 |
-
- **
|
| 194 |
-
- **
|
| 195 |
-
- **
|
| 196 |
-
- **
|
| 197 |
-
- **
|
| 198 |
-
-
|
|
|
|
| 199 |
|
|
|
|
| 200 |
## Honesty notes
|
| 201 |
|
| 202 |
-
- **Test sets used for selection.** tr_insan, practical TR, EN-300, TR-50, the LegalBench rule test,
|
| 203 |
-
- **In-distribution parts.** The train splits of 16 JevBench configs and of the five tr_insan sources are in training (test texts excluded)
|
| 204 |
-
- **Single runs.** Each
|
| 205 |
-
- **
|
|
|
|
| 206 |
|
| 207 |
## License
|
| 208 |
|
|
@@ -211,27 +415,27 @@ The base model Qwen/Qwen3.5-4B is licensed under Apache-2.0 (the `LICENSE` file
|
|
| 211 |
<a id="ai-assistance"></a>
|
| 212 |
## AI assistance
|
| 213 |
|
| 214 |
-
This model card, `karar.py`, `birlestir.py`, the tests and most of the training and evaluation code were written with Claude coding agents (Claude Code, Anthropic) under the author's direction
|
| 215 |
|
| 216 |
## Citation
|
| 217 |
|
| 218 |
```bibtex
|
| 219 |
-
@misc{
|
| 220 |
-
title = "
|
| 221 |
author = "Hasret, Berkay",
|
| 222 |
year = "2026",
|
| 223 |
-
howpublished = "https://huggingface.co/berkay123001/
|
| 224 |
}
|
| 225 |
```
|
| 226 |
|
| 227 |
## Türkçe özet
|
| 228 |
|
| 229 |
-
|
| 230 |
|
| 231 |
Herkese açık Türkçe ve İngilizce veri setleri, LLM ile etiketlenmiş açık metinler ve sentetik veriyle eğitildi; veri setleri yayınlanmıyor. Herhangi bir ticari karar API'sinden damıtılmadı. Bu sürümde web verisi yok; tarayıcı sürümü ayrı yayınlanacak.
|
| 232 |
|
| 233 |
-
Tam JevBench (22.773 satır, resmi `jevify-run report`, 22 config eşit ağırlıklı makro)
|
| 234 |
|
| 235 |
-
Sınırlamalar: "Yukarıdakilerden hiçbiri"
|
| 236 |
|
| 237 |
-
Lisans: taban model Apache-2.0; adaptör, birleştirilmiş ağırlıklar ve kod Apache-2.0. Kullanım için `karar.py` içindeki `Karar(...).sor(metin, sorular)` yeterli. Bu kart ve çıkarım kodu yazarın yönlendirmesiyle Claude (Anthropic) kodlama ajanları tarafından yazıldı; sayılar yazar tarafından deney kayıtlarıyla doğrulandı.
|
|
|
|
| 15 |
- multiple-choice
|
| 16 |
---
|
| 17 |
|
| 18 |
+

|
| 19 |
|
| 20 |
+
# Turnike-4B
|
| 21 |
+
|
| 22 |
+
**Turnike-4B: a bilingual (English + Turkish) 4B decision model — text plus closed questions in, calibrated option probabilities out, ~100 ms, fully offline.**
|
| 23 |
+
|
| 24 |
+
**Turnike-4B: 4B parametreli, iki dilli (İngilizce + Türkçe) bir karar modeli — metin ve kapalı sorular girer, kalibre seçenek olasılıkları çıkar; ~100 ms, tamamen çevrim dışı.**
|
| 25 |
+
|
| 26 |
+
- **English: most accurate in its class on JevBench.** Macro accuracy **0.774** on the full JevBench test (22 configs, 22,773 rows, official `jevify-run report`). The best 4B entry on the public jev-bench leaderboard has 0.751 and the top entry overall 0.770 (a 35B MoE). Our result has not been submitted to the leaderboard and the 0.004 lead is small. Jev 1.13.0 scores 0.733 on the same set.
|
| 27 |
+
- **Turkish: best on human-labelled decisions.** tr_insan (1,379 human-labelled Turkish decisions): **0.748**, against 0.727 for Jev, 0.714 for Microsoft-Decision-1 (9B, API; paired McNemar p = 0.003), 0.708 for Kev-4B and 0.675 for Intern-Decision-4B.
|
| 28 |
+
- **Calibrated without a recipe.** Macro ECE on full JevBench is **0.084** from raw probabilities, against 0.113 for Jev 1.13.0. Leaderboard models that use a fitted recipe get to about 0.05, so fit a temperature if you need tighter calibration.
|
| 29 |
+
- **Fast.** One decision takes **~109 ms** in bf16 and **~82 ms** in FP8. Eight questions on one text, in one pass, take ~156 / ~126 ms. Measured on one RTX 5060 Ti 16 GB (8.7 GB used), with an earlier run of the same architecture and size.
|
| 30 |
+
- **Many questions, one pass.** Up to 16 questions about one text are answered in a single forward pass.
|
| 31 |
+
- **Runs on your hardware, offline.** You need a ~130 MB LoRA adapter (or the merged bf16 weights) and one consumer GPU. There is no API and the text never leaves the machine. CPU works, but slowly.
|
| 32 |
+
- **Cheap.** On your own hardware there is no per-call fee. For comparison, our logs show Jev's API at about 1.8e-5 $ per one-question request and 3.6e-5 $ per eight-question request, and Microsoft-Decision-1 at 0.7–1.5e-5 $ per request.
|
| 33 |
+
- **Three sizes, one recipe.** Turnike-0.8B, Turnike-4B and Turnike-9B share the data, the draw and the hyperparameters; nothing was tuned per size. See [Model family](#model-family).
|
| 34 |
+
- **Honest.** We found and fixed two training-data shortcuts (see [Training](#training)). "None of the above" is tested on a balanced set whose option count does not give the answer away. All test sets used to pick this run are listed in [Honesty notes](#honesty-notes).
|
| 35 |
+
|
| 36 |
+

|
| 37 |
|
| 38 |
```python
|
| 39 |
+
# pip install torch "transformers>=5.17" peft accelerate ; karar.py: hf download berkay123001/turnike-4b karar.py --local-dir .
|
| 40 |
from karar import Karar
|
| 41 |
+
k = Karar("berkay123001/turnike-4b")
|
| 42 |
|
| 43 |
# TR
|
| 44 |
k.sor("Kulaklık iki günde bozuldu, paramı geri istiyorum.",
|
|
|
|
| 58 |
> - AI assistance: the card and code were written with Claude coding agents under the author's direction ([details](#ai-assistance)). / Kart ve kod, yazarın yönlendirmesiyle Claude kodlama ajanlarıyla yazıldı.
|
| 59 |
> - No web/browser data in this release; the browser version will be published separately. / Bu sürümde web verisi yok; tarayıcı sürümü ayrı yayınlanacak.
|
| 60 |
|
| 61 |
+
Turnike-4B is a LoRA adapter (r=16, about 130 MB) on [Qwen/Qwen3.5-4B](https://huggingface.co/Qwen/Qwen3.5-4B). It does not generate text. The text and the questions go into one chat prompt, and the assistant turn is a JSON skeleton with one placeholder per question. Every option of a question is mapped to a single-token symbol (A, B, C, ...). At the token just before each placeholder, the model's final hidden state is multiplied with the output rows of that question's symbols only. A softmax over those symbols gives the option probabilities.
|
| 62 |
|
| 63 |
- Author: Berkay Hasret (independent researcher)
|
| 64 |
+
- Languages: English and Turkish
|
| 65 |
- Base model: Qwen/Qwen3.5-4B (Apache-2.0, Copyright 2026 Alibaba Cloud), frozen during training; only the adapter is trained
|
| 66 |
- License of the adapter, the merged weights, `karar.py` and this card: Apache-2.0 (see [License](#license))
|
| 67 |
+
- Merged bf16 weights (no PEFT needed) are published as a separate repo: `berkay123001/turnike-4b-merged`
|
| 68 |
+
|
| 69 |
+
<a id="model-family"></a>
|
| 70 |
+
## Model family
|
| 71 |
+
|
| 72 |
+
Three sizes, one recipe: the same data, the same draw (seed 3) and the same hyperparameters (LoRA r=16, α=32, lr 1e-4, 2,831 steps, effective batch 32) for every size. Nothing was tuned per size, so the smallest and the largest model are probably somewhat under-tuned. Each size is its own repo; the row in bold is the model of this card.
|
| 73 |
+
|
| 74 |
+
| model | base | trainable params | JevBench EN-300 (EN) | held-6 / other-16 | tr_insan, 1,379 human-labelled (TR) | JevBench TR-50 (TR) | practical TR, 2,966 | LegalBench rules, 885 (EN) | trained on |
|
| 75 |
+
|---|---|---|---|---|---|---|---|---|---|
|
| 76 |
+
| [Turnike-0.8B](https://huggingface.co/berkay123001/turnike-0.8b) | Qwen/Qwen3.5-0.8B | 10.8M | 0.676 | 0.682 / 0.674 | 0.699 | 0.560 | 0.840 | 0.724 | RTX 5060 Ti 16 GB, batch 4 × 8 |
|
| 77 |
+
| **Turnike-4B** (this card) | Qwen/Qwen3.5-4B | 32.5M | 0.769 | 0.826 / 0.748 | 0.748 | 0.700 | 0.891 | 0.832 | RTX 5060 Ti 16 GB, batch 4 × 8 |
|
| 78 |
+
|
| 79 |
+
<sub>Same protocol as the tables below: single runs, the same items for every model, no calibration recipe. Differences below about 0.01 (0.02 on TR-50) are within run-to-run noise.</sub>
|
| 80 |
|
| 81 |
## Quick start
|
| 82 |
|
| 83 |
```bash
|
| 84 |
+
hf download berkay123001/turnike-4b karar.py requirements.txt --local-dir .
|
| 85 |
pip install -r requirements.txt # torch, transformers 5.x, peft, safetensors, accelerate
|
| 86 |
# optional: pip install torchao (FP8), causal-conv1d and flash-linear-attention (faster linear-attention layers)
|
| 87 |
```
|
| 88 |
|
| 89 |
```python
|
| 90 |
from karar import Karar # karar.py from this repo
|
| 91 |
+
k = Karar("berkay123001/turnike-4b") # base model is downloaded automatically
|
| 92 |
print(k.sor(
|
| 93 |
"Hocam merhaba, BIL204 proje ödevi bu cuma teslim ama geçen hafta ameliyat oldum, raporum ekte. "
|
| 94 |
"Teslimi salıya uzatabilir misiniz?",
|
|
|
|
| 98 |
"nötr": "duygu belirtmiyor, yalnızca bilgi aktarıyor"}}))
|
| 99 |
```
|
| 100 |
|
| 101 |
+
`sor(text, questions)` returns `{question: {label: probability}}`, and the probabilities of each question sum to 1. A question can be given in any of these forms:
|
| 102 |
|
| 103 |
| form | meaning |
|
| 104 |
|---|---|
|
|
|
|
| 107 |
| `{"type": "noul"}` | yes/no; returns `{"false": p, "true": p}` |
|
| 108 |
| `{"type": "choice" \| "score" \| "noul", "instructions": "...", "criteria": {...}}` | full form, as used in our evaluations |
|
| 109 |
|
| 110 |
+
If every key is a short identifier (`"urgent"`, `"iade_istiyor"`), it becomes the field name in the prompt and the question text comes from `instructions`. Otherwise the fields are named `q` / `q1..qN` and the key is the question text. More than 16 questions are split into balanced passes. A question with more than 62 options is solved by two-stage elimination; options that do not reach the final get probability 0.
|
| 111 |
|
| 112 |
Command line:
|
| 113 |
|
| 114 |
```bash
|
| 115 |
+
python karar.py --adapter berkay123001/turnike-4b --metin "..." \
|
| 116 |
--soru "Mesajın tonu nedir?|olumlu|olumsuz|nötr" --soru "Cevap bekliyor mu?|evet|hayır" [--fp8]
|
| 117 |
```
|
| 118 |
|
| 119 |
+
Merged weights (no PEFT): `Karar(None, taban="berkay123001/turnike-4b-merged")`. `birlestir.py` produces them from this adapter with the same bf16 merge that `karar.py` does at load time.
|
| 120 |
+
|
| 121 |
+
`karar.py` is a single self-contained file (about 400 lines; imports only torch, transformers, peft and optionally torchao). `test_karar.py` has 9 tests:
|
| 122 |
|
| 123 |
+
- input validation and independence from any other code;
|
| 124 |
+
- 9 Turkish/English examples and the CLI;
|
| 125 |
+
- long text, many options and many questions;
|
| 126 |
+
- FP8;
|
| 127 |
+
- when the evaluation code is available, token-level prompt identity and probability identity with the code behind all numbers below.
|
| 128 |
+
|
| 129 |
+
Measured (bf16, RTX 5060 Ti): max |Δp| 0.00000 against that code on 33 questions, including an 18-question case and a 71-option case. FP8 against bf16: max |Δp| 0.071 on the 9 examples, with the same argmax.
|
| 130 |
|
| 131 |
Tips:
|
| 132 |
- Options work best as full candidate answers or with a short description ("olumsuz": "genel olarak olumsuz"), not bare codes.
|
| 133 |
- The question wording matters; an explicit rule with examples beats a vague question.
|
| 134 |
- Yes/no questions: either `{"type": "noul"}` (the form used in training for most yes/no data) or two descriptive options.
|
| 135 |
|
| 136 |
+
## Method
|
| 137 |
+
|
| 138 |
+

|
| 139 |
+
|
| 140 |
+
**Readout.** For question $q$ with options $O_q$, let $s(o)$ be the symbol of option $o$, $h_q$ the final hidden state at the token just before $q$'s placeholder, and $w_s$ the output-head row of symbol $s$. Then
|
| 141 |
+
|
| 142 |
+
$$
|
| 143 |
+
p_q(o) \;=\; \frac{\exp\!\big(w_{s(o)}^{\top} h_q\big)}{\sum_{o' \in O_q} \exp\!\big(w_{s(o')}^{\top} h_q\big)}, \qquad |O_q| \le 62 .
|
| 144 |
+
$$
|
| 145 |
+
|
| 146 |
+
Only the $|O_q|$ symbol rows are multiplied (in fp32). The 248,320-token vocabulary is never scored, and no temperature is applied ($T = 1$).
|
| 147 |
+
|
| 148 |
+
**Packing.** One prompt holds $Q \le 16$ questions about the same text, and one forward pass yields all $h_{q_1}, \dots, h_{q_Q}$. Attention is causal, so a question's readout sees the text, the full decision schema and the `?` placeholders of the earlier fields. It does not see their answers.
|
| 149 |
+
|
| 150 |
+
**Training loss.** Each question has a target distribution $y_q$: the gold option smoothed to 0.9, or the crowd-label distribution where the source has one. Over all questions $\mathcal{B}$ in a mini-batch:
|
| 151 |
+
|
| 152 |
+
$$
|
| 153 |
+
\mathcal{L} \;=\; \frac{1}{|\mathcal{B}|} \sum_{q \in \mathcal{B}} \mathrm{KL}\!\left(y_q \,\middle\|\, p_q\right)
|
| 154 |
+
\;=\; \frac{1}{|\mathcal{B}|} \sum_{q \in \mathcal{B}} \sum_{o \in O_q} y_q(o) \big[\log y_q(o) - \log p_q(o)\big].
|
| 155 |
+
$$
|
| 156 |
+
|
| 157 |
+
There is no per-question weight in the loss. Group shares, class balancing (α = 0.5) and the 0.08 share of broad-template questions act on how often an item is drawn, not on $\mathcal{L}$.
|
| 158 |
+
|
| 159 |
+
```text
|
| 160 |
+
Algorithm 1 One Turnike-4B call: sor(text, questions)
|
| 161 |
+
Input : text x; questions q_1..q_N, each with options O_q (labels, optional descriptions)
|
| 162 |
+
Output: for every q, a probability for every option in O_q
|
| 163 |
+
|
| 164 |
+
1 validate the inputs; write every question in the full form {type, instructions, criteria}
|
| 165 |
+
2 questions with |O_q| > 62 go to line 10; split the rest into ceil(N/16) balanced packs
|
| 166 |
+
3 for each pack P:
|
| 167 |
+
4 prompt <- system message
|
| 168 |
+
+ "## State" + x as a JSON string
|
| 169 |
+
+ "## Decision schema": per q, "field: question" then "A = label: description", ...
|
| 170 |
+
+ assistant skeleton { "q1": "?", "q2": "?", ... } (x truncated if > 4,096 tokens)
|
| 171 |
+
5 run ONE forward pass of base + LoRA (no generation)
|
| 172 |
+
6 for each q in P:
|
| 173 |
+
7 h_q <- final hidden state at the token just before q's "?"
|
| 174 |
+
8 z_q <- W_sym[symbols of q] . h_q (<= 62 rows, fp32)
|
| 175 |
+
9 p_q <- softmax(z_q) (T = 1; any temperature is up to the caller)
|
| 176 |
+
10 for each q with |O_q| > 62:
|
| 177 |
+
k <- ceil(|O_q| / 62); split O_q into k balanced chunks; one pass per chunk;
|
| 178 |
+
keep the top floor(62/k) options of every chunk; one final pass over the finalists;
|
| 179 |
+
options that are not finalists get probability 0
|
| 180 |
+
11 return {q: {label: p_q(label)}}
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
## Prompt format
|
| 184 |
|
| 185 |
The prompt is the Qwen3.5 chat template with an empty thinking block. The system message and the user-message layout are fixed and must not be changed:
|
|
|
|
| 197 |
B = <label>: <description>
|
| 198 |
q2: ...
|
| 199 |
assistant: {
|
| 200 |
+
"q1": "?", <- probabilities are read at the token before each "?" placeholder
|
| 201 |
+
"q2": "?"
|
| 202 |
}
|
| 203 |
```
|
| 204 |
|
| 205 |
+
Yes/no questions are written as `A = no: The answer is no (...)`, `B = yes: The answer is yes (...)`. If the prompt exceeds `MAKS_ISTEM_TOKEN = 4096` tokens (training used 1536), the text is truncated from the end. Questions and options are never truncated.
|
| 206 |
|
| 207 |
## Results
|
| 208 |
|
| 209 |
+
All numbers below are our own measurements unless marked as reported by someone else. Every model saw the same items and was scored by the same code. Probabilities are raw, with no calibration recipe. Each result is a single run.
|
| 210 |
+
|
| 211 |
+
| model | JevBench EN-300 (EN) | LegalBench rules, 885 (EN) | tr_insan, 1,379 human-labelled (TR) | JevBench TR-50 (TR) | practical TR, 2,966 (TR) |
|
| 212 |
+
|---|---|---|---|---|---|
|
| 213 |
+
| **Turnike-4B** | 0.769 | 0.832 | 0.748 | 0.700 | 0.891 |
|
| 214 |
+
| **Turnike-0.8B** | 0.676 | 0.724 | 0.699 | 0.560 | 0.840 |
|
| 215 |
+
| Microsoft-Decision-1 (9B, closed, API) | 0.775 | – | 0.714 | 0.745 | 0.857 |
|
| 216 |
+
| Jev 1.13.0 (API) | – | 0.924 † | 0.727 | – | 0.872 |
|
| 217 |
+
| Kev-4B | 0.706 | 0.786 | 0.708 | 0.655 | 0.797 |
|
| 218 |
+
| jevify Qwen3.5-4B readout LoRA + coherence | – | – | 0.642 | 0.687 | 0.847 |
|
| 219 |
+
| internlm/Intern-Decision-4B | 0.687 | 0.656 | 0.675 | 0.624 | 0.843 |
|
| 220 |
+
| OpenJev 4B (qwen3.5-4b-nli-v5) | – | – | 0.645 | 0.621 | 0.738 |
|
| 221 |
+
| Qwen3.5-4B, zero-shot (our prompt) | – | – | 0.612 | – | 0.745 |
|
| 222 |
+
| Kev-0.8B | 0.570 | 0.496 | 0.583 | 0.508 | 0.697 |
|
| 223 |
|
| 224 |
+
† Reported by the jevify author on the same 885 items (jevify README, finding 14); not our run. – = not measured. Run-to-run noise: retraining an earlier run of the same recipe with another seed moved tr_insan by +0.001, EN-300 by +0.002 and TR-50 by +0.020.
|
| 225 |
+
|
| 226 |
+
### English
|
| 227 |
+
|
| 228 |
+
#### Table 1. Full JevBench test, next to the public leaderboard
|
| 229 |
+
|
| 230 |
+
| model | source | macro acc | macro ECE ↓ | macro Brier ↓ | sel@90 | choice / score / noul acc |
|
| 231 |
|---|---|---|---|---|---|---|
|
| 232 |
+
| **Turnike-4B** | our run, not submitted | **0.774** | 0.084 | 0.293 | **0.805** | 0.791 / 0.599 / 0.902 |
|
| 233 |
+
| Qwen/Qwen-AgentWorld-35B-A3B (readout LoRA + coherence) | reported by the leaderboard | 0.770 | 0.055 | 0.292 | 0.799 | 0.792 / 0.566 / 0.917 |
|
| 234 |
+
| Qwen/Qwen3.5-9B (readout LoRA + coherence) | reported by the leaderboard | 0.763 | 0.056 | 0.299 | 0.791 | 0.791 / 0.554 / 0.906 |
|
| 235 |
+
| Qwen/Qwen3.5-9B (readout LoRA) | reported by the leaderboard | 0.759 | 0.052 | 0.302 | 0.787 | 0.790 / 0.541 / 0.905 |
|
| 236 |
+
| Qwen/Qwen3.5-4B (readout LoRA + coherence) | reported by the leaderboard | 0.751 | 0.058 | 0.316 | 0.779 | 0.771 / 0.555 / 0.893 |
|
| 237 |
+
| perplexity-ai/pplx-decider-v1-27b (hosted API) | reported by the leaderboard | 0.748 | 0.080 | 0.318 | 0.774 | 0.775 / 0.519 / 0.911 |
|
| 238 |
+
| Qwen/Qwen3.5-4B-Base (readout LoRA + coherence) | reported by the leaderboard | 0.747 | 0.058 | 0.318 | 0.777 | 0.771 / 0.535 / 0.897 |
|
| 239 |
+
| Qwen/Qwen3.5-4B (Tier 2 residual) | reported by the leaderboard | 0.747 | 0.110 | 0.342 | 0.774 | 0.770 / 0.529 / 0.903 |
|
| 240 |
+
| Qwen/Qwen3.5-4B (readout LoRA) | reported by the leaderboard | 0.743 | 0.059 | 0.321 | 0.772 | 0.766 / 0.530 / 0.896 |
|
| 241 |
+
| google/gemma-4-E4B-it (readout LoRA + coherence) | reported by the leaderboard | 0.742 | 0.053 | 0.322 | 0.772 | 0.759 / 0.546 / 0.889 |
|
| 242 |
+
| Qwen/Qwen3.5-4B-Base (readout LoRA) | reported by the leaderboard | 0.741 | 0.064 | 0.324 | 0.770 | 0.768 / 0.522 / 0.893 |
|
| 243 |
+
| google/gemma-4-E4B-it (readout LoRA) | reported by the leaderboard | 0.737 | 0.057 | 0.330 | 0.766 | 0.754 / 0.531 / 0.892 |
|
| 244 |
+
| Qwen/Qwen3.5-4B (Tier 2 residual, lr 3e-05) | reported by the leaderboard | 0.734 | 0.096 | 0.342 | 0.762 | 0.761 / 0.518 / 0.884 |
|
| 245 |
+
| Jev 1.13.0 (TypeSafe API) | reported by the leaderboard | 0.733 | 0.113 | 0.349 | 0.760 | 0.770 / 0.503 / 0.881 |
|
| 246 |
+
| google/gemma-4-E4B (readout LoRA) | reported by the leaderboard | 0.708 | 0.061 | 0.359 | 0.736 | 0.717 / 0.502 / 0.874 |
|
| 247 |
+
| togethercomputer/Tev1-4B-experimental (Tev1 prompt) | reported by the leaderboard | 0.703 | 0.086 | 0.361 | 0.732 | 0.717 / 0.512 / 0.850 |
|
| 248 |
+
| Qwen/Qwen3.5-4B (Tier 1 residual) | reported by the leaderboard | 0.698 | 0.089 | 0.378 | 0.729 | 0.699 / 0.507 / 0.862 |
|
| 249 |
+
| Qwen/Qwen3.5-4B (Tier 0, no training) | reported by the leaderboard | 0.662 | 0.090 | 0.401 | 0.689 | 0.687 / 0.468 / 0.796 |
|
| 250 |
+
|
| 251 |
+
<sub>Leaderboard rows: the public jev-bench leaderboard (Praveenrajus/jev-bench dataset card, data v0.1.1, 46 rows), values as published: the top 10, the other 4B-class entries with macro ≥ 0.66, and the Jev reference row. Per-config values for these rows are not available to us. Ours: full test split (22,773 rows), official `jevify-run report`, argmax and yes/no at p ≥ 0.5, raw probabilities, one deterministic run. The lead over the top row is small and has not been checked by the leaderboard maintainers; leaderboard models report lower ECE (≈0.05).</sub>
|
| 252 |
+
|
| 253 |
+

|
| 254 |
+
|
| 255 |
+
#### Table 2. Full JevBench, per config, against Jev 1.13.0
|
| 256 |
+
|
| 257 |
+
| config | type | n | Turnike-4B acc | Turnike-4B ECE | Jev 1.13.0 acc | Jev 1.13.0 ECE | Turnike-4B − Jev |
|
| 258 |
+
|---|---|---|---|---|---|---|---|
|
| 259 |
+
| *held-out 6 (not in training)* | | | | | | | |
|
| 260 |
+
| `arc_challenge` | choice | 1,000 | 0.932 | 0.087 | 0.979 | 0.010 | −0.047 |
|
| 261 |
+
| `clinc150` | choice | 1,000 | 0.856 | 0.162 | 0.893 | 0.033 | −0.037 |
|
| 262 |
+
| `fever_evidence` | noul | 1,000 | 0.952 | 0.066 | 0.972 | 0.028 | −0.020 |
|
| 263 |
+
| `measuring_hate_speech` | score | 1,000 | 0.631 | 0.070 | 0.527 | 0.237 | +0.104 |
|
| 264 |
+
| `strategyqa_grounded` | noul | 687 | 0.943 | 0.118 | 0.956 | 0.059 | −0.013 |
|
| 265 |
+
| `yelp5` | score | 1,000 | 0.668 | 0.061 | 0.685 | 0.174 | −0.017 |
|
| 266 |
+
| *held-out 6 (not in training), macro* | | 5,687 | **0.830** | 0.094 | 0.835 | 0.090 | −0.005 |
|
| 267 |
+
| *other 16 (train splits in training)* | | | | | | | |
|
| 268 |
+
| `banking77` | choice | 1,000 | 0.862 | 0.100 | 0.796 | 0.095 | +0.066 |
|
| 269 |
+
| `boolq` | noul | 1,000 | 0.904 | 0.085 | 0.917 | 0.021 | −0.013 |
|
| 270 |
+
| `chaosnli` | choice | 1,599 | 0.631 | 0.108 | 0.615 | 0.222 | +0.016 |
|
| 271 |
+
| `civil_comments` | noul | 2,000 | 0.919 | 0.086 | 0.729 | 0.045 | +0.191 |
|
| 272 |
+
| `go_emotions` | choice | 1,000 | 0.569 | 0.032 | 0.282 | 0.384 | +0.287 |
|
| 273 |
+
| `helpsteer2_helpfulness` | score | 1,000 | 0.446 | 0.025 | 0.363 | 0.232 | +0.083 |
|
| 274 |
+
| `helpsteer2_verbosity` | score | 1,000 | 0.679 | 0.160 | 0.341 | 0.231 | +0.338 |
|
| 275 |
+
| `ledgar` | choice | 1,000 | 0.775 | 0.099 | 0.751 | 0.117 | +0.024 |
|
| 276 |
+
| `massive` | choice | 1,000 | 0.857 | 0.099 | 0.808 | 0.090 | +0.049 |
|
| 277 |
+
| `mmlu` | choice | 1,000 | 0.729 | 0.075 | 0.923 | 0.027 | −0.194 |
|
| 278 |
+
| `mnli` | choice | 1,000 | 0.905 | 0.085 | 0.883 | 0.032 | +0.022 |
|
| 279 |
+
| `paws` | noul | 1,000 | 0.909 | 0.079 | 0.846 | 0.040 | +0.063 |
|
| 280 |
+
| `sms_spam` | noul | 800 | 0.983 | 0.093 | 0.965 | 0.068 | +0.018 |
|
| 281 |
+
| `sst5` | score | 1,000 | 0.582 | 0.059 | 0.565 | 0.190 | +0.017 |
|
| 282 |
+
| `strategyqa_closed` | noul | 687 | 0.703 | 0.055 | 0.785 | 0.042 | −0.082 |
|
| 283 |
+
| `stsb` | score | 1,000 | 0.585 | 0.048 | 0.538 | 0.119 | +0.047 |
|
| 284 |
+
| *other 16 (train splits in training), macro* | | 17,086 | **0.752** | 0.081 | 0.694 | 0.122 | +0.058 |
|
| 285 |
+
| **all 22, macro** | | 22,773 | **0.774** | **0.084** | 0.733 | 0.113 | +0.041 |
|
| 286 |
+
|
| 287 |
+
<sub>Jev values: the Jev 1.13.0 per-config table on the jev-bench dataset card (TypeSafe API, every test record); our project records hold the same accuracies for all 22 configs. Turnike-4B is ahead of Jev on 14 of 22 configs and by +0.041 macro. On the six held-out sources Turnike-4B (0.830) is above the leaderboard author's best open model (0.828, as stated on the card) and behind Jev (0.835) and the hosted pplx-decider-v1-27b (0.840). Jev also leads on the knowledge configs (mmlu, strategyqa_closed, arc_challenge).</sub>
|
| 288 |
+
|
| 289 |
+
#### Table 3. JevBench EN-300: the same 6,600 rows for every model
|
| 290 |
+
|
| 291 |
+
| model | EN-300 macro | held-6 / other-16 | McNemar vs Turnike-4B (only-Turnike-4B / only-other) |
|
| 292 |
+
|---|---|---|---|
|
| 293 |
+
| **Turnike-4B** | 0.769 | 0.826 / 0.748 | – |
|
| 294 |
+
| **Turnike-0.8B** | 0.676 | 0.682 / 0.674 | 993 / 378, p = 6e-64 |
|
| 295 |
+
| Microsoft-Decision-1 (9B, closed, API) | 0.775 | 0.843 / 0.750 | 426 / 465, p = 0.20 |
|
| 296 |
+
| Kev-4B | 0.706 | 0.772 / 0.682 | 795 / 381, p = 4e-34 |
|
| 297 |
+
| internlm/Intern-Decision-4B | 0.687 | 0.783 / 0.651 | 901 / 362, p = 2e-53 |
|
| 298 |
+
| Kev-0.8B | 0.570 | 0.586 / 0.564 | 1666 / 349, p = 4e-205 |
|
| 299 |
+
|
| 300 |
+
<sub>Protocol: 300 rows per config from the JevBench test split (seed 7), 22 configs, the same 6,600 rows for every model; exact two-sided McNemar on paired rows. Not measured on this subset: Jev 1.13.0 (API), jevify Qwen3.5-4B readout LoRA + coherence, OpenJev 4B (qwen3.5-4b-nli-v5), Qwen3.5-4B, zero-shot (our prompt) (Jev: see Tables 1–2). We do not know whether Microsoft-Decision-1 was trained on JevBench. Intern-Decision-4B used its own prompt and calibration temperature (for more than 62 options we added the same elimination step), Kev its own HTTP server. EN-300 macro ECE: Turnike-4B 0.087, Microsoft-Decision-1 0.079, Intern-Decision-4B 0.115, Kev-4B 0.135.</sub>
|
| 301 |
+
|
| 302 |
+
### Turkish
|
| 303 |
+
|
| 304 |
+
#### Table 4. Turkish human gold (tr_insan), per task
|
| 305 |
+
|
| 306 |
+
| model | topic (301) | sentiment 3-way (300) | NLI (300) | film stars 1–5 (178) | product stars 1–5 (300) | all (1,379) | McNemar vs Turnike-4B |
|
| 307 |
+
|---|---|---|---|---|---|---|---|
|
| 308 |
+
| **Turnike-4B** | 0.924 | 0.910 | 0.790 | 0.562 | 0.480 | **0.748** | – |
|
| 309 |
+
| **Turnike-0.8B** | 0.884 | 0.870 | 0.727 | 0.472 | 0.450 | **0.699** | 152 / 84, p = 1e-5 |
|
| 310 |
+
| Microsoft-Decision-1 (9B, closed, API) | 0.887 | 0.893 | 0.727 | 0.545 | 0.450 | **0.714** | 141 / 94, p = 0.003 |
|
| 311 |
+
| Jev 1.13.0 (API) | 0.904 | 0.893 | 0.777 | 0.517 | 0.457 | **0.727** | 131 / 101, p = 0.06 |
|
| 312 |
+
| Kev-4B | 0.887 | 0.860 | 0.737 | 0.528 | 0.453 | **0.708** | 126 / 70, p = 8e-5 |
|
| 313 |
+
| jevify Qwen3.5-4B readout LoRA + coherence | 0.844 | 0.823 | 0.747 | 0.337 | 0.333 | **0.642** | 222 / 75, p = 5e-18 |
|
| 314 |
+
| internlm/Intern-Decision-4B | 0.867 | 0.860 | 0.707 | 0.455 | 0.397 | **0.675** | 172 / 71, p = 8e-11 |
|
| 315 |
+
| OpenJev 4B (qwen3.5-4b-nli-v5) | 0.804 | 0.837 | 0.683 | 0.455 | 0.370 | **0.645** | 253 / 111, p = 7e-14 |
|
| 316 |
+
| Qwen3.5-4B, zero-shot (our prompt) | 0.850 | 0.850 | 0.540 | 0.326 | 0.377 | **0.612** | 280 / 92, p = 3e-23 |
|
| 317 |
+
| Kev-0.8B | 0.841 | 0.693 | 0.630 | 0.371 | 0.293 | **0.583** | 309 / 81, p = 2e-32 |
|
| 318 |
+
|
| 319 |
+
<sub>Protocol: human gold labels from five public Turkish datasets, test texts only. All models were scored by the same script against the same gold file; no model answered "unsure". Stars are exact match on a 5-point scale (within ±1 star Turnike-4B gets 0.91 / 0.90). Our leakage scan flagged 19 items as overlapping with training texts; without them Turnike-4B gets 0.746. Microsoft-Decision-1 and Jev were called through their APIs, the others ran locally.</sub>
|
| 320 |
+
|
| 321 |
+
#### Table 5. JevBench TR-50: the same 1,100 machine-translated rows for every model
|
| 322 |
+
|
| 323 |
+
| model | TR-50 macro | held-6 / other-16 | McNemar vs Turnike-4B (only-Turnike-4B / only-other) |
|
| 324 |
|---|---|---|---|
|
| 325 |
+
| **Turnike-4B** | 0.700 | 0.750 / 0.681 | – |
|
| 326 |
+
| **Turnike-0.8B** | 0.560 | 0.583 / 0.551 | 239 / 85, p = 4e-18 |
|
| 327 |
+
| Microsoft-Decision-1 (9B, closed, API) | 0.745 | 0.840 / 0.709 | 72 / 121, p = 5e-4 |
|
| 328 |
+
| Kev-4B | 0.655 | 0.720 / 0.630 | 127 / 77, p = 6e-4 |
|
| 329 |
+
| jevify Qwen3.5-4B readout LoRA + coherence | 0.687 | 0.743 / 0.666 | 106 / 92, p = 0.36 |
|
| 330 |
+
| internlm/Intern-Decision-4B | 0.624 | 0.700 / 0.595 | 143 / 59, p = 3e-9 |
|
| 331 |
+
| OpenJev 4B (qwen3.5-4b-nli-v5) | 0.621 | 0.717 / 0.585 | 172 / 85, p = 6e-8 |
|
| 332 |
+
| Kev-0.8B | 0.508 | 0.580 / 0.481 | 295 / 84, p = 1e-28 |
|
| 333 |
+
|
| 334 |
+
<sub>Protocol: 50 rows per config (seed 7) from a machine-translated Turkish JevBench test, 22 configs, 1,100 rows. The set is small and noisy: it moved by 0.02 between two seeds of the same recipe. Exact two-sided McNemar on paired rows; Microsoft-Decision-1 rescored from its raw API answers with the same decision rule (macro 0.7445, as originally reported).</sub>
|
| 335 |
+
|
| 336 |
+
### Behaviour tests (Table 6)
|
| 337 |
+
|
| 338 |
+
| test | **Turnike-4B** | **Turnike-0.8B** | Jev 1.13.0 (API) | Kev-4B | internlm/Intern-Decision-4B | Kev-0.8B |
|
| 339 |
+
|---|---|---|---|---|---|---|
|
| 340 |
+
| planted instruction: accuracy clean / planted (800 × 2) | 0.927 / 0.941 | 0.885 / 0.892 | – | 0.897 / 0.816 | 0.877 / 0.599 | 0.826 / 0.568 |
|
| 341 |
+
| hijack rate ↓ | −0.014 | −0.008 | – | +0.081 | +0.279 | +0.259 |
|
| 342 |
+
| LegalBench rule application (885, 10 tasks, none trained on) | 0.832 | 0.724 | 0.924 † | 0.786 | 0.656 | 0.496 |
|
| 343 |
+
|
| 344 |
+
<sub>Protocol: one run each, same items and scoring code. Planted instruction: the same 800 items with a sentence inserted into the text that names a wrong answer; hijack rate = how much more often the model gives the named answer than on the clean copy (0 = ignored). † Reported by the jevify author on the same 885 items; not our run.</sub>
|
| 345 |
+
|
| 346 |
+
Measured for the Turnike models only (the competitors were not run on these two tests):
|
| 347 |
+
|
| 348 |
+
- **Turnike-4B, "None of the above", balanced** (908 MMLU/ARC items × 2 variants, half with the right option removed; the option count no longer gives the answer away): accuracy 0.768 (drop variant) / 0.742 (add variant). It picks "none" in 0.809 / 0.798 of the items where the right option is missing, and wrongly in 0.223 / 0.252 where it is present. In the "add" variant the added option is off-topic only in items without the right option, which can itself hint "none"; the "drop" variant has no such hint.
|
| 349 |
+
- **Turnike-4B, KVKK sufficiency** (internal Turkish personal-data check; two yes/no questions: is a legal basis stated, is the data controller named; 1,172 decisions): 2 errors. On the real policy texts it is 1,160/1,160 correct (named controller 9/9, legal basis 10/10, must-be-insufficient 1,141/1,141); all 2 errors are on the 12 short synthetic passages (titles, headings, one-sentence statements) of the controller question (10/12). Gold sets are tiny; two seeds of the 4B recipe gave 11 and 16 errors.
|
| 350 |
+
|
| 351 |
+
- **Turnike-0.8B, "None of the above", balanced** (908 MMLU/ARC items × 2 variants, half with the right option removed; the option count no longer gives the answer away): accuracy 0.558 (drop variant) / 0.532 (add variant). It picks "none" in 0.666 / 0.653 of the items where the right option is missing, and wrongly in 0.402 / 0.428 where it is present. In the "add" variant the added option is off-topic only in items without the right option, which can itself hint "none"; the "drop" variant has no such hint.
|
| 352 |
+
- **Turnike-0.8B, KVKK sufficiency** (internal Turkish personal-data check; two yes/no questions: is a legal basis stated, is the data controller named; 1,172 decisions): 6 errors. On the real policy texts it is 1,158/1,160 correct (named controller 7/9, legal basis 10/10, must-be-insufficient 1,141/1,141); errors by set: A-controller 2/9, S 4/12. Gold sets are tiny; two seeds of the 4B recipe gave 11 and 16 errors.
|
| 353 |
+
|
| 354 |
+
### Latency and cost (Table 7)
|
| 355 |
+
|
| 356 |
+
| model | where | 1 question, p50 | 8 questions on one text, p50 | GPU memory | cost per call |
|
| 357 |
+
|---|---|---|---|---|---|
|
| 358 |
+
| **Turnike-4B**, bf16, optional kernels | RTX 5060 Ti 16 GB | **109 ms** | **156 ms** | 8.7 GB | none (own hardware) |
|
| 359 |
+
| **Turnike-4B**, FP8 (`--fp8`), optional kernels | RTX 5060 Ti 16 GB | **82 ms** | **126 ms** | 8.8 GB | none |
|
| 360 |
+
| Turnike-4B architecture, bf16, no optional kernels | RTX 5060 Ti 16 GB | 120 ms | 173 ms | 8.7 GB | none |
|
| 361 |
+
| Microsoft-Decision-1 | API, network included | 913 ms (tr_insan run, n = 1,379) | – | – | ≈0.7e-5–1.5e-5 $ (0.042 $/M input tokens) |
|
| 362 |
+
| Jev 1.13.0 | API, network included | 592 ms (n = 42) | 386 ms (n = 44) | – | ≈1.8e-5 $ (1 q) / ≈3.6e-5 $ (8 q) |
|
| 363 |
+
| Kev-4B, its own HTTP server | RTX 5060 Ti 16 GB | – | 282 ms | – | none |
|
| 364 |
+
| Intern-Decision-4B, bf16, no optional kernels | RTX 5060 Ti 16 GB | 121 ms | 173 ms | 8.7 GB | none |
|
| 365 |
+
| Qwen3.5-4B zero-shot, bf16, no optional kernels | RTX 5060 Ti 16 GB | 120 ms | 173 ms | 8.7 GB | none |
|
| 366 |
+
|
| 367 |
+

|
| 368 |
|
| 369 |
+
<sub>Protocol: local rows are wall-clock `decide()` time, CUDA-synchronised, 2 warm-up calls dropped. "1 question" is a 7-option topic question over 60 tr_insan texts; "8 questions" is 15 messages × 8 support-desk questions in one call. The Turnike-4B kernel and FP8 rows were measured with an earlier run of the same architecture and size, with `causal-conv1d` and `flash-linear-attention` installed. The rows marked "no optional kernels" come from one session without them. Kev-4B: 8-question p50 through its own HTTP server (our earlier run). API latencies come from separate runs and include the network round trip; Jev from our demo logs, Microsoft-Decision-1 with 4 parallel requests, cost = our spend / requests. FP8 kept accuracy on tr_insan (0.7505 → 0.7513, McNemar p = 1.0) and EN-300 (0.7641 → 0.7641), but about 2–2.5% of individual decisions flip (mean |Δp| 0.017, max 0.34); use bf16 if you threshold on probabilities.</sub>
|
| 370 |
|
| 371 |
## Training
|
| 372 |
|
| 373 |
+
- **Objective:** KL divergence between the target distribution and the model's option distribution, per question, averaged over the questions in a batch (see [Method](#method)). Hard labels are smoothed to 0.9 on the gold option; crowd-label distributions are used where the source has them.
|
| 374 |
+
- **Items:** each training item is one text with 8–16 questions packed into one prompt, exactly the inference format. Items are drawn from the pool with per-group shares (about 47% English), max length 1,536 tokens, seed 3. If two questions have option lists that reveal each other's answer, they never share a prompt.
|
| 375 |
+
- **Adapter:** LoRA r=16, alpha=32, dropout 0.05 on all attention, MLP and linear-attention projections of the language model; the vision tower is untouched and not used. lr 1e-4, warm-up 3%, effective batch 32, 2,831 steps on one RTX 5060 Ti 16 GB (batch 4 × accumulation 8).
|
| 376 |
+
- **Shortcut history:** we found and removed two training-data shortcuts.
|
| 377 |
+
- In an earlier run, the model could read a "none of the above" answer from a sibling question in the same packed item. Fix: such questions are now split into separate prompts.
|
| 378 |
+
- The next run inferred "none" from the option count, because items with the right option removed had one option fewer. Fix in this release: the option count and any added off-topic option are independent of the answer (7,585 rows rebuilt).
|
| 379 |
+
- **Family:** Turnike-0.8B (Qwen3.5-0.8B) and Turnike-9B (Qwen3.5-9B) were trained with exactly this recipe and this draw, with no per-size tuning; the 9B ran on a Colab RTX PRO 6000 96 GB with batch 16 × accumulation 2 (the same effective batch of 32).
|
| 380 |
+
- An earlier internal model distilled from a commercial API was not released; Turnike-4B is trained without such labels.
|
| 381 |
|
| 382 |
<a id="training-data"></a>
|
| 383 |
## Training data
|
|
|
|
| 390 |
|
| 391 |
## Limitations
|
| 392 |
|
| 393 |
+
- **"None of the above" is used somewhat too often.** On the balanced test the model picks "none" in 0.80–0.81 of the items where the right option is missing, but also in 0.22–0.25 of the items where it is present.
|
| 394 |
+
- **Not the best everywhere.** Microsoft-Decision-1 (9B) is ahead on JevBench TR-50 (0.745 vs 0.700, p = 5e-4) and slightly ahead on EN-300 (n.s.). Jev is ahead on the six held-out JevBench sources, on knowledge questions (mmlu 0.923 vs 0.729) and on LegalBench rule application (0.924 vs 0.832).
|
| 395 |
+
- **KVKK thresholds are noisy.** The gold sets are tiny (9, 10, 12 items) and a few items sit near p = 0.5. Do not threshold yes/no probabilities near 0.5 without your own validation set.
|
| 396 |
+
- **go_emotions, hate speech, star ratings:** on full JevBench the model scores 0.569 on go_emotions and 0.631 on measuring_hate_speech. The boundary between offensive language and group-targeted hate is the weakest point, together with 5-level star ratings.
|
| 397 |
+
- **Mostly LLM labels.** Much of the training data is LLM-labelled, so the model inherits those judgements. tr_insan is the only human-labelled Turkish test.
|
| 398 |
+
- **Knowledge questions** come from the backbone. The model is meant to judge a given text, not to recall facts.
|
| 399 |
+
- **Calibration:** no temperature or recipe is applied (macro ECE 0.084 on full JevBench). Fit a temperature on your own validation data if you need calibrated probabilities.
|
| 400 |
+
- Prompts longer than 4,096 tokens are truncated (text only); training used 1,536 tokens. Questions with more than 62 options use an elimination step (two or more passes).
|
| 401 |
|
| 402 |
+
<a id="honesty-notes"></a>
|
| 403 |
## Honesty notes
|
| 404 |
|
| 405 |
+
- **Test sets were used for selection.** We compared training runs on tr_insan, practical TR, EN-300, TR-50, the LegalBench rule test, the balanced "none of the above" test and KVKK. The acceptance rule for this run was set on them too: tr_insan ≥ 0.74, EN-300 ≥ 0.743, balanced "none of the above" ≥ 0.62 overall and ≥ 0.50 "none" found, KVKK errors ≤ 12; the run passed 5/5. None of these numbers is a fully untouched estimate. The full JevBench run was made after selection.
|
| 406 |
+
- **In-distribution parts.** The train splits of 16 JevBench configs and of the five tr_insan sources are in training (test texts excluded). Only the six held-out JevBench sources, the LegalBench rule test and the behaviour tests are unseen tasks.
|
| 407 |
+
- **Single runs.** Each number comes from one training run. Differences below about 0.02 on TR-50 and 0.01 elsewhere are within run-to-run noise.
|
| 408 |
+
- **Earlier runs.** Two earlier clean runs (temiz3 / temiz4) scored 0.753 / 0.740 on tr_insan and 0.764 / 0.767 on EN-300. They were rejected because of the shortcuts described above.
|
| 409 |
+
- **Competitors.** Microsoft-Decision-1 and Jev were run through their APIs; whether they were trained on JevBench is unknown. Intern-Decision-4B was run locally with its own prompt format and calibration temperature.
|
| 410 |
|
| 411 |
## License
|
| 412 |
|
|
|
|
| 415 |
<a id="ai-assistance"></a>
|
| 416 |
## AI assistance
|
| 417 |
|
| 418 |
+
This model card, its figures, `karar.py`, `birlestir.py`, the tests and most of the training and evaluation code were written with Claude coding agents (Claude Code, Anthropic) under the author's direction. The author is responsible for the content and verified the reported numbers.
|
| 419 |
|
| 420 |
## Citation
|
| 421 |
|
| 422 |
```bibtex
|
| 423 |
+
@misc{turnike_4b_2026,
|
| 424 |
+
title = "Turnike-4B: a bilingual (English + Turkish) decision model on Qwen3.5-4B",
|
| 425 |
author = "Hasret, Berkay",
|
| 426 |
year = "2026",
|
| 427 |
+
howpublished = "https://huggingface.co/berkay123001/turnike-4b"
|
| 428 |
}
|
| 429 |
```
|
| 430 |
|
| 431 |
## Türkçe özet
|
| 432 |
|
| 433 |
+
Turnike-4B, İngilizce ve Türkçe için Qwen3.5-4B üzerine kurulu, yaklaşık 130 MB'lık bir LoRA adaptörüdür. Metin üretmez: bir metin ve bir ya da birkaç kapalı soru verildiğinde her seçeneğe bir olasılık döndürür. Bir metnin 16 sorusuna kadar tek ileri geçişte cevap verir. RTX 5060 Ti'de tek karar bf16 ≈ 109 ms, FP8 ≈ 82 ms; 8 soru bf16 ≈ 156 ms, FP8 ≈ 126 ms sürer. Her sorunun seçenekleri tek-token sembollere (A, B, C, ...) eşlenir. Olasılık, her yer tutucudan hemen önceki konumda yalnız o sorunun sembol satırlarından okunur.
|
| 434 |
|
| 435 |
Herkese açık Türkçe ve İngilizce veri setleri, LLM ile etiketlenmiş açık metinler ve sentetik veriyle eğitildi; veri setleri yayınlanmıyor. Herhangi bir ticari karar API'sinden damıtılmadı. Bu sürümde web verisi yok; tarayıcı sürümü ayrı yayınlanacak.
|
| 436 |
|
| 437 |
+
Tam JevBench'te (22.773 satır, resmi `jevify-run report`, 22 config eşit ağırlıklı makro) Turnike-4B 0,774 alır: makro ECE 0,084, held-6 0,830, diğer-16 0,752. Aynı sette Jev 1.13.0 0,733 alır. Herkese açık jev-bench liderlik tablosunda (46 satır) 0,774, en üstteki girdinin (35B MoE, 0,770) önünde birinci sıraya denk gelirdi; fark küçük ve sonuç tabloya gönderilmedi. İnsan etiketli Türkçe testte (tr_insan, 1.379 karar) Turnike-4B 0,748, Microsoft-Decision-1 0,714 (p = 0,003), Jev 0,727, Intern-Decision-4B 0,675 alır. Çeviri JevBench TR-50'de Microsoft-Decision-1 önde: 0,745'e 0,700. Gömülü talimata kanma −0,014 (Intern +0,279). Dengeli "hiçbiri" testinde Turnike-4B 0,742–0,768. Bu test setlerinin hepsi koşular arasında seçim yaparken kullanıldı; tamamen bağımsız tahmin değiller.
|
| 438 |
|
| 439 |
+
Sınırlamalar: "Yukarıdakilerden hiçbiri" biraz fazla kullanılıyor; doğru şık varken de 0,22–0,25 oranında seçiliyor. KVKK eşikleri gürültülü (2 hata, ama tohumlar arasında 11 ile 16 arasında oynuyor). go_emotions, nefret/saldırgan dil sınırı ve yıldız puanı zayıf. Etiketlerin çoğu LLM'den geliyor.
|
| 440 |
|
| 441 |
+
Lisans: taban model Apache-2.0; adaptör, birleştirilmiş ağırlıklar ve kod da Apache-2.0. Kullanım için `karar.py` içindeki `Karar(...).sor(metin, sorular)` yeterli. Bu kart ve çıkarım kodu, yazarın yönlendirmesiyle Claude (Anthropic) kodlama ajanları tarafından yazıldı; sayılar yazar tarafından deney kayıtlarıyla doğrulandı.
|
assets/arch.png
ADDED
|
Git LFS Details
|
assets/banner_turnike.png
ADDED
|
Git LFS Details
|
assets/fig_compare.png
ADDED
|
Git LFS Details
|
assets/fig_jevbench_delta.png
ADDED
|
Git LFS Details
|
assets/fig_speed.png
ADDED
|
Git LFS Details
|
birlestir.py
CHANGED
|
@@ -28,8 +28,10 @@ DOGRULAMA_SORULARI = {"iade": {"type": "noul", "instructions": "Yazar para iades
|
|
| 28 |
DOGRULAMA_TOLERANS = 1e-3
|
| 29 |
EK_DOSYALAR = ("LICENSE",)
|
| 30 |
ISLEMCI_DOSYALARI = ("preprocessor_config.json", "video_preprocessor_config.json")
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
| 33 |
BIRLESIK_KART = """---
|
| 34 |
license: apache-2.0
|
| 35 |
base_model: Qwen/Qwen3.5-4B
|
|
@@ -43,7 +45,7 @@ tags:
|
|
| 43 |
- turkish
|
| 44 |
---
|
| 45 |
|
| 46 |
-
#
|
| 47 |
|
| 48 |
The LoRA adapter [{ana}](https://huggingface.co/{ana}) merged into Qwen/Qwen3.5-4B (bf16, same merge that
|
| 49 |
`karar.py` performs at load time). Use it without PEFT:
|
|
@@ -85,7 +87,8 @@ def birlestir(adapter: str, taban: str, cikti: Path) -> None:
|
|
| 85 |
for ad in EK_DOSYALAR:
|
| 86 |
if (burasi / ad).exists():
|
| 87 |
shutil.copy2(burasi / ad, cikti / ad)
|
| 88 |
-
|
|
|
|
| 89 |
print(f"yazildi: {cikti} ({sum(f.stat().st_size for f in cikti.iterdir()) / 1e9:.2f} GB)")
|
| 90 |
|
| 91 |
|
|
|
|
| 28 |
DOGRULAMA_TOLERANS = 1e-3
|
| 29 |
EK_DOSYALAR = ("LICENSE",)
|
| 30 |
ISLEMCI_DOSYALARI = ("preprocessor_config.json", "video_preprocessor_config.json")
|
| 31 |
+
# Ad ve repo kimlikleri isim.txt'den isim_uygula.py ile yazilir (elle degistirmeyin).
|
| 32 |
+
MODEL_ADI = "Turnike-4B"
|
| 33 |
+
ANA_REPO = "berkay123001/turnike-4b"
|
| 34 |
+
BIRLESIK_REPO = "berkay123001/turnike-4b-merged"
|
| 35 |
BIRLESIK_KART = """---
|
| 36 |
license: apache-2.0
|
| 37 |
base_model: Qwen/Qwen3.5-4B
|
|
|
|
| 45 |
- turkish
|
| 46 |
---
|
| 47 |
|
| 48 |
+
# {ad} (merged bf16 weights)
|
| 49 |
|
| 50 |
The LoRA adapter [{ana}](https://huggingface.co/{ana}) merged into Qwen/Qwen3.5-4B (bf16, same merge that
|
| 51 |
`karar.py` performs at load time). Use it without PEFT:
|
|
|
|
| 87 |
for ad in EK_DOSYALAR:
|
| 88 |
if (burasi / ad).exists():
|
| 89 |
shutil.copy2(burasi / ad, cikti / ad)
|
| 90 |
+
kart = BIRLESIK_KART.format(ad=MODEL_ADI, ana=ANA_REPO, birlesik=BIRLESIK_REPO)
|
| 91 |
+
(cikti / "README.md").write_text(kart, encoding="utf-8")
|
| 92 |
print(f"yazildi: {cikti} ({sum(f.stat().st_size for f in cikti.iterdir()) / 1e9:.2f} GB)")
|
| 93 |
|
| 94 |
|
karar.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""
|
| 2 |
|
| 3 |
Model metin uretmez. Metin ("State") ve sorular ("Decision schema") tek bir sohbet istemine yazilir; asistanin
|
| 4 |
cevabi bir JSON iskeletidir, her soru icin bir yer tutucu. Her sorunun siklari tek-token sembollere eslenir
|
|
@@ -14,7 +14,7 @@ bunlari kullanir (opsiyonel hizlandirici; yoksa saf torch yolu, ayni sonuc, daha
|
|
| 14 |
|
| 15 |
Kullanim:
|
| 16 |
from karar import Karar
|
| 17 |
-
k = Karar("
|
| 18 |
k.sor("Kulaklık bozuk geldi, paramı geri istiyorum.",
|
| 19 |
{"Yazar para iadesi istiyor mu?": ["evet", "hayır"],
|
| 20 |
"Mesajın tonu nedir?": {"olumlu": "memnun", "olumsuz": "şikâyetçi", "nötr": "duygu yok"}})
|
|
@@ -266,17 +266,33 @@ def _adaptor_yolu(adapter: str | Path) -> str:
|
|
| 266 |
return str(yol)
|
| 267 |
|
| 268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
class Karar:
|
| 270 |
-
"""Qwen3.5
|
| 271 |
|
| 272 |
-
def __init__(self, adapter: str | Path | None, taban: str =
|
| 273 |
fp8: bool = False, maks_istem_token: int = MAKS_ISTEM_TOKEN) -> None:
|
| 274 |
-
"""adapter=None: taban zaten birlestirilmis agirliklardir (birlestir.py ciktisi ya da onun Hub reposu).
|
|
|
|
| 275 |
from transformers import AutoTokenizer, Qwen3_5ForConditionalGeneration
|
| 276 |
|
| 277 |
if not isinstance(maks_istem_token, int) or maks_istem_token < 256:
|
| 278 |
raise ValueError("maks_istem_token en az 256 olan bir int olmali")
|
| 279 |
adapter = None if adapter is None else _adaptor_yolu(adapter)
|
|
|
|
|
|
|
| 280 |
if adapter is None and taban == VARSAYILAN_TABAN:
|
| 281 |
warnings.warn("adaptor yok ve taban egitimsiz Qwen3.5-4B: sifir-atis calisir (karar modeli degil).",
|
| 282 |
stacklevel=2)
|
|
@@ -284,7 +300,7 @@ class Karar:
|
|
| 284 |
if fp8 and self.device.type != "cuda":
|
| 285 |
raise RuntimeError("fp8=True yalniz CUDA'da calisir (FP8 matris carpimi, sm_89+)")
|
| 286 |
if self.device.type == "cpu":
|
| 287 |
-
warnings.warn("CUDA yok:
|
| 288 |
try:
|
| 289 |
self.tok = AutoTokenizer.from_pretrained(taban)
|
| 290 |
# CPU'ya yuklenir, LoRA CPU'da birlestirilir, sonra cihaza tasinir (olcum koduyla ayni yol).
|
|
@@ -380,7 +396,8 @@ def arguman_ayristirici() -> argparse.ArgumentParser:
|
|
| 380 |
p = argparse.ArgumentParser(description="Metin + sorular -> secenek olasiliklari (tek geciste).")
|
| 381 |
p.add_argument("--adapter", default=None,
|
| 382 |
help="adaptor dizini ya da Hub kimligi (verilmezse --taban birlestirilmis agirlik sayilir)")
|
| 383 |
-
p.add_argument("--taban", default=
|
|
|
|
| 384 |
p.add_argument("--metin", required=True, help="karar verilecek metin")
|
| 385 |
p.add_argument("--soru", action="append", required=True, type=soru_ayristir,
|
| 386 |
help=f"'Soru?{CLI_SORU_AYRACI}secenek1{CLI_SORU_AYRACI}secenek2...' (tekrarlanabilir)")
|
|
|
|
| 1 |
+
"""Turnike-4B tek dosyalik cikarim modulu: Qwen3.5 (0.8B / 4B / 9B) + LoRA adaptoru ile tek geciste cok soruluk karar.
|
| 2 |
|
| 3 |
Model metin uretmez. Metin ("State") ve sorular ("Decision schema") tek bir sohbet istemine yazilir; asistanin
|
| 4 |
cevabi bir JSON iskeletidir, her soru icin bir yer tutucu. Her sorunun siklari tek-token sembollere eslenir
|
|
|
|
| 14 |
|
| 15 |
Kullanim:
|
| 16 |
from karar import Karar
|
| 17 |
+
k = Karar("berkay123001/turnike-4b") # ya da yerel adaptor dizini
|
| 18 |
k.sor("Kulaklık bozuk geldi, paramı geri istiyorum.",
|
| 19 |
{"Yazar para iadesi istiyor mu?": ["evet", "hayır"],
|
| 20 |
"Mesajın tonu nedir?": {"olumlu": "memnun", "olumsuz": "şikâyetçi", "nötr": "duygu yok"}})
|
|
|
|
| 266 |
return str(yol)
|
| 267 |
|
| 268 |
|
| 269 |
+
def _adaptor_tabani(adapter: str) -> str:
|
| 270 |
+
"""Adaptorun adapter_config.json'undaki base_model_name_or_path (yerel dizin ya da Hub kimligi; peft okur)."""
|
| 271 |
+
from peft import PeftConfig
|
| 272 |
+
|
| 273 |
+
try:
|
| 274 |
+
taban = PeftConfig.from_pretrained(adapter).base_model_name_or_path
|
| 275 |
+
except (OSError, ValueError) as hata:
|
| 276 |
+
raise FileNotFoundError(f"adaptorun adapter_config.json dosyasi okunamadi: '{adapter}' ({hata})") from hata
|
| 277 |
+
if not taban:
|
| 278 |
+
raise FileNotFoundError(f"adapter_config.json icinde base_model_name_or_path yok: {adapter}")
|
| 279 |
+
return taban
|
| 280 |
+
|
| 281 |
+
|
| 282 |
class Karar:
|
| 283 |
+
"""Qwen3.5 (0.8B / 4B / 9B) + LoRA karar katmani. Bir ornek bircok metinde kullanilabilir."""
|
| 284 |
|
| 285 |
+
def __init__(self, adapter: str | Path | None, taban: str | None = None, device: str | None = None,
|
| 286 |
fp8: bool = False, maks_istem_token: int = MAKS_ISTEM_TOKEN) -> None:
|
| 287 |
+
"""adapter=None: taban zaten birlestirilmis agirliklardir (birlestir.py ciktisi ya da onun Hub reposu).
|
| 288 |
+
taban=None: adaptor varsa adapter_config.json'daki taban (0.8B / 4B / 9B dogru eslesir), yoksa Qwen3.5-4B."""
|
| 289 |
from transformers import AutoTokenizer, Qwen3_5ForConditionalGeneration
|
| 290 |
|
| 291 |
if not isinstance(maks_istem_token, int) or maks_istem_token < 256:
|
| 292 |
raise ValueError("maks_istem_token en az 256 olan bir int olmali")
|
| 293 |
adapter = None if adapter is None else _adaptor_yolu(adapter)
|
| 294 |
+
if taban is None:
|
| 295 |
+
taban = _adaptor_tabani(adapter) if adapter is not None else VARSAYILAN_TABAN
|
| 296 |
if adapter is None and taban == VARSAYILAN_TABAN:
|
| 297 |
warnings.warn("adaptor yok ve taban egitimsiz Qwen3.5-4B: sifir-atis calisir (karar modeli degil).",
|
| 298 |
stacklevel=2)
|
|
|
|
| 300 |
if fp8 and self.device.type != "cuda":
|
| 301 |
raise RuntimeError("fp8=True yalniz CUDA'da calisir (FP8 matris carpimi, sm_89+)")
|
| 302 |
if self.device.type == "cpu":
|
| 303 |
+
warnings.warn("CUDA yok: model CPU'da bf16 calisir ama cok yavastir.", stacklevel=2)
|
| 304 |
try:
|
| 305 |
self.tok = AutoTokenizer.from_pretrained(taban)
|
| 306 |
# CPU'ya yuklenir, LoRA CPU'da birlestirilir, sonra cihaza tasinir (olcum koduyla ayni yol).
|
|
|
|
| 396 |
p = argparse.ArgumentParser(description="Metin + sorular -> secenek olasiliklari (tek geciste).")
|
| 397 |
p.add_argument("--adapter", default=None,
|
| 398 |
help="adaptor dizini ya da Hub kimligi (verilmezse --taban birlestirilmis agirlik sayilir)")
|
| 399 |
+
p.add_argument("--taban", default=None,
|
| 400 |
+
help="taban model (Hub kimligi ya da yerel yol); verilmezse adaptorun adapter_config.json tabani")
|
| 401 |
p.add_argument("--metin", required=True, help="karar verilecek metin")
|
| 402 |
p.add_argument("--soru", action="append", required=True, type=soru_ayristir,
|
| 403 |
help=f"'Soru?{CLI_SORU_AYRACI}secenek1{CLI_SORU_AYRACI}secenek2...' (tekrarlanabilir)")
|