---
title: 'Doğası Gereği Yorumlanabilir Modeller'
source: 'https://academia.sh/tr/kurslar/model-etik-ve-yorum/dogasi-geregi-yorumlanabilir-modeller'
course: 'Model Değerlendirme, Yorumlanabilirlik ve Etik'
language: tr
updated: '2026-08-17T18:10:14+00:00'
license: 'CC BY-SA 4.0'
---

# Doğası Gereği Yorumlanabilir Modeller

Açıklamayı sonradan üretmek yerine modeli baştan okunabilir kurmanın bedelinin ölçülmesi: iki kurallık, dört koşullu bir sıralı liste sınamada 0,7897 veriyor, otuz üç sütunlu en iyi ailenin 0,8056'sına 0,0159 yani dört abone kalıyor. Listeyi üç ve dört kurala uzatmak yaklaştırmıyor, 0,7857'ye geri düşürüyor. On üç alt gruba bakıldı, okunan on ikisinin altısında en iyi aileye 0,02 içinde yaklaşılamıyor; en uzağı merkez bölgesidir, 0,7414 yerine 0,6552 ve fark 0,0862. Üç alt grupta kural listesi en iyi aileyi geçiyor.

Dört derste açıklama hep modelin dışından üretildi: kurulmuş bir modelin kararı sonradan okundu,
bozuldu, taklit edildi ve ortalandı. Her seferinde açıklama bir yaklaşımdı ve sadakati ayrıca
ölçülmek zorunda kaldı.

Başka bir yol vardır: modeli baştan okunabilir kurmak. Kararı birkaç kuralın sırayla
uygulanmasıyla veren bir model açıklamayı ayrıca üretmez — modelin kendisi açıklamadır ve
sadakati tanım gereği tamdır. Bu dersin sorusu o yolun bedelidir. M27/K03'ün Doğrusal Modeller
konusu bedeli bir kez ölçmüştü: on iki katsayılı model ile otuz üç sütunlu en iyi aile arasında
0,0040, yani tek abone. O sayı burada tekrarlanmaz, **girdi alınır** — çünkü kural listesi on iki
katsayıdan çok daha okunabilirdir ve bedelin okunabilirlikle nasıl büyüdüğü asıl sorudur.

- **YO25.** Kurulum önceki derslerinkidir: aynı kurgu abone tablosu, tohum 20260218, bölme
  756/252/252, sınama kümesinde taban çizgisi 0,7579. Alt gruplar ve 25 kayıtlık okuma eşiği
  sürer; **bakılan alt grup sayısı 13'tür**.
- **YO26.** Karşılaştırma çizgileri M27/K03'ten gelir ve aynı tohumla yeniden hesaplanır: on iki
  sütunlu doğrusal model 0,8016, en iyi aile (otuz üç sütunlu, karesel cezalı) 0,8056. Karar ağacı
  ailesinin sayısı aynı kurstan 0,7857'dir.
- **YO27.** Kural listesi sıralı bir modeldir: ilk uyan kural kararı verir, hiçbiri uymazsa son
  etiket verilir. Her kural en fazla iki koşul taşır ve iki koşul aynı sütundan olamaz.
- **YO28.** Liste açgözlü kurulur: her adımda, kalan kayıtların varsayılan etiketine göre en çok
  doğru kazandıran kural eklenir ve kapsadığı kayıtlar çıkarılır. En az kapsam 12 kayıttır ve
  koşul eşikleri eğitim kümesinin ondalıklarından alınır.
- **YO29.** "En iyi aileye yaklaşmak", sınama doğruluğu farkının 0,02'yi geçmemesidir; 0,02 bu
  sınama kümesinde beş abonedir.

## Üç Karşılaştırma Çizgisi

Kurulum önceki derslerinkidir; üstüne M27/K03'ün en iyi ailesi aynı tohumla yeniden kurulur.

```python
# yorum.py — MODELDIR. M27/K01–K07'nin KURGU abone tablosu ayni tohumla uretilir;
# on iki sutunluk oznitelik kumesi ve bolme kurs boyunca sabittir.
import itertools
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21, 0.00), ("guney", 0.22, 17, -0.10),
         ("dogu", 0.18, 26, 0.30), ("bati", 0.14, 14, -0.05),
         ("merkez", 0.18, 23, 0.05)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort, oyn, r = sum(v) / len(v), max(v) - min(v), uretec(TOHUM + 51000 + k["no"])
    z = (0.052 * (ort - 20) + 0.85 * (min(v) == 0.0) + 0.026 * oyn
         + 0.24 * (k["hane"] >= 5) - 0.20 * (k["memnuniyet"] >= 4) + k["bolge"][3]
         + 0.9 * (k["no"] - 10001) / 1399 + (r() + r() + r() - 1.5) * 1.30)
    x = {"hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v),
         "ort_tuketim": round(ort, 2), "oynaklik": round(oyn, 2),
         "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35),
         "bolge": k["bolge"][0]}
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


K = karistir(VERI, TOHUM + 90000)
EGT, DOG, SIN = K[:756], K[756:1008], K[1008:]
ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma",
        "kisi_basi", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
TABAN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)


def sikistir(z):
    if z >= 0:
        return 1.0 / (1.0 + math.exp(-z)) if z < 700 else 1.0
    e = math.exp(z) if z > -700 else 0.0
    return e / (1.0 + e)


def olcek(egt, alan):
    return ({a: statistics.fmean(x[a] for x in egt) for a in alan},
            {a: max(1e-9, statistics.pstdev([x[a] for x in egt])) for a in alan})


def egit(egt, alan, o, s, adim=0.3, tur=300, ceza=0.0):   # MODELDIR: lojistik baglanim
    X = [[1.0] + [(x[a] - o[a]) / s[a] for a in alan] for x in egt]
    Y = [x["supheli"] for x in egt]
    w = [0.0] * len(X[0])
    for _ in range(tur):
        g = [0.0] * len(w)
        for i in range(len(X)):
            h = sikistir(sum(w[j] * X[i][j] for j in range(len(w)))) - Y[i]
            for j in range(len(w)):
                g[j] += h * X[i][j]
        for j in range(len(w)):
            w[j] -= adim * (g[j] / len(X) + (ceza * w[j] if j else 0.0))
    return w


def puan(w, alan, o, s, x):
    return sikistir(w[0] + sum(w[j + 1] * (x[a] - o[a]) / s[a]
                               for j, a in enumerate(alan)))


def karar(w, alan, o, s, x):
    return int(puan(w, alan, o, s, x) > 0.5)


O, S = olcek(EGT, ALAN)
W = egit(EGT, ALAN, O, S)
print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}")


def basamak(x):
    return ("tarife 10" if x["ort_tuketim"] <= 10 else
            "tarife 25" if x["ort_tuketim"] <= 25 else "tarife 40")


def hane_grup(x):
    return "hane 1-2" if x["hane"] <= 2 else ("hane 3-4" if x["hane"] <= 4
                                              else "hane 5+")


def okuma_grup(x):
    return "okuma az" if x["donem"] <= 2 else "okuma tam"


def esitse(f, deger):
    return lambda x: f(x) == deger


ALTGRUP = ([("bolge " + b[0], esitse(lambda x: x["bolge"], b[0])) for b in BOLGE]
           + [(t, esitse(basamak, t)) for t in ("tarife 10", "tarife 25", "tarife 40")]
           + [(h, esitse(hane_grup, h)) for h in ("hane 1-2", "hane 3-4", "hane 5+")]
           + [(o, esitse(okuma_grup, o)) for o in ("okuma az", "okuma tam")])
ENAZ = 25
BOY = {ad: sum(1 for x in SIN if f(x)) for ad, f in ALTGRUP}
print(f"alt grup sayisi {len(ALTGRUP)}, esik {ENAZ} kayit, esigin altinda "
      f"{[a for a in BOY if BOY[a] < ENAZ]}")
CARPIM = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi"]


def genislet(x):
    d = dict(x)
    for a, b in itertools.combinations(CARPIM, 2):
        d[a + "*" + b] = x[a] * x[b]
    for a in CARPIM:
        d[a + "^2"] = x[a] * x[a]
    return d


GENIS = (ALAN + [a + "*" + b for a, b in itertools.combinations(CARPIM, 2)]
         + [a + "^2" for a in CARPIM])
GE, GS = [genislet(x) for x in EGT], [genislet(x) for x in SIN]
OG, SG = olcek(GE, GENIS)
WG = egit(GE, GENIS, OG, SG, ceza=0.05)          # M27/K03'un en iyi ailesi
EN_IYI = sum(karar(WG, GENIS, OG, SG, x) == x["supheli"] for x in GS) / len(GS)
DM = sum(karar(W, ALAN, O, S, x) == x["supheli"] for x in SIN) / len(SIN)
print(f"taban cizgisi {TABAN:.4f}, on iki sutunlu dogrusal model {DM:.4f}, "
      f"en iyi aile ({len(GENIS)} sutun) {EN_IYI:.4f}")
```

```
egitim 756, dogrulama 252, sinama 252
alt grup sayisi 13, esik 25 kayit, esigin altinda ['tarife 10']
taban cizgisi 0.7579, on iki sutunlu dogrusal model 0.8016, en iyi aile (33 sutun) 0.8056
```

## Kural Listesi Uzadıkça

Liste açgözlü kurulur ve uzunluk birden altıya süpürülür.

```python
KURAL_SUTUN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem",
               "sifir_okuma", "bolge"]


def uyar(k, x):
    a, yon, e = k
    return x[a] == e if yon == "=" else (x[a] < e if yon == "<" else x[a] >= e)


def kosullar(kume):
    for a in KURAL_SUTUN:
        d = sorted({x[a] for x in kume})
        if a == "bolge":
            for v in d:
                yield (a, "=", v)
        else:
            for v in (d if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                yield (a, "<", v)
                yield (a, ">=", v)


def kazanc(g, v0, enaz):                 # varsayilana gore kazanilan dogru sayisi
    if len(g) < enaz:
        return None
    poz = sum(x["supheli"] for x in g)
    etiket = int(poz * 2 > len(g))
    return ((poz if etiket else len(g) - poz)
            - (poz if v0 else len(g) - poz), etiket)


def kural_listesi(egt, uzunluk, enaz=12, tohum=8):   # MODELDIR: sirali kural listesi
    kalan, liste = list(egt), []
    for _ in range(uzunluk):
        v0 = int(sum(x["supheli"] for x in kalan) * 2 > len(kalan))
        aday = []
        for k1 in kosullar(kalan):
            g1 = [x for x in kalan if uyar(k1, x)]
            z = kazanc(g1, v0, enaz)
            if z:
                p = sum(x["supheli"] for x in g1) / len(g1)
                aday.append((p if v0 == 0 else 1 - p, z[0], k1, g1, z[1]))
        if not aday:
            break
        aday.sort(key=lambda t: -t[0])
        en = max(((t[1], [t[2]], t[4]) for t in aday[:tohum]), key=lambda t: t[0])
        for _, _, k1, g1, _ in aday[:tohum]:
            for k2 in kosullar(g1):
                if k2[0] == k1[0]:
                    continue
                z = kazanc([x for x in g1 if uyar(k2, x)], v0, enaz)
                if z and z[0] > en[0]:
                    en = (z[0], [k1, k2], z[1])
        if en[0] <= 0:
            break
        liste.append((en[1], en[2]))
        kalan = [x for x in kalan if not all(uyar(k, x) for k in en[1])]
        if not kalan:
            break
    p = sum(x["supheli"] for x in kalan)
    return liste, (int(p * 2 > len(kalan)) if kalan else 0)


def kural_tahmin(liste, son, x):
    for kosul, etiket in liste:
        if all(uyar(k, x) for k in kosul):
            return etiket
    return son


def yaz(kosul):
    return " ve ".join(f"{a} {y} {e}" for a, y, e in kosul)


YAKIN = 0.02                      # sinama kumesinde bes abone
print(f"{'kural':>6}{'kosul':>7}{'sinama':>9}{'en iyi aileye fark':>20}")
SWEEP = []
for n in range(1, 7):
    liste, son = kural_listesi(EGT, n)
    d = sum(kural_tahmin(liste, son, x) == x["supheli"] for x in SIN) / len(SIN)
    k = sum(len(c) for c, e in liste)
    SWEEP.append((d, -len(liste), liste, son, k))
    print(f"{len(liste):>6}{k:>7}{d:>9.4f}{EN_IYI - d:>+20.4f}")
SECIM = max(SWEEP, key=lambda t: (t[0], t[1]))
print(f"\nen iyi aileye en cok yaklasan liste {-SECIM[1]} kural, {SECIM[4]} kosul, "
      f"sinama {SECIM[0]:.4f}, fark {EN_IYI - SECIM[0]:+.4f} "
      f"({round((EN_IYI - SECIM[0]) * len(SIN))} abone)")
for kosul, etiket in SECIM[2]:
    print(f"  eger {yaz(kosul)} ise {etiket}")
print(f"  yoksa {SECIM[3]}")
```

```
 kural  kosul   sinama  en iyi aileye fark
     1      2   0.7857             +0.0198
     2      4   0.7897             +0.0159
     3      6   0.7857             +0.0198
     4      8   0.7857             +0.0198
     4      8   0.7857             +0.0198
     4      8   0.7857             +0.0198

en iyi aileye en cok yaklasan liste 2 kural, 4 kosul, sinama 0.7897, fark +0.0159 (4 abone)
  eger bolge = dogu ve oynaklik >= 11.86 ise 1
  eger sifir_okuma >= 1 ve oynaklik >= 20.06 ise 1
  yoksa 0
```

Tek kural iki koşulla 0,7857 veriyor; bu, M27/K03'te budanmış karar ağacı ailesinin sayısıdır.
İkinci kural sayıyı 0,7897'ye çıkarıyor ve en iyi aileye 0,0159, yani dört abone kalıyor. Üçüncü
ve dördüncü kurallar yaklaştırmıyor, 0,7857'ye geri düşürüyor; beşinci adımda açgözlü yordam
kazanç bulamayıp duruyor. **Uzunluk ile doğruluk arasında tek yönlü bir ilişki yok.**

Bedelin büyüklüğü asıl okunacak yerdedir. M27/K03 on iki katsayı için farkı 0,0040, yani tek abone
ölçmüştü; dört koşula inildiğinde aynı fark dört aboneye çıkıyor. Okunabilirlik sürekli bir
eksendir ve bedeli o eksende büyür; hangi noktanın seçileceği bir ölçüm değil bir karardır, ama
karar ancak iki ucun sayısı yazıldığında verilebilir.

Listenin kendisi üç satırda okunuyor ve her satır bir cümledir. Bu modelin açıklamasının sadakati
tanım gereği 1'dir, çünkü açıklama modelin kendisidir; üçüncü dersin 41 boş açıklaması burada
oluşmaz.

## Hangi Alt Grupta Yaklaşılamıyor

Bütünde ölçülen 0,0159 tek başına okunmaz. Aynı iki model on üç alt grupta yan yana konur.

```python
LISTE, SON = SECIM[2], SECIM[3]
print(f"{'alt grup':<14}{'kayit':>6}{'kural listesi':>15}{'en iyi aile':>13}"
      f"{'fark':>9}")
UZAK, OKUNAN = [], 0
for ad, f in ALTGRUP:
    g = [x for x in SIN if f(x)]
    kl = sum(kural_tahmin(LISTE, SON, x) == x["supheli"] for x in g) / len(g)
    ei = sum(karar(WG, GENIS, OG, SG, genislet(x)) == x["supheli"]
             for x in g) / len(g)
    if BOY[ad] >= ENAZ:
        OKUNAN += 1
        if ei - kl > YAKIN:
            UZAK.append((ad, kl, ei))
    print(f"{ad:<14}{len(g):>6}{kl:>15.4f}{ei:>13.4f}{ei - kl:>+9.4f}")
UZAK.sort(key=lambda t: t[1] - t[2])
print(f"\nokunan alt grup {OKUNAN}/{len(ALTGRUP)}; en iyi aileye {YAKIN} icinde "
      f"yaklasamayan alt grup {len(UZAK)}, en uzagi {UZAK[0][0]} "
      f"({UZAK[0][1]:.4f} yerine {UZAK[0][2]:.4f}, fark {UZAK[0][2] - UZAK[0][1]:+.4f})")
```

```
alt grup       kayit  kural listesi  en iyi aile     fark
bolge kuzey       68         0.8088       0.7794  -0.0294
bolge guney       58         0.8621       0.8448  -0.0172
bolge dogu        36         0.6667       0.7222  +0.0556
bolge bati        32         1.0000       1.0000  +0.0000
bolge merkez      58         0.6552       0.7414  +0.0862
tarife 10         12         0.6667       0.6667  +0.0000
tarife 25        193         0.8290       0.8342  +0.0052
tarife 40         47         0.6596       0.7234  +0.0638
hane 1-2          73         0.7945       0.8356  +0.0411
hane 3-4         143         0.8042       0.7972  -0.0070
hane 5+           36         0.7222       0.7778  +0.0556
okuma az          85         0.7529       0.7765  +0.0235
okuma tam        167         0.8084       0.8204  +0.0120

okunan alt grup 12/13; en iyi aileye 0.02 icinde yaklasamayan alt grup 6, en uzagi bolge merkez (0.6552 yerine 0.7414, fark +0.0862)
```

Bütünde dört abone olan fark alt gruplarda dağılıyor. On üç alt gruba bakıldı, eşiği geçen 12'si
okundu ve bunların altısında en iyi aileye 0,02 içinde yaklaşılamıyor. En uzağı merkez bölgesidir:
0,6552'ye karşı 0,7414, fark 0,0862 — bütündeki farkın beş katından fazlası. En üst tarife
basamağında fark 0,0638, doğu bölgesinde ve büyük hanelerde 0,0556.

Nedeni listenin kendisinden okunuyor. İki kuralın biri doğu bölgesine, öbürü sıfır okumasına
bağlı; merkez bölgesinin şüpheli aboneleri ikisine de girmiyor ve hepsi son satırın varsayılan
etiketini alıyor. **Kısalık bütün için ödenir, faturası bazı alt gruplara çıkar.**

Ters yön de aynı tabloda duruyor. Üç alt grupta kural listesi en iyi aileyi geçiyor: kuzeyde
0,0294, güneyde 0,0172, orta hane büyüklüğünde 0,0070. Bütündeki tek sayı, hangi modelin kimin
için daha iyi olduğunu söylemiyor.

## Özet

- İki kurallık, dört koşullu sıralı liste sınamada 0,7897 veriyor; en iyi ailenin 0,8056'sına
  0,0159, yani dört abone kalıyor.
- Listeyi uzatmak yaklaştırmıyor: üç ve dört kural 0,7857'ye düşürüyor ve liste dördüncü kuraldan
  sonra doyuyor. Tek kural 0,7857 ile karar ağacı ailesinin sayısını veriyor.
- Okunabilirliğin bedeli o eksende büyüyor: M27/K03'te on iki katsayı için bir abone, burada dört
  koşul için dört abone.
- Kural listesinin sadakati tanım gereği 1'dir; üçüncü dersin 41 boş açıklaması burada oluşmaz.
- On üç alt gruba bakıldı, 12'si okundu: altısında en iyi aileye 0,02 içinde yaklaşılamıyor, en
  uzağı merkez bölgesinde 0,0862; üç alt grupta ise kural listesi en iyi aileyi geçiyor.

## Sonraki Adım

On derste bir sayının ne olduğu, kimin için olduğu ve neye dayandığı ölçüldü: bölmenin şişirdiği
pay, bedel yapısının oynattığı işletme noktası, tabanın seçimi, farkın payı, dağılım kayması,
açıklamanın kapattığı soru sayısı, iki önem sıralamasının ayrışması, açıklamanın sadakati,
ortalamanın gizlediği etkileşim ve kural listesinin bedeli. Hepsinde ölçülen şey **doğruluk ya da
okunabilirlikti**.

Hiçbiri bir **zarar** ölçüsü değildi. Merkez bölgesindeki 0,0862'lik fark, oradaki abonelerin
şüpheli işaretini daha sık yanlış aldığı anlamına gelir; kimin ne kadar yanlış işaret aldığı ve o
işaretin ona ne yaptığı on derste hiç sorulmadı. Sonraki konu bu soruyla açılır: yanlışlık kümenin
neresinden geliyor, adil olmanın kaç ayrı tanımı var ve bu tanımlar aynı anda sağlanabiliyor mu,
ve modelin öğrendiği şey tek tek kayıtlar hakkında ne sızdırıyor.
