---
title: 'MLOps Temelleri'
source: 'https://academia.sh/tr/kurslar/mlops-temelleri'
language: tr
updated: '2026-08-17T04:40:16+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Makine öğrenmesi yaşam döngüsünü sürümlenebilir bileşenlere ayırır ve hangi kalemleri sabitlemenin koşumu yeniden üretilebilir kıldığını sayar'
    - 'Eğitim ve sunum yollarının aynı özniteliği üretip üretmediğini ölçer ve sapmanın modelden değil yoldan geldiğini gösterir'
    - 'Eğitim ve dağıtım hatlarını kurar, sunum kalıplarını gecikme sınıfı ve tahmin başına maliyetle karşılaştırır'
---

# MLOps Temelleri

Aynı veriyle aynı modelin yeniden üretilip üretilemediğinin ve eğitimdeki yolla sunumdaki yolun aynı olup olmadığının sayılması: yazılım mühendisliği pratiğinin modele uygulanmasıyla sürümsüz tabanın ne verdiği, deney takibinde parametre, ölçüt ve çıktı kaydının hangi koşumu yeniden bulunabilir kıldığı, veri ve model sürümlemesinin izlenebilir kıldığı değişim, öznitelik deposunun eğitim ile çıkarım arasında tuttuğu tutarlılık ve aynı ağırlıklarla isabetin 0,8067'den 0,7800'e düşmesinin modelden değil öznitelik yolundan gelmesi, ortam sabitlemede beş kalemden üçünün üç koşumu birebir aynı yapması ve ikisinin katkısının ölçülememesi, model kayıt defterinin yaşam döngüsü aşamaları, veri hazırlama ile eğitim ve değerlendirme adımlarının hatta bağlanması, kod, veri ve model kapılarının sürekli tümleştirmede ayrı ayrı ateşlemesi, çalışma zamanı ve arayüz sözleşmesiyle paketleme, toplu, çevrimiçi ve akış tabanlı çıkarımın gecikme sınıfı ile bedeli, toplu isteme ve önbelleklemenin tahmin başına maliyete etkisi, kenarda çalıştırmak için küçültmenin isabetten götürdüğü.

## Yeniden Üretilebilirlik

Aynı veriyle aynı modelin yeniden üretilip üretilemediğinin kalem kalem sayılması: sürümsüz ve sabitlemesiz tabanda üç koşumun üç ayrı sınama isabeti vermesi, deney takibinde parametre, ölçüt ve çıktı kaydının hangi koşumu yeniden bulunabilir kıldığı ve kaydın kendi bedeli, veri ile model sürümlemesinin aynı ada verilen iki farklı içeriği ayırması, öznitelik deposunun eğitim ile çıkarım arasında tuttuğu tutarlılık ve aynı ağırlıklarla isabetin eğitim yolunda 0,8067 sunum yolunda 0,7800 çıkması, sapmanın öznitelik penceresi ikiye çıkarıldığında tümüyle kapanması, ortam sabitlemede beş kalemden üçünün üç koşumu birebir aynı yapması ile ızgara ve yuvarlama kalemlerinin katkısının ölçülememesi, model kayıt defterinde aşama geçişlerinin hangi koşuma bağlandığı.

### MLOps Nedir

Source: https://academia.sh/tr/kurslar/mlops-temelleri/mlops-nedir

Yazılım mühendisliği pratiğinin model üreten bir hatta uygulanmasının kurulması ve sabitlemesiz tabanın ölçülmesi: hattan çıkan 2200 satır 40 m3 eşiğiyle etiketlendiğinde dağılım 1752 ile 448 oluyor, taban sınıf oranı 0,7964 çıkıyor, 27 adaylık ızgarada aranan model eğitimde 0,7931 sınamada 0,8067 veriyor, ama sınama kümesinin kendi taban oranı da 0,8067 olduğu için model 600 sınama satırının hiçbirine yüksek demiyor ve duyarlılığı 0,0000 kalıyor; hiçbir kalem sabitlenmediğinde aynı iş üç kez koşturulduğunda ağırlık üç koşumda da aynı kalırken sınama isabeti 0,8067, 0,7955 ve 0,8078 çıkıyor ve üç sayının üçü de kendi sınama kümesinin taban oranına eşit olduğu için fark modelden değil kümenin değişmesinden geliyor.

Kırk dokuz derste veri taşındı, saklandı, dönüştürüldü ve bölüştürüldü. Hattın her aşaması kaç
satır düşürdüğü sayıldı, bozulmanın kaç aşama sonra yakalandığı ölçüldü, satırların hangi düzende
durduğu ve dört işçiye bölündüğünde ne kazanıldığı yazıldı. Bütün bu işin bir amacı vardı ve o amaç
bir kez bile açılmadı: bu verinin beslediği **model**.

Bu kurs iki soruyu sorar: aynı veriyle **aynı modelin yeniden üretilip üretilemeyeceği** ve
eğitilen bir modelin **üretime nasıl çıkacağı**. İlk konu birincisini ele alır. Bu ders kursun ölçü
düzenini, etiketi ve kurgu modeli kurar, sonra tek bir şeyi ölçer: hiçbir kalem sabitlenmemişken
aynı iş üç kez koşturulduğunda kaç ayrı sayı çıkıyor.

- **YU1.** Kaynak, hat ve model **kurgudur**. Gerçek bir öğrenme kitaplığı, deney takip düzeneği,
  öznitelik deposu, model kayıt defteri ya da sunum çatısı çağrılmaz ve adı yazılmaz. Tohum
  **20260218**.
- **YU2.** Kursun kuralı: bir modelin sayısı **onu üreten yolla birlikte** anlamlıdır. **Yeniden
  üretilemeyen sayı bir ölçüm değil bir anıdır.**
- **YU3.** Taban çizgisi her derste **sürümsüz, sabitlemesiz ve tek yollu kurulumdur**.
- **YU4.** Etiket: bir dönemin tüketimi **40 m3**'ü aşıyorsa "yüksek" (1), aşmıyorsa 0. Eşik bir
  **seçimdir** ve ona duyarlı her sonuç eşikle birlikte yazılır.
- **YU5.** Etiket dağılımı **dengesizdir**: `{0: 1752, 1: 448}`, yani **taban sınıf oranı
  0,7964**. Bir isabet sayısı bunun ne kadar üstünde olduğu yazılmadan **ölçülmüş sayılmaz**.
  Taban çizgisi yordamı Denetimli Öğrenme kursunda ölçüldü; burada **devralınır**, tekrarlanmaz.
- **YU6.** Kurgu model **üç ağırlıklı bir eşiktir**; eğitim, **27 adaylık bir ızgarada** aramaktır.
- **YU7.** Eğitim dönemi sekiz (`2026-02` ile `2026-09`, **1600 satır**), sınama dönemi üç
  (`2026-10` ile `2026-12`, **600 satır**).
- **YU8.** Çözünürlük: sınama kümesi 600 satır olduğu için isabetin en küçük adımı **0,0017**'dir.
- **YU9.** **Eğitim hattı bu kursta tek makinede ele alınır.** Bölüşüm, karıştırma ve çarpıklık
  Dağıtık Veri İşleme kursunda ölçüldü ve burada tekrarlanmaz.
- **YU10.** Sayılan birimler isabet, aday, eğitim satırı, sabitlenen kalem ve aynı çıkan koşumdur;
  **süre ham milisaniye olarak yazılmaz**.
- **YU11.** Beş sabitleme kalemi: `veri_surumu`, `oznitelik_kodu`, `bolunme`, `izgara`,
  `yuvarlama`. MLOps bir **pratik adıdır**, bir ürün adı değildir.

#### Yazılım Pratiğinin Modele Uygulanması

Bir programın çıktısını kaynağı belirler ve yazılım pratiğinin bütün araçları bu varsayımın üzerine
kuruldu: sürüm denetimi kaynağı saklar, gerileme testi davranışı sabitler, sürekli tümleştirme her
değişikliği aynı yoldan geçirir. Bu yordamlar DevOps ve Sürekli Tümleştirme kursunda ve Veri
Hatları ve Orkestrasyon kursunda ölçüldü; tekrarlanmaz.

Bir modelde varsayım düşer. Bir modelin verdiği sayıyı belirleyen şey yalnız kaynak kodu değildir.
En az beş kalem vardır: modelin gördüğü **veri**, o verinin eğitimle sınamaya **nasıl bölündüğü**,
özniteliğin **hangi kodla** hesaplandığı, arama uzayının **hangi adaylardan** oluştuğu ve sayıların
**kaç basamağa yuvarlandığı**. Bu beş kalemden yalnız biri kaynak dosyasının içinde durur; kalan
dördü ya bir dizinde, ya bir komut satırı seçeneğinde, ya da hiçbir yerde. MLOps bu kalemleri
kaynak kodunun zaten sahip olduğu disipline bağlama pratiğinin adıdır: yeni bir matematik değil,
**kayıt tutma** getirir.

#### Kurgu Model ve Etiket

Aşağıdaki blok Veri Mühendisliğine Giriş kursundaki kurgu ölçüm ağı kaynağını ve hattın ilk üç
aşamasını aynen yeniden kurar, üzerine bu kursun etiketini, öznitelik üretimini ve kurgu modelini
ekler.

```python
# KURGUDUR. Sayac okumalari, hat ve model ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, deney takip duzenegi ya da model kayit defteri 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):
    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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]        # 2026-02 .. 2026-09
SINAMA = DONEM[9:]         # 2026-10 .. 2026-12
ESIK = 40                  # bu m3 uzerindeki tuketim "yuksek" sayilir


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


# ---- Oznitelik uretimi. Agirliktan bagimsizdir , bir kez hesaplanir.
def oznitelikler(kayitlar, pencere=None, basamak=4):
    """pencere=None -> tam gecmis (egitim yolu)."""
    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


# ---- KURGU model: uc agirlikli esik. Egitim = agirliklari izgarada aramak.
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"]


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)


# ---- Yeniden uretilebilirlik: sabitlenen kalemler
KALEM = ("veri_surumu", "oznitelik_kodu", "bolunme", "izgara", "yuvarlama")


def kosum(kayitlar, sabit, tur=0):
    """Sabitlenmeyen her kalem tur'a gore degisir."""
    kay = kayitlar if "veri_surumu" in sabit else kayitlar[: len(kayitlar) - 20 * tur]
    pencere = None if "oznitelik_kodu" in sabit else (None if tur == 0 else 12 - tur)
    basamak = 4 if "yuvarlama" in sabit else max(4 - tur, 0)
    izgara = IZGARA if "izgara" in sabit else IZGARA[: len(IZGARA) - 6 * tur]
    donemler = SINAMA if "bolunme" in sabit else DONEM[9 + (tur % 2):]
    oz = oznitelikler(kay, pencere, basamak)
    m = egit(kay, oz, izgara)
    return {"agirlik": m["agirlik"], "egitim": m["egitim_isabeti"],
            "sinama": sina(kay, m, oz, donemler=donemler)}
```

Model bir abonenin geçmiş tüketim ortalamasına, bölgesinin ortalamasına ve işyeri olup olmadığına
bakar; üç ağırlıkla bir puan üretir ve puan eşiği aşarsa "yüksek" der. Eğitim, 27 adayı deneyip
eğitim kümesinde en çok tutturanı seçmektir. Öznitelikler ağırlıktan bağımsız olduğu için
`oznitelikler` bunları **bir kez** hesaplar.

```python
S = satirlar()
dagilim = {0: 0, 1: 0}
for k in S:
    dagilim[etiket(k)] += 1
print("hattan cikan satir:", len(S), " esik:", ESIK)
print("etiket dagilimi:", dagilim)
print("taban sinif orani:", round(dagilim[0] / len(S), 4))
print()
oz = oznitelikler(S)
model = egit(S, oz)
print("aday:", model["aday"], " egitim satiri:", model["egitim_satiri"])
print("agirlik:", model["agirlik"])
print("egitim isabeti:", model["egitim_isabeti"])
print("sinama isabeti:", sina(S, model, oz))
```

```
hattan cikan satir: 2200  esik: 40
etiket dagilimi: {0: 1752, 1: 448}
taban sinif orani: 0.7964

aday: 27  egitim satiri: 1600
agirlik: (0.6, 0.4, 0)
egitim isabeti: 0.7931
sinama isabeti: 0.8067
```

İki sayı burada bir arada durur ve ikisi de kursun geri kalanında geçer. Birincisi **etiket
dağılımıdır**: 2200 satırın 1752'si "yüksek değil", 448'i "yüksek", yani dağılım dengesizdir.
İkincisi **taban sınıf oranıdır**: hiçbir şey öğrenmeden her satıra "yüksek değil" diyen bir kural
**0,7964** isabet alır. Model 0,8067 aldı; kâğıt üzerinde tabandan 0,0103 yukarıda görünür.

#### Taban Sınıf Oranı Yazılmadan Sayı Okunmaz

O karşılaştırma yanlıştır, çünkü 0,7964 **bütün** verinin taban oranıdır, model ise yalnız sınama
kümesinde ölçüldü. Doğrusu sınama kümesinin kendi taban oranıyla yapılır.

```python
sk = [k for k in S if k["donem"] in SINAMA]
yuksek = sum(1 for k in sk if etiket(k) == 1)
tahmin = {0: 0, 1: 0}
tutan = 0
for k in sk:
    t = 1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > ESIK else 0
    tahmin[t] += 1
    if t == 1 and etiket(k) == 1:
        tutan += 1
print("sinama satiri:", len(sk), " yuksek etiketli:", yuksek)
print("sinama taban sinif orani:", round((len(sk) - yuksek) / len(sk), 4))
print("modelin sinama isabeti :", sina(S, model, oz))
print("modelin tahmin dagilimi:", tahmin)
print(f"duyarlilik: {tutan / yuksek:.4f}")
print(f"tabanin ustunde: {sina(S, model, oz) - (len(sk) - yuksek) / len(sk):.4f}")
```

```
sinama satiri: 600  yuksek etiketli: 116
sinama taban sinif orani: 0.8067
modelin sinama isabeti : 0.8067
modelin tahmin dagilimi: {0: 600, 1: 0}
duyarlilik: 0.0000
tabanin ustunde: 0.0000
```

Sınama kümesinin taban oranı **0,8067**'dir ve modelin isabeti de **0,8067**. Fark **0,0000**.
Sebebi son iki satırda görünür: model 600 sınama satırının **hiçbirine** "yüksek" demiyor, yüksek
etiketli 116 satırın hiçbirini yakalamıyor ve **duyarlılığı 0,0000** kalıyor.

Bu sonuç bilerek yazılmıştır. Kurgu model taban çizgisinin **üzerine çıkmaz** ve bu kursun
ölçtüğü şeyi karışıklıktan kurtarır: buradan sonra isabetin her oynayışı **modelin başarımından
değil, sayıyı üreten yoldan** gelir. Model kurmanın, eşik seçmenin ve dengesizlikle başa çıkmanın
yordamı Makine Öğrenmesi müfredatında ölçüldü ve tekrarlanmaz. Kural şudur: **0,8067 tek başına
yazıldığında bir başarım iddiasıdır ve yanlıştır**; taban oranıyla yan yana yazıldığında modelin
hiçbir şey ölçmediğini söyler.

#### Aynı İş, Üç Koşum, Üç Sayı

Şimdi asıl soru sorulabilir. Yukarıdaki koşum bir kez yapıldı ve bir sayı verdi; aynı iş bir hafta
sonra tekrar yapılırsa aynı sayı çıkar mı. Hiçbir kalem sabitlenmemiştir: depodan o gün ne varsa
okunur, öznitelik kodu değişmiş, ızgara kısalmış, bölünme kaymış olabilir. `kosum` bu kurulumu üç
tur için canlandırır.

```python
print("sabitlenen kalem: 0 -- besinin hicbiri sabit degil")
print(f"{'tur':<5}{'agirlik':>16}{'egitim':>9}{'sinama':>9}")
for tur in range(3):
    r = kosum(S, (), tur)
    print(f"{tur:<5}{str(r['agirlik']):>16}{r['egitim']:>9.4f}{r['sinama']:>9.4f}")
print()
print(f"{'tur':<5}{'satir':>7}{'aday':>6}{'sinama donemi':>15}{'sinama satiri':>15}{'taban orani':>13}")
for tur in range(3):
    kay = S[: len(S) - 20 * tur]
    donemler = DONEM[9 + (tur % 2):]
    sk3 = [k for k in kay if k["donem"] in donemler]
    y3 = sum(1 for k in sk3 if etiket(k) == 1)
    print(f"{tur:<5}{len(kay):>7}{len(IZGARA) - 6 * tur:>6}{len(donemler):>15}"
          f"{len(sk3):>15}{(len(sk3) - y3) / len(sk3):>13.4f}")
```

```
sabitlenen kalem: 0 -- besinin hicbiri sabit degil
tur           agirlik   egitim   sinama
0       (0.6, 0.4, 0)   0.7931   0.8067
1       (0.6, 0.4, 0)   0.7932   0.7955
2       (0.6, 0.4, 0)   0.7926   0.8078

tur    satir  aday  sinama donemi  sinama satiri  taban orani
0       2200    27              3            600       0.8067
1       2180    21              2            396       0.7955
2       2160    15              3            588       0.8078
```

Üst tablo kursun taban çizgisidir: **0,8067**, **0,7955**, **0,8078**. En büyüğüyle en küçüğü
arasında **0,0123** fark var; çözünürlük 0,0017 olduğuna göre bu yedi adımdan büyüktür, yani
gürültü değildir. Üç koşumun **ağırlığı ise birebir aynıdır**: `(0.6, 0.4, 0)`.

Alt tablo farkın nereden geldiğini söyler. Her turda okunan satır düştü, aday sayısı 27'den 21'e ve
15'e indi ve **sınama dönemi değişti**: birinci turda üç dönem, ikinci turda iki. Son sütun kesin
cevabı verir. Üç turun sınama taban oranı sırasıyla **0,8067**, **0,7955** ve **0,8078**'dir; üst
tablodaki üç sayının **aynısı**. Model üç turda da her satıra "yüksek değil" dediği için ölçülen
şey modelin başarımı değil, o turda hangi satırların sınamaya düştüğüdür.

Buradan kursun kuralı okunur. Üç sayı aynı sorunun üç cevabı değildir; **üç ayrı sorunun** birer
cevabıdır ve hangi soruya cevap verdikleri hiçbir yerde yazmaz. Bu sayılardan biri bir hafta sonra
bir rapora girse, onu okuyan kişi ne kadar veri okunduğunu, kaç aday denendiğini ve hangi dönemlerin
sınamaya ayrıldığını bilemez. **Yeniden üretilemeyen sayı bir ölçüm değil bir anıdır.**

#### Kursun Üç Sütunu

Kursun geri kalanında her ders üç sayıyı yan yana koyar. Birincisi **sabitlemesiz tabandır**:
yukarıdaki üç koşum, üç ayrı sayı. İkincisi **sabitlenmiş kurulumdur**: aynı iş, belirli kalemler
sabitlenmiş, koşumların kaçının aynı çıktığı sayılmış. Üçüncüsü **bedeldir**: sabitlenen kalem,
saklanan kayıt, eklenen adım. Üçüncü sütun boş bırakılamaz, çünkü **katkısı gösterilemeyen bir
sabitleme ölçülmemiş bir güvence verir**.

#### Özet

- MLOps, bir modelin sayısını belirleyen beş kalemi kaynak kodunun zaten sahip olduğu disipline
  bağlama pratiğinin adıdır: veri, bölünme, öznitelik kodu, arama uzayı ve yuvarlama. Beşinden
  yalnız biri kaynak dosyasının içinde durur.
- Kurgu model üç ağırlıklı bir eşiktir, eğitim 27 adaylık bir ızgarada aramaktır. Etiket 40 m3
  eşiğiyle üretilir, dağılım `{0: 1752, 1: 448}` çıkar, **taban sınıf oranı 0,7964**'tür.
- Modelin sınama isabeti **0,8067**, sınama kümesinin taban oranı da **0,8067**'dir. Model 600
  satırın hiçbirine "yüksek" demez, **duyarlılığı 0,0000**'dır ve tabanın üstünde **0,0000** durur.
- Sabitlemesiz tabanda üç koşum **0,8067**, **0,7955** ve **0,8078** verir; aradaki **0,0123** fark
  0,0017'lik çözünürlüğün yedi katıdır. Üç koşumun ağırlığı ise birebir aynıdır ve üç sayının üçü
  de kendi sınama kümesinin taban oranına eşittir.
- Kursun kuralı budur: bir modelin sayısı onu üreten yolla birlikte anlamlıdır ve **yeniden
  üretilemeyen sayı bir ölçüm değil bir anıdır**.

#### Sonraki Adım

Üç koşum yapıldı ve üç sayı çıktı, ama hiçbiri bir yere yazılmadı. Sayıları yan yana
görebilmemizin tek sebebi üçünü de aynı blokta koşturmuş olmamızdır; gerçek bir çalışmada bu üç
koşum üç ayrı güne dağılır ve aralarında yalnız hatırlanan bir sayı kalır. Sonraki ders o kaydı
kurar ve tek bir soruyu ölçer: bir sonucu üreten ağırlığı bulmak için kaç koşumun tekrar
koşturulması gerekiyor, kayıtla kaç ve kaydın kendi bedeli ne kadar.

### Deney Takibi

Source: https://academia.sh/tr/kurslar/mlops-temelleri/deney-takibi

Parametre, ölçüt ve çıktı kaydının bir koşumu yeniden bulunabilir kılmasının ölçülmesi: on sekiz koşumluk bir uzayda kayıt tutulmadığında bildirilen bir sayıyı üreten ağırlığı bulmak için 0,8078 hedefinde 3, 0,7955 hedefinde 2, 0,7950 hedefinde 5, 0,8067 hedefinde 1 ve hiç üretilmemiş bir hedefte 18 koşum yeniden koşturuluyor, tam kayıtla beşinde de 0 koşum tekrarlanıyor; sayının kendisi koşumu tanımlamıyor çünkü on sekiz koşumun on dördü 0,8067 veriyor ve yeniden koşturarak bulunan aday on dört adaydan yalnız biri oluyor; kaydın bedeli koşum başına dokuz alan ve toplam 162 alan, yalnız ölçüt kaydeden 36 alanlık defter ise beş aramadan yalnız birini kısaltıyor.

Önceki derste üç koşum yapıldı ve üç ayrı sayı çıktı. Üçünü yan yana görebilmemizin tek sebebi
üçünün de aynı blokta koşturulmuş olmasıydı. Gerçek bir çalışmada bu koşumlar günlere yayılır,
farklı kişiler tarafından yapılır ve geriye yalnız bir sayı kalır: bir sunumda yazılı, bir iletide
aktarılmış ya da bir kişinin hatırladığı bir isabet.

Bu ders o sayının peşine düşer. Elimizde bir koşum uzayı olsun ve birinden şu bilgi gelsin: "bir
koşum 0,8078 verdi". Soru tek: o koşumun ağırlıklarını ve kullandığı kalemleri bulmak ne kadar
işe mal olur. Ölçü, **tekrar koşturulan koşum sayısıdır**.

- **YU12.** Koşum uzayı **on sekiz koşumdur**: altı sabitleme düzeyi (sıfırdan beş kaleme kadar)
  çarpı üç tur. Uzay bir seçimdir ve ona duyarlı her sayı uzay boyutuyla birlikte yazılır.
- **YU13.** **Deney takibi**, bir koşumun parametrelerini, ölçütünü ve çıktısını koşumun kendisiyle
  birlikte saklamaktır. Bir ürün değil, bir **kayıt disiplinidir**.
- **YU14.** Taban çizgisi **kayıtsız kurulumdur**: hiçbir koşum yazılmamıştır, elde yalnız
  bildirilen sayı vardır.
- **YU15.** Kayıtsız aramanın yordamı şudur: uzay **baştan sona sırayla** yeniden koşturulur ve
  bildirilen sayı tuttuğu anda durulur. Tekrar sayısı bu sıraya bağlıdır ve sıra bir **seçimdir**.
- **YU16.** Kaydın bedeli **saklanan alan** olarak sayılır: kayıt başına alan sayısı çarpı koşum
  sayısı. Ham bayt yazılmaz.
- **YU17.** Eğitim pratiği, koşum değişkenliği ve ayar bütçesi Derin Öğrenme kursunda ölçüldü;
  burada **tekrarlanmaz**. Bu ders yalnız **kaydın aramayı ne kadar kısalttığını** sayar.
- **YU18.** Etiket dağılımı önceki derste kurulduğu gibi `{0: 1752, 1: 448}`, **taban sınıf oranı
  0,7964**'tür ve bu dersteki isabet sayıları o oranın üstünde bir başarım iddiası taşımaz.
  Çözünürlük **0,0017**'dir.

#### Koşum Uzayı ve Defter

İlk blok önceki dersin kurgu kaynağını, hattını ve modelini aynen yeniden kurar.

```python
# KURGUDUR. Sayac okumalari, hat ve model ders icinde tanimlanir; gercek bir
# ogrenme kitapligi ya da deney takip urunu 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):
    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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]
SINAMA = DONEM[9:]
ESIK = 40


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """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 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)]


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


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)


KALEM = ("veri_surumu", "oznitelik_kodu", "bolunme", "izgara", "yuvarlama")


def kosum(kayitlar, sabit, tur=0):
    """Sabitlenmeyen her kalem tur'a gore degisir."""
    kay = kayitlar if "veri_surumu" in sabit else kayitlar[: len(kayitlar) - 20 * tur]
    pencere = None if "oznitelik_kodu" in sabit else (None if tur == 0 else 12 - tur)
    basamak = 4 if "yuvarlama" in sabit else max(4 - tur, 0)
    izgara = IZGARA if "izgara" in sabit else IZGARA[: len(IZGARA) - 6 * tur]
    donemler = SINAMA if "bolunme" in sabit else DONEM[9 + (tur % 2):]
    oz = oznitelikler(kay, pencere, basamak)
    m = egit(kay, oz, izgara)
    return {"agirlik": m["agirlik"], "egitim": m["egitim_isabeti"],
            "sinama": sina(kay, m, oz, donemler=donemler)}
```

Bir deney kaydı, koşum bittikten sonra yazılan bir satır değildir; koşumun **kendi içinde**
üretilir, çünkü koşumun hangi kalemleri kullandığını yalnız koşumun kendisi bilir. Aşağıdaki
`kayit_uret` bunu yapar: uzaydaki bir koşumu çalıştırır ve o koşumun okuduğu satır sayısını,
denediği aday sayısını, öznitelik penceresini, sınama dönemi sayısını, bulduğu ağırlığı ve elde
ettiği isabeti tek bir kayda yazar.

```python
S = satirlar()
UZAY = [(KALEM[:n], tur) for n in range(6) for tur in range(3)]


def kayit_uret(no):
    """Uzaydaki no'lu kosumu calistirir ve kullandigi kalemleri kaydeder."""
    sabit, tur = UZAY[no - 1]
    r = kosum(S, sabit, tur)
    return {"kosum": no, "sabit": len(sabit), "tur": tur,
            "satir": len(S) if "veri_surumu" in sabit else len(S) - 20 * tur,
            "aday": len(IZGARA) if "izgara" in sabit else len(IZGARA) - 6 * tur,
            "pencere": None if "oznitelik_kodu" in sabit else (None if tur == 0 else 12 - tur),
            "sinama_donemi": len(SINAMA) if "bolunme" in sabit else len(DONEM) - 9 - (tur % 2),
            "agirlik": r["agirlik"], "sinama": r["sinama"]}


DEFTER = [kayit_uret(no) for no in range(1, len(UZAY) + 1)]
print("kosum:", len(DEFTER), " kayit basina alan:", len(DEFTER[0]),
      " saklanan alan:", len(DEFTER) * len(DEFTER[0]))
print()
print(f"{'kosum':<7}{'sabit':>6}{'tur':>5}{'satir':>7}{'aday':>6}{'pencere':>9}{'donem':>7}{'sinama':>9}")
for k in DEFTER[:3] + DEFTER[-3:]:
    print(f"{k['kosum']:<7}{k['sabit']:>6}{k['tur']:>5}{k['satir']:>7}{k['aday']:>6}"
          f"{str(k['pencere']):>9}{k['sinama_donemi']:>7}{k['sinama']:>9.4f}")
say = {}
for k in DEFTER:
    say[k["sinama"]] = say.get(k["sinama"], 0) + 1
print()
print("farkli sinama sayisi:", len(say), " sayi -> kac kosum:", dict(sorted(say.items())))
```

```
kosum: 18  kayit basina alan: 9  saklanan alan: 162

kosum   sabit  tur  satir  aday  pencere  donem   sinama
1           0    0   2200    27     None      3   0.8067
2           0    1   2180    21       11      2   0.7955
3           0    2   2160    15       10      3   0.8078
16          5    0   2200    27     None      3   0.8067
17          5    1   2200    27     None      3   0.8067
18          5    2   2200    27     None      3   0.8067

farkli sinama sayisi: 4  sayi -> kac kosum: {0.795: 2, 0.7955: 1, 0.8067: 14, 0.8078: 1}
```

Defterin ilk üç satırı önceki dersin sabitlemesiz tabanıdır; son üç satırda beş kalemin hepsi
sabitlenmiştir ve üç tur da aynı satırı, aynı adayı, aynı pencereyi ve aynı sınama dönemini
kullanır. Son satır asıl bulguyu verir: on sekiz koşum yalnız **dört ayrı sayı** üretmiştir ve
bunlardan **0,8067 on dört koşumda** çıkmıştır.

#### Bir Sayıyı Üreten Koşumu Aramak

Şimdi kaydın işe yaradığı yer ölçülebilir. Kayıtsız kurulumda elde yalnız bildirilen sayı vardır ve
tek yordam uzayı baştan yeniden koşturmaktır. Kayıtlı kurulumda defter okunur. Arada bir üçüncü
durum daha vardır ve pratikte en sık görülen odur: **yalnız ölçüt kaydedilmiştir**, yani koşum
numarası ile isabet yazılmış ama parametreler yazılmamıştır.

```python
HEDEF = (0.8078, 0.7955, 0.7950, 0.8067, 0.8100)


def kayitsiz_ara(hedef):
    """Kayit yok: uzay bastan tekrar kosturulur, sayi tutunca durulur."""
    tekrar = 0
    for sabit, tur in UZAY:
        tekrar += 1
        if kosum(S, sabit, tur)["sinama"] == hedef:
            return tekrar
    return tekrar


OLCUT = [(k["kosum"], k["sinama"]) for k in DEFTER]
print("yalniz olcut kaydi -- alan:", len(OLCUT) * 2, " tam kayit -- alan:",
      len(DEFTER) * len(DEFTER[0]))
print()
print(f"{'hedef':<9}{'kayitsiz':>10}{'yalniz olcut':>14}{'tam kayit':>11}{'ayni sayiyi veren':>19}")
for h in HEDEF:
    a = kayitsiz_ara(h)
    aday = [k["kosum"] for k in DEFTER if k["sinama"] == h]
    o = 0 if not any(s == h for _, s in OLCUT) else a
    print(f"{h:<9.4f}{a:>10}{o:>14}{0:>11}{len(aday):>19}")
print()
print("0,8067 veren kosumlar:", [k["kosum"] for k in DEFTER if k["sinama"] == 0.8067])
```

```
yalniz olcut kaydi -- alan: 36  tam kayit -- alan: 162

hedef      kayitsiz  yalniz olcut  tam kayit  ayni sayiyi veren
0.8078            3             3          0                  1
0.7955            2             2          0                  1
0.7950            5             5          0                  2
0.8067            1             1          0                 14
0.8100           18             0          0                  0

0,8067 veren kosumlar: [1, 4, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
```

İlk sütun kayıtsız kurulumun bedelidir ve hedefe göre 1 ile 18 arasında değişir. Son satır en
pahalısıdır: bildirilen 0,8100 bu uzayda hiç üretilmemiştir, dolayısıyla arama **on sekiz koşumun
tamamını** tekrarlar ve elde hiçbir şey kalmaz. Bu, kayıtsız aramanın en sık karşılaşılan sonucudur,
çünkü bildirilen sayı çoğu zaman uzayın o günkü hâlinden gelir ve uzay değişmiştir.

Üçüncü sütun her hedefte **0**'dır. Tam kayıtla arama bir koşum tekrarı gerektirmez; defter okunur
ve o kadar.

#### Sayı Koşumu Tanımlamaz

Tablonun asıl bulgusu son sütunda durur ve tekrar sayılarından daha önemlidir. 0,8067 hedefinde
kayıtsız arama **bir** koşumda "başarılı" olur: uzayın birinci koşumu bu sayıyı verir ve arama
durur. Ama aynı sayıyı **on dört koşum** veriyor. Aramanın bulduğu birinci koşum, bildirilen sayıyı
üreten koşum olmak zorunda değildir; on dört adaydan yalnız biridir.

Bunun sonucu şudur: yeniden koşturma **bir sayıyı** yeniden üretebilir, ama **bir koşumu** teşhis
edemez. Bildirilen sayı 0,8067 ise, o koşumun 2200 satır mı 2160 satır mı okuduğu, 27 aday mı 15
aday mı denediği ve sınamayı üç döneme mi iki döneme mi ayırdığı sayıdan çıkarılamaz. Defter bu
soruların hepsini tek satırda yanıtlar ve on dört adayı birbirinden ayırır. Kaydın verdiği şey
**tekrar tasarrufu değil, teşhistir**; tekrar tasarrufu yan üründür.

#### Kaydın Bedeli

Defterin bedeli iki kalemdir. Birincisi **saklanan alandır**: her koşum dokuz alan yazar, on sekiz
koşum **162 alan** tutar. Bu alan koşum sayısıyla doğru orantılı büyür; bir ayar taramasında yüzlerce
koşum yapılırsa defter de o oranda büyür ve bir saklama politikası gerektirir. İkincisi **eklenen
adımdır**: `kayit_uret` koşumun içinde çalışır, yani eğitim hattına bir yazma adımı girer ve o adım
koşumun kullandığı kalemleri okumak zorundadır. Kalemi okuyamayan bir kayıt onu yazamaz.

İkinci sütun bu bedelin nasıl boşa harcanabileceğini gösterir. Yalnız ölçüt kaydeden defter 36 alan
tutar, yani tam defterin beşte birinin biraz üstündedir ve beş aramadan **yalnız birini** kısaltır:
hiç üretilmemiş 0,8100 hedefinde on sekiz tekrarı sıfıra indirir. Kalan dört hedefte tekrar sayısı
kayıtsız kurulumla **birebir aynıdır**, çünkü parametre yazılmadığında koşum yine yeniden
koşturulmak zorundadır. Bir defter tutuluyor olması bir güvence değildir; **hangi alanları tuttuğu**
ölçülmedikçe defter bir bedelden ibarettir.

#### Özet

- Deney takibi bir koşumun parametrelerini, ölçütünü ve çıktısını koşumun kendi içinde yazmaktır;
  kalemleri yalnız koşumun kendisi bildiği için kayıt sonradan tamamlanamaz.
- On sekiz koşumluk uzay yalnız **dört ayrı sayı** üretir ve **0,8067 on dört koşumda** çıkar.
- Kayıtsız kurulumda bildirilen bir sayıyı üreten koşumu bulmak **1**, **2**, **3**, **5** ya da
  **18** koşum tekrarına mal olur; hiç üretilmemiş bir sayıda uzayın tamamı boşa koşturulur. Tam
  kayıtla beş hedefte de tekrar sayısı **0**'dır.
- Yeniden koşturma bir sayıyı yeniden üretir ama koşumu teşhis edemez: 0,8067 hedefinde arama bir
  tekrarda durur ve bulduğu koşum **on dört adaydan** yalnız biridir.
- Kaydın bedeli koşum başına **dokuz alan**, toplam **162 alan** ve eğitim hattına eklenen bir yazma
  adımıdır.
- Yalnız ölçüt kaydeden **36 alanlık** defter beş aramadan yalnız birini kısaltır; bir defterin
  varlığı değil, **hangi alanları tuttuğu** ölçülür.

#### Sonraki Adım

Defter koşumun kullandığı satır sayısını yazdı, ama satırların **hangileri** olduğunu yazmadı. On
sekiz koşumun hepsi verisini aynı yerden okudu ve o yerin adı hiç değişmedi; içeriğinin değişip
değişmediği ise sorulmadı. Bir koşum 2200 satır okuduğunu kaydettiğinde, bir hafta sonraki koşum da
2200 satır okuduğunu kaydeder ve iki sayı aynı görünür. Sonraki ders bu adın altındaki içeriği
ölçer: sürümsüz bir kurulumda kaç koşum aynı adı gösterip farklı sonuç veriyor, sürümle kaç ve
sürümün kendi bedeli ne kadar.

### Veri ve Model Sürümleme

Source: https://academia.sh/tr/kurslar/mlops-temelleri/veri-ve-model-surumleme

Aynı ada verilen iki farklı içeriğin ayrılmasının ölçülmesi: dört teslim aynı `olcum-2026` adı altında 2200, 2180, 2160 ve yine 2200 satır getirdiğinde sürümsüz kurulumda tek bir ad dört koşumun tamamını belirsiz bırakıyor ve üç ayrı sınama sayısı 0,8067, 0,8064 ile 0,8078 aynı adın arkasında duruyor, içerik imzasıyla adlandırıldığında üç ad doğuyor ve belirsiz koşum sıfıra iniyor; model tarafında dört koşumun ağırlığı birebir aynı olduğu için yalnız ağırlığa bakan bir model kimliği dört koşumu tek sürümde topluyor ve ancak veri imzası eklendiğinde üç sürüm ayrılıyor; bedel olarak tarih adlı şema 8740 satır saklarken içerik adlı şema 6540 satır saklıyor ve her teslimde imza için 8740 satır okunuyor; çıktı imzası hattın yuttuğu değişikliği görmüyor, altmış kayıt iki kez geldiğinde kaynak imzası değişirken çıktı imzası ve isabet birebir aynı kalıyor.

Önceki dersin defteri her koşumun kaç satır okuduğunu yazdı. On sekiz koşumun hepsi verisini aynı
yerden okudu ve o yerin adı hiç değişmedi. Defter "2200 satır" yazdığında iki koşumun aynı 2200
satırı okuduğunu varsaydık, ama bu varsayım hiçbir yerde sınanmadı.

Bu ders o varsayımı sınar. Veri soyağacı, yani bir sunum değerinden kaynağa giden bağın kaydı, Veri
Mühendisliğine Giriş kursunda ölçüldü ve burada tekrarlanmaz. Buradaki soru daha dar: bir ad
altındaki içerik değiştiğinde ve ad değişmediğinde, kaç koşum birbirinden ayırt edilemez hâle
geliyor.

- **YU19.** Kurgu üretici veriyi **dört teslimde** getirir. Teslimler sırasıyla 2200, 2180, 2160 ve
  yine 2200 satırdır; dördü de aynı `olcum-2026` adıyla yazılır. **Dördüncü teslim birincinin
  içeriğiyle birebir aynıdır.**
- **YU20.** **Veri sürümü** bir adla bir içeriğin eşleşmesidir. Üç şema karşılaştırılır: **sürümsüz**
  (tek ad, üzerine yazılır), **tarih adlı** (her teslime bir ad) ve **içerik adlı** (ad, içerikten
  üretilen imzayı taşır).
- **YU21.** **İmza** içerikten üretilen kısa bir özettir ve ders içinde yazılır; hiçbir dış kitaplık
  çağrılmaz. İmza bir güvenlik aracı değil, bir **kimlik** aracıdır.
- **YU22.** Taban çizgisi **sürümsüz kurulumdur**: tek ad, üzerine yazılan içerik, geriye dönük
  yeniden üretim yok.
- **YU23.** Ölçülen birimler **ad sayısı**, **belirsiz koşum** ve **saklanan satırdır**. Belirsiz
  koşum, aynı adı taşıyıp o ad altında farklı bir sonuç veren koşumdur.
- **YU24.** Öznitelik kodu bu derste **sabittir**; dört koşum da tam geçmişi görür ve dört basamağa
  yuvarlar. Değişen tek şey **verinin içeriğidir**.
- **YU25.** Etiket dağılımı `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**'tür ve bu dersteki
  isabet sayıları bir başarım iddiası taşımaz. Çözünürlük **0,0017**'dir.

#### Aynı Adın Altındaki İçerik

İlk blok önceki iki dersin kurgu kaynağını, hattını ve modelini aynen yeniden kurar.

```python
# KURGUDUR. Sayac okumalari, hat ve model ders icinde tanimlanir; gercek bir
# ogrenme kitapligi ya da model kayit defteri urunu 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):
    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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]
SINAMA = DONEM[9:]
ESIK = 40


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """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 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)]


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


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)
```

Dört teslim aşağıdaki blokta koşturulur. Her teslimde aynı kod aynı adla veriyi okur, modeli eğitir
ve sonucu `tuketim-esigi` adıyla yazar. `imza` içerikten kısa bir özet üretir ve şimdilik yalnız
raporlanır; hiçbir karara girmez.

```python
S = satirlar()


def imza(metin):
    """Icerikten uretilen kisa bir ozet. Guvenlik degil , kimlik araci."""
    h = 2166136261
    for c in metin:
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return f"{h:08x}"


def veri_imzasi(kayitlar):
    return imza("|".join(f"{k['abone']}:{k['donem']}:{k['m3']}" for k in kayitlar))


TESLIM = [2200, 2180, 2160, 2200]
KOSUM = []
for no, n in enumerate(TESLIM, start=1):
    kay = S[:n]
    oz = oznitelikler(kay)
    m = egit(kay, oz)
    KOSUM.append({"kosum": no, "veri_adi": "olcum-2026", "satir": n,
                  "veri_imza": veri_imzasi(kay), "model_adi": "tuketim-esigi",
                  "agirlik": m["agirlik"], "sinama": sina(kay, m, oz)})
print(f"{'teslim':<8}{'veri adi':<13}{'satir':>7}{'veri imzasi':>13}"
      f"{'model adi':>15}{'agirlik':>17}{'sinama':>9}")
for k in KOSUM:
    print(f"{k['kosum']:<8}{k['veri_adi']:<13}{k['satir']:>7}{k['veri_imza']:>13}"
          f"{k['model_adi']:>15}{str(k['agirlik']):>17}{k['sinama']:>9.4f}")
print()
print("ayri sinama sayisi:", len(set(k["sinama"] for k in KOSUM)),
      " ayri agirlik:", len(set(k["agirlik"] for k in KOSUM)),
      " ayri veri icerigi:", len(set(k["veri_imza"] for k in KOSUM)))
```

```
teslim  veri adi       satir  veri imzasi      model adi          agirlik   sinama
1       olcum-2026      2200     bd88a253  tuketim-esigi    (0.6, 0.4, 0)   0.8067
2       olcum-2026      2180     f008c71c  tuketim-esigi    (0.6, 0.4, 0)   0.8064
3       olcum-2026      2160     cebfcc57  tuketim-esigi    (0.6, 0.4, 0)   0.8078
4       olcum-2026      2200     bd88a253  tuketim-esigi    (0.6, 0.4, 0)   0.8067

ayri sinama sayisi: 3  ayri agirlik: 1  ayri veri icerigi: 3
```

Tablo iki sütununda hiç değişmez: veri adı dört satırda da `olcum-2026`, model adı dört satırda da
`tuketim-esigi`. Bir sütununda da hiç değişmez: ağırlık dört koşumda da `(0.6, 0.4, 0)`, yani
üretilen model dosyası **birebir aynıdır**. Değişen iki sütun vardır: içerik imzası üç ayrı değer
alır ve sınama isabeti üç ayrı sayı verir.

#### Adın Kaç İçeriği Var

Sayılacak şey artık nettir. Bir adlandırma şeması, aynı adı taşıyıp farklı sonuç veren koşum
bırakıyorsa o koşumlar **belirsizdir**: hangi içerikten geldikleri addan okunamaz.

```python
def olc(kimlik):
    """Kac ad var , kac kosum belirsiz kaliyor , kac satir saklaniyor."""
    grup = {}
    for k in KOSUM:
        grup.setdefault(kimlik(k), []).append(k)
    belirsiz = sum(len(v) for v in grup.values() if len(set(x["sinama"] for x in v)) > 1)
    return len(grup), belirsiz, sum(v[-1]["satir"] for v in grup.values())


print(f"{'sema':<14}{'veri adi':>10}{'belirsiz kosum':>16}{'saklanan satir':>16}")
for ad, kim in (("surumsuz", lambda k: k["veri_adi"]),
                ("tarih adli", lambda k: f"{k['veri_adi']}@t{k['kosum']}"),
                ("icerik adli", lambda k: f"{k['veri_adi']}@{k['veri_imza']}")):
    a, b, s = olc(kim)
    print(f"{ad:<14}{a:>10}{b:>16}{s:>16}")
print()
print(f"{'model kimligi':<26}{'ayri surum':>12}{'belirsiz kosum':>16}")
for ad, kim in (("yalniz agirlik", lambda k: imza(str(k["agirlik"]))),
                ("veri imzasi + agirlik", lambda k: imza(f"{k['veri_imza']}|{k['agirlik']}"))):
    a, b, _ = olc(kim)
    print(f"{ad:<26}{a:>12}{b:>16}")
print()
print("imza icin okunan satir:", sum(TESLIM))
```

```
sema            veri adi  belirsiz kosum  saklanan satir
surumsuz               1               4            2200
tarih adli             4               0            8740
icerik adli            3               0            6540

model kimligi               ayri surum  belirsiz kosum
yalniz agirlik                       1               4
veri imzasi + agirlik                3               0

imza icin okunan satir: 8740
```

Sürümsüz kurulumda tek bir ad vardır ve **dört koşumun dördü de belirsizdir**. Bu satır kursun
kuralının veri tarafındaki hâlidir: 0,8078 sayısı bir rapora girdiğinde onu üreten içerik artık
yoktur, çünkü dördüncü teslim üçüncünün üzerine yazmıştır. Saklanan satır sütunu bunu doğrular:
sürümsüz şema en az yeri tutar, **2200 satır** ve karşılığında geçmişin tamamını kaybeder.

Tarih adlı şema belirsizliği sıfıra indirir, ama bir kusuru vardır: dört ad üretir, oysa üç içerik
vardır. Birinci ve dördüncü teslim **birebir aynı içeriktir** ve tarih şeması bunu göremediği için
aynı 2200 satırı iki kez saklar. İçerik adlı şema aynı belirsizliği **üç adla** ve **6540 satırla**
karşılar; dördüncü teslim hiç yeni yer tutmaz, çünkü imzası zaten defterdedir.

#### İmza Neyi İmzalar

`veri_imzasi` hattın **çıktısını** imzalar, yani abone, dönem ve hesaplanmış tüketim üçlüsünü. Bu
bir seçimdir ve bedeli vardır: hattın yuttuğu bir üretici değişikliği çıktı imzasına hiç yansımaz.
Aşağıdaki blok bunu ölçer. Üretici, temmuz ayında altmış kaydı iki kez göndersin; kaynak değişmiştir
ama tekilleştirme aşaması yinelenen satırları düşürür.

```python
def kaynak_imzasi(kayitlar):
    return imza("|".join(f"{k['abone']}:{k['donem']}:{k['endeks']}" for k in kayitlar))


def hatta_ver(kaynak):
    v, _ = a1_ayikla(kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


ham = kaynak_uret()
# Uretici 60 kaydi iki kez gonderdi: kaynak degisti , hat onu yutuyor.
yinelenen = ham + [dict(k) for k in ham if k["abone"] <= "A060" and k["donem"] == "2026-07"]
print(f"{'teslim':<12}{'kaynak satiri':>15}{'kaynak imzasi':>15}"
      f"{'cikti satiri':>14}{'cikti imzasi':>14}{'sinama':>9}")
for ad, kaynak in (("saglam", ham), ("yinelenen", yinelenen)):
    c = hatta_ver(kaynak)
    oz2 = oznitelikler(c)
    m2 = egit(c, oz2)
    print(f"{ad:<12}{len(kaynak):>15}{kaynak_imzasi(kaynak):>15}{len(c):>14}"
          f"{veri_imzasi(c):>14}{sina(c, m2, oz2):>9.4f}")
print()
print(f"{'teslim':<8}{'satir':>7}{'sinama satiri':>15}{'cozunurluk':>12}")
for n in TESLIM[:3]:
    sk = [k for k in S[:n] if k["donem"] in SINAMA]
    print(f"{n:<8}{n:>7}{len(sk):>15}{1 / len(sk):>12.4f}")
```

```
teslim        kaynak satiri  kaynak imzasi  cikti satiri  cikti imzasi   sinama
saglam                 2400       d501f04f          2200      bd88a253   0.8067
yinelenen              2460       93576832          2200      bd88a253   0.8067

teslim    satir  sinama satiri  cozunurluk
2200       2200            600      0.0017
2180       2180            594      0.0017
2160       2160            588      0.0017
```

Kaynak imzası değişti, çıktı imzası **birebir aynı** kaldı ve isabet de aynı. İki teslimin sürüm
kaydı aynı satırı gösterir, oysa üreticiden gelen içerik farklıdır. Bu, hattın **doğru** davranışıdır
ve sürümlemenin bir sınırıdır: çıktı imzası hattın yuttuğu değişikliği görmez. Ters yönde de eksiktir;
kaynağı imzalayan bir şema burada bir sürüm farkı işaretler, oysa modele giden veri hiç değişmemiştir.
İki imza iki ayrı soruyu yanıtlar ve **hangisinin tutulduğu yazılmadan** bir veri sürümü eksik
kalır.

Alt tablo bir başka ayrıntıyı kapatır. Teslim küçüldükçe sınama kümesi de küçülür: 600, 594 ve 588
satır. Üç teslimde çözünürlük 0,0017'de kalır, ama karşılaştırılan sayılar **aynı kümede** ölçülmüş
değildir. 0,8067 ile 0,8064 arasındaki fark bu yüzden bir başarım farkı olarak okunamaz.

#### Modelin Kimliği Ağırlığından İbaret Değildir

Alt tablo dersin en sessiz bulgusunu taşır. Model dosyası dört koşumda da aynıdır, dolayısıyla
modeli **kendi içeriğine göre** adlandıran bir şema dört koşumu **tek bir sürümde** toplar ve
dördünü de belirsiz bırakır. Bu, veri tarafındaki hatanın aynadaki görüntüsüdür: orada bir ad üç
içeriği örtüyordu, burada bir içerik üç sayıyı örtüyor.

Dört model dosyası birbirinin yerine konabilir görünür, çünkü aynı ağırlıkları taşırlar. Yerlerine
konamazlar: her biri farklı bir veri kümesinde ölçülmüştür ve bildirdikleri sayı 0,8067, 0,8064 ve
0,8078'dir. Aradaki 0,0014 ile 0,0011 fark 0,0017'lik çözünürlüğün **altındadır** ve sayılar üstelik
aynı kümede ölçülmemiştir; bildirilen sayı bu dört dosyayı birbirinden ayırmaya yetmez. Ağırlığa
veri imzası eklendiğinde kimlik **üç sürüme** ayrılır ve belirsiz koşum **0**'a iner. Bir model
sürümü, model dosyasının değil, **model dosyasıyla onu üreten veri sürümünün** birlikte imzasıdır.

#### Sürümlemenin Bedeli ve Söylemediği

Bedel üç kalemdir. Birincisi **saklanan satırdır**: içerik adlı şema 6540 satır tutar, sürümsüz
şemanın **2,97 katı**. İkincisi **okunan satırdır**: imza içerikten hesaplandığı için her teslimde
bütün satırlar bir kez daha okunur, dört teslimde **8740 satır**. Üçüncüsü **eklenen adımdır**:
yazma işlemi artık imza hesaplamak ve defter satırı eklemek zorundadır.

Bunun karşılığında alınan şey dar ve kesindir: sürümleme **hangi içeriğin hangi sayıyı ürettiğini**
söyler. Söylemediği şeyse hangi içeriğin **doğru** olduğudur. Üç teslimden hangisinin eksik geldiğini,
hangisinin tam olduğunu ve 0,8078 sayısının güvenilir olup olmadığını üç imza da söylemez; onu
söyleyen şey Veri Mühendisliğine Giriş kursunda ölçülen denetimlerdir. Sürümleme bir kalite aracı
değil, bir **kimlik aracıdır** ve katkısı yalnız belirsiz koşum sayısıyla ölçülür.

#### Özet

- Dört teslim aynı `olcum-2026` adıyla 2200, 2180, 2160 ve yine 2200 satır getirdi; ad hiç
  değişmedi, içerik **üç ayrı imza** aldı ve sınama isabeti **0,8067**, **0,8064** ile **0,8078**
  oldu.
- Sürümsüz şemada tek ad vardır ve **dört koşumun dördü de belirsizdir**; içerik adlı şemada **üç
  ad** vardır ve belirsiz koşum **0**'dır.
- Tarih adlı şema belirsizliği aynı şekilde kapatır ama **dört ad** üretip **8740 satır** saklar;
  içerik adlı şema aynı işi **üç ad** ve **6540 satırla**, yani sürümsüz şemanın **2,97 katıyla**
  yapar, çünkü dördüncü teslimin birinciyle aynı olduğunu görür.
- Çıktı imzası hattın yuttuğu değişikliği görmez: altmış kayıt iki kez geldiğinde kaynak imzası
  değişir, **çıktı imzası ve isabet birebir aynı kalır**. Hangi imzanın tutulduğu yazılmadan bir
  veri sürümü eksik kalır.
- Model dosyası dört koşumda da birebir aynıdır. Yalnız ağırlığa bakan bir model kimliği dört koşumu
  **tek sürümde** toplar; veri imzası eklendiğinde **üç sürüm** ayrılır ve belirsiz koşum **0** olur.
- Sürümleme hangi içeriğin hangi sayıyı ürettiğini söyler, hangisinin **doğru** olduğunu söylemez;
  o soru denetimlerin işidir.

#### Sonraki Adım

Üç derste koşumun kendisi kayda geçti ve sürümlendi: hangi kalemlerle koşulduğu yazıldı, hangi
içeriği okuduğu bir imzaya bağlandı ve aynı adın arkasındaki üç içerik birbirinden ayrıldı. Bütün
bu iş **eğitim tarafında** yapıldı. Modele giren özniteliğin eğitim sırasında bir kodla, üretimde
çağrıldığında **başka bir kodla** hesaplanıyor olabileceği hiç sorulmadı. Sonraki ders o iki yolu
yan yana koyar ve tek bir soruyu ölçer: ağırlıklar birebir aynıyken isabet neden düşüyor.

### Öznitelik Depoları

Source: https://academia.sh/tr/kurslar/mlops-temelleri/oznitelik-depolari

Eğitim ile çıkarım arasında özniteliğin aynı yolla hesaplanmasının ölçülmesi: aynı ağırlıklarla sınama isabeti eğitim yoluyla 0,8067, sunum yoluyla 0,7800 çıkıyor ve model hiç değişmiyor. Aradaki 0,0267 tümüyle özniteliğin hangi geçmişle hesaplandığından geliyor, 600 sınama satırının 22'sinde tahmini çeviriyor, 19'unu bozup üçünü düzeltiyor ve öznitelik penceresi ikiye çıkarıldığında tümüyle kapanıyor. Öznitelik deposu iki hesabı tek hesaba indirip sapmayı sıfırlıyor; karşılığında 6600 değerlik bir saklama, modele bakan kolda dördüncü bir aşama ve öznitelik kodu değiştiğinde saklananın tamamının yeniden yazılması bedelini getiriyor.

Üç derste sürümlenen şey **koşumun kendisi** oldu: hangi parametreyle çalıştığı, hangi ölçütü
ürettiği, hangi veri sürümünü okuduğu kayda geçti. Bu kayıtların hepsi koşuma **dışarıdan** bakar
ve hiçbiri modele giren sayının kendisine, yani **özniteliğin nasıl hesaplandığına** inmedi.

Bu ders o eksiği kapatır. Modeli eğiten kod bir yerde çalışır, tahmin üreten kod başka bir yerde;
iki yerde de aynı adı taşıyan bir öznitelik hesaplanır ve iki hesabın aynı sayıyı vermesi kimsenin
denetlediği bir şey değildir. Ölçülecek olan o farkın isabete yansımasıdır ve dersin taşıdığı sayı
şudur: **model hiç değişmeden isabet düşer.**

- **YU41.** Kaynak, hattın ilk üç aşaması ve eşik tabanlı model **kurgudur**; gerçek bir öğrenme
  kitaplığı, öznitelik deposu ürünü ya da sunum çatısı çağrılmaz. Tohum **20260218**, çıktı
  koşumdan bağımsızdır.
- **YU42.** **Öznitelik (feature)** modele giren, ham satırdan **hesaplanmış** sayıdır. Burada üç
  tane var: abonenin geçmiş tüketim ortalaması, bölgesinin geçmiş tüketim ortalaması ve işyeri
  göstergesi. Öznitelik üretme yordamı Veri Hazırlama ve Öznitelik Mühendisliği kursunda ölçüldü
  ve **tekrarlanmaz**.
- **YU43.** **Eğitim yolu** özniteliği **tam geçmişten** hesaplar; **sunum yolu** yalnız **son
  dönemi** görür. Aynı ağırlıkla iki yolun verdiği isabet arasındaki fark **eğitim–sunum sapmasıdır
  (training–serving skew)**.
- **YU44.** Etiket dengesizdir: `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**, sınama kümesinde
  **0,8067**. İsabet tek başına okunduğunda yanıltır ve bu derste iki kez yanıltacaktır.
- **YU45.** Model üç ağırlıklı bir eşiktir; eğitim, **27 adaylık bir ızgarada** aramaktır. Izgara
  araması Denetimli Öğrenme kursunda ölçüldü ve **tekrarlanmaz**.
- **YU46.** **Çıkarım (inference)** eğitilmiş ağırlıklarla tek satıra tahmin üretmektir; bu kursta
  eğitim de çıkarım da **tek makinede** ele alınır.
- **YU47.** Taban çizgisi **tek yollu kurulumdur**: öznitelik iki ayrı yerde ayrı ayrı hesaplanır
  ve ikisinin aynı olduğunu söyleyen hiçbir kayıt yoktur.
- **YU48.** Çözünürlük: sınama kümesi **600 satır**, isabetin en küçük adımı **1/600 = 0,0017**.
  Bu dersin sapması **0,0267**, yani adımın **on altı katı** ve ölçülmüştür.

#### Modele Giren Sayının İki Hesabı

Bir öznitelik ham satırın kendisi değildir; ham satırdan bir **kuralla** üretilir. "Abonenin geçmiş
tüketim ortalaması" böyle bir kuraldır ve tek cümleyle yazıldığında eksiksiz görünür. Eksik olan,
cümlenin sessizce varsaydığı şeydir: **geçmiş nereden başlıyor.**

Eğitimde yanıt kendiliğinden gelir; on iki dönemin hepsi masadadır ve ortalama tam geçmişten
alınır. Sunumda durum başkadır: tahmin dönem gelir gelmez üretilir, elde çoğu zaman **yalnız o
dönem** vardır ve aynı adı taşıyan öznitelik son dönemin değeriyle hesaplanır. İki kural da doğru
yazılmıştır, ikisi de aynı adı taşır ve ikisi aynı satır için farklı sayı üretir.

Aşağıdaki blok kaynağı, hattın modele bakan ilk üç aşamasını, etiketi ve kurgu modeli kurar.
`oznitelikler` işlevinin `pencere` değiştirgesi iki yolu ayırır: `None` tam geçmiş, `1` yalnız son
dönem demektir. Öznitelikler ağırlıktan bağımsızdır ve **bir kez** hesaplanır.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin ilk uc asamasi ve esik tabanli
# model ders icinde tanimlanir; gercek bir ogrenme kitapligi, oznitelik deposu
# urunu ya da sunum catisi 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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]        # 2026-02 .. 2026-09
SINAMA = DONEM[9:]         # 2026-10 .. 2026-12
ESIK = 40                  # bu m3 uzerindeki tuketim "yuksek" sayilir


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """pencere=None -> tam gecmis (egitim yolu); pencere=1 -> son donem (sunum yolu)."""
    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)]


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


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    """Egitim = agirlik uclusunu izgarada aramak. Oznitelik disaridan gelir."""
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)
```

#### Aynı Ağırlık, İki İsabet

İkinci blok modeli **bir kez** eğitir ve çıkan ağırlık üçlüsünü iki yolda sınar: aynı üç sayı, aynı
eşik, aynı sınama dönemleri. Değişen tek şey, özniteliğin hangi geçmişle hesaplandığıdır.

```python
import collections

S = satirlar()
SIN = [k for k in S if k["donem"] in SINAMA]
ANAHTAR = [(k["abone"], k["donem"]) for k in SIN]
GERCEK = {(k["abone"], k["donem"]): etiket(k) for k in SIN}
OZ_EGITIM = oznitelikler(S)                    # tam gecmis  -> egitim yolu
OZ_SUNUM = oznitelikler(S, pencere=1)          # son donem   -> sunum yolu
MODEL = egit(S, OZ_EGITIM)

dag = collections.Counter(etiket(k) for k in S)
tab = collections.Counter(GERCEK.values())
print("satir:", len(S), " egitim:", MODEL["egitim_satiri"], " sinama:", len(SIN))
print("etiket dagilimi:", dict(sorted(dag.items())),
      " taban sinif orani:", round(dag[0] / len(S), 4),
      " sinamada:", round(tab[0] / len(SIN), 4))
print("model agirligi:", MODEL["agirlik"], " aday:", MODEL["aday"],
      " egitim isabeti:", MODEL["egitim_isabeti"])


def tahminler(oz):
    return {a: (1 if puan(oz[a], MODEL["agirlik"]) > ESIK else 0) for a in ANAHTAR}


TE, TS = tahminler(OZ_EGITIM), tahminler(OZ_SUNUM)
print(f"\n{'yol':<8}{'oznitelik farkli':>18}{'yuksek dedi':>13}{'dogru pozitif':>15}{'isabet':>9}")
for ad, oz, t in (("egitim", OZ_EGITIM, TE), ("sunum", OZ_SUNUM, TS)):
    print(f"{ad:<8}{sum(1 for a in ANAHTAR if oz[a] != OZ_EGITIM[a]):>18}"
          f"{sum(t.values()):>13}{sum(1 for a in ANAHTAR if t[a] == 1 == GERCEK[a]):>15}"
          f"{sina(S, MODEL, oz):>9.4f}")
bozulan = sum(1 for a in ANAHTAR if TE[a] == GERCEK[a] != TS[a])
duzelen = sum(1 for a in ANAHTAR if TS[a] == GERCEK[a] != TE[a])
print("degisen tahmin:", bozulan + duzelen, " bozulan:", bozulan, " duzelen:", duzelen,
      " net:", bozulan - duzelen)
print("sapma:", round(sina(S, MODEL, OZ_EGITIM) - sina(S, MODEL, OZ_SUNUM), 4),
      "= net / 600 ; isabet adimi 1/600 =", round(1 / len(SIN), 4))
```

```
satir: 2200  egitim: 1600  sinama: 600
etiket dagilimi: {0: 1752, 1: 448}  taban sinif orani: 0.7964  sinamada: 0.8067
model agirligi: (0.6, 0.4, 0)  aday: 27  egitim isabeti: 0.7931

yol       oznitelik farkli  yuksek dedi  dogru pozitif   isabet
egitim                   0            0              0   0.8067
sunum                  600           22              3   0.7800
degisen tahmin: 22  bozulan: 19  duzelen: 3  net: 16
sapma: 0.0267 = net / 600 ; isabet adimi 1/600 = 0.0017
```

Ağırlık üçlüsü `(0.6, 0.4, 0)` iki satırda da aynıdır. İsabet **0,8067'den 0,7800'e** düşüyor ve
düşüşün tamamı öznitelik yolundan geliyor; modelde değişen tek bir sayı yoktur. Ayrıştırma farkın
nasıl oluştuğunu gösterir: **600 satırın 600'ünde öznitelik farklı**, ama tahmin yalnız
**22'sinde** çevriliyor, çünkü fark ancak eşiğin öbür yanına taşıyabildiğinde tahmini değiştirir.
Çevrilen 22 tahminin **19'u bozuluyor**, **üçü düzeliyor**, net **16 satır** kaybediliyor ve
16/600 tam olarak **0,0267** eder — çözünürlüğün on altı katı, yani ölçülmüş bir büyüklük.

İsabetin ikinci kez yanılttığı yer de burasıdır. Sınama kümesinin taban sınıf oranı **0,8067**;
`yuksek dedi` sütunu eğitim yolunda **sıfırdır**. Yani 0,8067 puanlayan model hiçbir satıra
"yüksek" demiyor ve tam olarak **çoğunluk sınıfını söyleyen** bir modelin sayısını üretiyor. İki
sayı da bir modelin işe yaradığını göstermiyor; ölçülen şey **iki yolun ne kadar saptığıdır**.

#### Sapma Nereden Geliyor

Fark bir hata değildir; hesabın gördüğü geçmiş kısaldıkça ortalama tek bir dönemin oynaklığına
yaklaşır. Üçüncü blok pencereyi 1'den tam geçmişe süpürür ve aynı ağırlıklarla her adımda sınar.
Yanına, sunum yolunda **eğitilmiş** bir model konur.

```python
TAM = sina(S, MODEL, OZ_EGITIM)
print(f"{'pencere':<9}{'oznitelik farkli':>18}{'isabet':>9}{'sapma':>9}")
for p in (1, 2, 3, 6, None):
    ozp = oznitelikler(S, pencere=p)
    print(f"{('tam' if p is None else p):<9}"
          f"{sum(1 for a in ANAHTAR if ozp[a] != OZ_EGITIM[a]):>18}"
          f"{sina(S, MODEL, ozp):>9.4f}{TAM - sina(S, MODEL, ozp):>9.4f}")

SUNUM_MODELI = egit(S, OZ_SUNUM)
print("\nsunum yolunda egitilen model:", SUNUM_MODELI["agirlik"],
      " egitim:", SUNUM_MODELI["egitim_isabeti"],
      " sunum yoluyla sinama:", f"{sina(S, SUNUM_MODELI, OZ_SUNUM):.4f}")
```

```
pencere    oznitelik farkli   isabet    sapma
1                       600   0.7800   0.0267
2                       600   0.8067   0.0000
3                       600   0.8067   0.0000
6                       600   0.8067   0.0000
tam                       0   0.8067   0.0000

sunum yolunda egitilen model: (0.6, 0.0, 0)  egitim: 0.7925  sunum yoluyla sinama: 0.8067
```

Sapma **pencere 2'de tümüyle kapanıyor** ve daha geniş pencerelerde açılmıyor. Bu, sorunun
"geçmişin kısalığı" değil **sıfıra yakınlığı** olduğunu söyler. İkinci sütun bunu keskinleştirir —
pencere 2, 3 ve 6'da öznitelik **600 satırın 600'ünde hâlâ farklı**, buna karşın isabet birebir
aynı. Sapmanın büyüklüğü öznitelik değerlerinin farkıyla değil, o farkın **kaç satırı eşiğin öbür
yanına taşıdığıyla** ölçülür.

Son satır ikinci bir onarım gösterir. Model kısa pencereli sunum yolunun kendisiyle eğitildiğinde
farklı bir ağırlık buluyor `(0.6, 0.0, 0)` ve aynı kısa yolla sınandığında yine **0,8067**
veriyor. Tutarsızlık kalktığı anda sayı geri geliyor; hangi yolun seçildiği değil, **eğitimle
sunumun aynı yolu kullanması** belirleyicidir.

#### Öznitelik Deposu ve Bedeli

**Öznitelik deposu (feature store)** bu tutarsızlığı bir kural yerine bir **yapıyla** kapatır:
öznitelik bir kez hesaplanır, `(abone, dönem)` anahtarıyla yazılır, hem eğitim hem çıkarım aynı
anahtardan **okur**. İki hesabın uyuştuğunu kimse denetlemek zorunda değildir, çünkü ortada iki
hesap yoktur. Dördüncü blok kapanan sapmanın yanına deponun kendi kalemlerini yazar.

```python
# Oznitelik deposu: deger BIR KEZ hesaplanir , (abone , donem) anahtariyla yazilir ,
# hem egitim hem sunum AYNI anahtardan okur.
DEPO = oznitelikler(S)
ALAN = ("abone_ort", "bolge_ort", "isyeri")


def depodan(anahtar):
    return DEPO[anahtar]


son = SINAMA[-1]
print("depo anahtari:", len(DEPO), " alan:", len(ALAN), " saklanan deger:", len(DEPO) * len(ALAN))
print("sunumun bir donemde okudugu:", sum(1 for a in DEPO if a[1] == son), "anahtar ,",
      sum(1 for a in DEPO if a[1] == son) * len(ALAN), "deger")
print("model kolunun asamasi:", 3, "->", 4, " oznitelik kuralinin yasadigi yer:", 2, "->", 1)
print("oznitelik kodu degistiginde yeniden yazilan deger:", len(DEPO) * len(ALAN))

for ad, oz in (("iki yol , iki hesap", OZ_SUNUM), ("tek depo , tek hesap", DEPO)):
    print(f"{ad:<22} sunum isabeti: {sina(S, MODEL, oz):.4f}"
          f"  sapma: {TAM - sina(S, MODEL, oz):.4f}")
print("depodan okunan deger egitimdekiyle ayni mi:",
      all(depodan(a) == OZ_EGITIM[a] for a in ANAHTAR))
```

```
depo anahtari: 2200  alan: 3  saklanan deger: 6600
sunumun bir donemde okudugu: 200 anahtar , 600 deger
model kolunun asamasi: 3 -> 4  oznitelik kuralinin yasadigi yer: 2 -> 1
oznitelik kodu degistiginde yeniden yazilan deger: 6600
iki yol , iki hesap    sunum isabeti: 0.7800  sapma: 0.0267
tek depo , tek hesap   sunum isabeti: 0.8067  sapma: 0.0000
depodan okunan deger egitimdekiyle ayni mi: True
```

Kazanç tek satırdır: sapma **0,0267'den 0,0000'a** iner ve depodan okunan her değer eğitimdekiyle
birebir aynıdır. Bedel dört kalemdir. Hattın modele bakan kolu **üç aşamadan dörde** çıkar, çünkü
öznitelik yazma ayrı bir adımdır. **6600 değer** saklanır, oysa bir dönemlik çıkarım bunların
yalnız **600'ünü** okur; kalanı eğitimin geçmişi görebilmesi içindir. Öznitelik kuralı değiştiğinde
**6600 değerin tamamı** yeniden yazılmalıdır, yoksa depo eski kuralla yeni kuralı yan yana taşır.
Dördüncüsü sayılamaz ama gerçektir: sunum artık deponun yazılmış olmasına bağlıdır.

Bu takas kursun kuralının karşılığıdır. Depo yokken 0,8067 gerçekten üretilmiş bir sayıydı, ama
**hangi yolla** üretildiği yazılı olmadığı için sunumda geri gelmedi. Yeniden üretilemeyen bir
sayı bir ölçüm değil, bir anıdır.

#### Özet

- Öznitelik ham satır değil, ham satırdan bir kuralla üretilen sayıdır; kural "geçmiş nereden
  başlıyor" sorusunu sessizce yanıtlar ve eğitimle sunum ona farklı yanıt verebilir.
- Aynı ağırlık üçlüsüyle isabet eğitim yoluyla **0,8067**, sunum yoluyla **0,7800**; model hiç
  değişmemiştir ve **0,0267**'lik eğitim–sunum sapmasının tamamı öznitelik yolundan gelir.
- Sapma 600 satırın 600'ünde özniteliği değiştirse de tahmini yalnız **22** satırda çeviriyor:
  19 bozulma, üç düzelme, net 16 satır, yani 16/600.
- Etiket dengesizdir (`{0: 1752, 1: 448}`, taban oranı 0,7964, sınamada 0,8067) ve 0,8067 puanlayan
  model hiçbir satıra "yüksek" demiyor; isabet tek başına bunu gizler.
- Sapma pencere 2'de tümüyle kapanıyor; öznitelik deposu aynı sonucu tek hesapla verir, bedeli
  6600 saklanan değer, dörde çıkan aşama ve kural değişince tamamının yeniden yazılmasıdır.

#### Sonraki Adım

Öznitelik deposu bir kalemi sabitledi: özniteliğin **hangi kodla** hesaplandığı artık tek yerde
duruyor. Ama sabitlenen yalnızca oydu. Aynı iş yeniden koşturulduğunda okunan veri kesitinin,
sınama bölünmesinin, aranan ızgaranın ve sayıların kaç basamağa yuvarlandığının değişmediğini
söyleyen hiçbir kayıt yok ve bunlardan herhangi birinin oynaması sonucu değiştirebilir. Sonraki
ders bu beş kalemi tek tek sayar: kaçının sabitlenmesinin üç koşumu **birebir** aynı yaptığını ve
katkısı ölçülemeyen bir sabitlemenin ne tür bir güvence verdiğini ölçer.

### Ortam Sabitleme

Source: https://academia.sh/tr/kurslar/mlops-temelleri/ortam-sabitleme

Bir koşumu ikinci kez aynı sonuca ulaştıran kalemlerin tek tek sayılması: sabitlemesiz kurulumda üç koşum aynı ağırlığı bulduğu hâlde 0,8067, 0,7955 ve 0,8078 gibi üç ayrı sınama isabeti veriyor, çünkü okunan satır, öznitelik penceresi, yuvarlama basamağı, aranan ızgara ve sınama bölünmesi her koşumda oynuyor. Beş kalemden üçü sabitlendiğinde üç koşum birebir aynı çıkıyor, ızgara ile yuvarlama sabitlenmese de sonuç değişmiyor ve teker teker sınandığında katkısı ölçülebilen yalnız iki kalem kalıyor. Sabitlemenin kendi bedeli on beş kalem kaydı, on sekiz fazladan aday değerlendirmesi ve altmış fazladan satır okumasıdır.

Öznitelik deposu bir kalemi tek yere indirdi ve o kalemin isabete kaç puan taşıdığı ölçüldü. Ama
bir koşumun sonucunu belirleyen tek şey öznitelik kodu değildir. Aynı iş ikinci kez
koşturulduğunda okunan veri kesiti, sınamaya ayrılan dönemler, aranan ağırlık ızgarası ve ara
sayıların kaç basamağa yuvarlandığı da değişmiş olabilir. Hiçbiri kayıt altında değildir, hiçbiri
hata sayılmaz ve hepsi birden sonucu oynatır.

**Ortam sabitleme (environment pinning)** bu kalemlerin her birini bir koşumdan öbürüne değişmez
tutmaktır. Bu ders **sabitlemeyi (pinning)** bir alışkanlık olarak değil bir ölçü olarak ele alır
ve üç soru sorar: hangi kalemin sabitlenmesi üç koşumu **birebir** aynı yapıyor, sabitlemenin
kendi bedeli ne ve katkısı gösterilemeyen bir sabitleme ne söylemiş oluyor.

- **YU49.** Kaynak, hattın ilk üç aşaması ve eşik tabanlı model **kurgudur**. Tohum **20260218**,
  çıktı koşumdan bağımsızdır.
- **YU50.** **Sabitleme**, bir kalemin koşumdan koşuma aynı kalmasını güvence altına almaktır.
  Burada beş kalem sayılır: veri sürümü, öznitelik kodu, sınama bölünmesi, ızgara ve yuvarlama.
  **Hiçbir donanım, çalışma zamanı ya da bağımlılık adı yazılmaz**; kalemler türleriyle anılır.
- **YU51.** **Tur**, sabitlenmemiş kalemleri belirli bir yönde oynatan sayıdır ve üç tur aynı işin
  üç ayrı koşumunu temsil eder. Tur bir rastgelelik değil, yeniden üretilebilir bir kaydırmadır.
- **YU52.** Taban çizgisi **sabitlemesiz kurulumdur**: beş kalemin hiçbiri sabit değildir ve
  koşumun neyi okuduğunu söyleyen hiçbir kayıt yoktur.
- **YU53.** Etiket dengesizdir: `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**, sınama kümesinde
  **0,8067**. İsabet tek başına okunduğunda yanıltır.
- **YU54.** Koşum değişkenliği ve ayar bütçesi Denetimli Öğrenme kursunda ölçüldü ve
  **tekrarlanmaz**; burada sayılan tek şey **hangi kalemin sabitlenmesinin koşumu aynılaştırdığıdır**.
- **YU55.** Öznitelik penceresinin etkisi bir önceki derste ölçüldü ve burada **girdi** olur:
  pencere 1'de isabet 0,7800, pencere 2 ve üstünde 0,8067.
- **YU56.** Çözünürlük: tam sınama kümesi **600 satır**, adım **1/600 = 0,0017**. Bu derste
  koşumlar arası en geniş fark **0,0123**, yani adımın yedi katından büyük ve ölçülmüştür.

#### Sabitlenmemiş Kurulumun Üç Sayısı

Bir koşumun sonucu üç katmandan gelir: hangi veriyi okuduğu, veriyi hangi kodla işlediği ve sonucu
hangi kümede ölçtüğü. Her biri kayıt altına alınmadığında sessizce kayabilir ve kaymanın belirtisi
bir hata iletisi değil **başka bir sayıdır**. Aşağıdaki blok kaynağı, hattın modele bakan ilk üç
aşamasını, kurgu modeli ve beş sabitleme kalemini kurar; `kosum` işlevi sabitlenmeyen her kalemi
tur numarasına göre oynatır.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin ilk uc asamasi ve esik tabanli
# model ders icinde tanimlanir; gercek bir ogrenme kitapligi, calisma zamani
# ya da bagimlilik yoneticisi 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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]        # 2026-02 .. 2026-09
SINAMA = DONEM[9:]         # 2026-10 .. 2026-12
ESIK = 40                  # bu m3 uzerindeki tuketim "yuksek" sayilir


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """pencere=None -> tam gecmis; basamak -> ara ortalamalarin yuvarlanmasi."""
    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)]


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


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)


# ---- Yeniden uretilebilirlik: bes sabitleme kalemi.
# Sabitlenmeyen her kalem tur'a gore oynar; sabitlenen kalem her turda ayni kalir.
KALEM = ("veri_surumu", "oznitelik_kodu", "bolunme", "izgara", "yuvarlama")


def ayar(sabit, tur):
    return {"satir_kirpma": 0 if "veri_surumu" in sabit else 20 * tur,
            "pencere": None if "oznitelik_kodu" in sabit else (None if tur == 0 else 12 - tur),
            "basamak": 4 if "yuvarlama" in sabit else max(4 - tur, 0),
            "aday": len(IZGARA) if "izgara" in sabit else len(IZGARA) - 6 * tur,
            "donemler": SINAMA if "bolunme" in sabit else DONEM[9 + (tur % 2):]}


def kosum(kayitlar, sabit, tur=0):
    kay = kayitlar if "veri_surumu" in sabit else kayitlar[: len(kayitlar) - 20 * tur]
    pencere = None if "oznitelik_kodu" in sabit else (None if tur == 0 else 12 - tur)
    basamak = 4 if "yuvarlama" in sabit else max(4 - tur, 0)
    izgara = IZGARA if "izgara" in sabit else IZGARA[: len(IZGARA) - 6 * tur]
    donemler = SINAMA if "bolunme" in sabit else DONEM[9 + (tur % 2):]
    oz = oznitelikler(kay, pencere, basamak)
    m = egit(kay, oz, izgara)
    return {"agirlik": m["agirlik"], "egitim": m["egitim_isabeti"],
            "sinama": sina(kay, m, oz, donemler=donemler)}
```

İkinci blok taban çizgisini koşturur: hiçbir kalem sabit değil, üç tur, üç sonuç.

```python
import collections

S = satirlar()
dag = collections.Counter(etiket(k) for k in S)
print("satir:", len(S), " etiket dagilimi:", dict(sorted(dag.items())),
      " taban sinif orani:", round(dag[0] / len(S), 4),
      " sinamada:", round(sum(1 for k in S if k["donem"] in SINAMA and etiket(k) == 0)
                          / sum(1 for k in S if k["donem"] in SINAMA), 4))
print("sabitleme kalemi:", len(KALEM), "->", ", ".join(KALEM))

print(f"\n{'tur':<5}{'satir':>7}{'pencere':>9}{'basamak':>9}{'aday':>6}"
      f"{'sinama satiri':>15}{'agirlik':>18}{'sinama':>9}")
TABAN = []
for t in range(3):
    a, r = ayar(set(), t), kosum(S, set(), t)
    kalan = len(S) - a["satir_kirpma"]
    TABAN.append(r["sinama"])
    print(f"{t:<5}{kalan:>7}{str(a['pencere'] or 'tam'):>9}{a['basamak']:>9}{a['aday']:>6}"
          f"{sum(1 for k in S[:kalan] if k['donem'] in a['donemler']):>15}"
          f"{str(r['agirlik']):>18}{r['sinama']:>9.4f}")
print("uc kosum birebir ayni mi:", len(set(TABAN)) == 1, " farkli sonuc:", len(set(TABAN)),
      " en genis fark:", round(max(TABAN) - min(TABAN), 4))
```

```
satir: 2200  etiket dagilimi: {0: 1752, 1: 448}  taban sinif orani: 0.7964  sinamada: 0.8067
sabitleme kalemi: 5 -> veri_surumu, oznitelik_kodu, bolunme, izgara, yuvarlama

tur    satir  pencere  basamak  aday  sinama satiri           agirlik   sinama
0       2200      tam        4    27            600     (0.6, 0.4, 0)   0.8067
1       2180       11        3    21            396     (0.6, 0.4, 0)   0.7955
2       2160       10        2    15            588     (0.6, 0.4, 0)   0.8078
uc kosum birebir ayni mi: False  farkli sonuc: 3  en genis fark: 0.0123
```

Üç koşum **üç ayrı sayı** veriyor ve en geniş fark 0,0123, çözünürlüğün yedi katından büyük. Asıl
söylenmesi gereken **ağırlık sütunudur**: üç koşum da `(0.6, 0.4, 0)` üçlüsünü buluyor. Öğrenilen
model aynı; değişen yalnızca **o modelin hangi sayıyla rapor edildiğidir**. Birinci koşumun
0,8067'si ile üçüncünün 0,8078'i aynı modelin iki ayrı anısıdır ve ikisi de savunulabilir görünür,
çünkü hangi kesitte ölçüldükleri yazılı değildir.

`sinama satiri` sütunu bunu tamamlar. Bölünme sabit olmadığı için ikinci koşum yalnız **396**
satırda ölçülüyor; onun adımı 1/396 = 0,0025'tir. Üç sayı yalnızca farklı değil, **farklı paydayla
üretilmiş** sayılardır. Etiket dengesizliği yanlış okumayı büyütür: sınamada taban sınıf oranı
0,8067 olduğu için üç sayının üçü de çoğunluk sınıfını söyleyen bir kurulumun bandındadır.

#### Kaç Kalem Yeter

Üçüncü blok kalemleri listedeki sırayla teker teker sabitler ve her adımda üç koşumu yeniden
başlatır. Sorulan şey tek: eklenen kalem üç sonucu **birebir** aynı yapıyor mu.

```python
def uc_kosum(sabit):
    return [kosum(S, set(sabit), t)["sinama"] for t in range(3)]


print(f"{'sabitlenen':<12}{'eklenen kalem':<17}{'uc kosum':>28}{'ayni mi':>9}")
for n in range(len(KALEM) + 1):
    r = uc_kosum(KALEM[:n])
    print(f"{n:<12}{('-' if n == 0 else KALEM[n - 1]):<17}"
          f"{'[' + ', '.join(f'{x:.4f}' for x in r) + ']':>28}"
          f"{('evet' if len(set(r)) == 1 else 'hayir'):>9}")
```

```
sabitlenen  eklenen kalem                        uc kosum  ayni mi
0           -                    [0.8067, 0.7955, 0.8078]    hayir
1           veri_surumu          [0.8067, 0.7950, 0.8067]    hayir
2           oznitelik_kodu       [0.8067, 0.7950, 0.8067]    hayir
3           bolunme              [0.8067, 0.8067, 0.8067]     evet
4           izgara               [0.8067, 0.8067, 0.8067]     evet
5           yuvarlama            [0.8067, 0.8067, 0.8067]     evet
```

Aynılık **üçüncü kalemde** geliyor: veri sürümü, öznitelik kodu ve bölünme sabitlendiğinde üç koşum
`[0,8067, 0,8067, 0,8067]` veriyor. Dördüncü ve beşinci kalemler eklendiğinde sonuç değişmiyor,
çünkü değişecek bir şey kalmamıştır. Buradan iki okuma çıkar: **ızgara ile yuvarlamanın
sabitlenmesi bu ölçüme hiçbir şey katmıyor** ve listenin **uzunluğu** değil **içeriği**
belirleyici — iki kalem sabitlenmiş bir kurulum hâlâ üç ayrı sayı veriyor, üç kalem sabitlenmiş
olan vermiyor.

#### Katkısı Ölçülemeyen Sabitleme

Sıralı ekleme her kalemin payını ayrı göstermez, çünkü sonraki kalemler zaten aynılaşmış bir
sonucun üstüne biner. Dördüncü blok tersini yapar: beş kalemin hepsini sabitler, sonra tek bir
kalemi serbest bırakır. Bir kalemin katkısı ancak serbest bırakıldığında sonuç değiştiğinde
**ölçülmüş** sayılır. Aynı blok sabitlemenin kendi bedelini de yazar.

```python
print(f"{'sabitlenmeyen tek kalem':<24}{'uc kosum':>28}{'katkisi olculdu mu':>20}")
for kalem in KALEM:
    r = uc_kosum([k for k in KALEM if k != kalem])
    print(f"{kalem:<24}{'[' + ', '.join(f'{x:.4f}' for x in r) + ']':>28}"
          f"{('evet' if len(set(r)) > 1 else 'hayir'):>20}")

# Sabitlemenin kendi bedeli: her kosumda yazilan kalem , degerlendirilen aday , okunan satir.
bos, tam = [ayar(set(), t) for t in range(3)], [ayar(set(KALEM), t) for t in range(3)]
print(f"\n{'bedel':<24}{'sabitlemesiz':>14}{'sabitlenmis':>13}")
print(f"{'yazilan kalem kaydi':<24}{0:>14}{len(KALEM) * 3:>13}")
print(f"{'degerlendirilen aday':<24}{sum(a['aday'] for a in bos):>14}"
      f"{sum(a['aday'] for a in tam):>13}")
print(f"{'okunan satir':<24}{sum(len(S) - a['satir_kirpma'] for a in bos):>14}"
      f"{sum(len(S) - a['satir_kirpma'] for a in tam):>13}")
print(f"{'farkli sonuc':<24}{len(set(uc_kosum([]))):>14}"
      f"{len(set(uc_kosum(KALEM))):>13}")
```

```
sabitlenmeyen tek kalem                     uc kosum  katkisi olculdu mu
veri_surumu                 [0.8067, 0.8064, 0.8078]                evet
oznitelik_kodu              [0.8067, 0.8067, 0.8067]               hayir
bolunme                     [0.8067, 0.7950, 0.8067]                evet
izgara                      [0.8067, 0.8067, 0.8067]               hayir
yuvarlama                   [0.8067, 0.8067, 0.8067]               hayir

bedel                     sabitlemesiz  sabitlenmis
yazilan kalem kaydi                  0           15
degerlendirilen aday                63           81
okunan satir                      6540         6600
farkli sonuc                         3            1
```

Tek tek sınandığında katkısı ölçülebilen yalnız **iki kalem** kalıyor: veri sürümü ve bölünme.
Kalan üçü serbest bırakıldığında üç koşum hâlâ birebir aynı; bu ölçüm onların sabitlenmesinden
kazanılan hiçbir şeyi gösteremiyor. Bu, o üç sabitlemenin gereksiz olduğu anlamına gelmez, tam
olarak şu anlama gelir: verdikleri güvence **ölçülmemiş** bir güvencedir. Bir kurulumun "beş kalemi
de sabitliyoruz" demesi, o beş kalemin beşinin de sonuca dokunduğunu göstermez; gösterilmiş olan
yalnızca ikisidir.

Öznitelik kodu satırı bunu en iyi anlatan yerdir. Bu kalem serbest bırakıldığında pencere 11 ve
10'a iner ve önceki dersten bilinen süpürme, pencere 2 ve üstünde sonucun değişmediğini söyler.
Yani kalem burada **yeterince sarsılmadığı** için katkısı görünmüyor; aynı kalem pencere 1'e
düştüğünde isabeti 0,8067'den 0,7800'e taşıdığı ölçülmüştü. "Katkısı ölçülemedi" bir kalemin
özelliği değil, **o sınamanın** özelliğidir.

Bedel tablosu takasın öbür yanıdır. Sabitlenmiş kurulum koşum başına beş kalem kaydı yazar, üç
koşumda **15 kayıt**; sabitlemesiz kurulumda bu sayı sıfırdır ve sorunun kaynağı odur. Sabitleme
işten de tasarruf ettirmez: ızgara her koşumda tam boyunda kaldığı için değerlendirilen aday
**63'ten 81'e**, veri sürümü kırpılmadığı için okunan satır **6540'tan 6600'e** çıkar. Karşılığında
alınan tek şey son satırdır — üç koşum **üç ayrı sonuç** yerine **tek sonuç** verir. Kursun kuralı
bu satırın bedelini savunur: yeniden üretilemeyen bir sayı bir ölçüm değil, bir anıdır.

#### Özet

- Sabitlemesiz kurulumda üç koşum aynı ağırlık üçlüsünü bulduğu hâlde 0,8067, 0,7955 ve 0,8078
  veriyor; öğrenilen model aynı, değişen yalnızca sayının hangi kesitte ölçüldüğüdür.
- Bölünme sabit olmadığında ikinci koşum 396 satırda ölçülüyor; üç sayı farklı paydayla
  üretildiği için yan yana konmaları bile hatalıdır. Etiket dengesizliği (taban oranı 0,7964,
  sınamada 0,8067) bu yanlış okumayı büyütür.
- Beş kalemden üçü sabitlendiğinde üç koşum birebir aynı çıkıyor; ızgara ile yuvarlama
  sabitlenmese de sonuç değişmez, yani sabitleme listesinin uzunluğu değil içeriği belirleyicidir.
- Kalemler tek tek serbest bırakıldığında katkısı ölçülebilen yalnız veri sürümü ve bölünme kalır;
  kalan üçü **ölçülmemiş bir güvence** verir ve bu, sınamanın sarsma gücüyle ilgilidir.
- Sabitlemenin bedeli üç koşumda 15 kalem kaydı, 63 yerine 81 aday değerlendirmesi ve 6540 yerine
  6600 satır okumasıdır; karşılığında üç ayrı sonuç tek sonuca iner.

#### Sonraki Adım

Beş kalem sabitlendi ve üç koşum artık aynı sayıyı veriyor. Ama bu sayıyı üreten koşumun
**hangisi** olduğu hâlâ hiçbir yerde yazmıyor. Elde ağırlık üçlüsünden başka bir şey taşımayan bir
model duruyor ve üç koşum da aynı üçlüyü bulduğu için o model kendisini üreten koşumu işaret
etmiyor. Bir modelin denenmekte mi, sınanmakta mı yoksa iş görmekte mi olduğu ve o aşamaya ne
zaman taşındığı da aynı boşluktadır. Sonraki ders bu boşluğu sayar: bir aşamadaki modelin hangi
koşumdan geldiğini bulmak için kaç kayda bakıldığını, kayıt defteriyle bu sayının kaça indiğini ve
aynı sonucu veren koşumların birbirinden nasıl ayrıldığını ölçer.

### Model Kayıt Defteri

Source: https://academia.sh/tr/kurslar/mlops-temelleri/model-kayit-defteri

Bir modelin yaşam döngüsü aşamalarının ve her aşama geçişinin hangi koşuma bağlandığının sayılması: on altı koşumluk bir kayıtta yalnız iki ayrı ağırlık üçlüsü çıkıyor, üretimdeki üçlüyü on iki koşum birebir taşıyor ve bu on iki koşum kendi sonucunu 0,7950 ile 0,8067 gibi iki ayrı sayıyla bildiriyor. Kayıt defteri olmadan üretimdeki modelin hangi koşumdan geldiğini bulmak için on altı kaydın tamamına bakılıyor ve arama on iki adaydan aşağı inmiyor, geri alınacak önceki üretim koşumu ise hiç bulunamıyor; dört aşama ve beş geçişi koşum kimliğine bağlayan defterle iki sorunun ikisi de tek kayıt okunarak yanıtlanıyor ve defterin bedeli yirmi bir kayıttır.

Beş kalem sabitlendi ve üç koşum artık aynı sayıyı veriyor. Yeniden üretilebilirlik sorusunun
yanıtı budur, ama bir soruyu daha açar: elde duran model **hangi koşumdan** geldi. Eğitilmiş bir
modelin taşıdığı tek şey ağırlıklarıdır; ağırlıklar kendilerini üreten veri sürümünü, öznitelik
penceresini ya da sınama bölünmesini içlerinde taşımaz.

Bu ders o boşluğu ölçer. Bir modelin denenmekte mi, sınanmakta mı yoksa iş görmekte mi olduğu
bilgisi ve o duruma **hangi koşumla** gelindiği, modelin dosyasında değil ayrı bir kayıtta durur.
**Model kayıt defteri (model registry)** tam olarak bu kayıttır: aşamaları ve aşamalar arasındaki
geçişleri koşum kimliğine bağlar. Ölçülecek olan, o bağın bir soruya yanıt verirken kaç kayıt
okuttuğudur.

- **YU57.** Kaynak, hattın ilk üç aşaması, kurgu model ve kayıt defteri **kurgudur**; gerçek bir
  öğrenme kitaplığı ya da kayıt defteri ürünü çağrılmaz. Tohum **20260218**.
- **YU58.** **Koşum**, belirli ayarlarla yürütülmüş tek bir eğitim işidir ve bir kimlik taşır.
  **Model** o koşumun ürettiği ağırlık üçlüsüdür ve kimlik taşımaz.
- **YU59.** **Aşama**, modelin yaşam döngüsündeki durumudur. Burada dördü sayılır: `aday`,
  `sinamada`, `uretimde`, `arsivde`. **Geçiş**, bir modeli bir aşamadan öbürüne taşıyan kayıttır ve
  **bir koşum kimliğine bağlıdır**.
- **YU60.** Taban çizgisi **deftersiz kurulumdur**: koşumlar bir yerde kayıtlıdır, ama hiçbir aşama
  bir koşuma bağlı değildir. Bir aşamadaki modelden koşuma gitmenin tek yolu **ağırlığı
  eşleştirmektir**.
- **YU61.** Etiket dengesizdir: `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**, sınama kümesinde
  **0,8067**. İsabet tek başına okunduğunda yanıltır; bu derste iki koşum aynı modeli taşıyıp
  farklı sayı bildirecektir.
- **YU62.** Koşum kaydının kendisi ve veri sürümleme bu konunun önceki derslerinde ele alındı;
  burada eklenen tek şey **aşama geçişinin koşuma bağlanmasıdır**. Soyağacı yordamı Veri
  Mühendisliğine Giriş kursunda ölçüldü ve **tekrarlanmaz**.
- **YU63.** Kayıt defteri **on altı koşumluk** bir kayıt üzerinde kurulur. Koşumlar dört öznitelik
  penceresi, iki ızgara boyu ve iki sınama bölünmesinin birleşimidir.
- **YU64.** Çözünürlük: tam sınama kümesi **600 satır**, adım **1/600 = 0,0017**. Bu derste aynı
  ağırlığın bildirdiği iki sayı arasındaki fark **0,0117**, yani adımın altı katından büyük.

#### Modelin Kimliği Yoktur

Eğitilmiş bir model bir yordamın çıktısıdır ve çıktı, kendisini üreten yordamı içermez. Burada
model üç sayıdan ibarettir; gerçek bir kurulumda çok daha büyük olur, ama durum değişmez. Modelin
içinde hangi veriyle eğitildiği, hangi öznitelik penceresinin seçildiği ya da sınamanın hangi
dönemler üzerinde yapıldığı yazmaz. Bu bilgiyi taşıyan tek şey **koşum kaydıdır** ve model ile
koşum arasındaki bağ ayrıca kurulmadıkça yoktur.

Bağ kurulmadığında yapılabilecek tek şey **eşleştirmedir**: elde duran ağırlıkları koşum kaydındaki
ağırlıklarla karşılaştırıp hangisinin tuttuğuna bakmak. Bu yöntemin sessiz bir varsayımı vardır —
her koşumun farklı bir model ürettiği. Aşağıdaki bloklar o varsayımı sınayacaktır. İlk blok kaynağı,
hattın modele bakan ilk üç aşamasını ve kurgu modeli kurar.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hattin ilk uc asamasi ve esik tabanli
# model ders icinde tanimlanir; gercek bir ogrenme kitapligi ya da model kayit
# defteri urunu cagrilmaz. Defter de ders icinde bir kayit listesi olarak kurulur.
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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM = DONEM[1:9]        # 2026-02 .. 2026-09
SINAMA = DONEM[9:]         # 2026-10 .. 2026-12
ESIK = 40                  # bu m3 uzerindeki tuketim "yuksek" sayilir


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


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """pencere=None -> tam gecmis; pencere=n -> son n donem."""
    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)]


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


def egit(kayitlar, oz, izgara=None, esik=ESIK, donemler=None):
    izgara = IZGARA if izgara is None else izgara
    donemler = EGITIM if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    en_iyi, en_iyi_w = -1.0, None
    for w in izgara:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > esik else 0) == etiket(k))
        o = d / len(kume)
        if o > en_iyi:
            en_iyi, en_iyi_w = o, w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(izgara), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz, esik=ESIK, donemler=None):
    donemler = SINAMA if donemler is None else donemler
    kume = [k for k in kayitlar if k["donem"] in donemler]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > esik else 0)
            == etiket(k))
    return round(d / len(kume), 4)
```

#### Aynı Ağırlığı Üreten On İki Koşum

İkinci blok on altı koşumluk bir kayıt üretir. Koşumlar birbirinden gerçekten farklıdır: dört ayrı
öznitelik penceresi, iki ayrı ızgara boyu ve iki ayrı sınama bölünmesi denenmiştir. Sonra kayıt
üretilen ağırlık üçlüsüne ve bildirilen sınama isabetine göre gruplanır.

```python
import collections

S = satirlar()
dag = collections.Counter(etiket(k) for k in S)
print("satir:", len(S), " etiket dagilimi:", dict(sorted(dag.items())),
      " taban sinif orani:", round(dag[0] / len(S), 4),
      " sinamada:", round(sum(1 for k in S if k["donem"] in SINAMA and etiket(k) == 0)
                          / sum(1 for k in S if k["donem"] in SINAMA), 4))

# Kosum kaydi: dort oznitelik penceresi x iki izgara boyu x iki sinama bolunmesi.
OZ = {p: oznitelikler(S, p) for p in (None, 1, 2, 3)}
KAYIT = []
for p in (None, 1, 2, 3):
    for aday in (27, 21):
        for don in (SINAMA, DONEM[10:]):
            m = egit(S, OZ[p], IZGARA[:aday])
            KAYIT.append({"kimlik": f"K{len(KAYIT) + 1:02d}", "pencere": p, "aday": aday,
                          "donem": len(don), "agirlik": m["agirlik"],
                          "sinama": sina(S, m, OZ[p], donemler=don)})

grup = {}
for k in KAYIT:
    grup.setdefault((k["agirlik"], k["sinama"]), []).append(k["kimlik"])
print("\nkosum:", len(KAYIT), " farkli agirlik uclusu:",
      len(set(k["agirlik"] for k in KAYIT)), " farkli (agirlik , sinama):", len(grup))
print(f"{'agirlik':<16}{'sinama':>8}{'kosum':>7}   kimlikler")
for (w, s), ks in grup.items():
    print(f"{str(w):<16}{s:>8.4f}{len(ks):>7}   {','.join(ks)}")
```

```
satir: 2200  etiket dagilimi: {0: 1752, 1: 448}  taban sinif orani: 0.7964  sinamada: 0.8067

kosum: 16  farkli agirlik uclusu: 2  farkli (agirlik , sinama): 4
agirlik           sinama  kosum   kimlikler
(0.6, 0.4, 0)     0.8067      6   K01,K03,K09,K11,K13,K15
(0.6, 0.4, 0)     0.7950      6   K02,K04,K10,K12,K14,K16
(0.6, 0.0, 0)     0.8067      2   K05,K07
(0.6, 0.0, 0)     0.7950      2   K06,K08
```

On altı ayrı koşum yalnız **iki ayrı ağırlık üçlüsü** üretiyor. Eşleştirmenin varsayımı burada
çöker: elde `(0.6, 0.4, 0)` duran biri, koşum kaydının tamamını okusa bile **on iki aday** bulur ve
arama daha aşağı inmez. Bu bir kusur değil, aramanın doğal sonucudur — sonlu bir ızgarada gezinen
her eğitim işi aynı adaya varabilir ve çoğu zaman varır.

Tablonun taşıdığı ikinci sayı daha ağırdır. Aynı `(0.6, 0.4, 0)` üçlüsünü taşıyan on iki koşumun
altısı sonucunu **0,8067**, altısı **0,7950** diye bildiriyor. İkisi de doğrudur; farkı yapan şey
modelin kendisi değil, koşumun sınamayı **kaç dönem üzerinde** yaptığıdır. Yani üretimdeki model
için "isabeti neydi" diye sorulduğunda, ağırlıklardan gidilerek verilecek yanıt 0,0117 genişliğinde
bir aralıktır ve bu çözünürlüğün altı katından büyüktür. Etiket dengesizliği bunu daha da
sakıncalı kılar: sınamada taban sınıf oranı 0,8067 olduğu için iki sayı da çoğunluk sınıfının
bandındadır ve aradaki farkı bir başarım farkı sanmak yanıltıcıdır.

#### Aşamalar ve Geçişlerin Bağlandığı Koşum

Kayıt defteri modelin dosyasına dokunmaz; onun yerine **ayrı bir kayıt** tutar ve bu kayıt bir
aşamayı bir koşum kimliğine bağlar. Dört aşama ile bunlar arasındaki geçişler dizisi defterin
içeriğidir; her geçiş satırı hangi koşumun hangi aşamaya taşındığını söyler. Üçüncü blok defteri
kurar ve iki soruyu iki kurulumda yanıtlar.

```python
# Model kayit defteri: dort asama ve asamalar arasi gecisler. Her gecis BIR KOSUMA baglidir.
ASAMA = ("aday", "sinamada", "uretimde", "arsivde")
GECIS = [("K03", "aday", "sinamada"), ("K03", "sinamada", "uretimde"),
         ("K11", "aday", "sinamada"), ("K11", "sinamada", "uretimde"),
         ("K03", "uretimde", "arsivde")]
GUNCEL = {}
for kim, _, hedef in GECIS:
    GUNCEL[hedef] = kim
URETIMDEKI = next(k for k in KAYIT if k["kimlik"] == GUNCEL["uretimde"])["agirlik"]

esleyen = [k for k in KAYIT if k["agirlik"] == URETIMDEKI]
skor = sorted(set(k["sinama"] for k in esleyen))
print("uretimdeki agirlik:", URETIMDEKI, " defterdeki gecis:", len(GECIS))
print(f"\n{'soru':<34}{'defterisiz bakilan':>20}{'defterisiz yanit':>19}"
      f"{'defterli':>10}{'yanit':>7}")
print(f"{'bu agirlik hangi kosumdan geldi':<34}{len(KAYIT):>20}"
      f"{str(len(esleyen)) + ' aday':>19}{1:>10}{GUNCEL['uretimde']:>7}")
print(f"{'geri alinacak: onceki uretim':<34}{len(KAYIT):>20}{'yok':>19}"
      f"{1:>10}{GUNCEL['arsivde']:>7}")
print("\nayni agirligi tasiyan kosum:", len(esleyen), " bildirdikleri farkli sinama:",
      len(skor), "->", " .. ".join(f"{x:.4f}" for x in skor),
      " fark:", round(skor[-1] - skor[0], 4))
print("defterin bedeli: kosum kaydi", len(KAYIT), "+ gecis kaydi", len(GECIS),
      "=", len(KAYIT) + len(GECIS), "kayit ;", "asama:", len(ASAMA))
```

```
uretimdeki agirlik: (0.6, 0.4, 0)  defterdeki gecis: 5

soru                                defterisiz bakilan   defterisiz yanit  defterli  yanit
bu agirlik hangi kosumdan geldi                     16            12 aday         1    K11
geri alinacak: onceki uretim                        16                yok         1    K03

ayni agirligi tasiyan kosum: 12  bildirdikleri farkli sinama: 2 -> 0.7950 .. 0.8067  fark: 0.0117
defterin bedeli: kosum kaydi 16 + gecis kaydi 5 = 21 kayit ; asama: 4
```

Birinci satır aşama geçişinin ne kazandırdığını gösterir. Deftersiz kurulumda on altı kaydın
tamamına bakılır ve elde on iki aday kalır; arama bakılan kayıt sayısı kadar iş yapıp **hiç
daralmaz**. Defterli kurulumda `uretimde` aşaması bir koşum kimliğine bağlı olduğu için tek kayıt
okunur ve yanıt `K11`'dir. Kazanılan şey on beş kayıtlık bir tasarruf değildir; kazanılan şey
**tekil bir yanıttır** ve deftersiz kurulumda o yanıt kayıt sayısı ne olursa olsun elde edilemez.

İkinci satır daha keskindir. Geri alma gerektiğinde sorulan soru "önceki üretim koşumu hangisiydi"
olur ve deftersiz kurulumda bunun yanıtı yalnız zor değil, **yoktur**: eldeki tek şey şu anda
yerinde duran ağırlıklardır ve önceki modelin ağırlıkları yerine yazılmıştır. Defterde ise
`K03`'ün `uretimde` aşamasından `arsivde` aşamasına geçtiği kayıtlıdır; geri dönülecek koşum tek
kayıtta okunur. Aşamaların değeri modelin nerede olduğunu söylemelerinden çok, **nereden geldiğini
ve nereye dönüleceğini** söylemelerindedir.

Defterin bedeli son satırdadır ve mütevazıdır: on altı koşum kaydı ve beş geçiş kaydı, toplam
**21 kayıt**. Bu bedel modelin kendisine dokunmaz, ağırlıkları büyütmez, çıkarımı yavaşlatmaz;
yalnızca her aşama geçişinde bir satır yazılmasını zorunlu kılar. Yazılmadığında kaybedilen şey de
tam olarak bu dersin ölçtüğü şeydir: üretimdeki sayının kime ait olduğu.

#### Özet

- Eğitilmiş bir model yalnız ağırlıklarını taşır; kendisini üreten veri sürümünü, öznitelik
  penceresini ya da sınama bölünmesini içinde taşımaz, dolayısıyla kimliği yoktur.
- On altı ayrı koşum yalnız iki ayrı ağırlık üçlüsü üretiyor; üretimdeki üçlüyü **12 koşum** birebir
  taşıdığı için ağırlık eşleştirerek yapılan arama tekil bir yanıta hiç ulaşmıyor.
- Aynı üçlüyü taşıyan koşumlar sonuçlarını **0,7950** ve **0,8067** diye bildiriyor; aradaki 0,0117
  çözünürlüğün altı katından büyüktür ve etiket dengesizliği (taban oranı 0,7964, sınamada 0,8067)
  bu farkı başarım farkı sanmayı kolaylaştırır.
- Dört aşama ve beş geçişi koşum kimliğine bağlayan defterle "üretimdeki model hangi koşumdan
  geldi" sorusu **tek kayıtta** yanıtlanıyor, deftersiz kurulumda 16 kayıt okunup 12 aday kalıyor.
- Geri alma sorusunun deftersiz kurulumda yanıtı yoktur; defterde arşiv geçişi tek kayıtta önceki
  üretim koşumunu veriyor ve defterin toplam bedeli 21 kayıttır.

#### Sonraki Adım

Altı derste model **yeniden üretilebilir** kılındı. Koşumun kendisi kaydedildi ve sürümlendi, aynı
ada verilen iki farklı içerik ayrıldı, öznitelik iki yerde iki kez hesaplanmaktan çıkıp tek yerde
hesaplanır oldu, hangi kalemin sabitlenmesinin üç koşumu aynılaştırdığı sayıldı ve her aşama
geçişi bir koşum kimliğine bağlandı. Artık üretimde duran bir sayının kime ait olduğu ve nasıl
yeniden elde edileceği yazılıdır.

Yazılı olmayan tek şey, bu modelin **oraya nasıl gideceğidir**. Altı dersin hiçbirinde eğitimin
kendisi uçtan uca bir hat olarak kurulmadı, hiçbir kapı yeni bir veri sürümünün ya da düşen bir
ölçütün yayına çıkmasını durdurmadı ve model bir çağıranın kullanabileceği bir arayüz olarak hiç
paketlenmedi. Sonraki konu bu üç boşluğu sırayla kapatır: eğitim hattının adımlarını ve uçtan uca
yeniden koşturulabilirliğini sayar, kod, veri ve model kapılarının hangi bozulmayı durdurduğunu
ölçer ve arayüz sözleşmesi yazılmadığında kaç çağıranın kırıldığını yazar.

## Eğitim ve Dağıtım

Eğitilen bir modelin üretime çıkma yolunun adım adım sayılması: veri hazırlama, eğitim ve değerlendirme adımlarının tek hatta bağlanması ve her adımın düşürdüğü satır, kod, veri ve model kapılarının sürekli tümleştirmede ayrı ayrı ateşlemesi ve hangi kapının hangi bozulmayı durdurduğu, çalışma zamanı ile arayüz sözleşmesinin paketlemede sabitlenmesi ve sözleşme yazılmadığında çağıranın kırılması, toplu, çevrimiçi ve akış tabanlı çıkarımın gecikme sınıfı, tahmin başına bedeli ve tazelik takası, toplu isteme ile önbelleklemenin tahmin başına maliyeti düşürürken gecikmeye kattığı, kenarda çalıştırmak için modeli küçültmenin isabetten götürdüğü ve hangi görevde önce götürdüğü.

### Eğitim Hatları

Source: https://academia.sh/tr/kurslar/mlops-temelleri/egitim-hatlari

Eğitilen bir modelin üretime çıkma yolunun ilk adımı olarak eğitim hattının sayılması: altı adımın düşürdüğü satır ayrı ayrı ölçülüyor ve tüketim adımı 200 satır kaybederken kendi sayacı 0 yazıyor, öznitelik adımı hiç satır düşürmeden 200 satırı geçmişsiz bırakıyor, bölünme 1600 ile 600'ü hiç satır düşürmeden ayırıyor; etiket dağılımı 1752'ye 448 ve taban sınıf oranı 0,7964 iken sınama kümesinde model 600 satırın 600'üne 0 diyor ve isabet 0,8067 ile taban sınıf oranının birebir kendisi oluyor; geç gelen 40 satır geri doldurulduğunda eğitim satırını 1520'den 1600'e çıkaran tek kurulum uçtan uca yeniden koşan hat oluyor ve yedi kurulumun yedisi de aynı 0,8067'yi rapor ediyor.

Bu kursun ilk altı dersi modeli **yeniden üretilebilir** kıldı: koşum kaydedildi, veri ile model
sürümlendi, eğitimdeki öznitelik yolu sunumdaki yolla karşılaştırıldı, ortam kalemleri sabitlendi ve
aşama geçişleri bir deftere bağlandı. Hepsinin ortak varsayımı tek cümleyle yazılabilir: model bir
kez eğitilmiştir ve mesele onu **yeniden** eğitebilmektir. Hiçbirinde eğitilen modelin **üretime
nasıl çıkacağı** sorulmadı.

Bu ders o sorunun ilk adımını açar. Veri Mühendisliğine Giriş kursundaki hat beş aşamada 2400
satırdan 2400'e, 2200'e, 55 özet hücresine ve 5 sunum değerine iniyordu. Model o zincirin **sonuna**
bağlanmaz; **üçüncü aşamanın çıktısına**, yani 2200 tüketim satırına bağlanır. Bu dersin sorusu, o
noktadan sonra gelen adımların elle koşturulan bir dizi olmaktan çıkıp uçtan uca koşturulabilir bir
**eğitim hattına** dönüştüğünde neyin ölçülebilir hâle geldiğidir.

- **ED1.** Kaynak, hat ve model **kurgudur**; gerçek bir öğrenme kitaplığı, deney takip düzeneği,
  öznitelik deposu ya da sunum çatısı çağrılmaz. Tohum **20260218**.
- **ED2.** Eğitim hattı bu kursta **tek makinede** ele alınır; küme boyutlandırma ve bölüşüm Dağıtık
  Veri İşleme kursunda ölçüldü ve tekrarlanmaz.
- **ED3.** Adımların birbirine bağlanması, çizge çözümü ve yeniden deneme yordamı Veri Hatları ve
  Orkestrasyon kursunda ölçüldü. Burada sorulan şey sıra değil, **her adımın satıra ne yaptığı**.
- **ED4.** Etiket: tüketimi **40 m3**'ü aşan satır "yüksek" sayılır. Dağılım **dengesizdir** ve her
  derste yazılır.
- **ED5.** Eğitim dönemleri `2026-02`–`2026-09`, sınama dönemleri `2026-10`–`2026-12`.
- **ED6.** Model üç ağırlıklı bir eşiktir; eğitim, 27 adaylık bir ızgarada aramaktır.
- **ED7.** Öznitelikler **bir kez** hesaplanır ve ağırlıktan bağımsızdır.
- **ED8.** Taban çizgisi **elle koşturulan adımlardır**: ara çıktılar elde durur, yeniden koşum
  kısmidir.
- **ED9.** Çözünürlük: sınama kümesi **600 satır**, isabetin en küçük adımı **0,0017**.
- **ED10.** Süre ham milisaniye olarak yazılmaz; ölçü **satır**, **adım** ve **dönem**dir.

#### Eğitim Hattının Altı Adımı

İlk blok kaynağı, hattın ilk üç aşamasını ve modelin tarafını kurar. Hiçbiri bu derse özgü değildir:
ilk üçü veri hattından gelir, model tarafı bu kursun ortak tanımıdır.

```python
# KURGUDUR. Sayac okumalari, hat ve model ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, deney takip duzenegi ya da sunum catisi 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


# ---- Veri hattinin ilk uc asamasi
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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


# ---- Modelin tarafi
EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """pencere=None -> tam gecmis. 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 and (pencere is None or d >= i - pencere))]
        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"]


def egit(kayitlar, oz):
    kume = [k for k in kayitlar if k["donem"] in EGITIM]
    en_iyi, en_iyi_w = -1.0, None
    for w in IZGARA:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > ESIK else 0) == etiket(k))
        if d / len(kume) > en_iyi:
            en_iyi, en_iyi_w = d / len(kume), w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "aday": len(IZGARA), "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz):
    kume = [k for k in kayitlar if k["donem"] in SINAMA]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > ESIK else 0)
            == etiket(k))
    return round(d / len(kume), 4)
```

İkinci blok hattı kurar. Altı adımın ilk üçü yukarıdaki aşamalardır; kalan üçü etiketleme, öznitelik
üretimi ve bölünmedir. `hat_kostur` her adımın girdisini, çıktısını ve **kendi düşen satır sayacını**
ayrı ayrı bildirir; `bastan` değeri verildiğinde ilk adımlar saklanan çıktıdan gelir ve yalnız kalanı
yeniden koşar.

```python
def h4_etiketle(v):
    return [dict(k, y=etiket(k)) for k in v], 0


def h5_oznitelik(v):
    oz = oznitelikler(v)
    return [dict(k, **oz[(k["abone"], k["donem"])]) for k in v], 0


def h6_bolunme(v):
    return {"egitim": [k for k in v if k["donem"] in EGITIM],
            "sinama": [k for k in v if k["donem"] in SINAMA]}, 0


ADIM = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
        ("etiketle", h4_etiketle), ("oznitelik", h5_oznitelik), ("bolunme", h6_bolunme)]


def uzunluk(v):
    return len(v) if isinstance(v, list) else sum(len(x) for x in v.values())


def hat_kostur(kaynak, bastan=0, saklanan=None):
    """bastan=p ise ilk p adim saklanan ciktidan gelir , kalani yeniden kosar."""
    veri = kaynak if bastan == 0 else saklanan[ADIM[bastan - 1][0]]
    ara, iz, islenen = dict(saklanan or {}), [], 0
    for i, (ad, f) in enumerate(ADIM):
        if i < bastan:
            continue
        g = uzunluk(veri)
        veri, sayac = f(veri)
        ara[ad], islenen = veri, islenen + g
        iz.append((ad, g, uzunluk(veri), sayac))
    return veri, iz, ara, islenen


def degerlendir(bolunmus):
    kume = bolunmus["egitim"] + bolunmus["sinama"]
    oz = {(k["abone"], k["donem"]): {a: k[a] for a in ("abone_ort", "bolge_ort", "isyeri")}
          for k in kume}
    m = egit(kume, oz)
    return m, sina(kume, m, oz), oz


KAYNAK = kaynak_uret()
BOLUNMUS, IZ, ARA, ISLENEN = hat_kostur(KAYNAK)
print(f"{'adim':<11}{'girdi':>7}{'cikti':>7}{'kaybolan':>10}{'adimin sayaci':>15}")
for ad, g, c, sayac in IZ:
    print(f"{ad:<11}{g:>7}{c:>7}{g - c:>10}{sayac:>15}")
print("bolunme:", {a: len(b) for a, b in BOLUNMUS.items()},
      " gecmissiz satir:", sum(1 for k in ARA["oznitelik"] if k["abone_ort"] == 0.0))
DAGILIM = {0: 0, 1: 0}
for k in ARA["etiketle"]:
    DAGILIM[k["y"]] += 1
print("etiket dagilimi:", DAGILIM, " taban sinif orani:",
      round(DAGILIM[0] / len(ARA["etiketle"]), 4), " islenen satir:", ISLENEN)
```

```
adim         girdi  cikti  kaybolan  adimin sayaci
ayikla        2400   2400         0              0
tekille       2400   2400         0              0
tuketim       2400   2200       200              0
etiketle      2200   2200         0              0
oznitelik     2200   2200         0              0
bolunme       2200   2200         0              0
bolunme: {'egitim': 1600, 'sinama': 600}  gecmissiz satir: 200
etiket dagilimi: {0: 1752, 1: 448}  taban sinif orani: 0.7964  islenen satir: 13800
```

Tablonun son iki sütunu birbiriyle çelişir ve çelişki tek bir adımdadır. Tüketim adımının girdisi
2400, çıktısı 2200; arada **200 satır** kayboluyor. Aynı adımın kendi sayacı **0** yazıyor. İkisi de
doğrudur: sayaç yalnızca **beklenmedik** düşüşü, yani önceki dönemi bulunamayan satırı sayar. İlk
dönemin 200 satırı ise tanımı gereği düşer, çünkü tüketim iki dönem arasındaki farktır. Bir adımın
düşen satır sayacına bakmak, o adımın kaç satır kaybettiğini söylemez; **girdi ile çıktı arasındaki
farkı ayrıca ölçmek gerekir.**

İkinci çelişki öznitelik adımındadır ve ters yönlüdür. Bu adım **hiç satır düşürmez**, ama
çıktısındaki **200 satırın abone ortalaması 0,0**'dır, çünkü `2026-02` satırlarının kendilerinden
önce hiçbir dönemi yoktur. Düşen satırı sıfır olan bir adım da veriyi bozabilir; hattın sayacı bunu
görmez.

Bölünme adımı ise gerçekten hiçbir satır düşürmez: 1600 eğitim ve 600 sınama satırı toplamda 2200
eder. Uçtan uca bir koşum altı adım boyunca **13800 satır** işler.

#### Değerlendirme Adımının Ürettiği Sayı

Hattın yedinci ve sekizinci adımları eğitim ile değerlendirmedir. Üçüncü blok ikisini birden koşturur
ve çıkan sayıyı taban sınıf oranıyla yan yana koyar.

```python
MODEL, ISABET, OZTABLO = degerlendir(BOLUNMUS)
SINAMA_KUME = BOLUNMUS["sinama"]
TAHMIN = [1 if puan(OZTABLO[(k["abone"], k["donem"])], MODEL["agirlik"]) > ESIK else 0
          for k in SINAMA_KUME]
TABAN_S = sum(1 for k in SINAMA_KUME if k["y"] == 0) / len(SINAMA_KUME)
print("agirlik:", MODEL["agirlik"], " aday:", MODEL["aday"],
      " egitim satiri:", MODEL["egitim_satiri"], " egitim isabeti:", MODEL["egitim_isabeti"])
print(f"sinama isabeti: {ISABET:.4f}  sinama taban sinif orani: {TABAN_S:.4f}"
      f"  taban uzerindeki kazanc: {ISABET - TABAN_S:.4f}")
print("sinama tahmin dagilimi:", {0: TAHMIN.count(0), 1: TAHMIN.count(1)})
```

```
agirlik: (0.6, 0.4, 0)  aday: 27  egitim satiri: 1600  egitim isabeti: 0.7931
sinama isabeti: 0.8067  sinama taban sinif orani: 0.8067  taban uzerindeki kazanc: 0.0000
sinama tahmin dagilimi: {0: 600, 1: 0}
```

Hattın ürettiği sayı **0,8067**'dir ve tek başına okunduğunda iyi görünür. Yanındaki iki sayı onu
okunamaz hâle getirir. Etiket dağılımı **1752'ye 448**, yani taban sınıf oranı **0,7964**; sınama
kümesinde bu oran **0,8067**'dir. Modelin isabeti taban sınıf oranının **birebir kendisidir** ve
taban üzerindeki kazanç **0,0000**.

Son satır nedenini yazar: model 600 sınama satırının **600'üne** 0 diyor. Hiçbir satırı "yüksek"
saymıyor, dolayısıyla isabeti çoğunluk sınıfının payından ibaret. Sınama kümesinin çözünürlüğü
**0,0017**'dir; kazanç bu en küçük adımın altında bile değil, tam olarak **sıfırdır**. Bir eğitim
hattının değerlendirme adımı tek bir isabet basıyorsa, o hat ölçmüyor demektir.

#### Uçtan Uca Yeniden Koşturmak

Kaynak değişmez bir şey değildir. `2026-07` döneminde 40 abonenin okuması **geç gelir** ve sonradan
geri doldurulur. Dördüncü blok önce eksik kaynakla koşar, sonra geri doldurulmuş kaynakla hattı
farklı noktalardan devam ettirir. `bayat adim` sayısı, saklanan eski çıktıdan gelen adım sayısıdır.

```python
GEC = [k for k in KAYNAK if not (k["donem"] == "2026-07" and k["abone"] <= "A040")]
BOL1, IZ1, ARA1, _ = hat_kostur(GEC)
M1, S1, _ = degerlendir(BOL1)
print("gec gelen kaynak:", len(GEC), " tuketim ciktisi:", IZ1[2][2],
      " egitim satiri:", M1["egitim_satiri"], f" sinama isabeti: {S1:.4f}")
print()
print(f"{'bayat adim':>11}{'yeniden kosan':>14}{'islenen satir':>14}"
      f"{'egitim satiri':>14}{'egitim isabeti':>15}{'sinama isabeti':>15}")
for p in range(7):
    if p == 6:
        bol, islenen = ARA1["bolunme"], 0
    else:
        bol, _, _, islenen = hat_kostur(KAYNAK, bastan=p, saklanan=ARA1)
    m, s, _ = degerlendir(bol)
    print(f"{p:>11}{6 - p:>14}{islenen:>14}{m['egitim_satiri']:>14}"
          f"{m['egitim_isabeti']:>15.4f}{s:>15.4f}")
```

```
gec gelen kaynak: 2360  tuketim ciktisi: 2120  egitim satiri: 1520  sinama isabeti: 0.8067

 bayat adim yeniden kosan islenen satir egitim satiri egitim isabeti sinama isabeti
          0             6         13800          1600         0.7931         0.8067
          1             5         11080          1520         0.7914         0.8067
          2             4          8720          1520         0.7914         0.8067
          3             3          6360          1520         0.7914         0.8067
          4             2          4240          1520         0.7914         0.8067
          5             1          2120          1520         0.7914         0.8067
          6             0             0          1520         0.7914         0.8067
```

Geç gelen 40 satır hattın çıktısından **80 satır** götürüyor: kendileri düşüyor ve bir sonraki dönemin
40 satırı önceki dönemini bulamadığı için düşüyor. Eğitim kümesi 1600 yerine **1520** satırla
kuruluyor.

Tablo tek bir şeyi söyler. Geri doldurma yapıldıktan sonra eğitim satırını **1600**'e çıkaran tek
kurulum, bayat adımı sıfır olan, yani **kaynaktan itibaren uçtan uca yeniden koşan** hattır. Bir tek
adımın bile saklanan eski çıktıdan gelmesi yeterlidir: hat kalan beş adımı gerçekten yeniden koşsa
bile eğitim satırı **1520**'de kalır, çünkü kaybolan satırlar zaten ilk adımın çıktısında yoktu.
Kısmi koşum, işlenen satırı 13800'den 11080'e düşürerek tasarruf eder ve karşılığında **düzeltilmiş
kaynağı hiç görmez**.

Rapor edilen sayı yedi kurulumda da **0,8067**'dir. Sınama isabetine bakan hiç kimse, 1600 satırla
eğitilmiş bir modelle 1520 satırla eğitilmiş bir modeli birbirinden ayıramaz. Farkı gösteren tek yer
eğitim isabetidir ve orada da fark **0,0017**'dir. Kursun kuralı burada tam karşılığını bulur:
**yeniden üretilemeyen sayı bir ölçüm değil bir anıdır** ve bir hattın uçtan uca koşturulabilir
olması, o sayının hangi kaynaktan geldiğinin **belirlenebilir** olması demektir.

Uçtan uca koşumun bedeli de tablodadır: her koşumda **13800 satır** yeniden işlenir; tümüyle bayat
kurulumda bu sayı **0**'dır. Sabitlemek bedelsiz değildir.

#### Özet

- Eğitim hattı veri hattının **beşinci** aşamasından değil **üçüncüsünden** dallanır: model 5 sunum
  değerine değil, 2200 tüketim satırına bağlanır.
- Altı adımın girdi ve çıktısı ayrı ayrı sayıldığında tüketim adımı **200 satır** kaybediyor, ama
  adımın kendi sayacı **0** yazıyor; sayaç yalnızca beklenmedik düşüşü sayar.
- Öznitelik adımı hiç satır düşürmeden **200 satırı geçmişsiz** bırakır; düşen satırı sıfır olan
  adım da veriyi bozabilir.
- Etiket dağılımı **1752'ye 448**, taban sınıf oranı **0,7964**; sınama kümesinde **0,8067**. Model
  600 satırın 600'üne 0 diyor, isabeti **0,8067** ve taban üzerindeki kazancı **0,0000**.
- Geç gelen 40 satır eğitim kümesinden **80 satır** götürür; geri doldurmadan sonra 1600 satıra
  dönen tek kurulum bayat adımı **sıfır** olan uçtan uca koşumdur, bedeli koşum başına **13800
  işlenen satır**.
- Yedi kurulumun yedisi de aynı **0,8067**'yi rapor eder; sınama isabeti 1600 satırlık eğitimle 1520
  satırlık eğitimi ayırt edemez.

#### Sonraki Adım

Bu dersin hattı bir kez kurulduktan sonra hep aynı biçimde koşuyordu ve koştuğu her seferde bir model
üretiyordu. Üretilen modelin **yayına çıkıp çıkmayacağı** hiç sorulmadı. Geç gelen satırların
eksikliğiyle eğitilen 1520 satırlık model de, geri doldurulmuş 1600 satırlık model de aynı kolaylıkla
yayına gidebilirdi, çünkü ikisi de aynı sayıyı rapor ediyordu. Sonraki ders koşum ile yayın arasına
**kapılar** koyar: kod kapısı, veri kapısı ve model kapısı. Ölçü, Veri Mühendisliğine Giriş kursunda
tanımlanan altı bozulma sınıfının bu üç kapıya nasıl dağıldığı ve **hiçbir kapının durdurmadığı**
sınıfın hangisi olduğudur.

### Makine Öğrenmesi için Sürekli Tümleştirme

Source: https://academia.sh/tr/kurslar/mlops-temelleri/makine-ogrenmesi-icin-surekli-tumlestirme

Koşum ile yayın arasına konan kod, veri ve model kapılarının hangi bozulmayı durdurduğunun sayılması: altı bozulma sınıfından dördünü veri kapısı, üçünü model kapısı durduruyor, kod kapısı hiçbirini durdurmuyor ama öznitelik kodu değişimini üretim verisine hiç bakmadan birinci adımda yakalıyor; birim değişimi yalnız model kapısında, eğitim isabetini 0,7931'den 0,5919'a düşürerek duruyor; geç gelen satır hiçbir kapıda durmuyor çünkü eğitim satırındaki sapması tam 0,0500 ve hoşgörü katı karşılaştırmayla 0,0500; veri kapısının durdurduğu dört koşumun ikisi başvuruyla birebir aynı modeli verecekti ve sekiz koşumun sekizinde sınama isabeti 0,8067 ile taban sınıf oranının üstüne çıkamıyor.

Bir önceki dersin hattı koştuğu her seferde bir model üretiyordu ve üretilen modelin **yayına çıkıp
çıkmayacağı** hiç sorulmadı. Geç gelen satırların eksikliğiyle 1520 satır üzerinde eğitilen model de,
geri doldurulmuş 1600 satırla eğitilen model de aynı **0,8067**'yi rapor ediyordu; ikisi de aynı
kolaylıkla yayına gidebilirdi.

Bu ders koşum ile yayın arasına **kapılar** koyar. Bir kapı, bir koşumu yayına çıkmadan durduran
denetimdir. Üçü vardır ve üçü ayrı şeye bakar: **kod kapısı** yazılan koda, **veri kapısı** hattın
girdisine, **model kapısı** eğitilen modelin sayılarına. Sorunun kendisi tek cümledir: Veri
Mühendisliğine Giriş kursunda tanımlanan altı bozulma sınıfı bu üç kapıya nasıl dağılır ve
**hiçbirinin durdurmadığı** sınıf hangisidir.

- **ED11.** Kaynak, hat, model ve kapılar **kurgudur**; gerçek bir öğrenme kitaplığı ya da sürekli
  tümleştirme ürünü çağrılmaz. Tohum **20260218**.
- **ED12.** Sürekli tümleştirmenin kendi yordamı, tetikleyiciler, aşama sıralaması ve dağıtım
  biçimleri DevOps ve Platform Mühendisliği müfredatındaki Sürekli Tümleştirme ve Teslimat kursunda,
  hat orkestrasyonu ise Veri Hatları ve Orkestrasyon kursunda ölçüldü. **Tekrarlanmaz**; bu ders
  yalnız **veri ve model kapılarını** ekler.
- **ED13.** Altı bozulma sınıfının hiçbiri saldırı değildir; hepsi üretici tarafındaki kazalardır ve
  her biri **tek bir dönemi** (`2026-07`) bozar.
- **ED14.** Kapılar sırayla koşar: kod **1**, veri **2**, model **3**. Bir olayı ilk durduran kapının
  numarası, bozulmanın hattı ne kadar kirlettiğini de söyler.
- **ED15.** Kod kapısı **üretim verisini hiç görmez**; sabit, üç satırlık bir girdi üzerinde koşar.
- **ED16.** Model kapısının karşılaştırdığı **başvuru**, sağlam kaynakla koşulan modeldir; hoşgörüler
  eğitim satırında **0,0500**, eğitim isabetinde **0,0100**.
- **ED17.** Etiket dağılımı **dengesizdir**; taban sınıf oranı bu derste de yazılır.
- **ED18.** Taban çizgisi **kapısız kurulumdur**: hiçbir kapı yok, her koşum yayına çıkar.
- **ED19.** Çözünürlük: sınama kümesi **600 satır**, isabetin adımı **0,0017**; kaynak **2400 satır**,
  satır oranının adımı **0,0004**.
- **ED20.** Süre ham milisaniye olarak yazılmaz; ölçü **koşum**, **satır** ve **adım**dır.

#### Üç Kapı

İlk blok kaynağı, altı bozulma sınıfını, hattın ilk üç aşamasını ve modeli kurar. Hiçbiri bu derse
özgü değildir.

```python
# KURGUDUR. Kaynak, hat, model ve kapilar ders icinde tanimlanir; gercek bir
# ogrenme kitapligi ya da surekli tumlestirme urunu 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


# ---- Alti bozulma sinifi. Hicbiri saldiri degil , hepsi uretici tarafi kazasi.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan on bir donem saglam kalir."""
    yeni = []
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
        elif sinif == "gec_gelen":
            if k["abone"] > "A040":                  # 40 abone bu donemde hic gelmedi
                yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                 # 60 kayit iki kez geldi
                yeni.append(dict(k))
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")     # alan adi degisti
            yeni.append(y)
        elif sinif == "birim_degisti":
            yeni.append(dict(k, endeks=k["endeks"] * 1000, birim="m3"))   # etiket degismedi
        elif sinif == "eksik_bolum":
            if k["bolge"] != "kuzey":                # bir bolgenin bolumu yazilmadi
                yeni.append(k)
        elif sinif == "kayan_tip":
            yeni.append(dict(k, endeks=str(k["endeks"])))                 # sayi metne dondu
    return yeni


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):
    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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


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

    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"]


def egit(kayitlar, oz):
    kume = [k for k in kayitlar if k["donem"] in EGITIM]
    en_iyi, en_iyi_w = -1.0, None
    for w in IZGARA:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > ESIK else 0) == etiket(k))
        if d / len(kume) > en_iyi:
            en_iyi, en_iyi_w = d / len(kume), w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz):
    kume = [k for k in kayitlar if k["donem"] in SINAMA]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > ESIK else 0)
            == etiket(k))
    return round(d / len(kume), 4), round(sum(1 for k in kume if etiket(k) == 0) / len(kume), 4)


def kosum(kayit, pencere=None):
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz = oznitelikler(v, pencere)
    m = egit(v, oz)
    isabet, taban = sina(v, m, oz)
    return m, isabet, taban, len(v)
```

İkinci blok üç kapıyı tanımlar ve sekiz olayı sırayla kapılardan geçirir. Yedi olay veri tarafındadır:
sağlam koşum ve altı bozulma sınıfı. Sekizincisi kod tarafındadır: öznitelik penceresinin tam geçmiş
yerine tek döneme indirilmesi.

```python
# ---- KAPI 1: kod. Sabit mini girdi , uretim verisini hic gormez.
MINI = [{"abone": "A001", "donem": d, "bolge": "dogu", "tip": "isyeri", "m3": v}
        for d, v in (("2026-02", 30), ("2026-03", 50), ("2026-04", 10))]
BEKLENEN = ({"abone_ort": 40.0, "bolge_ort": 40.0, "isyeri": 1}, [0, 1, 0])


def kod_kapisi(pencere=None):
    oz = oznitelikler(MINI, pencere)
    return (oz[("A001", "2026-04")], [etiket(k) for k in MINI]) != BEKLENEN


# ---- KAPI 2: veri. Hattin girdisi uzerinde bes denetim.
def veri_kapisi(kayit, beklenen=2400, pay=0.05):
    ALAN = ("abone", "donem", "bolge", "endeks", "birim")
    ad = [(k["abone"], k["donem"]) for k in kayit]
    bulgu = []
    if any(any(a not in k for a in ALAN) for k in kayit):
        bulgu.append("sema")
    if any(not isinstance(k.get("endeks"), int) for k in kayit):
        bulgu.append("tip")
    if len(ad) != len(set(ad)):
        bulgu.append("tekillik")
    if abs(len(kayit) - beklenen) / beklenen > pay:
        bulgu.append("satir_sayisi")
    if set((k["bolge"], k["donem"]) for k in kayit) != set((b, d) for b in BOLGE for d in DONEM):
        bulgu.append("bolum_donem")
    return bulgu


# ---- KAPI 3: model. Egitilen model yayindaki basvuruyla karsilastirilir.
def model_kapisi(m, isabet, taban, basvuru, pay=0.05, dusus=0.0100):
    bulgu = []
    if abs(m["egitim_satiri"] - basvuru["egitim_satiri"]) / basvuru["egitim_satiri"] > pay:
        bulgu.append("egitim_satiri")
    if basvuru["egitim_isabeti"] - m["egitim_isabeti"] > dusus:
        bulgu.append("egitim_dususu")
    if m["agirlik"] != basvuru["agirlik"]:
        bulgu.append("agirlik_kaymasi")
    return bulgu, isabet <= taban


KAYNAK = kaynak_uret()
BASVURU = kosum(KAYNAK)[0]
OLAY = [("saglam", KAYNAK, None)] + [(s, boz(KAYNAK, s), None) for s in BOZULMA] \
    + [("oznitelik_kodu", KAYNAK, 1)]
SONUC = {}
print(f"{'olay':<15}{'kod (1)':>9}{'veri (2)':>26}{'model (3)':>18}{'ilk kapi':>10}")
for ad, kay, pen in OLAY:
    kk = kod_kapisi(pen)
    vk = veri_kapisi(kay)
    m, isabet, taban, satir = kosum(kay, pen)
    mk, taban_alti = model_kapisi(m, isabet, taban, BASVURU)
    ilk = 1 if kk else (2 if vk else (3 if mk else 0))
    SONUC[ad] = (m, isabet, taban, satir, kk, vk, mk, taban_alti, ilk)
    print(f"{ad:<15}{('durdurdu' if kk else '-'):>9}{(','.join(vk) or '-'):>26}"
          f"{(','.join(mk) or '-'):>18}{(ilk or '-'):>10}")
print("hicbir kapinin durdurmadigi olay:", [a for a in SONUC if SONUC[a][8] == 0])
print("sinama isabeti taban sinif oranini gecemeyen kosum:",
      sum(1 for a in SONUC if SONUC[a][7]), "/", len(OLAY))
```

```
olay             kod (1)                  veri (2)         model (3)  ilk kapi
saglam                 -                         -                 -         -
gec_gelen              -                         -                 -         -
yinelenen              -                  tekillik                 -         2
sema_degisti           -                  sema,tip     egitim_satiri         2
birim_degisti          -                         -     egitim_dususu         3
eksik_bolum            -               bolum_donem     egitim_satiri         2
kayan_tip              -                       tip                 -         2
oznitelik_kodu  durdurdu                         -   agirlik_kaymasi         1
hicbir kapinin durdurmadigi olay: ['saglam', 'gec_gelen']
sinama isabeti taban sinif oranini gecemeyen kosum: 8 / 8
```

#### Hangi Kapı Hangi Bozulmayı Durduruyor

Dağılım eşit değildir. Veri kapısı altı sınıftan **dördünü** durdurur: yinelenen kaydı tekillik
denetimiyle, alan adı değişimini şema ve tür denetimiyle, eksik bölümü dönem duyarlı bütünlük
denetimiyle, metne dönen sayıyı tür denetimiyle. Model kapısı **üçünü** durdurur ve bunlardan
**birim değişimi yalnız orada** durur: hattın girdisi kusursuz görünürken eğitim isabeti 0,7931'den
**0,5919**'a iner ve **0,2012**'lik düşüş, 0,0100'lük hoşgörünün yirmi katıdır.

Kod kapısı altı sınıftan **hiçbirini** durdurmaz ve bu bir kusur değildir. Kod kapısı üretim
verisini görmez; gördüğü tek şey üç satırlık sabit bir girdidir. Buna karşılık öznitelik penceresinin
tek döneme indirilmesini **birinci adımda**, kaynağa hiç dokunulmadan yakalar. Aynı değişikliği model
kapısı da yakalar, ama üçüncü adımda: seçilen ağırlık `(0.6, 0.4, 0)` yerine `(0.6, 0.0, 0)` çıktığı
için ağırlık kayması ateşler. İki kapı da doğrudur; aralarındaki fark, birinin bozulmayı hat hiç
koşmadan, öbürünün 2200 satır işlendikten sonra durdurmasıdır.

Son satır dersin en rahatsız edici sayısıdır. Sekiz koşumun **sekizinde** sınama isabeti taban sınıf
oranını **geçemiyor**. Etiket dağılımı dengesizdir, taban sınıf oranı **0,7964**, sınama kümesinde
**0,8067**; model tam olarak o sayıyı üretir. Sınama isabetini yayın ölçütü yapan bir model kapısı bu
düzenekte hiçbir modeli geçirmez. Bu bir yanlış alarm değildir: modelin taban sınıf oranının üstüne
hiç çıkmadığının, isabet tek başına okunduğunda görünmeyen kanıtıdır.

#### Hiçbir Kapının Durdurmadığı Sınıf

Tablo iki olayı durdurulmamış bırakıyor. Biri sağlam koşumdur ve durmaması beklenir. Öbürü **geç
gelen** satırdır ve bu bir boşluktur. Üçüncü blok boşluğun nereden geldiğini ölçer.

```python
print(f"{'olay':<15}{'kaynak':>8}{'hat':>7}{'egitim':>8}{'egt.isabet':>12}{'sinama':>8}")
for ad, kay, pen in OLAY:
    m, isabet, taban, satir, *_ = SONUC[ad]
    print(f"{ad:<15}{len(kay):>8}{satir:>7}{m['egitim_satiri']:>8}"
          f"{m['egitim_isabeti']:>12.4f}{isabet:>8.4f}")
print()
G, GI, GT, *_ = SONUC["gec_gelen"]
print(f"gec_gelen egitim satiri sapmasi: {abs(G['egitim_satiri'] - 1600) / 1600:.4f}"
      f"  kapi hosgorusu: 0.0500  karsilastirma: kati")
print("hosgoru 0.0400 olsaydi gec_gelen:", model_kapisi(G, GI, GT, BASVURU, pay=0.04)[0],
      " ayni hosgoruyle saglam kosum:",
      model_kapisi(SONUC["saglam"][0], SONUC["saglam"][1], SONUC["saglam"][2],
                   BASVURU, pay=0.04)[0] or "durmadi")
print("veri kapisinin durdurdugu ama basvuruyla birebir ayni modeli veren kosum:",
      [a for a in SONUC if a != "saglam" and SONUC[a][5] and SONUC[a][0] == BASVURU])
```

```
olay             kaynak    hat  egitim  egt.isabet  sinama
saglam             2400   2200    1600      0.7931  0.8067
gec_gelen          2360   2120    1520      0.7914  0.8067
yinelenen          2460   2200    1600      0.7931  0.8067
sema_degisti       2400   1800    1200      0.7892  0.8067
birim_degisti      2400   2200    1600      0.5919  0.8067
eksik_bolum        2354   2108    1508      0.7924  0.8067
kayan_tip          2400   2200    1600      0.7931  0.8067
oznitelik_kodu     2400   2200    1600      0.7925  0.8067

gec_gelen egitim satiri sapmasi: 0.0500  kapi hosgorusu: 0.0500  karsilastirma: kati
hosgoru 0.0400 olsaydi gec_gelen: ['egitim_satiri']  ayni hosgoruyle saglam kosum: durmadi
veri kapisinin durdurdugu ama basvuruyla birebir ayni modeli veren kosum: ['yinelenen', 'kayan_tip']
```

Geç gelen satır iki kapıdan da kıl payı geçiyor. Veri kapısında kaynak 2400 yerine 2360'tır; sapma
**0,0167**, hoşgörü 0,0500. Model kapısında eğitim satırı 1600 yerine 1520'dir; sapma **tam
0,0500**, hoşgörü de 0,0500. Kapıyı geçiren şey bir ölçüm değil, bir **karşılaştırma işaretidir**:
katı eşitsizlik kullanıldığı için sınıra oturan sapma içeride sayılır. Hoşgörü 0,0400'e indirildiğinde
geç gelen satır duruyor ve sağlam koşum durmuyor.

Bu, eşik tabanlı her kapının taşıdığı kırılganlıktır ve sayısı yazılmadan görülmez. Ölçülen yanlış
alarmın sıfır çıkması ise kapının değil **bu kümenin** bir özelliğidir: kurgu kaynakta her dönem tam
200 satır taşır, yani satır sayısındaki doğal oynama sıfırdır. Doğal oynaması olan bir kaynakta aynı
sıkılaştırma sağlam koşumları da durdururdu.

Kapıların bedeli son satırdadır. Veri kapısı dört koşumu durduruyor, ama bunların **ikisi**
başvuruyla **birebir aynı** modeli üretecekti: yinelenen kayıt tekilleştirme aşamasında, metne dönen
sayı tür dönüşümünde yutuluyor ve ikisinde de ağırlık, eğitim satırı ve eğitim isabeti
değişmiyor. Kapısız kurulumda sekiz koşumun sekizi yayına çıkar ve altısı bozuktur; kapılı kurulumda
altı koşum durur, ikisi geçer ve geçenlerden biri hâlâ bozuktur. Durdurulan koşumların **üçte biri**
ise hiçbir şey kazandırmadan durdurulmuştur.

Kursun kuralı kapıya da uygulanır: **yeniden üretilemeyen sayı bir ölçüm değil bir anıdır.** Bir
kapının yeşil olması da bir ölçüm değildir; geç gelen satır üç kapıdan da yeşil geçerek 1520 satırla
eğitilmiş modeli 1600 satırlık modelin yerine yayına taşır.

#### Özet

- Kapılar sırayla koşar: kod **1**, veri **2**, model **3**. Bir olayı ilk durduran kapının numarası,
  bozulmanın hattı ne kadar kirlettiğini de söyler.
- Altı bozulma sınıfından **dördünü** veri kapısı, **üçünü** model kapısı durdurur; **birim değişimi
  yalnız model kapısında**, eğitim isabetini 0,7931'den **0,5919**'a düşürerek durur.
- Kod kapısı altı sınıftan **hiçbirini** durdurmaz, çünkü üretim verisini görmez; buna karşılık
  öznitelik kodu değişimini **birinci adımda**, hat hiç koşmadan yakalar.
- **Geç gelen** satırı hiçbir kapı durdurmaz: kaynak sapması 0,0167, eğitim satırı sapması **tam
  0,0500** ve hoşgörü katı karşılaştırmayla 0,0500. Hoşgörü 0,0400 olsaydı durur ve sağlam koşum
  durmazdı.
- Etiket dağılımı dengesizdir, taban sınıf oranı **0,7964**; sekiz koşumun **sekizinde** sınama
  isabeti 0,8067 ile taban sınıf oranının üstüne çıkamaz.
- Veri kapısının durdurduğu dört koşumun **ikisi** başvuruyla birebir aynı modeli verecekti; kapı
  eklemenin bedeli, kazandırmadığı yerde de durdurmasıdır.

#### Sonraki Adım

Kapılardan geçen bir koşum artık bir model üretmiş sayılır, ama üretilen şeyin ne olduğu hâlâ
yazılmadı. Buraya kadar model üç ağırlıktan ve bir eşikten ibaret bir sözlüktü; onu çağıran taraf
alanların adını, türünü ve sırasını **tahmin ederek** kullanıyordu. Sonraki ders modeli
**paketler**: ağırlığın yanına hangi alanların hangi türle beklendiğini ve karşılığında hangi alanın
döneceğini yazan bir **arayüz sözleşmesi** koyar. Ölçü, sözleşme yazılmadığında kırılan çağırandır:
alan adı ya da tür değiştiğinde kaç çağrı düşüyor, kaçı hiç düşmeden yanlış cevap veriyor ve
sözleşmeyle bunların kaçı **yayın öncesi** yakalanıyor.

### Model Paketleme

Source: https://academia.sh/tr/kurslar/mlops-temelleri/model-paketleme

Eğitilen modelin ağırlığı, eşiği ve arayüz bildirimiyle tek bir birim hâline getirilmesi ve sözleşme yazılmadığında kırılan çağıranın sayılması: üç çağıranın 1800 çağrısında alan adı değiştiğinde düşen çağrı 600'den 1200'e çıkıyor, eşik alanı paketten düştüğünde düşen çağrı sayısı değişmiyor ama geçen 1200 çağrının 1200'ü birden yanıt değiştiriyor; yalnız girdi alanlarını bildiren sözleşme üç sürümün ikisini yakalarken çıktıyı ve parametreyi de bildiren sözleşme üçünü de yayın öncesinde yakalıyor ve bunun için 1800 çağrı yerine 3 örnek kayıt okuyor; etiket dağılımı 1752'ye 448, taban sınıf oranı 0,7964 ve modelin sınama isabeti 0,8067 ile sınama kümesindeki taban oranının birebir kendisi.

Bir önceki dersin kapılarından geçen koşum artık bir model üretmiş sayılıyordu, ama üretilen şeyin ne
olduğu hâlâ yazılmamıştı. Model üç ağırlıktan ve bir eşikten ibaret bir sözlüktü; onu çağıran taraf
alanların adını, türünü ve sırasını **tahmin ederek** kullanıyordu.

Bu ders o sözlüğü bir **pakete** çevirir. Paketleme, ağırlığın yanına çağıranın bilmek zorunda olduğu
her şeyi koymaktır: hangi alanlar hangi türle beklenir, hangi parametre pakette taşınır, karşılığında
hangi alan döner. Bu bildirime **arayüz sözleşmesi** denir. Dersin sorusu, sözleşme yazılmadığında
kimin kırıldığıdır: alan adı ya da tür değiştiğinde kaç çağrı düşüyor, kaçı hiç düşmeden yanlış
yanıt veriyor ve sözleşmeyle bunların kaçı **yayın öncesi** yakalanıyor.

- **ED21.** Kaynak, hat, model ve paket **kurgudur**. **Hiçbir paketleme biçimi, kap çalışma zamanı,
  sunum çatısı ya da öğrenme kitaplığı adı yazılmaz.** Tohum **20260218**.
- **ED22.** Üretici ile tüketici arasındaki **veri** şeması ve şema değişiminin kimi kırdığı Veri
  Mühendisliğine Giriş kursunda ölçüldü ve **tekrarlanmaz**. Buradaki sözleşme verinin değil,
  **modelin arayüzünün** sözleşmesidir.
- **ED23.** Paket dört şey taşır: **ağırlık**, **eşik**, **girdi alanlarının bildirimi** ve **çıktı
  alanının adı**. Çalışma zamanı, kaynak ayrımı ve dağıtım biçimi bu dersin dışındadır.
- **ED24.** Bir çağrı **düşmüş** sayılır: paketin beklediği alan kayıtta yoksa ya da alanın türü
  aritmetiğe girmiyorsa. Kayıtta bulunan ve türü uyan her çağrı **geçer**.
- **ED25.** Üç çağıran vardır ve üçü de aynı **600** sınama satırını gönderir; toplam **1800** çağrı.
- **ED26.** Çağıranlar üç noktada ayrışır: biri alan adlarını **paketin bildiriminden okur**, biri
  adları **kendi kodunda sabitlemiştir**, biri adları sabitlemiştir ve bir alanı **metin** taşır.
- **ED27.** Sözleşme yayımlanmış arayüzdür; hem paket hem çağıran **ona** uyar. İki biçimi
  karşılaştırılır: yalnız girdiyi kapsayan ve çıktıyı ile parametreyi de kapsayan.
- **ED28.** Etiket dağılımı **dengesizdir**; taban sınıf oranı bu derste de yazılır.
- **ED29.** Taban çizgisi **sözleşmesiz paketlemedir**: bildirim yok, yayın öncesi doğrulama yok.
- **ED30.** Çözünürlük: sınama kümesi **600 satır**, isabetin adımı **0,0017**; çağrı sayımının adımı
  **1 çağrı**.
- **ED31.** Süre ham milisaniye olarak yazılmaz; ölçü **çağrı**, **kayıt** ve **bildirim**dir.

#### Paketin İçinde Ne Var

İlk blok kaynağı, hattın ilk üç aşamasını ve modeli kurar; hiçbiri bu derse özgü değildir.

```python
# KURGUDUR. Kaynak, hat, model ve paket ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, paketleme bicimi ya da calisma zamani 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):
    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
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


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

    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"]


def egit(kayitlar, oz):
    kume = [k for k in kayitlar if k["donem"] in EGITIM]
    en_iyi, en_iyi_w = -1.0, None
    for w in IZGARA:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > ESIK else 0) == etiket(k))
        if d / len(kume) > en_iyi:
            en_iyi, en_iyi_w = d / len(kume), w
    return {"agirlik": en_iyi_w, "egitim_isabeti": round(en_iyi, 4),
            "egitim_satiri": len(kume)}


def sina(kayitlar, model, oz):
    kume = [k for k in kayitlar if k["donem"] in SINAMA]
    d = sum(1 for k in kume
            if (1 if puan(oz[(k["abone"], k["donem"])], model["agirlik"]) > ESIK else 0)
            == etiket(k))
    return round(d / len(kume), 4)
```

İkinci blok modeli paketler ve üç sürümünü kurar. `v1` yayındaki paket, `v2` bir girdi alanının adını
değiştirmiş paket, `v3` ise eşik alanını **hiç taşımayan** pakettir. Üç çağıran aynı 600 satırı
gönderir ve düşen çağrı sayılır.

```python
# ---- Paketleme: agirlik , esik ve arayuz bildirimi tek bir birimde
KAYNAK = kaynak_uret()
V, _ = a1_ayikla(KAYNAK)
V, _ = a2_tekille(V)
SATIR, _ = a3_tuketim(V)
OZ = oznitelikler(SATIR)
MODEL = egit(SATIR, OZ)
SINAMA_KUME = [k for k in SATIR if k["donem"] in SINAMA]
DAGILIM = {e: sum(1 for k in SATIR if etiket(k) == e) for e in (0, 1)}
TABAN_S = round(sum(1 for k in SINAMA_KUME if etiket(k) == 0) / len(SINAMA_KUME), 4)
print("etiket dagilimi:", DAGILIM, " taban sinif orani:", round(DAGILIM[0] / len(SATIR), 4))
print("agirlik:", MODEL["agirlik"], " egitim satiri:", MODEL["egitim_satiri"],
      " sinama satiri:", len(SINAMA_KUME),
      f" sinama isabeti: {sina(SATIR, MODEL, OZ):.4f}  sinama taban orani: {TABAN_S:.4f}")

PAKET_V1 = {"agirlik": MODEL["agirlik"], "esik": ESIK, "cikti": "yuksek",
            "girdi": ("abone_ort", "bolge_ort", "isyeri")}
PAKET_V2 = dict(PAKET_V1, girdi=("abone_ortalamasi", "bolge_ort", "isyeri"))
PAKET_V3 = {a: v for a, v in PAKET_V1.items() if a != "esik"}
SURUM = [("v1 basvuru", PAKET_V1), ("v2 alan adi", PAKET_V2), ("v3 esik yok", PAKET_V3)]
VARSAYILAN_ESIK = 0


def cagir(paket, kayit):
    w, g = paket["agirlik"], paket["girdi"]
    o = {a: kayit[a] for a in g}                                # eksik alan -> KeyError
    p = w[0] * o[g[0]] + w[1] * o[g[1]] + w[2] * o[g[2]]        # tur uyusmazligi -> TypeError
    return {paket["cikti"]: 1 if p > paket.get("esik", VARSAYILAN_ESIK) else 0}


def c_uyarlanan(paket, o):
    """Alan adlarini paketin bildiriminden okur."""
    return dict(zip(paket["girdi"], (o["abone_ort"], o["bolge_ort"], o["isyeri"])))


def c_sabit_ad(paket, o):
    """Alan adlarini kendi kodunda sabitlemis."""
    return {"abone_ort": o["abone_ort"], "bolge_ort": o["bolge_ort"], "isyeri": o["isyeri"]}


def c_sabit_tur(paket, o):
    """Adlari sabitlemis ve isyeri alanini metin tasiyor."""
    return dict(c_sabit_ad(paket, o), isyeri=str(o["isyeri"]))


CAGIRAN = [("uyarlanan", c_uyarlanan), ("sabit_ad", c_sabit_ad), ("sabit_tur", c_sabit_tur)]
print(f"{'surum':<13}{'cagri':>7}{'dusen':>7}{'gecen':>7}{'yuksek':>8}"
      + "".join(f"{a:>12}" for a, _ in CAGIRAN))
for sad, pk in SURUM:
    dusen, gecen, yuksek, kirilan = 0, 0, 0, []
    for cad, cf in CAGIRAN:
        d = 0
        for k in SINAMA_KUME:
            try:
                yuksek += cagir(pk, cf(pk, OZ[(k["abone"], k["donem"])]))[pk["cikti"]]
                gecen += 1
            except (KeyError, TypeError):
                d += 1
        dusen += d
        kirilan.append(d)
    print(f"{sad:<13}{len(SINAMA_KUME) * 3:>7}{dusen:>7}{gecen:>7}{yuksek:>8}"
          + "".join(f"{x:>12}" for x in kirilan))
```

```
etiket dagilimi: {0: 1752, 1: 448}  taban sinif orani: 0.7964
agirlik: (0.6, 0.4, 0)  egitim satiri: 1600  sinama satiri: 600  sinama isabeti: 0.8067  sinama taban orani: 0.8067
surum          cagri  dusen  gecen  yuksek   uyarlanan    sabit_ad   sabit_tur
v1 basvuru      1800    600   1200       0           0           0         600
v2 alan adi     1800   1200    600       0           0         600         600
v3 esik yok     1800    600   1200    1200           0           0         600
```

#### Sözleşme Yazılmadığında Kırılan Çağıran

İlk iki satır modelin kendisini yazar ve bu kursta her derste tekrarlanan uyarıyı taşır. Etiket
dağılımı **1752'ye 448**, yani taban sınıf oranı **0,7964**; sınama kümesinde bu oran **0,8067** ve
modelin sınama isabeti de **0,8067**. İsabet tek başına okunduğunda yanıltır: paketlenen model,
sınama kümesinde çoğunluk sınıfının payının **birebir kendisini** üretiyor. Aşağıdaki bütün çağrı
sayıları bu modelin çağrılarıdır.

Taban satırı **v1**'dir ve orada bile 1800 çağrının **600'ü** düşer. Kıran şey bir sürüm değişikliği
değil, baştan var olan bir **tür uyuşmazlığıdır**: `sabit_tur` çağıranı `isyeri` alanını metin
taşıyor ve puan hesabı aritmetiğe giremiyor. Sözleşmesiz kurulumda bu uyuşmazlık ancak üretimde,
çağrı çağrı ortaya çıkar.

**v2** satırı alan adı değişiminin bedelini verir. Paket `abone_ort` yerine `abone_ortalamasi`
beklemeye başlıyor ve düşen çağrı 600'den **1200**'e çıkıyor. Adları kendi kodunda sabitlemiş iki
çağıran birden kırılıyor, çünkü gönderdikleri kayıtta paketin aradığı alan yok. Ayakta kalan tek
çağıran, adları paketin bildiriminden **okuyan** çağırandır ve onun 600 çağrısının hepsi geçiyor. Bir
paketin alan adlarını bildirmesi ile çağıranların o bildirimi okuması ayrı iki karardır; ikincisi
olmadan birincisi işe yaramaz.

**v3** satırı en tehlikeli olanıdır ve düşen çağrı sayısına bakan hiçbir gözlem onu göremez. Eşik
alanı paketten çıkarılmıştır; çağıran alanı bulamayınca kendi öntanımlı değerine, yani **0**'a
düşüyor. Düşen çağrı sayısı değişmiyor: v3'te de v1'deki gibi **600** çağrı düşer, **1200** geçer.
Değişen tek sütun sonuncusudur. Geçen 1200 çağrının **1200'ü** birden "yüksek" yanıtı dönüyor, oysa
v1'de dönen yüksek sayısı **0**'dı. Model aynı, ağırlıklar aynı, çağıran aynı; **eksik tek bir
parametre yüzünden her yanıt tersine dönmüş** durumda. Hata kaydına düşen hiçbir satır yok.

#### Sözleşmenin Yayın Öncesinde Yakaladığı

Üçüncü blok sözleşmeyi yazar. Sözleşme **yayımlanmış arayüzdür**: hem paket hem çağıran ona uyar,
ikisi birbirine değil sözleşmeye bakar. İki biçimi karşılaştırılır: yalnız girdi alanlarını
bildiren sözleşme ile çıktıyı ve parametreyi de bildiren sözleşme.

```python
# ---- Arayuz sozlesmesi: yayimlanmis arayuz. Paket de cagiran da buna uyar.
TUR = {"ondalik": float, "tamsayi": int}
SOZLESME_GIRDI = {"girdi": {"abone_ort": "ondalik", "bolge_ort": "ondalik",
                            "isyeri": "tamsayi"}}
SOZLESME_TAM = dict(SOZLESME_GIRDI, cikti="yuksek", parametre=("esik",))


def dogrula(sozlesme, paket, ornekler):
    bulgu = []
    if set(paket["girdi"]) != set(sozlesme["girdi"]):
        bulgu.append("paket: girdi alan adi")
    if "cikti" in sozlesme and paket["cikti"] != sozlesme["cikti"]:
        bulgu.append("paket: cikti alan adi")
    for p in sozlesme.get("parametre", ()):
        if p not in paket:
            bulgu.append(f"paket: eksik parametre {p}")
    for ad, ornek in ornekler:
        for a, t in sozlesme["girdi"].items():
            if a not in ornek:
                bulgu.append(f"{ad}: eksik alan {a}")
            elif type(ornek[a]) is not TUR[t]:
                bulgu.append(f"{ad}: {a} turu")
    return bulgu


ORNEK = OZ[(SINAMA_KUME[0]["abone"], SINAMA_KUME[0]["donem"])]
print(f"{'surum':<13}{'girdi sozlesmesi':>18}{'tam sozlesme':>14}   tam sozlesmenin bulgusu")
for sad, pk in SURUM:
    ornekler = [(cad, cf(pk, ORNEK)) for cad, cf in CAGIRAN]
    g = dogrula(SOZLESME_GIRDI, pk, ornekler)
    t = dogrula(SOZLESME_TAM, pk, ornekler)
    print(f"{sad:<13}{len(g):>18}{len(t):>14}   {t}")
print("yayin oncesi okunan kayit:", len(CAGIRAN),
      " sozlesmesiz kurulumda cagri:", len(SINAMA_KUME) * 3,
      " sozlesmedeki bildirim:", len(SOZLESME_TAM["girdi"]) + 1 + len(SOZLESME_TAM["parametre"]))
```

```
surum          girdi sozlesmesi  tam sozlesme   tam sozlesmenin bulgusu
v1 basvuru                    1             1   ['sabit_tur: isyeri turu']
v2 alan adi                   3             3   ['paket: girdi alan adi', 'uyarlanan: eksik alan abone_ort', 'sabit_tur: isyeri turu']
v3 esik yok                   1             2   ['paket: eksik parametre esik', 'sabit_tur: isyeri turu']
yayin oncesi okunan kayit: 3  sozlesmesiz kurulumda cagri: 1800  sozlesmedeki bildirim: 5
```

Üç sürümün üçünde de düşen çağrı sayısı **0**'a iner, çünkü uyuşmazlık yayın öncesinde bulunur ve
sürüm yayımlanmaz. Ölçülen fark, uyuşmazlığın nerede görüldüğüdür: sözleşmesiz kurulumda **1800
çağrının** içinde, sözleşmeli kurulumda **3 örnek kayıtta**. Doğrulama çağıran başına tek kayıt okur.

v2 satırı sözleşmenin ne olduğunu da gösterir. Üç bulgunun biri pakete, ikisi çağıranlara aittir. Bu
bulgulardan biri şaşırtıcıdır: paketin bildirimini okuyan `uyarlanan` çağıranı üretimde **hiç
düşmüyordu**, ama sözleşmeye göre uyumsuz bir kayıt üretiyor. İkisi de doğrudur. O çağıran pakete
uyuyor, sözleşmeye uymuyor; çünkü paket sözleşmeden sapmış ve çağıran sapmayı sessizce izlemiş.
Sözleşmenin işi paketle çağıranı değil, **ikisini birden yayımlanmış arayüzle** karşılaştırmaktır.

v3 satırı ise sözleşmenin **kendisinin kusurlu olabileceğini** yazar. Yalnız girdi alanlarını bildiren
sözleşme v3'te tek bulgu üretir ve o bulgu ilgisizdir: `sabit_tur` çağıranının baştan beri var olan
tür uyuşmazlığı. Eşik alanının eksikliğini görmez, çünkü sözleşmesinde parametre diye bir kalem
yoktur. Çıktıyı ve parametreyi de bildiren sözleşme aynı sürümde **iki** bulgu üretir ve **ilki**
tam olarak eksik parametredir. Üç sürümün ikisini girdi sözleşmesi de yakalıyor; **1200 yanıtı
tersine çeviren sessiz kırılmayı yakalayan tek şey parametre bildirimidir.**

Sözleşmenin bedeli de sayılabilir. Beş bildirim yazılır: üç girdi alanı, bir çıktı alanı, bir
parametre. Her yayından önce bir doğrulama adımı eklenir ve o adım çağıran başına bir örnek kayıt
ister. Bir kalemi eksik bırakılan sözleşme, kapsamadığı yerde **ölçülmemiş bir güvence** verir:
girdi sözleşmesi v3'te yeşil görünüyordu.

Kursun kuralı arayüze de uygulanır: **yeniden üretilemeyen sayı bir ölçüm değil bir anıdır.** Bir
çağrının döndürdüğü yanıt, onu üreten arayüz yazılı değilse yeniden üretilemez; v3'ün 1200 yanıtı
bunun kanıtıdır.

#### Özet

- Paketleme, ağırlığın yanına çağıranın bilmek zorunda olduğu her şeyi koymaktır: **ağırlık, eşik,
  girdi bildirimi ve çıktı alanı**. Bu bildirim arayüz sözleşmesidir.
- Etiket dağılımı **1752'ye 448**, taban sınıf oranı **0,7964**; paketlenen modelin sınama isabeti
  **0,8067** ile sınama kümesindeki taban oranının birebir kendisidir.
- Sözleşmesiz kurulumda taban sürümde bile 1800 çağrının **600'ü** düşer; alan adı değiştiğinde düşen
  çağrı **1200**'e çıkar ve ayakta kalan tek çağıran adları paketin bildiriminden okuyandır.
- Eşik alanı paketten düştüğünde **düşen çağrı sayısı değişmez**: v1'deki gibi 600 çağrı düşer,
  1200 çağrı geçer ve **1200'ü birden** yanıt değiştirir; v1'de 0 olan yüksek sayısı v3'te **1200**
  olur.
- Sözleşmeli kurulumda üç sürümde de düşen çağrı **0**'dır; uyuşmazlık 1800 çağrı yerine **3 örnek
  kayıtta** görülür ve bedeli **5 bildirim** ile bir doğrulama adımıdır.
- Yalnız girdiyi kapsayan sözleşme üç sürümün **ikisini** yakalar; sessiz kırılmayı yakalayan tek
  kalem **parametre bildirimidir**.

#### Sonraki Adım

Bu üç derste model bir hatta bağlandı, kapılardan geçirildi ve bir arayüzle paketlendi. Üçünün de
ortak varsayımı tek cümleyle yazılabilir: çağıran ne zaman isterse çağırır ve yanıt hemen gelir. Bu
dersin 1800 çağrısı da öyle sayıldı, sanki hepsi aynı anda ve aynı bedelle koşuyormuş gibi. Oysa
paketlenmiş bir modeli **gecenin sonunda bir kerede on iki dönem için** koşturmakla, bir isteğin
ortasında **tek satır için** koşturmak aynı şey değildir. Sonraki ders sunum kalıplarını ayırır:
toplu, çevrimiçi ve akış tabanlı çıkarım. Ölçü, üç kalıbın **gecikme sınıfı**, **tazeliği** ve
**tahmin başına bedelidir**.

### Sunum Kalıpları

Source: https://academia.sh/tr/kurslar/mlops-temelleri/sunum-kaliplari

Aynı paketin toplu, çevrimiçi ve akış tabanlı üç yerleşimde koşturulup üçünün gecikme sınıfı, tazeliği ve tahmin başına bedelinin ayrı ayrı sayılması: üç kalıp da aynı 600 isteğe aynı yanıtı verir ve isabet üçünde de 0,8067'dir, yani çıktıya bakan bir gözlem hangi kalıbın koştuğunu söyleyemez. Fark üç sütundadır: istek anında yapılan iş toplu ve akış kalıbında arama, çevrimiçi kalıpta 352 ile 550 satırlık tarama; yanıtın yaşı toplu kalıpta bir dönem, ötekilerde sıfır; tahmin başına okunan satır 11,00, 463,87 ve 3,67. Bayatlık süpürmesi altı döneme kadar 0 değişen tahmin verir ve bunun nedeni kalıp değil karar puanının eşiğe en az 7,6468 uzaklıkta durmasıdır. Bedel payda kararıdır: toplu ile çevrimiçi kalıp 4,74 kullanılan tahminde eşitlenir ve hangi görevin hangi kalıba dayandığı bu noktadan okunur.

Bir önceki ders modeli paketledi: çalışma zamanı sabitlendi, arayüz sözleşmesi yazıldı ve sözleşme
yazılmadığında kaç çağrının düştüğü sayıldı. Paketin ne kabul ettiği ve ne döndürdüğü artık
yazılıdır. Yazılmayan tek şey, bu paketin **ne zaman** çalıştığıdır. Aynı paket dönem başında bir kez
koşup iki yüz tahmini bir tabloya yazabilir, her istek geldiğinde bir kez koşabilir, ya da her yeni
okuma indiğinde kendini tazeleyebilir. Üçünde de ağırlıklar aynıdır.

Bu üç yerleşimin adı **sunum kalıbı (serving pattern)**. Aralarındaki fark modelde değil, **çıkarımın
(inference)** hangi anda ve hangi kapsamla yapıldığındadır. Bu ders üçünü aynı iş yükünde yan yana
koşturur ve tek bir isabet sayısı yerine üç sayıyı ayrı ayrı yazar: isteğin geldiği anda yapılan iş,
yanıtın dayandığı en yeni okumanın yaşı ve **tahmin başına maliyet (cost per prediction)**.

- **ED41.** Kaynak, hat ve model **kurgudur** ve ders içinde üretilir. Hiçbir sunum çatısı, çıkarım
  servisi, hızlandırıcı ya da bulut servisi çağrılmaz; üç kalıp da burada yazılır. Tohum 20260218.
- **ED42.** Etiket dengesizdir: dağılım `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**. Hiçbir
  isabet bu oranın ne kadar üstünde olduğu yazılmadan okunmaz. Sınama kümesi 600 satırdır, yani
  isabetin en küçük adımı 0,0017'dir.
- **ED43.** Eğitim hattı bu kursta **tek makinede** ele alınır; bölüşüm ve karıştırma Dağıtık Veri
  İşleme kursunda ölçüldü ve tekrarlanmaz.
- **ED44.** Bedel **okunan satır** olarak sayılır; ham milisaniye yazılmaz. Gecikme **sınıf** olarak
  yazılır: `arama`, istek anında tek bir saklanmış kaydın okunmasıdır; `tarama`, istek anında
  abonenin ve bölgesinin geçmişinin okunmasıdır.
- **ED45.** Tazelik **dönem** olarak sayılır: yanıtın yaşı, en yeni okumanın tahmine yansıması için
  beklenen dönem sayısıdır.
- **ED46.** Sunum ufku sınama dönemleridir — `2026-10`, `2026-11`, `2026-12` — ve her dönem iki yüz
  abone için bir istek gelir. Toplam 600 istek.
- **ED47.** Toplu kalıp her dönem bir kez koşar. Bu bir **seçimdir** ve aşağıda süpürülür.

#### Üç Kalıp Yan Yana

İlk blok kaynağı, hattın tüketime kadarki üç aşamasını, öznitelik yolunu ve kurgu modeli kurar; sonra
üç kalıbı aynı ağırlıklarla koşturur. Öznitelikler **bir kez** hesaplanır ve ağırlıktan bağımsızdır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi , oznitelik
# yolu ve uc agirlikli esik modeli ders icinde uretilir; hicbir ogrenme
# kitapligi , sunum catisi ya da cikarim servisi 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 a3_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


EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar, pencere=None, basamak=4):
    """Egitim yolu tam gecmisi gorur. Agirliktan bagimsiz , 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 and (pencere is None or d >= i - pencere))]
        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"]


def egit(kayitlar, oz):
    kume = [k for k in kayitlar if k["donem"] in EGITIM]
    en_iyi, en_iyi_w = -1.0, None
    for w in IZGARA:
        d = sum(1 for k in kume
                if (1 if puan(oz[(k["abone"], k["donem"])], w) > ESIK else 0) == etiket(k))
        if d / len(kume) > en_iyi:
            en_iyi, en_iyi_w = d / len(kume), w
    return {"agirlik": en_iyi_w, "egitim_satiri": len(kume), "aday": len(IZGARA)}


TUM = a3_tuketim(kaynak_uret())
OZ = oznitelikler(TUM)
M = egit(TUM, OZ)
W = M["agirlik"]
SUNUM, ISTEK = SINAMA, sorted(ABONE)


def toplu_kosum(kayitlar, donem, w):
    """Butun gecmis bir kez taranir , butun tahminler uretilip saklanir."""
    oz = oznitelikler(kayitlar)
    return ({k["abone"]: (1 if puan(oz[(k["abone"], k["donem"])], w) > ESIK else 0)
             for k in kayitlar if k["donem"] == donem}, len(kayitlar))


def cevrimici_istek(dz, abone, donem, w):
    """Istek aninda abonenin ve bolgesinin gecmisi taranir."""
    ab, bo = dz
    i, b = DONEM.index(donem), ab[abone][0]["bolge"]
    sa = [k["m3"] for k in ab[abone] if DONEM.index(k["donem"]) < i]
    sb = [k["m3"] for k in bo[b] if DONEM.index(k["donem"]) < i]
    o = {"abone_ort": round(sum(sa) / len(sa), 4) if sa else 0.0,
         "bolge_ort": round(sum(sb) / len(sb), 4) if sb else 0.0,
         "isyeri": 1 if ab[abone][0]["tip"] == "isyeri" else 0}
    return (1 if puan(o, w) > ESIK else 0), len(ab[abone]) + len(bo[b])


def akis_kosum(kayitlar, w):
    """Her okuma geldiginde durum artimli guncellenir ve tahmin tazelenir."""
    da, db, tah, okunan, tazeleme = {}, {}, {}, 0, 0
    for k in sorted(kayitlar, key=lambda k: (DONEM.index(k["donem"]), k["abone"])):
        okunan += 1
        a, b = k["abone"], k["bolge"]
        o = {"abone_ort": round(da[a][0] / da[a][1], 4) if a in da else 0.0,
             "bolge_ort": round(db[b][0] / db[b][1], 4) if b in db else 0.0,
             "isyeri": 1 if k["tip"] == "isyeri" else 0}
        tah[(a, k["donem"])] = 1 if puan(o, w) > ESIK else 0
        da.setdefault(a, [0, 0]); db.setdefault(b, [0, 0])
        da[a][0] += k["m3"]; da[a][1] += 1
        db[b][0] += k["m3"]; db[b][1] += 1
        tazeleme += 1
    return tah, okunan, tazeleme, len(da) + len(db)


ab, bo = {}, {}
for k in TUM:
    ab.setdefault(k["abone"], []).append(k)
    bo.setdefault(k["bolge"], []).append(k)
DZ = (ab, bo)

GERCEK = {(k["abone"], k["donem"]): etiket(k) for k in TUM if k["donem"] in SUNUM}
N = len(ISTEK) * len(SUNUM)


def isabet(t):
    return round(sum(1 for c in GERCEK if t[c] == GERCEK[c]) / N, 4)


TOPLU, ok_t = {}, 0
for d in SUNUM:
    t, o = toplu_kosum(TUM, d, W)
    ok_t += o
    for a in ISTEK:
        TOPLU[(a, d)] = t[a]

CEV, ok_c, en_az, en_cok = {}, 0, 10 ** 9, 0
for d in SUNUM:
    for a in ISTEK:
        p, o = cevrimici_istek(DZ, a, d, W)
        CEV[(a, d)], ok_c = p, ok_c + o
        en_az, en_cok = min(en_az, o), max(en_cok, o)

AKIS, ok_a, taz, durum = akis_kosum(TUM, W)
print(f"egitim satiri {M['egitim_satiri']} , agirlik {W} , aday {M['aday']}")
print(f"sunum ufku {len(SUNUM)} donem x {len(ISTEK)} abone = {N} istek")
print("kalip      istek aninda  yanit yasi  okunan satir  tahmin basina  isabet")
for adi, yas, ok, ia in (("toplu", 1, ok_t, isabet(TOPLU)),
                         ("cevrimici", 0, ok_c, isabet(CEV)),
                         ("akis", 0, ok_a, isabet(AKIS))):
    print(f"{adi:<9}  {'tarama' if adi == 'cevrimici' else 'arama':>12}  {yas:>10}"
          f"  {ok:>12}  {ok / N:>13.2f}  {ia}")
print(f"cevrimici istek basina okunan: en az {en_az} , en cok {en_cok}")
print(f"akis: tazeleme {taz} , kullanilan {N} , tutulan durum girdisi {durum}")
```

```
egitim satiri 1600 , agirlik (0.6, 0.4, 0) , aday 27
sunum ufku 3 donem x 200 abone = 600 istek
kalip      istek aninda  yanit yasi  okunan satir  tahmin basina  isabet
toplu             arama           1          6600          11.00  0.8067
cevrimici        tarama           0        278322         463.87  0.8067
akis              arama           0          2200           3.67  0.8067
cevrimici istek basina okunan: en az 352 , en cok 550
akis: tazeleme 2200 , kullanilan 600 , tutulan durum girdisi 205
```

Son sütun üç satırda da **0,8067**. Aynı ağırlıklar, aynı öznitelik yolu, aynı 600 istek; üç kalıp da
aynı yanıtları verir. Etiket dengesizdir ve taban sınıf oranı 0,7964'tür; sınama kümesinin kendi
taban oranı da 0,8067'dir, yani bu kurulum kursun ilk dersinde yazıldığı gibi tabanın **0,0000**
üstünde durur. Kalıp seçimi de onu değiştirmez ve çıktıya bakan bir gözlem hangi kalıbın koştuğunu
söyleyemez.

Fark öbür sütunlarda durur. Çevrimiçi kalıp istek başına 352 ile 550 satır okur, ortalaması 463,87.
Bunun neredeyse tamamı **bölge geçmişidir**; abonenin kendi geçmişi on bir satırdır. `bolge_ort`
özniteliği tek bir abone için hesaplanamaz, bölgenin bütün abonelerini ister. **Çevrimiçi çıkarımın
(online inference) pahalı oluşu modelden değil, özniteliğin kapsamından gelir** — bir tahmin başına
kırk iki kat fazla satır okunmasının nedeni budur.

**Toplu çıkarım (batch inference)** aynı bölge ortalamasını bir kez hesaplayıp bölgedeki bütün
abonelere paylaştırır: 6600 satır, 600 tahmin, tahmin başına 11,00. Karşılığında yanıtı bir dönem
yaşlanır, çünkü iki koşum arasında gelen hiçbir okuma tahmine giremez. Akış kalıbı her satırı bir kez
okur ve durumu artımlı günceller: 2200 satır, tahmin başına 3,67. Tablo onun bedelini **göstermez**;
2200 tazelemenin yalnız 600'ü kullanılır, 205 durum girdisi sürekli tutulur ve durum kaybolduğunda
yeniden kurmak 2200 satırı baştan okumak demektir. Okunan satır sütunu akışın en ucuz göründüğü
sütundur ve akışın bedeli tam olarak o sütunda yazmaz.

#### Bedel Hangi Paydayla Bölünüyor

Yanıtın bir dönem yaşlanması bir bedel gibi durur. İkinci blok önce onu ölçer: aynı istekleri gitgide
daha eski bir koşumdan yanıtlar. Sonra tahmin başına maliyetin paydasını değiştirir.

```python
# Bu blok ilk bloktaki TUM , OZ , W , SUNUM , ISTEK , CEV , ok_c , N ,
# puan , ESIK , DONEM ve isabet tanimlarini surdurur.
print("yanit yasi (donem)  degisen tahmin  isabet")
for g in range(0, 7):
    t = {}
    for d in SUNUM:
        j = max(DONEM.index(d) - g, 1)
        for a in ISTEK:
            t[(a, d)] = 1 if puan(OZ[(a, DONEM[j])], W) > ESIK else 0
    deg = sum(1 for c in t if t[c] != CEV[c])
    print(f"{g:>18}  {deg:>14}  {isabet(t)}")

uz = sorted(abs(puan(OZ[c], W) - ESIK) for c in CEV)
print(f"esik {ESIK} , karar puaninin esige uzakligi: en az {uz[0]:.4f}"
      f" , medyan {uz[len(uz) // 2]:.4f}")

print("\nkullanilan tahmin    toplu  cevrimici    akis  en ucuz  toplu kullanilmayan")
ort_c = ok_c / N
for u in (1, 2, 5, 20, 50, 200):
    s = [len(TUM) / u, ort_c, len(ISTEK) / u]
    print(f"{u:>17}  {s[0]:>7.2f}  {s[1]:>9.2f}  {s[2]:>6.2f}"
          f"  {('toplu', 'cevrimici', 'akis')[s.index(min(s))]:>7}  {len(ISTEK) - u:>19}")
print(f"toplu ile cevrimici {len(TUM) / ort_c:.2f} kullanilan tahminde esitlenir")
```

```
yanit yasi (donem)  degisen tahmin  isabet
                 0               0  0.8067
                 1               0  0.8067
                 2               0  0.8067
                 3               0  0.8067
                 4               0  0.8067
                 5               0  0.8067
                 6               0  0.8067
esik 40 , karar puaninin esige uzakligi: en az 7.6468 , medyan 13.6826

kullanilan tahmin    toplu  cevrimici    akis  en ucuz  toplu kullanilmayan
                1  2200.00     463.87  200.00     akis                  199
                2  1100.00     463.87  100.00     akis                  198
                5   440.00     463.87   40.00     akis                  195
               20   110.00     463.87   10.00     akis                  180
               50    44.00     463.87    4.00     akis                  150
              200    11.00     463.87    1.00     akis                    0
toplu ile cevrimici 4.74 kullanilan tahminde esitlenir
```

Bayatlık süpürmesinin sonucu tek bir sayıdır: yanıtın yaşı sıfırdan altı döneme çıkarken **değişen
tahmin 0** ve isabet 0,8067'de kalır. Bu iş yükünde bayatlığın ölçülebilir bir karşılığı yoktur ve
buradan "toplu kalıp bedelsizdir" **çıkarılmaz**. Nedeni bir alt satırdadır: karar puanının eşiğe en
yakın uzaklığı 7,6468, medyanı 13,6826. Kararlar eşikten uzak durur ve bir dönemlik ek geçmiş, on
bir dönemlik kümülatif ortalamayı bu uzaklığı kapatacak kadar oynatmaz. Kararlar eşiğe yaklaştığında
aynı bayatlık tahmini oynatır. **Ölçülemeyen bedel yok sayılmaz; ölçülemediği ve hangi koşulda
görüneceği yazılır** — kursun ikinci iddiası burada bedel tarafından okunmuş hâlidir.

İkinci tablo tahmin başına maliyetin bir **payda kararı** olduğunu gösterir. Toplu kalıp bir koşumda
2200 satır okur ve iki yüz tahmin üretir; o tahminlerin kaçının gerçekten okunduğu kalıbın değil
işin sorusudur. Tek tahmin kullanılıyorsa toplu kalıbın tahmin başına maliyeti **2200,00**'dir ve
çevrimiçi kalıbın 463,87'sinin dört katından fazladır; iki kalıp **4,74 kullanılan tahminde**
eşitlenir. Son sütun toplu kalıbın bedelinin adıdır: bir tahmin kullanıldığında 199 tahmin üretilip
hiç okunmaz.

Hangi görevin hangi kalıba dayandığı bu iki tablodan okunur. Dönem başında bütün abonelerin
listelendiği iş toplu kalıba dayanır: payda 200'dür, bayatlık ölçülemez ve maliyet 11,00'e iner.
Önceden bilinmeyen tek abonelik bir sorgu çevrimiçi kalıba dayanır: payda 1'dir ve toplu kalıp orada
dört kattan fazla pahalıdır, üstelik listede olmayan bir anahtarı hiç yanıtlayamaz. Okumanın **geldiği anda**
tetiklenmesi gereken iş yalnız akışla yapılır: akış 2200 tetikleme üretir, çevrimiçi kalıp yalnız
istek geldiğinde 600 üretir ve hiç istek gelmezse hiç tetiklenmez, toplu kalıp ise 600'ü dönem
başında ve bir dönem gecikmeli üretir.

#### Özet

- Üç sunum kalıbı aynı 600 isteğe **aynı yanıtları** verir ve isabet üçünde de **0,8067**'dir; taban
  sınıf oranı 0,7964, sınama kümesinin taban oranı 0,8067 ve bu kurulum onun **0,0000** üstünde.
  Kalıp seçimi bir isabet kararı değildir ve çıktıya bakan bir gözlem hangi kalıbın koştuğunu
  söyleyemez.
- Fark üç sütundadır: istek anındaki gecikme sınıfı toplu ve akış kalıbında `arama`, çevrimiçi
  kalıpta 352 ile 550 satırlık `tarama`; yanıtın yaşı 1, 0 ve 0 dönem; tahmin başına okunan satır
  **11,00**, **463,87** ve **3,67**.
- Çevrimiçi kalıbın kırk iki katlık farkı modelden değil `bolge_ort` özniteliğinin kapsamından gelir;
  abonenin kendi geçmişi on bir satır, bölgesininki 341 ile 539 satır arasındadır.
- Yanıtın yaşı altı döneme çıkarıldığında değişen tahmin **0**'dır; bunun nedeni kalıp değil, karar
  puanının eşiğe en az **7,6468** uzaklıkta durmasıdır. Ölçülemeyen bedel, ölçülmüş bir güvence
  değildir.
- Tahmin başına maliyet paydaya bağlıdır: toplu kalıp bir tahmin kullanıldığında **2200,00**, iki yüz
  tahmin kullanıldığında **11,00** verir ve çevrimiçi kalıpla **4,74** kullanılan tahminde eşitlenir.
- Akış kalıbı okunan satırda en ucuzudur, ama 2200 tazelemenin 1600'ü kullanılmaz ve 205 durum
  girdisi sürekli tutulur; bu bedel okunan satır sütununda hiç görünmez.

#### Sonraki Adım

Kalıp seçildi, ama her kalıp içinde hâlâ tek bir soru yanıtsızdır: bir çağrı kaç isteği **birlikte**
taşımalıdır. Toplu kalıpta bu, koşumun kaç tahmin ürettiğidir; çevrimiçi kalıpta ise gelen istekleri
biriktirip bir arada işlemek demektir ve biriktirmenin bedeli, ilk gelen isteğin **beklemesidir**.
Sonraki ders aynı istekleri toplu boyut bir ile altmış dört arasında koşturur, tahmin başına
maliyetin düşüşünü bekleyen isteğin gecikmesiyle yan yana sayar ve aynı süpürmeyi iki hızlandırıcı
sınıfında tekrarlar.

### Çıkarım Ölçekleme

Source: https://academia.sh/tr/kurslar/mlops-temelleri/cikarim-olcekleme

Toplu isteme, önbellekleme ve hızlandırıcı sınıfı seçiminin tahmin başına maliyet ile bekleyen isteğin gecikmesi arasında verildiğinin sayılması: aynı 600 istek toplu boyut bir, iki, dört, sekiz, on altı, otuz iki, altmış dört, yüz yirmi sekiz ve iki yüzde tahmin başına 463,87'den 22,00'ye inerken en kötü bekleme 0'dan 199 varışa ve ortalama bekleme 99,50'ye çıkar; yanıtlar dokuz satırda da birebir aynıdır ve isabet 0,8067'de kalır, yani toplu boyut bir kalite ayarı değildir. Her dönemin son grubu boyut on altı, otuz iki ve altmış dörtte sekiz istekte kalır ve dolmayacak bir grubu bekler; bu bekleme en kötü bekleme sütununda hiç görünmez. İki hızlandırıcı sınıfının kesişme noktası kurulum farkının birim farkına oranıdır ve 80 istektir, ama tabloda geçiş yüz yirmi sekizde görünür çünkü ölçülen şey istenen boyut değil gerçekleşen grup boyutudur. Önbellek 600 isteklik akışta 0,6783 isabetle istek başına okunan satırı 464,86'dan 148,70'e indirir ve toplu istemeyle birlikte 26,38 verir; iki kazanç çarpılamaz.

Bir önceki ders üç sunum kalıbını yan yana koydu ve tahmin başına maliyetin bir payda kararı
olduğunu gösterdi. Çevrimiçi kalıp orada en pahalı olandı: istek başına ortalama 463,87 satır. O
sayının içinde bir varsayım vardı — her istek **tek başına** işleniyordu. Oysa aynı bölgeye ait iki
istek bölge geçmişini iki kez okumak zorunda değildir; ikisi bir arada işlenirse o geçmiş bir kez
okunur.

Bu ders o birlikteliği bir ayar hâline getirir. Gelen istekler bir grup dolana kadar biriktirilir,
sonra tek çağrıda işlenir. Kazanç ölçülebilir ve büyüktür; bedeli de ölçülebilir ve aynı yerde durur:
grubu bekleyen ilk istek, grup dolana kadar hiçbir yanıt almaz. Ders bu iki sayıyı süpürür, sonra
aynı süpürmeyi iki **hızlandırıcı** sınıfında ve bir önbellek altında tekrarlar.

- **ED48.** Kaynak, hat, model ve iki hızlandırıcı sınıfı **kurgudur** ve ders içinde üretilir.
  Hiçbir çıkarım servisi, sunum çatısı, hızlandırıcı donanım ya da bulut servisi adı yazılmaz;
  hızlandırıcılar yalnız **türleriyle** anılır. Tohum 20260218.
- **ED49.** Etiket dengesizdir: dağılım `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**. Sınama
  kümesi 600 satırdır, isabetin en küçük adımı 0,0017'dir.
- **ED50.** Toplu çağrı ve önbellek yordamı Yapay Zekâ Mühendisliği müfredatındaki Model API'leri ile
  Uygulama Geliştirme kursunda ölçüldü — bağlam penceresinin koyduğu üst sınır, başarısızlığın
  yaydığı zarar ve önbellek ömrünün bayatlığı orada sayıldı. **Tekrarlanmaz**; burada yalnız **tahmin
  başına maliyet** ve **bekleme** sayılır.
- **ED51.** Gecikme **varış** olarak sayılır: bir isteğin grubu dolana kadar beklediği varış sayısı.
  Ham milisaniye yazılmaz.
- **ED52.** Bir gruptaki her abonenin ve her bölgenin geçmişi **bir kez** okunur; toplu istemenin
  bütün kazancı bu paylaşımdan gelir.
- **ED53.** İki hızlandırıcı sınıfı **iş birimi** ile yazılır: genel amaçlı işlem birimi `(60, 4)`,
  hızlandırıcı `(300, 1)`; ilki çağrı başına kurulum, ikincisi istek başına birim. Bu bir **seçimdir**
  ve duyarlılığı aşağıda süpürülür.
- **ED54.** Sunum ufku bir önceki dersinkidir: `2026-10`, `2026-11`, `2026-12` ve her dönem iki yüz
  istek. Gruplar dönem içinde oluşur.

#### Toplu Boyut Süpürmesi

İlk blok kaynağı, hattı, öznitelik yolunu ve modeli kurar; sonra aynı 600 isteği dokuz ayrı toplu
boyutta koşturur. Öznitelikler ağırlıktan bağımsızdır ve **bir kez** hesaplanır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi , oznitelik
# yolu , uc agirlikli esik modeli ve iki hizlandirici sinifi ders icinde
# uretilir; hicbir cikarim servisi ya da donanim 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 a3_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


EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def etiket(k):
    return 1 if k["m3"] > ESIK else 0


def oznitelikler(kayitlar):
    """Egitim yolu tam gecmisi gorur. Agirliktan bagimsiz , 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)]


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


TUM = a3_tuketim(kaynak_uret())
OZ = oznitelikler(TUM)
EG = [k for k in TUM if k["donem"] in EGITIM]
W = max(IZGARA, key=lambda w: sum(
    1 for k in EG if (1 if puan(OZ[(k["abone"], k["donem"])], w) > ESIK else 0) == etiket(k)))

SUNUM, ISTEK = SINAMA, sorted(ABONE)
ab, bo = {}, {}
for k in TUM:
    ab.setdefault(k["abone"], []).append(k)
    bo.setdefault(k["bolge"], []).append(k)
GERCEK = {(k["abone"], k["donem"]): etiket(k) for k in TUM if k["donem"] in SUNUM}
N = len(ISTEK) * len(SUNUM)


def grup_isle(istekler, donem):
    """Bir gruptaki her abonenin ve her bolgenin gecmisi BIR kez okunur."""
    i = DONEM.index(donem)
    aa = sorted(set(istekler))
    bb = sorted(set(ABONE[a]["bolge"] for a in aa))
    okunan = sum(len(ab[a]) for a in aa) + sum(len(bo[b]) for b in bb)
    bort = {}
    for b in bb:
        s = [k["m3"] for k in bo[b] if DONEM.index(k["donem"]) < i]
        bort[b] = round(sum(s) / len(s), 4) if s else 0.0
    t = {}
    for a in aa:
        s = [k["m3"] for k in ab[a] if DONEM.index(k["donem"]) < i]
        t[(a, donem)] = 1 if puan({"abone_ort": round(sum(s) / len(s), 4) if s else 0.0,
                                   "bolge_ort": bort[ABONE[a]["bolge"]],
                                   "isyeri": 1 if ABONE[a]["tip"] == "isyeri" else 0},
                                  W) > ESIK else 0
    return t, okunan


GENEL, HIZ = (60, 4), (300, 1)
BOYUT = (1, 2, 4, 8, 16, 32, 64, 128, 200)


def supur(boyut):
    okunan, bekleme, cagri, tumt, is_g, is_h, eksik = 0, 0, 0, {}, 0, 0, 0
    for d in SUNUM:
        gr = [ISTEK[i:i + boyut] for i in range(0, len(ISTEK), boyut)]
        for g in gr:
            t, o = grup_isle(g, d)
            tumt.update(t)
            okunan += o
            bekleme += len(g) * (len(g) - 1) // 2
            cagri += 1
            is_g += GENEL[0] + GENEL[1] * len(g)
            is_h += HIZ[0] + HIZ[1] * len(g)
        eksik = len(gr[-1]) if len(gr[-1]) != boyut else 0
    return tumt, okunan, bekleme, cagri, eksik, is_g, is_h


TEK = supur(1)[0]
IS = {}
print("boyut  cagri  eksik  okunan satir  satir/tahmin  en kotu bekleme"
      "  ort bekleme  ayni yanit  isabet")
for b in BOYUT:
    t, ok, bk, cg, ek, ig, ih = supur(b)
    IS[b] = (ig, ih)
    isa = round(sum(1 for c in GERCEK if t[c] == GERCEK[c]) / N, 4)
    print(f"{b:>5}  {cg:>5}  {ek:>5}  {ok:>12}  {ok / N:>12.2f}  {b - 1:>15}"
          f"  {bk / N:>11.2f}  {str(all(t[c] == TEK[c] for c in TEK)):>10}  {isa}")
```

```
boyut  cagri  eksik  okunan satir  satir/tahmin  en kotu bekleme  ort bekleme  ayni yanit  isabet
    1    600      0        278322        463.87                0         0.00        True  0.8067
    2    300      0        252450        420.75                1         0.50        True  0.8067
    4    150      0        203610        339.35                3         1.50        True  0.8067
    8     75      0        140316        233.86                7         3.50        True  0.8067
   16     39      8         87318        145.53               15         7.34        True  0.8067
   32     21      8         51183         85.31               31        15.02        True  0.8067
   64     12      8         31383         52.30               63        30.38        True  0.8067
  128      6     72         19800         33.00              127        53.42        True  0.8067
  200      3      0         13200         22.00              199        99.50        True  0.8067
```

İki sütun ters yönlere gider ve hiç geri dönmez. Tahmin başına okunan satır 463,87'den **22,00**'ye
iner, yirmi bir kat; en kötü bekleme 0'dan **199 varışa**, ortalama bekleme 99,50'ye çıkar. Son iki
sütun kararın **kalite** tarafını kapatır: dokuz boyutta da yanıtlar birebir aynıdır ve isabet
0,8067'de kalır — taban sınıf oranı 0,7964, sınama kümesinin taban oranı ise 0,8067, yani bu kurulum
tabanın **0,0000** üstündedir. **Toplu boyut bir kalite ayarı değil, maliyet ile bekleme arasında
bir ayardır.**

Kazanç doğrusal değildir. İlk ikiye katlama 463,87'yi yalnız 420,75'e indirir, çünkü iki isteğin
bölgesi beş bölge arasında çoğunlukla farklıdır ve iki bölge geçmişi yine ayrı ayrı okunur. Kazanç
boyut 16 ile 32 arasında en yüksek hızına ulaşır, sonra yavaşlar: boyut 200'de zaten bütün bölgeler
tek bir grupta olduğu için paylaşılacak yeni geçmiş kalmaz. **Toplu istemenin kazandırdığı şey
isteğin sayısı değil, isteklerin ortak okuduğu satırdır.**

Üçüncü sütun tablonun kendi kusurunu taşır. Boyut 16, 32 ve 64'te her dönemin son grubu **sekiz**
istekte kalır, boyut 128'de yetmiş iki istekte. Bu istekler hiç dolmayacak bir grubu bekler ve ancak
bir zaman aşımıyla boşaltılır. Beklemeleri "en kötü bekleme" sütununda **görünmez**, çünkü o sütun
grubun dolduğunu varsayar. Ölçülen en kötü değer, ölçülmemiş bir varsayımın altındadır.

#### Kurulum Bedeli ve Önbellek

Okunan satır bedelin tek adı değildir. Her çağrı bir kurulum bedeli öder ve bu bedel, çağrının
koştuğu birime göre değişir. İkinci blok iki hızlandırıcı sınıfını aynı süpürmede karşılaştırır,
kesişme noktasını hesaplar ve sonuna tekrarlı bir istek akışı ekler.

```python
# Bu blok ilk bloktaki TUM , ISTEK , SUNUM , N , BOYUT , IS , GENEL , HIZ ,
# ABONE , DONEM , TOHUM , uretec ve grup_isle tanimlarini surdurur.
print("boyut  gerceklesen grup  genel amacli  hizlandirici  ucuz olan")
for b in BOYUT:
    ig, ih = IS[b]
    gr = [len(ISTEK[i:i + b]) for i in range(0, len(ISTEK), b)]
    print(f"{b:>5}  {len(ISTEK) / len(gr):>16.1f}  {ig / N:>12.2f}  {ih / N:>12.2f}"
          f"  {'hizlandirici' if ih < ig else 'genel amacli':>13}")
for kh in (150, 300, 600):
    print(f"kurulum {kh:>3} -> donus noktasi {(kh - GENEL[0]) / (GENEL[1] - HIZ[1]):.1f} istek")


def istek_akisi(n):
    r = uretec(TOHUM + 911)
    return [f"A{int(r() * 200) + 1:03d}" for _ in range(n)]


def maliyet(cagrilar, boyut, donem="2026-12"):
    return sum(grup_isle(cagrilar[i:i + boyut], donem)[1]
               for i in range(0, len(cagrilar), boyut))


AKIS = istek_akisi(600)
FARKLI = list(dict.fromkeys(AKIS))
print(f"\nakis {len(AKIS)} istek , farkli {len(FARKLI)} , onbellek isabeti"
      f" {(len(AKIS) - len(FARKLI)) / len(AKIS):.4f}")
taban = maliyet(AKIS, 1)
for adi, cag, b in (("ikisi de yok , boyut 1", AKIS, 1), ("yalniz toplu isteme", AKIS, 32),
                    ("yalniz onbellek", FARKLI, 1), ("ikisi birlikte", FARKLI, 32)):
    ok = maliyet(cag, b)
    print(f"{adi:<24} okunan {ok:>7}  istek basina {ok / len(AKIS):>7.2f}"
          f"  carpan {ok / taban:.4f}")
t1, t2 = maliyet(AKIS, 32) / taban, maliyet(FARKLI, 1) / taban
print(f"carpimlarin ongordugu {t1 * t2:.4f} , gerceklesen"
      f" {maliyet(FARKLI, 32) / taban:.4f}")
```

```
boyut  gerceklesen grup  genel amacli  hizlandirici  ucuz olan
    1               1.0         64.00        301.00   genel amacli
    2               2.0         34.00        151.00   genel amacli
    4               4.0         19.00         76.00   genel amacli
    8               8.0         11.50         38.50   genel amacli
   16              15.4          7.90         20.50   genel amacli
   32              28.6          6.10         11.50   genel amacli
   64              50.0          5.20          7.00   genel amacli
  128             100.0          4.60          4.00   hizlandirici
  200             200.0          4.30          2.50   hizlandirici
kurulum 150 -> donus noktasi 30.0 istek
kurulum 300 -> donus noktasi 80.0 istek
kurulum 600 -> donus noktasi 180.0 istek

akis 600 istek , farkli 193 , onbellek isabeti 0.6783
ikisi de yok , boyut 1   okunan  278916  istek basina  464.86  carpan 1.0000
yalniz toplu isteme      okunan   47872  istek basina   79.79  carpan 0.1716
yalniz onbellek          okunan   89221  istek basina  148.70  carpan 0.3199
ikisi birlikte           okunan   15829  istek basina   26.38  carpan 0.0568
carpimlarin ongordugu 0.0549 , gerceklesen 0.0568
```

Hızlandırıcı sınıfı boyut 128'e kadar pahalıdır. Kesişme noktası bir ölçüm değil bir orandır: kurulum
farkının birim farkına bölümü, yani `(300 - 60) / (4 - 1) = 80` istek. Tabloda geçişin 128'de
görünmesinin nedeni, ikinci sütundur. Boyut 64 istendiğinde her dönem 64, 64, 64 ve 8 isteklik
gruplara bölünür ve **gerçekleşen ortalama grup 50**'dir, yani 80'in altında; boyut 128'de gerçekleşen
ortalama 100'dür ve 80'in üstündedir. **Bir hızlandırıcının gerekçesi kendi hızında değil,
doldurulabilen grup boyutundadır**; doldurulamayan bir grupla hızlandırıcı yalnız pahalı bir genel
amaçlı birimdir. Kurulum bedeli süpürmesi bunu üç sayıyla gösterir: 150, 300 ve 600 kurulumun dönüş
noktaları 30, 80 ve 180 istektir.

Son dört satır önbelleği ekler. Altı yüz isteklik akışta yalnız **193** farklı istek vardır; önbellek
isabeti 0,6783 ve istek başına okunan satır 464,86'dan 148,70'e iner. Toplu isteme tek başına daha
güçlüdür: 79,79. İkisi birlikte **26,38** verir. Ama iki çarpan ayrı ölçülüp çarpılamaz: 0,1716 ile
0,3199'un çarpımı 0,0549'u öngörür, gerçekleşen 0,0568'dir. Fark küçüktür ve nedeni ölçülebilir —
önbellek grupları **seyreltir**. Geriye kalan 193 istek otuz ikilik gruplara bölündüğünde son grupta
tek bir istek kalır ve o tek istek bütün bölge geçmişini kendi başına okur. **Bir düzeneğin kazancı,
yanında hangi düzeneğin durduğuna bağlıdır.**

#### Özet

- Toplu boyut bir ile iki yüz arasında süpürüldüğünde tahmin başına okunan satır **463,87'den
  22,00'ye** iner, en kötü bekleme **0'dan 199 varışa** ve ortalama bekleme 99,50'ye çıkar; iki sütun
  ters yönlere gider ve hangisinin ödendiği yazılmadan boyut seçilemez.
- Dokuz boyutta da yanıtlar birebir aynıdır ve isabet 0,8067'de kalır; taban sınıf oranı 0,7964,
  sınama kümesinin taban oranı 0,8067 ve bu kurulum onun 0,0000 üstünde: toplu boyut bir kalite
  ayarı değildir.
- Kazanç isteğin sayısından değil isteklerin **ortak okuduğu satırdan** gelir; ilk ikiye katlama
  463,87'yi yalnız 420,75'e indirir, çünkü iki isteğin bölgesi çoğunlukla farklıdır.
- Boyut 16, 32 ve 64'te her dönemin son grubu sekiz istekte kalır ve dolmayacak bir grubu bekler; bu
  bekleme "en kötü bekleme" sütununda hiç görünmez, çünkü o sütun grubun dolduğunu varsayar.
- Hızlandırıcı sınıfının kesişme noktası `(300 - 60) / (4 - 1) = 80` istektir, ama tabloda geçiş
  128'de görünür: ölçülen şey istenen boyut değil **gerçekleşen grup boyutudur** (64 istendiğinde 50,
  128 istendiğinde 100).
- Önbellek 600 isteklik akışta 0,6783 isabetle 464,86'yı 148,70'e indirir; toplu istemeyle birlikte
  26,38 verir, oysa iki çarpanın çarpımı 25,52 öngörür — önbellek grupları seyreltir.

#### Sonraki Adım

Beş derste model paketlendi, bir kalıba yerleştirildi ve ölçeklendi. Beşinde de sessiz bir varsayım
vardı: model isteğin geldiği yerin **ağ üzerinden erişilebilir** bir ucundadır. Ağ koptuğunda bu
düzeneğin tahmin başına maliyeti tanımsızdır, çünkü tahmin yoktur. Sonraki ders modeli isteğin
kaynağına, aygıtın kendisine taşır; küçültmenin isabetten ne götürdüğünü ve hangi görevde önce
götürdüğünü ölçer, kenarda çalıştırmanın kazandırdığı bağımsızlığı yanıtlanan istek olarak sayar.

### Kenar ve Aygıt Üzerinde Çıkarım

Source: https://academia.sh/tr/kurslar/mlops-temelleri/kenar-ve-aygit-uzerinde-cikarim

Modeli isteğin kaynağına taşımanın isabetten ne götürdüğünün ve hangi görevde önce götürdüğünün sayılması: aygıta yazılan sayı beştir, bırakılan şey bölge özniteliğini üreten 341 ile 539 satırlık geçmiştir ve o geçmiş bırakıldığında iki sınıflı görev 0,8067'de kalırken üç kademeli görev 0,4833'ten 0,4733'e iner. Nicemleme süpürmesi aynı sırayı verir: iki sınıflı görev seviye 4'e kadar tek bir tahmin oynatmaz, üç kademeli görev seviye 16'da iki ve seviye 8'de beş tahmin oynatır, seviye 2'de ikisi 0,3833 ile 0,2250'ye çöker. Kenarda çalıştırmanın kazandırdığı bağımsızlık doğru yanıt sayısıyla ölçülür: ağ kesintisi payı 0,20'ye çıktığında merkezî kurulumun doğru yanıtı 484'ten 391'e ve 290'dan 234'e iner, kenardaki 484 ile 284'te sabit kalır; iki kurulumun eşitlendiği kesinti payı iki sınıflı görevde 0,0000, üç kademeli görevde 0,0207'dir. Ders kursun kapanışıdır ve on iki dersin sayılarını tek tabloda toplar.

Bir önceki ders isteği sunucu tarafında ucuzlattı: toplu boyut iki yüze çıktığında tahmin başına
okunan satır 22,00'ye indi. Beş derste model paketlendi, yerleştirildi ve ölçeklendi; beşinin altında
aynı sessiz varsayım duruyordu — model, isteğin doğduğu yerden **ağ üzerinden** erişilebilir bir
uçtadır. O ağ koptuğunda tahmin başına maliyet tanımsızdır, çünkü tahmin yoktur.

Bu ders modeli isteğin kaynağına taşır. Taşınacak şey küçüktür: üç ağırlık. Taşınamayan şey o
ağırlıkların **çarptığı** özniteliği üreten geçmiştir. Ders önce onu sayar, sonra geçmişin
bırakılmasının ve **nicemlemenin (quantisation)** iki görevden ne götürdüğünü ölçer, en sonunda
kenarın bağımsızlığını yanıtlanan istek olarak yazar.

- **ED55.** Kaynak, hat, model ve küçültme adımları **kurgudur**; hiçbir kenar aygıtı, çalışma
  zamanı, hızlandırıcı donanım ya da bulut servisi adı yazılmaz. Tohum 20260218.
- **ED56.** Etiket dengesizdir: `{0: 1752, 1: 448}`, **taban sınıf oranı 0,7964**; sınama kümesi
  600 satır, adım **0,0017**.
- **ED57.** Nicemleme ve **damıtma (distillation)** yordamı Model API'leri ile Uygulama Geliştirme
  kursunda ölçüldü; **tekrarlanmaz**, sayıları **girdidir**.
- **ED58.** Küçültme iki adımdır: bölge geçmişi aygıtta olmadığı için `bolge_ort` düşer ve model
  dokuz adaylık ızgarada yeniden eğitilir; ardından öznitelikler `seviye` ayrı değere indirgenir.
- **ED59.** İki görev ölçülür: **görev A** iki sınıflı (eşik 40), **görev B** üç kademeli (eşik 20
  ve 40).
- **ED60.** Ağ kesintisi payı `p`, isteklerin `p` payının merkezden **hiç yanıt alamaması**
  demektir; kenar **tamamını** yanıtlar. İkisini ortak birimle karşılaştıran tek sayı **doğru yanıt
  sayısıdır** (yanıtlanan istek çarpı isabet).

#### Aygıta Taşınamayan Geçmiş

İlk blok kaynağı, hattı ve iki görevi kurar; iki modeli ayrı eğitir ve seviyeyi süpürür.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi , oznitelik
# yolu , uc agirlikli esik modeli ve kucultme adimlari ders icinde uretilir;
# hicbir kenar aygiti , calisma zamani ya da donanim 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 a3_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


EGITIM, SINAMA, ESIK = DONEM[1:9], DONEM[9:], 40


def oznitelikler(kayitlar):
    """Egitim yolu tam gecmisi gorur. Agirliktan bagimsiz , 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}


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


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)]
KENAR_IZGARA = [w for w in IZGARA if w[1] == 0.0]      # bolge gecmisi aygitta yok

TUM = a3_tuketim(kaynak_uret())
OZ = oznitelikler(TUM)
M3 = {(k["abone"], k["donem"]): k["m3"] for k in TUM}
EGK = [c for c in M3 if c[1] in EGITIM]
SNK = [c for c in M3 if c[1] in SINAMA]
N = len(SNK)
ALT_A, UST_A = min(o["abone_ort"] for o in OZ.values()), max(o["abone_ort"] for o in OZ.values())
ALT_B, UST_B = min(o["bolge_ort"] for o in OZ.values()), max(o["bolge_ort"] for o in OZ.values())


def gorev_a(v):
    return 1 if v > ESIK else 0


def gorev_b(v):
    return 0 if v <= 20 else (1 if v <= ESIK else 2)


def egit(gorev, izgara, oz):
    return max(izgara, key=lambda w: sum(1 for c in EGK
                                         if gorev(puan(oz[c], w)) == gorev(M3[c])))


def isabet(gorev, w, oz):
    return round(sum(1 for c in SNK if gorev(puan(oz[c], w)) == gorev(M3[c])) / N, 4)


def payi(gorev):
    return round(max(sum(1 for c in SNK if gorev(M3[c]) == s) for s in (0, 1, 2)) / N, 4)


def nicem_oz(seviye):
    def q(x, alt, ust):
        adim = (ust - alt) / (seviye - 1)
        return alt + round((x - alt) / adim) * adim

    return {c: {"abone_ort": q(o["abone_ort"], ALT_A, UST_A),
                "bolge_ort": q(o["bolge_ort"], ALT_B, UST_B),
                "isyeri": o["isyeri"]} for c, o in OZ.items()}


WA, WB = egit(gorev_a, IZGARA, OZ), egit(gorev_b, IZGARA, OZ)
WAK, WBK = egit(gorev_a, KENAR_IZGARA, OZ), egit(gorev_b, KENAR_IZGARA, OZ)
bs = [sum(1 for c in M3 if ABONE[c[0]]["bolge"] == b) for b in BOLGE]
print(f"sunum ufkunda en kalabalik sinif payi: gorev A {payi(gorev_a)} , gorev B {payi(gorev_b)}")
print(f"merkez modeli  A {WA} isabet {isabet(gorev_a, WA, OZ)}"
      f" | B {WB} isabet {isabet(gorev_b, WB, OZ)}")
print(f"kenar modeli   A {WAK} isabet {isabet(gorev_a, WAK, OZ)}"
      f" | B {WBK} isabet {isabet(gorev_b, WBK, OZ)}")
print(f"aygita yazilan sayi 5 (3 agirlik + abonenin 2 toplami) ;"
      f" birakilan bolge gecmisi {min(bs)}-{max(bs)} satir")

t0a = {c: gorev_a(puan(OZ[c], WA)) for c in SNK}
t0b = {c: gorev_b(puan(OZ[c], WB)) for c in SNK}
print("\nseviye  farkli deger  A degisen  A isabet  B degisen  B isabet")
for s in (256, 16, 12, 8, 4, 2):
    oz = nicem_oz(s)
    ta = {c: gorev_a(puan(oz[c], WA)) for c in SNK}
    tb = {c: gorev_b(puan(oz[c], WB)) for c in SNK}
    fd = (len(set(o["abone_ort"] for o in oz.values()))
          + len(set(o["bolge_ort"] for o in oz.values())))
    print(f"{s:>6}  {fd:>12}  {sum(1 for c in SNK if ta[c] != t0a[c]):>9}"
          f"  {isabet(gorev_a, WA, oz)}  {sum(1 for c in SNK if tb[c] != t0b[c]):>9}"
          f"  {isabet(gorev_b, WB, oz)}")
```

```
sunum ufkunda en kalabalik sinif payi: gorev A 0.8067 , gorev B 0.4833
merkez modeli  A (0.6, 0.4, 0) isabet 0.8067 | B (0.6, 0.4, 4) isabet 0.4833
kenar modeli   A (0.6, 0.0, 0) isabet 0.8067 | B (1.0, 0.0, 4) isabet 0.4733
aygita yazilan sayi 5 (3 agirlik + abonenin 2 toplami) ; birakilan bolge gecmisi 341-539 satir

seviye  farkli deger  A degisen  A isabet  B degisen  B isabet
   256           199          0  0.8067          0  0.4833
    16            21          0  0.8067          2  0.48
    12            16          0  0.8067          0  0.4833
     8            11          0  0.8067          5  0.4783
     4             7          0  0.8067          0  0.4833
     2             4        402  0.3833        600  0.225
```

Dördüncü satır dersin ilk cümlesidir: aygıta yazılan sayı **beştir**, bırakılan şey 341 ile 539
satırlık **bölge geçmişidir**. **Kenara taşınamayan şey model değil, özniteliği üreten geçmiştir.**
Bedeli ayrışır: görev A'da isabet 0,8067'de kalır, görev B'de **0,4733**'e iner.

İki isabeti tabanın yanına koymak bedelin adını değiştirir. Etiket dengesizdir, **taban sınıf oranı
0,7964**'tür; sunum ufkunda görev A'nın en kalabalık sınıfı 0,8067, görev B'nin en kalabalık kademesi
0,4833 pay tutar ve merkez ikisinde de tam o payı verir. Görev A'daki sıfır düşüş bundan gelir:
küçültmenin oradan **götüreceği bir şey yoktur**. Kenar modeli ise görev B'de tabanın **0,0100
altına** düşer — her satıra en kalabalık kademeyi diyen bir yordamdan **altı adım kötü**. Küçültmenin
bedeli isabetteki düşüş değil, **tabanın hangi yanında kalındığıdır**.

Nicemleme aynı sırayı bağımsız doğrular. Görev A seviye 4'e kadar **tek bir tahmin oynatmaz**; görev
B seviye 16'da iki, seviye 8'de beş tahmin oynatır. Bu oynamalar 0,0017'lik çözünürlüğün hemen
üstündedir ve süpürme monoton değildir, bu yüzden bozulmanın başladığı yer "16 ile 2 arasında" diye
yazılır. Seviye 2'de ikisi de tabanın altına iner: görev A **0,3833** (0,4234 altı), görev B
**0,2250** (0,2583 altı). **Küçültme ayrımı siler, en ince görevi önce siler ve yeterince
ilerlediğinde modeli taban çizgisinin altına taşır.**

#### Kesinti Altında Yanıtlanan İstek

Küçültmenin götürdüğü yazıldı; kazandırdığı yazılmadı.

```python
# Bu blok ilk bloktaki N , OZ , WA , WB , WAK , WBK , gorev_a , gorev_b ,
# isabet , nicem_oz , uretec ve TOHUM tanimlarini surdurur.
mA, mB = isabet(gorev_a, WA, OZ), isabet(gorev_b, WB, OZ)
kA, kB = isabet(gorev_a, WAK, OZ), isabet(gorev_b, WBK, OZ)
r = uretec(TOHUM + 733)
kesinti = [r() for _ in range(N)]
print("kesinti payi  yanitlanan  merkez A  kenar A  merkez B  kenar B")
for p in (0.00, 0.02, 0.05, 0.10, 0.20):
    y = sum(1 for x in kesinti if x >= p)
    print(f"{p:>12.2f}  {y:>10}  {round(y * mA):>8}  {round(N * kA):>7}"
          f"  {round(y * mB):>8}  {round(N * kB):>6}")
print(f"donus noktasi: gorev A {1 - kA / mA:.4f} , gorev B {1 - kB / mB:.4f}")
print("kenar modeli nicemlenince  seviye 8: gorev B", isabet(gorev_b, WBK, nicem_oz(8)),
      ", seviye 4:", isabet(gorev_b, WBK, nicem_oz(4)))
```

```
kesinti payi  yanitlanan  merkez A  kenar A  merkez B  kenar B
        0.00         600       484      484       290     284
        0.02         589       475      484       285     284
        0.05         566       457      484       274     284
        0.10         536       432      484       259     284
        0.20         485       391      484       234     284
donus noktasi: gorev A 0.0000 , gorev B 0.0207
kenar modeli nicemlenince  seviye 8: gorev B 0.4767 , seviye 4: 0.4533
```

Kenar sütunları sabittir ve bu kenarın tek gerçek kazancıdır: kesinti payı ne olursa olsun 600
isteğin **hepsi** yanıtlanır, merkezde yanıtlanan istek 485'e iner. Doğru yanıt görev A'da 484'ten
**391**'e, görev B'de 290'dan **234**'e düşer. İki kurulum görev A'da **0,0000**, görev B'de
**0,0207** kesinti payında eşitlenir: bu payın üstünde, tabanın altına düşmüş kenar modeli yine de
daha çok doğru yanıt üretir. **Bağımsızlık bir isabet sayısı değildir; isabetle ancak doğru yanıt
sayısı üzerinden aynı tabloya girer.** Son satır iki küçültmeyi birlikte gösterir: seviye 8'de
0,4767, seviye 4'te 0,4533.

#### Özet

- Aygıta yazılan sayı **beştir**; taşınamayan şey 341 ile 539 satırlık **bölge geçmişidir**.
- Geçmişin bırakılması görev A'da isabeti değiştirmez, görev B'de **0,4733**'e indirir. Taban sınıf
  oranı 0,7964, görev B'nin en kalabalık kademesi 0,4833'tür: kenar modeli **tabanın 0,0100
  altındadır**, A'daki sıfır ise dayanıklılık değil ölçüsüzlüktür.
- Nicemleme aynı sırayı doğrular: görev A seviye 4'e kadar tek bir tahmin oynatmaz, görev B seviye
  16'da iki ve seviye 8'de beş oynatır; seviye 2'de ikisi **0,3833** ve **0,2250**'ye çöker.
- Kenar, kesinti payı ne olursa olsun 600 isteğin hepsini yanıtlar; merkezin doğru yanıtı payı
  0,20'de 484'ten **391**'e ve 290'dan **234**'e iner. İki kurulum görev A'da 0,0000, görev B'de
  **0,0207** kesinti payında eşitlenir ve kenarın gerekçesi o orandır.

#### Kurs Kapanışı

Satırlar ortak birim taşımaz; hangi ölçütle okunduğu ders adının yanındadır ve parantezdeki fark
**sabitlenmiş kurulum eksi tabandır**.

| Ders | Sabitlemesiz taban | Sabitlenmiş kurulum | Bedel |
|---|---|---|---|
| `yeniden-uretilebilirlik/01` — üç koşumun sınama isabeti | sabitleme yok: **0,8067 / 0,7955 / 0,8078** (yayılım **0,0123**) | üç koşumun ağırlığı aynı: `(0.6, 0.4, 0)` | isabet sınama kümesinin taban oranının kendisi, duyarlılık **0,0000** |
| `yeniden-uretilebilirlik/02` — bir sayıyı üreten koşumu bulmak | kayıtsız: **1, 2, 3, 5, 18** koşum tekrarı | tam kayıtla beşinde de **0** (**−18**'e kadar) | **162 alan**; yalnız ölçüt tutan defter beş aramadan birini kısaltır |
| `yeniden-uretilebilirlik/03` — belirsiz koşum sayısı | sürümsüz: **1 ad**, belirsiz koşum **4** | içerik adlı: **3 ad**, belirsiz koşum **0** (**−4**) | **6540 satır** (tarih adlı şema 8740); çıktı imzası yutulan yinelenmeyi görmez |
| `yeniden-uretilebilirlik/04` — aynı ağırlıkla sınama isabeti | sunum yolu, pencere 1: **0,7800** | öznitelik deposu, pencere 2: **0,8067** (**+0,0267**) | 6600 saklanan değer ve dördüncü aşama; sapma 600 özniteliği değiştirir, tahmini **16** satırda çevirir |
| `yeniden-uretilebilirlik/05` — üç koşumun aynılığı | sabitleme yok: **[0,8067 , 0,7955 , 0,8078]** | üç kalemle: **[0,8067 x 3]** | 15 kalem kaydı, **81** aday, **6600** satır; ızgara ile yuvarlamanın katkısı **ölçülemez** |
| `yeniden-uretilebilirlik/06` — üretimdeki modelin koşumunu bulmak | deftersiz: **16 kayıt**, **12 aday** | defterle: **1 kayıt** (**−15**), **1 aday** (**−11**) | **21 kayıt**; aynı ağırlıklı koşumlar 0,7950 ve 0,8067 bildirir |
| `egitim-ve-dagitim/01` — eğitim satırı | geç gelen 40 satırla: **1520** | uçtan uca koşum: **1600** (**+80**), bayat adım **0** | **13800 işlenen satır**; yedi kurulumun yedisi de aynı **0,8067**'yi bildirir |
| `egitim-ve-dagitim/02` — durdurulan bozulma sınıfı | kapısız: **0 / 6** | veri kapısı **4**, model kapısı **3**, kod kapısı **0** | durdurulan dört koşumun **ikisi** zaten aynı modeli verecekti; **geç gelen** hiçbir kapıda durmaz |
| `egitim-ve-dagitim/03` — 1800 çağrının düşeni | sözleşmesiz: **600**, alan adı değişince **1200** | sözleşmeli: **0** (**−1200**) | 5 bildirim; eşik alanı düştüğünde **düşen çağrı sayısı değişmez** ve 1200 yanıt sessizce değişir |
| `egitim-ve-dagitim/04` — tahmin başına okunan satır | kalıbı yazılmamış çağrı: **463,87** | toplu: **11,00** (**−452,87**), akış: **3,67** | toplu kalıpta yanıt bir dönem yaşlanır ve tek tahmin kullanıldığında 200'ün **199**'u boşa üretilir; akışta **1600** kullanılmayan tazeleme |
| `egitim-ve-dagitim/05` — tahmin başına satır ve bekleme | boyut 1: **463,87**, bekleme **0 varış** | boyut 200: **22,00** (**−441,87**), bekleme **199 varış** (**+199**) | dönem başına **8** istek dolmayan grupta bekler ve sütunda görünmez; hızlandırıcının dönüşü **80** istek |
| `egitim-ve-dagitim/06` — doğru yanıt, kesinti payı 0,20 | merkez: **391** (A), **234** (B) | kenar: **484** (**+93**), **284** (**+50**) | bölge geçmişi bırakılır, görev B **0,4833'ten 0,4733**'e iner; kesişme noktası **0,0207** |

Tablo tek bir kuralın on iki kez yazılmasıdır. **Bir modelin sayısı onu üreten yolla birlikte
anlamlıdır; yeniden üretilemeyen sayı bir ölçüm değil bir anıdır.** `yeniden-uretilebilirlik/01`'de
aynı iş üç ayrı sayı verir ve ağırlık üçünde de aynıdır; `/03`'te aynı ad üç ayrı içerik taşır;
`/06`'da aynı ağırlıklı koşumlar 0,7950 ve 0,8067 bildirir. Değişen model değil, sayının **hangi
yolla** üretildiğidir.

**İkinci iddia aynı tablodan okunur: sabitlemek de bedel getirir ve katkısı gösterilemeyen
sabitleme ölçülmemiş bir güvence verir.** `yeniden-uretilebilirlik/05`'te üç kalem yeter ve **ızgara
ile yuvarlamanın katkısı ölçülemez**; `/02`'de yalnız ölçüt tutan defter beş aramadan yalnız birini
kısaltır; `egitim-ve-dagitim/02`'de veri kapısının durdurduğu dört koşumun ikisi zaten aynı modeli
verecekti; `/04`'te bayatlığın ölçülememesi kalıbın güvencesi değil, karar puanının eşiğe 7,6468
uzaklıkta durmasının sonucudur. Dört satırda ödenen sabitlemenin karşılığı alınmamış ya da
gösterilememiştir.

**Üçüncü iddia tek bir satırda en keskin hâline gelir.** `yeniden-uretilebilirlik/04`'te aynı ağırlık
üçlüsüyle isabet eğitim yoluyla **0,8067**, sunum yoluyla **0,7800**'dür; **model hiç değişmemiştir**
ve 0,0267'nin tamamı özniteliğin hangi geçmişle hesaplandığından gelir. `egitim-ve-dagitim/03` bunun
sessiz eşidir: eşik alanı paketten düştüğünde düşen çağrı sayısı değişmez, 1200 çağrının
1200'ü yanıt değiştirir. **Modeli sınamak yetmez; sunumdaki öznitelik yolu ve paketin bildirimi de
sınanmalıdır.**

Altmış bir derste veri alındı, taşındı, saklandı ve bölüştürüldü; sonra bir modele bağlandı, yeniden
üretilebilir kılındı, paketlendi, sunuldu, ölçeklendi ve aygıta taşındı. Hiçbiri modelin **çıktıktan
sonrasını** sormadı. Dünya değişir ve girdi **kaymaya** başlar; etiket **haftalar sonra** gelir, yani
bugünün isabeti bugün ölçülemez; ve modelin ne zaman **emekliye ayrılacağı** hiçbir yerde yazılı
değildir. Sonraki kurs üretimdeki modeli bir teslim değil bir gözlem konusu sayar ve bu üç
soruyu ölçer.

