---
title: 'Denetimsiz Öğrenme'
source: 'https://academia.sh/tr/kurslar/denetimsiz-ogrenme'
language: tr
updated: '2026-08-17T04:42:12+00:00'
license: 'CC BY-SA 4.0'
lessons: 11
outcomes:
    - 'Kümeleme yöntemlerini veri yapısına göre seçer ve her sonucun tabanını kendisi kurar'
    - 'Boyut azaltmayı geri kurma hatası ve komşuluk korunmasıyla ölçer ve neyin kaybolduğunu yazar'
    - 'Denetimsiz sonuçları etiket olmadan değerlendirir ve ölçüt seçiminin cevabı belirlediğini gösterir'
---

# Denetimsiz Öğrenme

Etiketin olmadığı bir kümede tabanın da kurulmak zorunda olması ve sonucun rastgele bir bölmelemeden ne kadar ayrıldığının ölçülmesi: benzerlik tanımının aynı kümeden üç bambaşka bölmeleme çıkarması, aynı küme sayısında başlangıcın oynattığı sonuç, bağlantı ölçütünün ayrıştırdığı nokta sayısı, yoğunluk tabanlı yaklaşımda gürültü noktası sayısı, yumuşak atamanın taşıdığı ve sert atamaya geçilince kaybolan belirsizlik, üç iç geçerlilik ölçütünün üç ayrı küme sayısı seçmesi, izdüşümün geri kurma hatası ve koruduğu geometri, rank ile hata arasındaki takas, gömmenin komşuluk korunma oranı, işaretlenen anomalinin kaçının gerçek olduğu ve güvenin tek başına yanıltması.

## Kümeleme

Noktaların etiketsiz gruplanmasında sonucun rastgele bir bölmelemeden ne kadar ayrıldığının sayılması: üç ayrı benzerlik tanımının aynı kümeden çıkardığı üç bambaşka bölmeleme, aynı küme sayısında farklı başlangıçların ürettiği farklı sonuç ve dirseğin belirsizliği, tek, tam ve ortalama bağlantının ayrıştırdığı nokta sayısı ile kesme yüksekliğinin bir karar olması, komşuluk yarıçapı ve en az nokta sayısının oynattığı küme ile gürültü sayısı, yumuşak atamanın taşıdığı belirsizliğin sert atamada kaybolması, üç iç geçerlilik ölçütünün üç ayrı küme sayısı seçmesi ve yapısız bir kümede bile küme bulunması.

### Kümeleme Problemi

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/kumeleme-problemi

Etiket kaldırıldığında ölçünün kendisinin kurulması: aynı 756 abone üç ayrı benzerlik tanımıyla dört kümeye bölünür ve üç bambaşka bölmeleme çıkar — nokta çiftlerinin 0,3750 ile 0,4801 arası bir payında bölmelemeler ayrışır. Üç taban çizgisi tanımlanır; standart puan tanımında tek küme 6,0000, rastgele atama 5,9790, tek sütuna göre dilimleme 4,9044 ve yöntem 3,8910 verir, yani yöntemin farkı hangi tabanın yazıldığına göre 2,0880 ya da 1,0134'tür. Üç tanımın sayıları birbiriyle karşılaştırılamaz; oransal kazanç 0,525, 0,349 ve 0,295'tir.

Denetimli öğrenme kursu yirmi derste yedi model ailesini aynı kurgu abone tablosunda denedi ve her
sayının yanına bütçesini yazdı. Bu ölçümlerin hepsinin ortak bir dayanağı vardı ve o dayanak hiç
sorgulanmadı — **etiket**. Taban çizgisi en sık etiketi söylüyordu, ölçüt tahmin edilen etiketle
gerçeğini karşılaştırıyordu, ayar araması doğrulama kümesindeki etiketlere bakıyordu. Etiket sütunu
kaldırıldığında bu üç mekanizmanın üçü birden çalışmaz.

Bu ders o durumla başlar. Elde etiketi olmayan bir tablo vardır ve amaç benzeyen aboneleri bir araya
toplamaktır. Sorun yöntemin bulunmasında değil — bir bölmeleme üretmek kolaydır, rastgele atama da
bir bölmeleme üretir. Sorun, üretilen bölmelemenin **neye göre** ölçüleceğindedir. Ölçü kurulmadan
önce de daha temel bir karar durur: "benzer" sözcüğünün tanımı.

- **KU1.** **Küme**, bu kursta noktaların bir alt öbeğidir; M17'nin veritabanı kümesi bir sunucu
  öbeğiydi, **veri kümesi** ise satırların tamamıdır.
- **KU2.** Küme M27/K01–K03'ten gelen **kurgu** abone tablosudur: tohum 20260218, 1.260 abone,
  bölme 756/252/252. Yordam eğitim kümesindeki 756 abone üzerinde çalışır.
- **KU3.** **Şüpheli sütunu bu kursta üretilmez.** Hiçbir yöntem etiketi görmez; etiket yalnız
  konunun son dersinde, dış geçerlilik ölçülürken bir kez kullanılacaktır.
- **KU4.** Sütunlar altı sayısal alandır: ortalama tüketim, oynaklık, kişi başına tüketim, hane
  büyüklüğü, memnuniyet ve dönem sayısı. Bölge göstergeleri dışarıda kalır, çünkü iki bölge
  arasındaki "uzaklık" ikili sütunlarda her zaman aynı sabittir.
- **KU5.** **Bölmeleme**, her noktayı tam olarak bir kümeye atayan bir yerleştirmedir. Ölçüsü
  **küme içi toplam uzaklık**tır: her noktanın kendi kümesinin **merkezine** uzaklığının toplamı,
  nokta sayısına bölünmüş hali; küçük olması iyidir. M27/K01'in uygulama alanları dersinde basılan
  sayılar başka bir sütun kümesinden geldiği için tekrarlanmaz.
- **KU6.** Üç taban çizgisi kullanılır: **rastgele atama**, **tek küme** ve **tek sütuna göre
  dilimleme**. Rastgele atamanın sayısı beş tohumun ortalamasıdır; tek tohum kendi salınımını gizler.
- **KU7.** Küme sayısı **4'te**, yordam da sabit tutulur: en uzak nokta tohumlamasıyla dört çekirdek
  seçilir ve iki düzeltme turu yapılır. Küme sayısının ayar değişkeni olması ve başlangıcın sonucu
  oynatması sonraki derse aittir; burada değişen tek şey **benzerlik tanımıdır**.
- **KU8.** Farklı tanımların sayıları **birbiriyle karşılaştırılamaz**, çünkü her tanım kendi
  uzaklığını ölçer; karşılaştırılabilir olan yalnız kendi tabanına göre farktır.

#### Benzerlik Tanımı Bir Seçimdir

Bir kümeleme yordamı "yakın" sözcüğünün karşılığını veriden öğrenmez; dışarıdan alır. Birinci tanım
sütunları **ham ölçekte** bırakıp farkların karesini toplar: ortalama tüketimin aralığı hane
büyüklüğünün aralığından yaklaşık sekiz buçuk kat geniş olduğu için toplam sessizce bir
ağırlıklandırmadır. İkincisi her sütunu **standart puana** çevirir ve altı sütun eşit ağırlık alır.
Üçüncüsü her değeri **sütun içindeki sırasının yüzdesine** çevirip mutlak farkları toplar; uç
değerler sıkışır, yalnız sıralama kalır.

Tanım yalnız uzaklığı değil, **merkezin ne olduğunu** da belirler: kare uzaklığı en küçüğe indiren
merkez ortalama vektördür, mutlak farkı en küçüğe indiren merkez ortanca vektördür. Üç tanım tek bir
abonenin en yakın komşusunu bile aynı seçmez.

```python
# kumeleme.py — MODELDIR. M27/K01–K03'un KURGU abone tablosu ayni tohumla
# yeniden kurulur; supheli sutunu bu kursta uretilmez, hicbir yordam etiket gormez.
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21), (0.22, 17), (0.18, 26), (0.14, 14), (0.18, 23)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


VERI = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    temel = BOLGE[ayrik(r(), [x[0] for x in BOLGE])][1]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:
        continue
    r, v = uretec(TOHUM + 17001 + i), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            temel * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        x = {"ort_tuketim": round(sum(v) / len(v), 2), "oynaklik": round(max(v) - min(v), 2),
             "hane": hane, "memnuniyet": memnun, "donem": len(v)}
        x["kisi_basi"] = round(x["ort_tuketim"] / hane, 3)
        VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
N = len(EGT)
HAM_V = [[x[a] for a in ALAN] for x in EGT]
ORT = [statistics.fmean(c) for c in zip(*HAM_V)]
SAP = [statistics.pstdev(c) for c in zip(*HAM_V)]
STD_V = [[(v[j] - ORT[j]) / SAP[j] for j in range(len(ALAN))] for v in HAM_V]


def sira_yuzdesi(sutun):                    # deger, sutundaki ortalama sirasinin yuzdesine doner
    s, yer = sorted(range(len(sutun)), key=lambda i: sutun[i]), {}
    for p, i in enumerate(s):
        yer.setdefault(sutun[i], []).append(p)
    orta = {v: statistics.fmean(p) / (len(s) - 1) for v, p in yer.items()}
    return [orta[x] for x in sutun]


SIRA_S = [sira_yuzdesi(c) for c in zip(*HAM_V)]
SIRA_V = [[SIRA_S[j][i] for j in range(len(ALAN))] for i in range(N)]


kare = lambda a, b: sum((p - q) * (p - q) for p, q in zip(a, b))
mutlak = lambda a, b: sum(abs(p - q) for p, q in zip(a, b))
ort_merkez = lambda g: [statistics.fmean(c) for c in zip(*g)]
ortanca_merkez = lambda g: [statistics.median(c) for c in zip(*g)]

TANIM = [("ham olcek, kare uzaklik", HAM_V, kare, ort_merkez),
         ("standart puan, kare uzaklik", STD_V, kare, ort_merkez),
         ("sira yuzdesi, mutlak fark", SIRA_V, mutlak, ortanca_merkez)]

print(f"kumelenen abone {N}, sutun {len(ALAN)}, etiket sutunu yok")
print(f"{'':<28}{'komsu':>7}" + "".join(f"{a:>12}" for a in ALAN))
print(f"{'abone 48 (kendisi)':<28}{'--':>7}" + "".join(f"{EGT[48][a]:>12.3f}" for a in ALAN))
for ad, V, uzak, _ in TANIM:
    j = min((x for x in range(N) if x != 48), key=lambda x: (uzak(V[48], V[x]), x))
    print(f"{ad:<28}{j:>7}" + "".join(f"{EGT[j][a]:>12.3f}" for a in ALAN))
```

```
kumelenen abone 756, sutun 6, etiket sutunu yok
                              komsu ort_tuketim    oynaklik   kisi_basi        hane  memnuniyet       donem
abone 48 (kendisi)               --      16.090      10.560       3.218       5.000       4.000       3.000
ham olcek, kare uzaklik         711      16.280      10.600       4.070       4.000       4.000       2.000
standart puan, kare uzaklik      52      13.540      11.990       2.708       5.000       4.000       3.000
sira yuzdesi, mutlak fark       241      17.400       9.830       2.900       6.000       4.000       3.000
```

Üç satır aynı aboneye üç ayrı komşu veriyor ve gerekçe her satırda görünüyor. Ham ölçekte seçilen
711 numaralı abonenin ortalama tüketimi 48 numaralınınkinden yalnız 0,19 uzakta; hane ve dönem
sütunları tutmuyor ama bunların farkı toplama en çok 2 katıyor. Standart puanla seçilen 52 numaralı
abonenin tüketimi 2,55 uzakta, buna karşılık hane, memnuniyet ve dönem sütunlarının üçü de tutuyor:
bir sütundaki tam uyum bir sapmalık farkı dengeliyor.

Üç komşunun hiçbiri yanlış değildir. Üçü de "en yakın" tanımına uyuyor; yalnız üç ayrı tanıma.

#### Üç Taban Çizgisi

Küme içi toplam uzaklık tek başına yorumlanamaz; sayının büyük mü küçük mü olduğu, aynı ölçüyle
üretilmiş bir karşılaştırma noktası olmadan bilinemez. Üç taban üç ayrı soruyu yanıtlar. **Rastgele
atama** hiçbir yapı kullanmadan aynı sayıda küme üretir: "bu bölmeleme şanstan ne kadar iyi". **Tek
küme** noktaları bir arada tutar: "bölmek hiç bölmemeye göre ne kazandırdı". **Tek sütuna göre
dilimleme** noktaları ortalama tüketime göre dört eşit dilime böler: "bütün sütunlara bakmak, tek
sütuna bakan bir çözüme göre ne kazandırdı". Üçüncüsü en zorlu tabandır, çünkü öğrenmese de veriyi
kullanır.

```python
KUME = 4


def ic_uzaklik(V, uzak, merkez, atama):     # kume ici toplam uzaklik, nokta basina
    t = 0.0
    for c in set(atama):
        g = [V[i] for i in range(len(V)) if atama[i] == c]
        t += sum(uzak(v, merkez(g)) for v in g)
    return t / len(V)


def rastgele_atama(n, k, tohum):            # TABAN 1: hicbir yapi kullanmaz
    r = uretec(tohum)
    return [int(r() * k) for _ in range(n)]


def sutuna_gore(V, j, k):                   # TABAN 3: tek sutunu k esit dilime boler
    s, a = sorted(range(len(V)), key=lambda i: (V[i][j], i)), [0] * len(V)
    for p, i in enumerate(s):
        a[i] = min(k - 1, p * k // len(V))
    return a


def bolumle(V, uzak, merkez, k, tur=2):     # MODELDIR: en uzak nokta tohumlama + iki tur
    cek = [min(range(len(V)), key=lambda i: (uzak(V[i], merkez(V)), i))]
    while len(cek) < k:
        cek.append(max(range(len(V)),
                       key=lambda i: (min(uzak(V[i], V[c]) for c in cek), -i)))
    M = [V[c] for c in cek]
    for _ in range(tur + 1):
        a = [min(range(k), key=lambda c: uzak(v, M[c])) for v in V]
        M = [merkez([V[i] for i in range(len(V)) if a[i] == c]) for c in range(k)]
    return a


_, V2, U2, M2 = TANIM[1]
RAS = [ic_uzaklik(V2, U2, M2, rastgele_atama(N, KUME, TOHUM + 400 + t)) for t in range(5)]
A2 = bolumle(V2, U2, M2, KUME)
tek, ras = ic_uzaklik(V2, U2, M2, [0] * N), statistics.fmean(RAS)
dilim = ic_uzaklik(V2, U2, M2, sutuna_gore(V2, 0, KUME))
yon = ic_uzaklik(V2, U2, M2, A2)
print(f"standart puan, kare uzaklik tanimiyla, k = 4\n"
      f"  taban - tek kume          {tek:8.4f}\n"
      f"  taban - rastgele atama    {ras:8.4f}   (bes tohum: {min(RAS):.4f} - {max(RAS):.4f})\n"
      f"  taban - ort_tuketim dilim {dilim:8.4f}\n"
      f"  yontem                    {yon:8.4f}"
      f"   kume boylari {sorted(A2.count(c) for c in range(KUME))}\n"
      f"  fark: rastgeleye gore {ras - yon:+.4f}, dilime gore {dilim - yon:+.4f}")
```

```
standart puan, kare uzaklik tanimiyla, k = 4
  taban - tek kume            6.0000
  taban - rastgele atama      5.9790   (bes tohum: 5.9763 - 5.9819)
  taban - ort_tuketim dilim   4.9044
  yontem                      3.8910   kume boylari [61, 67, 117, 511]
  fark: rastgeleye gore +2.0880, dilime gore +1.0134
```

Tek kümenin sayısı **tam olarak 6,0000**, yani sütun sayısı. Bu bir rastlantı değil: her sütun
standart puana çevrildiğinde birim değişkenlik taşır, dolayısıyla tek merkeze uzaklığın nokta
başına toplamı sütun sayısına eşit çıkar. Ölçünün üst sınırı hesapla bilinir ve her bölmeleme bu
6,0000'ı ne kadar aşağı çektiğine göre okunur.

İkinci satır bu kursun en önemli sayısıdır. Rastgele atama 5,9790 veriyor — tek kümeden yalnız
0,0210 aşağıda. Dört rastgele gruba bölmek hiç bölmemeye göre neredeyse hiçbir şey kazandırmıyor,
çünkü rastgele bir kümenin merkezi verinin genel merkezine düşüyor. Beş tohumun salınımı 0,0056,
yani ölçülen farkların yanında ihmal edilebilir.

Üçüncü satır tabloyu değiştiriyor. Yalnız ortalama tüketime bakıp dört eşit dilime bölmek 4,9044
veriyor. Yöntemin sayısı 3,8910 ve fark **hangi tabanın yazıldığına göre 2,0880 ya da 1,0134'tür**.
Dilimleme yazılmasaydı yöntem, altı sütuna birden bakmanın kazandırdığı 1,0134'ü kendi hanesine
yazardı. Taban çizgisinin seçimi bir ölçüm kararıdır; gerekçesiyle yazılmadığında bildirilen fark
bir bulgu değil bir seçimdir.

#### Aynı Küme, Üç Bölmeleme

Yordam ve küme sayısı sabit tutulup yalnız benzerlik tanımı değiştirildiğinde ne olduğu üçüncü
blokta ölçülür. İki bölmelemenin ayrışması, kümeleri eşleştirmeden, nokta **çiftleri** üzerinden
sayılır: 756 abonenin 285.390 çifti vardır ve her çift için iki bölmeleme "aynı kümede" ya da "ayrı
kümelerde" der. Uyuşmazlık, farklı yanıt verdikleri çiftlerin oranıdır.

```python
BOLME = []
print(f"{'tanim':<28}{'tek kume':>10}{'rastgele':>10}{'dilim':>10}"
      f"{'yontem':>10}{'rastgeleye gore pay':>21}")
for ad, V, uzak, merkez in TANIM:
    a = bolumle(V, uzak, merkez, KUME)
    BOLME.append((ad, a))
    tek = ic_uzaklik(V, uzak, merkez, [0] * N)
    ras = statistics.fmean(ic_uzaklik(V, uzak, merkez, rastgele_atama(N, KUME, TOHUM + 400 + t))
                           for t in range(5))
    sut = ic_uzaklik(V, uzak, merkez, sutuna_gore(V, 0, KUME))
    y = ic_uzaklik(V, uzak, merkez, a)
    print(f"{ad:<28}{tek:>10.4f}{ras:>10.4f}{sut:>10.4f}{y:>10.4f}{(ras - y) / ras:>21.3f}")


def uyusmazlik(a, b):                       # iki bolmeleme kac nokta ciftinde ayrisiyor
    ayrisan = sum((a[i] == a[j]) != (b[i] == b[j])
                  for i in range(len(a)) for j in range(i + 1, len(a)))
    return 2 * ayrisan / (len(a) * (len(a) - 1))


print("\nbolmelemeler arasi uyusmazlik (ayrisan nokta cifti orani)")
for i in range(3):
    for j in range(i + 1, 3):
        print(f"  {BOLME[i][0]:<28} - {BOLME[j][0]:<28} {uyusmazlik(BOLME[i][1], BOLME[j][1]):.4f}")
print("\nen buyuk kumenin ortalamalari")
for ad, a in BOLME:
    g = [EGT[i] for i in range(N) if a[i] == max(a, key=a.count)]
    print(f"  {ad:<28} n {len(g):>4}" + "".join(
        f"  {x}={statistics.fmean(k[x] for k in g):.2f}"
        for x in ("ort_tuketim", "oynaklik")))
```

```
tanim                         tek kume  rastgele     dilim    yontem  rastgeleye gore pay
ham olcek, kare uzaklik       130.6679  130.2948   92.4562   61.8604                0.525
standart puan, kare uzaklik     6.0000    5.9790    4.9044    3.8910                0.349
sira yuzdesi, mutlak fark       1.3983    1.3907    1.1475    0.9809                0.295

bolmelemeler arasi uyusmazlik (ayrisan nokta cifti orani)
  ham olcek, kare uzaklik      - standart puan, kare uzaklik  0.4192
  ham olcek, kare uzaklik      - sira yuzdesi, mutlak fark    0.4801
  standart puan, kare uzaklik  - sira yuzdesi, mutlak fark    0.3750

en buyuk kumenin ortalamalari
  ham olcek, kare uzaklik      n  509  ort_tuketim=17.40  oynaklik=6.86
  standart puan, kare uzaklik  n  511  ort_tuketim=19.18  oynaklik=11.97
  sira yuzdesi, mutlak fark    n  325  ort_tuketim=21.50  oynaklik=12.66
```

Birinci tablo okunurken bir kural işler: **satırlar arasında karşılaştırma yapılamaz.** 61,8604 ile
0,9809 aynı ölçünün iki değeri değildir; biri metreküp karesi, öteki yüzdelik farkların toplamı
mertebesindedir. Karşılaştırılabilir olan tek sütun sağdaki paydır: her tanımın kendi rastgele
tabanına göre kazancı sırasıyla 0,525, 0,349 ve 0,295. Ham ölçekte pay en yüksek çıkıyor, ama bu
tanımı daha iyi yapmaz — orada toplam saçılmanın neredeyse tamamı tek bir sütundan geldiği için
bölmek o sütunu kesmekle aynı şeydir. Payın büyüklüğü tanımın başarısını değil, **veriye ne kadar
tek yönlü baktığını** ölçüyor.

İkinci tablo dersin asıl sonucudur. Üç bölmeleme, nokta çiftlerinin 0,3750 ile 0,4801 arası bir
payında birbirinden ayrılıyor. Ham ölçek ile sıra yüzdesi arasındaki 0,4801, 285.390 çiftin
137.002'sinde iki bölmelemenin ters yanıt verdiği anlamına gelir. Bunlar aynı veriyi, aynı yordamla,
aynı küme sayısıyla bölen üç koşumdur; değişen tek şey uzaklığın tanımıdır.

Üçüncü tablo farkın nerede oturduğunu gösteriyor. Ham ölçekte en büyük küme 509 abonelik ve
ortalama oynaklığı 6,86 — düşük oynaklıklı orta tüketimliler bir arada. Standart puanda küme benzer
boyda ama ortalama oynaklığı 11,97: oynaklık sütunu artık tüketim kadar söz sahibi. Sıra yüzdesinde
en büyük küme 325'e düşüyor, çünkü uç değerler sıralamaya çevrilince noktaları birbirinden
uzaklaştırmıyor.

Bu üç bölmelemeden birinin doğru olduğunu söyleyecek bir ölçüt yoktur. Her tanım kendi ölçüsünde
kendi bölmelemesini en iyi bulur ve tanımlar arasında bir üst mahkeme kurulamaz, çünkü kurmak için
yine bir uzaklık tanımı seçmek gerekir. Denetimli kursta bu soru etiketle kapanıyordu; burada
kapanmaz.

#### Özet

- Etiket kaldırıldığında ölçünün üç bileşeni de kurulmak zorundadır: taban çizgisi, yöntemin sayısı
  ve fark. Kümelemede taban çizgisi hazır gelmez, seçilir ve gerekçelendirilir.
- Standart puan tanımında tek küme 6,0000, rastgele atama 5,9790 verir; öğrenmeyen bir bölmeleme
  ölçüyü nokta başına yalnız 0,0210 oynatır ve beş tohumdaki salınımı 0,0056'dır.
- Yöntemin sayısı 3,8910'dur; farkı rastgele tabana göre 2,0880, dilimleme tabanına göre 1,0134.
  Hangi tabanın yazıldığı bildirilen kazancı iki katına yakın oynatır.
- Üç benzerlik tanımı aynı 756 aboneyi aynı yordamla böldüğünde nokta çiftlerinin 0,3750 ile 0,4801
  arası bir payında ayrışan üç bölmeleme çıkar; ham ölçek ile sıra yüzdesi 285.390 çiftin
  137.002'sinde ters yanıt verir.
- Tanımların sayıları karşılaştırılamaz; okunabilen tek şey kendi tabanına göre oransal kazançtır ve
  o pay tanımın doğruluğunu değil, veriye ne kadar tek yönlü baktığını ölçer.

#### Sonraki Adım

Bu derste yordam bilerek dondurulmuştu: çekirdekler tek bir kuralla seçildi, iki düzeltme turu
yapıldı, küme sayısı 4'te tutuldu. Üç sabitin üçü de birer karardır. Sonraki ders ikisini çözer:
yineleme durana kadar sürdürüldüğünde ve çekirdekler rastgele seçildiğinde aynı küme sayısıyla kaç
ayrı bölmeleme çıkıyor, en iyi ile en kötü koşum arasındaki fark ne kadar. Ardından küme sayısı bir
ayar değişkeni olarak ele alınır; küme içi toplam uzaklık küme sayısı arttıkça tek yönde azaldığı
için en iyi değeri gösteremez ve ardışık farkların oluşturduğu "dirsek" bir karar noktası olarak
sayılır.

### k-Ortalamalar

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/k-ortalamalar

Yinelemenin ve küme sayısının ayrı ayrı ölçülmesi: aynı k ile otuz rastgele başlangıç 26 ayrı bölmeleme üretir, sayılar 3,5649 ile 3,7763 arasında kalır ama en iyi ile en kötü koşum nokta çiftlerinin 0,2844'ünde ayrışır — dört ondalıkta aynı sayıyı veren iki koşum bile 0,0037'sinde ayrışır. Küme sayısı bir ayar değişkenidir; 120 koşumluk süpürmede rastgele taban 6,0000'dan 5,9181'e inerken yöntem 6,0000'dan 2,2570'e iner ve dirseğin üç ayrı okuması üç ayrı k seçer: 3, 4 ve 5.

Önceki ders yordamı bilerek dondurmuştu: çekirdekler tek bir kuralla seçildi, iki düzeltme turu
yapıldı, küme sayısı 4'te tutuldu. Değişen tek şey benzerlik tanımıydı. Bu ders tanımı sabitler —
standart puan ile kare uzaklık — ve dondurulan üç sabitten ikisini serbest bırakır: yineleme durana
kadar sürdürülür, çekirdekler rastgele seçilir.

Ortaya çıkan yordam kısadır: her nokta en yakın merkeze atanır, her merkez kendi kümesinin
ortalamasına taşınır, değişiklik kalmayana kadar yinelenir. Ders bunun iki ayrı yerde karar
ürettiğini ölçer. Birincisi başlangıçtır: aynı küme sayısıyla farklı çekirdeklerden koşulduğunda
kaç ayrı bölmeleme çıkıyor. İkincisi küme sayısının kendisidir; k dışarıdan verilen bir **ayar
değişkenidir** ve seçimi bir aramadır.

- **KU9.** Küme, sütunlar, bölme ve ölçü önceki dersteki gibidir: kurgu abone tablosu, tohum
  20260218, eğitim kümesindeki 756 abone, altı sayısal sütun, standart puan, kare uzaklık.
  Etiket yine üretilmez.
- **KU10.** Ölçü yine **küme içi toplam uzaklık**tır, nokta başına, küçük iyidir.
- **KU11.** Taban çizgisi **rastgele atamadır** ve her k için ayrı hesaplanır; taban küme sayısıyla
  değiştiği için tek bir sayı bütün tabloya yazılamaz.
- **KU12.** Çekirdekler kümedeki **ayrı noktalardan** seçilir; boş kalan bir küme olursa merkezi
  yerinde bırakılır.
- **KU13.** Yineleme, atama iki tur üst üste değişmediğinde durur; üst sınır 60 turdur ve hiçbir
  koşum bu sınıra dayanmaz. Kayan noktalı sayı `==` ile karşılaştırılmaz — durma ölçütü atama
  listesinin eşitliğidir, yani tam sayı karşılaştırmasıdır.
- **KU14.** İki bölmelemenin farkı, önceki derste kurulan **ayrışan nokta çifti oranıyla** ölçülür.
- **KU15.** Küme sayısı süpürmesinde her k için 12 başlangıç denenir ve en iyisi alınır; **denenen
  aday sayısı yazılır**. Bildirilen sayı arama bütçesinin de bir işlevidir.

#### Yinelemenin Durduğu Yer

Yordam iki adımın dönüşümlü tekrarıdır ve her adım ölçüyü ayrı bir yönden düşürür. Atama adımı
merkezleri sabit tutup her noktayı en yakınına verir. Güncelleme adımı atamayı sabit tutup her
merkezi kendi kümesinin ortalamasına taşır; kare uzaklık toplamını en küçüğe indiren nokta ortalama
olduğu için bu adım da ölçüyü düşürür ya da aynı bırakır. Ölçü hiç artmadığı ve aşağıdan sınırlı
olduğu için yineleme durmak zorundadır. Durduğu **yer** hakkında ise bu akıl yürütme hiçbir şey
söylemez.

```python
# k_ortalamalar.py — MODELDIR. 01. dersin KURGU kumesi ve standart puan tanimi
# ayni tohumla yeniden kurulur; etiket yine uretilmez.
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21), (0.22, 17), (0.18, 26), (0.14, 14), (0.18, 23)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


VERI = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    temel = BOLGE[ayrik(r(), [x[0] for x in BOLGE])][1]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:
        continue
    r, v = uretec(TOHUM + 17001 + i), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            temel * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        x = {"ort_tuketim": round(sum(v) / len(v), 2), "oynaklik": round(max(v) - min(v), 2),
             "hane": hane, "memnuniyet": memnun, "donem": len(v)}
        x["kisi_basi"] = round(x["ort_tuketim"] / hane, 3)
        VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
HAM_V = [[x[a] for a in ALAN] for x in EGT]
ORT = [statistics.fmean(c) for c in zip(*HAM_V)]
SAP = [statistics.pstdev(c) for c in zip(*HAM_V)]
V = [[(v[j] - ORT[j]) / SAP[j] for j in range(len(ALAN))] for v in HAM_V]
N = len(V)

kare = lambda a, b: sum((p - q) * (p - q) for p, q in zip(a, b))
merkez = lambda g: [statistics.fmean(c) for c in zip(*g)]


def ic_uzaklik(atama, k):                   # kume ici toplam uzaklik, nokta basina
    t = 0.0
    for c in range(k):
        g = [V[i] for i in range(N) if atama[i] == c]
        if g:
            t += sum(kare(v, merkez(g)) for v in g)
    return t / N


def cekirdek(r, k):                         # k ayri noktayi baslangic merkezi sec
    s = []
    while len(s) < k:
        j = int(r() * N)
        if j not in s:
            s.append(j)
    return [V[j] for j in s]


def k_ortalamalar(M, ust=60):               # MODELDIR: ata, merkezleri guncelle, yinele
    k, onceki = len(M), None
    for tur in range(1, ust + 1):
        a = [min(range(k), key=lambda c: kare(v, M[c])) for v in V]
        if a == onceki:
            return a, tur
        onceki = a
        M = [merkez([V[i] for i in range(N) if a[i] == c]) or M[c] for c in range(k)]
    return a, ust


r = uretec(TOHUM + 5100)
M0 = cekirdek(r, 4)
print("tek bir baslangictan yakinsama izi (k = 4)")
for tur in range(1, 9):
    a = [min(range(4), key=lambda c: kare(v, M0[c])) for v in V]
    print(f"  tur {tur}  kume ici toplam uzaklik {ic_uzaklik(a, 4):.4f}"
          f"  kume boylari {sorted(a.count(c) for c in range(4))}")
    M0 = [merkez([V[i] for i in range(N) if a[i] == c]) or M0[c] for c in range(4)]
```

```
tek bir baslangictan yakinsama izi (k = 4)
  tur 1  kume ici toplam uzaklik 4.2484  kume boylari [109, 159, 233, 255]
  tur 2  kume ici toplam uzaklik 3.9366  kume boylari [148, 166, 213, 229]
  tur 3  kume ici toplam uzaklik 3.7729  kume boylari [153, 180, 198, 225]
  tur 4  kume ici toplam uzaklik 3.6519  kume boylari [145, 175, 201, 235]
  tur 5  kume ici toplam uzaklik 3.6125  kume boylari [140, 170, 215, 231]
  tur 6  kume ici toplam uzaklik 3.6058  kume boylari [137, 172, 221, 226]
  tur 7  kume ici toplam uzaklik 3.6050  kume boylari [138, 171, 222, 225]
  tur 8  kume ici toplam uzaklik 3.6049  kume boylari [138, 170, 223, 225]
```

İlk turda 4,2484 olan sayı sekizinci turda 3,6049'a iniyor ve düşüş hızla yavaşlıyor: birinci turdan
üçüncüye 0,4755 kazanılırken beşinciden sekizinciye yalnız 0,0076 kazanılıyor. Küme boyları da
yerine oturuyor, ilk turdaki 109–255 aralığı 138–225'e daralıyor. Önceki dersin iki düzeltme turlu
yordamı 3,8910'da kesilmişti; burada üçüncü tur bile onun altına iniyor.

Bu izin söylemediği şey daha önemlidir. Sayının düşüp durması, durduğu yerin **elde edilebilecek en
düşük sayı** olduğunu göstermez. Her tur bir önceki bölmelemeye komşu bölmelemeleri deniyor; hiçbir
tur uzaktaki bir yerleşimi denemiyor. Yordam bu yüzden başladığı yere yakın bir çözümde durur.

#### Aynı k, Farklı Başlangıç

Başlangıcın ne kadar belirleyici olduğu, aynı küme sayısıyla otuz farklı çekirdek kümesinden
koşularak ölçülür. Bir bölmelemenin kimliği, küme numaralarından bağımsız olarak, hangi noktaların
birlikte durduğudur.

```python
def olcut(a, k):                            # bolmelemenin etiketten bagimsiz kimligi
    return frozenset(frozenset(i for i in range(N) if a[i] == c) for c in range(k))


def uyusmazlik(a, b):                       # iki bolmeleme kac nokta ciftinde ayrisiyor
    ayrisan = sum((a[i] == a[j]) != (b[i] == b[j])
                  for i in range(N) for j in range(i + 1, N))
    return 2 * ayrisan / (N * (N - 1))


r, KOSUM = uretec(TOHUM + 7700), 30
SONUC = []
for s in range(KOSUM):
    a, tur = k_ortalamalar(cekirdek(r, 4))
    SONUC.append((ic_uzaklik(a, 4), tur, a))
SONUC.sort(key=lambda x: x[0])
AYRI = {olcut(a, 4) for _, _, a in SONUC}
print(f"{KOSUM} rastgele baslangic, k = 4 -> ayri bolmeleme {len(AYRI)}")
print("  siralanmis kume ici toplam uzakliklar")
for b in range(0, KOSUM, 10):
    print("   " + " ".join(f"{u:.4f}" for u, _, _ in SONUC[b:b + 10]))
print(f"  en iyi {SONUC[0][0]:.4f}, ortanca {SONUC[KOSUM // 2][0]:.4f}, "
      f"en kotu {SONUC[-1][0]:.4f}, aralik {SONUC[-1][0] - SONUC[0][0]:.4f}")
print(f"  yakinsama turu en az {min(t for _, t, _ in SONUC)}, "
      f"en cok {max(t for _, t, _ in SONUC)}")
print(f"  01. dersin tek gecisli yordami 3.8910 vermisti; iyilesme "
      f"{3.8910 - SONUC[0][0]:.4f}")
print(f"  en iyi - ikinci en iyi: sayi farki {SONUC[1][0] - SONUC[0][0]:.6f}, "
      f"ayrisan nokta cifti {uyusmazlik(SONUC[0][2], SONUC[1][2]):.4f}")
print(f"  en iyi - en kotu:       sayi farki {SONUC[-1][0] - SONUC[0][0]:.4f}, "
      f"ayrisan nokta cifti {uyusmazlik(SONUC[0][2], SONUC[-1][2]):.4f}")
```

```
30 rastgele baslangic, k = 4 -> ayri bolmeleme 26
  siralanmis kume ici toplam uzakliklar
   3.5649 3.5649 3.5656 3.5656 3.5656 3.5658 3.5711 3.5711 3.5711 3.5711
   3.5711 3.5712 3.5712 3.5727 3.5903 3.5906 3.5936 3.5936 3.5941 3.5955
   3.5956 3.5957 3.5981 3.5995 3.6026 3.6077 3.6165 3.7467 3.7744 3.7763
  en iyi 3.5649, ortanca 3.5906, en kotu 3.7763, aralik 0.2115
  yakinsama turu en az 11, en cok 52
  01. dersin tek gecisli yordami 3.8910 vermisti; iyilesme 0.3261
  en iyi - ikinci en iyi: sayi farki 0.000028, ayrisan nokta cifti 0.0037
  en iyi - en kotu:       sayi farki 0.2115, ayrisan nokta cifti 0.2844
```

Otuz koşum **26 ayrı bölmeleme** verdi. Aynı veri, aynı yordam, aynı küme sayısı; değişen tek şey
başlangıç noktaları. Sayılar dar bir bantta duruyor — 3,5649 ile 3,7763 arası — ve otuz koşumun
yirmi yedisi 3,62'nin altında. Rastgele atama tabanı bu küme sayısında 5,9730 olduğuna göre
**koşumların hepsi tabanı geniş farkla geçiyor**; en kötüsü bile tabanın 2,1967 altında.

Alt iki satır dersin asıl bulgusudur. En iyi iki koşumun sayıları arasındaki fark **0,000028**,
yani dört ondalıkta aynı sayı; buna karşılık iki bölmeleme nokta çiftlerinin **0,0037**'sinde
ayrışıyor. En iyi ile en kötü koşum arasında sayı farkı 0,2115, ayrışma **0,2844** — çiftlerin
dörtte birinden fazlası. Küme içi toplam uzaklık bir bölmelemeyi tek bir sayıya indirir ve o sayının
aynı kalmasıyla korunan şey, kimin kiminle aynı kümede olduğu değildir.

Yakınsama turu 11 ile 52 arasında değişiyor ve hiçbir koşum 60 turluk sınıra dayanmıyor. Yineleme
gerçekten duruyor, ama nerede durduğu çekirdeklerin nereye düştüğüne bağlı.

#### Küme Sayısı Bir Ayar Değişkeni

k modelin veriden öğrendiği bir şey değil, dışarıdan verilen bir sayıdır — M27/K03'ün terimiyle bir
**ayar değişkeni**. Ölçüye bakarak seçmek doğrudan işlemez: küme sayısı arttıkça küme içi toplam
uzaklık **tek yönde azalır** ve en küçük değeri her noktanın kendi kümesi olduğu bölmeleme verir.
Okuma bu yüzden sayının kendisinden değil **ardışık farklardan** yapılır; farkların keskin biçimde
küçüldüğü yere **dirsek** denir.

```python
def rastgele_taban(k, tohum):
    r = uretec(tohum)
    return ic_uzaklik([int(r() * k) for _ in range(N)], k)


BASLANGIC, ADAY, ICUZ = 12, 0, {}
print(f"{'k':>3}{'taban':>9}{'yontem':>9}{'fark':>9}{'ardisik fark':>14}{'dusus orani':>13}")
for k in range(1, 11):
    r = uretec(TOHUM + 8800 + k)
    ICUZ[k] = min(ic_uzaklik(k_ortalamalar(cekirdek(r, k))[0], k) for _ in range(BASLANGIC))
    ADAY += BASLANGIC
    taban = statistics.fmean(rastgele_taban(k, TOHUM + 9900 + 50 * k + t) for t in range(5))
    d = "" if k == 1 else f"{ICUZ[k - 1] - ICUZ[k]:14.4f}"
    o = "" if k == 1 else f"{(ICUZ[k - 1] - ICUZ[k]) / ICUZ[k - 1]:13.3f}"
    print(f"{k:>3}{taban:>9.4f}{ICUZ[k]:>9.4f}{taban - ICUZ[k]:>9.4f}{d}{o}")
print(f"denenen aday {ADAY} kosum ({BASLANGIC} baslangic x 10 kume sayisi)")

FARK = {k: ICUZ[k - 1] - ICUZ[k] for k in range(2, 11)}
kir = max(range(2, 10), key=lambda k: FARK[k] - FARK[k + 1])
esik = min(k for k in range(2, 11) if FARK[k] / ICUZ[k - 1] < 0.10)
egim = (ICUZ[10] - ICUZ[1]) / 9
sapma = {k: (ICUZ[1] + egim * (k - 1)) - ICUZ[k] for k in range(2, 10)}
kiris = max(sapma, key=sapma.get)
ikinci = sorted(sapma, key=sapma.get)[-2]
print("\ndirsegin uc ayri okumasi")
print(f"  ardisik farkin en cok azaldigi k      {kir}")
print(f"  dusus orani ilk kez 0.10'un altina indigi k  {esik}")
print(f"  uc noktayi birlestiren kirise en uzak k      {kiris}"
      f"   (sapma {sapma[kiris]:.4f}; ikincisi k = {ikinci}, sapma {sapma[ikinci]:.4f})")
print(f"  ardisik farklar tek yonlu degil: k = 9'da {FARK[9]:.4f}, k = 10'da {FARK[10]:.4f}")
```

```
  k    taban   yontem     fark  ardisik fark  dusus orani
  1   6.0000   6.0000   0.0000
  2   5.9928   4.8521   1.1407        1.1479        0.191
  3   5.9894   3.9996   1.9897        0.8524        0.176
  4   5.9730   3.5655   2.4075        0.4342        0.109
  5   5.9721   3.2151   2.7570        0.3504        0.098
  6   5.9635   2.8969   3.0666        0.3182        0.099
  7   5.9486   2.6732   3.2754        0.2237        0.077
  8   5.9384   2.5020   3.4365        0.1712        0.064
  9   5.9443   2.3874   3.5568        0.1146        0.046
 10   5.9181   2.2570   3.6611        0.1304        0.055
denenen aday 120 kosum (12 baslangic x 10 kume sayisi)

dirsegin uc ayri okumasi
  ardisik farkin en cok azaldigi k      3
  dusus orani ilk kez 0.10'un altina indigi k  5
  uc noktayi birlestiren kirise en uzak k      4   (sapma 1.1869; ikincisi k = 3, sapma 1.1686)
  ardisik farklar tek yonlu degil: k = 9'da 0.1146, k = 10'da 0.1304
```

Taban sütunu tek başına bir uyarıdır. Rastgele atamanın sayısı da k büyüdükçe düşüyor: 6,0000'dan
5,9181'e. Düşüş küçüktür ama gerçektir, çünkü her yeni küme kendi merkezini kendi noktalarından
kestirir ve kestirim gürültüsü ölçüyü aşağı çeker. Öğrenmeyen bir yordamın sayısı bile k arttıkça
iyileşiyorsa, yöntemin düşüşünün tamamı yapıya bağlanamaz. Fark sütunu bu yüzden gereklidir.

Dirsek ise tablodan tek bir yanıt vermiyor. Aynı on satır üzerinde üç yaygın okuma denendi ve
**üçü üç ayrı küme sayısı seçti**: ardışık farkın en çok azaldığı yer k = 3, düşüş oranının ilk kez
0,10'un altına indiği yer k = 5, uç noktaları birleştiren kirişe en uzak nokta k = 4. Üçüncüsünde
kazananın sapması 1,1869, ikincisininki 1,1686 — aradaki fark yüzde ikiden az. Son satır tabloyu
büsbütün kapatıyor: ardışık farklar tek yönlü bile değil, k = 9'da 0,1146 iken k = 10'da 0,1304'e
çıkıyor.

Denenen aday sayısı **120 koşumdur** ve bu sayı bildirilen her hücrenin yanında durur. k = 4
satırındaki 3,5655, on iki başlangıcın en iyisidir; otuz başlangıçla yapılan arama aynı k için
3,5649 bulmuştu. Aradaki 0,0006 veriden değil **arama bütçesinden** gelir.

#### Özet

- Yordam iki adımın dönüşümlü tekrarıdır ve her adım ölçüyü düşürür ya da aynı bırakır; yineleme
  bu yüzden durmak zorundadır, ama durduğu yerin en iyi olduğu güvence altında değildir.
- Otuz rastgele başlangıç k = 4'te **26 ayrı bölmeleme** verdi; sayılar 3,5649 ile 3,7763 arasında,
  yakınsama 11 ile 52 tur arasında değişti.
- Sayı farkı ile bölmeleme farkı aynı şey değildir: dört ondalıkta aynı sayıyı veren iki koşum nokta
  çiftlerinin 0,0037'sinde, en iyi ile en kötü koşum 0,2844'ünde ayrışıyor.
- Rastgele atama tabanı da küme sayısıyla düşer, 6,0000'dan 5,9181'e; yöntemin farkı bu taban
  çıkarıldıktan sonra 2,4075'ten 3,6611'e çıkar.
- Dirseğin üç ayrı okuması üç ayrı küme sayısı seçti — 3, 5 ve 4 — ve ardışık farklar tek yönlü bile
  değil; seçilen sayı 120 koşumluk bir arama bütçesinin sonucudur.

#### Sonraki Adım

Bu yordamın iki kısıtı birbirine bağlıdır: küme sayısı **başlamadan önce** verilmek zorundadır ve
her koşum tek bir bölmeleme üretir. k = 3, k = 4 ve k = 5 için üç ayrı arama yapıldı ve üç sonuç
birbiriyle ilişkisiz çıktı — k = 3'ün kümeleri k = 5'in kümelerinin birleşimleri değil. Sonraki ders
sırayı tersine çeviren bir aileyi alır: her nokta kendi kümesiyle başlar, en yakın iki küme
birleştirilir ve tek kümeye kadar **iç içe geçmiş bir küme ailesi** üretilir. Küme sayısı burada
girdi değil, ağacın hangi yükseklikten kesildiği sorusunun yanıtıdır. Buna karşılık yeni bir karar
belirir: "en yakın iki küme" ifadesi nokta uzaklığından türetilemez, kümeler arası uzaklık ayrıca
tanımlanmalıdır. Ders üç **bağlantı ölçütünün** aynı veride kaç noktada ayrıştığını sayar ve ağaç
şemasını metin olarak basar.

### Hiyerarşik Kümeleme

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/hiyerarsik-kumeleme

Küme sayısını girdi olmaktan çıkarıp kesme kararına dönüştüren bir aile ve o kararın bedeli: aynı 200 abonede tek, tam ve ortalama bağlantı aynı ilk birleştirmeden (0,079) başlayıp 2,245, 8,917 ve 4,994'te biter; 2,0 yüksekliğinden kesildiğinde sırasıyla 3, 56 ve 37 küme verir. Dört kümede tek bağlantı 6,0849 ile rastgele atama tabanını yalnız 0,1492 geçer ve [1, 1, 1, 197] üretir, tam bağlantı 4,5419 ile 1,6922 geçer; iki bağlantı 200 abonenin 86'sını ayrı yerlere düşürür.

Önceki dersin yordamı küme sayısını **başlamadan önce** istiyordu ve her koşum tek bir bölmeleme
üretiyordu. Üç ayrı k için üç ayrı arama yapıldığında çıkan üç sonuç birbiriyle ilişkisizdi: üç
kümeli bölmelemenin kümeleri, beş kümeli bölmelemenin kümelerinin birleşimi değildi. Dirseğin üç
ayrı okuması üç ayrı k seçtiğine göre bu, karar verilemeyen bir noktada üç ilişkisiz cevap demekti.

Bu ders sırayı tersine çevirir. Her abone kendi kümesiyle başlar, her adımda **en yakın iki küme**
birleştirilir ve 200 aboneden tek kümeye kadar iç içe geçmiş bir aile üretilir. Küme sayısı artık
girdi değildir; ağacın hangi yükseklikten kesildiği sorusunun yanıtıdır. Buna karşılık yeni bir
karar belirir ve dersin ölçtüğü şey odur: "en yakın iki küme" ifadesi nokta uzaklığından
türetilemez, çünkü iki **küme** arasındaki uzaklığın kendisi ayrıca tanımlanmalıdır.

- **KU16.** Küme, sütunlar ve benzerlik tanımı önceki dersteki gibidir; ölçek yine 756 abonelik
  eğitim kümesinden öğrenilir. Kümeleme ise **ilk 200 abone** üzerinde yapılır: birleştirmeli
  yordamın maliyeti nokta sayısının küpüyle büyür. Bu bir ölçek kararıdır ve buradaki sayılar 756
  abone için değil, bu 200 abone içindir.
- **KU17.** Nokta uzaklığı standart puanlarda kare uzaklığın kareköküdür; birleştirme yükseklikleri
  sütun birimiyle okunabilsin diye karekök alınır.
- **KU18.** **Bağlantı ölçütü**, iki küme arasındaki uzaklığın tanımıdır. Üçü denenir: **tek
  bağlantı** en yakın nokta çiftini, **tam bağlantı** en uzak nokta çiftini, **ortalama bağlantı**
  bütün nokta çiftlerinin ortalamasını alır.
- **KU19.** **Birleştirme yüksekliği**, o adımda birleştirilen iki küme arasındaki bağlantı
  uzaklığıdır; ölçüte özgüdür ve ölçütler arasında karşılaştırılamaz.
- **KU20.** Yordam belirlenimcidir; başlangıç seçimi yoktur ve eşit uzaklıkta iki aday çıkarsa
  küçük numaralı çift seçilir.
- **KU21.** Ölçü yine **küme içi toplam uzaklık**tır, taban çizgileri **rastgele atama** ve **tek
  küme**dir. Ağaçtan k küme okumak için kökten başlanır ve en yüksek dal, k dal kalana kadar açılır.
- **KU22.** İki bölmelemenin ayrışması iki ölçüyle verilir: **ayrışan nokta çifti oranı** ve **en
  iyi küme eşleştirmesiyle ayrı düşen nokta sayısı**; ikincisi için dört kümenin 24 eşleştirmesi
  denenir.
- **KU23.** Ölçüt, önceki dersin yordamının **doğrudan en küçüğe indirmeye çalıştığı** sayıdır;
  hiyerarşik yordamlar onu hedeflemez. Karşılaştırma bu yüzden ölçütün lehinedir.

#### Birleştirerek Kurulan Ağaç

Yordamın iskeleti kısadır: kümeler arası uzaklıklar tutulur, en küçüğü bulunur, iki küme
birleştirilir ve yeni kümenin ötekilere uzaklığı bağlantı ölçütünün kuralıyla eskilerinden
hesaplanır. Bu son adım yordamı ucuzlatır — birleşme sonrası hiçbir nokta çiftine yeniden bakılmaz.
Üç ölçüt yalnız o tek satırda ayrılır.

```python
# hiyerarsik.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum, ayni standart
# puan tanimi; etiket yine uretilmez.
import itertools
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21), (0.22, 17), (0.18, 26), (0.14, 14), (0.18, 23)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


VERI = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    temel = BOLGE[ayrik(r(), [x[0] for x in BOLGE])][1]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:
        continue
    r, v = uretec(TOHUM + 17001 + i), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            temel * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        x = {"ort_tuketim": round(sum(v) / len(v), 2), "oynaklik": round(max(v) - min(v), 2),
             "hane": hane, "memnuniyet": memnun, "donem": len(v)}
        x["kisi_basi"] = round(x["ort_tuketim"] / hane, 3)
        VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
HAM_V = [[x[a] for a in ALAN] for x in EGT]
ORT = [statistics.fmean(c) for c in zip(*HAM_V)]
SAP = [statistics.pstdev(c) for c in zip(*HAM_V)]
V = [[(v[j] - ORT[j]) / SAP[j] for j in range(len(ALAN))] for v in HAM_V]

ALT = 200
W = V[:ALT]
kare = lambda a, b: sum((p - q) * (p - q) for p, q in zip(a, b))
merkez = lambda g: [statistics.fmean(c) for c in zip(*g)]
uzak = lambda a, b: math.sqrt(kare(a, b))

BAGLANTI = {"tek": lambda da, db, na, nb: min(da, db),
            "tam": lambda da, db, na, nb: max(da, db),
            "ortalama": lambda da, db, na, nb: (na * da + nb * db) / (na + nb)}


def birlestir(kural):                       # MODELDIR: en yakin iki kumeyi birlestir
    D = {(i, j): uzak(W[i], W[j]) for i in range(ALT) for j in range(i + 1, ALT)}
    ikili = lambda x, y: (x, y) if x < y else (y, x)
    etkin, boy, cocuk, yukseklik, yeni = list(range(ALT)), {i: 1 for i in range(ALT)}, {}, {}, ALT
    while len(etkin) > 1:
        a, b = min(((x, y) for i, x in enumerate(etkin) for y in etkin[i + 1:]),
                   key=lambda p: (D[ikili(*p)], p))
        cocuk[yeni], yukseklik[yeni] = (a, b), D[ikili(a, b)]
        for c in etkin:
            if c not in (a, b):
                D[ikili(c, yeni)] = kural(D[ikili(a, c)], D[ikili(b, c)], boy[a], boy[b])
        boy[yeni] = boy[a] + boy[b]
        etkin = [c for c in etkin if c not in (a, b)] + [yeni]
        yeni += 1
    return cocuk, yukseklik, boy, yeni - 1


AGAC = {ad: birlestir(kural) for ad, kural in BAGLANTI.items()}
print(f"alt kume {ALT} abone; her baglanti olcutu {ALT - 1} birlestirme yapti")
for ad, (_, yukseklik, _, _) in AGAC.items():
    h = sorted(yukseklik.values())
    print(f"  {ad:<9} en kucuk birlestirme {h[0]:.3f}, ortanca {statistics.median(h):.3f}, "
          f"son uc {h[-3]:.3f} {h[-2]:.3f} {h[-1]:.3f}")
```

```
alt kume 200 abone; her baglanti olcutu 199 birlestirme yapti
  tek       en kucuk birlestirme 0.079, ortanca 0.896, son uc 1.977 2.173 2.245
  tam       en kucuk birlestirme 0.079, ortanca 1.211, son uc 6.370 6.791 8.917
  ortalama  en kucuk birlestirme 0.079, ortanca 1.086, son uc 4.536 4.715 4.994
```

Üç ölçüt de aynı yerden başlıyor. İlk birleştirme her üçünde 0,079; tek nokta içeren iki küme
arasında en yakın, en uzak ve ortalama çift aynı çift olduğu için ölçütler tek elemanlı kümelerde
ayrışamaz. Ayrışma kümeler büyüdükçe başlıyor ve tepede uçurum var: son birleştirme tek bağlantıda
2,245, ortalama bağlantıda 4,994, tam bağlantıda 8,917. **Yükseklik verinin değil ölçütün
birimidir** ve iki ağacın yükseklikleri yan yana okunamaz.

#### Ağaç Şeması ve Kesme Yüksekliği

Ağaç metin olarak basılır: her satır bir dalı, girinti dalın derinliğini, `h` birleştirme
yüksekliğini, `n` dalın altındaki abone sayısını verir. Üç düzeyle sınırlandırılmıştır, daha derin
dallar tek satırda özetlenir.

```python
def sema(ad, ust=3, en_az=15):
    cocuk, yukseklik, boy, kok = AGAC[ad]
    print(f"agac semasi — {ad} baglanti, ust {ust} duzey")
    yig = [(kok, 0)]
    while yig:
        d, dr = yig.pop()
        if d in cocuk and boy[d] >= en_az and dr < ust:
            print(f"  {'  ' * dr}h={yukseklik[d]:.3f}  n={boy[d]}")
            yig.extend((c, dr + 1) for c in sorted(cocuk[d], key=lambda c: boy[c]))
        else:
            print(f"  {'  ' * dr}dal       n={boy[d]}")


sema("ortalama")
sema("tek")


def kume_sayisi(ad, h):                     # h yuksekliginin ustunde kac birlestirme kaldi
    _, yukseklik, _, _ = AGAC[ad]
    return sum(1 for y in yukseklik.values() if y > h) + 1


print(f"\n{'kesme yuksekligi':>18}" + "".join(f"{ad:>12}" for ad in BAGLANTI))
for h in (1.0, 1.5, 2.0, 3.0, 4.0, 5.0):
    print(f"{h:>18.1f}" + "".join(f"{kume_sayisi(ad, h):>12}" for ad in BAGLANTI))
```

```
agac semasi — ortalama baglanti, ust 3 duzey
  h=4.994  n=200
    h=4.715  n=198
      h=3.704  n=181
        dal       n=140
        dal       n=41
      h=4.536  n=17
        dal       n=15
        dal       n=2
    dal       n=2
agac semasi — tek baglanti, ust 3 duzey
  h=2.245  n=200
    h=2.173  n=199
      h=1.977  n=198
        dal       n=197
        dal       n=1
      dal       n=1
    dal       n=1

  kesme yuksekligi         tek         tam    ortalama
               1.0          62         123         114
               1.5          16          80          59
               2.0           3          56          37
               3.0           1          26          10
               4.0           1          13           4
               5.0           1           9           1
```

İki şema iki ayrı ağaç gösteriyor. Ortalama bağlantıda kök 200'ü 198 ile 2'ye, sonra 181 ile 17'ye,
sonra 140 ile 41'e ayırıyor; her adımda iki yanı da dolu bir bölünme var. Tek bağlantıda ise her
adımda **tek bir abone** kopuyor: 200'den 199, ondan 198, ondan 197. Sebep ölçütün tanımındadır —
en yakın çifte bakıldığı için büyük bir kümeye yakın duran her nokta o kümeye eklenir. Ağaç bir
zincire dönüşür ve üstten kesildiğinde verdiği şey bir bölmeleme değil, tek küme artı birkaç tek
noktadır.

Alt tablo kesme yüksekliğinin ne olduğunu gösteriyor. 2,0 yüksekliğinden kesmek tek bağlantıda 3,
ortalama bağlantıda 37, tam bağlantıda 56 küme verir; tek bir sayı, üç ölçütte on sekiz kat oynayan
bir küme sayısına karşılık geliyor. 5,0'da tek ve ortalama bağlantı 1 küme derken tam bağlantı hâlâ
9 küme sayıyor. Kesme yüksekliği bu yüzden bir gözlem değil bir **karardır**; küme sayısını ağaçtan
okuma iddiası, kararın yerini değiştirmekten başka bir şey yapmaz.

#### Bağlantı Ölçütünün Oynattığı Bölmeleme

Üç ağaç dört kümeye kadar açılıp aynı ölçüyle, aynı tabanla karşılaştırılır. Yanına önceki dersin
merkez temelli yordamı da aynı 200 abone üzerinde konur.

```python
def kesim_k(ad, k):                         # kokten baslayip en yuksek dali k'ya kadar ac
    cocuk, yukseklik, _, kok = AGAC[ad]
    dallar = [kok]
    while len(dallar) < k:
        d = max((x for x in dallar if x in cocuk), key=lambda x: yukseklik[x])
        dallar = [x for x in dallar if x != d] + list(cocuk[d])
    a = [0] * ALT
    for c, d in enumerate(dallar):
        yig = [d]
        while yig:
            x = yig.pop()
            if x in cocuk:
                yig.extend(cocuk[x])
            else:
                a[x] = c
    return a


def ic_uzaklik(a, k):                       # kume ici toplam uzaklik, nokta basina
    t = 0.0
    for c in range(k):
        g = [W[i] for i in range(ALT) if a[i] == c]
        if g:
            t += sum(kare(v, merkez(g)) for v in g)
    return t / ALT


def uyusmazlik(a, b):
    ay = sum((a[i] == a[j]) != (b[i] == b[j]) for i in range(ALT) for j in range(i + 1, ALT))
    return 2 * ay / (ALT * (ALT - 1))


def ayri_nokta(a, b, k):                    # en iyi kume eslestirmesiyle uyusmayan nokta
    return ALT - max(sum(p[a[i]] == b[i] for i in range(ALT))
                     for p in itertools.permutations(range(k)))


def k_ortalamalar(k, tohum, kosum=12):      # 02. dersin yordami, ayni alt kumede
    r, en_iyi = uretec(tohum), None
    for _ in range(kosum):
        s = []
        while len(s) < k:
            j = int(r() * ALT)
            if j not in s:
                s.append(j)
        M, a = [W[j] for j in s], None
        for _tur in range(60):
            y = [min(range(k), key=lambda c: kare(v, M[c])) for v in W]
            if y == a:
                break
            a = y
            M = [merkez([W[i] for i in range(ALT) if a[i] == c]) or M[c] for c in range(k)]
        u = ic_uzaklik(a, k)
        en_iyi = u if en_iyi is None else min(en_iyi, u)
    return en_iyi


def rastgele_atama(k, tohum):               # TABAN: hicbir yapi kullanmaz
    r = uretec(tohum)
    return [int(r() * k) for _ in range(ALT)]


K = 4
P = {ad: kesim_k(ad, K) for ad in BAGLANTI}
taban = statistics.fmean(ic_uzaklik(rastgele_atama(K, TOHUM + 33300 + t), K) for t in range(5))
print(f"k = {K}, {ALT} abone")
print(f"  {'taban - tek kume':<24} {ic_uzaklik([0] * ALT, K):.4f}")
print(f"  {'taban - rastgele atama':<24} {taban:.4f}")
for ad in BAGLANTI:
    u = ic_uzaklik(P[ad], K)
    print(f"  {ad + ' baglanti':<24} {u:.4f}   fark {taban - u:+.4f}"
          f"   kume boylari {sorted(P[ad].count(c) for c in range(K))}")
print(f"  {'k-ortalamalar (12 kosum)':<24} {k_ortalamalar(K, TOHUM + 44400):.4f}")
print("\nbaglanti olcutleri arasi ayrisma")
A = list(BAGLANTI)
for i in range(3):
    for j in range(i + 1, 3):
        print(f"  {A[i]:<9} - {A[j]:<9} nokta cifti {uyusmazlik(P[A[i]], P[A[j]]):.4f}, "
              f"ayri dusen nokta {ayri_nokta(P[A[i]], P[A[j]], K):>3} / {ALT}")
```

```
k = 4, 200 abone
  taban - tek kume         6.3473
  taban - rastgele atama   6.2340
  tek baglanti             6.0849   fark +0.1492   kume boylari [1, 1, 1, 197]
  tam baglanti             4.5419   fark +1.6922   kume boylari [6, 16, 65, 113]
  ortalama baglanti        5.0080   fark +1.2260   kume boylari [2, 2, 15, 181]
  k-ortalamalar (12 kosum) 3.4245

baglanti olcutleri arasi ayrisma
  tek       - tam       nokta cifti 0.5451, ayri dusen nokta  86 / 200
  tek       - ortalama  nokta cifti 0.1477, ayri dusen nokta  17 / 200
  tam       - ortalama  nokta cifti 0.4673, ayri dusen nokta  82 / 200
```

Tek bağlantının sayısı 6,0849 ve rastgele atama tabanını yalnız **0,1492** geçiyor. Küme boyları
gerekçeyi veriyor: [1, 1, 1, 197]. Dört küme istendi, üç tanesi tek abone; sonuç öğrenmeyen bir
yordamdan ayırt edilemeyecek kadar ona yakın. Tam bağlantı 4,5419 ile tabanı 1,6922 geçiyor ve
boyları [6, 16, 65, 113] — en dengeli bölmeleme onunki, çünkü en uzak çifte bakan ölçüt geniş
kümeleri cezalandırıyor. Ortalama bağlantı ikisinin arasında: 5,0080, fark 1,2260. **Bağlantı
ölçütünün seçimi, tabanı geçen farkı 0,1492 ile 1,6922 arasında, yani on bir kattan fazla
oynatıyor.**

Alt tablo aynı şeyi bölmeleme düzeyinde sayıyor. Tek ile tam bağlantı nokta çiftlerinin
0,5451'inde ayrışıyor ve en iyi eşleştirmeyle bile **86 abone** ayrı yerlere düşüyor — 200 abonenin
yüzde kırk üçü. Tek ile ortalama bağlantı arasındaki ayrışma çok daha küçük, 17 abone; ortalama
bağlantı da bir büyük kümeye eğilimli olduğu için tekle akraba kalıyor. Tam ile ortalama bağlantı
ise 82 abonede ayrışıyor.

Son satırdaki 3,4245 dikkatli okunmalıdır. Önceki dersin merkez temelli yordamı bu ölçüde üçünü de
açık farkla geçiyor, ama ölçü **onun doğrudan en küçüğe indirmeye çalıştığı sayıdır**; hiyerarşik
yordamlar hiçbir adımda bu toplamı hesaplamaz bile. Karşılaştırma yordamların değerini değil,
ölçütün kimin tarafında durduğunu gösteriyor. Ölçütü bağlantı uzaklıklarının tutarlılığı ya da
zincirlemeye direnç olarak seçseydik sıralama tersine dönerdi.

#### Özet

- Birleştirmeli yordam küme sayısını girdi olmaktan çıkarır; iç içe geçmiş bir küme ailesi üretir ve
  karar **kesme yüksekliğine** taşınır.
- Üç bağlantı ölçütü aynı ilk birleştirmeden (0,079) başlar ama kökleri 2,245, 4,994 ve 8,917'dir;
  yükseklik ölçütün birimidir ve ölçütler arasında karşılaştırılamaz.
- 2,0 yüksekliğinden kesmek tek bağlantıda 3, ortalama bağlantıda 37, tam bağlantıda 56 küme verir.
- Dört kümede tek bağlantı 6,0849 ile rastgele atama tabanını yalnız 0,1492 geçer ve [1, 1, 1, 197]
  üretir; tam bağlantı 4,5419 ile 1,6922 geçer. Ölçütün seçimi farkı on bir kattan fazla oynatır.
- Tek ile tam bağlantı 200 abonenin 86'sını en iyi eşleştirmede bile ayrı yerlere düşürür; merkez
  temelli yordamın 3,4245'i ise ölçünün onun hedefi olmasıyla birlikte okunur.

#### Sonraki Adım

Üç derste dört yordam denendi — tek geçişli merkez seçimi, yinelemeli merkez güncellemesi ve üç
bağlantı ölçütü — ve hepsi tek bir varsayımı paylaştı: **her nokta bir kümeye atanmak zorundadır.**
Tek bağlantının [1, 1, 1, 197] bölmelemesi bu varsayımın bedelini gösteriyor. Üç abone kendi başına
duruyor ama yordam onları "küme" ilan etmek zorunda kaldı, çünkü elinde başka bir kutu yok. Aynı
biçimde, tam bağlantının 113 abonelik kümesinin kenarında duran ve hiçbir merkeze gerçekten yakın
olmayan noktalar da bir kümeye yazıldı. Hiçbir kümeye ait olmayan nokta diye bir şey yoktu. Sonraki
ders bu varsayımı kaldıran bir aileyi alır — noktaların yoğunluğuna bakan, seyrek bölgelerdeki
noktaları **gürültü** olarak dışarıda bırakan ve küme sayısını hiç sormayan bir yordam — ve dışarıda
bırakılan nokta sayısının kendisi bir ölçüye dönüşür.

### Yoğunluk Tabanlı Kümeleme

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/yogunluk-tabanli-kumeleme

Her noktayı bir kümeye atamak zorunda olmayan bir yordamın ölçülmesi: 756 abonenin 73'ü hiçbir kümeye bağlanmıyor ve bu 73 noktanın beşinci komşu uzaklığı 1,7551, rastgele işaretlenen 73 noktanınki 1,0200. On sekiz ayar bileşiminde küme sayısı 1 ile 56, gürültü sayısı 4 ile 674 arasında geziniyor. Sütunları kendi içinde karıştırılmış kümede on sekiz ayarın on sekizinde gürültü daha yüksek çıkıyor, ama küme sayısı on üçünde zaten farklı. İki halkalı bir kurguda merkez temelli bölmeleme küme içi toplam uzaklığı 703,25'te tutuyor, yoğunluk tabanlı bölmeleme 878,71'de bırakıyor; en uzun zorunlu bağlantıda sıralama tersine dönüyor.

Önceki üç ders aynı örtük kuralı paylaşıyordu: bölmeleme, kümedeki **her** noktayı bir kümeye
atamak zorundaydı. Benzerlik tanımı, başlangıç noktası ve bağlantı ölçütü bölmelemeyi
değiştiriyordu, ama hiçbirinde "bu nokta hiçbir kümeye ait değil" gibi bir çıktı yoktu.
Merkezlerden uzakta, tek başına kalmış bir abone de en yakın merkeze yazılıyor ve o kümenin
ortalamasını kendi yönüne çekiyordu.

Bu ders o zorunluluğu kaldıran bir aile alır. **Yoğunluk tabanlı kümeleme** küme sayısını girdi
olarak istemez; "küme" sözcüğünü yerel bir kalabalık üzerinden tanımlar ve kalabalığa
bağlanamayan noktaları **gürültü noktası** olarak dışarıda bırakır. Bedeli, dışarıda bırakma
kararının iki ayar değişkenine bağlı olmasıdır; bu iki sayı çıkan küme sayısını da gürültü
sayısını da geniş bir aralıkta oynatır.

- **KU31.** Küme M27/K01–K03'ten gelen **kurgu** abone tablosudur: 1.260 abone, tohum 20260218,
  bölme 756/252/252. Kümeleme yalnız 756 satırlık eğitim payı üzerinde çalışır; **etiket sütunu
  bu kursta bir kenara konmuştur** ve hiçbir yöntem onu görmez.
- **KU32.** Altı sayısal sütun eğitim payından öğrenilen standart puanla ölçeklenir. Bölge
  göstergeleri dışarıda kalır: iki ayrı bölgeden gelen her nokta çiftine aynı sabit katkı
  eklenirdi ve bu, yoğunluğa sessizce konmuş bir ağırlık olurdu. Uzaklık kare uzaklığın
  kareköküdür, böylece yarıçap ölçeklenmiş sütunlarla aynı birimde okunur.
- **KU33.** **Çekirdek nokta**, komşuluk yarıçapı içinde kendisiyle birlikte en az `m` nokta
  bulunan noktadır. Bir çekirdek noktanın yarıçapına giren ama kendisi çekirdek olmayan nokta
  **sınır noktası**, hiçbir çekirdek noktanın yarıçapına girmeyen nokta **gürültü noktasıdır**.
- **KU34.** Bu dersin taban çizgisi **rastgele işaretlemedir**: yöntemin çıktısı bir işaretleme
  olduğu için taban da işaretleme olmalıdır. Tek küme tabanı hiçbir noktayı işaretlemez ve
  karşılaştırılacak sayı üretmez.
- **KU35.** Yapısız başvuru **sütun içi yeniden karıştırmayla** kurulur: her sütun kendi içinde
  bağımsız karıştırılır, sütunun dağılımı birebir korunur, yalnız sütunlar arası ilişki bozulur.
  Araç M26/K05'in yeniden karıştırma sınamasınındır; sınama kuramı burada tekrarlanmaz.
- **KU36.** Halka kurgusu bu ders için üretilmiş ayrı bir **kurgu** kümedir; abone tablosuyla
  ilgisi yoktur ve yalnız biçim karşılaştırması için kullanılır.

#### Çekirdek, Sınır ve Gürültü Noktası

Yordam üç satırlık bir tanımdan doğar. Her nokta için komşuluk yarıçapı içindeki komşular
sayılır; sayı eşiği aşıyorsa nokta çekirdektir. Birbirinin yarıçapına giren çekirdek noktalar
aynı kümeye bağlanır ve bağlantı zincir gibi yayılır. Zincir bir sınır noktasına ulaştığında
durur: sınır noktası kümeye katılır ama kümeyi genişletmez.

```python
# yogunluk.py — MODELDIR. M27/K01–K03'un KURGU abone tablosu ayni tohumla
# yeniden uretilir; etiket sutunu bu kursta bilerek bir kenara konur.
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21), (0.22, 17), (0.18, 26), (0.14, 14), (0.18, 23)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[0] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append((10001 + i, b[1], hane, memnun))
for no, taban, hane, memnun in ABONE:
    r, v = uretec(TOHUM + 7000 + no), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            taban * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        ort = round(sum(v) / len(v), 2)
        VERI.append({"ort_tuketim": ort, "oynaklik": round(max(v) - min(v), 2),
                     "hane": hane, "memnuniyet": memnun, "donem": len(v),
                     "kisi_basi": round(ort / hane, 3)})

SAYISAL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
OLCEK = {a: (statistics.fmean(x[a] for x in EGT),
             statistics.pstdev([x[a] for x in EGT])) for a in SAYISAL}
N = [[(x[a] - OLCEK[a][0]) / OLCEK[a][1] for a in SAYISAL] for x in EGT]


def uzaklik_dizeyi(P):
    D = [[0.0] * len(P) for _ in P]
    for i in range(len(P)):
        for j in range(i + 1, len(P)):
            D[i][j] = D[j][i] = math.dist(P[i], P[j])
    return D


def komsuluk(D, e):
    return [[j for j in range(len(D)) if j != i and D[i][j] <= e] for i in range(len(D))]


def yogunluk_kumele(kom, m):            # MODELDIR: yogunluk tabanli bolmeleme
    cek = [len(k) + 1 >= m for k in kom]         # cekirdek nokta
    etk, c = [-1] * len(kom), 0
    for i in range(len(kom)):
        if etk[i] != -1 or not cek[i]:
            continue
        etk[i], yigin = c, [i]
        while yigin:
            u = yigin.pop()
            if not cek[u]:                       # sinir noktasi genislemez
                continue
            for v in kom[u]:
                if etk[v] == -1:
                    etk[v], _ = c, yigin.append(v)
        c += 1
    return etk, c, sum(1 for x in etk if x == -1)


D = uzaklik_dizeyi(N)
BES = [sorted(D[i])[5] for i in range(len(N))]   # besinci en yakin komsuya uzaklik
ETK, KUME, GURULTU = yogunluk_kumele(komsuluk(D, 1.2), 5)
gur = [i for i in range(len(N)) if ETK[i] == -1]
rs = karistir(list(range(len(N))), TOHUM + 410000)[:len(gur)]
buyuk = sorted((ETK.count(c) for c in range(KUME)), reverse=True)
print(f"nokta {len(N)}, sutun {len(SAYISAL)}")
print(f"e=1.20 m=5 -> kume {KUME}, gurultu {GURULTU}, buyuklukler {buyuk}")
print(f"besinci komsu uzakligi: gurultu {statistics.fmean(BES[i] for i in gur):.4f}, "
      f"rastgele isaretlenen {statistics.fmean(BES[i] for i in rs):.4f}, "
      f"tumu {statistics.fmean(BES):.4f}")
```

```
nokta 756, sutun 6
e=1.20 m=5 -> kume 6, gurultu 73, buyuklukler [454, 112, 82, 23, 6, 6]
besinci komsu uzakligi: gurultu 1.7551, rastgele isaretlenen 1.0200, tumu 0.9611
```

İlk okunacak şey küme büyüklükleridir: 454, 112, 82, 23, 6, 6. Yöntem eşit büyüklükte kümeler
üretmiyor; 756 noktanın 454'ü tek bir kümede toplanıyor, son iki küme altışar noktadan oluşuyor.
Merkez temelli bölmeleme böyle bir çıktı veremez, çünkü her merkez kendine yakın noktaları çeker
ve büyüklükler dengelenir. Burada büyüklüğü belirleyen merkez değil **bağlantı zinciridir**.

İkinci sayı 73'tür: bu ayarda 756 abonenin 73'ü hiçbir kümeye bağlanmıyor. Ama bir işaretlemenin
kendi başına anlamı yoktur. Taban çizgisi olarak aynı sayıda noktayı rastgele işaretleyen yordam
seçilir, çünkü yöntemin ürettiği şey bir bölmeleme değil bir **işaretlemedir**. Ölçü, işaretlenen
noktaların beşinci en yakın komşusuna uzaklığıdır: yöntemin işaretlediği 73 noktada ortalama
**1,7551**, rastgele işaretlenen 73 noktada **1,0200**, tüm kümede **0,9611**. Fark **0,7351**.

Fark nereden geliyor sorusunun yanıtı burada rahatsız edicidir: **ölçütten**. En az nokta sayısı
beş olduğu için yöntem tam olarak "dördüncü komşusu yarıçap içinde mi" sorusuna bakarak
işaretliyor ve beşinci komşu uzaklığı neredeyse aynı büyüklüktür. Bir yöntemi kendi ölçütüyle
sınamak, tanımı yeniden yazmaktan başka bir şey değildir; kullanılabilir bir fark için ölçütün
dışına çıkmak gerekir.

#### İki Ayar Değişkeninin Süpürülmesi

Küme sayısı bu ailede girdi değildir, ama iki ayar değişkeni vardır ve ikisi de veriden
öğrenilmez. M27/K03'ün kuralı sürer: denenen aday sayısı yazılır. Süpürme altı yarıçap ile üç
eşiği çaprazlar, her hücrede küme sayısı ile gürültü sayısını birlikte basar ve aynı süpürme
sütunları kendi içinde karıştırılmış kümede de koşturulur.

```python
def sutun_karistir(P, tohum):
    Y = [list(x) for x in P]
    for s in range(len(P[0])):
        for i, j in enumerate(karistir(list(range(len(P))), tohum + s * 17)):
            Y[i][s] = P[j][s]
    return Y


YAP = sutun_karistir(N, TOHUM + 610000)          # KURGU: sutun ici yeniden karistirma
DY = uzaklik_dizeyi(YAP)
ELER, MLER, SUPURME = (0.6, 0.8, 1.0, 1.2, 1.5, 1.8), (3, 5, 9), []
print(f"{'e':>5}" + "".join(f"{'m=' + str(m):>12}" for m in MLER) + "   (kume/gurultu)")
for e in ELER:
    kv, ky, sv, sy = komsuluk(D, e), komsuluk(DY, e), [], []
    for m in MLER:
        _, c, g = yogunluk_kumele(kv, m)
        _, cy, gy = yogunluk_kumele(ky, m)
        SUPURME.append((c, g, cy, gy))
        sv.append(f"{c}/{g}")
        sy.append(f"{cy}/{gy}")
    print(f"{e:>5.2f}" + "".join(f"{x:>12}" for x in sv) + "   veri")
    print(f"{'':>5}" + "".join(f"{x:>12}" for x in sy) + "   karistirilmis")
print(f"\ndenenen aday {len(SUPURME)}; kume sayisi {min(s[0] for s in SUPURME)} ile "
      f"{max(s[0] for s in SUPURME)} arasinda, gurultu {min(s[1] for s in SUPURME)} ile "
      f"{max(s[1] for s in SUPURME)} arasinda")
print(f"gurultunun karistirilmis kumeden az oldugu ayar "
      f"{sum(1 for s in SUPURME if s[1] < s[3])}/{len(SUPURME)}; "
      f"kume sayisinin farkli oldugu ayar {sum(1 for s in SUPURME if s[0] != s[2])}")
```

```
    e         m=3         m=5         m=9   (kume/gurultu)
 0.60      56/332      23/490       6/674   veri
           33/602       6/720       0/756   karistirilmis
 0.80      52/220      24/356      10/550   veri
           35/468      13/595       1/742   karistirilmis
 1.00       15/74       6/132       4/230   veri
           16/232       6/332       2/449   karistirilmis
 1.20       13/39        6/73       4/118   veri
            6/113       6/171       3/275   karistirilmis
 1.50        2/17        1/26        1/43   veri
             3/46        2/57        1/89   karistirilmis
 1.80         1/4         1/5         1/6   veri
             2/14        1/22        1/30   karistirilmis

denenen aday 18; kume sayisi 1 ile 56 arasinda, gurultu 4 ile 674 arasinda
gurultunun karistirilmis kumeden az oldugu ayar 18/18; kume sayisinin farkli oldugu ayar 13
```

Aynı veride küme sayısı **1 ile 56**, gürültü sayısı **4 ile 674** arasında geziniyor. Yarıçap
0,60 ve eşik 9 olduğunda 756 abonenin 674'ü gürültü sayılıyor, yarıçap 1,80 olduğunda 4'ü. "Bu
veride kaç küme var" sorusunun yanıtı sorunun kendisinden değil, iki ayarın seçilmesinden sonra
doğuyor.

Karıştırılmış satırlar farkın kaynağını ayırır. Sütun içi karıştırma her sütunun kendi dağılımını
birebir korur; bozduğu tek şey sütunlar arasındaki ilişkidir, dolayısıyla iki koşum arasındaki
her fark **sütunlar arası yapıdan** gelir. Gürültü sayısı on sekiz ayarın **on sekizinde**
karıştırılmış kümede daha yüksek: yarıçap 1,20 ve eşik 5'te 73'e karşı 171. Aynı yoğunluk
eşiğinde gerçek küme daha derli topludur ve bu fark veriden gelir.

Küme sayısı bunu söylemez. On sekiz ayarın beşinde iki koşum **aynı** küme sayısını veriyor, geri
kalanında fark bir yön taşımıyor: yarıçap 0,60 ile eşik 3'te gerçek veri 56, karıştırılmış küme
33 çıkarıyor, ama yarıçap 1,00'de sıralama tersine dönüyor. Yapısız bir kümede de "küme" bulunur;
bulunan kümelerin **sayısı** yapının kanıtı değildir.

#### Merkezin Bölemediği Biçim

Bu ailenin merkez temelli aileden ayrıldığı yer küme sayısı değil, kümenin **biçimidir**. Merkez
temelli bölmeleme uzayı merkezler arasındaki dik doğrularla böler ve çıkan parçalar her zaman
dışbükeydir. Ortak merkezli iki halka bu bölmelemenin altından kalkamayacağı en yalın biçimdir:
iç halkanın da dış halkanın da merkezi aynı noktadır.

```python
def halka(n, yaricap, tohum):                    # KURGU: merkezi ortak iki halka
    r, p = uretec(tohum), []
    for _ in range(n):
        a, s = r() * 2 * math.pi, yaricap + (r() + r() + r() - 1.5) * 0.30
        p.append([s * math.cos(a), s * math.sin(a)])
    return p


H = halka(120, 1.0, TOHUM + 800000) + halka(240, 3.2, TOHUM + 801000)
DH = uzaklik_dizeyi(H)


def merkez_bolmele(P, k, tohum, tur=60):         # MODELDIR: merkez temelli bolmeleme
    r = uretec(tohum)
    M, e = [list(P[int(r() * len(P))]) for _ in range(k)], [0] * len(P)
    for _ in range(tur):
        for i, x in enumerate(P):
            e[i] = min(range(k), key=lambda c: math.dist(x, M[c]))
        for c in range(k):
            g = [P[i] for i in range(len(P)) if e[i] == c]
            M[c] = [statistics.fmean(x[d] for x in g) for d in range(2)] if g else M[c]
    return e


def kume_ici(P, e):                              # olcut 1: merkezlere toplam uzaklik
    t = 0.0
    for c in set(e):
        g = [P[i] for i in range(len(P)) if e[i] == c]
        m = [statistics.fmean(x[d] for x in g) for d in range(2)]
        t += sum(math.dist(x, m) for x in g)
    return t


def en_uzun_bag(P, e, D):                        # olcut 2: en uzun zorunlu baglanti
    u = 0.0
    for c in set(e):
        idx = [i for i in range(len(P)) if e[i] == c]
        ic, kal = [idx[0]], set(idx[1:])
        while kal:
            d, v = min((min(D[j][i] for i in ic), j) for j in kal)
            u, _ = max(u, d), ic.append(v)
            kal.discard(v)
    return u


EM = merkez_bolmele(H, 2, TOHUM + 805000)
EY, cy, gy = yogunluk_kumele(komsuluk(DH, 0.6), 4)
print(f"halka noktalari {len(H)}; ic halka 120, dis halka 240")
print(f"merkez temelli k=2 -> buyuklukler {[EM.count(0), EM.count(1)]}")
print(f"yogunluk e=0.60 m=4 -> kume {cy}, gurultu {gy}, "
      f"buyuklukler {[EY.count(0), EY.count(1)]}")
print(f"{'olcut':<26}{'merkez temelli':>16}{'yogunluk tabanli':>18}")
print(f"{'kume ici toplam uzaklik':<26}{kume_ici(H, EM):>16.2f}{kume_ici(H, EY):>18.2f}")
print(f"{'en uzun zorunlu baglanti':<26}{en_uzun_bag(H, EM, DH):>16.4f}"
      f"{en_uzun_bag(H, EY, DH):>18.4f}")
print(f"iki bolmelemenin ayristigi nokta "
      f"{sum(1 for i in range(len(H)) if (EM[i] == EM[0]) != (EY[i] == EY[0]))}")
```

```
halka noktalari 360; ic halka 120, dis halka 240
merkez temelli k=2 -> buyuklukler [171, 189]
yogunluk e=0.60 m=4 -> kume 2, gurultu 0, buyuklukler [120, 240]
olcut                       merkez temelli  yogunluk tabanli
kume ici toplam uzaklik             703.25            878.71
en uzun zorunlu baglanti            1.6311            0.5041
iki bolmelemenin ayristigi nokta 153
```

İki bölmeleme 360 noktanın **153'ünde** ayrışıyor. Merkez temelli yordam 171 ve 189 büyüklüğünde
iki parça veriyor: her iki halkayı da ortadan kesen bir doğru çiziyor. Yoğunluk tabanlı yordam
120 ve 240 veriyor, iki halkayı ayırıyor ve tek bir noktayı bile gürültüye atmıyor.

Buradan "yoğunluk tabanlı yöntem doğru sonucu buldu" çıkarmak, kursun uyardığı hatanın tam
kendisidir. İki ölçüt basılıyor ve **ters sıralama** veriyorlar. Küme içi toplam uzaklıkta merkez
temelli bölmeleme 703,25 ile önde, yoğunluk tabanlı bölmeleme 878,71 ile geride — bu şaşırtıcı
değildir, çünkü merkez temelli yordam tam olarak bu sayıyı küçültmek üzere çalışır. Bir kümeyi
bir arada tutmak için katlanılması gereken en büyük sıçramada sıralama tersine döner: 1,6311'e
karşı **0,5041**.

Hangi bölmelemenin daha iyi olduğu sorusunun yanıtı yoktur, çünkü soru eksiktir. Yanıtlanabilir
soru şudur: hangi ölçüte göre. Ölçüt "merkezlerden ortalama uzaklık" ise merkez temelli bölmeleme
kazanır, "bir arada durma" ise yoğunluk tabanlı bölmeleme. Fark veriden değil **ölçütten** gelir;
aynı 360 nokta üzerinde iki ayrı soru soruluyor.

#### Özet

- Yoğunluk tabanlı kümeleme her noktayı bir kümeye atamak zorunda değildir; seçilen ayarda 756
  noktanın 73'ü dışarıda kalıyor ve küme büyüklükleri 454 ile 6 arasında dağılıyor.
- İşaretlenen 73 noktanın beşinci komşu uzaklığı 1,7551, aynı sayıda noktayı rastgele işaretleyen
  taban çizgisinde 1,0200; fark 0,7351 ama bu fark **ölçütün kendisinden** gelir, çünkü yöntem
  neredeyse aynı büyüklüğe bakarak işaretliyor.
- On sekiz ayar bileşiminde küme sayısı 1 ile 56, gürültü sayısı 4 ile 674 arasında değişiyor.
- Sütun içi yeniden karıştırılmış kümede gürültü sayısı on sekiz ayarın on sekizinde daha yüksek
  (1,20 ve 5'te 73'e karşı 171); bu fark veriden gelir. Küme sayısı ayrım yapmaz.
- Ortak merkezli iki halkada iki bölmeleme 360 noktanın 153'ünde ayrışıyor ve iki ölçüt ters
  sıralama veriyor: küme içi toplam uzaklıkta 703,25'e karşı 878,71, en uzun zorunlu bağlantıda
  1,6311'e karşı 0,5041.

#### Sonraki Adım

Bu ailede bir nokta ya bir kümededir, ya sınırdadır, ya da dışarıdadır; üç durum da kesindir.
Oysa iki yoğun bölgenin tam ortasına düşen bir abone için bu kesinlik uydurmadır: yordam onu bir
tarafa yazar ve ne kadar zor bir kararla yazdığını kaydetmez. Sonraki ders atamayı bir sayıya
çevirir. Her nokta her kümeye bir **sorumluluk payıyla** bağlanır, paylar toplamı bire eşittir ve
payları birbirine yakın olan nokta kararsızdır. Kümeler merkez ve değişkenlikle tanımlanan bir
**karışım modeli** olarak yazılır, parametreler yinelemeli bir kestirimle bulunur ve iki sayı
ölçülür: kaç nokta iki kümeye de yakın payla bağlı ve bu paylar sert atamaya indirildiğinde ne
kadar bilgi kayboluyor.

### Olasılıksal Kümeleme

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/olasiliksal-kumeleme

Bir noktanın her kümeye bir sorumluluk payıyla bağlandığı karışım modelinin ölçülmesi: rastgele atama tabanının uyum puanı -4,2424, yinelemeli kestirimin bulduğu modelinki -3,7755, fark +0,4669. Aynı fark sütun içi karıştırılmış kümede +0,3358 çıkıyor, yani farkın yalnız +0,1311'i veriden geliyor. 756 noktanın 74'ünde en büyük pay 0,60'ın altında ve bu noktalarda ortalama en büyük pay 0,5418. Sert atamaya geçildiğinde ortalama belirsizlik 0,3797'den sıfıra düşüyor ve küme büyüklükleri yumuşak kütleden 28,8 noktaya kadar sapıyor. İkinci bir başlangıçta kararsız noktaların yüzde 43,24'ü, kesin noktaların yüzde 7,20'si küme değiştiriyor.

Önceki dersin yordamı bir noktayı ya bir kümeye yazıyor, ya sınıra koyuyor, ya da dışarıda
bırakıyordu. Üç durum da kesindi ve karar defterinde yalnız sonuç duruyordu. İki yoğun bölgenin
tam ortasına düşen bir abone ile bir kümenin göbeğinde duran abone aynı biçimde kaydediliyor,
kararın ne kadar zor verildiği hiçbir yere yazılmıyordu. Aynı şey merkez temelli ve hiyerarşik
bölmeleme için de geçerliydi: çıktı bir küme numarasıydı, bir güven derecesi değil.

Bu ders atamayı bir sayıya çevirir. Her nokta her kümeye bir **sorumluluk payıyla** bağlanır,
bir noktanın payları toplamı bire eşittir ve payların birbirine yakın olması o noktanın
kararsızlığını doğrudan ölçer. Bunun için kümeler artık merkez listeleri değil, kendi merkezi ve
kendi değişkenliği olan **bileşenler** olarak yazılır; kümenin tamamı bu bileşenlerin bir
**karışım modelidir**. Ölçülecek iki şey vardır: model rastgele bir atamayı ne kadar geçiyor ve
yumuşak atamanın taşıdığı bilgi sert atamaya inilince ne kadar kayboluyor.

- **KU37.** Küme M27/K01–K03'ten gelen **kurgu** abone tablosunun 756 satırlık eğitim payıdır,
  tohum 20260218. Etiket sütunu bu derste de bir kenarda durur.
- **KU38.** Üç sürekli sütun kullanılır: ortalama tüketim, oynaklık ve kişi başına tüketim.
  Tamsayı değerli sütunlar dışarıda bırakılır, çünkü bir bileşen tek bir tamsayı değerine
  çökebilir ve o sütundaki değişkenliği sıfıra indirebilir.
- **KU39.** Her bileşen bir ağırlık, sütun başına bir merkez ve sütun başına bir değişkenlikle
  tanımlanır. Sütunlar arası bağımlılık modellenmez; bu bir **model kararıdır** ve bileşenlerin
  eksen hizalı olmasını gerektirir.
- **KU40.** Değişkenliğe **0,02 alt sınırı** konur. Alt sınır olmadan bir bileşen tek bir noktaya
  çökebilir, o noktadaki yoğunluk sınırsız büyür ve uyum puanı anlamını yitirir. Sınır ölçekli
  sütunlarda birim değişkenliğin ellide biridir.
- **KU41.** **Uyum puanı**, modelin bir noktaya verdiği yoğunluğun doğal logaritmasının nokta
  başına ortalamasıdır. Yüksek olan daha iyidir ve puan yalnız aynı sütun kümesi üzerinde
  karşılaştırılabilir.
- **KU42.** Taban çizgisi **rastgele atamadır**: noktalar üç gruba rastgele dağıtılır ve
  parametreler o dağıtımdan hesaplanır. Bu taban seçilir, çünkü **aynı model biçimini** ve aynı
  parametre sayısını kullanır; aradaki fark yalnız atamadan gelir, model ailesinden değil.
- **KU43.** Küme sayısı üçtür ve bir **ayar değişkenidir**; seçiminin gerekçesi ve seçimin ne
  kadar keyfi olduğu konunun kapanış dersinde ölçülür.
- **KU44.** Yapısız başvuru önceki dersteki gibi **sütun içi yeniden karıştırmayla** kurulur;
  M26/K05'in aracıdır ve sınama kuramı tekrarlanmaz.

#### Bileşenlerin Karışımı

Model şudur: kümedeki her nokta, üç bileşenden birinden gelmiş sayılır ama hangisinden geldiği
kaydedilmemiştir. Her bileşen $c$ bir ağırlık $a_c$ taşır (o bileşenden gelen noktaların payı) ve
her sütun için bir merkez $m_{cd}$ ile bir değişkenlik $v_{cd}$ tutar. Bileşenin bir noktaya
verdiği yoğunluk merkezden uzaklaştıkça hızla söner:

$$f_c(x) = \prod_{d} \frac{1}{\sqrt{2\pi v_{cd}}}\,
\exp\left(-\frac{(x_d - m_{cd})^2}{2\,v_{cd}}\right)$$

Bir noktanın bir bileşene bağlanma payı, o bileşenin katkısının toplam katkıya oranıdır:

$$r_{ic} = \frac{a_c\, f_c(x_i)}{\sum_j a_j\, f_j(x_i)}$$

Parametreler bilinmediği için paylar hesaplanamaz, paylar bilinmediği için parametreler
hesaplanamaz. **Yinelemeli kestirim** bu döngüyü kırar: bir tarafı geçici olarak sabitler,
öbürünü hesaplar, sonra yer değiştirir. Her tur uyum puanını artırır ya da yerinde bırakır;
ulaştığı yer başlangıca bağlıdır.

```python
# karisim.py — MODELDIR. M27/K01–K03'un KURGU abone tablosu ayni tohumla uretilir;
# etiket sutunu bu kursta da bir kenarda durur.
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21), (0.22, 17), (0.18, 26), (0.14, 14), (0.18, 23)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[0] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append((10001 + i, b[1], hane, memnun))
for no, taban, hane, memnun in ABONE:
    r, v = uretec(TOHUM + 7000 + no), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            taban * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        ort = round(sum(v) / len(v), 2)
        VERI.append({"ort_tuketim": ort, "oynaklik": round(max(v) - min(v), 2),
                     "kisi_basi": round(ort / hane, 3)})

SEC = ["ort_tuketim", "oynaklik", "kisi_basi"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
OLCEK = {a: (statistics.fmean(x[a] for x in EGT),
             statistics.pstdev([x[a] for x in EGT])) for a in SEC}
P = [[(x[a] - OLCEK[a][0]) / OLCEK[a][1] for a in SEC] for x in EGT]
K, ALT = 3, 0.02                                 # kume sayisi ve degiskenlik alt siniri


def parametre(P, uye, k):                        # MODELDIR: agirlik, merkez, degiskenlik
    A, M, V = [], [], []
    for c in range(k):
        w = [u[c] for u in uye]
        t = sum(w) + 1e-9
        m = [sum(w[i] * P[i][d] for i in range(len(P))) / t for d in range(len(P[0]))]
        V.append([max(sum(w[i] * (P[i][d] - m[d]) ** 2 for i in range(len(P))) / t, ALT)
                  for d in range(len(P[0]))])
        A.append(t / len(P))
        M.append(m)
    return A, M, V


def sorumluluk(P, A, M, V):                      # yumusak atama ve uyum puani
    R, top = [], 0.0
    for x in P:
        g = [math.log(A[c]) + sum(-0.5 * (math.log(2 * math.pi * V[c][d])
                                          + (x[d] - M[c][d]) ** 2 / V[c][d])
                                  for d in range(len(x))) for c in range(len(A))]
        b = max(g)
        s = sum(math.exp(u - b) for u in g)
        top += b + math.log(s)
        R.append([math.exp(u - b) / s for u in g])
    return R, top / len(P)


def rastgele_uyelik(n, k, tohum):                # TABAN CIZGISI: rastgele atama
    r, U = uretec(tohum), []
    for _ in range(n):
        c = int(r() * k)
        U.append([1.0 if j == c else 0.0 for j in range(k)])
    return U


def kestir(P, k, tohum, tur=80):                 # MODELDIR: yinelemeli kestirim
    U = rastgele_uyelik(len(P), k, tohum)
    for _ in range(tur):
        U, puan = sorumluluk(P, *parametre(P, U, k))
    return U, puan


def sutun_karistir(P, tohum):
    Y = [list(x) for x in P]
    for s in range(len(P[0])):
        for i, j in enumerate(karistir(list(range(len(P))), tohum + s * 17)):
            Y[i][s] = P[j][s]
    return Y


def taban_puani(P, k, tohum):
    return sorumluluk(P, *parametre(P, rastgele_uyelik(len(P), k, tohum), k))[1]


U, PUAN = kestir(P, K, TOHUM + 120000)
TABAN = taban_puani(P, K, TOHUM + 120000)
YAP = sutun_karistir(P, TOHUM + 610000)          # KURGU: sutun ici yeniden karistirma
_, YPUAN = kestir(YAP, K, TOHUM + 120000)
YTABAN = taban_puani(YAP, K, TOHUM + 120000)
print(f"nokta {len(P)}, sutun {len(SEC)}, bilesen {K}")
print(f"{'kume':<16}{'taban cizgisi':>15}{'karisim modeli':>16}{'fark':>10}")
print(f"{'veri':<16}{TABAN:>15.4f}{PUAN:>16.4f}{PUAN - TABAN:>+10.4f}")
print(f"{'karistirilmis':<16}{YTABAN:>15.4f}{YPUAN:>16.4f}{YPUAN - YTABAN:>+10.4f}")
print(f"veriden gelen pay {(PUAN - TABAN) - (YPUAN - YTABAN):+.4f}")
```

```
nokta 756, sutun 3, bilesen 3
kume              taban cizgisi  karisim modeli      fark
veri                    -4.2424         -3.7755   +0.4669
karistirilmis           -4.2443         -3.9085   +0.3358
veriden gelen pay +0.1311
```

Rastgele atama tabanı **-4,2424**, karışım modeli **-3,7755**, fark **+0,4669**. Taban çizgisi
burada bilinçli olarak zor seçilmiştir: rastgele atama da üç bileşen kurar, aynı sayıda parametre
hesaplar ve aynı yoğunluk biçimini kullanır. Tek eksiği, hangi noktanın hangi bileşene ait
olduğunu **öğrenmemesidir**. Böylece fark model ailesinden değil, yalnız atamadan gelir.

İkinci satır bu farkın ne kadarının veriden geldiğini söyler. Sütunları kendi içinde karıştırılan
kümede aynı yordam **+0,3358** kazanıyor. Yani kazancın yaklaşık dörtte üçü, sütunlar arasında
hiçbir ilişki kalmadığında bile duruyor. Sebep açıktır: üç bileşen, tek bir bileşenin
yakalayamayacağı çarpık ve uzun kuyruklu bir dağılımı eksen eksen daha iyi sarar; bu, verideki
küme yapısı hakkında hiçbir şey söylemez, modelin esnekliği hakkında bir şey söyler. Sütunlar
arası yapıya düşen pay **+0,1311**, yani toplam farkın yaklaşık yüzde yirmi sekizi. Kursun sorusu
tam burada karşılığını buluyor: fark ölçütten mi veriden mi geliyor sorusunun yanıtı çoğu zaman
"ikisinden de" olur ve payları ayırmadan sayı okunamaz.

#### Yumuşak Atamanın Taşıdığı Belirsizlik

Sorumluluk payı, sert atamanın atmak zorunda olduğu bilgiyi tutar. Bir noktanın payları
$0{,}98 / 0{,}01 / 0{,}01$ ise atama kesindir; $0{,}40 / 0{,}38 / 0{,}22$ ise atama bir seçimdir
ve seçimin ne kadar ince olduğu paylarda yazılıdır. Belirsizliği tek sayıya indirmek için
payların **doğal logaritmayla ağırlıklı toplamı** kullanılır; bu sayı tek bir paya bütün ağırlık
verildiğinde sıfır, paylar eşit dağıldığında en büyük değerine ulaşır.

```python
def belirsizlik(r):
    return -sum(p * math.log(p) for p in r if p > 1e-12)


SERT = [max(range(K), key=lambda c: r[c]) for r in U]
kars = [i for i, r in enumerate(U) if max(r) < 0.60]
A, _, _ = parametre(P, U, K)
print(f"en buyuk sorumluluk payi 0.60'in altinda olan nokta {len(kars)}; "
      f"bu noktalarda ortalama en buyuk pay {sum(max(U[i]) for i in kars) / len(kars):.4f}")
print(f"ilk iki payi 0.10'dan yakin {sum(1 for r in U if sorted(r)[-1] - sorted(r)[-2] < 0.10)}"
      f"; en buyuk payi 0.99 ve ustu {sum(1 for r in U if max(r) >= 0.99)}")
print(f"ortalama belirsizlik: yumusak atamada "
      f"{sum(belirsizlik(r) for r in U) / len(U):.4f}, sert atamada 0.0000, "
      f"ust sinir {math.log(K):.4f}")
print(f"{'kume':<8}{'yumusak kutle':>15}{'sert sayim':>12}{'fark':>8}")
for c in range(K):
    print(f"{c:<8}{A[c] * len(P):>15.1f}{SERT.count(c):>12}"
          f"{SERT.count(c) - A[c] * len(P):>+8.1f}")
```

```
en buyuk sorumluluk payi 0.60'in altinda olan nokta 74; bu noktalarda ortalama en buyuk pay 0.5418
ilk iki payi 0.10'dan yakin 28; en buyuk payi 0.99 ve ustu 94
ortalama belirsizlik: yumusak atamada 0.3797, sert atamada 0.0000, ust sinir 1.0986
kume      yumusak kutle  sert sayim    fark
0                 217.8         189   -28.8
1                 200.3         213   +12.7
2                 337.9         354   +16.1
```

Eşik 0,60 seçilmiştir, çünkü üç bileşende eşit dağılım 0,3333 eder ve 0,60'ın altındaki bir en
büyük pay, o noktanın ağırlığının **çoğunluğunun tek bir kümede toplanmadığı** anlamına gelir.
756 noktanın **74'ü** bu durumdadır ve bu noktalarda en büyük pay ortalama **0,5418**. Yirmi
sekiz noktada ilk iki pay birbirinden 0,10'dan az farklıdır — bu noktalarda sert atamanın
seçtiği küme ile ikinci sıradaki küme neredeyse eşit haklıdır. Öbür uçta 94 nokta 0,99 ve üstü
bir payla tek bir kümeye bağlıdır.

Ortalama belirsizlik yumuşak atamada **0,3797**, üst sınırı **1,0986**. Sert atamaya geçildiğinde
bu sayı tanım gereği **sıfıra** düşer: her nokta tek bir kümeye tam ağırlıkla yazılır ve 74
noktanın kararsız olduğu bilgisi tabloda kalmaz. Kaybın somut karşılığı küme büyüklüklerindedir.
Yumuşak atamada birinci kümenin kütlesi 217,8 nokta ederken sert atama o kümeye 189 nokta yazar;
aradaki **28,8 noktalık** sapma, sınırdaki noktaların tamamının bir tarafa yuvarlanmasından
gelir. Kümelerin göreli büyüklüğüne dayanan her karar bu yuvarlamayı devralır.

#### Payların Sınanması

Buraya kadar sorumluluk payının anlamlı olduğu varsayıldı. Bu sınanabilir bir iddiadır: pay
gerçekten kararın zorluğunu ölçüyorsa, kararsız noktalar koşullar biraz değiştiğinde küme
değiştirmeli, kesin noktalar değiştirmemelidir. Değiştirilecek koşul hazırdır — yinelemeli
kestirim başlangıca duyarlıdır ve konunun ikinci dersi bunu merkez temelli bölmelemede ölçmüştü.
İkinci bir başlangıçla koşulan model, kümeler merkezleri üzerinden eşleştirildikten sonra
birinciyle karşılaştırılır.

```python
U2, PUAN2 = kestir(P, K, TOHUM + 555000)
_, M, _ = parametre(P, U, K)
_, M2, _ = parametre(P, U2, K)
ESLE = [min(range(K), key=lambda c: math.dist(M2[j], M[c])) for j in range(K)]
SERT2 = [ESLE[max(range(K), key=lambda c: r[c])] for r in U2]
degisen = [i for i in range(len(P)) if SERT[i] != SERT2[i]]
kesin = [i for i, r in enumerate(U) if max(r) >= 0.90]
dk = sum(1 for i in degisen if max(U[i]) < 0.60)
de = sum(1 for i in degisen if max(U[i]) >= 0.90)
print(f"ikinci baslangicin uyum puani {PUAN2:.4f} (birincisi {PUAN:.4f}); "
      f"kume eslestirmesi {ESLE}")
print(f"kume degistiren nokta {len(degisen)} / {len(P)}")
print(f"kararsiz noktalarda degisim {dk}/{len(kars)} = {dk / len(kars):.4f}")
print(f"kesin noktalarda (pay >= 0.90) degisim {de}/{len(kesin)} = {de / len(kesin):.4f}")
```

```
ikinci baslangicin uyum puani -3.7827 (birincisi -3.7755); kume eslestirmesi [1, 0, 2]
kume degistiren nokta 125 / 756
kararsiz noktalarda degisim 32/74 = 0.4324
kesin noktalarda (pay >= 0.90) degisim 29/403 = 0.0720
```

İki koşum farklı yerlerde duruyor: uyum puanları **-3,7755** ve **-3,7827**. Aradaki fark
küçüktür ama bölmeleme farkı küçük değildir — 756 noktanın **125'i** küme değiştiriyor. Bu, yalnız
uyum puanına bakarak "model yakınsadı" demenin yeterli olmadığını gösterir.

Asıl sayı ayrışmadadır. Kararsız 74 noktanın **32'si**, yani yüzde **43,24**'ü küme değiştiriyor.
Payı 0,90 ve üstü olan 403 noktanın ise yalnız **29'u**, yüzde **7,20**'si. Oran altı katın
üzerinde. Sorumluluk payı, hesaplandığı modelin dışında bir şeyi doğru kestiriyor: bir noktanın
atamasının ne kadar kırılgan olduğunu. Sert atama bu bilgiyi taşımaz — bir küme numarası, yüzde
43 ihtimalle değişecek bir numara ile yüzde 7 ihtimalle değişecek bir numarayı aynı biçimde
gösterir.

#### Özet

- Karışım modeli kümeleri merkez ve değişkenlikle tanımlanan bileşenler olarak yazar; her nokta
  her bileşene bir sorumluluk payıyla bağlanır ve paylar toplamı birdir.
- Rastgele atama tabanı -4,2424, modelin uyum puanı -3,7755, fark +0,4669; taban aynı model
  biçimini ve aynı parametre sayısını kullandığı için fark yalnız atamadan gelir.
- Aynı fark sütun içi karıştırılmış kümede +0,3358 çıkıyor; sütunlar arası yapıya düşen pay
  +0,1311, yani toplam farkın yaklaşık yüzde yirmi sekizi. Geri kalanı modelin esnekliğidir.
- 756 noktanın 74'ünde en büyük pay 0,60'ın altında (ortalama 0,5418) ve 28 noktada ilk iki pay
  0,10'dan yakın. Sert atamaya geçince ortalama belirsizlik 0,3797'den sıfıra düşer ve bir kümenin
  sayımı yumuşak kütlesinden 28,8 nokta sapar.
- Sorumluluk payı modelin dışında da bir şey ölçüyor: ikinci bir başlangıçta kararsız noktaların
  yüzde 43,24'ü, kesin noktaların yüzde 7,20'si küme değiştiriyor.

#### Sonraki Adım

Bu derste küme sayısı üç alındı ve bu seçim gerekçelendirilmedi. Önceki üç derste de benzer
seçimler yapıldı: kaç küme, hangi kesme yüksekliği, hangi komşuluk yarıçapı. Her seferinde
seçimin sonucu oynattığı gösterildi ama seçimin **kendisinin** nasıl yapılacağı ertelendi. Konunun
kapanış dersi bu soruyu alır ve yanıtın olmadığını sayıyla gösterir: aynı veri üzerinde üç ayrı
iç geçerlilik ölçütü üç ayrı küme sayısı seçer, hiçbiri diğerinden daha doğru değildir ve tümüyle
rastgele üretilmiş yapısız bir kümede aynı üç ölçüt yine bir küme sayısı seçer. Ardından etiket,
bu kursta ilk ve tek kez, **dış geçerlilik** için açılır ve etiketin işaret ettiği küme sayısıyla
iç ölçütlerin seçtiklerinin uyuşmadığı sayılır.

### Kümeleme Değerlendirmesi

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/kumeleme-degerlendirmesi

Küme sayısını seçen ölçütlerin kendilerinin ölçülmesi: aynı 756 abone üzerinde siluet katsayısı 3, saçılma oranı 6, boşluk ölçütü 10 küme seçiyor ve hiçbiri diğerinden doğru değil. Hiçbir yapı taşımayan rastgele bir bulutta aynı üç ölçüt yine 9, 7 ve 9 seçiyor; en yüksek siluet gerçek veride 0,2142, yapısız bulutta 0,1255. Etiket kursta ilk ve tek kez açılıyor ve çift uyum oranı en iyi bölmelemede 0,5235 çıkıyor — tek küme tabanı 0,6539 ile dokuz bölmelemenin dokuzunu da geçiyor. Yirmi yedi karşılaştırma yapıldı ve etiketin işaret ettiği küme sayısını iç ölçütlerin hiçbiri seçmedi.

Beş ders boyunca her yöntem bir sayı istedi ve o sayı hep dışarıdan verildi: kaç küme, hangi
kesme yüksekliği, hangi komşuluk yarıçapı, kaç bileşen. Her seferinde seçimin sonucu oynattığı
ölçüldü, ama seçimin **kendisinin** nasıl yapılacağı ertelendi. Denetimli kursta bu soru
kolaydı — doğrulama kümesindeki etiketlere bakılır ve en iyi sayıyı veren aday alınırdı. Burada
etiket yok, dolayısıyla ölçüt de veriden kurulmak zorunda.

Bu ders üç **iç geçerlilik** ölçütü yazar ve aynı veriye uygular. Üçü de makul, üçü de yaygın bir
sezgiyi sayıya çeviriyor ve üçü **üç ayrı küme sayısı** seçiyor. Ardından ölçütler hiçbir yapı
taşımayan bir bulutta koşturulur; orada da bir küme sayısı seçiyorlar. En sonda etiket, bu kursta
ilk ve tek kez, **dış geçerlilik** için açılır ve iç ölçütlerin seçtikleriyle karşılaştırılır.

- **KU45.** Küme M27/K01–K03'ten gelen **kurgu** abone tablosunun 756 satırlık eğitim payıdır,
  tohum 20260218, altı ölçekli sayısal sütun. Etiket üretilir ama üçüncü bölüme kadar hiçbir
  hesaba girmez.
- **KU46.** Bölmeleme konunun ikinci dersindeki merkez temelli yordamdır ve her küme sayısı için
  aynı tohumla koşulur; ölçütler karşılaştırılabilir olsun diye bölmeleme yordamı sabit tutulur.
- **KU47.** **Siluet katsayısı**: bir nokta için kendi kümesindeki öbür noktalara ortalama uzaklık
  $a$, en yakın öbür kümedeki noktalara ortalama uzaklık $b$ alınır ve $(b-a)/\max(a,b)$
  hesaplanır. Küme ortalaması alınır, **büyük olan seçilir**.
- **KU48.** **Saçılma oranı**: her kümenin merkezine ortalama uzaklığı o kümenin yarıçapı sayılır;
  bir küme çifti için iki yarıçapın toplamı merkezler arası uzaklığa bölünür, her küme için en
  kötü ortağı alınır ve ortalaması hesaplanır. **Küçük olan seçilir.**
- **KU49.** **Boşluk ölçütü**: kümedeki noktaların merkezlerine toplam uzaklığının doğal
  logaritması, hiçbir yapı taşımayan **üç başvuru bulutunda** aynı büyüklüğün ortalamasından
  çıkarılır. **Büyük olan seçilir.**
- **KU50.** Denenen küme sayısı 2'den 10'a kadar dokuz adaydır; üç ölçütle **yirmi yedi
  karşılaştırma** yapılır. M26/K05 aynı veriye çok sayıda soru sorulduğunda en iyi sonucun bir
  seçim olduğunu ölçtü; o sayılar tekrarlanmaz, kural sürer.
- **KU51.** Yapısız bulut, sütunları birbirinden bağımsız üretilmiş 756 noktadır. Önceki
  derslerin sütun içi karıştırmasından farkı, orada gerçek dağılımların korunmasıdır.
- **KU52.** **Uyum göstergesi** kendi tanımıyla kurulur: bütün nokta çiftleri gezilir ve
  bölmelemenin "aynı kümede mi" yanıtıyla etiketin "aynı sınıfta mı" yanıtının uyuştuğu çiftlerin
  oranı alınır. 756 nokta 285.390 çift eder.
- **KU53.** Dış geçerlilikte iki taban çizgisi kullanılır: aynı küme sayısına **rastgele
  bölmeleme** ve herkesi tek kümeye koyan **tek küme**. İkincisi gereklidir, çünkü etiket dengesiz
  olduğunda tek küme yüksek bir uyum oranı üretir.

#### Üç Ölçüt, Üç Ayrı Küme Sayısı

Üç ölçüt de aynı sezginin farklı biçimleridir: bir bölmeleme iyiyse aynı kümedeki noktalar
birbirine yakın, farklı kümelerdekiler uzak olmalıdır. Ayrıldıkları yer, "yakın" ile "uzak"
arasındaki dengeyi nasıl kurdukları ve küme sayısı büyüdükçe bunu nasıl cezalandırdıklarıdır.

```python
# gecerlilik.py — MODELDIR. M27/K01–K03'un KURGU abone tablosu; etiket sutunu
# uretilir ama bu blokta ve sonrakinde HIC kullanilmaz.
import math
import statistics

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [(0.28, 21, 0.00), (0.22, 17, -0.10), (0.18, 26, 0.30),
         (0.14, 14, -0.05), (0.18, 23, 0.05)]


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[0] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append((10001 + i, b, hane, memnun))
for no, b, hane, memnun in ABONE:
    r, v = uretec(TOHUM + 7000 + no), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            b[1] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort, oyn, q = round(sum(v) / len(v), 2), round(max(v) - min(v), 2), uretec(TOHUM + 51000 + no)
    z = (0.052 * (sum(v) / len(v) - 20) + 0.85 * (min(v) == 0.0) + 0.026 * oyn
         + 0.24 * (hane >= 5) - 0.20 * (memnun >= 4) + b[2]
         + 0.9 * (no - 10001) / 1399 + (q() + q() + q() - 1.5) * 1.30)
    VERI.append({"ort_tuketim": ort, "oynaklik": oyn, "hane": hane, "memnuniyet": memnun,
                 "donem": len(v), "kisi_basi": round(ort / hane, 3), "supheli": int(z > 1.35)})

SAYISAL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "kisi_basi"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


EGT = karistir(VERI, TOHUM + 90000)[:756]
OLCEK = {a: (statistics.fmean(x[a] for x in EGT),
             statistics.pstdev([x[a] for x in EGT])) for a in SAYISAL}
N = [[(x[a] - OLCEK[a][0]) / OLCEK[a][1] for a in SAYISAL] for x in EGT]
ETIKET = [x["supheli"] for x in EGT]              # ucuncu bolume kadar acilmaz
KLER = range(2, 11)


def uzaklik_dizeyi(P):
    D = [[0.0] * len(P) for _ in P]
    for i in range(len(P)):
        for j in range(i + 1, len(P)):
            D[i][j] = D[j][i] = math.dist(P[i], P[j])
    return D


def bolmele(P, k, tohum, tur=40):                 # MODELDIR: merkez temelli bolmeleme
    r = uretec(tohum)
    M, e = [list(P[int(r() * len(P))]) for _ in range(k)], [0] * len(P)
    for _ in range(tur):
        for i, x in enumerate(P):
            e[i] = min(range(k), key=lambda c: math.dist(x, M[c]))
        for c in range(k):
            g = [P[i] for i in range(len(P)) if e[i] == c]
            M[c] = [statistics.fmean(x[d] for x in g) for d in range(len(P[0]))] if g else M[c]
    return e, M


def siluet(D, e, k):                              # OLCUT 1: buyuk olan secilir
    G = [[i for i in range(len(e)) if e[i] == c] for c in range(k)]
    t = 0.0
    for i in range(len(e)):
        o = e[i]
        if len(G[o]) < 2:
            continue
        a = sum(D[i][j] for j in G[o]) / (len(G[o]) - 1)
        b = min(sum(D[i][j] for j in G[c]) / len(G[c]) for c in range(k) if c != o and G[c])
        t += (b - a) / max(a, b)
    return t / len(e)


def sacilma(P, e, M, k):                          # OLCUT 2: kucuk olan secilir
    R = [statistics.fmean([math.dist(P[i], M[c]) for i in range(len(P)) if e[i] == c] or [0.0])
         for c in range(k)]
    return statistics.fmean(
        max((R[c] + R[j]) / max(math.dist(M[c], M[j]), 1e-9) for j in range(k) if j != c)
        for c in range(k))


def kume_ici(P, e, M):
    return sum(math.dist(P[i], M[e[i]]) for i in range(len(P)))


def yapisiz(n, d, tohum):                         # KURGU: hicbir yapi tasimayan bulut
    r = uretec(tohum)
    return [[(r() + r() + r() - 1.5) * 1.4142 for _ in range(d)] for _ in range(n)]


BASVURU = [yapisiz(756, len(SAYISAL), TOHUM + 300000 + 1000 * b) for b in range(3)]


def bosluk(P, k, W):                              # OLCUT 3: buyuk olan secilir
    return statistics.fmean(math.log(kume_ici(R, *bolmele(R, k, TOHUM + 200000 + k)))
                            for R in BASVURU) - math.log(W)


def supur(P, D):
    out = {}
    for k in KLER:
        e, M = bolmele(P, k, TOHUM + 200000 + k)
        out[k] = (siluet(D, e, k), sacilma(P, e, M, k), bosluk(P, k, kume_ici(P, e, M)))
    return out


def secim(t):
    return (max(t, key=lambda k: t[k][0]), min(t, key=lambda k: t[k][1]),
            max(t, key=lambda k: t[k][2]))


D = uzaklik_dizeyi(N)
VT = supur(N, D)
print(f"nokta {len(N)}, sutun {len(SAYISAL)}, denenen kume sayisi {len(KLER)}")
print(f"{'k':>4}{'siluet':>10}{'sacilma':>10}{'bosluk':>10}")
for k in KLER:
    print(f"{k:>4}{VT[k][0]:>10.4f}{VT[k][1]:>10.4f}{VT[k][2]:>10.4f}")
print(f"secilen k -> siluet {secim(VT)[0]}, sacilma {secim(VT)[1]}, bosluk {secim(VT)[2]}")
print(f"karsilastirilan aday {len(KLER) * 3}")
```

```
nokta 756, sutun 6, denenen kume sayisi 9
   k    siluet   sacilma    bosluk
   2    0.1954    1.8924   -0.2894
   3    0.2142    1.5466   -0.2481
   4    0.1760    1.6043   -0.2288
   5    0.1799    1.4596   -0.2148
   6    0.1928    1.3328   -0.1882
   7    0.1838    1.3481   -0.1865
   8    0.1810    1.3773   -0.1856
   9    0.1781    1.3388   -0.1646
  10    0.1833    1.3947   -0.1520
secilen k -> siluet 3, sacilma 6, bosluk 10
karsilastirilan aday 27
```

Üç ölçüt, üç yanıt: **3, 6 ve 10**. Aynı veri, aynı bölmeleme yordamı, aynı tohum. Değişen tek
şey "iyi bölmeleme" tanımıdır ve tanım değişince yanıt değişiyor.

Sayıların büyüklüğü de okunmalıdır. Siluetin en yüksek değeri 0,2142; ölçütün üst sınırı 1'dir ve
0,2142 kümelerin **büyük ölçüde iç içe geçtiği** anlamına gelir. Sıralama 3 ile 6 arasında
0,0214'lük bir farkla belirleniyor, yani ölçüt bir tepe göstermiyor, hafif bir eğim gösteriyor.
Boşluk ölçütü ise hiç tepe yapmıyor: 2'den 10'a kadar tek yönde artıyor ve seçtiği değer,
denenen aralığın **son adayı**. Bu ölçütün yanıtı veriden değil, süpürmenin nerede kesildiğinden
geliyor.

Üstelik yirmi yedi karşılaştırmadan en iyisi alınıyor. M26/K05 aynı veriye çok sayıda soru
sorulduğunda en yüksek sonucun bir bulgu değil bir seçim olduğunu ölçmüştü; kural burada küme
sayısı için aynen geçerlidir.

#### Yapısız Bir Kümede Ölçütler

Bir ölçüt yalnız bir sayı üretiyorsa, o sayının ne zaman "yapı yok" dediğini bilmek gerekir. Üç
ölçüt de en iyi adayı **her zaman** bulur; hiçbirinin "burada küme yok" diye bir çıktısı yoktur.
Bunu görmenin yolu ölçütleri yapısız bir bulutta koşturmaktır.

```python
YAP = yapisiz(756, len(SAYISAL), TOHUM + 777000)
YT = supur(YAP, uzaklik_dizeyi(YAP))
print(f"{'k':>4}{'siluet':>10}{'sacilma':>10}{'bosluk':>10}")
for k in KLER:
    print(f"{k:>4}{YT[k][0]:>10.4f}{YT[k][1]:>10.4f}{YT[k][2]:>10.4f}")
print(f"yapisiz kumede secilen k -> siluet {secim(YT)[0]}, sacilma {secim(YT)[1]}, "
      f"bosluk {secim(YT)[2]}")
print(f"en yuksek siluet: veri {VT[secim(VT)[0]][0]:.4f}, yapisiz {YT[secim(YT)[0]][0]:.4f}, "
      f"fark {VT[secim(VT)[0]][0] - YT[secim(YT)[0]][0]:+.4f}")
```

```
   k    siluet   sacilma    bosluk
   2    0.1169    2.6101   -0.0336
   3    0.1093    2.3145   -0.0405
   4    0.1136    2.0117   -0.0374
   5    0.1151    1.8629   -0.0408
   6    0.1142    1.8107   -0.0388
   7    0.1252    1.6415   -0.0360
   8    0.1135    1.7448   -0.0417
   9    0.1255    1.6649   -0.0301
  10    0.1201    1.6989   -0.0331
yapisiz kumede secilen k -> siluet 9, sacilma 7, bosluk 9
en yuksek siluet: veri 0.2142, yapisiz 0.1255, fark +0.0887
```

Bu bulutta hiçbir küme yoktur. Altı sütun birbirinden bağımsız üretildi, tek bir yoğunlaşma
bölgesi yok. Yine de üç ölçüt üç yanıt veriyor: **9, 7 ve 9**. Hiçbiri "yapı yok" demiyor, çünkü
hiçbirinin böyle bir çıktısı yok — her ölçüt kendisine verilen adaylar arasından en iyisini
işaret etmek zorunda.

Değerler karşılaştırıldığında durum daha da rahatsız edicidir. Yapısız bulutta en yüksek siluet
**0,1255**, gerçek veride **0,2142**; fark **0,0887**, oran yaklaşık 1,7 kat. Bu, sıfırdan farklı
bir yapı olduğunu gösterir ama tek başına okunan bir 0,21 değerinin "belirgin kümeler" anlamına
gelmediğini de gösterir. Boşluk ölçütünün yapısız bulutta -0,0301 ile -0,0417 arasında kalması
beklenendir: yapısız veri başvuru bulutlarından ayrışmıyor. Aynı ölçütün gerçek veride de
**negatif** kalması ayrı bir uyarıdır; merkez temelli bölmeleme bu kümede başvuru
bulutlarındakinden daha uzun toplam uzaklık bırakıyor.

#### Etiketin Açılması

Bu kursta buraya kadar hiçbir yöntem etiketi görmedi. Burada, bir kez, açılır. Amaç modeli
etiketle eğitmek değil, iç ölçütlerin seçtiği küme sayısının etiketin işaret ettiğiyle uyuşup
uyuşmadığını sormaktır. Ölçü **uyum göstergesidir**: bütün nokta çiftleri gezilir ve bölmelemenin
"aynı kümede" yanıtıyla etiketin "aynı sınıfta" yanıtının uyuştuğu çiftler sayılır.

```python
def uyum_gostergesi(e, y):                        # DIS GECERLILIK: cift uyum orani
    n, ayni = len(y), 0
    for i in range(n):
        for j in range(i + 1, n):
            ayni += (e[i] == e[j]) == (y[i] == y[j])
    return ayni / (n * (n - 1) // 2)


def rastgele_atama(n, k, tohum):                  # TABAN CIZGISI: rastgele bolmeleme
    r = uretec(tohum)
    return [int(r() * k) for _ in range(n)]


print(f"etiket: {len(ETIKET)} abonenin {sum(ETIKET)}'i supheli "
      f"({sum(ETIKET) / len(ETIKET):.4f}); etiketin isaret ettigi kume sayisi 2")
print(f"{'k':>4}{'uyum':>10}{'rastgele':>10}{'fark':>9}")
UY = {}
for k in KLER:
    e, _ = bolmele(N, k, TOHUM + 200000 + k)
    UY[k] = (uyum_gostergesi(e, ETIKET),
             uyum_gostergesi(rastgele_atama(len(ETIKET), k, TOHUM + 900000 + k), ETIKET))
    print(f"{k:>4}{UY[k][0]:>10.4f}{UY[k][1]:>10.4f}{UY[k][0] - UY[k][1]:>+9.4f}")
TEK = uyum_gostergesi([0] * len(ETIKET), ETIKET)
en = max(UY, key=lambda k: UY[k][0])
print(f"tek kume tabani {TEK:.4f}; bunu gecen bolmeleme "
      f"{sum(1 for k in KLER if UY[k][0] > TEK)}/{len(KLER)}")
print(f"uyumu en yuksek bolmeleme k={en} ({UY[en][0]:.4f}), tek kume tabaninin "
      f"{UY[en][0] - TEK:+.4f} altinda")
print(f"ic olcutlerin sectigi k {list(secim(VT))}; etiketin isaret ettigi k 2")
```

```
etiket: 756 abonenin 168'i supheli (0.2222); etiketin isaret ettigi kume sayisi 2
   k      uyum  rastgele     fark
   2    0.5235    0.4996  +0.0239
   3    0.4690    0.4484  +0.0206
   4    0.4290    0.4225  +0.0065
   5    0.4195    0.4073  +0.0121
   6    0.4209    0.3967  +0.0241
   7    0.4051    0.3898  +0.0153
   8    0.4044    0.3840  +0.0204
   9    0.3936    0.3804  +0.0132
  10    0.3843    0.3778  +0.0065
tek kume tabani 0.6539; bunu gecen bolmeleme 0/9
uyumu en yuksek bolmeleme k=2 (0.5235), tek kume tabaninin -0.1304 altinda
ic olcutlerin sectigi k [3, 6, 10]; etiketin isaret ettigi k 2
```

Etiket iki sınıflıdır: 756 abonenin 168'i şüpheli, oran 0,2222. Dolayısıyla etiketin işaret
ettiği küme sayısı **2**'dir. İç ölçütlerin seçtikleri **3, 6 ve 10**; hiçbiri 2 demedi.

Rastgele bölmeleme tabanına göre fark her küme sayısında pozitif ama küçük: en büyüğü 0,0241.
Bölmeleme, etiketle rastgeleden biraz fazla uyuşuyor. Asıl sayı ikinci tabandadır. Herkesi
**tek kümeye** koyan yordam 0,6539 alıyor ve dokuz bölmelemenin **dokuzu da** bunun altında
kalıyor; en iyisi 0,1304 geride. Sebep etiketin dengesizliğidir: şüpheli oranı 0,2222 olduğunda
rastgele seçilen iki abonenin aynı sınıfta olma olasılığı zaten yüksektir ve "hepsi aynı" demek
bu çiftlerin tamamını doğru bilir.

Buradan çıkan sonuç kümelemenin işe yaramadığı değil, **etiketi bulmadığıdır** — ki bulması için
bir sebep de yoktu, çünkü hiçbir yöntem etiketi görmedi. Kümeler tüketim düzeyine, oynaklığa ve
hane büyüklüğüne göre ayrıştı; şüphe etiketi bu eksenlerin hiçbiriyle örtüşmüyor. Denetimsiz bir
sonucu dış etikete göre ölçmek, çoğu zaman kümelemenin başarısını değil **etiketin veride
görünür olup olmadığını** ölçer.

#### Özet

- Üç iç geçerlilik ölçütü aynı veride üç ayrı küme sayısı seçiyor: siluet katsayısı 3, saçılma
  oranı 6, boşluk ölçütü 10. Değişen tek şey "iyi bölmeleme" tanımıdır.
- Boşluk ölçütü tepe yapmıyor, denenen aralığın son adayını seçiyor; yanıtı veriden değil
  süpürmenin nerede kesildiğinden geliyor. Yirmi yedi karşılaştırmadan en iyisi alındı.
- Hiçbir yapı taşımayan bir bulutta aynı üç ölçüt yine bir küme sayısı seçiyor (9, 7 ve 9); en
  yüksek siluet orada 0,1255, gerçek veride 0,2142, fark 0,0887.
- Etiket kursta ilk ve tek kez açıldı: uyum göstergesi en iyi bölmelemede 0,5235, rastgele
  bölmeleme tabanının 0,0239 üstünde ama tek küme tabanının (0,6539) 0,1304 altında; dokuz
  bölmelemenin dokuzu da bu tabanın altında.
- İç ölçütlerin seçtiği küme sayıları (3, 6, 10) etiketin işaret ettiğiyle (2) uyuşmuyor;
  denetimsiz bir sonucu dış etiketle ölçmek kümelemenin değil, etiketin veride görünürlüğünün
  ölçüsüdür.

#### Sonraki Adım

Altı derste noktalar gruplandı ve her grup bir taban çizgisiyle yan yana okundu. Yöntemler
birbirinden çok ayrıldı — merkez, bağlantı, yoğunluk, karışım — ama bir şeyi paylaştılar: hepsi
bir noktanın bütün sütunlarına birden baktı ve iki nokta arasındaki uzaklığı bu sütunların
toplamından okudu. Sütun sayısı arttıkça **uzaklığın kendisinin** ne olduğu hiçbir yerde
sorulmadı; altı sütunla üç yüz sütunun aynı işlemi aynı anlamda ürettiği varsayıldı. Oysa sütun
sayısı büyüdükçe uzaklıklar birbirine yaklaşır ve "en yakın" sözcüğü anlamını yitirir. Sonraki
konu bu soruyla açılır: noktaları daha az sayıyla göstermek mümkün müdür ve bu gösterimde **ne
kaybedilir**. Ölçü artık bölmeleme değil **geri kurma hatasıdır** — az sayıdan yeniden kurulan
noktalar özgün yerlerinden ne kadar uzakta kalıyor.

## Boyut Azaltma ve Anomali

Sütun sayısı düşürüldüğünde neyin korunup neyin kaybolduğunun ve tek başına duran noktanın nasıl işaretlendiğinin ölçülmesi: değişkenliği koruyan izdüşümün geri kurma hatası ile ikili uzaklıkların ne kadarının ayakta kaldığı ve ölçekleme kararının izdüşümü değiştirmesi, rank düştükçe artan hata ile saklanan sayı adedi arasındaki takas, komşuluk koruyan gömmede en yakın komşuların kaçının komşu kalması ve iki nokta arasındaki mesafenin yorumlanamaması, işaretlenen anomalinin kaçının gerçek olduğu ve tek değişkenli yordamın yakalayamadığı çok değişkenli anomali, yüksek güvenli kuralların yalnız sık öğeden gelmesi ve kaldıracın bunu düzeltmesi.

### Temel Bileşen Çözümlemesi

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/temel-bilesen-cozumlemesi

On iki sütunlu bir abone tablosu daha az sayıya indirildiğinde neyin korunup neyin kaybolduğunun ölçülmesi: sütun ortalamalarıyla geri kurma 3,3411 verirken iki bileşenle geri kurma 2,7762 veriyor ve fark tabanın yalnız 0,169'u kadar; aynı iki bileşen bileşen payının 0,341'ini taşıdığı hâlde ikili uzaklıkların ortalaması özgününün 0,4965 katına iniyor ve 31.626 çiftin yalnız 0,0057'si yüzde on yanılma payıyla ayakta kalıyor. Ölçeklenmemiş kümede birinci bileşenin yükü tek bir sütuna +0,96 ile bağlanıyor ve ilk iki bileşenin payı 0,863'e çıkıyor, ama ortak birimde ölçülen geri kurma hatası 2,8473 ile ölçeklenmiş izdüşümün gerisinde kalıyor.

Altı derste noktalar gruplandı. Küme sayısı seçildi, bağlantı ölçütü değiştirildi, yoğunluk
eşikleri süpürüldü, sonuçlar rastgele bölmelemeye karşı okundu. Bütün bu işlemlerin altında tek
bir işlem vardı: iki abone arasındaki uzaklığı hesaplamak. Uzaklık her seferinde sütunların
hepsine birden bakılarak bulundu ve sütunların kaç tane olduğu hiç sorgulanmadı.

Bu bölümün sorusu şudur: bir noktayı tarif etmek için gerçekten kaç sayı gerekiyor. On iki
sütunlu bir tabloda her abone on iki sayıyla duruyor, ama sütunlar birbirinden bağımsız değil —
kişi başı tüketim ortalama tüketimden ve hane büyüklüğünden türetilmiş, bölge göstergeleri
birbirini dışlıyor. Sütunları atmak yerine karıştırıp daha az sayıya indirmek bir seçenektir ve bu
dersin ölçtüğü şey o indirmenin **bedelidir**: nokta az sayıdan geri kurulmaya çalışılınca özgün
yerinden ne kadar uzağa düşüyor.

- **BA1.** Kurgu abone tablosu M27/K01–K03 ile aynıdır ve **kurgudur**: tohum 20260218, 1.260
  satır, bölme 756/252/252. Etiket sütunu bu kursta **hiç üretilmez**.
- **BA2.** On iki sütun kullanılır: altı sayısal ölçüm, sıfır okuma göstergesi ve beş bölge
  göstergesi.
- **BA3.** Ölçek ve bileşen yönleri **yalnız eğitim kümesinden** öğrenilir, sınama kümesine aynı
  sayılarla uygulanır. Bütün kümeden öğrenilseydi ölçüm şişerdi.
- **BA4.** **Bileşen payı** hesabı burada tekrarlanmaz. M26/K05'in keşifsel analiz kursu payı bir
  keşif aracı olarak, M27/K02'nin öznitelik seçimi konusu ise bir öznitelik ölçüsü olarak
  kurmuştu; bu derste pay yalnız **girdidir** ve okunmak için kullanılır.
- **BA5.** **Geri kurma hatası**, ölçeklenmiş uzayda noktanın özgün yeri ile az sayıdan kurulmuş
  yeri arasındaki uzaklığın nokta başına ortalamasıdır. Birimi standart sapmadır.
- **BA6.** Taban çizgisi **sütun ortalamalarıyla geri kurmadır**: hiçbir bileşen kullanılmaz, her
  nokta eğitim kümesinin ortalama noktasına eşitlenir. Öğrenmeyen en yalın geri kurma budur.
- **BA7.** İkinci bir taban çizgisi daha okunur: **k tane rastgele dik yön**. Bileşenlerin
  farkının yön sayısından mı seçiminden mi geldiğini yalnız bu ayırır. Üç çekiliş ortalanır.
- **BA8.** Yönler ders içinde yinelemeyle bulunur ve **modeldir**: eşdeğişim dizeyi kurulur, en
  büyük yön 200 yinelemede aranır, her yeni yön öncekilere diklenir. Kitaplık çağrılmaz.

#### Geri Kurma Hatası ve Tabanı

Yordam üç adımdır. Sütunlar ortak bir ölçeğe getirilir, eğitim kümesinin eşdeğişim dizeyi kurulur
ve bu dizeyin en büyük değişkenliği taşıyan yönü yinelemeyle bulunup listeye eklenir. Her yeni yön
kendinden öncekilere dik olmaya zorlanır. Bir nokta k yöne izdüşürülüp geri kurulduğunda kalan
uzaklık, o k yönün taşımadığı her şeydir.

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir.
# Etiket sutunu bu kursta hic uretilmez.
import math

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


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort = sum(v) / len(v)
    x = {"no": k["no"], "hane": k["hane"], "memnuniyet": k["memnuniyet"],
         "donem": len(v), "ort_tuketim": round(ort, 2),
         "oynaklik": round(max(v) - min(v), 2), "sifir_okuma": int(min(v) == 0.0),
         "kisi_basi": round(ort / k["hane"], 3)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem",
        "sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


K = karistir(VERI, TOHUM + 90000)
EGT, DOG, SIN = K[:756], K[756:1008], K[1008:]
```

```python
n = len(ALAN)
ORT = {a: sum(x[a] for x in EGT) / len(EGT) for a in ALAN}
SAC = {a: max(1e-9, (sum((x[a] - ORT[a]) ** 2 for x in EGT) / len(EGT)) ** 0.5)
       for a in ALAN}


def carp(m, v):
    return [sum(m[i][j] * v[j] for j in range(len(v))) for i in range(len(m))]


def diklestir(v, B):
    for u in B:
        c = sum(v[i] * u[i] for i in range(len(v)))
        v = [v[i] - c * u[i] for i in range(len(v))]
    return v


def yonleri_bul(Z):
    C = [[sum(z[i] * z[j] for z in Z) / len(Z) for j in range(n)] for i in range(n)]
    B, pay = [], []
    for _ in range(n):
        v, lam = [1.0 / (i + 1) for i in range(n)], 0.0
        for _ in range(200):
            w = diklestir(carp(C, v), B)
            u = sum(q * q for q in w) ** 0.5
            if u < 1e-9:
                break
            v, lam = [q / u for q in w], u
        if lam < 1e-9:
            break
        B.append(v)
        pay.append(lam)
    return B, pay


def geri_kur(z, B):
    c = [sum(z[j] * b[j] for j in range(n)) for b in B]
    return [sum(c[i] * B[i][j] for i in range(len(B))) for j in range(n)]


def hata(Z, B):
    return sum(sum((z[j] - geri_kur(z, B)[j]) ** 2 for j in range(n)) ** 0.5
               for z in Z) / len(Z)


def rastgele_yonler(k, tohum):
    r, B = uretec(tohum), []
    while len(B) < k:
        v = diklestir([r() + r() + r() - 1.5 for _ in range(n)], B)
        u = sum(q * q for q in v) ** 0.5
        if u > 1e-6:
            B.append([q / u for q in v])
    return B


ZE = [[(x[a] - ORT[a]) / SAC[a] for a in ALAN] for x in EGT]
ZS = [[(x[a] - ORT[a]) / SAC[a] for a in ALAN] for x in SIN]
BILESEN, PAY = yonleri_bul(ZE)
TOP, TABAN = sum(PAY), hata(ZS, [])

print(f"sutun {n}, bulunan yon {len(BILESEN)}, taban cizgisi {TABAN:.4f}")
print(f"{'k':>3} {'pay':>7} {'egitim':>8} {'ayrilmis':>9} {'rastgele':>9} {'fark':>8}")
for k in (1, 2, 3, 4, 6, 8, 10, 11):
    B, s = BILESEN[:k], 0.0
    for t in range(3):
        s += hata(ZS, rastgele_yonler(k, TOHUM + 900 * (t + 1)))
    print(f"{k:>3} {sum(PAY[:k]) / TOP:>7.3f} {hata(ZE, B):>8.4f} {hata(ZS, B):>9.4f} "
          f"{s / 3:>9.4f} {hata(ZS, B) - TABAN:>+8.4f}")
```

```
sutun 12, bulunan yon 11, taban cizgisi 3.3411
  k     pay   egitim  ayrilmis  rastgele     fark
  1   0.190   3.0359    3.0295    3.1971  -0.3116
  2   0.341   2.7663    2.7762    3.0531  -0.5650
  3   0.458   2.5071    2.5419    2.8281  -0.7993
  4   0.571   2.1700    2.2211    2.6875  -1.1200
  6   0.771   1.5551    1.5474    2.2723  -1.7937
  8   0.935   0.7878    0.8316    1.8246  -2.5096
 10   0.989   0.2625    0.2668    1.1637  -3.0743
 11   1.000   0.0000    0.0000    0.8436  -3.3411
```

Üç sayı tablodadır. Taban çizgisi 3,3411: hiçbir bileşen kullanılmadığında bir nokta ortalama
noktadan bu kadar uzaktır. İki bileşenle geri kurma 2,7762 verir; fark 0,5650, yani tabanın
0,169'u. Aynı k'da üç rastgele dik yönün ortalaması 3,0531'dir — bileşenlerin rastgele yönlerden
farkı 0,2769. Düşüşün geri kalanı, yani 0,2880, yalnız **iki yön kullanılmasından** gelir; hangi
iki yön olduğu bilinmese bile taban o kadar aşağı iner. Bileşenlerin katkısı ancak bu payın
üstünde kalan kısımdır ve tabanla arasındaki farkın yarısına yakınıdır.

Tablonun ikinci okuması, pay sütunuyla hata sütununun **aynı hızda gitmemesidir**. İki bileşende
pay 0,341 — kulağa üçte birden fazla gibi gelir — ama hata yalnız 0,169 düşmüştür. Pay kareli
büyüklükler üzerinden, geri kurma hatası ise uzaklık üzerinden okunur; payın yarısı uzaklığın
yarısı değildir. Bu iki sütunun birbirinin yerine okunması boyut azaltmada en sık yapılan yanlıştır.

Üçüncü okuma son satırdadır. On iki sütun var ama yordam yalnız **on bir yön** bulabildi ve on
birinci yönde hata tam olarak sıfıra indi. Beş bölge göstergesinin toplamı her satırda bire eşit
olduğu için biri diğerlerinden okunabiliyor; on ikinci yön diye bir şey yok. Tablo on iki sayı
tutuyor, ama on bir sayıdan fazlasını taşımıyor.

#### Uzaklıkların Ne Kadarı Ayakta Kalıyor

Geri kurma hatası noktanın **yerini** ölçer. Kümeleme ise noktanın yerini değil, noktalar
**arasındaki uzaklığı** kullanır. İzdüşüm bu ikisini aynı ölçüde korumak zorunda değildir, çünkü
bir izdüşüm uzaklıkları yalnız kısaltabilir — hiçbir zaman uzatamaz.

```python
def uzaklik_korunumu(Z, B):
    P = [[sum(z[j] * b[j] for j in range(n)) for b in B] for z in Z]
    toplam, sayi, korunan = 0.0, 0, 0
    for a in range(len(Z)):
        for b in range(a + 1, len(Z)):
            d = sum((Z[a][j] - Z[b][j]) ** 2 for j in range(n)) ** 0.5
            if d < 1e-9:
                continue
            dk = sum((P[a][i] - P[b][i]) ** 2 for i in range(len(B))) ** 0.5
            toplam, sayi = toplam + dk / d, sayi + 1
            korunan += dk / d >= 0.9
    return toplam / sayi, korunan / sayi, sayi


print(f"{'k':>3} {'pay':>7} {'ort oran':>9} {'korunan':>9} {'rastgele oran':>14}")
for k in (1, 2, 4, 6, 8, 10):
    o, g, c = uzaklik_korunumu(ZS, BILESEN[:k])
    r = uzaklik_korunumu(ZS, rastgele_yonler(k, TOHUM + 900))[0]
    print(f"{k:>3} {sum(PAY[:k]) / TOP:>7.3f} {o:>9.4f} {g:>9.4f} {r:>14.4f}")
print(f"olculen cift sayisi {c}")
```

```
  k     pay  ort oran   korunan  rastgele oran
  1   0.190    0.3398    0.0004         0.2341
  2   0.341    0.4965    0.0057         0.3747
  4   0.571    0.7029    0.0542         0.5358
  6   0.771    0.8535    0.4299         0.6945
  8   0.935    0.9540    0.8910         0.7924
 10   0.989    0.9943    0.9994         0.9049
olculen cift sayisi 31626
```

İkinci tablo birincisinden serttir. İki bileşende ikili uzaklıklar ortalama olarak özgününün
0,4965 katına iner — uzaklıkların yarısı kaybolmuştur — ve 31.626 çiftin yalnız 0,0057'si yüzde on
yanılma payı içinde ayakta kalır. Altı bileşende ortalama oran 0,8535'e çıkar ve çiftlerin
0,4299'u korunur. Uzaklıkların çoğunun ayakta kaldığı ilk satır sekiz bileşendir: 0,9540 ve 0,8910.

Buradan çıkan karar açıktır. İki bileşenli bir izdüşüm üzerinde kümeleme yapmak, uzaklıkların
yarısını attıktan sonra uzaklığa dayalı bir yordam çalıştırmaktır. Rastgele yönlerin aynı k'da
0,3747 vermesi bileşenlerin bir şey kazandırdığını gösterir, ama kazanç 0,4965 ile 0,3747
arasındadır ve iki sayı da tam korunmadan uzaktır. Bileşen payı bu satırların hiçbirini haber
vermez: pay 0,341 iken korunan çift oranı 0,0057'dir ve iki sayı aynı büyüklük sırasında bile
değildir.

#### Ölçekleme Kararı İzdüşümü Değiştirir

Bileşenler en çok değişkenliği taşıyan yönleri arar. Değişkenlik sütunun biriminden okunduğu için,
sütunlar farklı birimlerdeyse en büyük birimli sütun en büyük yönü kendine çeker. Bu bir kusur
değil, yordamın tanımıdır — ama ölçekleme kararının izdüşümü nereye çevirdiği ölçülmeden verilirse
bir karar değil bir kazadır.

```python
for olcekli in (True, False):
    bol = (lambda a: SAC[a]) if olcekli else (lambda a: 1.0)
    HE = [[(x[a] - ORT[a]) / bol(a) for a in ALAN] for x in EGT]
    HS = [[(x[a] - ORT[a]) / bol(a) for a in ALAN] for x in SIN]
    B, P = yonleri_bul(HE)
    y = sorted((-abs(B[0][j]), ALAN[j], B[0][j]) for j in range(n))
    print(f"{'olcekli' if olcekli else 'ham':<8} ilk iki pay "
          f"{(P[0] + P[1]) / sum(P):.3f}  c0 yukleri: " +
          ", ".join(f"{a} {w:+.2f}" for _, a, w in y[:4]))
    kalan, toplam = [0.0] * n, 0.0
    for z in HS:
        g = geri_kur(z, B[:2])
        d = [(z[j] - g[j]) / (1.0 if olcekli else SAC[ALAN[j]]) for j in range(n)]
        toplam += sum(q * q for q in d) ** 0.5
        for j in range(n):
            kalan[j] += d[j] * d[j] / len(HS)
    print(f"{'':8} k=2 ortak birimde hata {toplam / len(HS):.4f}  sutun kalanlari: " +
          ", ".join(f"{ALAN[j]} {kalan[j]:.2f}" for j in (0, 1, 4, 11)))
```

```
olcekli  ilk iki pay 0.341  c0 yukleri: kisi_basi +0.55, ort_tuketim +0.54, b_bati -0.34, hane -0.31
         k=2 ortak birimde hata 2.7762  sutun kalanlari: ort_tuketim 0.36, oynaklik 0.19, memnuniyet 1.00, b_merkez 1.07
ham      ilk iki pay 0.863  c0 yukleri: oynaklik +0.96, ort_tuketim +0.24, kisi_basi +0.12, donem +0.04
         k=2 ortak birimde hata 2.8473  sutun kalanlari: ort_tuketim 0.10, oynaklik 0.00, memnuniyet 1.00, b_merkez 1.21
```

İki satır aynı veriyi iki ayrı yere götürüyor. Ölçeklenmemiş kümede birinci bileşen neredeyse
tümüyle tek bir sütundur: `oynaklik` yükü +0,96 ve geri kalan on bir sütunun hiçbiri o bileşende
0,25'i geçmiyor. İlk iki bileşenin payı 0,863'e çıkıyor. Ölçeklenmiş kümede aynı bileşen
`kisi_basi` +0,55 ve `ort_tuketim` +0,54 taşıyor, `oynaklik` ilk dörde bile girmiyor ve ilk iki
bileşenin payı 0,341'de kalıyor.

Yalnız pay sütununa bakan biri ölçeklenmemiş izdüşümü seçerdi: 0,863, 0,341'in iki buçuk katı.
Ortak birimde ölçülen geri kurma hatası bunun tersini söylüyor — ölçeklenmemişte 2,8473,
ölçeklenmişte 2,7762. Ölçeklenmemiş izdüşüm payın 0,863'ünü topluyor çünkü tek bir sütunun ham
birimi bütün payı yutuyor; o sütunu kusursuz geri kuruyor (`oynaklik` kalanı 0,00) ve karşılığında
`memnuniyet` sütununu 1,00 kalanla, yani tabandan hiç iyi olmayan bir yerde bırakıyor. `b_merkez`
sütununda kalan 1,21 — izdüşüm o sütunu sütun ortalamasından **daha kötü** kuruyor, çünkü seçtiği
yön o sütunu yanlış yöne itiyor.

Ölçeklenmiş izdüşüm de kusursuz değildir: `memnuniyet` orada da 1,00 kalanla duruyor ve
`b_merkez` 1,07. Fark, kaybın nereye dağıtıldığındadır. Ölçekleme kararı hangi sütunun
kurtarılacağını seçmektir ve bu seçim, bileşenler hesaplanmadan çok önce, ölçekleme adımında
verilmiştir.

#### Özet

- Geri kurma hatasının tabanı sütun ortalamalarıyla geri kurmadır ve 3,3411'dir; iki bileşenle
  geri kurma 2,7762 verir, fark tabanın 0,169'u kadardır.
- İkinci taban rastgele dik yönlerdir: aynı k'da 3,0531. Bileşenlerin katkısı 0,2769'dur, düşüşün
  kalan 0,2880'i yalnız iki yön kullanılmasından gelir.
- Bileşen payı ile geri kurma hatası aynı hızda gitmez: pay 0,341 iken hata yalnız 0,169 düşer,
  çünkü pay kareli büyüklükler, hata ise uzaklık üzerinden okunur.
- Korunan geometri paydan daha azdır: iki bileşende ikili uzaklıklar özgününün 0,4965 katına iner
  ve 31.626 çiftin yalnız 0,0057'si yüzde on yanılma payıyla ayakta kalır.
- Ölçekleme izdüşümü değiştirir: ölçeklenmemiş kümede ilk iki bileşenin payı 0,863'e çıkar ama
  ortak birimde hata 2,8473 ile ölçeklenmişin gerisinde kalır ve bazı sütunlar tabandan kötü
  geri kurulur.

#### Sonraki Adım

Bu derste bileşenler eşdeğişim dizeyi üzerinden yineleyerek bulundu ve her nokta bulunan yönlere
izdüşürülüp geri kuruldu. Yordam işledi, ama bir soruyu hiç sormadı: geri kurulan tabloyu saklamak
için kaç sayı gerekiyor. On iki sütunlu 252 satırlık bir tablo 3.024 sayıdır; iki bileşenli
gösterim aynı tabloyu kaç sayıyla tutar ve o adet düştükçe hata hangi eğriyle artar. Sonraki ders
izdüşümü bir dizey ayrışımı olarak yazar, rank kavramını ölçünün içine sokar ve saklanan sayı
adedi ile geri kurma hatası arasındaki takası aynı taban çizgisiyle birlikte basar.

### Tekil Değer Ayrışımı Uygulaması

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/tekil-deger-ayrisimi-uygulamasi

Bir dizeyi düşük ranklı bir yaklaşımla saklamanın hata ve sayı adedi cinsinden bedelinin ölçülmesi: elle kurulmuş 6x5'lik dizeyde rank 2 yaklaşımın toplam hatası 0,6746 ve bu sayı atılan tekil değerlerin karelerinin köküne birebir eşit, ama rank 3'te saklanan sayı 36'ya çıkıp özgün 30 sayıyı geçiyor. 252x12'lik abone tablosunda sütun ortalamalarıyla geri kurma 3,3411 verirken rank 2 yaklaşım 2,7464 veriyor ve 3.024 sayı yerine 554 sayı saklıyor; aynı bütçeyle rastgele iki ham sütun saklamak 3,0617 veriyor ve düşük ranklı yaklaşımın üstünlüğü rank 2'de 0,3153, rank 8'de 0,9663 oluyor.

Önceki ders bileşen yönlerini eşdeğişim dizeyi üzerinden buldu ve her noktayı k yöne izdüşürüp geri
kurdu. Ölçü nokta başına ortalama uzaklıktı ve sütun ortalamalarıyla geri kurma 3,3411 veriyordu.
Yordamın sormadığı soru şuydu: bu gösterimi saklamak için kaç sayı gerekiyor. On iki sütunlu 252
satırlık bir tablo 3.024 sayıdır; iki bileşenli gösterim aynı tabloyu kaçla tutar.

Soru sorulduğu anda izdüşüm bir **dizey işlemine** dönüşür. Tablo, satırları abone ve sütunları
ölçüm olan bir dizeydir; izdüşüm bu dizeyi iki küçük dizeyin çarpımı olarak yeniden yazmaktır.
**Tekil değer ayrışımı** bunu yapan ayrışımdır: dizeyi, satır yönleri, sütun yönleri ve her yön
çiftine bir ağırlık veren **tekil değerler** olarak üçe ayırır. İlk r tekil değeri tutup kalanı
atmak **düşük ranklı yaklaşım** üretir. Bu dersin ölçtüğü şey o yaklaşımın iki tarafıdır: hata ne
kadar artıyor ve saklanan sayı adedi ne kadar azalıyor.

- **BA9.** Ölçülen dizey sınama kümesidir: 252 satır, 12 sütun, 3.024 sayı. Sütunlar önceki
  dersteki ölçekle, yani **yalnız eğitim kümesinden okunan** ortalama ve standart sapmayla
  ölçeklenir.
- **BA10.** Ayrışım ders içinde yazılır ve **modeldir**: dizeyin sütun çarpım dizeyi kurulur, en
  büyük yön 300 yinelemede aranır, her yeni yön öncekilere diklenir, tekil değer bulunan büyüklüğün
  kökü olarak alınır. Kitaplık çağrılmaz ve ölçek küçük tutulur. Ayrışım **ölçülen dizeyin kendisi
  üzerinde** kurulur; önceki dersin bileşen yönleri eğitim kümesinden öğrenilip sınama kümesine
  uygulanıyordu, bu yüzden aynı boyut sayısında iki dersin geri kurma hataları birebir eşleşmez.
- **BA11.** Tekil değerler negatif değildir ve büyükten küçüğe sıralanır. **Rank**, sıfırdan farklı
  tekil değerlerin sayısıdır; eşik $10^{-10}$ alınmıştır, çünkü yinelemeli arama sıfırı tam olarak
  değil yaklaşık üretir.
- **BA12.** **Saklanan sayı adedi** açıkça sayılır: rank r için satır yönleri $252r$, sütun
  yönleri $12r$, tekil değerler $r$ ve tabloyu özgün birimine döndürmek için 12 ortalama ile 12
  standart sapma. Toplam $265r + 24$.
- **BA13.** **Geri kurma hatası** tanımı önceki dersten sürer: nokta başına ortalama uzaklık,
  ölçeklenmiş uzayda, birimi standart sapma.
- **BA14.** Taban çizgisi yine **sütun ortalamalarıyla geri kurmadır** ve rank 0 satırıdır.
- **BA15.** Küçük dizey elle kurulmuştur, abone tablosundan gelmez ve yalnız ayrışımın ne yaptığını
  görünür kılmak içindir.
- **BA16.** İkinci karşılaştırma **aynı bütçeyle r ham sütun saklamaktır**: rastgele r sütun tutulur,
  kalan sütunlar ortalamayla kurulur, üç çekiliş ortalanır.

#### Ayrışım Küçük Bir Dizeyde

Altı satırlı, beş sütunlu bir dizey elle kurulur. Satırların çoğu iki temel örüntünün ağırlıklı
toplamıdır: biri uçlarda yüksek ortada düşük, öbürü tam tersi. Üçüncü satırın ilk hücresi bilerek
bir birim kaydırılmıştır, böylece dizey iki örüntüye tam oturmaz.

```python
def carp(m, v):
    return [sum(m[i][j] * v[j] for j in range(len(v))) for i in range(len(m))]


def diklestir(v, B):
    for u in B:
        c = sum(v[i] * u[i] for i in range(len(v)))
        v = [v[i] - c * u[i] for i in range(len(v))]
    return v


def ayristir(A):
    # MODELDIR: tekil deger ayrisimi ders icinde, yalniz standart kitaplikla yazilir.
    m = len(A[0])
    G = [[sum(s[i] * s[j] for s in A) for j in range(m)] for i in range(m)]
    sag, tekil = [], []
    for _ in range(m):
        v, lam = [1.0 / (i + 1) for i in range(m)], 0.0
        for _ in range(300):
            w = diklestir(carp(G, v), sag)
            u = sum(q * q for q in w) ** 0.5
            if u < 1e-12:
                break
            v, lam = [q / u for q in w], u
        if lam < 1e-10:
            break
        sag.append(v)
        tekil.append(lam ** 0.5)
    sol = [[sum(s[j] * v[j] for j in range(m)) / tekil[i] for s in A]
           for i, v in enumerate(sag)]
    return sol, tekil, sag


def yaklas(sol, tekil, sag, r, a, b):
    return sum(sol[i][a] * tekil[i] * sag[i][b] for i in range(r))


KUCUK = [[6, 3, 0, 3, 6], [4, 3, 2, 3, 4], [3, 3, 4, 3, 2],
         [0, 3, 6, 3, 0], [4, 4, 4, 4, 4], [2, 2, 2, 2, 2]]
SOL, TEKIL, SAG = ayristir([[float(q) for q in s] for s in KUCUK])
print("tekil degerler: " + ", ".join(f"{s:.4f}" for s in TEKIL))
print(f"{'rank':>4} {'toplam hata':>12} {'atilanlarin koku':>17} {'saklanan':>9}")
for r in range(len(TEKIL) + 1):
    h = sum((KUCUK[a][b] - yaklas(SOL, TEKIL, SAG, r, a, b)) ** 2
            for a in range(6) for b in range(5)) ** 0.5
    print(f"{r:>4} {h:>12.4f} {sum(s * s for s in TEKIL[r:]) ** 0.5:>17.4f} "
          f"{r * (6 + 5 + 1):>9}")
print("ozgun dizey 30 sayi. rank 2 yaklasim:")
for a in range(6):
    print("  " + " ".join(f"{yaklas(SOL, TEKIL, SAG, 2, a, b):6.2f}" for b in range(5)))
```

```
tekil degerler: 17.0637, 7.3058, 0.6746
rank  toplam hata  atilanlarin koku  saklanan
   0      18.5742           18.5742         0
   1       7.3369            7.3369        12
   2       0.6746            0.6746        24
   3       0.0000            0.0000        36
ozgun dizey 30 sayi. rank 2 yaklasim:
    6.03   2.99   0.01   2.99   5.98
    4.07   2.98   2.02   2.98   3.95
    2.54   3.12   3.90   3.12   2.34
    0.16   2.96   6.04   2.96  -0.12
    4.13   3.97   4.03   3.97   3.90
    2.06   1.98   2.01   1.98   1.95
```

İki sütun birbirine birebir eşit. Rank r yaklaşımın toplam hatası, **atılan tekil değerlerin
karelerinin toplamının köküdür** — rank 2'de hata 0,6746 ve atılan tek tekil değer de 0,6746. Bu,
ayrışımın verdiği en kullanışlı şeydir: hata, yaklaşım kurulmadan önce, yalnız tekil değerler
listesine bakılarak bilinir. Yordamı çalıştırıp ölçmeye gerek yoktur.

İkinci sütun okumayı tersine çevirir. Rank 2 yaklaşım 24 sayı saklar, özgün dizey 30 sayı; kazanç
6 sayıdır ve hata 0,6746'dır. Rank 3 yaklaşım hatasızdır ama 36 sayı saklar — özgün dizeyden 6
fazla. **Tam ayrışım sıkıştırma değildir.** Kazanç yalnız atılan ranktan gelir ve dizeyin kendisi
küçükse hiç gelmeyebilir.

Yaklaşımın çıktısı da okunabilir. İlk, ikinci, dördüncü, beşinci ve altıncı satırlar neredeyse
birebir geri geliyor, çünkü onlar gerçekten iki örüntünün toplamıydı. Kaydırılmış üçüncü satır
`3, 3, 4, 3, 2` yerine `2.54, 3.12, 3.90, 3.12, 2.34` dönüyor. Hatanın tamamı bu tek satırda
toplanmış durumda. Düşük ranklı yaklaşım hatayı eşit dağıtmaz; **örüntüye uymayan satırı düzeltir**
ve o satır hakkındaki bilgiyi atar.

#### Abone Tablosunda Rank ve Saklanan Sayı

Aynı ayrışım 252 satırlık, 12 sütunlu abone tablosuna uygulanır. Sütunlar önceki dersteki ölçekle
ölçeklenir, ki taban çizgisi değişmesin.

```python
import math

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


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort = sum(v) / len(v)
    x = {"hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v),
         "ort_tuketim": round(ort, 2), "oynaklik": round(max(v) - min(v), 2),
         "sifir_okuma": int(min(v) == 0.0), "kisi_basi": round(ort / k["hane"], 3)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem",
        "sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
r, s = uretec(TOHUM + 90000), list(range(len(VERI)))
for i in range(len(s) - 1, 0, -1):
    j = int(r() * (i + 1))
    s[i], s[j] = s[j], s[i]
K = [VERI[i] for i in s]
EGT, SIN = K[:756], K[1008:]

n = len(ALAN)
ORT = {a: sum(x[a] for x in EGT) / len(EGT) for a in ALAN}
SAC = {a: max(1e-9, (sum((x[a] - ORT[a]) ** 2 for x in EGT) / len(EGT)) ** 0.5)
       for a in ALAN}
Z = [[(x[a] - ORT[a]) / SAC[a] for a in ALAN] for x in SIN]
N = len(Z)
SOL, TEKIL, SAG = ayristir(Z)


def hata(r):
    return sum(sum((Z[a][b] - yaklas(SOL, TEKIL, SAG, r, a, b)) ** 2
                   for b in range(n)) ** 0.5 for a in range(N)) / N


print(f"ozgun tablo {N} x {n} = {N * n} sayi, bulunan rank {len(TEKIL)}")
print("tekil degerler: " + ", ".join(f"{s:.2f}" for s in TEKIL))
print(f"{'rank':>4} {'hata':>8} {'fark':>8} {'saklanan':>9} {'oran':>6}")
for r in (0, 1, 2, 3, 4, 6, 8, 10, 11):
    print(f"{r:>4} {hata(r):>8.4f} {hata(r) - hata(0):>+8.4f} "
          f"{24 + r * (N + n + 1):>9} {(24 + r * (N + n + 1)) / (N * n):>6.3f}")
```

```
ozgun tablo 252 x 12 = 3024 sayi, bulunan rank 11
tekil degerler: 24.19, 21.44, 19.46, 18.48, 18.14, 16.64, 15.66, 14.15, 10.50, 7.14, 5.69
rank     hata     fark  saklanan   oran
   0   3.3411  +0.0000        24  0.008
   1   3.0129  -0.3282       289  0.096
   2   2.7464  -0.5947       554  0.183
   3   2.4284  -0.9127       819  0.271
   4   2.1363  -1.2048      1084  0.358
   6   1.5054  -1.8357      1614  0.534
   8   0.7925  -2.5486      2144  0.709
  10   0.2568  -3.0843      2674  0.884
  11   0.0000  -3.3411      2939  0.972
```

Tekil değerler bu tabloda hiç dik inmiyor: en büyüğü 24,19, en küçüğü 5,69 ve arada bir uçurum yok.
Küçük dizeyde iki tekil değer 17,06 ve 7,31 iken üçüncüsü 0,67'ye düşüyordu; orada rank 2 doğal bir
kesme noktasıydı. Burada öyle bir nokta yok. Bu, kesmenin veriden değil **karardan** geldiği
anlamına gelir ve kararın bedeli tabloda okunur.

Rank 2 yaklaşım 3.024 sayı yerine 554 sayı saklıyor — özgünün 0,183'ü — ve hatayı tabandan 0,5947
aşağı çekiyor. Rank 8'e çıkıldığında hata 0,7925'e iniyor ama saklanan sayı 2.144'e, yani özgünün
0,709'una çıkıyor. Rank 11'de hata sıfır ve saklama 2.939 sayı; özgün 3.024'ten yalnız 85 sayı az.
Hatasız sıkıştırma bu tabloda pratikte yok, çünkü **rank on birdir ve on iki sütunla arasındaki tek
fark bölge göstergelerinin toplamıdır** — önceki dersin on birinci yönde sıfıra inen hatası burada
rank olarak adını buluyor.

#### Aynı Bütçeyle Ham Sütun Saklamak

Sayı adedi bir bütçedir ve bütçeyi harcamanın başka bir yolu vardır: ham sütunların bir kısmını
olduğu gibi tutup kalanını ortalamayla kurmak. Bu, Veri Hazırlama ve Öznitelik Mühendisliği kursunun
öznitelik seçimi konusunda ölçtüğü karara benzer ama burada ölçüt farklıdır — soru hangi sütunun
ayırt ettiği değil, hangi sütunun tabloyu geri kurduğudur. Karşılaştırma anlamlı olsun diye
tutulacak sütunlar rastgele seçilir ve üç çekiliş ortalanır.

```python
def sutun_taban(r, tohum):
    rr, idx = uretec(tohum), list(range(n))
    for i in range(n - 1, 0, -1):
        j = int(rr() * (i + 1))
        idx[i], idx[j] = idx[j], idx[i]
    tut = set(idx[:r])
    return sum(sum(z[j] * z[j] for j in range(n) if j not in tut) ** 0.5
               for z in Z) / N


print(f"{'r':>3} {'dusuk rank':>11} {'saklanan':>9} {'r ham sutun':>12} "
      f"{'saklanan':>9} {'fark':>8}")
for r in (1, 2, 4, 6, 8, 11):
    st = sum(sutun_taban(r, TOHUM + 500 * (t + 1)) for t in range(3)) / 3
    print(f"{r:>3} {hata(r):>11.4f} {24 + r * (N + n + 1):>9} {st:>12.4f} "
          f"{r * N + r:>9} {st - hata(r):>+8.4f}")
```

```
  r  dusuk rank  saklanan  r ham sutun  saklanan     fark
  1      3.0129       289       3.2008       253  +0.1879
  2      2.7464       554       3.0617       506  +0.3153
  4      2.1363      1084       2.6787      1012  +0.5423
  6      1.5054      1614       2.2730      1518  +0.7676
  8      0.7925      2144       1.7588      2024  +0.9663
 11      0.0000      2939       0.7857      2783  +0.7857
```

İki yol neredeyse aynı bütçeyi harcıyor: rank 2 için 554 sayı, iki ham sütun için 506. Fark 48
sayıdır, bütçenin yüzde onundan azı. Karşılığında alınan hata farkı rank 2'de 0,3153. Düşük ranklı
yaklaşım kazanıyor, ama kazanç küçük — ve bu tablo için önemli bir bulgudur, çünkü boyut azaltmanın
ham sütun saklamaya üstünlüğünün **kendiliğinden** olduğu varsayılır.

Üstünlük rank büyüdükçe açılıyor: 4'te 0,5423, 6'da 0,7676, 8'de 0,9663. Nedeni tekil değer
listesinde duruyor. Düşük ranklı yaklaşım her rankta dizeyin o rankta taşınabilecek **en iyi**
parçasını alır; ham sütun saklamak ise sütunları olduğu gibi alır ve sütunlar arasındaki ortak
yapıyı hiç kullanmaz. Rank arttıkça bu ortak yapı birikir. Rank 11'de fark tersine dönmeye başlar
(0,7857), çünkü rank 11 zaten hatasızdır ve karşı taraf tek bir sütunu atmanın hatasını taşır.

Karar bu iki tablodan okunur. Sıkıştırma amacıyla düşük ranklı yaklaşım kullanmak, hatayı
tekil değerlerden önceden bilebilmek ve her sayıya karşılık en çok geri kurmayı almak demektir.
Ama tablo dar ve rank yüksekse, kazanç ne saklamada ne hatada büyüktür — 3.024 sayının 554'e
inmesi hatanın tabandan yalnız 0,5947 uzaklaşmasıyla ödenir.

#### Özet

- Tekil değer ayrışımı dizeyi satır yönleri, sütun yönleri ve tekil değerler olarak yazar; ilk r
  terimi tutmak düşük ranklı yaklaşımdır.
- Geri kurma hatası atılan tekil değerlerden okunur: küçük dizeyde rank 2'nin toplam hatası 0,6746
  ve atılan tek tekil değer de 0,6746'dır.
- Tam ayrışım sıkıştırma değildir: küçük dizeyde rank 3 yaklaşım hatasızdır ama 36 sayı saklar,
  özgün dizey 30 sayıdır.
- Abone tablosunda taban çizgisi 3,3411; rank 2 yaklaşım 2,7464 verir ve 3.024 sayı yerine 554
  sayı saklar. Rank 11 hatasızdır ve yalnız 85 sayı kazandırır.
- Aynı bütçeyle rastgele ham sütun saklamak rank 2'de 3,0617 verir; düşük ranklı yaklaşımın
  üstünlüğü orada 0,3153'tür ve ancak rank 8'de 0,9663'e çıkar.

#### Sonraki Adım

İki derste izdüşüm hep aynı biçimdeydi: her yeni sayı, eski sütunların **ağırlıklı toplamıydı**.
Bu kısıt hatayı hesaplanabilir kıldı ve tekil değerlerden okunmasını sağladı, ama bir bedeli var —
doğrular yalnız doğrulara gider. Noktalar bir düzlemde değil de kıvrılmış bir yüzeyde duruyorsa,
ağırlıklı toplamla kurulan hiçbir izdüşüm o kıvrımı takip edemez ve komşu noktalar izdüşümde
birbirinden uzağa düşer. Sonraki ders bu kısıtı kaldıran yaklaşımları ele alır: uzaklıkları değil
**komşulukları** korumayı hedefleyen gömmeler. Ölçü de değişir, çünkü geri kurma hatası orada
tanımsızdır — sorulacak soru, özgün uzayda en yakın komşuların kaçının gömmede de komşu kaldığıdır.

### Doğrusal Olmayan Gömme

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/dogrusal-olmayan-gomme

Komşulukları korumayı hedefleyen iki boyutlu yerleşimlerin doğrusal izdüşüme karşı ölçülmesi: rastgele yerleşim en yakın on komşunun 0,0390'ını korurken doğrusal izdüşüm 0,2639, komşuluk hedefli gömme 0,5032 koruyor. Aynı gömme en uzak on çiftin yalnız 0,0071'ini koruyor, doğrusal izdüşüm ise 0,6012'sini; iki yöntem birbirinin tersini koruyor. Ortalama ikili uzaklık eşitlendikten sonra gömmede aynı uzaklıkta duran çiftlerin özgün uzaklığı 3,47 ile 9,46 arasında değişiyor ve aynı ayarla ikinci koşumda aynı çiftin gömülmüş uzaklığı ortalama 0,2906 bağıl farkla oynuyor.

İki derste izdüşüm hep aynı biçimdeydi: her yeni sayı, eski sütunların ağırlıklı toplamıydı. Bu
kısıt hatayı hesaplanabilir kıldı ve tekil değerlerden okunmasını sağladı. Bedeli de vardı —
ağırlıklı toplam düz bir alt uzaya izdüşürür ve noktalar düz bir alt uzayda durmuyorsa o düzlem
onları takip edemez. Kıvrılmış bir yüzey üzerinde birbirine yakın duran iki nokta, yüzeyi düzleme
bastıran bir izdüşümde birbirinden uzağa düşebilir.

Bu dersin ele aldığı yaklaşımlar o kısıtı kaldırır. Ortak yanları şudur: **uzaklıkları değil
komşulukları hedeflerler**. Her noktanın kimlere yakın olduğu özgün uzayda bir kez saptanır, sonra
düşük boyutlu bir uzayda o komşuluk ilişkisini olabildiğince yeniden kuran bir yerleşim aranır.
Aile içinde onlarca ayrı yordam vardır ve ayrıntılarda ayrışırlar; burada ortak çekirdek yazılır ve
ölçülür. Ölçü de değişmek zorundadır, çünkü geri kurma hatası burada tanımsızdır: gömmeden özgün
uzaya geri dönen bir yol yoktur. Sorulacak soru şudur — **özgün uzayda en yakın k komşunun kaçı
gömmede de komşu kalıyor.**

- **BA17.** Ölçülen küme sınama kümesidir: 252 nokta, 12 ölçeklenmiş sütun. Ölçek yine yalnız
  eğitim kümesinden okunur.
- **BA18.** **Komşuluk korunma oranı**, her noktanın özgün uzaydaki en yakın $k$ komşusundan
  kaçının gömmedeki en yakın $k$ komşusu arasında da bulunduğunun, bütün noktalar üzerinden
  ortalamasıdır. $k = 10$ alınmıştır.
- **BA19.** Taban çizgisi **rastgele yerleşimdir**: 252 nokta düzleme rastgele serpilir. Beklenen
  değeri $k / (N-1)$'dir ve üç çekiliş ortalanır. Bu taban seçilmiştir çünkü öğrenmeyen bir
  yerleşimin komşuluk korumasını doğrudan verir.
- **BA20.** İkinci karşılaştırma **doğrusal izdüşümdür**: ilk dersin bileşen yönlerinden ilk iki
  ve ilk üçü. Aynı boyut sayısında karşılaştırma yapılabilsin diye iki boyut esas alınır.
- **BA21.** Yerleşim ders içinde yazılır ve **modeldir**: rastgele başlatılır, her adımda komşular
  birbirine çekilir ve rastgele seçilen komşu olmayan noktalar itilir, adım boyu doğrusal olarak
  sıfıra iner. Kitaplık çağrılmaz.
- **BA22.** İtme için nokta başına 10 rastgele örnek çekilir. Bütün çiftler taransaydı maliyet
  nokta sayısının karesiyle büyürdü.
- **BA23.** Ayar bütçesi **altı adaydır**: iki adım sayısı ile üç adım boyu. Denenen adayların
  hepsi basılır.
- **BA24.** Seçim **ölçülen sayının kendisiyle** yapılır ve ayrı bir küme kullanılmaz. Bu, sayının
  bir kısmını ölçütün kendisinden aldığı anlamına gelir; altı adayın aralığı bu yüzden basılır.
- **BA25.** Gömmenin birimi yoktur. Uzaklık karşılaştırmalarında gömme, ortalama ikili uzaklığı
  özgün uzayınkine eşitlenecek biçimde ölçeklenir; yoksa karşılaştırma ölçek seçimini ölçerdi.
- **BA26.** Aynı ayarla iki ayrı başlangıçtan iki koşum yapılır. Başlangıç duyarlılığı ölçünün
  parçasıdır.

#### Komşuluk Korunma Oranı ve Tabanı

Ölçü iki kümenin kesişimidir. Bir nokta için özgün uzaydaki en yakın on komşu bir küme, gömmedeki
en yakın on komşu ikinci bir kümedir; kesişimin büyüklüğü ona bölünür ve bütün noktalar üzerinden
ortalanır. Rastgele bir yerleşimde beklenen değer, on komşunun 251 aday arasından rastgele
seçilmesi kadardır.

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir.
# Etiket sutunu bu kursta hic uretilmez.
import math

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


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort = sum(v) / len(v)
    x = {"hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v),
         "ort_tuketim": round(ort, 2), "oynaklik": round(max(v) - min(v), 2),
         "sifir_okuma": int(min(v) == 0.0), "kisi_basi": round(ort / k["hane"], 3)}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)

ALAN = ["ort_tuketim", "oynaklik", "kisi_basi", "hane", "memnuniyet", "donem",
        "sifir_okuma", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
r, s = uretec(TOHUM + 90000), list(range(len(VERI)))
for i in range(len(s) - 1, 0, -1):
    j = int(r() * (i + 1))
    s[i], s[j] = s[j], s[i]
K = [VERI[i] for i in s]
EGT, SIN = K[:756], K[1008:]

n = len(ALAN)
ORT = {a: sum(x[a] for x in EGT) / len(EGT) for a in ALAN}
SAC = {a: max(1e-9, (sum((x[a] - ORT[a]) ** 2 for x in EGT) / len(EGT)) ** 0.5)
       for a in ALAN}
ZE = [[(x[a] - ORT[a]) / SAC[a] for a in ALAN] for x in EGT]
Z = [[(x[a] - ORT[a]) / SAC[a] for a in ALAN] for x in SIN]
N = len(Z)
```

```python
def carp(m, v):
    return [sum(m[i][j] * v[j] for j in range(len(v))) for i in range(len(m))]


def diklestir(v, B):
    for u in B:
        c = sum(v[i] * u[i] for i in range(len(v)))
        v = [v[i] - c * u[i] for i in range(len(v))]
    return v


def yonleri_bul(Y):
    C = [[sum(z[i] * z[j] for z in Y) / len(Y) for j in range(n)] for i in range(n)]
    B = []
    for _ in range(n):
        v, lam = [1.0 / (i + 1) for i in range(n)], 0.0
        for _ in range(200):
            w = diklestir(carp(C, v), B)
            u = sum(q * q for q in w) ** 0.5
            if u < 1e-9:
                break
            v, lam = [q / u for q in w], u
        if lam < 1e-9:
            break
        B.append(v)
    return B


BILESEN, KOM = yonleri_bul(ZE), 10


def uzaklik(P, a, b):
    return sum((P[a][i] - P[b][i]) ** 2 for i in range(len(P[a]))) ** 0.5


def en_yakinlar(P, k):
    return [{b for _, b in sorted((uzaklik(P, a, b), b)
                                  for b in range(len(P)) if b != a)[:k]}
            for a in range(len(P))]


KOMSU = en_yakinlar(Z, KOM)


def korunma(P, k=KOM):
    q = en_yakinlar(P, k)
    return sum(len(KOMSU[a] & q[a]) for a in range(N)) / (N * k)


def izdusum(d):
    return [[sum(z[j] * BILESEN[i][j] for j in range(n)) for i in range(d)] for z in Z]


def rastgele_yerlesim(tohum, d=2):
    r = uretec(tohum)
    return [[r() * 2 - 1 for _ in range(d)] for _ in range(N)]


rs = sum(korunma(rastgele_yerlesim(TOHUM + 7 * (t + 1))) for t in range(3)) / 3
print(f"nokta {N}, sutun {n}, komsu sayisi {KOM}")
print(f"taban: rastgele yerlesim {rs:.4f} (beklenen {KOM / (N - 1):.4f})")
print(f"dogrusal izdusum 2 boyut {korunma(izdusum(2)):.4f}")
print(f"dogrusal izdusum 3 boyut {korunma(izdusum(3)):.4f}")
```

```
nokta 252, sutun 12, komsu sayisi 10
taban: rastgele yerlesim 0.0390 (beklenen 0.0398)
dogrusal izdusum 2 boyut 0.2639
dogrusal izdusum 3 boyut 0.3849
```

Taban çizgisi 0,0390 ve kuramsal beklentiye, yani 0,0398'e oturuyor. Doğrusal izdüşüm iki boyutta
0,2639 veriyor: tabanın 0,2249 üstünde, ama en yakın on komşunun yalnız yaklaşık dörtte biri. İlk
dersin uzaklık tablosunda iki bileşenin ikili uzaklıkların ortalamasını 0,4965 katına indirdiği
görülmüştü; bu satır aynı kaybın komşuluk cinsinden karşılığıdır. Uzaklıklar orantılı olarak
kısalmıyor, sıralamaları da bozuluyor.

#### Komşuluğu Hedefleyen Bir Yerleşim

Yordam üç parçadır. Her nokta için özgün uzaydaki en yakın on komşu bir kez saptanır. Düzlemde
rastgele bir yerleşim başlatılır. Sonra her adımda iki kuvvet uygulanır: komşular birbirine
çekilir, komşu olmayan rastgele seçilmiş noktalar itilir. İtme kuvveti uzaklıkla azalır, böylece
zaten uzak duran çiftler birbirini sonsuza itmez. Adım boyu doğrusal olarak sıfıra iner.

```python
def gom(tohum, adim, eta, k=KOM, neg=10):
    # MODELDIR: komsuluk koruyan yerlesim kucuk olcekte, standart kitaplikla yazilir.
    r = uretec(tohum)
    Y = [[(r() - 0.5) * 0.1 for _ in range(2)] for _ in range(N)]
    kl = [sorted(KOMSU[a]) for a in range(N)]
    for it in range(adim):
        h = eta * (1 - it / adim)
        for a in range(N):
            gx = gy = 0.0
            for b in kl[a]:
                gx += Y[a][0] - Y[b][0]
                gy += Y[a][1] - Y[b][1]
            for _ in range(neg):
                c = int(r() * N)
                if c == a or c in KOMSU[a]:
                    continue
                dx, dy = Y[a][0] - Y[c][0], Y[a][1] - Y[c][1]
                w = 1.0 / (1.0 + dx * dx + dy * dy)
                gx -= w * dx
                gy -= w * dy
            Y[a][0] -= h * gx / k
            Y[a][1] -= h * gy / k
    return Y


print(f"{'adim':>5} {'adim boyu':>10} {'korunma':>9}")
en = None
for adim in (200, 400):
    for eta in (0.05, 0.10, 0.20):
        d = korunma(gom(TOHUM + 11, adim, eta))
        print(f"{adim:>5} {eta:>10.2f} {d:>9.4f}")
        if en is None or d > en[0]:
            en = (d, adim, eta)
print(f"denenen aday 6, secilen adim {en[1]}, adim boyu {en[2]:.2f}")
GOMME = gom(TOHUM + 11, en[1], en[2])
IKINCI = gom(TOHUM + 22, en[1], en[2])
print(f"gomme {korunma(GOMME):.4f}, ayni ayarla ikinci kosum {korunma(IKINCI):.4f}")
```

```
 adim  adim boyu   korunma
  200       0.05    0.4099
  200       0.10    0.4869
  200       0.20    0.4286
  400       0.05    0.5032
  400       0.10    0.4377
  400       0.20    0.3940
denenen aday 6, secilen adim 400, adim boyu 0.05
gomme 0.5032, ayni ayarla ikinci kosum 0.4806
```

Üç sayı yan yana duruyor. Taban 0,0390, doğrusal izdüşüm 0,2639, gömme 0,5032. Gömmenin doğrusal
izdüşümden farkı 0,2393 ve bu fark tabandan doğrusal izdüşüme olan farkla (0,2249) hemen hemen
aynı büyüklükte. Aynı iki boyutta, komşuluğu hedeflemek komşuluğu neredeyse iki katına çıkarıyor.

Farkın nereden geldiği ayar tablosunda okunur. Altı adayın en iyisi 0,5032, en kötüsü 0,3940. En
kötü aday bile doğrusal izdüşümü 0,1301 geçiyor, dolayısıyla üstünlük ayar seçiminden gelmiyor.
Ama seçilen sayı ölçülen sayının kendisiyle seçildiği için 0,5032 bir üst sınırdır; dürüst aralık
0,3940 ile 0,5032'dir. Başlangıç duyarlılığı da aynı yöne işaret ediyor: aynı ayarla ikinci koşum
0,4806 veriyor. Gömme, kümelemede olduğu gibi, tek bir sayı değil bir sayı aralığı üretir.

#### Gömmedeki Mesafe Neyi Söyler

Komşuluk korunma oranının yüksek olması gömmenin uzaklıkları koruduğu anlamına gelmez. İkisi ayrı
sorulardır ve gömme yalnız birincisine yanıt verir. Bunu ölçmenin iki yolu var: gömme en **uzak**
çiftleri de koruyor mu ve gömmede aynı uzaklıkta duran çiftlerin özgün uzaklıkları ne kadar
dağınık.

```python
def en_uzaklar(P, k):
    return [{b for _, b in sorted((-uzaklik(P, a, b), b)
                                  for b in range(len(P)) if b != a)[:k]}
            for a in range(len(P))]


UZAK = en_uzaklar(Z, KOM)


def ortalama_uzaklik(P):
    return sum(uzaklik(P, a, b) for a in range(N)
               for b in range(a + 1, N)) / (N * (N - 1) / 2)


OZ = ortalama_uzaklik(Z)
print(f"ozgun ortalama ikili uzaklik {OZ:.3f}")
for ad, P in (("dogrusal 2", izdusum(2)), ("gomme", GOMME), ("ikinci kosum", IKINCI)):
    q = en_uzaklar(P, KOM)
    uk = sum(len(UZAK[a] & q[a]) for a in range(N)) / (N * KOM)
    f = OZ / ortalama_uzaklik(P)
    Q = [[y * f for y in p] for p in P]
    b = sorted(uzaklik(Z, a, c) for a in range(N) for c in range(a + 1, N)
               if 0.95 * OZ <= uzaklik(Q, a, c) <= 1.05 * OZ)
    print(f"{ad:<13} uzak korunma {uk:.4f}  bant {len(b):>4} cift  ozgun uzaklik "
          f"{b[0]:.2f}-{b[-1]:.2f}  yuzde 10-90 {b[len(b) // 10]:.2f}-"
          f"{b[len(b) * 9 // 10]:.2f}")
f1, f2 = OZ / ortalama_uzaklik(GOMME), OZ / ortalama_uzaklik(IKINCI)
Q1 = [[y * f1 for y in p] for p in GOMME]
Q2 = [[y * f2 for y in p] for p in IKINCI]
s, c, buyuk = 0.0, 0, 0
for a in range(N):
    for b in range(a + 1, N):
        d1, d2 = uzaklik(Q1, a, b), uzaklik(Q2, a, b)
        o = abs(d1 - d2) / max(d1, d2, 1e-9)
        s, c, buyuk = s + o, c + 1, buyuk + (o > 0.5)
print(f"iki kosum arasinda ayni ciftin bagil farki: ortalama {s / c:.4f}, "
      f"yarisindan cok sapan cift orani {buyuk / c:.4f}")
```

```
ozgun ortalama ikili uzaklik 4.695
dogrusal 2    uzak korunma 0.6012  bant 1899 cift  ozgun uzaklik 2.87-7.20  yuzde 10-90 3.93-5.74
gomme         uzak korunma 0.0071  bant 2085 cift  ozgun uzaklik 3.47-9.46  yuzde 10-90 4.04-6.79
ikinci kosum  uzak korunma 0.0000  bant 5439 cift  ozgun uzaklik 3.39-9.21  yuzde 10-90 3.97-6.01
iki kosum arasinda ayni ciftin bagil farki: ortalama 0.2906, yarisindan cok sapan cift orani 0.2088
```

İlk sütun beklenmedik biçimde keskin. Doğrusal izdüşüm en uzak on çiftin 0,6012'sini koruyor;
gömme 0,0071'ini, ikinci koşum ise hiçbirini. **İki yaklaşım birbirinin tersini koruyor.** Doğrusal
izdüşüm büyük uzaklıkları taşır çünkü en çok değişkenliği taşıyan yönleri seçmiştir ve uzak
çiftleri ayıran şey tam olarak odur; yakın komşuları ise ezer. Gömme yakın komşulukları kurtarır ve
karşılığında uzaklığın büyük ölçeğini tümüyle atar. Gömmede iki nokta uzak duruyorsa bu, özgün
uzayda uzak oldukları anlamına gelmez — yalnız komşu olmadıkları anlamına gelir.

İkinci ölçüm aynı şeyi tek bir aralıkla söylüyor. Ortalama ikili uzaklık eşitlendikten sonra
gömmede 4,695'in yüzde beş çevresinde duran 2.085 çiftin özgün uzaklığı 3,47 ile 9,46 arasında
değişiyor; yüzde 10 ile yüzde 90 arasındaki aralık bile 4,04–6,79. Aynı bantta doğrusal izdüşümün
aralığı 2,87–7,20 ve yüzde 10–90 aralığı 3,93–5,74, yani belirgin biçimde dar. Gömmede iki nokta
arasındaki mesafeye bakıp "şu kadar farklılar" demek, en büyüğü en küçüğünün 2,7 katı olan bir
aralıktan rastgele bir sayı okumaktır.

Son satır kararı kapatıyor. Aynı ayarla yalnız başlangıcı değiştirmek, aynı çiftin gömülmüş
uzaklığını ortalama 0,2906 bağıl farkla oynatıyor ve çiftlerin 0,2088'inde fark yarıdan büyük.
Gömmenin koordinatları da, koordinatlar arasındaki uzaklık da koşuma bağlıdır. Bir gömmede
raporlanabilecek tek sayı komşuluk korunma oranıdır; eksenler, yönler ve mesafeler
raporlanamaz.

#### Özet

- Komşuluk korunma oranının tabanı rastgele yerleşimdir ve 0,0390'dır; kuramsal beklenti 0,0398 ile
  buna oturur.
- Doğrusal izdüşüm iki boyutta 0,2639, komşuluğu hedefleyen gömme 0,5032 verir; fark 0,2393'tür ve
  altı adayın en kötüsü olan 0,3940 bile doğrusal izdüşümü geçtiği için ayar seçiminden gelmez.
- Sayı bir aralıktır: ayar aralığı 0,3940–0,5032, aynı ayarla ikinci koşum 0,4806.
- İki yaklaşım birbirinin tersini korur: doğrusal izdüşüm en uzak çiftlerin 0,6012'sini, gömme
  0,0071'ini korur.
- Gömme uzaklık koruma iddiası taşımaz: ortalama uzaklık eşitlendikten sonra gömmede aynı bantta
  duran çiftlerin özgün uzaklığı 3,47 ile 9,46 arasında değişir ve ikinci koşumda aynı çiftin
  uzaklığı ortalama 0,2906 bağıl farkla oynar.

#### Sonraki Adım

Üç derste noktalar daha az sayıya indirildi ve her seferinde ne kaybedildiği ölçüldü: geri kurma
hatası, saklanan sayı adedi, komşuluk korunma oranı. Üç ölçünün de ortak bir yanı vardı — hepsi
**bütün noktalar üzerinden** ortalandı. Bir izdüşüm 252 noktanın ortalamasında iyiyse iyi sayıldı,
bir gömme komşulukların yarısını koruyorsa yarısını koruyor sayıldı. Ortalamanın altında kalan tek
tek noktalara, örneğin geri kurma hatası ötekilerin on katı olan bir aboneye hiç bakılmadı. Oysa
böyle bir noktanın kendisi bir bulgu olabilir: tabloya uymayan, tek başına duran bir kayıt. Sonraki
ders bu soruyu ölçü hâline getirir — tek başına duran nokta nasıl işaretlenir ve işaretlenenin
kaçı gerçekten öyledir.

### Anomali Tespiti

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/anomali-tespiti

Kurgu abone tablosunun 1.260 satırına bilerek yerleştirilmiş 63 anomali üzerinde işaretlemenin ölçülmesi: rastgele işaretleme yüzde 5'lik oranda 0,0488 kesinlik verirken tek sütuna göre işaretleme 0,3968, her sütuna tek tek bakan yordam 0,6190, dört bileşenle geri kurma hatası 0,4921 verir. Kesinlik sıralaması yanıltıcıdır — en yüksek kesinlikli üç işaretleyici hiçbir sütunda uca gitmeyen 24 çok değişkenli anomalinin sıfırını bulurken, en düşük kesinlikli olan on beşini bulur ve işaretleme oranı yüzde 10'a çıkınca yirmi dördünü de bulur. Oran yüzde 2'de üç işaretleyici 1,0000 kesinlik verir ve aynı anda 63 anomalinin yalnız 25'ini yakalar.

Bu konunun ilk üç dersi aynı işi üç ayrı yoldan yaptı: noktalar daha az sayıya indirildi ve her
seferinde ne kaybedildiği ölçüldü — geri kurma hatası, rankın bedeli, komşuluk korunma oranı. Üç
ölçü de **bütün noktalar birlikte** ele alınarak okundu. Tek başına duran bir noktanın ne olduğu,
hatta böyle bir noktanın var olup olmadığı sorulmadı.

Anomali tespiti bu soruyu sorar ve etiketsiz bir problem olarak sorar: hiçbir sütun bir satırın
anomali olduğunu söylemez, dolayısıyla "anomali" tanımı yöntemin kendisinden gelir. Böyle bir
kurulumda hangi işaretlemenin iyi olduğunu söylemenin tek yolu, işaretlenenlerin **kaçının gerçek
olduğunu** sayabilmektir. Bu ders bunu, anomalileri kurgu kümeye **bilerek yerleştirerek** yapar.

- **BA31.** Küme M27/K01–K03'ün **kurgu** abone tablosudur, tohum 20260218, 1.260 satır ve beş
  sayısal sütun. Etiket sütunu bu kursta okunmaz ve burada da hesaplanmaz.
- **BA32.** Anomaliler tabloya **bilerek yerleştirilmiştir**. Hangi satırın yerleştirildiği yalnız
  **ölçüm için** bilinir; hiçbir işaretleyici bu listeyi görmez. Kurgunun yeri burasıdır: gerçek
  bir kümede bu liste yoktur ve bu dersin sayıları orada hesaplanamaz.
- **BA33.** `anomali` ile `aykırı değer` ayrı tutulur. Aykırı değer M26/K03'ün terimidir ve **bir
  sütundaki tek bir değerin** uçta durmasını anlatır. Anomali burada **satırın bütününün** kümeye
  uymamasıdır; hiçbir değeri uçta olmayan bir satır anomali olabilir.
- **BA34.** Taban çizgisi M26/K03'ün çeyrekler açıklığı, çit ve standart puan yordamıdır. Yordam
  orada kuruldu, burada **tekrarlanmaz**; sabit sayıda satır işaretlemek gerektiği için çit yerine
  aynı yordamın sürekli biçimi, standart puanın büyüklüğü, sıralama ölçütü olarak çağrılır.
- **BA35.** **İşaretleme oranı** bir ayar değişkenidir: kaç satırın anomali sayılacağı veriden
  çıkmaz. Süpürülür ve her oranın sayısı ayrı yazılır.

#### Yerleştirilmiş Anomali

```python
# ORTAK — KURGUDUR. M27/K01-K03'un abone tablosu ayni tohumla yeniden kurulur;
# etiket sutunu bu kursta hic okunmaz. Anomaliler tabloya BILEREK yerlestirilir.
import math

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


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if v:
        VERI.append({"no": k["no"], "hane": k["hane"], "memnuniyet": k["memnuniyet"],
                     "donem": len(v), "ort_tuketim": round(sum(v) / len(v), 2),
                     "oynaklik": round(max(v) - min(v), 2)})

# Yerlestirme. Uc tur: A ve B tek sutunda uca tasinir, C hicbir sutunda uca
# gitmeden sutunlar arasi iliskiyi bozar (uc okuma, aralarinda neredeyse fark yok).
r, alinan, YER = uretec(TOHUM + 31000), set(), {}


def sec(kosul, adet):
    s = [i for i in range(len(VERI)) if kosul(VERI[i]) and i not in alinan]
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    alinan.update(s[:adet])
    return s[:adet]


for i in sec(lambda x: 12 <= x["ort_tuketim"] <= 30 and x["donem"] >= 2, 24):
    VERI[i]["ort_tuketim"], YER[i] = round(VERI[i]["ort_tuketim"] * 5.0, 2), "A"
for i in sec(lambda x: x["donem"] == 3 and 12 <= x["oynaklik"] <= 20, 15):
    VERI[i]["oynaklik"], YER[i] = round(VERI[i]["oynaklik"] * 5.0, 2), "B"
for i in sec(lambda x: x["donem"] == 3 and 22 <= x["ort_tuketim"] <= 33, 24):
    VERI[i]["oynaklik"], YER[i] = round(VERI[i]["ort_tuketim"] * 0.02, 2), "C"

ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem"]
n, p = len(VERI), len(ALAN)
ORT = [sum(x[a] for x in VERI) / n for a in ALAN]
SAP = [math.sqrt(sum((x[a] - ORT[j]) ** 2 for x in VERI) / n)
       for j, a in enumerate(ALAN)]
P = [[(x[a] - ORT[j]) / SAP[j] for j, a in enumerate(ALAN)] for x in VERI]

print(f"kurgu tablo {n} satir, {p} sutun; yerlestirilen anomali {len(YER)} "
      f"({len(YER) / n:.4f})")
for t, ad in (("A", "olcek kaymasi"), ("B", "oynaklik sicramasi"),
              ("C", "duragan yuksek okuma")):
    g = [i for i in YER if YER[i] == t]
    print(f"  tur {t} {ad:<21} {len(g):>3} satir  "
          f"ort_tuketim {min(VERI[i]['ort_tuketim'] for i in g):>6.2f}-"
          f"{max(VERI[i]['ort_tuketim'] for i in g):<6.2f} "
          f"oynaklik {min(VERI[i]['oynaklik'] for i in g):>5.2f}-"
          f"{max(VERI[i]['oynaklik'] for i in g):.2f}")
print(f"  yerlestirilmemis satirlarda ort_tuketim en cok "
      f"{max(VERI[i]['ort_tuketim'] for i in range(n) if i not in YER):.2f}, "
      f"oynaklik en cok "
      f"{max(VERI[i]['oynaklik'] for i in range(n) if i not in YER):.2f}")
```

```
kurgu tablo 1260 satir, 5 sutun; yerlestirilen anomali 63 (0.0500)
  tur A olcek kaymasi          24 satir  ort_tuketim  62.45-144.40 oynaklik  1.71-23.22
  tur B oynaklik sicramasi     15 satir  ort_tuketim   9.50-35.29  oynaklik 61.10-98.55
  tur C duragan yuksek okuma   24 satir  ort_tuketim  22.09-31.68  oynaklik  0.44-0.63
  yerlestirilmemis satirlarda ort_tuketim en cok 43.48, oynaklik en cok 51.15
```

Üç tür arasındaki fark son satırda duruyor. A türünün tüketimi 62,45 ile 144,40 arasında,
yerleştirilmemişlerin en büyüğü 43,48; B türünün oynaklığı 61,10 ile 98,55, yerleştirilmemişlerin
en büyüğü 51,15. İkisi de **bir sütunda uçta** duruyor ve M26/K03'ün terimiyle o sütunda birer
aykırı değer taşıyor.

C türü hiçbir sütunda uca gitmiyor. Tüketimi 22,09 ile 31,68 arasında, kümenin üst çeyreğinde ama
sınırın hayli içinde. Oynaklığı 0,44 ile 0,63 arasında ve kümede oynaklığı 1'in altında 187 satır
var, çünkü tek okuması olan abonede oynaklık tanımı gereği sıfırdır. Anomali **değerlerde değil,
değerlerin birlikteliğinde**: üç dönemde okunmuş, tüketimi yüksek ve üç okuması arasında neredeyse
hiç fark olmayan bir abone.

#### Beş İşaretleyici, Üç Taban

```python
# Bes isaretleyici. Ilk ucu taban cizgisi, son ikisi cok degiskenli.
def bilesenler(k, tohum):
    # Esdegisim dizeyi ve ilk k ozvektor: guc yinelemesi ve ayirma.
    # Bilesenin kendisi bu konunun ilk iki dersinde kuruldu, burada girdidir.
    C = [[sum(P[i][a] * P[i][b] for i in range(n)) / n for b in range(p)]
         for a in range(p)]
    rr, cikan = uretec(tohum), []
    for _ in range(k):
        v = [rr() - 0.5 for _ in range(p)]
        for _ in range(300):
            w = [sum(C[a][b] * v[b] for b in range(p)) for a in range(p)]
            u = math.sqrt(sum(x * x for x in w)) or 1.0
            v = [x / u for x in w]
        lam = sum(v[a] * sum(C[a][b] * v[b] for b in range(p)) for a in range(p))
        cikan.append(v)
        C = [[C[a][b] - lam * v[a] * v[b] for b in range(p)] for a in range(p)]
    return cikan


def geri_kurma(k):
    V = bilesenler(k, TOHUM + 31500)
    s = []
    for i in range(n):
        pr = [sum(P[i][a] * v[a] for a in range(p)) for v in V]
        gk = [sum(pr[j] * V[j][a] for j in range(k)) for a in range(p)]
        s.append(math.sqrt(sum((P[i][a] - gk[a]) ** 2 for a in range(p))))
    return s


UZAK = []
for i in range(n):
    d = sorted(sum((P[i][a] - P[j][a]) ** 2 for a in range(p))
               for j in range(n) if j != i)
    UZAK.append(math.sqrt(d[9]))          # 10. komsunun uzakligi

SKOR = [("rastgele", "rastgele isaretleme", None),
        ("tek sutun", "tek sutuna gore: ort_tuketim", [abs(P[i][0]) for i in range(n)]),
        ("sutun sutun", "her sutuna tek tek: en buyuk standart puan",
         [max(abs(P[i][a]) for a in range(p)) for i in range(n)]),
        ("komsu", "10. komsunun uzakligi", UZAK),
        ("geri kurma 2", "iki bilesenle geri kurma hatasi", geri_kurma(2)),
        ("geri kurma 4", "dort bilesenle geri kurma hatasi", geri_kurma(4))]


def isaretle(skor, m, tohum=0):
    if skor is None:
        rr, s = uretec(tohum), list(range(n))
        for i in range(n - 1, 0, -1):
            j = int(rr() * (i + 1))
            s[i], s[j] = s[j], s[i]
        return s[:m]
    return sorted(range(n), key=lambda i: (-skor[i], i))[:m]


M = 63
print(f"isaretleme orani 0,0500 -> {M} satir isaretleniyor; yerlestirilen 63\n")
print(f"{'isaretleyici':<42} {'gercek':>7} {'kesinlik':>9} "
      f"{'A':>4} {'B':>4} {'C':>4}")
for _, ad, skor in SKOR:
    if skor is None:
        d = [sum(1 for i in isaretle(None, M, TOHUM + 32000 + t) if i in YER)
             for t in range(200)]
        g = sum(d) / len(d)
        print(f"{ad:<42} {g:>7.2f} {g / M:>9.4f} {'':>4} {'':>4} {'':>4}")
        continue
    s = isaretle(skor, M)
    say = {t: sum(1 for i in s if YER.get(i) == t) for t in "ABC"}
    print(f"{ad:<42} {sum(say.values()):>7} {sum(say.values()) / M:>9.4f} "
          f"{say['A']:>4} {say['B']:>4} {say['C']:>4}")
```

```
isaretleme orani 0,0500 -> 63 satir isaretleniyor; yerlestirilen 63

isaretleyici                                gercek  kesinlik    A    B    C
rastgele isaretleme                           3.08    0.0488               
tek sutuna gore: ort_tuketim                    25    0.3968   24    1    0
her sutuna tek tek: en buyuk standart puan      39    0.6190   24   15    0
10. komsunun uzakligi                           39    0.6190   24   15    0
iki bilesenle geri kurma hatasi                 40    0.6349   23   15    2
dort bilesenle geri kurma hatasi                31    0.4921    1   15   15
```

Taban çizgisi ilk satırda: 63 satır rastgele işaretlendiğinde 200 çekilişin ortalaması 3,08 gerçek
anomali, kesinlik **0,0488** — işaretleme oranının kendisi. Bu sayının yanında kalan hiçbir
işaretleyici bir şey bulmuş sayılmaz.

İkinci taban tek bir sütuna bakıyor ve 0,3968 veriyor — rastgelenin sekiz katı. Ne bulduğu üçüncü
sütunda: A türünün **24'ünü de** yakalamış, B türünden yalnız birini, C türünden hiçbirini. Üçüncü
taban aynı yordamı beş sütuna tek tek uyguluyor ve en büyük standart puana göre sıralıyor; kesinlik
0,6190'a çıkıyor ve eksik kalan B türü de tamamlanıyor. C türü hâlâ sıfır.

Son üç satır çok değişkenli. Onuncu komşunun uzaklığı sütun sütun bakan tabanla **aynı** sayıyı
veriyor: 39 gerçek, aynı dağılım. İki bileşenle geri kurma hatası biraz daha iyi, 0,6349. Dört
bileşenle geri kurma hatası ise kesinlikte hepsinden geride, **0,4921** — ve C türünün on beşini
tek başına buluyor.

Kesinlik sıralaması bu tabloda yanıltıcıdır. En yüksek kesinlikli üç işaretleyici, hiçbir sütunda
uca gitmeyen 24 anomalinin **sıfırını** görüyor; en düşük kesinlikli olan on beşini görüyor. Fark
yöntemin duyarlılığından değil, **neye baktığından** geliyor. A türü kümenin en güçlü bileşeni
boyunca uzağa gitmiştir ve dört bileşen onu neredeyse tam geri kurar, artığı sıfıra yakındır. C
türü ise hiçbir bileşen boyunca uzakta değildir; **bileşenlerin dışında kalan yönde** durur, çünkü
bozduğu şey bir değer değil, dönem sayısı ile oynaklık ve tüketim arasındaki ilişkidir. Aynı
nedenle dört bileşenle geri kurma A türünü kaçırır: aradığı artık, A'nın hiç ürettiği bir artık
değildir.

#### İşaretleme Oranı Süpürülünce

```python
# Isaretleme orani bir ayar degiskenidir: kac satirin anomali sayilacagi
# veriden degil karardan cikar. Bes oran suprulur.
ORANLAR = [0.02, 0.05, 0.10, 0.15, 0.20]
BASLIK = f"{'oran':>5} {'isaret':>7} " + " ".join(f"{a:>13}" for a, _, _ in SKOR)
print("kesinlik: isaretlenenin kaci yerlestirilmis anomali\n" + BASLIK)
for o in ORANLAR:
    m, satir = int(round(o * n)), []
    for _, _, skor in SKOR:
        if skor is None:
            d = [sum(1 for i in isaretle(None, m, TOHUM + 32000 + t) if i in YER)
                 for t in range(200)]
            satir.append(sum(d) / len(d) / m)
        else:
            satir.append(sum(1 for i in isaretle(skor, m) if i in YER) / m)
    print(f"{o:>5.2f} {m:>7} " + " ".join(f"{x:>13.4f}" for x in satir))

print("\nyakalama: 63 anomalinin kaci / bunlarin 24 C turunun kaci\n" + BASLIK)
for o in ORANLAR:
    m, satir = int(round(o * n)), []
    for _, _, skor in SKOR:
        s = isaretle(skor, m, TOHUM + 32000) if skor is None else isaretle(skor, m)
        satir.append(f"{sum(1 for i in s if i in YER)}/"
                     f"{sum(1 for i in s if YER.get(i) == 'C')}")
    print(f"{o:>5.2f} {m:>7} " + " ".join(f"{x:>13}" for x in satir))
```

```
kesinlik: isaretlenenin kaci yerlestirilmis anomali
 oran  isaret      rastgele     tek sutun   sutun sutun         komsu  geri kurma 2  geri kurma 4
 0.02      25        0.0506        0.9600        1.0000        1.0000        1.0000        0.6000
 0.05      63        0.0488        0.3968        0.6190        0.6190        0.6349        0.4921
 0.10     126        0.0496        0.2143        0.3175        0.3175        0.3333        0.3333
 0.15     189        0.0501        0.1534        0.2116        0.2169        0.2275        0.2328
 0.20     252        0.0501        0.1230        0.1587        0.1706        0.1706        0.1865

yakalama: 63 anomalinin kaci / bunlarin 24 C turunun kaci
 oran  isaret      rastgele     tek sutun   sutun sutun         komsu  geri kurma 2  geri kurma 4
 0.02      25           1/1          24/0          25/0          25/0          25/0          15/0
 0.05      63           2/1          25/0          39/0          39/0          40/2         31/15
 0.10     126           3/1          27/0          40/1          40/1          42/3         42/24
 0.15     189           6/3          29/1          40/1          41/2          43/4         44/24
 0.20     252           7/3          31/2          40/1          43/4          43/4         47/24
```

İlk satır kesinliğin ne kadar kolay büyütüldüğünü gösteriyor. Oran yüzde 2'ye çekilince üç
işaretleyici **1,0000** veriyor: işaretlenen 25 satırın 25'i de gerçek. İkinci tabloda aynı satır
tersini söylüyor — 63 anomalinin yalnız 25'i bulunmuş, C türünden hiçbiri. Kusursuz kesinlik dar
bir eşiğin ürünüdür ve 38 anomaliyi hiç görmemekle satın alınmıştır.

Oran büyüdükçe her işaretleyicinin kesinliği düşüyor, çünkü işaretlenen satır sayısı yerleştirilen
anomali sayısını geçiyor ve fazlası zorunlu olarak yanlış oluyor. Tek sabit kalan **rastgele
işaretlemedir**: 0,0506, 0,0488, 0,0496, 0,0501, 0,0501. Taban çizgisi orandan bağımsızdır ve
kesinlik ancak bu sayının yanında okunabilir.

İkinci tablo tek değişkenli yordamların sınırını sayıyor. Tek sütuna göre işaretleme yakalanan
anomaliyi yüzde 20'lik oranda bile ancak 24'ten 31'e taşıyor; sütun sütun bakan yordam 40'ta
duruyor ve yüzde 10'dan sonra hiç ilerlemiyor. Bu iki yordamda oranı büyütmek görülmeyeni
göstermiyor, yalnız yanlış işaretlenen satırı çoğaltıyor. Dört bileşenle geri kurma hatası ise
yüzde 10'da C türünün **24'ünü de** bulmuş oluyor.

Yüzde 20 oranında en iyi kesinlik 0,1865'tir: 252 satır işaretlenmiş, 47'si gerçek. Geri kalan 205
satır incelenmek üzere ayrılmış ve hiçbirinde yerleştirilmiş bir anomali yok. İşaretleme oranını
seçmek, bu 205 satırın bedelini kabul etmektir; ölçüt oranı seçmez, oran ölçütün sayısını belirler.

#### Özet

- Kurgu tabloya bilerek yerleştirilen 63 anomalinin 24'ü hiçbir sütunda uca gitmez: anomali
  değerlerde değil, değerlerin birlikteliğindedir.
- Yüzde 5'lik işaretleme oranında taban 0,0488'dir; tek sütuna göre işaretleme 0,3968, her sütuna
  tek tek bakan yordam 0,6190, onuncu komşunun uzaklığı 0,6190, dört bileşenle geri kurma hatası
  0,4921 verir.
- Kesinlik sıralaması yanıltıcıdır: en yüksek kesinlikli üç işaretleyici çok değişkenli 24
  anomalinin sıfırını, en düşük kesinlikli olan on beşini bulur; yüzde 10 oranında yirmi dördünü de
  bulur.
- Oran yüzde 2'ye çekilince üç işaretleyici 1,0000 kesinlik verir ve aynı anda 63 anomalinin yalnız
  25'ini yakalar; kusursuz kesinlik dar eşiğin ürünüdür.
- Rastgele işaretlemenin kesinliği bütün oranlarda 0,0488 ile 0,0506 arasında kalır; taban orandan
  bağımsızdır ve öbür sayılar ancak onun yanında okunur.

#### Sonraki Adım

Bu derste satır beş sayısal sütunla ele alındı ve bir noktanın tek başına durup durmadığı ölçüldü.
Abone kayıtlarının sayı olmayan bir yanı daha var: hangi olayların **birlikte** geçtiği. İki olayın
aynı abonede birlikte görülmesi bir kural mıdır, yoksa ikisinin ayrı ayrı ne kadar sık olduğuyla
zaten açıklanabilir mi. Sonraki ders birliktelik kurallarını kurar, yüksek güvenli kuralların
kaçının yalnız sık öğeden geldiğini sayar ve tabanı rastgele eşleşmenin ürettiği destekten okur.

### Birliktelik Kuralları

Source: https://academia.sh/tr/kurslar/denetimsiz-ogrenme/birliktelik-kurallari

Kurgu abone tablosunun 1.329 sepetinde destek tabanını geçen 166 kuralın ölçülmesi: güvene göre ilk altı kuralın altısı da aynı sık öğeyi sonuç gösterir ve kaldıraçları 1,0139 ile 1,0489 arasındadır; güveni 0,50'nin üstündeki 97 kuralın 75'inin kaldıracı 1'e 0,05'ten yakındır. Taban çizgisi rastgele eşleşmenin ürettiği destektir — öğe sütunları ayrı ayrı karıştırıldığında gecikme ile taksitlendirmenin 0,1279 desteği altmış karıştırmanın hiçbirinde 0,0467'yi aşmaz, ama güveni 0,9246 olan bir kuralın desteği rastgele aralığın içinde kalır. Kurs kapanışı on bir dersin tabanını, yöntemin sayısını ve farkını tek tabloda toplar.

Önceki ders bir satırın tek başına durup durmadığını sordu ve yanıtı beş sayısal sütunda aradı.
Abone kayıtlarının sayı olmayan bir yanı daha var: dönem içinde o abonede **hangi olayların
birlikte** geçtiği. Soru biçimini koruyor: iki olayın birlikte görülmesi bir bulgu mudur, yoksa
ikisinin ayrı ayrı ne kadar sık olduğuyla zaten açıklanabilir mi.

Birliktelik kuralları bu soruyu üç ölçütle yanıtlar ve biri tek başına yanıltır. Dersin sayısı,
**güvene göre seçilen kuralların kaçının yalnız sık öğeden geldiğidir**; tabanı, öğeler birbirine
**rastgele eşlendiğinde** çıkan destektir.

- **BA36.** Küme M27/K01–K03'ün **kurgu** abone tablosudur, tohum 20260218, 1.329 abone. Her abone
  bir **öğe kümesi** olarak okunur: dönem içinde o abonede geçen hizmet kayıtları.
- **BA37.** Bağımlılıklar üretece **bilerek** konmuştur ve hangisinin gerçek olduğu yalnız ölçüm
  için bilinir; hiçbir ölçüt bu bilgiyi görmez.
- **BA38.** Üç ölçüt: **destek**, öğe kümesinin sepetlerdeki payı; **güven**, öncül geçtiğinde
  sonucun da geçme payı; **kaldıraç**, güvenin sonucun desteğine bölümü. Kaldıraç **1'den sapma**
  olarak okunur; 1 birlikteliğin yokluğudur.
- **BA39.** Taban çizgisi **rastgele eşleşmenin ürettiği destektir**: her öğenin sütunu sepetler
  arasında ayrı karıştırılır, sıklıklar korunur, birlikte geçme bağı kopar.
- **BA40.** Bu ders yalnız **kural çıkarmanın ölçütlerini** kurar; hangi kuralın kime gösterileceği
  M27/K07'ye bırakılır.

#### Sepetler ve Öğe Destekleri

```python
# ORTAK — KURGUDUR. M27/K01-K03'un abone tablosu ayni tohumla kurulur; bu kez her
# abonenin donem icindeki hizmet kayitlari bir OGE KUMESI olarak tutulur.
# Bagimliliklar uretece BILEREK konmustur ve yalniz olcum icin bilinir.
TOHUM, HAM, M32 = 20260218, 1400, 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


ABONE = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    r(), r(), r()          # bolge, hane ve memnuniyet bu derste okunmuyor
    if r() >= 0.046:
        ABONE.append(10001 + i)

# (oge, kosul ogesi, kosul saglaninca olasilik, saglanmayinca olasilik)
TABLO = [("mobil_bildirim", None, 0.91, 0.91),        # bagimsiz, cok sik
         ("kesinti_bildirimi", None, 0.55, 0.55),     # bagimsiz, cok sik
         ("gecikme", None, 0.22, 0.22),
         ("taksitlendirme", "gecikme", 0.58, 0.04),   # yerlestirilmis bagimlilik
         ("otomatik_odeme", "gecikme", 0.13, 0.47),   # yerlestirilmis ters bagimlilik
         ("itiraz", None, 0.16, 0.16),
         ("cagri_merkezi", "itiraz", 0.90, 0.58),     # zayif bagimlilik, sik oge
         ("adres_degisikligi", None, 0.08, 0.08),
         ("sayac_degisimi", "adres_degisikligi", 0.36, 0.09),
         ("saha_ziyareti", "sayac_degisimi", 0.86, 0.19),
         ("tarife_degisikligi", None, 0.12, 0.12),
         ("kacak_ihbari", None, 0.05, 0.05)]
OGELER = [t[0] for t in TABLO]

SEPET = []
for no in ABONE:
    r, s = uretec(TOHUM + 37000 + no), set()
    for oge, kosul, p1, p0 in TABLO:
        if r() < (p1 if kosul is None or kosul in s else p0):
            s.add(oge)
    SEPET.append(s)

N = len(SEPET)
D1 = {o: sum(1 for s in SEPET if o in s) / N for o in OGELER}
print(f"kurgu: {N} abone sepeti, {len(OGELER)} oge; "
      f"sepet basina ortalama {sum(len(s) for s in SEPET) / N:.2f} oge")
for o in sorted(OGELER, key=lambda x: -D1[x]):
    print(f"  {o:<20} destek {D1[o]:.4f}")
```

```
kurgu: 1329 abone sepeti, 12 oge; sepet basina ortalama 3.68 oge
  mobil_bildirim       destek 0.9120
  cagri_merkezi        destek 0.6388
  kesinti_bildirimi    destek 0.5403
  otomatik_odeme       destek 0.3973
  saha_ziyareti        destek 0.2671
  gecikme              destek 0.2220
  taksitlendirme       destek 0.1670
  itiraz               destek 0.1497
  sayac_degisimi       destek 0.1332
  tarife_degisikligi   destek 0.1151
  adres_degisikligi    destek 0.0835
  kacak_ihbari         destek 0.0512
```

Destekler geniş bir aralığa yayılıyor: en sık öğe sepetlerin 0,9120'sinde, en seyreği 0,0512'sinde.
Bu dağılım sonraki bölümün tuzağını kurar.

#### Güven Tek Başına

```python
# Kural cikarma: onculde bir ya da iki oge, sonucta bir oge. Destek tabani
# bir karardir ve olcumden once sabitlenir.
from itertools import combinations

DESTEK_TABANI, GUVEN_TABANI = 0.05, 0.50


def destek(kume):
    return sum(1 for s in SEPET if kume <= s) / N


KURAL = []
onculler = [frozenset([o]) for o in OGELER] + \
           [frozenset(c) for c in combinations(OGELER, 2)]
for X in onculler:
    dx = destek(X)
    if dx < DESTEK_TABANI:
        continue
    for y in OGELER:
        if y in X:
            continue
        d = destek(X | {y})
        if d < DESTEK_TABANI:
            continue
        g = d / dx
        KURAL.append({"X": X, "y": y, "destek": d, "guven": g,
                      "kaldirac": g / D1[y]})

BIR = sum(1 for k in KURAL if len(k["X"]) == 1)
yakin = [k for k in KURAL if abs(k["kaldirac"] - 1) <= 0.05]
print(f"destek tabani {DESTEK_TABANI:.2f} ve uzerinde {len(KURAL)} kural "
      f"({BIR} tek ogeli oncul, {len(KURAL) - BIR} iki ogeli)")
print(f"kaldiraci 1'e 0,05'ten yakin kural: {len(yakin)} "
      f"({len(yakin) / len(KURAL):.4f})\n")


def yaz(baslik, sirali, adet=8):
    print(baslik)
    print(f"  {'kural':<46} {'destek':>7} {'guven':>7} {'kaldirac':>9}")
    for k in sirali[:adet]:
        ad = " + ".join(sorted(k["X"])) + " -> " + k["y"]
        print(f"  {ad:<46} {k['destek']:>7.4f} {k['guven']:>7.4f} "
              f"{k['kaldirac']:>9.4f}")


yuksek = sorted(KURAL, key=lambda k: (-k["guven"], sorted(k["X"]), k["y"]))
yaz("guvene gore ilk alti kural", yuksek, 6)
ilk = [k for k in yuksek if k["guven"] >= GUVEN_TABANI]
sik = sum(1 for k in ilk if abs(k["kaldirac"] - 1) <= 0.05)
print(f"\nguveni {GUVEN_TABANI:.2f} ve uzerinde {len(ilk)} kural var; "
      f"bunlarin {sik} tanesinin kaldiraci 1'e 0,05'ten yakin ({sik / len(ilk):.4f})")
son = {}
for k in ilk:
    son[k["y"]] = son.get(k["y"], 0) + 1
print("  sonuc ogesine gore: " + ", ".join(
    f"{o} {son[o]} (destek {D1[o]:.4f})" for o in sorted(son, key=lambda x: -son[x])))

tek = [k for k in KURAL if len(k["X"]) == 1]
yaz("\nkaldirac'a gore ilk dort kural (tek ogeli oncul)",
    sorted(tek, key=lambda k: (-k["kaldirac"], sorted(k["X"]), k["y"])), 4)
en_dusuk = min(tek, key=lambda k: (k["kaldirac"], sorted(k["X"]), k["y"]))
print(f"  en dusuk kaldiracli kural: {' + '.join(sorted(en_dusuk['X']))} -> "
      f"{en_dusuk['y']}, destek {en_dusuk['destek']:.4f}, "
      f"guven {en_dusuk['guven']:.4f}, kaldirac {en_dusuk['kaldirac']:.4f}")
```

```
destek tabani 0.05 ve uzerinde 166 kural (64 tek ogeli oncul, 102 iki ogeli)
kaldiraci 1'e 0,05'ten yakin kural: 115 (0.6928)

guvene gore ilk alti kural
  kural                                           destek   guven  kaldirac
  kesinti_bildirimi + tarife_degisikligi -> mobil_bildirim  0.0662  0.9565    1.0489
  tarife_degisikligi -> mobil_bildirim            0.1076  0.9346    1.0249
  itiraz + otomatik_odeme -> mobil_bildirim       0.0602  0.9302    1.0200
  cagri_merkezi + tarife_degisikligi -> mobil_bildirim  0.0700  0.9300    1.0198
  adres_degisikligi -> mobil_bildirim             0.0775  0.9279    1.0175
  itiraz -> mobil_bildirim                        0.1384  0.9246    1.0139

guveni 0.50 ve uzerinde 97 kural var; bunlarin 75 tanesinin kaldiraci 1'e 0,05'ten yakin (0.7732)
  sonuc ogesine gore: mobil_bildirim 32 (destek 0.9120), cagri_merkezi 28 (destek 0.6388), kesinti_bildirimi 25 (destek 0.5403), saha_ziyareti 4 (destek 0.2671), gecikme 4 (destek 0.2220), taksitlendirme 4 (destek 0.1670)

kaldirac'a gore ilk dort kural (tek ogeli oncul)
  kural                                           destek   guven  kaldirac
  taksitlendirme -> gecikme                       0.1279  0.7658    3.4498
  gecikme -> taksitlendirme                       0.1279  0.5763    3.4498
  saha_ziyareti -> sayac_degisimi                 0.1159  0.4338    3.2572
  sayac_degisimi -> saha_ziyareti                 0.1159  0.8701    3.2572
  en dusuk kaldiracli kural: kesinti_bildirimi -> saha_ziyareti, destek 0.1362, guven 0.2521, kaldirac 0.9437
```

Güvene göre ilk altı kuralın **altısının da sonucu aynı öğe**: sepetlerin 0,9120'sinde geçen
kayıt. Güvenleri 0,9246–0,9565, öncülleri birbiriyle ilgisiz. **Hangi öncül seçilirse seçilsin**
güven 0,91 dolayına oturur; kaldıraç sütunu bunu söylüyor, 1,0139 ile 1,0489 arasında.

Güveni 0,50'nin üstünde 97 kural var ve **75'inin** kaldıracı 1'e 0,05'ten yakın: 0,7732. Sonuç
öğesine göre 32, 28 ve 25 kural en sık üç öğeyi gösteriyor — 97'nin 85'i. Eşiği yükseltmek bunu
düzeltmez; eşik yükseldikçe onu **yalnız sık öğeler** geçebilir.

Kaldıraca göre sıralama başka bir liste veriyor: ilk dörtte iki çift, her çift iki yönde —
taksitlendirme ile gecikme 3,4498, sayaç değişimi ile saha ziyareti 3,2572. Aynı satırlarda güvenin
**bakışımsız**, kaldıracın **bakışımlı** olduğu okunuyor: gecikmeden taksitlendirmeye güven 0,5763,
ters yönde 0,7658, kaldıraç ikisinde de aynı. En düşük kaldıraç 0,9437; 1'in altına düşen bir
kaldıraç birlikte geçmenin rastlantıdan **daha seyrek** olduğunu söyler.

#### Rastgele Eşleşme Tabanı

```python
# TABAN CIZGISI: rastgele eslesmenin urettigi destek. Her ogenin sutunu
# sepetler arasinda ayri ayri karistirilir; oge sikliklari birebir korunur,
# birlikte gecme bagi kopar. Kaldirac bu tabanin kapali bicimidir.
BIT = {o: 1 << b for b, o in enumerate(OGELER)}
SUTUN = [[1 if o in s else 0 for s in SEPET] for o in OGELER]
TEKRAR = 60


def ustkume_toplami(maskeler):
    # Her desen icin o deseni kapsayan sepet sayisi: 12 bitlik tek gecis.
    h = [0] * (1 << len(OGELER))
    for m in maskeler:
        h[m] += 1
    for b in range(len(OGELER)):
        for m in range(len(h)):
            if not m & (1 << b):
                h[m] += h[m | (1 << b)]
    return h


def karistirilmis(tohum):
    rr, yeni = uretec(tohum), [0] * N
    for b, sut in enumerate(SUTUN):
        c = list(sut)
        for i in range(N - 1, 0, -1):
            j = int(rr() * (i + 1))
            c[i], c[j] = c[j], c[i]
        for i in range(N):
            if c[i]:
                yeni[i] |= 1 << b
    return yeni


RAST = [ustkume_toplami(karistirilmis(TOHUM + 41000 + t)) for t in range(TEKRAR)]
ORNEK = (("gecikme", "taksitlendirme"), ("sayac_degisimi", "saha_ziyareti"),
         ("tarife_degisikligi", "mobil_bildirim"), ("itiraz", "mobil_bildirim"))
desen = lambda k: sum(BIT[o] for o in k["X"]) | BIT[k["y"]]
rastgele = lambda k: [R[desen(k)] / N for R in RAST]

print(f"{TEKRAR} karistirmada rastgele eslesmenin urettigi destek\n")
print(f"  {'kural':<38} {'gercek':>7} {'rastgele':>9} {'en cok':>7} {'kaldirac':>9}")
for k in sorted((k for k in KURAL if len(k["X"]) == 1
                 and (sorted(k["X"])[0], k["y"]) in ORNEK),
                key=lambda k: -k["kaldirac"]):
    r = rastgele(k)
    print(f"  {sorted(k['X'])[0] + ' -> ' + k['y']:<38} {k['destek']:>7.4f} "
          f"{sum(r) / len(r):>9.4f} {max(r):>7.4f} {k['kaldirac']:>9.4f}")

DISARIDA = {i: k["destek"] > max(rastgele(k)) or k["destek"] < min(rastgele(k))
            for i, k in enumerate(KURAL)}
print(f"\n{len(KURAL)} kural iki olcute gore ayni anda ayriliyor")
print(f"  {'':<22} {'aralik icinde':>14} {'aralik disinda':>15}")
for ad, kosul in (("kaldirac 1'e yakin", True), ("kaldirac 1'den uzak", False)):
    g = [i for i, k in enumerate(KURAL) if (abs(k["kaldirac"] - 1) <= 0.05) == kosul]
    ic = sum(1 for i in g if not DISARIDA[i])
    print(f"  {ad:<22} {ic:>14} {len(g) - ic:>15}")

dt = destek(frozenset(["gecikme", "otomatik_odeme"]))
print(f"\ndestek tabaninin altinda kalan bir bagimlilik: gecikme + otomatik_odeme "
      f"destek {dt:.4f}, kaldirac {dt / (D1['gecikme'] * D1['otomatik_odeme']):.4f}; "
      f"taban {DESTEK_TABANI:.2f} oldugu icin kural listesine hic girmiyor")
```

```
60 karistirmada rastgele eslesmenin urettigi destek

  kural                                   gercek  rastgele  en cok  kaldirac
  gecikme -> taksitlendirme               0.1279    0.0362  0.0467    3.4498
  sayac_degisimi -> saha_ziyareti         0.1159    0.0354  0.0451    3.2572
  tarife_degisikligi -> mobil_bildirim    0.1076    0.1043  0.1099    1.0249
  itiraz -> mobil_bildirim                0.1384    0.1363  0.1407    1.0139

166 kural iki olcute gore ayni anda ayriliyor
                          aralik icinde  aralik disinda
  kaldirac 1'e yakin                107               8
  kaldirac 1'den uzak                26              25

destek tabaninin altinda kalan bir bagimlilik: gecikme + otomatik_odeme destek 0.0316, kaldirac 0.3584; taban 0.05 oldugu icin kural listesine hic girmiyor
```

Taban çizgisi ilk iki satırda. Gecikme ile taksitlendirme sepetlerin 0,1279'unda birlikte geçiyor;
sütunlar karıştırıldığında aynı çift ortalama 0,0362'ye iniyor ve altmış karıştırmanın **en yükseği
0,0467**. Fark 0,0917 ve veriden geliyor.

Son iki satır tersini gösteriyor. Güveni 0,9246 olan kuralın desteği 0,1384; rastgele eşleşme aynı
çifti ortalama 0,1363'e, en yüksek 0,1407'ye taşıyor. **Gerçek destek rastgele aralığın içinde**;
kural yüksek güveni tümüyle sonucun kendi sıklığından alıyor. Kaldıraç bu tabanın kapalı biçimidir:
desteklerin çarpımına bölmek, karıştırmanın ortalamada ürettiğine bölmektir.

İki ölçüt çaprazlandığında 166 kural dört kutuya dağılıyor: kaldıracı 1'e yakın 115 kuralın 107'si
rastgele aralığın içinde, kaldıracı uzak 51 kuralın 26'sı da. Toplam 133 kural rastgele eşleşmenin
ürettiğinden ayırt edilemiyor. Ters yöndeki 8 satır da anlamlıdır: destek büyüdükçe aralık daralır
ve 1,04'lük bir kaldıraç bile dışarı düşer. Kaldıracın sapması yetmez, **sapmanın rastlantıyla ne
sıklıkta üretildiği** de gerekir.

Son satır başka bir yüz açıyor. Gecikme ile otomatik ödeme arasındaki kurgu bağımlılığın kaldıracı
0,3584, listedeki her şeyden uzak; ama ikisi birlikte sepetlerin yalnız 0,0316'sında geçtiği için
destek tabanının altında kalıyor ve listeye **hiç girmiyor**. Taban 0,03 olsaydı bu kural listenin
başında olurdu.

#### Özet

- Güvene göre ilk altı kuralın altısının da sonucu sepetlerin 0,9120'sinde geçen aynı öğedir;
  kaldıraçları 1,0139–1,0489.
- Güveni 0,50'nin üstündeki 97 kuralın 75'inin kaldıracı 1'e 0,05'ten yakındır ve 85'i en sık üç
  öğeyi sonuç gösterir.
- Taban rastgele eşleşmenin ürettiği destektir: gecikme ile taksitlendirmenin 0,1279'luk desteği
  altmış karıştırmanın hiçbirinde 0,0467'yi aşmaz, ama güveni 0,9246 olan kuralın desteği rastgele
  aralığın içinde kalır.
- 166 kuralın 133'ü rastgele eşleşmenin ürettiğinden ayırt edilemiyor.
- Destek tabanı bir karardır: kaldıracı 0,3584 olan gerçek bir bağımlılık, desteği 0,0316 olduğu
  için 0,05'lik tabanın altında kalır ve listeye hiç girmez.

#### Kurs Kapanışı

On bir dersin hesabı tek tabloda ve her sayı kendi dersinden okundu. Üçlü her satırda aynıdır,
ama **satırlar ortak bir birim taşımaz**: etiket yokken ölçütü de her ders kendisi seçer. Son sütun
bunu yazar — uzaklık ve hata satırlarında küçük olan iyidir, oran ve puan satırlarında büyük olan.
Fark, yöntemin sayısı eksi tabandır.

| Ders | Taban çizgisi | Yöntemin sayısı | Fark | Ölçüt |
|---|---|---|---|---|
| Kümeleme 01 | rastgele atama 5,9790 | 3,8910 | −2,0880 | küme içi uzaklık |
| Kümeleme 02 | rastgele atama 5,9181 | 2,2570 | −3,6611 | küme içi uzaklık |
| Kümeleme 03 | rastgele atama 6,2340 | 4,5419 | −1,6922 | küme içi uzaklık |
| Kümeleme 04 | rastgele işaretleme 1,0200 | 1,7551 | +0,7351 | komşu uzaklığı |
| Kümeleme 05 | rastgele atama −4,2424 | −3,7755 | +0,4669 | uyum puanı |
| Kümeleme 06 | tek küme 0,6539 | 0,5235 | −0,1304 | uyum göstergesi |
| Boyut azaltma 01 | sütun ortalaması 3,3411 | 2,7762 | −0,5650 | geri kurma hatası |
| Boyut azaltma 02 | sütun ortalaması 3,3411 | 2,7464 | −0,5947 | geri kurma hatası |
| Boyut azaltma 03 | rastgele yerleşim 0,0390 | 0,5032 | +0,4642 | komşuluk korunma oranı |
| Boyut azaltma 04 | rastgele işaretleme 0,0488 | 0,6190 | +0,5702 | kesinlik |
| Boyut azaltma 05 | rastgele eşleşme 0,0362 | 0,1279 | +0,0917 | kuralın desteği |

İkinci sütun her derste ayrı bir yordamla üretildi: rastgele atama, tek küme, rastgele işaretleme,
sütun ortalamasıyla geri kurma, rastgele yerleşim, rastgele eşleşme. Denetimli kursta taban hazırdı
— en sık etiketi söylemek — burada her ders tabanını kendisi kurdu. **Kursun kuralı budur:
etiketsiz bir sonuç, rastgele bir sonuçtan ne kadar ayrıldığı kadar vardır; tabanı kurulmayan
kümeleme ölçülmemiş sayılır.** Üç satır dipnotsuz okunamaz: Kümeleme 06'da 0,5235 tabanının
altındadır, Kümeleme 04'ün +0,7351'i veriden değil ölçütten gelir, Boyut azaltma 03'ün 0,5032'si
altı adayın en iyisidir ve dürüst aralık 0,3940–0,5032'dir.

Kursun ikinci iddiası aynı tablodan okunur: denetimsiz sonuçta doğru cevap yoktur, **ölçüt seçimi
cevabı belirler**. Ölçüt değiştirildiğinde cevabın da değiştiği ders sayısı **on** — üç benzerlik
tanımı üç bölmeleme, üç bağlantı ölçütü aynı yükseklikte 3, 37 ve 56 küme, üç iç geçerlilik ölçütü
3, 6 ve 10 seçti; kümeleme 04'te iki ölçüt ters sıralama verdi, boyut azaltma 03'te birinin
koruduğunu öbürü attı, burada da güven ile kaldıraç iki ayrı liste çıkardı.

Yirmi bir derste — on biri etiketsiz — her yöntem özniteliklerin **verildiği** varsayımıyla
çalıştı. Ölçekleme, kodlama, üretme ve azaltma hep **elle** karar verildi ve her kararın bedeli
ayrı ayrı ölçüldü. Bu kararları da verinin kendisinden öğrenen bir yordamın neyi kazandırdığı ve
neyi görünmez kıldığı hiç sorulmadı. Sonraki kurs, Derin Öğrenme, bu soruyla açılır.

