Ders 07 / 20
Bağımsızlık Varsayımlı Olasılıksal Sınıflandırıcı
Sütunları toplamak yerine sayan bir ailenin ölçülmesi: sekiz sütunun 201.600 bileşimi yerine 80 hücrelik bir sayım tablosu tutulur, yirmi aday denenir ve doğrulama kümesinden seçilen aday sınama kümesinde 0,7817 verir. Sıfır sayım bir sınıfı tümüyle eler; 60 satırlık eğitimde 137 sınama satırı vetolanır ve düzleştirme vetoyu kaldırır ama doğruluğu güvenceye almaz. Aynı bilgiyi taşıyan sütun sekiz kez eklendiğinde uç olasılıklı abone 2'den 169'a, ortalama güven 0,842'den 0,970'e çıkar; sıralamanın üst 61'indeki isabet 29'dan 33'e gider.
İçindekiler
Önceki ders on iki sütunu tek bir uzaklık toplamına indirdi. O toplamda bölge göstergesiyle ortalama tüketim aynı terim listesinde durur ve kararı hangisinin taşıdığı görünmez; kategorik bir alan için “uzaklık” zaten sonradan takılmış bir kavramdır. Bu ders sütunları toplamayan, sayan bir aile alır: her sütunun her düzeyinin her sınıf altında kaç kez göründüğünü sayar.
Sayarak sınıflandırmanın önünde bir engel vardır. Belirli bir sütun bileşiminin kaç kez şüpheli çıktığını doğrudan saymak mümkün değildir, çünkü bileşimlerin neredeyse tamamı eğitim kümesinde hiç görülmemiştir. Aile bu engeli tek bir varsayımla aşar: sınıf verildiğinde sütunların birbirinden bağımsız olduğunu kabul eder ve ortak sayımı tek tek sütun sayımlarının çarpımıyla değiştirir. Varsayım bu kümede doğru değildir; ders yanlışlığın iki ayrı sonucunu ayrı ayrı sayar.
- DO9. Küme, bölme ve taban çizgisi değişmez: 1.260 abonelik kurgu tablo, tohum 20260218, bölme 756/252/252, sınama kümesinde taban çizgisi 0,7579 ve 61 şüpheli abone.
- DO10. Öznitelik kümesi sabittir; değişen yalnız gösterimidir. Bölgenin beş ikili sütunu tek bir beş düzeyli sütuna döner, ortalama tüketim M26’dan gelen 10, 25 ve 40 m³ tarife basamaklarına, oynaklık ile kişi başına tüketim eşit sayıda satır taşıyan aralıklara ayrılır. Aralık sınırları yalnız eğitim kümesinden öğrenilir.
- DO11. İki ayar değişkeni vardır ve ikisi de veriden öğrenilmez: aralık sayısı ve düzleştirme katsayısı. Çarpım logaritma toplamı olarak hesaplanır; sekiz küçük olasılığın doğrudan çarpımı kayan noktada sıfıra iner.
- DO12. Önsel, bir sınıfın eğitim kümesindeki oranıdır; sonsal, sütunlar görüldükten sonraki olasılığıdır. Karar eşiği 0,5’tir; eşik seçimi kursun değerlendirme konusuna bırakılır.
- DO13. Metin sınıflandırma kurulmaz ve M27/K06’ya bırakılır; kullanılan bütün alanlar kurgu abone tablosunun kategorik alanlarıdır.
- DO14. Seçim doğrulama kümesinden okunur. Sıralama ölçümünde en yüksek olasılıklı 61 abone alınır; 61, sınama kümesindeki gerçek şüpheli sayısıdır.
Sayımdan Olasılığa
Kurulum kümeyi üretir, düzey gösterimini kurar ve varsayımın gerekçesini iki sayıyla verir.
# sayan-model.py — MODELDIR. M27/K01 ve K02'nin KURGU abone tablosu ayni tohumla # uretilir; oznitelik kumesi sabittir, yalniz gosterimi duzeylere cevrilir. import collections import math import statistics 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 = {"hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v), "ort_tuketim": round(ort, 2), "oynaklik": round(oyn, 2), "bolge_ad": k["bolge"][0], "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35)} x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3) VERI.append(x) 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:] TABAN_SIN = sum(x["supheli"] == 0 for x in SIN) / len(SIN) def basamak(v): # tarife basamaklari 10 / 25 / 40 m3 return 0 if v < 10 else (1 if v < 25 else (2 if v < 40 else 3)) def kesimler(egt, a, n): # OGRENME: aralik sinirlari egitimden v = sorted(x[a] for x in egt) return [v[int(i * len(v) / n)] for i in range(1, n)] def duzey(v, kesim): return sum(1 for k in kesim if v >= k) def kategorik(s, kesim): # UYGULAMA return [({"tarife": basamak(x["ort_tuketim"]), "bolge": x["bolge_ad"], "hane": x["hane"], "memnuniyet": x["memnuniyet"], "donem": x["donem"], "sifir_okuma": x["sifir_okuma"], "oynaklik": duzey(x["oynaklik"], kesim["oynaklik"]), "kisi_basi": duzey(x["kisi_basi"], kesim["kisi_basi"])}, x["supheli"]) for x in s] SUTUN = ["tarife", "bolge", "hane", "memnuniyet", "donem", "sifir_okuma", "oynaklik", "kisi_basi"] KESIM = {a: kesimler(EGT, a, 8) for a in ("oynaklik", "kisi_basi")} KE, KD, KS = kategorik(EGT, KESIM), kategorik(DOG, KESIM), kategorik(SIN, KESIM) print(f"egitim {len(KE)}, sinama {len(KS)}, sinamada supheli {sum(y for _, y in KS)}, " f"taban cizgisi {TABAN_SIN:.4f}") DUZEY = {a: {x[a] for x, _ in KE} for a in SUTUN} bilesim, hucre = 1, 0 for a in SUTUN: bilesim, hucre = bilesim * len(DUZEY[a]), hucre + 2 * len(DUZEY[a]) print(f"sutun {len(SUTUN)}, bilesim sayisi {bilesim}, egitim satiri {len(KE)}, " f"sutunlara ayrilmis sayim tablosunda hucre {hucre}")
egitim 756, sinama 252, sinamada supheli 61, taban cizgisi 0.7579 sutun 8, bilesim sayisi 201600, egitim satiri 756, sutunlara ayrilmis sayim tablosunda hucre 80
İkinci satır varsayımın gerekçesidir. Sekiz sütunun düzeyleri 201.600 ayrı bileşim üretiyor ve elde 756 eğitim satırı var; ortak sayımı doğrudan tutmak bir seçenek değildir. Sütunları ayrı ayrı saymak aynı tabloyu 80 hücreye indirir. Bedeli çarpmanın kendisindedir: bir abonenin şüpheli olma olasılığı, sınıfın önseli ile her sütundaki düzeyinin o sınıf altındaki oranlarının çarpımıdır ve bu çarpım ancak sütunlar sınıf verildiğinde bağımsızsa doğru sayıyı verir.
Modelin Sayısı ve Yirmi Aday
Model iki ayar değişkeniyle yazılır ve bir ızgara taranır. Basılan tablo doğrulama kümesinindir; sınama kümesinin sayıları özet satırlarında verilir.
def ogren(kume, sutun): # MODELDIR: sayimlar ve onsel oranlar top = collections.Counter(y for _, y in kume) say = {(a, c): collections.Counter(d[a] for d, y in kume if y == c) for a in sutun for c in (0, 1)} return top, say, {a: len({d[a] for d, _ in kume}) for a in sutun} def sonsal(m, d, sutun, alfa): # sinif 1 olasiligi; carpim log toplamidir top, say, ds = m n, lg = sum(top.values()), [] for c in (0, 1): t = math.log(top[c] / n) for a in sutun: pay = say[(a, c)][d[a]] + alfa t = -math.inf if pay <= 0.0 else t + math.log(pay / (top[c] + alfa * ds[a])) lg.append(t) if lg[0] == -math.inf and lg[1] == -math.inf: return None # her iki sinif da elendi: model susuyor e = [math.exp(t - max(lg)) for t in lg] return e[1] / (e[0] + e[1]) def degerlendir(egt, hedef, sutun, alfa): m, p, susan = ogren(egt, sutun), [], 0 for d, y in hedef: o = sonsal(m, d, sutun, alfa) if o is None: susan, o = susan + 1, 0.0 p.append((o, y)) return sum((o > 0.5) == y for o, y in p) / len(p), p, susan IZGARA, ALFALAR = {}, (0.0, 0.25, 1.0, 4.0) for n in (3, 4, 6, 8, 12): KS_N = {a: kesimler(EGT, a, n) for a in ("oynaklik", "kisi_basi")} E2, D2, S2 = kategorik(EGT, KS_N), kategorik(DOG, KS_N), kategorik(SIN, KS_N) for alfa in ALFALAR: IZGARA[(n, alfa)] = (degerlendir(E2, D2, SUTUN, alfa)[0], degerlendir(E2, S2, SUTUN, alfa)[0]) print(f"{'aralik':>6}" + "".join(f"{a:>9}" for a in ALFALAR) + " (dogrulama)") for n in (3, 4, 6, 8, 12): print(f"{n:>6}" + "".join(f"{IZGARA[(n, a)][0]:>9.4f}" for a in ALFALAR)) sd = max(IZGARA, key=lambda a: (IZGARA[a][0], -a[0], -a[1])) ss = max(IZGARA, key=lambda a: (IZGARA[a][1], -a[0], -a[1])) print(f"\ndenenen aday {len(IZGARA)}") for ad, a in (("dogrulamadan", sd), ("sinamadan", ss)): print(f"{ad + ' secilen:':<22}aralik {a[0]}, alfa {a[1]} -> dogrulama " f"{IZGARA[a][0]:.4f}, sinama {IZGARA[a][1]:.4f}") print(f"ailenin sayisi {IZGARA[sd][1]:.4f}, taban cizgisinin " f"{IZGARA[sd][1] - TABAN_SIN:+.4f} ustunde; iki secim kumesi " f"{IZGARA[ss][1] - IZGARA[sd][1]:+.4f} ayriliyor")
aralik 0.0 0.25 1.0 4.0 (dogrulama)
3 0.7540 0.7540 0.7540 0.7579
4 0.7540 0.7540 0.7540 0.7579
6 0.7738 0.7738 0.7659 0.7698
8 0.7778 0.7778 0.7857 0.7857
12 0.7579 0.7579 0.7619 0.7698
denenen aday 20
dogrulamadan secilen: aralik 8, alfa 1.0 -> dogrulama 0.7857, sinama 0.7817
sinamadan secilen: aralik 4, alfa 4.0 -> dogrulama 0.7579, sinama 0.7857
ailenin sayisi 0.7817, taban cizgisinin +0.0238 ustunde; iki secim kumesi +0.0040 ayriliyor
Ailenin sayısı 0,7817’dir, taban çizgisini 0,0238 — altı abone — geçer ve 20 aday denenerek bulunmuştur. Önceki dersin örnek tabanlı yordamı aynı öznitelik kümesiyle 0,7937 okumuştu. Olasılık üreten ve açık bir varsayıma dayanan bu aile, uzaklık toplayan yordamın gerisinde kaldı; fark üç abone. Kursun ikinci iddiasının ilk örneği budur ve saklanmaz.
İki seçim kümesi ayrı adaylar veriyor. Doğrulamadan sekiz aralık ile 1,0 çıkıyor; sınamadan bakılsaydı dört aralık ile 4,0 seçilir ve bildirilen sayı 0,7857 olurdu. O adayın doğrulama sayısı 0,7579’dur, yani yirmi adayın en kötüleri arasındadır.
Sıfır Sayım ve Düzleştirme
Seksen hücrenin en seyreği tarife sütununun en üst basamağı ile şüpheli sınıfının kesişimidir ve sayımı sıfırdır: kırk metreküpü aşan tüketimi olan bir abone eğitimde şüpheli olarak hiç görülmemiş. Çarpımda bir sıfır, öteki yedi sütunun söylediği her şeyi siler. Düzleştirme her sayıma küçük bir sabit ekler; gerekçesi bir başarım ayarı değil bu yapısal kusurdur.
def veto_hesabi(egt, hedef, sutun, tum_duzey): gorulen = {(a, c): {d[a] for d, y in egt if y == c} for a in sutun for c in (0, 1)} bos = sum(len(tum_duzey[a] - gorulen[(a, c)]) for a in sutun for c in (0, 1)) veto = yanlis = 0 for d, y in hedef: elenen = [c for c in (0, 1) if any(d[a] not in gorulen[(a, c)] for a in sutun)] if elenen: veto, yanlis = veto + 1, yanlis + (y in elenen) return bos, veto, yanlis print(f"{'egitim':>6} {'bos hucre':>10} {'vetolanan':>10} {'yanlis veto':>12} " f"{'alfa=0':>8} {'alfa=1':>8}") for m in (60, 120, 250, 500, 756): b, v, y = veto_hesabi(KE[:m], KS, SUTUN, DUZEY) print(f"{m:>6} {b:>10} {v:>10} {y:>12} " f"{degerlendir(KE[:m], KS, SUTUN, 0.0)[0]:>8.4f} " f"{degerlendir(KE[:m], KS, SUTUN, 1.0)[0]:>8.4f}")
egitim bos hucre vetolanan yanlis veto alfa=0 alfa=1
60 8 137 24 0.7341 0.7302
120 4 56 9 0.7460 0.7579
250 2 1 1 0.7381 0.7540
500 1 1 1 0.7659 0.7698
756 1 1 1 0.7778 0.7817
Altmış satırlık eğitimde seksen hücrenin sekizi boştur ve bu sekiz boşluk 252 sınama satırının 137’sini vetoluyor; 24’ünde vetolanan sınıf abonenin gerçek sınıfıdır, yani karar sekiz sütunun kanıtına değil tek bir eksik sayıma dayanıyor. Eğitim 756 satıra çıktığında boş hücre bire, veto tek satıra iner; düzleştirme katsayısı sıfırdan büyük olduğu anda veto tümüyle kalkar.
Son iki sütun düzeltmenin sınırını gösteriyor. Altmış satırlık eğitimde düzleştirme sayıyı 0,7341’den 0,7302’ye düşürüyor; 120 satırda 0,7460’tan 0,7579’a çıkarıyor. Ödediği şey doğruluk değil, modelin susmaması.
Varsayım Bozulduğunda
Koşullu bağımsızlık ölçülebilir bir iddiadır: iki sütunun sınıf içindeki ortak sayımı tek tek oranlarının çarpımından ne kadar sapıyor. Sapma sıfırsa varsayım o çift için doğrudur.
def bagimlilik(kume, a, b): # sinif verildiginde ortak sayimin carpimdan sapmasi t = 0.0 for c in (0, 1): alt = [d for d, y in kume if y == c] n = len(alt) pa = collections.Counter(d[a] for d in alt) pb = collections.Counter(d[b] for d in alt) pab = collections.Counter((d[a], d[b]) for d in alt) t += n / len(kume) * sum(abs(pab[(u, v)] / n - pa[u] * pb[v] / n / n) for u in pa for v in pb) / 2 return t for a, b in (("hane", "kisi_basi"), ("donem", "oynaklik"), ("donem", "sifir_okuma")): print(f"{a:<8} {b:<12} sapma {bagimlilik(KE, a, b):.3f}") def kopyali(kume, k): # ayni bilgiyi tasiyan sutunun tabloya yeniden girmesi return [(dict(d, **{f"tarife_{i}": d["tarife"] for i in range(k)}), y) for d, y in kume] POZ = sum(y for _, y in KS) print(f"\n{'kopya':>5} {'sutun':>6} {'dogruluk':>9} {'uc olasilik':>12} " f"{'ort guven':>10} {'ust-' + str(POZ) + ' isabet':>14}") for k in (0, 1, 2, 4, 8): sut = SUTUN + [f"tarife_{i}" for i in range(k)] d, p, _ = degerlendir(kopyali(KE, k), kopyali(KS, k), sut, 1.0) ust = sorted(p, key=lambda t: -t[0])[:POZ] print(f"{k:>5} {len(sut):>6} {d:>9.4f} " f"{sum(1 for o, _ in p if o > 0.99 or o < 0.01):>12} " f"{statistics.fmean(max(o, 1 - o) for o, _ in p):>10.3f} " f"{sum(y for _, y in ust):>14}")
hane kisi_basi sapma 0.407
donem oynaklik sapma 0.235
donem sifir_okuma sapma 0.011
kopya sutun dogruluk uc olasilik ort guven ust-61 isabet
0 8 0.7817 2 0.842 29
1 9 0.7778 5 0.862 32
2 10 0.7778 17 0.889 33
4 12 0.7817 72 0.932 32
8 16 0.7857 169 0.970 33
Varsayım bu tabloda zaten bozuktur. Hane büyüklüğü ile kişi başına tüketimin sapması 0,407’dir ve bu tanımın kendisidir: kişi başına tüketim, ortalama tüketimin hane büyüklüğüne bölünmüş halidir. Dönem sayısıyla oynaklık 0,235 sapıyor, çünkü tek dönemi olan abonenin oynaklığı sıfırdır. En bağımsız çift 0,011 ile dönem sayısı ve sıfır okumadır.
Alt tablo iki sonucu ayırır. Olasılık çıktısı uçlara gidiyor: uç olasılıklı abone sayısı 2’den 169’a, ortalama güven 0,842’den 0,970’e çıkıyor; aynı kanıt sekiz kez çarpıldığında model bağımsız sekiz kanıt görmüş gibi davranır. Sıralama ise ayakta kalıyor: üst 61 abonedeki gerçek şüpheli sayısı 29’dan 33’e çıkıyor, doğruluk 0,7817’den 0,7857’ye gidiyor. Kopyalanan sütun bütün abonelerin skorunu aynı yönde ittiği için sıralamayı bozmuyor; bozduğu şey sayının büyüklüğüdür. Bu ailenin çıktısı bir sıralama skoru olarak kullanılabilir, bir güven ölçüsü olarak kullanılamaz.
Özet
- Sekiz sütunun 201.600 bileşimine karşılık 756 eğitim satırı vardır; koşullu bağımsızlık varsayımı sayım tablosunu 80 hücreye indirir ve ailenin tek gerekçesi budur.
- Ailenin sayısı 0,7817’dir, taban çizgisini 0,0238 geçer ve 20 aday denenerek bulunmuştur; önceki dersin örnek tabanlı yordamı aynı öznitelik kümesiyle 0,7937 okumuştu.
- Sınamadan seçim yapılsaydı başka bir aday seçilir ve bildirilen sayı 0,7857 olurdu.
- Sıfır sayım bir sınıfı tümüyle eler: 60 satırlık eğitimde 137 sınama satırı vetolanıyor, 24’ünde vetolanan sınıf doğru sınıftır. Düzleştirme vetoyu kaldırır ama doğruluğu güvenceye almaz; 60 satırda sayı 0,7341’den 0,7302’ye düşüyor.
- Varsayım bozulduğunda iki sonuç ayrışır: uç olasılıklı abone 2’den 169’a çıkarken üst 61’deki isabet 29’dan 33’e gidiyor.
Sonraki Adım
Bu ailenin kararı bütün eğitim satırlarının sayımına dayanıyor: sınırdan uzakta duran abone ile tam sınırın üstünde duran abone aynı hücreyi bir birim büyütüyor. Önceki dersin komşu yordamında da 31 komşunun her biri eşit oy veriyordu. Sonraki ders bunun tersini yapan bir aile alır. Marj temelli sınıflandırıcı yalnız sınıra yakın örneklerle ilgilenir; sınırı iki sınıf arasındaki boşluğun ortasına oturtur ve kararı taşıyan örnekleri destek vektörü olarak açıkça sayar. Ders marj genişliğini ayarlayan katsayıyı süpürür, her genişlikte kaç örneğin kararı taşıdığını basar ve eğri sınırı sütun açmadan üreten çekirdek dönüşümünü açıkça hesaplanmış polinom öznitelikleriyle ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.