İçeriğe geç
academia.sh

Ders 08 / 22

İstem Yinelemesi

Ölçerek iyileştirme döngüsünün kendisi ölçü altına alınıyor. On aday değişiklik 30 kayıtlık bir doğrulama kümesinde sekiz yineleme boyunca aç gözlü aranıyor: doğrulama isabeti 0,4000'den 1,0000'a çıkıyor ve beşinci yinelemede tavana vuruyor, sınama isabeti aynı noktada 0,9333 ile en yükseğine ulaşıp sekizinci yinelemede 0,8111'e iniyor. Doğrulamadaki iki koşum farkının ortalaması 0,1037, adım kazancının ortalaması 0,0625: sekiz yinelemenin yalnızca ikisi kendi ölçüm gürültüsünden büyük. Arama başka bir koşumda tekrarlandığında sekiz adımın yalnız biri aynı yerde aynı değişikliği seçiyor. Bedel 2640 çağrıdır ve seçilen sekiz değişiklikten üçü görevle ilgisizdir.

İçindekiler

Önceki dört derste dört tasarım kararı ölçüldü ve her biri tek başına, bir defada denendi. Gerçek bir istem böyle yazılmaz. Bir istem yazılır, ölçülür, bir yeri değiştirilir, yeniden ölçülür; bu döngü sayı iyileşmez olana kadar sürer. Döngünün adı istem yinelemesidir ve bu ders döngünün kendisini ölçü altına alır.

Ölçü altına almak, önceki müfredatın Makine Öğrenmesine Giriş kursunda kurulmuş bir ayrımı buraya taşımak demektir: bir sayıya bakarak karar veriyorsan, o sayı artık senin karar verdiğin kümenin sayısıdır ve genel başarımı temsil etmez. Orada bu ayrım eğitim, doğrulama ve sınama kümesiyle kuruldu. Burada model eğitilmiyor, istem aranıyor; ama ayrım aynı ayrımdır. Her yinelemede bakılan küme doğrulama, hiç bakılmayan küme sınamadır.

  • IT61. Model bir benzeticidir, tohumu 20260218’dir ve gerçek bir uç nokta çağrılmaz. Destek talepleri kurgudur.
  • IT62. 120 kurgu talep ikiye ayrılır: 30 kayıt doğrulama, 90 kayıt sınama. Arama yalnız doğrulamayı görür; sınama hiçbir karara girmez.
  • IT63. On aday değişiklik vardır. Beşi görevle ilgilidir (etiket kümesi, biçim kısıtı, iki örnek, iki örnek daha, olumlu bildirim), beşi ilgisizdir (nazik giriş, kısa yaz, teşekkür, not satırı, okuyucu notu). İlgisiz olanlar istemin yalnız metnini değiştirir.
  • IT64. Arama aç gözlüdür: her yinelemede kalan bütün adaylar doğrulamada denenir, en iyi sayıyı veren seçilir ve isteme kalıcı olarak eklenir.
  • IT65. Bedel çağrı sayısıyla yazılır. Denenen her aday, doğrulama kümesi büyüklüğü kadar çağrı harcar ve bu çağrılar geri alınamaz.

Sekiz Yinelemenin İki Sayısı

# BENZETICI -- KURGUDUR. Gercek bir model uc noktasi cagrilmaz; standart kitaplikla
# yazilmis bir dil modeli benzeticisidir. Talepler de kurgudur.
import math

TOHUM, M32 = 20260218, 0xFFFFFFFF
PENCERE, EN_UZUN = 240, 24


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


def belirtecle(m):                  # KURGU belirtecleyici: parca en cok bes damga
    b = []
    for s in m.lower().replace(",", " , ").split():
        while len(s) > 5:
            b.append(s[:5])
            s = s[5:]
        b.append(s)
    return b


def izno(m):
    h = 2166136261
    for c in m:
        h = ((h ^ ord(c)) * 16777619) & M32
    return h


KONU = ["fatura", "ariza", "sayac", "tarife", "kesinti"]
KALIP = {"fatura": ["bu ayki fatura tutari gecen aya gore cok yuksek geldi",
                    "faturami odedim ama sistemde borc gorunuyor",
                    "fatura ayrintisini nereden gorebilirim"],
         "ariza": ["evde su akmiyor bir ariza var galiba",
                   "baglanti bozuk oldu ve hic calismiyor",
                   "ariza kaydi actim ama ekip gelmedi"],
         "sayac": ["sayac okumasi yanlis girilmis endeks tutmuyor",
                   "sayac degisimi icin randevu istiyorum",
                   "sayac uzerindeki endeks ile fatura ayni degil"],
         "tarife": ["hangi tarife basamagindayim ogrenmek istiyorum",
                    "tarife degisikligi icin ne yapmam gerekiyor",
                    "birim ucret abone tipine gore degisiyor mu"],
         "kesinti": ["mahallede kesinti var ne zaman gelecek",
                     "planli kesinti duyurusu nereden takip edilir",
                     "aksamdan beri kesinti yasiyoruz"]}
EK = ["ayrica gecen ay da ayni sorun olmustu", "bu konuda memnun degilim",
      "acil bilgi almam gerekiyor", "kayit numaram elimde yok"]

TALEP = []
for i in range(120):
    r = uretec(TOHUM + i)
    k = KONU[ayrik(r(), [0.24, 0.22, 0.18, 0.20, 0.16])]
    m = KALIP[k][int(r() * 3)]
    if r() < 0.34:
        m += " " + EK[int(r() * 4)]
    TALEP.append({"no": 4001 + i, "metin": m, "konu": k})

KAVRAM = KONU + ["odeme", "sikayet", "bilgi", "genel"]
IZ = {"fatur": "fatura", "tutar": "fatura", "ayrin": "fatura", "ariza": "ariza",
      "bozuk": "ariza", "calis": "ariza", "akmiy": "ariza", "sayac": "sayac",
      "endek": "sayac", "okuma": "sayac", "tarif": "tarife", "birim": "tarife",
      "abone": "tarife", "basam": "tarife", "kesin": "kesinti", "duyur": "kesinti",
      "mahal": "kesinti", "borc": "odeme", "odedi": "odeme", "ucret": "odeme",
      "memnu": "sikayet", "sorun": "sikayet", "acil": "sikayet", "bilgi": "bilgi",
      "ogren": "bilgi", "nered": "bilgi"}
TUZAK = {"degis": "tarife", "kayit": "ariza", "yukse": "tarife", "gorun": "fatura",
         "ekip": "kesinti", "takip": "tarife", "giril": "fatura"}
SERBEST = ["talebiniz {k} konusuyla ilgili gorunuyor",
           "bu kayit {k} basligina girer , ilgili ekibe yonlendirildi",
           "kaydiniz alindi , konuyu inceleyip donus yapacagiz"]
SAYAC = [0]


def coz(istem):
    ust, _, talep = istem.partition("Talep : ")
    o = []
    for parca in ust.split("Ornek : ")[1:]:
        m, _, e = parca.partition(" => ")
        o.append((m.strip(), e.split(" .")[0].strip()))
    return o, talep


def cagir(istem, tohum=TOHUM, sicaklik=0.6, en_uzun=EN_UZUN):
    SAYAC[0] += 1
    bel = belirtecle(istem)
    bos = {"giris": len(bel), "cikis": 0, "yanit": ""}
    if len(bel) > PENCERE:
        return dict(bos, durum="baglam_asimi")
    if SAYAC[0] % 37 == 0:
        return dict(bos, durum="hiz_siniri")
    r = uretec(tohum ^ izno(istem))
    ornek, talep = coz(istem)
    bt = belirtecle(talep)
    aday = [k for k in KAVRAM if k[:5] in bel]
    if len(aday) < 3:
        aday = KAVRAM
    p = {k: 0.15 for k in aday}
    for t in bt:
        for tablo, w in ((IZ, 1.0), (TUZAK, 1.3)):
            if tablo.get(t) in p:
                p[tablo[t]] += w
    for j, (m, e) in enumerate(ornek):
        if e in p:
            w = 0.5 + (j + 1) / len(ornek)
            for t in set(belirtecle(m)):
                if t in bt:
                    p[e] += 0.30 * w
    e = [math.exp(p[a] / sicaklik) for a in sorted(p)]
    k = sorted(p)[ayrik(r(), [v / sum(e) for v in e])]
    y = (f"etiket : {k}" if "bicim" in bel
         else SERBEST[ayrik(r(), [0.42, 0.26, 0.32])].format(k=k))
    cb = belirtecle(y)
    if len(cb) > en_uzun:
        cb, y = cb[:en_uzun], " ".join(cb[:en_uzun])
    return {"durum": "tamam", "giris": len(bel), "cikis": len(cb), "yanit": y}


def oku(y):
    for s in y.lower().split():
        if s in KONU:
            return s
    return None


DOGRULAMA, SINAMA = TALEP[:30], TALEP[30:]      # 30 dogrulama , 90 sinama
TABAN = "Su destek talebini yanitla . "
DEGISIKLIK = [
    ("etiket kumesi", "Talebi su etiketlerden birine ata : fatura , ariza , sayac , "
                      "tarife , kesinti . "),
    ("bicim kisiti", "Yanitin bicimi : etiket : <deger> . "),
    ("iki ornek", "Ornek : fatura ayrintisini nereden gorebilirim => fatura . "
                  "Ornek : ariza kaydi actim ama ekip gelmedi => ariza . "),
    ("iki ornek daha", "Ornek : sayac okumasi yanlis girilmis endeks tutmuyor => sayac . "
                       "Ornek : hangi tarife basamagindayim ogrenmek istiyorum => tarife . "),
    ("olumlu bildirim", "Yanitinda yalnizca konu etiketi olsun . "),
    ("nazik giris", "Lutfen dikkatli davran . "),
    ("kisa yaz", "Yanitini kisa tut . "),
    ("tesekkur", "Tesekkurler . "),
    ("not satiri", "Not : kayit sistemine islenecek . "),
    ("okuyucu notu", "Yanit destek ekibi tarafindan okunacak . "),
]


def olc(parcalar, kume, tohum=TOHUM):
    SAYAC[0] = 0
    d = 0
    for x in kume:
        s = cagir(TABAN + "".join(parcalar) + "Talep : " + x["metin"], tohum=tohum)
        d += oku(s["yanit"]) == x["konu"]
    return d / len(kume), SAYAC[0]


def ara(tohum, adim=8):
    """Her yinelemede butun adaylari DOGRULAMA uzerinde dener, en iyisini secer."""
    secili, kalan, cagri, iz = [], list(DEGISIKLIK), 0, []
    for _ in range(adim):
        en, ad = None, None
        for a, parca in kalan:
            v, c = olc(secili + [parca], DOGRULAMA, tohum)
            cagri += c
            if en is None or v > en:
                en, ad = v, (a, parca)
        secili.append(ad[1])
        kalan = [x for x in kalan if x[0] != ad[0]]
        dv, c1 = olc(secili, DOGRULAMA, tohum)
        sv, c2 = olc(secili, SINAMA, tohum)
        cagri += c1 + c2
        iz.append((ad[0], dv, sv, cagri))
    return iz


d0, c0 = olc([], DOGRULAMA)
s0, c1 = olc([], SINAMA)
print("yineleme  secilen degisiklik    dogrulama  sinama  ayrisma  toplam cagri")
print(f"{0:>8}  {'-':<20}  {d0:>9.4f}  {s0:.4f}  {d0 - s0:+.4f}  {c0 + c1:>12}")
IZLEK = ara(TOHUM)
for i, (ad, dv, sv, cg) in enumerate(IZLEK, 1):
    print(f"{i:>8}  {ad:<20}  {dv:>9.4f}  {sv:.4f}  {dv - sv:+.4f}  {cg + c0 + c1:>12}")
yineleme  secilen degisiklik    dogrulama  sinama  ayrisma  toplam cagri
       0  -                        0.4000  0.3778  +0.0222           120
       1  not satiri               0.7000  0.5889  +0.1111           540
       2  bicim kisiti             0.8000  0.5889  +0.2111           930
       3  iki ornek                0.8333  0.8556  -0.0222          1290
       4  etiket kumesi            0.9667  0.8556  +0.1111          1620
       5  olumlu bildirim          1.0000  0.9333  +0.0667          1920
       6  kisa yaz                 0.9667  0.9111  +0.0556          2190
       7  iki ornek daha           0.9667  0.8667  +0.1000          2430
       8  tesekkur                 0.9000  0.8111  +0.0889          2640

Doğrulama sütunu bir arama kaydının olağan görüntüsüdür: 0,4000’den 1,0000’a, kesintisiz yükseliş. Bu sütuna bakan biri sekiz yinelemede istemi tam isabetli hâle getirdiğini söyler. Sınama sütunu aynı hikâyeyi anlatmıyor. En yüksek sınama sayısı 0,9333 ve beşinci yinelemede geliyor; sonraki üç yineleme onu 0,8111’e indiriyor. Beşinci yinelemeden sonra yapılan her değişiklik doğrulamayı korumak için seçildi ve sınamada zarar verdi.

Ayrışma sütunu iki kümenin arasındaki farkı taşıyor. Başlangıçta +0,0222, yani iki küme neredeyse aynı şeyi söylüyor. İkinci yinelemede +0,2111’e çıkıyor: doğrulama 0,8000 derken sınama hâlâ 0,5889. Bu noktada doğrulamaya bakarak verilen her karar, kendisiyle ilgisi olmayan bir küme hakkında konuşuyor. Ayrışmanın kaynağı kümenin küçüklüğüdür — otuz kayıtta bir kaydın düzelmesi sayıyı 0,0333 oynatır ve üç kaydın rastgele düzelmesi bir iyileştirme gibi görünür.

Birinci satır bunun en açık örneğidir. Aramanın ilk seçtiği değişiklik not satırıdır: göreve hiçbir bilgi eklemeyen, etiket kümesinden, biçimden ve örnekten söz etmeyen bir cümle. Doğrulamada +0,3000 getirdi ve bütün arama boyunca ölçülen en büyük tek adım kazancı oldu. Sekiz değişiklikten üçü bu türdendir. Aç gözlü arama, sayıyı yükselten şeyi seçer; sayıyı niçin yükselttiğini sormaz.

Aramanın Kendi Güvenilirliği

IKINCI = ara(TOHUM + 1)
print("ayni arama , baska kosum")
for i, ((a1, *_), (a2, d2, s2, _)) in enumerate(zip(IZLEK, IKINCI), 1):
    print(f"{i:>2}  {a1:<18} -> {a2:<18} dogrulama {d2:.4f}  sinama {s2:.4f}")
print(f"iki aramanin ayni sirada sectigi degisiklik: "
      f"{sum(a == b for (a, *_), (b, *_) in zip(IZLEK, IKINCI))}/8")

sec, fd, fs = [], [], []
for ad, *_ in [(None,)] + IZLEK:
    if ad:
        sec.append(dict(DEGISIKLIK)[ad])
    fd.append(abs(olc(sec, DOGRULAMA)[0] - olc(sec, DOGRULAMA, TOHUM + 1)[0]))
    fs.append(abs(olc(sec, SINAMA)[0] - olc(sec, SINAMA, TOHUM + 1)[0]))
gd = sum(fd) / len(fd)
print(f"\niki kosum farki -- dogrulama (30 kayit) ortalama {gd:.4f} , en buyuk {max(fd):.4f}")
print(f"iki kosum farki -- sinama (90 kayit) ortalama {sum(fs) / len(fs):.4f} ,"
      f" en buyuk {max(fs):.4f}")

dv, sv = [d0] + [x[1] for x in IZLEK], [s0] + [x[2] for x in IZLEK]
adim = [dv[i] - dv[i - 1] for i in range(1, 9)]
print(f"adim kazanci iki kosum farkindan buyuk olan yineleme: "
      f"{sum(x > gd for x in adim)}/8 , adim kazanci ortalamasi {sum(adim) / 8:+.4f}")
print(f"\ndogrulama en yuksek {max(dv):.4f} ({dv.index(max(dv))}. yineleme) ,"
      f" sinama en yuksek {max(sv):.4f} ({sv.index(max(sv))}. yineleme)")
print(f"5 -> 8 : dogrulama {dv[5]:.4f} -> {dv[8]:.4f} , sinama {sv[5]:.4f} -> {sv[8]:.4f}")
print(f"ayrisma en buyuk {max(d - s for d, s in zip(dv, sv)):+.4f} , 8. yinelemede "
      f"{dv[8] - sv[8]:+.4f}")
ilgisiz = ("nazik giris", "kisa yaz", "tesekkur", "not satiri", "okuyucu notu")
print(f"arama bedeli {IZLEK[-1][3] + c0 + c1} cagri , secilen sekiz degisiklikten "
      f"{sum(a in ilgisiz for a, *_ in IZLEK)} tanesi gorevle ilgisiz")
ayni arama , baska kosum
 1  not satiri         -> kisa yaz           dogrulama 0.7000  sinama 0.5667
 2  bicim kisiti       -> etiket kumesi      dogrulama 0.6333  sinama 0.4444
 3  iki ornek          -> bicim kisiti       dogrulama 0.7667  sinama 0.7333
 4  etiket kumesi      -> iki ornek          dogrulama 0.9667  sinama 0.8778
 5  olumlu bildirim    -> nazik giris        dogrulama 0.9667  sinama 0.9111
 6  kisa yaz           -> not satiri         dogrulama 0.8667  sinama 0.7111
 7  iki ornek daha     -> iki ornek daha     dogrulama 0.9000  sinama 0.8889
 8  tesekkur           -> okuyucu notu       dogrulama 0.9333  sinama 0.8333
iki aramanin ayni sirada sectigi degisiklik: 1/8

iki kosum farki -- dogrulama (30 kayit) ortalama 0.1037 , en buyuk 0.1667
iki kosum farki -- sinama (90 kayit) ortalama 0.0728 , en buyuk 0.1778
adim kazanci iki kosum farkindan buyuk olan yineleme: 2/8 , adim kazanci ortalamasi +0.0625

dogrulama en yuksek 1.0000 (5. yineleme) , sinama en yuksek 0.9333 (5. yineleme)
5 -> 8 : dogrulama 1.0000 -> 0.9000 , sinama 0.9333 -> 0.8111
ayrisma en buyuk +0.2111 , 8. yinelemede +0.0889
arama bedeli 2640 cagri , secilen sekiz degisiklikten 3 tanesi gorevle ilgisiz

Üstteki çizelge aramayı aynı düzenekte, yalnız tohumu değiştirerek yeniden koşturuyor. Sekiz adımın yalnız biri aynı yerde aynı değişikliği seçiyor. İkinci koşum ilk adımda “kısa yaz”ı, ikinci adımda etiket kümesini seçiyor ve beşinci adımda “nazik giriş”e ulaşıyor. İki arama iki ayrı istem üretti; ikisi de kendi doğrulama kümesinde iyi görünüyor ve ikisi de aynı düzeneğin ürünü. Arama, aradığı şeyi değil, koşumu buluyor.

Alt bölüm bunun sayısını veriyor. Doğrulama kümesinde aynı istemin iki koşumu arasındaki fark ortalama 0,1037, en büyüğü 0,1667. Aramanın adım başına ortalama kazancı +0,0625. Sekiz yinelemenin yalnız ikisinde adım kazancı kendi ölçüm gürültüsünden büyük; kalan altı adım, kursun kuralına göre ölçülmemiş sayılır ve buna rağmen isteme kalıcı olarak eklendiler. Sınama kümesinde aynı fark ortalama 0,0728, yani üç kat büyük bir kümede ölçü gözle görülür biçimde daha durgun. Küçük doğrulama kümesi hem daha ucuzdur hem de aramanın kovalayacağı gürültüyü kendisi üretir.

Doyma noktası beşinci yinelemedir: doğrulama orada 1,0000’a vurur ve daha yükselemez, sınama da orada en yüksek değerini alır. Sonraki üç yineleme 720 ek çağrı harcayıp sınamayı 0,9333’ten 0,8111’e indiriyor. Aramanın toplam bedeli 2640 çağrıdır ve bu çağrıların büyük bölümü seçilmeyen adaylara gitti. Bir istem araması, bir ayar aramasıyla aynı yasaya tabidir: bütçe büyüdükçe doğrulamadaki kazanç sınamaya taşınmaz. Ölçülmeden değiştirilen istem bir iyileştirme değil bir tahmindir; ölçülen küme yanlışsa, ölçmek de tahmin etmektir.

Özet

  • Aç gözlü arama sekiz yinelemede doğrulama isabetini 0,4000’den 1,0000’a çıkardı; sınama isabeti aynı sürede 0,3778’den en yüksek 0,9333’e çıkıp 0,8111’e indi.
  • Kazanç beşinci yinelemede doydu. Sonraki üç yineleme 720 ek çağrı harcayıp sınamayı 0,1222 düşürdü; iki küme arasındaki ayrışma en büyük değerini ikinci yinelemede +0,2111 olarak aldı.
  • Aramanın ilk ve en büyük adımı (+0,3000) görevle ilgisiz bir cümleden geldi; seçilen sekiz değişiklikten üçü göreve hiçbir bilgi eklemiyor.
  • Doğrulamada iki koşum farkı ortalama 0,1037, adım kazancı ortalama +0,0625: sekiz yinelemenin yalnız ikisi kendi ölçüm gürültüsünden büyük. Sınamada aynı fark 0,0728.
  • Aynı arama başka bir tohumla koşturulduğunda sekiz adımın yalnız biri aynı yerde aynı değişikliği seçiyor; toplam bedel 2640 çağrıdır.

Sonraki Adım

Sekiz derste istem bir tasarım nesnesine dönüştü. Bileşenlerine ayrıldı, kalıcı bir yönerge kazandı, rol ve talep düzeyinde açıldı, biçimi ve uzunluğu belirlendi, örnek aldı, kuralları olumlu yazıldı ve ölçülerek yinelendi. Her adımda değişen tek şey metin oldu ve her adımın bedeli belirteçle, çağrıyla ya da ihlalle yazıldı. Ortak yanları da açıktır: hepsi modele ne söyleneceğini düzenledi.

Hiçbiri modele nasıl düşüneceğini sormadı. Bu sekiz derste model her talebe tek bir hamlede yanıt verdi: istemi aldı, bir etiket seçti, bir cümle yazdı. Arada bir adım yoktu. Oysa iki fatura döneminin farkını hesaplayıp hangi tarifeye göre fazla ödendiğini bulmak gibi bir talep tek hamlede çözülecek bir şey değildir; birkaç ara adım ister ve o adımların modelin kendisi tarafından üretilmesi gerekir. Sonraki konu bunu ele alır ve ilk dersinde kursun ikinci iddiasını kurar: ara adım istemek belirteci katlar ve her görevde kazandırmaz. Aynı teknik çok adımlı bir hesapta kazandırırken tek adımlı bir sınıflandırmada bedeli artırıp sayıyı yerinde bırakır, ve ikisi de bası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