Pipeline: üks objekt, mis teeb kogu töö

Eeltöötlus ei ole asi, mida teed andmetega enne mudelit. Eeltöötlus on osa mudelist. Pipeline on scikit-learni viis seda kirja panna: imputeerija, kodeerija, skaleerija ja mudel ühes objektis, millel on fit ja predict nagu igal mudelil.

1Mis probleemi see lahendab

Kui teed eeltöötluse käsitsi, pead iga sammu kirjutama vähemalt kaks korda. Üks kord treeningandmetele, kus samm ka õpib midagi (mediaani, kategooriate loendi, keskmise ja hälbe) — see on fit_transform. Teine kord testandmetele, kus ta tohib ainult rakendada juba õpitut — see on transform. Ristvalideerimisel kordub sama veel viis korda.

Käsitsi: kolm sammu, kaks korda

# treeningandmed: õpib ja rakendab X_train = taida.fit_transform(X_train) X_train = kodeeri.fit_transform(X_train) X_train = skaleeri.fit_transform(X_train) mudel.fit(X_train, y_train) # testandmed: ainult rakendab — samad sammud, sama järjekord X_test = taida.transform(X_test) X_test = kodeeri.transform(X_test) X_test = skaleeri.transform(X_test) mudel.predict(X_test)

Kaheksa rida, millest igaühe võib unustada, valesse järjekorda panna või kogemata fit_transform'iks kirjutada. Ristvalideerimisega tuleb sama asi veel kord.

Torustikuga: kirjeldad üks kord

mudel = Pipeline([ ("taida", SimpleImputer(strategy="median")), ("kodeeri", OneHotEncoder(handle_unknown="ignore")), ("skaleeri", StandardScaler()), ("mudel", LogisticRegression()), ]) mudel.fit(X_train, y_train) mudel.predict(X_test)

Testandmetel käivad läbi täpselt samad sammud, samas järjekorras, nende parameetritega, mis õpiti treeningandmetelt. Sammu vahele jätta ei saa, sest käsitsi neid keegi ei rakenda.

Mis juhtub, kui üks samm testandmetel ununeb

Kolm tüüpilist unustamist. Kaks esimest annavad veateate kohe, kolmas ei anna midagi.

Unustatud sammMida mudel testandmetelt saabTagajärg
taida.transform read, kus pindala on endiselt NaN ValueError: Input X contains NaN
Mudel arvutab ennustuse nii, et korrutab iga tunnuse väärtuse oma kordajaga ja liidab kokku. NaN-iga korrutamine annab NaN-i ja liitmine samuti, nii et ennustus tuleks „puuduv väärtus”. Sellest poleks kellelegi kasu, seega scikit-learn ei lase arvutust alustadagi. Treeningandmetel seda viga ei tulnud, sest seal sai imputeerija oma töö tehtud.
kodeeri.transform veerg, kus on sõna "Kesklinn" ValueError: could not convert string to float: 'Kesklinn'
Mudel oskab arvutada ainult arvudega. Treeningul olid tal kodeeritud 0/1 veerud, nüüd tuleb sõna.
skaleeri.transform pindala 70 seal, kus treeningul oli 2,4 Veateadet ei tule.
Mudel õppis kordajad skaleeritud andmetel, kus väärtused jäid umbes vahemikku −2 kuni 2. Nüüd saab ta ette 70 ja korrutab selle sama kordajaga läbi. Ennustused on valed, aga programm töötab edasi. Ainus märk on halvem testitulemus, mille sa paned tõenäoliselt millegi muu süüks.
Kõige ohtlikum ongi kolmas rida. Esimesed kaks viga leiad üles sekundiga. Kolmandat ei leia keegi enne, kui hakkab imestama, miks mudel päriselus halvemini töötab kui katsetes.

Mida torustik annab

Mida torustik ei tee. Ta ei paranda andmeid sinu eest ega otsusta, milline samm on õige. Reeglipõhine puhastus — tüübid, kirjavead, duplikaadid, võimatud väärtused — tehakse enne jagamist ja see ei kuulu torustikku, sest ei õpi andmetest midagi.

2Anatoomia

Pipeline võtab nimega sammude loendi. Kõik peale viimase peavad olema teisendajad (oskavad fit ja transform), viimane võib olla hinnang ehk mudel (oskab fit ja predict).

from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression mudel = Pipeline([ ("taida", SimpleImputer(strategy="median")), # teisendaja ("skaleeri", StandardScaler()), # teisendaja ("mudel", LogisticRegression()), # hinnang, alati viimane ]) mudel.fit(X_train, y_train) # fit_transform igal teisendajal, siis fit mudelil mudel.predict(X_test) # transform igal teisendajal, siis predict mudel.score(X_test, y_test)
KutseMis sammudega juhtub
fit(X, y)Iga teisendaja: fit_transform — õpib parameetrid ja annab tulemuse edasi. Viimane samm: fit.
predict(X)Iga teisendaja: ainult transform varem õpitud parameetritega. Viimane samm: predict.
transform(X)Töötab, kui viimane samm on teisendaja (torustik ilma mudelita).

Nimed on sinu valida, aga nad ei ole kaunistus: nende kaudu pääsed sammudeni (mudel.named_steps["skaleeri"]) ja nende kaudu käivad parameetrite nimed otsingus (osa 6). Kui nimi pole oluline, teeb make_pipeline(...) need ise, väiketähtedega klassinimedest.

3Samm-sammult läbi ühe fit'i ja ühe predict'i

Kaks rida koodi — mudel.fit(X_train, y_train) ja mudel.predict(X_test) — teevad tegelikult kümme asja. Siin on need kümme asja ükshaaval lahti võetud, väikese näidisandmestikuga: kuus treeningrida ja kaks testrida, üks arvuline tunnus (pindala, ühel real puudu) ja üks kategooriline (seisukord).

Mida teha: vajuta „Edasi” ja liigu etapp haaval lõpuni. Üles saab ka otse numbrile klõpsates.
Mis on etapid 1–5: treeningfaas. Iga samm vaatab treeningandmeid, õpib sealt mingi arvu ja jätab selle meelde, ning teisendab siis andmed.
Mis on etapid 6–10: testfaas. Samad sammud samas järjekorras, aga nüüd ei õpi keegi enam midagi — kasutatakse treeningul meelde jäetud arve.
Mida jälgida: vasakul muutub tabel, paremal kasvab nimekiri „Mida torustik mäletab”. Vaata, millal see nimekiri kasvab ja millal mitte.
Mis on lõpuks: testread on jõudnud täpselt samasse kujusse nagu treeningread — sama arv veerge, samas järjekorras, sama skaalaga — ja alles siis saab mudel ennustada.

Kaks asja, mida siit kaasa võtta. Esiteks: testrea puuduv väärtus täideti treeningandmete mediaaniga, mitte testandmete omaga — testandmetest ei arvutatud midagi. Teiseks: testis esinenud tundmatu kategooria ei lõhkunud midagi, sest kodeerija teadis juba treeningust, millised veerud eksisteerivad.

4ColumnTransformer: eri veerud, eri kohtlemine

Üks torustik rakendab iga sammu kõigile veergudele. Päris andmestikus on aga arvud, nominaalsed ja järjestatud kategooriad, mis vajavad eri töötlust. ColumnTransformer jagab veerud harudesse, töötleb neid paralleelselt ja paneb tulemuse kõrvuti kokku.

Allpool on kuue veeruga näidisandmestik üürikorteritest. Iga veeru kõrval on rippmenüü: sina otsustad, millisesse harusse see veerg läheb või kas ta üldse mudelini jõuab. Vaikimisi valikud on juba mõistlikud.

Mida teha: muuda ühte valikut korraga ja vaata, mis kolmest kohast muutub.
1. Loend sellest, mitu tunnust ja millise nimega mudelini jõuab. Üks tekstiveerg võib tekitada mitu 0/1 veergu.
2. Hinnang iga veeru all: kas see valik on õige, kas ta töötab mööndustega või on vale — ja miks.
3. Kood kõige all — see kirjutatakse ümber iga valikuga ja täpselt sellise koodi sa päriselt kirjutaksidki.

5Miks just ristvalideerimisel on see oluline

Ristvalideerimine jagab treeningandmed viieks ja igas voorus on neli osa treeningus ning üks valideerimises. Kui imputeerija õppis mediaani enne seda jagamist, sisaldab iga vooru treeninginfo ka valideerimisosa väärtusi. Vaata, kuidas muutub täiteväärtus.

# vale: eeltöötlus on juba tehtud, cross_val_score ei saa seda enam lahutada X_t = imputer.fit_transform(X_train) cross_val_score(LogisticRegression(), X_t, y_train, cv=5) # õige: torustik läheb sisse tervikuna, iga voor fitib selle oma osal cross_val_score(mudel, X_train, y_train, cv=5, scoring="roc_auc")

Mõju suurus sõltub andmetest: kui puuduvaid väärtusi on vähe ja jaotus on ühtlane, on vahe väike. Kui eeltöötlus õpib midagi tugevat (kategooriate loend, sihtkodeerimine, tunnuste valik), võib vahe olla suur. Vahe suund on alati sama: ilma torustikuta tuleb tulemus liiga ilus.

6Seadete läbiproovimine

Parameetrid on seaded, mille sa ise ette annad: kas täita puuduvad väärtused mediaani või keskmisega, kui tugev on mudeli regulariseerimine. Õiget väärtust ei tea keegi ette. GridSearchCV proovib kõik antud variandid läbi, mõõdab igaühe ristvalideerimisega ja ütleb, milline oli parim.

Kuna torustik on üks objekt, saab korraga proovida nii eeltöötluse kui mudeli seadeid — näiteks kas mediaan koos nõrga regulariseerimisega töötab paremini kui keskmine koos tugevaga. Käsitsi tehtud eeltöötlusega see ei õnnestu, sest GridSearchCV näeb ainult mudelit.

Kuidas parameetrile viidata. Tuleb öelda, millise sammu seadet sa muudad. Nimi kirjutatakse nagu aadress, osad eraldatud kahe alakriipsuga: kõigepealt välimise sammu nimi, siis selle sees oleva sammu nimi, lõpuks parameeter. Näiteks eeltootlus__arv__taida__strategy loetakse nii: torustiku samm eeltootlus → selle sees haru arv → selle sees samm taida → selle parameeter strategy. Nimed on needsamad, mille sa ise torustikku kirjutades andsid.

Vali allpool samm ja parameeter — leht paneb nime kokku ja näitab, mida see seade teeb.

Kui sa ei mäleta nime, küsi seda objektilt endalt: mudel.get_params().keys() loetleb kõik, mida otsida saab. Sama nimeskeem kehtib ka set_params() juures.

7Levinud veateated

Torustikuga töötades tulevad vead muus kohas, kui käsitsi töötades. Siin on need, mis esimesel korral kindlasti ette tulevad.

8Spikker

Mida mis klass teeb ja kust dokumentatsiooni leiab.

Terviknäide

from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.linear_model import LogisticRegression import joblib X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, stratify=y, random_state=42) eeltootlus = ColumnTransformer([ ("arv", Pipeline([ ("taida", SimpleImputer(strategy="median")), ("skaleeri", StandardScaler()), ]), ["pindala", "tube"]), ("nom", Pipeline([ ("taida", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore")), ]), ["linnaosa"]), ("jrk", OrdinalEncoder(categories=[["halb", "keskmine", "hea"]]), ["seisukord"]), ], remainder="drop", verbose_feature_names_out=False) mudel = Pipeline([ ("eeltootlus", eeltootlus), ("klassifikaator", LogisticRegression(max_iter=1000)), ]) ruudustik = { "eeltootlus__arv__taida__strategy": ["median", "mean"], "klassifikaator__C": [0.1, 1, 10], } otsing = GridSearchCV(mudel, ruudustik, cv=5, scoring="roc_auc") otsing.fit(X_train, y_train) print(otsing.best_params_, otsing.best_score_) print(otsing.score(X_test, y_test)) # test alles lõpus joblib.dump(otsing.best_estimator_, "mudel.joblib") # kogu töö ühes failis