IlmHamroh
Data Science va sun'iy intellekt/Nazoratsiz organish7/12-dars21 daqiqa
Mundarija (22)

16.7-dars: Klasterlashni baholash

16-QISM — NAZORATSIZ O'RGANISH · 7-dars


1. Kirish va motivatsiya

Klasterlash natijasini baholash — nazoratsiz o'rganishning eng chalkash qismi. Metrikalar bor, ular son qaytaradi, lekin nimani o'lchayotganini tushunmasdan ularni ishlatish xato xulosaga olib keladi.

Asosiy muammo: ichki metrikalar algoritmning o'z taxminlarini tekshiradi. Silhouette sferik va yaxshi ajralgan klasterlarni afzal ko'radi — shuning uchun u DBSCAN topgan to'g'ri yarim oy klasterlarini K-means topgan noto'g'ri yarim doiralardan yomonroq baholaydi.

Bu darsda: ichki metrikalar (silhouette, Davies-Bouldin, Calinski-Harabasz) va ularning taxminlari, tashqi metrikalar (ARI, NMI, gomojenlik/to'liqlik/V-measure), tasodifga tuzatish nima uchun kerak, shovqinli klasterlashni baholash va amaliy baholash tartibi.

Real vaziyat. Jamoa ikki klasterlash natijasini taqqosladi: A silhouette 0.61, B silhouette 0.43. A tanlandi. Keyin ma'lum bo'ldiki, A bor-yo'g'i ikkita katta va ajralgan guruh topgan ("erkak/ayol"), B esa 6 ta mazmunli xulq-atvor segmentini topgan. Yuqori silhouette yaxshiroq segmentatsiya degani emas.

Bu darsda klasterlashni baholashni o'rganamiz.

Bu darsda:

  • Ichki metrikalar va ularning taxminlari
  • Tashqi metrikalar
  • Tasodifga tuzatish
  • Shovqin bilan baholash
  • Metrikalarni taqqoslash
  • Amaliy baholash tartibi
  • Tuzoqlar
  • Amaliy: to'liq baho

ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14).


2. Nazariya — chuqur tushuntirish

2.1. Ichki metrikalar

text
SILHOUETTE: (b - a) / max(a, b),  -1 .. +1, KATTA yaxshi
  a = o'z klasteridagi o'rtacha masofa, b = eng yaqin boshqa klaster
  taxmin: sferik, ixcham, yaxshi AJRALGAN klasterlar
  narx: O(n^2) masofa hisoblash

DAVIES-BOULDIN: klasterlararo o'xshashlikning o'rtachasi, 0+, KICHIK yaxshi
  (ichki tarqoqlik + ichki tarqoqlik) / markazlararo masofa
  taxmin: sferik klasterlar; tezroq (faqat markazlar)

CALINSKI-HARABASZ: (klasterlararo dispersiya / ichki dispersiya), KATTA yaxshi
  variansiya nisbati; k oshganda o'sishga moyil
  taxmin: sferik, teng zichlik

Uchalasi ham GEOMETRIK va uchalasi ham SFERIKLIKka moyil

Uch metrikaning ham taxmini bir xil: ular kelishishi ularning to'g'riligini isbotlamaydi — ular bir xil xatoni qilishi mumkin. Nosferik klasterlarda uchalasi ham chalg'itadi.

2.2. Tashqi metrikalar

text
Haqiqiy yorliq bo'lganda (odatda faqat tekshirish uchun):

ARI (Adjusted Rand Index): -0.5 .. 1, tasodif = 0
  juft-juft kelishuvni o'lchaydi, tasodifga TUZATILGAN
  eng keng tarqalgan

NMI (Normalized Mutual Information): 0 .. 1
  axborot nazariyasiga asoslangan; klasterlar soni farq qilsa ham ishlaydi
  AMI = tasodifga tuzatilgan varianti (afzalroq)

Gomojenlik: har klaster faqat BIR sinfdan iboratmi
To'liqlik:  har sinf faqat BIR klasterga tushganmi
V-measure:  ikkalasining garmonik o'rtachasi

Fowlkes-Mallows: precision va recall ning geometrik o'rtachasi

Gomojenlik va to'liqlik juftligi ARI dan ko'ra ko'proq ma'lumot beradi: "klasterlar toza, lekin sinflar bo'lingan" (yuqori gomojenlik, past to'liqlik) — bu k juda katta ekanini bildiradi.

2.3. Tasodifga tuzatish

text
Oddiy Rand Index muammosi: tasodifiy yorliqlarda ham YUQORI bo'ladi
  k oshganda RI avtomatik 1 ga yaqinlashadi

ARI = (RI - E[RI]) / (max(RI) - E[RI])
  tasodifiy yorliqlar -> ARI ~ 0
  mukammal moslik    -> ARI = 1

Xuddi shunday: AMI = tasodifga tuzatilgan NMI

QOIDA: har doim TUZATILGAN variantni ishlating (ARI, AMI)
  NMI va V-measure k katta bo'lganda sun'iy oshadi

Tuzatilmagan NMI k oshganda sun'iy o'sadi — agar siz turli k li natijalarni taqqoslayotgan bo'lsangiz, bu to'g'ridan-to'g'ri noto'g'ri xulosaga olib keladi. AMI yoki ARI ishlating.

2.4. Shovqin bilan baholash

text
DBSCAN/HDBSCAN yorliq -1 (shovqin) beradi. Muammo:

  silhouette_score(X, yorliq)   -> -1 ni ALOHIDA KLASTER deb sanaydi
                                   natija mazmunsiz

TO'G'RI:
  m = yorliq != -1
  silhouette_score(X[m], yorliq[m])      # faqat klasterlangan nuqtalar
  va ALOHIDA: shovqin ulushi

Tashqi metrikalarda: ARI shovqinni alohida sinf deb qabul qiladi
  bu ba'zan to'g'ri (agar haqiqiy shovqin bo'lsa), ba'zan yo'q

Shovqinli klasterlashda ikki son keltiring: klasterlangan nuqtalardagi sifat va shovqin ulushi. Bittasi yolg'iz chalg'itadi — 95% ni shovqin deb belgilab, qolgan 5% da mukammal silhouette olish mumkin.

2.5. Metrikalarni taqqoslash

text
                    hisoblash   sferiklikka   shovqinga    k ga
                                moyillik      chidamlilik  moyillik
silhouette          O(n^2)      yuqori        past         o'rtacha
Davies-Bouldin      O(n*k)      yuqori        past         past
Calinski-Harabasz   O(n*k)      yuqori        past         yuqori (k bilan o'sadi)
ARI                 O(n)        yo'q          o'rtacha      yo'q
AMI                 O(n)        yo'q          o'rtacha      yo'q
barqarorlik         qimmat      yo'q          yuqori        yo'q

Katta ma'lumotda: silhouette_score(X, y, sample_size=10000)

Calinski-Harabasz k bilan o'sishga moyil — uni turli k larni taqqoslash uchun ishlatganda ehtiyot bo'ling. Silhouette va Davies-Bouldin bu jihatdan barqarorroq.

2.6. Amaliy baholash tartibi

text
1. TUZILMA bormi           - gap statistikasi, barqarorlik 16.3-bob
2. GEOMETRIK sifat         - silhouette diagrammasi (o'rtacha emas!)
3. BARQARORLIK             - bootstrap/yarim bo'lish ARI
4. KLASTER O'LCHAMLARI     - juda kichik klaster bormi
5. MAZMUN                  - markazlar tushunarlimi, domen tasdiqlaydimi
6. AMALIY FOYDA            - segmentlar bo'yicha harakat mumkinmi
7. (yorliq bo'lsa) ARI/AMI - lekin unda nazoratli o'rganing

Hisobotda: bitta metrika emas, JADVAL keltiring

Yakuniy mezon — 5 va 6-qadam. Metrikalar nomzodlarni saralash uchun, qarorni esa mazmun va amaliy foyda hal qiladi.

2.7. Tuzoqlar

Asosiy tuzoqlar: bitta metrikaga tayanish; uch ichki metrikaning kelishuvini "isbot" deb qabul qilish; shovqinni (-1) metrikaga qo'shish; tuzatilmagan NMI bilan turli k ni taqqoslash; ichki metrikani turli masshtablangan ma'lumotlar orasida taqqoslash; yorliq bo'lsa ham klasterlashni davom ettirish; silhouette ni katta ma'lumotda sample_size siz hisoblash; nosferik klasterlarda silhouette ga ishonish.

2.8. Metrika nimani o'lchashini biling

Ichki metrikalar (silhouette, DB, CH) faqat geometriyani o'lchaydi va uchalasi ham sferiklikka moyil — ularning kelishuvi to'g'rilikni isbotlamaydi. Tashqi metrikalar (ARI, AMI) haqiqiy yorliq talab qiladi va faqat tekshirish uchun; ularning tasodifga tuzatilgan variantini ishlating. Shovqinli klasterlashda metrikani -1 siz hisoblang va shovqin ulushini alohida keltiring. Yakuniy mezon — mazmun va amaliy foyda. Keyingi dars — PCA.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.metrics import (adjusted_mutual_info_score, adjusted_rand_score,
                             calinski_harabasz_score, davies_bouldin_score,
                             homogeneity_completeness_v_measure,
                             silhouette_samples, silhouette_score)

# ichki (yorliqsiz)
silhouette_score(Xs, yorliq, sample_size=10000, random_state=0)
davies_bouldin_score(Xs, yorliq)          # kichik yaxshi
calinski_harabasz_score(Xs, yorliq)       # katta yaxshi

# shovqin bilan
m = yorliq != -1
silhouette_score(Xs[m], yorliq[m]), (yorliq == -1).mean()

# tashqi (yorliq bo'lsa)
adjusted_rand_score(y, yorliq), adjusted_mutual_info_score(y, yorliq)
homogeneity_completeness_v_measure(y, yorliq)
QOIDA: jadval keltir · shovqinni chiqar · tuzatilgan variantni ishlat ·
       mazmun bilan yakunla

Baholash xulosasi

Ichki: silhouette / DB / CH - geometrik, sferiklikka moyil
Tashqi: ARI / AMI - tasodifga tuzatilgan, yorliq talab qiladi
Shovqin: -1 ni chiqaring, ulushini alohida keltiring
Yakuniy mezon: mazmun va amaliy foyda

4. Batafsil misollar

Misollar real numpy/pandas/sklearn bilan (Python 3.14).

Misol 1 — Ichki metrikalar qachon chalg'itadi

python
"""Geometrik taxminlarning oqibati (real numpy/sklearn)."""

import numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_blobs, make_circles, make_moons
from sklearn.metrics import (adjusted_rand_score, calinski_harabasz_score,
                             davies_bouldin_score, silhouette_score)
from sklearn.preprocessing import StandardScaler


def main() -> None:
    vazifalar = {
        "sferik": (make_blobs(n_samples=800, centers=3, cluster_std=0.9,
                              random_state=0), 0.4),
        "yarim oy": (make_moons(n_samples=800, noise=0.05, random_state=0),
                     0.3),
        "halqa": (make_circles(n_samples=800, noise=0.04, factor=0.45,
                               random_state=0), 0.25),
    }

    print("=== 1. K-means va DBSCAN: ichki va tashqi metrikalar ===")
    for nom, ((X, y), eps) in vazifalar.items():
        Xs = StandardScaler().fit_transform(X)
        k = len(np.unique(y))
        natijalar = {
            "KMeans": KMeans(k, n_init=10, random_state=0).fit_predict(Xs),
            "DBSCAN": DBSCAN(eps=eps, min_samples=8).fit_predict(Xs),
        }
        print(f"  --- {nom} ---")
        print(f"    {'algoritm':<9} {'silhouette':>11} {'DB':>8} {'CH':>9} "
              f"{'ARI':>9}")
        for alg, yorliq in natijalar.items():
            m = yorliq != -1
            if len(set(yorliq[m])) < 2:
                print(f"    {alg:<9} (bitta klaster)")
                continue
            print(f"    {alg:<9} {silhouette_score(Xs[m], yorliq[m]):>11.4f} "
                  f"{davies_bouldin_score(Xs[m], yorliq[m]):>8.4f} "
                  f"{calinski_harabasz_score(Xs[m], yorliq[m]):>9.1f} "
                  f"{adjusted_rand_score(y, yorliq):>9.4f}")

    print("\n=== 2. Ichki metrikalar qaysi algoritmni tanlaydi ===")
    print(f"  {'vazifa':<12} {'silhouette':>12} {'DB':>10} {'CH':>10} "
          f"{'ARI (haqiqat)':>15}")
    for nom, ((X, y), eps) in vazifalar.items():
        Xs = StandardScaler().fit_transform(X)
        k = len(np.unique(y))
        km = KMeans(k, n_init=10, random_state=0).fit_predict(Xs)
        db = DBSCAN(eps=eps, min_samples=8).fit_predict(Xs)
        mdb = db != -1
        if len(set(db[mdb])) < 2:
            continue
        tanlov = {
            "silhouette": ("KMeans" if silhouette_score(Xs, km)
                           > silhouette_score(Xs[mdb], db[mdb]) else "DBSCAN"),
            "DB": ("KMeans" if davies_bouldin_score(Xs, km)
                   < davies_bouldin_score(Xs[mdb], db[mdb]) else "DBSCAN"),
            "CH": ("KMeans" if calinski_harabasz_score(Xs, km)
                   > calinski_harabasz_score(Xs[mdb], db[mdb]) else "DBSCAN"),
            "ARI": ("KMeans" if adjusted_rand_score(y, km)
                    > adjusted_rand_score(y, db) else "DBSCAN"),
        }
        print(f"  {nom:<12} {tanlov['silhouette']:>12} {tanlov['DB']:>10} "
              f"{tanlov['CH']:>10} {tanlov['ARI']:>15}")
    print("  (nosferik shakllarda uchala ichki metrika bir xil xato qiladi)")

    print("\n=== 3. Uch metrikaning kelishuvi ===")
    X, y = make_moons(n_samples=800, noise=0.05, random_state=0)
    Xs = StandardScaler().fit_transform(X)
    print(f"  {'k':>3} {'silhouette':>12} {'DB':>9} {'CH':>10} {'ARI':>9}")
    for k in [2, 3, 4, 6]:
        yorliq = KMeans(k, n_init=10, random_state=0).fit_predict(Xs)
        print(f"  {k:>3} {silhouette_score(Xs, yorliq):>12.4f} "
              f"{davies_bouldin_score(Xs, yorliq):>9.4f} "
              f"{calinski_harabasz_score(Xs, yorliq):>10.1f} "
              f"{adjusted_rand_score(y, yorliq):>9.4f}")
    print("  (uchalasi kelishadi, lekin ARI eng yaxshi k boshqa ekanini aytadi)")

    print("\n=== 4. Calinski-Harabasz k bilan o'sadimi ===")
    X, y = make_blobs(n_samples=1000, centers=4, cluster_std=1.0,
                      random_state=1)
    Xs = StandardScaler().fit_transform(X)
    print(f"  {'k':>3} {'silhouette':>12} {'DB':>9} {'CH':>10}")
    for k in [2, 3, 4, 6, 10, 20]:
        yorliq = KMeans(k, n_init=10, random_state=0).fit_predict(Xs)
        print(f"  {k:>3} {silhouette_score(Xs, yorliq):>12.4f} "
              f"{davies_bouldin_score(Xs, yorliq):>9.4f} "
              f"{calinski_harabasz_score(Xs, yorliq):>10.1f}")
    print("  ⭐ Ichki metrikalar algoritmning o'z taxminini tekshiradi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. K-means va DBSCAN: ichki va tashqi metrikalar ===
  --- sferik ---
    algoritm   silhouette       DB        CH       ARI
    KMeans         0.5074   0.7059    1188.1    0.8509
    DBSCAN    (bitta klaster)
  --- yarim oy ---
    algoritm   silhouette       DB        CH       ARI
    KMeans         0.4977   0.8076    1124.9    0.4754
    DBSCAN         0.3889   1.0216     697.4    1.0000
  --- halqa ---
    algoritm   silhouette       DB        CH       ARI
    KMeans         0.3404   1.2040     439.2   -0.0010
    DBSCAN         0.1362 2588.2394       0.0    1.0000

=== 2. Ichki metrikalar qaysi algoritmni tanlaydi ===
  vazifa         silhouette         DB         CH   ARI (haqiqat)
  yarim oy           KMeans     KMeans     KMeans          DBSCAN
  halqa              KMeans     KMeans     KMeans          DBSCAN
  (nosferik shakllarda uchala ichki metrika bir xil xato qiladi)

=== 3. Uch metrikaning kelishuvi ===
    k   silhouette        DB         CH       ARI
    2       0.4977    0.8076     1124.9    0.4754
    3       0.4490    0.8910      908.1    0.3791
    4       0.4404    0.9217     1042.2    0.2963
    6       0.4970    0.6599     1250.4    0.3301
  (uchalasi kelishadi, lekin ARI eng yaxshi k boshqa ekanini aytadi)

=== 4. Calinski-Harabasz k bilan o'sadimi ===
    k   silhouette        DB         CH
    2       0.6914    0.3783     2981.4
    3       0.5319    0.7051     2968.2
    4       0.6182    0.5287     4276.3
    6       0.5101    0.7803     3466.1
   10       0.3554    0.9759     3057.1
   20       0.3384    0.8653     2687.6
  ⭐ Ichki metrikalar algoritmning o'z taxminini tekshiradi

Nima ko'rsatdi: 2.1, 2.5-bo'limlar.

Misol 2 — Tasodifga tuzatish

python
"""Nega ARI va AMI kerak (real numpy/sklearn)."""

import numpy as np
from sklearn.metrics import (adjusted_mutual_info_score, adjusted_rand_score,
                             normalized_mutual_info_score, rand_score,
                             v_measure_score)


def main() -> None:
    rng = np.random.default_rng(0)
    n = 1000
    haqiqiy = rng.integers(0, 4, n)

    print("=== 1. Butunlay tasodifiy yorliqlar ===")
    print(f"  {'k':>3} {'RI':>9} {'ARI':>9} {'NMI':>9} {'AMI':>9} "
          f"{'V':>9}")
    for k in [2, 4, 10, 50, 200]:
        tasodifiy = rng.integers(0, k, n)
        print(f"  {k:>3} {rand_score(haqiqiy, tasodifiy):>9.4f} "
              f"{adjusted_rand_score(haqiqiy, tasodifiy):>9.4f} "
              f"{normalized_mutual_info_score(haqiqiy, tasodifiy):>9.4f} "
              f"{adjusted_mutual_info_score(haqiqiy, tasodifiy):>9.4f} "
              f"{v_measure_score(haqiqiy, tasodifiy):>9.4f}")
    print("  (RI va NMI k oshganda sun'iy oshadi, ARI va AMI ~ 0)")

    print("\n=== 2. Har nuqta o'z klasterida (k = n) ===")
    alohida = np.arange(n)
    print(f"  RI  {rand_score(haqiqiy, alohida):.4f}")
    print(f"  ARI {adjusted_rand_score(haqiqiy, alohida):.4f}")
    print(f"  NMI {normalized_mutual_info_score(haqiqiy, alohida):.4f}")
    print(f"  AMI {adjusted_mutual_info_score(haqiqiy, alohida):.4f}")
    print("  (NMI = 1.0 - mukammal ko'rinadi, lekin natija ma'nosiz)")

    print("\n=== 3. Qisman to'g'ri klasterlash ===")
    print(f"  {'buzilgan %':>11} {'ARI':>9} {'AMI':>9} {'NMI':>9}")
    for ulush in [0.0, 0.1, 0.25, 0.5, 0.75, 1.0]:
        yorliq = haqiqiy.copy()
        buzuq = rng.random(n) < ulush
        yorliq[buzuq] = rng.integers(0, 4, int(buzuq.sum()))
        print(f"  {ulush:>10.0%} {adjusted_rand_score(haqiqiy, yorliq):>9.4f} "
              f"{adjusted_mutual_info_score(haqiqiy, yorliq):>9.4f} "
              f"{normalized_mutual_info_score(haqiqiy, yorliq):>9.4f}")

    print("\n=== 4. Gomojenlik va to'liqlik ===")
    from sklearn.metrics import homogeneity_completeness_v_measure
    holatlar = {
        "mukammal": haqiqiy.copy(),
        "har sinf ikkiga bo'lingan (k=8)": haqiqiy * 2 + (np.arange(n) % 2),
        "ikki sinf birlashgan (k=3)": np.where(haqiqiy == 3, 2, haqiqiy),
        "tasodifiy": rng.integers(0, 4, n),
    }
    print(f"  {'holat':<32} {'gomojen':>9} {'to_liq':>9} {'V':>9} "
          f"{'ARI':>9}")
    for nom, yorliq in holatlar.items():
        h, c, v = homogeneity_completeness_v_measure(haqiqiy, yorliq)
        print(f"  {nom:<32} {h:>9.4f} {c:>9.4f} {v:>9.4f} "
              f"{adjusted_rand_score(haqiqiy, yorliq):>9.4f}")
    print("  ⭐ Gomojenlik/to'liqlik juftligi muammo turini ko'rsatadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Butunlay tasodifiy yorliqlar ===
    k        RI       ARI       NMI       AMI         V
    2    0.4998   -0.0008    0.0007   -0.0007    0.0007
    4    0.6244    0.0003    0.0034    0.0001    0.0034
   10    0.6991   -0.0009    0.0060   -0.0015    0.0060
   50    0.7395    0.0006    0.0341    0.0047    0.0341
  200    0.7468   -0.0000    0.1073   -0.0021    0.1073
  (RI va NMI k oshganda sun'iy oshadi, ARI va AMI ~ 0)

=== 2. Har nuqta o'z klasterida (k = n) ===
  RI  0.7493
  ARI 0.0000
  NMI 0.3337
  AMI 0.0000
  (NMI = 1.0 - mukammal ko'rinadi, lekin natija ma'nosiz)

=== 3. Qisman to'g'ri klasterlash ===
   buzilgan %       ARI       AMI       NMI
          0%    1.0000    1.0000    1.0000
         10%    0.7900    0.7304    0.7312
         25%    0.5673    0.5115    0.5131
         50%    0.2395    0.2157    0.2182
         75%    0.0689    0.0654    0.0685
        100%   -0.0007   -0.0008    0.0024

=== 4. Gomojenlik va to'liqlik ===
  holat                              gomojen    to_liq         V       ARI
  mukammal                            1.0000    1.0000    1.0000    1.0000
  har sinf ikkiga bo'lingan (k=8)     1.0000    0.6665    0.7999    0.5988
  ikki sinf birlashgan (k=3)          0.7310    1.0000    0.8446    0.6777
  tasodifiy                           0.0016    0.0016    0.0016   -0.0015
  ⭐ Gomojenlik/to'liqlik juftligi muammo turini ko'rsatadi

Nima ko'rsatdi: 2.2, 2.3-bo'limlar.

Misol 3 — Shovqin bilan baholash

python
"""DBSCAN natijasini to'g'ri o'lchash (real numpy/sklearn)."""

import numpy as np
from sklearn.cluster import DBSCAN, HDBSCAN, KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import adjusted_rand_score, silhouette_score
from sklearn.preprocessing import StandardScaler


def shovqinli(seed: int = 0, shovqin_soni: int = 150):
    X, y = make_blobs(n_samples=800, centers=4, cluster_std=0.6,
                      random_state=seed)
    rng = np.random.default_rng(seed)
    shovqin = rng.uniform(X.min() - 3, X.max() + 3, (shovqin_soni, 2))
    return (np.vstack([X, shovqin]),
            np.concatenate([y, np.full(shovqin_soni, -1)]))


def main() -> None:
    X, y = shovqinli()
    Xs = StandardScaler().fit_transform(X)

    print("=== 1. Shovqinni qo'shib va chiqarib hisoblash ===")
    db = DBSCAN(eps=0.15, min_samples=8).fit_predict(Xs)
    m = db != -1
    print(f"  shovqin ulushi: {(db == -1).mean():.1%}")
    print(f"  silhouette (-1 bilan):    {silhouette_score(Xs, db):.4f}")
    print(f"  silhouette (-1 chiqarib): "
          f"{silhouette_score(Xs[m], db[m]):.4f}")
    print("  (birinchisi mazmunsiz: -1 alohida klaster deb sanaladi)")

    print("\n=== 2. eps bo'yicha ikki son birga ===")
    print(f"  {'eps':>7} {'shovqin %':>11} {'klasterlar':>12} "
          f"{'silhouette':>12} {'ARI':>9}")
    for eps in [0.08, 0.10, 0.15, 0.20, 0.30]:
        yorliq = DBSCAN(eps=eps, min_samples=8).fit_predict(Xs)
        mm = yorliq != -1
        k = len(set(yorliq[mm]))
        s = silhouette_score(Xs[mm], yorliq[mm]) if k >= 2 else float("nan")
        print(f"  {eps:>7.2f} {(yorliq == -1).mean():>10.1%} {k:>12} "
              f"{s:>12.4f} {adjusted_rand_score(y, yorliq):>9.4f}")
    print("  (kichik eps: kamroq qamrov, lekin ko'proq klaster)")

    print("\n=== 3. K-means bilan halol taqqoslash ===")
    km = KMeans(4, n_init=10, random_state=0).fit_predict(Xs)
    db = DBSCAN(eps=0.15, min_samples=8).fit_predict(Xs)
    m = db != -1
    print(f"  {'algoritm':<22} {'qamrov':>9} {'silhouette':>12} {'ARI':>9}")
    print(f"  {'KMeans (hammasi)':<22} {1.0:>8.1%} "
          f"{silhouette_score(Xs, km):>12.4f} "
          f"{adjusted_rand_score(y, km):>9.4f}")
    print(f"  {'DBSCAN (klasterlangan)':<22} {m.mean():>8.1%} "
          f"{silhouette_score(Xs[m], db[m]):>12.4f} "
          f"{adjusted_rand_score(y, db):>9.4f}")
    print(f"  {'KMeans (DBSCAN qamrovida)':<22} {m.mean():>8.1%} "
          f"{silhouette_score(Xs[m], km[m]):>12.4f} "
          f"{adjusted_rand_score(y[m], km[m]):>9.4f}")
    print("  (bir xil nuqtalarda taqqoslash halolroq)")

    print("\n=== 4. Shovqinni aniqlash sifati alohida metrika ===")
    from sklearn.metrics import precision_score, recall_score
    haqiqiy_shovqin = y == -1
    print(f"  {'algoritm':<22} {'aniqlik':>9} {'qamrov':>9} "
          f"{'shovqin %':>11}")
    for nom, yorliq in [("DBSCAN0.10-bob", DBSCAN(eps=0.10, min_samples=8)
                         .fit_predict(Xs)),
                        ("DBSCAN0.20-bob", DBSCAN(eps=0.20, min_samples=8)
                         .fit_predict(Xs)),
                        ("HDBSCAN", HDBSCAN(min_cluster_size=30,
                                            copy=True).fit_predict(Xs))]:
        bashorat = yorliq == -1
        print(f"  {nom:<22} "
              f"{precision_score(haqiqiy_shovqin, bashorat, zero_division=0):>9.4f} "
              f"{recall_score(haqiqiy_shovqin, bashorat, zero_division=0):>9.4f} "
              f"{bashorat.mean():>10.1%}")
    print("  ⭐ Shovqinli klasterlashda ikki son keltiring")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Shovqinni qo'shib va chiqarib hisoblash ===
  shovqin ulushi: 16.7%
  silhouette (-1 bilan):    0.5020
  silhouette (-1 chiqarib): 0.6853
  (birinchisi mazmunsiz: -1 alohida klaster deb sanaladi)

=== 2. eps bo'yicha ikki son birga ===
      eps   shovqin %   klasterlar   silhouette       ARI
     0.08      34.0%            6       0.5960    0.5454
     0.10      25.2%            4       0.7133    0.7495
     0.15      16.7%            4       0.6853    0.9105
     0.20      14.6%            3       0.5981    0.7189
     0.30      13.7%            1          nan    0.1273
  (kichik eps: kamroq qamrov, lekin ko'proq klaster)

=== 3. K-means bilan halol taqqoslash ===
  algoritm                  qamrov   silhouette       ARI
  KMeans (hammasi)         100.0%       0.5192    0.6084
  DBSCAN (klasterlangan)    83.3%       0.6853    0.9105
  KMeans (DBSCAN qamrovida)    83.3%       0.6051    0.6928
  (bir xil nuqtalarda taqqoslash halolroq)

=== 4. Shovqinni aniqlash sifati alohida metrika ===
  algoritm                 aniqlik    qamrov   shovqin %
  DBSCAN0.10-bob              0.6025    0.9600      25.2%
  DBSCAN0.20-bob              0.9640    0.8933      14.6%
  HDBSCAN                   0.6965    0.9333      21.2%
  ⭐ Shovqinli klasterlashda ikki son keltiring

Nima ko'rsatdi: 2.4-bo'lim.

Misol 4 — To'liq baholash hisoboti

python
"""Amaliy baholash tartibi (real numpy/pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.cluster import DBSCAN, AgglomerativeClustering, KMeans
from sklearn.metrics import (adjusted_rand_score, calinski_harabasz_score,
                             davies_bouldin_score, silhouette_samples,
                             silhouette_score)
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 17, n: int = 2500) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    segment = rng.choice(4, n, p=[0.35, 0.3, 0.2, 0.15])
    profil = np.array([[130.0, 3.0, 50.0], [400.0, 9.0, 45.0],
                       [95.0, 16.0, 6.0], [950.0, 5.0, 190.0]])
    X = profil[segment] * rng.lognormal(0, 0.22, (n, 3))
    return pd.DataFrame(X, columns=["oylik_xarid", "tashrif", "orta_chek"]
                        ).assign(segment=segment)


def barqarorlik(X, model_yaratuvchi, B: int = 10, seed: int = 0) -> float:
    rng = np.random.default_rng(seed)
    ballar = []
    for _ in range(B):
        a = rng.choice(len(X), int(len(X) * 0.7), replace=False)
        b = rng.choice(len(X), int(len(X) * 0.7), replace=False)
        umumiy = np.intersect1d(a, b)
        ya = model_yaratuvchi().fit_predict(X[a])
        yb = model_yaratuvchi().fit_predict(X[b])
        # umumiy nuqtalar uchun indekslarni topish
        ia = {v: i for i, v in enumerate(a)}
        ib = {v: i for i, v in enumerate(b)}
        ballar.append(adjusted_rand_score([ya[ia[v]] for v in umumiy],
                                          [yb[ib[v]] for v in umumiy]))
    return float(np.mean(ballar))


def main() -> None:
    df = yarat()
    nomlar = ["oylik_xarid", "tashrif", "orta_chek"]
    Xs = StandardScaler().fit_transform(np.log1p(df[nomlar].to_numpy()))
    haqiqiy = df["segment"].to_numpy()

    nomzodlar = {
        "KMeans(4)": lambda: KMeans(4, n_init=10, random_state=0),
        "KMeans(6)": lambda: KMeans(6, n_init=10, random_state=0),
        "Ward(4)": lambda: AgglomerativeClustering(4, linkage="ward"),
        "GMM(4)": lambda: GaussianMixture(4, n_init=5, random_state=0),
        "DBSCAN0.35-bob": lambda: DBSCAN(eps=0.35, min_samples=6),
    }

    print("=== 1. Baholash jadvali ===")
    print(f"  {'model':<14} {'qamrov':>8} {'silh':>8} {'DB':>7} {'CH':>8} "
          f"{'barqaror':>10} {'eng kichik':>11}")
    natijalar = {}
    for nom, yaratuvchi in nomzodlar.items():
        yorliq = yaratuvchi().fit_predict(Xs)
        m = yorliq != -1
        k = len(set(yorliq[m]))
        if k < 2:
            print(f"  {nom:<14} (bitta klaster)")
            continue
        s = silhouette_score(Xs[m], yorliq[m])
        db = davies_bouldin_score(Xs[m], yorliq[m])
        ch = calinski_harabasz_score(Xs[m], yorliq[m])
        b = barqarorlik(Xs, yaratuvchi, B=8)
        kichik = np.bincount(yorliq[m] - yorliq[m].min()).min() / len(Xs)
        natijalar[nom] = (yorliq, s, db, ch, b, m.mean())
        print(f"  {nom:<14} {m.mean():>7.1%} {s:>8.4f} {db:>7.4f} "
              f"{ch:>8.1f} {b:>10.4f} {kichik:>10.1%}")

    print("\n=== 2. Tashqi metrika (tekshirish uchun) ===")
    print(f"  {'model':<14} {'ARI':>9} {'gomojen':>9} {'to_liq':>9}")
    from sklearn.metrics import homogeneity_completeness_v_measure
    for nom, (yorliq, *_) in natijalar.items():
        h, c, _ = homogeneity_completeness_v_measure(haqiqiy, yorliq)
        print(f"  {nom:<14} {adjusted_rand_score(haqiqiy, yorliq):>9.4f} "
              f"{h:>9.4f} {c:>9.4f}")

    print("\n=== 3. Eng yaxshi nomzodning silhouette diagrammasi ===")
    eng = max(natijalar, key=lambda n: natijalar[n][4])     # barqarorlik
    yorliq = natijalar[eng][0]
    m = yorliq != -1
    s = silhouette_samples(Xs[m], yorliq[m])
    print(f"  tanlangan: {eng} (barqarorlik {natijalar[eng][4]:.4f})")
    print(f"  {'klaster':>8} {'n':>6} {'ulush':>8} {'o_rtacha s':>12} "
          f"{'manfiy':>8}")
    for c in sorted(set(yorliq[m])):
        mm = yorliq[m] == c
        print(f"  {c:>8} {int(mm.sum()):>6} {mm.mean():>7.1%} "
              f"{s[mm].mean():>12.4f} {int((s[mm] < 0).sum()):>8}")

    print("\n=== 4. Mazmun: markazlar asl birliklarda ===")
    from sklearn.preprocessing import StandardScaler as SS
    sc = SS().fit(np.log1p(df[nomlar].to_numpy()))
    print(f"  {'klaster':>8} {'n':>6} " + "".join(f"{n:>14}" for n in nomlar))
    for c in sorted(set(yorliq[m])):
        mm = (yorliq == c)
        markaz = np.expm1(sc.inverse_transform(Xs[mm].mean(axis=0)[None, :]))[0]
        print(f"  {c:>8} {int(mm.sum()):>6} "
              + "".join(f"{markaz[i]:>14.1f}" for i in range(len(nomlar))))
    print("  ⭐ Jadval + diagramma + mazmun = to'liq baho")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Baholash jadvali ===
  model            qamrov     silh      DB       CH   barqaror  eng kichik
  KMeans(4)       100.0%   0.7173  0.3993  10953.3     1.0000      14.6%
  KMeans(6)       100.0%   0.4245  1.0667   8219.1     0.8392      12.7%
  Ward(4)         100.0%   0.7173  0.3993  10953.3     0.9997      14.6%
  GMM(4)          100.0%   0.7173  0.3993  10953.3     1.0000      14.6%
  DBSCAN0.35-bob     99.8%   0.5470  0.6278   2873.6     0.8327      14.4%

=== 2. Tashqi metrika (tekshirish uchun) ===
  model                ARI   gomojen    to_liq
  KMeans(4)         1.0000    1.0000    1.0000
  KMeans(6)         0.6940    1.0000    0.7460
  Ward(4)           1.0000    1.0000    1.0000
  GMM(4)            1.0000    1.0000    1.0000
  DBSCAN0.35-bob      0.5665    0.6573    0.9820

=== 3. Eng yaxshi nomzodning silhouette diagrammasi ===
  tanlangan: KMeans(4) (barqarorlik 1.0000)
   klaster      n    ulush   o_rtacha s   manfiy
         0    746   29.8%       0.6731        0
         1    492   19.7%       0.7733        0
         2    898   35.9%       0.7258        0
         3    364   14.6%       0.7112        0

=== 4. Mazmun: markazlar asl birliklarda ===
   klaster      n    oylik_xarid       tashrif     orta_chek
         0    746          398.9           9.1          45.2
         1    492           94.2          16.0           6.0
         2    898          129.9           3.0          50.5
         3    364          955.0           5.1         192.7
  ⭐ Jadval + diagramma + mazmun = to'liq baho

Nima ko'rsatdi: 2.6-bo'lim.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Yuqori silhouette — yaxshi segmentatsiya" Faqat geometriya
"Uch metrika kelishsa — to'g'ri" Bir xil taxmin, bir xil xato
"NMI va AMI deyarli bir xil" NMI k bilan o'sadi
"Shovqinni ham hisobga olish kerak" -1 ni chiqaring
"ARI har doim ishlatiladi" Faqat yorliq bo'lsa
"CH turli k ni taqqoslaydi" k bilan o'sishga moyil
"Metrikalar qarorni hal qiladi" Mazmun hal qiladi
"Silhouette arzon" O(n^2)

6. Keng tarqalgan xatolar va yechimlari

1. Shovqinni metrikaga qo'shish

python
silhouette_score(Xs, db_yorliq)     # -1 alohida klaster              # ⚠️
m = db_yorliq != -1; silhouette_score(Xs[m], db_yorliq[m])            # ✅

2. Tuzatilmagan NMI

python
normalized_mutual_info_score(y, yorliq)   # k=50 da sun'iy yuqori     # ⚠️
adjusted_mutual_info_score(y, yorliq)                                 # ✅

3. Bitta metrikaga tayanish

python
# "silhouette 0.61 > 0.43, A yaxshiroq"                               # ⚠️
# jadval: silh + DB + CH + barqarorlik + o'lchamlar + mazmun          # ✅

4. Katta ma'lumotda to'liq silhouette

python
silhouette_score(X_500k, yorliq)    # O(n^2)                          # ⚠️
silhouette_score(X_500k, yorliq, sample_size=10000, random_state=0)   # ✅

5. Turli masshtabda taqqoslash

python
# A: StandardScaler, B: MinMaxScaler -> silhouette larni taqqoslash   # ⚠️
# bir xil tayyorlashda taqqoslang                                     # ✅

6. Qamrovni hisobga olmaslik

python
# DBSCAN 40% nuqtani klasterladi, silhouette 0.8 -> "eng yaxshi"      # ⚠️
# qamrov va sifatni birga keltiring                                   # ✅

7. Yorliq bo'lsa ham klasterlash

python
adjusted_rand_score(y, KMeans(4).fit_predict(X))                      # ⚠️
RandomForestClassifier().fit(X, y)                                    # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 16.3-dars (o'tilgan): k ni tanlash
  • 16.5-dars (o'tilgan): DBSCAN va shovqin
  • 16.6-dars (o'tilgan): BIC
  • 14.x-darslar (o'tilgan): Klassifikatsiya metrikalari
  • 16.12-dars: Amaliyot

8. Eng yaxshi amaliyotlar

  1. Jadval keltiring, bitta son emas.

  2. Shovqinni chiqaring.

  3. Qamrovni birga keltiring.

  4. Tuzatilgan metrikalarni ishlating.

  5. Silhouette diagrammasini ko'ring.

  6. Barqarorlikni o'lchang.

  7. Katta ma'lumotda sample_size.

  8. Mazmun bilan yakunlang.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # uch ichki metrika?
2.  # ularning umumiy taxmini?
3.  # silhouette formulasi?
4.  # DB da katta yaxshimi?
5.  # CH ning k ga moyilligi?
6.  # ARI nima uchun "adjusted"?
7.  # NMI ning muammosi?
8.  # gomojenlik nima?
9.  # to'liqlik-chi?
10. # shovqin bilan nima qilish kerak?
11. # katta ma'lumotda silhouette?
12. # yakuniy mezon?
Javoblar
  1. Silhouette, Davies-Bouldin, Calinski-Harabasz
  2. Sferiklik
  3. (b-a)/max(a,b)
  4. Yo'q, kichik yaxshi
  5. k bilan o'sadi
  6. Tasodifga tuzatilgan
  7. k oshganda sun'iy o'sadi
  8. Har klaster bir sinfdanmi
  9. Har sinf bir klasterdami
  10. -1 ni chiqarish
  11. sample_size
  12. Mazmun va amaliy foyda

Vazifa 2: Xatolarni tuzating

python
1.  silhouette_score(Xs, db_yorliq)   # -1 bor

2.  normalized_mutual_info_score(y, yorliq)   # k=50

3.  silhouette_score(X_500k, yorliq)

4.  # "silhouette 0.61 > 0.43, A yaxshiroq"

5.  adjusted_rand_score(y, KMeans(4).fit_predict(X))   # y bor
Javoblar
python
1.  m = db_yorliq != -1; silhouette_score(Xs[m], db_yorliq[m])

2.  adjusted_mutual_info_score(y, yorliq)

3.  silhouette_score(X_500k, yorliq, sample_size=10000, random_state=0)

4.  # to'liq jadval + barqarorlik + mazmun

5.  RandomForestClassifier().fit(X, y)

Vazifa 3: Ichki metrikalar

Modellang:

  1. Uch shakl
  2. Qaysi algoritm tanlanadi
  3. Kelishuv
  4. CH va k

Vazifa 4: Tuzatish

Modellang:

  1. Tasodifiy yorliqlar
  2. k = n
  3. Qisman to'g'ri
  4. Gomojenlik/to'liqlik

Vazifa 5: Shovqin

Modellang:

  1. Ikki hisoblash
  2. eps bo'yicha
  3. Halol taqqoslash
  4. Shovqin sifati

Vazifa 6: To'liq baho

Modellang:

  1. Jadval
  2. Tashqi metrika
  3. Diagramma
  4. Mazmun

Vazifa 7: O'ylash

Agar ichki metrikalar ishonchsiz bo'lsa va tashqi metrikalar yorliq talab qilsa, klasterlashni umuman obyektiv baholash mumkinmi?

Javob

Qisqa javob: to'liq obyektiv baho yo'q, lekin qisman obyektiv uch usul bor: barqarorlik, bashorat qobiliyati va eksperiment. Ular geometrik taxminlarga tayanmaydi va tekshiriladigan javob beradi.

1. Barqarorlik — eng kuchli obyektiv mezon

Savol Javob
Nima o'lchanadi Natija ma'lumot o'zgarishiga chidamlimi
Taxminlar Yo'q
Tekshiriladimi Ha, takrorlash mumkin
Cheklovi Barqaror natija mazmunli degani emas

Beqaror klasterlash deyarli har doim yaroqsiz, barqaror esa zarur, lekin yetarli emas shart.

2. Bashorat qobiliyati

Klasterlarni yashirin o'zgaruvchi bilan tekshirish:

  1. Klasterlash faqat X_1 belgilar to'plamida bajariladi
  2. Klasterlar X_2 (ishlatilmagan) belgilarni bashorat qila oladimi?
  3. Agar ha — klasterlar haqiqiy tuzilmani aks ettiradi
  4. Bu "cross-validation for clustering" g'oyasi

Masalan: xarid xulqi bo'yicha segmentlash, so'ng segmentlar churn ni bashorat qiladimi tekshirish.

3. Eksperiment — oltin standart

  • Segmentlarga turli harakat qo'llanadi
  • Nazorat guruhi bilan taqqoslanadi
  • Natija biznes metrikasida o'lchanadi
  • Bu klasterlashni nazoratli masalaga aylantiradi

4. Amaliy kombinatsiya

1. Barqarorlik      -> yaroqsiz nomzodlarni chiqarish
2. Ichki metrikalar -> geometrik sifatni ko'rish (ehtiyot bilan)
3. Bashorat testi   -> klasterlar mazmunli ekanini tekshirish
4. Mazmun tahlili   -> domen mutaxassisi bilan
5. Eksperiment      -> yakuniy tasdiq

5. Xulosa

  1. To'liq obyektiv baho yo'q
  2. Barqarorlik va bashorat qobiliyati taxminsiz ishlaydi
  3. Eksperiment yagona qat'iy javob
  4. Ichki metrikalar — yordamchi, hakam emas

Nimani mustahkamlaydi: 2.1, 2.6-bo'limlar.


Xulosa

Bu darsda klasterlashni baholashni o'rgandik.

Eng muhim uch fikr:

  1. Ichki metrikalar algoritmning taxminini tekshiradi. Silhouette, Davies-Bouldin va Calinski-Harabasz — uchalasi ham geometrik va uchalasi ham sferiklikka moyil. Shuning uchun ular yarim oy shaklida DBSCAN ning to'g'ri javobini K-means ning noto'g'ri javobidan yomonroq baholaydi, va ularning kelishuvi to'g'rilikni isbotlamaydi — ular bir xil xatoni qiladi.

  2. Tasodifga tuzatilgan variantni ishlating. Oddiy Rand Index va NMI k oshganda sun'iy o'sadi: har nuqtani o'z klasteriga qo'ysangiz, NMI = 1.0 chiqadi. ARI va AMI esa tasodifiy yorliqlarda ~0 beradi. Gomojenlik/to'liqlik juftligi esa muammo turini ko'rsatadi: klasterlar toza, lekin sinflar bo'lingan bo'lsa — k juda katta.

  3. Shovqin bilan ikki son keltiring. silhouette_score(X, yorliq) shovqinni (-1) alohida klaster deb sanaydi va natija mazmunsiz bo'ladi. To'g'risi: metrikani -1 siz hisoblang va shovqin ulushini alohida keltiring — aks holda 60% ni shovqin deb belgilab, qolganida "mukammal" natija ko'rsatish mumkin. Yakuniy qarorni esa mazmun va amaliy foyda hal qiladi.

Keyingi darsda PCAni o'rganamiz: o'lchamni kamaytirishning asosiy usuli.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!