---
title: 'Model İzleme ve Yönetişim'
source: 'https://academia.sh/tr/kurslar/model-izleme'
language: tr
updated: '2026-08-17T04:40:17+00:00'
license: 'CC BY-SA 4.0'
lessons: 13
outcomes:
    - 'Veri kayması ile kavram kaymasını ayrı ayrı ölçer ve hangisinin başarımı gerçekten bozduğunu gösterir'
    - 'Gecikmeli etiket altında vekil ölçütlerin neyi görüp neyi kaçırdığını sayar ve uyarı eşiğini gerekçelendirir'
    - 'Yayın kademelendirmesi, yeniden eğitim politikası ve emekliye ayırma kararlarını denetlenebilir biçimde kurar'
---

# Model İzleme ve Yönetişim

Üretime çıkmış bir modelin çıktıktan sonraki ömrünün sayılması: girdi dağılımı dönem dönem kayarken tahmin payının 0,4550'den 0,5450'ye çıkması ama isabetin hiç düşmemesi, kural değiştiğinde girdi özetinin hiç değişmemesine karşın isabetin 0,9700'den 0,8150'ye çökmesi, gecikmeli etiket yüzünden tek gören ölçünün iki dönem sonra gelmesi, etiket yokken kullanılan vekil ölçütlerin zararsız kaymada ateşleyip zararlı kaymada susması, uyarı eşiği daraldıkça artan yanlış ateşleme, üretimden etiket toplamanın kapsadığı pay, gölge ve kanarya dağıtımında yeni modele giden isteğin sonucu ne kadar oynattığı, çevrimiçi deneyin ayırt edebildiği en küçük fark, yeniden eğitim tetikleyicisinin kural değişmediğinde hiçbir şey kazandırmaması, karar kayıtlarının bir tahminin nedenini bulunabilir kılması, uyumluluk yükümlülüklerinin sınıf olarak karşılığı, eğitim ile çıkarım maliyetinin ayrı izlenmesi ve modelin emekliye ayrılmasının hangi ölçüyle kararlaştırıldığı.

## İzleme

Üretimdeki bir modelin bozulup bozulmadığının hangi ölçüyle ve kaç dönem sonra görüldüğünün sayılması: girdi dağılımı dönem başına büyürken girdi özetinin 25,9220'den 27,0472'ye çıkması ve hiçbir uyarı bandında ateşlememesi, aynı kaymada tahmin payının 0,4550'den 0,5450'ye çıkıp 0,05 bandında üç dönem ateşlemesine karşın isabetin 0,9700'den 0,9800'e giderek hiç düşmemesi, kural değiştiğinde girdi özetinin hiç değişmemesi ve tahmin payı neredeyse sabit kalırken isabetin 0,9700'den 0,8150'ye çökmesi, gecikmeli etiket yüzünden bir dönemin isabetinin ancak iki dönem sonra bilinmesi ve bozulmanın olduğu an ile görüldüğü an arasındaki iki dönemlik boşluk, etiket yokken kullanılan vekil ölçütlerin zararsız kaymada ateşleyip zararlı kaymada susması, uyarı bandı daraldıkça artan ateşleme sayısı ile bunların kaçının gerçek bozulmaya karşılık geldiği, üretimden etiket toplamanın kapsadığı pay ve toplanan etiketin yanlılığı.

### Veri Kayması

Source: https://academia.sh/tr/kurslar/model-izleme/veri-kaymasi

Üretimdeki girdi dağılımının değişmesinin ölçülmesi ve izlenebilir bir modelin üç koşulunun kurulması: karar eşiği etiket eşiğinden ayrı bir ayar olarak arandığında, eğitim ölçütü isabetten dengeli isabete çevrildiğinde ve etiket özniteliklerin gördüğü bir kurala bağlandığında duyarlılık 0,0000'dan 1,0000'a çıkıyor, çünkü aynı 135 adaylık arama eski etiketle dengeli isabette 0,5203'te yani yazı tura düzeyinde kalıyor. Düzeltilmiş kurulumda girdi dönem dönem kayıyor, girdi özeti 25,9220'den 27,0472'ye çıkıyor, tahmin payı 0,4550'den 0,5450'ye gidiyor ve 0,05 bandında üç dönem uyarı ateşliyor, ama isabet hiçbir dönemde 0,9700'lük tabanın altına inmiyor; üç ateşlemenin üçü de yanlış alarm ve kaymanın gerçekten götürdüğü, kaymasız koşumla karşılaştırıldığında 2026-12'de 0,0100, yani iki çözünürlük adımı.

Altmış bir derste modelin **üretime çıkmasına kadar** olan yol ölçüldü: hattın taşıdığı satır
sayıldı, satırın nerede duracağı seçildi, iş bölüştürüldü, koşum sabitlendi, öznitelik tek yerden
okundu ve model paketlenip sunuldu. Bu yolun sonundaki sayı **çıkış anına** aittir ve o an
geçtikten sonra hiçbir şey anlatmaz.

Çıktıktan sonrası hiç sorulmadı. Dünya modelin altından değişir: girdi dağılımı kayar, girdi ile
etiket arasındaki kural değişir, gerçek etiket haftalar sonra gelir ve bir gün model emekliye
ayrılır. Bu kurs o zamanı ölçer, ama ilk işi bir düzeltmedir. **İzlenemeyen model izlenemez:**
MLOps Temelleri kursunun bıraktığı model üretimdeki hiçbir bozulmayı gösteremez ve bunun birbirinden
bağımsız üç nedeni vardır.

- **MI1.** Kaynak, hattın modele bakan ilk üç aşaması, eşik tabanlı model ve kayma senaryosu
  **kurgudur**; gerçek bir öğrenme kitaplığı, izleme ürünü ya da bulut servisi çağrılmaz. Tohum
  **20260218**.
- **MI2.** **Kayma (drift)** üretimdeki verinin eğitimdekinden uzaklaşmasıdır: **veri kayması
  (data drift)** girdi dağılımının, **kavram kayması (concept drift)** girdi ile etiket arasındaki
  kuralın değişmesidir. Ölçüm yordamı Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda
  kuruldu ve **tekrarlanmaz**; bu kurs yalnız **üretimdeki ömrü** ekler.
- **MI3.** İzleme dönemleri **2026-07…2026-12**, dönem başına **200 satır**. Eğitim penceresi
  **2026-02…2026-09**; ilk üç izleme dönemi onunla örtüşür, **kaymanın ölçüldüğü üç dönem
  dışındadır**.
- **MI4.** Bu kursun etiketi `abone_ort + 6 x isyeri > 30` kuralıdır ve **bu kural KAVRAM'dır**.
  Veri kayması onu değiştirmez. İzleme dönemlerinde **taban sınıf oranı 0,5342**; küme dengelidir
  ve isabet burada anlamlı okunabilir.
- **MI5.** **Karar eşiği (decision threshold)** puanı tahmine çeviren ayardır ve etiket eşiğinden
  ayrıdır. **Dengeli isabet (balanced accuracy)**, **duyarlılık (recall)** ile **özgüllüğün
  (specificity)** ortalamasıdır.
- **MI6.** Taban çizgisi **izlemesiz kurulumdur**: gösterge yok, uyarı yok; bozulma **görülmez**.
  **Uyarı eşiği (alert threshold)** iki yönlü bir **bant** olarak yazılır.
- **MI7.** Çözünürlük: dönem isabetinin adımı **1/200 = 0,0050**; bu derste isabetin toplam
  oynaması **0,0200**, yani dört adım.

#### İzlenemeyen Model İzlenemez

İlk blok kaynağı, hattın modele bakan üç aşamasını, öznitelikleri ve kurgu modeli kurar. Üç
düzeltme kodun içinde adlarıyla işaretlidir: **karar eşiği ayrı bir ayardır** ve `KARAR_IZGARA`
içinde ağırlıklarla birlikte aranır, eğitim ölçütü isabet değil **dengeli isabettir**, etiket
özniteliklerin **gördüğü** bir kurala bağlanmıştır.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin modele bakan ilk uc asamasi,
# esik tabanli model ve kayma senaryosu ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, izleme urunu ya da bulut servisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, basamak=4):
    """Oznitelik agirliktan bagimsizdir; tam gecmisten BIR KEZ hesaplanir."""
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if d < i]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ortalama(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ortalama(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


# ---- DUZELTME 3: etiket, ozniteliklerin GORDUGU bir kurala baglanir. Kural KAVRAM'dir.
KURAL_ESIK = 30
KURAL_ISYERI = 6


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


IZLEME = DONEM[6:]                    # 2026-07 .. 2026-12 : uretimde izlenen donemler
# ---- DUZELTME 1: karar esigi ETIKET esiginden AYRI bir ayardir ve egitimde aranir.
KARAR_IZGARA = (18, 22, 26, 30, 34)


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


# ---- DUZELTME 2: egitim olcutu ISABET degil DENGELI ISABETTIR.
# kural=None -> bu kursun etiketi; disaridan kural gecmek eski kurulumu olcmeye yarar.
def dengeli_isabet(kume, oz, w, karar, kural=None):
    c = {(0, 0): 0, (0, 1): 0, (1, 0): 0, (1, 1): 0}
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        c[(tahmin(o_k, w, karar), kural(k, o_k) if kural else etiket(o_k))] += 1
    du = c[(1, 1)] / max(c[(1, 1)] + c[(0, 1)], 1)      # duyarlilik
    oz_ = c[(0, 0)] / max(c[(0, 0)] + c[(1, 0)], 1)     # ozgulluk
    return {"isabet": (c[(1, 1)] + c[(0, 0)]) / len(kume), "duyarlilik": du, "ozgulluk": oz_,
            "dengeli": (du + oz_) / 2, "agirlik": w, "karar": karar}


def egit(kayitlar, oz, donemler=None, kural=None):
    """Egitim = agirlik ucLUsu ILE karar esigini birlikte aramak: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    return max((dengeli_isabet(kume, oz, w, karar, kural)
                for w in IZGARA for karar in KARAR_IZGARA), key=lambda o: o["dengeli"])


S = satirlar()
OZ = oznitelikler(S)
IZL = [k for k in S if k["donem"] in IZLEME]
dag = {0: 0, 1: 0}
for k in IZL:
    dag[etiket(OZ[(k["abone"], k["donem"])])] += 1
print("satir:", len(S), " izleme donemi:", len(IZLEME), " izleme satiri:", len(IZL),
      " donem basina:", len(IZL) // len(IZLEME))
print("izleme etiket dagilimi:", dag, " taban sinif orani:", round(dag[0] / len(IZL), 4))
```

```
satir: 2200  izleme donemi: 6  izleme satiri: 1200  donem basina: 200
izleme etiket dagilimi: {0: 641, 1: 559}  taban sinif orani: 0.5342
```

Taban sınıf oranı **0,5342**'dir; MLOps Temelleri kursunda 0,7964'tü ve isabet tek başına
okunduğunda yanıltıyordu. İkinci blok düzeltmelerin ne kazandırdığını sayar: üstteki tablo iki
kurulumu **aynı sınama dönemlerinde** karşılaştırır, alttaki **aynı aramayı** iki farklı etiket
kuralıyla koşturur.

```python
SIN = [k for k in S if k["donem"] in DONEM[9:]]
MODEL = egit(S, OZ)


def eski_kural(k, o_k):
    return 1 if k["m3"] > 40 else 0


BASLIK = (f"{'kurulum':<36}{'agirlik / karar':>22}{'isabet':>9}"
          f"{'duyarlilik':>12}{'ozgulluk':>10}{'dengeli':>9}")


def yaz(ad, o):
    print(f"{ad:<36}{str(o['agirlik']) + ' / ' + str(o['karar']):>22}{o['isabet']:>9.4f}"
          f"{o['duyarlilik']:>12.4f}{o['ozgulluk']:>10.4f}{o['dengeli']:>9.4f}")


print("sinama donemlerinde (2026-10 .. 2026-12):\n" + BASLIK)
yaz("eski : olcut isabet", dengeli_isabet(SIN, OZ, (0.6, 0.4, 0), 40, eski_kural))
yaz("K06  : olcut dengeli isabet", dengeli_isabet(SIN, OZ, MODEL["agirlik"], MODEL["karar"]))
print("\nayni 135 adaylik aramanin en iyisi , egitim donemlerinde:\n" + BASLIK)
yaz("etiket m3 > 40", egit(S, OZ, kural=eski_kural))
yaz("etiket abone_ort + 6 x isyeri > 30", MODEL)
```

```
sinama donemlerinde (2026-10 .. 2026-12):
kurulum                                    agirlik / karar   isabet  duyarlilik  ozgulluk  dengeli
eski : olcut isabet                     (0.6, 0.4, 0) / 40   0.8067      0.0000    1.0000   0.5000
K06  : olcut dengeli isabet             (0.6, 0.0, 4) / 18   0.9833      1.0000    0.9670   0.9835

ayni 135 adaylik aramanin en iyisi , egitim donemlerinde:
kurulum                                    agirlik / karar   isabet  duyarlilik  ozgulluk  dengeli
etiket m3 > 40                          (0.6, 0.2, 4) / 22   0.5075      0.5422    0.4984   0.5203
etiket abone_ort + 6 x isyeri > 30      (0.6, 0.0, 4) / 18   0.9838      1.0000    0.9728   0.9864
```

Üstteki 0,8067 hatalı değildir; ölçtüğü şey başkadır. Karar eşiği etiket eşiğiyle aynı tutulup
ölçüt isabet olduğunda arama, dengesiz kümede **hiçbir satıra yüksek demeyen** modelde durur:
**duyarlılık 0,0000**, dengeli isabet **0,5000**, yani yazı tura düzeyi. Böyle bir model üretimde
bozulsa da göstergesi kıpırdamaz, çünkü çıktısı zaten sabittir. Düzeltilmiş kurulumda duyarlılık
**1,0000**, özgüllük **0,9670**, dengeli isabet **0,9835**.

Alttaki tablo üçüncü düzeltmenin neden gerektiğini söyler. İlk iki düzeltme yapılsa bile eski
etiket kuralıyla 135 adayın **en iyisi 0,5203**'te kalıyor. Neden yapısaldır: o kural satırın
**kendi dönemindeki** tüketimine bakar, öznitelikler ise yalnız **geçmiş dönemleri** görür.
İlişki olmadığı için öğrenilecek bir şey de yoktur. Etiket özniteliklerin gördüğü bir kurala
bağlandığında aynı arama **0,9864** verir.

#### Girdi Kayarken Ne Oluyor

Veri kaymasında girdi dağılımı değişir, **kural aynı kalır**. Kurgu senaryo şudur: `2026-09`'dan
sonra işyeri abonelerinin tüketimi her dönem 0,06 kat büyüyor. Üçüncü blok kaymayı uygular ve üç
göstergeyi dönem dönem okur: **girdi özeti** (özniteliğin ortalaması), **tahmin payı** (modelin
yüksek dediği satırların oranı) ve **isabet**. Son sütun aynı modelin **kaymasız** veride verdiği
isabettir, yani karşı olgusal koşum.

```python
# ---- KURGU veri kaymasi: 2026-09'dan sonra isyeri tuketimi her donem 0,06 buyuyor.
# KURAL AYNI KALIR; degisen yalniz girdi dagilimidir.
def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def girdi_ozeti(kayitlar, donem, oz):
    d = [oz[(k["abone"], k["donem"])]["abone_ort"] for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def tahmin_payi(kayitlar, model, oz, donem):
    d = [tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
         for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def isabet(kayitlar, model, oz, donem, etiketler=None):
    """etiketler=None -> kural degismemis demektir."""
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        g = etiketler[(k["abone"], k["donem"])] if etiketler else etiket(o_k)
        d += tahmin(o_k, model["agirlik"], model["karar"]) == g
    return round(d / len(kume), 4)


KAYAN = kaydir(S)
OZK = oznitelikler(KAYAN)
OLCU = {"girdi ozeti": [(d, girdi_ozeti(KAYAN, d, OZK)) for d in IZLEME],
        "tahmin payi": [(d, tahmin_payi(KAYAN, MODEL, OZK, d)) for d in IZLEME],
        "isabet": [(d, isabet(KAYAN, MODEL, OZK, d)) for d in IZLEME]}
print(f"{'donem':<10}{'girdi ozeti':>13}{'tahmin payi':>13}{'isabet':>9}{'kaymasiz isabet':>17}")
for i, d in enumerate(IZLEME):
    print(f"{d:<10}{OLCU['girdi ozeti'][i][1]:>13.4f}{OLCU['tahmin payi'][i][1]:>13.4f}"
          f"{OLCU['isabet'][i][1]:>9.4f}{isabet(S, MODEL, OZ, d):>17.4f}")
```

```
donem       girdi ozeti  tahmin payi   isabet  kaymasiz isabet
2026-07         25.9220       0.4550   0.9700           0.9700
2026-08         26.0517       0.4700   0.9700           0.9700
2026-09         25.9800       0.4550   0.9900           0.9900
2026-10         26.4685       0.5150   0.9750           0.9750
2026-11         26.6053       0.5200   0.9800           0.9850
2026-12         27.0472       0.5450   0.9800           0.9900
```

Girdi gerçekten kayıyor: girdi özeti **25,9220'den 27,0472'ye**, tahmin payı **0,4550'den
0,5450'ye** çıkıyor, yani yüksek diyen çıkarımın oranı yaklaşık yüzde yirmi büyüyor. Buna karşılık
isabet hiçbir dönemde **0,9700'lük tabanın altına inmiyor**. Kaymanın gerçekten götürdüğü karşı
olgusal sütunla ölçülür: `2026-12`'de kaymasız koşum 0,9900, kaymalı koşum 0,9800 verir; fark
**0,0100**, iki çözünürlük adımı. Kural değişmediği sürece doğru bir model kaymış girdide de doğru
kalır, çünkü etiket de tahmin de aynı kaymış özniteliklerden hesaplanır.

#### Ateşleyen Uyarı Bir Kanıt Değildir

Dördüncü blok göstergelere uyarı bandı takar. Bant tabandan sapmanın oranıdır; taban `2026-08`,
kaymanın başladığı dönemden önceki son dönem. Aynı üç bant üç göstergeye de aynı biçimde uygulanır.

```python
# ---- Uyari: bir olcu tabandan `pay` kadar saparsa ates eder. Bant iki yonludur.
def uyari(dizi, taban, pay):
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


TABAN_DONEM = IZLEME[1]          # 2026-08 : kaymanin basladigi donemden onceki son donem
ISABET = dict(OLCU["isabet"])
print(f"{'olcu':<14}{'taban':>10}{'en buyuk sapma':>16}{'0,05':>7}{'0,10':>7}"
      f"{'0,20':>7}{'ilk atesleyen':>16}")
for ad, dizi in OLCU.items():
    taban = dict(dizi)[TABAN_DONEM]
    ates = [uyari(dizi, taban, p) for p in (0.05, 0.10, 0.20)]
    sapma = max(abs(v - taban) / taban for _, v in dizi)
    print(f"{ad:<14}{taban:>10.4f}{sapma:>16.4f}{len(ates[0]):>7}{len(ates[1]):>7}"
          f"{len(ates[2]):>7}{(ates[0][0] if ates[0] else '-'):>16}")

ATES = uyari(OLCU["tahmin payi"], dict(OLCU["tahmin payi"])[TABAN_DONEM], 0.05)
GERCEK = [d for d in ATES if ISABET[d] < ISABET[TABAN_DONEM]]
print("\n0,05 bandinda atesleyen donem:", ATES)
print("bunlarin kacinda isabet tabanin altina indi:", len(GERCEK),
      " yanlis alarm:", len(ATES) - len(GERCEK))
print("isabetin toplam oynamasi:", f"{max(ISABET.values()) - min(ISABET.values()):.4f}",
      " donem isabetinin adimi:", f"{1 / 200:.4f}",
      " kaymanin 2026-12'de goturdugu:",
      f"{isabet(S, MODEL, OZ, IZLEME[-1]) - ISABET[IZLEME[-1]]:.4f}")
```

```
olcu               taban  en buyuk sapma   0,05   0,10   0,20   ilk atesleyen
girdi ozeti      26.0517          0.0382      0      0      0               -
tahmin payi       0.4700          0.1596      3      2      0         2026-10
isabet            0.9700          0.0206      0      0      0               -

0,05 bandinda atesleyen donem: ['2026-10', '2026-11', '2026-12']
bunlarin kacinda isabet tabanin altina indi: 0  yanlis alarm: 3
isabetin toplam oynamasi: 0.0200  donem isabetinin adimi: 0.0050  kaymanin 2026-12'de goturdugu: 0.0100
```

Aynı kayma, aynı bant, üç ayrı yanıt. Girdi özetinin en büyük sapması **0,0382**'dir ve hiçbir
bantta ateşlemez, çünkü bir ortalamadır ve ortalama tek tek satırlardaki değişimi söndürür. Tahmin
payının sapması **0,1596**'dır; 0,05 bandında **üç dönem**, 0,10 bandında iki dönem ateşler, çünkü
karar eşiğinin **çevresindeki** satırlara duyarlıdır. İsabetin sapması **0,0206**'dır, ateşlemez ve
üstelik **yukarı** yöndedir.

Kursun kuralı buradan okunur. `2026-10`'daki uyarı yanlış çalışmamıştır: girdi gerçekten kaymıştır.
Ama söylediği tek şey **girdinin kaydığıdır**; bozulduğunu söylemez. Ateşleyen üç dönemin
hiçbirinde isabet tabanın altına inmemiştir, üçü de **yanlış alarm (false positive)** sayılır. Bir
göstergenin ateşlemesi bozulmanın kanıtı değildir; **ateşlemesi ile bozulması ayrı ayrı
gösterilmeyen gösterge ölçülmemiş sayılır**. İzlemesiz taban çizgisi burada tuhaf bir konuma
düşer: gösterge takmayan kurulum hiç yanılmamıştır, çünkü yanılacak bir uyarısı yoktur.

#### Özet

- İzlenebilir bir model üç ayara bağlıdır: karar eşiği etiket eşiğinden **ayrı** bir ayardır ve
  eğitimde aranır, eğitim ölçütü **dengeli isabettir**, etiket özniteliklerin **gördüğü** bir
  kurala bağlanır.
- Eski kurulumda duyarlılık **0,0000**, dengeli isabet **0,5000**; düzeltilmişte duyarlılık
  **1,0000**, özgüllük **0,9670**, dengeli isabet **0,9835** ve taban sınıf oranı **0,5342**.
- İlk iki düzeltme tek başına yetmez: eski etiket kuralıyla 135 adayın en iyisi **0,5203**'te,
  yazı tura düzeyinde kalır, çünkü o kural özniteliklerin görmediği bir sayıya bakar.
- Veri kaymasında girdi özeti **25,9220'den 27,0472'ye**, tahmin payı **0,4550'den 0,5450'ye**
  çıkar, ama isabet **0,9700'den 0,9800'e** gider; kaymanın götürdüğü `2026-12`'de **0,0100**,
  iki çözünürlük adımı.
- Tahmin payı 0,05 bandında **üç dönem** ateşler ve üçünde de isabet tabanın altına inmemiştir:
  **ateşleyen uyarı bir sorun kanıtı değildir**.

#### Sonraki Adım

Bu derste kayan şey **girdiydi** ve kural yerinde durduğu için model doğru kalabildi; ateşleyen
gösterge de tam olarak bunu gördü. Peki değişen girdi değil de **kuralın kendisi** olsaydı, yani
aynı öznitelikler artık başka bir etiket üretmeye başlasaydı, aynı üç gösterge ne derdi. Girdi
özeti kıpırdamaz, çünkü girdi kıpırdamamıştır; tahmin payı da kıpırdamaz, çünkü model aynı
girdilere aynı yanıtı vermeyi sürdürür. Sonraki ders bu durumu ölçer ve tam tersi bir sonuç bulur:
göstergeler susarken isabet **0,9700'den 0,8150'ye** düşer.

### Kavram Kayması

Source: https://academia.sh/tr/kurslar/model-izleme/kavram-kaymasi

Girdi hiç değişmeden girdi ile etiket arasındaki kuralın değişmesinin ölçülmesi: yüksek sayılma eşiği 30'dan 27'ye indiğinde girdi tarafında değişen tek bir değer olmamasına karşın 1200 izleme satırının 132'sinin etiketi değişiyor ve modelin isabeti 0,9700'den 0,8150'ye, yani otuz bir çözünürlük adımı düşüyor. Aynı üç gösterge iki kayma türünde yan yana okunduğunda vekil ölçütlerin neyi görüp neyi kaçırdığı sayıya dönüyor: girdi özeti iki senaryoda da hiçbir bantta ateşlemiyor, tahmin payı 0,05 bandında ikisinde de üç dönem ateşleyerek zararsızı zararlıdan ayıramıyor ve 0,10 bandında zararsız kaymada iki dönem ateşlerken zararlı kaymada hiç ateşlemiyor, isabet ise zararsız kaymada susup zararlı kaymada dört dönem ateşleyerek tek ayırt eden ölçü olarak kalıyor.

Önceki derste kayan şey **girdiydi**: girdi özeti 25,9220'den 27,0472'ye çıktı, tahmin payı üç
dönem uyarı ateşledi ve isabet hiçbir dönemde 0,9700'lük tabanın altına inmedi. Kural yerinde
durduğu için model doğru kalabildi ve ateşleyen uyarıların üçü de yanlış alarm sayıldı.

Bu ders tam tersini kurar. Girdi tarafında **tek bir değer bile değişmez**; değişen, girdi ile
etiket arasındaki **kuralın kendisidir**. Aynı abonenin aynı geçmişi artık başka bir etiket
üretmeye başlar, çünkü "yüksek sayılma" ölçütü kaymıştır. Model hiç dokunulmadan yerinde durur ve
ölçülecek soru şudur: önceki dersin üç göstergesi bu durumda ne der.

- **MI8.** Kaynak, hat, model ve kayma senaryoları **kurgudur**; gerçek bir öğrenme kitaplığı,
  izleme ürünü ya da bulut servisi çağrılmaz. Tohum **20260218**.
- **MI9.** **Kavram kayması (concept drift)** girdi ile etiket arasındaki ilişkinin değişmesidir.
  **Veri kayması** girdi dağılımını, kavram kayması **kuralı** oynatır. Kaymanın ölçüm yordamı
  Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda kuruldu ve **tekrarlanmaz**; buradaki ek
  yalnız **üretimdeki ömürdür**.
- **MI10.** Bu kursun etiketi `abone_ort + 6 x isyeri > 30` kuralıdır ve **kural KAVRAM'dır**.
  Kurgu kavram kayması `2026-09`'dan itibaren eşiği **30'dan 27'ye** indirir; kayıtlara dokunmaz,
  yalnız etiket üretir.
- **MI11.** İzleme dönemleri **2026-07…2026-12**, dönem başına **200 satır**, toplam **1200**.
  İzleme dönemlerinde **taban sınıf oranı 0,5342**; küme dengelidir.
- **MI12.** Üç gösterge: **girdi özeti** (özniteliğin dönem ortalaması), **tahmin payı** (modelin
  yüksek dediği satırların oranı) ve **isabet**. İlk ikisi etiket istemez, üçüncüsü ister.
- **MI13.** Taban çizgisi **izlemesiz kurulumdur**: gösterge yok, uyarı yok; bozulma **görülmez**.
- **MI14.** **Uyarı eşiği** iki yönlü bir **bant**tır: ölçü tabandan pay kadar saparsa ateşler.
  Taban dönem `2026-08`, yani kaymanın başladığı dönemden önceki son dönem.
- **MI15.** Çözünürlük: dönem isabetinin adımı **1/200 = 0,0050**. Bu derste düşüş **0,1550**, yani
  **otuz bir adım** ve bandın çok üstünde; önceki dersin oynaması dört adımdı.

#### Değişmeyen Girdi, Değişen Kural

İlk blok önceki dersin kurulumunu aynen kurar: kaynak, hattın modele bakan üç aşaması,
öznitelikler, karar eşiği ayrı aranan model ve üç gösterge. Model **bu derste hiç yeniden
eğitilmez**; ölçülen şey, sabit bir modelin altından kuralın çekilmesidir.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin modele bakan ilk uc asamasi,
# esik tabanli model ve kayma senaryolari ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, izleme urunu ya da bulut servisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, basamak=4):
    """Oznitelik agirliktan bagimsizdir; tam gecmisten BIR KEZ hesaplanir."""
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if d < i]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ortalama(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ortalama(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


# Etiket, ozniteliklerin GORDUGU bir kurala baglidir. KURAL = KAVRAM.
KURAL_ESIK = 30
KURAL_ISYERI = 6


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


IZLEME = DONEM[6:]                    # 2026-07 .. 2026-12 : uretimde izlenen donemler
KARAR_IZGARA = (18, 22, 26, 30, 34)   # karar esigi ETIKET esiginden AYRI bir ayardir


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar, kural=None):
    c = {(0, 0): 0, (0, 1): 0, (1, 0): 0, (1, 1): 0}
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        c[(tahmin(o_k, w, karar), kural(k, o_k) if kural else etiket(o_k))] += 1
    du = c[(1, 1)] / max(c[(1, 1)] + c[(0, 1)], 1)      # duyarlilik
    oz_ = c[(0, 0)] / max(c[(0, 0)] + c[(1, 0)], 1)     # ozgulluk
    return {"isabet": (c[(1, 1)] + c[(0, 0)]) / len(kume), "duyarlilik": du, "ozgulluk": oz_,
            "dengeli": (du + oz_) / 2, "agirlik": w, "karar": karar}


def egit(kayitlar, oz, donemler=None, kural=None):
    """Egitim = agirlik ucLUsu ILE karar esigini birlikte aramak: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    return max((dengeli_isabet(kume, oz, w, karar, kural)
                for w in IZGARA for karar in KARAR_IZGARA), key=lambda o: o["dengeli"])


def girdi_ozeti(kayitlar, donem, oz):
    d = [oz[(k["abone"], k["donem"])]["abone_ort"] for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def tahmin_payi(kayitlar, model, oz, donem):
    d = [tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
         for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def isabet(kayitlar, model, oz, donem, etiketler=None):
    """etiketler=None -> kural degismemis demektir."""
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        g = etiketler[(k["abone"], k["donem"])] if etiketler else etiket(o_k)
        d += tahmin(o_k, model["agirlik"], model["karar"]) == g
    return round(d / len(kume), 4)


S = satirlar()
OZ = oznitelikler(S)
MODEL = egit(S, OZ)
IZL = [k for k in S if k["donem"] in IZLEME]
dag = {0: 0, 1: 0}
for k in IZL:
    dag[etiket(OZ[(k["abone"], k["donem"])])] += 1
print("izleme satiri:", len(IZL), " donem basina:", len(IZL) // len(IZLEME),
      " taban sinif orani:", round(dag[0] / len(IZL), 4))
print("model:", MODEL["agirlik"], "/ karar esigi", MODEL["karar"],
      " dengeli isabet:", round(MODEL["dengeli"], 4))
```

```
izleme satiri: 1200  donem basina: 200  taban sinif orani: 0.5342
model: (0.6, 0.0, 4) / karar esigi 18  dengeli isabet: 0.9864
```

#### Kavram Kaydığında Ne Görünüyor

İkinci blok kavram kaymasını uygular. `kavram_kaydir` işlevi **kayıtlara dokunmaz**; yalnız
`2026-09`'dan başlayarak yüksek sayılma eşiğini 27'ye indirilmiş biçimde etiket üretir. Tabloda son
iki sütun önemlidir: **kural değişmeseydi** o dönemde alınacak isabet ve o dönemde **etiketi
değişen** satır sayısı.

```python
# ---- KURGU kavram kaymasi: GIRDI AYNI KALIR , kural degisir.
# 2026-09'dan sonra "yuksek sayilma" esigi 30'dan 27'ye iniyor.
def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]):
            etiket(oz[(k["abone"], k["donem"])],
                   yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK)
            for k in kayitlar}


YENI = kavram_kaydir(S, OZ)
print(f"{'donem':<10}{'girdi ozeti':>13}{'tahmin payi':>13}{'isabet':>9}"
      f"{'kural degismeseydi':>20}{'etiketi degisen':>17}")
for d in IZLEME:
    kume = [k for k in S if k["donem"] == d]
    ceviren = sum(1 for k in kume
                  if YENI[(k["abone"], d)] != etiket(OZ[(k["abone"], d)]))
    print(f"{d:<10}{girdi_ozeti(S, d, OZ):>13.4f}{tahmin_payi(S, MODEL, OZ, d):>13.4f}"
          f"{isabet(S, MODEL, OZ, d, YENI):>9.4f}{isabet(S, MODEL, OZ, d):>20.4f}"
          f"{ceviren:>17}")
```

```
donem       girdi ozeti  tahmin payi   isabet  kural degismeseydi  etiketi degisen
2026-07         25.9220       0.4550   0.9700              0.9700                0
2026-08         26.0517       0.4700   0.9700              0.9700                0
2026-09         25.9800       0.4550   0.8150              0.9900               39
2026-10         26.3525       0.5100   0.8700              0.9750               31
2026-11         26.3261       0.5100   0.8650              0.9850               30
2026-12         26.5325       0.5150   0.8500              0.9900               32
```

İsabet `2026-09`'da **0,9700'den 0,8150'ye** düşüyor. Düşüş **0,1550**, yani çözünürlüğün **otuz
bir adımı**; önceki dersin bütün oynaması dört adımdı. Modelde değişen tek bir sayı yoktur ve
girdi tarafında da yoktur: son sütun kaymanın nereden geldiğini söyler, `2026-09`'da **39**
satırın etiketi değişmiştir. Değişen etiketlerin hepsi hataya dönüşmez, çünkü modelin karar eşiği
bazı satırlarda yeni kuralla zaten uyumludur; sonraki dönemlerde isabet 0,8700 dolayında
tutunur ama **eski düzeyine hiç dönmez**.

İlk iki sütun tam olarak beklendiği gibi davranır. Girdi özeti 25,9220'den 26,5325'e, tahmin payı
0,4550'den 0,5150'ye gider. Bu iki seri, kavram kayması **hiç olmasaydı** da birebir aynı olurdu,
çünkü ikisi de yalnız girdiye ve modele bakar; kuralın nereye kaydığından haberleri yoktur.

#### Zararsız Kaymada Ateşleyen, Zararlı Kaymada Susan

Üçüncü blok önceki dersin veri kayması senaryosunu geri getirir ve iki senaryoyu **aynı üç
göstergeyle, aynı üç bantla** yan yana koyar. Tabloda okunacak şey tek tek sayılar değil,
**satırların birbirine göre durumudur**.

```python
# ---- KURGU veri kaymasi (onceki ders): GIRDI kayar , KURAL AYNI KALIR.
def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def uyari(dizi, taban, pay):
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


KAYAN = kaydir(S)
OZK = oznitelikler(KAYAN)
print(f"{'senaryo':<16}{'olcu':<14}{'taban':>10}{'en buyuk sapma':>16}"
      f"{'0,05':>7}{'0,10':>7}{'0,20':>7}")
for ad, (kay, oz, et) in (("veri kaymasi", (KAYAN, OZK, None)),
                          ("kavram kaymasi", (S, OZ, YENI))):
    for olcu_ad, dizi in (("girdi ozeti", [(d, girdi_ozeti(kay, d, oz)) for d in IZLEME]),
                          ("tahmin payi", [(d, tahmin_payi(kay, MODEL, oz, d)) for d in IZLEME]),
                          ("isabet", [(d, isabet(kay, MODEL, oz, d, et)) for d in IZLEME])):
        taban = dict(dizi)[IZLEME[1]]
        print(f"{ad:<16}{olcu_ad:<14}{taban:>10.4f}"
              f"{max(abs(v - taban) / taban for _, v in dizi):>16.4f}"
              + "".join(f"{len(uyari(dizi, taban, p)):>7}" for p in (0.05, 0.10, 0.20)))

print("\nkavram kaymasinda degisen satir sayisi:",
      sum(1 for k in IZL if YENI[(k["abone"], k["donem"])] != etiket(OZ[(k["abone"], k["donem"])])),
      "/", len(IZL), " girdi tarafinda degisen deger: 0")
DUSUS = isabet(S, MODEL, OZ, IZLEME[0], YENI) - isabet(S, MODEL, OZ, IZLEME[2], YENI)
print("isabetin dustugu:", f"{DUSUS:.4f}", " donem isabetinin adimi:", f"{1 / 200:.4f}",
      " yani", round(DUSUS * 200), "adim")
```

```
senaryo         olcu               taban  en buyuk sapma   0,05   0,10   0,20
veri kaymasi    girdi ozeti      26.0517          0.0382      0      0      0
veri kaymasi    tahmin payi       0.4700          0.1596      3      2      0
veri kaymasi    isabet            0.9700          0.0206      0      0      0
kavram kaymasi  girdi ozeti      26.0517          0.0185      0      0      0
kavram kaymasi  tahmin payi       0.4700          0.0957      3      0      0
kavram kaymasi  isabet            0.9700          0.1598      4      4      0

kavram kaymasinda degisen satir sayisi: 132 / 1200  girdi tarafinda degisen deger: 0
isabetin dustugu: 0.1550  donem isabetinin adimi: 0.0050  yani 31 adim
```

Girdi özeti iki senaryoda da hiçbir bantta ateşlemez. Kavram kaymasında bunun başka türlüsü de
mümkün değildir: girdi tarafında değişen **sıfır** değer vardır ve gösterge yalnız girdiye bakar.

Tahmin payı iki kez ateşler ve her ikisinde de yanıltır. 0,05 bandında **her iki senaryoda da üç
dönem** ateşliyor, yani zararsız kaymayı zararlı kaymadan **ayırt edemiyor**. 0,10 bandında ise
tam ters çalışıyor: zararsız kaymada **iki dönem** ateşliyor, zararlı kaymada **hiç**. Kavram
kaymasındaki 0,0957'lik sapmanın kaymayla ilgisi de yoktur; o seri, kural hiç değişmeseydi de
aynıydı. Bir gösterge ateşlediğinde tek başına ne bozulmayı ne de nedenini gösterir.

İsabet tek ayırt eden ölçüdür. Zararsız kaymada sapması **0,0206** ve hiçbir bantta ateşlemiyor;
zararlı kaymada sapması **0,1598** ve hem 0,05 hem 0,10 bandında **dört dönem** ateşliyor. Bu
kursun kuralı burada iki yönlü okunur: bir göstergenin ateşlemesi bozulmanın kanıtı olmadığı gibi,
**susması da sağlamlığın kanıtı değildir**; ateşlemesi ile bozulması ayrı ayrı gösterilmeyen
gösterge ölçülmemiş sayılır. İzlemesiz taban çizgisinde ise 132 satırın etiketi değişmiş, isabet
otuz bir adım düşmüş ve hiçbir şey haber vermemiştir.

#### Özet

- Kavram kayması girdi ile etiket arasındaki **kuralın** değişmesidir; veri kaymasının aksine
  girdi tarafında değişen tek bir değer yoktur.
- Eşik 30'dan 27'ye indiğinde 1200 izleme satırının **132'sinin** etiketi değişir ve isabet
  **0,9700'den 0,8150'ye** düşer: **0,1550**, yani **otuz bir çözünürlük adımı**. Model hiç
  değişmemiştir.
- Girdi özeti iki senaryoda da hiçbir bantta ateşlemez; kavram kaymasında ateşlemesi zaten
  olanaksızdır, çünkü baktığı yerde değişen bir şey yoktur.
- Tahmin payı 0,05 bandında iki senaryoda da **üç dönem** ateşleyerek ikisini ayırt edemez, 0,10
  bandında ise zararsız kaymada iki dönem ateşleyip zararlı kaymada **hiç** ateşlemez.
- İsabet tek ayırt eden ölçüdür: zararsız kaymada sapması 0,0206 ve susar, zararlı kaymada 0,1598
  ve 0,05 ile 0,10 bantlarında dört dönem ateşler.

#### Sonraki Adım

İki derste ortaya çıkan sonuç şudur: bozulmayı gören tek gösterge **isabettir**. Fakat isabet
öbür ikisinden farklı bir şey ister, gerçek etiketi. Girdi özeti ile tahmin payı, tahmin
üretildiği anda hesaplanabilir; isabet ise satırın gerçekten yüksek olup olmadığı bilinene kadar
hesaplanamaz ve o bilgi üretimde tahminle aynı anda gelmez. Bu derste etiket, hesaplama sırasında
elde varmış gibi kullanıldı. Sonraki ders bu varsayımı kaldırır: etiket **iki dönem sonra**
geldiğinde bir dönemin isabetinin hangi dönemde bilinebildiğini, bozulmanın olduğu an ile
görüldüğü an arasındaki boşluğu ve o boşlukta kaç tahminin **ölçülmeden** üretildiğini sayar.

### Başarım İzleme

Source: https://academia.sh/tr/kurslar/model-izleme/basarim-izleme

Gerçek etiketin tahminle aynı anda gelmediği bir üretim kurulumunda başarımın ne zaman bilinebildiğinin ölçülmesi: bir dönemin isabeti ancak iki dönem sonra hesaplanabildiği için kavram kaymasının 2026-09'da düşürdüğü isabet ilk kez 2026-11'de görülebiliyor ve bu boşlukta 400 tahmin ölçülmeden üretiliyor. 2026-09'da gerçek isabet 0,8150 iken elde olan en yeni ölçü 2026-07'nin 0,9700'ü oluyor, yani gösterge yanlış değil eski. Gecikme süpürmesi bedeli doğrusal veriyor: 0 dönem gecikmede bozulma aynı dönemde görülüyor ve ölçülmeden üretilen tahmin 0, 1 dönemde 200, 2 dönemde 400, 3 dönemde 600, 4 dönemde bozulma izleme penceresi içinde hiç görülmüyor; etiket hiç toplanmadığında ölçülebilen dönem sayısı 0 ve bozulma hiçbir zaman görülmüyor.

İki derste ortaya çıkan sonuç şuydu: kavram kaymasını gören tek gösterge **isabettir**. Girdi
özeti kuralın kaydığından habersizdir, tahmin payı zararsız kaymada ateşleyip zararlı kaymada
susar, isabet ise 0,9700'den 0,8150'ye düşerek bozulmayı adıyla gösterir.

Bu bulgunun sessizce yaptığı bir varsayım var. İsabet hesaplanabilmesi için satırın gerçekten
yüksek olup olmadığının **bilinmesi** gerekir ve önceki iki derste bu bilgi hep elde varmış gibi
davranıldı. Üretimde öyle değildir: tahmin dönem başında üretilir, gerçek etiket ise sayaç
okunduktan, itiraz süresi geçtikten ve kayıt kapandıktan sonra gelir. Bu ders o gecikmeyi kurulumun
içine koyar ve tek bir soruyu ölçer: **hangi dönemin isabeti hangi dönemde bilinebiliyor.**

- **MI16.** Kaynak, hat, model ve kavram kayması **kurgudur**; gerçek bir öğrenme kitaplığı, izleme
  ürünü ya da bulut servisi çağrılmaz. Tohum **20260218**.
- **MI17.** **Gecikmeli etiket (delayed label)** gerçek etiketin tahminden sonra gelmesidir. Burada
  gecikme `GECIKME = 2` **dönemdir**: bir dönemin isabeti ancak iki dönem sonra hesaplanabilir.
  **Süre ham birimle değil, dönem olarak yazılır.**
- **MI18.** **Yakalama gecikmesi (detection delay)** bozulmanın olduğu dönem ile ilk kez
  görülebildiği dönem arasındaki farktır. Bir göstergenin **kaç dönem sonra gösterdiği** ölçüsü
  Veri Hatları ve Orkestrasyon kursunda kuruldu; burada **tekrarlanmaz**, girdi olarak alınır.
- **MI19.** Bozulma kaynağı önceki dersin kavram kaymasıdır: `2026-09`'dan itibaren yüksek sayılma
  eşiği 30'dan 27'ye iner. Girdi tarafında değişen değer yoktur.
- **MI20.** İzleme dönemleri **2026-07…2026-12**, dönem başına **200 satır**, toplam **1200**.
  İzleme dönemlerinde **taban sınıf oranı 0,5342**; küme dengelidir.
- **MI21.** Uyarı bandı **0,05**'tir ve taban `2026-07`'nin isabeti olan **0,9700**'dür. Bant
  iki yönlüdür.
- **MI22.** Taban çizgisi **izlemesiz kurulumdur**: etiket hiç toplanmaz, ölçülebilen dönem
  **0**, bozulma **görülmez**.
- **MI23.** Çözünürlük: dönem isabetinin adımı **1/200 = 0,0050**; kavram kaymasının düşürdüğü
  **0,1550**, yani otuz bir adım ve bandın çok üstünde. Gecikme **dönem** olarak sayılır ve bir
  dönem **200 tahmin** demektir.

#### Etiket İki Dönem Sonra

İlk blok önceki iki dersin kurulumunu kurar ve üstüne tek bir şey ekler: `gorulen_donem`. Bu
işlev bir dönemin isabetinin **verili anda hesaplanabilir olup olmadığını** söyler; hesabın kendisi
değişmez, yalnız **ne zaman yapılabildiği** değişir.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin modele bakan ilk uc asamasi,
# esik tabanli model, kavram kaymasi ve etiket gecikmesi ders icinde tanimlanir;
# gercek bir ogrenme kitapligi, izleme urunu ya da bulut servisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, basamak=4):
    """Oznitelik agirliktan bagimsizdir; tam gecmisten BIR KEZ hesaplanir."""
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if d < i]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ortalama(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ortalama(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


KURAL_ESIK = 30
KURAL_ISYERI = 6


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


IZLEME = DONEM[6:]                    # 2026-07 .. 2026-12 : uretimde izlenen donemler
KARAR_IZGARA = (18, 22, 26, 30, 34)   # karar esigi ETIKET esiginden AYRI bir ayardir


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar, kural=None):
    c = {(0, 0): 0, (0, 1): 0, (1, 0): 0, (1, 1): 0}
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        c[(tahmin(o_k, w, karar), kural(k, o_k) if kural else etiket(o_k))] += 1
    du = c[(1, 1)] / max(c[(1, 1)] + c[(0, 1)], 1)      # duyarlilik
    oz_ = c[(0, 0)] / max(c[(0, 0)] + c[(1, 0)], 1)     # ozgulluk
    return {"isabet": (c[(1, 1)] + c[(0, 0)]) / len(kume), "duyarlilik": du, "ozgulluk": oz_,
            "dengeli": (du + oz_) / 2, "agirlik": w, "karar": karar}


def egit(kayitlar, oz, donemler=None, kural=None):
    """Egitim = agirlik ucLUsu ILE karar esigini birlikte aramak: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    return max((dengeli_isabet(kume, oz, w, karar, kural)
                for w in IZGARA for karar in KARAR_IZGARA), key=lambda o: o["dengeli"])


def isabet(kayitlar, model, oz, donem, etiketler=None):
    """etiketler=None -> kural degismemis demektir."""
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        g = etiketler[(k["abone"], k["donem"])] if etiketler else etiket(o_k)
        d += tahmin(o_k, model["agirlik"], model["karar"]) == g
    return round(d / len(kume), 4)


# ---- KURGU kavram kaymasi (onceki ders): GIRDI AYNI KALIR , kural degisir.
def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]):
            etiket(oz[(k["abone"], k["donem"])],
                   yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK)
            for k in kayitlar}


GECIKME = 2                  # bir donemin etiketi kac donem sonra eline geciyor


def gorulen_donem(donem, simdi, gecikme=GECIKME):
    """Bir donemin isabeti ancak `gecikme` donem sonra hesaplanabilir."""
    return DONEM.index(donem) + gecikme <= DONEM.index(simdi)


S = satirlar()
OZ = oznitelikler(S)
MODEL = egit(S, OZ)
YENI = kavram_kaydir(S, OZ)
IZL = [k for k in S if k["donem"] in IZLEME]
dag = {0: 0, 1: 0}
for k in IZL:
    dag[etiket(OZ[(k["abone"], k["donem"])])] += 1
GERCEK = {d: isabet(S, MODEL, OZ, d, YENI) for d in IZLEME}
print("izleme satiri:", len(IZL), " donem basina:", len(IZL) // len(IZLEME),
      " taban sinif orani:", round(dag[0] / len(IZL), 4), " gecikme:", GECIKME, "donem")
print("gercek isabet:", [f"{GERCEK[d]:.4f}" for d in IZLEME])
```

```
izleme satiri: 1200  donem basina: 200  taban sinif orani: 0.5342  gecikme: 2 donem
gercek isabet: ['0.9700', '0.9700', '0.8150', '0.8700', '0.8650', '0.8500']
```

`GERCEK` sözlüğü, sonradan bakan birinin görebileceği isabet dizisidir. Üretimde duran biri bu
dizinin tamamını hiçbir zaman elinde tutmaz; her dönemde onun yalnız bir **önekini** görür.

#### Hangi Dönemin İsabeti Hangi Dönemde Bilinir

İkinci blok her dönem için üretimdeki tabloyu kurar: o anda **ölçülebilen** en yeni dönem, o
dönemin isabeti, o andaki **gerçek** isabet ve elde olup henüz **ölçülmemiş** tahmin sayısı.
Üçüncü sütun bir gözlemcinin değil, sonradan bakanın bilgisidir; üretimde duran kişi ikinci
sütuna bakar.

```python
BANT, TABAN = 0.05, GERCEK[IZLEME[0]]
print(f"{'simdi':<10}{'olculebilen son donem':>22}{'o donemin isabeti':>19}"
      f"{'simdiki gercek isabet':>23}{'olculmemis tahmin':>19}")
for simdi in IZLEME:
    olculen = [d for d in IZLEME if gorulen_donem(d, simdi)]
    uretilen = IZLEME[: IZLEME.index(simdi) + 1]
    son = olculen[-1] if olculen else None
    print(f"{simdi:<10}{(son or '-'):>22}"
          f"{(f'{GERCEK[son]:.4f}' if son else '-'):>19}{GERCEK[simdi]:>23.4f}"
          f"{(len(uretilen) - len(olculen)) * 200:>19}")

BOZULAN = next(d for d in IZLEME if abs(GERCEK[d] - TABAN) / TABAN > BANT)
GORULEN = next(s for s in IZLEME
               if any(gorulen_donem(d, s) and abs(GERCEK[d] - TABAN) / TABAN > BANT
                      for d in IZLEME))
print("\nbozulmanin oldugu donem:", BOZULAN, " uyarinin atesleyebildigi donem:", GORULEN,
      " gorulme gecikmesi:", DONEM.index(GORULEN) - DONEM.index(BOZULAN), "donem")
BOSLUK = IZLEME[IZLEME.index(BOZULAN):IZLEME.index(GORULEN)]
print("bu boslukta olculmeden uretilen tahmin:", len(BOSLUK) * 200,
      " bosluktaki donemler:", BOSLUK)
print("bunlarin sonradan yanlis oldugu anlasilan sayisi:",
      sum(round((1 - GERCEK[d]) * 200) for d in BOSLUK),
      " kural degismeseydi olacak sayi:",
      sum(round((1 - isabet(S, MODEL, OZ, d)) * 200) for d in BOSLUK))
```

```
simdi      olculebilen son donem  o donemin isabeti  simdiki gercek isabet  olculmemis tahmin
2026-07                        -                  -                 0.9700                200
2026-08                        -                  -                 0.9700                400
2026-09                  2026-07             0.9700                 0.8150                400
2026-10                  2026-08             0.9700                 0.8700                400
2026-11                  2026-09             0.8150                 0.8650                400
2026-12                  2026-10             0.8700                 0.8500                400

bozulmanin oldugu donem: 2026-09  uyarinin atesleyebildigi donem: 2026-11  gorulme gecikmesi: 2 donem
bu boslukta olculmeden uretilen tahmin: 400  bosluktaki donemler: ['2026-09', '2026-10']
bunlarin sonradan yanlis oldugu anlasilan sayisi: 63  kural degismeseydi olacak sayi: 7
```

Bozulmanın olduğu dönem `2026-09`'dur ve o dönemde ekranda duran sayı **0,9700**'dür. Bu sayı
yanlış değildir; `2026-07`'nin isabeti gerçekten 0,9700'dü. Sayı yalnızca **eskidir** ve eskiliği
hiçbir yerde yazmıyorsa yanlıştan ayırt edilemez. Bir sonraki dönemde de aynı şey olur: gerçek
isabet 0,8700'e inmişken elde olan en yeni ölçü hâlâ 0,9700'dür.

Uyarının ateşleyebildiği ilk dönem **2026-11**'dir, çünkü `2026-09`'un etiketleri ancak o zaman
tamamlanır. **Yakalama gecikmesi iki dönemdir** ve bu boşlukta **400 tahmin** ölçülmeden
üretilmiştir. Bu 400 tahminin **63'ünün** yanlış olduğu sonradan anlaşılır; kural değişmeseydi
aynı iki dönemde yanlış sayısı **7** olacaktı. Aradaki 56 tahmin, bozulmanın görülmediği süre
boyunca üretilip kullanılmış olanlardır ve geriye dönüp düzeltilemezler, çünkü karar zaten
verilmiştir.

Dikkat edilecek ikinci şey son sütundur: boşluk kapandıktan sonra da her dönemde **400 tahmin**
ölçülmemiş durumda kalır. Gecikme geçici bir başlangıç sorunu değil, **sürekli taşınan bir
borçtur**; üretimdeki gösterge hiçbir zaman şimdiyi göstermez, her zaman iki dönem öncesini
gösterir.

#### Gecikmenin Bedeli

Üçüncü blok gecikmeyi süpürür. Kurulumun geri kalanı sabittir; değişen tek şey etiketin kaç dönem
sonra geldiğidir. Son satır izlemesiz taban çizgisidir: etiket hiç toplanmaz.

```python
SON = len(IZLEME) - IZLEME.index(BOZULAN)     # bozulmadan sonraki donem sayisi
print(f"{'gecikme':<12}{'bozulma gorulur':>18}{'gorulme gecikmesi':>19}"
      f"{'olculebilen donem':>19}{'olculmeden uretilen':>21}")
for g in (0, 1, 2, 3, 4):
    gor = next((s for s in IZLEME
                if any(gorulen_donem(d, s, g) and abs(GERCEK[d] - TABAN) / TABAN > BANT
                       for d in IZLEME)), None)
    gec = DONEM.index(gor) - DONEM.index(BOZULAN) if gor else SON
    print(f"{g:<12}{(gor or 'gorulmedi'):>18}{(gec if gor else '-'):>19}"
          f"{sum(1 for d in IZLEME if gorulen_donem(d, IZLEME[-1], g)):>19}{gec * 200:>21}")
print(f"{'etiket yok':<12}{'gorulmedi':>18}{'-':>19}{0:>19}{SON * 200:>21}")
```

```
gecikme        bozulma gorulur  gorulme gecikmesi  olculebilen donem  olculmeden uretilen
0                      2026-09                  0                  6                    0
1                      2026-10                  1                  5                  200
2                      2026-11                  2                  4                  400
3                      2026-12                  3                  3                  600
4                    gorulmedi                  -                  2                  800
etiket yok           gorulmedi                  -                  0                  800
```

Bedel doğrusaldır ve birimi bellidir: **her dönemlik gecikme 200 ölçülmemiş tahmin** demektir.
Dördüncü satırda gecikme, izleme penceresinin sonuna dayanır; bozulma `2026-09`'da olmuştur ama
etiketi `2027-01`'de geleceği için pencere içinde **hiç görülmez**. Bu satır önemlidir, çünkü
gecikme yeterince büyüdüğünde ölçü **var olmayı sürdürür ama işe yaramaz**.

Son satır izlemesiz taban çizgisidir ve gecikme 4 ile aynı sonucu verir: bozulma görülmez, 800
tahmin ölçülmeden üretilir. Aradaki tek fark ölçülebilen dönem sayısındadır, **2**'ye karşı
**0**. Kursun kuralı burada da geçerlidir: bir gösterge ancak **ateşleyebildiği zaman** ölçülmüş
sayılır ve ateşlemesi pencere dışına düşen bir gösterge ölçülmemiş bir güvence verir.

#### Özet

- İsabet, girdi özeti ile tahmin payından farklı olarak **gerçek etiket** ister; etiket üretimde
  tahminle aynı anda gelmez ve bu kurulumda **iki dönem** sonra gelir.
- Bozulma `2026-09`'da olur, ama uyarının ateşleyebildiği ilk dönem **2026-11**'dir: görülme
  gecikmesi **iki dönem** ve bu boşlukta **400 tahmin** ölçülmeden üretilmiştir; bunların
  **63'ünün** yanlış olduğu sonradan anlaşılır, kural değişmeseydi sayı **7** olacaktı.
- `2026-09`'da ekrandaki sayı 0,9700'dür ve yanlış değildir, yalnızca **iki dönem eskidir**;
  eskiliği yazılmayan bir gösterge yanlış bir göstergeden ayırt edilemez.
- Boşluk kapandıktan sonra da her dönemde 400 tahmin ölçülmemiş kalır: gecikme geçici değil,
  **sürekli taşınan bir borçtur**.
- Süpürme bedeli doğrusal verir: gecikme 0'da 0, 1'de 200, 2'de 400, 3'te 600 ölçülmemiş tahmin;
  4'te bozulma izleme penceresi içinde hiç görülmez ve izlemesiz taban çizgisinde ölçülebilen
  dönem sayısı **0**'dır.

#### Sonraki Adım

Üç derste bozulmanın **görülmesi** ölçüldü ve son ölçü bir boşluk bıraktı: bozulma olduktan sonra
**iki dönem** boyunca tek gören ölçü suskundur ve o boşlukta 400 tahmin ölçülmeden üretilir. Bu
boşlukta elde ne olduğu ise hiç sorulmadı. Girdi özeti ve tahmin payı oradadır, etiket
beklemeden hesaplanırlar ve önceki derste ikisinin de yanıltabildiği görüldü. Sonraki ders bunları
**vekil ölçüt** olarak ele alır ve tek tek ne gördüklerini sayar: hangi vekil kavram kaymasını
görebiliyor, hangisi zararsız kaymada ateşliyor ve girdi eksiksizliği gibi üçüncü bir vekil
eklendiğinde tablo değişiyor mu.

### Vekil Ölçütler

Source: https://academia.sh/tr/kurslar/model-izleme/vekil-olcutler

Etiket gelmeden önceki iki dönemde elde kalan ölçülerin sayılması: girdi özeti, tahmin payı ve girdi eksiksizliği üç senaryoda yan yana konduğunda kavram kaymalı kurulumun üç vekili sağlam kurulumun üç vekiliyle hane hane aynı çıkar, çünkü kural değişmiştir ve girdi değişmemiştir. Tahmin payı sağlam kurulumda bile 0,0957 sapıyor ve 0,05 bandını aşıyor, veri kaymalı kurulumda 0,1596'ya çıkıyor ama o kurulumda isabet 0,9700'den 0,9800'e gidiyor; girdi özeti gerçekten kayan tek senaryoda 0,0382'de kalıp ateşlemiyor, çünkü kayma 200 abonenin 102'sinde ve ortalama onu seyreltiyor; girdi eksiksizliği üç senaryoda da 0,0000 sapıyor ve tek bir dönemin okuması hiç gelmediğinde iki dönemi birden 0,8000'e düşürerek çalıştığını gösteriyor. Senaryoları ayıran tek ölçü isabettir ve iki dönem sonra gelir.

Bir önceki ders gecikmeli etiketi saydı: bir dönemin isabeti ancak `GECIKME = 2` dönem sonra
biliniyor, bozulmanın olduğu an ile görüldüğü an arasında iki dönemlik bir boşluk var ve o boşlukta
tahmin üretilmeye devam ediyor. Üç derste bozulmanın **iki dönem geç** görüldüğü ölçüldü.

Sorulmayan şey o iki dönemde **elde ne olduğudur**. İsabet yoktur, çünkü etiket gelmemiştir. Elde
yalnız etiket gerektirmeyen ölçüler kalır: girdinin kendisinden ve modelin çıktısından okunabilenler.
Bunlara **vekil ölçüt (proxy metric)** denir, çünkü ölçmek istediğimiz şeyin kendisi değil, onunla
ilişkili olduğu **varsayılan** bir şeydir. Bu ders üç vekili üç senaryoda yan yana koyar ve tek soru
sorar: hangisi kavram kaymasını görüyor, hangisi görmediği bir şey için ateşliyor.

- **MI41.** Kaynak, beş aşamalı hat, kurgu model ve iki kayma türü **kurgudur** ve ders içinde
  üretilir. Hiçbir izleme ürünü, gösterge servisi, bulut servisi ya da öğrenme kitaplığı çağrılmaz.
  Tohum 20260218.
- **MI42.** Üç vekil şunlardır: **girdi özeti**, dönemdeki abone ortalamalarının ortalaması;
  **tahmin payı**, modelin 1 dediği satırların oranı; **girdi eksiksizliği**, dönemde oluşan satırın
  beklenen 200 satıra oranı. Üçü de **etiket okumaz**, bu yüzden gecikmesizdir.
- **MI43.** Üç senaryo: **sağlam**; **veri kaymalı**, `2026-09`'dan sonra işyeri tüketimi dönem
  başına 0,06 büyür; **kavram kaymalı**, girdi aynı kalır ve `abone_ort + 6 x isyeri > 30` kuralının
  eşiği 27'ye iner.
- **MI44. Kayma (drift)** yordamı — türlerinin ayrımı, ölçümü ve düzeltmesi — Makine Öğrenmesi ve
  Veri Bilimi müfredatındaki Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda ölçüldü;
  **tekrarlanmaz**. Buradaki ek yalnız üretimdeki ömre aittir: etiket yokken vekilin ne gördüğü.
- **MI45.** Model, MLOps Temelleri kursunun kusuru düzeltilmiş biçimde devralınır: karar eşiği ayrı
  bir ayardır ve eğitimde aranır, eğitim ölçütü **dengeli isabettir**, etiket özniteliklerin gördüğü
  bir kurala bağlıdır. İzleme dönemlerinde **taban sınıf oranı 0,5342**.
- **MI46.** Taban çizgisi **izlemesiz kurulumdur**: gösterge yok, uyarı yok, bozulma görülmez. Dönem
  isabetinin adımı **0,0050** ve bu adımın altındaki fark okunmaz.

#### Etiket Gelmeden Önce Elde Ne Var

Aşağıdaki blok ortak tanımı kurar — kaynak, hat, öznitelik, kurgu model, iki kayma türü ve üç ölçü —
ve modeli eğitip taban çizgisini basar. Öznitelikler senaryo başına **bir kez** hesaplanır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat , kurgu model ve iki kayma
# turu ders icinde uretilir; hicbir izleme urunu , gosterge servisi , bulut servisi ya
# da ogrenme kitapligi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad not in gor:
            gor.add(ad)
            tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is None:
            dusen += i > 0
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar(kaynak=None):
    v, _ = a1_ayikla(kaynak_uret() if kaynak is None else kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, pencere=None, basamak=4):
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if (d < i and (pencere is None or d >= i - pencere))]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    oz = {}
    for k in kayitlar:
        i = DONEM.index(k["donem"])
        oz[(k["abone"], k["donem"])] = {"abone_ort": ortalama(ab[k["abone"]], i),
                                        "bolge_ort": ortalama(bo[k["bolge"]], i),
                                        "isyeri": 1 if k["tip"] == "isyeri" else 0}
    return oz


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)     # karar esigi AYRI bir ayardir , egitimde aranir
IZLEME = DONEM[6:]                      # 2026-07 .. 2026-12 : uretimde izlenen donemler
GECIKME = 2                             # etiket kac donem sonra geliyor
KURAL_ESIK, KURAL_ISYERI = 30, 6        # KAVRAM: abone_ort + 6 x isyeri > 30


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    dp = dn = yp = yn = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o_k, w, karar), etiket(o_k)
        dp, yp = dp + (t and g), yp + (t and not g)
        yn, dn = yn + (g and not t), dn + (not t and not g)
    duyarlilik = dp / (dp + yn) if dp + yn else 0.0
    ozgulluk = dn / (dn + yp) if dn + yp else 0.0
    return (duyarlilik + ozgulluk) / 2, round(duyarlilik, 4), round(ozgulluk, 4)


def egit(kayitlar, oz, donemler=None):
    """Egitim olcutu DENGELI ISABETTIR: duyarlilik ile ozgullugun ortalamasi."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    en = (-1.0, None, None, 0.0, 0.0)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            o, du, oz_ = dengeli_isabet(kume, oz, w, karar)
            if o > en[0]:
                en = (o, w, karar, du, oz_)
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "duyarlilik": en[3], "ozgulluk": en[4], "egitim_satiri": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """VERI KAYMASI: baslangictan sonra isyeri tuketimi her donem egim kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    """KAVRAM KAYMASI: girdi AYNI , kural degisiyor. Esik 30'dan yeni_esik'e iniyor."""
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]): etiket(
        oz[(k["abone"], k["donem"])],
        yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK) for k in kayitlar}


def girdi_ozeti(kayitlar, donem, oz):
    d = [oz[(k["abone"], k["donem"])]["abone_ort"] for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4) if d else 0.0


def tahmin_payi(kayitlar, model, oz, donem):
    d = [tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
         for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4) if d else 0.0


def isabet(kayitlar, model, oz, donem, etiketler=None):
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = sum(1 for k in kume
            if tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
            == (etiketler[(k["abone"], k["donem"])] if etiketler
                else etiket(oz[(k["abone"], k["donem"])])))
    return round(d / len(kume), 4) if kume else None


SATIR = satirlar()
OZ = oznitelikler(SATIR)                  # oznitelik BIR KEZ hesaplanir
MODEL = egit(SATIR, OZ)
IZL = [k for k in SATIR if k["donem"] in IZLEME]
buyuk = max(sum(1 for k in IZL if etiket(OZ[(k["abone"], k["donem"])]) == s) for s in (0, 1))
print(f"model {MODEL['agirlik']} karar esigi {MODEL['karar']} , dengeli isabet"
      f" {MODEL['dengeli']} , duyarlilik {MODEL['duyarlilik']} , ozgulluk {MODEL['ozgulluk']}")
print(f"izleme {len(IZLEME)} donem , {len(IZL)} satir , isyeri abone"
      f" {sum(1 for a in ABONE.values() if a['tip'] == 'isyeri')} , taban sinif orani"
      f" {buyuk / len(IZL):.4f} , donem isabetinin adimi {1 / 200:.4f}")
```

```
model (0.6, 0.0, 4) karar esigi 18 , dengeli isabet 0.9864 , duyarlilik 1.0 , ozgulluk 0.9728
izleme 6 donem , 1200 satir , isyeri abone 102 , taban sinif orani 0.5342 , donem isabetinin adimi 0.0050
```

Taban sınıf oranı **0,5342** olduğu için küme dengelidir: hep aynı sınıfı söyleyen bir yordam bu
oranı alır ve modelin 0,9864'ü onun çok üstündedir. Model izlenebilir durumdadır. İkinci satırdaki
son iki sayı bütün dersi sınırlar: kaymanın dokunduğu abone **102** tanedir, geri kalan 98'i
dokunulmamıştır; ve dönem isabetinin adımı **0,0050** olduğu için bundan küçük hiçbir hareket
okunmaz.

#### Üç Vekil, Üç Senaryo

Aşağıdaki blok üç ölçüyü üç senaryonun altı izleme döneminde hesaplar ve dördüncü satır olarak
isabeti ekler. Son sütun, `2026-08` tabanına göre sonraki dört dönemde görülen **en büyük göreli
sapmadır**; bir uyarı bandı tam olarak bu sayıyla karşılaştırılır.

```python
# Bu blok ilk bloktaki tanimlari surdurur. Her senaryonun oznitelikleri BIR KEZ hesaplanir.
def eksiksizlik(kayitlar, donem, beklenen=200):
    """UCUNCU VEKIL: donemde kac satir olustu. Deger okumaz , varlik sayar."""
    return round(sum(1 for k in kayitlar if k["donem"] == donem) / beklenen, 4)


KAYAN = kaydir(SATIR)
OZ_K = oznitelikler(KAYAN)
KAVRAM = kavram_kaydir(SATIR, OZ)
SENARYO = (("saglam", SATIR, OZ, None), ("veri kaymali", KAYAN, OZ_K, None),
           ("kavram kaymali", SATIR, OZ, KAVRAM))
OLCU = (("girdi ozeti", lambda kay, oz, et, d: girdi_ozeti(kay, d, oz)),
        ("tahmin payi", lambda kay, oz, et, d: tahmin_payi(kay, MODEL, oz, d)),
        ("eksiksizlik", lambda kay, oz, et, d: eksiksizlik(kay, d)),
        ("isabet", lambda kay, oz, et, d: isabet(kay, MODEL, oz, d, et)))

print("olcu         senaryo        " + "".join(f"{d[-2:]:>8}" for d in IZLEME) + "   sapma")
for ad, f in OLCU:
    for sad, kay, oz, et in SENARYO:
        dizi = [f(kay, oz, et, d) for d in IZLEME]
        sapma = max(abs(v - dizi[1]) / dizi[1] for v in dizi[2:])
        print(f"{ad:<12} {sad:<14}" + "".join(f"{v:8.4f}" for v in dizi) + f"  {sapma:.4f}")

HAM = [k for k in kaynak_uret() if not (k["abone"] <= "A040" and k["donem"] == "2026-10")]
GEC = satirlar(HAM)
print("\nkontrol , 40 abonenin 2026-10 okumasi hic gelmedi:")
print("eksiksizlik " + "".join(f"{eksiksizlik(GEC, d):8.4f}" for d in IZLEME))
```

```
olcu         senaryo              07      08      09      10      11      12   sapma
girdi ozeti  saglam         25.9220 26.0517 25.9800 26.3525 26.3261 26.5325  0.0185
girdi ozeti  veri kaymali   25.9220 26.0517 25.9800 26.4685 26.6053 27.0472  0.0382
girdi ozeti  kavram kaymali 25.9220 26.0517 25.9800 26.3525 26.3261 26.5325  0.0185
tahmin payi  saglam          0.4550  0.4700  0.4550  0.5100  0.5100  0.5150  0.0957
tahmin payi  veri kaymali    0.4550  0.4700  0.4550  0.5150  0.5200  0.5450  0.1596
tahmin payi  kavram kaymali  0.4550  0.4700  0.4550  0.5100  0.5100  0.5150  0.0957
eksiksizlik  saglam          1.0000  1.0000  1.0000  1.0000  1.0000  1.0000  0.0000
eksiksizlik  veri kaymali    1.0000  1.0000  1.0000  1.0000  1.0000  1.0000  0.0000
eksiksizlik  kavram kaymali  1.0000  1.0000  1.0000  1.0000  1.0000  1.0000  0.0000
isabet       saglam          0.9700  0.9700  0.9900  0.9750  0.9850  0.9900  0.0206
isabet       veri kaymali    0.9700  0.9700  0.9900  0.9750  0.9800  0.9800  0.0206
isabet       kavram kaymali  0.9700  0.9700  0.8150  0.8700  0.8650  0.8500  0.1598

kontrol , 40 abonenin 2026-10 okumasi hic gelmedi:
eksiksizlik   1.0000  1.0000  1.0000  0.8000  0.8000  1.0000
```

Tablonun en sert yeri **sağlam** ile **kavram kaymalı** satırlarının karşılaştırılmasıdır. Üç vekilin
üçü de bu iki senaryoda **hane hane aynı** sayıyı veriyor: 26,5325 ile 26,5325, 0,5150 ile 0,5150,
1,0000 ile 1,0000. Bu bir yaklaşıklık değil, kimliktir. Kavram kayması tanımı gereği girdiye
dokunmaz, yalnız kuralı değiştirir; girdiden okunan hiçbir ölçü de o değişimi taşıyamaz. Aynı
dönemlerde isabet 0,9900'den 0,8500'e iniyor ve son sütunda sapma 0,0206'dan 0,1598'e çıkıyor.
**Üç vekilden hiçbiri kavram kaymasını görmüyor.**

#### Ateşleyen Vekil ve Susan Vekil

Tahmin payı satırı ikinci okumayı verir. Sapması **sağlam** kurulumda bile 0,0957'dir; yani hiçbir
şeyin bozulmadığı bir üretimde 0,05'lik bir bant ateşler. Veri kaymalı kurulumda 0,1596'ya çıkıp
0,10'luk bandı da aşar — ve tam o kurulumda isabet 0,9700'den 0,9800'e **çıkar**. Vekil, başarımın
düzeldiği senaryoda en yüksek alarmı verir. Bu, kursun kuralının en çıplak biçimidir: bir göstergenin
ateşlemesi bozulmanın kanıtı değildir. Nedeni de görünür: kural değişmedikçe doğru bir model kaymış
girdide de doğru kalır, yalnız daha çok satıra 1 der.

Girdi özeti ters yönde şaşırtır. Girdinin **gerçekten** kaydığı tek senaryoda sapması 0,0382'de
kalır ve 0,05'i aşmaz. Sayı yanlış değildir: kayma 200 abonenin **102'sine** dokunur, ortalama bunu
kalan 98 aboneyle böler ve 25,9220'den 27,0472'ye giden hareket bandın altında kalır. Bir vekilin
neye baktığı kadar **hangi kırılımda** baktığı da ölçünün parçasıdır; kırılımsız ortalama, dokunduğu
kesim küçüldükçe kaymayı seyreltir.

Girdi eksiksizliği üç senaryoda da 0,0000 sapar. Bu, ölçünün bozuk olduğu anlamına gelmez; kontrol
koşumu bunu ayırır. Kaynakta 40 abonenin `2026-10` okuması hiç gelmediğinde eksiksizlik **iki**
dönemi birden 0,8000'e düşürür — Veri Mühendisliğine Giriş kursunun `gec_gelen` sınıfı budur ve iki
dönem çıkmasının nedeni tüketimin bir önceki dönemin endeksine bağlı olmasıdır. Ölçü çalışıyor;
yalnız **başka bir bozulma sınıfını** görüyor. Kaymanın hiçbir türü satır düşürmez, bu yüzden
eksiksizlik kayma izlemesinde toplanma bedelini öder ve karşılığında sıfır bilgi verir.

Dört satırı birlikte okumak dersin cevabıdır. Senaryoları ayıran tek ölçü **isabettir**: 0,0206
karşısında 0,1598, yani otuz bir çözünürlük adımı. Ama isabet etiket ister ve etiket iki dönem sonra
gelir. Elde gecikmesiz olan üç ölçü vardır ve üçü de ayırt etmez; ayırt eden tek ölçü gecikmelidir.
**Vekil ölçüt, gecikmenin bedelini ödemekten kurtarmaz.**

#### Özet

- Üç vekil — girdi özeti, tahmin payı, girdi eksiksizliği — etiket okumaz ve gecikmesizdir; bu
  yüzden gecikmeli etiketin bıraktığı iki dönemlik boşlukta elde kalan tek ölçü sınıfıdır.
- Kavram kaymalı senaryoda üç vekilin üçü de sağlam senaryoyla **hane hane aynı** sayıyı verir
  (26,5325 / 0,5150 / 1,0000), çünkü kural değişir ve girdi değişmez. Sapmalar 0,0185, 0,0957 ve
  0,0000'dır; **hiçbiri kavram kaymasını görmez**.
- Tahmin payı sağlam kurulumda 0,0957 sapar ve 0,05 bandını aşar; veri kaymalı kurulumda 0,1596'ya
  çıkar, o kurulumda isabet 0,9700'den 0,9800'e gider. **Zararsız kaymada ateşleyen vekil budur.**
- Girdi özeti, girdinin gerçekten kaydığı tek senaryoda 0,0382'de kalıp ateşlemez: kayma 200
  abonenin 102'sine dokunur ve kırılımsız ortalama onu seyreltir.
- Girdi eksiksizliği üç senaryoda da 0,0000 sapar ama bozuk değildir: 40 abonenin bir dönem okuması
  gelmediğinde iki dönemi birden 0,8000'e düşürür. Kayma satır düşürmez, bu yüzden vekil burada
  ölçülebilir katkı vermez.
- Senaryoları ayıran tek ölçü isabettir (0,1598'e karşı 0,0206, otuz bir çözünürlük adımı) ve o da
  iki dönem sonra gelir.

#### Sonraki Adım

Bu dersin son sütunu bir sapma sayısıydı ve o sayı tek başına hiçbir şey yapmaz; bir **bant**la
karşılaştırıldığında karara dönüşür. 0,05 dendiğinde tahmin payı üç senaryoda da konuşur, 0,20
dendiğinde hiçbirinde konuşmaz. Sonraki ders bandı değişken yapar: 0,05, 0,10 ve 0,20'de üç ölçünün
kaç dönem ateşlediğini, bu ateşlemelerin kaçının **gerçek bozulmaya** karşılık geldiğini ve kaçının
yanlış alarm olduğunu sayar; sonunda hiç yanlış alarm vermeyen bandın tek gerçek bozulmayı da
kaçırdığı görülür.

### Uyarı ve Eşikler

Source: https://academia.sh/tr/kurslar/model-izleme/uyari-ve-esikler

Uyarı bandının daraldıkça ne kazandırıp ne kaybettirdiğinin sayılması: 0,05 bandında üç ölçü ve üç senaryo boyunca 13 dönem ateşliyor, bunların 6'sı yanlış alarm; 0,10'da 6 ateşleme ve 2 yanlış alarm; 0,20'de hiç yanlış alarm yok ama gerçek bozulmanın dördü de kaçıyor, çünkü 0,20'lik göreli bant 0,9700 tabanında isabetin 0,7760'ın altına inmesini ister ve en dip değer 0,8150'dir. Tahmin payı 0,05 bandında hem sağlam kurulumda hem kavram kaymalı kurulumda tam üç dönem ateşliyor; aynı ateşleme bir yerde saf yanlış alarm, öbür yerde yakalama sayılıyor, bu yüzden ölçülmemiş sayılır. Yalnız isabet iki zararsız senaryoda hiç ateşlemeyip zararlı senaryoda dört dönem ateşliyor ve yakalama gecikmesi iki dönem.

Bir önceki ders üç vekilin sapmasını saydı ve son sütunda tek bir sayı bıraktı: `2026-08` tabanına
göre en büyük göreli sapma. O sayı tek başına bir karar vermez. Karar, sapmanın bir **bantla**
karşılaştırılmasıyla doğar; bandın adı **uyarı eşiğidir (alert threshold)** ve bir ölçünün değil,
**bir seçimin** parçasıdır.

Seçim ucuz görünür ve değildir. Bant daraldıkça daha çok dönem ateşler; ateşleyen dönemlerin kaçının
gerçekten bozulmuş olduğu ise bandın bilmediği bir şeydir. Bu ders bandı 0,05, 0,10 ve 0,20'de
sabitler, üç ölçüyü üç senaryoda koşturur ve iki sayıyı ayrı ayrı yazar: **ateşleyen dönem** ile
**bunların kaçının gerçek bozulmaya karşılık geldiği**.

- **MI47.** Kaynak, hat, kurgu model ve iki kayma türü **kurgudur** ve ders içinde üretilir. Hiçbir
  izleme ürünü, gösterge servisi, bulut servisi ya da öğrenme kitaplığı çağrılmaz. Tohum 20260218.
- **MI48. Uyarı eşiği**, bir ölçünün `2026-08` tabanından **göreli** sapmasının aşmaması gereken
  paydır. Üç bant sınanır: 0,05, 0,10 ve 0,20. Taban dönemi kaymaların hiçbirinin başlamadığı son
  dönemdir.
- **MI49. Gerçek bozulma** banttan bağımsız tanımlanır: bir dönemin **gerçek** isabetinin `2026-08`
  tabanından **0,0500'den fazla** düşmesi, yani on çözünürlük adımı. Bu ölçü değerlendirme içindir;
  üretimde iki dönem gecikmeyle bilinir.
- **MI50. Yanlış alarm (false positive)**, ateşleyen ama gerçek bozulma listesinde olmayan dönemdir.
  Gösterge gecikmesinin ölçümü Veri Hatları ve Orkestrasyon kursunun hat gözlemlenebilirliği
  dersinde yapıldı ve **tekrarlanmaz**. Buradaki tek ek **gecikmeli etikettir**: isabet üzerinden
  ateşleyen bir uyarının gecikmesine `GECIKME` eklenir.
- **MI51.** Model MLOps Temelleri kursundan düzeltilmiş biçimde devralınır: karar eşiği ayrı bir
  ayardır, eğitim ölçütü **dengeli isabettir**. İzleme dönemlerinde **taban sınıf oranı 0,5342**.
- **MI52.** Taban çizgisi **izlemesiz kurulumdur**: bant yok, ateşleyen dönem 0, yakalama gecikmesi
  tanımsız. Dönem isabetinin adımı **0,0050**.

#### Uyarı Eşiği Bir Karşılaştırmadır

Aşağıdaki blok ortak tanımı kurar ve üzerine iki şey ekler: bir ölçü dizisini tabanla karşılaştıran
`uyari` ve bir dönemin isabetinin hangi dönemde bilinebildiğini söyleyen `gorulen_donem`.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat , kurgu model ve iki kayma
# turu ders icinde uretilir; hicbir izleme urunu , gosterge servisi , bulut servisi ya
# da ogrenme kitapligi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad not in gor:
            gor.add(ad)
            tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is None:
            dusen += i > 0
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar(kaynak=None):
    v, _ = a1_ayikla(kaynak_uret() if kaynak is None else kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, pencere=None, basamak=4):
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if (d < i and (pencere is None or d >= i - pencere))]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    oz = {}
    for k in kayitlar:
        i = DONEM.index(k["donem"])
        oz[(k["abone"], k["donem"])] = {"abone_ort": ortalama(ab[k["abone"]], i),
                                        "bolge_ort": ortalama(bo[k["bolge"]], i),
                                        "isyeri": 1 if k["tip"] == "isyeri" else 0}
    return oz


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)     # karar esigi AYRI bir ayardir , egitimde aranir
IZLEME = DONEM[6:]                      # 2026-07 .. 2026-12 : uretimde izlenen donemler
GECIKME = 2                             # etiket kac donem sonra geliyor
KURAL_ESIK, KURAL_ISYERI = 30, 6        # KAVRAM: abone_ort + 6 x isyeri > 30


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    dp = dn = yp = yn = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o_k, w, karar), etiket(o_k)
        dp, yp = dp + (t and g), yp + (t and not g)
        yn, dn = yn + (g and not t), dn + (not t and not g)
    duyarlilik = dp / (dp + yn) if dp + yn else 0.0
    ozgulluk = dn / (dn + yp) if dn + yp else 0.0
    return (duyarlilik + ozgulluk) / 2, round(duyarlilik, 4), round(ozgulluk, 4)


def egit(kayitlar, oz, donemler=None):
    """Egitim olcutu DENGELI ISABETTIR: duyarlilik ile ozgullugun ortalamasi."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    en = (-1.0, None, None, 0.0, 0.0)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            o, du, oz_ = dengeli_isabet(kume, oz, w, karar)
            if o > en[0]:
                en = (o, w, karar, du, oz_)
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "duyarlilik": en[3], "ozgulluk": en[4], "egitim_satiri": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """VERI KAYMASI: baslangictan sonra isyeri tuketimi her donem egim kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    """KAVRAM KAYMASI: girdi AYNI , kural degisiyor. Esik 30'dan yeni_esik'e iniyor."""
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]): etiket(
        oz[(k["abone"], k["donem"])],
        yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK) for k in kayitlar}


def girdi_ozeti(kayitlar, donem, oz):
    d = [oz[(k["abone"], k["donem"])]["abone_ort"] for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4) if d else 0.0


def tahmin_payi(kayitlar, model, oz, donem):
    d = [tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
         for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4) if d else 0.0


def isabet(kayitlar, model, oz, donem, etiketler=None):
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = sum(1 for k in kume
            if tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
            == (etiketler[(k["abone"], k["donem"])] if etiketler
                else etiket(oz[(k["abone"], k["donem"])])))
    return round(d / len(kume), 4) if kume else None


def uyari(dizi, taban, pay):
    """Bir olcu tabandan `pay` kadar saparsa o donem ates eder."""
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


def gorulen_donem(donem, simdi):
    """Gecikmeli etiket: bir donemin isabeti ancak GECIKME donem sonra bilinir."""
    return DONEM.index(donem) + GECIKME <= DONEM.index(simdi)


SATIR = satirlar()
OZ = oznitelikler(SATIR)                  # oznitelik BIR KEZ hesaplanir
MODEL = egit(SATIR, OZ)
IZL = [k for k in SATIR if k["donem"] in IZLEME]
buyuk = max(sum(1 for k in IZL if etiket(OZ[(k["abone"], k["donem"])]) == s) for s in (0, 1))
print(f"model {MODEL['agirlik']} karar esigi {MODEL['karar']} , dengeli isabet"
      f" {MODEL['dengeli']} , {len(IZL)} satir , taban sinif orani {buyuk / len(IZL):.4f} ,"
      f" adim {1 / 200:.4f} , 2026-09'un isabeti"
      f" {[d for d in DONEM if gorulen_donem('2026-09', d)][0]} doneminde bilinir")
```

```
model (0.6, 0.0, 4) karar esigi 18 , dengeli isabet 0.9864 , 1200 satir , taban sinif orani 0.5342 , adim 0.0050 , 2026-09'un isabeti 2026-11 doneminde bilinir
```

Son alan bandın ölçemediği şeyi baştan söyler: `2026-09`'da bir şey bozulsa bile o dönemin isabeti
`2026-11`'de bilinir. İsabet üzerine kurulan bir uyarı, bandı ne kadar dar olursa olsun **iki dönem
sonra** konuşur. Bandın oynatabildiği tek şey, gecikmesiz vekillerin kaç kez konuşacağıdır.

#### Bant Daraldıkça

Aşağıdaki blok üç ölçüyü üç senaryoda üç bantla karşılaştırır. Her hücre iki sayı taşır: **ateşleyen
dönem** ve bunların kaçının **yanlış alarm** olduğu. `bozulan` sütunu banttan bağımsızdır; o
senaryoda gerçekten bozulmuş dönem sayısını verir.

```python
# Bu blok ilk bloktaki tanimlari surdurur. Her senaryonun oznitelikleri BIR KEZ hesaplanir.
BANT = (0.05, 0.10, 0.20)
DUSUS = 0.0500        # GERCEK BOZULMA: isabetin tabandan bu kadardan fazla dusmesi
KAYAN = kaydir(SATIR)
OZ_K = oznitelikler(KAYAN)
KAVRAM = kavram_kaydir(SATIR, OZ)
SENARYO = (("saglam", SATIR, OZ, None), ("veri kaymali", KAYAN, OZ_K, None),
           ("kavram kaymali", SATIR, OZ, KAVRAM))
OLCU = (("girdi ozeti", lambda kay, oz, et, d: girdi_ozeti(kay, d, oz)),
        ("tahmin payi", lambda kay, oz, et, d: tahmin_payi(kay, MODEL, oz, d)),
        ("isabet", lambda kay, oz, et, d: isabet(kay, MODEL, oz, d, et)))
ATES, GERCEK = {}, {}
for sad, kay, oz, et in SENARYO:
    isb = [(d, isabet(kay, MODEL, oz, d, et)) for d in IZLEME]
    GERCEK[sad] = set(d for d, v in isb[2:] if dict(isb)["2026-08"] - v > DUSUS)
    for ad, f in OLCU:
        dizi = [(d, f(kay, oz, et, d)) for d in IZLEME]
        for b in BANT:
            ATES[(sad, ad, b)] = set(uyari(dizi[2:], dict(dizi)["2026-08"], b))

print(f"gercek bozulma: isabetin 2026-08 tabanindan {DUSUS:.4f} uzerinde dusmesi")
print("hucre: atesleyen donem / yanlis alarm\n")
print("olcu         senaryo         bozulan " + "".join(f"{b:>12.2f}" for b in BANT))
for ad, _ in OLCU:
    for sad, *_ in SENARYO:
        h = [f"{len(ATES[(sad, ad, b)])} / {len(ATES[(sad, ad, b)] - GERCEK[sad])}"
             for b in BANT]
        print(f"{ad:<12} {sad:<14} {len(GERCEK[sad]):>7} " + "".join(f"{x:>12}" for x in h))

print("\nbant  toplam ates  yanlis alarm  yakalanan  gecikme: tahmin payi  isabet")
for b in BANT:
    t = [sum(len(ATES[(s, a, b)]) for s, *_ in SENARYO for a, _ in OLCU),
         sum(len(ATES[(s, a, b)] - GERCEK[s]) for s, *_ in SENARYO for a, _ in OLCU),
         sum(len(ATES[(s, a, b)] & GERCEK[s]) for s, *_ in SENARYO for a, _ in OLCU)]
    g = []
    for ad, ek in (("tahmin payi", 0), ("isabet", GECIKME)):
        a = sorted(ATES[("kavram kaymali", ad, b)])
        g.append(str(DONEM.index(a[0]) - DONEM.index("2026-09") + ek) if a else "tanimsiz")
    print(f"{b:<5.2f} {t[0]:>11} {t[1]:>13} {t[2]:>10} {g[0]:>21} {g[1]:>7}")
```

```
gercek bozulma: isabetin 2026-08 tabanindan 0.0500 uzerinde dusmesi
hucre: atesleyen donem / yanlis alarm

olcu         senaryo         bozulan         0.05        0.10        0.20
girdi ozeti  saglam               0        0 / 0       0 / 0       0 / 0
girdi ozeti  veri kaymali         0        0 / 0       0 / 0       0 / 0
girdi ozeti  kavram kaymali       4        0 / 0       0 / 0       0 / 0
tahmin payi  saglam               0        3 / 3       0 / 0       0 / 0
tahmin payi  veri kaymali         0        3 / 3       2 / 2       0 / 0
tahmin payi  kavram kaymali       4        3 / 0       0 / 0       0 / 0
isabet       saglam               0        0 / 0       0 / 0       0 / 0
isabet       veri kaymali         0        0 / 0       0 / 0       0 / 0
isabet       kavram kaymali       4        4 / 0       4 / 0       0 / 0

bant  toplam ates  yanlis alarm  yakalanan  gecikme: tahmin payi  isabet
0.05           13             6          7                     1       2
0.10            6             2          4              tanimsiz       2
0.20            0             0          0              tanimsiz tanimsiz
```

#### Yakalama Sayısının Geçersiz Olduğu Yer

Alt tablonun ilk satırı bandı savunuyor görünür: 0,05'te 13 ateşleme, 7'si gerçek bozulmaya
denk geliyor. Bu sayı geçersizdir ve gerekçesi üst tablodadır. `tahmin payi` satırlarına bakın:
**sağlam** kurulumda 3 dönem ateşliyor, **kavram kaymalı** kurulumda yine tam 3 dönem. Aynı ölçü,
aynı bant, aynı ateşleme sayısı; tek fark, ikinci durumda o dönemlerin bozulmuş olması. Ateşleme
bozulmadan gelmiyor, bozulmayla **aynı zamana denk düşüyor**. Sağlam satır bunu kanıtlar: hiçbir şey
bozuk değilken de aynı üç dönem konuşuyor.

Bu kursun kuralının tam tanımıdır: ateşlemesi ile bozulması ayrı ayrı gösterilmeyen bir gösterge
ölçülmemiş sayılır. Sağlam satır olmasaydı 0,05 bandındaki tahmin payı kusursuz bir gösterge gibi
okunurdu — 3 ateşleme, 0 yanlış alarm. Sağlam satırla birlikte 7 yakalamanın 3'ü düşer ve geriye
**4** kalır. O dördünü tek bir ölçü verir: isabet, iki zararsız senaryoda 0,05 ve 0,10 bantlarında
**hiç** ateşlemeyip zararlı senaryoda dört dönemin dördünde ateşleyen tek satır. Bedeli son
sütundadır: yakalama gecikmesi **2 dönem** ve bant seçilerek küçültülemez.

#### Yanlış Alarm ile Körlüğün Takası

Bandı genişletmek sayıyı düzeltmez, sıfırlar. 0,05'ten 0,10'a çıkıldığında yanlış alarm 6'dan 2'ye
iner ve yakalanan 7'den 4'e — ama düşen üç yakalama zaten geçersiz olan tahmin payı yakalamalarıdır,
yani 0,10 bandı **hiçbir gerçek bilgi kaybetmeden** yanlış alarmın üçte ikisini atar. Bu, bandı
genişletmenin bedava olduğu tek adımdır.

0,20 satırı ise üç sıfırdır: yanlış alarm yok, ateşleme yok, yakalama yok. Bandın isabet üzerindeki
karşılığı hesaplanabilir: 0,9700 tabanının 0,80 katı **0,7760**'tır, yani 0,20'lik bir bant isabetin
bu değerin altına inmesini bekler. Kavram kaymasının en dip değeri **0,8150**'dir ve 0,7760'ın
0,0390 üstünde, yaklaşık sekiz çözünürlük adımı yukarıda kalır. Bant, ölçünün ulaşamayacağı bir yere
konmuştur; sessizliği bir sağlık işareti değil, **tanım gereği ölü bir göstergedir**.

Üç bandı birlikte okumak kursun ikinci iddiasını verir: izleme de bozar. Dar bant hiçbir şeyin
bozulmadığı bir üretimde altı kez uyandırır; geniş bant isabetin 0,9700'den 0,8150'ye düştüğü bir
üretimde hiç konuşmaz. Aradaki bant yalnız bu kurguda iyi bir seçimdir, çünkü hangi ateşlemenin
gerçek olduğunu **gerçek isabete bakarak** biliyoruz — üretimde o bilgi iki dönem sonra gelir ve
bant onsuz seçilir.

#### Özet

- Uyarı eşiği bir ölçü değil bir seçimdir: `2026-08` tabanından göreli sapmanın aşmaması gereken
  paydır ve ateşleyen dönem sayısını doğrudan belirler.
- 0,05 bandında üç ölçü ve üç senaryo boyunca 13 dönem ateşler, 6'sı yanlış alarmdır; 0,10'da 6
  ateşleme ve 2 yanlış alarm; 0,20'de üçü de sıfırdır.
- 0,05'teki 7 yakalamanın 3'ü geçersizdir: tahmin payı **sağlam** kurulumda da tam 3 dönem ateşler.
  Ateşlemesi ile bozulması ayrı ayrı gösterilmeyen gösterge ölçülmemiş sayılır; geçerli yakalama
  **4**'tür ve hepsi isabetten gelir.
- İsabet, iki zararsız senaryoda hiç ateşlemeyip zararlı senaryoda dört dönemin dördünde ateşleyen
  tek ölçüdür; bedeli **2 dönemlik** yakalama gecikmesidir ve bant seçimiyle küçültülemez.
- 0,20 bandı isabetin 0,7760'ın altına inmesini ister; en dip değer 0,8150'dir, yani bant ölçünün
  ulaşamayacağı yerdedir ve sessizliği bir sağlık işareti değildir.

#### Sonraki Adım

Bu dersin geçerli tek göstergesi isabetti ve isabet **etiket** ister. Şimdiye kadar etiket kurgunun
bir armağanıydı: her satırın gerçek sınıfı biliniyordu, yalnız iki dönem geç geliyordu. Üretimde
öyle değildir — etiket toplanır ve toplanan şey **bütün tahminler için gelmez**. Sonraki ders
etiketin nereden geldiğini sorar: toplama yordamı tahminlerin ancak bir kısmını kapsadığında ölçülen
isabetin gerçek isabetten ne kadar saptığını ve hangi toplama biçiminin kapsamı büyütürken yanlılığı
da büyüttüğünü sayar.

### Geri Bildirim Toplama

Source: https://academia.sh/tr/kurslar/model-izleme/geri-bildirim-toplama

Üretimden toplanan etiketin kapsadığı payın ve taşıdığı yanlılığın sayılması: beş toplama yordamı aynı 1200 satır üzerinde denendiğinde yalnız pozitif tahminler için etiket toplamak 0,4858 kapsam verir ve kavram kaymalı üretimde ölçülen isabeti gerçek isabetten +0,0894 yukarı kaydırır, çünkü o yordam isabeti değil kesinliği ölçer; dönem dönem bakıldığında ölçülen isabet 0,9362'den 1,0000'e çıkarken gerçek isabet 0,8500'e iner ve düşüşe kurulmuş bir uyarı hiç ateşlemez. Yalnız negatif tahminler ters yönde −0,0845, karar eşiğine yakın satırlar 0,1850 kapsamla −0,2909 sapar. Her dördüncü satırı toplayan yordam yarı kapsamla +0,0033 ve +0,0100 sapar; kapsam yanlılığı düzeltmez, yanlılık seçim kuralından gelir.

Bir önceki ders üç bantta üç ölçüyü saydı ve geriye tek geçerli gösterge kaldı: isabet. İki zararsız
senaryoda hiç ateşlemeyen, zararlı senaryoda dört dönemin dördünde ateşleyen tek ölçü oydu. Ama
isabet **etiket** ister ve şimdiye kadar etiket kurgunun bir armağanıydı — her satırın gerçek sınıfı
biliniyordu, yalnız iki dönem geç geliyordu.

Üretimde etiket armağan değildir, **toplanır**. Toplanan şey de nadiren her tahmini kapsar: bir
inceleme yalnız işaretlenmiş kayıtlara gider, bir itiraz yalnız rahatsız olandan gelir, bir doğrulama
yalnız kararsız kalınan durumda istenir. Bu ders beş toplama yordamını aynı küme üzerinde koşturur ve
iki sayıyı ayrı ayrı yazar: **kapsam**, yani etiketi gelen satırın payı; ve **sapma**, yani o
etiketlerle ölçülen isabetin gerçek isabetten farkı.

- **MI53.** Kaynak, hat, kurgu model, kavram kayması ve beş toplama yordamı **kurgudur** ve ders
  içinde üretilir. Hiçbir izleme ürünü, etiketleme servisi, bulut servisi ya da öğrenme kitaplığı
  çağrılmaz. Tohum 20260218.
- **MI54. Toplama yordamı**, hangi tahminin etiketinin geleceğini belirleyen kuraldır. Beşi sınanır:
  `tamami`; `yalniz pozitif`, modelin 1 dediği satırlar; `yalniz negatif`, 0 dediği satırlar;
  `esik yakini`, puanı karar eşiğine `MARJ` kadar yakın satırlar; `her dorduncu`, dizideki her
  dördüncü satır.
- **MI55. Kapsam**, etiketi gelen satırın izleme dönemlerindeki 1200 satıra oranıdır. **Sapma**,
  toplanan etiketlerle ölçülen isabetten bütün satırlarla ölçülen **gerçek isabetin** çıkarılmasıdır.
  Yönü anlamlıdır: artı işaret modelin olduğundan iyi göründüğünü söyler.
- **MI56.** Kullanıcı sinyalinin değerlendirmeye dönmesi ve o sinyalin yanlılığı, Yapay Zekâ
  Mühendisliği müfredatındaki Yapay Zekâ Güvenliği ve Değerlendirme kursunun geri bildirim döngüsü
  dersinde ölçüldü; **yordam tekrarlanmaz**. Oradaki ölçü dil modeli çıktısı içindi; buradaki ek
  **sayısal modelin gecikmeli etiketidir**.
- **MI57.** Model MLOps Temelleri kursundan düzeltilmiş biçimde devralınır: karar eşiği ayrı bir
  ayardır, eğitim ölçütü **dengeli isabettir**. İzleme dönemlerinde **taban sınıf oranı 0,5342**.
- **MI58.** Taban çizgisi **toplamasız kurulumdur**: etiket gelmez, ölçülen isabet yoktur, sapma
  tanımsızdır. Dönem isabetinin adımı **0,0050** ve bundan küçük sapma okunmaz.

#### Etiket Nereden Geliyor

Aşağıdaki blok ortak tanımı kurar ve modeli eğitir. Son satır dersin sınırlarını verir: kaç satır
üzerinde ölçüyoruz, tabanın altına inilemeyecek çözünürlük nedir ve etiket kaç dönem sonra gelir.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat , kurgu model , kavram
# kaymasi ve bes toplama yordami ders icinde uretilir; hicbir izleme urunu ,
# etiketleme servisi , bulut servisi ya da ogrenme kitapligi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad not in gor:
            gor.add(ad)
            tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is None:
            dusen += i > 0
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar(kaynak=None):
    v, _ = a1_ayikla(kaynak_uret() if kaynak is None else kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, pencere=None, basamak=4):
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if (d < i and (pencere is None or d >= i - pencere))]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    oz = {}
    for k in kayitlar:
        i = DONEM.index(k["donem"])
        oz[(k["abone"], k["donem"])] = {"abone_ort": ortalama(ab[k["abone"]], i),
                                        "bolge_ort": ortalama(bo[k["bolge"]], i),
                                        "isyeri": 1 if k["tip"] == "isyeri" else 0}
    return oz


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)     # karar esigi AYRI bir ayardir , egitimde aranir
IZLEME = DONEM[6:]                      # 2026-07 .. 2026-12 : uretimde izlenen donemler
GECIKME = 2                             # etiket kac donem sonra geliyor
KURAL_ESIK, KURAL_ISYERI = 30, 6        # KAVRAM: abone_ort + 6 x isyeri > 30


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    dp = dn = yp = yn = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o_k, w, karar), etiket(o_k)
        dp, yp = dp + (t and g), yp + (t and not g)
        yn, dn = yn + (g and not t), dn + (not t and not g)
    duyarlilik = dp / (dp + yn) if dp + yn else 0.0
    ozgulluk = dn / (dn + yp) if dn + yp else 0.0
    return (duyarlilik + ozgulluk) / 2, round(duyarlilik, 4), round(ozgulluk, 4)


def egit(kayitlar, oz, donemler=None):
    """Egitim olcutu DENGELI ISABETTIR: duyarlilik ile ozgullugun ortalamasi."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    en = (-1.0, None, None, 0.0, 0.0)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            o, du, oz_ = dengeli_isabet(kume, oz, w, karar)
            if o > en[0]:
                en = (o, w, karar, du, oz_)
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "duyarlilik": en[3], "ozgulluk": en[4], "egitim_satiri": len(kume)}


def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    """KAVRAM KAYMASI: girdi AYNI , kural degisiyor. Esik 30'dan yeni_esik'e iniyor."""
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]): etiket(
        oz[(k["abone"], k["donem"])],
        yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK) for k in kayitlar}


def uyari(dizi, taban, pay):
    """Bir olcu tabandan `pay` kadar saparsa o donem ates eder."""
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


SATIR = satirlar()
OZ = oznitelikler(SATIR)                  # oznitelik BIR KEZ hesaplanir
MODEL = egit(SATIR, OZ)
IZL = [k for k in SATIR if k["donem"] in IZLEME]
buyuk = max(sum(1 for k in IZL if etiket(OZ[(k["abone"], k["donem"])]) == s) for s in (0, 1))
print(f"model {MODEL['agirlik']} karar esigi {MODEL['karar']} , dengeli isabet"
      f" {MODEL['dengeli']} , duyarlilik {MODEL['duyarlilik']} , izleme {len(IZL)} satir ,"
      f" taban sinif orani {buyuk / len(IZL):.4f} , adim {1 / 200:.4f} , etiket gecikmesi"
      f" {GECIKME} donem")
```

```
model (0.6, 0.0, 4) karar esigi 18 , dengeli isabet 0.9864 , duyarlilik 1.0 , izleme 1200 satir , taban sinif orani 0.5342 , adim 0.0050 , etiket gecikmesi 2 donem
```

#### Kapsam ve Sapma

Aşağıdaki blok beş yordamı iki senaryoda koşturur. Üst tablo izleme penceresinin tamamı için
kapsamı, toplanan etiketlerle ölçülen isabeti, bütün satırlarla ölçülen gerçek isabeti ve ikisinin
farkını verir. Alt tablo yalnız kavram kaymalı üretimde dönem dönem ölçülen isabeti ve **düşüşe**
kurulmuş 0,05'lik bir uyarının kaç dönem ateşlediğini sayar.

```python
# Bu blok ilk bloktaki tanimlari surdurur. Oznitelikler BIR KEZ hesaplanmistir.
MARJ = 1.0            # karar esigine bu kadar yakin puanlar "esik yakini" sayilir
KAVRAM = kavram_kaydir(SATIR, OZ)
W, KR = MODEL["agirlik"], MODEL["karar"]
YORDAM = (("tamami", lambda o, j: True),
          ("yalniz pozitif", lambda o, j: tahmin(o, W, KR) == 1),
          ("yalniz negatif", lambda o, j: tahmin(o, W, KR) == 0),
          ("esik yakini", lambda o, j: abs(puan(o, W) - KR) <= MARJ),
          ("her dorduncu", lambda o, j: j % 4 == 0))


def olc(etiketler, sec, donemler):
    """Yalniz sec'in sectigi satirin etiketi geliyor; isabet ONLARLA olculur."""
    top = dogru = tam = tam_dogru = 0
    for j, k in enumerate(SATIR):
        if k["donem"] not in donemler:
            continue
        o = OZ[(k["abone"], k["donem"])]
        d = tahmin(o, W, KR) == (etiketler[(k["abone"], k["donem"])] if etiketler
                                 else etiket(o))
        tam, tam_dogru = tam + 1, tam_dogru + d
        if sec(o, j):
            top, dogru = top + 1, dogru + d
    return round(top / tam, 4), round(dogru / top, 4), round(tam_dogru / tam, 4)


print("yordam           senaryo         kapsam  olculen  gercek   sapma")
for ad, sec in YORDAM:
    for sad, et in (("saglam", None), ("kavram kaymali", KAVRAM)):
        kap, olcu, ger = olc(et, sec, IZLEME)
        print(f"{ad:<16} {sad:<14} {kap:.4f}   {olcu:.4f}  {ger:.4f}  {olcu - ger:+.4f}")

print("\nkavram kaymali , donem donem olculen isabet ve 0.05 bandinda DUSUSE atesleme")
for ad, sec in YORDAM:
    dizi = [(d, olc(KAVRAM, sec, [d])[1]) for d in IZLEME]
    taban = dict(dizi)["2026-08"]
    a = [d for d, v in dizi[2:] if (taban - v) / taban > 0.05]
    print(f"{ad:<16}" + "".join(f"{v:8.4f}" for _, v in dizi) + f"   ates {len(a)}")
```

```
yordam           senaryo         kapsam  olculen  gercek   sapma
tamami           saglam         1.0000   0.9800  0.9800  +0.0000
tamami           kavram kaymali 1.0000   0.8900  0.8900  +0.0000
yalniz pozitif   saglam         0.4858   0.9588  0.9800  -0.0212
yalniz pozitif   kavram kaymali 0.4858   0.9794  0.8900  +0.0894
yalniz negatif   saglam         0.5142   1.0000  0.9800  +0.0200
yalniz negatif   kavram kaymali 0.5142   0.8055  0.8900  -0.0845
esik yakini      saglam         0.1850   0.8919  0.9800  -0.0881
esik yakini      kavram kaymali 0.1850   0.5991  0.8900  -0.2909
her dorduncu     saglam         0.2500   0.9833  0.9800  +0.0033
her dorduncu     kavram kaymali 0.2500   0.9000  0.8900  +0.0100

kavram kaymali , donem donem olculen isabet ve 0.05 bandinda DUSUSE atesleme
tamami            0.9700  0.9700  0.8150  0.8700  0.8650  0.8500   ates 4
yalniz pozitif    0.9341  0.9362  1.0000  1.0000  1.0000  1.0000   ates 0
yalniz negatif    1.0000  1.0000  0.6606  0.7347  0.7245  0.6907   ates 4
esik yakini       0.8286  0.8537  0.3824  0.5526  0.4857  0.4615   ates 4
her dorduncu      0.9800  0.9800  0.8600  0.8600  0.8200  0.9000   ates 4
```

#### Toplama Yordamı Ölçütü de Seçer

`yalniz pozitif` satırı dersin en sert bulgusudur. Kapsamı 0,4858, yani tahminlerin neredeyse
yarısını görüyor — az bir örneklem değil. Kavram kaymalı üretimde ölçtüğü isabet **0,9794**, gerçek
isabet **0,8900**; sapma **+0,0894**, on sekiz çözünürlük adımı ve **yukarı** yönlü. Alt tabloda daha
da kötüsü var: ölçülen isabet 0,9362'den **1,0000'e çıkıyor**, gerçeği 0,8500'e inerken. Düşüşe
kurulmuş uyarı **hiç ateşlemiyor**.

Mekanizma seçim kuralındadır. Yalnız 1 denen satırların etiketi geldiğinde ölçülen şey isabet değil
**kesinliktir (precision)**: modelin 1 dediklerinin ne kadarı gerçekten 1. Kavram kayması etiket
kuralını **gevşetiyor** — eşik 30'dan 27'ye iniyor — ve daha çok satır gerçekten 1 oluyor. Modelin
eski pozitifleri toptan doğrulanıyor, kesinlik 1,0000'e çıkıyor; kaçırdığı yeni pozitifler ise
pozitif olmayan tarafta kaldığı için hiç ölçülmüyor. **Toplama yordamı, bozulmanın en görünmez
olduğu ölçütü seçmiş oluyor.**

`yalniz negatif` aynı kusuru ters işaretle taşır: sağlam üretimde 1,0000 ölçer ve modeli olduğundan
iyi gösterir, kavram kaymalı üretimde 0,8055 ölçüp **−0,0845** ile fazla karamsar olur. En açık
örnek `esik yakini`'dir: karar eşiğine yakın satırlar tanımı gereği modelin en kararsız olduğu
satırlardır ve yalnız onları toplamak 0,8900'lük bir modeli **0,5991** okutur. Sapma **−0,2909**,
elli sekiz çözünürlük adımı.

#### Kapsam Yanlılığı Düzeltmez

İki satırı yan yana koymak dersin kuralını verir. `yalniz pozitif` **0,4858** kapsamla +0,0894
sapıyor; `her dorduncu` **0,2500** kapsamla +0,0100. Kapsamın yarısı, sapmanın dokuzda biri. Sağlam
üretimde `her dorduncu`nun sapması **+0,0033**, yani çözünürlük adımının bile altında ve
**okunamaz**. Toplanan etiket sayısını büyütmek yanlılığı küçültmez; yanlılığı belirleyen tek şey
**seçim kuralının tahminle ilişkili olup olmadığıdır**. `her dorduncu` dizideki konuma bakar, modelin
çıktısına değil — bu yüzden yarı kapsamla dokuz kat daha doğrudur.

Buradan kursun kuralının etiket tarafındaki karşılığı çıkar. Ateşleyen bir gösterge nasıl bozulmanın
kanıtı değilse, **toplanmış bir etiket kümesi de ölçümün kanıtı değildir**. Bir kapsam sayısı tek
başına rapor edilirse — "tahminlerin yüzde 49'u için etiketimiz var" — ölçünün ne olduğu hakkında
hiçbir şey söylemez; söyleyen şey, o yüzde 49'un **hangi kuralla** seçildiğidir. Sapması ayrıca
gösterilmeyen bir toplama yordamı **ölçülmemiş sayılır**.

#### Özet

- Toplama yordamı, hangi tahminin etiketinin geleceğini belirleyen kuraldır; kapsam ile sapma ayrı
  iki sayıdır ve `tamami` dışındaki her yordamda sapma sıfırdan farklıdır.
- `yalniz pozitif` 0,4858 kapsamla kavram kaymalı üretimde **+0,0894** sapar ve ölçülen isabeti
  0,9362'den 1,0000'e **çıkarır**; düşüşe kurulmuş 0,05'lik uyarı hiç ateşlemez. Ölçtüğü şey isabet
  değil **kesinliktir** ve kural gevşediğinde kesinlik yükselir.
- `yalniz negatif` ters yönde −0,0845 sapar, `esik yakini` 0,1850 kapsamla −0,2909 sapar; ikisi de
  bozulmayı gösterir ama düzeyi on yedi ile elli sekiz çözünürlük adımı yanlış okur.
- `her dorduncu` 0,2500 kapsamla +0,0033 ve +0,0100 sapar; kapsamın yarısı, sapmanın dokuzda biri.
  **Kapsam yanlılığı düzeltmez**, yanlılık seçim kuralının tahminle ilişkili olmasından gelir.
- Sapması ayrıca gösterilmeyen bir toplama yordamı ölçülmemiş sayılır; bir kapsam sayısı tek başına
  ölçümün kanıtı değildir.

#### Sonraki Adım

Altı ders tek bir soruyu böldü: bozulma **görülüyor mu**. Veri kayması ateşleyen ama bozmayan bir
değişimdi, kavram kayması bozan ama ateşlemeyen bir değişim; gecikmeli etiket tek gören ölçüyü iki
dönem geciktirdi, vekil ölçütler o boşlukta ayırt etmedi, bant daraldıkça yanlış alarm büyüdü ve
toplanan etiketin kendisi ölçüyü kaydırdı. Ölçülen şey hep aynıydı: bir bozulmanın **görülmesi**.

Hiç sorulmayan şey, görüldükten **sonra** ne yapılacağıdır. `2026-09`'da kuralın değiştiğini
`2026-11`'de öğrenen bir ekibin elinde bir karar dizisi vardır ve her biri kendi bedelini taşır: yeni
bir model eğitip yayına almak, onu önce trafiğin bir payına vermek, iki modeli aynı anda koşturup
farkın gerçek olduğunu göstermek, kararın gerekçesini kaydetmek. Sonraki konu bu kararları sayar ve
ilk ölçüsü şudur: yeni modele giden istek payı arttıkça sonuç ne kadar oynuyor ve **hiç istek
göndermeden** ölçüm almak mümkün mü.

## Yayın ve Yönetişim

Bir modelin yayına alınmasının, değiştirilmesinin ve kullanımdan kaldırılmasının denetlenebilir biçimde sayılması: gölge dağıtımda hiçbir isteğin yeni modele gitmemesine karşın ölçümün alınabilmesi ve kanaryada yeni modele giden payın sonucu ne kadar oynattığı, çevrimiçi deneyin dönem başına iki yüz satırla ayırt edebildiği en küçük fark ve bu farkın altındaki karşılaştırmanın sonuçsuz kalması, yeniden eğitim tetikleyicisinin kural değişmediğinde üç pencerede de eski modelle aynı 0,9800'ü vermesi ve eğitim bedelinin boşa ödenmesi, karar kayıtlarının bir tahminin hangi model ve hangi öznitelikle üretildiğini bulunabilir kılması, uyumluluk yükümlülüklerinin düzenleme adı yazılmadan sınıflarıyla anılması ve her sınıfın mühendislik karşılığının kaç kayıt gerektirdiği, eğitim ile çıkarım maliyetinin ayrı izlenmesi, modelin emekliye ayrılmasının hangi ölçüyle kararlaştırıldığı ve kaldırılan modelin geriye dönük denetlenebilirliğinin ne kadar süre korunduğu.

### Gölge ve Kanarya Dağıtımı

Source: https://academia.sh/tr/kurslar/model-izleme/golge-ve-kanarya-dagitimi

Yeni bir modelin yayına alınmasının kademelendirilmesi ve yeni modele giden payın hem sonucu hem de ölçüyü ne kadar oynattığının sayılması: gölge yerleşimde yeni modele 0 istek gider ama aday iki yüz satırın tamamında ölçülür ve 0,9650 çıkar, kanarya payı 0,10 olduğunda aday yalnız yirmi satırda ölçülür, çözünürlük 0,0050'den 0,0500'e çıkar ve aynı aday 1,0000 görünür. Eğitim araması iki adayı 0,9800 ile 0,9741'de ayıramazken üretimde aralarında 0,0150 fark vardır ve pay küçüldükçe bu farkın izi ölçünün altına iner.

Bir önceki konu altı derste bozulmanın **görülmesini** ölçtü: girdi kayması ateşleyen bir uyarı üretti ama isabeti düşürmedi, kavram kayması hiçbir vekil ölçüte görünmeden isabeti düşürdü, tek gören ölçü iki dönem geç geldi ve üretimden toplanan etiket yanlıydı. Altı dersin ortak kusuru şudur: hiçbirinde **görüldükten sonra ne yapılacağı** sorulmadı.

Görüldükten sonra yapılan ilk şey modeli değiştirmektir. Değiştirmenin kendisi bir risktir: yeni model üretimde ilk kez çalışır ve hakkında bilinen tek şey **eğitim kümesindeki** davranışıdır. Bu ders yeni modeli üç ayrı yerleşimde yayına alır ve tek bir soruyu sayar: **yeni modele giden pay sonucu ne kadar oynatıyor, ölçüyü ne kadar oynatıyor.** Yayın kademelendirmesinin kendisi Sürekli Tümleştirme ve Teslimat kursunda kuruldu, geri alma ve aşamalı yayın da Dayanıklılık ve Güvenilirlik kursunda ölçüldü; ikisi de burada tekrarlanmaz. Buradaki soru dağıtımın değil **ölçünün** sorusudur.

- **MY1.** Kaynak, hat ve model **kurgudur** ve ders içinde üretilir. Hiçbir dağıtım aracı, yönlendirici, deney platformu ya da izleme ürünü çağrılmaz. Tohum 20260218.
- **MY2.** Etiket, özniteliklerin gördüğü bir kurala bağlıdır: `abone_ort + 6 x isyeri > 30`. Karar eşiği bu kuraldan **ayrı bir ayardır** ve eğitimde aranır; eğitim ölçütü **dengeli isabettir**, yani **duyarlılık (recall)** ile **özgüllüğün** ortalamasıdır. Üçü de bu kursun ilk dersinde kuruldu.
- **MY3.** İzleme dönemlerinde **taban sınıf oranı 0,5342**; hiçbir isabet bu oran yazılmadan okunmaz. Dönem başına 200 satır vardır, yani dönem isabetinin en küçük adımı **0,0050**.
- **MY4.** Üretim verisi **veri kaymalıdır**: `2026-09`'dan sonra işyeri tüketimi dönem başına 0,06 büyür. Kural değişmemiştir.
- **MY5.** Yayın penceresi tek dönemdir: `2026-12`, 200 istek. Kanaryada isteklerin baştan itibaren pay kadarı yeni modele gider; bu **kurgu** bir bölüştürmedir.
- **MY6.** **Gölge** yerleşimde yeni model aynı isteği eski modele paralel puanlar, sonucu yalnız kaydedilir ve **hiçbir yanıt kullanıcıya dönmez**.
- **MY7.** Bedel **istek** ve **satır** olarak sayılır; ham süre yazılmaz.

#### İki Aday, Ayıramayan Eğitim

İlk blok kaynağı, tüketim aşamasını, öznitelik yolunu ve kurgu modeli kurar; sonra üretimdeki kaymış veriyle yeniden eğitim yapar ve aramanın **ilk iki sırasını** yayın adayı olarak alır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin tuketim asamasi , oznitelik
# yolu ve uc agirlikli esik modeli ders icinde uretilir; hicbir ogrenme
# kitapligi , dagitim araci , deney platformu ya da izleme urunu cagrilmaz.
# Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Kaynak okumalari ve donem farkindan tuketim: hattin ilk uc asamasi."""
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e})
    t = {(k["abone"], k["donem"]): k["endeks"] for k in kayit}
    return [dict(k, m3=k["endeks"] - t[(k["abone"], DONEM[DONEM.index(k["donem"]) - 1])])
            for k in kayit if DONEM.index(k["donem"])]


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir ve BIR KEZ hesaplanir; tam gecmisi gorur."""
    ab, bo = {}, {}
    for k in sorted(kayitlar, key=lambda k: DONEM.index(k["donem"])):
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ort(dizi, i):
        s = [v for d, v in dizi if d < i]
        return round(sum(s) / len(s), 4) if s else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ort(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ort(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


KURAL_ESIK, KURAL_ISYERI = 30, 6          # etiketi ureten KURAL , yani kavram
IZLEME = DONEM[6:]                        # uretimde izlenen alti donem
IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)       # karar esigi AYRI bir ayardir


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    """Egitim olcutu: duyarlilik ile ozgullugun ortalamasi."""
    dp = dn = yp = yn = 0
    for k in kume:
        o = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o, w, karar), etiket(o)
        if t and g:
            dp += 1
        elif t:
            yp += 1
        elif g:
            yn += 1
        else:
            dn += 1
    du = dp / (dp + yn) if dp + yn else 0.0
    oz_ = dn / (dn + yp) if dn + yp else 0.0
    return (du + oz_) / 2, round(du, 4), round(oz_, 4)


def egit(kayitlar, oz, donemler):
    """Agirlik ve karar esigi birlikte aranir: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    sirali = sorted(((dengeli_isabet(kume, oz, w, kr)[0], w, kr)
                     for w in IZGARA for kr in KARAR_IZGARA), key=lambda a: -a[0])
    en = sirali[0]
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "sirali": sirali, "satir": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """KURGU veri kaymasi: isyeri tuketimi her donem `egim` kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


TUM = satirlar()
OZ = oznitelikler(TUM)
TABAN = egit(TUM, OZ, DONEM[1:9])
URETIM = kaydir(TUM)                      # uretimde girdi kaymis durumda
OZU = oznitelikler(URETIM)
d, du, oz_ = dengeli_isabet([k for k in TUM if k["donem"] in DONEM[1:9]], OZ,
                            TABAN["agirlik"], TABAN["karar"])
izl = [k for k in TUM if k["donem"] in IZLEME]
e = [etiket(OZ[(k["abone"], k["donem"])]) for k in izl]
print(f"taban model {TABAN['agirlik']} karar esigi {TABAN['karar']}"
      f" dengeli isabet {TABAN['dengeli']} duyarlilik {du:.4f} ozgulluk {oz_:.4f}")
print(f"izleme {len(izl)} satir , taban sinif orani {round(1 - sum(e) / len(e), 4)}"
      f" , donem basina 200 satir , isabetin adimi 0.0050")

# Iki aday: yeniden egitilmis model ve ayni aramanin ikinci sirasi.
YE = egit(URETIM, OZU, DONEM[5:11])
A = {"ad": "A yeniden egitilmis", "agirlik": YE["agirlik"], "karar": YE["karar"],
     "egitim": YE["dengeli"]}
ik = YE["sirali"][1]
B = {"ad": "B aramanin ikincisi", "agirlik": ik[1], "karar": ik[2], "egitim": round(ik[0], 4)}
son = [k for k in URETIM if k["donem"] == "2026-12"]
for m in (A, B):
    m["ayrisan"] = sum(1 for k in son
                       if tahmin(OZU[(k["abone"], k["donem"])], m["agirlik"], m["karar"])
                       != tahmin(OZU[(k["abone"], k["donem"])], TABAN["agirlik"], TABAN["karar"]))
    print(f"aday {m['ad']} {m['agirlik']}/{m['karar']}"
          f" egitim dengeli isabeti {m['egitim']} , 2026-12'de ayrisan tahmin {m['ayrisan']}")
```

```
taban model (0.6, 0.0, 4) karar esigi 18 dengeli isabet 0.9864 duyarlilik 1.0000 ozgulluk 0.9728
izleme 1200 satir , taban sinif orani 0.5342 , donem basina 200 satir , isabetin adimi 0.0050
aday A yeniden egitilmis (0.6, 0.0, 4)/18 egitim dengeli isabeti 0.98 , 2026-12'de ayrisan tahmin 0
aday B aramanin ikincisi (0.8, 0.4, 4)/34 egitim dengeli isabeti 0.9741 , 2026-12'de ayrisan tahmin 11
```

İki aday eğitimde **0,9800** ile **0,9741** arasında durur. Aralarındaki 0,0059, altı dönemlik eğitim penceresinin 1200 satırında yaklaşık yedi satırlık bir farktır; hiçbir eğitim raporu bu ikisini güvenle sıralayamaz. Üretimdeki durumları ise ayrıdır: A eski modelle **birebir aynı ağırlık ve aynı karar eşiğini** taşır, yani `2026-12`'de ayrışan tahmini **0**'dır ve yayına alınması hiçbir şeyi değiştiremez. B ise aynı dönemde **11 tahminde** eski modelden ayrılır. Yayın kararının bütün konusu bu 11 tahmindir.

#### Payın Ölçüye Yaptığı

İkinci blok üç yerleşimi aynı 200 istek üzerinde koşturur. `giden` sütunu yeni modele giden istek sayısı, `hizmet edilen` kullanıcıya dönen yanıtların isabeti, `olculen satir` adayın hakkında ölçü toplanabilen satır sayısı, `ek yanlis` ise eski modelin doğru bildiği ama yeni modelin yanlış hizmet ettiği istek sayısıdır.

```python
# Bu blok ilk bloktaki TABAN , URETIM , OZU , A , B , son , tahmin ,
# etiket ve puan tanimlarini surdurur.
def yayin(eski, yeni, oz, kume, pay, golge=False):
    """Golge: hicbir istek yeni modele gitmez , olcum yine de alinir."""
    n = 0 if golge else int(len(kume) * pay)
    olculen = len(kume) if golge else n
    hizmet = aday = ek_yanlis = 0
    for j, k in enumerate(kume):
        o = oz[(k["abone"], k["donem"])]
        g = etiket(o)
        te = tahmin(o, eski["agirlik"], eski["karar"])
        ty = tahmin(o, yeni["agirlik"], yeni["karar"])
        hizmet += ((ty if j < n else te) == g)
        ek_yanlis += (j < n and ty != g and te == g)
        aday += ((golge or j < n) and ty == g)
    return (n, round(hizmet / len(kume), 4), olculen, round(aday / olculen, 4),
            round(1 / olculen, 4), ek_yanlis)


eski_isabet = sum(1 for k in son
                  if tahmin(OZU[(k["abone"], k["donem"])], TABAN["agirlik"],
                            TABAN["karar"]) == etiket(OZU[(k["abone"], k["donem"])]))
print(f"2026-12 istek {len(son)} , eski modelin isabeti {eski_isabet / len(son)}")
print("aday  yerlesim      giden  hizmet edilen  olculen satir  aday olcusu"
      "  cozunurluk  ek yanlis")
for m in (A, B):
    for ad, pay, g in (("golge", 0.0, True), ("kanarya 0.10", 0.10, False),
                       ("kanarya 0.50", 0.50, False), ("tam gecis", 1.00, False)):
        n, h, ol, ao, cz, ey = yayin(TABAN, m, OZU, son, pay, g)
        print(f"{m['ad'][0]}     {ad:<12}  {n:>5}  {h:>13}  {ol:>13}  {ao:>11}"
              f"  {cz:>10}  {ey:>9}")
```

```
2026-12 istek 200 , eski modelin isabeti 0.98
aday  yerlesim      giden  hizmet edilen  olculen satir  aday olcusu  cozunurluk  ek yanlis
A     golge             0           0.98            200         0.98       0.005          0
A     kanarya 0.10     20           0.98             20          1.0        0.05          0
A     kanarya 0.50    100           0.98            100         0.99        0.01          0
A     tam gecis       200           0.98            200         0.98       0.005          0
B     golge             0           0.98            200        0.965       0.005          0
B     kanarya 0.10     20           0.98             20          1.0        0.05          0
B     kanarya 0.50    100           0.97            100         0.97        0.01          3
B     tam gecis       200          0.965            200        0.965       0.005          7
```

A satırları dört yerleşimde de aynıdır: hizmet edilen isabet **0,9800**, ek yanlış **0**. Bu bir başarı değil, bir **boşluğun** ölçüsüdür. Ayrışan tahmini sıfır olan bir aday için kanarya yayını, hiçbir şeyi değiştiremeyecek bir değişikliği kademelendirmek demektir; taşınan risk sıfır olduğu gibi kazanılan bilgi de sıfırdır. A'nın `kanarya 0.10` satırındaki 1,0000 bile bir bilgi değildir: yirmi satırda ölçülen bir sayıdır ve eski modelin aynı yirmi satırdaki değeri de 1,0000'dır.

B satırları asıl okumayı taşır. **Gölge yerleşimde yeni modele 0 istek gider**, hizmet edilen isabet eski modelin 0,9800'ünde kalır, hiçbir kullanıcı yeni modelin çıktısını görmez ve ek yanlış **0**'dır. Buna karşın aday **200 satırın tamamında** ölçülür ve gerçek değeri okunur: **0,9650**, yani eski modelden **0,0150** daha kötü. Bu fark, dönem çözünürlüğünün **üç adımıdır** ve bandın üstündedir.

Kanarya payı 0,10 olduğunda tablo tersine döner. Yeni modele **20 istek** gider, ama aday artık yalnız o 20 satırda ölçülebilir: ölçünün çözünürlüğü **0,0050'den 0,0500'e** çıkar, on kat kabalaşır. O yirmi satırda B'nin ölçüsü **1,0000**'dır. Gerçekte 0,9650 olan bir aday, kanaryanın en yaygın payında **hatasız** görünür. Hizmet edilen isabet de **0,9800**'de kalır, yani yayın panosuna bakan biri hiçbir şeyin değişmediğini görür. Kötü adayın izi ölçünün altına inmiştir.

Pay 0,50'ye çıkarıldığında ölçü düzelmeye başlar — çözünürlük 0,0100, adayın ölçüsü 0,9700 — ama bedeli de görünür olur: **3 istek**, eski modelin doğru bildiği hâlde yanlış yanıtlanır. Pay 1,00 kanarya değil **tam geçiştir**; ölçü tam çözünürlüğüne döner ve ek yanlış **7**'ye çıkar. Kanaryanın **geri alması** payı 0'a çekmektir ve bu, yanlış hizmet edilmiş istekleri geri getirmez. Gölgenin geri alınacak bir şeyi yoktur, çünkü hiç istek gitmemiştir.

Buradan iki tarafı olan tek bir kural çıkar. **Kanaryada ölçünün çözünürlüğü ile taşınan risk aynı sayıya, paya bağlıdır ve ters yönde hareket eder**: payı küçültmek riski küçültürken ölçüyü de körleştirir. **Gölge bu bağı koparır** — çözünürlük tam geçişinkiyle aynıdır (0,0050) ve yeni modele giden istek 0'dır. Gölgenin ödediği bedel başka bir sütundadır ve bu tabloda yoktur: her istek iki kez puanlanır, yani 200 istek için 400 çıkarım yapılır. Gölgenin ölçemediği tek şey ise **yanıtın kendisinin** yol açtığı davranıştır; puanı kimse görmediği için sonucu da kimse etkilemez.

#### Özet

- Aynı eğitim araması iki adayı **0,9800** ile **0,9741**'de bırakır ve ayıramaz; üretimde biri eski modelle ayrışan tahmini **0** olan bir kopya, öbürü `2026-12`'de **11 tahminde** ayrılan gerçek bir değişikliktir. Taban sınıf oranı 0,5342 ve dönem isabetinin adımı 0,0050.
- **Gölge yerleşim yeni modele 0 istek gönderir** ve adayı yine de 200 satırın tamamında ölçer: gerçek değeri **0,9650**, eski modelden **0,0150** aşağıda ve çözünürlüğün üç adımı kadar.
- **Kanarya payı ölçüyü de böler.** Pay 0,10'da aday yalnız 20 satırda ölçülür, çözünürlük **0,0500**'e çıkar ve gerçekte kötü olan aday **1,0000** görünür; hizmet edilen isabet 0,9800'de kaldığı için panoya bakan hiçbir şey görmez.
- Payı büyütmek ölçüyü düzeltir ama bedelini yazar: 0,50'de **3**, 1,00'de **7** istek eski modelin doğru bildiği hâlde yanlış yanıtlanır. Pay 1,00 kanarya değil tam geçiştir ve geri alma bu istekleri geri getirmez.
- Kanaryada çözünürlük ile risk **aynı paya** bağlıdır ve ters yönde gider; gölge bu bağı koparır ve bedelini **çıkarım sayısında** öder: 200 istek için 400 puanlama.

#### Sonraki Adım

Gölge, adayın gerçek değerini tek bir dönemde 0,9650 olarak okudu ve eski modelin 0,9800'ünden 0,0150 aşağıda buldu. Bu sayı bir karar için yeterli görünüyor, ama altında yanıtsız bir soru duruyor: 200 satırla ölçülen bir fark **ne kadar küçüldüğünde** okunamaz hâle gelir. Sonraki ders iki modelin karşılaştırmasını dönem sayısına göre süpürür, aynı adayın farkının dönemden döneme hangi bant içinde oynadığını sayar ve bu küme büyüklüğünün **ayırt edebildiği en küçük farkı** hesaplar. Bulunan sayının altında kalan her karşılaştırma sonuçsuzdur ve ders bunun bir örneğini işaretiyle birlikte gösterir.

### Çevrimiçi Deneyler

Source: https://academia.sh/tr/kurslar/model-izleme/cevrimici-deneyler

İki modelin üretimde karşılaştırılmasının bu küme büyüklüğünde ne kadar küçük bir farkı ayırt edebildiğinin sayılması: dönem başına iki yüz satırla çözünürlük 0,0050, altı dönemle 0,0008'dir, ama tek dönemlik ölçümler aynı aday için 0,0400 genişliğinde bir bant içinde oynar ve işaret değiştirir. Yüz otuz beş adayın taraması sınırı verir: altı dönemin altısında da aynı işareti taşıyan 133 adayın en küçük farkı 0,0275, işareti oynayan tek adayın farkı 0,0050'dir. Aradaki aralıkta ölçülmüş aday yoktur ve ayırt edilebilir en küçük fark bu iki sayının arasında kalır; altında kalan her karşılaştırma sonuçsuzdur.

Bir önceki ders gölge yerleşimin adayı iki yüz satırın tamamında ölçtüğünü ve `2026-12`'de **0,9650** verdiğini gösterdi; eski model aynı dönemde **0,9800**'dü. Aradaki **0,0150** bir karar için yeterli göründü. Bu sayının altında yanıtlanmamış bir soru duruyor: iki modelin farkı **ne kadar küçüldüğünde** bu kümeyle okunamaz hâle gelir.

Soru bir deney tasarımı sorusudur. İki kolun karşılaştırılması, örneklem büyüklüğü ve güven aralığı Kullanıcı Deneyimi ve Davranış Tasarımı kursunda kuruldu, dağılım varsayımları ve aralık yordamı İstatistiksel Analiz ve Keşifsel Veri Analizi kursunda ölçüldü; ikisi de burada tekrarlanmaz. Bu ders yalnız bir sayıyı hesaplar: **bu küme büyüklüğünün ayırt edebildiği en küçük fark.** Bulunan sayının altındaki her karşılaştırma, hangi yordamla yapılırsa yapılsın **sonuçsuzdur** ve ders bunun tek örneğini işaretiyle birlikte gösterir.

- **MY11.** Kaynak, hat ve model **kurgudur**; hiçbir deney platformu, dağıtım aracı ya da izleme ürünü çağrılmaz. Tohum 20260218.
- **MY12.** Etiket `abone_ort + 6 x isyeri > 30` kuralına bağlıdır, karar eşiği ayrı bir ayardır ve eğitim ölçütü dengeli isabettir. İzleme dönemlerinde **taban sınıf oranı 0,5342**.
- **MY13.** Deney ufku altı izleme dönemidir: dönem başına **200 satır**, toplam **1200**. Dönem isabetinin adımı **0,0050**, altı dönemin adımı **0,0008**.
- **MY14.** Karşılaştırılan iki kol **aynı satırları** görür ve aynı etiketle ölçülür. Kol ayrımından gelen bir seçim yanlılığı yoktur; bu, gerçek bir deneyde ayrıca gösterilmesi gereken bir varsayımdır ve burada kurgu gereği sağlanmıştır.
- **MY15.** Üretim verisi **veri kaymalıdır** ve kural değişmemiştir.
- **MY16.** Adaylar önceki dersin yeniden eğitim aramasının ilk dört sırasıdır; ızgara 27 ağırlık ile 5 karar eşiğinden **135 adaydır**.

#### Aynı Adayın Dönemden Döneme Oynaması

İlk blok kurulumu kurar, dört adayı alır ve her birinin taban modele göre farkını **dönem dönem** ölçer.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin tuketim asamasi , oznitelik
# yolu ve uc agirlikli esik modeli ders icinde uretilir; hicbir ogrenme
# kitapligi , deney platformu ya da izleme urunu cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Kaynak okumalari ve donem farkindan tuketim: hattin ilk uc asamasi."""
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e})
    t = {(k["abone"], k["donem"]): k["endeks"] for k in kayit}
    return [dict(k, m3=k["endeks"] - t[(k["abone"], DONEM[DONEM.index(k["donem"]) - 1])])
            for k in kayit if DONEM.index(k["donem"])]


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir ve BIR KEZ hesaplanir; tam gecmisi gorur."""
    ab, bo = {}, {}
    for k in sorted(kayitlar, key=lambda k: DONEM.index(k["donem"])):
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ort(dizi, i):
        s = [v for d, v in dizi if d < i]
        return round(sum(s) / len(s), 4) if s else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ort(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ort(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


KURAL_ESIK, KURAL_ISYERI = 30, 6          # etiketi ureten KURAL , yani kavram
IZLEME = DONEM[6:]                        # uretimde izlenen alti donem
IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)       # karar esigi AYRI bir ayardir


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    """Egitim olcutu: duyarlilik ile ozgullugun ortalamasi."""
    dp = dn = yp = yn = 0
    for k in kume:
        o = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o, w, karar), etiket(o)
        if t and g:
            dp += 1
        elif t:
            yp += 1
        elif g:
            yn += 1
        else:
            dn += 1
    du = dp / (dp + yn) if dp + yn else 0.0
    oz_ = dn / (dn + yp) if dn + yp else 0.0
    return (du + oz_) / 2, round(du, 4), round(oz_, 4)


def egit(kayitlar, oz, donemler):
    """Agirlik ve karar esigi birlikte aranir: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    sirali = sorted(((dengeli_isabet(kume, oz, w, kr)[0], w, kr)
                     for w in IZGARA for kr in KARAR_IZGARA), key=lambda a: -a[0])
    en = sirali[0]
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "sirali": sirali, "satir": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """KURGU veri kaymasi: isyeri tuketimi her donem `egim` kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


TUM = satirlar()
OZ = oznitelikler(TUM)
TABAN = egit(TUM, OZ, DONEM[1:9])
URETIM = kaydir(TUM)                      # uretimde girdi kaymis durumda
OZU = oznitelikler(URETIM)
izl = [k for k in TUM if k["donem"] in IZLEME]
e = [etiket(OZ[(k["abone"], k["donem"])]) for k in izl]
print(f"taban model {TABAN['agirlik']}/{TABAN['karar']}"
      f" , izleme {len(izl)} satir , taban sinif orani {round(1 - sum(e) / len(e), 4)}")

YE = egit(URETIM, OZU, DONEM[5:11])       # onceki dersin yeniden egitim aramasi
ADAY = [{"ad": ad, "agirlik": s[1], "karar": s[2], "egitim": round(s[0], 4)}
        for ad, s in zip("ABCD", YE["sirali"][:4])]


def fark(m, kume):
    """Adayin taban modele gore isabet farki. Ayni satirlar , ayni etiket."""
    def dogru(x):
        return sum(1 for k in kume
                   if tahmin(OZU[(k["abone"], k["donem"])], x["agirlik"], x["karar"])
                   == etiket(OZU[(k["abone"], k["donem"])]))
    return round((dogru(m) - dogru(TABAN)) / len(kume), 4)


DONEMLIK = {m["ad"]: [fark(m, [k for k in URETIM if k["donem"] == d]) for d in IZLEME]
            for m in ADAY}
print("aday  agirlik/karar         egitim dengeli isabeti  ayrisan tahmin")
for m in ADAY:
    ayr = sum(1 for k in URETIM if k["donem"] in IZLEME and
              tahmin(OZU[(k["abone"], k["donem"])], m["agirlik"], m["karar"])
              != tahmin(OZU[(k["abone"], k["donem"])], TABAN["agirlik"], TABAN["karar"]))
    print(f"{m['ad']}     {str(m['agirlik']) + '/' + str(m['karar']):<20}"
          f"  {m['egitim']:>20}  {ayr:>14}")
print("\ndonem      A        B        C        D")
for i, d in enumerate(IZLEME):
    print(f"{d}  " + "  ".join(f"{DONEMLIK[m['ad']][i]:+.4f}" for m in ADAY))
tumu = {m["ad"]: fark(m, [k for k in URETIM if k["donem"] in IZLEME]) for m in ADAY}
print("6 donem  " + "  ".join(f"{tumu[m['ad']]:+.4f}" for m in ADAY))
print("bant      " + "   ".join(f"{max(DONEMLIK[m['ad']]) - min(DONEMLIK[m['ad']]):.4f}"
                                for m in ADAY))
```

```
taban model (0.6, 0.0, 4)/18 , izleme 1200 satir , taban sinif orani 0.5342
aday  agirlik/karar         egitim dengeli isabeti  ayrisan tahmin
A     (0.6, 0.0, 4)/18                      0.98               0
B     (0.8, 0.4, 4)/34                    0.9741              60
C     (1.0, 0.0, 8)/30                    0.9492              33
D     (1.0, 0.2, 4)/34                    0.9434              97

donem      A        B        C        D
2026-07  +0.0000  +0.0250  -0.0200  -0.0150
2026-08  +0.0000  +0.0000  -0.0400  -0.0250
2026-09  +0.0000  -0.0150  -0.0350  -0.0500
2026-10  +0.0000  -0.0150  -0.0250  -0.0450
2026-11  +0.0000  -0.0100  -0.0250  -0.0400
2026-12  +0.0000  -0.0150  -0.0200  -0.0400
6 donem  +0.0000  -0.0050  -0.0275  -0.0358
bant      0.0000   0.0400   0.0200   0.0350
```

A sütunu tamamen sıfırdır ve bu bir ölçüm sonucu değil, bir **tanım** sonucudur: A'nın ayrışan tahmini 0'dır, yani iki model her satırda aynı şeyi söyler. Aralarındaki fark hiçbir küme büyüklüğüyle ölçülemez, çünkü ortada ölçülecek bir fark yoktur. Deneye başlamadan önce sorulacak ilk soru budur.

B sütunu asıl bulgudur. Altı dönemin toplamında B, taban modelden **0,0050** kötüdür — tek dönemin çözünürlüğünün tam bir adımı. Ama dönemlik değerler **+0,0250** ile **−0,0150** arasında, **0,0400** genişliğinde bir bantta oynar ve **işaret değiştirir**. `2026-07`'de yapılmış tek dönemlik bir deney B'yi taban modelden **beş çözünürlük adımı daha iyi** ilan eder. Sayı yanlış değildir; o dönemde gerçekten öyledir. Yanlış olan, o sayıdan bir sıralama çıkarmaktır.

C ile D'de bant da geniştir (0,0200 ve 0,0350) ama **hiçbir dönemde işaret dönmez**. İkisi de her dönemde taban modelin altındadır ve tek dönemlik bir deney onları doğru sıralar.

#### Ayırt Edilebilir En Küçük Fark

İkinci blok iki şey yapar. Önce deney kümesini büyütür ve farkın nereye yerleştiğini izler; sonra ızgaradaki **135 adayın tamamını** tarayarak sınırı arar.

```python
# Bu blok ilk bloktaki ADAY , fark , IZLEME , URETIM , TABAN ,
# IZGARA ve KARAR_IZGARA tanimlarini surdurur.
print("donem sayisi  satir  cozunurluk        B        C        D")
for n in range(1, 7):
    alt = [k for k in URETIM if k["donem"] in IZLEME[:n]]
    print(f"{n:>12}  {len(alt):>5}  {1 / len(alt):>10.4f}  " +
          "  ".join(f"{fark(m, alt):+.4f}" for m in ADAY[1:]))

# 135 adayin tamami: tek donemlik bir deney 6 donemlik sonucu dogru siraliyor mu
KUME = {d: [k for k in URETIM if k["donem"] == d] for d in IZLEME}
TUMKUME = [k for k in URETIM if k["donem"] in IZLEME]
uyan, oynayan = [], []
for w in IZGARA:
    for kr in KARAR_IZGARA:
        m = {"agirlik": w, "karar": kr}
        h = fark(m, TUMKUME)
        if h == 0:
            continue
        isaret = (h > 0) - (h < 0)
        ps = [fark(m, KUME[d]) for d in IZLEME]
        (uyan if all((p > 0) - (p < 0) == isaret for p in ps) else oynayan).append(abs(h))
print(f"\n6 donemlik farki sifir olmayan aday: {len(uyan) + len(oynayan)}")
print(f"alti donemin altisinda da ayni isaret: {len(uyan)} aday"
      f" , en kucuk farki {min(uyan):.4f}")
print(f"isareti donemden doneme oynayan: {len(oynayan)} aday"
      f" , en buyuk farki {max(oynayan):.4f}")
```

```
donem sayisi  satir  cozunurluk        B        C        D
           1    200      0.0050  +0.0250  -0.0200  -0.0150
           2    400      0.0025  +0.0125  -0.0300  -0.0200
           3    600      0.0017  +0.0033  -0.0317  -0.0300
           4    800      0.0013  -0.0013  -0.0300  -0.0338
           5   1000      0.0010  -0.0030  -0.0290  -0.0350
           6   1200      0.0008  -0.0050  -0.0275  -0.0358

6 donemlik farki sifir olmayan aday: 134
alti donemin altisinda da ayni isaret: 133 aday , en kucuk farki 0.0275
isareti donemden doneme oynayan: 1 aday , en buyuk farki 0.0050
```

Üstteki tablo çözünürlüğün küme büyüklüğüyle düzeldiğini gösterir: 200 satırda 0,0050, 1200 satırda **0,0008**. Bunun altındaki hiçbir fark **temsil bile edilemez**; 1200 satırlık bir deneyin verebileceği en küçük sıfırdan farklı sayı budur. B'nin işareti **dördüncü dönemde**, yani 800 satırda döner ve orada bile değeri −0,0013'tür, o kümenin tam bir adımı. C ile D ilk dönemden itibaren doğru işareti taşır ve büyütmek yalnız değeri oturtur.

Alttaki tarama sınırı verir. Izgaradaki 134 adayın 6 dönemlik farkı sıfırdan farklıdır. Bunların **133'ü** altı dönemin altısında da aynı işareti taşır, yani hangi tek dönemde ölçülürse ölçülsün doğru sıralanır; bu grubun **en küçük farkı 0,0275**'tir. İşareti oynatan **tek aday** B'dir ve farkı **0,0050**'dir. Buradan çıkan sayı bir aralıktır: **bu kümede tek dönemlik bir deneyin güvenle ayırt edebildiği en küçük fark 0,0050 ile 0,0275 arasında bir yerdedir.** Izgara bu aralıkta hiç aday içermediği için daha dar söylenemez ve **söylenmemelidir**; ölçünün kendi belirsizliği de ölçünün bir parçasıdır.

Pratik karşılığı tektir: **0,0275'ten küçük bir farkı tek dönemlik bir deneyle ilan eden her karşılaştırma sonuçsuzdur** ve B bunun işaretli örneğidir. Neden bu kadar büyük bir eşik gerektiği ayrışan tahmin sütununda yazar. B ile taban model 1200 satırın **1140'ında aynı şeyi söyler**; deneyin gerçek örneklemi 1200 değil, ayrıştıkları **60 satırdır**. İki tahmin ayrıştığında yalnız biri doğru olabileceğine göre, altı satırlık toplam fark bu altmışın 33'ünde tabanın, 27'sinde B'nin doğru olması demektir. Payda 1200'dür ama bilgi 60 satırdan gelir; ayrışmayan 1140 satır deneye hiçbir şey katmaz, yalnız farkı küçük gösterir. **Bir deneyin ayırt edebildiği en küçük fark, kümenin büyüklüğüyle değil, iki modelin ayrıştığı satır sayısıyla sınırlıdır.**

#### Özet

- Aynı aday altı dönemde **−0,0050**, tek dönemlerde **+0,0250** ile **−0,0150** arasında ölçülür; bandı **0,0400** ve işareti döner. Taban sınıf oranı 0,5342, dönem çözünürlüğü 0,0050, altı dönemin çözünürlüğü **0,0008**.
- Ayrışan tahmini **0** olan aday hiçbir deneyle ayırt edilemez, çünkü fark yoktur; deneyden önce sorulacak soru budur.
- Izgaranın 134 adayının **133'ü** her dönemde aynı işareti taşır ve bu grubun en küçük farkı **0,0275**; işareti oynayan tek adayın farkı **0,0050**. Ayırt edilebilir en küçük fark bu iki sayının arasındadır ve ızgara aralığı daraltacak aday içermez.
- **0,0275'ten küçük bir farkı tek dönemle ilan eden karşılaştırma sonuçsuzdur.** Küme büyütmek çözünürlüğü 0,0008'e indirir ama B'nin işaretini ancak dördüncü dönemde, 800 satırda çevirir.
- Deneyin gerçek örneklemi 1200 satır değil, iki modelin ayrıştığı **60 satırdır**; toplam fark bu altmışın 33'ünde tabanın, 27'sinde adayın doğru olmasından gelir ve kalan 1140 satır yalnız paydayı büyütür.

#### Sonraki Adım

Deney, iki modeli karşılaştırmanın ne kadar pahalı olduğunu gösterdi: ayırt edilebilir bir fark için ya ayrışmanın büyük olması ya da çok sayıda dönem beklenmesi gerekiyor. Bu, ardındaki soruyu daha da keskinleştirir: **yeni model neden eğitiliyor.** Bir uyarı ateşlediği için mi, takvim geldiği için mi, yoksa isabet gerçekten düştüğü için mi. Sonraki ders üç tetikleyici türünü iki ayrı bozulma senaryosunda koşturur, her birinin kaç model eğitilmesine yol açtığını sayar ve kazanılan isabeti yanına yazar. Veri kaymalı veride yeniden eğitim pencerelerinin üçü de aynı sayıyı verir ve o sayı eski modelin sayısıdır.

### Yeniden Eğitim Politikası

Source: https://academia.sh/tr/kurslar/model-izleme/yeniden-egitim-politikasi

Bir modelin ne zaman yeniden eğitileceğine karar veren tetikleyicilerin ve o kararın gerçekten ne kazandırdığının sayılması: veri kaymalı üretimde üç yeniden eğitim penceresinin üçü de 2026-12 döneminde 0,9800 verir ve bu, hiç yeniden eğitilmemiş eski modelin sayısıyla birebir aynıdır. Takvim, gösterge ve başarım tetikleyicileri iki bozulma senaryosunda koşturulduğunda altı bileşimin altısında da kazanılan isabet 0,0000 çıkar ve karşılığında 3.078.000 satır puanlanır. Kazancı belirleyen şeyin tetikleyici değil pencere olduğu, kavram kaymasında aynı anda tetiklenen yeniden eğitimin pencere 6 ile 0,0000, pencere 1 ile 0,1100 kazandırmasıyla gösterilir.

Bir önceki ders iki modeli üretimde karşılaştırmanın bedelini ölçtü: bu kümede tek dönemlik bir deneyin ayırt edebildiği en küçük fark 0,0050 ile 0,0275 arasındadır ve altında kalan her karşılaştırma sonuçsuzdur. Karşılaştırmanın pahalı olması, ondan önceki soruyu keskinleştirir: **yeni model neden eğitiliyor.**

Üretimde bu sorunun yanıtı bir **yeniden eğitim tetikleyicisidir**: takvim gelmiştir, bir gösterge bandın dışına çıkmıştır ya da isabet gerçekten düşmüştür. Bu ders üç tetikleyici türünü iki ayrı bozulma senaryosunda koşturur ve her birinin yanına iki sayı yazar: **kaç model eğitildi** ve **kazanılan isabet ne kadar**. İkinci sayı bu kursun ikinci iddiasının en net örneğidir.

- **MY21.** Kaynak, hat ve model **kurgudur**; hiçbir öğrenme kitaplığı, orkestratör, dağıtım aracı ya da izleme ürünü çağrılmaz. Tohum 20260218.
- **MY22.** Etiket `abone_ort + 6 x isyeri > 30` **kuralına** bağlıdır ve bu kural **kavramdır**. Karar eşiği ayrı bir ayardır, eğitim ölçütü **dengeli isabettir**, yani **duyarlılık (recall)** ile **özgüllüğün** ortalamasıdır. İzleme dönemlerinde **taban sınıf oranı 0,5342**, dönem başına 200 satır ve isabetin adımı **0,0050**.
- **MY23.** İki senaryo vardır. **Veri kaymasında** girdi kayar, **kural aynı kalır**. **Kavram kaymasında** girdi aynı kalır, kuralın eşiği `2026-09`'dan itibaren 30'dan 27'ye iner.
- **MY24.** Etiket **gecikmelidir**: bir dönemin etiketi **iki dönem sonra** bilinir. Yeniden eğitim yalnız etiketli dönemleri kullanabilir.
- **MY25.** Eğitim bedeli **puanlanan satır** olarak sayılır: her yeniden eğitim, penceredeki satırları 135 aday için puanlar. Ham süre yazılmaz.
- **MY26.** Kazanç, o senaryoda hiç yeniden eğitilmemiş eski modelin `2026-12` isabetine göre ölçülür.

#### Pencere Süpürmesi: En İyi Durum

İlk blok kurulumu kurar ve veri kaymalı üretimde üç ayrı pencereyle yeniden eğitim yapar. Etiket gecikmesi bu blokta **yok sayılır**, yani ulaşılabilecek en iyi durum ölçülür.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin tuketim asamasi , oznitelik
# yolu ve uc agirlikli esik modeli ders icinde uretilir; hicbir ogrenme
# kitapligi , dagitim araci , deney platformu ya da izleme urunu cagrilmaz.
# Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Kaynak okumalari ve donem farkindan tuketim: hattin ilk uc asamasi."""
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e})
    t = {(k["abone"], k["donem"]): k["endeks"] for k in kayit}
    return [dict(k, m3=k["endeks"] - t[(k["abone"], DONEM[DONEM.index(k["donem"]) - 1])])
            for k in kayit if DONEM.index(k["donem"])]


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir ve BIR KEZ hesaplanir; tam gecmisi gorur."""
    ab, bo = {}, {}
    for k in sorted(kayitlar, key=lambda k: DONEM.index(k["donem"])):
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ort(dizi, i):
        s = [v for d, v in dizi if d < i]
        return round(sum(s) / len(s), 4) if s else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ort(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ort(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


KURAL_ESIK, KURAL_ISYERI = 30, 6          # etiketi ureten KURAL , yani kavram
IZLEME = DONEM[6:]                        # uretimde izlenen alti donem
IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)       # karar esigi AYRI bir ayardir


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def egit(kayitlar, oz, donemler, et):
    """Agirlik ve karar esigi BIRLIKTE aranir (27 x 5 = 135 aday); olcut
    duyarlilik ile ozgullugun ortalamasi , yani DENGELI ISABETTIR."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en = (-1.0, None, None, 0.0, 0.0)
    for w in IZGARA:
        for kr in KARAR_IZGARA:
            dp = dn = yp = yn = 0
            for k in kume:
                o = oz[(k["abone"], k["donem"])]
                t, g = tahmin(o, w, kr), et[(k["abone"], k["donem"])]
                if t and g:
                    dp += 1
                elif t:
                    yp += 1
                elif g:
                    yn += 1
                else:
                    dn += 1
            du = dp / (dp + yn) if dp + yn else 0.0
            oz2 = dn / (dn + yp) if dn + yp else 0.0
            if (du + oz2) / 2 > en[0]:
                en = ((du + oz2) / 2, w, kr, du, oz2)
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "duyarlilik": round(en[3], 4), "ozgulluk": round(en[4], 4), "satir": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """KURGU veri kaymasi: isyeri tuketimi her donem `egim` kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]



def kavram_kaydir(oz, kayitlar, baslangic="2026-09", yeni_esik=27):
    """KURGU kavram kaymasi: GIRDI AYNI , kural degisiyor (esik 30'dan 27'ye)."""
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]): etiket(
        oz[(k["abone"], k["donem"])],
        yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK) for k in kayitlar}


def isb(kayitlar, oz, m, donem, et):
    kume = [k for k in kayitlar if k["donem"] == donem]
    return round(sum(1 for k in kume
                     if tahmin(oz[(k["abone"], k["donem"])], m["agirlik"], m["karar"])
                     == et[(k["abone"], k["donem"])]) / len(kume), 4)


def pay(kayitlar, oz, m, donem):
    kume = [k for k in kayitlar if k["donem"] == donem]
    return round(sum(tahmin(oz[(k["abone"], k["donem"])], m["agirlik"], m["karar"])
                     for k in kume) / len(kume), 4)


TUM = satirlar()
OZ = oznitelikler(TUM)
URETIM = kaydir(TUM)                      # uretimde girdi kaymis durumda
OZU = oznitelikler(URETIM)
ET_V = {c: etiket(o) for c, o in OZU.items()}      # veri kaymasi: KURAL AYNI
ET_K = kavram_kaydir(OZ, TUM)                      # kavram kaymasi: kural degisti
TABAN = egit(TUM, OZ, DONEM[1:9], {c: etiket(o) for c, o in OZ.items()})
izl = [k for k in TUM if k["donem"] in IZLEME]
e = [etiket(OZ[(k["abone"], k["donem"])]) for k in izl]
print(f"taban model {TABAN['agirlik']}/{TABAN['karar']} dengeli isabet {TABAN['dengeli']}"
      f" duyarlilik {TABAN['duyarlilik']:.4f} ozgulluk {TABAN['ozgulluk']:.4f}")
print(f"izleme {len(izl)} satir , taban sinif orani {round(1 - sum(e) / len(e), 4)}"
      f" , donem basina 200 satir , isabetin adimi 0.0050")

# Veri kaymali uretimde yeniden egitim: butun etiketler elde olsaydi , yani
# gecikme hic olmasaydi ulasilabilecek EN IYI durum.
print("\npencere  egitim donemi     satir  dengeli isabet  2026-12 isabeti")
for p in (3, 6, 11):
    don = [d for d in DONEM if DONEM.index("2026-11") - p < DONEM.index(d)
           <= DONEM.index("2026-11")]
    m = egit(URETIM, OZU, don, ET_V)
    print(f"{p:>7}  {don[0]}..{don[-1]}  {m['satir']:>5}  {m['dengeli']:>14}"
          f"  {isb(URETIM, OZU, m, '2026-12', ET_V):>15}")
print(f"   eski model                       {TABAN['dengeli']}"
      f"  {isb(URETIM, OZU, TABAN, '2026-12', ET_V):>15}")
```

```
taban model (0.6, 0.0, 4)/18 dengeli isabet 0.9864 duyarlilik 1.0000 ozgulluk 0.9728
izleme 1200 satir , taban sinif orani 0.5342 , donem basina 200 satir , isabetin adimi 0.0050

pencere  egitim donemi     satir  dengeli isabet  2026-12 isabeti
      3  2026-09..2026-11    600          0.9824             0.98
      6  2026-06..2026-11   1200            0.98             0.98
     11  2026-01..2026-11   2000          0.9849             0.98
   eski model                       0.9864             0.98
```

Eğitim penceresi büyüdükçe eğitim üzerindeki dengeli isabet oynar: 0,9824, 0,9800, 0,9849. Bu üç sayı bir seçim yapılabileceği izlenimi verir. Son sütun o izlenimi siler. **Üç pencerenin üçü de `2026-12`'de 0,9800 verir** ve bu, hiç yeniden eğitilmemiş eski modelin sayısıyla **birebir aynıdır**. Üçü de aynı ağırlığı ve aynı karar eşiğini bulmuştur, çünkü aramanın çözmeye çalıştığı **kural değişmemiştir**. Girdi kaymıştır, ama etiketi üreten bağıntı yerinde durduğu sürece doğru bir model kaymış girdide de doğru kalır. Kazanılan isabet **0,0000**'dır ve eğitim bedeli ödenmiştir.

#### Tetikleyici mi, Pencere mi

İkinci blok üç tetikleyiciyi iki senaryoda koşturur. Etiket gecikmesi artık uygulanır: bir dönemde tetiklenen yeniden eğitim yalnız **iki dönem öncesine** kadar etiketli veriyi görür. `atesleyen` sütunu tetikleyicinin kaç dönemde ateşlediği, yani kaç model eğitildiğidir.

```python
# Bu blok ilk bloktaki TUM , OZ , URETIM , OZU , TABAN , ET_V , ET_K ,
# IZLEME , IZGARA , KARAR_IZGARA , egit , isb ve pay tanimlarini surdurur.
GECIKME, PENCERE, BELLEK = 2, 6, {}


def tetikle(ad, kayitlar, oz, et, simdi, pencere=PENCERE):
    """Etiket gecikmelidir: `simdi` doneminde ancak `simdi - GECIKME` etiketlidir."""
    i = DONEM.index(simdi) - GECIKME
    don = [d for d in DONEM if i - pencere < DONEM.index(d) <= i]
    anahtar = (ad, don[0], don[-1], pencere)
    if anahtar not in BELLEK:
        BELLEK[anahtar] = egit(kayitlar, oz, don, et)
    m = BELLEK[anahtar]
    return m, m["satir"] * len(IZGARA) * len(KARAR_IZGARA)


def sapan(dizi, taban, band=0.05):
    return [d for d, v in dizi if abs(v - taban) / taban > band]


print("senaryo         tetikleyici  atesleyen  puanlanan satir  2026-12  kazanc")
for ad, kay, oz, et in (("veri kaymasi", URETIM, OZU, ET_V),
                        ("kavram kaymasi", TUM, OZ, ET_K)):
    t12 = isb(kay, oz, TABAN, "2026-12", et)
    ti = isb(kay, oz, TABAN, "2026-08", et)
    for t, donemler in (
            ("takvim", list(IZLEME)),
            ("gosterge", sapan([(d, pay(kay, oz, TABAN, d)) for d in IZLEME],
                               pay(kay, oz, TABAN, "2026-08"))),
            ("basarim", sapan([(d, isb(kay, oz, TABAN, DONEM[DONEM.index(d) - GECIKME], et))
                               for d in IZLEME[GECIKME:]], ti))):
        bedel, son = 0, None
        for d in donemler:
            m, b = tetikle(ad, kay, oz, et, d)
            bedel += b
            son = m if DONEM.index(d) < DONEM.index("2026-12") else son
        i12 = isb(kay, oz, son, "2026-12", et) if son else t12
        print(f"{ad:<15} {t:<11}  {len(donemler):>9}  {bedel:>15}  {i12:>7}  {i12 - t12:+.4f}")

print("\nkavram kaymasi , 2026-11'de tetiklenen yeniden egitim: pencere secimi")
for p in (1, 3, 6):
    m, _ = tetikle("kp", TUM, OZ, ET_K, "2026-11", pencere=p)
    i12 = isb(TUM, OZ, m, "2026-12", ET_K)
    print(f"  pencere {p}  {str(m['agirlik']) + '/' + str(m['karar']):<18}"
          f" egitim satiri {m['satir']:>4}  2026-12 isabeti {i12}"
          f"  kazanc {i12 - isb(TUM, OZ, TABAN, '2026-12', ET_K):+.4f}")
```

```
senaryo         tetikleyici  atesleyen  puanlanan satir  2026-12  kazanc
veri kaymasi    takvim               6           891000     0.98  +0.0000
veri kaymasi    gosterge             3           486000     0.98  +0.0000
veri kaymasi    basarim              0                0     0.98  +0.0000
kavram kaymasi  takvim               6           891000     0.85  +0.0000
kavram kaymasi  gosterge             3           486000     0.85  +0.0000
kavram kaymasi  basarim              2           324000     0.85  +0.0000

kavram kaymasi , 2026-11'de tetiklenen yeniden egitim: pencere secimi
  pencere 1  (0.6, 0.2, 4)/22   egitim satiri  200  2026-12 isabeti 0.96  kazanc +0.1100
  pencere 3  (0.8, 0.4, 4)/34   egitim satiri  600  2026-12 isabeti 0.85  kazanc +0.0000
  pencere 6  (0.6, 0.0, 4)/18   egitim satiri 1200  2026-12 isabeti 0.85  kazanc +0.0000
```

Altı satırın **altısında da kazanç 0,0000**. Toplam **3.078.000 satır** puanlanmış, yirmi kez yeniden eğitim koşmuş ve `2026-12`'de okunan sayı her seferinde tetikleyicisiz durumun sayısı çıkmıştır. Satırların ayrı ayrı okunması bunun üç ayrı nedeni olduğunu gösterir.

**Başarım tetikleyicisi veri kaymasında hiç ateşlemez** ve bu doğru karardır: kazanılacak bir şey yoktur, bedeli de 0'dır. Aynı tetikleyici kavram kaymasında ateşler ama **iki dönem geç**; `2026-09`'da düşen isabet ancak `2026-11`'de bilinir. **Gösterge tetikleyicisi iki senaryoda da aynı üç dönemde ateşler.** Zararlı bozulmanın olduğu senaryoyla zararsız olanı ayırt etmez, çünkü baktığı tahmin payı ikisinde de aynı biçimde kaymaktadır. Kursun kuralı bu satırda yazılıdır: **bir göstergenin ateşlemesi bozulmanın kanıtı değildir.** **Takvim tetikleyicisi** hiçbir şeye bakmadan en çok modeli eğitir ve en büyük bedeli öder.

Son tablo asıl nedeni verir. Kavram kaymasında `2026-11`'de tetiklenen yeniden eğitim, **aynı an, aynı veri ve aynı ızgarayla** pencere 6'da 0,8500, pencere 1'de **0,9600** verir; kazanç **0,0000** ile **0,1100** arasında değişir. Pencere 6, biri yeni kurala beşi eski kurala ait dönemleri birlikte görür ve dengeli isabeti en büyükleyen arama **çoğunluğu** izleyerek eski modeli yeniden bulur. Pencere 1 yalnız yeni kuralın dönemini görür ve başka bir modele iner. **Yeniden eğitimin kazandırdığı şey tetikleyici değil, pencerenin hangi kuralın dönemlerini içerdiğidir.** Kazanç 0,1100 önceki dersin ayırt edilebilir en küçük farkının çok üstündedir; 0,0000 ise zaten sıfırdır.

Onay akışının ölçülebilir karşılığı buradan çıkar. Bir yeniden eğitim önerisi onaya geldiğinde tek başına "gösterge ateşledi" cümlesi bir gerekçe değildir; kararı verilebilir kılan üç sayı **penceresi**, **adayın eski modelden ayrışan tahmin sayısı** ve **gölgede ölçülmüş kazancıdır**. Üçü yazılmadığında onay, sonucu 0,0000 olan bir eğitimi de onaylar.

#### Özet

- Veri kaymalı üretimde üç yeniden eğitim penceresi eğitimde 0,9824, 0,9800 ve 0,9849 verir, ama `2026-12`'de **üçü de 0,9800**'dür — hiç yeniden eğitilmemiş eski modelle **birebir aynı**. Taban sınıf oranı 0,5342, isabetin adımı 0,0050. **Kural değişmediyse yeniden eğitim hiçbir şey kazandırmaz** ve eğitim bedeli ödenmiş olur.
- Üç tetikleyici ile iki senaryonun **altı bileşiminin altısında da kazanç 0,0000**; karşılığında **3.078.000 satır** puanlanır. Takvim tetikleyicisi en pahalısıdır (891.000 satır), başarım tetikleyicisi veri kaymasında hiç ateşlemez ve bedeli 0'dır.
- **Gösterge tetikleyicisi iki senaryoda da aynı üç dönemde ateşler**; zararlı bozulmayı zararsızından ayırmaz. Bir göstergenin ateşlemesi bozulmanın kanıtı değildir.
- Başarım tetikleyicisi kavram kaymasını görür ama **iki dönem geç** görür; `2026-09`'da düşen isabet `2026-11`'de tetikler.
- Kazancı belirleyen tetikleyici değil **penceredir**: aynı anda tetiklenen yeniden eğitim pencere 6 ile **+0,0000**, pencere 1 ile **+0,1100** verir. Eski kuralın dönemlerini içeren bir pencere, aramayı çoğunluğa uydurup eski modeli yeniden bulur.

#### Sonraki Adım

Üç derste model gölgede ölçüldü, kanaryaya verildi, deneyle karşılaştırıldı ve yeniden eğitildi. Her adımda ortaya yeni bir model, yeni bir pencere ve yeni bir karar eşiği çıktı. Sorulmayan tek şey, `2026-12`'de bir aboneye verilmiş **tek bir tahminin** aylar sonra kime sorulacağıdır: hangi model üretti, hangi öznitelik değerleriyle, hangi karar eşiğiyle. Sonraki ders bu soruyu bir arama olarak sayar — karar kaydı varken ve yokken bir tahminin nedenini bulmak için kaç satır okunuyor, kanarya döneminde kaç tahmin **hiçbir okumayla** açıklanamıyor ve kaydın kendisi kaç alan saklamaya mal oluyor.

### Model Denetlenebilirliği

Source: https://academia.sh/tr/kurslar/model-izleme/model-denetlenebilirligi

Üretilmiş tek bir tahminin nedenine aylar sonra ulaşılabilmesinin karar kaydı varken ve yokken ayrı ayrı sayılması: karar kaydıyla bir tahminin modeli, karar eşiği, öznitelik değerleri ve puanı tek satırda okunur ve tahmin başına 1,00 satır düşer. Kayıt olmadığında aynı iki yüz tahmin için 84.740 satır okunur, tahmin başına 423,70 eder ve buna karşın 189 tahminin hangi modelden geldiği belirlenemez, çünkü kanarya döneminde iki model tahminlerin 189'unda aynı şeyi söyler. Kaydın bedeli altı dönem için 12.000 saklanan alandır ve kaynak okumalarının 0,8333 katıdır.

Üç derste model gölgede ölçüldü, kanaryaya verildi, deneyle karşılaştırıldı ve yeniden eğitildi. Her adımda ortaya yeni bir ağırlık, yeni bir karar eşiği ve yeni bir eğitim penceresi çıktı. Sorulmayan tek şey, `2026-12` döneminde tek bir aboneye verilmiş tek bir tahminin aylar sonra **kime sorulacağıdır**: hangi model üretti, hangi öznitelik değerleriyle, hangi karar eşiğiyle.

Bu soru modelin **yorumlanabilirliğinden** ayrıdır. Bir modelin kararını hangi özniteliğin sürüklediği, alt gruplarda nasıl davrandığı ve nasıl açıklanacağı Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda ölçüldü ve burada tekrarlanmaz. Buradaki soru üretimin sorusudur: **hangi modelin koştuğu.** Yorumlanabilirlik elde model varken çalışır; **denetim izi (audit trail)** ise modelin hangisi olduğunu söyleyen kaydın kendisidir. Bu ders o kaydı kurar, olmadığında yapılan aramayı sayar ve kaydın bedelini yazar.

- **MY31.** Kaynak, hat, model ve yayın **kurgudur**; hiçbir model kayıt defteri, izleme ürünü ya da dağıtım aracı çağrılmaz. Tohum 20260218.
- **MY32.** Etiket `abone_ort + 6 x isyeri > 30` kuralına bağlıdır, karar eşiği ayrı bir ayardır. İzleme dönemlerinde **taban sınıf oranı 0,5342**, dönem başına 200 satır, isabetin adımı **0,0050**.
- **MY33.** Denetlenen dönem `2026-12`'dir ve o dönemde **iki model açıktır**: eski model ile kanarya payı 0,10 ile yayılan aday.
- **MY34.** **Karar kaydı** her tahmin için tek satırdır ve on alan taşır: dönem, abone, model, ağırlık, karar eşiği, üç öznitelik değeri, puan ve tahmin.
- **MY35.** Arama bedeli **okunan satır** olarak sayılır; ham süre yazılmaz. Kayıtsız arama yayın kaydından 2 satır, ardından özniteliği yeniden hesaplamak için ham satırları okur.
- **MY36.** Saklama bedeli **alan** olarak sayılır: kayıt sayısı çarpı alan sayısı.

#### Karar Kaydı Neyi Taşır

İlk blok kurulumu kurar, `2026-12` yayınını kanarya payı 0,10 ile koşturur ve her tahmin için bir karar kaydı üretir.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin tuketim asamasi , oznitelik
# yolu ve uc agirlikli esik modeli ders icinde uretilir; hicbir ogrenme
# kitapligi , dagitim araci , deney platformu ya da izleme urunu cagrilmaz.
# Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Kaynak okumalari ve donem farkindan tuketim: hattin ilk uc asamasi."""
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e})
    t = {(k["abone"], k["donem"]): k["endeks"] for k in kayit}
    return [dict(k, m3=k["endeks"] - t[(k["abone"], DONEM[DONEM.index(k["donem"]) - 1])])
            for k in kayit if DONEM.index(k["donem"])]


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir ve BIR KEZ hesaplanir; tam gecmisi gorur."""
    ab, bo = {}, {}
    for k in sorted(kayitlar, key=lambda k: DONEM.index(k["donem"])):
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ort(dizi, i):
        s = [v for d, v in dizi if d < i]
        return round(sum(s) / len(s), 4) if s else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ort(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ort(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


KURAL_ESIK, KURAL_ISYERI = 30, 6          # etiketi ureten KURAL , yani kavram
IZLEME = DONEM[6:]                        # uretimde izlenen alti donem
IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)       # karar esigi AYRI bir ayardir


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar):
    """Egitim olcutu: duyarlilik ile ozgullugun ortalamasi."""
    dp = dn = yp = yn = 0
    for k in kume:
        o = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o, w, karar), etiket(o)
        if t and g:
            dp += 1
        elif t:
            yp += 1
        elif g:
            yn += 1
        else:
            dn += 1
    du = dp / (dp + yn) if dp + yn else 0.0
    oz_ = dn / (dn + yp) if dn + yp else 0.0
    return (du + oz_) / 2, round(du, 4), round(oz_, 4)


def egit(kayitlar, oz, donemler):
    """Agirlik ve karar esigi birlikte aranir: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    sirali = sorted(((dengeli_isabet(kume, oz, w, kr)[0], w, kr)
                     for w in IZGARA for kr in KARAR_IZGARA), key=lambda a: -a[0])
    en = sirali[0]
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "sirali": sirali, "satir": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """KURGU veri kaymasi: isyeri tuketimi her donem `egim` kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


TUM = satirlar()
OZ = oznitelikler(TUM)
TABAN = egit(TUM, OZ, DONEM[1:9])
URETIM = kaydir(TUM)                      # uretimde girdi kaymis durumda
OZU = oznitelikler(URETIM)
d, du, oz_ = dengeli_isabet([k for k in TUM if k["donem"] in DONEM[1:9]], OZ,
                            TABAN["agirlik"], TABAN["karar"])
izl = [k for k in TUM if k["donem"] in IZLEME]
e = [etiket(OZ[(k["abone"], k["donem"])]) for k in izl]
print(f"taban model {TABAN['agirlik']} karar esigi {TABAN['karar']}"
      f" dengeli isabet {TABAN['dengeli']} duyarlilik {du:.4f} ozgulluk {oz_:.4f}")
print(f"izleme {len(izl)} satir , taban sinif orani {round(1 - sum(e) / len(e), 4)}"
      f" , donem basina 200 satir , isabetin adimi 0.0050")


# Bu blok ilk bloktaki TUM , OZ , URETIM , OZU , TABAN , IZLEME , DONEM ,
# ABONE , etiket , puan ve tahmin tanimlarini surdurur.
ADAY = {"ad": "aday", "agirlik": (0.8, 0.4, 4), "karar": 34}
ESKI = dict(TABAN, ad="eski")
PAY, SON = 0.10, "2026-12"
son = [k for k in URETIM if k["donem"] == SON]
N_KAN = int(len(son) * PAY)
ALAN = ("donem", "abone", "model", "agirlik", "karar_esigi", "abone_ort",
        "bolge_ort", "isyeri", "puan", "tahmin")


def karar_kaydi(kume, oz, n_kanarya):
    """Her tahmin icin tek satir: hangi model , hangi oznitelik , hangi esik."""
    kayit = {}
    for j, k in enumerate(kume):
        o = oz[(k["abone"], k["donem"])]
        m = ADAY if j < n_kanarya else ESKI
        kayit[(k["abone"], k["donem"])] = dict(
            zip(ALAN, (k["donem"], k["abone"], m["ad"], m["agirlik"], m["karar"],
                       o["abone_ort"], o["bolge_ort"], o["isyeri"],
                       round(puan(o, m["agirlik"]), 4),
                       tahmin(o, m["agirlik"], m["karar"]))))
    return kayit


KAYIT = karar_kaydi(son, OZU, N_KAN)
ayrisan = [k["abone"] for k in son
           if tahmin(OZU[(k["abone"], SON)], ESKI["agirlik"], ESKI["karar"])
           != tahmin(OZU[(k["abone"], SON)], ADAY["agirlik"], ADAY["karar"])]
print(f"{SON} yayini: eski {ESKI['agirlik']}/{ESKI['karar']} ,"
      f" aday {ADAY['agirlik']}/{ADAY['karar']} , kanarya payi {PAY}")
print(f"istek {len(son)} , kanaryaya giden {N_KAN} ,"
      f" iki modelin ayristigi tahmin {len(ayrisan)}")
print("karar kaydi alanlari:", " ".join(ALAN))
print("ornek:", " ".join(str(v) for v in KAYIT[("A001", SON)].values()))
```

```
taban model (0.6, 0.0, 4) karar esigi 18 dengeli isabet 0.9864 duyarlilik 1.0000 ozgulluk 0.9728
izleme 1200 satir , taban sinif orani 0.5342 , donem basina 200 satir , isabetin adimi 0.0050
2026-12 yayini: eski (0.6, 0.0, 4)/18 , aday (0.8, 0.4, 4)/34 , kanarya payi 0.1
istek 200 , kanaryaya giden 20 , iki modelin ayristigi tahmin 11
karar kaydi alanlari: donem abone model agirlik karar_esigi abone_ort bolge_ort isyeri puan tahmin
ornek: 2026-12 A001 aday (0.8, 0.4, 4) 34 35.502 27.6233 1 43.4509 1
```

Örnek satır tek başına bir açıklamadır: `A001` abonesinin `2026-12` tahmini **aday** model tarafından, `(0.8, 0.4, 4)` ağırlığı ve **34** karar eşiğiyle üretilmiştir; abone ortalaması 35,502, bölge ortalaması 27,6233, abone tipi işyeri ve puan 43,4509'dur. Puan eşiği aştığı için tahmin 1'dir. Kayıt hiçbir yeniden hesap gerektirmez ve **hangi modelin koştuğunu** söyler.

#### Kayıt Olmadan Yapılan Arama

İkinci blok aynı iki yüz tahmini kayıtsız durumda arar: yayın kaydından o dönemde açık olan iki model bulunur, öznitelikler ham satırlardan yeniden hesaplanır ve gözlenen tahminle **hangi modellerin uyuştuğu** sayılır.

```python
# Bu blok ikinci bloktaki KAYIT , ALAN , ESKI , ADAY , son , SON , N_KAN ,
# URETIM , OZU , ABONE ve DONEM tanimlarini surdurur.
def arama_kayitsiz(abone, donem, kayitlar, tahmini):
    """Kayit yoksa: yayin kaydindan modeller , ham satirlardan oznitelikler."""
    i, b = DONEM.index(donem), ABONE[abone]["bolge"]
    ab = [k["m3"] for k in kayitlar if k["abone"] == abone and DONEM.index(k["donem"]) < i]
    bo = [k["m3"] for k in kayitlar if k["bolge"] == b and DONEM.index(k["donem"]) < i]
    o = {"abone_ort": round(sum(ab) / len(ab), 4), "bolge_ort": round(sum(bo) / len(bo), 4),
         "isyeri": 1 if ABONE[abone]["tip"] == "isyeri" else 0}
    uyan = [m["ad"] for m in (ESKI, ADAY) if tahmin(o, m["agirlik"], m["karar"]) == tahmini]
    return 2 + len(ab) + len(bo), uyan          # 2 satir yayin kaydi + ham satirlar


ok_k = ok_s = tek_k = tek_s = 0
for k in son:
    kd = KAYIT[(k["abone"], SON)]
    ok_k, tek_k = ok_k + 1, tek_k + 1           # kayitli arama: tek satir , tek model
    o, uyan = arama_kayitsiz(k["abone"], SON, URETIM, kd["tahmin"])
    ok_s, tek_s = ok_s + o, tek_s + (len(uyan) == 1)
print("arama                okunan satir  tahmin basina  modeli belirlenen  belirlenemeyen")
for ad, ok, tek in (("karar kaydi ile", ok_k, tek_k), ("karar kaydi olmadan", ok_s, tek_s)):
    print(f"{ad:<20} {ok:>12}  {ok / len(son):>13.2f}  {tek:>17}  {len(son) - tek:>14}")

esik_asan = sum(1 for c, kd in KAYIT.items() if kd["tahmin"] == 1)
isyeri_belirleyici = sum(1 for c, kd in KAYIT.items() if kd["tahmin"] == 1
                         and kd["puan"] - kd["agirlik"][2] * kd["isyeri"] <= kd["karar_esigi"])
print(f"\nkarar kaydindan okunan neden: esigi asan tahmin {esik_asan} , bunlarin"
      f" {isyeri_belirleyici} tanesinde abone tipi tek basina belirleyici")
kayit_sayisi = len(IZLEME) * len(son)
kaynak_alan = len(ABONE) * len(DONEM) * 6
print(f"alti izleme donemi kaydedilseydi: {kayit_sayisi} kayit x {len(ALAN)} alan ="
      f" {kayit_sayisi * len(ALAN)} saklanan alan")
print(f"    kaynak okumasi {len(ABONE) * len(DONEM)} satir x 6 alan = {kaynak_alan} alan")
print(f"    karar kaydinin kaynaga orani {kayit_sayisi * len(ALAN) / kaynak_alan:.4f}")
```

```
arama                okunan satir  tahmin basina  modeli belirlenen  belirlenemeyen
karar kaydi ile               200           1.00                200               0
karar kaydi olmadan         84740         423.70                 11             189

karar kaydindan okunan neden: esigi asan tahmin 109 , bunlarin 57 tanesinde abone tipi tek basina belirleyici
alti izleme donemi kaydedilseydi: 1200 kayit x 10 alan = 12000 saklanan alan
    kaynak okumasi 2400 satir x 6 alan = 14400 alan
    karar kaydinin kaynaga orani 0.8333
```

Okunan satır sütunu beklenen farkı verir: karar kaydıyla tahmin başına **1,00**, kayıt olmadan **423,70** satır. Aradaki dört yüz kat, öznitelik yolunun kapsamından gelir; `bolge_ort` tek bir abone için hesaplanamaz, bölgedeki bütün abonelerin geçmişini ister. Bu kadarı bir maliyet farkıdır ve yeterince veri okunursa kapanır.

Son iki sütun kapanmayanı gösterir. Karar kaydıyla **200 tahminin 200'ü** tek bir modele bağlanır. Kayıtsız aramada bu sayı **11**'e düşer ve **189 tahminin hangi modelden geldiği belirlenemez**. Nedeni tabloda yazılıdır: kanarya döneminde iki model 200 tahminin **189'unda aynı şeyi söyler**, dolayısıyla gözlenen çıktı ikisiyle de uyuşur. Yeniden hesap özniteliği geri getirir ama **hangi modelin koştuğu bilgisini geri getirmez**; o bilgi ölçülebilir bir izin değil, **yazılmış bir kaydın** ürünüdür.

Buradaki ters durum kayda değer. Kayıtsız aramanın çözebildiği tek grup, iki modelin **ayrıştığı** 11 tahmindir — yani denetimin kendiliğinden çalıştığı yer, adayın eski modelden gerçekten ayrıldığı yerdir. Kanaryanın küçük payları tam da bu ayrışmayı azaltmak için seçilir. **Riski küçülten aynı seçim, geriye dönük denetlenebilirliği de küçültür**; ilk derste ölçünün çözünürlüğüyle yaşanan takas burada denetimle yeniden karşımıza çıkar.

Bu iki tahminin açıklaması aynı değildir. Aynı çıktıyı veren iki model farklı ağırlık, farklı karar eşiği ve farklı puan taşır; **hangisinin geçerli olduğu seçilemediğinde ortada bir açıklama değil, iki uyumsuz aday açıklama vardır.** Bir itiraz geldiğinde verilecek yanıt bu ikisinden biri olamaz.

Kaydın kendisi bedavaya gelmez. Altı izleme döneminin tamamı kaydedilseydi **1200 kayıt** ve **12.000 saklanan alan** eder; kaynak okumalarının **0,8333** katı, yani hattın taşıdığı ham verinin neredeyse tamamı kadar bir yük. Karşılığında kayıt yalnız modeli değil **nedeni** de tek geçişte verir: eşiği aşan 109 tahminin **57'sinde abone tipi tek başına belirleyicidir**, yani puandan işyeri katkısı çıkarıldığında eşik aşılmaz. Bu sayı kayıttaki `puan` ile `isyeri` alanlarından okunur; kayıtsız durumda aynı sayı için 84.740 satır yeniden okunması gerekir ve sonucun 189 tahminde hangi modele ait olduğu yine bilinemez.

#### Özet

- Karar kaydı bir tahmini on alanda bağlar: model, ağırlık, karar eşiği, üç öznitelik değeri, puan ve tahmin. Tahmin başına okunan satır **1,00**.
- Kayıtsız arama aynı iki yüz tahmin için **84.740 satır** okur, tahmin başına **423,70** eder ve buna karşın **189 tahminin modeli belirlenemez**; belirlenebilen 11 tahmin, iki modelin ayrıştığı tahminlerdir.
- Yeniden hesap özniteliği geri getirir, **hangi modelin koştuğunu getirmez**. Yorumlanabilirlik elde model varken çalışır; denetim izi modelin hangisi olduğunu söyleyen **yazılmış kayıttır** ve kaydedilmemişse üretilemez.
- Kanaryanın küçük payı ayrışmayı azalttığı için **riski küçülten seçim geriye dönük denetlenebilirliği de küçültür**; aynı çıktıyı veren iki model farklı puan ve farklı eşik taşır, dolayısıyla belirlenemeyen tahminin bir değil **iki uyumsuz** açıklaması vardır.
- Kaydın bedeli altı dönem için **12.000 saklanan alandır** ve kaynak okumalarının **0,8333** katıdır. Karşılığında neden tek geçişte okunur: eşiği aşan 109 tahminin **57'sinde** abone tipi tek başına belirleyicidir. Taban sınıf oranı 0,5342 ve isabetin adımı 0,0050.

#### Sonraki Adım

Dört derste model yayına alındı, karşılaştırıldı, yeniden eğitildi ve ürettiği her tahmin kaydedildi. Bütün bu ölçüler **içeriye** dönüktür: ekibin kendi kararını verebilmesi için tutulmuştur. Sorulmayan şey, aynı düzeneğin **dışarıya karşı ne borçlu olduğudur** — bir kararın gerekçesinin istenmesi, bir tahmine itiraz edilmesi, bir kaydın belirli bir süre saklanmasının beklenmesi. Sonraki ders bu beklentileri sınıflarıyla sayar ve her sınıfın mühendislik karşılığını yazar: kaç ek alan, kaç saklanan kayıt ve hangilerinin bu derste kurulan karar kaydıyla **zaten karşılandığı**.

### Uyumluluk Gereksinimleri

Source: https://academia.sh/tr/kurslar/model-izleme/uyumluluk-gereksinimleri

Dışarıya karşı yüklenilen dört yükümlülük sınıfının kaç ek alan ve kaç saklanan kayıt istediğinin sayılması: taban kayıt karar başına üç alan tutar ve 1200 kararın 36'sı bugünkü kaynaktan yeniden hesaplandığında ayrılır, çünkü geç gelen 80 satır 239 kararın özniteliğini değiştirmiştir. Kayıt tutma yükümlülüğü karar başına dört alan ekler ve yeniden hesabı 1200'de 1200 yapar; açıklama yükümlülüğü sıfır alan ekler, çünkü beş açıklama değeri de o dört alandan türetilir; itiraz hakkı yalnız eşiğe bir puandan yakın 108 olumsuz kararda üç alan ekler ve o 108 karar modelin 26 yanlışının 26'sını kapsar; saklama politikası ise hiç alan eklemez, kayıt siler. Üç dönemlik saklamada 600 karar kaydı düşer ve itiraz penceresi hâlâ açıkken silinmiş karar sayısı 600 olur.

Dört derste model yayına alındı, gölgede ve kanaryada ölçüldü, karşılaştırıldı ve kararları
kaydedildi. Dördünün ortak yanı şudur: ölçünün muhatabı **içeridedir**. Kanaryanın payını,
deneyin ayırt edebildiği en küçük farkı ve karar kaydının aldığı yeri kuran ekip, onların
yeterli olup olmadığına da kendisi karar verir.

Bu ders muhatabı dışarı taşır. Bir modelin ürettiği karar, o kararın öznesine karşı bir
borç doğurur ve borcun ne olduğu ekibin tercihine bırakılmaz. Ders o borcu **dört yükümlülük
sınıfı** olarak alır — kayıt tutma, açıklama, itiraz ve saklama süresi — ve her birinin tek
bir mühendislik karşılığını sayar: **karar başına kaç ek alan ve kaç saklanan kayıt.**

- **MY41.** Kaynak, hat, öznitelik yolu, model ve karar defteri **kurgudur**. Hiçbir
  düzenleme, standart, kurum, ülke, sertifika ya da denetim kuruluşu adı yazılmaz; hiçbir
  ürün, servis ya da kitaplık çağrılmaz. Tohum 20260218.
- **MY42.** Yükümlülükler **sınıflarıyla** anılır ve kaynakları değil **sistemden ne
  istedikleri** ölçülür: kayıt tutma kararın yeniden hesaplanabilmesini, açıklama tek bir
  kararın gerekçesinin gösterilmesini, itiraz hakkı incelemenin sonucunun yazılmasını,
  saklama süresi ise kaydın **belirli bir süre sonra silinmesini** ister.
- **MY43.** Taban çizgisi **yükümlülüksüz kurulumdur**: karar defteri yalnız sunmak için
  gereken üç alanı tutar ve dışarıya hiçbir şey borçlanılmadığı varsayılır.
- **MY44.** Model ve etiket ortak tanımdan gelir: ağırlık `(0.6, 0.0, 4)`, karar eşiği 18,
  dengeli isabet 0,9864. İzleme altı dönem, karar 1200, **taban sınıf oranı 0,5342** ve dönem
  isabetinin en küçük adımı 0,0050'dir.
- **MY45.** Kayıt bir **kurgu geç gelen okumayla** sınanır: `2026-04` döneminde 40 abonenin
  sayacı zamanında okunmadı, satırlar kararlar verildikten **sonra** kaynağa eklendi. Kaynak
  karar anında 2120, bugün 2200 satırdır.
- **MY46.** İtiraz penceresi **üç dönemdir**: bir karara en geç üçüncü dönemin sonunda itiraz
  edilebilir. Saklama süresi de dönem olarak yazılır.

Denetim izi, sahiplik ve saklama politikası Veri Mühendisliğine Giriş ile Yönetişim, Risk ve
Uyumluluk kurslarında ölçüldü; yordamları **tekrarlanmaz**. Bir önceki ders karar kaydını
kurdu. Bu ders o kaydı **veri** olarak alır ve tek bir soru sorar: dört sınıfın her biri
kaydın üstüne ne koyar.

#### Yükümlülüğün Kayda Çevrilmesi

İlk blok kaynağı, hattın tüketim aşamasını, öznitelik yolunu ve modeli kurar; sonra aynı
kararları iki kez üretir — bir kez karar anındaki kaynaktan, bir kez bugünkü kaynaktan.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi , oznitelik
# yolu , esik modeli ve karar defteri ders icinde uretilir; hicbir duzenleme ,
# denetim kurulusu , urun ya da servis cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti = []
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir , bir kez hesaplanir."""
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if d < i]
        return round(sum(secili) / len(secili), 4) if secili else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ortalama(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ortalama(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)
IZLEME = DONEM[6:]
KURAL_ESIK, KURAL_ISYERI = 30, 6


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def egit(kayitlar, oz):
    """Egitim olcutu dengeli isabet; karar esigi AYRI bir ayardir ve aranir."""
    kume = [k for k in kayitlar if k["donem"] in DONEM[1:9]]
    en = (-1.0, None, None)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            dp = dn = yp = yn = 0
            for k in kume:
                o = oz[(k["abone"], k["donem"])]
                t, g = tahmin(o, w, karar), etiket(o)
                dp, yp, yn, dn = (dp + (t and g), yp + (t and not g),
                                  yn + (not t and g), dn + (not t and not g))
            d = (dp / (dp + yn) if dp + yn else 0.0) + (dn / (dn + yp) if dn + yp else 0.0)
            if d / 2 > en[0]:
                en = (d / 2, w, karar)
    return {"agirlik": en[1], "karar_esigi": en[2], "dengeli": round(en[0], 4)}


# KURGU gec gelen okuma: 2026-04'te 40 abonenin sayaci zamaninda okunmadi ve
# satirlar kararlar verildikten SONRA kaynaga eklendi.
HAM = kaynak_uret()
KARAR_ANI = tuketim([k for k in HAM
                     if not (k["donem"] == "2026-04" and k["abone"] <= "A040")])
BUGUN = tuketim(HAM)
OZ_A, OZ_B = oznitelikler(KARAR_ANI), oznitelikler(BUGUN)
M = egit(BUGUN, OZ_B)
IZL = [k for k in BUGUN if k["donem"] in IZLEME]
pay1 = sum(etiket(OZ_B[(k["abone"], k["donem"])]) for k in IZL) / len(IZL)
print(f"model: agirlik {M['agirlik']} , karar esigi {M['karar_esigi']} ,"
      f" dengeli isabet {M['dengeli']}")
print(f"izleme donemi {len(IZLEME)} , karar {len(IZL)} ,"
      f" taban sinif orani {round(max(pay1, 1 - pay1), 4)}")

# Taban kayit: sunmak icin gereken en az sey.
TABAN_ALAN = ("abone", "donem", "tahmin")
# Kayit tutma yukumlulugunun karsiligi: kararin OKUDUGU her sey saklanir.
S1_ALAN = ("model_surum", "abone_ort", "bolge_ort", "isyeri")
S1_MODEL_ALAN = ("agirlik", "karar_esigi")
SURUM = {"s1": {"agirlik": M["agirlik"], "karar_esigi": M["karar_esigi"]}}

defter = []
for k in [k for k in KARAR_ANI if k["donem"] in IZLEME]:
    o = OZ_A[(k["abone"], k["donem"])]
    defter.append({"abone": k["abone"], "donem": k["donem"], "model_surum": "s1",
                   "tahmin": tahmin(o, M["agirlik"], M["karar_esigi"]),
                   "abone_ort": o["abone_ort"], "bolge_ort": o["bolge_ort"],
                   "isyeri": o["isyeri"]})

d_ayni = sum(1 for r in defter if tahmin(r, SURUM[r["model_surum"]]["agirlik"],
                                         SURUM[r["model_surum"]]["karar_esigi"]) == r["tahmin"])
oz_fark = sum(1 for r in defter
              if OZ_B[(r["abone"], r["donem"])]["abone_ort"] != r["abone_ort"])
k_fark = sum(1 for r in defter
             if tahmin(OZ_B[(r["abone"], r["donem"])], M["agirlik"], M["karar_esigi"])
             != r["tahmin"])
print(f"\nkaynak satiri: karar aninda {len(KARAR_ANI)} , bugun {len(BUGUN)}")
print(f"defterden yeniden hesap    : birebir {d_ayni}/{len(defter)} , ayrilan"
      f" {len(defter) - d_ayni}")
print(f"bugunku kaynaktan yeniden hesap: birebir {len(defter) - k_fark}/{len(defter)} ,"
      f" ayrilan {k_fark} , oznitelik degeri degisen {oz_fark}")
```

```
model: agirlik (0.6, 0.0, 4) , karar esigi 18 , dengeli isabet 0.9864
izleme donemi 6 , karar 1200 , taban sinif orani 0.5342

kaynak satiri: karar aninda 2120 , bugun 2200
defterden yeniden hesap    : birebir 1200/1200 , ayrilan 0
bugunku kaynaktan yeniden hesap: birebir 1164/1200 , ayrilan 36 , oznitelik degeri degisen 239
```

Son iki satır kayıt tutma yükümlülüğünün neden bir **alan** sorusu olduğunu gösterir. Karar
defteri özniteliklerin karar anındaki değerini taşıdığında 1200 kararın 1200'ü birebir
yeniden hesaplanır. Aynı kararlar bugünkü kaynaktan yeniden üretilmeye kalkıldığında 239
kararın özniteliği değişmiştir ve **36 karar başka çıkar**. Kaynağa dokunan olmamıştır;
yalnızca geç kalan 80 satır yerine oturmuştur.

Bu bir uyumluluk konusundan önce bir ölçme konusudur. Bir kararın gerekçesi sorulduğunda
"kaynağa bakıp yeniden hesaplarız" yanıtı, kaynağın **karar anındaki hâli** saklanmadıkça bir
yanıt değildir. Geç gelen okuma hattın altı bozulma sınıfından biridir ve Veri Mühendisliğine
Giriş kursunda yakalama gecikmesi olarak ölçülmüştü; buradaki yenilik aynı bozulmanın
**geriye dönük** yüzüdür — hat düzelmiştir, kayıt bozulmuştur.

#### Dört Sınıfın Alan Bedeli

İkinci blok dört sınıfı tek tabloda sayar ve saklama süresini süpürür.

```python
# Bu blok ilk bloktaki DONEM , IZLEME , OZ_B , M , SURUM , defter , puan ,
# tahmin ve etiket tanimlarini surdurur.
MARJ, ITIRAZ_PENCERE, SAKLAMA = 1.0, 3, 3


def aciklama(r):
    """Sinif 1'in alanlarindan TURETILIR; yeni alan istemez."""
    w = SURUM[r["model_surum"]]["agirlik"]
    e = SURUM[r["model_surum"]]["karar_esigi"]
    p = puan(r, w)
    return {"katki_abone": round(w[0] * r["abone_ort"], 4),
            "katki_bolge": round(w[1] * r["bolge_ort"], 4),
            "katki_tip": round(w[2] * r["isyeri"], 4),
            "puan": round(p, 4), "esige_uzaklik": round(p - e, 4)}


itirazli = [r for r in defter if r["tahmin"] == 1
            and abs(aciklama(r)["esige_uzaklik"]) <= MARJ]
ornek = itirazli[0]
print(f"esige en yakin olumsuz karar ornegi: {ornek['abone']} {ornek['donem']}"
      f" tahmin {ornek['tahmin']} gercek {etiket(ornek)}")
print(f"aciklama {aciklama(ornek)}")

yanlis_sinirda = sum(1 for r in itirazli if etiket(r) != r["tahmin"])
yanlis_tum = sum(1 for r in defter if etiket(r) != r["tahmin"])
n, ta, sa = len(defter), len(TABAN_ALAN), len(S1_ALAN)
s1 = n * (ta + sa) + len(S1_MODEL_ALAN)
print(f"\nsinif                  karar basina alan  saklanan deger  yeniden hesap")
print(f"taban (yukumluluksuz)  {ta:>17}  {n * ta:>14}  {n - k_fark:>9}/{n}")
print(f"kayit tutma            {ta + sa:>17}  {s1:>14}  {n:>9}/{n}")
print(f"aciklama               {ta + sa:>17}  {s1:>14}  {n:>9}/{n}")
print(f"itiraz                 {str(ta + sa) + ' (+3)':>17}  {s1 + 3 * len(itirazli):>14}"
      f"  {n:>9}/{n}")
tut = len([d for d in IZLEME if DONEM.index(d) > len(DONEM) - 1 - SAKLAMA]) * 200
print(f"saklama ({SAKLAMA} donem)      {ta + sa:>17}  {tut * (ta + sa) + 2:>14}  {tut:>9}/{n}")
print(f"\nesige {MARJ} puandan yakin olumsuz karar: {len(itirazli)}"
      f" (pay {round(len(itirazli) / n, 4)}) ; bunlarin yanlis olani {yanlis_sinirda} ,"
      f" butun defterdeki yanlis {yanlis_tum}")
print(f"izleme isabeti {round((n - yanlis_tum) / n, 4)} , adim {round(1 / 200, 4)}")

print(f"\nsaklama (donem)  tutulan kayit  silinen kayit  itiraza acikken silinmis")
for R in (1, 2, 3, 6, 12):
    tutulan = [d for d in IZLEME if DONEM.index(d) > len(DONEM) - 1 - R]
    kirik = 0
    for t in range(len(DONEM)):
        for d in IZLEME:
            i = DONEM.index(d)
            if i <= t - R and i + ITIRAZ_PENCERE >= t:
                kirik += 200
    print(f"{R:>15}  {len(tutulan) * 200:>13}  {n - len(tutulan) * 200:>13}  {kirik:>24}")
```

```
esige en yakin olumsuz karar ornegi: A002 2026-08 tahmin 1 gercek 1
aciklama {'katki_abone': 18.6, 'katki_bolge': 0.0, 'katki_tip': 0, 'puan': 18.6, 'esige_uzaklik': 0.6}

sinif                  karar basina alan  saklanan deger  yeniden hesap
taban (yukumluluksuz)                  3            3600       1164/1200
kayit tutma                            7            8402       1200/1200
aciklama                               7            8402       1200/1200
itiraz                            7 (+3)            8726       1200/1200
saklama (3 donem)                      7            4202        600/1200

esige 1.0 puandan yakin olumsuz karar: 108 (pay 0.09) ; bunlarin yanlis olani 26 , butun defterdeki yanlis 26
izleme isabeti 0.9783 , adim 0.005

saklama (donem)  tutulan kayit  silinen kayit  itiraza acikken silinmis
              1            200           1000                      2400
              2            400            800                      1400
              3            600            600                       600
              6           1200              0                         0
             12           1200              0                         0
```

Tablonun okunuşu dört sınıfın **eşit olmadığıdır**. Yalnız **kayıt tutma** karar başına
gerçekten yeni alan ister: dört alan, 3600 saklanan değerden 8402'ye çıkış. **Açıklama
yükümlülüğü sıfır alan ekler.** Örnek satır bunu tek bir kararda gösterir: `A002` abonesinin
`2026-08` kararı için üç katkı, puan ve eşiğe uzaklık yazılmıştır ve beşi de kayıt tutma
sınıfının alanlarından türetilmiştir. Ağırlıklar ile eşik saklandığı sürece açıklama bir
**hesaptır**, bir kayıt değil. **İtiraz hakkı** kısmen karşılanır: kararı yeniden hesaplama
kısmını kayıt tutma zaten ödemiştir, yeni olan yalnız incelemenin sonucudur ve o da bütün
kararlara değil **108'ine** yazılır.

O 108 sayısı dersin en keskin okumasıdır. Eşiğe bir puandan yakın duran olumsuz kararlar
1200 kararın 0,0900'ünü tutar, ama modelin **26 yanlışının 26'sı** bu bandın içindedir.
İzleme isabeti 0,9783 ve dönem adımı 0,0050 olduğuna göre 26 yanlış ölçülebilir bir
büyüklüktür. Duyarlılık 1,0000 olduğu için yanlışların hepsi **yanlış alarmdır**: karar
eşiğinin hemen üstüne düşmüş, gerçekte düşük olan aboneler. **İtiraz hakkının mühendislik
karşılığı, kararların yüzde dokuzunu incelemeye açmak ve karşılığında yanlışların tamamına
ulaşabilmektir** — sınıra yakınlık bir tesadüf değil, eşikli her modelin yapısal özelliğidir.

#### Saklama Öbür Üçünü Keser

Dördüncü sınıfın karşılığı öbür üçüne benzemez, çünkü **hiçbir alan eklemez**: dönem zaten
kayıtlıdır ve süre tek bir politika sayısıdır. Saklama politikasının yaptığı şey **kayıt
silmektir** ve sildiği kayıt, ilk üç sınıfın dayandığı kayıttır. Üç dönemlik saklamada defter
1200 karardan 600'e iner, 8402 saklanan değer 4202'ye düşer ve **600 kararın gerekçesi
sorulamaz** hâle gelir. Veri Mühendisliğine Giriş kursunun son dersi bunu veri tarafında
ölçmüştü — saklama politikası geriye dönük denetlenemez dönem bırakır; karar tarafında aynı
şey aynı mekanizmayla olur.

Süpürmenin son sütunu çakışmayı sayıya çevirir. İtiraz penceresi ve saklama süresi üçer
dönemken her dönem bir dönemlik karar kümesi **itiraz hakkı hâlâ açıkken** silinir: toplam
600 karar. Süre ikiye indiğinde 1400, bire indiğinde 2400 olur; altı döneme çıktığında
sıfırlanır. Bu sayı bir uygulama hatası değil, **iki yükümlülük sınıfının birbirini
kesmesidir**: biri kaydı ister, öbürü siler ve ikisi aynı sistemde aynı anda yürürlüktedir.
Ödünleşimin ölçüsü tam olarak bu sütundur — saklama süresi **itiraz penceresinden uzun**
olmadığında karşılanmayan yükümlülük sayısı sıfırdan büyüktür; eşitlik yetmez.

Buradan çıkan kural sınıfların kaynağından bağımsızdır: **bir yükümlülük sınıfı, karşılığı
karar kaydındaki bir alana ya da bir kayıt sayısına yazılmadıkça ölçülmemiş sayılır.**

#### Özet

- Karar defteri karar anındaki öznitelikleri tuttuğunda 1200 kararın 1200'ü birebir yeniden
  hesaplanır; bugünkü kaynaktan hesaplandığında 239 kararın özniteliği değişir ve **36 karar
  başka çıkar**. Kaynağa dokunan yoktur; geç gelen 80 satır yerine oturmuştur.
- **Kayıt tutma** karar başına dört alan ekler ve saklanan değeri 3600'den 8402'ye çıkarır.
  Dört sınıf içinde gerçekten yeni alan isteyen tek sınıf budur.
- **Açıklama** sıfır alan ekler: üç katkı, puan ve eşiğe uzaklık kayıt tutmanın alanlarından
  türetilir. Ağırlık ile eşik saklandığı sürece açıklama bir hesaptır.
- **İtiraz** yalnız eşiğe bir puandan yakın 108 olumsuz kararda üç alan ekler; o 108 karar
  1200'ün 0,0900'üdür ve modelin **26 yanlışının 26'sını** kapsar.
- **Saklama** hiç alan eklemez, kayıt siler: üç dönemde defter 600'e iner ve itiraz penceresi
  hâlâ açıkken silinmiş karar sayısı 600 olur. İki yükümlülük sınıfı aynı sistemde
  birbirini keser.

#### Sonraki Adım

Yükümlülüklerin karşılığı alan ve kayıt olarak yazıldı; hepsi saklanana bir şey ekledi ya da
ondan bir şey götürdü. Ama bu dersin hiçbir satırı, modeli çalışır tutmanın kendisinin ne
tuttuğunu sormadı. Bir model iki ayrı iş yapar: dönem dönem **yeniden eğitilir** ve her dönem
**çıkarım** üretir. İkisi aynı bütçeye yazılır ve tek bir toplam olarak okunur. Sonraki ders o
toplamı ikiye ayırır, yeniden eğitim sıklığı arttıkça hangi kalemin büyüdüğünü ve tek sayıda
okumanın hangi kararı gizlediğini sayar.

### Maliyet Yönetimi

Source: https://academia.sh/tr/kurslar/model-izleme/maliyet-yonetimi

Eğitim ile çıkarımın ayrı izlenmesi ve tek toplamın hangi kararı gizlediğinin sayılması: iş birimi bir puan hesabıdır, altı izleme döneminin çıkarımı 1200 puan hesabı tutar ve sıklık ne olursa olsun sabit kalır, bir yeniden eğitim ise 162.000 tutar. Yeniden eğitim sıklığı altı dönemde bire indiğinde eğitim kalemi 162.000'den 972.000'e çıkar, eğitim payı 0,9926'dan 0,9988'e yükselir ve 2026-12 isabeti beş kurulumun beşinde de 0,9800'de kalır; kazanılan isabet 0,0000 olduğu için kazanç başına maliyet tanımsızdır. Toplamdan okunan tahmin başına maliyet 811,00, çıkarımdan okunan 1,00'dir: çıkarımı tümüyle bedavaya indirmek toplamın 0,0012'sini siler, sıklığı bire birden altıya indirmek 0,8323'ünü siler ve karşılığında 0,0000 isabet ödenir. Kesişme noktası dönem başına 27.000 ile 162.000 istek arasındadır.

Bir önceki ders dışarıya karşı olan borcu alan ve kayıt olarak yazdı: dört yükümlülük sınıfı,
karar başına yedi alan, 8402 saklanan değer. Sayılan her şey **saklamaya** yazıldı. Modeli
çalışır tutmanın kendisi, yani her dönem tekrar tekrar yapılan iş, hiçbir satıra girmedi.

O iş tek parça değildir. Bir üretim modeli iki ayrı iş yapar: dönem dönem **yeniden eğitilir**
ve her dönem **çıkarım** üretir. İkisi genellikle tek bir bütçe kalemine yazılır ve tek bir
toplam olarak okunur. Bu ders o toplamı ikiye ayırır, yeniden eğitim sıklığını süpürür ve
tek sayıda okumanın hangi kararı görünmez kıldığını sayar.

- **MY47.** Kaynak, hat, öznitelik yolu, model ve maliyet sayacı **kurgudur**. Hiçbir bulut
  servisi, hızlandırıcı, ürün ya da fiyat çağrılmaz; para birimi yazılmaz. Tohum 20260218.
- **MY48.** İş birimi **bir puan hesabıdır**: bir satırın özniteliklerinin ağırlıklarla
  çarpılıp toplanması. Eğitim de çıkarım da yalnız bunu yapar, bu yüzden ikisi **aynı birimle**
  aynı tabloya girer. Sayaç `puan` işlevinin içindedir; maliyet varsayılmaz, **ölçülür**.
- **MY49.** Süre yazılmaz. Maliyet **puan hesabı** olarak sayılır, gecikme **dönem** olarak.
- **MY50.** Taban çizgisi **yeniden eğitimsiz kurulumdur**: ilk model bir kez eğitilir ve altı
  izleme dönemi boyunca hiç değişmez.
- **MY51.** Üretimde girdi kayıyor: `2026-09`'dan sonra işyeri tüketimi dönem başına 0,06
  büyüyor. Model `(0.6, 0.0, 4)`, karar eşiği 18, aday 135, eğitim satırı 1600. İzleme altı
  dönem, **taban sınıf oranı 0,5342**, dönem isabetinin adımı 0,0050.
- **MY52.** Çıkarım hacmi dönem başına 200 istektir ve son bölümde bir **serbest değişken**
  olarak süpürülür.

Tahmin başına maliyet muhasebesi Model API'leri ile Uygulama Geliştirme kursunda ve MLOps
Temelleri kursunun ölçekleme dersinde ölçüldü; yordamları **tekrarlanmaz** ve buradaki ölçü
onların üstüne tek bir şey ekler: **eğitim ile çıkarım ayrı sayılır.**

#### İki Kalemin Ayrı Sayılması

İlk blok kaynağı, öznitelik yolunu ve modeli kurar, `puan` işlevine bir sayaç yerleştirir ve
yeniden eğitim sıklığını süpürür.

```python
# KURGUDUR. Kaynak , hattin tuketim asamasi , oznitelik yolu , esik modeli ve
# maliyet sayaci ders icinde uretilir; hicbir bulut servisi , hizlandirici ,
# urun ya da fiyat cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti = []
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir , bir kez hesaplanir."""
    sirali = sorted(kayitlar, key=lambda k: DONEM.index(k["donem"]))
    ab, bo = {}, {}
    for k in sirali:
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ortalama(dizi, i):
        secili = [v for d, v in dizi if d < i]
        return round(sum(secili) / len(secili), 4) if secili else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ortalama(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ortalama(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)
IZLEME = DONEM[6:]
KURAL_ESIK, KURAL_ISYERI = 30, 6
SAYAC = {"puan": 0}          # tek is birimi: bir puan hesabi


def puan(o, w):
    SAYAC["puan"] += 1
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def egit(kayitlar, oz, donemler):
    """Egitim olcutu dengeli isabet; karar esigi AYRI bir ayardir ve aranir."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en = (-1.0, None, None)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            dp = dn = yp = yn = 0
            for k in kume:
                o = oz[(k["abone"], k["donem"])]
                t, g = tahmin(o, w, karar), etiket(o)
                dp, yp, yn, dn = (dp + (t and g), yp + (t and not g),
                                  yn + (not t and g), dn + (not t and not g))
            d = (dp / (dp + yn) if dp + yn else 0.0) + (dn / (dn + yp) if dn + yp else 0.0)
            if d / 2 > en[0]:
                en = (d / 2, w, karar)
    return {"agirlik": en[1], "karar_esigi": en[2], "dengeli": round(en[0], 4),
            "satir": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """KURGU veri kaymasi: isyeri tuketimi her donem `egim` kadar buyuyor."""
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def isabet(kayitlar, m, oz, donem):
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = sum(1 for k in kume
            if tahmin(oz[(k["abone"], k["donem"])], m["agirlik"], m["karar_esigi"])
            == etiket(oz[(k["abone"], k["donem"])]))
    return round(d / len(kume), 4)


TEMIZ = tuketim(kaynak_uret())
OZ_T = oznitelikler(TEMIZ)
IZL_T = [k for k in TEMIZ if k["donem"] in IZLEME]
p1 = sum(etiket(OZ_T[(k["abone"], k["donem"])]) for k in IZL_T) / len(IZL_T)
KAYAN = kaydir(TEMIZ)                    # uretimde girdi kayiyor
OZ = oznitelikler(KAYAN)

SAYAC["puan"] = 0
ILK = egit(KAYAN, OZ, DONEM[1:9])
ILK_MALIYET = SAYAC["puan"]
print(f"taban sinif orani {round(max(p1, 1 - p1), 4)} , izleme donemi {len(IZLEME)} ,"
      f" donem basina istek 200")
print(f"ilk egitim: agirlik {ILK['agirlik']} , karar esigi {ILK['karar_esigi']} ,"
      f" satir {ILK['satir']} , aday {len(IZGARA) * len(KARAR_IZGARA)}")
print(f"olculen egitim maliyeti (puan hesabi): {ILK_MALIYET}")

SAYAC["puan"] = 0
son = isabet(KAYAN, ILK, OZ, "2026-12")
print(f"bir donemlik cikarim maliyeti (puan hesabi): {SAYAC['puan']} ,"
      f" 2026-12 isabeti {son}")

PENCERE = 6
print("\nsiklik  yeniden egitim  egitim maliyeti  cikarim maliyeti  toplam"
      "  egitim payi  2026-12 isabeti")
SONUC = []
for f in (0, 6, 3, 2, 1):
    SAYAC["puan"] = 0
    m = ILK
    if f:
        for i, d in enumerate(IZLEME):
            if (i + 1) % f == 0:
                j = DONEM.index(d)
                m = egit(KAYAN, OZ, [x for x in DONEM
                                     if j - PENCERE < DONEM.index(x) <= j])
    egitim = SAYAC["puan"]
    SAYAC["puan"] = 0
    isb = isabet(KAYAN, m, OZ, "2026-12")
    cikarim = len(IZLEME) * 200
    toplam = egitim + cikarim
    SONUC.append((f, egitim, cikarim, toplam, isb))
    print(f"{f if f else '-':>6}  {len(IZLEME) // f if f else 0:>14}  {egitim:>15}"
          f"  {cikarim:>16}  {toplam:>6}  {round(egitim / toplam, 4):>11}  {isb:>15}")
```

```
taban sinif orani 0.5342 , izleme donemi 6 , donem basina istek 200
ilk egitim: agirlik (0.6, 0.0, 4) , karar esigi 18 , satir 1600 , aday 135
olculen egitim maliyeti (puan hesabi): 216000
bir donemlik cikarim maliyeti (puan hesabi): 200 , 2026-12 isabeti 0.98

siklik  yeniden egitim  egitim maliyeti  cikarim maliyeti  toplam  egitim payi  2026-12 isabeti
     -               0                0              1200    1200          0.0             0.98
     6               1           162000              1200  163200       0.9926             0.98
     3               2           324000              1200  325200       0.9963             0.98
     2               3           486000              1200  487200       0.9975             0.98
     1               6           972000              1200  973200       0.9988             0.98
```

İki kalem arasındaki ölçek farkı doğrudan sayaçtan gelir. Bir dönemlik çıkarım **200 puan
hesabıdır** ve altı dönemde 1200 eder; ilk eğitim 1600 satırı 135 adayla tarar ve **216.000**
tutar. Altı dönemlik pencereyle yapılan bir yeniden eğitim **162.000**'dir, yani bütün izleme
ufkunun çıkarımının **135 katı**. Sıklık sütunu boyunca çıkarım kalemi **hiç değişmez**; değişen
yalnız eğitim kalemidir ve altı dönemde birden her döneme geçildiğinde 162.000'den
**972.000**'e çıkar.

Son sütun bu artışın karşılığını yazar. `2026-12` isabeti beş kurulumun beşinde de
**0,9800**'dür — hiç yeniden eğitilmeyen modelinkiyle **birebir aynı**. Bu, kursun ikinci
iddiasının maliyet tarafındaki karşılığıdır: girdi kaydı, uyarı ateşledi, altı yeniden eğitim
koşuldu ve **isabet tek bir adım bile oynamadı**. Dönem adımı 0,0050 olduğuna göre 0,0000
gerçekten sıfırdır, ölçülemeyen küçük bir kazanç değil. Kural değişmediği sürece doğru bir
model kaymış girdide de doğru kalır ve yeniden eğitim yalnız **bedeli** getirir.

#### Tek Toplamın Gizlediği

İkinci blok aynı sonuçları tahmin başına okur ve çıkarım hacmini süpürür.

```python
# Bu blok ilk bloktaki SONUC , IZLEME , ILK_MALIYET ve PENCERE tanimlarini surdurur.
taban_isabet = SONUC[0][4]
n_tahmin = len(IZLEME) * 200
print("siklik  toplamdan tahmin basina  cikarimdan tahmin basina"
      "  kazanilan isabet  kazanc basina maliyet")
for f, egitim, cikarim, toplam, isb in SONUC:
    kazanc = round(isb - taban_isabet, 4)
    bas = "tanimsiz" if kazanc <= 0 else str(round(egitim / kazanc))
    print(f"{f if f else '-':>6}  {round(toplam / n_tahmin, 2):>22}"
          f"  {round(cikarim / n_tahmin, 2):>24}  {kazanc:>16}  {bas:>21}")

print(f"\ncikarimi tumuyle bedavaya indirmenin toplamdan sildigi pay:"
      f" {round(SONUC[-1][2] / SONUC[-1][3], 4)}")
print(f"siklik 1'den siklik 6'ya inmenin toplamdan sildigi pay:"
      f" {round((SONUC[-1][3] - SONUC[1][3]) / SONUC[-1][3], 4)}"
      f" , odenen isabet {round(SONUC[1][4] - SONUC[-1][4], 4)}")

print("\ndonem basina istek  cikarim (6 donem)  egitim siklik 6  egitim siklik 1"
      "  cikarim hangisini gecti")
for istek in (200, 2000, 20000, 50000, 200000):
    c = len(IZLEME) * istek
    gecen = [str(f) for f, e, _, _, _ in SONUC[1:] if c > e]
    print(f"{istek:>18}  {c:>17}  {SONUC[1][1]:>15}  {SONUC[-1][1]:>15}"
          f"  {(' , '.join(gecen) if gecen else 'hicbiri'):>23}")
print(f"\nkesisme noktasi (donem basina istek): siklik 6 -> {SONUC[1][1] // len(IZLEME)}"
      f" , siklik 1 -> {SONUC[-1][1] // len(IZLEME)}")

# Izlemenin kendi kalemi: uc gosterge her donem hesaplanir.
gosterge = len(IZLEME) * 200          # tahmin payi , cikarim tahminleri yeniden kullanilirsa 0
print(f"izleme kalemi (puan hesabi): en cok {gosterge} , cikarim tahminleri"
      f" yeniden kullanildiginda 0 ; bir yeniden egitim {SONUC[1][1]}")
```

```
siklik  toplamdan tahmin basina  cikarimdan tahmin basina  kazanilan isabet  kazanc basina maliyet
     -                     1.0                       1.0               0.0               tanimsiz
     6                   136.0                       1.0               0.0               tanimsiz
     3                   271.0                       1.0               0.0               tanimsiz
     2                   406.0                       1.0               0.0               tanimsiz
     1                   811.0                       1.0               0.0               tanimsiz

cikarimi tumuyle bedavaya indirmenin toplamdan sildigi pay: 0.0012
siklik 1'den siklik 6'ya inmenin toplamdan sildigi pay: 0.8323 , odenen isabet 0.0

donem basina istek  cikarim (6 donem)  egitim siklik 6  egitim siklik 1  cikarim hangisini gecti
               200               1200           162000           972000                  hicbiri
              2000              12000           162000           972000                  hicbiri
             20000             120000           162000           972000                  hicbiri
             50000             300000           162000           972000                        6
            200000            1200000           162000           972000            6 , 3 , 2 , 1

kesisme noktasi (donem basina istek): siklik 6 -> 27000 , siklik 1 -> 162000
izleme kalemi (puan hesabi): en cok 1200 , cikarim tahminleri yeniden kullanildiginda 0 ; bir yeniden egitim 162000
```

İlk iki sütun dersin sorusunu yanıtlar. Toplam tek bir sayı olarak okunup tahmin sayısına
bölündüğünde tahmin başına maliyet **811,00**'dir; aynı hesap yalnız çıkarım kaleminden
yapıldığında **1,00**. Aradaki 810 katın tamamı eğitimdir ve **hiçbir tahmine ait değildir**.
Bu tek sayıyı gören bir okur, tahminin pahalı olduğu sonucuna varır ve çıkarımı ucuzlatmaya
yönelir: toplu istek, önbellek, küçültme. **O yolun tavanı 0,0012'dir** — çıkarım tümüyle
bedava olsaydı bile toplamın yalnız binde 1,2'si silinirdi.

Görünmez kalan karar öbür yöndedir. Sıklığı her dönemden altı dönemde bire indirmek toplamın
**0,8323'ünü** siler ve karşılığında **0,0000 isabet** ödenir. Tek toplam bu kararı gizler,
çünkü toplamda eğitim ile çıkarımı birbirinden ayıran hiçbir işaret yoktur; eğitim payı
0,9926 ile 0,9988 arasındadır ve **hangi sıklıkta olunduğu toplamdan okunamaz**. İki kalem
ayrı sayıldığında karar tek satırdır. Kazanç başına maliyet sütunu aynı şeyi bir kez daha
söyler: beş kurulumun beşinde **tanımsız**, çünkü bölen sıfırdır. **Bir maliyet kaleminin
gerekçesi ancak bir kazançla birlikte yazılabilir; kazancı gösterilmeyen harcama ölçülmemiş
sayılır.**

Son süpürme kararın hacme bağlı olduğunu gösterir. Dönem başına 200 istekte çıkarım hiçbir
eğitim kalemini geçmez; 50.000 istekte yalnız en seyrek sıklığı geçer; 200.000 istekte
dördünü birden geçer. **Kesişme noktası** sıklık 6 için dönem başına **27.000**, sıklık 1
için **162.000** istektir. Bu, "eğitim her zaman baskındır" gibi bir sonucun yazılamayacağı
anlamına gelir: baskın kalem hacmin hangi yanında durulduğuna bağlıdır ve **tek toplam o
yanı da göstermez**.

İzlemenin kendi kalemi bu tablonun en küçük satırıdır. Üç göstergeyi altı dönem boyunca
hesaplamak en çok 1200 puan hesabıdır ve çıkarımda üretilmiş tahminler yeniden kullanıldığında
sıfıra iner. Ama izlemenin **tetiklediği** iş küçük değildir: ateşleyen tek bir uyarının
başlattığı yeniden eğitim 162.000'dir. **İzlemenin bedeli göstergenin kendisinde değil,
göstergeye verilen yanıttadır.**

#### Özet

- İş birimi bir **puan hesabıdır** ve sayaç `puan` işlevinin içindedir; eğitim ile çıkarım
  aynı birimle aynı tabloya girer. Bir dönemlik çıkarım 200, ilk eğitim 216.000, altı
  dönemlik pencereyle bir yeniden eğitim **162.000** puan hesabıdır.
- Sıklık süpürmesinde çıkarım kalemi **1200'de sabit kalır**, eğitim kalemi 162.000'den
  **972.000'e** çıkar ve eğitim payı 0,9926'dan 0,9988'e yükselir.
- `2026-12` isabeti beş kurulumun beşinde de **0,9800**'dür. Dönem adımı 0,0050 olduğuna göre
  kazanılan isabet gerçekten **0,0000**'dır ve kazanç başına maliyet **tanımsızdır**.
- Toplamdan okunan tahmin başına maliyet **811,00**, çıkarımdan okunan **1,00**'dir. Çıkarımı
  bedavaya indirmek toplamın 0,0012'sini, sıklığı seyreltmek **0,8323'ünü** siler; ikincisi
  0,0000 isabete mal olur ve **tek toplamda görünmez**.
- Kesişme noktası dönem başına **27.000** ile **162.000** istek arasındadır; hangi kalemin
  baskın olduğu hacme bağlıdır. İzlemenin kendi kalemi en çok 1200'dür, tetiklediği yeniden
  eğitim 162.000.

#### Sonraki Adım

İki kalem ayrıldı ve ikisinin de karşılığı yazıldı: yeniden eğitim 0,0000 isabet kazandırıyor,
çıkarım ise dönem başına 200 istek karşılığında modelin bütün değerini üretiyor. Bu tabloda
eksik olan tek sütun **sonun** sütunudur. Yeniden eğitim seyreltilebilir, çıkarım
ucuzlatılabilir, kayıt kısaltılabilir; ama bir modelin ne zaman **biteceği** hiçbir yerde
yazılı değildir. Sonraki ders emekliye ayırma kararının ölçüsünü kurar ve modeli kaldırmanın
geriye dönük ne bıraktığını sayar: kaldırıldığında kaç dönemin kararı **açıklanamaz** hâle
geliyor.

### Model Emekliye Ayırma

Source: https://academia.sh/tr/kurslar/model-izleme/model-emekliye-ayirma

Emekliye ayırma kararının hangi ölçüyle verildiğinin ve modeli kaldırmanın geriye dönük ne bıraktığının sayılması: gösterge ölçütü iki senaryonun ikisinde de 2026-10'da ateşler, birinde emeklilik haksızdır çünkü isabet 0,9700'den 0,9800'e gitmiştir; isabet ölçütü zararsız kaymada hiç ateşlemez ve zararlı kaymada 2026-09'daki bozulmayı 2026-11'de gösterir, iki dönem geç; aday karşılaştırma ölçütü veri kaymasında 0,0000, kavram kaymasında 0,0900 üstünlük ölçer ve iki senaryoyu ayıran tek ölçüttür. Kaldırma sonrası karar kaydı olmadan altı dönemin altısı açıklanamaz kalır; karar kaydı varken model sürüm kaydı silindiğinde 1200 tahmin okunur ama gerekçesi hesaplanan 0'dır. Ders kurs ve müfredat kapanışıdır.

Bir önceki dersin tablosunda **sonun** sütunu eksikti: bir modelin ne zaman biteceği hiçbir
yerde yazılı değildi. Bu ders iki soru sorar — hangi sayı kaldırmayı haklı çıkarır, kaldırma
**geriye dönük** ne bırakır.

- **MY53.** Kaynak, hat, model ve iki kayma **kurgudur**; hiçbir ürün, servis ya da kurum
  çağrılmaz. Tohum 20260218.
- **MY54.** Model `(0.6, 0.0, 4)`, karar eşiği 18, dengeli isabet 0,9864; izleme altı dönem,
  **taban sınıf oranı 0,5342**, adım 0,0050, etiket **iki dönem** gecikmeli.
- **MY55.** **Veri kaymasında** girdi kayar ve kural aynı kalır; **kavram kaymasında** girdi
  aynı kalır ve kural değişir (eşik 30'dan 27'ye).
- **MY56.** Üç ölçüt sınanır: **gösterge** (tahmin payı tabandan 0,05 sapar), **isabet eşiği**
  (dönem isabeti 0,95 altına iner), **aday karşılaştırma** (üstünlük sıfırdan büyük).
- **MY57.** Kaldırma, ağırlık ve eşik kaydının **çözülemez** olmasıdır; gerekçesi
  hesaplanamayan dönem **açıklanamaz** sayılır. Emeklilik `2026-11`, itiraz ve saklama üçer
  dönem.

#### Emekliye Ayırma Kararının Ölçüsü

İlk blok modeli ve iki kaymayı kurar, üç ölçütü ateşler.

```python
# KURGUDUR. Kaynak , hattin tuketim asamasi , oznitelik yolu , esik modeli ,
# iki kayma ve emeklilik olcutleri ders icinde uretilir; hicbir urun , servis
# ya da kurum cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


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


BOLGE, TIP = ["dogu", "bati", "kuzey", "guney", "merkez"], ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e})
    t = {(k["abone"], k["donem"]): k for k in ham}
    return [dict(k, m3=k["endeks"] - t[(k["abone"], DONEM[DONEM.index(k["donem"]) - 1])]
                 ["endeks"]) for k in ham if DONEM.index(k["donem"])]


def oznitelikler(kayitlar):
    """Agirliktan bagimsizdir , bir kez hesaplanir."""
    ab, bo = {}, {}
    for k in sorted(kayitlar, key=lambda k: DONEM.index(k["donem"])):
        ab.setdefault(k["abone"], []).append((DONEM.index(k["donem"]), k["m3"]))
        bo.setdefault(k["bolge"], []).append((DONEM.index(k["donem"]), k["m3"]))

    def ort(dizi, i):
        s = [v for d, v in dizi if d < i]
        return round(sum(s) / len(s), 4) if s else 0.0

    return {(k["abone"], k["donem"]): {
        "abone_ort": ort(ab[k["abone"]], DONEM.index(k["donem"])),
        "bolge_ort": ort(bo[k["bolge"]], DONEM.index(k["donem"])),
        "isyeri": 1 if k["tip"] == "isyeri" else 0} for k in kayitlar}


IZGARA = [(a, b, c) for a in (0.6, 0.8, 1.0) for b in (0.0, 0.2, 0.4) for c in (0, 4, 8)]
KARAR_IZGARA = (18, 22, 26, 30, 34)
IZLEME, GECIKME = DONEM[6:], 2
KURAL_ESIK, KURAL_ISYERI, ISABET_ESIGI = 30, 6, 0.95


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


def tahmin(o, w, karar):
    return 1 if puan(o, w) > karar else 0


def etiket(o, esik=KURAL_ESIK):
    return 1 if o["abone_ort"] + KURAL_ISYERI * o["isyeri"] > esik else 0


def egit(kayitlar, oz, donemler, etiketler=None):
    """Olcut dengeli isabet; karar esigi AYRI bir ayardir ve aranir."""
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en = (-1.0, None, None)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            dp = dn = yp = yn = 0
            for k in kume:
                c = (k["abone"], k["donem"])
                t = tahmin(oz[c], w, karar)
                g = etiketler[c] if etiketler else etiket(oz[c])
                dp, yp, yn, dn = (dp + (t and g), yp + (t and not g),
                                  yn + (not t and g), dn + (not t and not g))
            d = (dp / (dp + yn) if dp + yn else 0.0) + (dn / (dn + yp) if dn + yp else 0.0)
            if d / 2 > en[0]:
                en = (d / 2, w, karar)
    return {"agirlik": en[1], "karar_esigi": en[2], "dengeli": round(en[0], 4)}


def olc(kayitlar, m, oz, donem, etiketler=None):
    kume = [(k["abone"], k["donem"]) for k in kayitlar if k["donem"] == donem]
    t = [tahmin(oz[c], m["agirlik"], m["karar_esigi"]) for c in kume]
    g = [etiketler[c] if etiketler else etiket(oz[c]) for c in kume]
    return (round(sum(t) / len(t), 4),
            round(sum(1 for a, b in zip(t, g) if a == b) / len(t), 4))


TEMIZ = satirlar()
OZ = oznitelikler(TEMIZ)
M = egit(TEMIZ, OZ, DONEM[1:9])
# KURGU veri kaymasi: girdi kayiyor , kural ayni.
b = DONEM.index("2026-09")
KAYAN = [dict(k, m3=round(k["m3"] * (1 + 0.06 * (DONEM.index(k["donem"]) - b + 1)), 4))
         if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k) for k in TEMIZ]
OZ_K = oznitelikler(KAYAN)
# KURGU kavram kaymasi: girdi AYNI , kural degisiyor (esik 30'dan 27'ye iniyor).
KAVRAM = {(k["abone"], k["donem"]): etiket(
    OZ[(k["abone"], k["donem"])], 27 if DONEM.index(k["donem"]) >= b else KURAL_ESIK)
    for k in TEMIZ}

p1 = sum(etiket(OZ[(k["abone"], k["donem"])]) for k in TEMIZ if k["donem"] in IZLEME) / 1200
print(f"model {M['agirlik']} karar esigi {M['karar_esigi']} dengeli {M['dengeli']} ,"
      f" taban sinif orani {round(max(p1, 1 - p1), 4)} , adim {round(1 / 200, 4)}")
print("\ndonem     veri: pay  isabet | kavram: pay  isabet")
SENARYO = {"veri": (KAYAN, OZ_K, None), "kavram": (TEMIZ, OZ, KAVRAM)}
OLCUM = {ad: [(d,) + olc(k, M, o, d, e) for d in IZLEME] for ad, (k, o, e) in SENARYO.items()}
for i, d in enumerate(IZLEME):
    print(f"{d}   {OLCUM['veri'][i][1]:>9}  {OLCUM['veri'][i][2]:>6} |"
          f" {OLCUM['kavram'][i][1]:>11}  {OLCUM['kavram'][i][2]:>6}")

print(f"\n{'olcut':<37}{'senaryo':>7}  {'atesleyen':>9}  {'bozulma':>7}"
      f"  {'gecikme':>7}  {'hakli':>5}")
for ad, (kay, oz, et) in SENARYO.items():
    taban = OLCUM[ad][1][1]
    gost = [d for d, p, _ in OLCUM[ad] if abs(p - taban) / taban > 0.05]
    dus = [d for d, _, i in OLCUM[ad] if i < ISABET_ESIGI]
    gor = DONEM[DONEM.index(dus[0]) + GECIKME] if dus else "-"
    aday = egit(kay, oz, IZLEME, et)
    ust = round(olc(kay, aday, oz, "2026-12", et)[1] - OLCUM[ad][-1][2], 4)
    for olcut, ates, gec in (("gosterge (pay 0,05)", gost[0] if gost else "-", "-"),
                             (f"isabet esigi {ISABET_ESIGI}", gor, GECIKME if dus else "-"),
                             (f"aday karsilastirma (ustunluk {ust})",
                              gor if ust > 0 else "-", GECIKME if ust > 0 else "-")):
        h = "hayir" if (ates != "-" and not dus) else ("evet" if ates != "-" else "-")
        print(f"{olcut:<37}{ad:>7}  {str(ates):>9}  {str(dus[0]) if dus else '-':>7}"
              f"  {str(gec):>7}  {h:>5}")
```

```
model (0.6, 0.0, 4) karar esigi 18 dengeli 0.9864 , taban sinif orani 0.5342 , adim 0.005

donem     veri: pay  isabet | kavram: pay  isabet
2026-07       0.455    0.97 |       0.455    0.97
2026-08        0.47    0.97 |        0.47    0.97
2026-09       0.455    0.99 |       0.455   0.815
2026-10       0.515   0.975 |        0.51    0.87
2026-11        0.52    0.98 |        0.51   0.865
2026-12       0.545    0.98 |       0.515    0.85

olcut                                senaryo  atesleyen  bozulma  gecikme  hakli
gosterge (pay 0,05)                     veri    2026-10        -        -  hayir
isabet esigi 0.95                       veri          -        -        -      -
aday karsilastirma (ustunluk 0.0)       veri          -        -        -      -
gosterge (pay 0,05)                   kavram    2026-10  2026-09        -   evet
isabet esigi 0.95                     kavram    2026-11  2026-09        2   evet
aday karsilastirma (ustunluk 0.09)    kavram    2026-11  2026-09        2   evet
```

Gösterge ölçütü **iki senaryoda da `2026-10`'da** ateşler: birinde isabet 0,9700'den 0,9800'e
gitmiştir ve emeklilik **haksızdır**, öbüründe bozulma gerçektir. Ateşleyen dönem aynı,
doğru karar zıttır. **Ateşlemesi ile bozulması ayrı ayrı gösterilmeyen bir gösterge bir modelin
kaldırılmasına gerekçe olamaz.**

İsabet ölçütü ikisinde de doğrudur: bozulma `2026-09`'da olur, ölçüt `2026-11`'de tetiklenir —
aradaki **iki dönem** gecikmeli etiketin kendisidir. Aday karşılaştırma üstünlüğü **0,0000** ile
**0,0900** ölçer, çözünürlüğün on sekiz adımı. **Ayıran iki ölçüt de etiket okur** ve geç gelir;
gecikmesiz olan tek ölçüt ayırt edemeyendir.

#### Kaldırmanın Geriye Dönük Bıraktığı

İkinci blok modeli kaldırır, kalanı sayar.

```python
# Bu blok ilk bloktaki DONEM , IZLEME , OZ , M , TEMIZ , puan ve tahmin
# tanimlarini surdurur.
EMEKLILIK, SAKLAMA, ITIRAZ = "2026-11", 3, 3
defter = [dict(OZ[(k["abone"], k["donem"])], donem=k["donem"], model_surum="s1",
               tahmin=tahmin(OZ[(k["abone"], k["donem"])], M["agirlik"], M["karar_esigi"]))
          for k in TEMIZ if k["donem"] in IZLEME]
TAM = {"s1": {"agirlik": M["agirlik"], "karar_esigi": M["karar_esigi"]}}
kalan = [d for d in IZLEME if DONEM.index(d) > len(DONEM) - 1 - SAKLAMA]


def gerekce(r, surum):
    """Gerekce ancak model surumu cozulebiliyorsa hesaplanir."""
    s = surum.get(r["model_surum"])
    return None if s is None else round(puan(r, s["agirlik"]) - s["karar_esigi"], 4)


print("kurulum                          okunan tahmin  gerekcesi hesaplanan"
      "  aciklanamaz donem")
for ad, kyt, sur in (("kayit yok , surum silindi", [], {}),
                     ("karar kaydi var , surum silindi", defter, {}),
                     ("karar kaydi + surum kaydi", defter, TAM),
                     (f"ayni , saklama {SAKLAMA} donem",
                      [r for r in defter if r["donem"] in kalan], TAM)):
    g = [r for r in kyt if gerekce(r, sur) is not None]
    print(f"{ad:<33}{len(kyt):>14}  {len(g):>20}"
          f"  {len(IZLEME) - len(set(r['donem'] for r in g)):>17}")

e = DONEM.index(EMEKLILIK)
sun = [d for d in IZLEME if DONEM.index(d) <= e]
acik = [d for d in sun if DONEM.index(d) + ITIRAZ >= e]
gec = [d for d in sun if DONEM.index(d) + GECIKME > e]
print(f"\nemeklilik {EMEKLILIK}: sunulan donem {len(sun)} , karar {len(sun) * 200} ;"
      f" itirazi hala acik {len(acik)} donem / {len(acik) * 200} karar ;"
      f" etiketi hic gelmeyen {len(gec)} donem / {len(gec) * 200} karar")
```

```
kurulum                          okunan tahmin  gerekcesi hesaplanan  aciklanamaz donem
kayit yok , surum silindi                     0                     0                  6
karar kaydi var , surum silindi            1200                     0                  6
karar kaydi + surum kaydi                  1200                  1200                  0
ayni , saklama 3 donem                      600                   600                  3

emeklilik 2026-11: sunulan donem 5 , karar 1000 ; itirazi hala acik 4 donem / 800 karar ; etiketi hic gelmeyen 2 donem / 400 karar
```

İkinci satır dersin ikinci bulgusudur: karar defteri eksiksiz durur, 1200 tahminin 1200'ü
okunur, ama gerekçesi hesaplanan **sıfırdır** ve altı dönem de **açıklanamaz** kalır. Eksik
olan iki sayıdır: ağırlık üçlüsü ile karar eşiği. **Tahmini okumak açıklamak değildir.** Sürüm
kaydı da saklandığında sayı 1200'e döner, üç dönemlik saklamada **üçe** çıkar. Veri
Mühendisliğine Giriş kursu bunu veri tarafında ölçmüştü: saklama politikası geriye dönük
denetlenemez dönem bırakır.

Son satır emekliliğin **zamanlamasına** ait bedeldir: `2026-11`'de kaldırılan modelin 1000
kararından 800'ü hâlâ itiraza açıktır ve **iki dönemin 400 kararının etiketi hiç
gelmeyecektir**, çünkü kaldırma ölçümü durdurur. **Emekliye ayırma son iki
dönemi kalıcı olarak ölçülmemiş bırakır.**

#### Özet

- Gösterge ölçütü iki senaryoda da `2026-10`'da ateşler ve birinde emeklilik **haksızdır**;
  isabet ölçütü `2026-09` bozulmasını `2026-11`'de gösterir ve aday karşılaştırma üstünlüğü
  **0,0000** ile **0,0900** ölçer.
- Sürüm kaydı silindiğinde 1200 tahmin okunur, gerekçesi hesaplanan **0**'dır ve altı dönem
  açıklanamaz kalır.
- `2026-11`'de kaldırılan modelin 1000 kararından 800'ü itiraza açık, **400'ünün etiketi hiç
  gelmeyecektir**.

#### Kurs Kapanışı

Ölçüt ders adının yanındadır; parantezdeki fark **izlemeli kurulum eksi tabandır**.

| Ders | İzlemesiz taban | İzlemeli kurulum | Bedel |
|---|---|---|---|
| `model-izlemesi/01` — dengeli isabet | eski kurulum: duyarlılık **0,0000**, dengeli **0,5000** | düzeltilmiş: duyarlılık 1,0000, dengeli **0,9835** (**+0,4835**) | pay 0,05'te üç dönem ateşler, üçünde de isabet 0,9700'lük tabanın **altına inmez** |
| `model-izlemesi/02` — `2026-09` isabeti | kayma görülmez: **0,9700** | kavram kaymasında **0,8150** (**−0,1550**) | otuz bir adım; girdi özeti hiçbir bantta ateşlemez, 1200 satırın 132'sinin etiketi değişir |
| `model-izlemesi/03` — ölçülen dönem | etiket beklenmez: **0** | gecikme 2: bozulma `2026-09`, uyarı `2026-11` | boşlukta **400 tahmin** ölçülmeden üretilir, **63'ü** yanlış çıkar; her dönem 400 ölçülmemiş kalır |
| `model-izlemesi/04` — vekilin sapması | vekil yok: **0** | üç vekil: **0,0185 / 0,0957 / 0,0000** | üçü de kavram kaymasında sağlam senaryoyla aynı; ayıran tek ölçü isabet (**0,1598**) |
| `model-izlemesi/05` — ateşleyen dönem | bant yok: **0** | 0,05 bandında **13** (**+13**) | **6 yanlış alarm**; geçerli yakalama **4**, hepsi isabetten, yakalama gecikmesi 2 dönem |
| `model-izlemesi/06` — ölçülen isabet | etiket gelmez: ölçüm **yok** | `yalniz pozitif`, kapsam 0,4858: **1,0000** | sapma **+0,0894**; ölçülen isabet 0,9362'den **1,0000'e çıkar**, gerçeği 0,8500'e inerken |
| `model-yonetisimi/01` — adayın ölçülen isabeti | yayın yok: ölçüm **yok** | gölge, 0 istek: **0,9650**; kanarya 0,10: **1,0000** | kanaryada çözünürlük **0,0500**'e çıkar; tam geçişte **7** istek yanlışlanır |
| `model-yonetisimi/02` — ölçülen fark | deney yok: **0** | altı dönem **−0,0050**, tek dönem **+0,0250** ile **−0,0150** | ayırt edilebilir en küçük fark **0,0275**; gerçek örneklem **60** satır |
| `model-yonetisimi/03` — `2026-12` isabeti | yeniden eğitim yok: **0,9800** | üç pencerenin üçü de **0,9800** (**+0,0000**) | **3.078.000** satır puanlanır; gösterge tetikleyicisi iki senaryoda da aynı dönemlerde ateşler |
| `model-yonetisimi/04` — tahmin başına okunan satır | kayıtsız: **423,70** | karar kaydıyla **1,00** (**−422,70**) | 12.000 saklanan alan; kayıtsız aramada **189 tahminin** modeli belirlenemez |
| `model-yonetisimi/05` — yeniden hesaplanan karar | taban kayıt: **1164/1200** | kayıt tutmayla **1200/1200** (**+36**) | 3600'den **8402** saklanan değer; saklamada **600** karar itiraz açıkken silinir |
| `model-yonetisimi/06` — tahmin başına maliyet | yeniden eğitim yok: **1,00** | sıklık 1: **811,00** (**+810,00**) | eğitim payı **0,9988**, kazanılan isabet **0,0000**, kazanç başına maliyet tanımsız |
| `model-yonetisimi/07` — açıklanamaz dönem | kayıtsız kaldırma: **6/6** | karar ile sürüm kaydı: **0/6** (**−6**) | emeklilikte **400** kararın etiketi hiç gelmez, **800**'ü itiraza açık kalır |

Tablo tek bir kuralın on üç kez yazılmasıdır. **Bir göstergenin ateşlemesi bozulmanın kanıtı
değildir; ateşlemesi ile bozulması ayrı ayrı gösterilmeyen gösterge ölçülmemiş sayılır.**
`model-izlemesi/05` bunu sayar: 0,05 bandındaki 13 ateşlemenin 6'sı yanlış alarmdır, geçerli
yakalama 4'tür ve `/01`'de uyarı üç dönem ateşlerken isabet yükselir.

**İkinci iddia aynı tablodan okunur: izleme de bozar.** `model-yonetisimi/01`'de kanarya payı
ölçüyü böler: 0,10'da kötü aday 1,0000 görünür, tam geçişte 7 istek yanlışlanır. `/03`'te üç
tetikleyici 3.078.000 satır puanlatır ve kazanç 0,0000'dır; `/06`'da seyreklik kararı toplamın
0,8323'ünü yine 0,0000 karşılığında taşır. `model-izlemesi/06`'da toplama yordamı ölçülen
isabeti gerçeğin **üstüne** çıkarır.

**Üçüncü iddia vekil ölçütlerin adıyla yazılır: zararsız kaymada ateşliyorlar, zararlı kaymada
susuyorlar.** `model-izlemesi/04`'te üç vekil de kavram kaymasında sağlam senaryoyla hane hane
aynı sayıyı verir; `/02`'de tahmin payı 0,10 bandında zararsız kaymada ateşleyip zararlı
kaymada susar. Bu dersin ilk tablosu bunun emeklilik hâlidir.

Altı kurs aynı ekseni altı yerde okudu: **bozulma** hattın aşamalarında sayıldı, **düzen**
depolamada, **koşum** orkestrasyonda, **bölüşüm** dağıtık işlemede, **yol** modelin yeniden
üretilmesinde ve **görülme** üretimdeki modelde. Müfredatın kuralı son kez yazılır: bir veri
hattının sayısı **taşıdığı satır değil, yanlış satırın kaç adım sonra yakalandığıdır; bozulmayı
yakalayan denetimi yazılmayan hat ölçülmemiş sayılır.**

Yetmiş dört derste veri taşındı, saklandı, dönüştürüldü, bölüştürüldü, modele verildi ve
izlendi. Bütün bunlar **sunucu tarafında** duran bir sistemdi. Bu sistemin ürettiği sayının
**kullanıcının cebindeki bir aygıtta** nasıl görüneceği, o aygıtın kendi kısıtlarıyla ne
yapabileceği ve **bağlantısız kaldığında** ne olacağı hiç sorulmadı. Sonraki müfredat sayıyı üreten yerden aldığı yere taşır.

