---
title: 'Denetimli Öğrenme'
source: 'https://academia.sh/tr/kurslar/denetimli-ogrenme'
language: tr
updated: '2026-08-17T04:42:10+00:00'
license: 'CC BY-SA 4.0'
lessons: 20
outcomes:
    - 'Bağlanım ve sınıflandırma ailelerini varsayımlarıyla uygular ve her ailenin kattığı farkı ayrılmış kümede ölçer'
    - 'Uygun değerlendirme ölçütünü problem bağlamına göre seçer ve ölçütün sıralamayı nasıl değiştirdiğini gösterir'
    - 'Ayar araması yapar ve bulduğu sayının yanına denenen aday sayısını yazarak bütçenin şişirdiği farkı ayırır'
---

# Denetimli Öğrenme

Öznitelikler sabit tutulup yalnız model değiştirildiğinde ailenin kattığı farkın, o farkı bulmak için denenen aday sayısıyla birlikte okunması: en küçük karelerin kestirim olarak ölçülmesi, polinom derecesinin ayrıştırdığı iki sayı, karesel, mutlak ve karma cezanın ürettiği seyreklik, olasılık çıktısının sürekliliği, yorumlanabilirliğin bedeli, komşu sayısı ile boyut lanetinin oranı, koşullu bağımsızlık bozulduğunda olasılık ile sıralamanın ayrışması, marj genişliği ile destek vektörü sayısı, bölme ölçütü ve budamanın derinlik sınırından farkı, torbalamanın düşürdüğü değişkenlik, artırmada öğrenme adımı ile tur sayısının takası, yığınlamanın ikinci katmanının hangi kümeden öğrenildiği, aynı doğruluğu veren iki modelin farklı hata dağılımı, ölçüt değişince değişen sıralama, eşiğin oynattığı iki sayı, söylenen olasılık ile gerçekleşen oran arasındaki fark, üç bağlanım ölçütünde yer değiştirme, kat sayısının kestirimi oynatması, arama bütçesi büyüdükçe şişen doğrulama sayısı ve hatanın yoğunlaştığı dilim.

## Doğrusal Modeller

Kararı bir doğru ya da düzlem olarak kuran ailelerin kestirim başarımıyla ölçülmesi: en küçük karelerin ayrılmış kümedeki sayısı ve artıkların yapısıyla denetlenen varsayımlar, polinom derecesi birden sekize süpürüldükçe eğitim ile ayrılmış küme sayılarının ayrışması ve seçimin hangi kümeden okunduğu, karesel, mutlak ve karma cezanın kaç katsayıyı tam sıfırladığı ile seyrekliğin ayrılmış kümedeki karşılığı, olasılık çıktısının eşikten bağımsız sürekliliği ve karar sınırı, okunabilir katsayı uğruna verilen puan ile katsayının söylemedikleri.

### Doğrusal Bağlanım

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/dogrusal-baglanim

En küçük karelerin bir açıklama aracı değil bir kestirim yordamı olarak ölçülmesi ve kursun üç sayısının kurulması: sınama kümesinde taban çizgisi 6,5324 m³ kök hata verirken doğrusal model 5,3096 verir, kazanç 1,2228 m³ ve denenen aday sayısı 1'dir. Artıkların ortalaması eğitim kümesinde bölge başına tam sıfırdır, sınama kümesinde -1,0769 ile +1,3685 arasında gezinir; artık değişkenliği kestirim büyüdükçe 3,566'dan 7,137'ye çıkar. Yedi sütunun 127 alt kümesi denendiğinde doğrulamadan seçilen alt küme sınamada 5,3157 ile tam modelin gerisine düşer, sınamadan seçilseydi 5,2931 raporlanırdı ve fark 0,0226 m³'tür.

Önceki kurs on iki derste özniteliklerin her adımını ölçtü ve kapanışında iki hesabı açık bıraktı.
Birincisi: bütün o ölçümler boyunca model hep aynı iki üç aile arasından seçildi — derinlik 6 bir
karar ağacı, bir eğim temelli doğrusal model, bir uzaklık temelli yordam — ve **ailenin kendisinin
ne kattığı** hiç sorulmadı. İkincisi: derinliğin niçin altı, komşu sayısının niçin on beş olduğu
ve bu sayıların **hangi kümeden okunması gerektiği** hiç ölçülmedi. Aynı kapanış sınama kümesine
en az yirmi dört kez bakıldığını saydı.

Bu kurs iki soruyu birlikte ele alır ve bunun için ölçüyü değiştirir. Öznitelikler **sabit
tutulur**; değişen tek şey modeldir. Her derste üç sayı yan yana durur: **taban çizgisi**,
**ailenin ayrılmış kümedeki sayısı** ve o sayıyı bulmak için **denenen aday sayısı**. Üçüncüsü
yeni ve kursun kuralıdır: **bir modelin sayısı, o sayıyı bulmak için kaç aday denendiği
yazılmadan okunamaz.** Ayar bütçesi yazılmayan sayı ölçülmemiş sayılır. Bu ders üçlüyü kurar ve
en basit aileyle, en küçük karelerle başlar.

- **DM1.** Küme M27/K01 ve K02'nin **kurgu** abone tablosudur; aynı tohumla (20260218) yeniden
  üretilir. Bölme 756 eğitim, 252 doğrulama, 252 sınama satırıdır ve kurs boyunca değişmez.
- **DM2.** Sınıflandırma derslerinde hedef kaçak ya da arıza şüphesi taşıyan abonedir ve taban
  çizgisi **en sık sınıfı söylemektir**: sınama kümesinde 0,7579.
- **DM3.** Bağlanım derslerinde hedef abonenin **üç döneme kadar uzanan okumalarının ortalama
  tüketimidir** (m³) ve taban çizgisi **eğitim kümesinin ortalamasıdır**: her aboneye aynı sayıyı
  söylemek.
- **DM4.** Bağlanım özniteliği, hedefin hesabına giren hiçbir okumadan türetilmez. Oynaklık,
  sıfır okuma ve kişi başına tüketim aynı okuma dizisinden çıktığı için **kullanılmaz**; geriye
  bölge, hane büyüklüğü, memnuniyet ve dönem sayısı kalır.
- **DM5.** Beş bölge göstergesinin toplamı her satırda bire, yani sabit terime eşittir. Biri
  (kuzey) dışarıda bırakılır ve **referans** olur.
- **DM6.** Ölçü **kök ortalama kare hatadır** (m³); ortalama mutlak hata yanına basılır. İkisinin
  ayrıştığı yer `model-degerlendirme/05`'e bırakılır.
- **DM7.** Açıklanan değişim oranı **eğitim ortalamasına göre** tanımlanır: sıfır, taban
  çizgisiyle aynı hatayı yapmaktır. Ayrılmış kümede eksi de çıkabilir.
- **DM8.** Katsayının **nedensel** yorumu bu kursun konusu değildir. İşaretin karıştırıcı bir
  değişkenle dönmesi M26'nın Keşifsel Analiz kursundaki Uygulamalı İstatistik konusunda ölçüldü;
  burada tekrarlanmaz.
- **DM9.** Sınama kümesine her derste yalnız seçim bittikten sonra bakılır ve kaç kez bakıldığı
  yazılır.

#### Üç Sayı

Kurulum kümeyi üretir, böler ve iki taban çizgisini basar.

```python
# baglanim.py — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden
# uretilir; hedef abonenin uc donemlik ortalama tuketimidir (m3).
import math

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 = {"no": k["no"], "bolge_ad": k["bolge"][0], "hane": k["hane"],
         "memnuniyet": k["memnuniyet"], "donem": len(v), "hedef": round(ort, 2),
         "supheli": int(z > 1.35)}
    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:]
ORT = sum(x["hedef"] for x in EGT) / len(EGT)


def kok(kestirim, s):
    return math.sqrt(sum((kestirim(x) - x["hedef"]) ** 2 for x in s) / len(s))


def mutlak(kestirim, s):
    return sum(abs(kestirim(x) - x["hedef"]) for x in s) / len(s)


print(f"abone {len(VERI)}, egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}")
print(f"siniflandirma taban cizgisi (en sik sinif, sinamada) "
      f"{sum(x['supheli'] == 0 for x in SIN) / len(SIN):.4f}")
print(f"baglanim taban cizgisi (egitim ortalamasi) {ORT:.4f} m3\n")
print(f"{'kume':<12} {'kok hata':>9} {'mutlak hata':>12}")
for ad, s in (("egitim", EGT), ("dogrulama", DOG), ("sinama", SIN)):
    print(f"{ad:<12} {kok(lambda x: ORT, s):>9.4f} {mutlak(lambda x: ORT, s):>12.4f}")
```

```
abone 1260, egitim 756, dogrulama 252, sinama 252
siniflandirma taban cizgisi (en sik sinif, sinamada) 0.7579
baglanim taban cizgisi (egitim ortalamasi) 19.7665 m3

kume          kok hata  mutlak hata
egitim          6.1995       4.9017
dogrulama       6.4193       5.1148
sinama          6.5324       5.0806
```

İki taban çizgisi de öğrenmeyen yordamlardır ve ikisi de bu kurs boyunca sabit kalır.
Sınıflandırmada 0,7579 sayısı önceki iki kurstan olduğu gibi devralınır. Bağlanımda taban çizgisi
her aboneye 19,7665 m³ söyler ve sınama kümesinde 6,5324 m³ kök hata verir: kalan bütün sayıların
ölçüleceği sıfır noktası.

#### En Küçük Kareler Bir Kestirim Yordamıdır

En küçük kareler, kare hata toplamını en küçük yapan katsayıları çözer. Çözüm kapalı biçimdedir:
sütunların birbiriyle çarpımlarından kurulan bir denklem takımı eleme ile çözülür.

```python
ALAN = ["b_guney", "b_dogu", "b_bati", "b_merkez", "hane", "memnuniyet", "donem"]


def coz(A, b):                              # kismi pivotlamali eleme
    n = len(b)
    for i in range(n):
        p = max(range(i, n), key=lambda k: abs(A[k][i]))
        A[i], A[p], b[i], b[p] = A[p], A[i], b[p], b[i]
        for k in range(i + 1, n):
            f = A[k][i] / A[i][i]
            for j in range(i, n):
                A[k][j] -= f * A[i][j]
            b[k] -= f * b[i]
    w = [0.0] * n
    for i in range(n - 1, -1, -1):
        w[i] = (b[i] - sum(A[i][j] * w[j] for j in range(i + 1, n))) / A[i][i]
    return w


def satir(x, alan):
    return [1.0] + [float(x[a]) for a in alan]


def en_kucuk_kare(s, alan):                 # MODELDIR: dogrusal baglanim
    X = [satir(x, alan) for x in s]
    y = [x["hedef"] for x in s]
    p = len(X[0])
    A = [[sum(X[i][a] * X[i][b] for i in range(len(X))) for b in range(p)]
         for a in range(p)]
    return coz(A, [sum(X[i][a] * y[i] for i in range(len(X))) for a in range(p)])


def kestirici(w, alan):
    return lambda x: sum(a * b for a, b in zip(w, satir(x, alan)))


def oran(kestirim, s):                      # aciklanan degisim orani
    return 1 - (sum((kestirim(x) - x["hedef"]) ** 2 for x in s)
                / sum((ORT - x["hedef"]) ** 2 for x in s))


W = en_kucuk_kare(EGT, ALAN)
f = kestirici(W, ALAN)
print(f"sabit terim {W[0]:>8.3f}   (kuzey referans)")
for i, a in enumerate(ALAN):
    print(f"{a:<12} {W[i + 1]:>8.3f}")
print(f"\n{'kume':<12} {'taban kok':>10} {'model kok':>10} {'fark':>8} "
      f"{'mutlak':>8} {'oran':>7}")
for ad, s in (("egitim", EGT), ("dogrulama", DOG), ("sinama", SIN)):
    t = kok(lambda x: ORT, s)
    print(f"{ad:<12} {t:>10.4f} {kok(f, s):>10.4f} {t - kok(f, s):>+8.4f} "
          f"{mutlak(f, s):>8.4f} {oran(f, s):>7.4f}")
print(f"\ndenenen aday sayisi 1")
```

```
sabit terim   21.820   (kuzey referans)
b_guney        -3.383
b_dogu          4.598
b_bati         -5.851
b_merkez        2.340
hane            0.062
memnuniyet     -0.148
donem          -0.550

kume          taban kok  model kok     fark   mutlak    oran
egitim           6.1995     5.0757  +1.1238   3.7461  0.3297
dogrulama        6.4193     5.3225  +1.0969   4.0005  0.3125
sinama           6.5324     5.3096  +1.2228   4.0020  0.3393

denenen aday sayisi 1
```

Üçlü tamamlandı: taban çizgisi 6,5324, ailenin sayısı 5,3096, denenen aday sayısı 1. Kazanç
1,2228 m³, yani tabanın yüzde 18,7'si. Aday sayısının bir olması bu dersin ayrıcalığıdır — sütun
kümesi baştan yazıldı, hiçbir ayar değişkeni yok, çözüm kapalı biçimde tek seferde geldi. Kursun
kalan derslerinde bu sayı bir daha bir olmayacak.

Aynı katsayı listesi iki ayrı işe koşulabilir ve ikisi aynı şey değildir. Bölge katsayıları
kurgudaki taban tüketim düzeylerinin sırasını doğru veriyor: doğu en yüksek, batı en düşük. Ama
bu dersin ölçüsü katsayının doğruluğu değil, **görülmemiş bir abonede yapılan hatadır**. Sınama
kümesindeki açıklanan değişim oranı 0,3393'tür ve tabana göre tanımlıdır: sıfır, her aboneye
eğitim ortalamasını söylemekle aynı hatayı yapmak demektir.

#### Varsayımlar Sayıyla Denetlenir

Doğrusal bağlanımın varsayımları vardır: artıkların ortalaması sıfırdır, değişkenlikleri
kestirimden bağımsızdır, uçlar simetriktir. Bunlar kuram olarak değil, sayı olarak denetlenir.

```python
def artiklar(kestirim, s):
    return [kestirim(x) - x["hedef"] for x in s]


def orta(v):                                # makine hassasiyetindeki artik sifirdir
    m = sum(v) / len(v)
    return m if abs(m) > 5e-5 else 0.0


print(f"{'bolge':<9} {'egitimde ort':>13} {'sinamada n':>11} {'sinamada ort':>13}")
for b in BOLGE:
    e = [x for x in EGT if x["bolge_ad"] == b[0]]
    v = [x for x in SIN if x["bolge_ad"] == b[0]]
    print(f"{b[0]:<9} {orta(artiklar(f, e)):>+13.4f} {len(v):>11} "
          f"{orta(artiklar(f, v)):>+13.4f}")
sirali = sorted(SIN, key=f)
n = len(sirali) // 4
print(f"\n{'dilim':<7} {'kestirim ort':>13} {'artik ort':>10} {'artik degiskenlik':>19}")
for k in range(4):
    d = sirali[k * n:(k + 1) * n] if k < 3 else sirali[3 * n:]
    v = artiklar(f, d)
    m = sum(v) / len(v)
    s2 = math.sqrt(sum((q - m) ** 2 for q in v) / len(v))
    print(f"dilim {k + 1:<1} {sum(f(x) for x in d) / len(d):>13.2f} {m:>+10.3f} "
          f"{s2:>19.3f}")
v = artiklar(f, SIN)
m = sum(v) / len(v)
s2 = math.sqrt(sum((q - m) ** 2 for q in v) / len(v))
print(f"\nsinamada iki degiskenlik disina dusen artik "
      f"{sum(1 for q in v if abs(q - m) > 2 * s2)}, "
      f"eksi yonde {sum(1 for q in v if q - m < -2 * s2)}, "
      f"arti yonde {sum(1 for q in v if q - m > 2 * s2)}")
```

```
bolge      egitimde ort  sinamada n  sinamada ort
kuzey           +0.0000          68       -1.0370
guney           +0.0000          58       -0.0391
dogu            +0.0000          36       -1.0769
bati            +0.0000          32       +1.3685
merkez          +0.0000          58       +0.5708

dilim    kestirim ort  artik ort   artik degiskenlik
dilim 1         15.33     +1.192               3.566
dilim 2         18.67     -1.007               4.746
dilim 3         21.29     -0.237               4.883
dilim 4         23.85     -0.498               7.137

sinamada iki degiskenlik disina dusen artik 17, eksi yonde 11, arti yonde 6
```

İlk sütun bir denetim değil, bir kimliktir. Eğitim kümesinde her bölgenin artık ortalaması **tam
sıfırdır** ve bunun nedeni modelin iyi olması değildir: bölge göstergeleri sütun olarak modelin
içindedir ve en küçük kareler çözümü her sütunu artıklara dik yapar. Bu sayı hiçbir zaman
sıfırdan farklı çıkamaz, dolayısıyla hiçbir şey söylemez. Söyleyen sütun üçüncüsüdür: aynı
ortalamalar sınama kümesinde -1,0769 ile +1,3685 arasında geziniyor. Batı bölgesindeki otuz iki
abonede model ortalama 1,37 m³ fazla söylüyor.

İkinci tablo daha keskin bir bozulma gösteriyor. Artıkların değişkenliği kestirim büyüdükçe
3,566'dan 7,137'ye, yani iki katına çıkıyor. Model, az tüketen abonelerde çok daha isabetli, çok
tüketenlerde çok daha kaba. Eşit değişkenlik varsayımının tutmamasının sebebi kurgunun kendisinde:
tüketim çarpımsal bir dağılımdan üretiliyor, dolayısıyla büyük değerlerin saçılması da büyük. Son
satır aynı asimetriyi öteki yönden veriyor — iki değişkenlik uzağındaki on yedi artığın on biri
bir yönde, altısı öteki yönde.

Bozulan varsayımlar bir açıklama çalışmasında katsayıların oynama payını geçersiz kılardı; M26'nın
Uygulamalı İstatistik konusu bu hesabı yaptı. Kestirim hedefinde ise ölçü tektir ve zaten
alınmıştır: 5,3096. Varsayım denetimi burada sayıyı geçersiz kılmaz, **sayının nerede kötü
olduğunu** söyler.

#### Ayar Bütçesi Yazılmayan Sayı Ölçülmemiş Sayılır

Bu derste tek bir aday denendi. Yedi sütunun boş olmayan bütün alt kümeleri denenirse ne olur.

```python
import itertools

en_dogrulama, en_sinama, aday = None, None, 0
for r in range(1, len(ALAN) + 1):
    for c in itertools.combinations(ALAN, r):
        aday += 1
        g = kestirici(en_kucuk_kare(EGT, list(c)), list(c))
        d, s = kok(g, DOG), kok(g, SIN)
        if en_dogrulama is None or d < en_dogrulama[0]:
            en_dogrulama = (d, s, c)
        if en_sinama is None or s < en_sinama[1]:
            en_sinama = (d, s, c)
print(f"denenen aday sayisi {aday}")
print(f"tam model            dogrulama {kok(f, DOG):.4f}  sinama {kok(f, SIN):.4f}")
print(f"dogrulamadan secilen dogrulama {en_dogrulama[0]:.4f}  "
      f"sinama {en_dogrulama[1]:.4f}  {len(en_dogrulama[2])} sutun")
print(f"sinamadan secilen    dogrulama {en_sinama[0]:.4f}  "
      f"sinama {en_sinama[1]:.4f}  {len(en_sinama[2])} sutun")
print(f"secim sinamada yapilsaydi rapor edilecek sayi {en_sinama[1]:.4f}, "
      f"durustu {en_dogrulama[1]:.4f}, fark {en_dogrulama[1] - en_sinama[1]:.4f} m3")
print(f"disarida birakilan sutun: "
      f"{[a for a in ALAN if a not in en_dogrulama[2]]}")
```

```
denenen aday sayisi 127
tam model            dogrulama 5.3225  sinama 5.3096
dogrulamadan secilen dogrulama 5.3073  sinama 5.3157  6 sutun
sinamadan secilen    dogrulama 5.4150  sinama 5.2931  6 sutun
secim sinamada yapilsaydi rapor edilecek sayi 5.2931, durustu 5.3157, fark 0.0226 m3
disarida birakilan sutun: ['memnuniyet']
```

Üç satır üç ayrı sayı veriyor ve üçü de aynı model ailesinden, aynı özniteliklerden, aynı
bölmeden çıkıyor. Aralarındaki tek fark **kaç aday denendiği** ve **seçimin hangi kümeden
okunduğudur**.

Doğrulama kümesinden okunan seçim memnuniyet sütununu atıyor ve doğrulamada 5,3073 ile tam
modelin 5,3225'ini geçiyor. Aynı seçim sınamada 5,3157 veriyor — yani **tam modelin gerisinde**.
Yüz yirmi yedi aday arasından doğrulamaya bakarak seçmek, hiç seçmemekten kötü bir sonuç üretti;
252 satırlık bir küme üzerinde 127 kez en küçüğü aramak, o kümenin kendi saçılmasını da seçer.

Son iki satır kursun kuralını sayıya çeviriyor. Seçim sınama kümesinden yapılsaydı raporlanacak
sayı 5,2931 olurdu; dürüst sayı 5,3157'dir ve fark 0,0226 m³'tür. Farkın işareti sabittir —
seçimin yapıldığı kümedeki sayı hiçbir zaman daha kötü çıkamaz. Bu yüzden 5,3096 ile 5,2931
arasındaki karşılaştırma ancak yanlarına 1 ve 127 yazıldığında anlam taşır.

#### Özet

- Kursun üç sayısı kuruldu: taban çizgisi, ailenin ayrılmış kümedeki sayısı ve denenen aday
  sayısı. Sınıflandırmada taban 0,7579, bağlanımda 6,5324 m³'tür.
- En küçük kareler yedi sütunla sınama kümesinde 5,3096 m³ veriyor; kazanç 1,2228 m³ ve denenen
  aday sayısı 1'dir. Bu, kursun aday sayısı bir olan tek dersidir.
- Artık ortalaması eğitim kümesinde bölge başına tam sıfırdır ve bu bir denetim değil, en küçük
  kareler çözümünün kimliğidir; aynı ortalama sınama kümesinde -1,0769 ile +1,3685 arasında gezinir.
- Eşit değişkenlik varsayımı tutmuyor: artık değişkenliği kestirim büyüdükçe 3,566'dan 7,137'ye
  çıkıyor. Kestirim hedefinde bu, sayıyı geçersiz kılmaz; sayının nerede kötü olduğunu söyler.
- Yedi sütunun 127 alt kümesi denendiğinde doğrulamadan seçilen alt küme sınamada 5,3157 ile tam
  modelin gerisine düşüyor; sınamadan seçilseydi 5,2931 raporlanırdı ve fark 0,0226 m³'tür.

#### Sonraki Adım

Bu derste model bir düzlemdi ve sütunları verildiği gibi kullanıldı. Oysa hane büyüklüğü ile
tüketim arasındaki ilişkinin bir doğru olması gerekmez; iki kişilik haneden dört kişiliğe
geçerken artan tüketim, dört kişilikten altıya geçerken aynı oranda artmayabilir. Doğrusal
olmayan bir ilişkiyi doğrusal bir modelle kurmanın yolu vardır ve sütun eklemekten ibarettir:
karesi, küpü, ikili çarpımları. Sonraki ders bu genişletmeyi dereceyle ölçer — derece 1'den 8'e
süpürülür, her derecenin eğitim ve ayrılmış küme sayıları yan yana basılır, seçim doğrulama
kümesinden okunur ve sınama kümesinden okunsaydı hangi derecenin seçileceği aynı tabloda
gösterilir.

### Polinom Bağlanım

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/polinom-baglanim

Doğrusal olmayan bir ilişkinin sütun ekleyerek doğrusal modelle kurulması ve derecenin iki sayıyı ayrıştırması: derece 1'den 8'e süpürüldüğünde sütun sayısı 7'den 168'e çıkar, eğitim kök hatası 5,0757'den 4,8437 m³'e iner, doğrulama kök hatası 5,2569'da dibe vurup 5,6877'ye tırmanır. Doğrulamadan okunan derece 4'tür ve sınamada 5,3775 verir; sınamadan okunsaydı derece 5 seçilir ve 5,2009 raporlanırdı, fark 0,1766 m³'tür. Doğrulamadan seçilen derece sınamada derece 1'in 0,0679 m³ gerisindedir. Dört öznitelik yalnız 450 ayrık birleşim üretir; her hücreye eğitim ortalamasını söyleyen tavan model eğitimde 3,8418, sınamada 6,3887 verir ve sınamadaki 33 abone eğitimde hiç görülmemiş bir hücreye düşer.

Önceki ders modeli bir düzlem olarak kurdu ve sütunları verildiği gibi kullandı: hane büyüklüğü
bir sütun, memnuniyet bir sütun, dönem sayısı bir sütun. O modelin söylediği şey, hane büyüklüğü
bir arttığında tüketimin **her yerde aynı miktarda** arttığıdır. Bunun doğru olması için bir
neden yok. İki kişilik haneden dört kişiliğe geçerken artan tüketim, dört kişilikten altıya
geçerken aynı oranda artmayabilir; bölgenin etkisi hane büyüklüğüne göre değişebilir.

Bu ilişkiyi kurmak için model ailesini değiştirmek gerekmez. Doğrusal model, **katsayılarında**
doğrusaldır; sütunlarında değil. Hane büyüklüğünün karesi bir sütun olarak eklendiğinde model
hâlâ en küçük karelerle çözülür, ama artık eğrilik taşır. **Polinom öznitelikleri** bu genişletmenin
adıdır ve tek bir ayar değişkeni vardır: derece. Bu ders dereceyi 1'den 8'e süpürür, her derecenin
eğitim ve ayrılmış küme sayılarını yan yana basar ve seçimin hangi kümeden okunduğunu iki ayrı
sayı olarak gösterir.

- **DM10.** Küme, bölme ve hedef önceki dersteki gibidir: aynı kurgu abone tablosu, tohum
  20260218, 756/252/252, hedef ortalama tüketim (m³), taban çizgisi eğitim ortalaması ve sınama
  kümesinde 6,5324 m³ kök hata.
- **DM11.** Polinom yalnız üç sayısal sütun üzerinde açılır: hane büyüklüğü, memnuniyet, dönem
  sayısı. Derece $d$ için $1 \le i+j+k \le d$ koşulunu sağlayan bütün tek terimliler eklenir.
- **DM12.** Dört bölge göstergesi her derecede olduğu gibi kalır; polinomun içine girmez. Kuzey
  önceki derste olduğu gibi referanstır.
- **DM13.** Sütunlar eğitim kümesinden öğrenilen ortalama ve değişkenlikle ölçeklenir. Hane
  büyüklüğünün sekizinci kuvveti bir milyonu aşar; ölçekleme olmadan eleme sayısal olarak çöker.
- **DM14.** Yüksek derecelerde sütunlar birbirinin doğrusal birleşimine dönüşür, çünkü hane
  büyüklüğü yalnız altı, memnuniyet beş, dönem sayısı üç ayrık değer alır. Denklem takımını
  çözülebilir kılmak için köşegene $10^{-6}$ eklenir. Bu bir düzenlileştirme kararı değil, tekil
  takıma karşı konan sayısal bir eşiktir; ceza kararı sonraki dersin konusudur.
- **DM15.** Denenen aday sayısı **8**'dir: sekiz derece, başka hiçbir ayar yok.
- **DM16.** Seçim **doğrulama kümesinden** okunur. Sınama kümesindeki sayılar tabloda yer alır
  ama seçime girmez; yer almalarının nedeni iki seçimin farkının ölçülmesidir.
- **DM17.** Tavan model, aynı dört özniteliğin her ayrık birleşimine eğitim kümesindeki
  ortalamayı söyleyen yordamdır. Bu özniteliklerle kurulabilecek hiçbir model eğitim kümesinde
  ondan iyi olamaz.

#### Derece Arttıkça İki Sayı Ayrışıyor

Kurulum önceki dersin kurulumudur; üzerine polinom öznitelikleri ve derece süpürmesi eklenir.

```python
# polinom.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum ve ayni bolme.
import math

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
    x = {"bolge_ad": k["bolge"][0], "hane": k["hane"], "memnuniyet": k["memnuniyet"],
         "donem": len(v), "hedef": round(sum(v) / len(v), 2)}
    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:]
ORT = sum(x["hedef"] for x in EGT) / len(EGT)


def kok(kestirim, s):
    return math.sqrt(sum((kestirim(x) - x["hedef"]) ** 2 for x in s) / len(s))


print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, "
      f"taban cizgisi sinamada {kok(lambda x: ORT, SIN):.4f} m3")
```

```
egitim 756, dogrulama 252, sinama 252, taban cizgisi sinamada 6.5324 m3
```

Polinom öznitelikleri, ölçekleme ve çözüm bir arada durur. Ölçekleme adımı önceki kursun kuralına
uyar ve iki yüzlüdür: **öğrenme** yalnız eğitim kümesinden, **uygulama** her kümeye.

```python
import itertools

SAYISAL = ["hane", "memnuniyet", "donem"]
GOSTERGE = ["b_guney", "b_dogu", "b_bati", "b_merkez"]


def usler(derece):                          # 1 <= i+j+k <= derece olan butun tek terimliler
    return [u for u in itertools.product(range(derece + 1), repeat=len(SAYISAL))
            if 1 <= sum(u) <= derece]


def ham(x, us):
    return ([float(x[a]) for a in GOSTERGE]
            + [math.prod(x[a] ** k for a, k in zip(SAYISAL, u)) for u in us])


def olcek_ogren(egt, us):                   # OGRENME: yalniz egitim kumesinden
    H = [ham(x, us) for x in egt]
    p = len(H[0])
    o = [sum(r[j] for r in H) / len(H) for j in range(p)]
    s = [max(1e-9, math.sqrt(sum((r[j] - o[j]) ** 2 for r in H) / len(H)))
         for j in range(p)]
    return o, s


def uygula(x, us, o, s):                    # UYGULAMA
    h = ham(x, us)
    return [1.0] + [(h[j] - o[j]) / s[j] for j in range(len(h))]


def coz(A, b):
    n = len(b)
    for i in range(n):
        p = max(range(i, n), key=lambda k: abs(A[k][i]))
        A[i], A[p], b[i], b[p] = A[p], A[i], b[p], b[i]
        for k in range(i + 1, n):
            f = A[k][i] / A[i][i]
            for j in range(i, n):
                A[k][j] -= f * A[i][j]
            b[k] -= f * b[i]
    w = [0.0] * n
    for i in range(n - 1, -1, -1):
        w[i] = (b[i] - sum(A[i][j] * w[j] for j in range(i + 1, n))) / A[i][i]
    return w


DENGE = 1e-6                                # tekil takimi cozulebilir kilan sayisal esik


def polinom(egt, us, o, s):                 # MODELDIR: polinom baglanim
    X = [uygula(x, us, o, s) for x in egt]
    y = [x["hedef"] for x in egt]
    p = len(X[0])
    A = [[sum(X[i][a] * X[i][b] for i in range(len(X)))
          + (DENGE if a == b and a else 0.0) for b in range(p)] for a in range(p)]
    return coz(A, [sum(X[i][a] * y[i] for i in range(len(X))) for a in range(p)])


def kestirici(w, us, o, s):
    return lambda x: sum(a * b for a, b in zip(w, uygula(x, us, o, s)))


SONUC = []
print(f"{'derece':>7} {'sutun':>6} {'egitimde':>9} {'dogrulama':>10} {'sinama':>8} "
      f"{'agirlik':>10}")
for derece in range(1, 9):
    us = usler(derece)
    o, s = olcek_ogren(EGT, us)
    w = polinom(EGT, us, o, s)
    g = kestirici(w, us, o, s)
    SONUC.append((derece, len(us) + len(GOSTERGE), kok(g, EGT), kok(g, DOG), kok(g, SIN)))
    print(f"{derece:>7} {SONUC[-1][1]:>6} {SONUC[-1][2]:>9.4f} {SONUC[-1][3]:>10.4f} "
          f"{SONUC[-1][4]:>8.4f} {math.sqrt(sum(v * v for v in w[1:])):>10.2f}")
print(f"\ntaban cizgisi sinamada {kok(lambda x: ORT, SIN):.4f} m3, denenen aday sayisi 8")
```

```
 derece  sutun  egitimde  dogrulama   sinama    agirlik
      1      7    5.0757     5.3225   5.3096       3.26
      2     13    5.0531     5.3219   5.2648       5.12
      3     23    5.0355     5.3184   5.3183      17.22
      4     38    5.0013     5.2569   5.3775     212.08
      5     59    4.9228     5.2635   5.2009    1520.80
      6     87    4.8933     5.3610   5.3224    3487.91
      7    123    4.8733     5.4923   5.5168    6959.24
      8    168    4.8437     5.6877   5.9085    9344.45

taban cizgisi sinamada 6.5324 m3, denenen aday sayisi 8
```

Eğitim sütunu tek yönlüdür ve **kesintisiz iyileşir**: 5,0757'den 4,8437'ye. Bu bir bulgu değil,
bir kimliktir. Derece $d$'nin sütunları derece $d-1$'in sütunlarını kapsar, dolayısıyla küçük
derecenin çözümü büyük derecede de erişilebilir; en küçük kareler onu ancak iyileştirebilir.
Eğitim sayısının artan bir sütun kümesi boyunca kötüleşmesi olanaksızdır ve bu yüzden eğitim
sayısı derece seçiminde hiçbir zaman kullanılamaz.

Doğrulama sütunu iki yönlüdür: 5,3225'ten 5,2569'a iner, sonra 5,6877'ye tırmanır. Ağırlık
sütunu tırmanışın mekanizmasını gösteriyor. Katsayı büyüklüğü 3,26'dan 9.344,45'e, yani yaklaşık üç
bin katına çıkıyor. Yüksek dereceli tek terimliler birbirinin neredeyse doğrusal birleşimidir —
altı ayrık değer alan bir sütunun altıncı kuvveti daha düşük kuvvetlerinin bir birleşimidir — ve
model bu neredeyse aynı sütunlara devasa, birbirini götüren katsayılar veriyor. Eğitim kümesinde
bu bedava; ayrılmış kümede o katsayılar tam olarak götürülmüyor ve fark hataya dönüşüyor.

#### Seçim Hangi Kümeden Okundu

Sekiz satırlık tablodan bir derece seçmek gerekiyor ve bu seçim iki farklı kümeden yapılabilir.

```python
en_dogrulama = min(SONUC, key=lambda t: t[3])
en_sinama = min(SONUC, key=lambda t: t[4])
birinci = SONUC[0]
print(f"dogrulamadan okunan derece {en_dogrulama[0]}: dogrulama "
      f"{en_dogrulama[3]:.4f}, sinama {en_dogrulama[4]:.4f}")
print(f"sinamadan okunsaydi derece {en_sinama[0]}: dogrulama "
      f"{en_sinama[3]:.4f}, sinama {en_sinama[4]:.4f}")
print(f"iki secim arasindaki fark {en_dogrulama[4] - en_sinama[4]:.4f} m3")
print(f"derece 1 sinamada {birinci[4]:.4f}; dogrulamadan secilen derece "
      f"{en_dogrulama[4] - birinci[4]:+.4f} m3 ile onun gerisinde\n")
print(f"{'derece':>7} {'ayrisma (dogrulama - egitim)':>30}")
for d, sutun, e, dg, sn in SONUC:
    print(f"{d:>7} {dg - e:>30.4f}")
```

```
dogrulamadan okunan derece 4: dogrulama 5.2569, sinama 5.3775
sinamadan okunsaydi derece 5: dogrulama 5.2635, sinama 5.2009
iki secim arasindaki fark 0.1766 m3
derece 1 sinamada 5.3096; dogrulamadan secilen derece +0.0679 m3 ile onun gerisinde

 derece   ayrisma (dogrulama - egitim)
      1                         0.2467
      2                         0.2687
      3                         0.2828
      4                         0.2556
      5                         0.3407
      6                         0.4677
      7                         0.6189
      8                         0.8440
```

İlk üç satır kursun kuralının niçin gerekli olduğunu tek bir örnekte gösteriyor. Doğrulama
kümesi derece 4'ü seçiyor; o derece sınamada 5,3775 veriyor. Sınama kümesinden seçilseydi derece
5 gelirdi ve 5,2009 raporlanırdı. İki sayı arasındaki 0,1766 m³, dersin sekiz adaylık bütçesinin
bedelidir ve modelin öğrendiği hiçbir şeyden gelmiyor.

Dördüncü satır daha rahatsız edicidir. Doğrulamadan seçilen derece 4, sınamada derece 1'in
**0,0679 m³ gerisinde**. Yani sekiz aday deneyip doğrulama kümesine bakarak seçmek, hiç
denemeyip en basit modelde kalmaktan kötü bir sonuç üretti. Bu bir hesap hatası değil, doğrulama
kümesinin de 252 satır olmasının sonucudur; o kümedeki en küçük sayı, o kümenin kendi
saçılmasını da içerir. Kursun ikinci iddiası bu satırda ilk kez ölçülüyor: **daha güçlü bir aile
daha zayıf olanı geçmedi.**

Ayrışma sütunu tabloyu tek bir sayıya indiriyor. Doğrulama ile eğitim arasındaki fark derece 4'e
kadar 0,25 ile 0,28 arasında duruyor, derece 5'te 0,3407'ye çıkıyor ve derece 8'de 0,8440
oluyor. Ayrışmanın başladığı yer derece 5'tir; doğrulama sayısının dibe vurduğu yerden bir
sonraki adım.

#### Bu Özniteliklerin Tavanı

Derece 8'de 168 sütun var. Bu sütunların hepsi dört özniteliğin işlevleridir ve o dört
öznitelik yalnız sonlu sayıda birleşim üretir.

```python
hucre = {}
for x in EGT:
    hucre.setdefault((x["bolge_ad"], x["hane"], x["memnuniyet"], x["donem"]),
                     []).append(x["hedef"])
ORTALAMA = {k: sum(v) / len(v) for k, v in hucre.items()}


def hucre_kestirimi(x):                     # her hucreye egitim ortalamasi: tavan model
    return ORTALAMA.get((x["bolge_ad"], x["hane"], x["memnuniyet"], x["donem"]), ORT)


print(f"ayrik oznitelik birlesimi 5*6*5*3 = {5 * 6 * 5 * 3}, "
      f"egitimde dolu hucre {len(hucre)}")
print(f"hucre ortalamasi modeli: egitim {kok(hucre_kestirimi, EGT):.4f}, "
      f"sinama {kok(hucre_kestirimi, SIN):.4f}")
print(f"sinamada gorulen ama egitimde bulunmayan hucre "
      f"{sum(1 for x in SIN if (x['bolge_ad'], x['hane'], x['memnuniyet'], x['donem']) not in ORTALAMA)}")
```

```
ayrik oznitelik birlesimi 5*6*5*3 = 450, egitimde dolu hucre 268
hucre ortalamasi modeli: egitim 3.8418, sinama 6.3887
sinamada gorulen ama egitimde bulunmayan hucre 33
```

Hücre ortalaması modeli, bu dört öznitelikle kurulabilecek en esnek yordamdır: her birleşime
kendi eğitim ortalamasını söyler ve hiçbir biçim varsayımı taşımaz. Eğitim kümesinde 3,8418
veriyor, yani derece 8 polinomunun 4,8437'sini rahatça geçiyor. Sınama kümesinde ise 6,3887
veriyor ve taban çizgisinin 6,5324'üne yalnızca 0,1437 m³ yaklaşıyor.

İki sayı arasındaki bu uçurum derece süpürmesinin nereye doğru gittiğini söylüyor. 268 dolu
hücreye 756 satır düşüyor, yani hücre başına ortalama üç abone; üç abonenin ortalaması bir
kestirim değil, o üç abonenin kendisidir. Polinom derecesi büyüdükçe model bu yordama yaklaşıyor
ve sınama sayısı da onun sayısına doğru gidiyor. Son satır aynı sınırı öteki yönden gösteriyor:
sınama kümesindeki 252 abonenin 33'ü eğitim kümesinde hiç görülmemiş bir hücreye düşüyor ve
onlara söylenecek tek şey taban çizgisidir.

#### Özet

- Polinom öznitelikleri model ailesini değiştirmez; doğrusal model katsayılarında doğrusaldır,
  sütunlarında değil. Derece 1'den 8'e giderken sütun sayısı 7'den 168'e çıkıyor.
- Eğitim kök hatası 5,0757'den 4,8437 m³'e kesintisiz iniyor ve bu bir kimliktir: büyük derecenin
  sütunları küçüğünü kapsar. Eğitim sayısı derece seçiminde kullanılamaz.
- Doğrulama kök hatası 5,2569'da dibe vurup 5,6877'ye tırmanıyor; ağırlık büyüklüğü aynı aralıkta
  3,26'dan 9.344,45'e çıkıyor. Tırmanışın mekanizması, neredeyse aynı sütunlara verilen birbirini
  götüren devasa katsayılardır.
- Doğrulamadan okunan derece 4 sınamada 5,3775 veriyor; sınamadan okunsaydı derece 5 ile 5,2009
  raporlanırdı ve fark 0,1766 m³'tür. Aynı seçim sınamada derece 1'in 0,0679 m³ gerisinde kalıyor.
- Dört öznitelik 450 ayrık birleşim üretir ve eğitimde 268'i doludur; her hücreye kendi
  ortalamasını söyleyen tavan model eğitimde 3,8418, sınamada 6,3887 verir. Sınamadaki 33 abone
  eğitimde hiç görülmemiş bir hücreye düşer.

#### Sonraki Adım

Derece süpürmesi bir düğmeyi tam sayı adımlarla çevirdi: derece 4 ile 5 arasında ara durak yok
ve derece 5'e geçmek yirmi bir sütunu birden ekliyor. Sütunları eklemekle silmek arasında üçüncü
bir yol vardır — hepsini bırakıp katsayılarını **cezalandırmak**. M27/K01 bu sezgiyi tek bir ceza
türüyle kurmuştu ve ceza katsayısı büyüdükçe iki sayının ters yönde gittiğini ölçmüştü. Sonraki
ders cezanın **biçimini** değiştirir: karesel, mutlak ve karma ceza aynı sütun kümesine ayrı ayrı
uygulanır ve ölçü artık hata değil **seyrekliktir** — hangi ceza kaç katsayıyı tam sıfırlıyor,
hangisi hepsini küçültüp bırakıyor ve sıfırlanan sütunların ayrılmış kümedeki karşılığı ne.

### Düzenlileştirme Cezaları

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/duzenlilestirme-cezalari

Karesel, mutlak ve karma cezanın aynı 87 sütuna uygulanıp seyreklikle ölçülmesi: karesel ceza altı ayarın hiçbirinde tek bir katsayıyı bile tam sıfırlamaz, mutlak ceza 42'den 84'e kadar sıfırlar, karma ceza 24 ile 83 arasında kalır. Mutlak ceza 10'da 87 katsayının 65'i tam sıfırdır ve kalan 22 sütun sınamada 5,2909 m³ verir; karesel ceza aynı sayıya 87 sütunun hepsini tutarak 5,2954 ile ulaşır. Doğrulamadan okunan seçim karesel ceza 3'tür ve sınamada 5,2954 verir, sınamadan okunsaydı mutlak ceza 10 seçilirdi ve fark 0,0046 m³ olurdu. Mutlak ceza 3000'de 87 katsayının hepsi sıfırlanır ve model tam olarak taban çizgisine, 6,5324'e döner; karesel ceza 100000'de bile 6,5086'da kalır.

Önceki ders bir düğmeyi tam sayı adımlarla çevirdi. Derece 4 ile 5 arasında ara durak yoktu ve
derece 5'e geçmek yirmi bir sütunu birden ekledi. Sütun eklemek ya da silmek dışında üçüncü bir
yol var: sütunları bırakıp **katsayılarını cezalandırmak**. M27/K01'in Genelleme konusu bu sezgiyi
tek bir ceza türüyle kurmuş, ceza katsayısı büyüdükçe eğitim sayısının kesintisiz kötüleştiğini,
ayrılmış küme sayısının önce iyileşip sonra kötüleştiğini ölçmüş ve en iyi noktanın doğrulama
kümesinden okunduğunu göstermişti. O süpürme burada tekrarlanmaz.

Bu dersin sorusu cezanın **büyüklüğü** değil **biçimidir**. Katsayıların kare toplamını cezalandırmak
ile mutlak değer toplamını cezalandırmak iki farklı yordam üretir ve fark hatada değil, **kaç
katsayının tam olarak sıfır olduğunda** görünür. Ölçü budur: **seyreklik**. Hangi ceza kaç sütunu
modelden atıyor, hangisi hepsini küçültüp bırakıyor ve atılan sütunların ayrılmış kümedeki
karşılığı ne.

- **DM18.** Küme, bölme, hedef ve taban çizgisi önceki iki dersteki gibidir: kurgu abone tablosu,
  tohum 20260218, 756/252/252, sınama kümesinde 6,5324 m³ kök hata.
- **DM19.** Sütun kümesi **derece 6 polinomudur**: dört bölge göstergesi ve üç sayısal sütunun
  toplam derecesi altıyı geçmeyen bütün tek terimlileri, toplam 87 sütun. Derece bu derste
  değişmez; değişen tek şey cezadır.
- **DM20.** Sütunlar eğitim kümesinden öğrenilen ortalama ve değişkenlikle ölçeklenir. Ceza
  ölçeğe duyarlıdır: ölçeklenmemiş sütunlarda aynı ceza katsayısı farklı sütunlara farklı
  şiddette uygulanır.
- **DM21.** Sabit terim cezalanmaz. Cezalansaydı bütün katsayılar sıfırlandığında model sıfır
  söylerdi; cezalanmadığında taban çizgisine, yani eğitim ortalamasına döner.
- **DM22.** Üç ceza biçimi: **karesel ceza** katsayıların kare toplamını, **mutlak ceza** mutlak
  değer toplamını, **karma ceza** ikisinin yarı yarıya karışımını cezalandırır. Ürün ya da mecaz
  ad kullanılmaz; ceza biçimiyle anılır.
- **DM23.** Çözüm **eş güdümlü iniştir**: her katsayı sırayla, ötekiler sabit tutularak
  güncellenir. Mutlak cezanın türevi sıfırda tanımsız olduğu için kapalı çözüm yoktur; iniş,
  eşik altında kalan katsayıyı **tam sıfıra** oturtur.
- **DM24.** Tur sayısı 120'dir ve bütün ayarlarda sabittir. Tur sayısı bir ayar değişkeni olarak
  süpürülmez; süpürülseydi aday sayısı üçe katlanırdı.
- **DM25.** "Tam sıfır" karşılaştırması `==` ile yapılmaz; eşik $10^{-9}$'dur ve iniş yordamının
  sıfırladığı katsayı tam olarak `0.0` atandığı için bu eşik yalnız bir güvencedir.
- **DM26.** Denenen aday sayısı **18**'dir: üç ceza biçimi, altı ceza katsayısı.

#### Üç Ceza Biçimi

Kurulum önceki iki dersin kurulumudur.

```python
# ceza.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum ve ayni bolme.
import math

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
    x = {"bolge_ad": k["bolge"][0], "hane": k["hane"], "memnuniyet": k["memnuniyet"],
         "donem": len(v), "hedef": round(sum(v) / len(v), 2)}
    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:]
ORT = sum(x["hedef"] for x in EGT) / len(EGT)


def kok(kestirim, s):
    return math.sqrt(sum((kestirim(x) - x["hedef"]) ** 2 for x in s) / len(s))


print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, "
      f"taban cizgisi sinamada {kok(lambda x: ORT, SIN):.4f} m3")
```

```
egitim 756, dogrulama 252, sinama 252, taban cizgisi sinamada 6.5324 m3
```

Eş güdümlü iniş, üç ceza biçimini tek bir yordamda toplar. Her katsayı için ötekiler sabit
tutulup en iyi değer hesaplanır; mutlak cezanın payı bu değeri sıfıra doğru **iter** ve eşiği
aşamayan katsayı tam sıfıra oturur.

```python
import itertools

SAYISAL = ["hane", "memnuniyet", "donem"]
GOSTERGE = ["b_guney", "b_dogu", "b_bati", "b_merkez"]
DERECE = 6
US = [u for u in itertools.product(range(DERECE + 1), repeat=3) if 1 <= sum(u) <= DERECE]
# h hane, m memnuniyet, d donem; h^2m^1 = hane karesi carpi memnuniyet
AD = GOSTERGE + ["".join(f"{a}^{k}" for a, k in zip("hmd", u) if k) for u in US]


def ham(x):
    return ([float(x[a]) for a in GOSTERGE]
            + [math.prod(x[a] ** k for a, k in zip(SAYISAL, u)) for u in US])


H = [ham(x) for x in EGT]
P = len(AD)
O = [sum(r[j] for r in H) / len(H) for j in range(P)]
S = [max(1e-9, math.sqrt(sum((r[j] - O[j]) ** 2 for r in H) / len(H))) for j in range(P)]


def uygula(x):
    h = ham(x)
    return [(h[j] - O[j]) / S[j] for j in range(P)]


KOL = [[(H[i][j] - O[j]) / S[j] for i in range(len(H))] for j in range(P)]
KK = [sum(v * v for v in KOL[j]) for j in range(P)]
Y = [x["hedef"] for x in EGT]


def inis(ceza, mutlak_pay, tur=120):        # MODELDIR: cezali dogrusal baglanim
    w, sabit = [0.0] * P, sum(Y) / len(Y)
    art = [y - sabit for y in Y]
    for _ in range(tur):
        for j in range(P):
            if KK[j] < 1e-12:
                continue
            r = sum(KOL[j][i] * art[i] for i in range(len(art))) + KK[j] * w[j]
            c1, c2 = ceza * mutlak_pay, ceza * (1 - mutlak_pay)
            if r > c1:
                yeni = (r - c1) / (KK[j] + c2)
            elif r < -c1:
                yeni = (r + c1) / (KK[j] + c2)
            else:
                yeni = 0.0
            fark = yeni - w[j]
            if fark:
                for i in range(len(art)):
                    art[i] -= KOL[j][i] * fark
                w[j] = yeni
        d = sum(art) / len(art)
        sabit += d
        for i in range(len(art)):
            art[i] -= d
    return sabit, w


def kestirici(m):
    return lambda x: m[0] + sum(a * b for a, b in zip(m[1], uygula(x)))


CEZA = (3, 10, 30, 100, 300, 1000)
BICIM = (("karesel", 0.0), ("mutlak", 1.0), ("karma", 0.5))
SONUC = []
print(f"sutun {P}, taban cizgisi sinamada {kok(lambda x: ORT, SIN):.4f} m3")
print(f"\n{'ceza bicimi':<12} {'katsayi':>8} {'tam sifir':>10} {'egitimde':>9} "
      f"{'dogrulama':>10} {'sinama':>8}")
for ad, pay in BICIM:
    for c in CEZA:
        m = inis(c, pay)
        g = kestirici(m)
        sifir = sum(1 for v in m[1] if abs(v) < 1e-9)
        SONUC.append((ad, c, sifir, kok(g, DOG), kok(g, SIN)))
        print(f"{ad:<12} {c:>8} {sifir:>10} {kok(g, EGT):>9.4f} "
              f"{kok(g, DOG):>10.4f} {kok(g, SIN):>8.4f}")
print(f"\ndenenen aday sayisi {len(SONUC)}")
```

```
sutun 87, taban cizgisi sinamada 6.5324 m3

ceza bicimi   katsayi  tam sifir  egitimde  dogrulama   sinama
karesel             3          0    5.0348     5.2982   5.2954
karesel            10          0    5.0367     5.3009   5.2968
karesel            30          0    5.0408     5.3088   5.3053
karesel           100          0    5.0581     5.3313   5.3342
karesel           300          0    5.1289     5.4180   5.4387
karesel          1000          0    5.3907     5.6894   5.7325
mutlak              3         42    5.0371     5.3028   5.2933
mutlak             10         65    5.0429     5.3092   5.2909
mutlak             30         74    5.0515     5.3179   5.3077
mutlak            100         79    5.0810     5.3486   5.3443
mutlak            300         83    5.1447     5.4056   5.4420
mutlak           1000         84    5.6036     5.8955   5.9568
karma               3         24    5.0360     5.3015   5.2947
karma              10         47    5.0405     5.3058   5.2941
karma              30         68    5.0477     5.3158   5.3022
karma             100         72    5.0722     5.3458   5.3483
karma             300         75    5.1431     5.4192   5.4509
karma            1000         83    5.4757     5.7450   5.8260

denenen aday sayisi 18
```

Üçüncü sütun dersin yanıtıdır ve üç blok arasındaki fark tartışmasız. Karesel cezanın satırlarında
o sütun **altı ayarın altısında da sıfırdır**: ceza bin katına çıkarıldığında bile 87 katsayının
tek biri tam sıfır olmuyor. Mutlak ceza aynı aralıkta 42'den 84'e çıkıyor. Karma ceza tam
ortada duruyor: 24'ten 83'e.

Ayrım katsayının sıfıra yaklaşırken hissettiği kuvvetten geliyor. Karesel cezanın türevi
katsayıyla orantılıdır; katsayı küçüldükçe onu daha da küçültmeye çalışan kuvvet de küçülür ve
sıfıra hiçbir zaman ulaşmaz. Mutlak cezanın türevi ise büyüklükten bağımsızdır — katsayı ne kadar
küçük olursa olsun aynı sabit kuvvetle itilir — ve o kuvvet katsayının hata düşürme katkısını
aştığı anda katsayı sıfırda kalır.

Son iki sütun aynı tabloyu hata tarafından okuyor ve orada fark küçüktür. Üç biçimin en iyi
doğrulama sayıları 5,2982, 5,3028 ve 5,3015; aralarındaki mesafe 0,005 m³'ün altında. Ceza
biçimi seçimi **hata sayısında görünmüyor**; sütun sayısında görünüyor.

#### Seyrekliğin Karşılığı

On sekiz aday arasından bir tanesini seçmek gerekiyor ve seyrekliğin bedeli tam burada okunur.

```python
en_dogrulama = min(SONUC, key=lambda t: t[3])
en_sinama = min(SONUC, key=lambda t: t[4])
print(f"dogrulamadan okunan {en_dogrulama[0]} ceza {en_dogrulama[1]}: "
      f"{en_dogrulama[2]} tam sifir, sinama {en_dogrulama[4]:.4f}")
print(f"sinamadan okunsaydi {en_sinama[0]} ceza {en_sinama[1]}: "
      f"{en_sinama[2]} tam sifir, sinama {en_sinama[4]:.4f}")
print(f"iki secim arasindaki fark {en_dogrulama[4] - en_sinama[4]:.4f} m3\n")
k, m = inis(3, 0.0), inis(10, 1.0)
print(f"{'esik':>8} {'karesel ceza 3':>16} {'mutlak ceza 10':>16}  (mutlak degeri altinda kalan katsayi)")
for e in (1e-9, 0.001, 0.01, 0.1):
    print(f"{e:>8} {sum(1 for v in k[1] if abs(v) < e):>16} "
          f"{sum(1 for v in m[1] if abs(v) < e):>16}")
kalan = [AD[j] for j in range(P) if abs(m[1][j]) > 1e-9]
print(f"\nmutlak ceza 10'un birakigi {len(kalan)} sutun: {', '.join(kalan[:9])} ...")
for c in (3000, 100000):
    for ad, pay in (("mutlak", 1.0), ("karesel", 0.0)):
        mm = inis(c, pay)
        print(f"{ad} ceza {c}: tam sifir {sum(1 for v in mm[1] if abs(v) < 1e-9)}/{P}, "
              f"sabit terim {mm[0]:.4f}, sinama {kok(kestirici(mm), SIN):.4f}")
print(f"egitim ortalamasi {ORT:.4f}, taban cizgisi sinamada {kok(lambda x: ORT, SIN):.4f}")
```

```
dogrulamadan okunan karesel ceza 3: 0 tam sifir, sinama 5.2954
sinamadan okunsaydi mutlak ceza 10: 65 tam sifir, sinama 5.2909
iki secim arasindaki fark 0.0046 m3

    esik   karesel ceza 3   mutlak ceza 10  (mutlak degeri altinda kalan katsayi)
   1e-09                0               65
   0.001                0               65
    0.01                5               66
     0.1               29               71

mutlak ceza 10'un birakigi 22 sutun: b_guney, b_dogu, b_bati, b_merkez, d^1, m^1d^4, m^1d^5, m^3d^1, h^1 ...
mutlak ceza 3000: tam sifir 87/87, sabit terim 19.7665, sinama 6.5324
karesel ceza 3000: tam sifir 0/87, sabit terim 19.7665, sinama 6.0935
mutlak ceza 100000: tam sifir 87/87, sabit terim 19.7665, sinama 6.5324
karesel ceza 100000: tam sifir 0/87, sabit terim 19.7665, sinama 6.5086
egitim ortalamasi 19.7665, taban cizgisi sinamada 6.5324
```

Doğrulama kümesi karesel cezayı seçiyor ve o seçim 87 katsayının hepsini tutuyor. Sınamadan
seçilseydi mutlak ceza 10 gelirdi: 65 katsayı tam sıfır, kalan 22 sütun ve 5,2909. İki seçim
arasındaki fark 0,0046 m³'tür ve bu, 252 satırlık bir küme üzerinde on sekiz kez en küçüğü
aramanın bedelidir.

Ama tablonun asıl söylediği fark bu değil. **Aynı hata düzeyine iki ayrı sütun sayısıyla
varılıyor**: karesel ceza 87 sütunla 5,2954, mutlak ceza 22 sütunla 5,2909. Seyrekliğin ayrılmış
kümedeki karşılığı bu kümede **sıfırdır** — 65 sütun modelden çıkıyor ve hata sayısı
kötüleşmiyor. Seyreklik bir başarım kazancı değil, bir **maliyet kazancıdır**: kestirim için
hesaplanacak, saklanacak ve açıklanacak sütun sayısı dörtte bire iniyor.

Eşik tablosu iki cezanın farkını daha keskin veriyor. Karesel cezanın 87 katsayısından hiçbiri
$10^{-9}$'un altında değil, ama 29'u 0,1'in altında. Yani karesel ceza da sütunları
işlevsizleştiriyor — yalnız onları **modelde tutuyor**. Mutlak ceza ise 65'ini gerçekten atıyor
ve 0,1 eşiğinde ancak 71'e çıkıyor; attıklarının dışında kalanları küçültmüyor.

Son dört satır iki biçimin sınırını gösteriyor. Mutlak ceza 3000'de 87 katsayının hepsi
sıfırlanıyor, sabit terim eğitim ortalamasında kalıyor ve model sınamada **tam olarak 6,5324**,
yani taban çizgisinin kendisini veriyor. Karesel ceza aynı ayarda 6,0935'te, yüz bin katsayıda
ise 6,5086'da. Karesel ceza taban çizgisine yaklaşır ama sonlu bir ayarda ona **ulaşmaz**; mutlak
ceza ulaşır. İki biçim arasındaki fark, hata eğrisinin herhangi bir yerinde değil, uçlarında
görünür.

#### Özet

- Cezanın biçimi hata sayısında değil sütun sayısında görünür: üç biçimin en iyi doğrulama
  sayıları 5,2982, 5,3028 ve 5,3015 ile birbirinin 0,005 m³ içindedir.
- Karesel ceza altı ayarın hiçbirinde tek bir katsayıyı bile tam sıfırlamaz; mutlak ceza 42'den
  84'e, karma ceza 24'ten 83'e çıkar. Sebep türevin biçimidir: karesel cezanın kuvveti katsayı
  küçüldükçe küçülür, mutlak cezanınki sabit kalır.
- Aynı hata düzeyine iki ayrı sütun sayısıyla varılıyor: karesel ceza 87 sütunla 5,2954, mutlak
  ceza 22 sütunla 5,2909. Seyrekliğin bu kümedeki karşılığı başarım değil maliyettir.
- Karesel ceza sütunları işlevsizleştirir ama atmaz: 87 katsayısından 29'u 0,1'in altındadır ve
  hiçbiri tam sıfır değildir.
- Mutlak ceza 3000'de bütün katsayılar sıfırlanır ve model tam olarak taban çizgisine, 6,5324'e
  döner; karesel ceza yüz bin katsayıda bile 6,5086'da kalır ve sonlu bir ayarda tabana ulaşmaz.
- Doğrulamadan okunan seçim karesel ceza 3'tür (sınamada 5,2954); sınamadan okunsaydı mutlak ceza
  10 seçilirdi (5,2909) ve on sekiz adaylık bütçenin bedeli 0,0046 m³ olurdu.

#### Sonraki Adım

Üç derste hedef sürekli bir sayıydı ve model o sayıyı doğrudan söyledi. Kursun asıl through-line
problemi ise bir sınıflandırmadır: bir abonenin kaçak ya da arıza şüphesi taşıyıp taşımadığı ve
oradaki taban çizgisi 0,7579. Aynı doğrusal model bu problemde doğrudan kullanılamaz, çünkü
düzlemin çıktısı eksi sonsuzdan artı sonsuza uzanır ve etiket yalnız sıfır ile birdir. Aradaki
köprü çıktının bir olasılığa sıkıştırılmasıdır. Sonraki ders bu sıkıştırmayı yazar ve ölçüsü
şudur: aynı model 0,5 eşiğiyle bir sayı, başka eşiklerle başka sayılar verir; eşiği seçmek ayrı
bir iştir ve `model-degerlendirme` konusuna bırakılır. Burada gösterilecek olan, çıktının **eşikten
önce** sürekli olduğu ve bu sürekliliğin tek bir sınıf etiketinden fazlasını taşıdığıdır.

### Lojistik Bağlanım

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/lojistik-baglanim

Doğrusal bir düzlemin çıktısının olasılığa sıkıştırılması ve o olasılığın eşikten önce sürekli olduğunun ölçülmesi: on iki sabit sütunla model sınama kümesinde 0,7937 verir ve taban çizgisini 0,0357, yani dokuz aboneyi geçer. Dokuz aday denendiğinde doğrulama kümesinde beşi 0,8373 ile berabere kalır ve o beşin sınama sayıları 0,7937 ile 0,8016 arasında bölünür; doğrulamadan okunan ayar sınamada 0,7937 verir, fark iki abonedir. Sınama kümesindeki 252 abonenin 252'si farklı bir olasılık alır; eşik 0,20'den 0,80'e süpürüldüğünde tek bir model doğrulukta 0,7183 ile 0,8016 arasında yedi ayrı sayı verir, işaretlenen abone 102'den 5'e iner ve 0,40 ile 0,60 arasında 27 abone sınıf değiştirir.

Üç derste hedef sürekli bir sayıydı ve model o sayıyı doğrudan söyledi. Kursun through-line
problemi ise bir sınıflandırmadır: bir abone kaçak ya da arıza şüphesi taşıyor mu ve oradaki
taban çizgisi 0,7579. Aynı doğrusal düzlem bu problemde doğrudan kullanılamaz. Düzlemin çıktısı
eksi sonsuzdan artı sonsuza uzanır; etiket ise yalnız sıfır ile birdir. Bir aboneye "-3,4
şüphelidir" demek anlamsızdır.

Köprü, düzlemin çıktısını sıfır ile bir arasına sıkıştıran S biçimli bir işlevdir. Sıkıştırılmış
sayı bir **olasılık** gibi okunur ve model bu haliyle **lojistik bağlanım** adını alır — bağlanım
yordamıdır, çünkü sürekli bir sayı kestirir; sınıflandırıcı olması ancak o sayıya bir **eşik**
uygulandıktan sonradır. Bu dersin ölçüsü tam olarak buradadır: eşik uygulanmadan önce çıktının
ne taşıdığı. Eşiği seçmek ayrı bir iştir ve `model-degerlendirme` konusuna bırakılır.

- **DM27.** Küme, bölme ve tohum önceki üç dersteki gibidir. Hedef **şüpheli** etiketidir ve
  taban çizgisi en sık sınıfı söylemektir: sınama kümesinde 0,7579.
- **DM28.** Öznitelik kümesi M27/K02'nin bıraktığı **on iki sütundur** ve kurs boyunca sabit
  kalır: ortalama tüketim, oynaklık, hane büyüklüğü, memnuniyet, dönem sayısı, sıfır okuma
  göstergesi, kişi başına tüketim ve beş bölge göstergesi. Bağlanım derslerinin dışarıda
  bıraktığı sütunlar burada kullanılır, çünkü hedef artık tüketim değil etikettir.
- **DM29.** Sütunlar eğitim kümesinden öğrenilen ortalama ve değişkenlikle ölçeklenir.
  M27/K02'nin Hazırlama Adımları konusu bu adımın eğim temelli bir modelde zorunlu olduğunu
  ölçmüştü; burada varsayılır.
- **DM30.** Beş bölge göstergesinin hepsi kullanılır. Bağlanım derslerinde biri referans olarak
  dışarıda bırakılmıştı; eğim temelli çözüm tekil takım çözmediği için burada gerekmiyor.
- **DM31.** Çözüm **eğim inişidir**: ağırlıklar kayıp yüzeyinin eğimi yönünde adım adım
  güncellenir. Ayar değişkenleri öğrenme adımı ve tur sayısıdır.
- **DM32.** Denenen aday sayısı **9**'dur: üç öğrenme adımı, üç tur sayısı.
- **DM33.** Ölçü doğruluktur ve sınama kümesi 252 abonedir; bir abone 0,0040 eder. Doğruluğun
  neyi sakladığı ve hangi ölçütle değiştirilmesi gerektiği `model-degerlendirme/01`–`02`'ye
  bırakılır.
- **DM34.** Eşik süpürmesi bu derste **seçim yapmaz**; tek bir modelin eşikten bağımsız olduğunu
  göstermek için basılır. İşletme noktasının bir bedel hesabıyla seçilmesi
  `model-degerlendirme/03`'ün konusudur.
- **DM35.** Söylenen olasılığın gerçekleşen orana oturup oturmadığı ayrı bir sorudur ve
  `model-degerlendirme/04`'e bırakılır.

#### Düzlemden Olasılığa

Kurulum kümeyi üretir, on iki sütunu sabitler ve iki oranı basar.

```python
# lojistik.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum ve ayni bolme;
# hedef bu derste kacak ya da ariza suphesi etiketidir.
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)}
    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"]
ORTALAMA = {a: statistics.fmean(x[a] for x in EGT) for a in ALAN}
SAPMA = {a: max(1e-9, statistics.pstdev([x[a] for x in EGT])) for a in ALAN}
TABAN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)


def olcekli(x):                             # OGRENME egitimden, UYGULAMA her kumeye
    return [1.0] + [(x[a] - ORTALAMA[a]) / SAPMA[a] for a in ALAN]


print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, sutun {len(ALAN)}")
print(f"supheli orani egitimde {sum(x['supheli'] for x in EGT) / len(EGT):.4f}, "
      f"sinamada {sum(x['supheli'] for x in SIN) / len(SIN):.4f}")
print(f"taban cizgisi (en sik sinif) sinamada {TABAN:.4f}")
```

```
egitim 756, dogrulama 252, sinama 252, sutun 12
supheli orani egitimde 0.2222, sinamada 0.2421
taban cizgisi (en sik sinif) sinamada 0.7579
```

Sıkıştırma ve eğim inişi birlikte durur. Sıkıştırılan sayı, doğrusal puanın **logit**
(log-odds) olarak okunmasıyla aynı şeydir: puan sıfırken olasılık 0,5, puan büyüdükçe bire,
küçüldükçe sıfıra gider ve hiçbir zaman uçlara değmez.

```python
def sikistir(z):                            # S bicimli islev, tasma korumali
    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)


X = [olcekli(x) for x in EGT]
Y = [x["supheli"] for x in EGT]
P = len(X[0])


def lojistik(adim, tur):                    # MODELDIR: lojistik baglanim
    w = [0.0] * P
    for _ in range(tur):
        g = [0.0] * P
        for i in range(len(X)):
            h = sikistir(sum(w[j] * X[i][j] for j in range(P))) - Y[i]
            for j in range(P):
                g[j] += h * X[i][j]
        for j in range(P):
            w[j] -= adim * g[j] / len(X)
    return w


def olasilik(w, x):
    return sikistir(sum(a * b for a, b in zip(w, olcekli(x))))


def dogruluk(w, s, esik=0.5):
    return sum((olasilik(w, x) > esik) == x["supheli"] for x in s) / len(s)


SONUC = []
print(f"{'ogrenme adimi':>14} {'tur':>5} {'egitimde':>9} {'dogrulama':>10} {'sinama':>8}")
for adim in (0.1, 0.3, 1.0):
    for tur in (100, 300, 600):
        w = lojistik(adim, tur)
        SONUC.append((adim, tur, dogruluk(w, DOG), dogruluk(w, SIN), w))
        print(f"{adim:>14} {tur:>5} {dogruluk(w, EGT):>9.4f} "
              f"{SONUC[-1][2]:>10.4f} {SONUC[-1][3]:>8.4f}")
en_d = max(SONUC, key=lambda t: t[2])
en_s = max(SONUC, key=lambda t: t[3])
print(f"\ntaban cizgisi {TABAN:.4f}, denenen aday sayisi {len(SONUC)}")
print(f"dogrulamadan okunan ayar ({en_d[0]}, {en_d[1]}): dogrulama {en_d[2]:.4f}, "
      f"sinama {en_d[3]:.4f}")
print(f"sinamadan okunsaydi ayar ({en_s[0]}, {en_s[1]}): dogrulama {en_s[2]:.4f}, "
      f"sinama {en_s[3]:.4f}")
print(f"iki secim arasindaki fark {en_s[3] - en_d[3]:.4f}, yani "
      f"{round((en_s[3] - en_d[3]) * len(SIN))} abone")
print(f"dogrulamada ayni en yuksek sayiyi veren aday "
      f"{sum(1 for t in SONUC if abs(t[2] - en_d[2]) < 1e-12)}")
W = en_d[4]
```

```
 ogrenme adimi   tur  egitimde  dogrulama   sinama
           0.1   100    0.8175     0.8056   0.8016
           0.1   300    0.8201     0.8373   0.7937
           0.1   600    0.8188     0.8373   0.8016
           0.3   100    0.8201     0.8373   0.7937
           0.3   300    0.8175     0.8333   0.8016
           0.3   600    0.8148     0.8333   0.8016
           1.0   100    0.8161     0.8333   0.8016
           1.0   300    0.8148     0.8373   0.8016
           1.0   600    0.8148     0.8373   0.8016

taban cizgisi 0.7579, denenen aday sayisi 9
dogrulamadan okunan ayar (0.1, 300): dogrulama 0.8373, sinama 0.7937
sinamadan okunsaydi ayar (0.1, 100): dogrulama 0.8056, sinama 0.8016
iki secim arasindaki fark 0.0079, yani 2 abone
dogrulamada ayni en yuksek sayiyi veren aday 5
```

Üçlü tamamlandı: taban çizgisi 0,7579, ailenin sayısı 0,7937, denenen aday sayısı 9. Fark
0,0357, yani sınama kümesinde dokuz abone. Dokuz adayın sınama sütunu yalnız iki ayrı değer
alıyor — 0,7937 ve 0,8016 — ve aradaki fark iki abonedir. Öğrenme adımı ile tur sayısı bu
problemde birbirinin yerine geçiyor: küçük adımla altı yüz tur, büyük adımla üç yüz tur aynı
yere varıyor.

Son satır kursun kuralını beklenmedik bir yerden gösteriyor. Dokuz adayın **beşi** doğrulama
kümesinde tam olarak aynı sayıyı, 0,8373'ü veriyor. Doğrulama kümesi 252 satırdır ve 0,0040'lık
adımlarla ölçer; beş farklı ayarın aynı basamağa düşmesi olağandır. Ama o beş aday sınama
kümesinde aynı sayıyı vermiyor. Beraberliği bozan kural — tablodaki ilk sırayı almak — modelin
hiçbir özelliği değildir ve iki abone ediyor.

#### Çıktı Eşikten Önce Süreklidir

Seçilen model tek bir ayardır, ama tek bir sayı vermez. Verdiği şey 252 olasılıktır ve o
olasılıklardan bir sınıf çıkarmak ayrı bir karardır.

```python
esit = [t for t in SONUC if abs(t[2] - en_d[2]) < 1e-12]
print(f"dogrulamada berabere kalan {len(esit)} adayin sinama sayilari: "
      f"{sorted({round(t[3], 4) for t in esit})}")
p = sorted((olasilik(W, x), x["supheli"]) for x in SIN)
print(f"sinamada farkli olasilik degeri {len({round(q, 9) for q, _ in p})}/{len(SIN)}, "
      f"en dusuk {p[0][0]:.4f}, en yuksek {p[-1][0]:.4f}\n")
print(f"{'olasilik araligi':<18} {'abone':>6} {'supheli':>8} {'supheli orani':>15}")
for k in range(5):
    a, b = k / 5, (k + 1) / 5
    d = [q for q in p if a <= q[0] < b or (k == 4 and q[0] >= b)]
    g = sum(t[1] for t in d)
    print(f"[{a:.1f}, {b:.1f})".ljust(18)
          + f" {len(d):>6} {g:>8} {g / len(d) if d else 0.0:>15.4f}")
print(f"\n{'esik':>6} {'dogruluk':>9} {'isaretlenen':>12} {'yakalanan':>10} "
      f"{'kacirilan':>10}")
for e in (0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80):
    isaret = sum(1 for q, _ in p if q > e)
    yakalanan = sum(1 for q, y in p if q > e and y)
    print(f"{e:>6.2f} {dogruluk(W, SIN, e):>9.4f} {isaret:>12} {yakalanan:>10} "
          f"{sum(t[1] for t in p) - yakalanan:>10}")
print(f"\n0,40 ile 0,60 arasinda sinif degistiren abone "
      f"{sum(1 for q, _ in p if 0.40 < q <= 0.60)}")
print(f"esik 0,5 sabit tutulunca dogruluk {dogruluk(W, SIN):.4f}, "
      f"taban cizgisi {TABAN:.4f}, en iyi esik 0,60'ta {dogruluk(W, SIN, 0.60):.4f}")
```

```
dogrulamada berabere kalan 5 adayin sinama sayilari: [0.7937, 0.8016]
sinamada farkli olasilik degeri 252/252, en dusuk 0.0196, en yuksek 0.9838

olasilik araligi    abone  supheli   supheli orani
[0.0, 0.2)            150       15          0.1000
[0.2, 0.4)             52       18          0.3462
[0.4, 0.6)             27       11          0.4074
[0.6, 0.8)             18       12          0.6667
[0.8, 1.0)              5        5          1.0000

  esik  dogruluk  isaretlenen  yakalanan  kacirilan
  0.20    0.7183          102         46         15
  0.30    0.7659           68         35         26
  0.40    0.7817           50         28         33
  0.50    0.7937           31         20         41
  0.60    0.8016           23         17         44
  0.70    0.7897           16         12         49
  0.80    0.7778            5          5         56

0,40 ile 0,60 arasinda sinif degistiren abone 27
esik 0,5 sabit tutulunca dogruluk 0.7937, taban cizgisi 0.7579, en iyi esik 0,60'ta 0.8016
```

İkinci satır dersin en yalın bulgusudur: 252 abonenin **252'si farklı bir olasılık alıyor**.
Model bir etiket üretmiyor, bir sıralama üretiyor. En düşük olasılık 0,0196, en yükseği 0,9838;
sıkıştırma uçlara değmiyor, çünkü S biçimli işlev sıfıra ve bire ancak sonsuzda ulaşır.

Aralık tablosu o sıralamanın boş olmadığını gösteriyor. En alt aralıktaki 150 abonenin 15'i,
yani onda biri şüpheli; en üst aralıktaki beş abonenin beşi de şüpheli. Sıralama etiketle aynı
yönde gidiyor. Söylenen olasılığın gerçekleşen orana **oturup oturmadığı** — örneğin 0,4–0,6
aralığındaki oranın 0,4074 çıkması — ayrı bir sorudur ve `model-degerlendirme/04`'e bırakılır.

Eşik tablosu dersin başlığını taşıyan sayıyı veriyor. Tek bir model, tek bir ağırlık kümesi, tek
bir eğitim — ve yedi ayrı doğruluk sayısı: 0,7183'ten 0,8016'ya. Eşik 0,20'de model 102 aboneyi
işaretliyor ve 61 şüphelinin 46'sını yakalıyor; eşik 0,80'de yalnız beş abone işaretleniyor ve
beşi de gerçekten şüpheli, ama 56 şüpheli kaçıyor. Bunların hiçbiri farklı bir model değil.

Bu yüzden "modelin doğruluğu 0,7937'dir" cümlesi eksiktir. Doğru cümle şudur: **0,5 eşiğiyle
0,7937'dir.** Eşik bir model özelliği değil, bir karardır ve karar 0,40 ile 0,60 arasında
gezdirilirse 27 abone sınıf değiştirir. Hangi eşiğin seçileceği yakalanan kaçakla boşa giden
saha ziyaretinin bedeline bağlıdır; o hesap `model-degerlendirme/03`'ün konusudur ve burada
yapılmaz.

#### Özet

- Lojistik bağlanım bir sınıflandırıcı değil, olasılık kestiren bir bağlanım yordamıdır;
  sınıflandırıcı olması ancak bir eşik uygulandıktan sonradır.
- On iki sabit sütunla model sınama kümesinde 0,7937 veriyor; taban çizgisi 0,7579, fark 0,0357
  ve denenen aday sayısı 9'dur.
- Dokuz adayın beşi doğrulama kümesinde tam olarak 0,8373 ile berabere kalıyor ama sınamada
  0,7937 ile 0,8016'ya bölünüyor; beraberliği bozan kural modelin özelliği değildir ve iki abone
  eder.
- Sınama kümesindeki 252 abonenin 252'si farklı bir olasılık alıyor, en düşüğü 0,0196, en yükseği
  0,9838. Model bir etiket değil, bir sıralama üretiyor ve sıralama etiketle aynı yönde gidiyor.
- Aynı model eşik 0,20'den 0,80'e süpürüldüğünde 0,7183 ile 0,8016 arasında yedi ayrı doğruluk
  veriyor; işaretlenen abone 102'den 5'e iniyor ve 0,40 ile 0,60 arasında 27 abone sınıf
  değiştiriyor.

#### Sonraki Adım

Bu dersin modeli on iki katsayı taşıyor ve o katsayılar okunabilir: her biri bir sütuna aittir,
işareti ve büyüklüğü vardır. Doğrusal modellerin en çok anılan üstünlüğü budur. Sonraki ders o
üstünlüğü bir övgü olarak değil **bir sayı** olarak ele alır: okunabilir katsayılı bir modelin
ayrılmış kümedeki sayısı ile bu konudaki en iyi ailenin sayısı arasındaki fark kaç abonedir ve
katsayıya bakarak ne söylenebilir, ne söylenemez. Konunun kapanışı da oradadır.

### Katsayı Yorumu

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/katsayi-yorumu

Okunabilirliğin bir övgü değil bir sayı olarak ele alınması: sekiz aday arasından doğrulama kümesinden seçilen okunabilir model on iki katsayı taşır ve sınamada 0,8016 verir, aynı kümeden seçilen en iyi aile 33 sütunla 0,8056 verir, yorumlanabilirliğin bedeli 0,0040 yani tek bir abonedir. On iki katsayı kurgudaki üretici yapının sırasını doğru veriyor. Aynı katsayılar sabit değildir: kişi başına tüketim sütunu çıkarıldığında ortalama tüketimin katsayısı 0,772'den 0,808'e çıkar ve sınama sayısı hiç değişmez, ortalama tüketim çıkarıldığında kişi başına tüketimin katsayısı 0,071'den 0,442'ye, yani altı katına fırlar. Karesel ceza katsayıları 0,772'den 0,326'ya indirirken sınama sayısı yalnız 0,0079 düşer.

Önceki dersin modeli on iki katsayı taşıyordu ve o katsayılar okunabilir: her biri bir sütuna
aittir, bir işareti ve bir büyüklüğü vardır. Doğrusal modellerin en çok anılan üstünlüğü budur ve
genellikle bir övgü olarak yazılır. Bu ders onu bir övgü olarak değil **bir sayı** olarak ele
alır: okunabilir katsayılı bir modelin ayrılmış kümedeki sayısı ile bu konudaki en iyi ailenin
sayısı arasındaki fark kaç abonedir.

İkinci soru birincisinden ayrılır ve daha zordur. Katsayı okunduğunda ne söylenmiş olur. M26'nın
Keşifsel Analiz kursundaki Uygulamalı İstatistik konusu bu sorunun **nedensel** yüzünü ölçmüştü:
tek değişkenli bir katsayının karıştırıcı bir değişken eklendiğinde işaret değiştirmesi ve grup
içi eğimlerin toplulaştırılmış eğimin tersine dönmesi orada sayıyla gösterildi. Burada
tekrarlanmaz. Buradaki soru kestirim hedefine özgüdür: **aynı ayrılmış küme sayısını veren kaç
farklı katsayı kümesi var** ve bir katsayının değeri o kümenin hangisinin seçildiğine ne kadar
bağlı.

- **DM36.** Küme, bölme, tohum, hedef ve on iki sütunluk sabit öznitelik kümesi önceki dersteki
  gibidir; taban çizgisi sınama kümesinde 0,7579.
- **DM37.** Bütün adaylar **aynı eğitim ayarıyla** öğrenilir: öğrenme adımı 0,3, tur 300.
  Önceki dersin ayar süpürmesi tekrarlanmaz; bu derste değişen tek şey **sütun kümesidir**.
- **DM38.** Okunabilir aday, katsayıları tek tek listelenip yorumlanabilecek modeldir: bir, iki,
  dört ve on iki sütunlu adaylar.
- **DM39.** Genişletilmiş aday, altı sayısal sütunun ikili çarpımları ve kareleriyle 33 sütuna
  çıkarılmış modeldir. Katsayıları listelenebilir ama tek tek okunamaz, çünkü aynı büyüklük
  birden çok sütuna dağılmıştır.
- **DM40.** Genişletilmiş adaya dört düzeyde karesel ceza uygulanır. Ceza biçimleri arasındaki
  fark önceki derste ölçüldü; burada yalnız karesel ceza kullanılır.
- **DM41.** Denenen aday sayısı **8**'dir: dört okunabilir aday ve dört cezalı genişletilmiş
  aday. Seçim doğrulama kümesinden okunur.
- **DM42.** Katsayılar **ölçekli sütunlar** üzerinde okunur, dolayısıyla birbiriyle
  karşılaştırılabilir. Ham birimdeki katsayı bir metreküp başına düşen değişimdir ve sütunların
  birimleri farklı olduğu için sıralanamaz.
- **DM43.** Bu ders katsayının **kim için** okunduğunu, hangi alt grupta ne olduğunu ve
  yorumlanabilirliğin bir yöntem olarak nasıl kurulacağını ele almaz; o sorular M27/K08'e aittir.
  Burada yorumlanabilirlik yalnız verilen puandır.

#### Yorumlanabilirliğin Bedeli

Kurulum önceki dersin kurulumudur.

```python
# yorum.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum, ayni bolme ve
# ayni on iki sutunluk oznitelik kumesi; hedef supheli etiketidir.
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)}
    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)
print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, "
      f"taban cizgisi {TABAN:.4f}")
```

```
egitim 756, dogrulama 252, sinama 252, taban cizgisi 0.7579
```

Sekiz aday tek bir merdivende sıralanır: bir sütundan on iki sütuna okunabilir modeller, sonra
33 sütunlu genişletilmiş model dört ceza düzeyinde.

```python
import itertools


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)


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, GD, GS = [[genislet(x) for x in s] for s in (EGT, DOG, SIN)]


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 w[0] + sum(w[j + 1] * (x[a] - o[a]) / s[a] for j, a in enumerate(alan))


def dogruluk(w, alan, o, s, st):
    return sum((sikistir(puan(w, alan, o, s, x)) > 0.5) == x["supheli"]
               for x in st) / len(st)


MERDIVEN = [("1 sutun", ["ort_tuketim"]),
            ("2 sutun", ["ort_tuketim", "sifir_okuma"]),
            ("4 sutun", ["ort_tuketim", "sifir_okuma", "hane", "memnuniyet"]),
            ("12 sutun", ALAN)]
SONUC, aday = [], 0
print(f"{'model':<12} {'katsayi':>8} {'dogrulama':>10} {'sinama':>8}")
for ad, alan in MERDIVEN:
    o, s = olcek(EGT, alan)
    w = egit(EGT, alan, o, s)
    aday += 1
    SONUC.append((ad, len(alan), dogruluk(w, alan, o, s, DOG),
                  dogruluk(w, alan, o, s, SIN)))
    print(f"{ad:<12} {len(alan):>8} {SONUC[-1][2]:>10.4f} {SONUC[-1][3]:>8.4f}")
og, os_ = olcek(GE, GENIS)
for ceza in (0.0, 0.01, 0.05, 0.2):
    w = egit(GE, GENIS, og, os_, ceza=ceza)
    aday += 1
    SONUC.append((f"33 sutun ceza {ceza}", len(GENIS),
                  dogruluk(w, GENIS, og, os_, GD), dogruluk(w, GENIS, og, os_, GS)))
    print(f"{SONUC[-1][0]:<12} {len(GENIS):>8} {SONUC[-1][2]:>10.4f} "
          f"{SONUC[-1][3]:>8.4f}")
print(f"\ntaban cizgisi {TABAN:.4f}, denenen aday sayisi {aday}")
oku = max(SONUC[:4], key=lambda t: t[2])
tum = max(SONUC, key=lambda t: t[2])
print(f"okunabilir aday icinde dogrulamadan secilen: {oku[0]}, sinama {oku[3]:.4f}")
print(f"butun adaylar icinde dogrulamadan secilen: {tum[0]}, sinama {tum[3]:.4f}")
print(f"yorumlanabilirligin bedeli {tum[3] - oku[3]:.4f}, yani "
      f"{round((tum[3] - oku[3]) * len(SIN))} abone")
```

```
model         katsayi  dogrulama   sinama
1 sutun             1     0.8254   0.7976
2 sutun             2     0.8095   0.7976
4 sutun             4     0.8095   0.7897
12 sutun           12     0.8333   0.8016
33 sutun ceza 0.0       33     0.8333   0.8056
33 sutun ceza 0.01       33     0.8373   0.7976
33 sutun ceza 0.05       33     0.8492   0.8056
33 sutun ceza 0.2       33     0.8214   0.7937

taban cizgisi 0.7579, denenen aday sayisi 8
okunabilir aday icinde dogrulamadan secilen: 12 sutun, sinama 0.8016
butun adaylar icinde dogrulamadan secilen: 33 sutun ceza 0.05, sinama 0.8056
yorumlanabilirligin bedeli 0.0040, yani 1 abone
```

Son satır dersin başlığını taşıyan sayıdır. Okunabilir modeller arasından doğrulama kümesine
bakarak seçmek on iki sütunlu modeli veriyor ve sınamada 0,8016 okunuyor. Bütün adaylar arasından
aynı kümeye bakarak seçmek 33 sütunlu, cezalı modeli veriyor ve sınamada 0,8056 okunuyor.
Yorumlanabilirliğin bedeli 0,0040, yani **252 abonenin biri**.

Bu sayının küçüklüğü bu kümeye özgüdür ve genel bir yasa değildir; ama okunması gereken şey
büyüklüğü değil, **ölçülmüş olmasıdır**. Yorumlanabilirlik uğruna başarımdan vazgeçildiği
söylenirken vazgeçilen miktar yazılmıyorsa, o cümle bir ölçüm değil bir tercih beyanıdır. Burada
miktar yazıldı ve yanına bütçesi kondu: sekiz aday.

Merdivenin kendisi ayrı bir şey söylüyor. Tek sütunlu model doğrulamada 0,8254 ile dört sütunlu
modeli geçiyor, sınamada ikisi de on iki sütunlunun altında kalıyor. Sütun eklemek tek yönlü bir
iyileşme değildir ve bu, M27/K02'nin on iki dersinin bulgusuydu.

#### Katsayı Ne Söyler, Ne Söylemez

Seçilen okunabilir modelin on iki katsayısı ölçekli sütunlar üzerinde okunur ve bu haliyle
birbiriyle karşılaştırılabilir.

```python
o, s = olcek(EGT, ALAN)
W = egit(EGT, ALAN, o, s)
print(f"{'sutun':<13} {'katsayi':>8}   (olcekli sutunlar, sabit terim "
      f"{W[0]:+.3f})")
for j in sorted(range(len(ALAN)), key=lambda j: -abs(W[j + 1])):
    print(f"{ALAN[j]:<13} {W[j + 1]:>+8.3f}")
print()
IKIZ = [("tam 12 sutun", ALAN),
        ("kisi_basi cikarildi", [a for a in ALAN if a != "kisi_basi"]),
        ("ort_tuketim cikarildi", [a for a in ALAN if a != "ort_tuketim"])]
print(f"{'model':<22} {'ort_tuketim':>12} {'kisi_basi':>10} {'sinama':>8}")
for ad, alan in IKIZ:
    oo, ss = olcek(EGT, alan)
    w = egit(EGT, alan, oo, ss)
    k = {a: w[j + 1] for j, a in enumerate(alan)}
    print(f"{ad:<22} {k.get('ort_tuketim', 0.0):>+12.3f} "
          f"{k.get('kisi_basi', 0.0):>+10.3f} {dogruluk(w, alan, oo, ss, SIN):>8.4f}")
print()
print(f"{'karesel ceza':>13} {'ort_tuketim':>12} {'oynaklik':>9} "
      f"{'sifir_okuma':>12} {'sinama':>8}")
for ceza in (0.0, 0.02, 0.1, 0.5):
    w = egit(EGT, ALAN, o, s, ceza=ceza)
    k = {a: w[j + 1] for j, a in enumerate(ALAN)}
    print(f"{ceza:>13} {k['ort_tuketim']:>+12.3f} {k['oynaklik']:>+9.3f} "
          f"{k['sifir_okuma']:>+12.3f} {dogruluk(w, ALAN, o, s, SIN):>8.4f}")
```

```
sutun          katsayi   (olcekli sutunlar, sabit terim -1.643)
ort_tuketim     +0.772
sifir_okuma     +0.684
oynaklik        +0.380
b_dogu          +0.306
memnuniyet      -0.273
hane            +0.156
donem           +0.148
b_bati          -0.147
b_guney         -0.109
kisi_basi       +0.071
b_merkez        -0.048
b_kuzey         -0.005

model                   ort_tuketim  kisi_basi   sinama
tam 12 sutun                 +0.772     +0.071   0.8016
kisi_basi cikarildi          +0.808     +0.000   0.8016
ort_tuketim cikarildi        +0.000     +0.442   0.7778

 karesel ceza  ort_tuketim  oynaklik  sifir_okuma   sinama
          0.0       +0.772    +0.380       +0.684   0.8016
         0.02       +0.573    +0.397       +0.523   0.7937
          0.1       +0.326    +0.342       +0.310   0.7937
          0.5       +0.133    +0.178       +0.127   0.7619
```

İlk tablo katsayının ne söylediğini gösteriyor ve söylediği şey doğrudur. Ortalama tüketim,
sıfır okuma göstergesi ve oynaklık ilk üç sırada; kurgunun etiketi üretirken kullandığı en
ağır üç terim tam olarak bunlardır. Memnuniyetin işareti eksi, hane büyüklüğününki artı ve
bölgeler arasında doğu en yüksek. Kuzeyin katsayısı -0,005 ile sıfıra oturuyor; kurguda kuzeyin
bölge katkısı da tam olarak sıfırdır. On iki sayı, üreten yapının sırasını okunur biçimde
veriyor.

İkinci tablo aynı katsayıların **ne söylemediğini** gösteriyor. Kişi başına tüketim on iki
sütunlu modelde +0,071 ile listenin sonlarında duruyor; bu sayıya bakan biri sütunun neredeyse
işe yaramadığı sonucunu çıkarır. Sütun modelden çıkarıldığında sınama sayısı **hiç değişmiyor**,
0,8016'da kalıyor; buraya kadar okuma doğru. Ama ortalama tüketim çıkarıldığında aynı sütunun
katsayısı +0,071'den +0,442'ye, yani altı katına fırlıyor. Kişi başına tüketim işe yaramaz bir
sütun değildi; **yanında ortalama tüketim varken** işe yaramaz görünüyordu, çünkü ikisi aynı
niceliğin iki biçimidir ve model o niceliğe ait ağırlığı ikisi arasında istediği gibi bölüştürür.

Üçüncü tablo aynı belirsizliği ceza üzerinden ölçüyor. Ortalama tüketimin katsayısı 0,772'den
0,326'ya, yani yarısının altına iniyor; sıfır okuma göstergesi 0,684'ten 0,310'a iniyor. Sınama
sayısı ise 0,8016'dan 0,7937'ye, yani yalnız iki abone düşüyor. **Bu üç model kestirim olarak
neredeyse aynıdır ve katsayıları neredeyse hiç benzemez.** Ayrılmış küme sayısı, hangi katsayı
kümesinin seçildiğini belirlemeye yetmiyor.

Buradan çıkan kural sınırlıdır ve tam olarak yazılmalıdır. Bir katsayının **sıralamadaki yeri**,
sütunlar birbirinden bağımsız olduğu ölçüde okunabilir bir bilgidir. Bir katsayının **değeri**
ise ancak yanındaki sütun kümesiyle ve uygulanan cezayla birlikte anlamlıdır. Bir katsayının
**nedensel** yorumu ise bu tabloların hiçbirinden çıkmaz; M26'nın Uygulamalı İstatistik konusu
o sınırı ayrıca ölçmüştü.

#### Özet

- Yorumlanabilirliğin bedeli bu kümede 0,0040, yani 252 abonenin biridir: doğrulamadan seçilen
  okunabilir model sınamada 0,8016, aynı kümeden seçilen en iyi aile 0,8056 veriyor ve denenen
  aday sayısı 8'dir.
- Merdiven tek yönlü değildir: tek sütunlu model doğrulamada dört sütunluyu geçiyor, sınamada
  ikisi de on iki sütunlunun altında kalıyor.
- On iki ölçekli katsayı kurgudaki üretici yapının sırasını doğru veriyor; ilk üç sıra ortalama
  tüketim, sıfır okuma ve oynaklıktır ve kuzeyin katsayısı -0,005 ile sıfıra oturuyor.
- Katsayının değeri yanındaki sütunlara bağlıdır: kişi başına tüketim +0,071 iken ortalama tüketim
  çıkarıldığında +0,442'ye fırlıyor, yani altı katına çıkıyor.
- Karesel ceza ortalama tüketim ile sıfır okuma katsayılarını yarısının altına indirip oynaklığı
  neredeyse hiç değiştirmezken sınama sayısı yalnız 0,0079 düşüyor; kestirim olarak neredeyse aynı
  olan modellerin katsayıları neredeyse hiç benzemiyor.

#### Sonraki Adım

Beş derste model ailesi değişti ama modelin **biçimi** hiç değişmedi. En küçük kareler bir
düzlemdi; polinom bağlanım sütunları eğdi ama karar yine sütunların doğrusal birleşimiydi; üç
ceza yalnız o birleşimin katsayılarını küçülttü; lojistik bağlanım düzlemin çıktısını sıkıştırdı
ama sıkıştırma tek yönlü ve artan bir işlevdir, dolayısıyla karar sınırı hâlâ bir düzlemdir. Beş
dersin tamamında sınıflandırılan abone, on iki sayının tek bir ağırlıklı toplamına göre bir
tarafa ya da öbür tarafa düştü.

Bu bir sınırdır ve hiç sınanmadı. Sınırın kendisinin **eğri** olduğu bir kümede — iki sütunun
belirli bir aralığında şüphe yükselip aralığın iki yanında da düştüğü bir kümede — hiçbir düzlem
işe yaramaz ve polinom sütunları o eğriyi ancak biçimini önceden bilirsek yakalar. Sonraki konu
kararı düzlemle kurmayan ailelerle açılır: kararı komşularına bakarak veren örnek tabanlı bir
yordamla ve o yordamın sütun sayısı arttıkça uzaklık ölçütünün kendisine ne olduğuyla.

## Doğrusal Olmayan Modeller

Karar sınırı eğri olduğunda devreye giren ailelerin kattığı farkın ve maliyetinin sayılması: komşu sayısı ile uzaklık ölçütünün oynattığı sayı ve sütun arttıkça en yakın ile en uzak komşunun birbirine yaklaşması, koşullu bağımsızlık varsayımı bozulduğunda olasılık çıktısının uçlara gitmesine karşın sıralamanın ayakta kalması, marj genişliği ile kararı taşıyan destek vektörü sayısı ve çekirdek dönüşümünün açılmadan verdiği sonuç, bölme ölçütünün seçtiği bölme ile budamanın derinlik sınırından farkı, ağaç sayısı arttıkça oturan sayı ve düşen değişkenlik ile torba dışı kestirim, ardışık artırmada öğrenme adımı ile tur sayısının takası, üç birleştirmenin kattığı fark ve toplulukların birlikte yanıldığı örnek sayısı.

### En Yakın Komşu

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/en-yakin-komsu

Hiçbir şey hesaplamayan, eğitim kümesini olduğu gibi saklayan bir ailenin ölçülmesi: komşu sayısı ile uzaklık ölçütü üzerinde yirmi dört aday denenir, doğrulama kümesinden seçilen aday sınama kümesinde 0,7937 verir ve taban çizgisini 0,0357 geçer, ama aynı adayın seçim kümesindeki sayısı 0,8294'tür. Bir komşuyla bakan yordam üç ölçütte de taban çizgisinin altına düşer. Sütun sayısı arttıkça en uzak komşunun en yakın komşuya oranı 26,91'den 1,49'a iner ve model tam olarak taban çizgisine oturur.

Doğrusal modeller konusu beş ders boyunca tek bir biçim üzerinde çalıştı: model, öznitelik
uzayında bir **doğru** ya da bir **düzlem** çiziyordu. Polinom öznitelikleri bu biçimi esnetti
ama esneme yine sabit katsayılı bir denklemin içinde kaldı. Konunun kapanışı bir soruyu açık
bıraktı: iki sınıfı ayıran sınırın kendisinin **eğri** olduğu bir kümede ne olur ve o eğriyi
elle yazmadan bulan bir aile kaç sayı eder.

Bu ders o soruya en aykırı yanıtla başlar: hiç denklem kurmayan bir model. **Örnek tabanlı
yordam** eğitim sırasında hiçbir şey hesaplamaz; eğitim kümesini olduğu gibi saklar ve bir
aboneyi sınıflandırırken ona **en yakın** eğitim satırlarına bakıp çoğunluğun etiketini verir.
Karar sınırı burada yazılmaz, örneklerin yerleşiminden doğar ve eğri olması için ayrıca bir şey
yapılmaz. Bedeli vardır ve ders o bedeli kursun üç sayısıyla ölçer: taban çizgisi, ailenin sayısı
ve o sayıyı bulmak için denenen aday sayısı.

- **DO1.** Küme M27/K01 ve K02'den gelen **kurgu** abone tablosudur: 1.260 abone, tohum
  20260218, bölme 756/252/252, sınama kümesinde taban çizgisi 0,7579. Taban çizgisi en sık
  sınıfı, yani "şüpheli değil"i söyleyen yordamdır.
- **DO2.** Öznitelik kümesi M27/K02'nin bıraktığı haliyle alınır ve **kurs boyunca sabit
  tutulur**: beş sayısal sütun, kişi başına tüketim oranı, sıfır okuma göstergesi ve beş bölge
  sütunu, toplam on iki. Metinden türetilen sütunlar M27/K06'ya bırakıldığı için dışarıda kalır.
  Değişen tek şey modeldir.
- **DO3.** Ölçü doğruluktur ve sınama kümesi 252 abonedir; bir abone 0,0040 eder.
- **DO4.** Ölçekleme M27/K02'nin ölçekleme dersinde kurulan biçimiyle uygulanır: standart puan,
  yalnız eğitim kümesinden öğrenilir ve öteki iki kümeye uygulanır. Uzaklık toplayan bir yordamda
  ölçek bir sütun ağırlığıdır; K02 bu kararın işaretinin aileye göre değiştiğini ölçtü ve o
  sayılar burada tekrarlanmaz.
- **DO5.** **Ayar değişkeni**, modelin veriden öğrenmediği, dışarıdan verilen sayıdır. Bu ailede
  iki tanedir: komşu sayısı ve uzaklık ölçütü.
- **DO6.** Seçim **doğrulama kümesinden** okunur. Sınama kümesindeki sayı yalnız bildirilir;
  aynı tablodan sınama kümesine göre seçim yapılsaydı ne çıkacağı ayrıca basılır.
- **DO7.** Komşu sayıları tek seçilir, böylece oylamada beraberlik oluşmaz.
- **DO8.** Boyut laneti ölçümünde eklenen sütunlar **kurgudur**, aynı üreteçten gelir ve etiketle
  hiçbir ilişki taşımaz.

#### Eğitimi Olmayan Bir Model

Kurulum kümeyi yeniden üretir, ölçeği eğitim kümesinden öğrenir ve yordamı yazar.

```python
# komsu.py — MODELDIR. M27/K01 ve K02'nin KURGU abone tablosu ayni tohumla
# yeniden uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabittir.
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), "bolge_ad": k["bolge"][0],
         "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    VERI.append(x)

SAYISAL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi"]
ALAN = SAYISAL + ["sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]


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:]
TABAN_SIN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)
TABAN_DOG = sum(x["supheli"] == 0 for x in DOG) / len(DOG)
OLCEK = {a: (statistics.fmean(x[a] for x in EGT),
             statistics.pstdev([x[a] for x in EGT])) for a in SAYISAL}


def olcekle(s):
    return [dict(x, **{a: (x[a] - OLCEK[a][0]) / OLCEK[a][1] for a in OLCEK}) for x in s]


def vektor(s, alan=None):
    return [([x[a] for a in (alan or ALAN)], x["supheli"]) for x in s]


def mutlak_fark(a, b):
    return sum(abs(p - q) for p, q in zip(a, b))


def komsu_dogruluk(egt, hedef, k, olcut):   # MODELDIR: ornek tabanli yordam
    d = 0
    for v, y in hedef:
        u = sorted((olcut(v, w), t) for w, t in egt)
        d += (sum(t for _, t in u[:k]) * 2 > k) == y
    return d / len(hedef)


E, D, S = olcekle(EGT), olcekle(DOG), olcekle(SIN)
VE, VD, VS = vektor(E), vektor(D), vektor(S)
print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, sutun {len(ALAN)}")
print(f"taban cizgisi: sinamada {TABAN_SIN:.4f}, dogrulamada {TABAN_DOG:.4f}")
print(f"olceksiz sutunlar (mutlak fark, k=31) sinama "
      f"{komsu_dogruluk(vektor(EGT), vektor(SIN), 31, mutlak_fark):.4f}")
print(f"olcekli sutunlar  (mutlak fark, k=31) sinama "
      f"{komsu_dogruluk(VE, VS, 31, mutlak_fark):.4f}")
```

```
egitim 756, dogrulama 252, sinama 252, sutun 12
taban cizgisi: sinamada 0.7579, dogrulamada 0.8135
olceksiz sutunlar (mutlak fark, k=31) sinama 0.7778
olcekli sutunlar  (mutlak fark, k=31) sinama 0.7937
```

`komsu_dogruluk` işlevinin gövdesinde model kurma adımı yoktur. Öteki ailelerde ağırlık,
katsayı ya da bölme eşiği çıkaran bir aşama bulunur; burada o aşama boştur ve modelin kendisi
**756 satırlık eğitim kümesidir**. Bunun bir sonucu tahmin tarafında görünür: her yeni abone
için 756 uzaklık hesaplanır ve sıralanır. Aile eğitimin maliyetini tahmine devreder.

İkinci gözlem ölçek satırındadır. Aynı yordam ölçeksiz sütunlarla 0,7778, ölçekli sütunlarla
0,7937 okuyor. Sebep, uzaklığın bir **toplam** olmasıdır: on iki farkın toplamında ortalama
tüketimin ham aralığı bölge sütununun aralığından kırk kat geniştir, dolayısıyla ölçeksiz
uzaklık sessizce bir ağırlıklandırmadır. M27/K02 bu kararı üç ailede ayrı ayrı ölçtü ve
işaretinin aileye göre değiştiğini gösterdi; burada ölçekleme bir düzeltme olarak değil, uzaklık
kavramının sütunlar arasında karşılaştırılabilir olmasının koşulu olarak duruyor.

Bir sayı daha dikkat ister: taban çizgisi iki kümede aynı değildir. Sınama kümesinde 0,7579,
doğrulama kümesinde 0,8135. Aynı bölmeden çıkan iki küme, şüpheli oranı bakımından 0,0556 kadar
ayrışmış durumda. Bu, bir modelin doğrulama sayısının sınama sayısıyla doğrudan
karşılaştırılamayacağı anlamına gelir; her sayı kendi kümesinin tabanına göre okunur.

#### Yirmi Dört Aday

Ailenin iki ayar değişkeni vardır ve ikisi de veriden öğrenilmez. Komşu sayısı kararın kaç
örneğe dayandığını belirler. Uzaklık ölçütü ise "yakın" sözcüğünün tanımıdır: farkların
karelerini toplayan **kare uzaklık**, mutlak farkları toplayan **mutlak fark** ve yalnız en
büyük tek farka bakan **en büyük fark**. Üçü aynı kümede aynı sıralamayı vermez.

```python
def kare_uzaklik(a, b):
    return sum((p - q) * (p - q) for p, q in zip(a, b))


def en_buyuk_fark(a, b):
    return max(abs(p - q) for p, q in zip(a, b))


KLER = [1, 3, 5, 9, 15, 21, 31, 45]
OLCUTLER = [("kare uzaklik", kare_uzaklik), ("mutlak fark", mutlak_fark),
            ("en buyuk fark", en_buyuk_fark)]


def komsu_listesi(egt, hedef, olcut):
    return [([t for _, t in sorted((olcut(v, w), t) for w, t in egt)][:max(KLER)], y)
            for v, y in hedef]


ADAY = {}
for ad, olcut in OLCUTLER:
    kd, ks = komsu_listesi(VE, VD, olcut), komsu_listesi(VE, VS, olcut)
    for k in KLER:
        ADAY[(ad, k)] = (sum((sum(t[:k]) * 2 > k) == y for t, y in kd) / len(kd),
                         sum((sum(t[:k]) * 2 > k) == y for t, y in ks) / len(ks))

for i, kume in enumerate(("dogrulama", "sinama")):
    print(f"{kume + ' k =':<16}" + "".join(f"{k:>8}" for k in KLER))
    for ad, _ in OLCUTLER:
        print(f"{ad:<16}" + "".join(f"{ADAY[(ad, k)][i]:>8.4f}" for k in KLER))
sd = max(ADAY, key=lambda a: (ADAY[a][0], -a[1]))
ss = max(ADAY, key=lambda a: (ADAY[a][1], -a[1]))
print(f"\ndenenen aday {len(ADAY)}")
print(f"dogrulamadan secilen: {sd[0]}, k={sd[1]} -> dogrulama {ADAY[sd][0]:.4f}, "
      f"sinama {ADAY[sd][1]:.4f}")
print(f"sinamadan secilen:    {ss[0]}, k={ss[1]} -> dogrulama {ADAY[ss][0]:.4f}, "
      f"sinama {ADAY[ss][1]:.4f}")
print(f"secilen adayda dogrulama - sinama {ADAY[sd][0] - ADAY[sd][1]:+.4f}; "
      f"ailenin sayisi taban cizgisinin {ADAY[sd][1] - TABAN_SIN:+.4f} ustunde")
print(f"dogrulama tabanini gecen aday {sum(1 for a in ADAY if ADAY[a][0] > TABAN_DOG)}, "
      f"sinama tabanini gecen {sum(1 for a in ADAY if ADAY[a][1] > TABAN_SIN)}")
```

```
dogrulama k =          1       3       5       9      15      21      31      45
kare uzaklik      0.7540  0.7738  0.7698  0.8214  0.8175  0.8254  0.8214  0.8254
mutlak fark       0.7778  0.7738  0.7937  0.8254  0.8214  0.8214  0.8294  0.8254
en buyuk fark     0.7500  0.7897  0.8056  0.8214  0.8056  0.8095  0.8056  0.8135
sinama k =             1       3       5       9      15      21      31      45
kare uzaklik      0.7262  0.7341  0.7341  0.7579  0.7619  0.7738  0.7778  0.7937
mutlak fark       0.7063  0.7262  0.7302  0.7698  0.7698  0.7619  0.7937  0.7937
en buyuk fark     0.7262  0.7460  0.7619  0.7817  0.7897  0.7738  0.7579  0.7778

denenen aday 24
dogrulamadan secilen: mutlak fark, k=31 -> dogrulama 0.8294, sinama 0.7937
sinamadan secilen:    mutlak fark, k=31 -> dogrulama 0.8294, sinama 0.7937
secilen adayda dogrulama - sinama +0.0357; ailenin sayisi taban cizgisinin +0.0357 ustunde
dogrulama tabanini gecen aday 11, sinama tabanini gecen 14
```

İlk sütun ailenin en tanınmış kusurunu veriyor. Tek komşuya bakan yordam sınama kümesinde üç
ölçütte de taban çizgisinin **altındadır**: 0,7262, 0,7063 ve 0,7262. Bir komşu, eğitim
kümesindeki tek bir satırın etiketini kopyalamaktır; o satır yanlış tarafta duruyorsa karar da
yanlış tarafta durur. Komşu sayısı büyüdükçe her karar daha çok satıra dayanır ve sayı yükselir.

Doğrulama kümesinden okunan seçim mutlak fark ile 31 komşudur; o adayın doğrulama sayısı 0,8294,
sınama sayısı 0,7937. **Ailenin sayısı 0,7937'dir** ve taban çizgisini 0,0357, yani dokuz abone
geçer. Aynı tablodan sınama kümesine göre seçim yapılsaydı seçilen aday değişmezdi — bu bir
şanstır ve tabloda görülüyor, çünkü sınamada 0,7937 üç ayrı adayda birden çıkıyor. Değişen şey
aday değil **bildirilen sayı** olurdu: seçim kümesindeki 0,8294 raporlansaydı aile dokuz abone
yerine on sekiz abone kazanmış görünürdü. Doğrulama kümesi seçim için harcanmış bir kümedir ve
üzerinde okunan en yüksek sayı bir genelleme kestirimi değildir.

Denenen aday sayısı **24**'tür ve bu sayı ailenin sayısının yanında durmadan okunamaz. Yirmi dört
adayın doğrulama kümesinde 11'i kendi tabanını geçiyor, biri tabana tam eşit çıkıyor; kalan on iki
aday hiçbir şey söylemeyen yordamdan kötü. Aile "taban çizgisini 0,0357 geçti" cümlesi, "yirmi
dört adayın en iyisi taban çizgisini 0,0357 geçti" cümlesiyle aynı şey değildir.

#### Boyut Laneti

Uzaklık, sütun sayısı büyüdükçe ayırt edici olmaktan çıkar. Mekanizma toplamın kendisindedir:
her yeni sütun toplama bir terim ekler ve terimler birbirinden bağımsızsa toplam, sütun sayısı
büyüdükçe ortak bir ortalamaya yaklaşır. Sonuçta en uzak komşu ile en yakın komşu arasındaki
oran 1'e iner ve "en yakın" sözcüğü anlamını yitirir. Bu, **boyut laneti** adıyla anılır ve
sayılabilir.

```python
def gurultulu(s, d, tohum):                 # KURGU: hicbir bilgi tasimayan sutunlar
    out = []
    for i, x in enumerate(s):
        r = uretec(tohum + i)
        out.append(([x[a] for a in ALAN] + [(r() + r() + r() - 1.5) * 1.15
                                            for _ in range(d)], x["supheli"]))
    return out


print(f"{'sutun':>6} {'en uzak / en yakin':>19} {'sinama':>8} {'tabana gore':>12}")
for d in (0, 4, 12, 40, 120):
    GE, GS = gurultulu(E, d, TOHUM + 300000), gurultulu(S, d, TOHUM + 700000)
    oran, dogru = [], 0
    for v, y in GS:
        u = sorted((mutlak_fark(v, w), t) for w, t in GE)
        oran.append(u[-1][0] / u[0][0])
        dogru += (sum(t for _, t in u[:31]) * 2 > 31) == y
    print(f"{len(ALAN) + d:>6} {statistics.fmean(oran):>19.2f} "
          f"{dogru / len(GS):>8.4f} {dogru / len(GS) - TABAN_SIN:>+12.4f}")
```

```
 sutun  en uzak / en yakin   sinama  tabana gore
    12               26.91   0.7937      +0.0357
    16                6.70   0.7817      +0.0238
    24                3.46   0.7738      +0.0159
    52                2.01   0.7698      +0.0119
   132                1.49   0.7579      +0.0000
```

İki sütun birlikte okunur. On iki sütunda en uzak eğitim satırı en yakın olandan 26,91 kat
uzaktadır ve "komşu" sözcüğünün karşılığı vardır. Dört gürültü sütunu eklendiğinde oran 6,70'e,
kırk sütunda 2,01'e iner. Yüz yirmi gürültü sütununda oran 1,49'dur: en yakın eğitim satırı ile
en uzak olan arasında yarım kattan az fark kalmıştır ve model tam olarak **taban çizgisine**,
0,7579'a oturur. Aile artık hiçbir şey öğrenmemektedir.

Eklenen sütunların hiçbiri yanlış bilgi taşımıyor; hiç bilgi taşımıyorlar. Fark, ailenin bu
sütunları görmezden gelememesinden gelir. Her bölmede tek bir sütun seçen bir karar ağacı,
bilgisiz sütunu bölme adayı olarak seçmeyerek dışarıda bırakabilir; katsayı öğrenen bir doğrusal
model o sütunun katsayısını küçültebilir. Uzaklık toplamının böyle bir aracı yoktur, çünkü
uzaklık öğrenilmez, tanımlanır. Öznitelik seçimi ve boyut azaltma bu ailede bir iyileştirme
değil, işleyebilmesinin koşuludur.

#### Özet

- Örnek tabanlı yordamın eğitim aşaması boştur; model 756 satırlık eğitim kümesinin kendisidir ve
  maliyet tahmin tarafındadır: her abone için 756 uzaklık hesaplanır.
- Doğrulama kümesinden seçilen aday mutlak fark ile 31 komşudur; ailenin sınama kümesindeki
  sayısı 0,7937, taban çizgisinin 0,0357 üstünde ve bu sayı **24 aday** denenerek bulundu.
- Aynı adayın doğrulama kümesindeki sayısı 0,8294'tür; seçim kümesinden okunan sayı bildirilseydi
  aile dokuz abone yerine on sekiz abone kazanmış görünürdü.
- Tek komşuya bakan yordam üç uzaklık ölçütünde de sınama kümesinde taban çizgisinin altına
  düşer (0,7262, 0,7063 ve 0,7262); yirmi dört adayın on üçü doğrulama kümesinde kendi tabanını
  geçemiyor.
- Sütun sayısı 12'den 132'ye çıktığında en uzak komşunun en yakın komşuya oranı 26,91'den 1,49'a
  iner ve ailenin sayısı tam olarak taban çizgisine, 0,7579'a oturur.

#### Sonraki Adım

Bu ailede on iki sütun tek bir uzaklık toplamında eriyor: bölge göstergesiyle ortalama tüketim
aynı toplamda buluşuyor ve kararı hangisinin taşıdığı görünmüyor. Kategorik bir alan için
"uzaklık" zaten zorlama bir kavramdır; bölge sütunları beş ikili sütuna açıldıktan sonra iki
farklı bölge arasındaki fark her zaman aynı sayıdır. Sonraki ders sütunları toplamayan, **sayan**
bir aile alır: her öznitelik değerinin her sınıf altında kaç kez görüldüğünü sayar ve bu sayımları
çarpar. Çarpmanın geçerli olabilmesi için sütunların, sınıf verildiğinde birbirinden bağımsız
olması gerekir; bu kümede değildir. Ders varsayımın bozulmasının iki ayrı sonucunu ayrı ayrı
sayar — olasılık çıktısının uçlara gitmesi ile sıralamanın ayakta kalması — ve hiç görülmemiş bir
sayımın modeli tümüyle susturmasının düzleştirmeyle nasıl çözüldüğünü ölçer.

### Bağımsızlık Varsayımlı Olasılıksal Sınıflandırıcı

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/bagimsizlik-varsayimli-siniflandirici

Sütunları toplamak yerine sayan bir ailenin ölçülmesi: sekiz sütunun 201.600 bileşimi yerine 80 hücrelik bir sayım tablosu tutulur, yirmi aday denenir ve doğrulama kümesinden seçilen aday sınama kümesinde 0,7817 verir. Sıfır sayım bir sınıfı tümüyle eler; 60 satırlık eğitimde 137 sınama satırı vetolanır ve düzleştirme vetoyu kaldırır ama doğruluğu güvenceye almaz. Aynı bilgiyi taşıyan sütun sekiz kez eklendiğinde uç olasılıklı abone 2'den 169'a, ortalama güven 0,842'den 0,970'e çıkar; sıralamanın üst 61'indeki isabet 29'dan 33'e gider.

Önceki ders on iki sütunu tek bir uzaklık toplamına indirdi. O toplamda bölge göstergesiyle
ortalama tüketim aynı terim listesinde durur ve kararı hangisinin taşıdığı görünmez; kategorik
bir alan için "uzaklık" zaten sonradan takılmış bir kavramdır. Bu ders sütunları toplamayan,
**sayan** bir aile alır: her sütunun her düzeyinin her sınıf altında kaç kez göründüğünü sayar.

Sayarak sınıflandırmanın önünde bir engel vardır. Belirli bir sütun bileşiminin kaç kez şüpheli
çıktığını doğrudan saymak mümkün değildir, çünkü bileşimlerin neredeyse tamamı eğitim kümesinde
hiç görülmemiştir. Aile bu engeli tek bir varsayımla aşar: sınıf verildiğinde sütunların
birbirinden bağımsız olduğunu kabul eder ve ortak sayımı tek tek sütun sayımlarının
**çarpımıyla** değiştirir. Varsayım bu kümede doğru değildir; ders yanlışlığın iki ayrı sonucunu
ayrı ayrı sayar.

- **DO9.** Küme, bölme ve taban çizgisi değişmez: 1.260 abonelik **kurgu** tablo, tohum 20260218,
  bölme 756/252/252, sınama kümesinde taban çizgisi 0,7579 ve 61 şüpheli abone.
- **DO10.** Öznitelik kümesi sabittir; değişen yalnız **gösterimidir**. Bölgenin beş ikili sütunu
  tek bir beş düzeyli sütuna döner, ortalama tüketim M26'dan gelen 10, 25 ve 40 m³ tarife
  basamaklarına, oynaklık ile kişi başına tüketim eşit sayıda satır taşıyan aralıklara ayrılır.
  Aralık sınırları **yalnız eğitim kümesinden** öğrenilir.
- **DO11.** İki ayar değişkeni vardır ve ikisi de veriden öğrenilmez: aralık sayısı ve
  **düzleştirme** katsayısı. Çarpım logaritma toplamı olarak hesaplanır; sekiz küçük olasılığın
  doğrudan çarpımı kayan noktada sıfıra iner.
- **DO12.** **Önsel**, bir sınıfın eğitim kümesindeki oranıdır; **sonsal**, sütunlar görüldükten
  sonraki olasılığıdır. Karar eşiği 0,5'tir; eşik seçimi kursun değerlendirme konusuna bırakılır.
- **DO13.** Metin sınıflandırma kurulmaz ve M27/K06'ya bırakılır; kullanılan bütün alanlar kurgu
  abone tablosunun kategorik alanlarıdır.
- **DO14.** Seçim doğrulama kümesinden okunur. Sıralama ölçümünde en yüksek olasılıklı 61 abone
  alınır; 61, sınama kümesindeki gerçek şüpheli sayısıdır.

#### Sayımdan Olasılığa

Kurulum kümeyi üretir, düzey gösterimini kurar ve varsayımın gerekçesini iki sayıyla verir.

```python
# sayan-model.py — MODELDIR. M27/K01 ve K02'nin KURGU abone tablosu ayni tohumla
# uretilir; oznitelik kumesi sabittir, yalniz gosterimi duzeylere cevrilir.
import collections
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),
         "bolge_ad": k["bolge"][0],
         "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35)}
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    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:]
TABAN_SIN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)


def basamak(v):                             # tarife basamaklari 10 / 25 / 40 m3
    return 0 if v < 10 else (1 if v < 25 else (2 if v < 40 else 3))


def kesimler(egt, a, n):                    # OGRENME: aralik sinirlari egitimden
    v = sorted(x[a] for x in egt)
    return [v[int(i * len(v) / n)] for i in range(1, n)]


def duzey(v, kesim):
    return sum(1 for k in kesim if v >= k)


def kategorik(s, kesim):                    # UYGULAMA
    return [({"tarife": basamak(x["ort_tuketim"]), "bolge": x["bolge_ad"],
              "hane": x["hane"], "memnuniyet": x["memnuniyet"], "donem": x["donem"],
              "sifir_okuma": x["sifir_okuma"],
              "oynaklik": duzey(x["oynaklik"], kesim["oynaklik"]),
              "kisi_basi": duzey(x["kisi_basi"], kesim["kisi_basi"])}, x["supheli"])
            for x in s]


SUTUN = ["tarife", "bolge", "hane", "memnuniyet", "donem", "sifir_okuma",
         "oynaklik", "kisi_basi"]
KESIM = {a: kesimler(EGT, a, 8) for a in ("oynaklik", "kisi_basi")}
KE, KD, KS = kategorik(EGT, KESIM), kategorik(DOG, KESIM), kategorik(SIN, KESIM)
print(f"egitim {len(KE)}, sinama {len(KS)}, sinamada supheli {sum(y for _, y in KS)}, "
      f"taban cizgisi {TABAN_SIN:.4f}")
DUZEY = {a: {x[a] for x, _ in KE} for a in SUTUN}
bilesim, hucre = 1, 0
for a in SUTUN:
    bilesim, hucre = bilesim * len(DUZEY[a]), hucre + 2 * len(DUZEY[a])
print(f"sutun {len(SUTUN)}, bilesim sayisi {bilesim}, egitim satiri {len(KE)}, "
      f"sutunlara ayrilmis sayim tablosunda hucre {hucre}")
```

```
egitim 756, sinama 252, sinamada supheli 61, taban cizgisi 0.7579
sutun 8, bilesim sayisi 201600, egitim satiri 756, sutunlara ayrilmis sayim tablosunda hucre 80
```

İkinci satır varsayımın gerekçesidir. Sekiz sütunun düzeyleri 201.600 ayrı bileşim üretiyor ve
elde 756 eğitim satırı var; ortak sayımı doğrudan tutmak bir seçenek değildir. Sütunları ayrı
ayrı saymak aynı tabloyu **80 hücreye** indirir. Bedeli çarpmanın kendisindedir: bir abonenin
şüpheli olma olasılığı, sınıfın önseli ile her sütundaki düzeyinin o sınıf altındaki oranlarının
çarpımıdır ve bu çarpım ancak sütunlar **sınıf verildiğinde** bağımsızsa doğru sayıyı verir.

#### Modelin Sayısı ve Yirmi Aday

Model iki ayar değişkeniyle yazılır ve bir ızgara taranır. Basılan tablo doğrulama kümesinindir;
sınama kümesinin sayıları özet satırlarında verilir.

```python
def ogren(kume, sutun):                     # MODELDIR: sayimlar ve onsel oranlar
    top = collections.Counter(y for _, y in kume)
    say = {(a, c): collections.Counter(d[a] for d, y in kume if y == c)
           for a in sutun for c in (0, 1)}
    return top, say, {a: len({d[a] for d, _ in kume}) for a in sutun}


def sonsal(m, d, sutun, alfa):              # sinif 1 olasiligi; carpim log toplamidir
    top, say, ds = m
    n, lg = sum(top.values()), []
    for c in (0, 1):
        t = math.log(top[c] / n)
        for a in sutun:
            pay = say[(a, c)][d[a]] + alfa
            t = -math.inf if pay <= 0.0 else t + math.log(pay / (top[c] + alfa * ds[a]))
        lg.append(t)
    if lg[0] == -math.inf and lg[1] == -math.inf:
        return None                         # her iki sinif da elendi: model susuyor
    e = [math.exp(t - max(lg)) for t in lg]
    return e[1] / (e[0] + e[1])


def degerlendir(egt, hedef, sutun, alfa):
    m, p, susan = ogren(egt, sutun), [], 0
    for d, y in hedef:
        o = sonsal(m, d, sutun, alfa)
        if o is None:
            susan, o = susan + 1, 0.0
        p.append((o, y))
    return sum((o > 0.5) == y for o, y in p) / len(p), p, susan


IZGARA, ALFALAR = {}, (0.0, 0.25, 1.0, 4.0)
for n in (3, 4, 6, 8, 12):
    KS_N = {a: kesimler(EGT, a, n) for a in ("oynaklik", "kisi_basi")}
    E2, D2, S2 = kategorik(EGT, KS_N), kategorik(DOG, KS_N), kategorik(SIN, KS_N)
    for alfa in ALFALAR:
        IZGARA[(n, alfa)] = (degerlendir(E2, D2, SUTUN, alfa)[0],
                             degerlendir(E2, S2, SUTUN, alfa)[0])
print(f"{'aralik':>6}" + "".join(f"{a:>9}" for a in ALFALAR) + "   (dogrulama)")
for n in (3, 4, 6, 8, 12):
    print(f"{n:>6}" + "".join(f"{IZGARA[(n, a)][0]:>9.4f}" for a in ALFALAR))
sd = max(IZGARA, key=lambda a: (IZGARA[a][0], -a[0], -a[1]))
ss = max(IZGARA, key=lambda a: (IZGARA[a][1], -a[0], -a[1]))
print(f"\ndenenen aday {len(IZGARA)}")
for ad, a in (("dogrulamadan", sd), ("sinamadan", ss)):
    print(f"{ad + ' secilen:':<22}aralik {a[0]}, alfa {a[1]} -> dogrulama "
          f"{IZGARA[a][0]:.4f}, sinama {IZGARA[a][1]:.4f}")
print(f"ailenin sayisi {IZGARA[sd][1]:.4f}, taban cizgisinin "
      f"{IZGARA[sd][1] - TABAN_SIN:+.4f} ustunde; iki secim kumesi "
      f"{IZGARA[ss][1] - IZGARA[sd][1]:+.4f} ayriliyor")
```

```
aralik      0.0     0.25      1.0      4.0   (dogrulama)
     3   0.7540   0.7540   0.7540   0.7579
     4   0.7540   0.7540   0.7540   0.7579
     6   0.7738   0.7738   0.7659   0.7698
     8   0.7778   0.7778   0.7857   0.7857
    12   0.7579   0.7579   0.7619   0.7698

denenen aday 20
dogrulamadan secilen: aralik 8, alfa 1.0 -> dogrulama 0.7857, sinama 0.7817
sinamadan secilen:    aralik 4, alfa 4.0 -> dogrulama 0.7579, sinama 0.7857
ailenin sayisi 0.7817, taban cizgisinin +0.0238 ustunde; iki secim kumesi +0.0040 ayriliyor
```

Ailenin sayısı **0,7817**'dir, taban çizgisini 0,0238 — altı abone — geçer ve **20 aday**
denenerek bulunmuştur. Önceki dersin örnek tabanlı yordamı aynı öznitelik kümesiyle 0,7937
okumuştu. Olasılık üreten ve açık bir varsayıma dayanan bu aile, uzaklık toplayan yordamın
**gerisinde** kaldı; fark üç abone. Kursun ikinci iddiasının ilk örneği budur ve saklanmaz.

İki seçim kümesi ayrı adaylar veriyor. Doğrulamadan sekiz aralık ile 1,0 çıkıyor; sınamadan
bakılsaydı dört aralık ile 4,0 seçilir ve bildirilen sayı 0,7857 olurdu. O adayın doğrulama
sayısı 0,7579'dur, yani yirmi adayın en kötüleri arasındadır.

#### Sıfır Sayım ve Düzleştirme

Seksen hücrenin en seyreği tarife sütununun en üst basamağı ile şüpheli sınıfının kesişimidir ve
sayımı sıfırdır: kırk metreküpü aşan tüketimi olan bir abone eğitimde şüpheli olarak hiç
görülmemiş. Çarpımda bir sıfır, öteki yedi sütunun söylediği her şeyi siler. **Düzleştirme** her
sayıma küçük bir sabit ekler; gerekçesi bir başarım ayarı değil bu yapısal kusurdur.

```python
def veto_hesabi(egt, hedef, sutun, tum_duzey):
    gorulen = {(a, c): {d[a] for d, y in egt if y == c} for a in sutun for c in (0, 1)}
    bos = sum(len(tum_duzey[a] - gorulen[(a, c)]) for a in sutun for c in (0, 1))
    veto = yanlis = 0
    for d, y in hedef:
        elenen = [c for c in (0, 1) if any(d[a] not in gorulen[(a, c)] for a in sutun)]
        if elenen:
            veto, yanlis = veto + 1, yanlis + (y in elenen)
    return bos, veto, yanlis


print(f"{'egitim':>6} {'bos hucre':>10} {'vetolanan':>10} {'yanlis veto':>12} "
      f"{'alfa=0':>8} {'alfa=1':>8}")
for m in (60, 120, 250, 500, 756):
    b, v, y = veto_hesabi(KE[:m], KS, SUTUN, DUZEY)
    print(f"{m:>6} {b:>10} {v:>10} {y:>12} "
          f"{degerlendir(KE[:m], KS, SUTUN, 0.0)[0]:>8.4f} "
          f"{degerlendir(KE[:m], KS, SUTUN, 1.0)[0]:>8.4f}")
```

```
egitim  bos hucre  vetolanan  yanlis veto   alfa=0   alfa=1
    60          8        137           24   0.7341   0.7302
   120          4         56            9   0.7460   0.7579
   250          2          1            1   0.7381   0.7540
   500          1          1            1   0.7659   0.7698
   756          1          1            1   0.7778   0.7817
```

Altmış satırlık eğitimde seksen hücrenin sekizi boştur ve bu sekiz boşluk 252 sınama satırının
**137'sini** vetoluyor; 24'ünde vetolanan sınıf abonenin gerçek sınıfıdır, yani karar sekiz
sütunun kanıtına değil tek bir eksik sayıma dayanıyor. Eğitim 756 satıra çıktığında boş hücre
bire, veto tek satıra iner; düzleştirme katsayısı sıfırdan büyük olduğu anda veto tümüyle
kalkar.

Son iki sütun düzeltmenin sınırını gösteriyor. Altmış satırlık eğitimde düzleştirme sayıyı
0,7341'den 0,7302'ye **düşürüyor**; 120 satırda 0,7460'tan 0,7579'a çıkarıyor. Ödediği şey
doğruluk değil, modelin susmaması.

#### Varsayım Bozulduğunda

Koşullu bağımsızlık ölçülebilir bir iddiadır: iki sütunun sınıf içindeki ortak sayımı tek tek
oranlarının çarpımından ne kadar sapıyor. Sapma sıfırsa varsayım o çift için doğrudur.

```python
def bagimlilik(kume, a, b):     # sinif verildiginde ortak sayimin carpimdan sapmasi
    t = 0.0
    for c in (0, 1):
        alt = [d for d, y in kume if y == c]
        n = len(alt)
        pa = collections.Counter(d[a] for d in alt)
        pb = collections.Counter(d[b] for d in alt)
        pab = collections.Counter((d[a], d[b]) for d in alt)
        t += n / len(kume) * sum(abs(pab[(u, v)] / n - pa[u] * pb[v] / n / n)
                                 for u in pa for v in pb) / 2
    return t


for a, b in (("hane", "kisi_basi"), ("donem", "oynaklik"),
             ("donem", "sifir_okuma")):
    print(f"{a:<8} {b:<12} sapma {bagimlilik(KE, a, b):.3f}")


def kopyali(kume, k):           # ayni bilgiyi tasiyan sutunun tabloya yeniden girmesi
    return [(dict(d, **{f"tarife_{i}": d["tarife"] for i in range(k)}), y)
            for d, y in kume]


POZ = sum(y for _, y in KS)
print(f"\n{'kopya':>5} {'sutun':>6} {'dogruluk':>9} {'uc olasilik':>12} "
      f"{'ort guven':>10} {'ust-' + str(POZ) + ' isabet':>14}")
for k in (0, 1, 2, 4, 8):
    sut = SUTUN + [f"tarife_{i}" for i in range(k)]
    d, p, _ = degerlendir(kopyali(KE, k), kopyali(KS, k), sut, 1.0)
    ust = sorted(p, key=lambda t: -t[0])[:POZ]
    print(f"{k:>5} {len(sut):>6} {d:>9.4f} "
          f"{sum(1 for o, _ in p if o > 0.99 or o < 0.01):>12} "
          f"{statistics.fmean(max(o, 1 - o) for o, _ in p):>10.3f} "
          f"{sum(y for _, y in ust):>14}")
```

```
hane     kisi_basi    sapma 0.407
donem    oynaklik     sapma 0.235
donem    sifir_okuma  sapma 0.011

kopya  sutun  dogruluk  uc olasilik  ort guven  ust-61 isabet
    0      8    0.7817            2      0.842             29
    1      9    0.7778            5      0.862             32
    2     10    0.7778           17      0.889             33
    4     12    0.7817           72      0.932             32
    8     16    0.7857          169      0.970             33
```

Varsayım bu tabloda zaten bozuktur. Hane büyüklüğü ile kişi başına tüketimin sapması 0,407'dir ve
bu tanımın kendisidir: kişi başına tüketim, ortalama tüketimin hane büyüklüğüne bölünmüş halidir.
Dönem sayısıyla oynaklık 0,235 sapıyor, çünkü tek dönemi olan abonenin oynaklığı sıfırdır. En
bağımsız çift 0,011 ile dönem sayısı ve sıfır okumadır.

Alt tablo iki sonucu ayırır. **Olasılık çıktısı uçlara gidiyor:** uç olasılıklı abone sayısı
2'den 169'a, ortalama güven 0,842'den 0,970'e çıkıyor; aynı kanıt sekiz kez çarpıldığında model
bağımsız sekiz kanıt görmüş gibi davranır. **Sıralama ise ayakta kalıyor:** üst 61 abonedeki
gerçek şüpheli sayısı 29'dan 33'e çıkıyor, doğruluk 0,7817'den 0,7857'ye gidiyor. Kopyalanan
sütun bütün abonelerin skorunu aynı yönde ittiği için sıralamayı bozmuyor; bozduğu şey **sayının
büyüklüğüdür**. Bu ailenin çıktısı bir sıralama skoru olarak kullanılabilir, bir güven ölçüsü
olarak kullanılamaz.

#### Özet

- Sekiz sütunun 201.600 bileşimine karşılık 756 eğitim satırı vardır; koşullu bağımsızlık
  varsayımı sayım tablosunu 80 hücreye indirir ve ailenin tek gerekçesi budur.
- Ailenin sayısı 0,7817'dir, taban çizgisini 0,0238 geçer ve **20 aday** denenerek bulunmuştur;
  önceki dersin örnek tabanlı yordamı aynı öznitelik kümesiyle 0,7937 okumuştu.
- Sınamadan seçim yapılsaydı başka bir aday seçilir ve bildirilen sayı 0,7857 olurdu.
- Sıfır sayım bir sınıfı tümüyle eler: 60 satırlık eğitimde 137 sınama satırı vetolanıyor,
  24'ünde vetolanan sınıf doğru sınıftır. Düzleştirme vetoyu kaldırır ama doğruluğu güvenceye
  almaz; 60 satırda sayı 0,7341'den 0,7302'ye düşüyor.
- Varsayım bozulduğunda iki sonuç ayrışır: uç olasılıklı abone 2'den 169'a çıkarken üst 61'deki
  isabet 29'dan 33'e gidiyor.

#### Sonraki Adım

Bu ailenin kararı bütün eğitim satırlarının sayımına dayanıyor: sınırdan uzakta duran abone ile
tam sınırın üstünde duran abone aynı hücreyi bir birim büyütüyor. Önceki dersin komşu yordamında
da 31 komşunun her biri eşit oy veriyordu. Sonraki ders bunun tersini yapan bir aile alır. **Marj
temelli sınıflandırıcı** yalnız sınıra yakın örneklerle ilgilenir; sınırı iki sınıf arasındaki
boşluğun ortasına oturtur ve kararı taşıyan örnekleri **destek vektörü** olarak açıkça sayar.
Ders marj genişliğini ayarlayan katsayıyı süpürür, her genişlikte kaç örneğin kararı taşıdığını
basar ve eğri sınırı sütun açmadan üreten **çekirdek dönüşümünü** açıkça hesaplanmış polinom
öznitelikleriyle ölçer.

### Marj Temelli Sınıflandırıcı

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/marj-temelli-siniflandirici

Kararı yalnız sınıra yakın örneklere dayandıran bir ailenin ölçülmesi: ceza katsayısı süpürüldükçe marj 0,047'den 93,488'e genişler, doğrulama kümesinden seçilen aday sınama kümesinde 0,7857 verir ve beraberliği çözme kuralı tek başına dört abone eder. Kararı marj içindeki 324 satır taşır; marj dışından 300 satır atılınca 4 tahmin, marj içinden 300 satır atılınca 21 tahmin değişir. Üç çekirdekle dokuz aday denenir ve eğri sınır düz sınırı geçemez; polinom çekirdeği 90 sütunu hiç açmadan, açıkça hesaplanmış 90 sütunla 252 sınama satırının 252'sinde aynı tahmini verir.

Önceki iki aile kararını bütün eğitim kümesine eşit ağırlıkta dayandırdı: komşu yordamında 31
komşunun her biri bir oy verdi, sayan ailede her satır bir hücreyi bir birim büyüttü.

Bu ders bunun tersini yapan bir aile alır. **Marj temelli sınıflandırıcı** sınırı iki sınıf
arasındaki boşluğun ortasına oturtur ve yalnız o boşluğa giren örneklerle ilgilenir; boşluğun
genişliğine **marj** denir. Aile ayrıca eğri sınırı sütun açmadan üretebilen bir yapı taşır:
**çekirdek dönüşümü**. Ders üçünü de sayar — marj genişliği ile ayrılmış küme sayısı arasındaki
ilişkiyi, kararı taşıyan örnek sayısını ve çekirdeğin açık sütunlara göre kazandırdığını.

- **DO17.** Küme, bölme ve taban çizgisi değişmez: 1.260 abonelik **kurgu** tablo, tohum
  20260218, bölme 756/252/252, taban çizgisi sınamada 0,7579 ve doğrulamada 0,8135. Öznitelik
  kümesi K02'den gelen on iki sütundur; ölçek yalnız eğitim kümesinden öğrenilir.
- **DO18.** Etiketler $+1$ ve $-1$ yazılır. Model, marj kaybı ile ağırlıkların karesel cezasının
  toplamını küçültür; **ceza katsayısı** bu dersin ayar değişkenidir ve marj genişliği
  $2 / \lVert w \rVert$'dir.
- **DO19.** **Destek vektörü**, marjın içinde ya da yanlış tarafında kalan eğitim satırıdır;
  marjın dışındakiler kayba hiç katkı vermez.
- **DO20.** Çözücü, adımı $1/(\lambda t)$ ile küçülen bir alt eğim inişidir ve birincil biçimde
  300, ikili biçimde 40 tur döner; basılan sayılar bu **sabit bütçenin** sayılarıdır.
- **DO21.** **Çekirdek**, genişletilmiş bir uzaydaki iç çarpımı o uzayı kurmadan veren işlevdir.
  Polinom çekirdeği $(x \cdot z + 1)^2/10$, radyal çekirdek $e^{-0{,}05 \lVert x - z \rVert^2}$
  biçimindedir; ondalık katsayılar üç çekirdekte de aynı ceza aralığı kullanılabilsin diye
  sabittir. İkili biçim eğitim satırı sayısının karesi kadar çekirdek değeri tutar.
- **DO22.** Seçim doğrulama kümesinden okunur; beraberlikte **daha geniş marj** seçilir.

#### Marjın Genişliği

Kurulum kümeyi üretir, modeli yazar ve ceza katsayısını sekiz değerde süpürür.

```python
# marj.py — MODELDIR. M27/K01 ve K02'nin KURGU abone tablosu ayni tohumla
# uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabittir.
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)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    VERI.append(x)


SAYISAL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi"]
ALAN = SAYISAL + ["sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]


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:]
TABAN_SIN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)
OLCEK = {a: (statistics.fmean(x[a] for x in EGT),
             statistics.pstdev([x[a] for x in EGT])) for a in SAYISAL}


def vektor(s):                              # etiketler +1 / -1
    return [([(x[a] - OLCEK[a][0]) / OLCEK[a][1] if a in OLCEK else x[a]
              for a in ALAN], 1 if x["supheli"] else -1) for x in s]


VE, VD, VS = vektor(EGT), vektor(DOG), vektor(SIN)


def skor(w, b, v):
    return sum(w[i] * v[i] for i in range(len(w))) + b


def marj_egit(egt, lam, tur=300, n=None):   # MODELDIR: marj temelli siniflandirici
    d, n = len(egt[0][0]), n or len(egt)
    w, b = [0.0] * d, 0.0
    for t in range(1, tur + 1):
        adim, gw, gb = 1.0 / (lam * t), [0.0] * d, 0.0
        for v, y in egt:
            if y * skor(w, b, v) < 1:       # marjin icinde ya da yanlis tarafta
                for i in range(d):
                    gw[i] -= y * v[i]
                gb -= y
        for i in range(d):
            w[i] -= adim * (gw[i] / n + lam * w[i])
        b -= adim * gb / n
    return w, b


def dogruluk(m, hedef):
    return sum((skor(m[0], m[1], v) > 0) == (y > 0) for v, y in hedef) / len(hedef)


ADAY = {}
print(f"taban cizgisi sinamada {TABAN_SIN:.4f}; egitim {len(VE)} satir, {len(ALAN)} sutun")
print(f"{'ceza':>8} {'|w|':>7} {'marj':>8} {'destek':>7} {'dogrulama':>10} {'sinama':>8}")
for lam in (0.0003, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1.0):
    m = marj_egit(VE, lam)
    nw = math.sqrt(sum(x * x for x in m[0]))
    ADAY[lam] = (dogruluk(m, VD), dogruluk(m, VS))
    ic = sum(1 for v, y in VE if y * skor(m[0], m[1], v) < 1)
    print(f"{lam:>8} {nw:>7.3f} {2 / nw:>8.3f} {ic:>7} "
          f"{ADAY[lam][0]:>10.4f} {ADAY[lam][1]:>8.4f}")
en = max(ADAY, key=lambda a: (ADAY[a][0], a))
kucuk = max(ADAY, key=lambda a: (ADAY[a][0], -a))
print(f"denenen aday {len(ADAY)}; dogrulamadan secilen ceza {en} -> sinama "
      f"{ADAY[en][1]:.4f}, taban cizgisinin {ADAY[en][1] - TABAN_SIN:+.4f} ustunde")
print(f"beraberlik en kucuk ceza lehine cozulseydi ceza {kucuk} -> sinama "
      f"{ADAY[kucuk][1]:.4f}")
```

```
taban cizgisi sinamada 0.7579; egitim 756 satir, 12 sutun
    ceza     |w|     marj  destek  dogrulama   sinama
  0.0003  42.768    0.047     252     0.7421   0.7619
   0.001  12.742    0.157     338     0.8294   0.8016
   0.003   4.289    0.466     320     0.8294   0.7976
    0.01   1.587    1.260     324     0.8294   0.7857
    0.03   0.833    2.401     333     0.8175   0.7857
     0.1   0.266    7.523     334     0.8135   0.7579
     0.3   0.075   26.789     335     0.8135   0.7579
     1.0   0.021   93.488     335     0.8135   0.7579
denenen aday 8; dogrulamadan secilen ceza 0.01 -> sinama 0.7857, taban cizgisinin +0.0278 ustunde
beraberlik en kucuk ceza lehine cozulseydi ceza 0.001 -> sinama 0.8016
```

Marj 0,047'den 93,488'e genişliyor ve tablonun iki ucu da kötüdür. En dar marjda ağırlık
vektörünün uzunluğu 42,768'e çıkıyor ve doğrulama sayısı kendi tabanının 0,0714 **altına**
düşüyor; en geniş marjda sınır düzleşiyor ve model sınamada tam olarak taban çizgisini okuyor.
Aradaki üç aday doğrulama kümesinde 0,8294'te beraberedir.

Beraberliğin çözümü bir karardır ve son iki satır bedelini veriyor. Geniş marj lehine
çözüldüğünde ceza 0,01 seçiliyor ve **ailenin sayısı 0,7857** oluyor, taban çizgisinin 0,0278
üstünde; dar marj lehine çözülseydi 0,8016 bildirilirdi. Aradaki 0,0159 dört abone eder ve hiçbir
ölçümden değil yalnız beraberlik kuralından gelir.

#### Kararı Kaç Örnek Taşıyor

Destek vektörü sayısı tabloda duruyor ama tek başına bir iddia değildir; kararı taşıyıp
taşımadıkları, o satırlar atılıp model yeniden eğitilerek ölçülür.

```python
SECILEN = 0.01
M = marj_egit(VE, SECILEN)
T0 = [skor(M[0], M[1], v) > 0 for v, _ in VS]
IC = [i for i, (v, y) in enumerate(VE) if y * skor(M[0], M[1], v) < 1]
DIS = [i for i, (v, y) in enumerate(VE) if y * skor(M[0], M[1], v) >= 1]
r = uretec(TOHUM + 610000)


def rastgele_sec(havuz, k):
    h = list(havuz)
    for i in range(len(h) - 1, 0, -1):
        j = int(r() * (i + 1))
        h[i], h[j] = h[j], h[i]
    return set(h[:k])


print(f"ceza {SECILEN}: marj icinde {len(IC)} satir, marj disinda {len(DIS)} satir")
print(f"{'atilan':>7} {'havuz':>14} {'degisen tahmin':>15} {'sinama':>8}")
for k in (200, 300):
    for ad, havuz in (("marj disindan", DIS), ("marj icinden", IC)):
        at = rastgele_sec(havuz, k)
        m2 = marj_egit([p for i, p in enumerate(VE) if i not in at], SECILEN, n=len(VE))
        t2 = [skor(m2[0], m2[1], v) > 0 for v, _ in VS]
        print(f"{k:>7} {ad:>14} {sum(a != b for a, b in zip(T0, t2)):>15} "
              f"{dogruluk(m2, VS):>8.4f}")
```

```
ceza 0.01: marj icinde 324 satir, marj disinda 432 satir
 atilan          havuz  degisen tahmin   sinama
    200  marj disindan               0   0.7857
    200   marj icinden              15   0.7817
    300  marj disindan               4   0.7937
    300   marj icinden              21   0.7579
```

Seçilen adayda 756 eğitim satırının 324'ü marjın içindedir; bu, sınıfların ne kadar iç içe
geçtiğinin ölçüsüdür. Marjın **dışından** 300 satır atıldığında 252 sınama satırının yalnız
dördünde tahmin değişiyor, 200 satır atıldığında hiçbiri değişmiyor. Marjın **içinden** aynı
sayıda satır atıldığında değişen tahmin 21'e çıkıyor. Değişen tahmin sayısı bir kayıp ölçüsü de
değildir: marj dışından 300 satır atıldığında sınama sayısı 0,7937'ye **çıkıyor**. Ölçülen şey
sınırın oynamasıdır.

#### Çekirdek Dönüşümü

Eğri sınır iki yolla elde edilir. Birincisi sütunları açmaktır: kareler ve çarpımlar hesaplanıp
tabloya eklenir. İkincisi çekirdek dönüşümüdür — model iki satırın genişletilmiş uzaydaki iç
çarpımından başka bir şey kullanmadığı için, o iç çarpımı doğrudan veren bir işlev yazılırsa uzayı
kurmaya gerek kalmaz. Bunun için model **ikili biçimde** yazılır: ağırlık vektörü yerine eğitim
satırlarının katsayıları tutulur.

```python
def gram(A, B):                             # ic carpim matrisi; cekirdekler bundan turer
    return [[sum(p * q for p, q in zip(a, b)) for b, _ in B] for a, _ in A]


def cekirdek(ad, G, n1, n2):
    if ad == "dogrusal":
        return G
    if ad == "polinom":                     # (x . z + 1)^2 / 10
        return [[(g + 1.0) ** 2 / 10.0 for g in s] for s in G]
    return [[math.exp(-0.05 * (n1[i] + n2[j] - 2 * g)) for j, g in enumerate(s)]
            for i, s in enumerate(G)]       # radyal cekirdek


def ikili_egit(K, y, lam, tur=40):          # MODELDIR: ayni olcut, ikili bicimde
    n, a, b = len(y), [0.0] * len(y), 0.0
    for t in range(1, tur + 1):
        adim = 1.0 / (lam * t)
        f = [sum(a[j] * K[i][j] for j in range(n)) + b for i in range(n)]
        a, gb = [x * (1.0 - adim * lam) for x in a], 0.0
        for i in range(n):
            if y[i] * f[i] < 1:
                a[i] += adim * y[i] / n
                gb += y[i]
        b += adim * gb / n
    return a, b


def ikili_dogruluk(a, b, KH, hedef):
    return sum((sum(a[j] * KH[i][j] for j in range(len(a))) + b > 0) == (y > 0)
               for i, (v, y) in enumerate(hedef)) / len(hedef)


Y = [y for _, y in VE]
GE, GD, GS = gram(VE, VE), gram(VD, VE), gram(VS, VE)
NE = [sum(p * p for p in v) for v, _ in VE]
ND = [sum(p * p for p in v) for v, _ in VD]
NS = [sum(p * p for p in v) for v, _ in VS]
CEK = {}
print(f"{'cekirdek':<10}{'ceza':>8}{'dogrulama':>11}{'sinama':>8}")
for ad in ("dogrusal", "polinom", "radyal"):
    K = cekirdek(ad, GE, NE, NE)
    KD, KS_ = cekirdek(ad, GD, ND, NE), cekirdek(ad, GS, NS, NE)
    for lam in (0.003, 0.01, 0.03):
        a, b = ikili_egit(K, Y, lam)
        CEK[(ad, lam)] = (ikili_dogruluk(a, b, KD, VD), ikili_dogruluk(a, b, KS_, VS))
        print(f"{ad:<10}{lam:>8}{CEK[(ad, lam)][0]:>11.4f}{CEK[(ad, lam)][1]:>8.4f}")
sec = max(CEK, key=lambda a: (CEK[a][0], -a[1]))
print(f"denenen aday {len(ADAY) + len(CEK)} (dogrusal {len(ADAY)} + cekirdek {len(CEK)}); "
      f"dogrulamadan secilen {sec[0]}, ceza {sec[1]} -> sinama {CEK[sec][1]:.4f}")
print(f"cekirdek matrisi {len(VE)} x {len(VE)} = {len(VE) ** 2} giris")
```

```
cekirdek      ceza  dogrulama  sinama
dogrusal     0.003     0.8016  0.7817
dogrusal      0.01     0.8095  0.7897
dogrusal      0.03     0.8294  0.7857
polinom      0.003     0.7778  0.7857
polinom       0.01     0.7897  0.7778
polinom       0.03     0.8175  0.7937
radyal       0.003     0.7222  0.6627
radyal        0.01     0.8135  0.7579
radyal        0.03     0.8135  0.7579
denenen aday 17 (dogrusal 8 + cekirdek 9); dogrulamadan secilen dogrusal, ceza 0.03 -> sinama 0.7857
cekirdek matrisi 756 x 756 = 571536 giris
```

Dokuz adayın hiçbiri düz sınırı geçemiyor. Doğrulamadan okunan seçim **doğrusal çekirdektir** ve
sınama sayısı 0,7857'dir, birincil biçimin verdiği sayının aynısı. Polinom çekirdeğinin en yüksek
sınama sayısı 0,7937'dir ve doğrusal çekirdeğinkini geçer, ama doğrulama sayısı 0,8175'te kalıyor;
o adayı seçmek sınama kümesine bakmak olurdu. Radyal çekirdek en küçük cezada taban çizgisinin
0,0952 altına düşüyor. Eğri sınırın bu kümede kattığı fark, seçim doğrulama kümesinden okunduğunda
**sıfırdır**; bedeli ise görünürdedir, ikili biçim 756 katsayı tutar ve 571.536 çekirdek girişi
hesaplar.

Geriye çekirdeğin asıl iddiası kalıyor: aynı model, sütunlar açıkça hesaplandığında da aynı kararı
vermelidir.

```python
def acik_polinom(v):        # (x . z + 1)^2 / 10 cekirdegine denk acik sutunlar
    d = len(v)
    o = [math.sqrt(2) * v[i] for i in range(d)] + [v[i] * v[i] for i in range(d)]
    o += [math.sqrt(2) * v[i] * v[j] for i in range(d) for j in range(i + 1, d)]
    return [x / math.sqrt(10.0) for x in o]


AE = [(acik_polinom(v), y) for v, y in VE]
AS = [(acik_polinom(v), y) for v, y in VS]
LAM = 0.03
ma = marj_egit(AE, LAM, tur=40)
KE_, KS_ = cekirdek("polinom", GE, NE, NE), cekirdek("polinom", GS, NS, NE)
a, b = ikili_egit(KE_, Y, LAM)
tk = [sum(a[j] * KS_[i][j] for j in range(len(a))) + b > 0 for i in range(len(VS))]
ta = [skor(ma[0], ma[1], v) > 0 for v, _ in AS]
print(f"acikca hesaplanan sutun {len(AE[0][0])}, cekirdekle acilan sutun 0")
print(f"acik sutunlarla sinama  {dogruluk(ma, AS):.4f}")
print(f"cekirdekle sinama       {CEK[('polinom', LAM)][1]:.4f}")
print(f"iki bicimin ayni tahmini verdigi sinama satiri "
      f"{sum(p == q for p, q in zip(tk, ta))}/{len(VS)}")
```

```
acikca hesaplanan sutun 90, cekirdekle acilan sutun 0
acik sutunlarla sinama  0.7937
cekirdekle sinama       0.7937
iki bicimin ayni tahmini verdigi sinama satiri 252/252
```

On iki sütunun ikinci dereceden bütün terimleri **90 sütun** açar ve iki biçim 252 sınama
satırının **252'sinde** aynı tahmini verir. Kazanç sayıda değil açılmayan sütundadır: derece üçe
çıksaydı açılacak sütun 454'e giderken çekirdek işlevi tek bir üs değişikliğiyle kalırdı. **On
yedi aday** denendi, ailenin sayısı 0,7857'dir ve eğri sınır taban çizgisi üstündeki farka hiçbir
şey eklemedi.

#### Özet

- Marj genişliği ceza katsayısıyla 0,047'den 93,488'e çıkar; en dar marjda doğrulama sayısı kendi
  tabanının 0,0714 altına düşer.
- Ailenin sayısı 0,7857'dir ve taban çizgisini 0,0278 geçer; doğrulamada üç aday berabere kaldığı
  için bu sayının 0,0159'u beraberlik kuralından gelir.
- Kararı marj içindeki 324 satır taşır: marj dışından 300 satır atılınca 4, marj içinden 21 tahmin
  değişir.
- Üç çekirdekle dokuz aday denendi; seçilen çekirdek **doğrusaldır** ve eğri sınır hiçbir şey
  eklemedi.
- Polinom çekirdeği, açıkça hesaplanmış 90 sütunla 252 sınama satırının 252'sinde aynı tahmini
  verir; ikili biçimin bedeli 571.536 çekirdek girişidir.

#### Sonraki Adım

Üç ailenin ortak bir kısıtı vardı: uzaklık toplamı, olasılık çarpımı ve iç çarpım bütün sütunları
aynı anda kullanır ve hangi sütunun kararı taşıdığı ancak dolaylı okunur. Sonraki ders sütunları
teker teker ele alan bir aileye döner: **karar ağacı** her adımda tek bir sütun ve tek bir eşik
seçer, seçimi bir **bölme ölçütüne** göre yapar. Ağacın kendisi M27/K01 ve K02 boyunca kullanıldı
ve derinliği ölçüldü; sorulmayan şey, bölmeyi hangi ölçütün seçtiği ve derinliği baştan sınırlamak
yerine büyümüş ağacı **budamanın** neyi geri aldığıdır.

### Karar Ağaçları

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/karar-agaclari

Bölme ölçütünün seçtiği bölmenin ve budamanın derinlik sınırından farkının ölçülmesi: saflıksızlık ile düzensizlik aynı kök bölmesini seçer ama kırk yedi bölmenin yalnız on sekizinde birleşir, yanlış sınıflandırma oranı ise 695/61'lik bambaşka bir bölme seçer. Budanmamış derinlik 12 ağacı 127 yaprakla sınama kümesinde 0,7341 verir, taban çizgisinin altında; budama aynı ağacı 20 yaprağa indirip 0,7857'ye çıkarır. Derinlik sınırı 48 yaprakla 0,7897 verirken budama 11 yaprakla 0,7857 verir ve bunların dördü altıncı düzeyden derindedir. Yüzde doksanlık sekiz alt örneklemin ağaçlarında ortak tek bir bölme yoktur.

Önceki üç aile sütunları birlikte kullandı: uzaklık toplamı, olasılık çarpımı ve iç çarpım her
kararda on iki sütunun hepsini işin içine soktu. **Karar ağacı** tersini yapar; her adımda tek
bir sütun ve tek bir eşik seçer, kümeyi ikiye böler ve aynı işi iki parçada yineler.

Ağacın kendisi bu müfredatta yeni değil: M27/K01 ve K02 boyunca derinlik 6 sabit bir ağaç
kullanıldı, kapasite arttıkça iki sayının ayrışması ve yaprak alt sınırının etkisi M27/K01'in
genelleme konusunda ölçüldü. Burada o ölçümler tekrarlanmaz. Sorulmamış olan iki şey vardır:
bölmeyi **hangi ölçütün** seçtiği ve derinliği baştan sınırlamak yerine büyümüş bir ağacı
**budamanın** neyi geri aldığı.

- **DO24.** Küme, bölme ve taban çizgisi değişmez: 1.260 abonelik **kurgu** tablo, tohum
  20260218, bölme 756/252/252, taban çizgisi sınamada 0,7579 ve doğrulamada 0,8135. Öznitelik
  kümesi K02'den gelen on iki sütundur. Ağaç sıralamaya baktığı için ölçekleme uygulanmaz;
  M27/K02 bu adımın ağaçtaki katkısının tam olarak sıfır olduğunu ölçtü.
- **DO25.** **Bölme ölçütü**, bir bölmenin ne kadar kazandırdığını ölçen işlevdir. Üçü
  karşılaştırılır: **saflıksızlık**, **düzensizlik** ve **yanlış sınıflandırma oranı**. Kazanç,
  bölmeden önceki değerden iki parçanın satır sayısıyla ağırlıklandırılmış değerinin çıkarılmasıdır.
- **DO26.** Her sütun için en çok on aday eşik denenir ve her yaprakta en az iki satır kalır.
  Derinlik süpürmesi, öğrenme eğrisi ve yaprak alt sınırı **M27/K01'de ölçüldü ve tekrarlanmaz**;
  buradaki derinlik 6 o kurstan gelen sabit, derinlik 12 ise "sınırsız büyüsün" karşılığıdır.
- **DO27.** **Budama**, büyümüş ağacın bir düğümünü yaprağa çevirmektir. Karar **doğrulama
  kümesinden** okunur: bir düğüm, yaprağa çevrildiğinde doğrulama kümesindeki hatası artmıyorsa
  çevrilir. **Budama eşiği**, ne kadar hata artışına göz yumulduğudur ve ayar değişkenidir.
- **DO28.** Yaprak sayısı ve yaprak derinliği ağacın büyüklük ölçüleridir; ikisi ayrı ayrı basılır.
- **DO29.** Seçim doğrulama kümesinden okunur; beraberlikte daha az yapraklı ağaç seçilir.
- **DO30.** Kırılganlık ölçümünde eğitim kümesinin yüzde 90'ı sekiz kez ayrı tohumla çekilir;
  doğrulama ve sınama kümeleri değişmez.

#### Bölme Ölçütü Ne Seçiyor

Kurulum kümeyi üretir, üç ölçütü yazar, kök bölmesini ve derinlik 6 ağaçlarını basar.

```python
# agac.py — MODELDIR. M27/K01 ve K02'nin KURGU abone tablosu ayni tohumla
# uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabittir.
import math



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)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    VERI.append(x)


ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi",
        "sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]


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:]
TABAN_SIN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)


def oran(s):
    return sum(x["supheli"] for x in s) / len(s) if s else 0.0


def saflik(s):                              # safliksizlik
    p = oran(s)
    return 2 * p * (1 - p)


def duzensizlik(s):                         # duzensizlik
    p = oran(s)
    return 0.0 if p <= 0.0 or p >= 1.0 else -(p * math.log2(p) + (1 - p) * math.log2(1 - p))


def yanlis_oran(s):                         # yanlis siniflandirma orani
    p = oran(s)
    return min(p, 1 - p)


OLCUT = [("safliksizlik", saflik), ("duzensizlik", duzensizlik),
         ("yanlis oran", yanlis_oran)]


def en_iyi_bolme(s, f, enaz=2):
    en = None
    for a in ALAN:
        d = sorted({x[a] for x in s})
        for v in (d[1:] if len(d) < 10 else [d[int(i * len(d) / 10)] for i in range(1, 10)]):
            sol = [x for x in s if x[a] < v]
            sag = [x for x in s if x[a] >= v]
            if min(len(sol), len(sag)) < enaz:
                continue
            k = f(s) - (len(sol) * f(sol) + len(sag) * f(sag)) / len(s)
            if en is None or k > en[0]:
                en = (k, a, v, sol, sag)
    return en


def agac(s, derinlik, f, enaz=2):           # MODELDIR: karar agaci
    p, poz = oran(s), sum(x["supheli"] for x in s)
    en = en_iyi_bolme(s, f, enaz) if derinlik and len(s) >= 2 * enaz and 0.0 < p < 1.0 else None
    if en is None or en[0] <= 1e-9:
        return {"tahmin": int(p > 0.5), "n": len(s), "poz": poz}
    return {"alan": en[1], "esik": en[2], "n": len(s), "poz": poz,
            "sol": agac(en[3], derinlik - 1, f, enaz),
            "sag": agac(en[4], derinlik - 1, f, enaz)}


def tahmin(d, x):
    while "tahmin" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["tahmin"]


def dogruluk(d, s):
    return sum(tahmin(d, x) == x["supheli"] for x in s) / len(s)


def bolmeler(d, yol=()):                    # (konum, sutun, esik) uclulari
    if "tahmin" in d:
        return []
    return ([(yol, d["alan"], round(d["esik"], 3))]
            + bolmeler(d["sol"], yol + ("s",)) + bolmeler(d["sag"], yol + ("g",)))


print(f"taban cizgisi sinamada {TABAN_SIN:.4f}; egitim {len(EGT)} satir")
print(f"{'olcut':<14}{'kok sutun':>11}{'esik':>8}{'kazanc':>9}{'sol/sag':>12}")
for ad, f in OLCUT:
    en = en_iyi_bolme(EGT, f)
    print(f"{ad:<14}{en[1]:>11}{en[2]:>8.2f}{en[0]:>9.5f}"
          f"{str(len(en[3])) + '/' + str(len(en[4])):>12}")
AGAC6 = {}
print(f"\n{'olcut (derinlik 6)':<20}{'yaprak':>7}{'dogrulama':>11}{'sinama':>8}")
for ad, f in OLCUT:
    AGAC6[ad] = agac(EGT, 6, f)
    print(f"{ad:<20}{len(bolmeler(AGAC6[ad])) + 1:>7}"
          f"{dogruluk(AGAC6[ad], DOG):>11.4f}{dogruluk(AGAC6[ad], SIN):>8.4f}")
B = {ad: set(bolmeler(t)) for ad, t in AGAC6.items()}
print(f"safliksizlik ile duzensizlik agaclarinin ortak bolmesi "
      f"{len(B['safliksizlik'] & B['duzensizlik'])}, "
      f"yanlis oran ile ortak bolmesi {len(B['safliksizlik'] & B['yanlis oran'])}")
```

```
taban cizgisi sinamada 0.7579; egitim 756 satir
olcut           kok sutun    esik   kazanc     sol/sag
safliksizlik     oynaklik   11.69  0.03923     511/245
duzensizlik      oynaklik   11.69  0.07761     511/245
yanlis oran      oynaklik   23.15  0.01984      695/61

olcut (derinlik 6)   yaprak  dogrulama  sinama
safliksizlik             48     0.8016  0.7897
duzensizlik              38     0.7778  0.7897
yanlis oran               8     0.7937  0.7698
safliksizlik ile duzensizlik agaclarinin ortak bolmesi 18, yanlis oran ile ortak bolmesi 0
```

Saflıksızlık ile düzensizlik kök bölmesinde birleşiyor: ikisi de oynaklık sütununu ve 11,69
eşiğini seçip kümeyi 511/245 olarak ayırıyor. Kazanç sayıları farklıdır — 0,03923 ve 0,07761 —
çünkü iki ölçüt aynı büyüklüğü ölçmez; seçtikleri bölme aynıdır. Yanlış sınıflandırma oranı ise
aynı kümede 695/61'lik bambaşka bir bölme seçiyor.

Fark ölçütün biçiminden gelir. Yanlış sınıflandırma oranı, sınıf oranının doğrusal parçalardan
oluşan bir işlevidir: iki parçanın da çoğunluk sınıfı aynı kalıyorsa kazanç tam olarak sıfırdır,
o bölme parçaları ne kadar saflaştırırsa saflaştırsın. Saflıksızlık ile düzensizlik kesin
içbükeydir ve çoğunluk sınıfı değişmese bile saflaşmayı ödüllendirir. Bu yüzden yanlış oran ölçütü
yalnız çoğunluğu çeviren bölmeleri görür ve ağacı sekiz yaprakta durdurur; sınama sayısı da
0,7698'de, ötekilerin 0,7897'sinin altında kalır.

Aynı kök, aynı ağaç anlamına gelmiyor. Saflıksızlık ağacı kırk yedi bölme, düzensizlik ağacı otuz
yedi bölme taşıyor ve bunların yalnız **on sekizi** hem sütun hem eşik olarak ortak. Ölçüt kök
bölmesinde ayrışmasa bile ağacın alt katlarında ayrışıyor; iki ağaç sınama kümesinde aynı sayıyı
verirken farklı sorular soruyor.

#### Budama Derinlik Sınırından Ne Kadar Farklı

Derinlik sınırı ağacı her dalda aynı düzeyde keser. Budama ise ağacı önce serbest bırakır, sonra
**yalnız ödemeyen dalları** geri alır; ödeyip ödemediği ayrılmış kümede ölçülür.

```python
def hata(d, s, yaprak_olsa):
    if yaprak_olsa:
        t = int(d["poz"] / d["n"] > 0.5)
        return sum(1 for x in s if x["supheli"] != t)
    return sum(1 for x in s if tahmin(d, x) != x["supheli"])


def buda(d, s, esik, n):                    # ayrilmis kumeden okunan budama
    if "tahmin" in d:
        return d
    d = dict(d, sol=buda(d["sol"], [x for x in s if x[d["alan"]] < d["esik"]], esik, n),
             sag=buda(d["sag"], [x for x in s if x[d["alan"]] >= d["esik"]], esik, n))
    if hata(d, s, True) <= hata(d, s, False) + esik * n:
        return {"tahmin": int(d["poz"] / d["n"] > 0.5), "n": d["n"], "poz": d["poz"]}
    return d


def yaprak_derinlikleri(d, k=0):
    if "tahmin" in d:
        return [k]
    return yaprak_derinlikleri(d["sol"], k + 1) + yaprak_derinlikleri(d["sag"], k + 1)


BUDANMIS, ESIKLER = {}, (0.0, 0.004, 0.012)
print(f"{'olcut':<14}{'budama':>8}{'yaprak':>8}{'derinlik':>9}{'dogrulama':>11}{'sinama':>8}")
for ad, f in OLCUT:
    buyuk = agac(EGT, 12, f)
    yd = yaprak_derinlikleri(buyuk)
    print(f"{ad:<14}{'yok':>8}{len(yd):>8}{max(yd):>9}"
          f"{dogruluk(buyuk, DOG):>11.4f}{dogruluk(buyuk, SIN):>8.4f}")
    for esik in ESIKLER:
        b = buda(buyuk, DOG, esik, len(DOG))
        yd = yaprak_derinlikleri(b)
        BUDANMIS[(ad, esik)] = (dogruluk(b, DOG), dogruluk(b, SIN), len(yd), max(yd))
        print(f"{ad:<14}{esik:>8}{len(yd):>8}{max(yd):>9}"
              f"{BUDANMIS[(ad, esik)][0]:>11.4f}{BUDANMIS[(ad, esik)][1]:>8.4f}")
sec = max(BUDANMIS, key=lambda a: (BUDANMIS[a][0], BUDANMIS[a][2]))
print(f"\ndenenen aday {len(OLCUT) + len(BUDANMIS)} (olcut {len(OLCUT)} + budama {len(BUDANMIS)})")
print(f"dogrulamadan secilen: {sec[0]}, budama esigi {sec[1]} -> sinama "
      f"{BUDANMIS[sec][1]:.4f}, taban cizgisinin {BUDANMIS[sec][1] - TABAN_SIN:+.4f} ustunde")
b = buda(agac(EGT, 12, saflik), DOG, 0.004, len(DOG))
print(f"derinlik siniri 6: {len(bolmeler(AGAC6['safliksizlik'])) + 1} yaprak, sinama "
      f"{dogruluk(AGAC6['safliksizlik'], SIN):.4f}")
print(f"budama esigi 0.004: {len(yaprak_derinlikleri(b))} yaprak, sinama "
      f"{dogruluk(b, SIN):.4f}, 6'dan derin yaprak "
      f"{sum(1 for k in yaprak_derinlikleri(b) if k > 6)}")
```

```
olcut           budama  yaprak derinlik  dogrulama  sinama
safliksizlik       yok     127       12     0.7619  0.7341
safliksizlik       0.0      20        8     0.8413  0.7857
safliksizlik     0.004      11        8     0.8294  0.7857
safliksizlik     0.012       1        0     0.8135  0.7579
duzensizlik        yok     122       12     0.7381  0.7460
duzensizlik        0.0      22       12     0.8373  0.7817
duzensizlik      0.004       3        2     0.8214  0.7857
duzensizlik      0.012       1        0     0.8135  0.7579
yanlis oran        yok       8        4     0.7937  0.7698
yanlis oran        0.0       1        0     0.8135  0.7579
yanlis oran      0.004       1        0     0.8135  0.7579
yanlis oran      0.012       1        0     0.8135  0.7579

denenen aday 12 (olcut 3 + budama 9)
dogrulamadan secilen: safliksizlik, budama esigi 0.0 -> sinama 0.7857, taban cizgisinin +0.0278 ustunde
derinlik siniri 6: 48 yaprak, sinama 0.7897
budama esigi 0.004: 11 yaprak, sinama 0.7857, 6'dan derin yaprak 4
```

Budanmamış ağaç ölçünün her yerinde kötüdür: 127 yaprakla sınama kümesinde 0,7341 okuyor, yani
taban çizgisinin 0,0238 **altında**. Aynı ağaç budandığında 20 yaprağa iniyor ve 0,7857'ye
çıkıyor; doğrulama sayısı 0,7619'dan 0,8413'e gidiyor. **Ailenin sayısı 0,7857**'dir, taban
çizgisini 0,0278 geçer ve **12 aday** denenerek bulunmuştur.

Son üç satır budamanın derinlik sınırından farkını veriyor. Derinlik 6 ağacı 48 yaprakla 0,7897,
budanmış ağaç 11 yaprakla 0,7857 okuyor: aradaki fark bir abone, yapraktaki fark dört kattan
fazla. Daha önemlisi budanmış ağacın **derinliği 8'dir** ve dört yaprağı altıncı düzeyden
derindedir. Derinlik sınırı bu dört yaprağı, ödeyip ödemediklerine bakmadan keserdi; budama
onları tutup başka yerdeki otuz yedi yaprağı attı. İki adımın geri aldığı şey aynı değildir:
biri **düzeye**, öteki **katkıya** bakar.

Yanlış oran ölçütünün satırları ayrı bir şey söylüyor: üç budama eşiğinde de ağaç tek yaprağa
iniyor ve model taban çizgisine düşüyor. Sekiz yapraklık ağacının hiçbir bölmesi doğrulama
kümesinde kendini ödemiyor.

#### Tek Ağacın Kararı Ne Kadar Sağlam

Seçilen ağaç bir sayı verdi. O sayının ne kadarının eğitim kümesinin tam olarak bu 756 satır
olmasından geldiği ayrıca ölçülür.

```python
def alt_orneklem(s, tohum, pay=0.9):        # egitim kumesinin yuzde 90'i
    r, k = uretec(tohum), list(s)
    for i in range(len(k) - 1, 0, -1):
        j = int(r() * (i + 1))
        k[i], k[j] = k[j], k[i]
    return k[:int(len(k) * pay)]


ORTAK, SAYI = None, []
print(f"{'orneklem':>9}{'kok bolme':>26}{'yaprak':>8}{'sinama':>8}")
for i in range(8):
    alt = alt_orneklem(EGT, TOHUM + 820000 + i * 13)
    t = buda(agac(alt, 12, saflik), DOG, 0.004, len(DOG))
    en = en_iyi_bolme(alt, saflik)
    b = set(bolmeler(t))
    ORTAK = b if ORTAK is None else ORTAK & b
    SAYI.append(dogruluk(t, SIN))
    print(f"{i + 1:>9}{en[1] + ' < ' + format(en[2], '.2f'):>26}"
          f"{len(yaprak_derinlikleri(t)):>8}{SAYI[-1]:>8.4f}")
print(f"sekiz agacin hepsinde bulunan bolme (sutun ve esik birlikte) {len(ORTAK)}")
print(f"sinama sayilari {min(SAYI):.4f} ile {max(SAYI):.4f} arasinda, "
      f"aralik {max(SAYI) - min(SAYI):.4f}")
```

```
 orneklem                 kok bolme  yaprak  sinama
        1          oynaklik < 13.99       3  0.7897
        2          oynaklik < 13.88      10  0.7897
        3          oynaklik < 13.80      13  0.7817
        4          oynaklik < 14.12       3  0.7897
        5          oynaklik < 11.41       8  0.7817
        6          oynaklik < 13.60       5  0.7619
        7          oynaklik < 13.91       6  0.7897
        8          oynaklik < 14.12      10  0.7857
sekiz agacin hepsinde bulunan bolme (sutun ve esik birlikte) 0
sinama sayilari 0.7619 ile 0.7897 arasinda, aralik 0.0278
```

Kök sütunu sekiz örneklemde de aynı kalıyor: oynaklık. Eşik ise 11,41 ile 14,12 arasında
geziniyor ve yaprak sayısı 3 ile 13 arasında değişiyor — aynı yordam, aynı ayarlar, yalnız
eğitim kümesinin onda biri değişmiş durumda. Sekiz ağacın **hepsinde** bulunan bölme sayısı
**sıfırdır**: sütun ve eşik birlikte alındığında tek bir ortak bölme yok.

Sınama sayıları 0,7619 ile 0,7897 arasında, aralık 0,0278. Bu sayı ailenin taban çizgisi
üstündeki farkının tamamına eşittir. Ölçülen fark, eğitim kümesinin hangi yüzde doksanının
çekildiğine bağlı olarak tümüyle silinebiliyor.

#### Özet

- Saflıksızlık ile düzensizlik aynı kök bölmesini seçer (oynaklık, 11,69, 511/245); yanlış
  sınıflandırma oranı 695/61'lik başka bir bölme seçer, çünkü çoğunluğu çevirmeyen bölmelere tam
  olarak sıfır kazanç verir.
- Aynı kökten çıkan iki ağaç kırk yedi ve otuz yedi bölme taşır ve yalnız on sekizinde birleşir.
- Budanmamış derinlik 12 ağacı 127 yaprakla 0,7341, yani taban çizgisinin 0,0238 altında okur;
  budama aynı ağacı 20 yaprağa indirip 0,7857'ye çıkarır.
- Ailenin sayısı 0,7857'dir, taban çizgisini 0,0278 geçer ve **12 aday** denenerek bulunmuştur.
- Budama derinlik sınırından farklı olarak **katkıya** bakar: 11 yapraklı budanmış ağacın dört
  yaprağı altıncı düzeyden derindir ve derinlik sınırı bunları ölçmeden keserdi.
- Yüzde doksanlık sekiz alt örneklemin ağaçlarında ortak tek bir bölme yoktur; sınama sayıları
  0,0278 aralıkta geziniyor.

#### Sonraki Adım

Tek bir ağaç budandı, elde kalan sayı taban çizgisini 0,0278 geçti ve on iki aday denendi. Ama son
tablo bu sayının altını oyuyor: aynı yordam, eğitim kümesinin onda biri değiştiğinde bambaşka
bölmeler seçiyor ve verdiği sayı taban çizgisi üstündeki farkın tamamı kadar geziniyor. Tek ağacın
kararı **kırılgandır** ve kırılganlık ağacın bir kusuru değil, tanımının sonucudur: her düğümde tek
bir eşik seçilir ve o eşik en yakın rakibinden yalnız birkaç satırla ayrılıyorsa, birkaç satır
değiştiğinde seçim de değişir. Sonraki ders bu kırılganlığı bir kusur olarak düzeltmek yerine
kullanır: aynı yordamı **farklı eğitim örneklemlerinde** birden çok kez çalıştırıp kararları
birleştirir. Ölçülecek şey ağaç sayısı arttıkça ayrılmış küme sayısının oturması ve değişkenliğin
düşmesidir.

### Torbalanmış Ağaç Toplulukları

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/torbalanmis-agac-topluluklari

Tek ağacın kırılganlığının sayılması ve torbalamanın kattığının ölçülmesi: 41 torba örnekleminde aynı yordam kök bölmesi için dokuz ayrı alan-eşik çifti seçiyor, iki ağaç doğrulama kümesindeki 252 abonenin ortalama 46,2'sini ayrı sınıflıyor ve tek ağacın sınama kümesindeki sayısı 0,6905 ile 0,7897 arasında geziniyor. Ağaç sayısı 1'den 41'e çıkarken doğrulama kümesindeki saçılma 0,0193'ten 0,0075'e iniyor. Öznitelik rastgeleliği eklendiğinde 31 aday arasından doğrulama kümesiyle seçilen topluluk sınama kümesinde 0,7897 veriyor; torba dışı kestirim 0,7884 ile bu sayıya 0,0013 uzakta durur, doğrulama kümesinin kendi sayısı ise 0,0516 uzaktadır.

Önceki ders karar ağacının bölme ölçütünü ve budamayı ayırdı; budanmış ağaç taban çizgisini geçti
ve dört derste denenen aileler arasında en okunaklı model oldu. Bir özelliği ölçülmeden kaldı:
eğitim kümesi biraz değiştiğinde ağaç bambaşka bölmeler seçiyor. Kök kayınca altındaki her şey
kayar; tek ağacın verdiği sayı bir ölçüm değil bir çekilişin sonucudur.

Bu ders önce o çekilişi sayar, sonra daraltmanın yolunu kurar. **Torbalama** (bagging) aynı yordamı
geri koyarak çekilmiş birçok örneklem üzerinde ayrı ayrı çalıştırıp ağaçların **çoğunluk oyunu**
alır. **Öznitelik rastgeleliği** ikinci bir ayrım ekler: her ağaç bölmelerini rastgele seçilmiş bir
alt sütun kümesinden seçer. Ölçülen tek sayı değil ikidir: topluluğun sayısı ve o sayının
saçılması.

- **DO31.** Küme M27/K01'in **kurgu** abone tablosudur: 1.260 abone, 756/252/252, tohum 20260218.
  Taban çizgisi sınamada 0,7579, doğrulamada 0,8135'tir; kursun taşıdığı sayı sınamadakidir.
- **DO32.** Öznitelik kümesi M27/K02'den gelir ve on bir sütundur; değişen tek şey model.
- **DO33.** Karşılaştırma noktası önceki derslerin ağacıdır: derinlik 6, yaprak alt sınırı 2, bölme
  ölçütü saflıksızlık azalması. Eşikler eğitim kümesinden bir kez çıkarılır ve sütun başına en çok
  yirmi dörttür; bu bir hız kararıdır, seçilebilecek bölmeleri değiştirmez.
- **DO34.** Torba örneklemi 756 satırlıktır ve geri koyarak çekilir. Geri koyarak örnekleme M26'nın
  Keşifsel Analiz kursundaki Uygulamalı İstatistik konusunda kuruldu; yöntemi tekrarlanmaz.
- **DO35.** Karar çoğunluk oyudur: olumlu diyen ağaçların oranı 0,5'i geçerse abone şüpheli
  sayılır. Eşik M27/K02'den sabittir; seçimi model değerlendirmeye bırakılır.
- **DO36.** Denenen aday sayısı 31'dir: yedi ağaç sayısı ve yirmi dört alan-derinlik çifti. Seçim
  **doğrulama kümesinden** okunur; sınama kümesi yalnız son satırda görünür.

#### Tek Ağacın Saçılması

Kurulum tabloyu üretir, sütunları eşiklere kutular ve karşılaştırma noktası olan tek ağacı basar.
```python
# torbalama.py — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden
# uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabit tutulur.
import bisect
import math

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)}
    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",
        "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
ESIK = {}
for a in ALAN:
    d = sorted({x[a] for x in EGT})
    ESIK[a] = d[1:] if len(d) <= 24 else [d[int(i * len(d) / 25)] for i in range(1, 25)]
KOVA = [len(ESIK[a]) + 1 for a in ALAN]


def kutula(s):                              # her satir: kova numaralari ve etiket
    return [(tuple(bisect.bisect_right(ESIK[a], x[a]) for a in ALAN), x["supheli"])
            for x in s]


KE, KD, KS = kutula(EGT), kutula(DOG), kutula(SIN)
TUM = list(range(len(ALAN)))


def agac(orn, derinlik, alanlar, enaz=2):   # MODELDIR: karar agaci
    n, p, en = len(orn), sum(y for _, y in orn), None
    if derinlik and n >= 2 * enaz and 0 < p < n:
        for k in alanlar:
            say, poz = [0] * KOVA[k], [0] * KOVA[k]
            for b, y in orn:
                say[b[k]] += 1
                poz[b[k]] += y
            sc = sp = 0
            for j in range(KOVA[k] - 1):
                sc, sp = sc + say[j], sp + poz[j]
                if sc < enaz or n - sc < enaz:
                    continue
                kazanc = sp * sp / sc + (p - sp) ** 2 / (n - sc) - p * p / n
                if en is None or kazanc > en[0]:
                    en = (kazanc, k, j)
    if en is None or en[0] <= 1e-9:
        return p / n
    _, k, j = en
    return (k, j, agac([o for o in orn if o[0][k] <= j], derinlik - 1, alanlar, enaz),
            agac([o for o in orn if o[0][k] > j], derinlik - 1, alanlar, enaz))


def oran(d, b):
    while type(d) is tuple:
        d = d[2] if b[d[0]] <= d[1] else d[3]
    return d


def dogruluk(d, kume):
    return sum((oran(d, b) > 0.5) == y for b, y in kume) / len(kume)


def taban(kume):
    return sum(y == 0 for _, y in kume) / len(kume)


TEK = agac(KE, 6, TUM)
print(f"taban cizgisi        dogrulama {taban(KD):.4f}  sinama {taban(KS):.4f}")
print(f"tek agac derinlik 6  dogrulama {dogruluk(TEK, KD):.4f}  "
      f"sinama {dogruluk(TEK, KS):.4f}  egitim {dogruluk(TEK, KE):.4f}")
```

```
taban cizgisi        dogrulama 0.8135  sinama 0.7579
tek agac derinlik 6  dogrulama 0.8056  sinama 0.7778  egitim 0.8426
```
İki ayrılmış küme aynı yordamla iki ayrı taban veriyor: sınamada 0,7579, doğrulamada 0,8135. Ağaç
sınamada 0,7778 okuyup tabanı 0,0199 geçiyor, doğrulamada 0,8056'da kalıp kendi tabanının 0,0079
altına düşüyor.

Kırılganlık şöyle sayılır: eğitim kümesinden geri koyarak 41 örneklem çekilir, her birine aynı
yordamla bir ağaç kurulur. Değişen tek şey hangi satırın kaç kez göründüğüdür.
```python
def sacilma(v):
    o = sum(v) / len(v)
    return (sum((q - o) ** 2 for q in v) / len(v)) ** 0.5


def orman(tohum, sayi, m, derinlik):        # MODELDIR: torbalanmis agac toplulugu
    r, agaclar, torbalar = uretec(tohum), [], []
    for _ in range(sayi):
        secim = [int(r() * len(KE)) for _ in range(len(KE))]
        havuz, alanlar = list(TUM), []
        for _ in range(min(m, len(TUM))):
            alanlar.append(havuz.pop(int(r() * len(havuz))))
        agaclar.append(agac([KE[i] for i in secim], derinlik, sorted(alanlar)))
        torbalar.append(set(secim))
    return agaclar, torbalar


def oy(agaclar, b):                         # cogunluk oyu: olumlu diyen agac orani
    return sum(oran(d, b) > 0.5 for d in agaclar) / len(agaclar)


def orman_dogruluk(agaclar, kume):
    return sum((oy(agaclar, b) > 0.5) == y for b, y in kume) / len(kume)


ORMANLAR = [orman(TOHUM + 13 * i, 41, 11, 6) for i in range(5)]
AGACLAR = ORMANLAR[0][0]
kok = {}
for d in AGACLAR:
    kok[(d[0], d[1])] = kok.get((d[0], d[1]), 0) + 1
etiket = [[oran(d, b) > 0.5 for b, _ in KD] for d in AGACLAR]
ayri = [sum(u != v for u, v in zip(etiket[i], etiket[j]))
        for i in range(len(AGACLAR)) for j in range(i + 1, len(AGACLAR))]
print(f"kok bolmesi icin {len(kok)} ayri (alan, esik) cifti, "
      f"en siki {max(kok.values())} agacta")
print(f"{'tek agac':<12} {'ortalama':>9} {'sacilma':>9} {'en dusuk':>9} {'en yuksek':>10}")
for ad, kume in (("dogrulama", KD), ("sinama", KS)):
    v = [dogruluk(d, kume) for d in AGACLAR]
    print(f"{'  ' + ad:<12} {sum(v) / len(v):>9.4f} {sacilma(v):>9.4f} "
          f"{min(v):>9.4f} {max(v):>10.4f}")
print(f"iki agacin ayri sinifladigi abone (252 icinde): ortalama "
      f"{sum(ayri) / len(ayri):.1f}, en az {min(ayri)}, en cok {max(ayri)}")
print(f"ayni 41 agacin oyu   dogrulama {orman_dogruluk(AGACLAR, KD):.4f}  "
      f"sinama {orman_dogruluk(AGACLAR, KS):.4f}")
```

```
kok bolmesi icin 9 ayri (alan, esik) cifti, en siki 16 agacta
tek agac      ortalama   sacilma  en dusuk  en yuksek
  dogrulama     0.7744    0.0218    0.7222     0.8135
  sinama        0.7489    0.0246    0.6905     0.7897
iki agacin ayri sinifladigi abone (252 icinde): ortalama 46.2, en az 24, en cok 77
ayni 41 agacin oyu   dogrulama 0.8095  sinama 0.7698
```
Kök bölmesi 41 ağacın hepsinde aynı değil: dokuz ayrı alan-eşik çifti kök oluyor, en sık görüleni
yalnız 16 ağaçta. Altındaki her şeyi belirleyen bölme, örneklem değişince ağaçların yüzde
altmışında başka bir yere kayıyor.

İkinci sayı daha somut: rastgele iki ağaç 252 abonenin ortalama 46,2'sini ayrı sınıflıyor, en
yakın çiftte 24, en uzak çiftte 77 aboneyi.

Üçüncüsü bunun ayrılmış kümeye yansımasıdır. Tek ağaçların sınama sayısı ortalama 0,7489, saçılma
0,0246, aralık 0,6905 ile 0,7897; iki uç arası 25 abone eder. Bir modelin sayısını tek örneklemden
okumak, bu aralıktan rastgele bir nokta seçmektir. **Saçılma**, ortalamadan kare uzaklıkların
ortalamasının kareköküdür.

Son satır aynı 41 ağacın oyudur: doğrulama 0,8095, sınama 0,7698. Bu sayı tek ağaçların ortalaması
0,7489'un 0,0209 üstünde, ama tek ağacın 0,7778'inin **altında**: bütün sütunlar her ağaca açıkken
torbalama ortalamayı yukarı çekiyor, tek ağacı geçmiyor.

#### Ağaç Sayısı ve Değişkenlik

Torbalamanın asıl kattığı ortalamada değil saçılmadadır. Beş tohumla beş topluluk kurulur, her
birinin ilk 1, 3, 5, 11, 21, 31 ve 41 ağacı ayrı ayrı oylanır.
```python
print(f"{'agac':>5} {'dogrulama':>10} {'sacilma':>9} {'sinama':>9} {'sacilma':>9}")
for sayi in (1, 3, 5, 11, 21, 31, 41):
    vd = [orman_dogruluk(a[:sayi], KD) for a, _ in ORMANLAR]
    vs = [orman_dogruluk(a[:sayi], KS) for a, _ in ORMANLAR]
    print(f"{sayi:>5} {sum(vd) / 5:>10.4f} {sacilma(vd):>9.4f} "
          f"{sum(vs) / 5:>9.4f} {sacilma(vs):>9.4f}")
```

```
 agac  dogrulama   sacilma    sinama   sacilma
    1     0.7786    0.0193    0.7627    0.0088
    3     0.7944    0.0136    0.7762    0.0102
    5     0.8032    0.0096    0.7778    0.0148
   11     0.7984    0.0095    0.7722    0.0122
   21     0.8103    0.0105    0.7762    0.0082
   31     0.8183    0.0119    0.7754    0.0124
   41     0.8214    0.0075    0.7778    0.0066
```
Saçılma sütunları dersin ölçüsüdür. Tek ağaçta doğrulama saçılması 0,0193, 41 ağaçta 0,0075: beş
koşum arasındaki fark 2,6 kat daralıyor. Ortalama 0,7786'dan 0,8214'e yükseliyor ama düzgün değil;
5 ağaçta 0,8032, 11 ağaçta 0,7984. Ağaç eklemek her adımda daha iyi bir sayı vermez; verdiği şey,
tekrarda daha az oynayan bir sayıdır. Sınamada da öyle: 0,7627'den 0,7778'e, kazanç dört abone,
saçılma 0,0088'den 0,0066'ya. Bedel 41 kat hesaptır.

#### Öznitelik Rastgeleliği ve Torba Dışı Kestirim

Torbalanmış ağaçlar birbirine benziyor, çünkü hepsi aynı on bir sütuna bakıp aynı güçlü sütunu
kökte buluyor. İkinci bir rastgelelik kaynağı bunu kırar: her ağaç bölmelerini rastgele seçilmiş
`m` sütunluk bir alt kümeden seçer. Izgara `m` değerini ikiden on bire, derinliği üçten sekize
taşır. Üçüncü bir sayı daha üretilir: **torba dışı** (out-of-bag) kestirim her eğitim satırını
yalnız o satırı torbasına almamış ağaçlara oylatır ve ayrılmış kümeye dokunmadan bir sayı verir.
```python
def torba_disi(agaclar, torbalar):          # her satiri, onu gormeyen agaclar oylar
    dis = [(b, y, [d for d, c in zip(agaclar, torbalar) if i not in c])
           for i, (b, y) in enumerate(KE)]
    return sum((oy(v, b) > 0.5) == y for b, y, v in dis if v) / sum(1 for *_, v in dis if v)


ADAY, IZGARA = 7, {}
print(f"{'m':>3}" + "".join(f"{'derinlik ' + str(d):>16}" for d in (3, 4, 6, 8)))
for m in (2, 3, 4, 6, 8, 11):
    satir = []
    for derinlik in (3, 4, 6, 8):
        a, t = orman(TOHUM, 41, m, derinlik)
        IZGARA[(m, derinlik)] = (orman_dogruluk(a, KD), orman_dogruluk(a, KS),
                                 torba_disi(a, t))
        ADAY += 1
        satir.append(f"{IZGARA[(m, derinlik)][0]:.4f}/{IZGARA[(m, derinlik)][1]:.4f}")
    print(f"{m:>3}" + "".join(f"{q:>16}" for q in satir))
SIRA = [(m, d) for m in (2, 3, 4, 6, 8, 11) for d in (3, 4, 6, 8)]


def enustte(sutun):
    e = max(IZGARA[k][sutun] for k in SIRA)
    return next(k for k in SIRA if IZGARA[k][sutun] == e)


for ad, sutun in (("dogrulamadan", 0), ("sinamadan", 1), ("torba disindan", 2)):
    m, derinlik = enustte(sutun)
    d, s, o = IZGARA[(m, derinlik)]
    print(f"secim {ad:<15} m={m:<3} derinlik={derinlik:<3} "
          f"dogrulama {d:.4f}  sinama {s:.4f}  torba disi {o:.4f}")
m, derinlik = enustte(0)
print(f"denenen aday {ADAY}, ailenin sayisi {IZGARA[(m, derinlik)][1]:.4f}, "
      f"taban cizgisi ustu {IZGARA[(m, derinlik)][1] - taban(KS):+.4f}")
```

```
  m      derinlik 3      derinlik 4      derinlik 6      derinlik 8
  2   0.8135/0.7579   0.8135/0.7579   0.8135/0.7579   0.8135/0.7579
  3   0.8095/0.7540   0.8135/0.7579   0.8175/0.7619   0.8095/0.7579
  4   0.8175/0.7698   0.8214/0.7817   0.8175/0.7817   0.8254/0.7817
  6   0.8214/0.7778   0.8413/0.7897   0.8373/0.7937   0.8413/0.7857
  8   0.8254/0.7857   0.8214/0.7817   0.8254/0.7698   0.8294/0.7738
 11   0.8175/0.7857   0.8214/0.7778   0.8095/0.7698   0.8016/0.7698
secim dogrulamadan    m=6   derinlik=4   dogrulama 0.8413  sinama 0.7897  torba disi 0.7884
secim sinamadan       m=6   derinlik=6   dogrulama 0.8373  sinama 0.7937  torba disi 0.7950
secim torba disindan  m=6   derinlik=8   dogrulama 0.8413  sinama 0.7857  torba disi 0.7989
denenen aday 31, ailenin sayisi 0.7897, taban cizgisi ustu +0.0317
```
Üst satır rastgeleliğin sınırını gösteriyor: `m` iki olduğunda topluluk dört derinlikte de tam
olarak taban çizgisini veriyor, çünkü iki sütunlu ağaçların oyu hiçbir aboneyi şüpheli tarafa
geçirmiyor. `m` altı olduğunda doğrulamada 0,8413, sınamada 0,7897 çıkıyor; `m` on bir, yani
rastgelelik yokken, sayı 0,7778'e geriliyor. Rastgeleliğin kattığı fark bu sütundur, üç ile dört
abone.

Son üç satır kursun kuralını ödüyor. Doğrulamadan okunan seçim `m` altı ve derinlik dört diyor;
o adayın sınama sayısı 0,7897. Sınamadan okunsaydı derinlik altı seçilir, sayı 0,7937 olurdu; fark
0,0040, tek abone. Küçük, ama yönü sabittir: seçim de ölçüm de aynı 252 satırdan yapılırsa sayı
büyür.

Torba dışı sütunu daha keskin. Seçilen adayın doğrulama sayısı 0,8413, sınama sayısı 0,7897, torba
dışı kestirimi 0,7884: sınamaya 0,0013, doğrulamaya 0,0516 uzakta. Torba dışı 756 satırdan okunur
ve ayrılmış küme harcamaz; doğrulama 252 satırdan okunur ve her bakışta tükenir.

Ailenin sayısı 0,7897, denenen aday 31; taban çizgisinin 0,0317 üstünde. Asıl fark şurada: tek
ağacın sayısı 0,6905 ile 0,7897 arasında bir çekilişti, topluluğunki değil.

#### Özet

- Aynı yordam 41 torba örnekleminde kök için dokuz ayrı alan-eşik çifti seçiyor; iki ağaç 252
  abonenin ortalama 46,2'sini ayrı sınıflıyor.
- Tek ağacın sınama sayısı 0,6905 ile 0,7897 arasında geziniyor, saçılması 0,0246; tek örneklemden
  okunan sayı bu aralıktan rastgele seçilmiş bir noktadır.
- Ağaç sayısı 1'den 41'e çıkınca doğrulama saçılması 0,0193'ten 0,0075'e, ortalama 0,7786'dan
  0,8214'e gidiyor; artış düzgün değil, düşüş düzgün.
- Öznitelik rastgeleliği olmadan torbalama tek ağacı geçmiyor: on bir sütun açıkken sınama 0,7698,
  tek ağaçta 0,7778; `m` altı olduğunda 0,7897.
- Torba dışı kestirim 0,7884, sınamaya 0,0013 ve doğrulamaya 0,0516 uzakta; ayrılmış küme
  harcamadan okunur.

#### Sonraki Adım

Torbalama ağaçları **birbirinden bağımsız** kurar: 41 ağacın hiçbiri ötekinin nerede yanıldığını
bilmiyor, hepsi aynı problemi baştan çözüp sonunda oy veriyor. Bağımsızlık saçılmayı düşürdü,
ortalamayı yalnız dört abone taşıdı. Ağaçlar birbirini görseydi ne olurdu: ikinci ağaç yalnız
birincinin yanıldığı yere baksaydı, üçüncü ilk ikisinin kalan hatasına. Sonraki ders bu ardışık
düzeni kurar ve iki ayar değişkenini karşı karşıya getirir: kaç tur sürüleceği ve her turun hangi
büyüklükte bir adımla ekleneceği. İkisi birbirinin yerine geçer; aynı sayı küçük adımla çok turda
ya da büyük adımla az turda çıkar ve iki yolun bütçesi aynı değildir.

### Ardışık Artırma

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/ardisik-artirma

Ağaçları birbirinden bağımsız değil, kalan hatanın üstüne kurmanın ölçülmesi: iki yüz turda eğitim sayısı 0,7778'den 0,8638'e çıkarken doğrulama kümesindeki sayı onuncu turda 0,8373'te tepe yapıp 0,8056'ya iniyor. Öğrenme adımı ile tur sayısı birbirinin yerine geçiyor; aynı hedefe 0,02 adımla 79, 0,05 adımla 30, 0,15 adımla 10, 0,45 adımla 4 turda varılıyor ve adım ile tur sayısının çarpımı 1,50 ile 1,80 arasında kalıyor. Yetmiş iki hücrelik ızgarada doğrulamadan seçilen aday sınama kümesinde 0,7738, sınamadan seçilen 0,8056 veriyor: fark 0,0318. Toplam 1.279 aday denendi ve 30 bölmelik artırma, 568 bölmelik torbalamanın 0,0159 gerisinde kaldı.

Önceki ders kırk bir ağacı birbirinden bağımsız kurdu; hiçbiri ötekinin nerede yanıldığını
bilmiyordu. Bağımsızlık saçılmayı 0,0193'ten 0,0075'e düşürdü ama sınama ortalamasını yalnız dört
abone yukarı taşıdı. Bu ders ters yönü dener: her yeni model, öncekilerin **kalan hatasına**
bakarak kurulur.

**Ardışık artırma** (boosting) bir toplam kurar. Başlangıç, eğitim kümesindeki şüpheli oranının
logitidir; her tur, toplamın her abonede bıraktığı farka bir **zayıf öğrenici** uydurur ve
katkısını bir **öğrenme adımı** ile çarparak toplama ekler. Zayıf öğrenici burada bir ya da iki
bölmeli küçük bir ağaçtır. Ölçülen üç şey var: tur sayısı arttıkça iki sayının ayrışması, adım ile
tur sayısının birbirinin yerine geçmesi ve bunların ayar bütçesine yazdığı hesap.

- **DO37.** Küme, bölme ve taban çizgisi önceki derstekiyle aynıdır: 1.260 abone, 756/252/252,
  tohum 20260218, taban çizgisi sınamada 0,7579 ve doğrulamada 0,8135. Öznitelikler değişmez.
- **DO38.** Ağaç bu derste bir **bağlanım** ağacıdır: her örnek bir eğim ve bir eğrilik taşır,
  yaprağın değeri bu ikisinin oranıdır. Etiketi eğim, biri de eğrilik vermek aynı işlevi önceki
  dersin sınıflandırma ağacına çevirir.
- **DO39.** Turun eğimi `etiket - olasilik`, eğriliği `olasilik * (1 - olasilik)` olarak alınır ve
  eğrilik `1e-6` altına inmez; bu bir taşma korumasıdır, ayar değil.
- **DO40.** Karar eşiği toplamın sıfırı, yani olasılığın 0,5'idir; M27/K02'den sabittir.
- **DO41.** Zayıf öğrenicinin derinliği 1, 2 ya da 3'tür; öğrenme adımı 0,02 / 0,05 / 0,15 / 0,45;
  tur sayısı en çok 300'dür.
- **DO42.** Takas ölçümünün hedefi doğrulama kümesinde 208/252, yani 0,8254'tür ve ölçümden önce
  sabitlendi.
- **DO43.** Maliyet ölçüsü **bölme sayısıdır**: iç düğümlerin toplamı. Süre yazılmaz.
- **DO44.** Ayar bütçesi doğrulama kümesinde ölçülen her `(adım, derinlik, tur)` üçlüsünü sayar;
  seçim yalnız yetmiş iki hücrelik ızgaradan yapılır.

#### Kalan Hatanın Üstüne Kurmak

Kurulum tabloyu üretir, ağacı ve artırma döngüsünü yazar, sabit bir ayarla tur sayısını süpürür.
```python
# artirma.py — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden
# uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabit tutulur.
import bisect
import math

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)}
    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",
        "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
ESIK = {}
for a in ALAN:
    d = sorted({x[a] for x in EGT})
    ESIK[a] = d[1:] if len(d) <= 24 else [d[int(i * len(d) / 25)] for i in range(1, 25)]
KOVA = [len(ESIK[a]) + 1 for a in ALAN]


def kutula(s):                              # her satir: kova numaralari ve etiket
    return [(tuple(bisect.bisect_right(ESIK[a], x[a]) for a in ALAN), x["supheli"])
            for x in s]


KE, KD, KS = kutula(EGT), kutula(DOG), kutula(SIN)
KUME, TUM = (KE, KD, KS), list(range(len(ALAN)))


def agac(orn, derinlik, alanlar, enaz=2):   # MODELDIR: her ornek (kova, egim, egrilik)
    n, en = len(orn), None
    G, H = sum(o[1] for o in orn), sum(o[2] for o in orn)
    if derinlik and n >= 2 * enaz:
        for k in alanlar:
            sg, sh, ss = [0.0] * KOVA[k], [0.0] * KOVA[k], [0] * KOVA[k]
            for b, g, h in orn:
                sg[b[k]] += g
                sh[b[k]] += h
                ss[b[k]] += 1
            cg = ch = cs = 0
            for j in range(KOVA[k] - 1):
                cg, ch, cs = cg + sg[j], ch + sh[j], cs + ss[j]
                if cs < enaz or n - cs < enaz:
                    continue
                kazanc = cg * cg / ch + (G - cg) ** 2 / (H - ch) - G * G / H
                if en is None or kazanc > en[0]:
                    en = (kazanc, k, j)
    if en is None or en[0] <= 1e-9:
        return G / H
    _, k, j = en
    return (k, j, agac([o for o in orn if o[0][k] <= j], derinlik - 1, alanlar, enaz),
            agac([o for o in orn if o[0][k] > j], derinlik - 1, alanlar, enaz))


def deger(d, b):
    while type(d) is tuple:
        d = d[2] if b[d[0]] <= d[1] else d[3]
    return d


def sikistir(z):
    return 1.0 / (1.0 + math.exp(-z)) if z > -30 else 0.0


def artir(adim, tur, derinlik, duraklar):   # MODELDIR: ardisik artirma
    p = sum(y for _, y in KE) / len(KE)
    F = [[math.log(p / (1 - p))] * len(k) for k in KUME]
    iz = {}
    for t in range(1, tur + 1):
        orn = []
        for i, (b, y) in enumerate(KE):
            q = sikistir(F[0][i])
            orn.append((b, y - q, max(q * (1 - q), 1e-6)))
        d = agac(orn, derinlik, TUM)
        for u, kume in enumerate(KUME):
            for i, (b, _) in enumerate(kume):
                F[u][i] += adim * deger(d, b)
        if t in duraklar:
            iz[t] = tuple(sum((F[u][i] > 0) == y for i, (_, y) in enumerate(kume)) / len(kume)
                          for u, kume in enumerate(KUME))
    return iz


def taban(kume):
    return sum(y == 0 for _, y in kume) / len(kume)


DURAK = (1, 5, 10, 25, 50, 100, 200)
IZ = artir(0.15, 200, 2, set(DURAK))
print(f"taban cizgisi           {'':>8} dogrulama {taban(KD):.4f}  sinama {taban(KS):.4f}")
print(f"{'tur':>5} {'egitim':>9} {'dogrulama':>10} {'sinama':>9}")
for t in DURAK:
    print(f"{t:>5} {IZ[t][0]:>9.4f} {IZ[t][1]:>10.4f} {IZ[t][2]:>9.4f}")
```

```
taban cizgisi                    dogrulama 0.8135  sinama 0.7579
  tur    egitim  dogrulama    sinama
    1    0.7778     0.8135    0.7579
    5    0.7804     0.8095    0.7579
   10    0.8082     0.8373    0.7738
   25    0.8280     0.8214    0.7937
   50    0.8439     0.8135    0.7897
  100    0.8532     0.8016    0.7778
  200    0.8638     0.8056    0.7817
```
Tablo bir ayrışmadır. Birinci turda eğitim sayısı 0,7778, doğrulama 0,8135; ayrılmış küme
**önde**. Onuncu turda doğrulama 0,8373 ile tepe yapıyor. Yirmi beşinci turdan sonra yönler
ayrılıyor: eğitim sayısı hiç durmadan 0,8638'e tırmanırken doğrulama 0,8056'ya iniyor. Aradaki
fark birinci turda 0,0357 iken ikiyüzüncü turda işaret değiştirip 0,0582 oluyor.

Kaynağı görünür: her tur, yanlış tarafta kalmış abonelere daha büyük bir eğim yazar ve yeni zayıf
öğrenici onlara göre kurulur; yüz tur sonra toplam tek tek abonelerini kovalar. Tur sayısı bir
kapasite düğmesidir ve **sınırı ayrılmış kümeden okunur**: onuncu turdan sonrası eğitim için kâr,
doğrulama için zarardır.

#### Öğrenme Adımı ile Tur Sayısının Takası

İkinci ayar öğrenme adımıdır. Aynı hedefe kaç turda varıldığı dört adım için ayrı ayrı sayılır;
zayıf öğrenici bu bölümde tek bölmeli ağaçtır.
```python
HEDEF, TUR = 208 / 252, 300
print(f"{'adim':>6} {'hedefe tur':>11} {'en iyi tur':>11} {'en iyi dog':>11} "
      f"{'o turda sin':>12} {'300. tur egt':>13}")
TAKAS = {}
for adim in (0.02, 0.05, 0.15, 0.45):
    iz = artir(adim, TUR, 1, set(range(1, TUR + 1)))
    ilk = next((t for t in range(1, TUR + 1) if iz[t][1] >= HEDEF), 0)
    en = max(range(1, TUR + 1), key=lambda t: iz[t][1])
    TAKAS[adim] = (ilk, en, iz[en][1], iz[en][2])
    print(f"{adim:>6.2f} {ilk:>11} {en:>11} {iz[en][1]:>11.4f} "
          f"{iz[en][2]:>12.4f} {iz[TUR][0]:>13.4f}")
print(f"hedef {HEDEF:.4f} (252 abonenin 208'i), zayif ogrenici tek bolmeli agac")
```

```
  adim  hedefe tur  en iyi tur  en iyi dog  o turda sin  300. tur egt
  0.02          79         204      0.8294       0.7897        0.8135
  0.05          30          84      0.8333       0.8016        0.8148
  0.15          10          36      0.8333       0.7976        0.8280
  0.45           4           8      0.8294       0.7976        0.8307
hedef 0.8254 (252 abonenin 208'i), zayif ogrenici tek bolmeli agac
```
İkinci sütun dersin ölçüsüdür. Doğrulama kümesinde 208 aboneye 0,02 adımla 79, 0,05 adımla 30,
0,15 adımla 10, 0,45 adımla 4 turda varılıyor. Adım ile tur sayısının çarpımı sırasıyla 1,58, 1,50,
1,50 ve 1,80: dördünde de yaklaşık aynı. İki ayar değişkeni tek düğmenin iki yüzüdür ve
**birlikte** okunmalıdır; tek başına "iki yüz tur" bir ayar tarifi değildir.

Çarpım sabit olsa da sonuç değil. En iyi doğrulama sayısı ortadaki iki adımda 0,8333, uçlarda
0,8294; aynı adayların sınama sayıları 0,7897 ile 0,8016 arasında geziniyor. Üç yüzüncü turdaki
eğitim sayısı 0,02 adımda 0,8135, 0,45 adımda 0,8307: büyük adım aynı yere daha hızlı gider ve
daha çok ezberler.

#### Bütçe, Maliyet ve Torbalamayla Fark

Son bölüm iki ayarı ve tur sayısını tek ızgarada birleştirir, seçimi doğrulamadan okur, sonra
artırmanın maliyetini önceki dersin torbalanmış topluluğuyla karşılaştırır.
```python
def bolme_sayisi(d):
    return 0 if type(d) is not tuple else 1 + bolme_sayisi(d[2]) + bolme_sayisi(d[3])


def orman(tohum, sayi, m, derinlik):        # onceki dersin torbalanmis toplulugu
    r, agaclar = uretec(tohum), []
    for _ in range(sayi):
        secim = [int(r() * len(KE)) for _ in range(len(KE))]
        havuz, alanlar = list(TUM), []
        for _ in range(min(m, len(TUM))):
            alanlar.append(havuz.pop(int(r() * len(havuz))))
        agaclar.append(agac([(KE[i][0], KE[i][1], 1.0) for i in secim],
                            derinlik, sorted(alanlar)))
    return agaclar


ORMAN = orman(TOHUM, 41, 6, 4)
IZGARA, DURAK3 = {}, (5, 10, 25, 50, 100, 200)
print(f"{'adim':>6} {'derinlik':>9}" + "".join(f"{'tur ' + str(t):>14}" for t in DURAK3))
for adim in (0.02, 0.05, 0.15, 0.45):
    for derinlik in (1, 2, 3):
        iz = artir(adim, 200, derinlik, set(DURAK3))
        for t in DURAK3:
            IZGARA[(adim, derinlik, t)] = iz[t][1:]
        print(f"{adim:>6.2f} {derinlik:>9}"
              + "".join(f"{iz[t][1]:>7.4f}/{iz[t][2]:.4f}" for t in DURAK3))
SIRA = [(a, d, t) for a in (0.02, 0.05, 0.15, 0.45) for d in (1, 2, 3) for t in DURAK3]
for ad, sutun in (("dogrulamadan", 0), ("sinamadan", 1)):
    e = max(IZGARA[k][sutun] for k in SIRA)
    a, d, t = next(k for k in SIRA if IZGARA[k][sutun] == e)
    print(f"secim {ad:<13} adim={a:<5} derinlik={d}  tur={t:<4} "
          f"dogrulama {IZGARA[(a, d, t)][0]:.4f}  sinama {IZGARA[(a, d, t)][1]:.4f}")
e = max(IZGARA[k][0] for k in SIRA)
a, d, t = next(k for k in SIRA if IZGARA[k][0] == e)
print(f"artirma  {t} tur x derinlik {d} = {t * (2 ** d - 1)} bolme, "
      f"sinama {IZGARA[(a, d, t)][1]:.4f}")
print(f"torbalama 41 agac x derinlik 4 = {sum(map(bolme_sayisi, ORMAN))} bolme, "
      f"sinama {sum((sum(deger(x, b) > 0.5 for x in ORMAN) * 2 > 41) == y for b, y in KS) / len(KS):.4f}")
print(f"denenen aday {7 + 4 * TUR + len(SIRA)}, ailenin sayisi {IZGARA[(a, d, t)][1]:.4f}, "
      f"taban cizgisi ustu {IZGARA[(a, d, t)][1] - taban(KS):+.4f}")
```

```
  adim  derinlik         tur 5        tur 10        tur 25        tur 50       tur 100       tur 200
  0.02         1 0.8135/0.7579 0.8135/0.7579 0.8135/0.7579 0.8135/0.7579 0.8254/0.7817 0.8254/0.7857
  0.02         2 0.8135/0.7579 0.8135/0.7579 0.8135/0.7579 0.8135/0.7659 0.8294/0.7817 0.8214/0.7937
  0.02         3 0.8135/0.7579 0.8135/0.7579 0.8135/0.7579 0.8214/0.7857 0.8135/0.7778 0.8254/0.7817
  0.05         1 0.8135/0.7579 0.8135/0.7579 0.8135/0.7579 0.8175/0.7857 0.8294/0.7976 0.8294/0.7937
  0.05         2 0.8135/0.7579 0.8135/0.7579 0.8294/0.7738 0.8175/0.7897 0.8294/0.7937 0.8175/0.7857
  0.05         3 0.8135/0.7579 0.8135/0.7579 0.8175/0.7817 0.8254/0.7778 0.8254/0.7817 0.8095/0.7857
  0.15         1 0.8135/0.7579 0.8254/0.7817 0.8254/0.7897 0.8333/0.7937 0.8175/0.8016 0.8135/0.8056
  0.15         2 0.8095/0.7579 0.8373/0.7738 0.8214/0.7937 0.8135/0.7897 0.8016/0.7778 0.8056/0.7817
  0.15         3 0.8254/0.7817 0.8056/0.7817 0.8095/0.7817 0.8056/0.7778 0.8016/0.7778 0.8016/0.7817
  0.45         1 0.8254/0.7857 0.8294/0.7897 0.8135/0.7897 0.8175/0.7937 0.8175/0.8016 0.8214/0.7897
  0.45         2 0.8135/0.7857 0.8214/0.7778 0.8254/0.7778 0.7976/0.7698 0.7857/0.7659 0.7976/0.7659
  0.45         3 0.8175/0.7778 0.7937/0.7738 0.7817/0.7540 0.7897/0.7659 0.7698/0.7619 0.7778/0.7738
secim dogrulamadan  adim=0.15  derinlik=2  tur=10   dogrulama 0.8373  sinama 0.7738
secim sinamadan     adim=0.15  derinlik=1  tur=200  dogrulama 0.8135  sinama 0.8056
artirma  10 tur x derinlik 2 = 30 bolme, sinama 0.7738
torbalama 41 agac x derinlik 4 = 568 bolme, sinama 0.7897
denenen aday 1279, ailenin sayisi 0.7738, taban cizgisi ustu +0.0159
```
Izgaranın verdiği en yüksek doğrulama sayısı 0,8373 ve tek bir hücrede: adım 0,15, derinlik 2, tur
10. O adayın sınama sayısı **0,7738**. Sınama kümesinden okunsaydı adım 0,15, derinlik 1, tur 200
seçilirdi ve sayı 0,8056 olurdu. Fark 0,0318, yani sekiz abone. Önceki derste aynı fark 0,0040,
tek aboneydi.

Nedeni ızgaranın kendisidir. Bu derste doğrulama kümesinde ölçülen üçlü sayısı 1.279, önceki
derste 31'di. 252 satırlık bir kümede bu kadar aday denendiğinde en yüksek hücre, en iyi ayarın
değil **en şanslı ayarın** hücresidir. Kursun kuralı burada tam karşılığını buluyor: bir modelin
sayısı, o sayıyı bulmak için kaç aday denendiği yazılmadan okunamaz.

Son iki satır aileler arasındaki farkı sayıyla veriyor. Doğrulamadan seçilen artırma 30 bölme
kurup 0,7738 veriyor; önceki dersin torbalanmış topluluğu 568 bölme kurup 0,7897 veriyor. Artırma
on dokuz kat ucuz ve dört abone geride. Aile değiştirmenin kattığı fark −0,0159; aynı ızgara içinde
bütçenin şişirdiği fark +0,0318. **Bütçenin farkı, ailenin farkının iki katı.**

Ailenin sayısı 0,7738'dir, taban çizgisinin 0,0159 üstünde. Tek ağacın 0,7778'inin de altındadır
ve düzeltilmez: ızgaranın en iyi doğrulama hücresi budur.

#### Özet

- İki yüz turda eğitim sayısı 0,7778'den 0,8638'e çıkarken doğrulama 0,8373'te tepe yapıp
  0,8056'ya iniyor; iki sayı arasındaki fark işaret değiştirip 0,0582 oluyor.
- Aynı doğrulama hedefine 0,02 adımla 79, 0,05 adımla 30, 0,15 adımla 10, 0,45 adımla 4 turda
  varılıyor; adım ile tur sayısının çarpımı 1,50 ile 1,80 arasında kalıyor.
- Çarpım sabit olsa da sonuç değildir: uç adımların en iyi doğrulama sayısı 0,8294, ortadakilerin
  0,8333; büyük adım üç yüz turda eğitimi 0,8307'ye kadar ezberliyor.
- Doğrulamadan okunan seçim sınama kümesinde 0,7738, sınamadan okunan 0,8056 veriyor; fark 0,0318
  ve 1.279 aday denenmiştir. Önceki derste 31 adayla aynı fark 0,0040'tı.
- Artırma 30 bölmeyle 0,7738, torbalama 568 bölmeyle 0,7897 veriyor: aile farkı −0,0159, bütçe
  farkı +0,0318.

#### Sonraki Adım

İki birleştirme denendi ve ikisi de aynı aileden, karar ağacından besleniyordu: torbalama bağımsız
ağaçları oylattı, artırma onları üst üste yığdı. Üçüncü bir biçim daha var: **farklı ailelerden**
gelen modellerin çıktılarını girdi kabul eden ikinci bir katman. Bu hemen bir soru doğurur, ikinci
katman hangi kümeden öğrenilecek. Birinci katmanın eğitim kümesindeki çıktılarından öğrenilirse
katman ezberlenmiş çıktıları görür ve sayı şişer. Sonraki ders üç birleştirmeyi yan yana koyar,
şişmeyi sayar ve toplulukların **birlikte** yanıldığı abone sayısını basar.

### Topluluk Yöntemleri

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/topluluk-yontemleri

Torbalama, artırma ve yığınlamanın kattığı farkın ve maliyetinin yan yana sayılması: üç temel model sınama kümesinde 0,7778, 0,7897 ve 0,7738 verirken maliyetleri 43, 568 ve 30 bölmedir. 252 abonenin 45'inde üçü birden yanılıyor, torbalama ile artırma yalnız 6 aboneyi ayrı sınıflıyor ve üç etiketten başka bir şey görmeyen hiçbir birleştirme 0,7897'yi geçemez. Yığınlamanın ikinci katmanı doğrulama kümesinden öğrenildiğinde 0,8254 rapor edip 0,7778 veriyor, eğitim kümesinden öğrenildiğinde 0,8532 rapor edip yine 0,7778 veriyor: şişme 0,0476'dan 0,0754'e çıkıyor ve ağırlık en çok ezberleyen modele kayıyor. Konunun toplam ayar bütçesi 1.313 adaydır.

Önceki iki ders aynı aileyi iki ayrı biçimde çoğalttı. Torbalama ağaçları paralel kurup oylattı ve
sınama kümesinde 0,7897 verdi; artırma onları ardışık bir toplama yığıp 0,7738 verdi. İkisi de tek
tip bir öğreniciyi çoğaltıyordu. Üçüncü biçim, **farklı modellerin çıktılarını girdi kabul eden
ikinci bir katman** kurmaktır ve hemen bir soru doğurur: o katman hangi kümeden öğrenilecek.

Bu ders üç birleştirmeyi yan yana koyar. Ölçü üç sayıdır: ayrılmış kümedeki fark, **bölme sayısı**
olarak maliyet ve denenen aday. Dördüncü bir ölçü eklenir, toplulukların **birbirine benzeyen
hataları**: üç model aynı abonelerde yanılıyorsa birleştirmenin kazanacağı bir şey yok demektir ve
bu bir sezgi değil, hesaplanabilir bir tavandır.

- **DO45.** Küme, bölme, taban çizgisi ve öznitelikler önceki iki derstekiyle aynıdır: 1.260 abone,
  756/252/252, tohum 20260218, taban çizgisi sınamada 0,7579 ve doğrulamada 0,8135.
- **DO46.** Üç temel model önceki derslerden **olduğu gibi taşınır** ve burada yeniden ayarlanmaz:
  derinlik 6 tek ağaç, 41 ağaçlık torbalanmış topluluk (`m` altı, derinlik 4) ve adım 0,15 /
  derinlik 2 / 10 turluk artırma.
- **DO47.** Her temel modelin çıktısı [0, 1] aralığındadır: yaprak oranı, oy oranı, artırmanın
  olasılığı. Karar eşiği 0,5'tir ve M27/K02'den sabittir.
- **DO48.** Yığınlamanın ikinci katmanı üç çıktıyı girdi alan eğim temelli bir doğrusal modeldir:
  üç ağırlık ve bir sabit, 400 yineleme, adım 2,0. Ayarları ölçümden önce sabitlendi.
- **DO49.** Maliyet ölçüsü **bölme sayısıdır**; yığınlamanın maliyeti üç temel modelin bölmeleri
  artı öğrenilen dört sayıdır.
- **DO50.** Ortak hata sayımı sınama kümesinde ve **etiket düzeyinde** yapılır.
- **DO51.** Etiket desenine bakan bir birleştirmenin **tavanı**, her desende o desenin çoğunluk
  etiketini söyleyen kuralın doğruluğudur; üç etiketten başkasını görmeyen hiçbir birleştirme bunu
  geçemez.
- **DO52.** Bu derste yeni ayar aranmaz; denenen aday üçtür. Konunun toplam bütçesi taşınan
  sayılarla birlikte 1.313 adaydır.

#### Üç Birleştirme ve Üç Temel Model

Kurulum tabloyu üretir, iki birleştirmeyi yeniden kurar ve üç temel modelin sayılarını maliyetiyle
birlikte basar.
```python
# yiginlama.py — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden
# uretilir; oznitelik kumesi K02'den gelir ve kurs boyunca sabit tutulur.
import bisect
import math

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)}
    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",
        "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
ESIK = {}
for a in ALAN:
    d = sorted({x[a] for x in EGT})
    ESIK[a] = d[1:] if len(d) <= 24 else [d[int(i * len(d) / 25)] for i in range(1, 25)]
KOVA = [len(ESIK[a]) + 1 for a in ALAN]


def kutula(s):                              # her satir: kova numaralari ve etiket
    return [(tuple(bisect.bisect_right(ESIK[a], x[a]) for a in ALAN), x["supheli"])
            for x in s]


KE, KD, KS = kutula(EGT), kutula(DOG), kutula(SIN)
KUME, TUM = (KE, KD, KS), list(range(len(ALAN)))


def agac(orn, derinlik, alanlar, enaz=2):   # MODELDIR: her ornek (kova, egim, egrilik)
    n, en = len(orn), None
    G, H = sum(o[1] for o in orn), sum(o[2] for o in orn)
    if derinlik and n >= 2 * enaz:
        for k in alanlar:
            sg, sh, ss = [0.0] * KOVA[k], [0.0] * KOVA[k], [0] * KOVA[k]
            for b, g, h in orn:
                sg[b[k]] += g
                sh[b[k]] += h
                ss[b[k]] += 1
            cg = ch = cs = 0
            for j in range(KOVA[k] - 1):
                cg, ch, cs = cg + sg[j], ch + sh[j], cs + ss[j]
                if cs < enaz or n - cs < enaz:
                    continue
                kazanc = cg * cg / ch + (G - cg) ** 2 / (H - ch) - G * G / H
                if en is None or kazanc > en[0]:
                    en = (kazanc, k, j)
    if en is None or en[0] <= 1e-9:
        return G / H
    _, k, j = en
    return (k, j, agac([o for o in orn if o[0][k] <= j], derinlik - 1, alanlar, enaz),
            agac([o for o in orn if o[0][k] > j], derinlik - 1, alanlar, enaz))


def deger(d, b):
    while type(d) is tuple:
        d = d[2] if b[d[0]] <= d[1] else d[3]
    return d


def sikistir(z):
    return 1.0 / (1.0 + math.exp(-z)) if z > -30 else 0.0


def bolme(d):
    return 0 if type(d) is not tuple else 1 + bolme(d[2]) + bolme(d[3])


def orman(tohum, sayi, m, derinlik):        # BIRLESTIRME 1: torbalama, paralel oy
    r, agaclar = uretec(tohum), []
    for _ in range(sayi):
        secim = [int(r() * len(KE)) for _ in range(len(KE))]
        havuz, alanlar = list(TUM), []
        for _ in range(min(m, len(TUM))):
            alanlar.append(havuz.pop(int(r() * len(havuz))))
        agaclar.append(agac([(KE[i][0], KE[i][1], 1.0) for i in secim],
                            derinlik, sorted(alanlar)))
    return agaclar


def artir(adim, tur, derinlik):             # BIRLESTIRME 2: artirma, ardisik toplam
    p = sum(y for _, y in KE) / len(KE)
    F, agaclar = [[math.log(p / (1 - p))] * len(k) for k in KUME], []
    for _ in range(tur):
        q = [sikistir(z) for z in F[0]]
        d = agac([(b, y - q[i], max(q[i] * (1 - q[i]), 1e-6))
                  for i, (b, y) in enumerate(KE)], derinlik, TUM)
        agaclar.append(d)
        for u, kume in enumerate(KUME):
            for i, (b, _) in enumerate(kume):
                F[u][i] += adim * deger(d, b)
    return agaclar, [[sikistir(z) for z in f] for f in F]


TEK = agac([(b, y, 1.0) for b, y in KE], 6, TUM)
ORMAN = orman(TOHUM, 41, 6, 4)
ARTIRMA, ARTIRMA_P = artir(0.15, 10, 2)
TEMEL = [("tek agac", [[deger(TEK, b) for b, _ in k] for k in KUME], bolme(TEK)),
         ("torbalama", [[sum(deger(x, b) > 0.5 for x in ORMAN) / 41 for b, _ in k]
                        for k in KUME], sum(map(bolme, ORMAN))),
         ("artirma", ARTIRMA_P, sum(map(bolme, ARTIRMA)))]


def dogruluk(cikti, kume):
    return sum((c > 0.5) == y for c, (_, y) in zip(cikti, kume)) / len(kume)


print(f"{'model':<12} {'dogrulama':>10} {'sinama':>8} {'bolme':>7}")
print(f"{'taban cizgisi':<12} {sum(y == 0 for _, y in KD) / len(KD):>10.4f} "
      f"{sum(y == 0 for _, y in KS) / len(KS):>8.4f} {0:>7}")
for ad, cikti, maliyet in TEMEL:
    print(f"{ad:<12} {dogruluk(cikti[1], KD):>10.4f} "
          f"{dogruluk(cikti[2], KS):>8.4f} {maliyet:>7}")
```

```
model         dogrulama   sinama   bolme
taban cizgisi     0.8135   0.7579       0
tek agac         0.8056   0.7778      43
torbalama        0.8413   0.7897     568
artirma          0.8373   0.7738      30
```
Üç satır üç ayrı takas gösteriyor. Torbalama en yüksek sayıyı veriyor, 0,7897 ve en pahalısı:
568 bölme. Artırma 30 bölmeyle, yani on dokuz kat ucuza, 0,7738 veriyor. Tek ağaç 43 bölmeyle
0,7778'de duruyor ve **artırmanın üstünde**. Asıl soru şu: bu üç model birbirinden ne kadar farklı
yanılıyor.
```python
def etiketler(u):
    return [tuple(c[u][i] > 0.5 for _, c, _ in TEMEL) for i in range(len(KUME[u]))]


E = etiketler(2)
Y = [sum(e[j] != y for j in range(3)) for e, (_, y) in zip(E, KS)]
DESEN = {}
for e, (_, y) in zip(E, KS):
    DESEN.setdefault(e, [0, 0])[y] += 1
print("sinamada yanilan model sayisi  " +
      "  ".join(f"{k}: {Y.count(k):>3}" for k in range(4)))
print(f"etiket desenine bakan bir birlestirmenin tavani "
      f"{sum(max(v) for v in DESEN.values()) / len(KS):.4f}, farkli desen {len(DESEN)}")
for i in range(3):
    for j in range(i + 1, 3):
        ayri = sum(e[i] != e[j] for e in E)
        ikisi = sum(e[i] != y and e[j] != y for e, (_, y) in zip(E, KS))
        print(f"{TEMEL[i][0]:>10} - {TEMEL[j][0]:<10} ayri sinifladigi {ayri:>3}, "
              f"ikisinin birden yanildigi {ikisi:>3}")
```

```
sinamada yanilan model sayisi  0: 185  1:  13  2:   9  3:  45
etiket desenine bakan bir birlestirmenin tavani 0.7897, farkli desen 7
  tek agac - torbalama  ayri sinifladigi  17, ikisinin birden yanildigi  46
  tek agac - artirma    ayri sinifladigi  21, ikisinin birden yanildigi  46
 torbalama - artirma    ayri sinifladigi   6, ikisinin birden yanildigi  52
```
İlk satır tavanı belirliyor. 252 abonenin 185'inde üçü de doğru, 45'inde üçü de yanlış; geriye
kalan 22 abone birleştirmenin çalışabileceği tek bölge. O 45 abone hiçbir oyla, hiçbir toplamla ve
hiçbir ikinci katmanla kurtarılamaz, çünkü üç girdi de aynı yanlış yönü gösteriyor.

İkinci satır bunu kesin bir sayıya çeviriyor. Üç etiket sekiz desen kurabilirdi, sınamada yalnız
yedisi görülüyor ve her desende çoğunluk etiketini söyleyen kural 0,7897 veriyor. **Üç etiketten
başkasını görmeyen hiçbir birleştirme bunu geçemez** ve bu sayı torbalamanın kendi sayısına eşit.

Son üç satır nedeni söylüyor. Torbalama ile artırma 252 abonenin yalnız **6'sını** ayrı sınıflıyor.
Biri paralel oy, öteki ardışık toplam; yapıları birbirinin tersi, kararları neredeyse aynı.
Torbalama 53, artırma 57 abonede yanılıyor ve ikisi 52 abonede **birlikte** yanılıyor: artırmanın
kaçırdığı neredeyse her aboneyi torbalama da kaçırıyor. Tek ağaç biraz daha uzakta (17 ve 21
abone), ama o da aynı aileden besleniyor.

#### İkinci Katman Hangi Kümeden Öğreniliyor

Yığınlama üç çıktıyı girdi alan bir model kurar. Bu modelin öğreneceği veri temel modellerin
çıktılarıdır; o çıktıların hangi küme üzerinde üretildiği dersin sorusudur.
```python
def ikinci_katman(u, adim=2.0, tur=400):    # BIRLESTIRME 3: yiginlama
    kume, w, sabit = KUME[u], [0.0] * len(TEMEL), 0.0
    for _ in range(tur):
        gw, gs = [0.0] * len(TEMEL), 0.0
        for i, (_, y) in enumerate(kume):
            h = sikistir(sum(w[j] * TEMEL[j][1][u][i]
                             for j in range(len(TEMEL))) + sabit) - y
            for j in range(len(TEMEL)):
                gw[j] += h * TEMEL[j][1][u][i]
            gs += h
        for j in range(len(TEMEL)):
            w[j] -= adim * gw[j] / len(kume)
        sabit -= adim * gs / len(kume)
    return w, sabit


def katman_dogruluk(model, u):
    w, sabit = model
    return sum((sum(w[j] * TEMEL[j][1][u][i] for j in range(len(TEMEL))) + sabit > 0)
               == y for i, (_, y) in enumerate(KUME[u])) / len(KUME[u])


DOGRU, YANLIS = ikinci_katman(1), ikinci_katman(0)
print(f"{'ikinci katman':<22} {'egitim':>8} {'dogrulama':>10} {'sinama':>8}   agirliklar")
for ad, model, u in (("dogrulamadan ogrenildi", DOGRU, 1), ("egitimden ogrenildi", YANLIS, 0)):
    print(f"{ad:<22}" + "".join(f"{katman_dogruluk(model, v):>{(8, 10, 8)[v]}.4f}"
                                for v in range(3))
          + "   " + " ".join(f"{q:+.2f}" for q in model[0]))
SON = [("torbalama", dogruluk(TEMEL[1][1][1], KD), dogruluk(TEMEL[1][1][2], KS),
        TEMEL[1][2], 31),
       ("artirma", dogruluk(TEMEL[2][1][1], KD), dogruluk(TEMEL[2][1][2], KS),
        TEMEL[2][2], 1279),
       ("yiginlama", katman_dogruluk(DOGRU, 1), katman_dogruluk(DOGRU, 2),
        sum(t[2] for t in TEMEL) + 4, 3)]
print(f"\n{'birlestirme':<12} {'rapor edilen':>13} {'sinama':>8} {'sisme':>8} "
      f"{'bolme':>7} {'aday':>6}")
for ad, d, s, maliyet, aday in SON:
    print(f"{ad:<12} {d:>13.4f} {s:>8.4f} {d - s:>8.4f} {maliyet:>7} {aday:>6}")
print(f"konunun toplam ayar butcesi {sum(t[4] for t in SON)} aday")
```

```
ikinci katman            egitim  dogrulama   sinama   agirliklar
dogrulamadan ogrenildi  0.8333    0.8254  0.7778   +1.99 +1.54 +1.45
egitimden ogrenildi     0.8532    0.7937  0.7778   +5.04 +2.38 +0.54

birlestirme   rapor edilen   sinama    sisme   bolme   aday
torbalama           0.8413   0.7897   0.0516     568     31
artirma             0.8373   0.7738   0.0635      30   1279
yiginlama           0.8254   0.7778   0.0476     645      3
konunun toplam ayar butcesi 1313 aday
```
İki satırın son sütunu aynı: 0,7778. Yanlış kümeden öğrenmek, bu tabloda **teslim edilen sayıyı
değiştirmedi**. Değiştirdiği iki şey var ve ikisi de görünür.

Birincisi rapor edilen sayıdır. Katman eğitim kümesinden öğrenildiğinde o kümede 0,8532 okuyor;
gerçek sayı 0,7778, yani şişme 0,0754. Doğrulamadan öğrenilen katman kendi kümesinde 0,8254 okuyor
ve şişmesi 0,0476. **İki sayı da şişmiştir**, çünkü ikisi de katmanın öğrendiği kümeden okunmuştur;
yanlış küme yalnız şişmeyi 0,0278, yani yedi abone büyütmüştür.

İkincisi ağırlıklardır. Doğrulamadan öğrenen katman dengeli ağırlık veriyor: +1,99, +1,54, +1,45.
Eğitimden öğrenen katman ise +5,04'ü tek ağaca yazıyor ve artırmayı +0,54'e indiriyor. Nedeni
eğitim sütunundadır: tek ağaç eğitim kümesinde 0,8426, torbalanmış topluluk 0,8175, artırma 0,8082
okuyor. İkinci katman en çok **ezberlemiş** modeli en iyi model sanıyor. Burada ezber ılımlı olduğu
için zarar da ılımlı kaldı; temel modellerden biri eğitim kümesini birebir öğrenseydi katman bütün
ağırlığı ona verirdi ve teslim edilen sayı da düşerdi.

Doğrulamadan öğrenmenin de bedeli var: o küme artık harcanmıştır. Harcamayan yol, ikinci katmanı
temel modellerin **kendilerini eğiten satırlarda görülmemiş** çıktılarından öğrenmektir; torba dışı
kestirim tam olarak bu biçimdir.

Son tablo konunun hesabıdır. Üç birleştirmenin şişmesi 0,0516, 0,0635 ve 0,0476; en yüksek sayıyı
en küçük bütçe veriyor. Torbalama 31 adayla 0,7897'ye ulaşıyor, artırma 1.279 adayla 0,7738'de
kalıyor. Konunun toplam bütçesi 1.313 aday ve karşılığı, taban çizgisinin 0,0318 üstünde bir
sayı.

#### Özet

- Üç temel model sınama kümesinde 0,7778, 0,7897 ve 0,7738 veriyor; maliyetleri 43, 568 ve 30
  bölme, yani en ucuz birleştirme en pahalısının on dokuzda biri.
- 252 abonenin 185'inde üçü de doğru, 45'inde üçü de yanlış; birleştirmenin çalışabileceği bölge
  22 abone.
- Üç etikete bakan bir birleştirmenin tavanı 0,7897'dir ve torbalamanın sayısına eşittir;
  torbalama ile artırma yalnız 6 aboneyi ayrı sınıflıyor.
- İkinci katman eğitimden öğrenildiğinde 0,8532 rapor edip 0,7778 veriyor (şişme 0,0754),
  doğrulamadan öğrenildiğinde 0,8254 rapor edip yine 0,7778 (şişme 0,0476).
- Yanlış küme ağırlığı en çok ezberleyen modele kaydırıyor: +5,04 tek ağaca karşı doğru kurulumda
  +1,99; üç birleştirmenin en yükseğini en küçük ayar bütçesi veriyor.

#### Sonraki Adım

Bu konu yedi derste yedi yapı denedi: örnek tabanlı yordam, koşullu bağımsızlık varsayımına dayanan
olasılıksal sınıflandırıcı, marj temelli sınıflandırıcı, karar ağacı, torbalanmış ağaç topluluğu,
ardışık artırma ve yığınlama. Hepsi aynı tabloyla, aynı bölmeyle ve aynı taban çizgisiyle
karşılaştırıldı ve her sayının yanına kaç aday denendiği yazıldı. Ama karşılaştırmanın kendisi
baştan sona **tek bir sayıyla** yapıldı: doğrulukla. O sayının neyi sakladığı hiç sorulmadı. Üçünün
birden yanıldığı 45 abonenin kaçı gerçekten şüpheliydi, kaçı boş yere kapıya gönderilmiş bir ekip
demekti, hiç sayılmadı. Aynı doğruluğu veren iki modelin bambaşka hatalar yapması olanaklıdır ve
iki hata türünün bedeli aynı değildir. Sonraki konu, model değerlendirme, bu soruyla açılır.

## Değerlendirme ve Ayar

Modelin sayısının hangi ölçüden, hangi eşikten ve kaç adaydan çıktığının ayrılması: aynı doğruluğu veren iki modelin farklı hata dağılımı ve iki hata türünün farklı bedeli, doğruluk, kesinlik, duyarlılık ve F1'de sıralamanın değişmesi, eşik süpürüldükçe kesinlik ile duyarlılığın karşılıklı hareketi ve dengesiz kümede hangi eğrinin yanıltıcı olduğu, söylenen olasılık ile gerçekleşen oran arasındaki fark ve sıralaması iyi kalibrasyonu kötü modelin varlığı, ortalama mutlak hata, ortalama kare hata ve açıklanan değişim oranının aynı sıralamayı vermemesi, kat sayısı büyüdükçe kestirimin oynaması ve eğitim sayısının artması, aynı bütçeyle üç aramanın bulduğu en iyi aday ile bütçe büyüdükçe şişen doğrulama sayısı, hatanın yoğunlaştığı dilim ve dilim çoğaldıkça rastlantıyla büyüyen kötü dilim sayısı.

### Karışıklık Matrisi

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/karisiklik-matrisi

Aynı doğruluğu veren iki modelin hata dağılımının ayrılması: kırk dokuz aday arasında on dört doğruluk değeri birden çok aday tarafından paylaşılır, 0,7817 veren iki aday 61 şüpheliden birinde 16'sını diğerinde 32'sini yakalar ve işaretlenen abone sayısı 26 ile 58 arasında değişir. Saha ziyareti 1, kaçırılan kaçak 6 birim sayıldığında bedel 296 ile 232 olur, taban çizgisinin bedeli 366'dır ve herkesi şüpheli demek 252 ile taban çizgisinden ucuzdur. Doğrulukta en iyi aday bedelde 263 verirken bedelde en iyi aday doğrulukta kırk dokuz aday içinde yirminci sıradadır.

Önceki konu yedi model ailesini aynı ayrılmış küme üzerinde denedi ve her sayının yanına o
sayıyı bulmak için kaç aday denendiğini yazdı. Karşılaştırmanın tamamı tek bir sayı üzerinden
yürüdü: **doğruluk**. Ailenin kattığı fark da, ayar bütçesinin şişirdiği fark da bu tek sayıda
okundu. O sayının **hangi hataları sakladığı** hiç sorulmadı.

Sınama kümesindeki 252 abonenin 61'i şüpheli. Doğruluğu 0,80 olan bir model elli abonede
yanılıyor demektir, ama bu elli yanılgının yönü yazmıyor: şüpheli olmayana şüpheli demek ile
şüpheliyi gözden kaçırmak aynı hücreye düşüyor. İkisi birbirinin yerine geçmez — birinin bedeli
boşa giden bir saha ziyareti, ötekinin bedeli fark edilmemiş bir kaçaktır.

- **MD1.** Küme, bölme ve öznitelikler kurs boyunca sabittir: kurgu abone tablosu, 756/252/252
  bölme, öznitelik mühendisliği kursunun bıraktığı on iki öznitelik. Değişen tek şey modeldir.
- **MD2.** Taban çizgisi değişmez: eğitim kümesinin en sık sınıfını her aboneye söylemek. Bu
  yordam sınama kümesinde 0,7579 verir.
- **MD3.** Olumlu sınıf **şüpheli abonedir**. Olumlu/olumsuz sözcükleri değer yargısı değil,
  sayılan sınıfın adıdır.
- **MD4.** Model bir olasılık üretir ve bu derste karar eşiği **0,5'te sabittir**. Eşiğin kendisi
  bu konunun üçüncü dersine bırakılır.
- **MD5.** Kırk dokuz aday denenir: altı derinlik ile yedi sınıf ağırlığının çarpımı olarak kırk
  iki karar ağacı, yedi sınıf ağırlığında eğim temelli doğrusal model. Sınıf ağırlığı öznitelik
  mühendisliği kursundaki biçimiyle kullanılır.
- **MD6.** Bu ders **model seçmez**; kırk dokuz adayın hepsinin sınama kümesindeki sayısı basılır.
  Doğrulama kümesinden seçim yapmak üçüncü dersin işidir.
- **MD7.** Bedel iki kalemden oluşur: işaretlenen her abone için bir **saha ziyareti**, kaçırılan
  her şüpheli için bir **kaçırılmış kaçak**. Ziyaret bedeli işaretlenen bütün abonelere yazılır,
  ziyaretin sonucu bedeli değiştirmez. Birim değerleri kurgudur ve veriden okunmaz.

#### Dört Hücre

**Karışıklık matrisi (confusion matrix)** iki sınıflı bir kararda dört sayı tutar. Model şüpheli
dediğinde abone gerçekten şüpheliyse **doğru olumlu (true positive)**, değilse **yanlış olumlu
(false positive)**. Model şüpheli demediğinde abone şüpheliyse **yanlış olumsuz (false negative)**,
değilse **doğru olumsuz (true negative)**. Doğruluk bu dört sayının ikisini toplayıp dörde
böler; kalan bilgi atılır.

```python
# degerlendirme.py — MODELDIR. Kurgu abone tablosu ayni tohumla yeniden uretilir.
import math

TOHUM, ADAY, 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, OKUMA = [], {}
for i in range(ADAY):
    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 = uretec(TOHUM + 7000 + k["no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        OKUMA.setdefault(k["no"], []).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()

VERI = []
for k in ABONE:
    v = OKUMA.get(k["no"])
    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)}
    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"]


def safsizlik(s):
    n = sum(x.get("w", 1.0) for x in s)
    p = sum(x.get("w", 1.0) * x["supheli"] for x in s) / n
    return 2 * p * (1 - p)


def agac(s, derinlik, alan, enaz=2):        # MODELDIR: agirlikli karar agaci
    n = sum(x.get("w", 1.0) for x in s)
    p = sum(x.get("w", 1.0) * x["supheli"] for x in s) / n
    en = None
    if derinlik and len(s) >= 2 * enaz and 0.0 < p < 1.0:
        for a in alan:
            d = sorted({x[a] for x in s})
            for v in (d[1:] if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < enaz:
                    continue
                k = safsizlik(s) - (sum(x.get("w", 1.0) for x in sol) * safsizlik(sol)
                               + sum(x.get("w", 1.0) for x in sag) * safsizlik(sag)) / n
                if en is None or k > en[0]:
                    en = (k, a, v, sol, sag)
    if en is None or en[0] <= 1e-9:
        return {"p": p}
    return {"alan": en[1], "esik": en[2], "sol": agac(en[3], derinlik - 1, alan, enaz),
            "sag": agac(en[4], derinlik - 1, alan, enaz)}


def olasilik(d, x):
    while "p" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["p"]


def agirlikla(egt, w):
    return [dict(x, w=(w if x["supheli"] else 1.0)) for x in egt]


def matris(skor, veri, esik=0.5):           # do, yo, yn, dn
    m = [0, 0, 0, 0]
    for x in veri:
        i = (0 if x["supheli"] else 1) + (0 if skor(x) >= esik else 2)
        m[i] += 1
    return m


NS = sum(x["supheli"] for x in SIN)
print(f"sinama kumesi {len(SIN)} abone, supheli {NS}, "
      f"taban cizgisi {1 - NS / len(SIN):.4f}")
t6 = agac(EGT, 6, ALAN)
m6 = matris(lambda x: olasilik(t6, x), SIN)
print(f"derinlik 6 karar agaci: dogruluk {(m6[0] + m6[3]) / len(SIN):.4f}")
print(f"{'':>18}{'gercek supheli':>16}{'gercek degil':>14}")
print(f"{'supheli dendi':>18}{m6[0]:>16}{m6[1]:>14}")
print(f"{'supheli denmedi':>18}{m6[2]:>16}{m6[3]:>14}")
```

```
sinama kumesi 252 abone, supheli 61, taban cizgisi 0.7579
derinlik 6 karar agaci: dogruluk 0.7937
                    gercek supheli  gercek degil
     supheli dendi              24            15
   supheli denmedi              37           176
```

Doğruluk 0,7937 ve taban çizgisinin üstünde. Matris aynı modeli başka türlü okutuyor: model 61
şüphelinin 24'ünü yakalıyor, 37'sini kaçırıyor, 15 aboneyi de boşuna işaretliyor. Doğruluğun
büyük bölümü sağ alt hücreden geliyor — kümede çoğunluk zaten onlar. Taban çizgisinin 0,7579'u
tamamen o hücreden gelir: hiç kimseye şüpheli demeyen yordam 191 doğru olumsuz toplar ve 61
yanlış olumsuz biriktirir.

#### Aynı Sayı, Başka Matris

Doğruluğun bilgi attığı en açık biçimde iki modelin aynı sayıyı vermesiyle görülür. Kırk dokuz
aday denenir ve aynı doğruluk değerini paylaşan adaylar aranır.

```python
def olcek(egt, alan):
    o = {a: sum(x[a] for x in egt) / len(egt) for a in alan}
    s = {a: (sum((x[a] - o[a]) ** 2 for x in egt) / len(egt)) ** 0.5 or 1.0 for a in alan}
    return lambda x: [(x[a] - o[a]) / s[a] for a in alan]


def dogrusal(egt, alan, w, tur=300, adim=0.5):   # MODELDIR: egim temelli dogrusal model
    f, k, b = olcek(egt, alan), [0.0] * len(alan), 0.0
    X = [f(x) for x in egt]
    y = [x["supheli"] for x in egt]
    a = [(w if x["supheli"] else 1.0) for x in egt]
    n = sum(a)
    for _ in range(tur):
        gk, gb = [0.0] * len(alan), 0.0
        for i, xi in enumerate(X):
            z = b + sum(k[j] * xi[j] for j in range(len(k)))
            e = (1 / (1 + math.exp(-max(-30.0, min(30.0, z)))) - y[i]) * a[i]
            gb += e
            for j in range(len(k)):
                gk[j] += e * xi[j]
        b -= adim * gb / n
        for j in range(len(k)):
            k[j] -= adim * gk[j] / n

    def skor(x):
        v = f(x)
        z = b + sum(k[j] * v[j] for j in range(len(k)))
        return 1 / (1 + math.exp(-max(-30.0, min(30.0, z))))
    return skor


ADAYLAR = {}
for d in (3, 4, 5, 6, 7, 8):
    for w in (1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0):
        t = agac(agirlikla(EGT, w), d, ALAN)
        ADAYLAR[f"agac d{d} a{w}"] = matris(lambda x, t=t: olasilik(t, x), SIN)
for w in (1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0):
    s = dogrusal(EGT, ALAN, w)
    ADAYLAR[f"dogrusal a{w}"] = matris(s, SIN)

ESIT = {}
for ad, m in ADAYLAR.items():
    ESIT.setdefault(round((m[0] + m[3]) / len(SIN), 4), []).append(ad)
paylasilan = {d: v for d, v in ESIT.items() if len(v) > 1}
print(f"denenen aday {len(ADAYLAR)}, farkli dogruluk degeri {len(ESIT)}, "
      f"birden cok aday tarafindan paylasilan {len(paylasilan)}")
en = max(paylasilan.items(),
         key=lambda kv: max(ADAYLAR[a][0] for a in kv[1]) - min(ADAYLAR[a][0] for a in kv[1]))
print(f"\n{'aday':<14}{'dogruluk':>9}{'do':>5}{'yo':>5}{'yn':>5}{'dn':>5}{'isaretli':>10}")
for d in sorted(paylasilan, reverse=True)[:3]:
    for ad in paylasilan[d]:
        m = ADAYLAR[ad]
        print(f"{ad:<14}{d:>9.4f}{m[0]:>5}{m[1]:>5}{m[2]:>5}{m[3]:>5}{m[0] + m[1]:>10}")
    print()
print(f"en genis ayrisma dogruluk {en[0]:.4f}: {' / '.join(en[1])}")
```

```
denenen aday 49, farkli dogruluk degeri 26, birden cok aday tarafindan paylasilan 14

aday           dogruluk   do   yo   yn   dn  isaretli
agac d5 a1.0     0.8016   23   12   38  179        35
dogrusal a1.0    0.8016   22   11   39  180        33
dogrusal a1.5    0.8016   29   18   32  173        47

agac d3 a1.0     0.7937   17    8   44  183        25
agac d6 a1.0     0.7937   24   15   37  176        39

agac d4 a1.0     0.7817   16   10   45  181        26
agac d8 a1.0     0.7817   32   26   29  165        58

en genis ayrisma dogruluk 0.7817: agac d4 a1.0 / agac d8 a1.0
```

Kırk dokuz aday yalnız yirmi altı ayrı doğruluk değeri üretiyor ve bunların on dördü birden çok
aday tarafından paylaşılıyor. Tablonun ilk bloğu kümenin en yüksek doğruluğunu gösteriyor: 0,8016
değerini üç aday birden veriyor, üstelik iki ayrı model ailesinden. Yakaladıkları şüpheli sayısı
22, 23 ve 29; işaretledikleri abone sayısı 33, 35 ve 47. Doğrulukla bakan biri için üçü aynı
modeldir.

Son blok ayrışmanın en genişi. Doğruluğu 0,7817 olan iki aday 61 şüpheliden birinde 16'sını,
öbüründe 32'sini yakalıyor — tam iki katı — ve 26'ya karşı 58 abone işaretliyor. Toplam doğru
sayısı 197'de eşitleniyor çünkü ikinci modelin kazandığı 16 doğru olumlu, kaybettiği 16 doğru
olumsuzla tam takas oluyor. Doğruluk bu takasa kayıtsızdır; matris değildir.

#### İki Hatanın Bedeli Bir Değil

Takasın kayıtsız kalmadığı yer, iki hata türünün karşılığının sayıldığı yerdir. Kurgu kümede
işaretlenen her abone için bir **saha ziyareti** yazılır, kaçırılan her şüpheli için bir
**kaçırılmış kaçak**. Ziyaretin birimi 1, kaçırılan kaçağınki 6 alınır; bu iki sayı veriden
okunmaz, dışarıdan verilir.

```python
ZIYARET, KACAK = 1.0, 6.0


def bedel(m):
    return ZIYARET * (m[0] + m[1]) + KACAK * m[2]


print(f"{'yordam':<24}{'dogruluk':>9}{'ziyaret':>9}{'kacirilan':>11}{'bedel':>8}")
for ad, m in (("taban (hicbiri supheli)", [0, 0, NS, len(SIN) - NS]),
              ("herkes supheli", [NS, len(SIN) - NS, 0, 0]),
              ("agac d4 a1.0", ADAYLAR["agac d4 a1.0"]),
              ("agac d8 a1.0", ADAYLAR["agac d8 a1.0"]),
              ("agac d5 a1.0", ADAYLAR["agac d5 a1.0"]),
              ("dogrusal a1.5", ADAYLAR["dogrusal a1.5"])):
    print(f"{ad:<24}{(m[0] + m[3]) / len(SIN):>9.4f}{m[0] + m[1]:>9}"
          f"{m[2]:>11}{bedel(m):>8.0f}")

a, b = ADAYLAR["agac d4 a1.0"], ADAYLAR["agac d8 a1.0"]
oran = ((a[0] + a[1]) - (b[0] + b[1])) / (b[2] - a[2])
print(f"\nesit bedel orani {oran:.2f}: kacirilan bir kacagin bedeli bir saha "
      f"ziyaretinin {oran:.2f} katini gecince siralama doner")
en_d = max(ADAYLAR, key=lambda k: (ADAYLAR[k][0] + ADAYLAR[k][3]))
en_b = min(ADAYLAR, key=lambda k: bedel(ADAYLAR[k]))
print(f"dogrulukta en iyi aday {en_d}, bedeli {bedel(ADAYLAR[en_d]):.0f}")
print(f"bedelde en iyi aday   {en_b}, dogrulugu "
      f"{(ADAYLAR[en_b][0] + ADAYLAR[en_b][3]) / len(SIN):.4f}")
print(f"bedelde en iyi adayin dogruluk sirasi "
      f"{sorted(ADAYLAR, key=lambda k: -(ADAYLAR[k][0] + ADAYLAR[k][3])).index(en_b) + 1}"
      f" / {len(ADAYLAR)}")
```

```
yordam                   dogruluk  ziyaret  kacirilan   bedel
taban (hicbiri supheli)    0.7579        0         61     366
herkes supheli             0.2421      252          0     252
agac d4 a1.0               0.7817       26         45     296
agac d8 a1.0               0.7817       58         29     232
agac d5 a1.0               0.8016       35         38     263
dogrusal a1.5              0.8016       47         32     239

esit bedel orani 2.00: kacirilan bir kacagin bedeli bir saha ziyaretinin 2.00 katini gecince siralama doner
dogrulukta en iyi aday agac d5 a1.0, bedeli 263
bedelde en iyi aday   dogrusal a3.5, dogrulugu 0.7460
bedelde en iyi adayin dogruluk sirasi 20 / 49
```

Tablonun ikinci satırı doğruluğun tek yönlü bir ölçü olduğunu tek başına gösteriyor. Herkese
şüpheli demek doğrulukta 0,2421 verir — kullanılamaz görünen bir sayı — ama bedelde 252 tutar ve
taban çizgisinin 366'sından ucuzdur. İki yordam da hiçbir şey öğrenmemiştir; biri doğrulukta,
öteki bedelde iyi görünür.

Aynı doğruluğu veren iki adayın bedeli 296 ile 232. Aradaki 64 birim, doğruluğun sakladığı
takasın karşılığıdır. Eşit bedel oranı 2,00 çıkıyor: kaçırılan bir kaçağın bedeli bir saha
ziyaretinin iki katının altındaysa az işaretleyen model, üstündeyse çok işaretleyen model
ucuzdur. Bu oran veriden okunamaz, ölçüm ağının işletmecisinden gelir — aynı iki sayı, oranın
2'nin hangi tarafında olduğuna göre iki ayrı karar.

Son iki satır kırk dokuz adayın tamamına bakıyor. Doğrulukta en iyi aday bedelde 263 veriyor;
bedelde en iyi aday doğruluk sıralamasında yirminci sırada. Bir ölçütte tepede olmak öteki
ölçütte tepede olmayı getirmiyor ve fark rastlantı düzeyinde değil. Doğruluk, iki hatayı eşit
sayan bir bedel hesabıdır; eşit saymayan her hesap başka bir aday seçer.

#### Özet

- Karışıklık matrisi iki sınıflı kararı dört sayıya ayırır — doğru olumlu, yanlış olumlu, yanlış
  olumsuz, doğru olumsuz — ve doğruluk bu dördün ikisini toplayıp toplam gözlem sayısına böler.
- Kırk dokuz aday yalnız yirmi altı ayrı doğruluk değeri üretir; on dört değer birden çok aday
  tarafından paylaşılır, yani aynı sayı çoğu zaman birden fazla hata dağılımına karşılık gelir.
- Doğruluğu 0,7817 olan iki aday 61 şüpheliden 16'sını ve 32'sini yakalar, 26 ve 58 abone
  işaretler; kazanılan doğru olumlu sayısı kaybedilen doğru olumsuz sayısıyla tam takas olduğu
  için doğruluk aynı kalır.
- Saha ziyareti 1, kaçırılan kaçak 6 birim sayıldığında bu iki adayın bedeli 296 ile 232 olur;
  sıralamanın döndüğü eşit bedel oranı 2,00'dir ve bu sayı veriden değil işletmeden gelir.
- Herkese şüpheli demek doğrulukta 0,2421 ile taban çizgisinin çok altında kalırken bedelde 252
  ile taban çizgisinin 366'sını geçer; doğrulukta en iyi aday ile bedelde en iyi aday aynı değildir.

#### Sonraki Adım

Bu ders dört hücreyi saydı ama hücrelerden türetilen ölçütleri adlandırmadı. Öznitelik
mühendisliği kursunun dengesiz sınıflar dersi, doğru işaretlenen azınlık payına **kesinlik**,
yakalanan azınlık payına **duyarlılık** dendiğini yazmış, ikisinin tanımını ve birbirine karşı
okunmasını bu kursa bırakmıştı. Sonraki ders o sözü öder: dört hücreden dört ölçüt türetir, beş
modeli bu ölçütlerde ayrı ayrı sıralar ve kaç ölçütte sıralamanın başının değiştiğini sayar. F1
ölçütünün niçin iki sayının ortalaması değil **uyumlu ortalaması** olduğu, ortalamanın hangi
durumda yanıltıcı bir yüksek değer verdiği ile birlikte sayıyla gösterilir.

### Sınıflandırma Ölçütleri

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/siniflandirma-olcutleri

Karışıklık matrisinden türeyen dört ölçütün beş modeli farklı sıralaması: doğrulukta birinci olan model kesinlikte ikinci, duyarlılıkta ve F1 ölçütünde dördüncü olur, dört ölçütte üç ayrı model başa geçer ve doğruluk ile F1 arasında on model çiftinin yedisi yer değiştirir. Taban çizgisi hiçbir aboneyi işaretlemediği için kesinliği tanımsızdır, duyarlılığı ve F1 ölçütü sıfırdır. Kesinliği 1,0000 duyarlılığı 0,0164 olan bir yordamın sıradan ortalaması 0,5082 iken F1 ölçütü 0,0323'tür ve aradaki fark 0,4759'a çıkar.

Önceki ders doğruluğu dört hücreye ayırdı ama hücrelerden türeyen ölçütlerin hiçbirini
adlandırmadı. Öznitelik mühendisliği kursunun dengesiz sınıflar dersi ise iki adı yazmış — doğru
işaretlenen azınlık payına **kesinlik**, yakalanan azınlık payına **duyarlılık** — ham sayıları
basmış, tanımları ve ikisinin birbirine karşı okunmasını bu kursa bırakmıştı. Bu ders o sözü öder.

Ölçü şudur: beş model dört ölçütte ayrı ayrı sıralanır ve kaç ölçütte sıralamanın **başının**
değiştiği sayılır. Ölçüt değiştiğinde sıralama korunuyorsa hangisinin seçildiği önemsizdir; sıra
dönüyorsa ölçüt seçimi model seçiminden önce gelen bir karardır ve o karar veriden okunamaz.

- **MD8.** Dört ölçütün hepsi tek bir karışıklık matrisinden türer; yeni ölçüm yapılmaz, aynı
  dört sayı başka biçimde birleştirilir.
- **MD9.** Beş model önceki dersin kırk dokuz adaylık ızgarasından alınır. Bu derste eğitilen
  aday sayısı **beştir**; seçim ölçüt çeşitliliği için yapılmıştır, sınama kümesindeki sayıya
  göre değil.
- **MD10.** Kesinliğin paydası modelin işaretlediği abone sayısıdır ve sıfır olabilir. Sıfırken
  kesinlik **tanımsızdır**, sıfır değildir; taban çizgisi bu durumdadır.
- **MD11.** Duyarlılığın paydası sınama kümesindeki 61 şüphelidir ve modelden bağımsızdır.
- **MD12.** F1 ölçütü kesinlik ile duyarlılığın uyumlu ortalamasıdır; doğru olumlu sayısı sıfırsa
  sıfır yazılır.
- **MD13.** Dört ölçüt de karar eşiği 0,5'te okunur. Eşiğin oynattığı sayı sonraki derste ölçülür.
- **MD14.** Sıralamalar yalnız beş model üzerinde kurulur; taban çizgisi karşılaştırma satırıdır
  ve sıralamaya girmez.
- **MD15.** Dengesiz sınıflar dersinin sayıları tekrarlanmaz. Oradaki küme on bir öznitelikliydi,
  buradaki on iki; sayılar birebir karşılaştırılamaz, ölçüt tanımları karşılaştırılır.

#### Dört Hücreden Dört Ölçüt

Doğruluk, doğru olumlu ile doğru olumsuz toplamının bütün gözlemlere oranıdır. **Kesinlik**,
şüpheli denen abonelerin kaçının gerçekten şüpheli olduğudur: doğru olumlunun, doğru olumlu ile
yanlış olumlu toplamına oranı. **Duyarlılık**, gerçekten şüpheli abonelerin kaçının yakalandığıdır:
doğru olumlunun, doğru olumlu ile yanlış olumsuz toplamına oranı. Kesinlik modelin işaretlediği
sütuna bakar, duyarlılık gerçeğin şüpheli satırına. **F1 ölçütü** ikisini tek sayıda birleştirir ve
birleştirme sıradan ortalama değil **uyumlu ortalamadır**: $F_1 = \dfrac{2kd}{k+d}$, burada $k$
kesinlik, $d$ duyarlılıktır.

```python
# degerlendirme.py — MODELDIR. Kurgu abone tablosu ayni tohumla yeniden uretilir.
import math

TOHUM, ADAY, 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, OKUMA = [], {}
for i in range(ADAY):
    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 = uretec(TOHUM + 7000 + k["no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        OKUMA.setdefault(k["no"], []).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()

VERI = []
for k in ABONE:
    v = OKUMA.get(k["no"])
    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)}
    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"]


def safsizlik(s):
    n = sum(x.get("w", 1.0) for x in s)
    p = sum(x.get("w", 1.0) * x["supheli"] for x in s) / n
    return 2 * p * (1 - p)


def agac(s, derinlik, alan, enaz=2):        # MODELDIR: agirlikli karar agaci
    n = sum(x.get("w", 1.0) for x in s)
    p = sum(x.get("w", 1.0) * x["supheli"] for x in s) / n
    en = None
    if derinlik and len(s) >= 2 * enaz and 0.0 < p < 1.0:
        for a in alan:
            d = sorted({x[a] for x in s})
            for v in (d[1:] if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < enaz:
                    continue
                k = safsizlik(s) - (sum(x.get("w", 1.0) for x in sol) * safsizlik(sol)
                               + sum(x.get("w", 1.0) for x in sag) * safsizlik(sag)) / n
                if en is None or k > en[0]:
                    en = (k, a, v, sol, sag)
    if en is None or en[0] <= 1e-9:
        return {"p": p}
    return {"alan": en[1], "esik": en[2], "sol": agac(en[3], derinlik - 1, alan, enaz),
            "sag": agac(en[4], derinlik - 1, alan, enaz)}


def olasilik(d, x):
    while "p" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["p"]


def agirlikla(egt, w):
    return [dict(x, w=(w if x["supheli"] else 1.0)) for x in egt]


def matris(skor, veri, esik=0.5):           # do, yo, yn, dn
    m = [0, 0, 0, 0]
    for x in veri:
        i = (0 if x["supheli"] else 1) + (0 if skor(x) >= esik else 2)
        m[i] += 1
    return m


def olcek(egt, alan):
    o = {a: sum(x[a] for x in egt) / len(egt) for a in alan}
    s = {a: (sum((x[a] - o[a]) ** 2 for x in egt) / len(egt)) ** 0.5 or 1.0 for a in alan}
    return lambda x: [(x[a] - o[a]) / s[a] for a in alan]


def dogrusal(egt, alan, w, tur=300, adim=0.5):   # MODELDIR: egim temelli dogrusal model
    f, k, b = olcek(egt, alan), [0.0] * len(alan), 0.0
    X = [f(x) for x in egt]
    y = [x["supheli"] for x in egt]
    a = [(w if x["supheli"] else 1.0) for x in egt]
    n = sum(a)
    for _ in range(tur):
        gk, gb = [0.0] * len(alan), 0.0
        for i, xi in enumerate(X):
            z = b + sum(k[j] * xi[j] for j in range(len(k)))
            e = (1 / (1 + math.exp(-max(-30.0, min(30.0, z)))) - y[i]) * a[i]
            gb += e
            for j in range(len(k)):
                gk[j] += e * xi[j]
        b -= adim * gb / n
        for j in range(len(k)):
            k[j] -= adim * gk[j] / n

    def skor(x):
        v = f(x)
        z = b + sum(k[j] * v[j] for j in range(len(k)))
        return 1 / (1 + math.exp(-max(-30.0, min(30.0, z))))
    return skor


NS = sum(x["supheli"] for x in SIN)
MODEL = {}
for ad, d, w in (("derinlik 5 agac", 5, 1.0), ("derinlik 3 agac", 3, 1.0),
                 ("derinlik 8 agac", 8, 1.0), ("derinlik 6 agac a3", 6, 3.0)):
    t = agac(agirlikla(EGT, w), d, ALAN)
    MODEL[ad] = matris(lambda x, t=t: olasilik(t, x), SIN)
MODEL["dogrusal a2.5"] = matris(dogrusal(EGT, ALAN, 2.5), SIN)
MODEL["taban cizgisi"] = [0, 0, NS, len(SIN) - NS]


def olcutler(m):
    do, yo, yn, dn = m
    k = do / (do + yo) if do + yo else float("nan")
    du = do / (do + yn)
    return (do + dn) / sum(m), k, du, (2 * k * du / (k + du) if do else 0.0)


print(f"{'model':<18}{'dogruluk':>9}{'kesinlik':>9}{'duyarlilik':>11}{'F1':>8}"
      f"{'isaretli':>9}")
for ad, m in MODEL.items():
    d, k, du, f = olcutler(m)
    print(f"{ad:<18}{d:>9.4f}{('tanimsiz' if k != k else f'{k:.4f}'):>9}"
          f"{du:>11.4f}{f:>8.4f}{m[0] + m[1]:>9}")
```

```
model              dogruluk kesinlik duyarlilik      F1 isaretli
derinlik 5 agac      0.8016   0.6571     0.3770  0.4792       35
derinlik 3 agac      0.7937   0.6800     0.2787  0.3953       25
derinlik 8 agac      0.7817   0.5517     0.5246  0.5378       58
derinlik 6 agac a3   0.7302   0.4545     0.5738  0.5072       77
dogrusal a2.5        0.7659   0.5135     0.6230  0.5630       74
taban cizgisi        0.7579 tanimsiz     0.0000  0.0000        0
```

Taban çizgisi satırı üç ölçütü birden bozuyor. Hiçbir aboneyi işaretlemeyen yordamın kesinliği
tanımsızdır — bölünecek bir şey yoktur — duyarlılığı ve F1 ölçütü sıfırdır. Doğrulukta 0,7579
okuyan aynı yordam öbür üç ölçütte kullanılamaz bir sonuç verir. Bu, dengesiz sınıflar dersinin
ham sayılarla gösterdiği tablonun tanımlanmış halidir.

Beş model satırında iki sütun ters yönde ilerliyor. İşaretlenen abone sayısı 25'ten 77'ye
çıkarken kesinlik 0,6800'den 0,4545'e iniyor, duyarlılık 0,2787'den 0,5738'e çıkıyor. Az
işaretleyen model işaretlediklerinde daha çok haklıdır ama çoğunu kaçırır; çok işaretleyen model
daha azını kaçırır ama işaretlediklerinin yarısından çoğu boştur. Doğruluk sütunu bu takasa
neredeyse kayıtsız: en yüksek ile en düşük arasında 0,0714 puan var, kesinlikte 0,2255,
duyarlılıkta 0,3443 puan.

#### Sıralama Ölçütle Birlikte Dönüyor

Aynı beş model dört ölçütte ayrı ayrı sıralanır ve sıralamalar yan yana basılır.

```python
BES = [a for a in MODEL if a != "taban cizgisi"]
OLCUT = ("dogruluk", "kesinlik", "duyarlilik", "F1")
SIRA = {o: {a: j + 1 for j, a in enumerate(
    sorted(BES, key=lambda a: -olcutler(MODEL[a])[i]))}
        for i, o in enumerate(OLCUT)}
print(f"{'model':<18}" + "".join(f"{o:>11}" for o in OLCUT))
for a in BES:
    print(f"{a:<18}" + "".join(f"{SIRA[o][a]:>11}" for o in OLCUT))
bas = [min(SIRA[o], key=SIRA[o].get) for o in OLCUT]
print(f"\nolcut basindaki model: {' | '.join(bas)}")
print(f"dort olcutte {len(set(bas))} ayri model basa geciyor; dogrulugun sectigi "
      f"model {sum(1 for b in bas[1:] if b != bas[0])} olcutte basta degil")
ters = sum(1 for i in range(len(BES)) for j in range(i + 1, len(BES))
           if (SIRA["dogruluk"][BES[i]] - SIRA["dogruluk"][BES[j]])
           * (SIRA["F1"][BES[i]] - SIRA["F1"][BES[j]]) < 0)
print(f"dogruluk ile F1 arasinda yer degistiren model cifti {ters} / "
      f"{len(BES) * (len(BES) - 1) // 2}")
```

```
model                dogruluk   kesinlik duyarlilik         F1
derinlik 5 agac             1          2          4          4
derinlik 3 agac             2          1          5          5
derinlik 8 agac             3          3          3          2
derinlik 6 agac a3          5          5          2          3
dogrusal a2.5               4          4          1          1

olcut basindaki model: derinlik 5 agac | derinlik 3 agac | dogrusal a2.5 | dogrusal a2.5
dort olcutte 3 ayri model basa geciyor; dogrulugun sectigi model 3 olcutte basta degil
dogruluk ile F1 arasinda yer degistiren model cifti 7 / 10
```

Doğruluk sütunu 1'den 5'e düzgün iniyor çünkü modeller doğruluğa göre listelendi. Öbür üç sütun
o düzeni tanımıyor. Doğrulukta birinci olan derinlik 5 ağacı kesinlikte ikinci, duyarlılıkta ve
F1 ölçütünde dördüncü. Doğrulukta ikinci olan derinlik 3 ağacı kesinlikte birinci ama duyarlılıkta
ve F1 ölçütünde sonuncu. Doğrulukta dördüncü olan doğrusal model duyarlılıkta ve F1 ölçütünde
birinci.

Sayılar üç yerde toplanıyor. Dört ölçütte **üç ayrı model** başa geçiyor; doğruluğun seçtiği
model dört ölçütün **üçünde** başta değil; doğruluk ile F1 arasında on model çiftinin **yedisi**
yer değiştiriyor. Sıralama ölçüde değil, ölçütte kuruluyor. Bir modelin sayısını hangi bütçeyle
bulduğunu yazmak yetmez — hangi ölçütle sıralandığını da yazmak gerekir, çünkü beş model dört
ölçütte dört ayrı yarışa girmiştir.

#### Uyumlu Ortalama Niçin Ortalama Değil

F1 ölçütü kesinlik ile duyarlılığı sıradan ortalamayla birleştirseydi, bir tarafı yüksek öbür
tarafı sıfıra yakın olan bir yordam orta yerde görünürdü. Uyumlu ortalama küçük olana ağırlık
verir. Fark, iki sayı ayrıştıkça büyür ve büyümesi sayılabilir.

```python
print(f"{'kesinlik':>9}{'duyarlilik':>11}{'ortalama':>10}{'F1':>8}{'fark':>8}")
for k, du in ((1.00, 1 / 61), (0.90, 0.10), (0.70, 0.30), (0.60, 0.40), (0.50, 0.50)):
    o, f = (k + du) / 2, 2 * k * du / (k + du)
    print(f"{k:>9.4f}{du:>11.4f}{o:>10.4f}{f:>8.4f}{o - f:>8.4f}")

ORT = {a: sum(olcutler(MODEL[a])[1:3]) / 2 for a in BES}
print(f"\n{'model':<18}{'ortalama':>10}{'F1':>8}{'fark':>8}{'ort/F1 sirasi':>15}")
for j, a in enumerate(sorted(BES, key=lambda a: -ORT[a])):
    f = olcutler(MODEL[a])[3]
    sira = str(j + 1) + " / " + str(SIRA["F1"][a])
    print(f"{a:<18}{ORT[a]:>10.4f}{f:>8.4f}{ORT[a] - f:>8.4f}{sira:>15}")
```

```
 kesinlik duyarlilik  ortalama      F1    fark
   1.0000     0.0164    0.5082  0.0323  0.4759
   0.9000     0.1000    0.5000  0.1800  0.3200
   0.7000     0.3000    0.5000  0.4200  0.0800
   0.6000     0.4000    0.5000  0.4800  0.0200
   0.5000     0.5000    0.5000  0.5000  0.0000

model               ortalama      F1    fark  ort/F1 sirasi
dogrusal a2.5         0.5682  0.5630  0.0053          1 / 1
derinlik 8 agac       0.5382  0.5378  0.0003          2 / 2
derinlik 5 agac       0.5171  0.4792  0.0379          3 / 4
derinlik 6 agac a3    0.5142  0.5072  0.0069          4 / 3
derinlik 3 agac       0.4793  0.3953  0.0840          5 / 5
```

İlk tablonun ilk satırı uç durumu gösteriyor: tek bir aboneyi işaretleyen ve o abonede haklı
çıkan bir yordamın kesinliği 1,0000, duyarlılığı 0,0164'tür. Sıradan ortalama 0,5082 verir — beş
gerçek modelin hepsinden yüksek bir sayı — F1 ölçütü 0,0323 verir. Aradaki fark 0,4759. Ortadaki
üç satır aynı sıradan ortalamayı, 0,5000'i, üç ayrı F1 değeriyle karşılıyor: 0,1800, 0,4200 ve
0,4800. İki sayı eşitlendiğinde fark sıfırlanır. Uyumlu ortalamanın yaptığı tek şey, iki ölçütten
biri çöktüğünde birleşik sayının da çökmesini sağlamaktır.

İkinci tablo aynı hesabı gerçek modellerde gösteriyor. Kesinlik ile duyarlılığı birbirine yakın
olan derinlik 8 ağacında iki ortalama arasındaki fark 0,0003; ikisi en çok ayrışan derinlik 3
ağacında 0,0840. Sıralama da bir yerde bozuluyor: sıradan ortalamaya göre üçüncü olan derinlik 5
ağacı F1 ölçütünde dördüncü, dördüncü olan ağırlıklı ağaç üçüncü sıraya geçiyor. İki modelin
kesinlik ve duyarlılık toplamı neredeyse aynıdır (0,5171 ile 0,5142) ama dağılımı farklıdır ve
uyumlu ortalama dağılımı görür.

#### Özet

- Kesinlik modelin işaretlediği sütundan, duyarlılık gerçeğin şüpheli satırından okunur; doğruluk
  ikisini de kullanmaz, dört hücrenin iki köşegenini toplar.
- Taban çizgisinin kesinliği tanımsızdır çünkü hiçbir abone işaretlenmemiştir; aynı yordam
  doğrulukta 0,7579 okurken duyarlılıkta ve F1 ölçütünde sıfır verir.
- Beş model doğrulukta 0,7302 ile 0,8016 arasında sıkışırken kesinlikte 0,2255, duyarlılıkta
  0,3443 puan yayılır; doğruluk bu takasa neredeyse kayıtsızdır.
- Dört ölçütte üç ayrı model başa geçer, doğruluğun seçtiği model üç ölçütte başta değildir ve
  doğruluk ile F1 arasında on model çiftinin yedisi yer değiştirir.
- F1 ölçütü uyumlu ortalamadır: kesinliği 1,0000 duyarlılığı 0,0164 olan bir yordamda sıradan
  ortalama 0,5082, F1 ölçütü 0,0323 verir; iki sayı eşitlendiğinde iki ortalama da eşitlenir.

#### Sonraki Adım

Bu dersin dört ölçütü de karar eşiği 0,5'te okundu ve o eşik hiç sorgulanmadı. Oysa modellerin
ürettiği sayı bir olasılıktır; 0,5 dışarıdan gelen bir seçimdir ve tabloyu baştan sona
değiştirir. Sonraki ders eşiği 0'dan 1'e süpürür, her eşikte kesinlik ile duyarlılığı yan yana
basar ve **işletme noktasını** ilk dersin bedel hesabıyla seçer. Aynı süpürme iki eğri üretir —
**işlem karakteristiği eğrisi** ve **kesinlik–duyarlılık eğrisi** — ve dengesiz bir kümede
hangisinin yanıltıcı olduğu, iki eğrinin altındaki alanın aynı modelde ne kadar ayrıştığı
sayılarak gösterilir.

### Eşik Seçimi ve Eğriler

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/esik-secimi-ve-egriler

Eşiğin oynattığı iki sayının ve eğri altındaki alanın ayrılması: eşik 0,10'dan 0,90'a çıkarken kesinlik 0,3608'den 1,0000'e yükselir, duyarlılık 0,9344'ten 0,0328'e iner ve bedel 182'den 356'ya çıkar. Doksan dokuz eşik adayı arasından doğrulama kümesiyle seçilen 0,16 sınama kümesinde 181 bedel verir, varsayılan 0,50 eşiğinden 86 birim ucuzdur ve sınamadan okunsaydı 7 birim daha iyi görünürdü. Şüpheli oranı 0,2421'den 0,0354'e indirilince işlem karakteristiği eğrisinin altındaki alan 0,8058'den 0,8624'e çıkarken kesinlik–duyarlılık eğrisininki 0,6212'den 0,4124'e iner.

Önceki dersin dört ölçütü de karar eşiği 0,5'te okundu. O eşik modelden gelmez: model bir olasılık
üretir, eşik o olasılığı bir karara çevirir ve dışarıdan verilir. Sıfır virgül beşin tek gerekçesi
iki sınıfın ortasında durmasıdır; şüpheli oranı 0,2421 olan bir kümede bu ortanın niçin doğru yer
olduğunu söyleyen bir şey yoktur.

Bu ders eşiği süpürür: aynı model, aynı sınama kümesi, tek değişen eşik. Kesinlik ile duyarlılık
her eşikte yeniden hesaplanır, **işletme noktası** bir bedel hesabıyla seçilir, süpürmenin
ürettiği iki eğri sayı olarak basılır.

- **MD16.** Model bir olasılık üretir; eşik bu olasılığı karara çevirir ve modelden bağımsızdır.
  Doksan dokuz eşik denenir, 0,01'den 0,99'a; bu dersin ayar bütçesi budur.
- **MD17.** Bedel ilk dersinkidir: işaretlenen her abone için 1 birim saha ziyareti, kaçırılan
  her şüpheli için 6 birim. İşletme noktası **doğrulama kümesinden** seçilir; sınama kümesindeki
  sayı yalnız raporlanır.
- **MD18.** Süpürme boyunca eğitim kümesi, öznitelikler ve model sabit kalır.
- **MD19.** İşlem karakteristiği eğrisinin yatay ekseni yanlış olumlu oranı, dikey ekseni
  duyarlılıktır; kesinlik–duyarlılık eğrisinin yatay ekseni duyarlılık, dikey ekseni kesinliktir.
  Eğriler **çizilmez**, nokta olarak basılır.
- **MD20.** Eğri altındaki alan yamuk kuralıyla hesaplanır ve eşit skorlu gözlemler tek nokta
  sayılır.
- **MD21.** Seyreltilmiş küme sınama kümesindeki şüphelilerin onda birini tutarak kurulur; eğitim
  ve model değişmez. Kalan yedi şüpheliyle yapılan kestirim oynaktır ve tabanıyla okunur.
- **MD22.** Eşik seçimi olasılığın doğru olmasını değil, yalnız **sıralamanın** doğru olmasını
  gerektirir.

#### Eşiği Süpürmek

Eşik yükseldikçe model daha az abone işaretler. İşaretlediklerinde daha çok haklı çıkar, ama
kaçırdığı şüpheli sayısı büyür.

```python
# degerlendirme.py — MODELDIR. Kurgu abone tablosu ayni tohumla yeniden uretilir.
import math

TOHUM, ADAY, 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, OKUMA = [], {}
for i in range(ADAY):
    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 = uretec(TOHUM + 7000 + k["no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        OKUMA.setdefault(k["no"], []).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()

VERI = []
for k in ABONE:
    v = OKUMA.get(k["no"])
    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)}
    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"]


def safsizlik(s):
    p = sum(x["supheli"] for x in s) / len(s)
    return 2 * p * (1 - p)


def agac(s, derinlik, alan, enaz=2):        # MODELDIR: karar agaci
    n = len(s)
    p = sum(x["supheli"] for x in s) / n
    en = None
    if derinlik and n >= 2 * enaz and 0.0 < p < 1.0:
        for a in alan:
            d = sorted({x[a] for x in s})
            for v in (d[1:] if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < enaz:
                    continue
                k = safsizlik(s) - (len(sol) * safsizlik(sol) + len(sag) * safsizlik(sag)) / n
                if en is None or k > en[0]:
                    en = (k, a, v, sol, sag)
    if en is None or en[0] <= 1e-9:
        return {"p": p}
    return {"alan": en[1], "esik": en[2], "sol": agac(en[3], derinlik - 1, alan, enaz),
            "sag": agac(en[4], derinlik - 1, alan, enaz)}


def olasilik(d, x):
    while "p" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["p"]


def matris(skor, veri, esik=0.5):           # do, yo, yn, dn
    m = [0, 0, 0, 0]
    for x in veri:
        m[(0 if x["supheli"] else 1) + (0 if skor(x) >= esik else 2)] += 1
    return m


def dogrusal(egt, alan, w, tur=300, adim=0.5):   # MODELDIR: egim temelli dogrusal model
    o = {a: sum(x[a] for x in egt) / len(egt) for a in alan}
    s = {a: (sum((x[a] - o[a]) ** 2 for x in egt) / len(egt)) ** 0.5 or 1.0 for a in alan}
    f = lambda x: [(x[a] - o[a]) / s[a] for a in alan]
    k, b = [0.0] * len(alan), 0.0
    X = [f(x) for x in egt]
    y = [x["supheli"] for x in egt]
    a = [(w if x["supheli"] else 1.0) for x in egt]
    n = sum(a)
    for _ in range(tur):
        gk, gb = [0.0] * len(alan), 0.0
        for i, xi in enumerate(X):
            z = b + sum(k[j] * xi[j] for j in range(len(k)))
            e = (1 / (1 + math.exp(-max(-30.0, min(30.0, z)))) - y[i]) * a[i]
            gb += e
            for j in range(len(k)):
                gk[j] += e * xi[j]
        b -= adim * gb / n
        for j in range(len(k)):
            k[j] -= adim * gk[j] / n

    def skor(x):
        v = f(x)
        z = b + sum(k[j] * v[j] for j in range(len(k)))
        return 1 / (1 + math.exp(-max(-30.0, min(30.0, z))))
    return skor


NS = sum(x["supheli"] for x in SIN)
S = dogrusal(EGT, ALAN, 1.0)        # MODELDIR: olasilik ureten egim temelli dogrusal model
ZIYARET, KACAK = 1.0, 6.0


def nokta(skor, veri, e):
    do, yo, yn, dn = matris(skor, veri, e)
    return {"isaretli": do + yo, "do": do, "yoo": yo / (yo + dn), "du": do / (do + yn),
            "k": do / (do + yo) if do + yo else float("nan"),
            "bedel": ZIYARET * (do + yo) + KACAK * yn}


print(f"{'esik':>5}{'isaretli':>9}{'yakalanan':>10}{'yo orani':>9}"
      f"{'duyarlilik':>11}{'kesinlik':>9}{'bedel':>7}")
for e in (0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90):
    n = nokta(S, SIN, e)
    print(f"{e:>5.2f}{n['isaretli']:>9}{n['do']:>10}{n['yoo']:>9.4f}"
          f"{n['du']:>11.4f}{n['k']:>9.4f}{n['bedel']:>7.0f}")
```

```
 esik isaretli yakalanan yo orani duyarlilik kesinlik  bedel
 0.10      158        57   0.5288     0.9344   0.3608    182
 0.20      100        45   0.2880     0.7377   0.4500    196
 0.30       66        34   0.1675     0.5574   0.5152    228
 0.40       47        29   0.0942     0.4754   0.6170    239
 0.50       33        22   0.0576     0.3607   0.6667    267
 0.60       24        18   0.0314     0.2951   0.7500    282
 0.70       17        12   0.0262     0.1967   0.7059    311
 0.80        8         8   0.0000     0.1311   1.0000    326
 0.90        2         2   0.0000     0.0328   1.0000    356
```

İki sütun ters yönde ilerliyor. Eşik 0,10'da model 158 abone işaretliyor, 61 şüphelinin 57'sini
yakalıyor, kesinliği 0,3608. Eşik 0,90'da yalnız 2 abone işaretliyor, ikisinde de haklı, ama
duyarlılık 0,0328. Kesinlik düzgün artmıyor: 0,60'ta 0,7500 iken 0,70'te 0,7059'a iniyor, çünkü
paydası da modelle birlikte küçülüyor. Duyarlılık ise eşikte azalmayan bir işlevdir, paydası
sabittir.

Son sütun kararı veren sütun: bedel 0,10 eşiğinde 182, 0,90 eşiğinde 356. Aradaki 174 birim
tamamen eşiğin ürünüdür; model, öznitelikler ve eğitim kümesi hiç değişmedi.

#### İşletme Noktası Bir Bedel Hesabıyla Seçilir

Eşik bir ayar değişkenidir ve kursun kuralı burada da geçerlidir: hangi kümeden okunduğu
yazılmadan seçilmiş sayılmaz.

```python
ESIKLER = [i / 100 for i in range(1, 100)]
bd = min(ESIKLER, key=lambda e: nokta(S, DOG, e)["bedel"])
bs = min(ESIKLER, key=lambda e: nokta(S, SIN, e)["bedel"])
print(f"denenen esik adayi {len(ESIKLER)}, dogrulama kumesi {len(DOG)} abone")
print(f"{'isletme noktasi':<22}{'esik':>6}{'isaretli':>9}{'yakalanan':>10}"
      f"{'dogrulama':>11}{'sinama':>8}")
for ad, e in (("varsayilan", 0.50), ("dogrulamadan secilen", bd),
              ("sinamadan secilen", bs)):
    n = nokta(S, SIN, e)
    print(f"{ad:<22}{e:>6.2f}{n['isaretli']:>9}{n['do']:>10}"
          f"{nokta(S, DOG, e)['bedel']:>11.0f}{n['bedel']:>8.0f}")
```

```
denenen esik adayi 99, dogrulama kumesi 252 abone
isletme noktasi         esik isaretli yakalanan  dogrulama  sinama
varsayilan              0.50       33        22        205     267
dogrulamadan secilen    0.16      121        51        160     181
sinamadan secilen       0.13      138        55        173     174
```

Doğrulama kümesindeki doksan dokuz adayın en ucuzu 0,16 eşiğidir ve o kümede 160 birim tutar; aynı
eşik sınama kümesinde 181 birim veriyor. Varsayılan 0,50 eşiği ise 267 birim: eşiği seçmek 86
birim, üçte bire yakın bir kazanç sağlıyor ve bu kazanç modelden değil, çıktısının nasıl
okunduğundan geliyor.

Üçüncü satır seçimin kaynağını ölçüyor. Sınama kümesindeki en küçük bedel 0,13 eşiğinde, 174
birim. Aradaki 7 birim, seçimi sınama kümesinden okuyan birinin fazladan iyi görünen payıdır —
küçük bir sayıdır çünkü bedel eğrisi eşiğe göre yayvandır, ama işareti hep aynı yöndedir.

#### İki Eğri ve Dengesizlik

Süpürme iki eğri üretir. **İşlem karakteristiği eğrisi (ROC)** yanlış olumlu oranına karşı
duyarlılığı gösterir; **kesinlik–duyarlılık eğrisi** duyarlılığa karşı kesinliği. Farkları şüpheli
oranı değiştiğinde ortaya çıkar.

```python
def egri(skor, veri):               # ROC ve kesinlik-duyarlilik noktalari
    p = sorted(((skor(x), x["supheli"]) for x in veri), key=lambda t: -t[0])
    P = sum(y for _, y in p)
    N = len(p) - P
    do = yo = i = 0
    n = [(0.0, 0.0, 1.0)]
    while i < len(p):
        j = i
        while j < len(p) and p[j][0] == p[i][0]:
            j += 1
        d = sum(y for _, y in p[i:j])
        do, yo, i = do + d, yo + (j - i) - d, j
        n.append((yo / N, do / P, do / (do + yo)))
    return n


def alan_roc(n, sinir=1.0):         # yamuk kurali; sinir yanlis olumlu oranini kirpar
    return sum((min(b[0], sinir) - a[0]) * (a[1] + b[1]) / 2
               for a, b in zip(n, n[1:]) if a[0] < sinir) / sinir


def alan_kd(n):                     # duyarlilik ekseninde basamak toplami
    return sum((b[1] - a[1]) * b[2] for a, b in zip(n, n[1:]))


SEY = [x for i, x in enumerate(SIN) if not x["supheli"] or i % 10 == 0]
sabit = lambda x: 0.5
print(f"{'sinama kumesi':<15}{'satir':>6}{'supheli':>8}{'oran':>7}{'ROC':>8}{'KD':>8}"
      f"{'sabit ROC':>10}{'sabit KD':>9}")
for ad, v in (("gercek dagilim", SIN), ("seyreltilmis", SEY)):
    n, s, ns = egri(S, v), egri(sabit, v), sum(x["supheli"] for x in v)
    print(f"{ad:<15}{len(v):>6}{ns:>8}{ns / len(v):>7.4f}{alan_roc(n):>8.4f}"
          f"{alan_kd(n):>8.4f}{alan_roc(s):>10.4f}{alan_kd(s):>9.4f}")
for h in (0.50, 0.70):
    r = [nokta(S, v, max(e for e in ESIKLER if nokta(S, v, e)["du"] >= h))["k"]
         for v in (SIN, SEY)]
    print(f"duyarlilik {h:.2f} ustunde kesinlik: gercek dagilim {r[0]:.4f}, "
          f"seyreltilmis {r[1]:.4f}")
```

```
sinama kumesi   satir supheli   oran     ROC      KD sabit ROC sabit KD
gercek dagilim    252      61 0.2421  0.8058  0.6212    0.5000   0.2421
seyreltilmis      198       7 0.0354  0.8624  0.4124    0.5000   0.0354
duyarlilik 0.50 ustunde kesinlik: gercek dagilim 0.5161, seyreltilmis 0.2500
duyarlilik 0.70 ustunde kesinlik: gercek dagilim 0.4624, seyreltilmis 0.1667
```

İki eğri ters yönde hareket ediyor. Şüpheli oranı 0,2421'den 0,0354'e inince işlem karakteristiği
eğrisinin altındaki alan 0,8058'den 0,8624'e **çıkıyor**, kesinlik–duyarlılık eğrisininki
0,6212'den 0,4124'e **iniyor**. Yükselişin bir bölümü yedi şüpheliyle yapılan kestirimin
oynaklığıdır; asıl mesele son iki sütunda. Sabit skorlu yordamın işlem karakteristiği eğrisi her
iki kümede de 0,5000 verir; bu eğrinin tabanı şüpheli oranına bakmaz. Öteki eğrinin tabanı ise
doğrudan şüpheli oranıdır.

Son iki satır bunun işletmedeki karşılığı. Duyarlılık 0,50'nin üstünde tutulduğunda kesinlik
gerçek dağılımda 0,5161, seyreltilmiş kümede 0,2500; 0,70'in üstünde tutulduğunda 0,4624'e karşı
0,1667. Alan 0,86 okurken, şüphelilerin yarısını yakalamak için işaretlenen her dört aboneden
üçü boşa gidiyor. Dengesiz kümede yanıltıcı olan eğri, tabanı dengesizlikle birlikte hareket
etmeyen eğridir.

#### Tek Sayının Sakladığı

Eğri altındaki alan bütün eşikleri tek sayıda toplar ve toplarken iki şeyi düşürür: hangi eşikte
çalışıldığını ve eğrinin hangi bölgesinin işe yaradığını.

```python
T3 = agac(EGT, 3, ALAN)                    # MODELDIR: derinlik 3 karar agaci
S1 = dogrusal(EGT, ["ort_tuketim"], 1.0)   # MODELDIR: tek oznitelikli dogrusal model
print(f"{'model':<22}{'ROC':>8}{'yo<0.10':>9}{'KD':>8}{'en iyi bedel':>14}"
      f"{'farkli skor':>12}")
for ad, s in (("dogrusal, 12 alan", S), ("agac d3, 12 alan", lambda x: olasilik(T3, x)),
              ("dogrusal, ort_tuketim", S1)):
    n = egri(s, SIN)
    print(f"{ad:<22}{alan_roc(n):>8.4f}{alan_roc(n, 0.10):>9.4f}{alan_kd(n):>8.4f}"
          f"{min(nokta(s, SIN, e)['bedel'] for e in ESIKLER):>14.0f}"
          f"{len({round(s(x), 12) for x in SIN}):>12}")
```

```
model                      ROC  yo<0.10      KD  en iyi bedel farkli skor
dogrusal, 12 alan       0.8058   0.3364  0.6212           174         252
agac d3, 12 alan        0.7222   0.2553  0.4485           197           8
dogrusal, ort_tuketim   0.7138   0.3008  0.5589           213         241
```

Asıl okuma son iki satırda. İki modelin işlem karakteristiği alanı 0,7222 ile 0,7138, arada 0,0084
var — bu ölçüye göre neredeyse eşitler. Öbür üç sütun aynı fikirde değil: kesinlik–duyarlılık
alanında tek öznitelikli model 0,1104 puan önde, yanlış olumlu oranının 0,10 altında kaldığı
bölgede 0,0455 puan önde, en iyi bedelde 16 birim geride. Üç okuma, üç ayrı kazanan.

Sekiz ayrı skor üreten derinlik 3 ağacı ile 241 ayrı skor üreten tek öznitelikli model eğri
üzerinde bambaşka ilerler: biri sekiz büyük basamak, öteki yüzlerce küçük adım. Alan ikisini de
aynı sayıya sıkıştırır. Kursun kuralına burada bir madde eklenir: hangi eşikte çalışılacağı
yazılmadan eğri altındaki alan bir işletme kararına çevrilemez.

#### Özet

- Eşik yükseldikçe duyarlılık azalmayan biçimde düşer ama kesinlik düzgün artmaz: 0,60 eşiğinde
  0,7500 olan kesinlik 0,70 eşiğinde 0,7059'a iner. Doksan dokuz eşik adayı bedeli 182 ile 356
  arasında oynatır; model ve eğitim kümesi hiç değişmez.
- Doğrulamadan seçilen 0,16 eşiği sınamada 181 birim verir, varsayılan 0,50 eşiğinden 86 birim
  ucuzdur; aynı seçim sınamadan okunsaydı 7 birim daha iyi görünürdü.
- Şüpheli oranı 0,2421'den 0,0354'e inince işlem karakteristiği alanı 0,8058'den 0,8624'e çıkar,
  kesinlik–duyarlılık alanı 0,6212'den 0,4124'e iner; birinci eğrinin tabanı her iki kümede
  0,5000, ikincisininki şüpheli oranının kendisidir.
- İşlem karakteristiği alanı 0,0084 farkla eşit sayılan iki model, kesinlik–duyarlılık alanında
  0,1104 ve en iyi bedelde 16 birim ayrışır.

#### Sonraki Adım

Bu dersin tamamı **sıralamaya** dayandı. Eşik süpürüldü, eğriler basıldı, işletme noktası seçildi;
hiçbirinde modelin söylediği sayının kendisinin doğru olması gerekmedi. Bütün skorlar artan bir
işlevden geçirilse sıralama değişmez, iki eğri aynı kalır, işletme noktası aynı aboneleri
işaretler. Oysa model bir abone için 0,80 dediğinde bu sayının bir karşılığı olmalıdır: 0,80 denen
abonelerin yaklaşık yüzde sekseni şüpheli çıkmalıdır. Sonraki ders söylenen olasılık ile
gerçekleşen oranı aralık aralık yan yana basar, sıralaması iyi kalibrasyonu kötü bir modelin
varlığını gösterir ve düzeltmeyi yalnız doğrulama kümesinden öğrenilen tek değişkenli bir eşleme
olarak yazar.

### Olasılık Kalibrasyonu

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/olasilik-kalibrasyonu

Söylenen olasılık ile gerçekleşen oran arasındaki farkın ölçülmesi: doğrusal modelin kalibrasyon hatası 0,0242, sıralaması ona tıpatıp eşit olan keskinleştirilmiş skorunki 0,1512'dir ve iki modelin eğri altındaki alanı da 0,8058'dir. Derinlik 8 ağacın eğitim kümesindeki kalibrasyon hatası tanım gereği 0,0000, sınama kümesinde 0,1435'tir; eğitimden öğrenilen düzeltme eşlemesi birim eşlemeye yakınsar ve hiçbir şey düzeltmez. Doğrulama kümesinden öğrenilen tek değişkenli eşleme keskinleştirilmiş skorun hatasını 0,1512'den 0,0463'e indirir ve eğri altındaki alanı 0,8058'de bırakır.

Önceki dersin tamamı sıralamaya dayanıyordu. Eşik süpürüldü, iki eğri basıldı, işletme noktası
bedel hesabıyla seçildi; hiçbir adımda modelin söylediği sayının kendisinin bir karşılığı olması
gerekmedi. Skorlar kesin artan bir dönüşümden geçirilse sıralama, eğriler ve seçilen abone kümesi
değişmeden kalırdı.

Ama model bir abone için 0,80 dediğinde bu sayı bir şey iddia eder: 0,80 denen abonelerin
yaklaşık yüzde sekseni şüpheli çıkmalıdır. Bu iddia sıralamadan bağımsızdır ve ayrı ölçülür.
**Kalibrasyon**, söylenen olasılık ile gerçekleşen oran arasındaki farktır.

- **MD23.** Kalibrasyon sıralamadan bağımsız bir ölçüdür; iki model aynı sıralamayı verip ayrı
  kalibrasyon hataları üretebilir.
- **MD24.** Olasılıklar beş eşit genişlikte aralığa bölünür. Aralık sayısı bir seçimdir: sayı
  büyüdükçe her aralıktaki abone sayısı düşer ve gerçekleşen oran oynar.
- **MD25.** Kalibrasyon hatası, her aralıkta söylenen olasılıkların toplamı ile gerçekleşen
  şüpheli sayısının mutlak farkı toplanıp gözlem sayısına bölünerek hesaplanır.
- **MD26.** Keskinleştirilmiş skor kesin artan bir dönüşümdür; sıralamayı ve dolayısıyla eğri
  altındaki alanı **tam olarak** korur.
- **MD27.** Düzeltme eşlemesi modelin skorundan başka hiçbir öznitelik görmez; tek girdisi skor,
  iki parametresi eğim ve kaydırmadır. Eğim pozitif kaldığı sürece sıralama değişmez.
- **MD28.** Eşleme **doğrulama kümesinden** öğrenilir; öbür iki küme karşılaştırma için basılır.
- **MD29.** Ağacın yaprak olasılığı o yaprağa düşen eğitim satırlarındaki şüpheli oranıdır; bu
  yüzden ağacın eğitim kümesindeki kalibrasyon hatası tanım gereği sıfırdır.
- **MD30.** Taban çizgisi burada da bir sayı verir: her aboneye eğitim kümesindeki şüpheli oranını
  söyleyen yordam kalibrasyonda ölçülür ve eğri altındaki alanı 0,5000'dir.

#### Söylenen ile Gerçekleşen

Kalibrasyon ölçmek için olasılıklar aralıklara bölünür, her aralıkta modelin söylediği ortalama
olasılık ile o aralıktaki abonelerin gerçekleşen şüpheli oranı yan yana yazılır. Bu tabloya
**güvenilirlik eğrisi** denir ve çizilmesi gerekmez, sayı olarak okunur.

```python
# degerlendirme.py — MODELDIR. Kurgu abone tablosu ayni tohumla yeniden uretilir.
import math

TOHUM, ADAY, 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, OKUMA = [], {}
for i in range(ADAY):
    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 = uretec(TOHUM + 7000 + k["no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        OKUMA.setdefault(k["no"], []).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()

VERI = []
for k in ABONE:
    v = OKUMA.get(k["no"])
    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)}
    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"]


def safsizlik(s):
    p = sum(x["supheli"] for x in s) / len(s)
    return 2 * p * (1 - p)


def agac(s, derinlik, alan, enaz=2):        # MODELDIR: karar agaci
    n = len(s)
    p = sum(x["supheli"] for x in s) / n
    en = None
    if derinlik and n >= 2 * enaz and 0.0 < p < 1.0:
        for a in alan:
            d = sorted({x[a] for x in s})
            for v in (d[1:] if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < enaz:
                    continue
                k = safsizlik(s) - (len(sol) * safsizlik(sol) + len(sag) * safsizlik(sag)) / n
                if en is None or k > en[0]:
                    en = (k, a, v, sol, sag)
    if en is None or en[0] <= 1e-9:
        return {"p": p}
    return {"alan": en[1], "esik": en[2], "sol": agac(en[3], derinlik - 1, alan, enaz),
            "sag": agac(en[4], derinlik - 1, alan, enaz)}


def olasilik(d, x):
    while "p" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["p"]


def dogrusal(egt, alan, w, tur=300, adim=0.5):   # MODELDIR: egim temelli dogrusal model
    o = {a: sum(x[a] for x in egt) / len(egt) for a in alan}
    s = {a: (sum((x[a] - o[a]) ** 2 for x in egt) / len(egt)) ** 0.5 or 1.0 for a in alan}
    f = lambda x: [(x[a] - o[a]) / s[a] for a in alan]
    k, b = [0.0] * len(alan), 0.0
    X = [f(x) for x in egt]
    y = [x["supheli"] for x in egt]
    a = [(w if x["supheli"] else 1.0) for x in egt]
    n = sum(a)
    for _ in range(tur):
        gk, gb = [0.0] * len(alan), 0.0
        for i, xi in enumerate(X):
            z = b + sum(k[j] * xi[j] for j in range(len(k)))
            e = (1 / (1 + math.exp(-max(-30.0, min(30.0, z)))) - y[i]) * a[i]
            gb += e
            for j in range(len(k)):
                gk[j] += e * xi[j]
        b -= adim * gb / n
        for j in range(len(k)):
            k[j] -= adim * gk[j] / n

    def skor(x):
        v = f(x)
        z = b + sum(k[j] * v[j] for j in range(len(k)))
        return 1 / (1 + math.exp(-max(-30.0, min(30.0, z))))
    return skor


S = dogrusal(EGT, ALAN, 1.0)        # MODELDIR: olasilik ureten egim temelli dogrusal model
ARALIK = 5


def guvenilirlik(skor, veri, n=ARALIK):     # her aralikta sayim, soylenen, gerceklesen
    kova = [[0, 0.0, 0] for _ in range(n)]
    for x in veri:
        p = skor(x)
        k = kova[min(n - 1, int(p * n))]
        k[0], k[1], k[2] = k[0] + 1, k[1] + p, k[2] + x["supheli"]
    return kova


def kal_hata(skor, veri, n=ARALIK):         # aralik agirlikli ortalama mutlak fark
    return sum(abs(k[1] - k[2]) for k in guvenilirlik(skor, veri, n)) / len(veri)


def guvenilirlik_bas(ad, skor, veri):
    print(f"{ad}  (kalibrasyon hatasi {kal_hata(skor, veri):.4f})")
    for i, k in enumerate(guvenilirlik(skor, veri)):
        if k[0]:
            print(f"  {i / ARALIK:.1f}-{(i + 1) / ARALIK:.1f}  abone {k[0]:>3}  "
                  f"soylenen {k[1] / k[0]:.4f}  gerceklesen {k[2] / k[0]:.4f}  "
                  f"fark {k[1] / k[0] - k[2] / k[0]:+.4f}")


guvenilirlik_bas("dogrusal model, sinama kumesi", S, SIN)
```

```
dogrusal model, sinama kumesi  (kalibrasyon hatasi 0.0242)
  0.0-0.2  abone 152  soylenen 0.0891  gerceklesen 0.1053  fark -0.0161
  0.2-0.4  abone  53  soylenen 0.2837  gerceklesen 0.3019  fark -0.0182
  0.4-0.6  abone  23  soylenen 0.4918  gerceklesen 0.4783  fark +0.0136
  0.6-0.8  abone  16  soylenen 0.7079  gerceklesen 0.6250  fark +0.0829
  0.8-1.0  abone   8  soylenen 0.8698  gerceklesen 1.0000  fark -0.1302
```

İlk üç aralıkta fark 0,02 düzeyinin altında ve kalibrasyon hatası 0,0242. Son iki aralık daha
oynak, çünkü orada toplam 24 abone var: 0,6-0,8 aralığında model 0,7079 söylerken 0,6250
gerçekleşiyor, 0,8-1,0 aralığında 0,8698 söylerken sekiz abonenin sekizi de şüpheli çıkıyor.
Sekiz aboneden hesaplanan bir oranın 0,13 sapması ölçünün kendi oynaklığıdır; aralık sayısı
büyüdükçe bu oynaklık büyür, küçüldükçe tablo körleşir.

#### Sıralaması İyi, Kalibrasyonu Kötü

Skorlar kesin artan bir dönüşümden geçirilirse hiçbir abonenin sırası değişmez. Sıralamaya
dayanan her ölçü sabit kalır; kalibrasyon kalmaz.

```python
def alan_roc(skor, veri):                   # esitlikleri ortalayan sira toplami
    p = sorted(((skor(x), x["supheli"]) for x in veri))
    P = sum(y for _, y in p)
    t, i = 0.0, 0
    while i < len(p):
        j = i
        while j < len(p) and p[j][0] == p[i][0]:
            j += 1
        t += (i + j + 1) / 2.0 * sum(y for _, y in p[i:j])
        i = j
    return (t - P * (P + 1) / 2) / (P * (len(p) - P))


def keskin(p, g=3.0):                       # kesin artan, siralamayi degistirmeyen donusum
    return p ** g / (p ** g + (1 - p) ** g)


K = lambda x: keskin(S(x))                  # MODELDIR: keskinlestirilmis skor
T8 = agac(EGT, 8, ALAN)                     # MODELDIR: derinlik 8 karar agaci
TABAN = sum(x["supheli"] for x in EGT) / len(EGT)
print(f"{'model':<20}{'ROC':>8}{'kal.hata':>10}{'ort skor':>10}{'supheli orani':>15}")
for ad, s in (("dogrusal", S), ("keskinlestirilmis", K),
              ("agac d8", lambda x: olasilik(T8, x)),
              ("taban (egitim orani)", lambda x: TABAN)):
    print(f"{ad:<20}{alan_roc(s, SIN):>8.4f}{kal_hata(s, SIN):>10.4f}"
          f"{sum(s(x) for x in SIN) / len(SIN):>10.4f}"
          f"{sum(x['supheli'] for x in SIN) / len(SIN):>15.4f}")
print()
guvenilirlik_bas("keskinlestirilmis skor, sinama kumesi", K, SIN)
```

```
model                    ROC  kal.hata  ort skor  supheli orani
dogrusal              0.8058    0.0242    0.2309         0.2421
keskinlestirilmis     0.8058    0.1512    0.1504         0.2421
agac d8               0.7391    0.1435    0.2586         0.2421
taban (egitim orani)  0.5000    0.0198    0.2222         0.2421

keskinlestirilmis skor, sinama kumesi  (kalibrasyon hatasi 0.1512)
  0.0-0.2  abone 204  soylenen 0.0199  gerceklesen 0.1569  fark -0.1369
  0.2-0.4  abone  10  soylenen 0.3033  gerceklesen 0.3000  fark +0.0033
  0.4-0.6  abone   7  soylenen 0.4756  gerceklesen 0.8571  fark -0.3816
  0.6-0.8  abone   8  soylenen 0.7001  gerceklesen 0.3750  fark +0.3251
  0.8-1.0  abone  23  soylenen 0.9509  gerceklesen 0.7391  fark +0.2117
```

İlk tablonun ilk iki satırı iddianın kanıtı: iki modelin eğri altındaki alanı aynı, 0,8058, çünkü
ikisi 252 aboneyi tıpatıp aynı sırada diziyor. Kalibrasyon hatası ise 0,0242'den 0,1512'ye, altı
katından fazlasına çıkıyor.

Üçüncü satır ayrı bir uyarı taşıyor. Derinlik 8 ağacının ortalama skoru 0,2586, sınama kümesindeki
gerçek şüpheli oranı 0,2421 — aradaki fark yalnız 0,0165. Toplamda doğru görünen bir model aralık
düzeyinde 0,1435 hata veriyor. Ortalamanın tutması kalibrasyon değildir; birbirini götüren aşırı
ve eksik güvenleri gizler.

Dördüncü satır kalibrasyonun tek başına neyi ölçmediğini söylüyor. Her aboneye eğitim kümesinin
şüpheli oranını, 0,2222'yi söyleyen taban çizgisinin kalibrasyon hatası 0,0198 ile tablodaki en
küçük sayıdır; eğri altındaki alanı ise 0,5000, yani sıralaması rastgeleyle aynıdır. Hiçbir şey
öğrenmemiş bir yordam kalibrasyonda birinci olabilir. İki ölçü ayrı sorulara yanıt verir ve
ikisinin de yazılması gerekir.

İkinci tablo keskinleştirilmiş skorun nerede bozulduğunu gösteriyor. En kalabalık aralıkta model
0,0199 söylüyor, gerçekleşen 0,1569 — yedi katından fazla. Üst uçta ise 0,9509 söyleyip 0,7391
gerçekleştiriyor. Model bir yandan aşırı çekingen, bir yandan aşırı güvenli; iki hata ortalamada
bir ölçüde birbirini götürür, aralık düzeyinde ikisi de durur.

#### Düzeltme: Tek Değişkenli Bir Eşleme

Bozulma sıralamada değil, skorun sayı değerinde. Düzeltmenin de sıralamaya dokunmaması gerekir:
skoru girdi alan, kesin artan, iki parametreli bir eşleme yeter.

```python
def logit(p, e=1e-6):
    p = min(1 - e, max(e, p))
    return math.log(p / (1 - p))


def esleme(skor, veri, tur=600, adim=0.5):  # MODELDIR: tek degiskenli duzeltme eslemesi
    z = [logit(skor(x)) for x in veri]
    y = [x["supheli"] for x in veri]
    a, b = 1.0, 0.0
    for _ in range(tur):
        ga = gb = 0.0
        for i, zi in enumerate(z):
            e = 1 / (1 + math.exp(-max(-30.0, min(30.0, a * zi + b)))) - y[i]
            ga, gb = ga + e * zi, gb + e
        a, b = a - adim * ga / len(z), b - adim * gb / len(z)
    return lambda x: 1 / (1 + math.exp(-max(-30.0, min(30.0, a * logit(skor(x)) + b)))), a, b


print(f"{'eslemenin ogrenildigi kume':<28}{'a':>8}{'b':>9}{'ROC':>8}{'kal.hata':>10}")
print(f"{'duzeltme yok':<28}{'-':>8}{'-':>9}{alan_roc(K, SIN):>8.4f}"
      f"{kal_hata(K, SIN):>10.4f}")
for ad, kume in (("egitim", EGT), ("dogrulama", DOG), ("sinama", SIN)):
    d, a, b = esleme(K, kume)
    print(f"{ad:<28}{a:>8.4f}{b:>9.4f}{alan_roc(d, SIN):>8.4f}{kal_hata(d, SIN):>10.4f}")
A8 = lambda x: olasilik(T8, x)
d8, a8, b8 = esleme(A8, EGT)
print(f"\nagac d8 kalibrasyon hatasi: egitim {kal_hata(A8, EGT):.4f}, "
      f"dogrulama {kal_hata(A8, DOG):.4f}, sinama {kal_hata(A8, SIN):.4f}")
print(f"egitimden ogrenilen esleme a {a8:.4f} b {b8:.4f}, sinamada kal.hata "
      f"{kal_hata(d8, SIN):.4f}")
```

```
eslemenin ogrenildigi kume         a        b     ROC  kal.hata
duzeltme yok                       -        -  0.8058    0.1512
egitim                        0.3340   0.0015  0.8058    0.0244
dogrulama                     0.3013  -0.3695  0.8058    0.0463
sinama                        0.3038  -0.0043  0.8058    0.0208

agac d8 kalibrasyon hatasi: egitim 0.0000, dogrulama 0.1624, sinama 0.1435
egitimden ogrenilen esleme a 1.0002 b 0.0001, sinamada kal.hata 0.1435
```

Eşlemenin eğimi üç kümede de 0,30 dolayında: keskinleştirme skorları ne kadar uçlara ittiyse,
eşleme o kadarını geri alıyor. Eğri altındaki alan dört satırda da 0,8058 — eğim pozitif olduğu
sürece sıralama değişmez ve düzeltme hiçbir aboneyi yerinden oynatmaz. Kalibrasyon hatası ise
0,1512'den doğrulama kümesiyle 0,0463'e iniyor, yani üçte birine.

Sınama kümesinden öğrenilen eşleme 0,0208 veriyor; bu sayı bir başarım değil, düzeltmenin
ölçüldüğü kümeden öğrenilmiş olmasının sonucudur ve 0,0463 ile arasındaki 0,0255 fark bir kestirim
değil bir iyimserliktir.

Eğitim satırı bu kümede iyi çıkıyor, 0,0244; doğrusal model eğitim kümesine sıkı uymadığı için
oradaki olasılıkları da aşırı güvenli değil. Kuralın gerekçesi son iki satırda. Derinlik 8
ağacının eğitim kümesindeki kalibrasyon hatası 0,0000, sınama kümesinde 0,1435 — ağacın yaprak
olasılığı o yaprağın eğitim oranıdır, dolayısıyla eğitim kümesinde söylenen ile gerçekleşen tanım
gereği eşittir. Eğitimden öğrenilen eşleme eğimi 1,0002, kaydırması 0,0001 buluyor ve sınamadaki
hatayı 0,1435'ten 0,1435'e taşıyor: düzeltilecek bir şey görmemiştir, çünkü baktığı kümede
düzeltilecek bir şey yoktur.

#### Özet

- Kalibrasyon, söylenen olasılık ile gerçekleşen oran arasındaki farktır ve olasılıklar aralıklara
  bölünerek ölçülür; doğrusal modelin sınama kümesindeki kalibrasyon hatası 0,0242'dir.
- Kesin artan bir dönüşüm sıralamayı korur: keskinleştirilmiş skorun eğri altındaki alanı da
  0,8058'dir, kalibrasyon hatası ise 0,1512, altı katından fazla.
- Ortalama skorun gerçek orana yakın olması yetmez: derinlik 8 ağacının ortalaması 0,2586, gerçek
  oran 0,2421, aralık düzeyindeki hatası 0,1435. Sabit 0,2222 söyleyen taban çizgisi ise
  kalibrasyonda 0,0198 ile en iyi, eğri altındaki alanda 0,5000 ile en kötüdür.
- Doğrulamadan öğrenilen tek değişkenli eşleme kalibrasyon hatasını 0,1512'den 0,0463'e indirir ve
  eğri altındaki alanı 0,8058'de bırakır; eğim pozitif olduğu için hiçbir sıra değişmez.
- Ağacın eğitim kümesindeki kalibrasyon hatası tanım gereği 0,0000'dır; oradan öğrenilen eşleme
  birim eşlemeye yakınsar ve sınamadaki 0,1435 hatayı olduğu gibi bırakır.

#### Sonraki Adım

Dört derste ölçütün her biri sınıflandırma içindi ve hepsi tek bir işlemden çıktı: **sayma**.
Karışıklık matrisi dört sayım, kesinlik ile duyarlılık iki oran, eğriler eşik başına birer sayım
çifti, kalibrasyon aralık başına bir sayım karşılaştırmasıydı. Hedef bir sınıf olduğu için doğru
ile yanlış ayrık iki kutuydu. Aynı kurgu kümede hedef dönem 3 ortalama tüketimi olduğunda
sayılacak bir kutu kalmaz: model 22,40 der, gerçek 26,15'tir ve bu tahmin ne doğru ne yanlıştır.
Sonraki ders ölçüyü **uzaklığa** çevirir ve taban çizgisini eğitim ortalaması olarak yeniden
kurar.

### Bağlanım Ölçütleri

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/baglanim-olcutleri

Hedef bir sayı olduğunda ölçütün sayımdan uzaklığa geçmesi ve aynı ailenin iki üyesinin üç ölçütte yer değiştirmesi: kare hataya uydurulmuş doğrusal model ortalama kare hatada 22,3164 ile önde, mutlak hataya uydurulmuş olan ortalama mutlak hatada 3,6178 ile önde. Taban çizgisi eğitim ortalamasıdır (19,7665 m³) ve açıklanan değişim oranı tam olarak bu tabana göre tanımlıdır. Sınama kümesinin 13 satırı kare hatanın yüzde 37,59'unu, mutlak hatanın yüzde 17,71'ini taşır; o 13 satır kırpıldığında sıralama iki ölçütte de tersine döner. Denenen aday sayısı üçtür ve hiçbir ayar değişkeni süpürülmemiştir.

Önceki dört ders ölçütü sınıflandırma için kurdu. Karışıklık matrisi, kesinlik, duyarlılık, eşik
ve kalibrasyon — hepsi tek bir işlemden çıktı: **saymak**. Kaç örnek doğru sınıfa düştü, kaç örnek
yanlış olumluya gitti, bir olasılık aralığında kaç örnek gerçekleşti. Sayılabilir bir şey vardı,
çünkü tahmin ile gerçek ya çakışıyordu ya çakışmıyordu.

Hedef bir sayı olduğunda çakışma diye bir şey yoktur. 24,80 m³ tüketen bir abone için 24,79
denmesi ile 11,20 denmesi arasındaki fark bir sayım farkı değil bir **uzaklık** farkıdır ve bu
uzaklığın nasıl toplanacağı bir karardır. Bu ders o kararı ölçer: aynı ailenin iki üyesi, aynı
ayrılmış kümede, iki ölçütte yer değiştirir.

- **MD36.** Hedef, abonenin üç döneme yayılan okumalarından hesaplanan **ortalama tüketimidir**
  (m³). Küme M27/K01'in kurgu abone tablosudur, tohum 20260218, bölme 756/252/252.
- **MD37.** Taban çizgisi **eğitim kümesinin ortalamasıdır**: her satır için aynı sayı söylenir.
  Sınıflandırmadaki "en sık sınıfı söyle" tabanının bağlanımdaki karşılığı budur ve öğrenmez.
- **MD38.** Öznitelikler kurs boyunca sabittir: beş bölge göstergesi, hane büyüklüğü, memnuniyet,
  okuma sayısı ve sıfır okuma göstergesi. Değişen tek şey modeldir.
- **MD39.** Ortalama mutlak hata (OMH) hataların mutlak değerlerinin ortalaması, ortalama kare
  hata (OKH) karelerinin ortalamasıdır. OKH hedefin biriminin karesindedir; karekökü alındığında
  birime döner.
- **MD40.** Açıklanan değişim oranı (ADO) $1 - \text{OKH}_{\text{model}} / \text{OKH}_{\text{taban}}$
  olarak hesaplanır ve payda **MD37'nin tabanıdır**. Taban değişirse ADO değişir.
- **MD41.** İki doğrusal model de aynı öznitelikleri ve aynı biçimi kullanır; ayrıldıkları tek yer
  **eğitim sırasında hangi hatayı küçülttükleridir**. İkisi de modeldir.
- **MD42.** Bu derste denenen aday sayısı **üçtür** ve hiçbir ayar değişkeni süpürülmemiştir;
  ayar bütçesinin şişirdiği fark `07`'de ölçülür.

#### Üç Ölçüt ve Öğrenmeyen Taban

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir;
# bu derste hedef sinif degil sayidir: ort_tuketim (m3).
import math

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 = sum(v) / len(v), max(v) - min(v)
    x = {"no": k["no"], "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), "bolge_ad": k["bolge"][0]}
    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 = ["b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez",
        "hane", "memnuniyet", "donem", "sifir_okuma"]


def omh(t, s):
    return sum(abs(t[i] - x["ort_tuketim"]) for i, x in enumerate(s)) / len(s)


def okh(t, s):
    return sum((t[i] - x["ort_tuketim"]) ** 2 for i, x in enumerate(s)) / len(s)


TABAN = sum(x["ort_tuketim"] for x in EGT) / len(EGT)
TABAN_OKH = okh([TABAN] * len(SIN), SIN)


def ado(t, s):
    return 1 - okh(t, s) / TABAN_OKH


print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}")
print(f"egitim ortalamasi {TABAN:.4f} m3, sinama ortalamasi "
      f"{sum(x['ort_tuketim'] for x in SIN) / len(SIN):.4f} m3")
print(f"taban cizgisi: OMH {omh([TABAN] * len(SIN), SIN):.4f}  "
      f"OKH {TABAN_OKH:.4f}  ADO {ado([TABAN] * len(SIN), SIN):.4f}")
```

```
egitim 756, dogrulama 252, sinama 252
egitim ortalamasi 19.7665 m3, sinama ortalamasi 19.9234 m3
taban cizgisi: OMH 5.0806  OKH 42.6718  ADO 0.0000
```

Taban çizgisi 19,7665 m³ söylüyor ve ortalama 5,0806 m³ şaşıyor. Aynı hatanın kare olarak
toplanmışı 42,6718'dir; karekökü 6,53 m³. İki sayı arasındaki bu boşluk ölçütün kendisinden gelir:
kare toplamı, büyük hataları küçük hatalardan daha ağır sayar, bu yüzden hata dağılımı çarpıkken
kareli ölçüt mutlak ölçütten büyük bir sayı verir. Tabanın ADO değeri tanım gereği tam sıfırdır —
taban, kendisine göre hiçbir değişim açıklamaz.

Tabanın **eğitim** ortalaması olması bir ayrıntı değil bir zorunluluktur. Sınama kümesinin kendi
ortalaması bir tahmin değil, ölçülecek satırlardan okunmuş bir sayıdır; onu taban yapmak sınama
kümesine bakmış bir yordamı taban saymak olur. Öğrenmeyen bir yordamın da yalnız eğitim kümesini
görmesi gerekir.

#### Aynı Ailenin İki Üyesi

Model bir doğrusal modeldir: her öznitelik bir katsayıyla çarpılır, toplanır. İki üye arasındaki
tek fark eğitim sırasında hangi hatayı küçülttükleridir. Birincisi kare hatayı küçültür ve
katsayıları en küçük karelerle çözer. İkincisi mutlak hatayı küçültür; bunu, her turda hatası
büyük satırların ağırlığını düşürerek ve aynı çözümü yeniden kurarak yapar.

```python
def coz(A, b):
    n = len(A)
    M = [list(A[i]) + [b[i]] for i in range(n)]
    for i in range(n):
        p = max(range(i, n), key=lambda r: abs(M[r][i]))
        M[i], M[p] = M[p], M[i]
        for r in range(n):
            if r != i:
                f = M[r][i] / M[i][i]
                for c in range(i, n + 1):
                    M[r][c] -= f * M[i][c]
    return [M[i][n] / M[i][i] for i in range(n)]


def agirlikli_ekk(s, agirlik, ceza=1e-6):
    d = len(ALAN) + 1
    A = [[0.0] * d for _ in range(d)]
    b = [0.0] * d
    for x, g in zip(s, agirlik):
        v = [1.0] + [float(x[a]) for a in ALAN]
        for i in range(d):
            b[i] += g * v[i] * x["ort_tuketim"]
            for j in range(d):
                A[i][j] += g * v[i] * v[j]
    for i in range(1, d):
        A[i][i] += ceza
    return coz(A, b)


def kestir(w, x):
    return w[0] + sum(w[i + 1] * x[a] for i, a in enumerate(ALAN))


def mutlak_uydur(s, tur=40, taban_agirlik=0.05):
    w = agirlikli_ekk(s, [1.0] * len(s))
    for _ in range(tur):
        g = [1.0 / max(abs(kestir(w, x) - x["ort_tuketim"]), taban_agirlik) for x in s]
        w = agirlikli_ekk(s, g)
    return w


ORT = {}
for x in EGT:
    ORT.setdefault(x["bolge_ad"], []).append(x["ort_tuketim"])
ORT = {k: sum(v) / len(v) for k, v in ORT.items()}

W_KARE, W_MUTLAK = agirlikli_ekk(EGT, [1.0] * len(EGT)), mutlak_uydur(EGT)
ADAY = [("taban cizgisi", [TABAN] * len(SIN)),
        ("bolge ortalamasi", [ORT[x["bolge_ad"]] for x in SIN]),
        ("kare hataya uydurulmus", [kestir(W_KARE, x) for x in SIN]),
        ("mutlak hataya uydurulmus", [kestir(W_MUTLAK, x) for x in SIN])]

print(f"{'model':<26} {'OMH':>8} {'OKH':>9} {'ADO':>8}")
for ad, t in ADAY:
    print(f"{ad:<26} {omh(t, SIN):>8.4f} {okh(t, SIN):>9.4f} {ado(t, SIN):>8.4f}")
for olcut, fn, yon in (("OMH", omh, 1), ("OKH", okh, 1), ("ADO", ado, -1)):
    s = sorted(ADAY[1:], key=lambda p: yon * fn(p[1], SIN))
    print(f"{olcut} siralamasi: "
          + ", ".join(f"{i + 1}) {a}" for i, (a, _) in enumerate(s)))
```

```
model                           OMH       OKH      ADO
taban cizgisi                5.0806   42.6718   0.0000
bolge ortalamasi             4.0374   28.7760   0.3256
kare hataya uydurulmus       3.6504   22.3164   0.4770
mutlak hataya uydurulmus     3.6178   22.3987   0.4751
OMH siralamasi: 1) mutlak hataya uydurulmus, 2) kare hataya uydurulmus, 3) bolge ortalamasi
OKH siralamasi: 1) kare hataya uydurulmus, 2) mutlak hataya uydurulmus, 3) bolge ortalamasi
ADO siralamasi: 1) kare hataya uydurulmus, 2) mutlak hataya uydurulmus, 3) bolge ortalamasi
```

Son üç satır dersin yanıtıdır. Ortalama mutlak hata mutlak hataya uydurulmuş modeli birinci
sırada okuyor: 3,6178'e karşı 3,6504, arada 0,0326 m³. Ortalama kare hata **başka bir modeli**
birinci sırada okuyor: 22,3164'e karşı 22,3987, arada 0,0823. **Üç aday, iki ölçüt, iki ayrı
birinci.** Hiçbir hesap yanlış değildir; iki ölçüt aynı hata listesini iki ayrı biçimde topluyor
ve iki ayrı sıralama üretiyor.

Bölge ortalaması her iki ölçütte de üçüncüdür ve tabanı ikisinde de açık farkla geçiyor. Ailenin
kattığı farkın büyük bölümü orada: taban 42,6718'den bölge ortalaması 28,7760'a düşüyor, doğrusal
model 22,3164'e indiriyor. Bölge tek başına açıklanan değişimin 0,3256'sını, kalan sekiz öznitelik
0,1514'ünü veriyor.

İki doğrusal modelin OKH farkı 0,0823'tür. Karekökleri alındığında bu 4,7240 m³ ile 4,7327 m³
demektir; iki modelin tipik şaşması 0,0087 m³ ayrılıyor. Ölçütün ham değeri hedefin
biriminin karesinde olduğu için 0,0823 gözle büyük görünür, oysa birime döndürüldüğünde ortalama
mutlak hatadaki 0,0326'lık farkla aynı büyüklük sınıfındadır. Kareli bir ölçütü yorumlamadan önce
karekökünü almak, farkın gerçek ölçeğini görmenin en ucuz yoludur.

#### Hangi Ölçüt Hangi Hatayı Ağırlıklandırıyor

Yer değiştirmenin kaynağı, iki ölçütün büyük hatalara verdiği ağırlıktır. Bunu ölçmenin yolu,
hataları büyükten küçüğe sıralayıp toplamın hangi payının hangi satırlardan geldiğini saymaktır.

```python
HATA = {ad: sorted((abs(t[i] - x["ort_tuketim"]) for i, x in enumerate(SIN)),
                   reverse=True) for ad, t in ADAY[2:]}
print(f"{'model':<26} {'top13 OMH':>10} {'top13 OKH':>10} "
      f"{'yari OMH':>9} {'yari OKH':>9}")
for ad, h in HATA.items():
    def yarisi(fn):
        toplam, birikim, sayac = sum(fn(q) for q in h), 0.0, 0
        for q in h:
            birikim, sayac = birikim + fn(q), sayac + 1
            if 2 * birikim >= toplam:
                return sayac
    print(f"{ad:<26} {sum(h[:13]) / sum(h):>10.4f} "
          f"{sum(q * q for q in h[:13]) / sum(q * q for q in h):>10.4f} "
          f"{yarisi(lambda q: q):>9} {yarisi(lambda q: q * q):>9}")

buyuk = sorted(range(len(SIN)),
               key=lambda i: -abs(ADAY[2][1][i] - SIN[i]["ort_tuketim"]))[:13]
kalan = [i for i in range(len(SIN)) if i not in set(buyuk)]
alt = [SIN[i] for i in kalan]
print(f"\n13 satir kirpildiktan sonra ({len(alt)} satir)")
for ad, t in ADAY[2:]:
    kt = [t[i] for i in kalan]
    print(f"{ad:<26} OMH {omh(kt, alt):.4f}  OKH {okh(kt, alt):.4f}")
print(f"kirpilan 13 satirin gercek degerleri: "
      f"{[SIN[i]['ort_tuketim'] for i in buyuk[:4]]}")
```

```
model                       top13 OMH  top13 OKH  yari OMH  yari OKH
kare hataya uydurulmus         0.1771     0.3759        58        22
mutlak hataya uydurulmus       0.1838     0.3962        57        21

13 satir kirpildiktan sonra (239 satir)
kare hataya uydurulmus     OMH 3.1674  OKH 14.6846
mutlak hataya uydurulmus   OMH 3.1186  OKH 14.3617
kirpilan 13 satirin gercek degerleri: [43.48, 39.49, 36.53, 32.18]
```

Sınama kümesinin 252 satırından **13'ü**, yani yüzde beşi, kare hatanın yüzde 37,59'unu taşıyor;
aynı 13 satır mutlak hatanın yalnız yüzde 17,71'ini taşıyor. Aynı ayrım toplamın yarısında da
görünür: mutlak hatanın yarısı 58 satıra dağılmışken kare hatanın yarısı **22 satırda** toplanıyor.
Kare ölçütü 252 satırlık bir kümede etkin olarak yirmi küsur satırdan okunur.

Bu 13 satır kırpıldığında sıralama iki ölçütte de tersine döner: mutlak hataya uydurulmuş model
hem OMH'de (3,1186'ya karşı 3,1674) hem OKH'de (14,3617'ye karşı 14,6846) öne geçiyor. Kare
hataya uydurulmuş modelin bütün OKH üstünlüğü, kırpılan o 13 satırdadır. Kırpılan satırların
gerçek değerleri 43,48, 39,49, 36,53, 32,18 m³ — kümenin en yüksek tüketimleri. Kare hatayı
küçülten model bu uçları kovalamak için kalan 239 satırda biraz daha çok şaşmayı göze alıyor;
mutlak hatayı küçülten model tersini yapıyor.

Buradan çıkan karar ölçüt seçiminin bir **bedel** kararı olduğudur. Bir kaçak şüphesi kümesinde
tek bir abonenin 18 m³ şaşması ile on abonenin 1,8 m³ şaşması aynı saha maliyetini üretmiyorsa,
iki durumu aynı sayıya toplayan bir ölçüt yanlış modeli seçtirir.

#### Açıklanan Değişim Oranı Üçüncü Bir Sıralama Vermez

ADO sütunu OKH sütunuyla **birebir aynı** sıralamayı veriyor ve bu bir rastlantı değildir. Tanım
$1 - \text{OKH}_{\text{model}} / \text{OKH}_{\text{taban}}$; payda bütün modeller için aynı sabittir
(42,6718). Sabit bir sayıya bölmek sıralamayı değiştiremez. ADO üçüncü bir ölçüt değil, **kare
hatanın tabana göre okunmuş biçimidir**: 1 − 22,3164 / 42,6718 = 0,4770.

Kattığı şey sıralama değil, **birim**. OKH 22,3164 tek başına büyük mü küçük mü söylemez; ADO
0,4770 aynı sayıyı "tabanın hatasının yarısına yakınını sildi" diye okur. Bu yüzden ADO yazılırken
**hangi tabana göre** hesaplandığı da yazılmalıdır. Burada taban eğitim kümesinin ortalamasıdır.
Payda sınama kümesinin kendi ortalamasından hesaplansaydı 42,6471 olurdu ve ADO 0,4770 yerine
0,4767 çıkardı; fark küçüktür, çünkü iki ortalama birbirine yakındır (19,7665'e karşı 19,9234).
Eğitim ve sınama ortalamaları uzak düştüğünde bu fark küçük kalmaz ve iki ADO değeri
karşılaştırılamaz hale gelir.

Denenen aday sayısı bu derste **üçtür**: bölge ortalaması, kare hataya uydurulmuş doğrusal model,
mutlak hataya uydurulmuş doğrusal model. Hiçbirinde bir ayar değişkeni süpürülmedi, dolayısıyla
tablodaki hiçbir sayıda arama şişmesi yoktur. Aynı tablonun bütçe altında nasıl değiştiği `07`'nin
konusudur.

#### Özet

- Hedef bir sayı olduğunda ölçüt sayımdan uzaklığa geçer ve uzaklığın nasıl toplanacağı bir
  karardır; taban çizgisi eğitim ortalamasıdır (19,7665 m³; OMH 5,0806, OKH 42,6718).
- Aynı ailenin iki üyesi iki ölçütte yer değiştirir: mutlak hataya uydurulmuş model OMH'de
  3,6178 ile, kare hataya uydurulmuş model OKH'de 22,3164 ile birincidir.
- Sınama kümesinin 13 satırı kare hatanın yüzde 37,59'unu, mutlak hatanın yüzde 17,71'ini taşır;
  kare hatanın yarısı 22, mutlak hatanın yarısı 58 satırdan gelir.
- O 13 satır kırpıldığında sıralama iki ölçütte de tersine döner (3,1186'ya karşı 3,1674 ve
  14,3617'ye karşı 14,6846); kare ölçütünün üstünlüğünün tamamı o satırlardadır.
- Açıklanan değişim oranı üçüncü bir sıralama üretmez; paydası bütün modeller için sabit olduğundan
  ortalama kare hatanın tabana göre okunmuş biçimidir ve tabansız yazılamaz.

#### Sonraki Adım

Bu dersin bütün sayıları tek bir bölmeden okundu: 756 satır eğitim, 252 satır sınama. İki doğrusal
model arasındaki fark OMH'de 0,0326, OKH'de 0,0823 çıktı. Bunlar küçük sayılardır ve şu ana kadar
yanlarında hiçbir dalgalanma ölçüsü yok — başka bir bölme aynı sıralamayı verir miydi, bilinmiyor.
Sonraki ders bölmenin kendisini bir ayar olarak ele alır: elde kalan veri kaç kata bölünürse
kestirim ne kadar oynar, kat sayısı büyüdükçe kaç eğitim yapılır ve her katta tek satır bırakan
uçtaki seçim niçin her zaman daha iyi değildir.

### Çapraz Doğrulama

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/capraz-dogrulama

Kat sayısının kendisinin ölçülmesi: k 2'den 10'a süpürüldüğünde kestirimin ortalaması 0,8115 ile 0,8204 arasında kalır, sınama kümesindeki gerçek sayı 0,8056'dır ve gerçeğe en yakın kestirimi k=4 verir. Tek çıkarmalı doğrulama 0,8175 veriyor, gerçekten 0,0119 uzakta, ama bunun için 1.008 eğitim ve 1.015.056 satır işliyor — k=10'un 111,89 katı. Kat başına saçılması 0,3863'tür ve bu sayı belirsizlik değil, tek satırlık katın doğru/yanlış saçılmasıdır; 1.008 kattan hiçbiri iki sınıf birden içermediği için kesinlik ve duyarlılık kat başına hesaplanamaz. Buna karşılık tek çıkarmalı doğrulamanın karıştırmaya bağlılığı tam sıfırdır.

Önceki dersin bütün sayıları tek bir bölmeden okundu. İki doğrusal model arasındaki fark ortalama
mutlak hatada 0,0326, ortalama kare hatada 0,0823 çıktı ve bu sayıların yanında hiçbir dalgalanma
ölçüsü yoktu. Başka bir bölme aynı sıralamayı verir miydi sorusunun yanıtı verilmedi, çünkü elde
tek bir sayı vardı.

Çapraz doğrulama bu boşluğu kapatır: elde kalan veri katlara bölünür, her kat sırayla ölçüm kümesi
olur ve tek sayı yerine birkaç sayı ile bunların saçılması elde edilir. Yordamın kendisi ve kat
içinde hazırlama disiplini M27/K02'de kuruldu; burada varsayılır ve tekrarlanmaz. Bu dersin ölçtüğü
şey **k'nın kendisidir**: kat sayısı büyüdükçe kestirim ne kadar oynuyor, karşılığında kaç eğitim
yapılıyor ve uçtaki seçim — her katta tek satır bırakmak — niçin her zaman daha iyi değil.

- **MD43.** Havuz K01'in eğitim ve doğrulama kümeleridir: 1.008 satır. Sınama kümesinin 252 satırı
  hiçbir kata girmez ve yalnız bir kez, gerçek sayıyı okumak için kullanılır.
- **MD44.** Model, 0/1 etikete en küçük karelerle uydurulan bir doğrusal sınıflandırıcıdır; çıktı
  0,5 eşiğinden kesilir. Küçük ölçekli olmasının nedeni 1.008 kez eğitilecek olmasıdır ve **model
  budur**, bir kitaplığın taklidi değildir.
- **MD45.** Modelin öğrenilen hiçbir hazırlama adımı yoktur; kat içinde öğrenilen tek şey modelin
  kendisidir. Hazırlama disiplininin ölçüsü M27/K02'ye aittir.
- **MD46.** Katlar, karıştırılmış havuzda sıra numarasının k'ya bölümünden kalanına göre atanır;
  böylece her satır tam olarak bir kez ölçüm satırı olur ve kat boyutları en fazla bir satır ayrılır.
- **MD47.** Maliyet birimi **eğitim sayısı** ve **işlenen satır sayısıdır** (katların eğitim
  kümelerinin toplam büyüklüğü). Bu birimler koşumdan koşuma değişmez.
- **MD48.** Kat başına saçılma, katların kendi ortalamalarından ortalama uzaklığıdır ve M26'nın
  değişkenlik ölçüsüdür. Kestirimin belirsizliği değildir.
- **MD49.** Bu derste denenen aday sayısı **birdir**: kat sayısı bir ayar değişkeni değil, ölçme
  yordamının parçasıdır ve modelin kendisini değiştirmez.

#### k Süpürülünce Ne Oynuyor

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir;
# model, 0/1 etikete en kucuk karelerle uydurulan dogrusal bir siniflandiricidir.
import math

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 = {"no": k["no"], "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_ad": k["bolge"][0]}
    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)
SIN = K[1008:]
ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma",
        "b_dogu", "b_bati", "b_guney"]
D = len(ALAN) + 1


def coz(A, b):
    n = len(A)
    M = [list(A[i]) + [b[i]] for i in range(n)]
    for i in range(n):
        p = max(range(i, n), key=lambda r: abs(M[r][i]))
        M[i], M[p] = M[p], M[i]
        for r in range(n):
            if r != i:
                f = M[r][i] / M[i][i]
                for c in range(i, n + 1):
                    M[r][c] -= f * M[i][c]
    return [M[i][n] / M[i][i] for i in range(n)]


def ogren(s, ceza=1e-4):
    A = [[0.0] * D for _ in range(D)]
    b = [0.0] * D
    for x in s:
        v = [1.0] + [float(x[a]) for a in ALAN]
        y = x["supheli"]
        for i in range(D):
            b[i] += v[i] * y
            for j in range(i, D):
                A[i][j] += v[i] * v[j]
    for i in range(D):
        for j in range(i):
            A[i][j] = A[j][i]
        if i:
            A[i][i] += ceza
    return coz(A, b)


def dogruluk(w, s):
    d = 0
    for x in s:
        p = w[0] + sum(w[i + 1] * x[a] for i, a in enumerate(ALAN))
        d += (1 if p >= 0.5 else 0) == x["supheli"]
    return d / len(s)


HAVUZ = K[:1008]
GERCEK = dogruluk(ogren(HAVUZ), SIN)
c = int(sum(x["supheli"] for x in HAVUZ) * 2 > len(HAVUZ))
TABAN = sum(x["supheli"] == c for x in SIN) / len(SIN)
print(f"havuz {len(HAVUZ)} satir, sinama {len(SIN)} satir")
print(f"gercek sayi {GERCEK:.4f}, taban cizgisi {TABAN:.4f}")
```

```
havuz 1008 satir, sinama 252 satir
gercek sayi 0.8056, taban cizgisi 0.7579
```

Gerçek sayı, havuzun tamamından öğrenilen modelin sınama kümesindeki sayısıdır: 0,8056, tabanın
0,0477 üstünde. Çapraz doğrulamanın işi bu sayıyı **sınama kümesine bakmadan** kestirmektir. Aşağıda
k 2'den 10'a süpürülür ve son satırda uçtaki seçim durur: 1.008 kat, yani her katta tek satır.

```python
def kat_dogrulama(veri, k):
    v = []
    for i in range(k):
        ic = [x for j, x in enumerate(veri) if j % k != i]
        dis = [x for j, x in enumerate(veri) if j % k == i]
        v.append((dogruluk(ogren(ic), dis), len(ic), dis))
    return v


def ortalama(v):
    return sum(v) / len(v)


def sacilma(v):
    o = ortalama(v)
    return (sum((q - o) ** 2 for q in v) / len(v)) ** 0.5


print(f"{'k':>5} {'ortalama':>9} {'sacilma':>8} {'egitim':>7} {'egitim satiri':>14} "
      f"{'islenen satir':>14} {'fark':>8} {'iki sinifli kat':>16}")
for k in list(range(2, 11)) + [1008]:
    v = kat_dogrulama(HAVUZ, k)
    s = [q[0] for q in v]
    ikili = sum(1 for q in v if 0 < sum(x["supheli"] for x in q[2]) < len(q[2]))
    print(f"{k:>5} {ortalama(s):>9.4f} {sacilma(s):>8.4f} {k:>7} {v[0][1]:>14} "
          f"{1008 * (k - 1):>14} {ortalama(s) - GERCEK:>+8.4f} {ikili:>16}")
```

```
    k  ortalama  sacilma  egitim  egitim satiri  islenen satir     fark  iki sinifli kat
    2    0.8135   0.0099       2            504           1008  +0.0079                2
    3    0.8165   0.0014       3            672           2016  +0.0109                3
    4    0.8115   0.0278       4            756           3024  +0.0060                4
    5    0.8185   0.0239       5            806           4032  +0.0129                5
    6    0.8145   0.0237       6            840           5040  +0.0089                6
    7    0.8175   0.0243       7            864           6048  +0.0119                7
    8    0.8204   0.0359       8            882           7056  +0.0149                8
    9    0.8175   0.0286       9            896           8064  +0.0119                9
   10    0.8194   0.0280      10            907           9072  +0.0139               10
 1008    0.8175   0.3863    1008           1007        1015056  +0.0119                0
```

Ortalama sütunu 0,8115 ile 0,8204 arasında dolaşıyor: dokuz farklı k değeri arasındaki toplam
oynama **0,0089**. Fark sütunu hepsinin **artı** olduğunu gösteriyor; çapraz doğrulama sistematik
olarak gerçek sayının üstünde bir sayı veriyor ve bu, M27/K02'de kat içinde hazırlamayla ölçülen
+0,0149'luk sapmayla aynı yöndedir. Nedeni sızıntı değil, havuzun her turda yeniden görülmesidir.

Beklenen düzen, k büyüdükçe eğitim kümesinin büyümesi ve kestirimin gerçek sayıya yaklaşmasıdır.
Eğitim satırı sütunu bu büyümeyi gösteriyor: 504'ten 907'ye, sonra 1.007'ye. Ama fark sütunu bu
düzeni izlemiyor. Gerçeğe en yakın kestirim **k=4**'ten geliyor (+0,0060) ve en uzak kestirim
**k=8**'den (+0,0149). Aradaki sıralama, eğitim kümesinin büyüklüğünden çok kat bölünmesinin
rastlantısından çıkıyor. k'yı 4'ten 10'a çıkarmak kestirimi iyileştirmedi, üç kat daha çok eğitim
yaptırdı.

Saçılma sütunu da tek başına okunamaz. k=3'te 0,0014, k=8'de 0,0359 — yaklaşık yirmi altı kat fark.
Üç sayının saçılması da, on sayının saçılması da kendi başına gürültülüdür; saçılma bir belirsizlik
kestirimi değil, katlar arasındaki dağılımın kaba bir özetidir.

Fark sütununun düzensizliği iki eğilimin üst üste binmesinden gelir ve ikisi ters yöne çeker.
Birincisi **karamsarlıktır**: k küçükken model, kullanıma verilecek modelden daha az satırdan
öğrenir. k=2'de eğitim 504 satırdır, yani sonunda kurulacak modelin gördüğü satırın yarısı; ölçülen
sayı o zayıf modelin sayısıdır. İkincisi **iyimserliktir**: havuzun her satırı k turun k−1'inde
eğitimde, birinde ölçümde yer alır ve aynı 1.008 satır baştan sona yeniden kullanılır. k büyüdükçe
birinci eğilim zayıflar, ikincisi olduğu gibi kalır. Toplamları hiçbir k değerinde sıfırlanmıyor ve
hangi k'nın gerçeğe yakın düştüğü önceden bilinemiyor — tablodaki en yakın satırı işaretlemek ancak
sınama kümesi okunduktan sonra olanaklı, o da çapraz doğrulamanın var oluş nedenini ortadan
kaldırıyor.

#### Uçtaki Seçimin Ödediği Bedel

Son satır tek çıkarmalı doğrulamadır: her satır sırayla tek başına ölçüm kümesi olur, model kalan
1.007 satırdan öğrenilir. Kestirim 0,8175, gerçeğin 0,0119 üstünde. Bu sayı k=7 ve k=9'un verdiği
sayıyla **tam olarak aynıdır** ve k=4'ün verdiğinden gerçeğe daha uzaktır.

Maliyeti üç sütunda okunuyor. 1.008 eğitim yapılıyor ve toplam **1.015.056 satır** işleniyor;
k=10'un işlediği 9.072 satırın 111,89 katı. Elde edilen fark, kestirimi 0,0020 oynatmak.

İkinci bedel saçılma sütunundadır: 0,3863. Bu sayı belirsizlik değildir. Tek satırlık bir katta
doğruluk yalnız 0 ya da 1 olabilir, dolayısıyla saçılma zorunlu olarak $\sqrt{p(1-p)}$ değerine
oturur; burada $p = 0{,}8175$ ve $\sqrt{0{,}8175 \times 0{,}1825} = 0{,}3863$. Sayı, modelin
kararlılığı hakkında hiçbir şey söylemez; yalnız ortalamanın kendisini yeniden yazar.

Üçüncü bedel son sütundadır: **iki sınıflı kat sayısı sıfır**. Tek satırlık bir kat tek bir etiket
taşır, dolayısıyla o katta doğru olumlu, yanlış olumlu ve yanlış olumsuz sayımlarının hepsi birden
bulunamaz. Kesinlik, duyarlılık, F1 ölçütü ve eğri altındaki alan **kat başına hesaplanamaz**.
Kat sayısı 2 ile 10 arasındayken her kat iki sınıfı da taşıyor ve bu ölçütlerin hepsi kat başına
hesaplanabiliyor. Doğruluk dışında bir ölçütle çalışılıyorsa tek çıkarmalı doğrulama seçenek
olmaktan çıkar.

#### Karıştırmaya Bağlılık

Kat sayısının etkisini ölçerken bir şey sabit tutuldu: havuzun sırası. Kat ataması sıra numarasına
bağlı olduğuna göre, havuz başka türlü karıştırılsaydı kestirim de değişirdi. Aşağıda aynı k
değerleri üç ayrı karıştırmayla yeniden ölçülüyor.

```python
print(f"{'k':>5} " + " ".join(f"{'tohum ' + str(i + 1):>9}" for i in range(3))
      + f" {'sacilma':>9}")
for k in (2, 4, 10, 1008):
    m = []
    for i in range(3):
        h = karistir(HAVUZ, TOHUM + 300 + i * 17)
        m.append(ortalama([q[0] for q in kat_dogrulama(h, k)]))
    print(f"{k:>5} " + " ".join(f"{q:>9.4f}" for q in m) + f" {sacilma(m):>9.4f}")
```

```
    k   tohum 1   tohum 2   tohum 3   sacilma
    2    0.8145    0.8175    0.8125    0.0020
    4    0.8194    0.8194    0.8185    0.0005
   10    0.8194    0.8164    0.8164    0.0014
 1008    0.8175    0.8175    0.8175    0.0000
```

Son satırdaki sıfır tek çıkarmalı doğrulamanın gerçek üstünlüğüdür. Katlar tek satır olduğu için
karıştırma katları değiştirmez; aynı 1.008 kat, aynı sırayla olmasa da aynı kümedir. Kestirim
**tamamen belirlenimlidir** ve tohum seçimi sonucu oynatamaz. k=2 ile aynı ölçüm üç karıştırmada
0,8125 ile 0,8175 arasında geziniyor, saçılma 0,0020.

Bu, tek çıkarmalı doğrulamanın hangi durumda ödediğine değdiğini de söyler: veri azken, tek bir
karıştırmanın kestirimi oynatma payı büyürken ve ölçüt doğruluk gibi kat başına tek satırdan
toplanabilen bir sayıyken. Havuz 1.008 satırken bu koşulların hiçbiri sağlanmıyor ve k=4 ile k=10
arasında bir seçim, on binlerce kat daha ucuza aynı kestirimi veriyor.

Karıştırma saçılmalarının büyüklüğü aynı zamanda bir uyarıdır: k=2'nin karıştırmaya bağlı 0,0020'lik
oynaması, önceki derste iki doğrusal model arasında ölçülen 0,0326'lık farkın on beşte biri kadardır.
Çapraz doğrulamayla iki modeli karşılaştırırken aradaki farkın bu oynamadan büyük olması gerekir.

#### Özet

- Kat sayısı 2'den 10'a süpürüldüğünde kestirim 0,8115 ile 0,8204 arasında kalıyor; toplam oynama
  0,0089 ve dokuz değerin hepsi gerçek sayının (0,8056) üstünde.
- Kestirim k ile düzenli iyileşmiyor: gerçeğe en yakın sayıyı k=4 (+0,0060), en uzağı k=8 (+0,0149)
  veriyor; eğitim kümesi büyümesine karşın sıralama kat bölünmesinin rastlantısından çıkıyor.
- Tek çıkarmalı doğrulama 0,8175 veriyor ve 1.008 eğitimle 1.015.056 satır işliyor; k=10'un 111,89
  katı iş karşılığında kestirimi 0,0020 oynatıyor.
- Tek çıkarmalı doğrulamanın kat başına saçılması 0,3863'tür ve bu sayı belirsizlik değil
  $\sqrt{p(1-p)}$ değeridir; ayrıca 1.008 katın hiçbiri iki sınıf birden taşımadığı için kesinlik,
  duyarlılık ve eğri altındaki alan kat başına hesaplanamaz.
- Tek çıkarmalı doğrulamanın gerçek üstünlüğü belirlenimli olmasıdır: üç ayrı karıştırmada saçılması
  tam sıfır, k=2'ninki 0,0020.

#### Sonraki Adım

Bu derste denenen aday sayısı birdir. Model tek bir biçimde kuruldu, hiçbir ayar değişkeni
süpürülmedi ve kat sayısı bir ayar değil ölçme yordamının parçası olarak ele alındı. Kursun kuralı
henüz ödenmedi: bir modelin sayısı, o sayıyı bulmak için kaç aday denendiği yazılmadan okunamaz.
Sonraki ders bu hesabı yapar — aynı bütçeyle üç arama yordamı karşılaştırılır ve aday sayısı 1'den
200'e çıkarken doğrulama kümesindeki en iyi sayı ile aynı adayın sınama kümesindeki sayısı arasında
açılan fark basılır. Aile değiştirmenin kattığı fark ile bütçenin şişirdiği fark orada yan yana durur.

### Ayar Değişkeni Araması

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/hiperparametre-arama

Kursun kuralının ödendiği ders: aynı 50 adaylık bütçeyle ızgara, rastgele ve model temelli aramanın bulduğu en iyi aday ve o adayın sınama kümesindeki sayısı yan yana basılır. Aday sayısı 1'den 200'e çıkarken doğrulama kümesindeki en iyi sayı 0,7937'den 0,8452'ye tırmanıyor, aynı adayın sınama kümesindeki sayısı 0,7778'den 0,7976'ya gidiyor ve aradaki şişme +0,0159'dan +0,0476'ya açılıyor. Ayarsız varsayılan aday doğrulama kümesinde zaten 0,8452 veriyor, yani 200 aday hiçbir şey bulmadı. Bütçe 50 ve 200'de tek bölmeli karar kuralı doğrusal modeli sınama kümesinde geçiyor: aile farkı −0,0159 ve −0,0040 iken bütçenin şişirdiği fark +0,0516 ve +0,0476.

Önceki ders tek bir modeli ölçtü ve denenen aday sayısı birdi. Kursun kuralı ise şudur: bir modelin
sayısı, o sayıyı bulmak için kaç aday denendiği yazılmadan okunamaz; ayar bütçesi yazılmayan sayı
ölçülmemiş sayılır. Bu ders o kuralın ödendiği yerdir.

Ölçü iki parçalıdır. Birincisi **aramanın kendisidir**: aynı uzay ve aynı bütçe verildiğinde üç
yordam — ızgara, rastgele ve model temelli — hangi adayı buluyor. İkincisi **bütçenin
şişirdiğidir**: aday sayısı 1'den 200'e çıkarken doğrulamadaki en iyi sayı ile o adayın sınama
sayısı arasında ne açılıyor. Bu şişme sonunda aile değiştirmenin kattığı farkla yan yana konur.

- **MD50.** Ayar uzayı dört eksenlidir: ceza katsayısı ($10^{-3}$–$10^{3}$, log ölçekte sürekli),
  karar eşiği (0,20–0,60 sürekli), polinom derecesi (1 ya da 2) ve sabit bir sıralamadan alınan
  sütun sayısı (3, 6 ya da 9). Üç arama da **aynı** uzayda çalışır.
- **MD51.** Aday seçimi **yalnız doğrulama kümesinden** okunur (252 satır); sınama kümesi hiçbir
  seçime girmez ve yalnız seçilmiş adayın sayısını bildirmek için okunur.
- **MD52.** Bütçe birimi **eğitilen ve doğrulama kümesinde ölçülen aday sayısıdır**; model temelli
  aramanın vekil model üzerinde denediği öneriler eğitim gerektirmez ve sayılmaz. Izgara arama
  ayrık eksenlerin çarpımı (2 derece × 3 sütun = 6) bütçeyi böldüğü için verilen bütçeyi **tam
  dolduramaz**.
- **MD53.** Model temelli arama, denenmiş adayların doğrulama sayılarından bir **vekil model**
  kurar: uzaklık ağırlıklı ortalama artı en yakın denenmiş adaya uzaklıkla orantılı bir keşif payı.
  Bu da bir modeldir.
- **MD54.** İkinci aile **tek bölmeli karar kuralıdır**: bir sütun, bir eşik, bir yön. Doğrusal
  modelden yapıca daha zayıftır ve aynı bütçeyle aranır.
- **MD55.** Taban çizgisi K01'den sürer: en sık sınıfı söyleyen yordam, sınama kümesinde 0,7579.
  Aday sayısı ders sonunda basılır ve yinelenen adaylar bir kez sayılır.

#### Aynı Uzay, Aynı Bütçe, Üç Arama

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir;
# model, 0/1 etikete en kucuk karelerle uydurulan dogrusal bir siniflandiricidir.
import math

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 = {"no": k["no"], "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_ad": k["bolge"][0]}
    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:]
SIRA = ["ort_tuketim", "sifir_okuma", "oynaklik", "hane", "memnuniyet", "donem",
        "b_dogu", "b_bati", "b_guney"]
KARE = {"ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem"}
SUTUN = (3, 6, 9)


def alanlar(j, derece):
    a = SIRA[:j]
    return a + ([q + "^2" for q in a if q in KARE] if derece == 2 else [])


def deger(x, ad):
    return float(x[ad[:-2]]) ** 2 if ad.endswith("^2") else float(x[ad])


def coz(A, b):
    n = len(A)
    M = [list(A[i]) + [b[i]] for i in range(n)]
    for i in range(n):
        p = max(range(i, n), key=lambda r: abs(M[r][i]))
        M[i], M[p] = M[p], M[i]
        for r in range(n):
            if r != i:
                f = M[r][i] / M[i][i]
                for c in range(i, n + 1):
                    M[r][c] -= f * M[i][c]
    return [M[i][n] / M[i][i] for i in range(n)]


def ogren(s, alan, ceza):
    d = len(alan) + 1
    A = [[0.0] * d for _ in range(d)]
    b = [0.0] * d
    for x in s:
        v = [1.0] + [deger(x, a) for a in alan]
        for i in range(d):
            b[i] += v[i] * x["supheli"]
            for j in range(i, d):
                A[i][j] += v[i] * v[j]
    for i in range(d):
        for j in range(i):
            A[i][j] = A[j][i]
        if i:
            A[i][i] += ceza
    return coz(A, b)


def dogruluk(w, alan, esik, s):
    d = 0
    for x in s:
        p = w[0] + sum(w[i + 1] * deger(x, a) for i, a in enumerate(alan))
        d += (1 if p >= esik else 0) == x["supheli"]
    return d / len(s)


ONBELLEK, DENENEN = {}, set()


def olc(aday):
    ceza, esik, derece, j = aday
    k = (round(ceza, 9), derece, j)
    if k not in ONBELLEK:
        ONBELLEK[k] = (ogren(EGT, alanlar(j, derece), ceza), alanlar(j, derece))
    DENENEN.add(("dogrusal", k, round(esik, 9)))
    w, alan = ONBELLEK[k]
    return dogruluk(w, alan, esik, DOG), dogruluk(w, alan, esik, SIN)


def en_iyi(adaylar, olcum):
    e = None
    for a in adaylar:
        d, s = olcum(a)
        if e is None or d > e[0]:
            e = (d, s, a)
    return e


c = int(sum(x["supheli"] for x in EGT) * 2 > len(EGT))
TABAN = sum(x["supheli"] == c for x in SIN) / len(SIN)
VD, VS = olc((1.0, 0.5, 1, 9))
print(f"taban cizgisi {TABAN:.4f}")
print(f"ayarsiz varsayilan aday: dogrulama {VD:.4f}, sinama {VS:.4f}")
```

```
taban cizgisi 0.7579
ayarsiz varsayilan aday: dogrulama 0.8452, sinama 0.7976
```

Ayarsız varsayılan aday — ceza 1, eşik 0,5, derece 1, dokuz sütun — doğrulama kümesinde 0,8452
veriyor. Aşağıdaki üç arama ve 200 adaylık süpürme bu sayıyı geçmeye çalışacak.

```python
def rastgele_aday(r):
    return (10 ** (-3 + 6 * r()), 0.20 + 0.40 * r(),
            1 + int(r() * 2), SUTUN[int(r() * 3)])


def izgara(butce):
    en = (1, 1)
    for nc in range(1, butce + 1):
        for ne in range(1, butce + 1):
            if nc * ne * 6 <= butce and (nc * ne, -abs(nc - ne), nc) > \
                    (en[0] * en[1], -abs(en[0] - en[1]), en[0]):
                en = (nc, ne)
    nc, ne = en
    return [(10 ** (-3 + 6 * (i + 0.5) / nc), 0.20 + 0.40 * (t + 0.5) / ne, derece, j)
            for i in range(nc) for t in range(ne) for derece in (1, 2) for j in SUTUN]


def rastgele(butce, tohum=TOHUM + 4100):
    r = uretec(tohum)
    return [rastgele_aday(r) for _ in range(butce)]


def uzaklik(a, b):
    return (((math.log10(a[0]) - math.log10(b[0])) / 6) ** 2
            + ((a[1] - b[1]) / 0.4) ** 2 + (a[2] - b[2]) ** 2
            + ((a[3] - b[3]) / 6) ** 2)


def model_temelli(butce, tohum=TOHUM + 4200, ilk=8, oneri=40):
    r, gecmis = uretec(tohum), []
    for _ in range(min(ilk, butce)):
        a = rastgele_aday(r)
        gecmis.append((a, olc(a)[0]))
    while len(gecmis) < butce:
        en, ena = None, None
        for _ in range(oneri):
            a = rastgele_aday(r)
            w = [1.0 / (uzaklik(a, g) + 0.01) for g, _ in gecmis]
            t = (sum(q * s for q, (_, s) in zip(w, gecmis)) / sum(w)
                 + 0.02 * min(uzaklik(a, g) for g, _ in gecmis) ** 0.5)
            if en is None or t > en:
                en, ena = t, a
        gecmis.append((ena, olc(ena)[0]))
    return [g for g, _ in gecmis]


print(f"{'arama':<16} {'aday':>5} {'dogrulama':>10} {'sinama':>8} {'sisme':>8}  ayar")
for ad, fn in (("izgara", izgara), ("rastgele", rastgele),
               ("model temelli", model_temelli)):
    A = fn(50)
    d, s, a = en_iyi(A, olc)
    print(f"{ad:<16} {len(A):>5} {d:>10.4f} {s:>8.4f} {d - s:>+8.4f}  "
          f"ceza {a[0]:.3g}, esik {a[1]:.3f}, derece {a[2]}, sutun {a[3]}")

print(f"\n{'butce':>6} {'dogrulama':>10} {'sinama':>8} {'sisme':>8}")
R = rastgele(200)
for b in (1, 2, 5, 10, 20, 50, 100, 200):
    d, s, a = en_iyi(R[:b], olc)
    print(f"{b:>6} {d:>10.4f} {s:>8.4f} {d - s:>+8.4f}")
```

```
arama             aday  dogrulama   sinama    sisme  ayar
izgara              48     0.8452   0.7937  +0.0516  ceza 5.62, esik 0.500, derece 2, sutun 9
rastgele            50     0.8373   0.7857  +0.0516  ceza 0.00481, esik 0.495, derece 1, sutun 9
model temelli       50     0.8452   0.8016  +0.0437  ceza 0.00103, esik 0.540, derece 1, sutun 9

 butce  dogrulama   sinama    sisme
     1     0.7937   0.7778  +0.0159
     2     0.7937   0.7778  +0.0159
     5     0.8254   0.8056  +0.0198
    10     0.8294   0.7976  +0.0317
    20     0.8294   0.7976  +0.0317
    50     0.8373   0.7857  +0.0516
   100     0.8373   0.7857  +0.0516
   200     0.8452   0.7976  +0.0476
```

Birinci tabloda ızgara 50 bütçeye karşılık **48** aday kullanabiliyor. Ayrık eksenlerin çarpımı
altıdır, sürekli eksenlere ancak 8 nokta düşer ve bunlar 4×2 dağılır: ceza ekseninde dört değer,
yani altı büyüklük mertebesi dört noktayla temsil ediliyor. Rastgele arama aynı bütçeyle o eksende
50 ayrı değer deniyor.

Üç aramanın sınama sayıları 0,7937, 0,7857 ve 0,8016 — doğrulama sıralamasıyla aynı değil. Model
temelli arama iki kümede de önde ve şişmesi en küçük (+0,0437), ama üçü arasındaki sınama farkı
0,0159, yani dört satır; bu büyüklükteki bir farkı aramaların niteliğine bağlamak için yeterli
kanıt yok.

Asıl bulgu ikinci tablodadır. Doğrulama sütunu **tanım gereği artıyor**: bütçe büyüdükçe en iyi
sayı büyüyen bir kümenin en büyüğüdür ve küçülemez. 0,7937'den 0,8452'ye, toplam +0,0515. Sınama
sütunu bu tırmanışı izlemiyor: 0,7778'den 0,7976'ya, toplam +0,0198. Tırmanışın **yüzde 62'si**
sınama kümesine hiç geçmiyor ve şişme sütununda +0,0159'dan +0,0476'ya açılıyor.

Nedeni doğrulama kümesinin 252 satır olmasıdır. Her satır 0,0040 ediyor; iki aday arasındaki gerçek
fark bu çözünürlüğün altındayken kazananı hangi satırların o kümeye düştüğü belirliyor. 200 aday
denendiğinde en yüksek sayıyı alan aday en iyi olan değil, **doğrulama kümesinin gürültüsüne en iyi
denk gelendir** — M26/K05'te ölçülen seçim yanlılığının ayar uzayındaki biçimi.

Karşılaştırma noktası açılış bloğundadır: ayarsız varsayılan aday doğrulama kümesinde zaten 0,8452
veriyordu. 200 aday bu sayıyı **geçemedi** ve ona eriştiği yerde sınama kümesinde de aynı noktada
(0,7976) durdu. Arama, 200 eğitim karşılığında sıfır kazanç üretti; bunu ancak sınama kümesi
okunduğunda söyleyebiliyoruz.

#### Ailenin Kattığı ile Bütçenin Şişirdiği

Kursun ikinci iddiası burada sayıya döner: aile değiştirmenin kattığı fark çoğu zaman ayar
bütçesinin şişirdiği farktan küçüktür. İkinci aile yapıca daha zayıftır — tek bölmeli bir karar
kuralı: bir sütun, bir eşik, bir yön.

```python
ESIK_TABLO = {}
for a in SIRA:
    d = sorted({x[a] for x in EGT})
    ESIK_TABLO[a] = (d[1:] if len(d) < 12
                     else [d[int(i * len(d) / 12)] for i in range(1, 12)])


def gudug_olc(aday):
    a, v, yon = aday
    DENENEN.add(("gudug",) + aday)

    def tah(x):
        return int((x[a] >= v) == (yon == 1))
    return (sum(tah(x) == x["supheli"] for x in DOG) / len(DOG),
            sum(tah(x) == x["supheli"] for x in SIN) / len(SIN))


def gudug_rastgele(butce, tohum=TOHUM + 4300):
    r, out = uretec(tohum), []
    for _ in range(butce):
        a = SIRA[int(r() * len(SIRA))]
        out.append((a, ESIK_TABLO[a][int(r() * len(ESIK_TABLO[a]))], 1 + int(r() * 2)))
    return out


print(f"{'butce':>6} {'gudug sin':>10} {'dogrusal sin':>13} {'aile farki':>11} "
      f"{'butce sismesi':>14}")
for b in (1, 10, 50, 200):
    gd, gs, ga = en_iyi(gudug_rastgele(b), gudug_olc)
    dd, ds, da = en_iyi(R[:b], olc)
    print(f"{b:>6} {gs:>10.4f} {ds:>13.4f} {ds - gs:>+11.4f} {dd - ds:>+14.4f}")
gd, gs, ga = en_iyi(gudug_rastgele(200), gudug_olc)
print(f"\ngudug butce 200: tek bolme '{ga[0]}', dogrulama {gd:.4f}, sinama {gs:.4f}")
print(f"bu derste denenen aday sayisi {len(DENENEN)}")
```

```
 butce  gudug sin  dogrusal sin  aile farki  butce sismesi
     1     0.7659        0.7778     +0.0119        +0.0159
    10     0.7381        0.7976     +0.0595        +0.0317
    50     0.8016        0.7857     -0.0159        +0.0516
   200     0.8016        0.7976     -0.0040        +0.0476

gudug butce 200: tek bolme 'ort_tuketim', dogrulama 0.8254, sinama 0.8016
bu derste denenen aday sayisi 352
```

Aile farkı sütunu bütçe boyunca işaret değiştiriyor: bütçe 1'de +0,0119, bütçe 10'da +0,0595,
bütçe 50'de **eksiye** dönüyor (−0,0159) ve bütçe 200'de eksi kalıyor (−0,0040). Yapıca daha güçlü
aile, iki bütçede yapıca daha zayıf aileyi sınama kümesinde **geçemedi**. Bütçe 10'daki 0,0595 ise
güdüğün rastlantıyla kötü bir adaya takılmasından geliyor; orada güdük 0,7381 ile tabanın altında.

Son sütun aynı satırlarda bütçenin şişirdiği farkı taşıyor: +0,0159, +0,0317, +0,0516, +0,0476.
Bütçe 200'de aile farkı −0,0040 iken bütçe şişmesi +0,0476 — şişme, aile farkının **on iki katına
yakın** ve ters yönde. Modelin ailesini değiştirmek bu kümede sayıyı oynatmadı; onu oynatan şey
kaç aday denendiğiydi ve o oynama gerçek değildi.

Güdüğün bulduğu kural tek bir sütuna dayanıyor: ortalama tüketim üzerinde tek bir bölme, sınama
kümesinde 0,8016. Bu dersin dokuz sütunlu, cezalı doğrusal modelleri tek bir eşiği geçemedi.

Denenen aday sayısı **352**'dir ve verilen bütçelerin toplamından küçüktür. Doğrusal ailede 299
ayrı aday var: bir varsayılan aday, üç aramanın 148 adayı ve bütçe süpürmesinin eklediği 150 aday.
Güdük ailesinde ise bütçe 200 olmasına karşın 53 ayrı aday var, çünkü o ailenin bütün ayar uzayında
yalnız 74 nokta bulunuyor (37 eşik, iki yön). Bütçe 50 ile 200'ün aynı sayıyı vermesinin nedeni
budur: uzay doymuştur ve bütçeyi büyütmek yeni aday üretmez. Bir ailenin ayar bütçesi, uzayının
büyüklüğü yazılmadan okunamaz.

Rapor edilen sayı, bütçe 200'de seçilen adayın sınama kümesindeki sayısıdır: **0,7976**, tabanın
0,0397 üstünde.

#### Özet

- Aynı 50 adaylık bütçeyle ızgara ancak 48 aday kullanabiliyor (ayrık eksenlerin çarpımı bütçeyi
  böler); üç aramanın sınama sayıları 0,7937, 0,7857 ve 0,8016.
- Bütçe 1'den 200'e çıkarken doğrulama kümesindeki en iyi sayı 0,7937'den 0,8452'ye tanım gereği
  tırmanıyor, aynı adayın sınama sayısı 0,7778'den 0,7976'ya gidiyor ve şişme +0,0159'dan
  +0,0476'ya açılıyor.
- Ayarsız varsayılan aday doğrulama kümesinde zaten 0,8452 veriyor; 200 aday bu sayıyı geçemedi ve
  arama harcanan bütçe karşılığında sıfır kazanç üretti.
- Bütçe 50 ve 200'de tek bölmeli karar kuralı doğrusal modeli sınama kümesinde geçiyor: aile farkı
  −0,0159 ve −0,0040 iken bütçe şişmesi +0,0516 ve +0,0476, yani şişme aile farkının on iki katına
  yakın.
- Bu derste 352 ayrı aday denendi (doğrusal ailede 299, güdük ailesinde 53); güdük ailesinin
  bütçesi 200'dü ama uzayında yalnız 74 nokta olduğu için arama doydu ve bütçeyi büyütmek yeni aday
  üretmedi. Rapor edilen sayı bütçe 200'de seçilen adayın sınama sayısıdır (0,7976).

#### Sonraki Adım

Bu dersin bütün sayıları tek bir orandır: 252 satırın kaçında doğru karar verildiği. O oranın
paydasındaki satırların hangi bölgelerden, hangi tarife basamaklarından, hangi hane
büyüklüklerinden ve kaç okumadan geldiği hiç sorulmadı. Sonraki ders hatayı bu dilimlere bölerek
soruyu sorar ve dilim sayısı arttıkça bulunan kötü dilim sayısının rastlantıyla da büyüdüğünü
ölçer.

### Hata Çözümlemesi

Source: https://academia.sh/tr/kurslar/denetimli-ogrenme/hata-cozumlemesi

Sınama kümesindeki 51 hatanın bölge, tarife basamağı, hane büyüklüğü ve okuma sayısına göre kırılması: on dört ölçülebilir dilimin ikisinde model taban çizgisinin altında kalıyor ve merkez bölgesi satırların yüzde 23'ünü tutarken hataların yüzde 35,3'ünü taşıyor. Dilim sayısı 14'ten 41'e, 41'den 50'ye çıkarken bulunan kötü dilim sayısı 2'den 6'ya, 6'dan 9'a çıkıyor — ama aynı hatalar satırlara rastgele dağıtıldığında ortalama 4,41, 14,26 ve 18,46 kötü dilim çıkıyor, yani gözlenen sayı her üç kümede de rastlantının altında. Kurs kapanışı yirmi dersin ailesini, aday sayısını ve taban çizgisi üstünü tek tabloda toplar.

Önceki ders bir sayıyla bitti: sınama kümesinin 252 satırının 201'inde doğru karar, 51'inde
yanlış. O 51 satır kurs boyunca tek bir orana sıkıştırıldı; hangi bölgelerden, hangi tarife
basamaklarından, hangi hane büyüklüklerinden ve kaç okumadan geldikleri sorulmadı.

Hata çözümlemesi bu soruyu sorar: hata kümeye eşit mi dağılmış, yoksa bir **dilimde** mi
yoğunlaşmış. Sorunun bir tuzağı var ve bu ders onu da ölçer: yeterince çok dilim tanımlanırsa
bir kısmı **rastlantıyla** kötü görünür.

- **MD56.** Model, önceki dersin seçtiği biçimdedir: derece 1, dokuz sütun. Ceza ve eşik aynı
  tohumla, 200 adaylık aramayla ve **yalnız doğrulama kümesinden** seçilir. Çözümleme sınama
  kümesinde yapılır ve buradan hiçbir seçim yapılmaz.
- **MD57.** Dilim eksenleri dörttür: bölge (beş değer), tarife basamağı (10, 25 ve 40 m³
  sınırlarıyla dört aralık), hane büyüklüğü (1–2, 3–4, 5–6) ve okuma sayısı (1, 2, 3). Bir dilim
  **en az 10 satır** taşımıyorsa ölçülemez sayılır; eşik ölçümden önce sabitlenmiştir.
- **MD58.** Dilim içindeki taban, kursun tabanıyla aynı yordamdır: eğitim kümesinin en sık sınıfını
  söylemek. Sınıf bileşimi değiştiği için taban dilimden dilime değişir.
- **MD59.** Rastlantı ölçüsü, doğru/yanlış kararların satırlar arasında **yeniden
  karıştırılmasıdır**; yordam M26/K05'te kuruldu ve tekrarlanmaz.
- **MD60.** Bu ders dilimlerde **başarım farkını** ölçer. Farkın kime ne yaptığı ve modelin kimin
  için kurulduğu M27/K08'e bırakılır.

#### Hata Nerede Yoğunlaşıyor

```python
# ORTAK — MODELDIR. Kume ve model M27/K01'in KURGU abone tablosundan gelir;
# ayar, onceki dersin 200 adaylik aramasiyla ve yalniz dogrulama kumesinden secilir.
import math

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 = {"no": k["no"], "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_ad": k["bolge"][0]}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)


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


K = karistir(VERI, TOHUM + 90000)
EGT, DOG, SIN = K[:756], K[756:1008], K[1008:]
ALAN = ["ort_tuketim", "sifir_okuma", "oynaklik", "hane", "memnuniyet", "donem",
        "b_dogu", "b_bati", "b_guney"]


def coz(A, b):
    n = len(A)
    M = [list(A[i]) + [b[i]] for i in range(n)]
    for i in range(n):
        p = max(range(i, n), key=lambda r: abs(M[r][i]))
        M[i], M[p] = M[p], M[i]
        for r in range(n):
            if r != i:
                f = M[r][i] / M[i][i]
                for c in range(i, n + 1):
                    M[r][c] -= f * M[i][c]
    return [M[i][n] / M[i][i] for i in range(n)]


def ogren(s, ceza):
    d = len(ALAN) + 1
    A = [[0.0] * d for _ in range(d)]
    b = [0.0] * d
    for x in s:
        v = [1.0] + [float(x[a]) for a in ALAN]
        for i in range(d):
            b[i] += v[i] * x["supheli"]
            for j in range(i, d):
                A[i][j] += v[i] * v[j]
    for i in range(d):
        for j in range(i):
            A[i][j] = A[j][i]
        if i:
            A[i][i] += ceza
    return coz(A, b)


ONBELLEK = {}


def kararlar(aday, kume):
    ceza, esik = aday
    if ceza not in ONBELLEK:
        ONBELLEK[ceza] = ogren(EGT, ceza)
    w = ONBELLEK[ceza]
    return [1 if w[0] + sum(w[i + 1] * float(x[a]) for i, a in enumerate(ALAN))
            >= esik else 0 for x in kume]


r = uretec(TOHUM + 4100)
ADAYLAR = [(10 ** (-3 + 6 * r()), 0.20 + 0.40 * r(), r(), r()) for _ in range(200)]
SECIM, EN = None, -1.0
for a in ADAYLAR:
    d = sum(k == x["supheli"] for k, x in zip(kararlar(a[:2], DOG), DOG)) / len(DOG)
    if d > EN:
        EN, SECIM = d, a[:2]

C = int(sum(x["supheli"] for x in EGT) * 2 > len(EGT))
DOGRU = [k == x["supheli"] for k, x in zip(kararlar(SECIM, SIN), SIN)]
TABAN = [C == x["supheli"] for x in SIN]
G = sum(DOGRU) / len(SIN)
print(f"secilen aday: ceza {SECIM[0]:.4g}, esik {SECIM[1]:.4f}, dogrulama {EN:.4f}")
print(f"sinama kumesi {len(SIN)} satir, model {G:.4f}, "
      f"taban {sum(TABAN) / len(SIN):.4f}, hata {len(SIN) - sum(DOGRU)}")
```

```
secilen aday: ceza 0.00372, esik 0.5540, dogrulama 0.8452
sinama kumesi 252 satir, model 0.7976, taban 0.7579, hata 51
```

Aşağıda 51 hata dört eksende kırılıyor. Modelin sayısı, dilimdeki taban, farkı ve iki pay
basılıyor: **hata payı** (hataların kaçı orada) ve **satır payı** (satırların kaçı orada). İki pay
ayrıştığında hata yoğunlaşmıştır.

```python
EKSEN = {"bolge": lambda x: x["bolge_ad"],
         "hane": lambda x: ("1-2", "3-4", "5-6")[min(2, (x["hane"] - 1) // 2)],
         "okuma": lambda x: str(x["donem"]),
         "tarife": lambda x: ("<10", "10-25", "25-40", ">=40")[
             0 if x["ort_tuketim"] < 10 else 1 if x["ort_tuketim"] < 25
             else 2 if x["ort_tuketim"] < 40 else 3]}
ENAZ = 10


def dilimle(kume):
    d = {}
    for eksenler in kume:
        for i, x in enumerate(SIN):
            d.setdefault((eksenler, tuple(EKSEN[e](x) for e in eksenler)), []).append(i)
    return d


TEK = [("bolge",), ("tarife",), ("hane",), ("okuma",)]
H = len(SIN) - sum(DOGRU)
print(f"{'eksen':<7} {'dilim':<7} {'n':>4} {'model':>7} {'taban':>7} {'fark':>8} "
      f"{'hata payi':>10} {'satir payi':>11}")
for k, idx in sorted(dilimle(TEK).items()):
    if len(idx) < ENAZ:
        print(f"{k[0][0]:<7} {k[1][0]:<7} {len(idx):>4}  olculemez ({ENAZ} satir alti)")
        continue
    m = sum(DOGRU[i] for i in idx) / len(idx)
    t = sum(TABAN[i] for i in idx) / len(idx)
    h = sum(1 for i in idx if not DOGRU[i])
    print(f"{k[0][0]:<7} {k[1][0]:<7} {len(idx):>4} {m:>7.4f} {t:>7.4f} {m - t:>+8.4f} "
          f"{h / H:>10.3f} {len(idx) / len(SIN):>11.3f}")
```

```
eksen   dilim      n   model   taban     fark  hata payi  satir payi
bolge   bati      32  1.0000  1.0000  +0.0000      0.000       0.127
bolge   dogu      36  0.6944  0.4722  +0.2222      0.216       0.143
bolge   guney     58  0.8448  0.8276  +0.0172      0.176       0.230
bolge   kuzey     68  0.8088  0.8235  -0.0147      0.255       0.270
bolge   merkez    58  0.6897  0.6552  +0.0345      0.353       0.230
hane    1-2       73  0.8219  0.7534  +0.0685      0.255       0.290
hane    3-4      143  0.7972  0.7692  +0.0280      0.569       0.567
hane    5-6       36  0.7500  0.7222  +0.0278      0.176       0.143
okuma   1         22  0.8636  0.8636  +0.0000      0.059       0.087
okuma   2         63  0.7460  0.7619  -0.0159      0.314       0.250
okuma   3        167  0.8084  0.7425  +0.0659      0.627       0.663
tarife  10-25    194  0.8402  0.8402  +0.0000      0.608       0.770
tarife  25-40     46  0.6522  0.4565  +0.1957      0.314       0.183
tarife  <10       11  0.6364  0.6364  +0.0000      0.078       0.044
tarife  >=40       1  olculemez (10 satir alti)
```

On beş dilimin on dördü ölçülebiliyor; en yüksek tarife basamağında **tek bir abone** var ve o
basamak hakkında hiçbir şey söylenemeyeceği tabloda duruyor.

Fark sütununda **iki eksi** var: kuzey bölgesinde model 0,8088, taban 0,8235; iki okumalı
abonelerde 0,7460'a karşı 0,7619. Genel sayının 0,7976 olması bu iki dilimde hiçbir şey söylemiyor.

Yoğunlaşma iki payın karşılaştırılmasında okunuyor. Merkez bölgesi satırların 0,230'unu tutarken
hataların **0,353'ünü** taşıyor, yani hata payı satır payının bir buçuk katı; 25–40 m³ basamağı
0,183'e karşı 0,314. Batı bölgesi ise satırların 0,127'sini tutuyor ve **hiç hata üretmiyor** —
orada şüpheli etiketli tek bir abone yok, taban 1,0000 ve modelin yapabileceği en iyi şey tabana
eşit kalmak. Doğu bölgesi ile 25–40 m³ basamağı hem tabana göre en çok kazandıran (+0,2222 ve
+0,1957) hem de mutlak olarak **en kötü** olunan yerler (0,6944 ve 0,6522).

#### Dilim Sayısı Büyüdükçe

Eksenler çaprazlanırsa dilim sayısı hızla büyür ve bulunacak "kötü dilim" sayısı da büyür. Sorun,
bu büyümenin ne kadarının gerçek olduğudur. Ölçüsü şudur: doğru ve yanlış kararlar satırlar
arasında yeniden karıştırılır — hata sayısı 51'de sabit kalır, dilim yapısıyla bağı kopar — ve
sayım yinelenir.

```python
IKI = TEK + [("bolge", "tarife"), ("bolge", "okuma"),
             ("tarife", "hane"), ("hane", "okuma")]
UC = IKI + [("bolge", "tarife", "okuma")]


def kotu_say(d, dogru):
    olculur = say = 0
    for idx in d.values():
        if len(idx) < ENAZ:
            continue
        olculur += 1
        if (sum(dogru[i] for i in idx) / len(idx)
                < sum(TABAN[i] for i in idx) / len(idx)):
            say += 1
    return olculur, say


print(f"{'dilim kumesi':<14} {'tanimli':>8} {'olculur':>8} {'kotu':>5} "
      f"{'rastlanti ort':>14} {'rastlanti en':>13} {'taban>model':>12}")
for ad, kume in (("tek eksen", TEK), ("iki eksen", IKI), ("uc eksen", UC)):
    d = dilimle(kume)
    olculur, say = kotu_say(d, DOGRU)
    rast = [kotu_say(d, karistir(DOGRU, TOHUM + 8000 + i))[1] for i in range(200)]
    ust = sum(1 for idx in d.values() if len(idx) >= ENAZ
              and sum(TABAN[i] for i in idx) / len(idx) > G)
    print(f"{ad:<14} {len(d):>8} {olculur:>8} {say:>5} {sum(rast) / len(rast):>14.2f} "
          f"{max(rast):>13} {ust:>12}")

en_kotu = min(((sum(DOGRU[i] for i in idx) / len(idx)
                - sum(TABAN[i] for i in idx) / len(idx), k, len(idx))
               for k, idx in dilimle(IKI).items() if len(idx) >= ENAZ))
print(f"\nen kotu iki eksenli dilim: {en_kotu[1][0]} = {en_kotu[1][1]}, "
      f"{en_kotu[2]} satir, fark {en_kotu[0]:+.4f}")
```

```
dilim kumesi    tanimli  olculur  kotu  rastlanti ort  rastlanti en  taban>model
tek eksen            15       14     2           4.41             6            5
iki eksen            62       41     6          14.26            18           16
uc eksen             92       50     9          18.46            23           21

en kotu iki eksenli dilim: ('bolge', 'tarife') = ('dogu', '10-25'), 17 satir, fark -0.1176
```

Kötü dilim sayısı 2'den 6'ya, 6'dan 9'a çıkıyor. Tek başına okunduğunda bu, dilimleri inceltince
modelin daha çok yerde çuvalladığının kanıtı gibi görünür. Rastlantı sütunları tersini söylüyor:
aynı 51 hata satırlara **rastgele** dağıtıldığında ortalama 4,41, 14,26 ve 18,46 kötü dilim
çıkıyor ve gözlenen sayı her üç kümede de bu ortalamanın **altında**.

Son sütun bunun nereden geldiğini söylüyor. Modelin genel sayısı 0,7976; tabanı bu sayının
üstünde olan bir dilimde, hataları rastgele dağıtılmış bir model tabanın altına düşer. Böyle
dilimler tek eksende 5, iki eksende 16, üç eksende 21 tane ve rastlantı ortalamaları bu sayıların
hemen yanında. "Kötü dilim" ölçütü aslında **tabanı genel başarımdan yüksek dilimleri** sayıyor.

Gözlenenin rastlantının altında kalması modelin lehinedir. Bu, M26/K05'te ölçülen kuralın buradaki
biçimi — bir sayı, aynı sayının rastlantıyla ne sıklıkta bulunacağı ölçülmeden bulgu değildir.
Bulunan farkların kimin için ne anlama geldiği ise M27/K08'e bırakılır.

#### Özet

- On beş dilimin on dördü ölçülebiliyor; en yüksek tarife basamağında tek bir abone var ve o
  basamak hakkında hiçbir şey söylenemiyor.
- İki dilimde model tabanın altında: kuzey bölgesi (0,8088'e karşı 0,8235) ve iki okumalı aboneler
  (0,7460'a karşı 0,7619).
- Merkez bölgesi satırların 0,230'unu tutup hataların 0,353'ünü taşıyor; doğu bölgesi hem tabana
  göre en çok kazandıran (+0,2222) hem de mutlak olarak en kötü olunan (0,6944) dilim.
- Dilim sayısı 14'ten 50'ye çıkarken kötü dilim 2'den 9'a çıkıyor, ama rastgele dağıtılmış hatada
  4,41'den 18,46'ya çıkıyor; gözlenen her kümede rastlantının altında.

#### Kurs Kapanışı

Yirmi dersin hesabı tek tabloda ve her sayı kendi dersinden okundu. İkinci sütun her derste
**doğrulama kümesinden seçilen** adayın sınama kümesindeki sayısıdır. Sınıflandırma satırları
doğruluktur, tabanı 0,7579; bağlanım satırları kök hatadır (m³), tabanı 6,5324.

| Ders | Ailenin sayısı | Denenen aday | Taban çizgisi üstü |
|---|---|---|---|
| Doğrusal 01 | 5,3096 m³ | 1 | +1,2228 |
| Doğrusal 02 | 5,3775 m³ | 8 | +1,1549 |
| Doğrusal 03 | 5,2954 m³ | 18 | +1,2370 |
| Doğrusal 04 | 0,7937 | 9 | +0,0357 |
| Doğrusal 05 | 0,8056 | 8 | +0,0477 |
| Doğrusal olmayan 01 | 0,7937 | 24 | +0,0357 |
| Doğrusal olmayan 02 | 0,7817 | 20 | +0,0238 |
| Doğrusal olmayan 03 | 0,7857 | 17 | +0,0278 |
| Doğrusal olmayan 04 | 0,7857 | 12 | +0,0278 |
| Doğrusal olmayan 05 | 0,7897 | 31 | +0,0317 |
| Doğrusal olmayan 06 | 0,7738 | 1.279 | +0,0159 |
| Doğrusal olmayan 07 | 0,7778 | 3 | +0,0199 |
| Değerlendirme 01 | 0,8016 | 49 | +0,0437 |
| Değerlendirme 02 | 0,8016 | 5 | +0,0437 |
| Değerlendirme 03 | 181 bedel | 99 | −185 bedel |
| Değerlendirme 04 | 0,0242 kalibrasyon | 5 | −0,0044 |
| Değerlendirme 05 | 0,4770 açıklanan değişim | 3 | +0,4770 |
| Değerlendirme 06 | 0,8056 | 1 | +0,0477 |
| Değerlendirme 07 | 0,7976 | 200 | +0,0397 |
| Değerlendirme 08 | 0,7976 | 1 | +0,0397 |

Üç satır kendi biriminde okunur. Değerlendirme 03'ün birimi bedeldir ve küçük olan iyidir: 181
birim, tabanın 366'sının 185 altında. Değerlendirme 04 kalibrasyon hatasıdır ve tablodaki tek eksi
satırdır: sabit sayı söyleyen taban 0,0198 ile modelin 0,0242'sinden iyidir, yani öğrenmiş model
orada öğrenmemiş yordamı geçemedi.

Kursun kuralı üçüncü sütundur: **bir modelin sayısı, o sayıyı bulmak için kaç aday denendiği
yazılmadan okunamaz; ayar bütçesi yazılmayan sayı ölçülmemiş sayılır.** Sütun toplanınca kursta
denenen aday sayısı çıkıyor: **1.793**.

Kursun ikinci iddiası tablodan okunur. Sınıflandırma satırlarının en iyisi 0,8056, en kötüsü
0,7738: yedi ailenin tamamı **0,0318 puanlık** bir bandın içinde. Ayar bütçesinin şişirdiği fark
bu bandın büyüklüğündedir — Değerlendirme 07'de bütçe 200'de doğrulama ile sınama arasında
**0,0476**, tablonun en yüksek bütçesi olan 1.279 adaylık derste iki seçim arasında **0,0318**.
**Yedi aile arasındaki bütün fark, tek bir dersin ayar bütçesinin ürettiği farka eşittir.**

Yirmi derste yedi model ailesi denendi ve her sayının yanına bütçesi yazıldı. Ama bütün bu
ölçümlerin ortak bir dayanağı vardı: **etiket**. Taban çizgisi en sık etiketi söylüyordu, ölçüt
tahmin edilen etiketle gerçeğini karşılaştırıyordu, arama doğrulama kümesindeki etiketlere
bakıyordu. Etiketin olmadığı bir kümede neyin öğrenildiği — hatta bir şey öğrenilip öğrenilmediği —
**hangi tabana** karşı okunur, hiç sorulmadı. Sonraki kurs, Denetimsiz Öğrenme, bu soruyla açılır.

