Ders 11 / 12
Boru Hatları
Hazırlama zincirinin bir nesneye bağlanması ve boru hattı olmadan kaç adımın sızdığının sayılması: dört adımlı zincir elle uygulandığında dördü de bütün kümeden öğrenilir ve sınama sayısı 0,7817'den 0,8294'e çıkar, şişme 0,0476. Doldurma ve ölçekleme sızdığında sayı hiç değişmez, hedef temelli kodlama sızdığında 0,0397, seçim de sızdığında 0,0476 şişer. Boru hattı 131 sayı öğrenir ve bu sayıların hepsi eğitim kümesinden gelir; ölçekleme adımının ağaç modelindeki katkısı tam sıfır, hedef temelli kodlama adımının katkısı ise eksi 0,0317'dir.
İçindekiler
Dört derste bir cümle tekrarlandı: bu adım yalnız eğitim kümesinden öğrenilir. Bölge ortancası, ölçek ortalaması, seçilen sütun kümesi, bileşen yönleri — dördü de veriden okunmuş sayılardır ve dördünün de doğru kümeden okunması gerekir. Cümle tekrarlandıkça bir başka şey görünür oldu: bu adımların hepsi aynı biçime sahiptir. Her birinin bir öğrenme yüzü vardır — veriden bir sayı ya da bir eşleme çıkarır — ve bir uygulama yüzü — o sayıyı verilen satırlara işler.
Bu biçim bir nesne tanımıdır. Adımlar tek tek elle çağrıldığında sıra karışabilir, bir adım yanlış kümeden öğrenebilir, sınama zamanı bir adım unutulabilir. Adımları tek bir nesnede toplayan yapıya boru hattı (pipeline) denir: boru hattının da bir öğrenme yüzü vardır ve yalnız eğitim kümesini görür, bir uygulama yüzü vardır ve her kümeye aynı öğrenilmiş sayıları uygular. Bu ders boru hattını yazar ve tek bir soru sorar: aynı zincir elle uygulandığında kaç adım sızar, sayı ne kadar şişer.
- OS30. Zincir dört adımdır: eksik değer doldurma, ölçekleme, hedef temelli kodlama, öznitelik seçimi. Beşinci sırada model vardır ve değişmez: derinlik 6 karar ağacı.
- OS31. Eksik memnuniyet değerleri ve
tarife_kodusütunu kurgudur, aynı tohumdan üretilir. Tarife kodu bölge ve hane büyüklüğüne bağlıdır, yani etiketle gerçek bir ilişki taşır. - OS32. Hedef temelli kodlama, tarife kodunun her düzeyi için şüpheli oranını öğrenir. Görülmemiş kategori genel orana düşer.
- OS33. Sızıntı ölçümünde ilk
kadım bütün kümeden öğrenir, kalanlar eğitim kümesinden; model her durumda yalnız eğitim kümesinden eğitilir. - OS34. Şişme, boru hattının sınama kümesindeki sayısına göre farktır. Taban çizgisi 0,7579.
- OS35. M27/K01’in sızıntı dersi dört sızıntı türünü tek tek saymıştı; buradaki kanal onun birinci türüdür — ayrımdan önce hazırlama — ve şişme sayıları tekrarlanmaz.
Adımın İki Yüzü
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir; # model derinlik 6 karar agacidir. Onceki dersten kalan tek yeni sutun kisi_basi. import math TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF BOLGE = [("kuzey", 0.28, 21, 0.00), ("guney", 0.22, 17, -0.10), ("dogu", 0.18, 26, 0.30), ("bati", 0.14, 14, -0.05), ("merkez", 0.18, 23, 0.05)] 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 def ayrik(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 ABONE, VERI = [], [] for i in range(HAM): r = uretec(TOHUM + i) b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])] hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]) memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) if r() >= 0.046: ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun}) for k in ABONE: r, v = uretec(TOHUM + 7000 + k["no"]), [] for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])): v.append(0.0 if r() < 0.038 else math.floor( k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100) r() if not v: continue ort, oyn, r = sum(v) / len(v), max(v) - min(v), uretec(TOHUM + 51000 + k["no"]) z = (0.052 * (ort - 20) + 0.85 * (min(v) == 0.0) + 0.026 * oyn + 0.24 * (k["hane"] >= 5) - 0.20 * (k["memnuniyet"] >= 4) + k["bolge"][3] + 0.9 * (k["no"] - 10001) / 1399 + (r() + r() + r() - 1.5) * 1.30) x = {"no": k["no"], "hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v), "ort_tuketim": round(ort, 2), "oynaklik": round(oyn, 2), "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35), "bolge_ad": k["bolge"][0]} for b in BOLGE: x["b_" + b[0]] = int(k["bolge"][0] == b[0]) VERI.append(x) TEMEL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"] TABAN_ALAN = TEMEL + ["kisi_basi"] for x in VERI: x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3) def karistir(veri, tohum): r, s = uretec(tohum), list(range(len(veri))) for i in range(len(s) - 1, 0, -1): j = int(r() * (i + 1)) s[i], s[j] = s[j], s[i] return [veri[i] for i in s] K = karistir(VERI, TOHUM + 90000) EGT, DOG, SIN = K[:756], K[756:1008], K[1008:] def gini(s): p = sum(x["supheli"] for x in s) / len(s) if s else 0.0 return 2 * p * (1 - p) def agac(s, derinlik, alanlar, enaz=2): p, en = sum(x["supheli"] for x in s) / len(s), None if derinlik and len(s) >= 2 * enaz and 0.0 < p < 1.0: for a in alanlar: d = sorted({x[a] for x in s}) for v in (d[1:] if len(d) < 10 else [d[int(i * len(d) / 10)] for i in range(1, 10)]): sol = [x for x in s if x[a] < v] sag = [x for x in s if x[a] >= v] if min(len(sol), len(sag)) < enaz: continue k = gini(s) - (len(sol) * gini(sol) + len(sag) * gini(sag)) / len(s) if en is None or k > en[0]: en = (k, a, v, sol, sag) if en is None or en[0] <= 1e-9: return {"tahmin": int(p > 0.5)} return {"alan": en[1], "esik": en[2], "sol": agac(en[3], derinlik - 1, alanlar, enaz), "sag": agac(en[4], derinlik - 1, alanlar, enaz)} def tahmin(d, x): while "tahmin" not in d: d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"] return d["tahmin"] def dogruluk(d, s): return sum(tahmin(d, x) == x["supheli"] for x in s) / len(s) def taban(egt, s): c = int(sum(x["supheli"] for x in egt) * 2 > len(egt)) return sum(x["supheli"] == c for x in s) / len(s)
Dört adımın dördü de aynı iki yöntemi taşır. ogren yalnız kendisine verilen satırlara bakar ve
öğrendiğini nesne alanına yazar; uygula o nesneyi verilen satırlara işler ve hiçbir şey
öğrenmez. Boru hattı bu ikiliği zincirin tamamına taşır.
for x in VERI: r = uretec(TOHUM + 61000 + x["no"]) r(), r() x["a_muhur"] = int(r() < (0.42 if x["supheli"] else 0.10)) r = uretec(TOHUM + 21000 + x["no"]) x["tarife_kodu"] = (24 * [b[0] for b in BOLGE].index(x["bolge_ad"]) + 4 * (x["hane"] - 1) + int(r() * 4)) x["memnuniyet"] = None if r() < 0.08 else x["memnuniyet"] HAVUZ = TABAN_ALAN + ["a_muhur", "tarife_hedef"] class Doldurma: ad = "doldurma" def ogren(self, s): v = sorted(x["memnuniyet"] for x in s if x["memnuniyet"] is not None) self.nesne = {"ortanca": v[len(v) // 2]} def uygula(self, s): for x in s: if x["memnuniyet"] is None: x["memnuniyet"] = self.nesne["ortanca"] class Olcekleme: ad = "olcekleme" def ogren(self, s): self.nesne = {} for a in ("ort_tuketim", "oynaklik", "kisi_basi"): o = sum(x[a] for x in s) / len(s) self.nesne[a] = (o, max(1e-9, (sum((x[a] - o) ** 2 for x in s) / len(s)) ** 0.5)) def uygula(self, s): for x in s: for a, (o, d) in self.nesne.items(): x[a] = round((x[a] - o) / d, 4) class HedefKodlama: ad = "hedef kodlama" def ogren(self, s): g, self.genel = {}, sum(x["supheli"] for x in s) / len(s) for x in s: g.setdefault(x["tarife_kodu"], []).append(x["supheli"]) self.nesne = {k: sum(v) / len(v) for k, v in g.items()} def uygula(self, s): for x in s: x["tarife_hedef"] = round(self.nesne.get(x["tarife_kodu"], self.genel), 4) class Secim: ad = "secim" def __init__(self, havuz): self.havuz = havuz def ogren(self, s): g0, self.nesne = gini(s), [] for a in self.havuz: d, en = sorted({x[a] for x in s}), 0.0 for v in (d[1:] if len(d) < 10 else [d[int(i * len(d) / 10)] for i in range(1, 10)]): sol = [x for x in s if x[a] < v] sag = [x for x in s if x[a] >= v] if min(len(sol), len(sag)) >= 20: en = max(en, g0 - (len(sol) * gini(sol) + len(sag) * gini(sag)) / len(s)) if en >= 0.004: self.nesne.append(a) def uygula(self, s): pass class BoruHatti: def __init__(self, adimlar, derinlik=6): self.adimlar, self.derinlik = adimlar, derinlik def ogren(self, egt, sizinti_kaynagi=None, sizan=0): egt = [dict(x) for x in egt] kirli = [dict(x) for x in (sizinti_kaynagi or [])] for i, a in enumerate(self.adimlar): if i < sizan: a.ogren(kirli) a.uygula(kirli) else: a.ogren(egt) a.uygula(egt) self.model = agac(egt, self.derinlik, self.adimlar[-1].nesne) self.egitimde = dogruluk(self.model, egt) return self def olc(self, s): s = [dict(x) for x in s] for a in self.adimlar: a.uygula(s) return dogruluk(self.model, s) def zincir(havuz=None): return [Doldurma(), Olcekleme(), HedefKodlama(), Secim(havuz or HAVUZ)] BH = BoruHatti(zincir()).ogren(EGT) print(f"{'kurulum':<26} {'sizan':>5} {'sutun':>5} {'egitimde':>9} {'ayrilmis':>9} " f"{'sisme':>8}") print(f"{'boru hatti':<26} {0:>5} {len(BH.adimlar[-1].nesne):>5} {BH.egitimde:>9.4f} " f"{BH.olc(SIN):>9.4f}") for k, ad in ((1, "elle: doldurma"), (2, "elle: + olcekleme"), (3, "elle: + hedef kodlama"), (4, "elle: + secim")): b = BoruHatti(zincir()).ogren(EGT, K, k) print(f"{ad:<26} {k:>5} {len(b.adimlar[-1].nesne):>5} {b.egitimde:>9.4f} " f"{b.olc(SIN):>9.4f} {b.olc(SIN) - BH.olc(SIN):>+8.4f}") print(f"\ntaban cizgisi {taban(EGT, SIN):.4f}; tarife kodu " f"{len({x['tarife_kodu'] for x in VERI})} duzey, eksik memnuniyet " f"{sum(1 for x in VERI if x['memnuniyet'] is None)} satir")
kurulum sizan sutun egitimde ayrilmis sisme boru hatti 0 8 0.8955 0.7817 elle: doldurma 1 8 0.8955 0.7817 +0.0000 elle: + olcekleme 2 8 0.8955 0.7817 +0.0000 elle: + hedef kodlama 3 8 0.9021 0.8214 +0.0397 elle: + secim 4 9 0.9008 0.8294 +0.0476 taban cizgisi 0.7579; tarife kodu 119 duzey, eksik memnuniyet 103 satir
Zincir elle uygulandığında dört adımın dördü de bütün kümeden öğrenilebilir, çünkü dördü de
bir ogren çağrısıdır ve o çağrıya hangi listenin verildiği kodun başka bir yerinde yazılıdır.
Boru hattında bu karar tek bir yerde durur: ogren yöntemi eğitim kümesini alır ve zincirin
tamamı o kümeden öğrenir. Adım eklemek sızıntı riskini artırmaz.
Şişme sütunu adım adım okunmalıdır. İlk iki satır sıfır: doldurma değeri bütün kümeden alınınca sayı değişmiyor, ölçek bütün kümeden alınınca da değişmiyor. Bu, o iki adımın sızmadığı anlamına gelmez — sızmıştır, ölçülen sayı yalnız bu kümede ve bu modelde değişmemiştir. Karar ağacı bölmelerini sıralamaya göre yapar ve standartlaştırma sıralamayı değiştirmez; ortanca ile doldurma ise 103 satırı etkiler ve iki ortanca birbirine yakındır. Sızıntının görünmemesi yokluğunun kanıtı değildir.
Üçüncü satır hedef temelli kodlamadır ve sayı 0,0397 şişiyor: on abone. Neden açık — bu adım etiketi doğrudan kullanır. Tarife kodunun 119 düzeyi vardır ve her düzeye ortalama on satır düşer; kodlama bütün kümeden öğrenildiğinde bir sınama satırının kendi etiketi, o satırın göreceği sayının onda birini oluşturur. Dördüncü satırda seçim de bütün kümeden yapılıyor, seçilen sütun sayısı sekizden dokuza çıkıyor ve toplam şişme 0,0476’ya, on iki aboneye ulaşıyor.
Kursun ikinci kuralı bu tabloda tamamlanıyor: her hazırlama adımı yalnız eğitim kümesinden öğrenilir ve ayrılmış kümeye uygulanır. Boru hattı bu kuralı bir yordama çevirir; kural yazılı bir uyarı olmaktan çıkıp nesnenin arayüzü olur.
Boru Hattının Ölçtüğü
Boru hattının ikinci yararı sızıntıyı önlemek değil, adımları ölçülebilir kılmaktır. Zincirden bir adım çıkarılıp aynı ölçüm tekrarlandığında o adımın dürüst katkısı okunur.
DAR = TABAN_ALAN + ["a_muhur"] print("boru hattinin ogrendigi nesneler: " + ", ".join( f"{a.ad} {len(a.nesne)}" for a in BH.adimlar)) gor = set(BH.adimlar[2].nesne) print(f"tarife kodunun {len({x['tarife_kodu'] for x in VERI})} duzeyinden {len(gor)} " f"tanesi egitimde goruldu; sinamada gorulmemis kategoriye dusen " f"{sum(x['tarife_kodu'] not in gor for x in SIN)} satir\n") print(f"{'zincir':<22} {'adim':>4} {'sutun':>5} {'egitimde':>9} {'ayrilmis':>9} " f"{'katki':>8}") print(f"{'dort adim':<22} {4:>4} {len(BH.adimlar[-1].nesne):>5} {BH.egitimde:>9.4f} " f"{BH.olc(SIN):>9.4f}") for ad, al in (("olcekleme cikarildi", [Doldurma(), HedefKodlama(), Secim(HAVUZ)]), ("hedef kodlama cikti", [Doldurma(), Olcekleme(), Secim(DAR)]), ("yalniz doldurma+secim", [Doldurma(), Secim(DAR)])): b = BoruHatti(al).ogren(EGT) print(f"{ad:<22} {len(al):>4} {len(b.adimlar[-1].nesne):>5} {b.egitimde:>9.4f} " f"{b.olc(SIN):>9.4f} {b.olc(SIN) - BH.olc(SIN):>+8.4f}")
boru hattinin ogrendigi nesneler: doldurma 1, olcekleme 3, hedef kodlama 116, secim 8 tarife kodunun 119 duzeyinden 116 tanesi egitimde goruldu; sinamada gorulmemis kategoriye dusen 1 satir zincir adim sutun egitimde ayrilmis katki dort adim 4 8 0.8955 0.7817 olcekleme cikarildi 3 8 0.8955 0.7817 +0.0000 hedef kodlama cikti 3 7 0.8730 0.8135 +0.0317 yalniz doldurma+secim 2 7 0.8730 0.8135 +0.0317
Boru hattı toplam 131 sayı öğreniyor: bir ortanca, üç sütun için ikişer sayı, 116 tarife düzeyi için birer oran ve sekiz sütunluk bir liste. Bu 131 sayının hepsi eğitim kümesinin 756 satırından gelir ve hiçbiri sınama kümesine bakmaz. Sızıntı tartışması bu sayının ne kadarının hangi kümeden geldiği tartışmasıdır.
Tarife kodunun 119 düzeyinin 116’sı eğitim kümesinde görülmüş; kalan üç düzeyden biri sınama kümesinde bir satırla karşımıza çıkıyor ve görülmemiş kategori olarak genel orana düşüyor. Elle yazılmış bir hazırlama zincirinde bu satır tipik olarak bir hataya ya da sessiz bir boş değere dönüşür; boru hattında adımın uygulama yüzü kararı bir kez verir ve her kümede aynı kararı verir.
Alt tablo iki adımın dürüst katkısını okuyor. Ölçekleme çıkarıldığında sayı hiç değişmiyor: katkı tam sıfır, çünkü karar ağacı sıralamaya bakar ve doğrusal bir ölçek dönüşümü sıralamayı değiştirmez. Adım yanlış yapıldığında da bir şey değiştirmediği için sessizce yanlış kalabilir. Hedef temelli kodlama çıkarıldığında ise sayı 0,7817’den 0,8135’e yükseliyor: adımın dürüst katkısı eksi 0,0317’dir. Yüksek kardinaliteli bir sütunu hedefle kodlamak, doğru kümeden öğrenilse bile bu kümede zarar veriyor; bütün kümeden öğrenildiğinde ise 0,0397 kazandırıyor görünüyordu. Aynı adım, sızdırıldığında en kârlı, dürüst uygulandığında en zararlı adımdır.
Bu dersin karar öncesi ve karar sonrası sayıları bu yüzden ters yönde okunur. Elle uygulanan zincir 0,8294, boru hattı 0,7817 veriyor; katkı eksi 0,0476. Eksi işaret burada kaybı değil, kaybedilen sayının öğrenilmiş olmadığını gösterir. Boru hattının kattığı şey sayı değil, sayının doğruluğudur.
Özet
- Hazırlama adımı iki yüzlü bir nesnedir:
ogrenyalnız eğitim kümesini görür ve öğrendiğini saklar,uygulahiçbir şey öğrenmeden o sayıları her kümeye işler. - Zincir elle uygulandığında dört adımın dördü de bütün kümeden öğrenilebilir; boru hattında karar tek bir yerde durur ve adım eklemek sızıntı riskini artırmaz.
- Şişme adım adım 0,0000, 0,0000, 0,0397 ve 0,0476 çıkıyor; ilk iki adımın sızması sayıyı değiştirmiyor, ve sızıntının görünmemesi yokluğunun kanıtı değildir.
- Boru hattı 131 sayı öğreniyor ve hepsi eğitim kümesinin 756 satırından geliyor; tarife kodunun 119 düzeyinden 116’sı görülmüş, sınamada bir satır görülmemiş kategoriye düşüyor.
- Aynı adım sızdırıldığında 0,0397 kazandırıyor, dürüst uygulandığında 0,0317 kaybettiriyor; boru hattının katkısı eksi 0,0476’dır ve bu eksi, kaybedilen sayının öğrenilmiş olmadığını gösterir.
Sonraki Adım
Boru hattı zincirin tamamını tek bir kümeden öğrenmeye bağladı ve ölçüm tek bir bölmeden okundu: 756 satır eğitim, 252 satır sınama. O 252 satır bir kez kullanıldı ve verdiği sayı tek bir sayıdır; başka bir bölmede başka bir sayı çıkardı. Ölçümü birden çok bölme üzerinden almanın adı çapraz doğrulamadır ve hazırlama zinciri buraya girince yeni bir soru doğar: zincir kat dışında bir kez mi öğrenilir, yoksa her katın içinde yeniden mi. İki yordam iki ayrı sayı verir ve sonraki ders hangisinin ayrılmış kümedeki gerçek sayıya yakın çıktığını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.