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.