Ders 07 / 12
Eğitim Hatları
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.
İçindekiler
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önemleri2026-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önemdir.
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.
# 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.
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.
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.
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.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.