Supervised model-lər dərindən: Naive Bayes-dən random forest-ə
Supervised model-lər dərindən: Naive Bayes-dən random forest-ə
Əvvəlki fəsildə scikit-learn-in Estimator API-sini, train/test bölgüsünü, cross-validation-ı və bias–variance trade-off-unu gördük. Bu alətlər hər model üçün eynidir: fit, predict, score. Amma model-lərin özləri eyni deyil. Hər biri data haqqında müəyyən fərziyyə edir və bu fərziyyə doğru olanda parlayır, yanlış olanda isə səssizcə uğursuz olur. Bu fəsildə dörd model ailəsini açırıq: naive Bayes, linear regression, support vector machine-lər və decision tree əsaslı random forest-lər. Məqsəd API-ni əzbərləmək deyil, hər model-in niyə belə davrandığını başa düşməkdir. Alqoritmlərin riyazi detalları və ümumi evaluation metodologiyası Machine Learning kitabında daha dərindən verilir; burada isə Python Data Science Handbook-un yanaşmasına sadiq qalırıq: intuisiya, kiçik nümunə, scikit-learn.
Naive Bayes: generative classification
Naive Bayes classifier-ləri Bayes teoremi-nə əsaslanır. Feature-ləri müşahidə etdikdən sonra label-ın ehtimalı belə yazılır:
P(L | features) = P(features | L) · P(L) / P(features)
İki label-ı müqayisə etmək üçün məxrəc lazım deyil; yalnız P(features | L) və P(L) hasillərinin nisbəti maraqlıdır. P(features | L) isə hər class üçün data-nın necə yarandığını təsvir edən generative model-dir. Yəni naive Bayes "sərhədi haradan çəkim?" sualını yox, "bu class-ın nümunələri hansı paylanmadan gəlir?" sualını verir. Sonra yeni nöqtə üçün hər paylanmanın onu nə qədər yaxşı izah etdiyinə baxır.
Bütün feature-lərin birgə paylanmasını öyrənmək ümumiyyətlə mümkün deyil. Buna görə "naive" fərziyyə edilir: label məlum olanda feature-lər bir-birindən müstəqildir. Bu fərziyyə demək olar həmişə yanlışdır, amma class-ların sıralamasını çox vaxt pozmur və hesablamanı trivial edir: birgə ehtimal sadəcə hər feature-in ehtimalının hasilinə çevrilir.
Gaussian naive Bayes hər class üçün hər feature-in ayrıca Gaussian paylanmasına tabe olduğunu fərz edir. Fit etmək class üzrə orta və variance hesablamaqdır, başqa heç nə:
from sklearn.naive_bayes import GaussianNB
model = GaussianNB().fit(X_train, y_train)
proba = model.predict_proba(X_test) # hər class üçün ehtimal
İki class-ın Gaussian-ları fərqli variance-a malik olanda sərhəd düz xətt yox, kvadratik əyri olur.
Multinomial naive Bayes isə feature-lərin say olduğunu fərz edir və mətn üçün təbii seçimdir. Sənəd bag of words kimi təqdim olunur: sözlərin sırası atılır, yalnız hər sözün neçə dəfə rast gəlindiyi saxlanılır. TF-IDF bu sayları nadir sözləri daha ağır çəkəcək şəkildə miqyaslayır. 20 Newsgroups tipli məsələdə, yəni forum yazılarını mövzulara görə ayırmaqda, bu kombinasiya bir neçə sətirdə işlək classifier verir:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
model.fit(train.data, train.target)
model.predict(["sending a payload to the ISS"]) # -> sci.space
Naive Bayes-in bir məlum qüsuru var: predict_proba nəticələri etibarsızdır. Müstəqillik fərziyyəsinə görə model eyni sübutu dəfələrlə sayır və ehtimallar 0-a və ya 1-ə doğru itələnir. Lakin bu ehtimalların sırası adətən düzgündür: ən yüksək ehtimallı class çox vaxt doğru class-dır. Nəticədə naive Bayes classification üçün yaxşı, calibrated ehtimal tələb edən məsələlər üçün isə pisdir.
Nə vaxt istifadə etməli: training və prediction çox sürətlidir, tənzimlənəcək parametri demək olar yoxdur, yüksək ölçülü data-da (mətn kimi) heyrətamiz dərəcədə yaxşı işləyir və hər məsələ üçün ideal ilk baseline-dır. Daha mürəkkəb model-in bu baseline-ı nə qədər üstələdiyini bilmədən onun dəyərini qiymətləndirmək olmaz.
Linear regression: sadədən basis function-lara
Linear regression ən sadə regression model-idir. Bir feature üçün düz xətt, çox feature üçün hiper-müstəvidir:
y = a0 + a1·x1 + a2·x2 + ... + an·xn
Fit etmək kvadratik səhvlərin cəmini minimallaşdıran əmsalları tapmaqdır və bunun qapalı həlli var: LinearRegression().fit(X, y) bir matris əməliyyatıdır. coef_ və intercept_ atributları əmsalları qaytarır.
Linear model-in gücü feature-lərin çevrilməsindədir. Model əmsallara görə xətti olmalıdır, x-in özünə görə yox. Basis function regression feature-ləri x → [f1(x), f2(x), ...] şəklində genişləndirir və üzərində adi linear regression işlədir. Polynomial basis x, x², x³ istifadə edir; Gaussian basis isə oxun müxtəlif nöqtələrinə yerləşdirilmiş təpəciklərdir. İkincisi scikit-learn-də hazır yoxdur, amma öz transformer-inizi yazsanız bir neçə sətirdir:
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
poly_model = make_pipeline(PolynomialFeatures(7), LinearRegression())
poly_model.fit(x[:, None], y)
Burada tanış təhlükə gizlənir. Basis function-ların sayı artdıqca model data-nı yox, noise-u izləməyə başlayır. Əlaməti əmsallardır: qonşu basis-lər bir-birini ləğv etmək üçün çox böyük müsbət və mənfi dəyərlər alır. Regularization bu əmsalları cəzalandırır.
Ridge regression (L2) əmsalların kvadratlarının cəmini loss-a əlavə edir:
loss = Σ (y_pred − y)² + α · Σ θ²
Lasso regression (L1) isə mütləq qiymətlərin cəmini:
loss = Σ (y_pred − y)² + α · Σ |θ|
Fərq kiçik görünür, nəticəsi isə böyükdür. Ridge bütün əmsalları kiçildir, amma heç birini sıfır etmir. Lasso bir çox əmsalı tam sıfıra endirir, yəni sparse model verir və faktiki olaraq feature selection edir. Səbəb həndəsidir: L1 cəzasının səviyyə çoxluğu romb formasındadır və künclər oxların üzərindədir; loss ilə kəsişmə çox vaxt məhz o künclərə düşür, küncdə isə bəzi əmsallar sıfırdır. α parametri hər iki halda cross-validation ilə seçilir.
Əmsalların şərhi linear model-in əsas üstünlüyüdür. Təsəvvür edin, körpü üzərindən gündəlik velosiped trafikini proqnozlaşdırırsınız. Feature-lər: həftənin günü üçün dummy sütunlar, temperatur, yağıntı, gündüz saatları, bayram flag-i. Fit etdikdən sonra coef_ oxunaqlı hekayə danışır: hər dərəcə istilik gündə təxminən +60 velosipedçi, hər santimetr yağış −400, həftəsonu günləri −1500. Bu rəqəmlər correlation-dır, causation yox, və feature-lər bir-biri ilə korrelyasiyalı olanda (gündüz saatları və temperatur) əmsallar arasında "paylaşılır". Amma heç bir black-box model bu qədər birbaşa izahat vermir.
Support vector machine-lər: margin-i maksimallaşdırmaq
Naive Bayes hər class-ı modelləşdirirdi. Support vector machine (SVM) isə discriminative yanaşmadır: class-ların paylanması ilə maraqlanmır, yalnız onları ayıran sərhədi axtarır.
İki ayrılabilən class üçün sonsuz sayda düz xətt mövcuddur. Hansını seçməli? SVM-in cavabı: hər iki class-dan ən uzaq olanı. Sərhədin ətrafında ən yaxın nöqtəyə qədər boş zolağa margin deyilir və SVM bu margin-i maksimallaşdırır. Margin-ə toxunan nöqtələr support vector-lardır. Qalan nöqtələrin yeri model üçün əhəmiyyətsizdir: onları hərəkət etdirsəniz, margin-i keçmədikcə sərhəd dəyişmir. Fit edilmiş model support_vectors_ atributunda məhz bu nöqtələri saxlayır.
Real data nadir hallarda tam ayrılabilir. Soft margin bəzi nöqtələrin margin-in içinə və ya sərhədin o biri tərəfinə keçməsinə icazə verir, hər pozuntuya görə cəza tətbiq edir. Cəzanın ağırlığı C parametridir: böyük C sərt margin, az pozuntu, overfitting riski; kiçik C yumşaq margin, daha çox nöqtə margin-in içində, daha hamar sərhəd.
Bəs class-lar düz xəttlə ümumiyyətlə ayrılmırsa? Məsələn, bir class dairənin içində, digəri ətrafındadırsa. Həll feature-ləri daha yüksək ölçülü fəzaya proyeksiya etməkdir: r = exp(−x²) kimi üçüncü ölçü əlavə etsəniz, dairə müstəvi ilə ayrılır. Bunu hər nöqtə ətrafında etmək bahadır, amma kernel trick proyeksiyanı heç vaxt açıq hesablamadan onun nəticəsinə çatmağa imkan verir: SVM-ə lazım olan yalnız nöqtələr arasındakı skalyar hasildir və kernel funksiyası həmin hasili yüksək ölçülü fəzada birbaşa verir. RBF kernel sonsuz ölçülü proyeksiyaya uyğun gəlir və gamma parametri ilə hər nöqtənin təsir radiusunu tənzimləyir:
from sklearn.svm import SVC
model = SVC(kernel="rbf", C=10, gamma=0.1).fit(X_train, y_train)
print(len(model.support_vectors_)) # sərhədi müəyyən edən nöqtələr
| Üstünlük | Çatışmazlıq |
|---|---|
| Yalnız support vector-lara əsaslanır: yaddaşa qənaətlidir, yüksək ölçülü data-da yaxşıdır | Training vaxtı nümunə sayı ilə kvadratik və ya kubik artır, yüz minlərlə sətirdə bahalıdır |
| Kernel ilə qeyri-xətti sərhədlər hamar və ifadəli alınır | C və gamma cross-validation ilə seçilməlidir və nəticə onlardan güclü asılıdır |
| Margin-ə əsaslandığı üçün yaxşı generalization | predict_proba default-da yoxdur; probability=True daxili 5-fold cross-validation işlədir və fit-i xeyli yavaşladır |
Decision tree-lər və random forest-lər
Decision tree ardıcıl sual-cavab oyunudur: "x1 > 0.5?" Bəli → sol budaq, xeyr → sağ. Hər node-da alqoritm bir feature və bir hədd seçir ki, bölünmədən sonra alt qruplar mümkün qədər "təmiz", yəni bir class-a yaxın olsun. Təmizlik Gini impurity və ya entropy ilə ölçülür. Sərhədlər həmişə oxlara paraleldir.
Problem odur ki, tree bölməyə davam etdikcə hər yarpaqda bir-iki nümunə qalır və model noise-u əzbərləyir. Daha pisi, eyni data-nın iki fərqli yarısında train edilmiş tree-lər tamam fərqli görünür. Bu yüksək variance-dır: model data-dakı kiçik dəyişikliklərə həddindən artıq həssasdır.
Variance-ı azaltmağın klassik yolu ortalamaqdır. Bagging (bootstrap aggregating) data-dan təkrarlı təsadüfi alt çoxluqlar götürür, hər birində bir tree train edir və proqnozları səsvermə ilə birləşdirir. Hər tree overfit edir, amma fərqli yerlərdə, və orta götürəndə səhvlər bir-birini ləğv edir. Random forest bu ideyanın üzərinə bir hiylə əlavə edir: hər split-də bütün feature-lər yox, onların təsadüfi alt çoxluğu nəzərdən keçirilir. Bu, tree-ləri bir-birindən daha müstəqil edir və ortalama daha güclü olur:
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=200, n_jobs=-1, random_state=0)
model.fit(X_train, y_train)
model.predict_proba(X_test) # tree-lərin səs nisbəti
Eyni ideya regression üçün də işləyir: RandomForestRegressor hər yarpaqda ortalama qaytarır və nəticədə pilləli, amma kifayət qədər çevik əyri alınır. Parametrik forma seçmədən (sinus? polynom?) mürəkkəb asılılıqları tutur. Bir məhdudiyyət: tree-lər training data-nın diapazonundan kənara extrapolate edə bilmir, kənarda proqnoz sabit qalır.
feature_importances_ atributu cazibədar, amma aldadıcıdır. Impurity əsaslı importance çox unikal dəyəri olan feature-lərə (continuous, yüksək cardinality) üstünlük verir və korrelyasiyalı feature-lər arasında əhəmiyyəti təsadüfi paylayır. Daha etibarlı ölçü permutation importance-dır: feature-i qarışdırıb validation score-un nə qədər düşdüyünə baxmaq.
Random forest-in güclü tərəfləri: training və prediction sürətlidir və trivial paralelləşir, predict_proba səsvermədən təbii şəkildə alınır, feature scaling tələb etmir və default parametrlərlə belə yaxşı işləyir. Zəif tərəfi: yüzlərlə tree-nin birgə qərarını izah etmək çətindir; tək tree-nin oxunaqlılığı itirilir.
Dörd ailə yan-yana
| Model | Əsas fərziyyə | Güclü tərəfi | Zəif tərəfi | Tipik istifadə |
|---|---|---|---|---|
| Naive Bayes | Feature-lər label daxilində müstəqildir | Çox sürətli, parametrsiz, yüksək ölçüdə işləyir | Ehtimallar calibrated deyil, mürəkkəb sərhəd tuta bilmir | Mətn classification, sürətli baseline |
| Linear regression | Hədəf feature-lərin (və ya basis-lərin) xətti kombinasiyasıdır | Şərh olunan əmsallar, qapalı həll, regularization ilə stabil | Qeyri-xətti əlaqələri yalnız düzgün basis ilə tutur | Trend analizi, effektlərin kəmiyyət qiymətləndirilməsi |
| SVM | Class-lar kernel fəzasında geniş margin ilə ayrılır | Yüksək ölçüdə güclü, yaddaşa qənaətli, hamar sərhəd | Böyük n-də yavaş, C/gamma tuning tələb edir | Orta ölçülü, təmiz dataset-lər, şəkil və mətn feature-ləri |
| Random forest | Fərziyyə demək olar yoxdur | Robust, paralel, scaling tələb etmir, ehtimal verir | İzah çətin, extrapolate etmir, model böyükdür | Tabular data üçün ilk ciddi model |
Praktik qayda: naive Bayes və ya linear model ilə başlayın, sonra random forest-ə keçin, SVM-i isə data orta ölçülü olanda və hər faiz əhəmiyyətli olanda sınayın. Hər keçiddə əvvəlki fəsildəki validation curve-lərə qayıdın: model-in dəyişməsi faydanı yalnız cross-validation sübut edəndə qəbul edilir.
Tapşırıqlar
- 20 Newsgroups-dan dörd kateqoriya seçin və
TfidfVectorizer+MultinomialNBpipeline-ı qurun. Confusion matrix çəkin və ən çox qarışdırılan iki kateqoriyanı tapın. Sonrapredict_probadəyərlərinin histogramına baxın və niyə demək olar hamısının 0-a və 1-ə yığıldığını izah edin. - Sinus funksiyasına noise əlavə edib 50 nöqtə yaradın. 3, 9 və 25 dərəcəli polynomial basis ilə
LinearRegression,RidgevəLassofit edin. Hər halda əmsalların mütləq qiymətlərini çap edin və lasso-nun hansı əmsalları sıfıra endirdiyini şərh edin. make_moonsdataset-indəSVC(kernel="rbf")üçünCvəgammaüzrə grid search aparın, hər kombinasiyada support vector-ların sayını və cross-validation score-unu cədvələ yazın. Sonra eyni data-daRandomForestClassifiertrain edin, sərhədləri yan-yana çəkin və hansı model-in sərhədinin oxlara paralel pillələrdən ibarət göründüyünü izah edin.