İçeriğe geç
academia.sh

Ders 08 / 15

Yerel Açıklama Yöntemleri

Tek bir tahmini açıklayan iki yöntemin aynı abonelere sorulması ve açıklamanın sadakatinin ölçülmesi: yerel vekil model komşuluktaki kararların ortalama 0,9417'sini doğru veriyor, ama 252 abonenin 41'inde komşuluk tek etiket taşıdığı için açıklama boş çıkıyor. Vekil modelle katkı payı dağıtımı 211 dolu açıklamanın 145'inde ayrı sütun gösteriyor. Sadakat batı bölgesinde 0,9664 ile en yüksek, orada boş açıklama oranı 0,4688; en düşük sadakat 0,9027 ile en üst tarife basamağındadır ve açıklık 0,0637'dir.

İçindekiler

Önceki dersin iki sıralaması da modelin bütününe aitti. Kurgudaki abone şüpheli işaretini aldığında modelin genel olarak neye dayandığını değil, kendi kararının neye dayandığını sorar. Güney bölgesinde küresel sıralamanın ilk sırasının değişmesi, bütünün cevabının tek bir abone için doğru olmayabileceğini göstermişti.

Yerel açıklama bu soruya iki ayrı yapıyla cevap verir. Birincisi, asıl modelin bir örneğin çevresindeki davranışını taklit eden küçük bir model kurar; ikincisi, çıktıyı öznitelikler arasında paylaştırır. İkisi de bir sütun adı üretir; bu ders o iki adın ne kadar sık aynı olduğunu ve açıklamanın asıl modeli gerçekten anlatıp anlatmadığını sayar.

  • YO14. Kurulum önceki iki dersinkidir: aynı kurgu abone tablosu, tohum 20260218, bölme 756/252/252, taban çizgisi 0,7579, doğrusal model sınamada 0,8016. Bakılan alt grup sayısı 13, okuma eşiği 25 kayıt.
  • YO15. Yerel vekil model, açıklanan abonenin çevresinde üretilmiş 40 komşuya oturtulan tek bölmeli küçük bir modeldir. Komşular sayısal sütunların bir standart sapmalık aralığında oynatılmasıyla, iki değerli sütunun ve bölgenin 0,2 olasılıkla değişmesiyle üretilir; etiketleri gerçek etiket değil asıl modelin kararıdır.
  • YO16. Açıklamanın sadakati, vekil modelin komşuluktaki kararların kaçını doğru verdiğidir. Komşuluğun tamamı tek etiket taşıyorsa vekil model sabittir: sadakati 1 olur, açıklaması boştur.
  • YO17. Katkı payı dağıtımı, taban kayıttan başlayıp abonenin sütunlarını rastgele bir sırayla tek tek yerine koyar, her adımda puandaki değişimi kaydeder ve 24 sıra üzerinden ortalar. Taban kayıt eğitim kümesinin ortalamasıdır, bölgesi beş göstergenin eğitim ortalamasıdır.
  • YO18. Tek özniteliği bozma, katkı payının tek sıralı biçimidir: yalnız bir sütun taban değerine çekilir. İki yöntem karşılaştırılırken her birinin verdiği ilk sütun karşılaştırılır.

İki Yöntem, Aynı Abone

Kurulum önceki derslerinkiyle aynıdır.

# yorum.py — MODELDIR. M27/K01–K07'nin KURGU abone tablosu ayni tohumla uretilir;
# on iki sutunluk oznitelik kumesi ve bolme kurs boyunca sabittir.
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),
         "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35),
         "bolge": k["bolge"][0]}
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    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:]
ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma",
        "kisi_basi", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
TABAN = sum(x["supheli"] == 0 for x in SIN) / len(SIN)


def sikistir(z):
    if z >= 0:
        return 1.0 / (1.0 + math.exp(-z)) if z < 700 else 1.0
    e = math.exp(z) if z > -700 else 0.0
    return e / (1.0 + e)


def olcek(egt, alan):
    return ({a: statistics.fmean(x[a] for x in egt) for a in alan},
            {a: max(1e-9, statistics.pstdev([x[a] for x in egt])) for a in alan})


def egit(egt, alan, o, s, adim=0.3, tur=300):    # MODELDIR: lojistik baglanim
    X = [[1.0] + [(x[a] - o[a]) / s[a] for a in alan] for x in egt]
    Y = [x["supheli"] for x in egt]
    w = [0.0] * len(X[0])
    for _ in range(tur):
        g = [0.0] * len(w)
        for i in range(len(X)):
            h = sikistir(sum(w[j] * X[i][j] for j in range(len(w)))) - Y[i]
            for j in range(len(w)):
                g[j] += h * X[i][j]
        for j in range(len(w)):
            w[j] -= adim * g[j] / len(X)
    return w


def puan(w, alan, o, s, x):
    return sikistir(w[0] + sum(w[j + 1] * (x[a] - o[a]) / s[a]
                               for j, a in enumerate(alan)))


def karar(w, alan, o, s, x):
    return int(puan(w, alan, o, s, x) > 0.5)


O, S = olcek(EGT, ALAN)
W = egit(EGT, ALAN, O, S)
DOGRULUK = sum(karar(W, ALAN, O, S, x) == x["supheli"] for x in SIN) / len(SIN)
print(f"egitim {len(EGT)}, dogrulama {len(DOG)}, sinama {len(SIN)}, taban "
      f"cizgisi {TABAN:.4f}, dogrusal modelin sinama dogrulugu {DOGRULUK:.4f}")


def basamak(x):
    return ("tarife 10" if x["ort_tuketim"] <= 10 else
            "tarife 25" if x["ort_tuketim"] <= 25 else "tarife 40")


def hane_grup(x):
    return "hane 1-2" if x["hane"] <= 2 else ("hane 3-4" if x["hane"] <= 4
                                              else "hane 5+")


def okuma_grup(x):
    return "okuma az" if x["donem"] <= 2 else "okuma tam"


def esitse(f, deger):
    return lambda x: f(x) == deger


ALTGRUP = ([("bolge " + b[0], esitse(lambda x: x["bolge"], b[0])) for b in BOLGE]
           + [(t, esitse(basamak, t)) for t in ("tarife 10", "tarife 25", "tarife 40")]
           + [(h, esitse(hane_grup, h)) for h in ("hane 1-2", "hane 3-4", "hane 5+")]
           + [(o, esitse(okuma_grup, o)) for o in ("okuma az", "okuma tam")])
ENAZ = 25
BOY = {ad: sum(1 for x in SIN if f(x)) for ad, f in ALTGRUP}
print(f"alt grup sayisi {len(ALTGRUP)}, esik {ENAZ} kayit, esigin altinda "
      f"{[a for a in BOY if BOY[a] < ENAZ]}")
egitim 756, dogrulama 252, sinama 252, taban cizgisi 0.7579, dogrusal modelin sinama dogrulugu 0.8016
alt grup sayisi 13, esik 25 kayit, esigin altinda ['tarife 10']

İki yöntem art arda kurulur ve her abone için birer sütun adı üretir.

YEREL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma",
         "bolge"]
TABAN_K = {a: statistics.fmean(x[a] for x in EGT) for a in
           ("ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem",
            "sifir_okuma")}
TABAN_K["bolge"] = None          # taban kayitta bolge tek bir bolge degil, ortalama
B_ORT = {"b_" + b[0]: statistics.fmean(x["b_" + b[0]] for x in EGT) for b in BOLGE}


def duzelt(d):                   # turetilmis sutunlari yeniden hesaplar
    d["kisi_basi"] = round(d["ort_tuketim"] / max(1, d["hane"]), 3)
    if d["bolge"] is None:
        d.update(B_ORT)
    else:
        for b in BOLGE:
            d["b_" + b[0]] = int(d["bolge"] == b[0])
    return d


TABAN_KAYIT = duzelt(dict(SIN[0], **TABAN_K))


def komsuluk(x, sayi=40, tohum=TOHUM + 600000):
    r, c = uretec(tohum + int(x["ort_tuketim"] * 100) + x["hane"]), []
    for _ in range(sayi):
        d = dict(x)
        for a in ("ort_tuketim", "oynaklik", "memnuniyet", "donem"):
            d[a] = x[a] + (2 * r() - 1) * S[a]
        d["hane"] = max(1, round(x["hane"] + (2 * r() - 1) * S["hane"]))
        if r() < 0.2:
            d["sifir_okuma"] = 1 - x["sifir_okuma"]
        if r() < 0.2:
            d["bolge"] = BOLGE[int(r() * 5)][0]
        c.append(duzelt(d))
    return c


def vekil(x):                    # MODELDIR: tek bolmeli karar agaci, yerel vekil
    c = komsuluk(x)
    y = [karar(W, ALAN, O, S, k) for k in c]
    if len(set(y)) == 1:
        return None, 1.0         # komsulugun tamami tek etiket: aciklama bos
    en = (0.0, None)
    for a in YEREL:
        d = sorted({k[a] for k in c})
        aday = d[1:] if a in ("sifir_okuma", "bolge") else \
            [d[int(i * len(d) / 8)] for i in range(1, 8)]
        for v in aday:
            sol = {i for i in range(len(c))
                   if (c[i][a] != v if a == "bolge" else c[i][a] < v)}
            if not sol or len(sol) == len(c):
                continue
            ps = sum(y[i] for i in sol) * 2 > len(sol)
            pg = sum(y[i] for i in range(len(c)) if i not in sol) * 2 > len(c) - len(sol)
            u = sum((ps if i in sol else pg) == y[i] for i in range(len(c))) / len(c)
            if u > en[0]:
                en = (u, a)
    return en[1], en[0]


def katki(x, tur=24, tohum=TOHUM + 700000):
    r = uretec(tohum + int(x["ort_tuketim"] * 100) + x["hane"])
    pay = {a: 0.0 for a in YEREL}
    for _ in range(tur):
        sira = list(YEREL)
        for i in range(len(sira) - 1, 0, -1):
            j = int(r() * (i + 1))
            sira[i], sira[j] = sira[j], sira[i]
        d, onceki = dict(TABAN_KAYIT), puan(W, ALAN, O, S, TABAN_KAYIT)
        for a in sira:
            d[a] = x[a]
            yeni = puan(W, ALAN, O, S, duzelt(d))
            pay[a] += yeni - onceki
            onceki = yeni
    return {a: pay[a] / tur for a in YEREL}


def tek_bozma(x):                # tek ozniteligi bozma: bir sutun tabana cekilir
    p0 = puan(W, ALAN, O, S, x)
    return {a: p0 - puan(W, ALAN, O, S, duzelt(dict(x, **{a: TABAN_K[a]})))
            for a in YEREL}


SADAKAT, VSUT, KSUT, TSUT = [], [], [], []
for x in SIN:
    a, u = vekil(x)
    p, t = katki(x), tek_bozma(x)
    SADAKAT.append(u)
    VSUT.append(a)
    KSUT.append(max(YEREL, key=lambda k: abs(p[k])))
    TSUT.append(max(YEREL, key=lambda k: abs(t[k])))
DOLU = [i for i in range(len(SIN)) if VSUT[i]]
AYRI = [i for i in DOLU if VSUT[i] != KSUT[i]]
print(f"ortalama sadakat {sum(SADAKAT) / len(SADAKAT):.4f}; komsulugunda tek "
      f"etiket bulunan, yani bos aciklama alan abone {len(SIN) - len(DOLU)}")
print(f"dolu aciklama {len(DOLU)}, bunlarda ortalama sadakat "
      f"{sum(SADAKAT[i] for i in DOLU) / len(DOLU):.4f}")
print(f"vekil model ile katki payinin ayni sutunu vermedigi abone "
      f"{len(AYRI)}/{len(DOLU)} ({len(AYRI) / len(DOLU):.4f})")
print(f"katki payi ile tek bozmanin ayristigi abone "
      f"{sum(1 for i in range(len(SIN)) if KSUT[i] != TSUT[i])}/{len(SIN)}")
i = min(AYRI, key=lambda i: abs(puan(W, ALAN, O, S, SIN[i]) - 0.5))
x, p = SIN[i], katki(SIN[i])
print(f"\nornek abone: bolge {x['bolge']}, ortalama tuketim {x['ort_tuketim']}, "
      f"sifir okuma {x['sifir_okuma']}, puan {puan(W, ALAN, O, S, x):.4f}")
print(f"  vekil modelin bolme sutunu {VSUT[i]}, sadakat {SADAKAT[i]:.4f}")
print("  katki paylari " + ", ".join(f"{a} {p[a]:+.4f}" for a in
                                     sorted(YEREL, key=lambda a: -abs(p[a]))[:3]))
ortalama sadakat 0.9417; komsulugunda tek etiket bulunan, yani bos aciklama alan abone 41
dolu aciklama 211, bunlarda ortalama sadakat 0.9303
vekil model ile katki payinin ayni sutunu vermedigi abone 145/211 (0.6872)
katki payi ile tek bozmanin ayristigi abone 27/252

ornek abone: bolge dogu, ortalama tuketim 24.67, sifir okuma 0, puan 0.4885
  vekil modelin bolme sutunu ort_tuketim, sadakat 0.7750
  katki paylari bolge +0.1631, ort_tuketim +0.1335, oynaklik +0.0428

Örnek abone iki açıklama alıyor ve ikisi başka şey söylüyor. Vekil model, o abonenin çevresinde kararı ayıran şeyin ortalama tüketim olduğunu söylüyor ve komşuluktaki kararların 0,7750’sini doğru veriyor. Katkı payı dağıtımı, aboneyi taban kayıttan bu puana taşıyan en büyük payın bölge olduğunu söylüyor: doğu bölgesinde olmak puana 0,1631 katmış. İkisi de doğrudur ve iki ayrı soruya cevap verir: birincisi “buradan çıkış hangi sütundan olur”, ikincisi “buraya hangi sütun getirdi”. 211 dolu açıklamanın 145’inde aynı sütunu vermiyorlar. Katkı payının tek sıralı biçimi olan tek özniteliği bozma ise 27 abonede ondan ayrışıyor: bir sütunun kattığı şey ondan önce hangi sütunların yerine konduğuna bağlıdır.

Sadakat Tek Başına Okunmaz

Ortalama sadakat 0,9417 ile yüksek görünüyor. Bu sayının içinde komşuluğu tek etiket taşıyan 41 abone var: vekil model sabittir, sadakati tanım gereği 1’dir ve söylediği hiçbir şey yoktur. Boş açıklamalar dışarıda bırakıldığında sadakat 0,9303’e iniyor.

print(f"{'alt grup':<14}{'kayit':>6}{'sadakat':>9}{'bos aciklama':>14}{'ayrisma':>9}")
OKUNAN = []
for ad, f in ALTGRUP:
    g = [i for i, x in enumerate(SIN) if f(x)]
    sd = sum(SADAKAT[i] for i in g) / len(g)
    bos = sum(1 for i in g if VSUT[i] is None) / len(g)
    d = [i for i in g if VSUT[i]]
    ay = sum(1 for i in d if VSUT[i] != KSUT[i]) / len(d)
    if BOY[ad] >= ENAZ:
        OKUNAN.append((ad, sd, ay))
    print(f"{ad:<14}{len(g):>6}{sd:>9.4f}{bos:>14.4f}{ay:>9.4f}")
eni, enk = max(OKUNAN, key=lambda t: t[1]), min(OKUNAN, key=lambda t: t[1])
ea = max(OKUNAN, key=lambda t: t[2])
print(f"\nokunan alt grup {len(OKUNAN)}/{len(ALTGRUP)}")
print(f"sadakat en yuksek {eni[0]} {eni[1]:.4f}, en dusuk {enk[0]} {enk[1]:.4f}, "
      f"aciklik {eni[1] - enk[1]:.4f}")
print(f"ayrisma en yuksek {ea[0]} {ea[2]:.4f}, en dusuk "
      f"{min(OKUNAN, key=lambda t: t[2])[0]} {min(t[2] for t in OKUNAN):.4f}")
alt grup       kayit  sadakat  bos aciklama  ayrisma
bolge kuzey       68   0.9401        0.0441   0.7231
bolge guney       58   0.9586        0.2414   0.7273
bolge dogu        36   0.9028        0.1389   0.7097
bolge bati        32   0.9664        0.4688   0.4118
bolge merkez      58   0.9371        0.0690   0.6852
tarife 10         12   0.9688        0.5833   1.0000
tarife 25        193   0.9495        0.1503   0.7012
tarife 40         47   0.9027        0.1064   0.5952
hane 1-2          73   0.9538        0.2603   0.6852
hane 3-4         143   0.9320        0.1329   0.7097
hane 5+           36   0.9556        0.0833   0.6061
okuma az          85   0.9456        0.2353   0.8154
okuma tam        167   0.9397        0.1257   0.6301

okunan alt grup 12/13
sadakat en yuksek bolge bati 0.9664, en dusuk tarife 40 0.9027, aciklik 0.0637
ayrisma en yuksek okuma az 0.8154, en dusuk bolge bati 0.4118

On üç alt gruba bakıldı, 12’si okundu. Sadakatin en yüksek olduğu alt grup batı bölgesidir, 0,9664; aynı satırda boş açıklama oranı 0,4688, yani otuz iki abonenin on beşi hiçbir şey söylemeyen bir açıklama alıyor. Sadakati yalnız başına raporlayan biri batıyı en iyi açıklanan grup diye yazar; iki sütun yan yana konduğunda o cümle tersine döner. Sadakat, açıklamanın bir şey söylediğini varsayar; söylemediğinde de yüksek çıkar.

En düşük sadakat 0,9027 ile en üst tarife basamağındadır ve açıklık 0,0637’dir. Yüksek tüketimli aboneler karar sınırının yakınında yoğunlaştığı için komşulukları iki etiket taşır ve tek bir bölme yetmez; aynı grup açıklamaların en dolu olduğu gruptur. İki yöntemin ayrışması ise alt gruplarda 0,4118 ile 0,8154 arasında değişiyor.

Özet

  • Yerel vekil model komşuluktaki kararların ortalama 0,9417’sini doğru veriyor; boş açıklamalar dışarıda bırakıldığında 0,9303’e iniyor.
  • 252 abonenin 41’inde komşuluk tek etiket taşıyor: vekil model sabittir, sadakati 1’dir ve açıklaması boştur. Sadakat bu yüzden tek başına okunmaz.
  • Vekil model ile katkı payı dağıtımı 211 dolu açıklamanın 145’inde ayrı sütun gösteriyor; ikisi “buradan çıkış hangi sütundan olur” ve “buraya hangi sütun getirdi” sorularına cevap verir.
  • On üç alt gruba bakıldı, 12’si okundu: sadakat 0,9027 ile 0,9664 arasında, açıklık 0,0637; en yüksek sadakatli grupta boş açıklama oranı 0,4688’dir.

Sonraki Adım

Bu derste açıklamalar tek tek abonelere aitti. Bir sütunun modele bütün olarak ne yaptığını görmenin başka bir yolu vardır: sütunun değeri bir uçtan öbür uca gezdirilirken öbür sütunlar kümenin kendi değerlerinde bırakılır ve ortalama puanın nasıl değiştiğine bakılır. Bu kısmi bağımlılıktır ve genellikle eğri olarak çizilir. Burada çizilmez, sayı basılır; sayı basıldığında ortalamanın ne gizlediği görünür olur. Sonraki ders bütünde düz görünen bir kısmi bağımlılığın iki alt grupta ters yönde eğimli olabildiğini kaç sütunda gerçekleştiğiyle sayar.

İ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