Fəsil 07 · 9 dəq oxu

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ü, və bias–variance 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ə əsaslı . 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 Bayes teoremi-nə əsaslanır. müşahidə etdikdən sonra ehtimalı belə yazılır:

P(L | features) = P(features | L) · P(L) / P(features)

İki 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 birgə paylanmasını öyrənmək ümumiyyətlə mümkün deyil. Buna görə "naive" fərziyyə edilir: məlum olanda 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 ehtimalının hasilinə çevrilir.

Gaussian naive Bayes hər class üçün hər 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ə 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 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 üçün yaxşı, 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 üçün düz xətt, çox üçü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ü çevrilməsindədir. Model əmsallara görə xətti olmalıdır, x-in özünə görə yox. Basis function regression 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 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. 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 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 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. : 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ə 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, 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 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ırTraining 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ırC və gamma ilə seçilməlidir və nəticə onlardan güclü asılıdır
Margin-ə əsaslandığı üçün yaxşı predict_proba default-da yoxdur; probability=True daxili 5-fold işlədir və fit-i xeyli yavaşladır

Decision tree-lər və random forest-lər

ardıcıl sual-cavab oyunudur: "x1 > 0.5?" Bəli → sol budaq, xeyr → sağ. Hər node-da alqoritm bir 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 edir, amma fərqli yerlərdə, və orta götürəndə səhvlər bir-birini ləğv edir. bu ideyanın üzərinə bir hiylə əlavə edir: hər split-də bütün 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 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 (continuous, yüksək cardinality) üstünlük verir və korrelyasiyalı arasında əhəmiyyəti təsadüfi paylayır. Daha etibarlı ölçü permutation importance-dır: qarışdırıb validation score-un nə qədər düşdüyünə baxmaq.

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, 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əfiZəif tərəfiTipik istifadə
Naive Bayes daxilində müstəqildirÇox sürətli, parametrsiz, yüksək ölçüdə işləyirEhtimallar deyil, mürəkkəb sərhəd tuta bilmirMətn , sürətli baseline
Linear regressionHədəf (və ya basis-lərin) xətti kombinasiyasıdırŞərh olunan əmsallar, qapalı həll, ilə stabilQeyri-xətti əlaqələri yalnız düzgün basis ilə tuturTrend analizi, effektlərin kəmiyyət qiymətləndirilməsi
SVMClass-lar kernel fəzasında geniş margin ilə ayrılırYüksək ölçüdə güclü, yaddaşa qənaətli, hamar sərhədBöyük n-də yavaş, C/gamma tuning tələb edirOrta ölçülü, təmiz dataset-lər, şəkil və mətn
Fərziyyə demək olar yoxdurRobust, paralel, scaling tələb etmir, ehtimal verirİzah çətin, extrapolate etmir, model böyükdürTabular data üçün ilk ciddi model

Praktik qayda: naive Bayes və ya linear model ilə başlayın, sonra 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 sübut edəndə qəbul edilir.

Tapşırıqlar

  1. 20 Newsgroups-dan dörd kateqoriya seçin və TfidfVectorizer + MultinomialNB pipeline-ı qurun. Confusion matrix çəkin və ən çox qarışdırılan iki kateqoriyanı tapın. Sonra predict_proba də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.
  2. Sinus funksiyasına noise əlavə edib 50 nöqtə yaradın. 3, 9 və 25 dərəcəli polynomial basis ilə LinearRegression, Ridge və Lasso fit 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.
  3. make_moons dataset-ində SVC(kernel="rbf") üçün C və gamma üzrə grid search aparın, hər kombinasiyada support vector-ların sayını və score-unu cədvələ yazın. Sonra eyni data-da RandomForestClassifier train 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.