İçeriğe geç
academia.sh

Ders 09 / 12

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 hiçbir çağrı düşmüyor 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.

İçindekiler

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 bildirimdir.

Paketin İçinde Ne Var

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

# 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.

# ---- 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. Hiçbir çağrı düşmüyor: v3’te de v1’deki gibi 600 çağrı düşer, 1200 çağrı 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.

# ---- 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 ikincisi 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 hiçbir çağrı düşmez: 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat