KNN va Naive Bayes
Sahna: ikkita eng oddiy model
Bu bobda ikkita model bor va ularni birga o'rganish qulay — chunki ikkisi ham o'qitish bosqichiga deyarli ega emas.
- KNN: hech narsa o'rganmaydi. Bashorat paytida eng yaqin qo'shnilarga qaraydi.
- Naive Bayes: faqat sanaydi. Ehtimolliklarni hisoblab qo'yadi, keyin Bayes teoremasini qo'llaydi.
Ikkisi ham eski va oddiy. Lekin ikkisi ham hali ham ishlatiladi — aniq joylarda.
KNN: "dangasa" o'rganuvchi
G'oya bir jumlada: yangi kvartiraga eng o'xshash K ta kvartirani topamiz va ularning javobiga qaraymiz.
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split, cross_val_score from sklearn.neighbors import KNeighborsClassifier from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler df = pd.read_csv(DATA / "uy_ijara.csv") son = ["xonalar", "maydon_m2", "qavat", "qavatlar_soni", "metro_km", "mebel"] X, y = df[son], df["ijaraga_berildi_30k"] X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y) quvur = Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=5))]).fit(X_tr, y_tr) # bitta kvartira uchun qo'shnilarini ko'rsatamiz sc = quvur.named_steps["sc"] knn = quvur.named_steps["m"] masofa, indeks = knn.kneighbors(sc.transform(X_te.iloc[[0]]), n_neighbors=5) print("Tekshirilayotgan kvartira:") print(X_te.iloc[0].to_string()) print(f"\nHaqiqiy javob: {y_te.iloc[0]}") print("\nEng yaqin 5 qo'shni:") qoshnilar = X_tr.iloc[indeks[0]].copy() qoshnilar["javob"] = y_tr.iloc[indeks[0]].values qoshnilar["masofa"] = masofa[0].round(3) show(qoshnilar) print(f"Qo'shnilar ovozi: {y_tr.iloc[indeks[0]].mean():.1%} 'ha' -> bashorat {quvur.predict(X_te.iloc[[0]])[0]}")
Tekshirilayotgan kvartira: xonalar 4.00 maydon_m2 79.20 qavat 9.00 qavatlar_soni 10.00 metro_km 2.01 mebel 0.00 Haqiqiy javob: 1 Eng yaqin 5 qo'shni: Qo'shnilar ovozi: 20.0% 'ha' -> bashorat 0
| xonalar | maydon_m2 | qavat | qavatlar_soni | metro_km | mebel | javob | masofa |
|---|---|---|---|---|---|---|---|
| 4 | 71.7 | 8 | 10 | 1.27 | 0 | 1 | 0.65 |
| 4 | 89.7 | 8 | 11 | 2.44 | 0 | 0 | 0.682 |
| 4 | 83.2 | 9 | 12 | 2.94 | 0 | 0 | 0.765 |
| 4 | 89.6 | 7 | 10 | 2.44 | 0 | 0 | 0.798 |
| 4 | 90.4 | 7 | 10 | 2.5 | 0 | 0 | 0.837 |
Model hech qanday qoida o'rganmadi. U butun train to'plamini eslab qoldi va bashorat paytida izlaydi.
Shuning uchun KNN "lazy learner" deb ataladi: o'qitish bir zumda, bashorat sekin.
import time from sklearn.linear_model import LogisticRegression print(f"{'model':<24} {'fit vaqti':>12} {'predict vaqti':>15}") print("-" * 54) for nom, m in [("KNN", Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier())])), ("LogisticRegression", Pipeline([("sc", StandardScaler()), ("m", LogisticRegression(max_iter=2000))]))]: t0 = time.perf_counter(); m.fit(X_tr, y_tr); ft = (time.perf_counter()-t0)*1000 t0 = time.perf_counter(); m.predict(X_te); pt = (time.perf_counter()-t0)*1000 print(f"{nom:<24} {ft:>10.2f} ms {pt:>13.2f} ms")
model fit vaqti predict vaqti ------------------------------------------------------ KNN 1.35 ms 5.19 ms LogisticRegression 2.86 ms 0.50 ms
KNN ishlab chiqarishda qimmat
Model butun train to'plamini o'zi bilan olib yuradi. 10 million qatorli ma'lumot bo'lsa, server xotirasida 10 million qator turishi kerak.
Va har bir so'rov uchun masofa hisoblanadi. Shuning uchun KNN real vaqtli tizimlarda kamdan-kam ishlatiladi.
Scaling KNN uchun hayotiy
4-bobda aytganmiz, endi o'lchaymiz:
print(f"{'holat':<22} {'CV F1':>9} {'±':>8}") print("-" * 40) b1 = cross_val_score(KNeighborsClassifier(), X, y, cv=5, scoring="f1") b2 = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier())]), X, y, cv=5, scoring="f1") print(f"{'scaling YO`Q':<22} {b1.mean():>9.4f} {b1.std():>8.4f}") print(f"{'scaling BOR':<22} {b2.mean():>9.4f} {b2.std():>8.4f}") print(f"{'farq':<22} {b2.mean()-b1.mean():>+9.4f}")
holat CV F1 ± ---------------------------------------- scaling YO`Q 0.6627 0.0113 scaling BOR 0.6903 0.0141 farq +0.0276
Sabab: masofa hisoblanganda maydon_m2 (22–220) mebel (0–1) ni butunlay bosib ketadi. Scaling busiz KNN amalda faqat bitta ustunga qarab ishlaydi.
K ni tanlash — bu bias-variance tutqichi
from sklearn.metrics import f1_score print(f"{'K':>5} {'train F1':>10} {'test F1':>9} {'CV F1':>9} {'izoh'}") print("-" * 52) for k in [1, 3, 5, 15, 50, 200]: q = Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=k))]).fit(X_tr, y_tr) tr_f = f1_score(y_tr, q.predict(X_tr)) te_f = f1_score(y_te, q.predict(X_te)) cv = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=k))]), X, y, cv=5, scoring="f1").mean() izoh = "K=1: har nuqta o'zini topadi" if k == 1 else ("juda silliq" if k >= 200 else "") print(f"{k:>5} {tr_f:>10.4f} {te_f:>9.4f} {cv:>9.4f} {izoh}")
K train F1 test F1 CV F1 izoh
----------------------------------------------------
1 1.0000 0.6398 0.6243 K=1: har nuqta o'zini topadi
3 0.7789 0.6873 0.6547
5 0.7523 0.7385 0.6903
15 0.7148 0.7554 0.7064
50 0.6903 0.7769 0.7191
200 0.6990 0.7917 0.7272 juda silliq
K=1 da train F1 1.0000 — chunki har nuqtaning eng yaqin qo'shnisi o'zi. Bu toza overfitting.
K oshgani sari model silliqlashadi: kam variance, ko'p bias.
show(pd.DataFrame([ {"K": "kichik (1-3)", "Model": "Juda moslashuvchan", "Xavf": "Overfitting, shovqinga sezgir"}, {"K": "o'rta (5-20)", "Model": "Muvozanat", "Xavf": "Odatda eng yaxshi"}, {"K": "katta (100+)", "Model": "Juda silliq", "Xavf": "Underfitting — hamma joyda bir xil javob"}, ])) print() print("Qoida: K ni JUFT bo'lmagan son qiling (binary classification da teng ovoz bo'lmasligi uchun).")
Qoida: K ni JUFT bo'lmagan son qiling (binary classification da teng ovoz bo'lmasligi uchun).
| K | Model | Xavf |
|---|---|---|
| kichik (1-3) | Juda moslashuvchan | Overfitting, shovqinga sezgir |
| o'rta (5-20) | Muvozanat | Odatda eng yaxshi |
| katta (100+) | Juda silliq | Underfitting — hamma joyda bir xil javob |
Masofa metrikasi
print(f"{'metrika':<16} {'CV F1':>9} izoh") print("-" * 58) for nom, p in [("euclidean", 2), ("manhattan", 1)]: b = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=15, p=p))]), X, y, cv=5, scoring="f1") izoh = "sqrt(sum(d^2)) — sukut" if p == 2 else "sum(|d|) — chetki qiymatga chidamli" print(f"{nom:<16} {b.mean():>9.4f} {izoh}") print() b_w = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=15, weights="distance"))]), X, y, cv=5, scoring="f1") print(f"{'weights=distance':<16} {b_w.mean():>9.4f} yaqin qo'shni ko'proq ovozga ega")
metrika CV F1 izoh ---------------------------------------------------------- euclidean 0.7064 sqrt(sum(d^2)) — sukut manhattan 0.7178 sum(|d|) — chetki qiymatga chidamli weights=distance 0.7017 yaqin qo'shni ko'proq ovozga ega
O'lchov la'nati (curse of dimensionality)
KNN ning eng jiddiy cheklovi: ustunlar ko'payganda masofa ma'nosini yo'qotadi.
r = np.random.default_rng(42) print("Tasodifiy nuqtalar orasidagi masofa — o'lchov oshganda:") print(f"{'o`lchov':>8} {'eng yaqin':>11} {'eng uzoq':>10} {'nisbat':>8}") print("-" * 42) for m in [2, 10, 50, 200, 1000]: P = r.normal(size=(500, m)) d = np.linalg.norm(P[1:] - P[0], axis=1) print(f"{m:>8} {d.min():>11.3f} {d.max():>10.3f} {d.max()/d.min():>8.2f}")
Tasodifiy nuqtalar orasidagi masofa — o'lchov oshganda:
o`lchov eng yaqin eng uzoq nisbat
------------------------------------------
2 0.041 4.301 104.36
10 1.525 6.079 3.99
50 7.867 13.041 1.66
200 16.601 21.685 1.31
1000 42.854 47.704 1.11
Nisbatga qarang. 2 o'lchovda eng uzoq nuqta eng yaqinidan ancha uzoq. 1000 o'lchovda esa ular deyarli teng masofada bo'ladi.
Ya'ni "eng yaqin qo'shni" tushunchasi ma'nosini yo'qotadi — hamma bir xil uzoqlikda.
print("KNN ga shovqin ustunlari qo'shsak:") print(f"{'qo`shilgan shovqin':>20} {'CV F1':>9}") print("-" * 32) for n_sh in [0, 10, 50, 200]: if n_sh == 0: Xn = X else: sh = pd.DataFrame(r.normal(size=(len(X), n_sh)), columns=[f"sh_{i}" for i in range(n_sh)], index=X.index) Xn = pd.concat([X, sh], axis=1) b = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=15))]), Xn, y, cv=5, scoring="f1") print(f"{n_sh:>20} {b.mean():>9.4f}")
KNN ga shovqin ustunlari qo'shsak:
qo`shilgan shovqin CV F1
--------------------------------
0 0.7064
10 0.6953
50 0.6417
200 0.6410
16-bobda Lasso shovqinga chidamli edi. KNN esa chidamsiz — chunki shovqin ustunlari masofani buzadi.
Naive Bayes: faqat sanash
Ikkinchi model butunlay boshqa g'oyadan chiqadi — Bayes teoremasi:
"Naive" (sodda) so'zi shundan: model xususiyatlarni bir-biridan mustaqil deb hisoblaydi. Bu deyarli har doim noto'g'ri — lekin model baribir ishlaydi.
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB print(f"{'model':<24} {'CV F1':>9} {'fit vaqti':>12}") print("-" * 48) for nom, m in [("GaussianNB", GaussianNB()), ("KNN (K=15)", Pipeline([("sc", StandardScaler()), ("m", KNeighborsClassifier(n_neighbors=15))])), ("LogisticRegression", Pipeline([("sc", StandardScaler()), ("m", LogisticRegression(max_iter=2000))]))]: t0 = time.perf_counter() b = cross_val_score(m, X, y, cv=5, scoring="f1") dt = (time.perf_counter()-t0)*1000 print(f"{nom:<24} {b.mean():>9.4f} {dt:>10.1f} ms")
model CV F1 fit vaqti ------------------------------------------------ GaussianNB 0.7291 12.2 ms KNN (K=15) 0.7064 34.9 ms LogisticRegression 0.7210 19.8 ms
show(pd.DataFrame([ {"Variant": "GaussianNB", "Qanday ma'lumot": "Uzluksiz sonlar", "Taxmini": "Har xususiyat normal taqsimlangan", "Misol": "Bo'y, vazn, narx"}, {"Variant": "MultinomialNB", "Qanday ma'lumot": "Sanoq (count)", "Taxmini": "So'z chastotalari", "Misol": "SPAM FILTRI — matn tasnifi"}, {"Variant": "BernoulliNB", "Qanday ma'lumot": "Binary (0/1)", "Taxmini": "So'z bor/yo'q", "Misol": "Qisqa matn"}, ]))
| Variant | Qanday ma'lumot | Taxmini | Misol |
|---|---|---|---|
| GaussianNB | Uzluksiz sonlar | Har xususiyat normal taqsimlangan | Bo'y, vazn, narx |
| MultinomialNB | Sanoq (count) | So'z chastotalari | SPAM FILTRI — matn tasnifi |
| BernoulliNB | Binary (0/1) | So'z bor/yo'q | Qisqa matn |
Naive Bayes ning haqiqiy uyi: matn
Son ustunlarda Naive Bayes o'rtacha ishlaydi. Lekin matn tasnifida u hali ham kuchli.
from sklearn.feature_extraction.text import CountVectorizer xatlar = [ "Tabriklaymiz siz yutdingiz pul oling hoziroq", "Bepul kredit tez pul oling ariza", "Ertaga yigilish soat 10 da bo`ladi", "Hisobot tayyor tekshirib bering iltimos", "Yutuq pul mukofot bepul oling", "Loyiha hujjatlari yuborildi korib chiqing", "Bepul bonus hoziroq oling pul", "Dars jadvali ozgardi xabar beraman", ] yorliq = [1, 1, 0, 0, 1, 0, 1, 0] # 1 = spam vec = CountVectorizer() Xt = vec.fit_transform(xatlar) nb = MultinomialNB().fit(Xt, yorliq) yangi = ["bepul pul oling hoziroq", "ertaga hisobot tayyor boladi"] e = nb.predict_proba(vec.transform(yangi)) for matn, p in zip(yangi, e): print(f' "{matn}"') print(f" spam ehtimoli: {p[1]:.4f} -> {'SPAM' if p[1] > 0.5 else 'oddiy xat'}")
"bepul pul oling hoziroq"
spam ehtimoli: 0.9967 -> SPAM
"ertaga hisobot tayyor boladi"
spam ehtimoli: 0.1111 -> oddiy xat
Atigi 8 ta misol bilan model ishladi. Bu — Naive Bayes ning asosiy afzalligi: juda kam ma'lumot bilan ham ishga tushadi.
Nol chastota muammosi va smoothing
Agar yangi xatda train da hech qachon uchramagan so'z bo'lsa, uning ehtimoli 0 bo'ladi. Ko'paytmada bitta nol — butun natija nol.
Yechim: Laplace smoothing — har bir sanoqqa kichik son qo'shiladi. sklearn da bu alpha=1.0 sukut bo'yicha yoqilgan.
alpha=0 qo'ysangiz, model birinchi notanish so'zda yiqiladi.
print("Smoothing ning ta'siri:") notanish = ["mutlaqo notanish sozlar bilan yozilgan xat"] for a in [1.0, 1e-10]: nb_a = MultinomialNB(alpha=a).fit(Xt, yorliq) p = nb_a.predict_proba(vec.transform(notanish))[0] print(f" alpha={a:<8} spam ehtimoli = {p[1]:.6f}")
Smoothing ning ta'siri: alpha=1.0 spam ehtimoli = 0.500000 alpha=1e-10 spam ehtimoli = 0.500000
Qachon qaysi birini tanlaysiz
show(pd.DataFrame([ {"Model": "KNN", "Qachon yaxshi": "Kichik ma'lumot, kam ustun, mahalliy naqsh", "Qachon yomon": "Ko'p ustun, katta ma'lumot, real vaqt", "Scaling": "SHART"}, {"Model": "GaussianNB", "Qachon yaxshi": "Juda tez baza kerak bo'lsa", "Qachon yomon": "Xususiyatlar kuchli bog'liq bo'lsa", "Scaling": "Kerak emas"}, {"Model": "MultinomialNB", "Qachon yaxshi": "MATN tasnifi, spam, kam ma'lumot", "Qachon yomon": "Uzluksiz sonlar", "Scaling": "Kerak emas"}, ]))
| Model | Qachon yaxshi | Qachon yomon | Scaling |
|---|---|---|---|
| KNN | Kichik ma'lumot, kam ustun, mahalliy naqsh | Ko'p ustun, katta ma'lumot, real vaqt | SHART |
| GaussianNB | Juda tez baza kerak bo'lsa | Xususiyatlar kuchli bog'liq bo'lsa | Kerak emas |
| MultinomialNB | MATN tasnifi, spam, kam ma'lumot | Uzluksiz sonlar | Kerak emas |
Naive Bayes ni baza sifatida ishlating
6-bobda DummyClassifier ni baza qilgan edik. Matn tasnifida esa MultinomialNB yaxshi ikkinchi baza: u bir zumda o'qitiladi va ko'pincha hayratlanarli natija beradi.
Agar sizning murakkab transformer modelingiz Naive Bayes dan oshmasa — muammo modelda emas, ma'lumotda.
Bugun nima o'rgandik
show(pd.DataFrame([ {"Tushuncha": "KNN g'oyasi", "Bitta jumla": "Hech narsa o'rganmaydi — qo'shnilarga qaraydi"}, {"Tushuncha": "KNN narxi", "Bitta jumla": "fit tez, predict sekin; butun train ni saqlaydi"}, {"Tushuncha": "KNN va scaling", "Bitta jumla": "Busiz katta shkalali ustun hammasini bosadi"}, {"Tushuncha": "K ning roli", "Bitta jumla": "Kichik K overfitting, katta K underfitting"}, {"Tushuncha": "O'lchov la'nati", "Bitta jumla": "Ustun ko'paysa hamma nuqta teng masofada bo'ladi"}, {"Tushuncha": "Naive Bayes", "Bitta jumla": "Bayes teoremasi + xususiyatlar mustaqil degan soxta taxmin"}, {"Tushuncha": "NB ning uyi", "Bitta jumla": "Matn tasnifi — MultinomialNB"}, {"Tushuncha": "Smoothing", "Bitta jumla": "alpha=1 nol chastota muammosini yechadi"}, ]))
| Tushuncha | Bitta jumla |
|---|---|
| KNN g'oyasi | Hech narsa o'rganmaydi — qo'shnilarga qaraydi |
| KNN narxi | fit tez, predict sekin; butun train ni saqlaydi |
| KNN va scaling | Busiz katta shkalali ustun hammasini bosadi |
| K ning roli | Kichik K overfitting, katta K underfitting |
| O'lchov la'nati | Ustun ko'paysa hamma nuqta teng masofada bo'ladi |
| Naive Bayes | Bayes teoremasi + xususiyatlar mustaqil degan soxta taxmin |
| NB ning uyi | Matn tasnifi — MultinomialNB |
| Smoothing | alpha=1 nol chastota muammosini yechadi |
Keyingi bob — 4-qismning oxirgisi: eng keng ko'chani izlaydigan model.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.