İçeriğe geç
academia.sh

Ders 02 / 13

Kavram Kayması

Girdi hiç değişmeden girdi ile etiket arasındaki kuralın değişmesinin ölçülmesi: yüksek sayılma eşiği 30'dan 27'ye indiğinde girdi tarafında değişen tek bir değer olmamasına karşın 1200 izleme satırının 132'sinin etiketi değişiyor ve modelin isabeti 0,9700'den 0,8150'ye, yani otuz bir çözünürlük adımı düşüyor. Aynı üç gösterge iki kayma türünde yan yana okunduğunda vekil ölçütlerin neyi görüp neyi kaçırdığı sayıya dönüyor: girdi özeti iki senaryoda da hiçbir bantta ateşlemiyor, tahmin payı 0,05 bandında ikisinde de üç dönem ateşleyerek zararsızı zararlıdan ayıramıyor ve 0,10 bandında zararsız kaymada iki dönem ateşlerken zararlı kaymada hiç ateşlemiyor, isabet ise zararsız kaymada susup zararlı kaymada dört dönem ateşleyerek tek ayırt eden ölçü olarak kalıyor.

İçindekiler

Önceki derste kayan şey girdiydi: girdi özeti 25,9220’den 27,0472’ye çıktı, tahmin payı üç dönem uyarı ateşledi ve isabet 0,9700’den 0,9800’e giderek hiç düşmedi. Kural yerinde durduğu için model doğru kalabildi ve ateşleyen uyarıların üçü de yanlış alarm sayıldı.

Bu ders tam tersini kurar. Girdi tarafında tek bir değer bile değişmez; değişen, girdi ile etiket arasındaki kuralın kendisidir. Aynı abonenin aynı geçmişi artık başka bir etiket üretmeye başlar, çünkü “yüksek sayılma” ölçütü kaymıştır. Model hiç dokunulmadan yerinde durur ve ölçülecek soru şudur: önceki dersin üç göstergesi bu durumda ne der.

  • MI8. Kaynak, hat, model ve kayma senaryoları kurgudur; gerçek bir öğrenme kitaplığı, izleme ürünü ya da bulut servisi çağrılmaz. Tohum 20260218.
  • MI9. Kavram kayması (concept drift) girdi ile etiket arasındaki ilişkinin değişmesidir. Veri kayması girdi dağılımını, kavram kayması kuralı oynatır. Kaymanın ölçüm yordamı Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda kuruldu ve tekrarlanmaz; buradaki ek yalnız üretimdeki ömürdür.
  • MI10. Bu kursun etiketi abone_ort + 6 x isyeri > 30 kuralıdır ve kural KAVRAM’dır. Kurgu kavram kayması 2026-09‘dan sonra eşiği **30’dan 27’ye** indirir; kayıtlara dokunmaz, yalnız etiket üretir.
  • MI11. İzleme dönemleri 2026-07…2026-12, dönem başına 200 satır, toplam 1200. İzleme dönemlerinde taban sınıf oranı 0,5342; küme dengelidir.
  • MI12. Üç gösterge: girdi özeti (özniteliğin dönem ortalaması), tahmin payı (modelin yüksek dediği satırların oranı) ve isabet. İlk ikisi etiket istemez, üçüncüsü ister.
  • MI13. Taban çizgisi izlemesiz kurulumdur: gösterge yok, uyarı yok; bozulma görülmez.
  • MI14. Uyarı eşiği iki yönlü bir banttır: ölçü tabandan pay kadar saparsa ateşler. Taban dönem 2026-08, yani kaymanın başladığı dönemden önceki son dönem.
  • MI15. Çözünürlük: dönem isabetinin adımı 1/200 = 0,0050. Bu derste düşüş 0,1550, yani otuz bir adım ve bandın çok üstünde; önceki dersin oynaması dört adımdı.

Değişmeyen Girdi, Değişen Kural

İlk blok önceki dersin kurulumunu aynen kurar: kaynak, hattın modele bakan üç aşaması, öznitelikler, karar eşiği ayrı aranan model ve üç gösterge. Model bu derste hiç yeniden eğitilmez; ölçülen şey, sabit bir modelin altından kuralın çekilmesidir.

# KURGUDUR. Olcum aginin sayac okumalari, hattin modele bakan ilk uc asamasi,
# esik tabanli model ve kayma senaryolari ders icinde tanimlanir; gercek bir
# ogrenme kitapligi, izleme urunu ya da bulut servisi 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


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
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, basamak=4):
    """Oznitelik agirliktan bagimsizdir; tam gecmisten 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]
        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"]


# Etiket, ozniteliklerin GORDUGU bir kurala baglidir. KURAL = KAVRAM.
KURAL_ESIK = 30
KURAL_ISYERI = 6


def etiket(oz_k, esik=KURAL_ESIK, isyeri_kat=KURAL_ISYERI):
    return 1 if oz_k["abone_ort"] + isyeri_kat * oz_k["isyeri"] > esik else 0


IZLEME = DONEM[6:]                    # 2026-07 .. 2026-12 : uretimde izlenen donemler
KARAR_IZGARA = (18, 22, 26, 30, 34)   # karar esigi ETIKET esiginden AYRI bir ayardir


def tahmin(oz_k, w, karar):
    return 1 if puan(oz_k, w) > karar else 0


def dengeli_isabet(kume, oz, w, karar, kural=None):
    c = {(0, 0): 0, (0, 1): 0, (1, 0): 0, (1, 1): 0}
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        c[(tahmin(o_k, w, karar), kural(k, o_k) if kural else etiket(o_k))] += 1
    du = c[(1, 1)] / max(c[(1, 1)] + c[(0, 1)], 1)      # duyarlilik
    oz_ = c[(0, 0)] / max(c[(0, 0)] + c[(1, 0)], 1)     # ozgulluk
    return {"isabet": (c[(1, 1)] + c[(0, 0)]) / len(kume), "duyarlilik": du, "ozgulluk": oz_,
            "dengeli": (du + oz_) / 2, "agirlik": w, "karar": karar}


def egit(kayitlar, oz, donemler=None, kural=None):
    """Egitim = agirlik ucLUsu ILE karar esigini birlikte aramak: 27 x 5 = 135 aday."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    return max((dengeli_isabet(kume, oz, w, karar, kural)
                for w in IZGARA for karar in KARAR_IZGARA), key=lambda o: o["dengeli"])


def girdi_ozeti(kayitlar, donem, oz):
    d = [oz[(k["abone"], k["donem"])]["abone_ort"] for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def tahmin_payi(kayitlar, model, oz, donem):
    d = [tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
         for k in kayitlar if k["donem"] == donem]
    return round(sum(d) / len(d), 4)


def isabet(kayitlar, model, oz, donem, etiketler=None):
    """etiketler=None -> kural degismemis demektir."""
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        g = etiketler[(k["abone"], k["donem"])] if etiketler else etiket(o_k)
        d += tahmin(o_k, model["agirlik"], model["karar"]) == g
    return round(d / len(kume), 4)


S = satirlar()
OZ = oznitelikler(S)
MODEL = egit(S, OZ)
IZL = [k for k in S if k["donem"] in IZLEME]
dag = {0: 0, 1: 0}
for k in IZL:
    dag[etiket(OZ[(k["abone"], k["donem"])])] += 1
print("izleme satiri:", len(IZL), " donem basina:", len(IZL) // len(IZLEME),
      " taban sinif orani:", round(dag[0] / len(IZL), 4))
print("model:", MODEL["agirlik"], "/ karar esigi", MODEL["karar"],
      " dengeli isabet:", round(MODEL["dengeli"], 4))
izleme satiri: 1200  donem basina: 200  taban sinif orani: 0.5342
model: (0.6, 0.0, 4) / karar esigi 18  dengeli isabet: 0.9864

Kavram Kaydığında Ne Görünüyor

İkinci blok kavram kaymasını uygular. kavram_kaydir işlevi kayıtlara dokunmaz; yalnız 2026-09‘dan başlayarak yüksek sayılma eşiğini 27’ye indirilmiş biçimde etiket üretir. Tabloda son iki sütun önemlidir: kural değişmeseydi o dönemde alınacak isabet ve o dönemde etiketi değişen satır sayısı.

# ---- KURGU kavram kaymasi: GIRDI AYNI KALIR , kural degisir.
# 2026-09'dan sonra "yuksek sayilma" esigi 30'dan 27'ye iniyor.
def kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    b = DONEM.index(baslangic)
    return {(k["abone"], k["donem"]):
            etiket(oz[(k["abone"], k["donem"])],
                   yeni_esik if DONEM.index(k["donem"]) >= b else KURAL_ESIK)
            for k in kayitlar}


YENI = kavram_kaydir(S, OZ)
print(f"{'donem':<10}{'girdi ozeti':>13}{'tahmin payi':>13}{'isabet':>9}"
      f"{'kural degismeseydi':>20}{'etiketi degisen':>17}")
for d in IZLEME:
    kume = [k for k in S if k["donem"] == d]
    ceviren = sum(1 for k in kume
                  if YENI[(k["abone"], d)] != etiket(OZ[(k["abone"], d)]))
    print(f"{d:<10}{girdi_ozeti(S, d, OZ):>13.4f}{tahmin_payi(S, MODEL, OZ, d):>13.4f}"
          f"{isabet(S, MODEL, OZ, d, YENI):>9.4f}{isabet(S, MODEL, OZ, d):>20.4f}"
          f"{ceviren:>17}")
donem       girdi ozeti  tahmin payi   isabet  kural degismeseydi  etiketi degisen
2026-07         25.9220       0.4550   0.9700              0.9700                0
2026-08         26.0517       0.4700   0.9700              0.9700                0
2026-09         25.9800       0.4550   0.8150              0.9900               39
2026-10         26.3525       0.5100   0.8700              0.9750               31
2026-11         26.3261       0.5100   0.8650              0.9850               30
2026-12         26.5325       0.5150   0.8500              0.9900               32

İsabet 2026-09‘da **0,9700’den 0,8150’ye** düşüyor. Düşüş 0,1550, yani çözünürlüğün otuz bir adımı; önceki dersin bütün oynaması dört adımdı. Modelde değişen tek bir sayı yoktur ve girdi tarafında da yoktur: son sütun kaymanın nereden geldiğini söyler, 2026-09’da 39 satırın etiketi değişmiştir. Değişen etiketlerin hepsi hataya dönüşmez, çünkü modelin karar eşiği bazı satırlarda yeni kuralla zaten uyumludur; sonraki dönemlerde isabet 0,8700 dolayında tutunur ama eski düzeyine hiç dönmez.

İlk iki sütun tam olarak beklendiği gibi davranır. Girdi özeti 25,9220’den 26,5325’e, tahmin payı 0,4550’den 0,5150’ye gider. Bu iki seri, kavram kayması hiç olmasaydı da birebir aynı olurdu, çünkü ikisi de yalnız girdiye ve modele bakar; kuralın nereye kaydığından haberleri yoktur.

Zararsız Kaymada Ateşleyen, Zararlı Kaymada Susan

Üçüncü blok önceki dersin veri kayması senaryosunu geri getirir ve iki senaryoyu aynı üç göstergeyle, aynı üç bantla yan yana koyar. Tabloda okunacak şey tek tek sayılar değil, satırların birbirine göre durumudur.

# ---- KURGU veri kaymasi (onceki ders): GIRDI kayar , KURAL AYNI KALIR.
def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    b = DONEM.index(baslangic)
    return [dict(k, m3=round(k["m3"] * (1.0 + egim * (DONEM.index(k["donem"]) - b + 1)), 4))
            if DONEM.index(k["donem"]) >= b and k["tip"] == "isyeri" else dict(k)
            for k in kayitlar]


def uyari(dizi, taban, pay):
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


KAYAN = kaydir(S)
OZK = oznitelikler(KAYAN)
print(f"{'senaryo':<16}{'olcu':<14}{'taban':>10}{'en buyuk sapma':>16}"
      f"{'0,05':>7}{'0,10':>7}{'0,20':>7}")
for ad, (kay, oz, et) in (("veri kaymasi", (KAYAN, OZK, None)),
                          ("kavram kaymasi", (S, OZ, YENI))):
    for olcu_ad, dizi in (("girdi ozeti", [(d, girdi_ozeti(kay, d, oz)) for d in IZLEME]),
                          ("tahmin payi", [(d, tahmin_payi(kay, MODEL, oz, d)) for d in IZLEME]),
                          ("isabet", [(d, isabet(kay, MODEL, oz, d, et)) for d in IZLEME])):
        taban = dict(dizi)[IZLEME[1]]
        print(f"{ad:<16}{olcu_ad:<14}{taban:>10.4f}"
              f"{max(abs(v - taban) / taban for _, v in dizi):>16.4f}"
              + "".join(f"{len(uyari(dizi, taban, p)):>7}" for p in (0.05, 0.10, 0.20)))

print("\nkavram kaymasinda degisen satir sayisi:",
      sum(1 for k in IZL if YENI[(k["abone"], k["donem"])] != etiket(OZ[(k["abone"], k["donem"])])),
      "/", len(IZL), " girdi tarafinda degisen deger: 0")
DUSUS = isabet(S, MODEL, OZ, IZLEME[0], YENI) - isabet(S, MODEL, OZ, IZLEME[2], YENI)
print("isabetin dustugu:", f"{DUSUS:.4f}", " donem isabetinin adimi:", f"{1 / 200:.4f}",
      " yani", round(DUSUS * 200), "adim")
senaryo         olcu               taban  en buyuk sapma   0,05   0,10   0,20
veri kaymasi    girdi ozeti      26.0517          0.0382      0      0      0
veri kaymasi    tahmin payi       0.4700          0.1596      3      2      0
veri kaymasi    isabet            0.9700          0.0206      0      0      0
kavram kaymasi  girdi ozeti      26.0517          0.0185      0      0      0
kavram kaymasi  tahmin payi       0.4700          0.0957      3      0      0
kavram kaymasi  isabet            0.9700          0.1598      4      4      0

kavram kaymasinda degisen satir sayisi: 132 / 1200  girdi tarafinda degisen deger: 0
isabetin dustugu: 0.1550  donem isabetinin adimi: 0.0050  yani 31 adim

Girdi özeti iki senaryoda da hiçbir bantta ateşlemez. Kavram kaymasında bunun başka türlüsü de mümkün değildir: girdi tarafında değişen sıfır değer vardır ve gösterge yalnız girdiye bakar.

Tahmin payı iki kez ateşler ve her ikisinde de yanıltır. 0,05 bandında her iki senaryoda da üç dönem ateşliyor, yani zararsız kaymayı zararlı kaymadan ayırt edemiyor. 0,10 bandında ise tam ters çalışıyor: zararsız kaymada iki dönem ateşliyor, zararlı kaymada hiç. Kavram kaymasındaki 0,0957’lik sapmanın kaymayla ilgisi de yoktur; o seri, kural hiç değişmeseydi de aynıydı. Bir gösterge ateşlediğinde tek başına ne bozulmayı ne de nedenini gösterir.

İsabet tek ayırt eden ölçüdür. Zararsız kaymada sapması 0,0206 ve hiçbir bantta ateşlemiyor; zararlı kaymada sapması 0,1598 ve hem 0,05 hem 0,10 bandında dört dönem ateşliyor. Bu kursun kuralı burada iki yönlü okunur: bir göstergenin ateşlemesi bozulmanın kanıtı olmadığı gibi, susması da sağlamlığın kanıtı değildir; ateşlemesi ile bozulması ayrı ayrı gösterilmeyen gösterge ölçülmemiş sayılır. İzlemesiz taban çizgisinde ise 132 satırın etiketi değişmiş, isabet otuz bir adım düşmüş ve hiçbir şey haber vermemiştir.

Özet

  • Kavram kayması girdi ile etiket arasındaki kuralın değişmesidir; veri kaymasının aksine girdi tarafında değişen tek bir değer yoktur.
  • Eşik 30’dan 27’ye indiğinde 1200 izleme satırının 132’sinin etiketi değişir ve isabet 0,9700’den 0,8150’ye düşer: 0,1550, yani otuz bir çözünürlük adımı. Model hiç değişmemiştir.
  • Girdi özeti iki senaryoda da hiçbir bantta ateşlemez; kavram kaymasında ateşlemesi zaten olanaksızdır, çünkü baktığı yerde değişen bir şey yoktur.
  • Tahmin payı 0,05 bandında iki senaryoda da üç dönem ateşleyerek ikisini ayırt edemez, 0,10 bandında ise zararsız kaymada iki dönem ateşleyip zararlı kaymada hiç ateşlemez.
  • İsabet tek ayırt eden ölçüdür: zararsız kaymada sapması 0,0206 ve susar, zararlı kaymada 0,1598 ve 0,05 ile 0,10 bantlarında dört dönem ateşler.

Sonraki Adım

İki derste ortaya çıkan sonuç şudur: bozulmayı gören tek gösterge isabettir. Fakat isabet öbür ikisinden farklı bir şey ister, gerçek etiketi. Girdi özeti ile tahmin payı, tahmin üretildiği anda hesaplanabilir; isabet ise satırın gerçekten yüksek olup olmadığı bilinene kadar hesaplanamaz ve o bilgi üretimde tahminle aynı anda gelmez. Bu derste etiket, hesaplama sırasında elde varmış gibi kullanıldı. Sonraki ders bu varsayımı kaldırır: etiket iki dönem sonra geldiğinde bir dönemin isabetinin hangi dönemde bilinebildiğini, bozulmanın olduğu an ile görüldüğü an arasındaki boşluğu ve o boşlukta kaç tahminin ölçülmeden üretildiğini 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