---
title: 'Pekiştirmeli Öğrenme ve Öneri Sistemleri'
source: 'https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri'
language: tr
updated: '2026-08-17T04:42:09+00:00'
license: 'CC BY-SA 4.0'
lessons: 14
outcomes:
    - 'Pekiştirmeli öğrenme problemini durum, eylem, geçiş ve ödülle biçimsel olarak tanımlar'
    - 'Değer ve politika temelli yöntemleri ayırır ve her birinin topladığı veriyi sayar'
    - 'Öneri sistemlerini soğuk başlangıç ve değerlendirme sorunlarıyla kurar ve geri besleme döngüsünü ölçer'
---

# Pekiştirmeli Öğrenme ve Öneri Sistemleri

Kararı veren bir sistemin kendi verisini değiştirdiği düzende ölçünün nasıl okunacağı: rastgele politikanın getirisi ile öğrenen politikanınki ve hangi durumların hiç görülmediği, geçişin yalnız o anki duruma bağlı olması varsayımı bozulduğunda getirinin oynaması, keşif oranı süpürüldükçe biriken kaçırılan kazanç ve hiç denenmeyen eylem, değer çizelgesinin hiç güncellenmeyen hücreleri, ardışık örneklerin eğitimi bozması ile deneyim tekrarının düzelttiği, eğim kestiriminin bölüm sayısına göre değişkenliği, eleştirmen kötüyken aktörün gittiği yer, en popüler kalemi herkese önermenin şaşırtıcı yüksekliği, içerik temelli önerinin kapsaması, komşu bulunamayan abone sayısı, gözlenmeyen hücrenin sıfır sayılmasıyla atlanması arasındaki fark, soğuk aboneyi ısıtmanın bedeli, çevrimdışı sıralamanın çevrimiçi davranışı söylememesi ve geri besleme döngüsünün daralttığı kapsama.

## Pekiştirmeli Öğrenme

Sonucu ancak eylemden sonra öğrenen ve kendi verisini toplayan bir ajanın ölçülmesi: saha ziyareti kurgusunda rastgele politikanın getirisi ile öğrenen politikanınki ve hiç görülmeyen durumlar, geçişin yalnız o anki duruma bağlı olması varsayımına bir geçmiş alanı eklendiğinde getirinin değişmesi, keşif oranı süpürüldükçe biriken kaçırılan kazanç ile hep sömüren politikanın hiç denemediği eylem, zaman farkı güncellemesiyle öğrenilen çizelgenin hiç güncellenmeyen hücreleri, ardışık örneklerin ilişkili olmasının eğitimi bozması ve deneyim tekrarının kattığı fark, aynı bütçede eğim kestiriminin kaç ayrı yön vermesi ve değer çizgisinin bunu düşürmesi, üstünlük kestiriminin kattığı ile eleştirmen kötüyken aktörün gittiği yer.

### Ajan, Ortam ve Ödül

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/ajan-ortam-ve-odul

Kararın kendi verisini ürettiği bir düzende üç sayının okunması: saha ziyareti kurgusunda hiç ekip çıkarmayan politika 0,000 getiri alıyor, taban çizgisi olan rastgele eylem 10,432, gecikmesi en büyük aboneye giden el kuralı 20,621. Üçüncü sayı toplanan veridir: rastgele politika 243 durumun 234'ünü ve 1.458 durum-eylem çiftinin 1.251'ini görüyor, daha iyi getiri alan kural yalnız 103 durumu ve 103 çifti görüyor. Aynı dönemde altı eylemin vereceği ödül -2, -2, 1, -2, 1 ve 0 iken ajan bunlardan yalnız birini öğreniyor; denetimli öğrenmenin etiketi yerine geçen şey budur.

Önceki kurs otuz sekiz dersin sonunda tek bir düzeni sürdürdü: modele bir girdi verildi, model
bir çıktı üretti, çıktının doğruluğu ya elde bulunan bir etiketten ya da çıktıyı tarif eden bir
ölçütten okundu. Girdi modelin kararından bağımsızdı. Model ne söylerse söylesin sıradaki satır,
sıradaki cümle, sıradaki görüntü değişmiyordu; küme baştan oradaydı ve orada kalıyordu.

Bu kurs o düzenin dışına çıkar. Burada kararın sonucu ancak karar verildikten **sonra** görünür
ve kararın kendisi bir sonraki girdiyi **değiştirir**. Bir aboneyi ziyaret etmek onun durumunu
açığa çıkarır, aynı zamanda o durumu ortadan kaldırır; ziyaret edilmeyen abone kötüleşmeye devam
eder. Böyle bir düzende "başarım" sayısı, sayının hangi veri üzerinde ölçüldüğü yazılmadan
okunamaz, çünkü **veriyi politikanın kendisi toplamıştır**. Bu dersin işi döngüyü kurmak ve
kursun üç sayısını yerine oturtmaktır: taban çizgisi, yöntemin sayısı, toplanan veri.

- **PO1. Kurgu saha ziyareti planlamasıdır ve üretilmiştir.** Bölgesel ölçüm ağı kurgusunun bir
  bölgesinde beş abone (A1–A5) bulunur. Hiçbir sayı gerçek bir kayıttan gelmez; hepsi tohum
  20260218 ile ders içinde üretilir.
- **PO2. Her dönem tek ekip çıkar.** Eylem kümesi altı ögelidir: beş aboneden birini ziyaret etmek
  ya da ekibi hiç çıkarmamak.
- **PO3. Abonenin saha durumu gizlidir ve üç kademelidir** — temiz, şüpheli, arızalı. Ziyaret bu
  kademeyi **açığa çıkarır** ve arıza onarıldığı için sıfırlar.
- **PO4. Ziyaret edilmeyen abone kötüleşir.** Her dönem, abonesine özgü bir olasılıkla bir kademe
  yükselir; olasılıklar A1'den A5'e 0,30, 0,05, 0,60, 0,15 ve 0,45'tir.
- **PO5. Ajanın gördüğü durum gecikme sınıfıdır.** Her abone için son ziyaretten bu yana geçen
  dönem sayısı üç kovaya bölünür: 0–2, 3–5, 6 ve üzeri. Durum uzayı $3^5 = 243$ ögedir.
- **PO6. Ödül ziyaretin bulduğudur.** Bulunan kademenin karşılığı sırasıyla 0, 3 ve 9'dur ve her
  ziyaret 2 birim maliyet götürür. Ekip çıkmazsa ödül tam olarak 0'dır.
- **PO7. Bölüm 30 dönemdir ve indirim katsayısı 0,9'dur.** Getiri, bölümdeki ödüllerin indirimli
  toplamıdır; yakın dönemin ödülü uzak dönemin ödülünden ağır basar.
- **PO8. Taban çizgisi rastgele eylemdir.** Hiç ekip çıkarmamak da ölçülür ama taban olamaz:
  getirisi tanım gereği sıfırdır ve sıfır, eylem seçmenin değil ölçünün başlangıç noktasıdır.
- **PO9. Ölçü 300 bölümün ortalama getirisidir** ve yönü büyük iyidir. Bölüm tohumları sabittir,
  bütün politikalar aynı 300 başlangıcı görür.

#### Ortam, Eylem ve Ödül Döngüsü

```python
# ortam.py — KURGUDUR. Saha ziyareti planlamasi: bir bolgedeki bes abone ve
# her donem cikan tek ekip. Butun sayilar uretilmistir, bir kayittan gelmez.
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
ETIKET = ("A1", "A2", "A3", "A4", "A5", "cikma")
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9


def uretec(t):
    """Kendi yazilmis uretec; tohum sabit oldugu icin cikti her kosumda ayni."""
    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


class Ortam:
    """Saha durumu d gizlidir; ajanin gordugu durum gecikme sinifidir."""

    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def bolum(politika, tohum, iz=None):
    o = Ortam(tohum)
    s, G, c = o.durum(), 0.0, 1.0
    for _ in range(UFUK):
        a = politika(s, o)
        s2, r = o.adim(a)
        if iz is not None:
            iz.append((s, a, r, s2))
        G, c, s = G + c * r, c * INDIRIM, s2
    return G


def kosum(politika, n=300, iz=None):
    return [bolum(politika, TOHUM + 1000 * k, iz) for k in range(n)]


def rastgele_politika(tohum):
    r = uretec(tohum)
    return lambda s, o: int(r() * EYLEM)


o = Ortam(TOHUM)
r = uretec(TOHUM + 7)
print("donem  durum            eylem   odul  getiriye katki")
G, c = 0.0, 1.0
for t in range(8):
    s, a = o.durum(), int(r() * EYLEM)
    _, odul = o.adim(a)
    G, k = G + c * odul, c * odul
    print(f"{t:>4}   {str(s):<17}{ETIKET[a]:<6}{odul:>6.1f}{k:>13.3f}")
    c *= INDIRIM
print(f"sekiz donemin getirisi {G:.3f}")
```

```
donem  durum            eylem   odul  getiriye katki
   0   (1, 0, 0, 1, 0)  A5       1.0        1.000
   1   (2, 0, 0, 1, 0)  A3       7.0        6.300
   2   (2, 0, 0, 2, 0)  A1      -2.0       -1.620
   3   (0, 1, 0, 2, 0)  A2      -2.0       -1.458
   4   (0, 0, 0, 2, 1)  A3       7.0        4.593
   5   (0, 0, 0, 2, 1)  A5       7.0        4.133
   6   (1, 0, 0, 2, 0)  A4       1.0        0.531
   7   (1, 1, 0, 0, 0)  A3       7.0        3.348
sekiz donemin getirisi 16.828
```

Sekiz satır döngünün tamamını taşıyor. **Ajan** ekibi yönlendiren taraftır, **ortam** aboneleri ve
onların kötüleşmesini tutan taraf. Ajan bir **durum** görür, bir **eylem** seçer, ortam bir **ödül**
döndürür ve yeni bir duruma **geçer**. Üçüncü dönemde A1 ziyaret ediliyor ve ödül $-2$ çıkıyor:
ekip gitmiş, abone temiz bulunmuş, maliyet boşa yazılmış. Sekizinci dönemde A3 ziyaret ediliyor ve
ödül 7 oluyor, çünkü arıza kademesi bulunmuş ve onarılmış.

Ödül tek adımlıktır ve ajanın istediği şey tek adım değildir. **Getiri**, bölüm boyunca alınan
ödüllerin indirimli toplamıdır: $G = \sum_{t} \gamma^{t} r_t$, burada $\gamma = 0{,}9$. Sağdaki
sütun aynı 7'nin beşinci dönemde 4,593, sekizinci dönemde 3,348 olarak sayıldığını gösteriyor.
İndirim katsayısı bir tercihtir ve tercihi ölçü hâline getirir: aynı kazanç ne kadar geç gelirse o
kadar az sayılır. Sekiz dönemin getirisi 16,828.

Üçüncü dönemin bir ayrıntısı ilerideki dersin konusudur. Durum $(2, 0, 0, 2, 0)$ iken A1'in gecikme
sınıfı 2'dir, yani en az altı dönemdir ziyaret edilmemiştir, ama bulunan kademe temizdir ve ödül
$-2$'dir. Gecikme sınıfı abonenin gerçek kademesini söylemez, yalnız onunla ilişkilidir.

#### Taban Çizgisi ve Politikanın Sayısı

```python
# politika.py — Ayni ortam, ayni 300 bolum tohumu, ayni ufuk; degisen politika.


def bos_politika(s, o):
    """Ekip hic cikmaz: getirisi tanim geregi sifirdir."""
    return ABONE


def kural_politika(s, o):
    """Gecikme sinifi en buyuk aboneyi ziyaret et; hepsi 0 ise ekip cikarma."""
    en = max(s)
    return ABONE if en == 0 else s.index(en)


print(f"{'politika':<26}{'getiri':>9}{'en dusuk':>10}{'en yuksek':>11}")
for ad, pol in (("hic ekip cikarma", bos_politika),
                ("rastgele eylem", rastgele_politika(TOHUM + 7)),
                ("gecikmesi en buyuge git", kural_politika)):
    g = kosum(pol)
    print(f"{ad:<26}{statistics.mean(g):>9.3f}{min(g):>10.3f}{max(g):>11.3f}")
```

```
politika                     getiri  en dusuk  en yuksek
hic ekip cikarma              0.000     0.000      0.000
rastgele eylem               10.432    -6.887     31.159
gecikmesi en buyuge git      20.621     2.370     41.179
```

**Politika**, durumdan eyleme giden kuraldır. Üç politika aynı 300 başlangıcı görüyor ve üç ayrı
sayı veriyor. İlki hiç ekip çıkarmaz: her dönemde ödülü 0'dır, dolayısıyla getirisi de tam olarak
0'dır, en düşük ve en yüksek bölümü bile 0'dır. İkincisi altı eylemden birini ayrım gözetmeden
seçer ve 10,432 alır. Üçüncüsü tek satırlık bir el kuralıdır — hiçbir şey öğrenmez, yalnız durumu
okur — ve 20,621 alır.

Taban çizgisi olarak **rastgele eylem** seçilir. Hiç ekip çıkarmamak daha düşük bir sayı verse de
taban olamaz, çünkü getirisi ölçünün sıfır noktasıdır: bir politikanın onu geçmesi, o politikanın
eylem seçmeyi öğrendiğini değil, yalnız hareket etmenin bu kurguda hareketsizlikten iyi olduğunu
söyler. Rastgele eylem ise **eylem seçen ama seçimini hiçbir bilgiye dayandırmayan** yordamdır ve
karşılaştırmayı anlamlı kılan budur. İki taban da basılır, çünkü aralarındaki 10,432'lik açıklık
kurgunun kendisinin ne kadar cömert olduğunu ölçer.

Kuralın rastgele eylemi 10,189 aşması nereden gelir. Rastgele politika beş aboneyi eşit görür,
oysa A2 dönem başına 0,05 olasılıkla kötüleşir; ona yapılan ziyaret çoğunlukla $-2$ yazar. Kural,
gecikmesi büyük aboneye giderek boşa ziyareti azaltır. En düşük bölüm sayıları da bunu gösteriyor:
rastgele politikanın en kötü bölümü $-6,887$ iken kuralın en kötüsü $+2,370$'tir.

En yüksek bölüm sayıları ise tersini söylüyor ve okunmayı hak ediyor. Rastgele politika bir
bölümde 31,159'a kadar çıkabiliyor; kuralın en iyi bölümü 41,179. İki dağılım örtüşüyor, yani tek
bir bölüme bakan biri rastgele politikayı kuraldan iyi görebilirdi. Ortalama 300 bölüm üzerinden
alınmasının nedeni budur: bölüm getirisi hem başlangıç durumundan hem de kötüleşmenin çekilişinden
etkilenir ve iki politikayı ayıran şey tek bölümde değil ortalamada görünür.

#### Toplanan Veri

```python
# veri.py — Ucuncu sayi: politika kendi verisini topladigi icin durum uzayinin
# ne kadarini gordugu politikanin kendisine baglidir.
from collections import Counter

gorulen = {}
for ad, pol in (("rastgele eylem", rastgele_politika(TOHUM + 7)),
                ("gecikmesi en buyuge git", kural_politika)):
    iz = []
    kosum(pol, 300, iz)
    say = Counter(x[0] for x in iz)
    gorulen[ad] = set(say)
    cift = {(x[0], x[1]) for x in iz}
    pay = sum(n for _, n in say.most_common(3)) / len(iz)
    print(f"{ad:<26} durum {len(say):>4}/243  cift {len(cift):>5}/1458  "
          f"adim {len(iz)}  ilk uc durumun payi {pay:.4f}")

R, K = gorulen["rastgele eylem"], gorulen["gecikmesi en buyuge git"]
print(f"rastgelenin gorup kuralin gormedigi durum {len(R - K)}, "
      f"tersi {len(K - R)}, ikisinin de gormedigi {243 - len(R | K)}")

print("\nayni donemde alti eylemin verecegi odul (yalniz kurgu sahibi gorebilir):")
for a in range(EYLEM):
    o2 = Ortam(TOHUM)
    s0 = o2.durum()
    print(f"  {ETIKET[a]:<7}{o2.adim(a)[1]:>6.1f}")
print(f"baslangic durumu {s0}; ajanin gordugu bu alti satirdan yalniz biri")
```

```
rastgele eylem             durum  234/243  cift  1251/1458  adim 9000  ilk uc durumun payi 0.0319
gecikmesi en buyuge git    durum  103/243  cift   103/1458  adim 9000  ilk uc durumun payi 0.3327
rastgelenin gorup kuralin gormedigi durum 131, tersi 0, ikisinin de gormedigi 9

ayni donemde alti eylemin verecegi odul (yalniz kurgu sahibi gorebilir):
  A1       -2.0
  A2       -2.0
  A3        1.0
  A4       -2.0
  A5        1.0
  cikma     0.0
baslangic durumu (1, 0, 0, 1, 0); ajanin gordugu bu alti satirdan yalniz biri
```

Üst iki satır bu kursun ayırt edici sayısıdır. İki politika **aynı** bütçeyi harcıyor: 300 bölüm,
bölüm başına 30 dönem, toplam 9.000 adım. Topladıkları veri aynı değil. Rastgele politika 243
durumun 234'ünü ve 1.458 durum-eylem çiftinin 1.251'ini görüyor. Getirisi iki katı olan kural ise
yalnız 103 durum ve 103 çift görüyor — 140 durum ve 1.355 çift hakkında elinde tek bir gözlem yok.

İki sayının eşit olması kuralın belirleyici olmasındandır: gördüğü her durumda tek bir eylem
seçer, dolayısıyla o durumdaki öteki beş eylemin ne vereceğini hiçbir zaman öğrenmez. Daha iyi
getiri alan politika daha dar bir veri topluyor. Bu, kursun kuralının doğrudan sonucudur:
**topladığı veriyi yazmayan sistem ölçülmemiş sayılır**, çünkü 20,621 sayısı 103 durumluk bir
kesitte geçerlidir ve o kesitin dışında politikanın ne yapacağı ölçülmemiştir.

Sağdaki sütun aynı daralmayı yoğunluk üzerinden okuyor. Rastgele politikanın en çok uğradığı üç
durum 9.000 adımın yalnız 0,0319'unu tutar, yani ziyaretler durum uzayına neredeyse düz yayılır.
Kuralın en çok uğradığı üç durum ise adımların 0,3327'sini tutar: her üç adımdan biri aynı üç
durumdan birinde geçer. Üçüncü satır ilişkiyi kapatıyor. Kuralın gördüğü 103 durumun tamamını
rastgele politika da görmüştür, tersi ise 131 durumda yanlıştır — yani daha iyi politikanın veri
kümesi, tabanın veri kümesinin **öz altkümesidir**. Dokuz durumu ikisi de hiç görmedi.

Bu üçüncü sayının önceki altı kursta karşılığı yoktu. Orada küme ne yapılırsa yapılsın aynıydı;
bir modelin taban çizgisini geçmesi, üzerinde ölçüldüğü kümeyi değiştirmiyordu. Burada politika
değiştiği anda küme de değişiyor, dolayısıyla iki politikanın getirisi karşılaştırılırken iki ayrı
veri kümesi de karşılaştırılmış oluyor. İleride bir politikanın **öğrenilmesi** gerektiğinde bu
daralma doğrudan bir sorun hâline gelir: 1.355 çift hakkında hiç gözlemi olmayan bir yordam, o
çiftlerden birinin daha iyi olup olmadığını hiçbir zaman öğrenemez.

Alt blok denetimli öğrenmeden farkı tek bir oranla veriyor. Başlangıç durumu $(1, 0, 0, 1, 0)$ iken
altı eylemin vereceği ödüller $-2$, $-2$, $1$, $-2$, $1$ ve $0$'dır. Bu altı satır yalnızca kurgu
sahibi ortamı altı kez aynı tohumdan kurabildiği için basılabiliyor; ajan bir eylem seçer ve
**yalnız bir satırı** öğrenir, ötekiler kapanır. Denetimli öğrenmede her örnek doğru cevabını
yanında getirirdi; burada her adımda altı sonuçtan biri, yani **altıda biri** görünür, üstelik
seçilmeyen beş eylemin sonucu sonradan da sorulamaz çünkü ortam artık başka bir durumdadır.

#### Özet

- Ajan durumu görür, eylem seçer, ortam ödül döndürür ve yeni duruma geçer; getiri bölümdeki
  ödüllerin indirim katsayısı 0,9 ile indirimli toplamıdır ve örnek sekiz dönemde 16,828'dir.
- Hiç ekip çıkarmayan politikanın getirisi tanım gereği 0,000'dır; taban çizgisi bu değil,
  eylem seçen ama seçimini hiçbir bilgiye dayandırmayan **rastgele eylem** politikasıdır: 10,432.
- Durumu okuyan tek satırlık el kuralı 20,621 alır; farkın kaynağı, kötüleşme olasılığı 0,05 olan
  aboneye yapılan boşa ziyaretlerin azalmasıdır.
- Aynı 9.000 adımlık bütçede rastgele politika 243 durumun 234'ünü, kural yalnız 103'ünü görür;
  belirleyici olduğu için kuralın gördüğü durum-eylem çifti sayısı da 103'te kalır.
- Kuralın veri kümesi tabanınkinin öz altkümesidir: rastgele politikanın gördüğü 131 durumu kural
  hiç görmez, tersi hiç olmaz ve kuralın adımlarının 0,3327'si yalnız üç durumda geçer.
- Ajan her adımda altı eylemden yalnız birinin sonucunu öğrenir; seçilmeyen beş eylemin ödülü
  sonradan da sorulamaz, çünkü ortam bu arada değişmiştir.

#### Sonraki Adım

Bu derste durum, eylem, geçiş ve ödül adlarıyla kullanıldı ama hiçbiri tanımlanmadı. Tanımın
gerektirdiği tek şey ise sayıların hepsini birden taşıyor: ortamın bir sonraki duruma nasıl
geçeceği ve hangi ödülü vereceği, **yalnız o anki duruma ve seçilen eyleme** bağlı olmalıdır. Bu
derste basılan bir satır bu koşulu şimdiden zorluyordu: A1'in gecikme sınıfı 2 iken bulunan kademe
temizdi ve ödül $-2$ oldu, oysa başka bir bölümde aynı sınıf 7 verebilirdi. Sonraki ders kurulumu
biçimsel bir beşliye oturtur, koşulun ne söylediğini yazar ve **koşul bozulduğunda ne kaybedildiğini
sayar**: aynı el kuralı, duruma bir geçmiş alanı eklendiğinde kaç puan daha fazla getiri alıyor.

### Karar Süreci Biçimselleştirmesi

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/karar-sureci-bicimsellestirmesi

Durum, eylem, geçiş, ödül ve indirim katsayısından kurulu beşlinin yazılması ve tanımlayıcı koşulunun kurguda ölçülmesi: aynı durum-eylem hücresinde ardıl durum dört ayrı değere 0,4307, 0,2239, 0,1770 ve 0,1046 olasılıkla dağılıyor, ödül ise -2, 1 ve 7'ye 0,0516, 0,2347 ve 0,7137 ile. Geçmişten bağımsızlık sınaması koşulun tam tutmadığını gösteriyor: en çok görülen hücrede aynı durum ve aynı eylem için ortalama ödül, gecikme 3 iken 4,518, gecikme 4 ve üzeriyken 5,944; fark 1,425 ve beş hücrenin beşinde de artı yönde. Aynı el kuralı 243 durumluk tanımda 20,621, geçmiş alanı eklenmiş 1.215 durumluk tanımda 23,148, gizli kademeyi gören üst referansta 29,053 getiri alıyor.

Önceki ders döngüyü kurdu ve dört adı kullandı: durum, eylem, geçiş, ödül. Hiçbiri tanımlanmadı.
Adların bir tanımı olduğunda ise tanım tek bir koşula dayanır ve o koşul önceki dersin son
satırında zaten zorlanıyordu: A1'in gecikme sınıfı 2 iken bulunan kademe temiz çıkmış ve ödül
$-2$ olmuştu, oysa aynı sınıfla başka bir bölümde 7 çıkabilirdi.

Bu ders kurulumu biçimsel bir **beşliye** oturtur, beşlinin tanımlayıcı koşulunu yazar ve o koşulun
kurguda **tam tutmadığını** ölçer. Sonra da bunun bedelini ödetir: koşul zayıfladığında politika ne
kaybediyor, duruma bir geçmiş alanı eklenince ne kadarı geri geliyor.

- **PO10. Kurgu 01'den aynen sürer.** Beş abone, tek ekip, aynı kötüleşme olasılıkları, aynı ödül
  çizelgesi, aynı 30 dönemlik ufuk, aynı indirim katsayısı 0,9 ve aynı tohum. Kurgu olduğu yeniden
  yazılır: hiçbir sayı gerçek bir kayıttan gelmez.
- **PO11. Karar süreci bir beşlidir:** durum kümesi $S$, eylem kümesi $A$, geçiş, ödül ve indirim
  katsayısı $\gamma$. Kurguda $\lvert S \rvert = 243$, $\lvert A \rvert = 6$, $\gamma = 0{,}9$.
- **PO12. Geçiş ve ödül birer dağılımdır**, tek bir sayı değil. Aynı durumda aynı eylem, farklı
  ardıl durumlar ve farklı ödüller verebilir.
- **PO13. Tanımlayıcı koşul:** ardıl durumun ve ödülün dağılımı **yalnız o anki duruma ve seçilen
  eyleme** bağlıdır; daha eski durumlar, daha eski eylemler ve geçen süre hiçbir şey eklemez. Bu
  koşula **geçmişten bağımsız geçiş** denir.
- **PO14. Koşul kurguda tam tutmaz ve bu ders bunu ölçer.** Ortamın gizli kademe vektörü koşulu
  taşır; ajanın gördüğü gecikme sınıfı vektörü onun bir **izdüşümüdür** ve taşımaz.
- **PO15. Sınama biçimi:** aynı durum-eylem hücresindeki ödül örnekleri, durumun taşımadığı bir
  geçmiş alanına göre ikiye bölünür. Ortalamalar ayrışıyorsa koşul o hücrede bozulmuştur.
- **PO16. Geçmiş alanı**, en uzun süredir ziyaret edilmemiş abonenin numarasıdır. Eklendiğinde
  durum uzayı 243'ten 1.215'e çıkar.
- **PO17. Üst referans gizli kademeyi doğrudan gören politikadır.** Ulaşılabilir değildir; yalnız
  izdüşümün ne kadarını kaybettirdiğini sınırlamak için basılır. İz 600 bölüm, 18.000 adımdır.

#### Beşli

```python
# ortam.py — KURGUDUR. Saha ziyareti planlamasi bir onceki dersten aynen surer:
# bir bolgedeki bes abone, her donem tek ekip, tohum 20260218.
import statistics
from collections import Counter, defaultdict

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
ETIKET = ("A1", "A2", "A3", "A4", "A5", "cikma")
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9


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


class Ortam:
    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def kosum(politika, n=300):
    G = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s, g, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s, r = o.adim(politika(s, o))
            g, c = g + c * r, c * INDIRIM
        G.append(g)
    return G


def kural(s, o):
    """Gecikme sinifi en buyuk aboneyi ziyaret et; hepsi 0 ise ekip cikarma."""
    en = max(s)
    return ABONE if en == 0 else s.index(en)


def iz_topla(politika, n=600):
    """Her adimda ziyaret edilen abonenin TAM gecikmesini de kaydeder."""
    iz = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s = o.durum()
        for _ in range(UFUK):
            a = politika(s, o)
            gec = o.g[a] if a < ABONE else -1
            s2, r = o.adim(a)
            iz.append((s, a, r, s2, gec))
            s = s2
    return iz


IZ = iz_topla(kural)
S, A = (0, 0, 1, 1, 0), 2
alt = [(r, s2, g) for s, a, r, s2, g in IZ if s == S and a == A]
print(f"durum kumesi {3 ** ABONE}, eylem kumesi {EYLEM}, ufuk {UFUK}, indirim {INDIRIM}")
print(f"iz {len(IZ)} adim; ornek hucre {S} eylem {ETIKET[A]}, {len(alt)} ornek")
print("ardil durum dagilimi (ilk dort):")
for s2, n in Counter(x[1] for x in alt).most_common(4):
    print(f"   {s2}  {n / len(alt):.4f}")
print("odul dagilimi:")
for r, n in sorted(Counter(x[0] for x in alt).items()):
    print(f"   {r:>5.1f}  {n / len(alt):.4f}")
```

```
durum kumesi 243, eylem kumesi 6, ufuk 30, indirim 0.9
iz 18000 adim; ornek hucre (0, 0, 1, 1, 0) eylem A3, 2113 ornek
ardil durum dagilimi (ilk dort):
   (1, 0, 0, 1, 0)  0.4307
   (0, 0, 0, 2, 1)  0.2239
   (0, 1, 0, 1, 0)  0.1770
   (0, 0, 0, 1, 1)  0.1046
odul dagilimi:
    -2.0  0.0516
     1.0  0.2347
     7.0  0.7137
```

Beşli beş nesneden kuruludur. **Durum kümesi** $S$: gecikme sınıfı vektörlerinin tamamı, 243 öge.
**Eylem kümesi** $A$: altı öge. **Geçiş**: bir durum-eylem çiftine, ardıl durumlar üzerinde bir
dağılım karşılık getirir. **Ödül**: aynı çifte, sayılar üzerinde bir dağılım karşılık getirir.
**İndirim katsayısı** $\gamma = 0{,}9$: gelecekteki ödüllerin bugünkü ağırlığı.

Çıktı geçişin ve ödülün niçin birer dağılım olduğunu tek bir hücrede gösteriyor. Durum $(0, 0, 1,
1, 0)$ iken A3 ziyaret edildiğinde 2.113 örnek toplanmış ve ardıl durum en az dört ayrı değer
almış: en sık geleni 0,4307 payla $(1, 0, 0, 1, 0)$. Fark, o dönem geri kalan dört aboneden
hangilerinin sınıf değiştirdiğinden gelir ve bu bir çekilişin sonucudur. Ödül de üç değere
yayılıyor: 0,7137 payla 7, 0,2347 payla 1, 0,0516 payla $-2$. Ajanın yaptığı seçim aynı, sonucu üç
ayrı sayı.

Üç ödül değerinin ne anlama geldiği kurgudan okunur: 7, arızalı bulunup onarılmış bir aboneyi,
1 şüpheli bulunmuşu, $-2$ temiz bulunmuşu gösterir. A3'ün dönem başına kötüleşme olasılığı 0,60'tır
ve gecikme sınıfı 1, en az üç dönem geçtiği anlamına gelir; bu yüzden ziyaretlerin 0,7137'sinde
arıza bulunur. Aynı sınıf A2 için okunsaydı payların yeri değişirdi, çünkü A2'nin olasılığı
0,05'tir. Ödül dağılımı eylemin kimliğine de, durumun içeriğine de bağlıdır.

Ardıl durumların listesi de kurgudan çıkar. A3 ziyaret edildiği için onun gecikmesi sıfırlanır ve
sınıfı 0'a düşer; geri kalan dört abonenin gecikmesi bir artar. Bir artış sınıfı yalnız kova
sınırında değiştirir, dolayısıyla ardıl durumların hangi bileşende ayrıldığı, o dönem hangi
abonelerin kova sınırını geçtiğine bakar. Dört satırın toplamı 0,9362; kalan pay daha seyrek
görülen ardıllara dağılmıştır.

Bu dağılımlar bir kusur değildir. Beşlinin tanımı ödülün sabit olmasını değil, **dağılımının
sabit olmasını** ister. Kusur, dağılımın sabit olmadığı yerdedir.

#### Geçmişten Bağımsızlık

```python
# gecmis.py — Sinama: ayni (durum, eylem) hucresindeki odul ornekleri, durumun
# tasimadigi bir gecmis alanina gore ikiye bolunuyor. Ortalamalar ayrisiyorsa
# odul o hucrede tek bir dagilima oturmuyor demektir.
hucre = defaultdict(list)
for s, a, r, s2, g in IZ:
    hucre[(s, a)].append((r, g))

print(f"{'durum':<17}{'eylem':<7}{'ornek':>7}{'ort odul':>10}"
      f"{'gecikme 3':>11}{'gecikme 4+':>12}{'fark':>8}")
for (s, a), v in sorted(hucre.items(), key=lambda kv: -len(kv[1]))[:5]:
    k = [r for r, g in v if g == 3]
    u = [r for r, g in v if g >= 4]
    print(f"{str(s):<17}{ETIKET[a]:<7}{len(v):>7}"
          f"{statistics.mean(r for r, _ in v):>10.3f}"
          f"{statistics.mean(k):>11.3f}{statistics.mean(u):>12.3f}"
          f"{statistics.mean(u) - statistics.mean(k):>8.3f}")
```

```
durum            eylem    ornek  ort odul  gecikme 3  gecikme 4+    fark
(0, 0, 1, 1, 0)  A3        2113     5.127      4.518       5.944   1.425
(1, 0, 0, 1, 0)  A1        1945     1.540      1.198       2.436   1.238
(0, 1, 0, 1, 0)  A2        1899    -1.455     -1.502      -1.341   0.162
(1, 0, 0, 0, 1)  A1        1659     1.204      1.132       2.154   1.022
(0, 1, 0, 0, 1)  A2        1613    -1.481     -1.530      -0.971   0.558
```

Sınamanın kurgusu şudur. Gecikme sınıfı 1, "son ziyaretten bu yana 3, 4 ya da 5 dönem geçti"
demektir; üç değer tek bir sınıfa katlanır. Durum bu katlanmadan sonra elde kalandır, yani ajan
gecikmenin 3 mü 5 mi olduğunu göremez. Ama ortam görür: kötüleşme her dönem yeniden çekilir,
dolayısıyla 5 dönem beklemiş bir abonenin arızalı bulunma olasılığı 3 dönem beklemişten yüksektir.

Tablo bunu sayıya çeviriyor. En çok görülen hücrede — durum $(0, 0, 1, 1, 0)$, eylem A3, 2.113
örnek — ortalama ödül 5,127. Aynı hücre gecikmeye göre ikiye bölündüğünde 4,518 ile 5,944 çıkıyor:
fark **1,425**. Beş hücrenin beşinde de fark artı yönde, en küçüğü 0,162, en büyüğü 1,425. Durum ve
eylem sabit tutulduğu hâlde ödülün ortalaması **durumun taşımadığı bir geçmiş bilgisine göre
kayıyor**. Koşul bu kurguda tam tutmuyor ve tutmadığı miktar ödül biriminden okunabiliyor.

Farkın hücreden hücreye değişmesi de okunmayı hak ediyor. En küçük iki fark, 0,162 ve 0,558, A2'ye
gidilen hücrelerdedir; A2'nin kötüleşme olasılığı 0,05'tir, dolayısıyla bir dönem daha beklemek
onun kademesini neredeyse hiç oynatmaz ve katlanan bilgi de neredeyse hiçbir şey taşımaz. En büyük
fark A3'tedir, olasılığı 0,60 olan abonede. Yani izdüşümün ne kadar bilgi attığı sabit bir sayı
değildir: durumun aynı kovası, hızlı kötüleşen bir abone için çok, yavaş kötüleşen için az saklar.

İki şey karıştırılmamalı. **Ortamın kendisi** geçmişten bağımsızdır: gizli kademe vektörü bir
sonraki kademeyi ve ödülü belirlemek için yeterlidir, ondan öncesi hiçbir şey eklemez. Bozulan
şey ortam değil, **ajanın durum tanımıdır**. Gecikme sınıfı vektörü gizli vektörün kaba bir
izdüşümüdür ve izdüşüm alınırken atılan bilgi, atıldığı için yok olmaz — geçmişin içinde durur ve
oradan ödülü oynatır. Bu, uygulamada karşılaşılan biçimin ta kendisidir: beşli genellikle ortamda
tutar, ajanın eline geçen gözlemde tutmaz.

#### Durum Tanımının Bedeli

```python
# tanim.py — Ayni kural, uc durum tanimi. Kuralin metni degismiyor; degisen,
# kuralin okuyabildigi alan.
def kural_gecmisli(s, o):
    """Gecmis alani: en uzun suredir ziyaret edilmemis abonenin numarasi."""
    en = max(s)
    if en == 0:
        return ABONE
    aday = [i for i in range(ABONE) if s[i] == en]
    uzun = max(range(ABONE), key=lambda i: o.g[i])
    return uzun if uzun in aday else aday[0]


def gizli_politika(s, o):
    """Ulasilamaz ust referans: ortamin gizli kademe vektorunu dogrudan okur."""
    en = max(o.d)
    return ABONE if en == 0 else o.d.index(en)


print(f"{'durum tanimi':<34}{'uzay':>7}{'getiri':>9}")
for ad, pol, uzay in (("gecikme sinifi", kural, 243),
                      ("gecikme sinifi + gecmis alani", kural_gecmisli, 1215),
                      ("gizli kademe (ust referans)", gizli_politika, 243)):
    print(f"{ad:<34}{uzay:>7}{statistics.mean(kosum(pol)):>9.3f}")
```

```
durum tanimi                         uzay   getiri
gecikme sinifi                        243   20.621
gecikme sinifi + gecmis alani        1215   23.148
gizli kademe (ust referans)           243   29.053
```

Üç satırın da okunacağı taban çizgisi önceki derstendir ve burada yeniden ölçülmez: rastgele eylem
**10,432**, hiç ekip çıkarmamak **0,000**. Üç satır da bu tabanın üstündedir; aralarındaki fark
tabanla aralarındaki farkın yanında küçüktür ve bu dersin ölçtüğü şey o küçük farktır.

Üç satırda kural aynıdır: gecikme sınıfı en büyük olan aboneye git. Değişen tek şey, kuralın eşitlik
durumunda neye bakabildiğidir. İlk satırda sınıf eşitse abone numarası küçük olan seçilir, çünkü
başka bilgi yoktur ve getiri 20,621'dir. İkinci satırda duruma tek bir alan eklenir — en uzun
süredir ziyaret edilmemiş abonenin numarası — ve eşitlik o alana göre bozulur. Getiri 23,148 olur:
**+2,527**, yani yüzde 12,3'lük bir artış, tek bir alan karşılığında.

Üçüncü satır kaybın büyüklüğünü sınırlıyor. Gizli kademeyi doğrudan okuyan politika 29,053 alıyor.
İzdüşümün açtığı toplam açıklık $29{,}053 - 20{,}621 = 8{,}432$; geçmiş alanı bunun 2,527'sini,
yani 0,300'ünü kapatıyor. Kalan 5,905 gecikmeden okunamaz, çünkü kötüleşme bir çekiliştir ve iki
abone aynı gecikmeyle iki ayrı kademede olabilir.

Alanın bedeli sütununda yazılı. Durum uzayı 243'ten 1.215'e, beş katına çıkıyor. Bu ders bir el
kuralı kullandığı için bedel görünmüyor; kural durumu okur ve çizelge tutmaz. Bir sonraki iki
derste durum başına bir sayı **öğrenilecek** ve o zaman 243 ile 1.215 arasındaki fark doğrudan
öğrenme hızına dönüşecek: her hücrenin dolması için o hücrenin ziyaret edilmesi gerekir. Durum
tanımını zenginleştirmek bedava değildir; geçmişten bağımsızlığa yaklaşmak için ödenen bedel,
her durumun daha seyrek görülmesidir.

#### Özet

- Karar süreci bir beşlidir: durum kümesi (243 öge), eylem kümesi (6 öge), geçiş, ödül ve indirim
  katsayısı (0,9). Geçiş ve ödül birer sayı değil, birer dağılımdır.
- Tek bir hücrede — durum $(0, 0, 1, 1, 0)$, eylem A3 — ardıl durum dört ayrı değere 0,4307,
  0,2239, 0,1770 ve 0,1046 payla, ödül ise $-2$, 1 ve 7'ye 0,0516, 0,2347 ve 0,7137 payla dağılır.
- Tanımlayıcı koşul, ardıl durumun ve ödülün dağılımının yalnız o anki duruma ve eyleme bağlı
  olmasıdır; buna geçmişten bağımsız geçiş denir.
- Koşul kurguda tam tutmaz: aynı hücrede ortalama ödül gecikme 3 iken 4,518, gecikme 4 ve
  üzeriyken 5,944'tür; fark 1,425 ve beş hücrenin beşinde de aynı yöndedir.
- Bozulan ortam değil ajanın durum tanımıdır. Gizli kademe vektörü koşulu taşır, gecikme sınıfı
  vektörü onun kaba bir izdüşümüdür.
- Aynı kural 243 durumlu tanımda 20,621, geçmiş alanı eklenmiş 1.215 durumlu tanımda 23,148,
  gizli kademeyi gören üst referansta 29,053 alır; geçmiş alanı açıklığın 0,300'ünü kapatır.

#### Sonraki Adım

Bu iki derste politika elle yazıldı: kuralın ne yapacağı baştan belliydi ve hiçbir sayı veriden
öğrenilmedi. Bir politikanın öğrenilmesi gerektiğinde ise yeni bir çelişki doğar. Öğrenmek için
eylemlerin karşılığını görmek gerekir, karşılığını görmek için o eylemi denemek gerekir ve her
deneme o dönemin getirisinden düşer. Önceki derste görüldüğü gibi ajan bir adımda yalnız seçtiği
eylemin sonucunu öğrenir; seçmediği beş eylem hakkında hiçbir şey duymaz. Sonraki ders bu
çelişkiyi ölçer: bilinen en iyi eylemi tekrarlamak ile bilinmeyeni denemek arasındaki değiş tokuş,
kaç adım kaybedilerek kaç adım kazanıldığı olarak sayılır — ve hiç keşif yapmayan bir ajanın
hangi eylemi **hiç denemediği** yazılır.

### Keşif ve Sömürü Dengesi

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/kesif-ve-somuru-dengesi

Öğrenmek için denemek, kazanmak için bilineni tekrarlamak gerektiğinde ödenen bedelin sayılması: karamsar ilk değerle hiç keşif yapmayan ajan altı eylemden beşini hiç denemiyor, 243 durumun 95'ini görüyor ve -16,492 getiri alıyor, kaçırılan kazancı ise tam 0,0. Keşif oranı 0,00'dan 1,00'a süpürüldüğünde getiri -16,492, 16,716, 22,210, 23,606, 25,659, 25,997 ve 23,000; biriken kaçırılan kazanç 0,0, -76,0, 402,0, 1.210,0, 3.545,0, 8.358,0 ve 33.811,0. En iyi getirinin 0,338 altında kalan getiri, 0,20 oranında 3.545 kaçırılan kazançla alınabiliyor. Kestirimlerin ilk değeri karamsardan iyimsere çekildiğinde hiç keşif yapmayan ajan 26,212'ye çıkıyor ve kaçırılan kazancı yine 0,0 kalıyor.

Önceki iki derste politika elle yazıldı. Kural ne yapacağını baştan biliyordu, hiçbir sayı veriden
öğrenilmedi, dolayısıyla öğrenmenin bir bedeli de yoktu. Politika öğrenilmeye başlandığında ise
kursun ilk dersinde sayılan bir kısıt doğrudan bir çelişkiye dönüşür: ajan bir adımda yalnız
seçtiği eylemin sonucunu görür, seçmediği beş eylem hakkında hiçbir şey duymaz. Bir eylemin ne
kadar iyi olduğunu öğrenmenin tek yolu onu denemektir ve her deneme o dönemin getirisinden düşer.

**Sömürü**, kestirimi en yüksek olan eylemi seçmektir; o anki bilgiyi paraya çevirir. **Keşif**,
kestirimi düşük ya da hiç olmayan bir eylemi seçmektir; bugünün getirisini bilgiye çevirir. Bu
ders çelişkiyi bir düğme hâline getirir, düğmeyi süpürür ve iki yönü de sayar: kaç adım
kaybedilerek kaç adım kazanılıyor.

- **PO18. Kurgu, ufuk, indirim katsayısı ve tohum 01–02'den aynen sürer.** Beş abone, tek ekip,
  aynı kötüleşme olasılıkları, 243 durum, 6 eylem, 30 dönemlik bölüm.
- **PO19. Bu derste kestirim anlık ödülün ortalamasıdır.** Her durum-eylem çifti için o çiftte
  görülen ödüllerin ortalaması tutulur; getiriye ve zaman farkına sonraki ders geçer.
- **PO20. Keşif biçimi tek bir orandır.** Olasılık $\varepsilon$ ile eylem ayrım gözetmeden
  seçilir, kalan durumlarda kestirimi en yüksek olan seçilir; eşitlik en küçük numarayla bozulur.
- **PO21. Kaçırılan kazanç, keşif adımlarının anlık bedelidir:** her adımda, açgözlü eylemin o
  adımda vereceği ödül ile gerçekten alınan ödül arasındaki fark biriktirilir. Sömürü adımlarında
  bu fark tam olarak sıfırdır, keşif adımlarında ise **eksi de çıkabilir**.
- **PO22. Kestirimlerin ilk değeri bir tercihtir ve bu ders onu da ölçer.** Karamsar ilk değer
  $-5{,}0$'tır ve olası bütün ödüllerin altındadır; iyimser ilk değer $0{,}0$'dır ve boşa giden
  bir ziyaretin ödülü olan $-2$'nin üstündedir.
- **PO23. Eğitim 300 bölüm, yani 9.000 adımdır.** Ardından çizelge dondurulur ve öğrenilen açgözlü
  politika, 01'deki aynı 300 bölüm tohumunda ölçülür; sayılar 10,432 ve 20,621 ile aynı ölçektedir.
- **PO24. Süpürülen keşif oranları yedi adaydır:** 0,00, 0,02, 0,05, 0,10, 0,20, 0,40 ve 1,00.
- **PO25. Ölçüt üçlüdür ve hep yan yana basılır:** dondurulmuş politikanın getirisi, biriken
  kaçırılan kazanç ve hiç denenmeyen eylem sayısı. Eğitimde hiç görülmemiş bir durumla
  karşılaşıldığında bütün kestirimler eşittir ve en küçük numaralı eylem seçilir.

#### Hep Sömüren Ajan

```python
# ortam.py — KURGUDUR. Saha ziyareti planlamasi onceki iki dersten aynen surer:
# bes abone, her donem tek ekip, tohum 20260218.
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
ETIKET = ("A1", "A2", "A3", "A4", "A5", "cikma")
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9


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


class Ortam:
    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def kosum(politika, n=300):
    G = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s, g, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s, r = o.adim(politika(s, o))
            g, c = g + c * r, c * INDIRIM
        G.append(g)
    return G


def egit(kesif, ilk, n_bolum=300, tohum=TOHUM + 11):
    """Her durum-eylem cifti icin ANLIK odulun ortalamasi tutulur."""
    r = uretec(tohum)
    Q, N, kacan, kesif_adim = {}, {}, 0.0, 0
    for k in range(n_bolum):
        o = Ortam(TOHUM + 1000 * k)
        s = o.durum()
        for _ in range(UFUK):
            q = Q.setdefault(s, [ilk] * EYLEM)
            n = N.setdefault(s, [0] * EYLEM)
            ac = max(range(EYLEM), key=lambda i: q[i])
            a = int(r() * EYLEM) if r() < kesif else ac
            beklenen = KAZANC[o.d[ac]] - MALIYET if ac < ABONE else 0.0
            s2, odul = o.adim(a)
            kacan += beklenen - odul
            kesif_adim += (a != ac)
            n[a] += 1
            q[a] += (odul - q[a]) / n[a]
            s = s2
    return Q, N, kacan, kesif_adim


def dondur(Q, ilk):
    """Ogrenme biter, cizelge donar; politika artik acgozludur."""
    def pol(s, o):
        q = Q.get(s, [ilk] * EYLEM)
        return max(range(EYLEM), key=lambda i: q[i])
    return pol


ILK = -5.0
Q0, N0, kacan0, kesif0 = egit(0.00, ILK)
denenen = [sum(N0[s][a] for s in N0) for a in range(EYLEM)]
print(f"kesif orani 0.00, kestirimlerin ilk degeri {ILK}")
print(f"  ogrenilen politikanin getirisi {statistics.mean(kosum(dondur(Q0, ILK))):.3f}")
print(f"  biriken kacirilan kazanc {kacan0:.1f}; kesif adimi {kesif0}")
print(f"  gorulen durum {len(N0)}/243; denenen durum-eylem cifti "
      f"{sum(1 for v in N0.values() for x in v if x > 0)}/1458")
print("  eylem basina deneme sayisi: "
      + ", ".join(f"{ETIKET[a]}={denenen[a]}" for a in range(EYLEM)))
```

```
kesif orani 0.00, kestirimlerin ilk degeri -5.0
  ogrenilen politikanin getirisi -16.492
  biriken kacirilan kazanc 0.0; kesif adimi 0
  gorulen durum 95/243; denenen durum-eylem cifti 95/1458
  eylem basina deneme sayisi: A1=9000, A2=0, A3=0, A4=0, A5=0, cikma=0
```

Hiç keşif yapmayan ajanın 9.000 adımı tek bir eylemde geçiyor. Nedeni tek satırda okunur:
başlangıçta bütün kestirimler $-5{,}0$'tır, eşitlik en küçük numarayla bozulur, ilk eylem A1
olur ve A1'in gerçek ödülü ne çıkarsa çıksın $-5{,}0$'ın üstündedir. Kestirim yükselir, A1 artık
tek başına en yüksektir ve bir daha hiçbir şey onu geçemez, çünkü öteki beş eylem hiç denenmediği
için kestirimleri $-5{,}0$'ta durur. **A2, A3, A4, A5 ve ekip çıkarmama seçeneği hiç denenmiyor**:
altı eylemden beşi.

Sonuç getiride görünüyor. Politika $-16{,}492$ alıyor; hiç ekip çıkarmamanın 0,000'ının ve
rastgele eylemin 10,432'sinin çok altında. Ajan her dönem A1'e gidiyor, A1 çoğunlukla bir dönem
önce onarıldığı için temiz bulunuyor ve her ziyaret 2 birim yazıyor. Sayı da bunu doğruluyor:
otuz dönem boyunca kesintisiz $-2$ almanın indirimli toplamı $-19{,}152$'dir, ölçülen $-16{,}492$
ise aradaki payı A1'in arada bir kötüleşip 1 ya da 7 vermesinden alıyor. Toplanan veri de
daralıyor: 243 durumun 95'i görülüyor ve 1.458 çiftin yalnız 95'i deneniyor. Bu 95 çift, 95
durumun her birinde tek bir eylem demektir — hep aynı eylem.

Kaçırılan kazanç ise **tam olarak 0,0**. Bu bir başarı değil, ölçünün tanımıdır: kaçırılan kazanç
keşif adımlarının anlık bedelini sayar, keşif adımı da hiç yoktur. Bir sistemin kaçırılan
kazancının sıfır olması, o sistemin iyi olduğunu değil, **hiçbir şey öğrenmediğini** söyler. İki
sayı ancak yan yana anlam taşır.

#### Keşif Oranının Süpürülmesi

```python
# supurme.py — Yedi kesif orani, ayni butce (300 bolum, 9.000 adim), ayni
# ufuk, ayni karamsar ilk deger. Ogrenilen politika sonra dondurulup olculur.
print(f"{'kesif':>6}{'getiri':>9}{'kacan':>10}{'kesif adimi':>13}"
      f"{'durum':>8}{'cift':>7}{'hic denenmeyen eylem':>22}")
for kesif in (0.00, 0.02, 0.05, 0.10, 0.20, 0.40, 1.00):
    Q, N, kacan, ka = egit(kesif, ILK)
    say = [sum(N[s][a] for s in N) for a in range(EYLEM)]
    hic = [ETIKET[a] for a in range(EYLEM) if say[a] == 0]
    cift = sum(1 for v in N.values() for x in v if x > 0)
    print(f"{kesif:>6.2f}{statistics.mean(kosum(dondur(Q, ILK))):>9.3f}{kacan:>10.1f}"
          f"{ka:>13}{len(N):>8}{cift:>7}{(','.join(hic) if hic else '-'):>22}")
```

```
 kesif   getiri     kacan  kesif adimi   durum   cift  hic denenmeyen eylem
  0.00  -16.492       0.0            0      95     95     A2,A3,A4,A5,cikma
  0.02   16.716     -76.0          150     127    237                     -
  0.05   22.210     402.0          364     143    335                     -
  0.10   23.606    1210.0          748     176    472                     -
  0.20   25.659    3545.0         1500     185    578                     -
  0.40   25.997    8358.0         2945     217    839                     -
  1.00   23.000   33811.0         7494     235   1237                     -
```

Getiri sütunu tek tepeli bir eğri çiziyor. Sıfır keşifte $-16{,}492$, 0,40'ta en yüksek değeri
25,997, 1,00'de yeniden 23,000. İki uç iki ayrı nedenle kötüdür. Solda ajan öğrenmez, çünkü
denemez. Sağda ajan her adımı ayrım gözetmeden seçtiği için topladığı veri iyi ama davranışı
kötüdür; yine de dondurulan çizelge 23,000 verir, yani **kötü davranışla iyi veri toplanabilir**
— bir sonraki kursun temel gerilimi burada tohum hâlinde duruyor.

Asıl okuma iki sütun arasındadır. 0,20 oranı 25,659 getiri veriyor ve bedeli 3.545 kaçırılan
kazanç. 0,40 oranı yalnız 0,338 daha fazla getiri veriyor ama bedeli 8.358, yani iki kattan fazla.
Keşifle satın alınan getiri azalarak artıyor; her ek keşif adımı bir öncekinden daha az bilgi
getiriyor, oysa bedeli aynı kalıyor. 1,00'e gidildiğinde 33.811 kaçırılan kazanç ödenip getiri
düşüyor.

0,02 satırındaki eksi işaret bir hata değil, ölçünün kendisidir. 150 keşif adımının toplam bedeli
$-76{,}0$: keşif adımları ortalamada açgözlü eylemden **daha iyi** ödül getirmiş. Bunun nedeni
açgözlü eylemin o aşamada A1'e kilitlenmiş olmasıdır; A1'e gitmek çoğunlukla $-2$ yazarken
ayrım gözetmeden seçilen eylem sık sık daha iyisini bulmuştur. Kötü bir politika sömürülürken
keşfin bedeli eksiye düşer. Bedel ancak sömürülen politika gerçekten iyiyken artıya geçer ve
tablodaki artış tam olarak bunu izliyor.

Denenen çift sütunu bir uyarı da taşıyor. En cömert keşifte bile 1.458 çiftin 1.237'si deneniyor,
yani 221'i hiç denenmiyor ve görülen durum 235'te kalıyor. Dondurulan politika kalan 8 durumla
karşılaşırsa bütün kestirimleri eşit bulur ve en küçük numaralı eylemi seçer. Bu, öğrenilen
sayının nerede geçerli olduğunu da tanımlıyor: çizelgenin dolu olduğu bölge kadar.

Sağdaki iki sütun kursun üçüncü sayısını taşıyor. Keşif oranı arttıkça görülen durum 95'ten 235'e,
denenen çift 95'ten 1.237'ye çıkıyor. Keşif bir bedel ödemekten ibaret değil; **topladığı veriyi
genişletiyor** ve genişleyen veri, dondurulan çizelgenin daha çok hücresinde bir sayı bulunmasını
sağlıyor. Ayar değişkeni araması bu ölçünün dışındadır ve tekrarlanmaz: M27/K03'ün model
değerlendirme konusunda kurulan aday süpürmesinde aday sayısı artırıldığında ödenen şey hesaplama
bütçesiydi, veri baştan sona sabit duruyordu ve arama bittikten sonra hiçbir kayıp kalmıyordu.
Burada ödenen şey **getirinin kendisidir**: her keşif adımı gerçekten yaşanmış bir dönemdir,
geri alınamaz ve o dönemde toplanan veriyi de kalıcı olarak değiştirir.

#### Keşif Rastgelelikten Gelmek Zorunda Değil

```python
# ilkdeger.py — Ayni supurme, iki ayri ilk deger. Karamsar ilk deger denenmemis
# eylemi kotu gosterir; iyimser ilk deger onu denemeye deger gosterir.
print(f"{'ilk deger':>10}{'kesif':>7}{'getiri':>9}{'kacan':>10}{'cift':>7}")
for ilk in (-5.0, 0.0):
    for kesif in (0.00, 0.05, 0.20):
        Q, N, kacan, ka = egit(kesif, ilk)
        cift = sum(1 for v in N.values() for x in v if x > 0)
        print(f"{ilk:>10.1f}{kesif:>7.2f}"
              f"{statistics.mean(kosum(dondur(Q, ilk))):>9.3f}{kacan:>10.1f}{cift:>7}")
```

```
 ilk deger  kesif   getiri     kacan   cift
      -5.0   0.00  -16.492       0.0     95
      -5.0   0.05   22.210     402.0    335
      -5.0   0.20   25.659    3545.0    578
       0.0   0.00   26.212       0.0    356
       0.0   0.05   26.155     772.0    496
       0.0   0.20   26.378    4147.0    636
```

Alt üç satır üstteki bütün tabloyu yeniden okutuyor. Keşif oranı yine 0,00 iken getiri
$-16{,}492$ değil **26,212**, kaçırılan kazanç yine tam 0,0, denenen çift 95 değil 356. Hiçbir
rastgele adım atılmadan keşif olmuş.

Nedeni ilk değerdedir. Denenmemiş bir eylemin kestirimi $0{,}0$'dır; boşa giden bir ziyaretin
gerçek ödülü $-2$'dir. Ajan bir eylemi deneyip $-2$ aldığında o eylemin kestirimi denenmemiş
eylemlerin altına düşer ve açgözlü seçim kendiliğinden bir sonrakine kayar. Kestirim gerçeğin
üstünde başladığı sürece açgözlülüğün kendisi keşif üretir; hayal kırıklığı sıradaki eylemi
gündeme getirir. Karamsar ilk değerde ise tersi olur: ilk denenen eylem ne verirse versin
denenmemişlerin üstüne çıkar ve kapı kapanır.

Bu, kaçırılan kazancı sıfır tutan bir keşif düzeneğidir, ama bedava değildir. İlk değerin gerçek
ödüllerin üstünde olması gerekir, bu da ödüllerin ölçeği hakkında önceden bilgi ister; kurguda
0,0'ın işe yaraması, boşa ziyaretin $-2$ yazdığını bilmemizdendir. Ayrıca etkisi geçicidir: her
hücre bir kez denendikten sonra iyimserlik tükenir ve ortam zamanla değişirse yeniden keşif
gerekir. Oranla yapılan keşif ise kalıcıdır ve her adımda ödenir. Tablodaki 4.147 ile 0,0 farkı
bu iki düzeneğin bedelini aynı birimde yan yana koyuyor.

#### Özet

- Sömürü kestirimi en yüksek eylemi seçer, keşif kestirimi belirsiz olanı dener; ajan bir adımda
  yalnız seçtiği eylemin sonucunu gördüğü için ikisi aynı anda yapılamaz.
- Karamsar ilk değerle hiç keşif yapmayan ajan 9.000 adımın tamamını A1'de geçirir, altı eylemden
  beşini hiç denemez, 243 durumun 95'ini görür ve $-16{,}492$ getiri alır.
- Kaçırılan kazancın 0,0 olması iyi bir işaret değildir: keşif adımı olmadığı için tanımı gereği
  sıfırdır ve getiriyle yan yana okunmadan bir şey söylemez.
- Keşif oranı 0,00'dan 1,00'a süpürüldüğünde getiri $-16{,}492$'den 25,997'ye çıkıp 23,000'e
  düşer; bedel 0,0'dan 33.811,0'a tırmanır. 0,20 oranı en iyi getirinin 0,338 altında kalırken
  bedelin yalnız 0,424'ünü öder.
- Düşük keşif oranında kaçırılan kazanç eksiye düşebilir ($-76{,}0$), çünkü sömürülen politika o
  aşamada kötüdür ve ayrım gözetmeden seçilen eylem ortalamada ondan iyi çıkar.
- Kestirimlerin ilk değeri de bir keşif düzeneğidir: iyimser başlangıçla hiç rastgele adım
  atmadan getiri 26,212'ye, denenen çift 95'ten 356'ya çıkar ve kaçırılan kazanç 0,0 kalır.

#### Sonraki Adım

Bu derste tutulan sayı **anlık ödülün** ortalamasıydı: bir durumda bir eylemin o dönem ne
getirdiği. Oysa ilk derste getiri otuz dönemin indirimli toplamı olarak tanımlanmıştı ve bir
ziyaretin asıl etkisi o dönemde bitmiyor — onarılan abone birkaç dönem daha temiz kalıyor,
ihmal edilen abone birkaç dönem sonra daha pahalıya patlıyor. Anlık ödüle bakan bir kestirim bu
zinciri hiç görmez, bu yüzden 26,378'de takılır. Sonraki ders çizelgede tutulan sayıyı anlık
ödülden **getiriye** çevirir: bir durumun değeri, oradan sonra toplanabilecek indirimli ödüllerin
toplamıdır ve bu sayı ardışık iki kestirimin farkından adım adım öğrenilebilir. Ölçü yine
toplanan veriden okunacak: çizelgenin kaç hücresi 9.000 adımın sonunda **hiç güncellenmemiş**
kalıyor.

### Değer Fonksiyonları ve Eylem Değeri

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/deger-fonksiyonlari-ve-eylem-degeri

Ortamın olasılıkları bilinmeden, yalnız yaşanmış dörtlülerden değer öğrenilmesi ve çizelgenin boş kalan yerinin sayılması: zaman farkı güncellemesi 9.000 adımlık iz üzerinde 40 kez süpürüldüğünde başlangıç durumlarının ortalama değeri 12,632'den 21,562'ye çıkıyor ve ölçülen 30 dönemlik getiri 20,621 ile arasındaki fark indirim katsayısının otuzuncu kuvveti olan 0,0424'lük kuyruğa oturuyor. Durum değerleri 29,060 ile 16,075 arasında dağılıyor. Kendi verisiyle beslenen eylem değeri çizelgesinin 1.458 hücresinden yalnız 103'ü güncelleniyor, 1.355'i hiç dokunulmadan kalıyor ve çizelgeden okunan politika tam olarak kendisini üreten kuralı, 20,621'i veriyor. Davranışa keşif eklendikçe dolan hücre 103'ten 1.237'ye çıkıyor ama beşten az örnekli ince hücre de 47'den 468'e çıkıyor ve getiri tek yönlü ilerlemiyor.

Önceki derste çizelgede tutulan sayı **anlık ödüldü**: bir durumda bir eylemin o dönem ne
getirdiği. Bu sayı bir ziyaretin bütün etkisini yakalayamaz. Onarılan abone birkaç dönem daha
temiz kalır, ihmal edilen abone birkaç dönem sonra daha pahalıya patlar ve ekibi bugün hangi
aboneye gönderdiğimiz yarın hangi seçeneklerin elimizde olacağını belirler. İlk derste getiri
otuz dönemin indirimli toplamı olarak tanımlanmıştı; çizelgede duran sayı ise tek bir dönemi
tutuyordu.

Bu ders çizelgedeki sayıyı anlık ödülden **getiriye** çevirir. Bunu yaparken ortamın kötüleşme
olasılıklarını ya da ödül çizelgesini kullanmaz — o bilgilerin hiçbiri elde yokmuş gibi davranır
ve yalnız yaşanmış dörtlülerden çalışır. Sonra da kursun üçüncü sayısını çizelgenin üzerinde okur:
9.000 adımın sonunda hücrelerin kaçı hâlâ boştur.

- **PO26. Kurgu, ufuk, indirim katsayısı, ölçü ve tohum 01–03'ten aynen sürer.** 243 durum, 6
  eylem, 30 dönemlik bölüm, indirim katsayısı 0,9, 300 bölümlük ölçüm.
- **PO27. Öğrenme model gerektirmez.** Kötüleşme olasılıkları da ödül çizelgesi de bilinmiyor
  sayılır; elde yalnız yaşanmış **(durum, eylem, ödül, ardıl durum)** dörtlüleri vardır.
- **PO28. Bir durumun değeri**, o durumdan başlayarak politikanın toplayacağı indirimli ödüllerin
  beklenen toplamıdır. Ödül tek dönemliktir, değer bütün geleceği taşır.
- **PO29. Güncelleme zaman farkı biçimindedir:** $r + \gamma V(s') - V(s)$ hesaplanır ve adım boyu
  0,1 ile çizelgeye eklenir. Bölümün bitmesi beklenmez, her adımda güncelleme yapılır.
- **PO30. İz bir kez toplanır, çizelge iz üzerinde 40 kez süpürülür.** Veri bütçesi 9.000 adımda
  sabittir; süpürme sayısı hesap bütçesidir ve yeni veri getirmez.
- **PO31. Değer sonsuz ufka göre tanımlıdır**, oysa ölçüm 30 dönemde kesilir. İki sayı arasındaki
  fark, indirim katsayısının otuzuncu kuvveti olan 0,0424'lük kuyruktan gelir.
- **PO32. Eylem değeri**, bir durum-eylem çiftinin değeridir; hedefte ardıl durumun **en iyi**
  eylem değeri kullanılır, böylece çizelge veriyi toplayan politikadan daha iyisini arar.
- **PO33. Çizelgenin ilk değeri 0,0'dır.** Hiç güncellenmemiş bir hücre bu değerde kalır ve açgözlü
  seçim onu da aday sayar; boş hücre yokluk değil, sıfır olarak okunur.
- **PO34. Ölçü üç sayıdır:** 1.458 hücrenin kaçı güncellendi, kaçı hiç güncellenmedi ve dolanların
  kaçı **beşten az** örnekle doldu.

#### Durumun Değeri

```python
# ortam.py — KURGUDUR. Saha ziyareti planlamasi onceki uc dersten aynen surer:
# bes abone, her donem tek ekip, tohum 20260218.
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
ETIKET = ("A1", "A2", "A3", "A4", "A5", "cikma")
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9


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


class Ortam:
    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def kosum(politika, n=300):
    G = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s, g, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s, r = o.adim(politika(s, o))
            g, c = g + c * r, c * INDIRIM
        G.append(g)
    return G


def kural(s, o):
    """Gecikme sinifi en buyuk aboneyi ziyaret et; hepsi 0 ise ekip cikarma."""
    en = max(s)
    return ABONE if en == 0 else s.index(en)


def iz_topla(kesif, n=300, tohum=TOHUM + 11):
    """Davranis politikasi: kesif orani kadar ayrim gozetmeden, kalani kural."""
    r = uretec(tohum)
    iz = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s = o.durum()
        for _ in range(UFUK):
            a = int(r() * EYLEM) if r() < kesif else kural(s, o)
            s2, odul = o.adim(a)
            iz.append((s, a, odul, s2))
            s = s2
    return iz


IZ = iz_topla(0.00)
BAS = [Ortam(TOHUM + 1000 * k).durum() for k in range(300)]
V = {}
for s, a, r, s2 in IZ:
    V.setdefault(s, 0.0)
    V.setdefault(s2, 0.0)
print(f"iz {len(IZ)} adim, cizelgede {len(V)} durum")
print(f"{'supurme':>8}{'V(s0) ortalamasi':>19}{'en buyuk guncelleme':>21}")
for tur in range(1, 41):
    enb = 0.0
    for s, a, r, s2 in IZ:
        d = 0.1 * (r + INDIRIM * V[s2] - V[s])
        V[s] += d
        enb = max(enb, abs(d))
    if tur in (1, 2, 5, 10, 20, 40):
        print(f"{tur:>8}{statistics.mean(V[s] for s in BAS):>19.3f}{enb:>21.4f}")
sirali = sorted(V.items(), key=lambda kv: -kv[1])
print("en yuksek uc durum: " + ", ".join(f"{s} {v:.3f}" for s, v in sirali[:3]))
print("en dusuk uc durum:  " + ", ".join(f"{s} {v:.3f}" for s, v in sirali[-3:]))
```

```
iz 9000 adim, cizelgede 103 durum
 supurme   V(s0) ortalamasi  en buyuk guncelleme
       1             12.632               2.4467
       2             15.944               2.3325
       5             19.119               1.8443
      10             20.608               1.4240
      20             21.345               1.2717
      40             21.562               1.2700
en yuksek uc durum: (1, 0, 1, 0, 2) 29.060, (1, 0, 1, 2, 1) 29.030, (0, 0, 2, 1, 2) 28.964
en dusuk uc durum:  (1, 2, 0, 0, 0) 17.801, (0, 2, 0, 0, 1) 16.573, (0, 2, 0, 1, 0) 16.075
```

Güncellemenin tamamı tek satırdadır: $V(s) \leftarrow V(s) + \alpha\,[\,r + \gamma V(s') - V(s)\,]$.
Köşeli parantez içindeki nicelik **zaman farkıdır**: bir yanda o adımda gerçekten alınan ödül ve
ardıl durumun mevcut kestirimi, öbür yanda o anki durumun mevcut kestirimi. İki kestirim ardışık
zamanlara aittir ve aralarındaki tutarsızlık, çizelgeyi düzeltmek için kullanılır. Ortamın
kötüleşme olasılıkları hiçbir yerde geçmiyor; kod yalnız yaşanmış dörtlüleri okuyor. Öğrenmenin
**model gerektirmemesi** budur.

Sayı adım adım yerine oturuyor. İlk süpürmede başlangıç durumlarının ortalama değeri 12,632, çünkü
bilgi henüz geriye doğru bir adım yayılmıştır. Onuncu süpürmede 20,608, kırkıncıda **21,562**.
Bağımsız ölçüm elimizde: aynı kuralın 300 bölümde ölçülen 30 dönemlik getirisi 20,621. İki sayı
aynı değil ve olmaması gerekiyor. Değer sonsuz ufka göre tanımlıdır, ölçüm ise 30 dönemde
kesiliyor; kesilen kuyruğun ağırlığı $\gamma^{30} = 0{,}0424$. Kuyruk hesaba katıldığında
$20{,}621 / (1 - 0{,}0424) = 21{,}534$ çıkıyor, öğrenilen 21,562 ile arasındaki fark 0,028.
Çizelge, hiçbir olasılığı bilmeden, ölçülen getiriyi kendi başına yeniden üretti.

Sağdaki sütun sıfıra inmiyor ve inmemesi gerekiyor. Adım boyu sabit 0,1 olduğu için çizelge
donmaz; aynı hücreye gelen ödüller $-2$, 1 ve 7 arasında değiştikçe kestirim ortalamanın çevresinde
salınır. En büyük güncelleme 1,2700'de duruyor, yani çizelge bir noktaya yerleşmiş ama titreşimi
sürüyor.

Alt iki satır değerin ne söylediğini gösteriyor. En yüksek durum $(1, 0, 1, 0, 2)$: A3 ile A5
bekletilmiş, ikisi de hızlı kötüleşen aboneler, üstelik A5 altı dönemi geçmiş. Bu durumda ekibi
bekleyen iş çoktur ve değeri 29,060. En düşük durum $(0, 2, 0, 1, 0)$: en uzun bekleyen abone A2,
onun kötüleşme olasılığı ise 0,05, yani beklemesi neredeyse bir şey ifade etmiyor — kural yine de
ekibi oraya gönderir. Değeri 16,075 ve en düşük üç durumun üçünde de en uzun bekleyen A2'dir.
Aradaki 12,985'lik açıklık, aynı politikanın hangi durumda başladığına göre ne kadar farklı bir
gelecek gördüğünü ölçüyor.

#### Eylemin Değeri

```python
# eylem_degeri.py — Ayni iz, ayni zaman farki bicimi; degisen, cizelgenin
# durum yerine durum-eylem ciftiyle anahtarlanmasi ve hedefteki en iyi eylem.
def q_supur(iz, tur=40, ab=0.1, ilk=0.0):
    Q, N = {}, {}
    for s, a, r, s2 in iz:
        for x in (s, s2):
            Q.setdefault(x, [ilk] * EYLEM)
            N.setdefault(x, [0] * EYLEM)
        N[s][a] += 1
    for _ in range(tur):
        for s, a, r, s2 in iz:
            Q[s][a] += ab * (r + INDIRIM * max(Q[s2]) - Q[s][a])
    return Q, N


def acgozlu(Q, ilk=0.0):
    def pol(s, o):
        q = Q.get(s, [ilk] * EYLEM)
        return max(range(EYLEM), key=lambda i: q[i])
    return pol


Q, N = q_supur(IZ)
dolu = [(s, i) for s in N for i in range(EYLEM) if N[s][i] > 0]
print(f"guncellenen hucre {len(dolu)}/1458, hic guncellenmeyen {1458 - len(dolu)}")
print(f"cizelgeden okunan politikanin getirisi "
      f"{statistics.mean(kosum(acgozlu(Q))):.3f}")
ornek = sorted(N, key=lambda s: -sum(N[s]))[0]
print(f"ornek durum {ornek}: " + ", ".join(
    f"{ETIKET[i]} {Q[ornek][i]:.3f} (n={N[ornek][i]})" for i in range(EYLEM)))
```

```
guncellenen hucre 103/1458, hic guncellenmeyen 1355
cizelgeden okunan politikanin getirisi 20.621
ornek durum (0, 0, 1, 1, 0): A1 0.000 (n=0), A2 0.000 (n=0), A3 23.183 (n=1057), A4 0.000 (n=0), A5 0.000 (n=0), cikma 0.000 (n=0)
```

Değer fonksiyonu bir durumun ne kadar iyi olduğunu söyler ama hangi eylemin seçileceğini söylemez;
onu söyleyebilmek için ortamın hangi eylemin nereye götürdüğünü bilmesi gerekirdi ve o bilgi elde
yok. **Eylem değeri** bu açığı kapatır: çizelge durumla değil durum-eylem çiftiyle anahtarlanır
ve seçim doğrudan satırın en büyüğünden okunur. Hedefteki $\max$ ise çizelgeyi veriyi toplayan
politikadan daha iyisini aramaya zorlar: ardıl durumda **o politikanın yapacağı** değil, **en iyi
görünen** eylemin değeri kullanılır.

Örnek satır bütün dersin özetidir. Durum $(0, 0, 1, 1, 0)$ 1.057 kez yaşanmış ve altı hücresinden
yalnız biri dolu: A3 için 23,183. Öteki beşi 0,000 ve yanlarındaki örnek sayısı sıfır. Bu sıfırlar
"bu eylem kötüdür" demiyor, "bu eylem hiç denenmedi" diyor — ama çizelge ikisini birbirinden
ayırmıyor, çünkü ilk değer neyse boş hücrede o durur.

Getiri satırı sonucu kapatıyor: çizelgeden okunan politika **20,621** alıyor, yani onu üreten
kuralın sayısının tam olarak kendisini. Şaşırtıcı değil, kaçınılmazdır. Kural belirleyicidir,
gördüğü her durumda tek bir eylem seçer, dolayısıyla 1.458 hücrenin yalnız 103'ü güncellenir ve
her durumda tek dolu hücre otomatik olarak en büyüğü olur. **Kendi verisiyle beslenen bir çizelge,
kendisini üreten politikadan başkasını öneremez.** Kursun ilk dersinde 103 sayısı bir gözlem
olarak basılmıştı; burada doğrudan bir tavana dönüşüyor.

#### Çizelgenin Boş Kalan Yeri

```python
# kapsama.py — Davranis politikasina kesif eklendikce cizelgenin kac hucresi
# doluyor, ve dolan hucrelerin kaci ince kaliyor.
print(f"{'kesif':>6}{'guncellenen':>13}{'hic bos':>9}{'ince (<5)':>11}"
      f"{'durum':>8}{'getiri':>9}")
for kesif in (0.00, 0.05, 0.20, 0.40, 1.00):
    iz = iz_topla(kesif)
    Qk, Nk = q_supur(iz)
    d = [(s, i) for s in Nk for i in range(EYLEM) if Nk[s][i] > 0]
    ince = sum(1 for s, i in d if Nk[s][i] < 5)
    print(f"{kesif:>6.2f}{len(d):>13}{1458 - len(d):>9}{ince:>11}"
          f"{len(Nk):>8}{statistics.mean(kosum(acgozlu(Qk))):>9.3f}")
```

```
 kesif  guncellenen  hic bos  ince (<5)   durum   getiri
  0.00          103     1355         47     103   20.621
  0.05          222     1236        122     114   15.176
  0.20          488      970        307     165   24.317
  0.40          749      709        437     196   21.703
  1.00         1237      221        468     235   24.419
```

Davranışa keşif eklendikçe çizelgenin dolu kısmı büyüyor: 103'ten 1.237'ye. Hiç boş kalan hücre
1.355'ten 221'e iniyor. Getiri sütunu ise tek yönlü ilerlemiyor — 20,621, sonra 15,176, sonra
24,317, sonra 21,703, sonra 24,419. Kapsama artıyor, sonuç düzenli iyileşmiyor.

Karşılaştırma tabanları birinci dersten sürüyor: rastgele politika 10,432, el kuralı 20,621. Beş
satırın beşi de rastgelenin üstünde ama yalnız üçü el kuralını geçiyor ve 0,05 satırı 15,176 ile
kuralın altına düşüyor. Kuralın verisine biraz keşif katmak sonucu doğrudan bozabiliyor.

Cevap dördüncü sütunda. Beşten az örnekle dolan hücre sayısı 47'den 468'e çıkıyor; keşif oranı
0,20'de dolu 488 hücrenin 307'si, yani 0,629'u ince. İnce bir hücrenin taşıdığı sayı bir iki
çekilişin ortalamasıdır ve hedefteki $\max$ tam da bu sayıları öne çıkarır: yanlışlıkla yüksek
çıkmış bir hücre hem kendi durumunda seçilir hem de kendisine komşu durumların hedefine sızar.
Kapsama tek başına bir kalite ölçüsü değildir; **kaç hücrenin dolduğu kadar, hücre başına kaç
örnek düştüğü de yazılmalıdır.**

Bu, kursun kuralının çizelge üzerindeki biçimidir. 24,317 sayısı 488 hücrede geçerlidir ve
bunların 307'sinde beşten az gözleme dayanır; 970 hücre hakkında çizelgenin söylediği tek şey
0,000'dır ve bu bir bilgi değil, bir başlangıç değeridir. Topladığı veriyi yazmayan bir çizelge
ölçülmemiş sayılır.

#### Özet

- Bir durumun değeri, o durumdan başlayarak politikanın toplayacağı indirimli ödüllerin beklenen
  toplamıdır; zaman farkı güncellemesi bu sayıyı ardışık iki kestirimin farkından öğrenir ve
  ortamın hiçbir olasılığını gerektirmez.
- 9.000 adımlık iz üzerinde 40 süpürmede başlangıç durumlarının ortalama değeri 12,632'den
  21,562'ye çıkar; bağımsız ölçülen 30 dönemlik getiri 20,621 olup kuyruk düzeltmesiyle 21,534
  eder, aradaki fark 0,028'dir.
- Sabit adım boyunda çizelge donmaz: ödül aynı hücrede $-2$ ile 7 arasında değiştiği için en büyük
  güncelleme 1,2700'de titreşmeye devam eder.
- Durum değerleri 29,060 ile 16,075 arasında dağılır; en yüksek durumda hızlı kötüleşen iki abone
  bekliyor, en düşükte kötüleşme olasılığı 0,05 olan tek abone.
- Kuralın kendi verisiyle beslenen eylem değeri çizelgesinin 1.458 hücresinden 103'ü güncellenir,
  1.355'i hiç dokunulmaz ve çizelgeden okunan politika tam olarak 20,621 verir: kendi verisiyle
  beslenen bir çizelge kendisini üreten politikadan başkasını öneremez.
- Keşif eklendikçe dolu hücre 103'ten 1.237'ye çıkar ama beşten az örnekli ince hücre de 47'den
  468'e çıkar; getiri tek yönlü ilerlemez, çünkü hedefteki en iyi eylem seçimi ince hücrelerin
  gürültüsünü öne taşır.

#### Sonraki Adım

Dört derste çizelge her şeyi taşıdı: 243 durum, 1.458 hücre, hepsi bellekte, hepsi tek tek
güncellenebilir. Bu, durum sayısının küçüklüğü sayesindedir ve o küçüklük bedava değildi. İkinci
derste duruma tek bir geçmiş alanı eklendiğinde getiri 20,621'den 23,148'e çıkmıştı; aynı ekleme
durum uzayını 243'ten 1.215'e, çizelgeyi 1.458 hücreden 7.290 hücreye taşır. Veri bütçesi 9.000
adımda kaldığına göre, bütün adımlar farklı hücrelere düşse bile hücre başına 1,23 örnek düşerdi
— oysa bu derste 488 hücrenin 307'si beşten az örnekle zaten ince sayılmıştı. Abone sayısı beşten
sekize çıksa durum uzayı 6.561, çizelge 59.049 hücre olurdu. Çizelge büyüdükçe dolmuyor ve
dolmayan çizelge sıfırlarla karar veriyor. Sonraki ders çizelgeyi bırakır: her hücreye ayrı bir
sayı tutmak yerine, durumu girdi alıp değeri **hesaplayan** bir fonksiyon kullanılır ve ilk işi
benzer durumların birbirinden öğrenmesini sağlamak olur.

### Fonksiyon Yaklaşımı ve Deneyim Tekrarı

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/fonksiyon-yaklasimi-ve-deneyim-tekrari

Çizelgenin yerini alan küçük bir ağın ve deneyim tekrarının sayıyla ölçülmesi: 243 durum ile 6 eylemin kurduğu 1.458 hücrenin 9.000 geçiş sonunda yalnız 780'i güncelleniyor, 37 durum hiç görülmüyor ve yalnız 60 durumda altı eylem de deneniyor. Ardışık altılı toplu iş ortalama 1,16 ayrı bölümden, tampondan çekilen altılı 5,50 ayrı bölümden geliyor. Taban çizgileri 0,000 ve 10,432, kural politikası 20,621, çizelge 24,495. 126 parametreli ağ çevrimiçi tek geçişle 23,211 ve 23,071, ardışık altılıyla 22,259 ve 20,146, tekrar tamponuyla 26,233 ve 25,302 veriyor; koşum bandı 2,113'ten 0,930'a iniyor.

Önceki ders eylem değerini bir çizelgede öğrendi ve çizelgenin kaç hücresinin hiç güncellenmediğini
saydı. Bu ders o sayıyı bir sınır olarak ele alır. Çizelgenin sorunu bellek değildir; her hücrenin
yalnız o hücreye uğrandığında öğrenilmesi ve iki komşu durum arasında hiçbir bilgi
paylaşılmamasıdır. `(1,0,2,0,1)` durumunda öğrenilen şey `(1,0,2,0,0)` durumuna geçmez.

Çizelgenin yerine bir **fonksiyon yaklaşımı** konur: durumu girdi alan, her eyleme bir sayı
döndüren küçük bir ağ. Ağ hiç görülmemiş bir durumda da cevap üretir, çünkü benzer durumlardan
öğrendiğini oraya taşır. Bedeli, öğrenmenin artık kararlı olmamasıdır — ve bu dersin ölçtüğü şey
kararsızlığın nereden geldiği ile **deneyim tekrarının** onu ne kadar düzelttiğidir.

- **PO41.** Ortam `01`'de kurulan kurgudur ve burada olduğu gibi sürer: beş abone, dönem başına
  tek ekip, ufuk 30, indirim katsayısı 0,9. Ajanın gördüğü durum gecikme sınıfıdır; arıza derecesi
  gizlidir.
- **PO42.** Durum beş gecikme sınıfının beşlisidir, yani $3^5 = 243$ durum; eylem altıdır — beş
  aboneden birine gitmek ya da ekip çıkarmamak. Durum–eylem çifti 1.458'dir.
- **PO43.** Ölçüm `01`'deki aynı 300 bölümde yapılır, böylece bu dersin sayıları önceki derslerin
  sayılarıyla doğrudan karşılaştırılabilir. Getiri indirimli ödül toplamıdır.
- **PO44.** Taban çizgisi iki tanedir: **hiç ekip çıkarmamak** ve **rastgele eylem**. Birincisi
  tanım gereği sıfırdır ve eylemsizliğin karşılığını verir, ikincisi sıralamasız çabanınkini.
  Yanlarına `01`'in kural politikası da yazılır, çünkü öğrenen yöntemin geçmesi gereken sayı odur.
- **PO45.** Çizelge, zaman farkı güncellemesiyle 300 bölüm boyunca öğrenilir; keşif oranı 0,60'tan
  0,05'e doğrusal iner. Sabit ve düşük bir keşifle ilk denenen eylem kalıcı biçimde öne geçtiği
  için oran düşürülerek uygulanır.
- **PO46.** Ağ M27/K05'ten girdi olarak gelir ve yordamı burada tekrarlanmaz: beş girdi, on gizli
  birim, altı çıktı; gizli katman hiperbolik tanjant, çıkış katmanı doğrusaldır.
- **PO47.** Ağın hedefi ödülün ona bölünmüş biçimidir. Eylem en büyük çıktı seçilerek belirlendiği
  için bu ölçek politikayı değiştirmez; yalnız hedefleri ağın çıkış aralığına yaklaştırır.
- **PO48.** Ağ kullanan her ölçü **iki koşumla** basılır ve koşumlar arasında yalnız eğitim tohumu
  değişir; bu tohum ağın başlangıç değerlerini, eğitim bölümlerinin ortam gerçekleşmesini ve
  keşif seçimlerini birlikte belirler. Değerlendirme bölümleri her koşumda aynıdır.

#### Çizelgenin Dolduramadığı Yer

İlk blok ortamı yeniden kurar, üç karşılaştırma sayısını basar ve çizelgenin doluluğunu sayar.

```python
# cizelge.py — Ortam 01'de kurulan KURGUDUR: bes abone, her donem tek ekip.
import math
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9


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


class Ortam:
    """Ariza derecesi d gizlidir; ajanin gordugu durum gecikme sinifidir."""

    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def olc(politika, n=300):                   # 01'deki ayni 300 bolum, ayni ufuk
    g = []
    for k in range(n):
        o = Ortam(TOHUM + 1000 * k)
        s, G, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s2, od = o.adim(politika(s))
            G, c, s = G + c * od, c * INDIRIM, s2
        g.append(G)
    return statistics.mean(g)


def cizelge_egit(tohum, B=300, ogrenme=0.20):
    Q, sayac, r = {}, {}, uretec(tohum)
    for b in range(B):
        kesif = 0.60 - 0.55 * b / (B - 1)
        o = Ortam(tohum + 100 * b)
        s = o.durum()
        for _ in range(UFUK):
            a = (int(r() * EYLEM) if r() < kesif
                 else max(range(EYLEM), key=lambda e: Q.get((s, e), 0.0)))
            s2, od = o.adim(a)
            eski = Q.get((s, a), 0.0)
            Q[(s, a)] = eski + ogrenme * (od + INDIRIM * max(Q.get((s2, e), 0.0)
                                                             for e in range(EYLEM)) - eski)
            sayac[(s, a)] = sayac.get((s, a), 0) + 1
            s = s2
    return Q, sayac


Q, SAYAC = cizelge_egit(TOHUM + 900)
DURUM, CIFT = 3 ** ABONE, 3 ** ABONE * EYLEM
gorulen = {s for s, _ in SAYAC}
tam = sum(all((s, e) in SAYAC for e in range(EYLEM)) for s in gorulen)
print(f"durum {DURUM}, eylem {EYLEM}, durum-eylem cifti {CIFT}")
print(f"300 bolum x {UFUK} adim = {300 * UFUK} gecis; guncellenen cift {len(SAYAC)}"
      f" ({CIFT - len(SAYAC)} hic guncellenmedi), gorulen durum {len(gorulen)}")
print(f"alti eylemi de denenmis durum {tam}, hic gorulmemis durum {DURUM - len(gorulen)}")
print(f"{'taban: hic ekip cikarmamak':<28}{olc(lambda s: ABONE):8.3f}")
rp = uretec(TOHUM + 7)
print(f"{'taban: rastgele eylem':<28}{olc(lambda s: int(rp() * EYLEM)):8.3f}")
print(f"{'kural: gecikmesi en buyuge':<28}"
      f"{olc(lambda s: ABONE if max(s) == 0 else s.index(max(s))):8.3f}")
print(f"{'cizelge':<28}{olc(lambda s: max(range(EYLEM), key=lambda e: Q.get((s, e), 0.0))):8.3f}")
```

```
durum 243, eylem 6, durum-eylem cifti 1458
300 bolum x 30 adim = 9000 gecis; guncellenen cift 780 (678 hic guncellenmedi), gorulen durum 206
alti eylemi de denenmis durum 60, hic gorulmemis durum 37
taban: hic ekip cikarmamak     0.000
taban: rastgele eylem         10.432
kural: gecikmesi en buyuge    20.621
cizelge                       24.495
```

Toplanan veri şudur: 9.000 geçiş yapıldı, 1.458 hücrenin 780'i güncellendi ve **678'i hiç
dokunulmadan kaldı**. Bu sayı keşif takvimine bağlıdır ve başka bir takvimle
başka çıkar; burada bağlayıcı olan büyüklük mertebesidir. Politika kendi verisini topladığı için
243 durumun 37'si hiç görülmedi ve
yalnız 60 durumda altı eylemin hepsi denendi. O 37 durumda çizelgenin altı girdisi de sıfırdır;
en büyüğünü seçme kuralı orada ilk eylemi seçer ve bu seçimin öğrenilmiş bir yanı yoktur.

Çizelge yine de kazanıyor: 24,495, kural politikasının 20,621'inin üstünde. Sorun bu ölçekte
sonucun kötü olması değil, ölçeğin kendisidir. Beş abone 243 durum verdi; yedi abone 2.187 durum
ve 17.496 çift verir. Geçiş sayısı aynı kaldığında boş hücre oranı büyür, çünkü çizelge komşu
durumlar arasında hiçbir şey paylaşmaz.

#### Çizelge Yerine Fonksiyon

Ağ bu paylaşımı kurar. Girdisi beş gecikme sınıfıdır, çıktısı altı eylem değeridir; hedef, önceki
dersteki zaman farkı hedefinin aynısıdır — alınan ödül artı indirimli en iyi bir sonraki eylem
değeri. Fark, güncellemenin tek bir hücreye değil bütün ağırlıklara yayılmasıdır. Kararsızlık da
buradan gelir: bir durumda yapılan düzeltme başka durumların çıktısını da oynatır.

İkinci sorun verinin geliş sırasıdır. Ajan geçişleri **ardışık** üretir; bir bölümün altı ardışık
adımı birbirinin devamıdır. Aşağıdaki blok ağı tanımlar ve bu ilişkiyi iki sayıyla ölçer.

```python
OLCEK = 10.0                                # ag hedefi odul/10 olceginde tutulur


def ilklendir(tohum, gizli=10):             # kucuk ag: 5 -> 10 -> 6; yordami M27/K05'te
    r, ag = uretec(tohum), []
    for a, b in zip((ABONE, gizli, EYLEM), (gizli, EYLEM)):
        o = math.sqrt(2.0 / a)
        ag.append(([[(r() * 2 - 1) * o for _ in range(a)] for _ in range(b)], [0.0] * b))
    return ag


def ileri(ag, x):
    (W1, y1), (W2, y2) = ag
    h = [math.tanh(sum(w * v for w, v in zip(s, x)) + b) for s, b in zip(W1, y1)]
    return h, [sum(w * v for w, v in zip(s, h)) + b for s, b in zip(W2, y2)]


def giris(s):
    return [x / 2.0 for x in s]


def guncelle(ag, toplu, ogrenme):           # yalniz secilen eylemin cikisi duzeltilir
    (W1, y1), (W2, y2) = ag
    n, g = len(toplu), len(y1)
    gW1, gy1 = [[0.0] * ABONE for _ in range(g)], [0.0] * g
    gW2, gy2 = [[0.0] * g for _ in range(EYLEM)], [0.0] * EYLEM
    for x, a, hedef in toplu:
        h, q = ileri(ag, x)
        e = q[a] - hedef
        for p in range(g):
            gW2[a][p] += e * h[p]
            dh = e * W2[a][p] * (1 - h[p] * h[p])
            gy1[p] += dh
            for j in range(ABONE):
                gW1[p][j] += dh * x[j]
        gy2[a] += e
    for j in range(EYLEM):
        for p in range(g):
            W2[j][p] -= ogrenme * gW2[j][p] / n
        y2[j] -= ogrenme * gy2[j] / n
    for p in range(g):
        for j in range(ABONE):
            W1[p][j] -= ogrenme * gW1[p][j] / n
        y1[p] -= ogrenme * gy1[p] / n


def ag_egit(tohum, tampon_boy, toplu, B=120, ogrenme=0.10):
    ag, r, tmp = ilklendir(tohum), uretec(tohum + 77), []
    for b in range(B):
        kesif = 0.60 - 0.55 * b / (B - 1)
        o = Ortam(tohum + 100 * b)
        s = o.durum()
        for _ in range(UFUK):
            a = (int(r() * EYLEM) if r() < kesif
                 else max(range(EYLEM), key=lambda e: ileri(ag, giris(s))[1][e]))
            s2, od = o.adim(a)
            tmp.append((s, a, od / OLCEK, s2))
            if len(tmp) > tampon_boy:
                tmp.pop(0)
            ornek = (tmp[-toplu:] if tampon_boy == toplu       # ardisik pencere
                     else [tmp[int(r() * len(tmp))] for _ in range(toplu)])
            guncelle(ag, [(giris(ss), sa, so + INDIRIM * max(ileri(ag, giris(sy))[1]))
                          for ss, sa, so, sy in ornek], ogrenme)
            s = s2
    return ag


def ag_politika(ag):
    return lambda s: max(range(EYLEM), key=lambda e: ileri(ag, giris(s))[1][e])


r = uretec(TOHUM + 3)                       # 30 bolumluk bir iz, cizelge politikasiyla
IZ = []
for b in range(30):
    o = Ortam(TOHUM + 100000 + b)
    s = o.durum()
    for _ in range(UFUK):
        a = (int(r() * EYLEM) if r() < 0.15
             else max(range(EYLEM), key=lambda e: Q.get((s, e), 0.0)))
        IZ.append((s, a, b))
        s = o.adim(a)[0]


def dagilim(g):                             # toplu isteki durumlar arasi ortalama sinif farki
    c = [abs(x - y) for i in range(len(g)) for j in range(i + 1, len(g))
         for x, y in zip(g[i][0], g[j][0])]
    return sum(c) / (len(g) * (len(g) - 1) / 2)


N = len(IZ) - 6
ard = [sum(dagilim(IZ[i:i + 6]) for i in range(N)) / N,
       sum(len({t[2] for t in IZ[i:i + 6]}) for i in range(N)) / N]
tam2 = [0.0, 0.0]
for _ in range(N):
    g = [IZ[int(r() * len(IZ))] for _ in range(6)]
    tam2[0] += dagilim(g) / N
    tam2[1] += len({t[2] for t in g}) / N
print(f"{'altili toplu is':<26}{'ardisik':>10}{'tampondan':>12}")
print(f"{'ortalama sinif farki':<26}{ard[0]:>10.2f}{tam2[0]:>12.2f}")
print(f"{'ayri bolum sayisi':<26}{ard[1]:>10.2f}{tam2[1]:>12.2f}")
```

```
altili toplu is              ardisik   tampondan
ortalama sinif farki            2.71        2.93
ayri bolum sayisi               1.16        5.50
```

İkinci satır ilişkiyi doğrudan söylüyor: ardışık bir altılı ortalama **1,16 ayrı bölümden** geliyor,
yani neredeyse tamamı tek bir bölümün art arda gelen adımlarıdır. Tampondan çekilen altılı **5,50
ayrı bölümden** geliyor. Durum tarafındaki fark daha ölçülüdür — ardışık altılıda iki durum
arasındaki ortalama gecikme sınıfı farkı 2,71, tampondan çekilende 2,93. Ağ ardışık kurulumda her
adımda birbirine yakın altı örneğe bakar ve ağırlıklarını o dar bölgeye göre kaydırır; birkaç adım
sonra başka bir bölgeye geçtiğinde kaymayı geri alır.

#### Tekrar Tamponunun Kattığı Fark

**Deneyim tekrarı** tek satırlık bir değişikliktir: geçişler bir tampona yazılır ve güncelleme
tampondan **rastgele çekilen** bir altılıyla yapılır. Farkın toplu iş boyundan mı yoksa örneğin
nereden geldiğinden mi doğduğunu ayırmak için üçüncü bir kurulum eklenir — toplu iş kullanmayan,
yalnız son geçişle güncelleyen çevrimiçi biçim. Üçünün de bütçesi aynıdır: 120 bölüm, 3.600 geçiş.

```python
SONUC = {}
print(f"{'kurulum':<22}{'kosum 1':>9}{'kosum 2':>9}{'band':>7}")
for ad, tampon, toplu in (("cevrimici tek gecis", 1, 1),
                          ("ardisik altili", 6, 6),
                          ("tekrar tamponu", 900, 6)):
    SONUC[ad] = [olc(ag_politika(ag_egit(TOHUM + k, tampon, toplu))) for k in (1, 2)]
    g = SONUC[ad]
    print(f"{ad:<22}{g[0]:>9.3f}{g[1]:>9.3f}{max(g) - min(g):>7.3f}")

A, T = SONUC["ardisik altili"], SONUC["tekrar tamponu"]
par = ABONE * 10 + 10 + 10 * EYLEM + EYLEM
print(f"\nayni toplu is boyu, ardisik yerine tampondan: "
      f"{max(A):.3f} -> {max(T):.3f} ve {min(A):.3f} -> {min(T):.3f}")
print(f"kosum bandi {max(A) - min(A):.3f} -> {max(T) - min(T):.3f}")
print(f"ag {par} parametre; cizelge {CIFT} hucre, {CIFT - len(SAYAC)} tanesi bos")
```

```
kurulum                 kosum 1  kosum 2   band
cevrimici tek gecis      23.211   23.071  0.139
ardisik altili           22.259   20.146  2.113
tekrar tamponu           26.233   25.302  0.930

ayni toplu is boyu, ardisik yerine tampondan: 22.259 -> 26.233 ve 20.146 -> 25.302
kosum bandi 2.113 -> 0.930
ag 126 parametre; cizelge 1458 hucre, 678 tanesi bos
```

Üç satır sırayla okunur. Toplu iş kullanmak tek başına bir kazanç değildir: çevrimiçi tek geçiş
23,211 ve 23,071 verirken ardışık altılı 22,259 ve 20,146 veriyor, yani altı örneği ortalamak
sayıyı **düşürüyor**. İkinci koşum 20,146 ile kural politikasının 20,621'inin de altına iniyor.
Aynı toplu iş boyu tampondan çekildiğinde 26,233 ve 25,302 çıkıyor. Kazanç toplu iştan değil,
örneğin **nereden geldiğinden** doğuyor.

İkinci sayı kararlılıktır. Ardışık kurulumun koşum bandı 2,113; tekrar tamponununki 0,930. Ardışık
kurulumda tek bir koşuma bakarak karar vermek, yalnız eğitim tohumu değişerek iki birimden
fazla oynayan bir sayıya bakmaktır. Bu kursun iki koşum kuralı bu yüzden vardır.

Son satır ölçeği kapatır. Ağ 126 parametre taşıyor ve 243 durumun tamamı için altı eylem değeri
üretiyor; çizelge 1.458 hücre taşıyordu ve 678'i boştu. Ağın hiç görülmemiş 37 durumda verdiği
cevap doğru olmak zorunda değildir, ama keyfi de değildir: komşu durumlardan taşınmış bir
tahmindir.

#### Özet

- Çizelge komşu durumlar arasında hiçbir şey paylaşmaz. 9.000 geçiş sonunda 1.458 hücrenin 678'i
  hiç güncellenmedi, 243 durumun 37'si hiç görülmedi ve yalnız 60 durumda altı eylem de denendi.
- Fonksiyon yaklaşımı çizelgenin yerine durumu girdi alan bir ağ koyar: 126 parametre, 1.458
  hücreye karşılık ve hiç görülmemiş durumda da bir cevap.
- Ardışık geçişler ilişkilidir ve sayısı yazılır: ardışık altılı ortalama 1,16 ayrı bölümden,
  tampondan çekilen altılı 5,50 ayrı bölümden geliyor.
- Aynı bütçede çevrimiçi tek geçiş 23,211 ve 23,071, ardışık altılı 22,259 ve 20,146, tekrar
  tamponu 26,233 ve 25,302 veriyor. Kazanç toplu iştan değil örneğin nereden geldiğinden gelir.
- Deneyim tekrarı düzeyi yükseltmenin yanında kararlılık da katar: koşum bandı 2,113'ten 0,930'a
  iniyor ve ardışık kurulumun kötü koşumu kural politikasının altına düşüyor.

#### Sonraki Adım

Beş derste ajan hep aynı yoldan karar verdi: her eylem için bir değer kestirildi ve en büyüğü
seçildi. Politika, değerin bir yan ürünüydü. Bu, değer kestirimindeki küçük bir kaymanın politikayı
tümüyle değiştirebileceği anlamına gelir — iki eylemin değeri birbirine yakınsa, kestirimin oynaması
seçimi de oynatır. Sonraki ders politikayı **doğrudan** parametreleştirir ve değeri aradan çıkarır:
her eyleme bir olasılık atanır ve olasılıklar getiriyi artıracak yönde güncellenir. Ölçülecek olan
bu yolun bedelidir — güncelleme yönü artık örneklenmiş bölümlerden kestirilir ve aynı politikayla
aynı bütçede kaç bölümün kaç ayrı yön verdiği sayılır.

### Politika Eğimi

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/politika-egimi

Politikayı doğrudan parametreleştirmenin bedelinin ölçülmesi: aynı politika ve 120 bölümlük aynı bütçeyle, tek bölümden okunan 120 güncelleme yönü ortalama 86,1 derecelik açı yapıyor ve uzlaşma oranı 0,290'da kalıyor; yirmi bölümlük altı kestirimde açı 44,0 dereceye, uzlaşma 0,878'e çıkıyor. Adım adım ortalama getiriden kurulan değer çizgisi çıkarıldığında aynı sayılar 81,2 derece ile 0,389 ve 39,8 derece ile 0,892 oluyor; taban çıkarmalı iki bölüm, ham dört bölümün uzlaşmasını veriyor. 240 bölümlük eğitimde iki bölümlük kestirim ham getiriyle 15,840 ve 19,962, taban çıkarmayla 22,866 ve 22,190 veriyor; koşum bandı 4,122'den 0,675'e iniyor.

Önceki beş derste politika hep dolaylı kuruldu: her eylem için bir değer kestirildi ve en büyüğü
seçildi. Bu düzende politika, değer kestiriminin bir yan ürünüdür — iki eylemin kestirilen değeri
birbirine yakınsa, kestirimdeki küçük bir kayma seçimi tümüyle çevirir. Ayrıca seçim keskindir:
en büyük değere sahip eylem her zaman, ikinci en iyi eylem hiçbir zaman seçilmez.

Bu ders başka bir yol tutar. Politika **doğrudan** parametreleştirilir: her eyleme bir ağırlık
satırı verilir, durumdan bir olasılık dağılımı üretilir ve ağırlıklar getiriyi artıracak yönde
kaydırılır. Yön artık bir çizelgeden okunmaz, **örneklenmiş bölümlerden kestirilir** — ve bu dersin
ölçtüğü şey o kestirimin ne kadar oynadığıdır.

- **PO49.** Ortam `01`'de kurulan kurgudur ve olduğu gibi sürer: beş abone, dönem başına tek ekip,
  ufuk 30, indirim katsayısı 0,9. Ölçüm `01`'deki aynı 300 bölümdedir.
- **PO50.** Politikanın özniteliği beş gecikme sınıfı ile bir sabittir, yani altı sayı. Ağırlık
  dizisi $6 \times 6 = 36$ sayıdır; ders boyunca "yön" bu 36 sayılık bir kaymayı anlatır.
- **PO51.** Eylem olasılıkları ağırlıklı toplamların üssel normalleştirmesinden çıkar. Bütün
  ağırlıklar sıfırken dağılım eşit olasılıklıdır; ölçümün başladığı politika budur ve `01`'in
  rastgele eylem tabanının aynısıdır.
- **PO52.** Bir bölümden okunan yön, her adımda o adımdan sonraki getiri ile seçilen eylemin
  olasılığını artıran kaymanın çarpımıdır. İndirim katsayısı adım ağırlığı olarak ayrıca
  uygulanmaz; getirinin kendisi zaten indirimlidir.
- **PO53.** Değişkenlik iki sayıyla yazılır: kestirimler arasındaki **ortalama ikili açı** ve
  **uzlaşma oranı** — ortalama kestirimin uzunluğunun tek tek kestirimlerin ortalama uzunluğuna
  bölümü. Uzlaşma 1'e yakınsa kestirimler aynı yeri gösteriyor, 0'a yakınsa birbirini götürüyor.
  İki ölçü de kestirim sayısından bağımsızdır.
- **PO54.** Değer çizgisi, aynı politikanın **ayrı** 200 bölümünden okunan adım adım ortalama
  getiridir. Ayrı bölümlerden okunması gerekir; ölçülen bölümün kendi getirisi çıkarılırsa yön
  tanım gereği sıfırlanır.
- **PO55.** Değer çizgisi eylemden bağımsızdır, bu yüzden kestirimin gittiği yeri kaydırmaz:
  bir durumdaki bütün eylemlerin olasılık kaymaları toplandığında sıfır çıkar, dolayısıyla sabit
  bir sayının çarpanı da sıfırdır. Değişen yalnız kestirimin oynamasıdır.
- **PO56.** Eğitimde bütçe 240 bölümdür ve öğrenme adımı 0,01'de sabit tutulur; değişen yalnız
  toplu iş boyu ile taban çıkarmanın olup olmadığıdır.

#### Politikayı Doğrudan Yazmak

Aşağıdaki blok ortamı yeniden kurar, politikayı tanımlar ve bir bölümden okunan yönü hesaplar.
Yönün mantığı tek cümledir: bir eylem seçildikten sonra gelen getiri büyükse o eylemin olasılığı
artırılır, küçükse azaltılır.

```python
# politika.py — Ayni KURGU ortam: bes abone, tek ekip, ufuk 30, indirim 0,9.
import math
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9
OZ = ABONE + 1                              # ozellik sayisi: bes gecikme sinifi + sabit


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


class Ortam:
    """Ariza derecesi d gizlidir; ajanin gordugu durum gecikme sinifidir."""

    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def olasilik(th, s):                        # ussel normallestirme; th[a] eylemin agirlik satiri
    x = [v / 2.0 for v in s] + [1.0]
    z = [sum(t * v for t, v in zip(th[a], x)) for a in range(EYLEM)]
    e = [math.exp(v - max(z)) for v in z]
    T = sum(e)
    return [v / T for v in e], x


def sec(th, s, r):
    p, _ = olasilik(th, s)
    u, t = r(), 0.0
    for a in range(EYLEM):
        t += p[a]
        if u < t:
            return a
    return EYLEM - 1


def olc(politika, n=300):                   # 01'deki ayni 300 bolum
    g = []
    for k in range(n):
        o, r = Ortam(TOHUM + 1000 * k), uretec(TOHUM + 90000 + k)
        s, G, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s2, od = o.adim(politika(s, r))
            G, c, s = G + c * od, c * INDIRIM, s2
        g.append(G)
    return statistics.mean(g)


def bolum_izi(th, tohum):
    o, r = Ortam(tohum), uretec(tohum + 5)
    s, iz = o.durum(), []
    for _ in range(UFUK):
        a = sec(th, s, r)
        s2, od = o.adim(a)
        iz.append((s, a, od))
        s = s2
    return iz


def getiriler(iz):                          # her adimdan sonraki indirimli getiri
    G, g = [0.0] * len(iz), 0.0
    for t in range(len(iz) - 1, -1, -1):
        g = iz[t][2] + INDIRIM * g
        G[t] = g
    return G


def bolum_egimi(th, iz, G, cizgi=None):     # tek bolumden okunan guncelleme yonu
    e = [0.0] * (EYLEM * OZ)
    for t, (s, a, _) in enumerate(iz):
        p, x = olasilik(th, s)
        w = G[t] - (cizgi[t] if cizgi else 0.0)
        for b in range(EYLEM):
            k = w * ((1.0 if b == a else 0.0) - p[b])
            for j in range(OZ):
                e[b * OZ + j] += k * x[j]
    return e


def deger_cizgisi(th, tohumlar):            # adim adim ortalama getiri, AYRI bolumlerden
    top = [0.0] * UFUK
    for tt in tohumlar:
        G = getiriler(bolum_izi(th, tt))
        for t in range(UFUK):
            top[t] += G[t] / len(tohumlar)
    return top


def aci(u, v):
    nu = math.sqrt(sum(x * x for x in u))
    nv = math.sqrt(sum(x * x for x in v))
    c = sum(a * b for a, b in zip(u, v)) / (nu * nv)
    return math.degrees(math.acos(max(-1.0, min(1.0, c))))


def olcum(kest):
    n = len(kest)
    ort = [sum(v[j] for v in kest) / n for j in range(len(kest[0]))]
    boy = statistics.mean(math.sqrt(sum(x * x for x in v)) for v in kest)
    return (statistics.mean(aci(kest[i], kest[j]) for i in range(n) for j in range(i + 1, n)),
            math.sqrt(sum(x * x for x in ort)) / boy)


TH = [[0.0] * OZ for _ in range(EYLEM)]     # butun agirliklar sifir: esit olasilikli politika
rp = uretec(TOHUM + 7)
print(f"{'taban: hic ekip cikarmamak':<30}{olc(lambda s, r: ABONE):8.3f}")
print(f"{'taban: rastgele eylem':<30}{olc(lambda s, r: int(rp() * EYLEM)):8.3f}")
print(f"{'olculen politika (agirliklar 0)':<30}{olc(lambda s, r: sec(TH, s, r)):8.3f}")

BUTCE = 120
CIZGI = deger_cizgisi(TH, [TOHUM + 700000 + i for i in range(200)])
HAM, TABANLI = [], []
for i in range(BUTCE):
    iz = bolum_izi(TH, TOHUM + 200000 + i)
    G = getiriler(iz)
    HAM.append(bolum_egimi(TH, iz, G))
    TABANLI.append(bolum_egimi(TH, iz, G, CIZGI))


def grupla(hepsi, B):
    return [[sum(v[j] for v in hepsi[i * B:(i + 1) * B]) / B for j in range(EYLEM * OZ)]
            for i in range(len(hepsi) // B)]


print(f"\ndeger cizgisi: ilk adim {CIZGI[0]:.2f}, onuncu {CIZGI[9]:.2f}, son {CIZGI[-1]:.2f}")
print(f"{'bolum/kestirim':>15}{'kestirim':>10}{'aci':>8}{'uzlasma':>9}"
      f"{'aci (taban)':>13}{'uzlasma (taban)':>17}")
for B in (1, 2, 4, 10, 20):
    h, t = olcum(grupla(HAM, B)), olcum(grupla(TABANLI, B))
    print(f"{B:>15}{BUTCE // B:>10}{h[0]:>8.1f}{h[1]:>9.3f}{t[0]:>13.1f}{t[1]:>17.3f}")
```

```
taban: hic ekip cikarmamak       0.000
taban: rastgele eylem           10.432
olculen politika (agirliklar 0)  10.177

deger cizgisi: ilk adim 9.88, onuncu 11.18, son 1.63
 bolum/kestirim  kestirim     aci  uzlasma  aci (taban)  uzlasma (taban)
              1       120    86.1    0.290         81.2            0.389
              2        60    83.1    0.386         74.8            0.511
              4        30    77.9    0.508         67.0            0.636
             10        12    62.4    0.736         53.1            0.790
             20         6    44.0    0.878         39.8            0.892
```

Üçüncü satır politikanın ölçüsünü veriyor: ağırlıkları sıfır olan politika 10,177 getiriyor, yani
rastgele eylemin 10,432'siyle aynı yerde. Beklenen budur — eşit olasılıklı seçim rastgele seçimdir
ve iki sayı arasındaki 0,255 fark yalnız örnekleme ayrıntısıdır.

Tablonun ilk satırı dersin sayısıdır. 120 bölümlük bütçe tek tek kullanıldığında **120 ayrı
güncelleme yönü** çıkıyor ve bu yönler ortalama **86,1 derece** açı yapıyor: birbirlerine
neredeyse dik. Uzlaşma oranı 0,290, yani yönlerin onda yedisi toplandığında birbirini götürüyor.
Tek bir bölümden okunan yöne bakarak ağırlıkları kaydırmak, gürültüyü politikanın içine yazmaktır.

Bütçe bölünmediğinde tablo yumuşuyor: yirmi bölümlük altı kestirim 44,0 derece ve 0,878 uzlaşma
veriyor. Ama bunun bir bedeli var — aynı bütçe altı güncellemeye düşüyor. Ders boyunca ölçülen
gerilim budur: yön ne kadar güvenilir olsun isteniyorsa o kadar az güncelleme yapılabiliyor.

#### Değer Çizgisini Çıkarmak

Değer çizgisi satırı sorunun kaynağını gösteriyor: bu ortamda getiriler ilk adımlarda 9,88 ile
11,18 arasında ve son adımda 1,63. Neredeyse hepsi pozitif. Getiri doğrudan çarpan olarak
kullanıldığında **seçilen her eylemin olasılığı artırılır**; eylemler arasındaki ayrım yalnız
artışın büyüklüğünden gelir. Kötü bir eylem de, ardından gelen getiri pozitif olduğu için,
olasılığını artırarak biter.

Taban çıkarma bunu düzeltir. Her adımın getirisinden o adımın **ortalama** getirisi düşülür;
ortalamanın üstünde kalan eylem artırılır, altında kalan azaltılır. Sayı tablonun sağ yarısındadır:
tek bölümde 86,1 derece 81,2 dereceye, uzlaşma 0,290'dan 0,389'a çıkıyor. Daha okunaklı biçimi
şudur — taban çıkarmalı **iki** bölüm 0,511 uzlaşma veriyor, ham **dört** bölüm 0,508. Aynı
uzlaşma yarı bütçeyle elde ediliyor.

Etki bütçe büyüdükçe küçülüyor: yirmi bölümde 44,0 derece 39,8 dereceye iniyor, uzlaşma 0,878'den
0,892'ye çıkıyor. Kestirim zaten yeterince örneklenmişse taban çıkarmanın düzeltecek fazla şeyi
kalmıyor.

#### Aynı Bütçe, Dört Kurulum

Değişkenlik ölçüsü ancak öğrenmeye çevrildiğinde bir anlam kazanır. Aşağıdaki blok 240 bölümlük
aynı bütçeyi dört biçimde harcar: toplu iş iki ya da on bölüm, taban çıkarmalı ya da ham getiriyle.

```python
def egit(tohum, tabanli, toplu, butce=240, ogrenme=0.01):
    th = [[0.0] * OZ for _ in range(EYLEM)]
    cizgi = [0.0] * UFUK
    for t in range(butce // toplu):
        egm, ortG = [0.0] * (EYLEM * OZ), [0.0] * UFUK
        for i in range(toplu):
            iz = bolum_izi(th, tohum + 1000 * t + i)
            G = getiriler(iz)
            e = bolum_egimi(th, iz, G, cizgi if tabanli else None)
            for j in range(EYLEM * OZ):
                egm[j] += e[j] / toplu
            for u in range(UFUK):
                ortG[u] += G[u] / toplu
        if tabanli:
            cizgi = ortG                    # deger cizgisi bir onceki toplu isten gelir
        for b in range(EYLEM):
            for j in range(OZ):
                th[b][j] += ogrenme * egm[b * OZ + j]
    return th


print(f"{'kurulum':<28}{'guncelleme':>11}{'kosum 1':>9}{'kosum 2':>9}{'band':>7}")
SON = {}
for toplu in (2, 10):
    for tabanli in (False, True):
        ad = f"{toplu} bolum, {'taban cikarmali' if tabanli else 'ham getiri'}"
        g = [olc(lambda s, r, th=egit(TOHUM + 300000 + k * 7919, tabanli, toplu): sec(th, s, r))
             for k in (1, 2)]
        SON[(toplu, tabanli)] = g
        print(f"{ad:<28}{240 // toplu:>11}{g[0]:>9.3f}{g[1]:>9.3f}{max(g) - min(g):>7.3f}")

h2, t2 = SON[(2, False)], SON[(2, True)]
print(f"\niki bolumluk kestirimde taban cikarma: {min(h2):.3f} -> {min(t2):.3f} ve "
      f"{max(h2):.3f} -> {max(t2):.3f}, band {max(h2) - min(h2):.3f} -> {max(t2) - min(t2):.3f}")
```

```
kurulum                      guncelleme  kosum 1  kosum 2   band
2 bolum, ham getiri                 120   15.840   19.962  4.122
2 bolum, taban cikarmali            120   22.866   22.190  0.675
10 bolum, ham getiri                 24   17.121   17.853  0.732
10 bolum, taban cikarmali            24   16.808   16.900  0.093

iki bolumluk kestirimde taban cikarma: 15.840 -> 22.190 ve 19.962 -> 22.866, band 4.122 -> 0.675
```

Dört satır tablonun okunuşunu doğruluyor. İki bölümlük kestirim ham getiriyle 15,840 ve 19,962
veriyor; ikisi de rastgele eylemin 10,432'sini geçiyor ama koşum bandı 4,122 — yalnız başlangıç
bölümleri değişerek dört birimden fazla oynayan bir sayı. Aynı kurulumda taban çıkarıldığında
22,866 ve 22,190 çıkıyor ve band 0,675'e iniyor. Taban çıkarma burada hem düzeyi yükseltiyor hem
kararlılığı getiriyor, çünkü iki bölümlük bir kestirim tabansız haliyle kullanılamayacak kadar
oynaktır.

On bölümlük kestirimde tablo tersine dönüyor: 17,121 ve 17,853'e karşı 16,808 ve 16,900. Taban
çıkarma burada kazanç sağlamıyor. Neden tablodadır — on bölümde uzlaşma zaten 0,736 ve taban
çıkarma onu 0,790'a taşıyor; bu iyileşme, bütçenin onda birine düşen güncelleme sayısını telafi
etmiyor. En iyi sayı **çok güncelleme ile uzlaşık yönün** birleştiği yerden geliyor.

Bu dersin hiçbir kurulumu önceki dersin 26,233'üne yaklaşmıyor. Politika eğimi burada eylem
değerinden daha zayıftır ve bunun yazılması gerekir: 36 sayılık doğrusal bir politika, gecikme
sınıflarını ağırlıklı toplayıp seçim yapabildiği kadarını öğrenir; ağın kurduğu eylem değeri ise
durumlar arasındaki doğrusal olmayan ayrımı da taşır.

#### Özet

- Politikayı doğrudan parametreleştirmek eylem değerini aradan çıkarır: her eyleme bir ağırlık
  satırı verilir, olasılıklar üssel normalleştirmeyle üretilir, ağırlıklar getiriyi artıracak yönde
  kaydırılır.
- Yön artık örneklenmiş bölümlerden kestirilir ve oynar: 120 bölümlük bütçe tek tek kullanıldığında
  120 ayrı yön çıkıyor, aralarındaki ortalama açı 86,1 derece ve uzlaşma oranı 0,290.
- Kestirimi uzlaştırmanın bir yolu bütçeyi bölmemektir — yirmi bölümde açı 44,0 dereceye, uzlaşma
  0,878'e geliyor — ama aynı bütçe altı güncellemeye düşüyor.
- Değer çizgisi eylemden bağımsız olduğu için kestirimin gittiği yeri kaydırmaz, yalnız oynamasını
  düşürür: taban çıkarmalı iki bölüm 0,511 uzlaşma veriyor, ham dört bölüm 0,508.
- Aynı 240 bölümlük bütçede iki bölümlük kestirim ham getiriyle 15,840 ve 19,962, taban çıkarmayla
  22,866 ve 22,190 veriyor; band 4,122'den 0,675'e iniyor. On bölümlük kestirimde taban çıkarmanın
  kazancı kalmıyor.

#### Sonraki Adım

Değer çizgisi bu derste sabit bir liste olarak kaldı: aynı politikanın ayrı bölümlerinden okunan
adım adım ortalama getiri. Durumu hiç kullanmıyor — otuzuncu adımdaki her durum için aynı sayıyı
veriyor, oysa bazı durumlar diğerlerinden açıkça daha iyidir. Sonraki ders çizgiyi **öğrenilen bir
değer fonksiyonuyla** değiştirir ve iki bileşeni birlikte eğitir: eylemi seçen taraf ile onu
değerlendiren taraf. Ölçülecek iki şey vardır — durumun kendi değerine göre okunan farkın kattığı
kazanç ve iki bileşenin birbirini bozduğu durum: değerlendiren taraf yanlış sayı verdiğinde seçen
tarafın nereye gittiği.

### Aktör–Eleştirmen Yöntemleri

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/aktor-elestirmen-yontemleri

Seçen ile değerlendiren bileşenin birlikte eğitilmesinin ve birbirini bozmasının ölçülmesi: 240 bölümlük aynı bütçede ham getiri 0,01 adımıyla 15,840 ve 19,962, sabit değer çizgisi 22,866 ve 22,190, öğrenilen eleştirmenin üstünlük kestirimi 21,154 ve 21,100 veriyor. Öğrenme adımı 0,06'ya çıkarıldığında tam getiriye dayanan iki yöntem bozuluyor — sabit değer çizgisinin bir koşumu 0,029 ile hiç ekip çıkarmama tabanına düşüyor, bandı 15,953 — üstünlük ise 23,401 ve 23,755 ile bandı 0,354'te tutuyor. Eleştirmenin öğrenme adımı 0,50'ye çıkarıldığında değerler 10 üzeri 102 mertebesine ıraksıyor, aktör tek bir eyleme yığılıyor, beş abonenin dördü hiç ziyaret edilmiyor ve getiri -18,772'ye iniyor.

Önceki ders taban çıkarmanın eğim kestirimini uzlaştırdığını ölçtü, ama çıkarılan taban kaba bir
listeydi: aynı politikanın ayrı bölümlerinden okunan adım adım ortalama getiri. Durumu hiç
kullanmıyordu. Otuzuncu adımda beş abonesi de bekleyen bir durumla hepsi yeni ziyaret edilmiş bir
durum aynı sayıyla karşılaştırılıyordu, oysa ikisi açıkça farklı yerlerdir.

Bu ders çizgiyi **öğrenilen bir durum değeriyle** değiştirir. Ortaya iki bileşenli bir düzen çıkar:
eylemi seçen **aktör** ve bulunulan durumun ne kadar değerli olduğunu söyleyen **eleştirmen**.
Aktörün güncellemesi artık getirinin kendisiyle değil, **üstünlükle** ölçeklenir — eylemin, o
durumda beklenene göre ne kadar iyi çıktığıyla. Ölçülecek iki şey vardır: üstünlük kestiriminin
kattığı fark ve iki bileşenin birbirini bozduğu durum.

- **PO57.** Ortam `01`'de kurulan kurgudur: beş abone, dönem başına tek ekip, ufuk 30, indirim
  katsayısı 0,9. Aktörün ağırlık dizisi `06`'daki gibi 36 sayıdır ve ölçüm aynı 300 bölümdedir.
- **PO58.** Eleştirmen aktörle aynı altı özniteliği kullanır ve tek bir ağırlık satırıdır — altı
  sayı. Doğrusal tutulmasının nedeni, bu dersin ölçtüğü şeyin eleştirmenin kapasitesi değil iki
  bileşenin **etkileşimi** olmasıdır.
- **PO59.** Üstünlük tek adımlıktır: alınan ödül, artı indirimli bir sonraki durumun değeri, eksi
  o anki durumun değeri. Bölümün son adımında bir sonraki durumun değeri sıfır alınır.
- **PO60.** Eleştirmen zaman farkı biçiminde güncellenir ve güncellemede aynı üstünlük kullanılır;
  yani aktör ile eleştirmen tek bir sayıyı paylaşır. Bu paylaşım, ikisinin birbirini bozabilmesinin
  de nedenidir.
- **PO61.** Üç yöntem aynı bütçeyle karşılaştırılır: 240 bölüm, iki bölümlük toplu iş, 120
  güncelleme. Değişen yalnız aktörün güncellemesini ölçekleyen sayıdır — ham getiri, sabit değer
  çizgisi çıkarılmış getiri, ya da üstünlük.
- **PO62.** Karşılaştırma **iki öğrenme adımında** yapılır: 0,01 ve 0,06. Tek bir adımda ölçmek
  yanıltıcıdır, çünkü yöntemler arasındaki asıl fark hangi adımın taşınabildiğidir.
- **PO63.** Eleştirmenin kendi öğrenme adımı ayrı bir ayar değişkenidir ve süpürülür; denenen
  aday sayısı dörttür.
- **PO64.** Her ölçü iki koşumla basılır; koşumlar arasında yalnız eğitim bölümlerinin tohumu
  değişir. Değerlendirme bölümleri her koşumda aynıdır.

#### Üstünlüğün Kattığı Fark

Aşağıdaki blok ortamı ve politikayı yeniden kurar, üç yöntemi tek bir eğitim yordamında toplar ve
iki öğrenme adımında ölçer. Üç yöntem yalnız `A` sayısında ayrılır.

```python
# aktor_elestirmen.py — Ayni KURGU ortam: bes abone, tek ekip, ufuk 30, indirim 0,9.
import math
import statistics

TOHUM, M32 = 20260218, 0xFFFFFFFF
ABONE, KAZANC, MALIYET = 5, (0, 3, 9), 2
KOTULESME = (0.30, 0.05, 0.60, 0.15, 0.45)
EYLEM, UFUK, INDIRIM = ABONE + 1, 30, 0.9
OZ = ABONE + 1                              # ozellik sayisi: bes gecikme sinifi + sabit


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


class Ortam:
    """Ariza derecesi d gizlidir; ajanin gordugu durum gecikme sinifidir."""

    def __init__(self, tohum):
        self.r = uretec(tohum)
        self.d, self.g = [0] * ABONE, [0] * ABONE
        for i in range(ABONE):
            self.g[i] = int(self.r() * 6)
            for _ in range(self.g[i]):
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1

    def durum(self):
        return tuple(min(2, x // 3) for x in self.g)

    def adim(self, a):
        odul = 0.0
        if a < ABONE:
            odul = KAZANC[self.d[a]] - MALIYET
            self.d[a] = 0
            self.g[a] = 0
        for i in range(ABONE):
            if i != a:
                self.g[i] += 1
                if self.d[i] < 2 and self.r() < KOTULESME[i]:
                    self.d[i] += 1
        return self.durum(), odul


def olasilik(th, s):                        # AKTOR: ussel normallestirme, 06'daki politika
    x = [v / 2.0 for v in s] + [1.0]
    z = [sum(t * v for t, v in zip(th[a], x)) for a in range(EYLEM)]
    e = [math.exp(v - max(z)) for v in z]
    T = sum(e)
    return [v / T for v in e], x


def deger(w, s):                            # ELESTIRMEN: durumun degeri, alti sayilik satir
    return sum(a * b for a, b in zip(w, [v / 2.0 for v in s] + [1.0]))


def sec(th, s, r):
    p, _ = olasilik(th, s)
    u, t = r(), 0.0
    for a in range(EYLEM):
        t += p[a]
        if u < t:
            return a
    return EYLEM - 1


def olc(politika, n=300):                   # 01'deki ayni 300 bolum
    g = []
    for k in range(n):
        o, r = Ortam(TOHUM + 1000 * k), uretec(TOHUM + 90000 + k)
        s, G, c = o.durum(), 0.0, 1.0
        for _ in range(UFUK):
            s2, od = o.adim(politika(s, r))
            G, c, s = G + c * od, c * INDIRIM, s2
        g.append(G)
    return statistics.mean(g)


def bolum_izi(th, tohum):
    o, r = Ortam(tohum), uretec(tohum + 5)
    s, iz = o.durum(), []
    for _ in range(UFUK):
        a = sec(th, s, r)
        s2, od = o.adim(a)
        iz.append((s, a, od))
        s = s2
    return iz


def getiriler(iz):
    G, g = [0.0] * len(iz), 0.0
    for t in range(len(iz) - 1, -1, -1):
        g = iz[t][2] + INDIRIM * g
        G[t] = g
    return G


def egit(tohum, kip, ogrenme, beta=0.05, butce=240, toplu=2):
    th, w, cizgi = [[0.0] * OZ for _ in range(EYLEM)], [0.0] * OZ, [0.0] * UFUK
    for t in range(butce // toplu):
        egm, egw, ortG = [0.0] * (EYLEM * OZ), [0.0] * OZ, [0.0] * UFUK
        for i in range(toplu):
            iz = bolum_izi(th, tohum + 1000 * t + i)
            G = getiriler(iz)
            for u, (s, a, od) in enumerate(iz):
                p, x = olasilik(th, s)
                if kip == "ham getiri":
                    A = G[u]
                elif kip == "sabit deger cizgisi":
                    A = G[u] - cizgi[u]
                else:                       # ogrenilen elestirmen: tek adimlik ustunluk
                    v2 = 0.0 if u == UFUK - 1 else deger(w, iz[u + 1][0])
                    A = od + INDIRIM * v2 - deger(w, s)
                    for j in range(OZ):
                        egw[j] += A * x[j] / toplu
                for b in range(EYLEM):
                    k = A * ((1.0 if b == a else 0.0) - p[b])
                    for j in range(OZ):
                        egm[b * OZ + j] += k * x[j] / toplu
            for u in range(UFUK):
                ortG[u] += G[u] / toplu
        cizgi = ortG
        for b in range(EYLEM):
            for j in range(OZ):
                th[b][j] += ogrenme * egm[b * OZ + j]
        for j in range(OZ):
            w[j] += beta * egw[j]
    return th, w


print(f"{'yontem':<22}{'adim 0,01':>20}{'adim 0,06':>20}")
SON = {}
for kip in ("ham getiri", "sabit deger cizgisi", "ogrenilen elestirmen"):
    sat = ""
    for ogr in (0.01, 0.06):
        g = [olc(lambda s, r, th=egit(TOHUM + 300000 + k * 7919, kip, ogr)[0]: sec(th, s, r))
             for k in (1, 2)]
        SON[(kip, ogr)] = g
        sat += f"{g[0]:>10.3f}{g[1]:>10.3f}"
    print(f"{kip:<22}{sat}")
for ogr in (0.01, 0.06):
    h, u = SON[("ham getiri", ogr)], SON[("ogrenilen elestirmen", ogr)]
    c = SON[("sabit deger cizgisi", ogr)]
    print(f"adim {ogr}: ustunlugun ham getiriye kattigi {min(u) - min(h):+.3f} / "
          f"{max(u) - max(h):+.3f}; band ham {max(h) - min(h):.3f}, "
          f"cizgi {max(c) - min(c):.3f}, ustunluk {max(u) - min(u):.3f}")
```

```
yontem                           adim 0,01           adim 0,06
ham getiri                15.840    19.962    14.207    17.218
sabit deger cizgisi       22.866    22.190     0.029    15.982
ogrenilen elestirmen      21.154    21.100    23.401    23.755
adim 0.01: ustunlugun ham getiriye kattigi +5.260 / +1.192; band ham 4.122, cizgi 0.675, ustunluk 0.053
adim 0.06: ustunlugun ham getiriye kattigi +9.194 / +6.537; band ham 3.011, cizgi 15.953, ustunluk 0.354
```

Tabloyu tek bir sütuna bakarak okumak yanıltır. Küçük adımda (0,01) en iyi sayıyı **sabit değer
çizgisi** veriyor: 22,866 ve 22,190. Öğrenilen eleştirmen 21,154 ve 21,100 ile onun altında kalıyor
ve `06`'nın bulgusu bozulmuyor — kaba bir taban da bu ortamda işini görüyor. Üstünlüğün ham
getiriye kattığı fark yine de belirgin: +5,260 ve +1,192.

Ayrım büyük adımda ortaya çıkıyor. Öğrenme adımı 0,06'ya çıkarıldığında tam getiriye dayanan iki
yöntem de bozuluyor: ham getiri 14,207 ve 17,218'e iniyor, sabit değer çizgisinin bir koşumu
**0,029** veriyor — yani hiç ekip çıkarmama tabanının üstünde neredeyse hiçbir şey — ve bandı
15,953'e çıkıyor. Üstünlük kestirimi aynı adımda 23,401 ve 23,755 veriyor ve bandı 0,354'te
kalıyor.

Üstünlüğün asıl kattığı budur. Düzeyi biraz yükseltmiyor, **oynamayı** düşürüyor; oynama düştüğü
için daha büyük bir öğrenme adımı taşınabiliyor; ve büyük adım aynı bütçeden daha fazla ilerleme
çıkarıyor. Tam getiri otuz adımın toplamıdır ve otuz adımın gürültüsünü taşır; tek adımlık
üstünlük yalnız bir ödül ile iki değer kestirimini taşır.

Bu takas bedelsiz değildir. Tam getiri, ne kadar oynarsa oynasın, gerçekten yaşanmış bir sonuçtur.
Tek adımlık üstünlük ise otuz adımın yirmi dokuzunu eleştirmenin kestirimine devreder. Eleştirmen
yanılıyorsa üstünlük yalnız oynak değil **sistemli biçimde yanlış** olur ve yanlışlık her koşumda
aynı yöne bakar. Yukarıdaki tabloda bu görünmüyor, çünkü eleştirmen çalışıyor. Bir sonraki blok
onu çalışmaz hale getirir.

#### Eleştirmen Bozulduğunda

İki bileşen tek bir sayıyı paylaşıyor: aynı üstünlük hem aktörü hem eleştirmeni güncelliyor.
Eleştirmen yanlış bir değer verdiğinde aktör yanlış yönde kayar; aktör kaydığında eleştirmenin
öğrenmeye çalıştığı dağılım da değişir. Aşağıdaki blok eleştirmenin öğrenme adımını süpürerek
bu döngünün nerede koptuğunu sayar.

```python
ETIKET = ("A1", "A2", "A3", "A4", "A5", "cikma")
HEPSI = [(a, b, c, e, f) for a in range(3) for b in range(3) for c in range(3)
         for e in range(3) for f in range(3)]


def eylem_dagilimi(th, n=300):
    say = [0] * EYLEM
    for k in range(n):
        o, r = Ortam(TOHUM + 1000 * k), uretec(TOHUM + 90000 + k)
        s = o.durum()
        for _ in range(UFUK):
            a = sec(th, s, r)
            say[a] += 1
            s = o.adim(a)[0]
    return say


print(f"{'elestirmen adimi':>17}{'kosum 1':>9}{'kosum 2':>9}{'|V| ortalama':>14}"
      f"{'en cok secilen':>16}{'hic gidilmeyen':>15}")
for beta in (0.00, 0.05, 0.50, 2.00):
    g = []
    for k in (1, 2):
        th, w = egit(TOHUM + 300000 + k * 7919, "ogrenilen elestirmen", 0.06, beta)
        g.append(olc(lambda s, r: sec(th, s, r)))
        if k == 1:
            d = eylem_dagilimi(th)
            vo = statistics.mean(abs(deger(w, s)) for s in HEPSI)
    t, en = sum(d), d.index(max(d))
    print(f"{beta:>17.2f}{g[0]:>9.3f}{g[1]:>9.3f}{vo:>14.3g}"
          f"{ETIKET[en] + ' ' + format(max(d) / t, '.3f'):>16}"
          f"{sum(x == 0 for x in d[:ABONE]):>15}")
```

```
 elestirmen adimi  kosum 1  kosum 2  |V| ortalama  en cok secilen hic gidilmeyen
             0.00   22.506   22.620             0        A3 0.305              0
             0.05   23.401   23.755          22.2        A3 0.258              0
             0.50  -16.492  -18.772      2.6e+102        A1 1.000              4
             2.00  -15.332  -14.622     5.84e+180        A5 1.000              4
```

İlk satır eleştirmensiz haldir: adım sıfır olduğu için değerler sıfırda kalır ve üstünlük tek
adımlık ödüle iner. Sonuç 22,506 ve 22,620 — kötü değil, çünkü bu ortamda ödül çoğunlukla hemen
gelir. Eleştirmen çalıştığında (0,05) sayı 23,401 ve 23,755'e çıkıyor; eleştirmenin ürettiği
ortalama değer 22,2, yani ölçülen getirinin mertebesinde. Eleştirmenin kazandırdığı yaklaşık bir
birimdir ve ölçülmeden yazılmamalıdır.

Son iki satır kopmayı gösteriyor. Eleştirmenin adımı 0,50'ye çıkarıldığında değerler $10^{102}$
mertebesine ıraksıyor. Üstünlük artık ödülü değil eleştirmenin kendi hatasını taşıyor; işareti
durumdan bağımsız biçimde sabitleniyor ve aktörün bütün olasılığı **tek bir eyleme** kayıyor:
`A1` payı 1,000. Beş abonenin **dördü hiç ziyaret edilmiyor**. Getiri −18,772 — yalnız kötü değil,
hiç ekip çıkarmamanın 0,000'inin de altında, çünkü her dönem ekip maliyeti ödenip karşılığında
neredeyse hiçbir şey alınmıyor. Adım 2,00'de aynı çöküş başka bir aboneye, `A5`'e oluyor; hangi
eyleme yığıldığı bile bir sonuç değil, kazadır.

Bu, kursun kuralının en keskin biçimidir. Getiri sütununa bakan biri iki koşumun da kötü olduğunu
görür ama nedenini bilemez. Dördüncü sütun — **hiç gidilmeyen abone sayısı** — nedeni doğrudan
söylüyor: politika kendi verisini topluyor, tek bir eyleme yığıldığında dört abone hakkında bir
daha hiçbir şey öğrenilmiyor ve eleştirmen o dört abonenin durumlarını hiçbir zaman doğru
kestiremiyor. Toplanan veriyi yazmayan sistem ölçülmemiş sayılır.

#### Özet

- Aktör–eleştirmen düzeninde aktör eylemi seçer, eleştirmen durumun değerini söyler ve aktörün
  güncellemesi getiriyle değil **üstünlükle** ölçeklenir: ödül artı indirimli sonraki durum değeri
  eksi o anki durum değeri.
- Aynı 240 bölümlük bütçede, 0,01 adımıyla ham getiri 15,840 ve 19,962, sabit değer çizgisi 22,866
  ve 22,190, üstünlük 21,154 ve 21,100 veriyor. Kaba bir taban bu adımda öğrenilen eleştirmenden
  geride kalmıyor.
- Ayrım öğrenme adımı 0,06'ya çıkınca görünüyor: sabit değer çizgisinin bir koşumu 0,029 ile hiç
  ekip çıkarmama tabanına düşüyor ve bandı 15,953 oluyor, üstünlük ise 23,401 ve 23,755 ile bandı
  0,354'te tutuyor. Üstünlüğün kattığı düzey değil kararlılıktır ve kararlılık daha büyük adımı
  taşınabilir kılar.
- Eleştirmenin kendi öğrenme adımı bir ayar değişkenidir: dört aday denendi, 0,05 en iyisini verdi
  ve eleştirmensiz hale göre yaklaşık bir birim kattı.
- Eleştirmenin adımı 0,50'ye çıkarıldığında değerler $10^{102}$ mertebesine ıraksıyor, aktörün
  bütün olasılığı tek bir eyleme yığılıyor, beş abonenin dördü hiç ziyaret edilmiyor ve getiri
  −18,772 ile eylemsizliğin bile altına düşüyor.

#### Sonraki Adım

Yedi derste ajan kendi eylemini seçti ve sonucunu doğrudan ortamdan öğrendi: ekibi gönderdiği
aboneden bir ödül geldi, göndermediği aboneler bozulmaya devam etti. Karşılık her adımda,
seçimin hemen ardından geldi ve seçim bir sonraki durumu değiştirdi. Sayılar aynı iki taban
çizgisinin üstüne yazıldı — hiç ekip çıkarmamak 0,000 ve rastgele eylem 10,432 — ve kural
politikasının 20,621'i geçilmesi gereken eşik oldu: `05`'in çizelgesi 24,495, aynı dersin tekrar
tamponlu ağı 26,233, bu dersin üstünlük kestirimli aktörü 23,755. Ölçünün üçüncü sayısı hep aynı biçimdeydi — hangi durumlar hiç
görülmedi, hangi eylem hiç denenmedi.

Sonraki konu aynı döngüyü bir yerinden kırıyor. Eylem artık ajanın **yaptığı** bir şey değil, bir
insana **sunulan bir seçenektir**: bir aboneye hangi hizmet kaleminin önerileceği. Öneri
gösterilir, ama sonucu ancak o insan seçerse görülür — gösterilmeyen kalem hiçbir zaman
puanlanmaz, puanlanmayan kalem de bir daha gösterilmez. Toplanan veri artık ajanın gezdiği
durumlar değil, **gösterdiği ve gösterilmediği için hiç öğrenilmeyen** kalemlerdir. Sonraki ders
bu düzenin tabanını kurar ve alanın şaşırtıcı biçimde yüksek taban çizgisini ölçer: herkese en çok
kullanılan kalemi önermek.

## Öneri Sistemleri

Gösterilmeyen kalemin puanlanmadığı, puanlanmayan kalemin gösterilmediği bir düzende önerinin ölçülmesi: seyrek abone-kalem dizeyinde en popüler kalemi herkese önermenin şaşırtıcı yüksekliği ve örtük ile açık geri bildirimin farkı, içerik temelli önerinin hiç öneri üretemediği abone sayısı ve önerilerin birbirine benzemesi, komşu sayısının oynattığı sıralama ile seyreklik arttıkça komşu bulunamayan abone, gözlenmeyen hücrenin sıfır sayılmasıyla atlanmasının iki ayrı sıralama vermesi, kaç abone ve kalemin soğuk olduğu ve her çözümün kaçını ısıttığı ile bedeli, çevrimdışı iyi görünen sıralamanın kendi kararlarıyla veri toplayınca başka sayı vermesi, sistem kendi önerileriyle beslendiğinde hiç gösterilmez olan kalem sayısı ve popülerlik yanlılığının büyümesi.

### Öneri Problemi

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/oneri-problemi

Kurgu abone-kalem etkileşim dizeyinde önerinin ölçülmesi: 360 abone ve 24 hizmet kalemi 8.640 hücrenin yalnız 577'sinde buluşur, seyreklik 0,9332'dir ve 80 abonenin hiçbir etkileşimi yoktur. Taban çizgisi eğitimde en çok seçilen kalemi herkese sunmaktır; ilk beş isabeti 0,6527, rastgele sıranınki 0,2036'dır. Aynı veri örtük okunduğunda ve açık puanla okunduğunda ilk beş kalemin yalnız biri ortaktır, ortalama puana göre öneri 0,1497 isabet verir, 24 kalemin 16'sının on taneden az puanı vardır ve hiç gösterilmemiş 5.638 hücrenin puan sayısı sıfırdır.

Yedi derste ajan kendi eylemini seçti ve sonucunu ortamdan öğrendi: bir aboneye ekip gönderilince
ne olduğu, gönderilmeyince ne olduğu adım adım kayda geçti. Şimdi eylem el değiştiriyor. Sistemin
yapabildiği tek şey bir aboneye **bir seçenek sunmak** ve sonucu ancak o abone seçerse görünür.
Sunulmayan seçeneğin sonucu hiç doğmaz; sunulan ama seçilmeyen seçeneğin niçin seçilmediği kayda
geçmez. Toplanan veri, sistemin kendi sunum kararlarının izidir.

Bu ders o düzenin verisini kurar ve iki sayı üretir: herkese aynı kalemi sunan tabanın ilk beş
isabeti ve aynı veriyi örtük ile açık okumanın ürettiği iki sıralamanın ne kadar ayrıldığı.
Kursun kuralı burada ilk kez ödenir: **gösterilmeyen kalem puanlanmaz, puanlanmayan kalem
gösterilmez.**

- **ON1.** Küme **kurgudur**, tohum 20260218: 360 abone, 24 hizmet kalemi, 8.640 hücre. Kalemler
  bölgesel ölçüm ağının hizmet başvurularıdır (tarife değişikliği, sayaç değişimi, taksitlendirme,
  uzaktan okuma başvurusu gibi).
- **ON2.** Üç olay ayrı ayrı üretilir: **gösterim**, **seçim**, **puan**. Bir kalem gösterilmeden
  seçilemez, seçilmeden puanlanamaz. Zincir kurgunun içine bilerek konmuştur.
- **ON3.** Abonenin bir kaleme gerçek eğilimi üretecin içindedir ve **yalnız ölçüm için** okunur;
  hiçbir yöntem onu görmez.
- **ON4.** **Örtük geri bildirim** seçimin kendisidir ve 0/1'dir. **Açık geri bildirim** 1 ile 5
  arasında bir puandır ve yalnız seçimlerin bir kısmında bırakılır.
- **ON5.** Geçmişte hangi kalemin kime gösterildiği **payına göre yanlıdır**: sık başvurulan kalem
  çok, seyrek olan az sunulmuştur. Bu yanlılık kurguya bilerek konmuştur.
- **ON6.** Ayrılmış küme: en az iki etkileşimi olan her abonenin **son** etkileşimi. Kalanı eğitim
  kümesidir.
- **ON7.** Ölçüt **ilk k isabetidir**: abonenin ayrılmış kalemi ilk k öneri içinde mi. Kesinlik ve
  duyarlılık tanımları M27/K03'te kuruldu ve burada tekrarlanmaz.
- **ON8.** İki taban çizgisi kurulur: **rastgele sıra** ve **eğitimde en çok seçilen kalem sırası**.
  İkincisi asıl tabandır, çünkü hiçbir kişiselleştirme içermez ve yine de kataloğun eğrisinden
  beslenir.

#### Etkileşim Dizeyi ve Seyreklik

Dizeyin satırı abone, sütunu hizmet kalemidir. Bir hücrenin dolu olması abonenin o kalemi
seçtiğini söyler; boş olması iki ayrı şeyin karışımıdır ve ayırt edilemez: kalem sunulmuş ama
seçilmemiş olabilir, ya da hiç sunulmamış olabilir. Kurgu bu ikisini ayrı ayrı üretir, böylece
ölçüm sırasında hangisinin ne kadar olduğu sayılabilir.

```python
# ORTAK -- KURGUDUR. Abone x hizmet kalemi etkilesim dizeyi ders icinde kurulur.
# Gosterim, secim ve puan ayri ayri uretilir: bir kalem once GOSTERILIR, sonra
# secilebilir, ancak secilirse puanlanabilir.
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


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {  # kesimin alanlara egilimi; yalniz uretecin icinde kullanilir
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]   # kalemin kendi doyuruculugu

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, GOSTERIM, ETKILESIM, PUAN, EGILIM = [], {}, {}, {}, {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        EGILIM[(a, j)] = egilim
        if r() < min(0.95, pay ** 0.55 * 0.72):        # sunulmus mu
            GOSTERIM[(a, j)] = 1
            if r() < egilim:                            # secilmis mi
                ETKILESIM[(a, j)] = r()                 # deger: sira damgasi
                if r() < 0.32:                          # puan birakmis mi
                    ham = (0.16 + 0.50 * KESIM[kesim][alan] / 1.9
                           + 0.42 * KALITE[j] + 0.26 * (r() - 0.5))
                    PUAN[(a, j)] = max(1, min(5, int(round(ham * 5))))
        else:
            r(), r(), r()

HUCRE = N_ABONE * N_KALEM
sayi = {}
for (a, j) in ETKILESIM:
    sayi[a] = sayi.get(a, 0) + 1
print("dizey", N_ABONE, "abone x", N_KALEM, "kalem =", HUCRE, "hucre")
for ad, d in (("gosterim", GOSTERIM), ("etkilesim", ETKILESIM), ("puan", PUAN)):
    print(f"  {ad:10s} {len(d):5d}  doluluk {len(d)/HUCRE:.4f}  seyreklik {1-len(d)/HUCRE:.4f}")
print("hic etkilesimi olmayan abone", N_ABONE - len(sayi),
      " abone basina ortalama", round(len(ETKILESIM) / N_ABONE, 3),
      " en cok", max(sayi.values()))
```

```
dizey 360 abone x 24 kalem = 8640 hucre
  gosterim    3002  doluluk 0.3475  seyreklik 0.6525
  etkilesim    577  doluluk 0.0668  seyreklik 0.9332
  puan         218  doluluk 0.0252  seyreklik 0.9748
hic etkilesimi olmayan abone 80  abone basina ortalama 1.603  en cok 7
```

Üç doluluk oranı üç ayrı problemi tarif ediyor. Gösterim dizeyi 0,3475 dolu, seçim dizeyi 0,0668,
puan dizeyi 0,0252. Seyreklik seçim düzeyinde 0,9332'dir: her yüz hücrenin altmış yedisi
sunulmuş ama seçilmemiş, yirmi altısı hiç sunulmamıştır. Abone başına ortalama 1,603 etkileşim
düşer ve 360 abonenin 80'inin hiçbir etkileşimi yoktur. Bu tabloda bir aboneye ne önerileceğine
karar vermek, satırların çoğunun neredeyse tümüyle boş olduğu bir dizeyden karar çıkarmaktır.

#### Herkese Aynı Kalemi Sunmak

Öneri alanının tabanı kişiselleştirmesiz bir yordamdır: eğitim kümesinde en çok seçilmiş kalemleri
sıraya dizip herkese aynı listeyi sunmak. Bu taban hiçbir aboneyi diğerinden ayırt etmez, yine de
kataloğun eğriliğinden beslenir ve şaşırtıcı biçimde yükseğe çıkar. Yanına ikinci bir taban
konur: abonenin görmediği kalemleri rastgele sıraya dizmek.

```python
# Ayirma: en az iki etkilesimi olan her abonenin SON etkilesimi ayrilmis kumeye.
EGIT, AYRI, GORULEN = {}, {}, {}
kayit = {}
for (a, j), damga in ETKILESIM.items():
    kayit.setdefault(a, []).append((damga, j))
for a, lst in kayit.items():
    lst.sort()
    if len(lst) >= 2:
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN.setdefault(a, set()).add(j)

pay = [0] * N_KALEM
for (a, j) in EGIT:
    pay[j] += 1
sirali = sorted(range(N_KALEM), key=lambda j: -pay[j])


def isabet(oner, K):
    return sum(1 for a in AYRI if AYRI[a] in oner(a)[:K]) / len(AYRI)


def taban_populerlik(a):
    return [j for j in sirali if j not in GORULEN.get(a, set())]


rg = uretec(TOHUM + 31)


def taban_rastgele(a):
    aday = [j for j in range(N_KALEM) if j not in GORULEN.get(a, set())]
    for i in range(len(aday) - 1, 0, -1):
        t = int(rg() * (i + 1))
        aday[i], aday[t] = aday[t], aday[i]
    return aday


print("egitim", len(EGIT), "etkilesim / ayrilmis", len(AYRI), "abone")
print("egitimde en cok secilen bes kalem:")
for j in sirali[:5]:
    print(f"  {KALEM[j][0]:26s} {pay[j]:3d}")
for K in (3, 5):
    p, s = isabet(taban_populerlik, K), isabet(taban_rastgele, K)
    print(f"ilk {K}: populerlik {p:.4f}  rastgele {s:.4f}  oran {p/s:.2f}")
print("populerlik tabaninin katalog kapsamasi",
      len({j for a in AYRI for j in taban_populerlik(a)[:5]}), "/", N_KALEM)
```

```
egitim 410 etkilesim / ayrilmis 167 abone
egitimde en cok secilen bes kalem:
  odeme_hatirlatma_sms        68
  e_fatura_gecisi             64
  otomatik_odeme_talimati     47
  taksitlendirme              39
  borc_yapilandirma           27
ilk 3: populerlik 0.5509  rastgele 0.1437  oran 3.83
ilk 5: populerlik 0.6527  rastgele 0.2036  oran 3.21
populerlik tabaninin katalog kapsamasi 9 / 24
```

Kişiselleştirmesiz taban, 167 abonenin 109'unun ayrılmış kalemini ilk beşte yakalıyor: isabet
0,6527. Rastgele sıra aynı ölçüde 0,2036'da kalıyor, aradaki oran 3,21. İlk üçe inildiğinde
taban 0,5509 veriyor ve oran 3,83'e çıkıyor. Bu sayı bu kursun geri kalanı için bir eşik: bir
öneri yöntemi 0,6527'yi geçmiyorsa, hiçbir abone ayrımı yapmayan bir listeden daha iyi değildir.

Tabanın ödediği bedel dördüncü satırda: 167 abonenin ilk beş listelerinde toplam **9 ayrı kalem**
geçiyor, katalogda 24 kalem var. On beş kalem hiçbir aboneye hiç önerilmiyor. Taban yüksek isabeti
kataloğun on beş kalemini tümüyle görünmez kılarak alıyor ve görünmeyen kalem bir sonraki dönemin
verisine de girmiyor.

#### Örtük ile Açık Geri Bildirim

Örtük geri bildirim seçimin kendisidir: abone başvurdu ya da başvurmadı. Açık geri bildirim
abonenin bıraktığı puandır. İkisi aynı kataloğu ölçüyor gibi görünür, ama farklı şeyleri sayarlar.
Örtük sinyal **hacmi** ölçer ve gösterim sayısını içinde taşır; açık sinyal **seçtikten sonraki
doyumu** ölçer ve yalnız seçenlerin bir kısmından gelir.

```python
gos, sec, adet, toplam = ([0] * N_KALEM for _ in range(4))
for (a, j) in GOSTERIM:
    gos[j] += 1
for (a, j) in ETKILESIM:
    sec[j] += 1
for (a, j), p in PUAN.items():
    adet[j] += 1
    toplam[j] += p
ort = [toplam[j] / adet[j] if adet[j] else 0.0 for j in range(N_KALEM)]

s_ortuk = sorted(range(N_KALEM), key=lambda j: -sec[j])
s_acik = sorted([j for j in range(N_KALEM) if adet[j] > 0], key=lambda j: -ort[j])
print(f"{'kalem':26s} {'gosterim':>8s} {'secim':>6s} {'puan':>5s} {'ortalama':>9s}")
for j in s_ortuk[:3] + s_acik[:3]:
    print(f"{KALEM[j][0]:26s} {gos[j]:8d} {sec[j]:6d} {adet[j]:5d} {ort[j]:9.2f}")
print("ortuk ilk 5 ile acik ilk 5 kesisimi", len(set(s_ortuk[:5]) & set(s_acik[:5])))
print("hic secilmemis kalem", sum(1 for j in range(N_KALEM) if sec[j] == 0),
      " on tane puandan az kalem", sum(1 for j in range(N_KALEM) if adet[j] < 10))
print("hic gosterilmemis hucre", HUCRE - len(GOSTERIM),
      " bunlarin puan sayisi", sum(1 for k in PUAN if k not in GOSTERIM))
print("hic puan birakmamis abone", N_ABONE - len({a for (a, j) in PUAN}))
s_puan = sorted(range(N_KALEM), key=lambda j: -ort[j])
print(f"ilk 5 isabet -- ortalama puana gore {isabet(lambda a: [j for j in s_puan if j not in GORULEN.get(a, set())], 5):.4f}")
```

```
kalem                      gosterim  secim  puan  ortalama
e_fatura_gecisi                 215     93    30      3.27
odeme_hatirlatma_sms            236     89    31      3.06
otomatik_odeme_talimati         242     73    24      3.83
adres_guncelleme                158     37    14      4.79
ikinci_sayac_talebi              96      9     3      4.33
isyeri_tarifesine_gecis          91      5     4      4.25
ortuk ilk 5 ile acik ilk 5 kesisimi 1
hic secilmemis kalem 1  on tane puandan az kalem 16
hic gosterilmemis hucre 5638  bunlarin puan sayisi 0
hic puan birakmamis abone 205
ilk 5 isabet -- ortalama puana gore 0.1497
```

İki sıralamanın ilk beşinde ortak olan kalem sayısı **1**. Örtük sıralamanın başındaki üç kalem
ikişer yüzü aşan gösterim almış ve ortalama puanları 3,06 ile 3,83 arasında; açık sıralamanın
başındaki kalemlerden ikisinin puan sayısı 3 ve 4. Ortalama puana göre öneri yapmak ilk beş
isabetini 0,1497'ye düşürüyor, yani rastgele sıranın 0,2036'sının bile altına.

Sayının kaynağı üçüncü ve dördüncü satırda duruyor. Kataloğun 24 kaleminin 16'sının on taneden az
puanı var, çünkü az gösterilen kalem az seçiliyor ve az seçilen kalem az puanlanıyor. Hiç
gösterilmemiş 5.638 hücrenin puan sayısı sıfır; bu bir bulgu değil, zincirin tanımı. Abonelerin
205'i hiç puan bırakmamış. Açık geri bildirim daha zengin bir sinyal taşır, ama taşıdığı yerde
taşır: sistemin daha önce gösterdiği kalemlerde. **Gösterilmeyen kalem puanlanmaz** ve ortalama
puanla sıralanan bir liste kataloğun görünmeyen kısmı hakkında hiçbir şey söylemez.

Toplanan veri bu dersin üçüncü sayısıdır ve iki parçadan oluşur: hiç gösterilmemiş 5.638 hücre
ve tabanın önerdiği listelerin dışında kalan 15 kalem. Bir öneri yöntemi ölçülürken bu iki sayı
yazılmıyorsa, ölçünün hangi kataloğa ait olduğu bilinmez.

#### Özet

- Kurgu dizey 360 abone ile 24 hizmet kalemini 8.640 hücrede buluşturur; gösterim doluluğu 0,3475,
  seçim 0,0668, puan 0,0252'dir. Seçim düzeyinde seyreklik 0,9332 ve 80 abonenin hiç etkileşimi
  yoktur.
- Taban çizgisi eğitimde en çok seçilen kalemleri herkese aynı sırayla sunmaktır; ilk beş isabeti
  0,6527, ilk üç isabeti 0,5509'dur. Rastgele sıra aynı ölçüde 0,2036 ve 0,1437 verir.
- Tabanın bedeli kapsamadır: 167 abonenin ilk beş listelerinde yalnız 9 ayrı kalem geçer, on beş
  kalem hiçbir aboneye önerilmez.
- Örtük ve açık geri bildirim aynı katalogda iki ayrı sıralama üretir; ilk beşlerinin kesişimi
  birdir ve ortalama puana göre öneri 0,1497 isabet verir.
- Açık sinyal seyrektir ve gösterime bağımlıdır: 24 kalemin 16'sının on taneden az puanı vardır,
  205 abone hiç puan bırakmamıştır, hiç gösterilmemiş 5.638 hücrenin puan sayısı sıfırdır.

#### Sonraki Adım

Taban 0,6527 ile bir eşik koydu, ama bunu kataloğun on beş kalemini hiç göstermeyerek yaptı ve
hiç etkileşimi olmayan 80 aboneye söyleyecek ayrı bir sözü yok. İlk soru şu: aboneden hiç veri
gelmemişken bile kalemin kendisi hakkında bilinen bir şey var mı. Katalogda her kalemin alanı,
kanalı, ücretli olup olmadığı, taahhüt ve randevu gerektirip gerektirmediği yazılı. Bir sonraki
ders bu özniteliklerden abone başına bir profil çıkarır, önerileri profile benzerlikle sıralar ve
iki sayıyı ölçer: kaç abone için hiç öneri üretilemiyor ve üretilen öneriler birbirine ne kadar
benziyor.

### İçerik Temelli Öneri

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/icerik-temelli-oneri

Kalemin özniteliklerinden abone profili çıkaran önerinin ölçülmesi: on bir bileşenli ikili öznitelik vektörüyle 360 abonenin 280'ine profil kurulur, 80'i için hiç öneri üretilemez. İlk beş isabet 0,4431 ile 0,6527'lik popülerlik tabanının altında kalır, ama katalog kapsaması 9 yerine 24'tür. Bedeli benzeşmedir: ilk beş listenin iç benzerliği 0,6769 ve listedeki kalemlerin 0,715'i tek bir alandan gelir. Hiç gösterilmemiş yeni bir kalem 280 abonenin tamamında sıralanır, 191'inin ilk onuna girer ve ortalama konumu 25 kalem içinde 11,55'tir; aynı kalem popülerlik sıralamasında sonuncudur.

Önceki dersin tabanı ilk beş isabetini 0,6527'ye çıkardı, ama bunu kataloğun 24 kaleminden
yalnız 9'unu göstererek yaptı ve hiç etkileşimi olmayan 80 aboneye herkese söylediğinin aynısını
söyledi. İki eksik de aynı yerden geliyor: taban yalnız **etkileşim sayılarına** bakıyor,
kalemin ne olduğuna bakmıyor.

Katalogda kalem başına yazılı bilgi var: hangi alana ait olduğu, hangi kanaldan yürüdüğü, ücretli
olup olmadığı, taahhüt ve randevu gerektirip gerektirmediği. Bu ders o sütunlardan bir benzerlik
kurar, aboneyi seçtiği kalemlerin ortalaması olarak tarif eder ve iki sayıyı ölçer: **kaç abone
için hiç öneri üretilemiyor** ve **üretilen öneriler birbirine ne kadar benziyor**.

- **ON9.** Kurgu ve tohum önceki dersle aynıdır; dizey, ayırma ve popülerlik tabanı aynen yeniden
  kurulur ve aynı sayıları verir.
- **ON10.** Öznitelik vektörü **on bir bileşenlidir**: beş alan, üç kanal ve üç ikili bayrak.
  Bileşenler 0/1'dir ve vektör birim uzunluğa getirilir.
- **ON11.** Abone profili, eğitim kümesinde **seçtiği** kalemlerin öznitelik vektörlerinin
  toplamının birim uzunluğa getirilmiş halidir. Puanlar kullanılmaz; profil örtük sinyalden gelir.
- **ON12.** İki birim vektörün benzerliği iç çarpımlarıdır ve 0 ile 1 arasındadır. Eşitlik
  durumunda katalog sırası bozar; kayan noktalı değerler doğrudan karşılaştırılmaz, sıralama
  anahtarı olarak kullanılır.
- **ON13.** Kapsama **iki ayrı sayıdır**: kaç aboneye öneri üretilebildiği ve kaç ayrı kalemin
  en az bir abonenin listesine girdiği.
- **ON14.** Bir listenin **iç benzerliği**, listedeki bütün kalem ikililerinin ortalama
  benzerliğidir.
- **ON15.** Sonda katalogda yeni bir kalem vardır: öznitelikleri yazılıdır, hiç gösterilmemiştir
  ve hiçbir etkileşimi yoktur.

#### Dizeyi Yeniden Kurmak

Ölçüm aynı veri üstünde yapılmazsa iki dersin sayıları karşılaştırılamaz. Aşağıdaki blok kurguyu,
ayırmayı ve popülerlik tabanını aynı tohumla yeniden kurar; iki satırlık çıktısı önceki dersin
seyreklik ve isabet sayılarıyla birebir aynı olmalıdır.

```python
# ORTAK -- KURGUDUR. Onceki dersin dizeyi ayni tohumla yeniden kurulur; bu kez
# katalogun oznitelik sutunlari da okunacak.
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


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, ETKILESIM = [], {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        if r() < min(0.95, pay ** 0.55 * 0.72):     # gosterildi mi
            if r() < egilim:                         # secildi mi
                ETKILESIM[(a, j)] = r()
                if r() < 0.32:
                    r()                              # puan cekimi: burada okunmuyor
        else:
            r(), r(), r()

EGIT, AYRI, GORULEN, kayit = {}, {}, {}, {}
for (a, j), damga in ETKILESIM.items():
    kayit.setdefault(a, []).append((damga, j))
for a, lst in kayit.items():
    lst.sort()
    if len(lst) >= 2:
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN.setdefault(a, set()).add(j)
pay = [0] * N_KALEM
for (a, j) in EGIT:
    pay[j] += 1
sirali = sorted(range(N_KALEM), key=lambda j: -pay[j])


def isabet(oner, K):
    return sum(1 for a in AYRI if AYRI[a] in oner(a)[:K]) / len(AYRI)


def taban(a):
    return [j for j in sirali if j not in GORULEN.get(a, set())]


print("etkilesim", len(ETKILESIM), " seyreklik",
      round(1 - len(ETKILESIM) / (N_ABONE * N_KALEM), 4))
print("ayrilmis abone", len(AYRI), " taban ilk 5 isabet", round(isabet(taban, 5), 4))
```

```
etkilesim 577  seyreklik 0.9332
ayrilmis abone 167  taban ilk 5 isabet 0.6527
```

İki satır önceki dersle birebir aynı: 577 etkileşim, 0,9332 seyreklik, 167 ayrılmış abone ve
0,6527 taban isabeti. Bu tekrar bir tören değil, ölçümün ön koşulu: iki yöntemin sayısı ancak
aynı ayırma, aynı ayrılmış küme ve aynı taban üstünde okunduğunda karşılaştırılabilir. Kurgu
aynı tohumdan yeniden üretildiği için aradaki her fark yöntemden gelir.

#### Öznitelikten Profile

Her kalem on bir bileşenli bir vektöre dönüşür ve birim uzunluğa getirilir; böylece çok
özniteliği olan kalem yalnız bu yüzden öne çıkmaz. Ücretli, taahhütlü ve randevulu bir saha
kalemi altı bileşeninde 1 taşır, ücretsiz bir çevrimiçi kalem yalnız ikisinde; ölçekleme
olmasaydı birincisi her profile daha yakın görünürdü. Bileşenler ikili olduğu için çok sayıda
eşit benzerlik çıkar ve eşitlik katalog sırasıyla bozulur; bu keyfi bir kuraldır ve sonucu
koşumdan koşuma sabit tutmak için yazılmıştır.

Abonenin profili, seçtiği kalemlerin vektörlerinin toplamıdır: iki saha başvurusu yapmış bir
abonenin profili saha yönünde uzar. Öneri, profile en yakın ve daha önce seçilmemiş kalemleri
sıralamaktır. Abonenin hiç seçimi yoksa toplanacak vektör yoktur ve profil kurulamaz.

```python
import math

ALAN = ["odeme", "olcum", "sozlesme", "bildirim", "tesisat"]
KANAL = ["cevrimici", "saha", "sube"]


def oznitelik(k):
    v = [1.0 if k[1] == x else 0.0 for x in ALAN]
    v += [1.0 if k[2] == x else 0.0 for x in KANAL]
    v += [float(k[3]), float(k[4]), float(k[5])]
    n = math.sqrt(sum(x * x for x in v))
    return [x / n for x in v]


def benzerlik(u, v):
    return sum(x * y for x, y in zip(u, v))


OZ = [oznitelik(k) for k in KALEM]
BOYUT = len(OZ[0])

PROFIL = {}
for a in ABONE:
    gor = GORULEN.get(a, set())
    if not gor:
        continue                                   # profil kurulamaz
    p = [sum(OZ[j][d] for j in gor) for d in range(BOYUT)]
    n = math.sqrt(sum(x * x for x in p))
    PROFIL[a] = [x / n for x in p]


def icerik(a):
    if a not in PROFIL:
        return []                                  # oneri uretilemiyor
    return sorted((j for j in range(len(OZ)) if j not in GORULEN.get(a, set())),
                  key=lambda j: (-benzerlik(PROFIL[a], OZ[j]), j))


def kapsama(oner, K=5):
    return len({j for a in AYRI for j in oner(a)[:K]})


def ic_benzerlik(oner, K=5):
    t = n = 0
    for a in AYRI:
        lst = oner(a)[:K]
        for x in range(len(lst)):
            for y in range(x + 1, len(lst)):
                t += benzerlik(OZ[lst[x]], OZ[lst[y]])
                n += 1
    return t / n


print("profil kurulan abone", len(PROFIL), "/", N_ABONE,
      " kurulamayan", N_ABONE - len(PROFIL))
for K in (3, 5):
    print(f"ilk {K}: icerik {isabet(icerik, K):.4f}  taban {isabet(taban, K):.4f}")
print("katalog kapsamasi   icerik", kapsama(icerik), " taban", kapsama(taban), "/", N_KALEM)
print(f"ilk 5 ic benzerlik  icerik {ic_benzerlik(icerik):.4f}  taban {ic_benzerlik(taban):.4f}")
t = 0
for a in AYRI:
    alanlar = [KALEM[j][1] for j in icerik(a)[:5]]
    t += max(alanlar.count(x) for x in set(alanlar))
print("ilk 5 te en cok gecen alanin ortalama payi", round(t / len(AYRI) / 5, 4))
```

```
profil kurulan abone 280 / 360  kurulamayan 80
ilk 3: icerik 0.2874  taban 0.5509
ilk 5: icerik 0.4431  taban 0.6527
katalog kapsamasi   icerik 24  taban 9 / 24
ilk 5 ic benzerlik  icerik 0.6769  taban 0.3827
ilk 5 te en cok gecen alanin ortalama payi 0.715
```

İçerik temelli öneri tabanın altında kalıyor: ilk beş isabeti 0,4431, tabanın 0,6527'sinin 0,2096
gerisinde; ilk üçte fark daha büyük, 0,2874'e karşı 0,5509. Farkın kaynağı yöntemin ne
gördüğüdür. Bir kalemin seçilme olasılığı kurguda iki çarpandan gelir: abonenin o alana eğilimi
ve kalemin kendi yaygınlığı. Öznitelik vektörü yalnız birincisini taşır; ikincisi, yani kaç
abonenin o kalemi seçtiği, öznitelik sütunlarında hiç yazılı değil. Yöntem "bu abone neyi
seçer" sorusunu "bu abonenin seçtiklerine ne benzer" sorusuyla değiştiriyor ve yaygınlığı
düşürüyor.

Kapsama sütunu ters yöne bakıyor. Taban 167 abonenin listelerine yalnız 9 kalem sokuyordu;
içerik temelli öneri **24 kalemin tamamını** en az bir listeye sokuyor. Buna karşılık listeler
kendi içlerinde daralıyor: ilk beşin iç benzerliği tabanda 0,3827 iken içerik temelli öneride
0,6769 ve ilk beşteki kalemlerin ortalama 0,715'i tek bir alandan geliyor. Saha başvurusu yapmış
aboneye dört saha başvurusu daha öneriliyor. Katalog düzeyinde çeşitlilik artıyor, abone
düzeyinde azalıyor ve iki sayı ayrı ayrı yazılmazsa kapsama tek başına yanıltır.

Abone kapsaması kapsamanın öbür yarısı: 360 abonenin 280'ine profil kurulabiliyor, **80'ine hiç
öneri üretilemiyor**. Bu 80 abone bir sonraki dönem de hiçbir şey görmeyecek, hiçbir şey
seçmeyecek ve satırları boş kalmaya devam edecek.

Üç sayı üç ayrı soruya yanıt veriyor ve hiçbiri diğerinin yerine geçmiyor. İsabet "ayrılmış kalem
listede mi" diye soruyor ve içerik temelli yöntem burada kaybediyor. Katalog kapsaması "kaç kalem
hiç görünmüyor" diye soruyor ve burada kazanıyor. İç benzerlik "listedeki beş kalem birbirinin
tekrarı mı" diye soruyor ve burada kaybediyor. Yalnız isabeti yazan bir rapor yöntemi gereksiz
gösterir, yalnız kapsamayı yazan bir rapor onu gereğinden iyi gösterir.

#### Yeni Kalem ve Yeni Abone

Soğuk başlangıç iki yönlüdür ve içerik temelli öneri ikisine iki ayrı yanıt verir. Katalogda
hiç gösterilmemiş bir kalem düşünelim: etkileşim sayısı sıfır, ama öznitelikleri katalogla
birlikte yazılmış. Bir de hiç etkileşimi olmayan bir abone düşünelim: öznitelikleri yok, çünkü
öznitelik kalemin, abonenin değil.

```python
# Kataloga hic gosterilmemis yeni bir kalem eklenir: hicbir etkilesimi yok,
# yalniz oznitelikleri var.
YENI = ("mobil_sayac_okuma", "olcum", "cevrimici", 0, 0, 0, 0.0)
OZ.append(oznitelik(YENI))
YJ = len(OZ) - 1

ilk5 = sum(1 for a in PROFIL if YJ in icerik(a)[:5])
ilk10 = sum(1 for a in PROFIL if YJ in icerik(a)[:10])
konum = sum(icerik(a).index(YJ) + 1 for a in PROFIL) / len(PROFIL)
print(f"{YENI[0]}: icerik temelli ilk 5 -> {ilk5} abone, ilk 10 -> {ilk10} abone")
print("ortalama konum", round(konum, 2), "/", len(OZ), " taban siralamasinda konum",
      len(OZ), "(etkilesim sayisi 0)")
print("profilsiz abone", N_ABONE - len(PROFIL), " icerik temelli oneri sayisi",
      sum(len(icerik(a)) for a in ABONE if a not in PROFIL),
      " tabanin verdigi", 5 * (N_ABONE - len(PROFIL)))
```

```
mobil_sayac_okuma: icerik temelli ilk 5 -> 13 abone, ilk 10 -> 191 abone
ortalama konum 11.55 / 25  taban siralamasinda konum 25 (etkilesim sayisi 0)
profilsiz abone 80  icerik temelli oneri sayisi 0  tabanin verdigi 400
```

Yeni kalem 280 abonenin **tamamında** sıralanıyor: hiç gösterilmemiş olmasına rağmen ortalama
konumu 25 kalem içinde 11,55 ve 191 abonenin ilk onuna, 13 abonenin ilk beşine giriyor. Bir
etkileşim sayısına dayanan sıralamada aynı kalem herkes için sonuncudur ve sonuncu kaldığı
sürece gösterilmediği için etkileşim de toplayamaz. Öznitelik bu döngüyü kıran şeydir: kalem
hakkında **etkileşimden önce** bilinen bir bilgi taşır.

Aynı yardım abone tarafında yok. Profilsiz 80 abone için içerik temelli yöntem toplam **sıfır**
öneri üretiyor; kişiselleştirmesiz taban aynı abonelere 400 öneri veriyor, hepsi aynı beş kalem.
Asimetri yöntemin tanımından geliyor: katalogda kalemin öznitelikleri yazılı, abonenin
öznitelikleri değil. Abone hakkında bilinen tek şey geçmiş seçimleri ve geçmiş seçimi olmayan
abone hakkında bilinen hiçbir şey yok.

Bu dersin toplanan veri sayısı budur: 80 abone hiçbir öneri almıyor ve alamadıkları için bir
sonraki dönemin etkileşim dizeyine de hiçbir satır eklemiyorlar.

#### Özet

- Öznitelik vektörü on bir bileşenlidir; abone profili seçtiği kalemlerin vektörlerinin birim
  uzunluğa getirilmiş toplamıdır ve 360 abonenin 280'ine kurulabilir.
- İçerik temelli önerinin ilk beş isabeti 0,4431, ilk üç isabeti 0,2874'tür; popülerlik tabanı
  aynı ölçülerde 0,6527 ve 0,5509 verir. Öznitelik sütunları kalemin yaygınlığını taşımaz.
- Katalog kapsaması 9'dan 24'e çıkar, ama listelerin iç benzerliği 0,3827'den 0,6769'a yükselir
  ve ilk beşteki kalemlerin 0,715'i tek bir alandan gelir.
- Kapsama iki sayıdır: 80 abone için hiç öneri üretilemez ve bu abonelere hiçbir şey
  gösterilmediği için satırları boş kalmaya devam eder.
- Hiç gösterilmemiş yeni bir kalem 280 abonenin tamamında sıralanır, 191'inin ilk onuna girer ve
  ortalama konumu 11,55'tir; etkileşim sayısına dayanan sıralamada aynı kalem sonuncudur.

#### Sonraki Adım

İçerik temelli öneri iki kalemi, ikisinin öznitelik sütunları benzediği için yakın sayıyor. Bu
yüzden yeni kalemi ilk günden sıralayabiliyor ve bu yüzden bir aboneye hep aynı alandan beş
kalem öneriyor. Ölçümde hiç kullanılmayan bir şey kaldı: abonelerin **birbirine benzemesi**.
Aynı üç kalemi seçmiş iki abone, öznitelik sütunlarında hiçbir izi olmayan bir ortaklık taşır;
birinin seçip diğerinin henüz seçmediği dördüncü kalem bir öneri adayıdır. Bir sonraki ders
benzerliği kalemin özniteliklerinden değil **etkileşim dizeyinin satır ve sütunlarından** kurar,
komşu sayısının sıralamayı ne kadar oynattığını ölçer ve seyreklik arttıkça hiç komşu
bulunamayan abone sayısını sayar.

### İşbirlikçi Süzgeçleme

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/isbirlikci-suzgecleme

Öneriyi kalemin özniteliklerinden değil abonelerin birbirine benzemesinden üretmenin ve bu benzerliğin seyreklikte kırılmasının ölçülmesi: 410 eğitim etkileşimli dizeyde en çok seçilen kalem tabanı 0,6527 isabet@5 verip 24 kalemin 9'unu gösterirken, kullanıcı temelli süzgeçleme üç komşuyla 0,6946 verip 16 kalem gösteriyor, ama ilk beşinin 4,07'si zaten taban listesinin kalemi. Komşu sayısı bir ayar değişkenidir ve yedi aday arasında 0,5988 ile 0,6946 arasında oynuyor; öğe temelli kurulum en iyi ayarında 0,6467'de kalıp tabanın altına düşüyor. Eğitim seyrekliği 0,9525'ten 0,9888'e çıkarıldığında hiç komşusu olmayan sınav abonesi 0'dan 105'e çıkıyor ve ölçünün yerinde durması yöntemden değil kataloğun sırasından geliyor.

Önceki ders öneriyi **kalemin özniteliklerinden** üretti. Her kalemin alanı, kanalı ve ücretli olup
olmadığı katalogda yazılıydı; abonenin geçmişinden bir profil çıkarıldı ve kalemler o profile
benzerliklerine göre sıralandı. O kurulumda her abone kendi başınadır: dosyasında ne varsa ona
bakılır, başka hiçbir abonenin dosyasına bakılmaz. Oysa elde 360 abonelik bir tablo duruyor ve
içinde hiç kullanılmamış bir bilgi var: **aboneler birbirine benziyor.** İki abone de taksitlendirme
ve borç yapılandırmaya başvurmuşsa, birinin başvurduğu üçüncü kalem öbürü için de bir adaydır — ve
bu tahmin, kalemlerin özniteliklerine hiç bakmadan yapılabilir.

**İşbirlikçi süzgeçleme** budur: öneri, kalemin ne olduğundan değil, **kimlerin birlikte
kullandığından** üretilir. Bu dersin ölçtüğü iki şey var. Birincisi, kaç komşuya bakıldığının
sıralamayı ne kadar oynattığı. İkincisi, dizey seyrekleştikçe komşuluğun nerede kırıldığı — çünkü
ortak kalemi olmayan iki abone arasında benzerlik tanımlı değildir ve seyreklik tam olarak bunu
üretir.

- **ON21.** Kurgu küme, ayırma kuralı ve ölçüt birinci dersten aynen sürer: **360 abone, 24 hizmet
  kalemi**, tohum 20260218, en az iki etkileşimi olan abonenin **son** etkileşimi ayrılmış kümeye
  gider. Kod burada yeniden kurulur, sayılar değişmez.
- **ON22.** Kalem kataloğundan bu derste yalnız **alan** ve **taban payı** okunur; öznitelikler
  önceki dersin konusuydu ve işbirlikçi süzgeçleme onlara hiç bakmaz.
- **ON23.** Ölçü **isabet@5** ve yanında **ortalama sıradır**. Birincinin yönü büyük iyidir,
  ikincininki küçük iyidir. Aday kümesi abonenin eğitimde görülmemiş kalemleridir.
- **ON24.** Taban çizgisi birinci dersteki **en çok seçilen kalem sırasıdır**; hiçbir abone ayrımı
  yapmaz ve bu kursta bir öneri yönteminin geçmesi gereken eşiktir.
- **ON25.** Benzerlik, iki ikili vektörün **ortak kalem sayısının uzunluklarının köküne
  bölünmesidir**. Ortak kalemi olmayan çift benzerlik listesine **hiç girmez**; sıfır benzerlik
  kaydedilmez ve bu, komşusuzluğun tanımıdır.
- **ON26.** Komşu sayısı $K$ bir **ayar değişkenidir**. Kullanıcı temelli tarafta **7**, öğe temelli
  tarafta **6** aday denenmiştir; ayrı bir ayar kümesi ayrılmamıştır, çünkü ölçülen şey en iyi ayar
  değil, ayarın sıralamayı ne kadar oynattığıdır.
- **ON27.** Eşit puanlı kalemler arasında sıra **katalog indisiyle** çözülür. Katalog taban payına
  göre azalan sıradadır, dolayısıyla puanı sıfır olan bir abone için sıralama sessizce popülerlik
  sırasına düşer. Bu, son bölümde ayrıca ölçülür.
- **ON28.** **Gösterilen kalem sayısı**, sınav abonelerinin ilk beşlerinin birleşimidir ve kursun
  üçüncü sayısıdır: hiç gösterilmeyen kalem hiç seçilmez.
- **ON29.** Seyreklik süpürmesinde eğitim etkileşimlerinin bir **payı** tutulur, ayrılmış kalemler
  hiç değişmez; böylece beş satır aynı 167 abone üzerinde okunur.

#### Ortak Kalem Olmadan Komşu Yok

İlk blok kurguyu, ayırmayı ve taban çizgisini kurar.

```python
# Kurgu katalog birinci dersten gelir; burada yalniz kalemin alani ve taban payi gerekir.
TOHUM, M32, N_ABONE = 20260218, 0xFFFFFFFF, 360
KALEM = [("odeme", 0.90), ("bildirim", 0.85), ("bildirim", 0.78), ("odeme", 0.62),
         ("olcum", 0.55), ("bildirim", 0.50), ("odeme", 0.44), ("sozlesme", 0.40),
         ("olcum", 0.36), ("bildirim", 0.32), ("odeme", 0.28), ("sozlesme", 0.25),
         ("olcum", 0.22), ("tesisat", 0.19), ("olcum", 0.16), ("sozlesme", 0.14),
         ("tesisat", 0.12), ("sozlesme", 0.10), ("olcum", 0.09), ("sozlesme", 0.08),
         ("tesisat", 0.07), ("odeme", 0.06), ("bildirim", 0.05), ("tesisat", 0.04)]
KESIM = {
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7}}
KESIM_AD, N_KALEM = list(KESIM), len(KALEM)


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


ABONE, ETKILESIM = [], {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (alan, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        if r() < min(0.95, pay ** 0.55 * 0.72):     # sunulmus mu
            if r() < egilim:                        # secilmis mi
                ETKILESIM[(a, j)] = r()             # deger: sira damgasi
                if r() < 0.32:
                    r()                             # puan birakilmis; burada kullanilmaz
        else:
            r(), r(), r()

KAYIT, EGIT, AYRI = {}, {}, {}
for (a, j), damga in ETKILESIM.items():
    KAYIT.setdefault(a, []).append((damga, j))
GORULEN = {a: set() for a in ABONE}
for a, lst in KAYIT.items():
    lst.sort()
    if len(lst) >= 2:                                # ayirma kurali birinci derstendir
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN[a].add(j)

SINAV = sorted(AYRI)
PAY = [sum(1 for (a, j) in EGIT if j == jj) for jj in range(N_KALEM)]


def olc(puan, gor=None, kimler=None, k=5):
    gor, kimler = gor or GORULEN, kimler or SINAV
    isabet, sira, gosterilen = 0, 0, set()
    for a in kimler:
        s = puan(a)
        aday = sorted((j for j in range(N_KALEM) if j not in gor[a]),
                      key=lambda j: (-s[j], j))
        gosterilen.update(aday[:k])
        p = aday.index(AYRI[a])
        isabet, sira = isabet + (p < k), sira + p + 1
    return isabet / len(kimler), sira / len(kimler), len(gosterilen)


print("etkilesim", len(ETKILESIM), "-- egitim", len(EGIT), "-- sinav abonesi", len(AYRI))
print("egitim dizeyinin seyrekligi",
      round(1 - len(EGIT) / (N_ABONE * N_KALEM), 4))
print("taban -- en cok secilen kalem: isabet@5 %.4f, ort sira %.2f, gosterilen %d/24"
      % olc(lambda a: PAY))
```

```
etkilesim 577 -- egitim 410 -- sinav abonesi 167
egitim dizeyinin seyrekligi 0.9525
taban -- en cok secilen kalem: isabet@5 0.6527, ort sira 4.74, gosterilen 9/24
```

Ayrılan kalemler çıkınca eğitim dizeyinin seyrekliği 0,9525'e çıkıyor. Üç yüz altmış satır, yirmi
dört sütun ve **410 dolu hücre**: yöntemin komşuluk kuracağı bütün malzeme budur.

#### Kullanıcı Temelli: Komşu Sayısı Sıralamayı Oynatıyor

Benzerlik yordamı bir kez yazılır ve iki yerde kullanılır — abonelerin kalem kümeleri verilirse
abone benzerliği, kalemlerin abone kümeleri verilirse kalem benzerliği çıkar. Bir abonenin bir
kaleme puanı, en benzer $K$ komşusundan o kaleme sahip olanların benzerlik toplamıdır.

```python
def benzerlikler(kume, anahtar):
    n = {u: max(1e-9, len(kume[u])) ** 0.5 for u in anahtar}
    cikti = {}
    for u in anahtar:
        b = [(len(kume[u] & kume[v]) / (n[u] * n[v]), v)
             for v in anahtar if v != u and kume[u] & kume[v]]
        b.sort(key=lambda t: (-t[0], t[1]))
        cikti[u] = b
    return cikti


def kullanici_temelli(B, gor, K):
    def puan(a):
        s = [0.0] * N_KALEM
        for w, v in B[a][:K]:
            for j in gor[v]:
                s[j] += w
        return s
    return puan


def ilk_k(puan, a, gor, k=5):
    s = puan(a)
    return set(sorted((j for j in range(N_KALEM) if j not in gor[a]),
                      key=lambda j: (-s[j], j))[:k])


BENZ = benzerlikler(GORULEN, ABONE)
TABAN5 = {a: ilk_k(lambda x: PAY, a, GORULEN) for a in SINAV}
print(f"{'K':>4} {'isabet@5':>9} {'ort sira':>9} {'gosterilen':>11} "
      f"{'onceki K':>9} {'tabanla':>8}")
onceki = None
for K in (3, 5, 10, 20, 40, 80, 160):
    p = kullanici_temelli(BENZ, GORULEN, K)
    i, s, kap = olc(p)
    su = {a: ilk_k(p, a, GORULEN) for a in SINAV}
    o = ("%.2f" % (sum(len(su[a] & onceki[a]) for a in SINAV) / len(SINAV))
         if onceki else "--")
    t = sum(len(su[a] & TABAN5[a]) for a in SINAV) / len(SINAV)
    print(f"{K:>4} {i:>9.4f} {s:>9.2f} {kap:>11} {o:>9} {t:>8.2f}")
    onceki = su
```

```
   K  isabet@5  ort sira  gosterilen  onceki K  tabanla
   3    0.6946      4.66          16        --     4.07
   5    0.6886      4.64          18      4.89     4.01
  10    0.6707      4.80          19      4.71     3.85
  20    0.6168      5.25          20      4.51     3.62
  40    0.5988      5.54          20      4.14     3.49
  80    0.6467      5.28          18      4.08     3.96
 160    0.6647      5.18          14      4.72     4.12
```

En iyi ayar üç komşuda **0,6946**, tabandan **0,0419** yukarıda. Ama aynı sütunun en kötü adayı kırk
komşuda **0,5988**, yani tabandan **0,0539 aşağıda**. Yedi aday arasındaki oynama 0,0958'dir ve
yöntemin en iyi ayarıyla tabanı geçtiği farkın **iki katından fazladır**. Komşu sayısı burada
yöntemin bir ayrıntısı değil, sonucunu belirleyen şeydir: kötü seçilmiş bir $K$ ile işbirlikçi
süzgeçleme hiçbir abone ayrımı yapmayan listeden daha kötüdür.

Altıncı sütun kazancın ne kadar dar olduğunu söylüyor. En iyi ayarda ilk beş listesinin **4,07'si**
zaten taban listesinin kalemidir; yöntem beş kalemden yalnız birini değiştirerek 0,0419 kazanıyor.
Kırk komşuda örtüşme 3,49'a iniyor, yani liste en çok orada kişiselleşiyor — ve isabetin en düşük
olduğu yer de orasıdır. Yüz altmışta örtüşme 4,12'ye geri tırmanıyor, çünkü komşu sayısı abone
sayısının yarısına yaklaştığında ağırlıklı toplam bütün kümenin ortalamasına, yani popülerliğe
yakınsıyor. **Ayar değişkeni iki uçta da tabana çıkar.**

Beşinci sütun oynaklığı doğrudan sayıyor: komşu sayısı bir adım değiştiğinde ilk beşin en iyi
durumda 4,89'u, en kötü durumda 4,08'i yerinde kalıyor — her adımda ortalama bir kalem yer
değiştiriyor.

Kazancın asıl göründüğü yer dördüncü sütundur. Taban 167 abonenin tamamına yalnız **9 ayrı kalem**
gösteriyordu. Kullanıcı temelli kurulum en iyi ayarında 16, yirmi komşuda 20 kalem gösteriyor.
Yöntem isabette 0,0419 kazanırken kataloğun görünen kısmını neredeyse iki katına çıkarıyor ve
gösterilmeyen kalem bir sonraki dönemin verisine de girmediği için bu ikinci sayı birincisinden
daha uzun ömürlüdür.

#### Öğe Temelli: Aynı Yordam, Dizeyin Öbür Kenarı

Aynı benzerlik yordamı dizeyin sütunlarına uygulanır: her kalem, kendisini seçen abonelerin
kümesiyle temsil edilir ve iki kalem ortak abonelerinin sayısına göre benzeşir. Bir abonenin puanı,
kendi kalemlerinin en benzer $K$ komşusuna dağıtılır. Bu sayım M27/K04'te ölçülen **kaldıraçla** aynı
ham malzemeyi kullanır — iki kalemin birlikte görülme sayısı — ama orada bir kural çıkarılıyordu,
burada bir sıralama üretiliyor ve kural çıkarma yordamı tekrarlanmıyor.

```python
KGOR = {j: set(a for a in ABONE if j in GORULEN[a]) for j in range(N_KALEM)}
KBENZ = benzerlikler(KGOR, list(range(N_KALEM)))


def oge_temelli(K):
    def puan(a):
        s = [0.0] * N_KALEM
        for i in GORULEN[a]:
            for w, j in KBENZ[i][:K]:
                s[j] += w
        return s
    return puan


print(f"{'K':>4} {'isabet@5':>9} {'ort sira':>9} {'gosterilen':>11} {'tabanla':>8}")
for K in (2, 3, 5, 8, 12, 23):
    i, s, kap = olc(oge_temelli(K))
    su = {a: ilk_k(oge_temelli(K), a, GORULEN) for a in SINAV}
    t = sum(len(su[a] & TABAN5[a]) for a in SINAV) / len(SINAV)
    print(f"{K:>4} {i:>9.4f} {s:>9.2f} {kap:>11} {t:>8.2f}")
```

```
   K  isabet@5  ort sira  gosterilen  tabanla
   2    0.6467      5.20          18     3.37
   3    0.6467      5.21          18     3.32
   5    0.5329      5.75          21     2.93
   8    0.5329      6.03          21     3.00
  12    0.5329      6.50          21     2.95
  23    0.5449      6.48          21     2.92
```

Öğe temelli kurulumun en iyi adayı **0,6467**, yani tabanın **altında**. Altı adayın hiçbiri 0,6527'yi
geçmiyor. Bu, kullanıcı temelli tarafla karşılaştırıldığında ters bir sonuçtur ve nedeni sayılabilir:
sınav abonelerinin eğitim kümesinde ortalama **1,78 kalemi** var. Öğe temelli puan bu bir iki
kalemin komşuluklarına dağıtılıyor, dolayısıyla toplam neredeyse tek bir kalemin komşu listesinden
ibaret kalıyor. Kullanıcı temelli tarafta ise toplam üç ayrı abonenin bütün geçmişini topluyor.
Seyrek dizeyde hangi kenarın daha çok gözlem taşıdığı yöntemin sırasını belirliyor ve bu kurguda
o kenar satırlardır.

Kurulumun kazandığı yer yine dördüncü sütundur: 21 kalem gösteriyor ve taban listesiyle örtüşmesi
hiçbir ayarda 3,37'yi aşmıyor. Öğe temelli sıralama daha kişiselleşmiş ve daha geniş kapsamalıdır;
bu kurguda bunun bedelini isabetten ödüyor. **Aynı veride iki kurulum, aynı ölçüde iki ayrı yönde
hareket ediyor.**

#### Seyreklik Arttıkça Komşu Kalmıyor

Her iki kurulum tek bir şeye dayanır: iki abonenin ya da iki kalemin **doğrudan ortak gözlemi**.
Eğitim etkileşimlerinin bir payı tutulup kalanı atılarak seyreklik yukarı çekilir; ayrılan kalemler
değişmez, böylece beş satır aynı 167 abone üzerinde okunur. Komşu sayısı en iyi ayarında,
yani üçte, sabitlenmiştir.

```python
CIFT = sorted(EGIT)
print(f"{'pay':>5} {'egitim':>7} {'seyreklik':>10} {'komsusuz':>9} "
      f"{'isabet@5':>9} {'komsulu':>8} {'komsusuz':>9} {'taban':>7}")
for pay in (1.00, 0.75, 0.55, 0.40, 0.25):
    r = uretec(TOHUM + 9100)
    G = {a: set() for a in ABONE}
    for a, j in CIFT:
        if r() < pay:
            G[a].add(j)
    B = benzerlikler(G, ABONE)
    ps = [sum(1 for a in ABONE if j in G[a]) for j in range(N_KALEM)]
    kt = kullanici_temelli(B, G, 3)
    var = [a for a in SINAV if B[a]]
    yok = [a for a in SINAV if not B[a]]
    t = sum(len(v) for v in G.values())
    iy = "%.4f" % olc(kt, G, yok)[0] if yok else "--"
    print(f"{pay:>5.2f} {t:>7} {1 - t / (N_ABONE * N_KALEM):>10.4f} {len(yok):>9} "
          f"{olc(kt, G)[0]:>9.4f} {olc(kt, G, var)[0]:>8.4f} {iy:>9} "
          f"{olc(lambda a: ps, G)[0]:>7.4f}")
```

```
  pay  egitim  seyreklik  komsusuz  isabet@5  komsulu  komsusuz   taban
 1.00     410     0.9525         0    0.6946   0.6946        --  0.6527
 0.75     308     0.9644        20    0.6886   0.6803    0.7500  0.6467
 0.55     221     0.9744        52    0.6587   0.6783    0.6154  0.6347
 0.40     167     0.9807        75    0.6587   0.6739    0.6400  0.6407
 0.25      97     0.9888       105    0.6407   0.6613    0.6286  0.6347
```

Seyreklik 0,9525'ten 0,9888'e çıkarken hiç komşusu kalmayan sınav abonesi **sıfırdan 105'e** çıkıyor
— 167 abonenin **yarısından fazlası**. Bu abonelerin puan vektörü baştan sona sıfırdır: işbirlikçi
süzgeçleme onlar için hiçbir şey üretmiyor.

Beşinci sütun buna rağmen 0,6946'dan yalnız 0,6407'ye iniyor. Ölçünün bu kadar az düşmesi yöntemin
seyrekliğe dayanıklı olduğunu göstermiyor; yedinci sütun nedeni söylüyor. Komşusuz abonelerde
isabet **0,6286** — komşusu olanlardaki 0,6613'ün yalnız 0,0327 altında. Oysa o abonelerde yöntem
hiç çalışmadı. Sayı, ON27'de yazılan eşitlik çözümünden geliyor: bütün puanlar sıfır olunca sıralama
**katalog indisine** düşüyor ve katalog taban payına göre azalan sırada. Yani komşusuz abone sessizce
taban çizgisiyle aynı listeyi alıyor ve ölçü yöntemin sayısı gibi görünen bir tabanın sayısını
basıyor.

Bu, kursun kuralının bir başka yüzüdür. Yöntemin sayısını okurken **kaç abone için gerçekten
çalıştığı** yazılmazsa, ölçü yöntemin değil ölçüm düzeninin ürettiği bir şeydir. Sütunlar ayrı
basıldığında kayıp görünür oluyor: 0,25 payında yöntem 167 abonenin 62'sinde çalışıyor ve orada
0,6613 veriyor, kalan 105'inde çalışmıyor ve orada 0,6286'yı katalog sırası veriyor. Taban ise beş
satır boyunca 0,6527 ile 0,6347 arasında yerinde duruyor.

#### Özet

- İşbirlikçi süzgeçleme öneriyi kalemin özniteliklerinden değil, kalemleri birlikte seçen
  abonelerden üretir; tek bir benzerlik yordamı dizeyin iki kenarında da çalışır.
- Eğitim dizeyi 410 etkileşimle 0,9525 seyrek; taban çizgisi olan en çok seçilen kalem sırası
  0,6527 isabet@5 veriyor ve 24 kalemin yalnız 9'unu gösteriyor.
- Kullanıcı temelli kurulum üç komşuyla 0,6946 veriyor, tabandan 0,0419 yukarıda; ama ilk beşinin
  4,07'si zaten taban listesinin kalemidir ve asıl kazanç 9 yerine 16 kalem göstermektir.
- Komşu sayısı bir ayar değişkenidir: yedi aday 0,5988 ile 0,6946 arasında oynuyor ve bu 0,0958'lik
  bant, yöntemin tabanı geçtiği farkın iki katından fazla.
- Öğe temelli kurulum en iyi ayarında 0,6467'de kalıp tabanın altına düşüyor, çünkü sınav abonesi
  başına ortalama 1,78 eğitim kalemi var; buna karşılık 21 kalem gösteriyor.
- Seyreklik 0,9525'ten 0,9888'e çıkarıldığında komşusuz abone 0'dan 105'e çıkıyor; ölçünün 0,6407'de
  kalması yöntemden değil, puanlar sıfırlanınca devreye giren katalog sırasından geliyor.

#### Sonraki Adım

Komşuluk tek bir şeye dayanıyor: iki abonenin ya da iki kalemin **doğrudan ortak gözlemi**. Ortak
gözlem yoksa benzerlik listeye hiç girmiyor ve son tablo bunun bedelini gösterdi. Oysa ortak kalemi
olmayan iki abone birbirine gerçekten benzemiyor olmak zorunda değil: biri taksitlendirmeye, öbürü
borç yapılandırmaya başvurmuşsa ikisi de ödeme alanında duruyor, ama benzerlikleri sıfır çıkıyor.
Bu ortaklığı yakalamak için benzerliği hücre hücre değil, her aboneyi ve her kalemi az sayıda
**gizli etkene** yerleştiren bir gösterim üzerinden okumak gerekir. Sonraki ders dizeyi böyle iki
küçük dizeyin çarpımı olarak yazar ve ölçülecek asıl karar şudur: dizeyin **gözlenmeyen hücreleri**
hesaba sıfır olarak mı girecek, yoksa hiç girmeyecek mi. İki kurulum aynı veride iki ayrı sıralama
üretir.

### Dizey Ayrıştırma

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/dizey-ayristirma

Eksik girdili bir etkileşim dizeyinde gözlenmeyen hücrenin sıfır sayılmasıyla hesaba hiç alınmaması arasındaki farkın ölçülmesi: aynı yordam, tek satır değişiklikle, aynı 410 eğitim etkileşiminden iki ayrı sıralama üretiyor. Atlayan kurulum dört etkenle 0,7006 isabet@5 veriyor ve 0,6527'lik tabanı geçiyor ama 24 kalemin yalnız 9'unu gösteriyor; sıfır sayan kurulum sekiz etkenle 0,2395'e düşerken 20 kalem gösteriyor ve ilk beş listelerinin en çok gösterilen beş kaleme düşen payını 0,7868'den 0,4467'ye indiriyor. Etken sayısı ayar değişkenidir, altı aday denenmiştir ve iki kurulumun ilk beşlerinin ortak kalem sayısı 4,02'den 0,86'ya iniyor.

Önceki ders benzerliği doğrudan ortak gözlemden kurdu: ortak kalemi olmayan iki abone arasında
benzerlik tanımlı değildi ve seyreklik yükseldikçe 167 sınav abonesinin 105'i komşusuz kaldı. Oysa
biri taksitlendirmeye, öbürü borç yapılandırmaya başvurmuş iki abone aynı ödeme davranışını
taşıyor olabilir; komşuluk yordamı bunu göremez, çünkü baktığı tek şey hücrelerin çakışmasıdır.

**Dizey ayrıştırma** bu kısıtı kaldırır: her abone ve her kalem az sayıda **gizli etken** üzerinde
bir vektörle temsil edilir ve bir hücrenin tahmini iki vektörün çarpımıdır. Ortak kalemi olmayan iki
abone aynı etken yönünde yüksek değer alabilir, dolayısıyla benzerlik hücrelerden değil
**gösterimden** okunur. Tam dizeyin düşük ranklı yaklaşımı M27/K04'ün boyut azaltma konusunda
ölçüldü ve burada tekrarlanmaz. Eklenen tek şey **eksik girdidir** ve bu tek şey bir karar
doğurur: 8.640 hücrenin 8.230'u boş ve boş hücre hesaba **sıfır olarak mı giriyor**, yoksa **hiç
girmiyor mu**. Bu dersin ölçtüğü şey o karardır.

- **ON30.** Kurgu küme, ayırma kuralı, taban çizgisi ve ölçüt önceki iki dersten aynen sürer:
  360 abone, 24 kalem, 410 eğitim etkileşimi, 167 sınav abonesi, ölçüt **isabet@5**, taban
  **en çok seçilen kalem sırası**.
- **ON31.** Ayrıştırma **modeldir** ve ders içinde yalnız standart kitaplıkla yazılır: abone dizeyi
  $P$ ile kalem dizeyi $Q$ **dönüşümlü** güncellenir ve her adımda **cezalı en küçük kareler**
  kapalı biçimde çözülür; M27/K03'ün eğim inişi tekrarlanmaz.
- **ON32.** İki kurulumun tek farkı, güncellemede **hangi hücrelerin toplama girdiğidir**.
  **Sıfır sayan** kurulumda satırın bütün 24 hücresi girer ve gözlenmeyenlerin hedefi 0'dır.
  **Atlayan** kurulumda yalnız gözlenen hücreler girer ve gözlenmeyen hiç yazılmaz.
- **ON33.** Ceza katsayısı **her iki kurulumda 1,0**'dır ve süpürülmez; ölçülen şey ceza
  katsayısının değil gözlenmeyen hücre kararının farkıdır.
- **ON34.** Etken sayısı $f$ bir **ayar değişkenidir**; **altı aday** denenmiştir (1, 2, 3, 4, 6, 8).
  Parametre sayısı $384f$, gözlenen etkileşim 410'dur; $f=2$'den itibaren parametre sayısı gözlem
  sayısını geçer.
- **ON35.** Dönüşümlü güncelleme **10 tur** sürer ve iki kurulum aynı başlangıçtan başlar. Küçük
  kare dizgeler yok etmeyle çözülür; köşegen için $10^{-12}$ eşiği yazılmıştır, çünkü kayan noktalı
  eşitlik sınanmaz.
- **ON36.** İki hata basılır: **gözlenen hücre hatası** 410 dolu hücrede, **tüm hücre hatası**
  8.640 hücrenin tamamında, ikisi de kök ortalama kare sapmadır. Her kurulum bunlardan birini
  küçültmek üzere kurulmuştur.
- **ON37.** **Gösterilen kalem sayısı** ve **ilk beş listelerinin en çok gösterilen beş kaleme düşen
  payı** kursun üçüncü sayısıdır; gösterilmeyen kalem bir sonraki dönemin verisine girmez.

#### Tek Satır Fark: Hangi Hücreler Hesaba Giriyor

İlk blok kurguyu ve ayırmayı önceki derslerdeki biçimiyle kurar; sayılar değişmez.

```python
# Kurgu katalog birinci dersten gelir; burada yalniz kalemin alani ve taban payi gerekir.
TOHUM, M32, N_ABONE = 20260218, 0xFFFFFFFF, 360
KALEM = [("odeme", 0.90), ("bildirim", 0.85), ("bildirim", 0.78), ("odeme", 0.62),
         ("olcum", 0.55), ("bildirim", 0.50), ("odeme", 0.44), ("sozlesme", 0.40),
         ("olcum", 0.36), ("bildirim", 0.32), ("odeme", 0.28), ("sozlesme", 0.25),
         ("olcum", 0.22), ("tesisat", 0.19), ("olcum", 0.16), ("sozlesme", 0.14),
         ("tesisat", 0.12), ("sozlesme", 0.10), ("olcum", 0.09), ("sozlesme", 0.08),
         ("tesisat", 0.07), ("odeme", 0.06), ("bildirim", 0.05), ("tesisat", 0.04)]
KESIM = {
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7}}
KESIM_AD, N_KALEM = list(KESIM), len(KALEM)


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


ABONE, ETKILESIM = [], {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (alan, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        if r() < min(0.95, pay ** 0.55 * 0.72):
            if r() < egilim:
                ETKILESIM[(a, j)] = r()
                if r() < 0.32:
                    r()
        else:
            r(), r(), r()

KAYIT, EGIT, AYRI = {}, {}, {}
for (a, j), damga in ETKILESIM.items():
    KAYIT.setdefault(a, []).append((damga, j))
GORULEN = {a: set() for a in ABONE}
for a, lst in KAYIT.items():
    lst.sort()
    if len(lst) >= 2:
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN[a].add(j)

SINAV = sorted(AYRI)
PAY = [sum(1 for (a, j) in EGIT if j == jj) for jj in range(N_KALEM)]
KGOR = {j: set(a for a in ABONE if j in GORULEN[a]) for j in range(N_KALEM)}


def olc(puan, k=5):
    isabet, sira, gosterilen = 0, 0, set()
    for a in SINAV:
        s = puan(a)
        aday = sorted((j for j in range(N_KALEM) if j not in GORULEN[a]),
                      key=lambda j: (-s[j], j))
        gosterilen.update(aday[:k])
        p = aday.index(AYRI[a])
        isabet, sira = isabet + (p < k), sira + p + 1
    return isabet / len(SINAV), sira / len(SINAV), len(gosterilen)


print("egitim", len(EGIT), "etkilesim -- bos hucre", N_ABONE * N_KALEM - len(EGIT),
      "-- sinav abonesi", len(SINAV))
print("taban -- en cok secilen kalem: isabet@5 %.4f, ort sira %.2f, gosterilen %d/24"
      % olc(lambda a: PAY))
```

```
egitim 410 etkilesim -- bos hucre 8230 -- sinav abonesi 167
taban -- en cok secilen kalem: isabet@5 0.6527, ort sira 4.74, gosterilen 9/24
```

İki kurulum arasındaki fark `sifir_say` bayrağının açtığı tek toplamdır. Bayrak açıkken kare dizge
bütün kalemler üzerinden kurulur ve her abone için aynıdır; kapalıyken yalnız abonenin gözlenen
kalemleri üzerinden. Hedef vektörü ikisinde de aynıdır, çünkü sıfır hedefli hücreler toplama katkı
vermez.

```python
def coz(A, b):                     # kucuk kare dizgede yok etme; f en cok 8
    n = len(b)
    M = [A[i][:] + [b[i]] for i in range(n)]
    for i in range(n):
        p = max(range(i, n), key=lambda t: abs(M[t][i]))
        M[i], M[p] = M[p], M[i]
        if abs(M[i][i]) < 1e-12:   # esik: ceza terimi kosegeni sifirdan uzak tutar
            continue
        d = M[i][i]
        for k in range(i, n + 1):
            M[i][k] /= d
        for t in range(n):
            if t != i and M[t][i]:
                c = M[t][i]
                for k in range(i, n + 1):
                    M[t][k] -= c * M[i][k]
    return [M[i][n] for i in range(n)]


def ayristir(f, sifir_say, tur=10, ceza=1.0):
    # MODELDIR: donusumlu guncelleme ile dizey ayristirma, yalniz standart kitaplik.
    r = uretec(TOHUM + 5100 + f)
    P = {a: [0.1 + 0.2 * r() for _ in range(f)] for a in ABONE}
    Q = [[0.1 + 0.2 * r() for _ in range(f)] for _ in range(N_KALEM)]
    bos = [[0.0] * f for _ in range(f)]
    for _ in range(tur):
        G = ([[sum(q[x] * q[y] for q in Q) for y in range(f)] for x in range(f)]
             if sifir_say else bos)
        for a in ABONE:
            idx = GORULEN[a]
            A = [[G[x][y] + (0 if sifir_say else sum(Q[j][x] * Q[j][y] for j in idx))
                  + (ceza if x == y else 0) for y in range(f)] for x in range(f)]
            P[a] = coz(A, [sum(Q[j][x] for j in idx) for x in range(f)])
        H = ([[sum(P[a][x] * P[a][y] for a in ABONE) for y in range(f)] for x in range(f)]
             if sifir_say else bos)
        for j in range(N_KALEM):
            us = KGOR[j]
            A = [[H[x][y] + (0 if sifir_say else sum(P[a][x] * P[a][y] for a in us))
                  + (ceza if x == y else 0) for y in range(f)] for x in range(f)]
            Q[j] = coz(A, [sum(P[a][x] for a in us) for x in range(f)])
    return P, Q


def tahmin(P, Q):
    return lambda a: [sum(P[a][x] * Q[j][x] for x in range(len(Q[0])))
                      for j in range(N_KALEM)]


def hatalar(P, Q):
    f, go, ga = len(Q[0]), 0.0, 0.0
    for a in ABONE:
        for j in range(N_KALEM):
            d = (1.0 if j in GORULEN[a] else 0.0) - sum(P[a][x] * Q[j][x]
                                                        for x in range(f))
            ga += d * d
            if j in GORULEN[a]:
                go += d * d
    return (go / len(EGIT)) ** 0.5, (ga / (N_ABONE * N_KALEM)) ** 0.5


def ilk5(puan, a):
    s = puan(a)
    return set(sorted((j for j in range(N_KALEM) if j not in GORULEN[a]),
                      key=lambda j: (-s[j], j))[:5])


for ad, etiket in ((True, "sifir sayilir"), (False, "atlanir     ")):
    P, Q = ayristir(3, ad)
    ho, ha = hatalar(P, Q)
    print(f"f=3, gozlenmeyen {etiket}: isabet@5 {olc(tahmin(P, Q))[0]:.4f}  "
          f"gozlenen hucre hatasi {ho:.4f}  tum hucre hatasi {ha:.4f}")
```

```
f=3, gozlenmeyen sifir sayilir: isabet@5 0.4731  gozlenen hucre hatasi 0.7168  tum hucre hatasi 0.1614
f=3, gozlenmeyen atlanir     : isabet@5 0.6826  gozlenen hucre hatasi 0.2125  tum hucre hatasi 0.5055
```

Atlayan kurulumun gözlenen hücre hatası **0,2125**, sıfır sayanınki **0,7168** — üç katından fazla.
Tüm hücre hatasında sıra tersine dönüyor: sıfır sayan 0,1614, atlayan 0,5055. Her kurulum
küçültmeye çalıştığı toplamı küçültüyor ve öbürünün ölçüsünde kaybediyor: atlayan kurulum
gözlenmeyenler hakkında hiçbir kısıt görmediği için oralarda 0,5 civarında bir değer bırakıyor,
sıfır sayan kurulum 8.230 boş hücreyi 0'a çekerken 410 dolu hücreyi 1'e çekecek kapasite
bulamıyor.

Asıl sayı üçüncüsüdür. **Geri kurma hatası küçük olan kurulum daha kötü sıralama veriyor:**
atlayan 0,6826, sıfır sayan 0,4731.

#### Etken Sayısı Bir Ayar Değişkenidir

Etken sayısı altı aday üzerinde süpürülür; yanına parametre sayısı, iki hata, gösterilen kalem
sayısı, taban listesiyle örtüşme ve iki kurulumun **birbiriyle** örtüşmesi basılır.

```python
TABAN5 = {a: ilk5(lambda x: PAY, a) for a in SINAV}
print(f"{'f':>2} {'par':>5} | {'A isb':>6} {'A gozl':>7} {'A tum':>6} {'A gst':>5} {'A tbn':>5}"
      f" | {'B isb':>6} {'B gozl':>7} {'B tum':>6} {'B gst':>5} {'B tbn':>5} | {'A~B':>5}")
for f in (1, 2, 3, 4, 6, 8):
    sat, top = [f"{f:>2} {(N_ABONE + N_KALEM) * f:>5}"], []
    for ad in (True, False):
        P, Q = ayristir(f, ad)
        pu = tahmin(P, Q)
        ho, ha = hatalar(P, Q)
        isb, sira, gst = olc(pu)
        L = {a: ilk5(pu, a) for a in SINAV}
        top.append(L)
        sat.append(f"| {isb:>6.4f} {ho:>7.4f} {ha:>6.4f} {gst:>5} "
                   f"{sum(len(L[a] & TABAN5[a]) for a in SINAV) / len(SINAV):>5.2f}")
    sat.append(f"| {sum(len(top[0][a] & top[1][a]) for a in SINAV) / len(SINAV):>5.2f}")
    print(" ".join(sat))
```

```
 f   par |  A isb  A gozl  A tum A gst A tbn |  B isb  B gozl  B tum B gst B tbn |   A~B
 1   384 | 0.6707  0.8377 0.1930    10  4.23 | 0.6707  0.2161 0.5207     9  4.34 |  4.02
 2   768 | 0.6048  0.7667 0.1760    11  4.16 | 0.6766  0.2124 0.5072     9  4.37 |  3.65
 3  1152 | 0.4731  0.7168 0.1614    12  3.43 | 0.6826  0.2125 0.5055     9  4.35 |  3.10
 4  1536 | 0.4251  0.6591 0.1479    15  2.68 | 0.7006  0.2115 0.5000     9  4.31 |  2.60
 6  2304 | 0.3593  0.5466 0.1263    20  1.45 | 0.6886  0.2113 0.4940     9  4.46 |  1.44
 8  3072 | 0.2395  0.4758 0.1066    20  0.82 | 0.6826  0.2107 0.4937     9  4.54 |  0.86
```

Son sütun dersin başlığıdır. Bir etkende iki kurulumun ilk beş listeleri **4,02 kalemi** paylaşıyor,
sekiz etkende **0,86**. Aynı veri, aynı yordam, aynı ceza, aynı başlangıç — ve tek satırlık bir fark
listelerin neredeyse tamamını değiştiriyor. Gözlenmeyen hücre kararı bir uygulama ayrıntısı değil,
**modelin ne öğrendiğinin tanımıdır**.

Sıfır sayan kurulum etken sayısıyla bozuluyor: 0,6707'den 0,2395'e. Nedeni ikinci sütunda duruyor.
Parametre sayısı $384f$; bir etkende 384, gözlenen etkileşim ise 410. **İki etkenden itibaren
parametre sayısı gözlem sayısını geçiyor** ve model 8.230 sıfırın arasından abone başına istisna
oymaya başlıyor. Taban listesiyle örtüşme 4,23'ten 0,82'ye iniyor: liste tabandan kopuyor, ama
koptuğu yer doğru yer değil.

Atlayan kurulum etken sayısına neredeyse duyarsız: en iyisi dört etkende **0,7006** — tabandan
**0,0479**, önceki dersin en iyi komşuluk kurulumundan 0,0060 yukarıda. Nedeni yapısaldır:
gözlenmeyen hücreye hiç bakılmadığı için fazladan etkenlerin yanlış öğrenebileceği bir şey yok ve
bir abonenin sıralaması gizli etken uzayında kendi kalemlerine yakın duran kalemlerden ibaret
kalıyor. Bu, önceki dersin öğe temelli komşuluğunun etkenler üzerinden yazılmış hâlidir.

#### İki Sıralamanın Gösterdiği Katalog

İsabet iki kurulumu bir yönde sıralıyor. Kursun üçüncü sayısı, yani gösterilen kalem, öbür yönde
sıralıyor. Her iki kurulumun ilk beş listeleri sayılır ve taban çizgisiyle yan yana konur.

```python
def dagilim(puan):
    say = [0] * N_KALEM
    for a in SINAV:
        for j in ilk5(puan, a):
            say[j] += 1
    d = sorted(say, reverse=True)
    return sum(1 for v in say if v == 0), d[0], sum(d[:5]) / sum(d)


print(f"{'kurulum':>13} {'f':>2} {'hic gosterilmeyen':>18} {'en cok gosterilen':>18} "
      f"{'ilk 5 kalemin payi':>19}")
for etiket, f, ad in (("sifir sayilir", 1, True), ("sifir sayilir", 8, True),
                      ("atlanir", 1, False), ("atlanir", 8, False)):
    print("%13s %2d %18d %18d %19.4f"
          % ((etiket, f) + dagilim(tahmin(*ayristir(f, ad)))))
print("%13s %2s %18d %18d %19.4f" % (("taban", "--") + dagilim(lambda a: PAY)))
```

```
      kurulum  f  hic gosterilmeyen  en cok gosterilen  ilk 5 kalemin payi
sifir sayilir  1                 14                150              0.7904
sifir sayilir  8                  4                 84              0.4467
      atlanir  1                 15                146              0.7940
      atlanir  8                 15                146              0.7856
        taban --                 15                146              0.7868
```

Atlayan kurulum her iki etken sayısında da taban çizgisiyle **aynı** kataloğu gösteriyor: 15 kalem
hiç görünmüyor, en çok gösterilen kalem 167 abonenin 146'sına çıkıyor ve gösterimlerin **0,7856'sı**
beş kaleme düşüyor. En yüksek isabeti veren kurulum kataloğun görünen kısmını hiç genişletmiyor;
taban listesini biraz yeniden sıralamaktan ibaret.

Sıfır sayan kurulum sekiz etkenle bunun tersini yapıyor: hiç gösterilmeyen kalem **15'ten 4'e**,
en çok gösterilen kalemin ulaştığı abone 146'dan 84'e, ilk beş kalemin payı 0,7904'ten
**0,4467'ye** iniyor. Kataloğun kuyruğunu yukarı iten tek şey gözlenmeyen hücreyi sıfır saymaktır,
çünkü popüler kalemlere ait sıfırlar da toplama girer ve popülerliğin yönü bastırılır. Bedeli
isabette ödeniyor: 0,2395, tabanın 0,4132 altında.

İki sayı arasında seçim yapılmıyor; kurulum seçildiğinde ikisi birden belirleniyor. Gösterilmeyen
kalem seçilmez, seçilmeyen kalem bir sonraki dönemin eğitim kümesine girmez: bugün 15 kalemi hiç
göstermeyen bir kurulum, yarınki 410 etkileşimin hangi kalemlerden geleceğine de karar vermiştir.

#### Özet

- Dizey ayrıştırma her aboneyi ve her kalemi az sayıda gizli etkene yerleştirir; ortak kalemi
  olmayan iki abone aynı etken yönünde buluşabilir. Eksik girdili dizeyde tek karar gözlenmeyen
  hücrenin ne sayıldığıdır ve yordamın geri kalanı iki kurulumda birebir aynıdır.
- Üç etkende atlayan kurulumun gözlenen hücre hatası 0,2125, sıfır sayanınki 0,7168; tüm hücre
  hatasında sıra tersine döner (0,5055 ve 0,1614). Küçük hata daha iyi sıralama demek değildir.
- Etken sayısı bir ayar değişkenidir ve altı aday denenmiştir: sıfır sayan kurulum 0,6707'den
  0,2395'e bozulur, atlayan kurulum en iyisini dört etkende (0,7006) verir.
- İki kurulumun ilk beş listelerinin ortak kalem sayısı bir etkende 4,02, sekiz etkende 0,86'dır:
  aynı veriden iki ayrı sıralama.
- Kapsama ters yönde hareket eder: atlayan kurulum taban çizgisiyle aynı 15 kalemi hiç göstermez;
  sıfır sayan kurulum bunu 4'e indirir ve ilk beş kalemin payını 0,7868'den 0,4467'ye çeker.

#### Sonraki Adım

Dört derste öneri hep aynı yerden üretildi: **var olan etkileşimlerden**. Taban çizgisi eğitimde
seçilmiş kalemleri saydı, içerik temelli kurulum abonenin seçtiklerinden profil çıkardı, komşuluk
iki abonenin ortak seçimini aradı, ayrıştırma 410 dolu hücreyi etkenlere yerleştirdi. Dördünün de
sessiz varsayımı, aboneden en az bir etkileşimin gelmiş olmasıdır. Oysa aynı kurguda 360 abonenin
80'inin hiçbir etkileşimi yok ve son tablodaki 15 kalem hiçbir aboneye gösterilmiyor; bu iki grup
için dört yöntemin de üreteceği şey aynıdır. Sonraki ders bu iki boşluğu adıyla ele alır — hiç
etkileşimi olmayan abone ve hiç gösterilmemiş kalem — ve her çözümün kaç aboneyi, kaç kalemi
kapsama aldığını ve karşılığında hangi ölçüden ne verdiğini sayar.

### Soğuk Başlangıç

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/soguk-baslangic

Hiç etkileşimi olmayan abone ve hiç seçilmemiş kalem için önerinin kurulması: 360 abonenin 80'inin hiç, 113'ünün tek etkileşimi vardır ve tek etkileşim gizlenince 193 abone soğuk sayılır, 464 etkileşimlik eğitim kümesinde bir kalem hiç seçilmemiş ve sekiz kalem beşin altındadır. Popülerlik geri dönüşü 193 abonenin tamamını ısıtır ama hepsine aynı beş kalemi verir, öznitelik köprüsü tek soğuk kalemi 114 aboneye ulaştırır ama hiçbir soğuk aboneyi ısıtmaz, üç soruluk giriş 339 soruyla 71 aboneyi ısıtıp kapsamayı 5'ten 13 kaleme çıkarır ve ilk beş isabetini 0,5752'den 0,5487'ye düşürür.

Dört derste öneri hep var olan etkileşimlerden üretildi: içerik temelli yaklaşım kalemin
özniteliklerinden, işbirlikçi süzgeçleme komşu abonelerin geçmişinden, dizey ayrıştırma gizli
etkenlerden. Üçünün de sessiz bir ortak koşulu vardı — abonenin dolu bir satırı olması. Satırı
tümüyle boş olan abone ile sütunu tümüyle boş olan kalem için ne yapılacağı sorulmadı.

Bu dersin ölçtüğü şey bir doğruluk değil, **ısıtmadır**: kaç abone ve kaç kalem soğuk ve her
çözüm bunların kaçını öneri üretilebilir hale getiriyor. Her ısıtmanın bir bedeli vardır ve bedel
aynı satırda sayılır.

- **ON41. Kurgu birinci derste kurulan bloğun aynısıdır** ve aynı sayıları verir: 360 abone,
  24 hizmet kalemi, 8.640 hücre, tohum 20260218; 3.002 gösterim, 577 etkileşim, 218 puan.
- **ON42. Soğuk abone, eğitim kümesinde hiç etkileşimi olmayan abonedir.** Soğuk kalem, eğitim
  kümesinde hiç seçilmemiş kalemdir.
- **ON43. Soğuk protokol ayırmayı değiştirir.** Hiç etkileşimi olmayan abonenin doğru cevabı da
  yoktur ve ölçüye giremez. Tek etkileşimi olan abonenin o tek etkileşimi gizlenir: satırı boşalır,
  doğru cevabı bilinir. Geriye kalan etkileşimler eğitim kümesidir.
- **ON44. Taban çizgisi, eğitimde en çok seçilen beş kalemi herkese sunmaktır.** Birinci derste
  kurulan taban budur; soğuk abone için elde başka hiçbir bilgi olmadığından burada da o seçilir.
- **ON45. Isıtma, bir çözümün soğuk aboneyi ya da soğuk kalemi öneri üretilebilir hale
  getirmesidir** ve sayısı kaç abone, kaç kalemdir.
- **ON46. Bedel üç sayıyla yazılır:** sorulan soru sayısı, cevapsız kalan abone sayısı ve
  önerilerin yayıldığı ayrı kalem sayısı.
- **ON47. Üç soruluk giriş kurgudur:** ilgilendiğiniz alan, ikinci alan, başvuru sıklığı.
  Cevaplama oranı 0,62'dir ve kurgunun bir parametresidir.
- **ON48. Ölçüt ilk beş isabetidir**; tanımı birinci derste kuruldu ve burada tekrarlanmaz.

#### Soğuk Olanı Saymak

```python
# ORTAK -- KURGUDUR. Abone x hizmet kalemi etkilesim dizeyi ders icinde kurulur.
# Gosterim, secim ve puan ayri ayri uretilir: bir kalem once GOSTERILIR, sonra
# secilebilir, ancak secilirse puanlanabilir.
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


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {  # kesimin alanlara egilimi; yalniz uretecin icinde kullanilir
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]   # kalemin kendi doyuruculugu

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, GOSTERIM, ETKILESIM, PUAN, EGILIM = [], {}, {}, {}, {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        EGILIM[(a, j)] = egilim
        if r() < min(0.95, pay ** 0.55 * 0.72):        # sunulmus mu
            GOSTERIM[(a, j)] = 1
            if r() < egilim:                            # secilmis mi
                ETKILESIM[(a, j)] = r()                 # deger: sira damgasi
                if r() < 0.32:                          # puan birakmis mi
                    ham = (0.16 + 0.50 * KESIM[kesim][alan] / 1.9
                           + 0.42 * KALITE[j] + 0.26 * (r() - 0.5))
                    PUAN[(a, j)] = max(1, min(5, int(round(ham * 5))))
        else:
            r(), r(), r()
kayit = {}
for (a, j), damga in ETKILESIM.items():
    kayit.setdefault(a, []).append((damga, j))
SIFIR = [a for a in ABONE if a not in kayit]
TEKIL = sorted(a for a in kayit if len(kayit[a]) == 1)
COKLU = [a for a in kayit if len(kayit[a]) >= 2]
GIZLI = {a: kayit[a][0][1] for a in TEKIL}          # SOGUK PROTOKOL: tek etkilesim gizlenir
EGIT = dict(((a, j), 1) for a in COKLU for _, j in kayit[a])
secim = [0] * N_KALEM
for (a, j) in EGIT:
    secim[j] += 1
SOGUK_K = [j for j in range(N_KALEM) if secim[j] == 0]
AZ_K = sorted(range(N_KALEM), key=lambda j: (secim[j], j))
SIRALI = sorted(range(N_KALEM), key=lambda j: (-secim[j], j))
print(f"{N_ABONE} abone: {len(SIFIR)} sifir etkilesim, {len(TEKIL)} tek etkilesim, "
      f"{len(COKLU)} iki ve ustu")
print(f"soguk abone {len(SIFIR) + len(TEKIL)}; olculebilen {len(TEKIL)} abone, "
      f"egitim kumesi {len(EGIT)} etkilesim")
print(f"soguk kalem {len(SOGUK_K)}: {', '.join(KALEM[j][0] for j in SOGUK_K)}; "
      f"bes etkilesimin altinda {sum(1 for j in range(N_KALEM) if secim[j] < 5)} kalem")
print("taban listesi (egitimde en cok secilen bes kalem):")
for j in SIRALI[:5]:
    print(f"  {KALEM[j][0]:26s} {secim[j]:3d}")
```

```
360 abone: 80 sifir etkilesim, 113 tek etkilesim, 167 iki ve ustu
soguk abone 193; olculebilen 113 abone, egitim kumesi 464 etkilesim
soguk kalem 1: mobil_erisim_izni; bes etkilesimin altinda 8 kalem
taban listesi (egitimde en cok secilen bes kalem):
  e_fatura_gecisi             76
  odeme_hatirlatma_sms        71
  otomatik_odeme_talimati     58
  taksitlendirme              46
  uzaktan_okuma_basvurusu     31
```

Üç sayı üç ayrı abone kümesi tarif ediyor. **80** abonenin hiç etkileşimi yok; bu aboneler için
elde ne bir geçmiş var ne de bir doğru cevap, dolayısıyla hiçbir ölçüye giremezler ama sisteme
her gün gelirler. **113** abonenin tek etkileşimi var ve soğuk protokol o tek etkileşimi gizler:
satırları boşalır, doğru cevapları elde kalır. Soğuk abone toplamı **193**, yani 360 abonenin
yarıdan biraz fazlası; ölçülebilen kısmı **113**.

Kalem tarafı çok daha dar: eğitim kümesinde hiç seçilmemiş **1** kalem var, beş etkileşimin
altında kalan **8** kalem. Taban listesi kataloğun eğriliğini olduğu gibi yansıtıyor — beş kalem
464 etkileşimin 282'sini topluyor. Soğuk olmak ile önerilmemek ayrı şeylerdir: önceki derslerde
taban listesinin 24 kalemin **15**'ini hiçbir aboneye önermediği ölçülmüştü, oysa o kalemlerin
etkileşimi vardır ve soğuk değildirler; yalnız sıralamaya girmezler. Soğuk başlangıcın iki yüzü
de aynı ağırlıkta değildir: bu kurguda soğuk abone sorunu soğuk kalem sorunundan çok büyüktür.

#### Kalemi Isıtmak: Öznitelik Köprüsü

Soğuk kalemin etkileşimi yoktur ama **özniteliği vardır**: hangi alana ait olduğu, hangi kanaldan
yürüdüğü, ücretli mi, taahhüt mü istediği, randevu mu gerektirdiği. Köprü bu özniteliklerle soğuk
kalemi sıcak komşularına bağlar ve kalem, o komşularla etkileşmiş abonelerin listesine girer.

```python
import math

ALAN = ["odeme", "olcum", "sozlesme", "bildirim", "tesisat"]
KANAL = ["cevrimici", "saha", "sube"]


def oz(j):
    """Kalem oznitelikleri: alan, kanal ve uc ikili bayrak. Etkilesim gerektirmez."""
    ad, alan, kanal, uc, ta, ra, p = KALEM[j]
    return ([1.0 if alan == x else 0.0 for x in ALAN]
            + [1.0 if kanal == x else 0.0 for x in KANAL] + [float(uc), float(ta), float(ra)])


def benzer(i, j):
    x, y = oz(i), oz(j)
    return sum(p * q for p, q in zip(x, y)) / (
        math.sqrt(sum(p * p for p in x) * sum(q * q for q in y)) + 1e-9)


sicak = [j for j in range(N_KALEM) if secim[j] > 0]
print("icerik temelli kopru; son sutun komsulariyla etkilesmis abone sayisi")
for j in AZ_K[:4]:
    kom = sorted(sicak, key=lambda t: (-benzer(j, t), t))[:2]
    if j in kom:
        kom = [t for t in kom if t != j] + [sorted(sicak, key=lambda t: (-benzer(j, t), t))[2]]
    ulas = set(a for (a, t) in EGIT if t in kom)
    print(f"  {KALEM[j][0]:24s}{secim[j]:2d}  komsu: {KALEM[kom[0]][0]}, "
          f"{KALEM[kom[1]][0]} -> {len(ulas):>3}")
print(f"kopru {len(SOGUK_K)}/{len(SOGUK_K)} soguk kalemi oneri havuzuna sokuyor, "
      f"0/{len(SIFIR) + len(TEKIL)} soguk aboneyi isitiyor")
```

```
icerik temelli kopru; son sutun komsulariyla etkilesmis abone sayisi
  mobil_erisim_izni        0  komsu: e_fatura_gecisi, odeme_hatirlatma_sms -> 114
  tesisat_uygunluk_raporu  1  komsu: basinc_duzenleyici_montaji, tesisat_bakim_paketi ->  10
  gecikme_faizi_muafiyeti  1  komsu: fatura_itiraz_kaydi, taksitlendirme ->  53
  ek_hat_tesisati          1  komsu: tesisat_bakim_paketi, basinc_duzenleyici_montaji ->  10
kopru 1/1 soguk kalemi oneri havuzuna sokuyor, 0/193 soguk aboneyi isitiyor
```

Köprü soğuk kalem sorununu çözüyor: **1/1**. Erişim genişliği ise komşunun kim olduğuna bağlı ve
bu, köprünün gizli bedelidir. `mobil_erisim_izni` en yakın komşuları çevrimiçi bildirim kalemleri
olduğu için **114** aboneye ulaşıyor; `tesisat_uygunluk_raporu` ile `ek_hat_tesisati` ise saha
tesisat kalemlerine komşu oldukları için **10**'ar aboneye. Köprü, kalemi kendi komşularının
yaygınlığı kadar ısıtır — yaygın komşusu olan soğuk kalem hemen ısınır, seyrek komşusu olan soğuk
kalır. Kalemi ısıtmak, sütunun kendi popülerliğini değil, komşusununkini ödünç almaktır.

Son satır asıl sınırı yazıyor: köprü **0/193** soğuk aboneyi ısıtıyor. Kurguda kalemin özniteliği
vardır, abonenin yoktur; abone hakkında bilinen tek şey geçmişidir ve soğuk abonenin geçmişi
boştur. İçerik temelli yaklaşım soğuk başlangıcın yalnız bir yüzünü görür.

#### Aboneyi Isıtmak: Üç Soruluk Giriş

Abone tarafında iki seçenek kalır: tabana düşmek — herkese aynı beş kalemi sunmak — ya da abonenin
kendisine sormak. İkincisi kayıtta olmayan veriyi **üretir** ve bedeli sorudur.

```python
KESIMI = dict((20001 + i, KESIM_AD[int(uretec(TOHUM + 7919 * i)() * 4)])
              for i in range(N_ABONE))
CEVAP = {}
for a in TEKIL:                                     # uc soru: alan, ikinci alan, etkinlik
    q = uretec(TOHUM + 555000 + a)
    if q() < 0.62:                                  # cevaplama orani kurgudur
        s = sorted(KESIM[KESIMI[a]], key=lambda x: -KESIM[KESIMI[a]][x])
        CEVAP[a] = (s[0], s[1], 0.35 + 1.15 * uretec(TOHUM + 7919 * (a - 20001))() ** 1.6)


def taban(a):
    return SIRALI[:5]


def giris(a, ucuncu=False):
    """Cevap verilmisse alan sirasi, verilmemisse taban listesi. MODELDIR."""
    if a not in CEVAP:
        return SIRALI[:5]
    a1, a2, etkinlik = CEVAP[a]
    kat = etkinlik if ucuncu else 1.0               # ucuncu cevap her kalemi esit carpar
    return sorted(range(N_KALEM), key=lambda j: (
        -kat * (secim[j] + 1) ** 0.35 * (3.0 if KALEM[j][1] == a1 else
                                       1.8 if KALEM[j][1] == a2 else 1.0), j))[:5]


print(f"uc soruluk giris: {3 * len(TEKIL)} soru soruldu, {len(CEVAP)} abone cevapladi, "
      f"{len(TEKIL) - len(CEVAP)} abone taban listesine dustu")
for ad, f in (("taban (populerlik)", taban), ("giris, iki cevap", giris),
              ("giris, uc cevap", lambda a: giris(a, True))):
    isabet = sum(1 for a in TEKIL if GIZLI[a] in f(a)) / len(TEKIL)
    kaps = len(set(j for a in TEKIL for j in f(a)))
    print(f"  {ad:<20} ilk bes isabet {isabet:.4f}  kapsama {kaps:>2}/{N_KALEM}")
```

```
uc soruluk giris: 339 soru soruldu, 71 abone cevapladi, 42 abone taban listesine dustu
  taban (populerlik)   ilk bes isabet 0.5752  kapsama  5/24
  giris, iki cevap     ilk bes isabet 0.5487  kapsama 13/24
  giris, uc cevap      ilk bes isabet 0.5487  kapsama 13/24
```

Üç çözümün ısıttığı artık yan yana okunabilir. Popülerlik geri dönüşü **193/193** aboneyi ısıtır
ve **0/1** kalemi; öznitelik köprüsü **0/193** aboneyi ve **1/1** kalemi; üç soruluk giriş
ölçülebilen 113 abonenin **71**'ini ısıtır ve **0/1** kalemi. Hiçbiri iki yüzü birden ısıtmaz.
Soğuk başlangıç tek bir sorun değildir; iki ayrı sorundur ve iki ayrı çözüm ister.

Bedeller de yan yana durur. Giriş için **339** soru soruldu, **42** abone hiç cevap vermeyip
tabana düştü; cevaplama oranının 0,62 olması kurgunun bir seçimidir ve daha düşük bir oran
ısıtılan abone sayısını doğrudan aşağı çeker. Üçüncü sorunun bedeli özel bir tür: başvuru sıklığı
cevabı her kalemi **aynı katsayıyla** çarptığı için sıralamayı hiç değiştirmiyor, isabet de kapsama
da iki cevaplı kurulumla birebir aynı çıkıyor. 113 abonenin her birine sorulan bu üçüncü soru
karşılığında sistemin eline hiçbir sıralama değişikliği geçmiyor. Bir cevabın bilgi taşıması, o
cevabın seçenekleri **birbirinden ayırmasına** bağlıdır.

Tabanın bedeli soruda değil, tekdüzeliktedir: 113 aboneye toplam **5** ayrı kalem önerildi. Giriş
bu sayıyı **13**'e çıkarıyor. Buna karşılık kayıttan okunan ilk beş isabeti tabanda **0,5752**,
girişte **0,5487** — soru sorulduğunda ölçü **düşüyor**. Bu sayıların hangi kayıttan okunduğu
önemlidir: gizlenen tek etkileşim, o abonenin **gösterilmiş** bir kalemle kurduğu tek etkileşimdir
ve gösterim düzeni ağırlıkla kataloğun üst ucunu sunmuştur. Taban tam olarak o üst ucu öneriyor.

#### Özet

- Soğuk başlangıç iki ayrı sorundur: kurgu dizeyde 80 abonenin hiç, 113 abonenin tek etkileşimi
  vardır ve tek etkileşim gizlenince 193 abone soğuk sayılır; kalem tarafında 464 etkileşimlik
  eğitim kümesinde 1 kalem hiç seçilmemiş, 8 kalem beşin altındadır.
- Popülerlik geri dönüşü 193/193 aboneyi ısıtır, hiçbir kalemi ısıtmaz ve hepsine aynı 5 kalemi
  verir; öznitelik köprüsü 1/1 kalemi ısıtır, hiçbir soğuk aboneyi ısıtmaz ve kalemi komşusunun
  yaygınlığı kadar taşır: 114 aboneye karşı 10 abone.
- Üç soruluk giriş 339 soru karşılığında 71 aboneyi ısıtır, 42 abone cevapsız kalır, kapsama
  5'ten 13 kaleme çıkar; üçüncü soru her kalemi aynı katsayıyla çarptığı için sıralamayı hiç
  değiştirmez.
- Kayıttan okunan ilk beş isabeti tabanda 0,5752, girişte 0,5487'dir; bu kayıt yalnız gösterilmiş
  kalemlerdeki etkileşimlerden oluşur.

#### Sonraki Adım

Bu dersin son tablosu tuhaf bir şey söylüyor: soğuk aboneye sorular sorup önerileri onun kendi
söylediği alana çevirmek, kayıttan okunan sayıyı **düşürdü**. İki açıklama var. Ya sorular işe
yaramıyor, ya da ölçü yanlış yerden okunuyor — çünkü kayıttaki her etkileşim, kararı sistemin
değil **gösterim düzeninin** verdiği bir anda doğdu ve gösterilmemiş bir kalemin etkileşimi
kayıtta hiç görünmüyor. Sonraki ders sıralama ölçütlerini kurar: ilk k, kesme noktası ve konum
indirimi. Sonra aynı sıralamayı iki kez ölçer — bir kez bu kayıt üzerinde, bir kez de sıralamanın
kendisi neyin gösterileceğine karar verirken. İki sayı yan yana basılır.

### Öneri Değerlendirmesi

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/oneri-degerlendirmesi

Sıralama ölçütlerinin kurulması ve aynı sıralamanın iki ayrı sayı vermesi: ilk k isabeti kesme noktasına göre üç düzenin sırasını değiştiriyor, k=3'te sonuncu olan düzen k=5'te birinci ve k=10'da üçü de ayırt edilemiyor, konum indirimi k=5'in birincisini sonuncuya düşürüyor. Çevrimdışı kayıtta 0,6886 ile birinci olan düzen, kendi kararlarıyla veri toplamaya başlayınca abone başına 1,4563 etkileşimle sonuncu ve tabanın da altında kalıyor; sınav hücrelerinin 0,6305'i hiç gösterilmemiştir, ayrılmış kalemlerin 0,5868'i beş kalemden gelir ve üç kalem ayrılmış kümede hiç görünmez.

Önceki ders soğuk aboneye sorular sorup önerileri onun kendi söylediği alana çevirdi ve kayıttan
okunan sayı **düştü**. Açıklama iki türlüydü: ya sorular işe yaramıyordu, ya da ölçü yanlış yerden
okunuyordu. Bu ders önce ölçünün kendisini kurar, sonra aynı sıralamayı iki ayrı yerde ölçer.

Kurulacak üç şey var. **İlk k isabeti** bir sıralamanın ilk k önerisinde doğru kalemi bulup
bulmadığını sayar. **Kesme noktası** k'nın kendisidir ve bir ölçüm kararıdır, verinin bir özelliği
değil. **Konum indirimi** ilk k içindeki konumu da hesaba katar: birinci sıradaki isabet ile
beşinci sıradaki isabet aynı değildir.

- **ON49. Kurgu birinci derste kurulan bloğun aynısıdır** ve aynı sayıları verir: 360 abone,
  24 kalem, 3.002 gösterim, 577 etkileşim. Ayırma da birinci dersteki ayırmadır: en az iki
  etkileşimi olan abonenin sıra damgası en büyük etkileşimi ayrılmış kümeye gider.
- **ON50. Karşılaştırılan üç düzen aynı ailedendir** ve tek bir ayar değişkeniyle ayrılır:
  puan, kalemin eğitimdeki seçim sayısı ile abonenin o alandaki geçmiş payının çarpımıdır.
  `w = 0` saf taban çizgisidir — kişiselleştirme yoktur; `w = 1` ve `w = 2` alan payını sırasıyla
  hafif ve güçlü ağırlıklandırır.
- **ON51. Taban çizgisi `w = 0`'dır**, yani eğitimde en çok seçilen kalem sırası. Aile tek olduğu
  için üç sayı arasındaki fark yalnız ağırlıktan gelir.
- **ON52. Konum indirimi ağırlığı `1 / log2(1 + konum)`'dur**; konum birden başlar. Ölçüt tüm
  sıralama üzerinden okunur, kesme noktası uygulanmaz.
- **ON53. Çevrimdışı ölçüm kayıt üzerindedir:** düzen sıralar, ayrılmış kalem doğru cevap sayılır.
  Kayıtta yalnız gösterilmiş kalemlerin etkileşimi vardır.
- **ON54. Çevrimiçi ölçümde sıralamanın kendisi neyin gösterileceğine karar verir.** Her düzen
  360 abonenin her birine kendi ilk beşini gösterir, sonuç kurgunun eğiliminden çekilir, ölçü
  yirmi dönemin abone başına etkileşim ortalamasıdır. Yönü büyük iyidir.
- **ON55. Kurgunun kendi eğilimi bir yöntem değil, tavandır.** Yalnız ölçüm için okunur ve
  çevrimiçi sayının ulaşabileceği sınırı verir.
- **ON56. Deneme tasarımı bu derste yapılmaz.** Örneklem büyüklüğü, erken durdurma ve çoklu
  karşılaştırma hesabı Kullanıcı Deneyimi ve Davranış Tasarımı kursunun Ölçme konusunda ve
  Uygulamalı İstatistik kursunda kuruldu; burada yalnız geri beslemeden doğan fark ölçülür.

#### İlk k, Kesme Noktası ve Konum İndirimi

```python
# ORTAK -- KURGUDUR. Abone x hizmet kalemi etkilesim dizeyi ders icinde kurulur.
# Gosterim, secim ve puan ayri ayri uretilir: bir kalem once GOSTERILIR, sonra
# secilebilir, ancak secilirse puanlanabilir.
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


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {  # kesimin alanlara egilimi; yalniz uretecin icinde kullanilir
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]   # kalemin kendi doyuruculugu

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, GOSTERIM, ETKILESIM, PUAN, EGILIM = [], {}, {}, {}, {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        EGILIM[(a, j)] = egilim
        if r() < min(0.95, pay ** 0.55 * 0.72):        # sunulmus mu
            GOSTERIM[(a, j)] = 1
            if r() < egilim:                            # secilmis mi
                ETKILESIM[(a, j)] = r()                 # deger: sira damgasi
                if r() < 0.32:                          # puan birakmis mi
                    ham = (0.16 + 0.50 * KESIM[kesim][alan] / 1.9
                           + 0.42 * KALITE[j] + 0.26 * (r() - 0.5))
                    PUAN[(a, j)] = max(1, min(5, int(round(ham * 5))))
        else:
            r(), r(), r()
import math

# 01'in ayirmasi: en az iki etkilesimi olan abonenin sira damgasi en buyuk etkilesimi ayrilir.
kayit = {}
for (a, j), damga in ETKILESIM.items():
    kayit.setdefault(a, []).append((damga, j))
EGIT, AYRI, GORULEN = {}, {}, {}
for a, lst in kayit.items():
    lst.sort()
    if len(lst) >= 2:
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN.setdefault(a, set()).add(j)
SINAV = sorted(AYRI)
secim = [0] * N_KALEM
for (a, j) in EGIT:
    secim[j] += 1
AL = [KALEM[j][1] for j in range(N_KALEM)]


def siralayici(w):
    """Populerlik x abonenin gecmisteki alan payi. w=0 saf taban cizgisidir. MODELDIR."""
    def puan(a):
        c = {}
        for i in GORULEN.get(a, set()):
            c[AL[i]] = c.get(AL[i], 0) + 1
        return [secim[j] * (1.0 + w * c.get(AL[j], 0)) for j in range(N_KALEM)]
    return puan


def sirala(puan, a):
    s = puan(a)
    return sorted((j for j in range(N_KALEM) if j not in GORULEN.get(a, set())),
                  key=lambda j: (-s[j], j))


DUZEN = (("taban, w=0", siralayici(0.0)), ("hafif, w=1", siralayici(1.0)),
         ("guclu, w=2", siralayici(2.0)))
print(f"egitim {len(EGIT)} etkilesim, ayrilmis {len(SINAV)} abone; cevrimdisi kayit uzerinde")
print("duzen        k=1     k=3     k=5     k=10    konum ind.  kapsama@5")
for ad, p in DUZEN:
    d = [sirala(p, a) for a in SINAV]
    k = [sum(1 for t, a in zip(d, SINAV) if AYRI[a] in t[:n]) / len(SINAV)
         for n in (1, 3, 5, 10)]
    ki = sum(1.0 / math.log2(2 + t.index(AYRI[a])) for t, a in zip(d, SINAV)) / len(SINAV)
    kaps = len(set(j for t in d for j in t[:5]))
    print(f"{ad:<12} {k[0]:.4f}  {k[1]:.4f}  {k[2]:.4f}  {k[3]:.4f}  {ki:.4f}      "
          f"{kaps:>2}/{N_KALEM}")
```

```
egitim 410 etkilesim, ayrilmis 167 abone; cevrimdisi kayit uzerinde
duzen        k=1     k=3     k=5     k=10    konum ind.  kapsama@5
taban, w=0   0.2575  0.5509  0.6527  0.9162  0.5736       9/24
hafif, w=1   0.2635  0.5569  0.6647  0.9162  0.5766      12/24
guclu, w=2   0.2635  0.5090  0.6886  0.9102  0.5727      13/24
```

Üç düzenin sırası **kesme noktasına göre değişiyor**. `k = 3`'te güçlü düzen sonuncudur
(**0,5090**), taban ondan öndedir. `k = 5`'te güçlü düzen birincidir (**0,6886**) ve tabanı
0,0359 geçer. `k = 10`'da üçü de ayırt edilemez hale gelir: 0,9162, 0,9162 ve 0,9102. Aynı üç
sıralama, aynı kayıt, üç ayrı kesme noktasında üç ayrı sıra. Kesme noktası raporlanan sayının
bir parçası değil, o sayıyı üreten karardır; yazılmadığı sürece ilk k isabeti okunamaz.

Konum indirimi dördüncü bir sıra veriyor. Bu ölçütte güçlü düzen **0,5727** ile sonuncudur, oysa
`k = 5`'te birinciydi. Sebep ilk k isabetinin görmediği şeydir: güçlü düzen doğru kalemi ilk beşe
sokuyor ama beşin **içinde daha aşağıya** koyuyor. İlk k isabeti kesme noktasının içindeki her
konumu eşit sayar; konum indirimi saymaz. İki ölçüt aynı sıralamayı iki ayrı sırada bitirir ve
ikisi de yanlış değildir — farklı şeyleri ölçerler.

Beşinci sütunun yanında altıncı duruyor: kapsama. Taban 167 aboneye toplam **9** ayrı kalem
öneriyor, güçlü düzen **13**. Üç düzende de kataloğun en az on bir kalemi hiçbir aboneye ilk beşte
hiç görünmüyor.

#### Aynı Sıralama, Kendi Verisini Toplarken

Buraya kadarki bütün sayılar tek bir kayıttan okundu ve o kayıt bu üç düzenin hiçbiri tarafından
toplanmadı. Şimdi karar el değiştiriyor: her düzen kendi ilk beşini gösterir ve sonucunu görür.

```python
DONEM = 20


def cevrimici(puan, k=5):
    """Siralamanin kendisi neyin gosterilecegine karar verir; sonuc kurgudan cekilir."""
    top, gos = 0, set()
    for a in ABONE:
        ilk = sirala(puan, a)[:k]
        gos.update(ilk)
        for d in range(DONEM):
            r = uretec(TOHUM + 880000 + a * 37 + d * 911)
            for j in ilk:
                if r() < EGILIM[(a, j)]:
                    top += 1
    return top / (len(ABONE) * DONEM), len(gos)


print(f"cevrimici: her duzen kendi ilk besini gosterir, {DONEM} donem, {len(ABONE)} abone")
for ad, p in DUZEN:
    e, g = cevrimici(p)
    bek = sum(sum(EGILIM[(a, j)] for j in sirala(p, a)[:5]) for a in ABONE) / len(ABONE)
    print(f"  {ad:<12} abone basina etkilesim {e:.4f}  (beklenen {bek:.4f})  "
          f"gosterilen kalem {g}/{N_KALEM}")
```

```
cevrimici: her duzen kendi ilk besini gosterir, 20 donem, 360 abone
  taban, w=0   abone basina etkilesim 1.4581  (beklenen 1.4519)  gosterilen kalem 9/24
  hafif, w=1   abone basina etkilesim 1.4788  (beklenen 1.4795)  gosterilen kalem 12/24
  guclu, w=2   abone basina etkilesim 1.4563  (beklenen 1.4560)  gosterilen kalem 13/24
```

Çevrimdışı sıralama şuydu: taban 0,6527, hafif 0,6647, güçlü 0,6886 — güçlü birinci. Çevrimiçi
sıralama şudur: güçlü **1,4563**, taban **1,4581**, hafif **1,4788** — güçlü **sonuncu** ve
tabanın da altında. Çevrimdışı kayıtta birinci olan düzen, kendi kararlarını vermeye başladığında
hiç kişiselleştirmeyen tabanı bile geçemiyor.

Farklar küçüktür ve öyle olduğu yazılmalıdır: çevrimiçi en iyi ile en kötü arasında abone başına
0,0225 etkileşim vardır. Ölçülen şey farkın büyüklüğü değil, **sıranın taşınmaması**. Yirmi dönem
boyunca çekilen sayı beklenen değerin binde beşi içinde kaldığı için bu sıralama çekim
gürültüsünden gelmiyor; düzenlerin kendisinden geliyor.

#### Kaydın Göremediği

```python
from collections import Counter

c = Counter(AYRI.values())
gorunmez = sum(1 for a in SINAV for j in range(N_KALEM) if (a, j) not in GOSTERIM)
print(f"kaydin gorebildigi: {len(SINAV)} abone x {N_KALEM} kalem = {len(SINAV) * N_KALEM} "
      f"hucrenin {gorunmez}'i ({gorunmez / (len(SINAV) * N_KALEM):.4f}) hic gosterilmemis")
ustbes = sorted(range(N_KALEM), key=lambda j: (-secim[j], j))[:5]
print(f"ayrilmis kalemlerin {sum(c[j] for j in ustbes) / len(AYRI):.4f}'i egitimde en cok "
      f"secilen bes kalemden; {sum(1 for j in range(N_KALEM) if c[j] == 0)} kalem "
      f"ayrilmis kumede hic gorunmuyor")


def kurgu_egilimi(a):
    """Bir yontem degil: kurgunun kendi egilimi. Cevrimici tavani verir."""
    return [EGILIM[(a, j)] for j in range(N_KALEM)]


e, g = cevrimici(kurgu_egilimi)
d = [sirala(kurgu_egilimi, a) for a in SINAV]
print(f"kurgu egilimi (cevrimici tavan): cevrimici {e:.4f}, gosterilen kalem {g}/{N_KALEM}")
print(f"  ayni siralamanin cevrimdisi ilk bes isabeti "
      f"{sum(1 for t, a in zip(d, SINAV) if AYRI[a] in t[:5]) / len(SINAV):.4f}")
```

```
kaydin gorebildigi: 167 abone x 24 kalem = 4008 hucrenin 2527'i (0.6305) hic gosterilmemis
ayrilmis kalemlerin 0.5868'i egitimde en cok secilen bes kalemden; 3 kalem ayrilmis kumede hic gorunmuyor
kurgu egilimi (cevrimici tavan): cevrimici 1.6606, gosterilen kalem 13/24
  ayni siralamanin cevrimdisi ilk bes isabeti 0.7485
```

Ayrılma buradan geliyor. Sınav kümesinin **0,6305**'i hiç gösterilmemiş hücredir: o hücreler için
kayıtta ne bir seçim vardır ne de bir ret, yalnız boşluk. Çevrimdışı ölçüm bu boşluğu **ret gibi**
saymak zorundadır, çünkü elinde başka bir okuma yok. Bir düzen ne kadar çok gösterilmemiş kalem
önerirse çevrimdışı o kadar cezalanır — o kalem aboneye uygun olsa bile.

İkinci satır aynı şeyi doğru cevap tarafından gösteriyor: ayrılmış kalemlerin **0,5868**'i
eğitimde en çok seçilen beş kalemden geliyor ve **3** kalem ayrılmış kümede hiç görünmüyor. O üç
kalemi öneren bir düzen çevrimdışı hiçbir koşulda puan alamaz. Doğru cevabın kendisi, geçmişteki
gösterim kararlarının bir izidir.

Üçüncü satır ölçünün tavanını veriyor. Kurgunun kendi eğilimiyle sıralamak çevrimiçi **1,6606**
veriyor; taban 1,4581'de, en iyi düzen 1,4788'de duruyor. Üç düzenin en iyisi tavanla taban
arasındaki açıklığın onda birini kapatıyor. Aynı sıralamanın çevrimdışı ilk beş isabeti
**0,7485**'tir, yani çevrimdışı ölçüt tavanı bile 1'e çıkarmaz — kayıt, en iyi olası sıralamaya da
yalnız gösterilmiş kalemler üzerinden puan verebilir.

#### Özet

- İlk k isabeti kesme noktası yazılmadan okunamaz: güçlü düzen k=3'te sonuncu (0,5090), k=5'te
  birinci (0,6886), k=10'da üç düzen ayırt edilemez (0,9162 / 0,9162 / 0,9102).
- Konum indirimi ilk k'nın görmediğini görür: k=5'in birincisi olan düzen konum ağırlığında
  0,5727 ile sonuncudur, çünkü doğru kalemi ilk beşin aşağısına koyar.
- Çevrimdışı birinci olan düzen (0,6886) çevrimiçi sonuncudur (1,4563) ve hiç kişiselleştirmeyen
  tabanın (1,4581) altında kalır; sıra taşınmaz.
- Ayrılmanın sebebi kaydın kendisidir: sınav hücrelerinin 0,6305'i hiç gösterilmemiştir, ayrılmış
  kalemlerin 0,5868'i beş kalemden gelir, 3 kalem ayrılmış kümede hiç görünmez.
- Çevrimiçi tavan 1,6606'dır; taban 1,4581 ile en iyi düzen 1,4788 arasındaki fark, tavanla taban
  arasındaki açıklığın yaklaşık onda biridir.

#### Sonraki Adım

Bu derste her düzen kayda **bir kez** baktı ve kararını **bir kez** verdi. Gerçek bir sistemde
karar tekrarlanır: gösterilen kalem etkileşim üretir, etkileşim kaydı büyütür, büyüyen kayıt bir
sonraki gösterimi belirler. Kapsama sütunu bu döngünün nereye gittiğini şimdiden söylüyor — taban
24 kalemin 9'unu, güçlü düzen 13'ünü gösteriyor ve gösterilmeyen kalem bir sonraki turda daha da
az sebeple gösterilecek. Sonraki ders bu döngüyü birkaç düzine dönem çalıştırır: kaç kalem hiç
gösterilmez oluyor, popülerlik yanlılığı ne kadar büyüyor ve çeşitliliği artırmanın kısa dönem
bedeli ile uzun dönem kazancı hangi dönemde eşitleniyor.

### Çeşitlilik ve Yankı Odası

Source: https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/cesitlilik-ve-yanki-odasi

Sistem kendi önerileriyle beslendiğinde kapsamanın daralması ve popülerlik yanlılığının büyümesi: kırk sekiz dönem boyunca dar düzen sekizinci dönemden sonra her dönem 24 kalemin 8'ini hiç göstermiyor, altı kalem son yirmi dört dönemin hiçbirinde görünmüyor ve ilk beş kalemin gösterim payı 0,4711'den 0,5461'e çıkıyor. Beşinci yuvayı çeşitliliğe ayıran düzen ilk on bir dönemde abone başına 1,0000 etkileşim kaybediyor, on ikinci dönemde koşulları değişen kalemi bulup öne geçiyor, kümülatif olarak yirmi yedinci dönemde eşitleniyor ve kırk sekizinci dönemde 1,2250 önde bitiriyor.

Önceki derste her düzen kayda bir kez baktı. Gerçek bir sistemde karar tekrarlanır: gösterilen
kalem etkileşim üretir, etkileşim kaydı büyütür, büyüyen kayıt bir sonraki gösterimi belirler.
Bu ders o döngüyü kırk sekiz dönem çalıştırır ve kapsamanın nereye gittiğini sayar.

- **ON57. Kurgu birinci derste kurulan bloğun aynısıdır**; döngünün başlangıç kaydı o kurgunun
  3.002 gösterimi ile 577 etkileşimidir.
- **ON58. Sistem her dönem her aboneye beş kalem gösterir** ve puanı, abonenin öbeğinde o kalemin
  gösterim başına etkileşim oranı olarak kestirir: `(etkileşim + 0,5) / (gösterim + 5)`. Öbek,
  abonenin geçmişindeki baskın alandır ve sistemin gördüğü tek abone özelliğidir.
- **ON59. İki düzen karşılaştırılır.** Dar düzen beş yuvayı da kestirime verir ve **taban
  çizgisidir**, çünkü hiçbir çeşitlilik kuralı içermez. Geniş düzen dördünü kestirime verir,
  beşinciyi o ana dek en az gösterilmiş kaleme ayırır.
- **ON60. On ikinci dönemde bir kalemin karşılığı sekiz katına çıkar** ve 0,95 tavanına dayanır;
  bu ortamın değiştiği andır ve hiçbir düzene haber verilmez.
- **ON61. Üç sayı her dönem yazılır:** abone başına etkileşim (büyük iyi), o dönem hiç
  gösterilmeyen kalem sayısı (küçük iyi) ve gösterimlerin en çok gösterilen beş kalemdeki payı
  (küçük iyi) — sonuncusu **popülerlik yanlılığının** ölçüsüdür. Seçim yanlılığı Uygulamalı
  İstatistik kursundadır.

#### Kendi Kaydıyla Beslenen Düzen

```python
# ORTAK -- KURGUDUR. Abone x hizmet kalemi etkilesim dizeyi ders icinde kurulur.
# Gosterim, secim ve puan ayri ayri uretilir: bir kalem once GOSTERILIR, sonra
# secilebilir, ancak secilirse puanlanabilir.
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


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {  # kesimin alanlara egilimi; yalniz uretecin icinde kullanilir
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]   # kalemin kendi doyuruculugu

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, GOSTERIM, ETKILESIM, PUAN, EGILIM = [], {}, {}, {}, {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        EGILIM[(a, j)] = egilim
        if r() < min(0.95, pay ** 0.55 * 0.72):        # sunulmus mu
            GOSTERIM[(a, j)] = 1
            if r() < egilim:                            # secilmis mi
                ETKILESIM[(a, j)] = r()                 # deger: sira damgasi
                if r() < 0.32:                          # puan birakmis mi
                    ham = (0.16 + 0.50 * KESIM[kesim][alan] / 1.9
                           + 0.42 * KALITE[j] + 0.26 * (r() - 0.5))
                    PUAN[(a, j)] = max(1, min(5, int(round(ham * 5))))
        else:
            r(), r(), r()
AL = [KALEM[j][1] for j in range(N_KALEM)]
SAY = {a: {} for a in ABONE}
for (a, j) in ETKILESIM:                            # abone obegi: gecmisteki baskin alan
    SAY[a][AL[j]] = SAY[a].get(AL[j], 0) + 1
OBEK = {a: (max(sorted(c), key=lambda x: c[x]) if c else "yok") for a, c in SAY.items()}
DONEM, K, DEGISIM, ARTAN = 48, 5, 12, 9             # 12. donemde 9. kalemin karsiligi degisir


def dongu(genis):
    """Sistem kendi kaydiyla besleniyor: gosterdigini olcuyor, olctugunu gosteriyor."""
    gos, etk = {}, {}
    for (a, j) in GOSTERIM:
        gos[(OBEK[a], j)] = gos.get((OBEK[a], j), 0) + 1
    for (a, j) in ETKILESIM:
        etk[(OBEK[a], j)] = etk.get((OBEK[a], j), 0) + 1
    toplam, satir = [0] * N_KALEM, []
    for d in range(1, DONEM + 1):
        don_gos, don_etk = [0] * N_KALEM, 0
        for a in ABONE:
            o = OBEK[a]
            s = sorted(range(N_KALEM),
                       key=lambda j: (-(etk.get((o, j), 0) + 0.5)
                                      / (gos.get((o, j), 0) + 5.0), j))
            ilk = s[:K] if not genis else s[:K - 1] + [        # besinci yuva cesitlilige
                min((j for j in range(N_KALEM) if j not in s[:K - 1]),
                    key=lambda j: (toplam[j], (j + a + d) % N_KALEM))]
            r = uretec(TOHUM + 660000 + a * 53 + d * 1301)
            for j in ilk:
                don_gos[j] += 1
                toplam[j] += 1
                gos[(o, j)] = gos.get((o, j), 0) + 1
                e = EGILIM[(a, j)]
                if j == ARTAN and d >= DEGISIM:
                    e = min(0.95, e * 8.0)
                if r() < e:
                    don_etk += 1
                    etk[(o, j)] = etk.get((o, j), 0) + 1
        u5 = sorted(range(N_KALEM), key=lambda j: -don_gos[j])[:5]
        satir.append((don_etk / len(ABONE),
                      [j for j in range(N_KALEM) if don_gos[j] == 0],
                      sum(don_gos[j] for j in u5) / sum(don_gos),
                      don_gos[ARTAN]))
    return satir


DAR = dongu(False)
print(f"dar duzen: her donem ilk {K}, {DONEM} donem, {len(ABONE)} abone")
print(f"{DEGISIM}. donemde {KALEM[ARTAN][0]} kaleminin karsiligi sekiz katina cikar")
print("donem  etkilesim/abone  o donem hic gosterilmeyen  ilk bes gosterim payi")
for d in (1, 8, 24, 48):
    e, yok, p, n = DAR[d - 1]
    print(f"  {d:>2}       {e:.4f}                {len(yok):>2}/{N_KALEM}                {p:.4f}")
KILIT = set(range(N_KALEM))
for x in DAR[DONEM // 2:]:
    KILIT &= set(x[1])
print(f"son {DONEM - DONEM // 2} donemin HEPSINDE gosterilmeyen kalem: {len(KILIT)}/{N_KALEM}")
```

```
dar duzen: her donem ilk 5, 48 donem, 360 abone
12. donemde tuketim_raporu_aboneligi kaleminin karsiligi sekiz katina cikar
donem  etkilesim/abone  o donem hic gosterilmeyen  ilk bes gosterim payi
   1       1.2250                 0/24                0.4711
   8       1.3917                 8/24                0.5461
  24       1.4583                 8/24                0.5461
  48       1.3194                 8/24                0.5461
son 24 donemin HEPSINDE gosterilmeyen kalem: 6/24
```

Birinci dönemde 24 kalemin hepsi gösteriliyor. Sekizinci dönemde sekiz kalem düşüyor; sayı orada
**8/24**'te donuyor ve kırk sekizinci döneme kadar oynamıyor. Altı kalem son yirmi dört dönemin
**hiçbirinde** gösterilmiyor. Popülerlik yanlılığı aynı hareketin öbür yüzü: ilk beş kalemin
gösterim payı 0,4711'den **0,5461**'e çıkıp orada duruyor.

Döngü kendini kapatıyor. Gösterilmeyen kalem hakkında yeni bilgi doğmuyor, kestirimi düşük
kalıyor, düşük kestirim onu bir sonraki dönem de dışarıda bırakıyor: kalem kötü olduğu için değil
**ölçülmediği için** dışarıda kalıyor. On ikinci dönemde ortam değişiyor ama dar düzen göremiyor,
çünkü sisteme bunu söyleyecek tek yol o kalemin gösterilmesidir.

#### Beşinci Yuvayı Çeşitliliğe Vermek

```python
GENIS = dongu(True)
GENISKILIT = set(range(N_KALEM))
for x in GENIS[DONEM // 2:]:
    GENISKILIT &= set(x[1])
print(f"genis duzen: ilk {K - 1} model, besinci yuva en az gosterilen kaleme")
print("donem  etkilesim/abone  o donem hic gosterilmeyen  ilk bes gosterim payi  degisen kalem")
for d in (1, 8, 24, 48):
    e, yok, p, n = GENIS[d - 1]
    print(f"  {d:>2}       {e:.4f}                {len(yok):>2}/{N_KALEM}                "
          f"{p:.4f}        {n:>3}")
bedel = sum(GENIS[d][0] - DAR[d][0] for d in range(DEGISIM - 1))
kazanc = sum(GENIS[d][0] - DAR[d][0] for d in range(DEGISIM - 1, DONEM))
esit = [d + 1 for d in range(DONEM)
        if sum(x[0] for x in GENIS[:d + 1]) > sum(x[0] for x in DAR[:d + 1])]
print(f"kisa donem bedeli (1-{DEGISIM - 1}. donem toplami): abone basina {bedel:+.4f}")
print(f"uzun donem kazanci ({DEGISIM}-{DONEM}. donem toplami): abone basina {kazanc:+.4f}")
print(f"kumulatif esitlenme donemi: {esit[0] if esit else 'yok'}; {DONEM}. donemde "
      f"kumulatif fark {sum(x[0] for x in GENIS) - sum(x[0] for x in DAR):+.4f}")
print(f"karsiligi degisen kalem {DONEM}. donemde: dar duzen {DAR[-1][3]} gosterim, "
      f"genis duzen {GENIS[-1][3]}")
print(f"toplanan veri -- dar: {len(KILIT)} kalem hic gosterilmiyor, ilk bes gosterim payi "
      f"{DAR[-1][2]:.4f}; genis: {len(GENISKILIT)} kalem, {GENIS[-1][2]:.4f}")
```

```
genis duzen: ilk 4 model, besinci yuva en az gosterilen kaleme
donem  etkilesim/abone  o donem hic gosterilmeyen  ilk bes gosterim payi  degisen kalem
   1       1.1833                 0/24                0.4611         37
   8       1.2722                 0/24                0.5011         81
  24       1.4806                 0/24                0.4989         97
  48       1.3889                 0/24                0.4989         97
kisa donem bedeli (1-11. donem toplami): abone basina -1.0000
uzun donem kazanci (12-48. donem toplami): abone basina +2.2250
kumulatif esitlenme donemi: 27; 48. donemde kumulatif fark +1.2250
karsiligi degisen kalem 48. donemde: dar duzen 17 gosterim, genis duzen 97
toplanan veri -- dar: 6 kalem hic gosterilmiyor, ilk bes gosterim payi 0.5461; genis: 0 kalem, 0.4989
```

Bedel ilk on bir dönemde ödeniyor: geniş düzen abone başına **1,0000** etkileşim geride
bitiriyor, çünkü beş yuvadan biri kestirimin en iyisine değil en az bilinene gidiyor. Buna
karşılık kapsama hiç daralmıyor ve ilk beş kalemin payı **0,4989**'da kalıyor.

On ikinci dönemde hesap dönüyor. Geniş düzen, karşılığı değişen kalemi zaten dönüşümlü yuvada
gösterdiği için değişimi **aynı dönem** fark ediyor. Kırk sekizinci dönemde o kalem geniş düzende
**97**, dar düzende **17** gösterim alıyor. On iki ile kırk sekiz arasındaki toplam kazanç abone
başına **+2,2250**; ilk on bir dönemin **−1,0000**'lık bedeliyle birlikte kümülatif fark
**yirmi yedinci dönemde** sıfırlanıyor ve kırk sekizincide **+1,2250** oluyor. Çeşitlilik bir
zevk tercihi değil **ölçüm bütçesidir**: hiç ölçmeyen sistem ortam sabit kaldıkça kazanır, ortam
her değiştiğinde kaybeder.

"Yankı odası" bu tabloyu anlatan bir **mecazdır** ve kendi sayısı yoktur; karşılığı gösterilmeyen
kalem sayısı ile ilk beşin gösterim payıdır. Yanıltıcı olduğu yer şurası: kurguda hiçbir abonenin
eğilimi değişmiyor. Daralan abonenin ilgisi değil **sistemin ölçtüğü alandır** ve ölçülmeyen bir
kalemin kötü sayılması bir inanç değil bir kayıt boşluğudur.

#### Özet

- Dar düzen sekizinci dönemden sonra her dönem 24 kalemin 8'ini göstermiyor, 6 kalem son yirmi
  dört dönemin hiçbirinde görünmüyor ve ilk beşin gösterim payı 0,4711'den 0,5461'e çıkıyor.
- Çeşitliliğin kısa dönem bedeli ilk on bir dönemde abone başına −1,0000, uzun dönem kazancı on
  iki ile kırk sekiz arasında +2,2250'dir; kümülatif fark yirmi yedinci dönemde eşitlenir.
- Yankı odası bir mecazdır; bu kurguda daralan abonenin ilgisi değil, sistemin ölçtüğü alandır.

#### Kurs Kapanışı

On dört dersin hesabı tek tabloda ve her sayı kendi dersinden okundu. Satırlar ortak birim
taşımaz; son sütun ölçütü ve yönünü yazar. **Fark tek işaret düzeniyle verilir: yöntemin sayısı
eksi taban çizgisi** ve üçüncü sütunda parantez içindedir. Dördüncü sütun kursun kendi sayısıdır:
görülmeyen durum, denenmeyen eylem, gösterilmeyen kalem.

| Ders | Taban çizgisi | Yöntemin sayısı | Toplanan veri | Ölçüt |
|---|---|---|---|---|
| Pekiştirmeli 01 | rastgele eylem 10,432 | el kuralı 20,621 (+10,189) | 1.458 çiftin 1.355'i görülmedi | getiri, büyük iyi |
| Pekiştirmeli 02 | rastgele eylem 10,432 | geçmiş alanlı kural 23,148 (+12,716) | durum uzayı 243'ten 1.215'e; sayım yok | getiri, büyük iyi |
| Pekiştirmeli 03 | keşifsiz ajan −16,492 | keşif oranı 0,20 ile 25,659 (+42,151) | 1.458 çiftin 880'i denenmedi | getiri, büyük iyi |
| Pekiştirmeli 04 | kural politikası 20,621 | keşifli çizelge 24,419 (+3,798) | 1.458 hücrenin 221'i güncellenmedi | getiri, büyük iyi |
| Pekiştirmeli 05 | rastgele eylem 10,432 | tekrar tamponu 26,233 / 25,302 (+15,801) | 1.458 hücrenin 678'i güncellenmedi | getiri, büyük iyi |
| Pekiştirmeli 06 | ağırlıklar sıfır 10,177 | taban çıkarmalı 22,866 / 22,190 (+12,689) | uzlaşma 0,290; sayım yok | getiri, büyük iyi |
| Pekiştirmeli 07 | ham getiri 14,207 / 17,218 | üstünlük 23,401 / 23,755 (+9,194) | 5 abonenin 0'ı ziyaretsiz kaldı | getiri, büyük iyi |
| Öneri 01 | rastgele sıra 0,2036 | en çok seçilen kalem 0,6527 (+0,4491) | 24 kalemin 15'i hiç önerilmedi | ilk beş isabeti, büyük iyi |
| Öneri 02 | popülerlik 0,6527 | içerik temelli 0,4431 (−0,2096) | 24 kalemin 0'ı gösterilmedi | ilk beş isabeti, büyük iyi |
| Öneri 03 | popülerlik 0,6527 | kullanıcı temelli 0,6946 (+0,0419) | 24 kalemin 8'i gösterilmedi | ilk beş isabeti, büyük iyi |
| Öneri 04 | popülerlik 0,6527 | atlayan kurulum 0,7006 (+0,0479) | 24 kalemin 15'i gösterilmedi | ilk beş isabeti, büyük iyi |
| Öneri 05 | popülerlik geri dönüşü 0,5752 | üç soruluk giriş 0,5487 (−0,0265) | 24 kalemin 11'i gösterilmedi | soğukta ilk beş isabeti, büyük iyi |
| Öneri 06 | çevrimiçi taban 1,4581 | hafif düzen 1,4788 (+0,0207) | 24 kalemin 12'si gösterilmedi | abone başına etkileşim, büyük iyi |
| Öneri 07 | dar düzen, 48 dönem | geniş düzen (+1,2250) | 24 kalemin 8'i gösterilmedi | kümülatif etkileşim, büyük iyi |

**Kursun kuralı budur: kararı veren bir sistemin ölçüsü, o kararın kendi verisini değiştirdiği
hesaba katılmadan okunamaz; topladığı veriyi yazmayan sistem ölçülmemiş sayılır.**

Dördüncü sütun tek bir sayıya indirgenir: sayım basan on iki derste görülmeyen çift,
güncellenmeyen hücre, ziyaretsiz abone ve gösterilmeyen kalem toplamı **3.203 birimdir**.
Birimler aynı değildir ve toplam bir ortalama değil bir sayımdır: bu kursta ölçülen her başarım
sayısının arkasında, yöntemin hakkında hiçbir şey öğrenmediği üç binden fazla birim durur.
Pekiştirmeli 02 ile 06 sayım basmaz, toplama girmez.

Kursun ikinci iddiası aynı tablodan okunur: **çevrimdışı ölçülen sayı çevrimiçi davranışı
söylemez.** Dört derste iki sayı ayrıldı. Öneri 06'da çevrimdışı 0,6886 ile birinci olan düzen,
kendi gösterim kararlarını verdiğinde 1,4563 ile sonuncu ve tabanın altında kaldı. Öneri 07'de
kayda göre iyi olan dar düzen kırk sekiz dönem sonunda 1,2250 geride bitirdi. Öneri 05'te kayıttan
okunan isabet, soru sorulduğunda 0,5752'den 0,5487'ye düştü. Pekiştirmeli 03'te ayrım ters yönde
göründü: keşif oranı 1,00 olan ajanın **davranışı** kötüydü ama **topladığı veri** iyiydi ve
dondurulan çizelge 23,000 verdi. Tek kaynak şudur: sayının okunduğu kayıt, kararı başka bir
düzenin verdiği kayıttır.

Elli iki derste her sayının yanına tabanı yazıldı ve son on dördünde sistemin kendi verisini
değiştirdiği sayıldı. Sorulmayan tek şey **kimin için** ölçüldüğüdür. Tablodaki her sayı bütün
abonelerin ortalamasıdır: aynı sayı iki alt grupta iki ayrı şey söylüyorsa hangisi rapor edilir,
sistemin bir kalemi kime gösterip kime göstermediği **neye dayanıyor** ve okunabilir mi ve ölçüsü
iyi çıkan bir sistemin ölçüye hiç girmeyen bir gruba verdiği zarar hangi sayıyla yazılır. Sonraki
kurs, Model Değerlendirme, Yorumlanabilirlik ve Etik, bu üç soruyla açılır.

