İçeriğe geç
academia.sh

Ders 01 / 11

Makine Öğrenmesi Nedir

Aynı kaçak şüphesi problemi üç yordamla çözülür ve üç sayı yan yana basılır: öğrenmeyen taban çizgisi ayrılmış kümede 0,7533, saha uzmanının elle yazdığı kural kümesi 0,7507, kurallarını veriden çıkaran üç soruluk karar ağacı 0,8594. Elle yazılmış kural tabanın 0,0027 altında kalır, ağaç 0,1061 üstüne çıkar. Fark eşiklerden gelmez: aynı eşikler elle yazılmış kurala taşındığında sayı 0,7480'e iner. Ayrılmış kümedeki 377 abonenin 93'ü şüphelidir; elle yazılmış kural 151 abone işaretler ve 76'sı boş çıkar, ağaç 66 abone işaretler ve 13'ü boş çıkar. Küme 3.199 satırlık okuma tablosundan 1.260 satırlık abone tablosuna indirilir, tohum 20260218.

İçindekiler

Veri Analitiği müfredatı doksan derste veriden karara giden yolun her adımını ölçtü ve her adımda kararın sayıyı oynattığını gösterdi. Kapanışta tek bir soru açık kaldı: bütün o kararları insan verdi. Hangi satır sayılacak, hangi eşik seçilecek, hangi kanal kullanılacak. Kararı veriden öğrenen bir yordamın hangi sayıyı oynattığı ve o yordamın kendi kararlarının nerede durduğu hiç sorulmadı.

Bu ders o soruyu sorar ve müfredatın ölçü birimini kurar. Bundan sonra bir modelin ürettiği hiçbir sayı tek başına yazılmaz; yanında taban çizgisi durur — aynı problemi öğrenmeden çözmeye çalışan bir yordamın aynı ölçüdeki sayısı. Müfredatın kuralı buradan çıkar: bir model, kendisinden daha aptal bir yordamı ne kadar geçtiği kadar iyidir. Taban çizgisi yazılmayan başarım ölçülmemiş sayılır.

  • MK1. Küme kurgudur ve Veri Analitiği müfredatından devralınır: 3.199 satırlık uzun biçimli okuma tablosu, 1.329 abone, üç dönem, beş bölge, tohum 20260218. Etiket bu müfredatta eklenir ve o da kurgudur; etiketi üreten kural koda açıkça yazılır ve hiçbir modele verilmez.
  • MK2. Makine öğrenmesi (machine learning), kuralı elle yazmak yerine örneklerden çıkarmaktır. Girdi bir kural değil, girdi–çıktı çiftlerinden oluşan bir örnek kümesidir; çıktı bir karardır ve o kararı veren yapı model diye anılır.
  • MK3. Öznitelik (feature) modele verilen sütun, etiket (label) modelin kestirmesi istenen sütundur. Aynı tabloda hangi sütunun öznitelik hangisinin etiket olduğu veriden değil, sorulan sorudan gelir.
  • MK4. Taban çizgisi (baseline), öğrenmeyen bir yordamdır ve her derste açıkça seçilir. Bu derste en sık sınıfı söylemektir; sınıflar dengesiz olduğu için bu yordam öğrenmeden de yüksek bir doğruluk üretir ve modelin geçmesi gereken çıtayı dürüstçe yükseltir.
  • MK5. Her sayı iki kümede ölçülür: yordamın kurulduğu eğitim kümesi ve yordamın hiç görmediği ayrılmış küme. Ayrımın niçin böyle yapıldığı bu konunun konusu değildir; Genelleme konusu bunu ele alır. Burada ayrım kursun kuralı olarak uygulanır.
  • MK6. Ölçünün kendisi bir karardır. Doğruluk, boşuna gidilen ziyaretle kaçırılan kaçağı aynı ağırlıkta sayar; bu ağırlık bir seçimdir ve seçildiği yerde yazılır.
  • MK7. Modeller bu müfredatta standart kitaplıkla ve küçük ölçekte yazılır. Amaç bir kitaplığı taklit etmek değil, yordamın davranışını satır satır ölçülebilir kılmaktır.

Problem ve Kurgu Küme

Soru şudur: sayaç okumalarından, kaçak ya da arıza şüphesi taşıyan aboneyi saha ekibi kapıya gitmeden önce ayırt etmek. Elde olan şey ölçüm ağının okuma tablosudur; istenen şey her abone için bir karardır. Tablo önce aynı tohumla yeniden üretilir.

# veri.py — MODELDIR: bolgesel olcum agi ayni tohumla yeniden uretilir.
# Kume kurgudur; hicbir gercek abone, sayac ya da bolge temsil edilmez.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
DONEM = ["2026-01", "2026-02", "2026-03"]
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


def uretec(t: int):
    """Dogrusal eslemeli uretec: tohum koda yazilidir, cikti kosumdan kosuma ayni."""
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki() -> float:
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


def ayrik(u: float, w: list[float]) -> int:
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_abone, _elenen = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    k = {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2],
         "hane_kisi": ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]),
         "memnuniyet": ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) + 1}
    (_elenen if r() < 0.046 else _abone).append(k)
_gecerli = {a["abone_no"] for a in _abone}

OKUMA = []
for k in _abone + _elenen:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(k["taban"] * math.exp(
            (r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100
        if k["abone_no"] in _gecerli:
            OKUMA.append({"abone_no": k["abone_no"], "bolge": k["bolge"],
                          "donem": DONEM[d], "tuketim_m3": t})
        r()
print(f"kurgu okuma tablosu (tohum {TOHUM}): {len(OKUMA)} satir, {len(_gecerli)} abone")
kurgu okuma tablosu (tohum 20260218): 3199 satir, 1329 abone

Bu tablo bir modele doğrudan verilemez. Karar abone başınadır, tablo ise okuma başınadır ve bir abone bir, iki ya da üç satırla temsil edilmektedir. Modelin göreceği biçim abone başına tek satır olmalıdır; her satır bir aboneyi özetleyen dokuz sayı ve bir etiket taşır.

# ozet.py — okuma tablosu abone basina tek satira indirilir ve etiket eklenir.
# Etiketi ureten kural KURGUDUR, koda acikca yazilmistir ve modele verilmez.
OZNITELIK = ["ortalama", "kisi_basi", "dusus", "sifir_okuma", "okuma_sayisi",
             "memnuniyet", "hane_kisi", "en_yuksek", "en_dusuk"]

_grup = {}
for o in OKUMA:
    _grup.setdefault(o["abone_no"], []).append(o)

ABONE = []
for k in _abone:
    o = _grup.get(k["abone_no"])
    if not o:
        continue
    t = [x["tuketim_m3"] for x in o]
    ort = sum(t) / len(t)
    a = {"abone_no": k["abone_no"], "bolge": k["bolge"],
         "hane_kisi": k["hane_kisi"], "memnuniyet": k["memnuniyet"],
         "okuma_sayisi": len(t), "ortalama": ort, "en_yuksek": max(t),
         "en_dusuk": min(t), "sifir_okuma": sum(1 for v in t if v == 0),
         "kisi_basi": ort / max(1, k["hane_kisi"]),
         "dusus": (t[0] - t[-1]) / t[0] if len(t) > 1 and t[0] > 0 else 0.0}
    r = uretec(TOHUM + 31000 + a["abone_no"])
    gizli = (1.30 * a["sifir_okuma"] + 1.25 * (a["dusus"] > 0.45)
             + 1.05 * (a["kisi_basi"] > 12.0) + 0.65 * (a["okuma_sayisi"] < 3)
             + 0.45 * (a["memnuniyet"] <= 2) - 1.55)
    a["suphe"] = 1 if gizli + (r() + r() - 1.0) * 1.1 > 0 else 0
    ABONE.append(a)

r = uretec(TOHUM + 90001)
EGITIM, AYRILMIS = [], []
for a in ABONE:
    (EGITIM if r() < 0.70 else AYRILMIS).append(a)


def dogruluk(tahmin, kume: list[dict]) -> float:
    return sum(1 for a in kume if tahmin(a) == a["suphe"]) / len(kume)


def poz(k: list[dict]) -> int:
    return sum(a["suphe"] for a in k)


print(f"model tablosu: {len(ABONE)} abone, {len(OZNITELIK)} oznitelik, 1 etiket")
print(f"etiket: {poz(ABONE)} supheli, {len(ABONE) - poz(ABONE)} temiz, "
      f"supheli orani yuzde {100 * poz(ABONE) / len(ABONE):.1f}")
print(f"egitim {len(EGITIM)} abone ({poz(EGITIM)} supheli), "
      f"ayrilmis {len(AYRILMIS)} abone ({poz(AYRILMIS)} supheli)")
model tablosu: 1260 abone, 9 oznitelik, 1 etiket
etiket: 327 supheli, 933 temiz, supheli orani yuzde 26.0
egitim 883 abone (234 supheli), ayrilmis 377 abone (93 supheli)

1.329 abonenin 1.260’ı model tablosuna girdi; 69 abonenin hiç okuması yoktur ve özetlenecek bir şeyi de yoktur. Şüpheli oranı yüzde 26,0’dır. Bu oran modelin geçmesi gereken çıtayı doğrudan belirler: hiç düşünmeden herkese “temiz” diyen bir yordam abonelerin yüzde 74’ünde haklı çıkar.

Etiketi üreten kural blokta durmaktadır ve gerçek bir kaçak tespiti değildir. Sıfır yazılmış okuma, dönemler arasında sert düşüş, kişi başına yüksek tüketim, eksik dönem ve düşük memnuniyet bir puanda toplanır, üstüne gürültü eklenir ve puan sıfırı geçerse abone şüpheli sayılır. Gürültü kasıtlıdır: gürültüsüz bir etiket, öznitelikleri yeterince bölen her yordamın tam isabet ettiği bir bulmacaya dönüşür ve hiçbir şey ölçülmez.

Taban Çizgisi ve Elle Yazılmış Kural

İki yordam yazılır. Birincisi hiçbir şey öğrenmez: eğitim kümesindeki en sık sınıfı bakar ve herkese onu söyler. İkincisi saha uzmanının kafasındaki kuraldır — sıfır okuma varsa, tüketim sert düştüyse ya da kişi başına tüketim yüksekse abone işaretlenir. Eşikler deneyimden gelir.

# kural.py — iki yordam: ogrenmeyen taban cizgisi ve elle yazilmis kural kumesi.
from collections import Counter

en_sik = Counter(a["suphe"] for a in EGITIM).most_common(1)[0][0]


def taban(a: dict) -> int:
    return en_sik


def elle(a: dict) -> int:
    """Saha uzmaninin yazdigi kural kumesi: esikler deneyimden gelir."""
    if a["sifir_okuma"] >= 1:
        return 1
    if a["dusus"] > 0.50:
        return 1
    if a["kisi_basi"] > 15.0:
        return 1
    return 0


EN = [26, 12, 12]
y = lambda h: str(h[0]).ljust(EN[0]) + str(h[1]).rjust(EN[1]) + str(h[2]).rjust(EN[2])
print(y(["yordam", "egitimde", "ayrilmista"]))
for ad, f in [("taban: hep en sik sinif", taban), ("elle yazilmis kural", elle)]:
    print(y([ad, f"{dogruluk(f, EGITIM):.4f}", f"{dogruluk(f, AYRILMIS):.4f}"]))
yordam                        egitimde  ayrilmista
taban: hep en sik sinif         0.7350      0.7533
elle yazilmis kural             0.7622      0.7507

Uzmanın kuralı eğitim kümesinde tabanı 0,0272 geçiyor, ayrılmış kümede ise 0,0027 altında kalıyor. Üç makul kural, üç makul eşik ve alan bilgisi, hiçbir şey öğrenmeyen bir yordamı geçmeye yetmemiştir. Bu sonucun kendisi bir bulgudur: eğer taban çizgisi yazılmasaydı, tablodaki 0,7507 rahatlıkla “kural kümesi abonelerin dörtte üçünde doğru karar veriyor” diye raporlanabilirdi.

Kurallarını Veriden Çıkaran Yordam

Üçüncü yordam kuralın biçimini insandan, içeriğini veriden alır. Biçim şudur: bir öznitelik seç, bir eşik seç, kümeyi ikiye böl, her parçada aynı işi yinele. Hangi özniteliğin hangi eşikle seçileceği aranarak bulunur; aranan şey bölmeden sonra iki parçanın etiket bakımından ne kadar karıştığıdır. Bu yapı karar ağacı diye anılır.

# agac.py — kurallarini veriden cikaran yordam: acgozlu bolme ile uc soruluk
# bir karar agaci. Kutuphane cagrilmaz; bolme olcutu ve durma kurali burada.
DERINLIK, EN_AZ = 3, 40


def safsizlik(k: list[dict]) -> float:
    """Bir kumede iki etiketin ne kadar karistigi: tek etiketliyse sifir."""
    if not k:
        return 0.0
    p = poz(k) / len(k)
    return 2 * p * (1 - p)


def en_iyi_bolme(k: list[dict]):
    en, secim = safsizlik(k) * len(k), None
    for oz in OZNITELIK:
        for esik in sorted({a[oz] for a in k})[1:]:
            sol = [a for a in k if a[oz] < esik]
            sag = [a for a in k if a[oz] >= esik]
            m = safsizlik(sol) * len(sol) + safsizlik(sag) * len(sag)
            if m < en - 1e-9:  # kayan noktali esitlik sinanmaz, kucuk pay birakilir
                en, secim = m, (oz, esik, sol, sag)
    return secim


def cogunluk(k: list[dict]) -> int:
    return 1 if 2 * poz(k) > len(k) else 0


def ogren(k: list[dict], derinlik: int):
    b = en_iyi_bolme(k) if derinlik and len(k) >= EN_AZ else None
    if b is None:
        return cogunluk(k)
    sol, sag = ogren(b[2], derinlik - 1), ogren(b[3], derinlik - 1)
    return sol if sol == sag else (b[0], b[1], sol, sag)


def uygula(dugum, a: dict) -> int:
    while isinstance(dugum, tuple):
        dugum = dugum[2] if a[dugum[0]] < dugum[1] else dugum[3]
    return dugum


def yazdir(d, girinti: str = "") -> None:
    if not isinstance(d, tuple):
        print(f"{girinti}-> {'supheli' if d else 'temiz'}")
        return
    print(f"{girinti}{d[0]} < {d[1]:.3f}")
    yazdir(d[2], girinti + "  ")
    yazdir(d[3], girinti + "  ")


AGAC = ogren(EGITIM, DERINLIK)
yazdir(AGAC)
print()
print(y(["yordam", "egitimde", "ayrilmista"]))
SIRA = [("taban: hep en sik sinif", taban), ("elle yazilmis kural", elle),
        ("veriden ogrenilen agac", lambda a: uygula(AGAC, a))]
for ad, f in SIRA:
    print(y([ad, f"{dogruluk(f, EGITIM):.4f}", f"{dogruluk(f, AYRILMIS):.4f}"]))
dusus < 0.461
  kisi_basi < 12.060
    -> temiz
    okuma_sayisi < 3.000
      -> supheli
      -> temiz
  en_dusuk < 6.000
    -> supheli
    kisi_basi < 13.052
      -> temiz
      -> supheli

yordam                        egitimde  ayrilmista
taban: hep en sik sinif         0.7350      0.7533
elle yazilmis kural             0.7622      0.7507
veriden ogrenilen agac          0.8709      0.8594

Ağaç ayrılmış kümede tabanı 0,1061 geçiyor. Bu, dersin ölçtüğü sayıdır ve tek başına değil üç satırlık tablonun içinde anlam taşır: aynı problemde öğrenmeyen yordam 0,7533, alan bilgisiyle yazılmış kural 0,7507, veriden çıkarılmış kural 0,8594 vermektedir.

Ağacın bulduğu eşikler etiketi üreten kuralın eşiklerine yakındır. Etiket kuralı düşüş için 0,45, kişi başına tüketim için 12,0 kullanıyor; ağaç 0,461 ve 12,060 buluyor. Ağaç bu sayıları hiç görmedi; yalnız 883 abonenin özniteliklerine ve etiketlerine baktı.

Farkın Nereden Geldiği

Fark eşiklerden geliyor gibi durur — uzman 0,50 ve 15,0 tahmin etmiş, ağaç 0,461 ve 12,060 bulmuştur. Bu açıklama sınanabilir: uzmanın kuralındaki eşikler ağacınkilerle değiştirilir ve sayı yeniden ölçülür.

# ayristir.py — fark esiklerden mi geliyor: uzmanin kurali ayni bicimde kalir,
# esikleri agacin buldugu degerlerle degistirilir.
def elle_veriden_esik(a: dict) -> int:
    return 1 if (a["sifir_okuma"] >= 1 or a["dusus"] > 0.461
                 or a["kisi_basi"] > 12.060) else 0


D = [26, 13, 8, 14, 8]
z = lambda h: str(h[0]).ljust(D[0]) + "".join(str(v).rjust(D[i + 1])
                                              for i, v in enumerate(h[1:]))
print(f"ayrilmis kume: {len(AYRILMIS)} abone, {poz(AYRILMIS)} supheli")
print(z(["yordam", "isaretlenen", "dogru", "bos ziyaret", "kacan"]))
YORDAM = SIRA + [("elle, agacin esikleriyle", elle_veriden_esik)]
for ad, f in YORDAM:
    im = [a for a in AYRILMIS if f(a) == 1]
    d = poz(im)
    print(z([ad, len(im), d, len(im) - d, poz(AYRILMIS) - d]))
print()
for ad, f in YORDAM:
    print(f"{ad:<26}ayrilmista dogruluk {dogruluk(f, AYRILMIS):.4f}")
ayrilmis kume: 377 abone, 93 supheli
yordam                      isaretlenen   dogru   bos ziyaret   kacan
taban: hep en sik sinif               0       0             0      93
elle yazilmis kural                 151      75            76      18
veriden ogrenilen agac               66      53            13      40
elle, agacin esikleriyle            184      91            93       2

taban: hep en sik sinif   ayrilmista dogruluk 0.7533
elle yazilmis kural       ayrilmista dogruluk 0.7507
veriden ogrenilen agac    ayrilmista dogruluk 0.8594
elle, agacin esikleriyle  ayrilmista dogruluk 0.7480

Açıklama çürüdü. Uzmanın kuralı ağacın eşikleriyle çalıştırıldığında sayı 0,7507’den 0,7480’e düştü. Fark eşiklerde değil, koşulların birleştirilme biçimindedir. Uzmanın kuralı üç koşulu “veya” ile bağlar: biri tutan her abone işaretlenir. Ağaç aynı koşulları iç içe kullanır — kişi başına tüketime yalnız düşüşün küçük olduğu dalda bakar, o dalda ikinci bir koşul olarak okuma sayısını sorar, düşüşün büyük olduğu dalda ise bambaşka bir özniteliğe geçer.

Sonucu sayım tablosu veriyor. Uzmanın kuralı 151 abone işaretliyor ve 75’ini doğru buluyor; 76 ziyaret boşa gidiyor. Ağaç 66 abone işaretliyor, 53’ünü doğru buluyor, 13 ziyaret boşa gidiyor. Ağaç daha az kaçak yakalıyor — 53’e karşı 75 — ama beş kat az boş ziyaret üretiyor ve doğruluk ölçüsü boş ziyareti kaçırılan kaçakla aynı ağırlıkta saydığı için ağaç kazanıyor.

Bu, MK6’nın somut karşılığıdır. Boş bir ziyaretin bedeli kaçırılan kaçağınkinden düşükse ölçü değişir, ölçü değişince sıralama da değişebilir: son satırdaki yordam 93 şüphelinin 91’ini yakalıyor ve yalnız 2’sini kaçırıyor, ama 93 boş ziyaretle. Hangi yordamın “iyi” olduğu, doğruluğun içine gömülü ağırlık seçimiyle birlikte belirlenir.

Özet

  • Makine öğrenmesi kuralı elle yazmak yerine örneklerden çıkarmaktır; bu derste kural üç soruluk bir karar ağacıdır ve 883 abonelik eğitim kümesinden çıkarılmıştır.
  • Taban çizgisi bu derste en sık sınıfı söylemektir ve ayrılmış kümede 0,7533 verir; şüpheli oranı yüzde 26,0 olduğu için öğrenmeyen bir yordam bile yüksek görünen bir sayı üretir.
  • Elle yazılmış kural kümesi ayrılmış kümede 0,7507 vererek tabanın altında kalır; veriden öğrenilen ağaç 0,8594 ile tabanı 0,1061 geçer.
  • Fark eşiklerden gelmez: aynı eşikler elle yazılmış kurala taşındığında sayı 0,7480’e iner. Fark koşulların iç içe mi “veya” ile mi birleştirildiğinden gelir.
  • Doğruluk ölçüsü boş ziyaretle kaçırılan kaçağı eşit sayar; ağaç 53 kaçak yakalayıp 13 boş ziyaret üretirken uzmanın kuralı 75 yakalayıp 76 boş ziyaret üretir ve ölçü değişirse sıralama da değişir.

Sonraki Adım

Bu ders tek bir kurulum kullandı: her abonenin etiketi biliniyordu ve yordam 883 etiketin tamamını gördü. Etiket bu müfredatta bedava üretildi, sahada değil. Gerçekte bir etiket bir saha ziyareti demektir ve ziyaret sayılıdır. Sonraki ders aynı problemi dört ayrı kurulumda kurar — denetimli, denetimsiz, yarı denetimli ve pekiştirmeli — ve her kurulumun kaç etiket istediğini sayar: 883 etiketin tamamı yerine 60 etiketle çalışıldığında tabanın üstündeki farkın ne kadarının kaldığı, etiketsiz bir kurulumun aynı kümeden ne çıkarabildiği ve etiketi ziyaret ederek öğrenen bir yordamın bütçesini nasıl harcadığı ölçülür.

İ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