İçeriğe geç
academia.sh

Ders 05 / 13

Uyarı ve Eşikler

Uyarı bandının daraldıkça ne kazandırıp ne kaybettirdiğinin sayılması: 0,05 bandında üç ölçü ve üç senaryo boyunca 13 dönem ateşliyor, bunların 6'sı yanlış alarm; 0,10'da 6 ateşleme ve 2 yanlış alarm; 0,20'de hiç yanlış alarm yok ama gerçek bozulmanın dördü de kaçıyor, çünkü 0,20'lik göreli bant 0,9700 tabanında isabetin 0,7760'ın altına inmesini ister ve en dip değer 0,8150'dir. Tahmin payı 0,05 bandında hem sağlam kurulumda hem kavram kaymalı kurulumda tam üç dönem ateşliyor; aynı ateşleme bir yerde saf yanlış alarm, öbür yerde yakalama sayılıyor, bu yüzden ölçülmemiş sayılır. Yalnız isabet iki zararsız senaryoda hiç ateşlemeyip zararlı senaryoda dört dönem ateşliyor ve yakalama gecikmesi iki dönem.

İçindekiler

Bir önceki ders üç vekilin sapmasını saydı ve son sütunda tek bir sayı bıraktı: 2026-08 tabanına göre en büyük göreli sapma. O sayı tek başına bir karar vermez. Karar, sapmanın bir bantla karşılaştırılmasıyla doğar; bandın adı uyarı eşiğidir (alert threshold) ve bir ölçünün değil, bir seçimin parçasıdır.

Seçim ucuz görünür ve değildir. Bant daraldıkça daha çok dönem ateşler; ateşleyen dönemlerin kaçının gerçekten bozulmuş olduğu ise bandın bilmediği bir şeydir. Bu ders bandı 0,05, 0,10 ve 0,20’de sabitler, üç ölçüyü üç senaryoda koşturur ve iki sayıyı ayrı ayrı yazar: ateşleyen dönem ile bunların kaçının gerçek bozulmaya karşılık geldiği.

  • MI47. Kaynak, hat, kurgu model ve iki kayma türü kurgudur ve ders içinde üretilir. Hiçbir izleme ürünü, gösterge servisi, bulut servisi ya da öğrenme kitaplığı çağrılmaz. Tohum 20260218.
  • MI48. Uyarı eşiği, bir ölçünün 2026-08 tabanından göreli sapmasının aşmaması gereken paydır. Üç bant sınanır: 0,05, 0,10 ve 0,20. Taban dönemi kaymaların hiçbirinin başlamadığı son dönemdir.
  • MI49. Gerçek bozulma banttan bağımsız tanımlanır: bir dönemin gerçek isabetinin 2026-08 tabanından 0,0500’den fazla düşmesi, yani on çözünürlük adımı. Bu ölçü değerlendirme içindir; üretimde iki dönem gecikmeyle bilinir.
  • MI50. Yanlış alarm (false positive), ateşleyen ama gerçek bozulma listesinde olmayan dönemdir. Gösterge gecikmesinin ölçümü Veri Hatları ve Orkestrasyon kursunun hat gözlemlenebilirliği dersinde yapıldı ve tekrarlanmaz. Buradaki tek ek gecikmeli etikettir: isabet üzerinden ateşleyen bir uyarının gecikmesine GECIKME eklenir.
  • MI51. Model MLOps Temelleri kursundan düzeltilmiş biçimde devralınır: karar eşiği ayrı bir ayardır, eğitim ölçütü dengeli isabettir. İzleme dönemlerinde taban sınıf oranı 0,5342.
  • MI52. Taban çizgisi izlemesiz kurulumdur: bant yok, ateşleyen dönem 0, yakalama gecikmesi tanımsız. Dönem isabetinin adımı 0,0050.

Uyarı Eşiği Bir Karşılaştırmadır

Aşağıdaki blok ortak tanımı kurar ve üzerine iki şey ekler: bir ölçü dizisini tabanla karşılaştıran uyari, ve bir dönemin isabetinin hangi dönemde bilinebildiğini söyleyen gorulen_donem.

# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat , kurgu model ve iki kayma
# turu ders icinde uretilir; hicbir izleme urunu , gosterge servisi , bulut servisi ya
# da ogrenme kitapligi cagrilmaz. Tohum 20260218.
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 not in gor:
            gor.add(ad)
            tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is None:
            dusen += i > 0
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def satirlar(kaynak=None):
    v, _ = a1_ayikla(kaynak_uret() if kaynak is None else kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def oznitelikler(kayitlar, pencere=None, basamak=4):
    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 and (pencere is None or d >= i - pencere))]
        return round(sum(secili) / len(secili), basamak) if secili else 0.0

    oz = {}
    for k in kayitlar:
        i = DONEM.index(k["donem"])
        oz[(k["abone"], k["donem"])] = {"abone_ort": ortalama(ab[k["abone"]], i),
                                        "bolge_ort": ortalama(bo[k["bolge"]], i),
                                        "isyeri": 1 if k["tip"] == "isyeri" else 0}
    return oz


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)]
KARAR_IZGARA = (18, 22, 26, 30, 34)     # karar esigi AYRI bir ayardir , egitimde aranir
IZLEME = DONEM[6:]                      # 2026-07 .. 2026-12 : uretimde izlenen donemler
GECIKME = 2                             # etiket kac donem sonra geliyor
KURAL_ESIK, KURAL_ISYERI = 30, 6        # KAVRAM: abone_ort + 6 x isyeri > 30


def puan(o, w):
    return w[0] * o["abone_ort"] + w[1] * o["bolge_ort"] + w[2] * o["isyeri"]


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


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


def dengeli_isabet(kume, oz, w, karar):
    dp = dn = yp = yn = 0
    for k in kume:
        o_k = oz[(k["abone"], k["donem"])]
        t, g = tahmin(o_k, w, karar), etiket(o_k)
        dp, yp = dp + (t and g), yp + (t and not g)
        yn, dn = yn + (g and not t), dn + (not t and not g)
    duyarlilik = dp / (dp + yn) if dp + yn else 0.0
    ozgulluk = dn / (dn + yp) if dn + yp else 0.0
    return (duyarlilik + ozgulluk) / 2, round(duyarlilik, 4), round(ozgulluk, 4)


def egit(kayitlar, oz, donemler=None):
    """Egitim olcutu DENGELI ISABETTIR: duyarlilik ile ozgullugun ortalamasi."""
    kume = [k for k in kayitlar if k["donem"] in (donemler or DONEM[1:9])]
    en = (-1.0, None, None, 0.0, 0.0)
    for w in IZGARA:
        for karar in KARAR_IZGARA:
            o, du, oz_ = dengeli_isabet(kume, oz, w, karar)
            if o > en[0]:
                en = (o, w, karar, du, oz_)
    return {"agirlik": en[1], "karar": en[2], "dengeli": round(en[0], 4),
            "duyarlilik": en[3], "ozgulluk": en[4], "egitim_satiri": len(kume)}


def kaydir(kayitlar, baslangic="2026-09", egim=0.06):
    """VERI KAYMASI: baslangictan sonra isyeri tuketimi her donem egim kadar buyuyor."""
    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 kavram_kaydir(kayitlar, oz, baslangic="2026-09", yeni_esik=27):
    """KAVRAM KAYMASI: girdi AYNI , kural degisiyor. Esik 30'dan yeni_esik'e iniyor."""
    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}


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) if d else 0.0


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) if d else 0.0


def isabet(kayitlar, model, oz, donem, etiketler=None):
    kume = [k for k in kayitlar if k["donem"] == donem]
    d = sum(1 for k in kume
            if tahmin(oz[(k["abone"], k["donem"])], model["agirlik"], model["karar"])
            == (etiketler[(k["abone"], k["donem"])] if etiketler
                else etiket(oz[(k["abone"], k["donem"])])))
    return round(d / len(kume), 4) if kume else None


def uyari(dizi, taban, pay):
    """Bir olcu tabandan `pay` kadar saparsa o donem ates eder."""
    return [d for d, v in dizi if abs(v - taban) / max(abs(taban), 1e-9) > pay]


def gorulen_donem(donem, simdi):
    """Gecikmeli etiket: bir donemin isabeti ancak GECIKME donem sonra bilinir."""
    return DONEM.index(donem) + GECIKME <= DONEM.index(simdi)


SATIR = satirlar()
OZ = oznitelikler(SATIR)                  # oznitelik BIR KEZ hesaplanir
MODEL = egit(SATIR, OZ)
IZL = [k for k in SATIR if k["donem"] in IZLEME]
buyuk = max(sum(1 for k in IZL if etiket(OZ[(k["abone"], k["donem"])]) == s) for s in (0, 1))
print(f"model {MODEL['agirlik']} karar esigi {MODEL['karar']} , dengeli isabet"
      f" {MODEL['dengeli']} , {len(IZL)} satir , taban sinif orani {buyuk / len(IZL):.4f} ,"
      f" adim {1 / 200:.4f} , 2026-09'un isabeti"
      f" {[d for d in DONEM if gorulen_donem('2026-09', d)][0]} doneminde bilinir")
model (0.6, 0.0, 4) karar esigi 18 , dengeli isabet 0.9864 , 1200 satir , taban sinif orani 0.5342 , adim 0.0050 , 2026-09'un isabeti 2026-11 doneminde bilinir

Son alan bandın ölçemediği şeyi baştan söyler: 2026-09’da bir şey bozulsa bile o dönemin isabeti 2026-11’de bilinir. İsabet üzerine kurulan bir uyarı, bandı ne kadar dar olursa olsun iki dönem sonra konuşur. Bandın oynatabildiği tek şey, gecikmesiz vekillerin kaç kez konuşacağıdır.

Bant Daraldıkça

Aşağıdaki blok üç ölçüyü üç senaryoda üç bantla karşılaştırır. Her hücre iki sayı taşır: ateşleyen dönem ve bunların kaçının yanlış alarm olduğu. bozulan sütunu banttan bağımsızdır; o senaryoda gerçekten bozulmuş dönem sayısını verir.

# Bu blok ilk bloktaki tanimlari surdurur. Her senaryonun oznitelikleri BIR KEZ hesaplanir.
BANT = (0.05, 0.10, 0.20)
DUSUS = 0.0500        # GERCEK BOZULMA: isabetin tabandan bu kadardan fazla dusmesi
KAYAN = kaydir(SATIR)
OZ_K = oznitelikler(KAYAN)
KAVRAM = kavram_kaydir(SATIR, OZ)
SENARYO = (("saglam", SATIR, OZ, None), ("veri kaymali", KAYAN, OZ_K, None),
           ("kavram kaymali", SATIR, OZ, KAVRAM))
OLCU = (("girdi ozeti", lambda kay, oz, et, d: girdi_ozeti(kay, d, oz)),
        ("tahmin payi", lambda kay, oz, et, d: tahmin_payi(kay, MODEL, oz, d)),
        ("isabet", lambda kay, oz, et, d: isabet(kay, MODEL, oz, d, et)))
ATES, GERCEK = {}, {}
for sad, kay, oz, et in SENARYO:
    isb = [(d, isabet(kay, MODEL, oz, d, et)) for d in IZLEME]
    GERCEK[sad] = set(d for d, v in isb[2:] if dict(isb)["2026-08"] - v > DUSUS)
    for ad, f in OLCU:
        dizi = [(d, f(kay, oz, et, d)) for d in IZLEME]
        for b in BANT:
            ATES[(sad, ad, b)] = set(uyari(dizi[2:], dict(dizi)["2026-08"], b))

print(f"gercek bozulma: isabetin 2026-08 tabanindan {DUSUS:.4f} uzerinde dusmesi")
print("hucre: atesleyen donem / yanlis alarm\n")
print("olcu         senaryo         bozulan " + "".join(f"{b:>12.2f}" for b in BANT))
for ad, _ in OLCU:
    for sad, *_ in SENARYO:
        h = [f"{len(ATES[(sad, ad, b)])} / {len(ATES[(sad, ad, b)] - GERCEK[sad])}"
             for b in BANT]
        print(f"{ad:<12} {sad:<14} {len(GERCEK[sad]):>7} " + "".join(f"{x:>12}" for x in h))

print("\nbant  toplam ates  yanlis alarm  yakalanan  gecikme: tahmin payi  isabet")
for b in BANT:
    t = [sum(len(ATES[(s, a, b)]) for s, *_ in SENARYO for a, _ in OLCU),
         sum(len(ATES[(s, a, b)] - GERCEK[s]) for s, *_ in SENARYO for a, _ in OLCU),
         sum(len(ATES[(s, a, b)] & GERCEK[s]) for s, *_ in SENARYO for a, _ in OLCU)]
    g = []
    for ad, ek in (("tahmin payi", 0), ("isabet", GECIKME)):
        a = sorted(ATES[("kavram kaymali", ad, b)])
        g.append(str(DONEM.index(a[0]) - DONEM.index("2026-09") + ek) if a else "tanimsiz")
    print(f"{b:<5.2f} {t[0]:>11} {t[1]:>13} {t[2]:>10} {g[0]:>21} {g[1]:>7}")
gercek bozulma: isabetin 2026-08 tabanindan 0.0500 uzerinde dusmesi
hucre: atesleyen donem / yanlis alarm

olcu         senaryo         bozulan         0.05        0.10        0.20
girdi ozeti  saglam               0        0 / 0       0 / 0       0 / 0
girdi ozeti  veri kaymali         0        0 / 0       0 / 0       0 / 0
girdi ozeti  kavram kaymali       4        0 / 0       0 / 0       0 / 0
tahmin payi  saglam               0        3 / 3       0 / 0       0 / 0
tahmin payi  veri kaymali         0        3 / 3       2 / 2       0 / 0
tahmin payi  kavram kaymali       4        3 / 0       0 / 0       0 / 0
isabet       saglam               0        0 / 0       0 / 0       0 / 0
isabet       veri kaymali         0        0 / 0       0 / 0       0 / 0
isabet       kavram kaymali       4        4 / 0       4 / 0       0 / 0

bant  toplam ates  yanlis alarm  yakalanan  gecikme: tahmin payi  isabet
0.05           13             6          7                     1       2
0.10            6             2          4              tanimsiz       2
0.20            0             0          0              tanimsiz tanimsiz

Yakalama Sayısının Geçersiz Olduğu Yer

Alt tablonun ilk satırı bandı savunuyor görünür: 0,05’te 13 ateşleme, 7’si gerçek bozulmaya denk geliyor. Bu sayı geçersizdir ve gerekçesi üst tablodadır. tahmin payi satırlarına bakın: sağlam kurulumda 3 dönem ateşliyor, kavram kaymalı kurulumda yine tam 3 dönem. Aynı ölçü, aynı bant, aynı ateşleme sayısı; tek fark, ikinci durumda o dönemlerin bozulmuş olması. Ateşleme bozulmadan gelmiyor, bozulmayla aynı zamana denk düşüyor. Sağlam satır bunu kanıtlar: hiçbir şey bozuk değilken de aynı üç dönem konuşuyor.

Bu kursun kuralının tam tanımıdır: ateşlemesi ile bozulması ayrı ayrı gösterilmeyen bir gösterge ölçülmemiş sayılır. Sağlam satır olmasaydı 0,05 bandındaki tahmin payı kusursuz bir gösterge gibi okunurdu — 3 ateşleme, 0 yanlış alarm. Sağlam satırla birlikte 7 yakalamanın 3’ü düşer ve geriye 4 kalır. O dördünü tek bir ölçü verir: isabet, iki zararsız senaryoda 0,05 ve 0,10 bantlarında hiç ateşlemeyip zararlı senaryoda dört dönemin dördünde ateşleyen tek satır. Bedeli son sütundadır: yakalama gecikmesi 2 dönem, ve bant seçilerek küçültülemez.

Yanlış Alarm ile Körlüğün Takası

Bandı genişletmek sayıyı düzeltmez, sıfırlar. 0,05’ten 0,10’a çıkıldığında yanlış alarm 6’dan 2’ye iner ve yakalanan 7’den 4’e — ama düşen üç yakalama zaten geçersiz olan tahmin payı yakalamalarıdır, yani 0,10 bandı hiçbir gerçek bilgi kaybetmeden yanlış alarmın üçte ikisini atar. Bu, bandı genişletmenin bedava olduğu tek adımdır.

0,20 satırı ise üç sıfırdır: yanlış alarm yok, ateşleme yok, yakalama yok. Bandın isabet üzerindeki karşılığı hesaplanabilir: 0,9700 tabanının 0,80 katı 0,7760‘tır, yani 0,20’lik bir bant isabetin bu değerin altına inmesini bekler. Kavram kaymasının en dip değeri 0,8150‘dir ve 0,7760’ın 0,0390 üstünde, yaklaşık sekiz çözünürlük adımı yukarıda kalır. Bant, ölçünün ulaşamayacağı bir yere konmuştur; sessizliği bir sağlık işareti değil, tanım gereği ölü bir göstergedir.

Üç bandı birlikte okumak kursun ikinci iddiasını verir: izleme de bozar. Dar bant hiçbir şeyin bozulmadığı bir üretimde altı kez uyandırır; geniş bant isabetin 0,9700’den 0,8150’ye düştüğü bir üretimde hiç konuşmaz. Aradaki bant yalnız bu kurguda iyi bir seçimdir, çünkü hangi ateşlemenin gerçek olduğunu gerçek isabete bakarak biliyoruz — üretimde o bilgi iki dönem sonra gelir ve bant onsuz seçilir.

Özet

  • Uyarı eşiği bir ölçü değil bir seçimdir: 2026-08 tabanından göreli sapmanın aşmaması gereken paydır ve ateşleyen dönem sayısını doğrudan belirler.
  • 0,05 bandında üç ölçü ve üç senaryo boyunca 13 dönem ateşler, 6’sı yanlış alarmdır; 0,10’da 6 ateşleme ve 2 yanlış alarm; 0,20’de üçü de sıfırdır.
  • 0,05’teki 7 yakalamanın 3’ü geçersizdir: tahmin payı sağlam kurulumda da tam 3 dönem ateşler. Ateşlemesi ile bozulması ayrı ayrı gösterilmeyen gösterge ölçülmemiş sayılır; geçerli yakalama 4’tür ve hepsi isabetten gelir.
  • İsabet, iki zararsız senaryoda hiç ateşlemeyip zararlı senaryoda dört dönemin dördünde ateşleyen tek ölçüdür; bedeli 2 dönemlik yakalama gecikmesidir ve bant seçimiyle küçültülemez.
  • 0,20 bandı isabetin 0,7760’ın altına inmesini ister; en dip değer 0,8150’dir, yani bant ölçünün ulaşamayacağı yerdedir ve sessizliği bir sağlık işareti değildir.

Sonraki Adım

Bu dersin geçerli tek göstergesi isabetti ve isabet etiket ister. Şimdiye kadar etiket kurgunun bir armağanıydı: her satırın gerçek sınıfı biliniyordu, yalnız iki dönem geç geliyordu. Üretimde öyle değildir — etiket toplanır ve toplanan şey bütün tahminler için gelmez. Sonraki ders etiketin nereden geldiğini sorar: toplama yordamı tahminlerin ancak bir kısmını kapsadığında ölçülen isabetin gerçek isabetten ne kadar saptığını ve hangi toplama biçiminin kapsamı büyütürken yanlılığı da büyüttüğünü 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