Metadata-Version: 2.4
Name: turkmorfbench
Version: 3.4.0
Summary: TurkMorfBench — Turkish morphology benchmark for language models / Türkçe morfoloji kıyası
Author-email: "eCloud Tech." <info@e-cloud.web.tr>
Maintainer-email: "eCloud Tech." <info@e-cloud.web.tr>
License: Apache-2.0
Project-URL: Homepage, https://github.com/ecloudtechnology/turkmorfbench
Project-URL: Repository, https://github.com/ecloudtechnology/turkmorfbench
Project-URL: Dataset, https://huggingface.co/datasets/ecloudtech/TurkMorfBench
Keywords: turkish,türkçe,morphology,morfoloji,benchmark,evaluation,llm,vowel-harmony,wug-test
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Natural Language :: Turkish
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# TurkMorfBench

**Turkish morphology benchmark for language models** — 465,241 items, diagnostic
reports, wug-test controls, tokenizer analysis.
**Dil modellerinin Türkçe morfoloji yetkinliğini ölçen kıyas** — 465.241 madde,
teşhis raporu, uydurma gövde kontrolü, tokenizer çözümlemesi.

Built by [eCloud Tech.](https://www.e-cloud.web.tr) · code Apache-2.0 · data CC BY 4.0

```bash
pip install turkmorfbench

turkmorfbench olc --model ecloudtech/Erk-32B
turkmorfbench olc --uc http://localhost:8000/v1 --model my-model --isci 16
```

---

## What it tells you / Ne söylüyor

Not a score. A diagnosis. / Puan değil, teşhis.

Real output from a 260-item run (forced choice, one open model served over an
OpenAI-compatible endpoint):

```
ZORUNLU SEÇİM   161 / 260   %61.9

GÖVDE TÜRÜ
  gerçek gövde (real stems)          124/179   %69.3
  uydurma gövde (nonce/wug stems)     37/81    %45.7

KOVA (bucket)
  istisna_uyum_kirici                  0/12    %0.0     harmony-breaking loans
  ek_zinciri                           4/15    %26.7    suffix chains
  cati                                 5/11    %45.5    voice
  ad_cekimi                           33/48    %68.8    nominal case
  fiil_cekimi                         10/10    %100.0   verb inflection

SON SES (stem-final sound)
  t    6/18   %33.3                                     the lenition consonants
  ç    4/10   %40.0
  p    6/12   %50.0
  ünlü (vowel)  28/37  %75.7

HANGİ KURAL BİLİNMİYOR (which rule was violated)
  uyum (vowel harmony)          28   %28.3
  iyelik_unlu (buffer vowel)    22   %22.2
  zamir_n (pronominal n)        10   %10.1
```

When run against local weights, the report adds a **tokenizer breakdown**:
accuracy split by whether the vocabulary kept the stem intact or fragmented it,
and the measured cost of fragmentation in points. That block answers the question
model developers actually ask — *does my vocabulary break Turkish morphology?* —
and no other Turkish benchmark reports accuracy conditioned on tokenization.

---

## Türkçe

### Ne ölçüyor

Türkçe sondan eklemeli bir dildir ve ek seçimi katı kurallara bağlıdır: büyük ve
küçük ünlü uyumu, ünsüz benzeşmesi, ünsüz yumuşaması, kaynaştırma. Üstüne
sözlüksel istisnalar biner: `burun → burnu`, `ret → reddi`, `kalp → kalbi`,
`buzdolabı → buzdolabına`. Bir de okunuşa bağlı ekler: `TCDD'yi`, `2026'da`.

TurkMorfBench bunların hepsini ayrı ayrı ölçer ve **hangisinde düştüğünü söyler.**

| Kova | Anahtar | Madde | Ne sınıyor |
|---|---|---|---|
| ad paradigma yuvaları | `ad_yuva` | 283.360 | çokluk + iyelik + hâl yığını, **zamir n'si** |
| ad çekimi | `ad_cekimi` | 166.307 | 7 hâl, ünlü uyumu, benzeşme, yumuşama, kaynaştırma |
| ek zinciri | `ek_zinciri` | 8.351 | 1'den 8'e derinlik, ek sırası |
| fiil çekimi | `fiil_cekimi` | 1.792 | 7 zaman/kip, olumsuzluk, **geniş zaman istisnaları** |
| yapım eki | `yapim_eki` | 1.477 | -lIk, -CI, -lI, -sIz, -sAl, -lAş, -lA |
| özel ad | `istisna_ozel_ad` | 1.200 | kesme işareti, **yumuşamama** (Sinop'a, Sinob'a değil) |
| birleşik isim | `istisna_birlesik_isim` | 896 | buzdolabına |
| çatı | `cati` | 728 | edilgen, dönüşlü, işteş, ettirgen |
| sayı | `istisna_sayi` | 507 | **okunuşa göre** ek (2026'da) |
| kısaltma | `istisna_kisaltma` | 304 | **okunuşa göre** ek (TCDD'yi) |
| ünlü düşmesi | `istisna_unlu_dusmesi` | 177 | burnu, aklı, nakdi · **TDK doğrulamalı** |
| uyum kırıcı alıntı | `istisna_uyum_kirici` | 105 | kalbi, rolü, kıraati · **TDK doğrulamalı** |
| ünsüz ikizleşmesi | `istisna_ikizlesme` | 35 | reddi, tıbbı, zıddı · **TDK doğrulamalı** |
| kaynaştırma istisnası | `istisna_kaynastirma_istisna` | 2 | suyu, neyi |
| **toplam** | | **465.241** | 14 kova |

### Neden uydurma (wug) gövde

Gerçek kelimede doğru ek üretmek ezberle de mümkündür: model *kitabı* biçimini
külliyatta on binlerce kez görmüştür. Uydurma gövde (*zakak*, *vısep*, *sövot*)
hiçbir külliyatta geçmez; model doğru eki ancak **kuralı** biliyorsa üretir.

Ölçtük: modeller gerçek gövdeyle uydurma gövde arasında 22-32 puan fark veriyor.
Yani büyük ölçüde kuralı değil komşuluğu kullanıyorlar.

### Ses ortamı kapsam dizeyi

Ek seçimini belirleyen eksenler **önce** çaprazlanır, gövdeler sonra o hücreleri
doldurmak için üretilir: son ünlü (8) × son ses sınıfı (8) × hece sayısı (3) ×
iç uyum (2) = **320 hücre**.

Bu kümenin **33 hücresinde Türkçede gerçek kelime yoktur.** Gerçek kelimelerden
kurulan bir kıyas oraları sınayamaz. Ölçtük: modeller tam o hücrelerde, ses
bileşimi eşleştirildikten sonra bile, 22-23 puan daha kötü.

### İki katman

| Katman | Madde | Kimin için |
|---|---|---|
| `cekirdek` | 1.856 | dakikalar içinde koşar, kova dengeli, **teşhis için** |
| `tam` | 465.241 | tasarlanan kapsamın tamamı, **manşet sayı için** |

Çekirdek kova dengelidir, yani tam kümenin yansız tahmini **değildir**; bilerek.
En küçük kovada bile ölçülebilir bir sayı çıksın diye. İkisi karşılaştırılmaz.

### Altın veri nereden gelir

Kural motorundan. **Hiçbir aşamada bir dil modelinden gelmez.**

Açık Türkçe morfolojik çözümleyicilerin doğruluğu %38-72 aralığında ölçülmüştür;
böyle bir aracı altın kaynak yapmak külliyata %28-62 hata enjekte eder. Bu yüzden
keyfi kelimeyi çözümlemiyoruz — altını inşadan belli olan maddeyi üretiyoruz.
Sözlüksel düzensizlikler (hangi gövde yumuşar, hangisinde ünlü düşer)
[Zemberek](https://github.com/ahmetaa/zemberek-nlp) sözlüğünden gelir (Apache-2.0);
çözümleyicisi kullanılmaz.

Kural motorunun 282 elle yazılmış altın iddiası vardır ve hepsi geçer.

### Üç istisna kovası TDK ile doğrulanmıştır

`ünlü düşmesi`, `uyum kırıcı alıntı` ve `ünsüz ikizleşmesi` kovalarının
**317 maddesinin tamamının** altını [TDK Güncel Türkçe Sözlük](https://sozluk.gov.tr)
ile birebir karşılaştırılmıştır. Sözlüğün madde başından sonra verdiği çekim
ipucu (`kıraat, -ti`) altının kendisidir; TDK bir gövde için biçim vermiyorsa ya
da birden fazla veriyorsa (`hak` → *hakkı* / *hakki*) o gövde kıyasa alınmaz.

Bu denetimi yapma sebebimiz, insan tavanı ölçümünde katılımcıların `uyum kırıcı`
kovasında **şans düzeyinin altına** düşmesiydi. Ana dili Türkçe olan insanlar bir
kurala şanstan kötü uyuyorsa, sorun insanda değil altındadır. Üç sistematik hata
çıktı — olmayan yumuşamanın uygulanması (*bidadi*, doğrusu **bidati**),
ince/kalın uyumun kaçırılması (*aczı*, doğrusu **aczi**), ikizleşirken
ötümlüleşmenin atlanması (*retti*, doğrusu **reddi**) — hepsi sözlük
bayraklarının eksikliğinden. Düzeltmeden sonra ölçülen insan doğruluğu
5,6 puan yükseldi.

### İnsan tavanı — ikinci ölçüm

**%73,0 [%64,7 – %80,6]** · 5 geçerli değerlendirici, 437 yargı · *toplama sürüyor*

Aynı maddeler ana dili Türkçe olan kişilere soruluyor
([morf.e-cloud.web.tr](https://morf.e-cloud.web.tr)). Bu hâlâ bir **ön
okumadır**: örneklem küçük, aralık geniş ve sayı katılımcı geldikçe
güncellenecek. Aralık **fonolojik hücre** kümeli önyüklemeyle verilmiştir;
madde düzeyinde hesaplansa %68,5–%77,3 çıkardı, çünkü aynı hücreye bağlı
maddeler bağımsız gözlem değildir.

**Önceki sürümde bildirdiğimiz %88,2 [83,1–91,8] geri çekilmiştir.** İki sebebi
var ve ikisi de bizim tarafımızda: (1) o sayı 3 değerlendiriciye dayanıyordu;
beşe çıkınca ortalama düştü — geçerli kişilerin tek tek doğrulukları %85,1,
%84,5, %75,0, %68,6 ve %53,2'dir, yani "tavan" tek bir sayıdan çok bir yelpaze.
(2) o ölçümün yapıldığı madde havuzunun 520 maddesinden 257'si güncel kıyas
verisinde yoktu — havuz bir önceki üretimden alınmıştı. Havuz 24 Eylül 2026'da
güncel veriden yeniden kuruldu; artık her madde kıyasta karşılığı olan bir
maddedir ve gövde/hücre kümelemesi yapılabilmektedir.

| kesit | doğruluk | n |
|---|---|---|
| gerçek gövde | %74,6 [%70,0 – %78,8] | 417 |
| uydurma gövde | %40,0 [%16,7 – %61,1] | 20 |

**Uydurma gövdedeki insan performansı hakkında henüz bir şey söylemiyoruz.**
Önceki sürümde bildirdiğimiz "insan düşüşü 12 puan" iddiası da geri
çekilmiştir: o sayı da, buradaki 34,6 puanlık nokta tahmini de onlarca yargıya
dayanıyor ve aralık her iki yönde de sonuca izin vermiyor. Uydurma gövde
karşılaştırması ancak ikisinin de bulunduğu kovalarda (`ad_cekimi`, `ad_yuva`,
`ek_zinciri`) eşleştirilmiş olarak anlamlıdır; havuz bu amaçla her birinde 20
gerçek + 20 uydurma olacak şekilde dengelendi, ama yeterli yargı henüz
birikmedi.

**Katılımcı taraması — ölçümü koruyan asıl savunma.** Doğruluk sayısı elenmeden
hesaplanmaz. Üç ölçüt önceden yazılır ve sonuca bakılarak değiştirilmez: en az
10 cevap; soru başına medyan süre 3 saniyenin altındaysa kişi soruyu
okumuyordur; doğruluğu kendi şans düzeyini (gördüğü maddelerin şık sayılarına
göre hesaplanır) tek yönlü binom testiyle α = 0,001'de geçemeyen kişi bilgi
taşımıyordur.

Bu tarama boşuna değil: **11 katılımcının 6'sı elendi**, beşi hız ölçütünden.
Bir katılımcı 111 soruyu **36 saniyede** cevaplamıştı — soru başına 0,3 saniye.
Hızdan elenen beş kişinin beşi aynı zamanda şans düzeyindeydi; iki bağımsız
ölçüt aynı kişileri gösteriyor. Süre hesabında sıfır saniyelik aralıklar
atılmaz: zaman damgaları saniye çözünürlüğünde olduğu için hızlı tıklamanın
kanıtı tam da o sıfırlardır.

Yayımlanan sayıların tamamı `insan_ozet.py` tarafından üretilir; eşikler
betiğin içinde yazılıdır.

## Bağımsız dış sınav: UD ağaç bankaları

Kural motorunu kendi testleriyle sınamak döngüseldir — 282 altın iddiayı da biz
yazdık. Motor ayrıca **Universal Dependencies Türkçe ağaç bankalarına** karşı
koşuluyor (BOUN, IMST, Penn): başka ekiplerin elle etiketlediği, hakemli,
bizim hiçbir şekilde etkilemediğimiz veri.

UD her kelimenin yüzey biçimini, sözlük biçimini ve biçimbirim özniteliklerini
verir (`kitabı / kitap / Case=Acc|Number=Sing`); bu, bizim çağrımızın tam
karşılığıdır. Üç bankanın da aynı etiketlediği **25.681 (gövde, yuva)** çifti
karşılaştırıldı:

| ölçü | oran |
|---|---|
| ham uyum (UD metnini birebir tutturma) | **%91,5** |
| çekim uyumu (kesme/düzeltme imi normalleşmiş) | **%93,9** |

Aradaki fark yazımdandır: UD özel adlarda kesme işareti kullanıyor (`bey'in`),
bazı bölümleri Türkçe harfsiz (`baliklarinin`), düzeltme imi bankadan bankaya
değişiyor. İkisi ayrı raporlanır; tek bir sayıya indirmek farkı gizler.

**Bu sınav dört gerçek motor hatası buldu ve hepsi düzeltildi:**

| hata | UD'de | bizde |
|---|---|---|
| çokluk + 3. çoğul iyelik `-lAr`ı iki kez yazıyordu | gözlerini | ~~gözlerlerini~~ |
| vasıta hâli iyelikten sonra zamir n'si alıyordu | hedefiyle | ~~hedefinle~~ |
| `su`/`ne` kaynaştırmada y yerine s alıyordu | suyunu | ~~susunu~~ |
| düzeltme imli ünlüler (â î û) envanterde yoktu | rüzgâra | ~~rüzgâre~~ |

Sonuncusu en genişi: `â` görünmez olduğu için `rüzgâr`ın son ünlüsü `ü`
sanılıyor ve ince ek geliyordu. Düzeltmeden sonra UD uyumu 3,5 puan yükseldi.


### Şıklar karıştırılmıştır

`secenek` listesinde altın rastgele bir konumda durur ve `altin_sira` kaçıncı
olduğunu söyler. Karıştırma madde kimliğinden türetilir: aynı madde her
üretimde aynı sırayı alır, sürümler arası karşılaştırma bozulmaz.

Bu 3.2.1'den önce böyle değildi — altın her maddede birinci şıktı. Zorunlu
seçim kipinde şıklar tek tek puanlandığı için sıra sonucu etkilemiyordu ve
gözden kaçmıştı; ama şıkları A/B/C diye harflendiren bir protokolde hep "A"
demek %100 verirdi. İç tutarlılık denetimiyle yakalandı.

Aynı denetim ikinci bir kusur daha buldu: uydurma gövde üretici yalnız iki
sözlük dosyasına bakıyordu, eskimiş ve gayriresmî sözlüklerdeki yirmi gövde
"uydurma" sayılıp kıyasa girmişti. Wug testinin tek işi gövdenin hiçbir
külliyatta geçmemesini garanti etmek; denetim artık bütün sözlüklere bakıyor.


### Puanlama kuralı (3.3.0'da değişti)

Aday, `log P(aday | istem)` toplamının **adayın karakter sayısına** bölünmesiyle
puanlanır. Ortak istem öneki puana girmez.

Önceki sürümler istem ve adayın birlikte oluşturduğu dizginin ortalama
log-olasılığını alıyordu. Önek her adayda aynı ama jeton sayıları farklı;
ortalama alınınca o sabit önek adaylar arasında farklı ağırlıklarla dağılıyor
ve gerçek fark eziliyordu. Bir maddede aday toplamları −17,2 ile −25,6 arasında
ayrışırken eski kural hepsini −5,1 ile −5,4 arasına sıkıştırıp yanlış adayı
0,08 farkla seçiyordu.

Kural beş aday arasından **doğruluğa göre değil yanlılığa göre** seçildi:
ölçeği ölçülen şeye göre ayarlamamak için. Çeldiricilerin bir kısmı altından
kısadır (eksik ek). Adayların uzunluğu farklı olan maddelerde altın %29,9
oranında en kısadır; yansız bir kural da o civarda en kısayı seçmeli:

| kural | en kısayı seçme (hedef %29,9) |
|---|---|
| tüm dizgi ortalaması *(eski)* | %47,2 · %47,7 |
| aday toplamı | %51,0 · %57,0 |
| jetona bölünmüş | %47,5 · %48,7 |
| **karaktere bölünmüş** | **%39,4 · %43,2** |
| koşulsuzla normalleştirilmiş | %39,2 · %45,0 |

İkinci ve bağımsız gerekçe: karakter sayısı **jetonlayıcıdan bağımsızdır.**
Farklı sözlüklü modeller karşılaştırılırken jeton sayısına bölen bir ölçü,
kelimeyi kaç parçaya böldüklerine göre modelleri farklı cezalandırır.

Kalan yanlılık gizlenmiyor: %39-43, hedef %29,9. Uzunluk etkisi azaldı ama
sıfırlanmadı.

**Uzak uç kullananlar için:** `/v1/completions` yanıtında `text_offset`
zorunludur; onsuz aday jetonları ayrılamaz. Uç bu alanı döndürmüyorsa ölçüm
sessizce farklı bir şey hesaplamak yerine durur.

### Kullanım

```bash
# yerel ağırlıklar
turkmorfbench olc --model ecloudtech/Erk-32B

# OpenAI uyumlu uç (vLLM, llama.cpp server, TGI…)
turkmorfbench olc --uc http://localhost:8000/v1 --model erk --isci 16

# tam katman, yalnız uydurma gövdeler, JSON rapor
turkmorfbench olc --model X --katman tam --govde uydurma --cikti rapor.json

# yalnız istisna kovaları
turkmorfbench olc --model X --kova istisna_unlu_dusmesi istisna_ikizlesme

turkmorfbench bilgi
```

İki kip vardır. **Zorunlu seçim** birincildir: altın biçim ile kural ihlali
çeldiricileri arasından log-olasılıkla seçtirilir, ayrıştırma yoktur. **Serbest
üretim** ikincildir ve "okunamadı" ile "yanlış" ayrı sayılır — bu ayrım olmadan
bir ölçümümüz elli puan yanılmıştı.

---

## English

### What it measures

Turkish is agglutinative and suffix selection follows strict rules: two-way and
four-way vowel harmony, consonant assimilation, consonant lenition, buffer
consonants. On top of these sit lexical exceptions (`burun → burnu`,
`kalp → kalbi`, `ret → reddi`) and pronunciation-driven suffixes
(`TCDD'yi`, `2026'da` — the suffix follows how the number is *read*).

TurkMorfBench measures each of these separately and tells you **which one fails**.

### Why nonce (wug) stems

Producing the right suffix on a real word can be memorisation — a model has seen
*kitabı* tens of thousands of times. A nonce stem (*zakak*, *vısep*) appears in no
corpus; the model can only inflect it from the **rule**. We measure a 22-32 point
gap between real and nonce stems: models are largely using lexical neighbourhood,
not rules.

### Designed coverage, not sampled coverage

The axes that determine suffix selection are crossed **first** — final vowel (8) ×
final-sound class (8) × syllable count (3) × internal harmony (2) = **320 cells** —
and stems are then generated to fill them. **33 of those cells contain no real
Turkish word at all**, so a benchmark built from real words is structurally blind
there. Models score 22-23 points worse in those cells even after matching
phonological composition.

### Gold data never comes from a language model

Published open Turkish morphological analysers measure 38-72% accuracy; using one
as ground truth would inject 28-62% error. Instead of *analysing* arbitrary words
we *generate* items whose gold form follows by construction. Lexical irregularity
flags come from the [Zemberek](https://github.com/ahmetaa/zemberek-nlp) dictionary
(Apache-2.0); its analyser is not used. The rule engine carries 282 hand-written
gold assertions, all passing.

### The exception buckets are verified against TDK

All 317 items in the vowel-drop, inverse-harmony and consonant-doubling buckets
carry a gold form checked character-for-character against the
[TDK Güncel Türkçe Sözlük](https://sozluk.gov.tr). We ran this audit because
native speakers scored **below chance** on the inverse-harmony bucket in the
human-ceiling study — when native speakers do worse than guessing, the gold is
what needs checking, not the speakers. It found three systematic errors inherited
from incomplete lexicon flags (*bidadi* → **bidati**, *aczı* → **aczi**,
*retti* → **reddi**). Measured human accuracy rose 5.6 points after the fix.

### Human ceiling — second measurement

**73.0% [64.7 – 80.6]** · 5 valid raters, 437 judgments · *collection ongoing*

The same items are put to native speakers at
[morf.e-cloud.web.tr](https://morf.e-cloud.web.tr). This is still a
**preliminary reading**: the sample is small, the interval wide, and the figure
will be updated as more raters finish. The interval comes from a
**phonological-cell cluster bootstrap**; computed per item it would read
68.5–77.3%, because items sharing a cell are not independent observations.

**The 88.2% [83.1–91.8] reported in an earlier release is withdrawn.** Two
reasons, both on our side: (1) that figure rested on 3 raters; with 5 the mean
fell — the individual accuracies of the valid raters are 85.1%, 84.5%, 75.0%,
68.6% and 53.2%, so the "ceiling" is a spread rather than a point. (2) 257 of
the 520 items in the pool used for that measurement did not exist in the current
benchmark data; the pool had been drawn from an earlier generation. It was
rebuilt from current data on 24 September 2026, so every item now corresponds to
a benchmark item and stem/cell clustering is possible.

| slice | accuracy | n |
|---|---|---|
| real stems | 74.6% [70.0 – 78.8] | 417 |
| nonce stems | 40.0% [16.7 – 61.1] | 20 |

**We make no claim about human performance on nonce stems yet.** The earlier
"12-point human drop" claim is also withdrawn: that figure, and the 34.6-point
point estimate here, both rest on a few dozen judgments and the interval permits
no conclusion in either direction. The real/nonce contrast is only meaningful as
a matched comparison inside the buckets that contain both (`ad_cekimi`,
`ad_yuva`, `ek_zinciri`); the pool was rebalanced to 20 real + 20 nonce in each,
but enough judgments have not yet accumulated.

**Rater screening — the defence that protects the measurement.** The accuracy
figure is not computed before screening. Three criteria are fixed in advance and
never adjusted after seeing the result: at least 10 answers; a median of under
3 seconds per question means the question was not read; and accuracy that fails
to beat the rater's own chance level (computed from the option counts of the
items they saw) on a one-sided binomial test at α = 0.001 carries no
information.

The screening is not decorative: **6 of 11 raters were removed**, five of them on
the speed criterion. One had answered 111 questions in **36 seconds** — 0.3 s per
question. All five speed-removed raters were also at chance level; two
independent criteria point at the same people. Zero-second gaps are not discarded
when computing the median: timestamps have one-second resolution, so those zeros
are precisely the evidence of click-through.

Every published figure is produced by `insan_ozet.py`; the thresholds are written
into the script.

### Confidence intervals: 465,241 items are NOT 465,241 independent observations

Items are not generated independently. One stem (*kitap*) yields dozens of items
across the plural × possessive × case cross; one phonological cell (final vowel,
final-consonant class, depth, harmony) binds hundreds of stems to the same rule.
A model that misses lenition on *kitap* misses it across most items derived from
that stem.

Item-level bootstrap ignores this: it treats each item as a separate observation,
overstates the sample, and returns intervals that are too narrow. That is false
precision, and it is the easiest place to attack a published figure.

`turkmorfbench.istatistik` moves the resampling unit from the item to the
**cluster**. In the full set:

| unit | clusters | items per cluster |
|---|---|---|
| stem | 32,140 | median 16 |
| **phonological cell** | **441** | median 247, largest 18,594 |

The difference is not cosmetic. Under a pattern where model competence varies at
the rule level — which is exactly what the benchmark sets out to measure — the
design effect we measured is:

| clustering | interval width | effective n |
|---|---|---|
| item | ±0.12 pt | 465,241 |
| stem | 2.0× | 118,274 |
| **phonological cell** | **30.8×** | **491** |

Those figures come from a simulation; the real design effect depends on how
correlated a real model's errors are within a cell. `tasarim_etkisi()` measures
and reports it for every run.

Comparing two models by checking whether two separate intervals overlap is wrong:
it throws away the paired design and can hide a real difference. `esli_fark()`
resamples the difference directly.

### Is the result an artefact of the scoring rule?

The scoring rule changed in 3.3.0 (see `olcum.py`) and the residual length bias
was not hidden: the gold is shortest in 29.9% of items with unequal candidate
lengths, while the chosen rule picks the shortest 39–43% of the time. That is not
the real question. The real question is whether the **result** changes when the
rule changes.

`turkmorfbench.duyarlilik` runs the model **once**, stores the raw sum, token
count, character count and unconditional probability per candidate, and derives
six rules from those three quantities: `ham` (raw), `jeton` (per token), `harf`
(per character — the 3.3.0 choice), `pmi`, `pmi_harf` and `esli` (pairwise
contrastive, Copeland). The model is not run six times.

The output states whether the **sign of the difference between two systems is the
same under all six rules**. If it is, the residual length bias does not carry the
finding; if it is not, we learn which finding depends on the rule before the
reader does.

### Independent external check: UD treebanks

Testing the rule engine against its own assertions is circular — we wrote those
too. The engine is therefore also run against the **Universal Dependencies
Turkish treebanks** (BOUN, IMST, Penn): hand-annotated, peer-reviewed data from
other teams that we had no hand in. On the 25,681 (lemma, slot) pairs all three
treebanks annotate identically, the engine reproduces the attested surface form
for **91.5%** verbatim and **93.9%** once spelling is normalised (UD uses
apostrophes on proper nouns, some sections are ASCII-folded, circumflex usage
differs between treebanks).

The check found four real engine bugs, all fixed: `-lAr` written twice in the
plural + 3rd-person-plural possessive slot (*gözlerlerini* → **gözlerini**), the
pronominal *n* wrongly inserted before the instrumental (*hedefinle* →
**hedefiyle**), the irregular buffer on `su`/`ne` (*susunu* → **suyunu**), and
circumflex vowels (â î û) missing from the vowel inventory, which made the
engine read the wrong vowel as final (*rüzgâre* → **rüzgâra**).

### Two tiers

`cekirdek` (1,856 items, bucket-balanced, runs in minutes, for diagnosis) and
`tam` (465,241 items, for the headline number). The core tier is deliberately
*not* an unbiased sample of the full set — it is balanced so that even the
smallest bucket yields a measurable estimate. Do not compare the two.

### Modes

Forced choice is primary: gold versus rule-violation distractors, scored by
log-likelihood, no output parsing. Free generation is secondary and reports
"unreadable" separately from "wrong" — without that distinction one of our own
measurements was off by fifty points.

---

## Citation

```bibtex
@misc{turkmorfbench2026,
  title  = {TurkMorfBench: A Diagnostic Morphology Benchmark for Turkish Language Models},
  author = {{eCloud Tech.}},
  year   = {2026},
  url    = {https://github.com/ecloudtechnology/turkmorfbench}
}
```

**Data:** [huggingface.co/datasets/ecloudtech/TurkMorfBench](https://huggingface.co/datasets/ecloudtech/TurkMorfBench)
**Code:** [github.com/ecloudtechnology/turkmorfbench](https://github.com/ecloudtechnology/turkmorfbench)

Keywords: Turkish NLP, Türkçe doğal dil işleme, morphology benchmark, morfoloji
kıyası, vowel harmony, ünlü uyumu, wug test, agglutinative languages, LLM
evaluation, dil modeli değerlendirme, tokenizer analysis, subword segmentation.

### Güven aralığı: 465.241 madde, 465.241 bağımsız gözlem DEĞİLDİR

Kıyastaki maddeler birbirinden bağımsız üretilmez. Tek bir gövde (*kitap*)
çokluk × iyelik × hâl çaprazından onlarca madde doğurur; tek bir fonolojik hücre
(son ünlü, son ünsüz sınıfı, derinlik, uyum) yüzlerce gövdeyi aynı kurala bağlar.
Bir model *kitap* gövdesinde yumuşamayı kaçırıyorsa, o gövdeden türeyen
maddelerin çoğunda birlikte kaçırır.

Madde düzeyinde klasik önyükleme bunu görmezden gelir: her maddeyi ayrı bir gözlem
sayar, örneklemi olduğundan büyük gösterir ve aralığı gereğinden daraltır. Bu,
sahte kesinliktir ve yayımlanan bir sayının en kolay çürütülen yeridir.

`turkmorfbench.istatistik` yeniden örnekleme birimini maddeden **kümeye** taşır.
Kümeler tam veri kümesinde şöyle dağılır:

| birim | küme sayısı | küme başına madde |
|---|---|---|
| gövde | 32.140 | medyan 16 |
| **fonolojik hücre** | **441** | medyan 247, en büyüğü 18.594 |

Fark önemsiz değil. Model yetkinliğinin kural düzeyinde değiştiği bir örüntüde —
ki kıyasın ölçmeye çalıştığı tam olarak budur — ölçtüğümüz tasarım etkisi şudur:

| kümeleme | aralık genişliği | etkin n |
|---|---|---|
| madde | ±0,12 puan | 465.241 |
| gövde | 2,0 kat | 118.274 |
| **fonolojik hücre** | **30,8 kat** | **491** |

Bu rakamlar bir benzetimden gelir; gerçek tasarım etkisi, gerçek bir modelin
hatalarının hücre içinde ne kadar ilintili olduğuna bağlıdır. `tasarim_etkisi()`
bunu her koşumda ölçer ve bildirir.

```python
from turkmorfbench.istatistik import kume_bootstrap, esli_fark, tasarim_etkisi
s = kume_bootstrap(maddeler, dogru, anahtar="hucre")     # gövde / hücre / gövde_görev
f = esli_fark(maddeler, a_dogru, b_dogru, anahtar="govde")   # iki sistemin FARKI
```

İki modeli karşılaştırırken iki ayrı aralığa bakıp "çakışıyor mu" demek yanlıştır:
eşli tasarımın gücünü atar ve gerçek farkı gizleyebilir. `esli_fark()` farkı
doğrudan örnekler.

### Sonuç puanlama kuralına bağlı mı

3.3.0'da puanlama kuralı değişti (bkz. `olcum.py`) ve kalan uzunluk yanlılığı
gizlenmedi: altın, uzunlukları farklı maddelerde %29,9 oranında en kısadır,
seçilen kural %39–43 oranında en kısayı seçer. Asıl soru bu değil — asıl soru,
kural değişince **sonucun** değişip değişmediğidir.

`turkmorfbench.duyarlilik` modeli **bir kez** koşturur, her aday için ham toplam,
jeton sayısı, karakter sayısı ve koşulsuz olasılığı saklar, ve altı kuralı bu üç
büyüklükten türetir: `ham`, `jeton`, `harf` (3.3.0'da seçilen), `pmi`,
`pmi_harf` ve `esli` (ikili karşılaştırma, Copeland). Model altı kez koşmaz.

```python
from turkmorfbench.duyarlilik import bilesen_topla, karsilastir, rapor
ka = bilesen_topla(arka_a, maddeler)
kb = bilesen_topla(arka_b, maddeler)
print(rapor(karsilastir(ka, kb, maddeler, "A", "B", anahtar="hucre"), "A", "B"))
```

Çıktı, iki sistemin farkının **yönünün altı kuralın hepsinde aynı olup olmadığını**
söyler. Aynıysa kalan uzunluk yanlılığı bulguyu taşımıyor demektir; değilse hangi
bulgunun kurala bağlı olduğunu okuyucudan önce biz bilmiş oluruz.
