Po co ci w ogóle uczenie maszynowe i czy to dobry moment, by zacząć
Intuicyjne wyjaśnienie uczenia maszynowego na zwykłym przykładzie
Uczenie maszynowe to sposób programowania komputerów, w którym zamiast pisać sztywne reguły typu „jeśli – to”, oddajesz decyzje w ręce modelu uczącego się na podstawie danych. Najprostszy przykład to filtr spamu w skrzynce e-mail. Nikt nie spisał ręcznie listy wszystkich możliwych spamerskich wiadomości. Zamiast tego model analizuje treści wiadomości oznaczonych przez ludzi jako „spam” lub „nie spam” i uczy się, jakie wzorce tekstu, nadawców, nagłówków czy linków częściej występują w spamie.
Podobnie działają rekomendacje filmów czy seriali. Serwis streamingowy nie zna cię osobiście, ale widzi historię twoich odtworzeń i zachowań użytkowników podobnych do ciebie. Analizuje dane: gatunki, aktorów, oceny, czas oglądania. Na tej podstawie „zgaduje”, co może ci się spodobać. To właśnie uczenie maszynowe w praktyce: zamiana surowych danych w przewidywania lub decyzje.
W Pythona wchodzi się tu idealnie, bo większość popularnych bibliotek ML jest pisana właśnie z myślą o nim. Kiedy połączysz proste skrypty z możliwością uczenia modeli, zwykły kod zaczyna podejmować sprytne decyzje zamiast prostych reakcji na instrukcje.
Tradycyjny program vs model uczący się z danych
W klasycznym programowaniu określasz zasady działania z góry. Na przykład, pisząc algorytm do nadawania rabatów, możesz założyć: jeśli klient kupił więcej niż 5 produktów – przyznaj 10% zniżki. Kod jest jasno zdefiniowany, a komputer jedynie bardzo szybko wykonuje to, co mu kazałeś. Problem zaczyna się tam, gdzie reguł jest zbyt dużo lub są zbyt mgliste, by sensownie je rozpisać.
Model uczenia maszynowego odwraca logikę. Ty nie wymyślasz reguł. Zamiast tego dajesz modelowi:
- dane wejściowe – opis sytuacji (np. parametry mieszkania: metraż, liczba pokoi, dzielnica),
- etykiety – poprawne odpowiedzi (np. rzeczywista cena, za którą mieszkanie zostało sprzedane).
Model sam „dopasowuje” własne parametry tak, by przewidywane odpowiedzi były jak najbliższe tym prawdziwym. Twoja rola to raczej przygotowanie danych, wybór modelu, kontrola jakości i interpretacja wyników, a nie ręczne pisanie reguł.
Co realnie daje znajomość ML w Pythonie
Znajomość uczenia maszynowego w Pythonie przestaje być egzotyczną umiejętnością. W praktyce otwiera kilka drzwi naraz. Po pierwsze, daje ci możliwość budowania własnych projektów: od prostego systemu przewidywania cen na OLX, przez analizę danych z własnego biznesu, aż po automatyczne rekomendacje treści. Nawet jeśli nigdy nie pójdziesz „na etat” jako data scientist, takie kompetencje w CV wyraźnie cię wyróżniają.
Po drugie, lepiej rozumiesz, co stoi za modnym hasłem „AI”. Zamiast wierzyć w ogólne marketingowe slogany, wiesz, czym różni się prosty model regresyjny od głębokiej sieci neuronowej i kiedy wystarczy zwykła regresja liniowa, a kiedy sens ma bardziej złożony algorytm. Taki trzeźwy ogląd bardzo się przydaje w wielu firmach, które planują „wdrożyć AI”, ale nie do końca wiedzą, co to znaczy.
Czy twój problem naprawdę wymaga uczenia maszynowego
Wielu początkujących szuka okazji do użycia ML na siłę. Tymczasem bardzo często wystarczy proste filtrowanie, kilka instrukcji warunkowych lub zwykłe raporty. Żeby ocenić, czy uczenie maszynowe ma sens, możesz zadać sobie kilka pytań:
- Czy problem polega na przewidywaniu liczby lub kategorii na podstawie wielu cech? (np. jasna prognoza, czy klient odejdzie, jaka będzie cena, czy mail to spam).
- Czy nie potrafisz wypisać prostych, jednoznacznych reguł rozwiązania problemu?
- Czy masz lub możesz zdobyć dane z przykładami: „wejście → poprawna odpowiedź”?
- Czy odpowiedź nie musi być w 100% idealna, ale „wystarczająco dobra” jest akceptowalna?
Jeśli na większość z nich odpowiadasz twierdząco, projekt z uczeniem maszynowym w Pythonie może mieć sens. Jeśli nie – może lepiej zacząć od dobrze napisanego skryptu, prostego algorytmu statycznego albo zwykłego raportu w Excelu. ML to narzędzie o dużej mocy, ale nie zawsze jest potrzebne; miałbyś wtedy młotek, który desperacko szuka gwoździa.
Minimalna teoria, bez której trudno ruszyć z miejsca
Dane wejściowe, cechy i etykiety – obraz całości
Na samym początku wystarczy myśleć o uczeniu maszynowym jak o sprytnym dopasowywaniu funkcji do danych. Każdy wiersz w tabeli to jeden przykład (obserwacja), a każda kolumna to pewna cecha. Cecha to po prostu liczba lub kategoria, która w jakiś sposób opisuje przykład, np. „liczba pokoi” albo „rodzaj przeglądarki użytkownika”.
Część cech traktujemy jako wejście (input), a jedną konkretną kolumnę wybieramy jako etykietę (label), czyli to, co model ma przewidywać. W zadaniu przewidywania ceny mieszkania cechami mogą być metraż, piętro, lokalizacja, rok budowy, a etykietą będzie cena transakcyjna. Model patrzy na setki takich obserwacji i stara się znaleźć zależności między wejściem a etykietą.
Regresja i klasyfikacja – dwa najważniejsze typy zadań
W praktyce większość zadań dla początkujących w ML wpada do jednego z dwóch koszyków:
- Regresja – model przewiduje liczbę. Przykłady: przewidywanie ceny mieszkania, wagi paczki, liczby wyświetleń filmu.
- Klasyfikacja – model przewiduje etykietę (kategorię). Często binarną: tak/nie, spam/nie spam, odejdzie/zostanie. Może też być wieloklasowa, np. przewidywanie gatunku kwiatu.
Regresja odpowiada na pytanie „ile?”, a klasyfikacja na pytanie „który wariant?”. W Pythonie, korzystając z biblioteki scikit-learn, te zadania wyglądają bardzo podobnie: przygotowujesz dane w tablicy cech i wektorze etykiet, a zmienia się tylko typ modelu i metryka oceny.
Zbiór treningowy, walidacyjny i testowy – dlaczego aż trzy
Żeby sprawdzić, czy model naprawdę umie generalizować, nie wystarczy ocenić go na danych, na których się uczył. To trochę jakby uczeń miał test, który wcześniej przepisał z kartki: wynik będzie świetny, ale nie będzie nic mówił o jego rzeczywistej wiedzy.
Dane dzieli się zazwyczaj na trzy części:
- Treningowe (train) – na tych przykładach model się uczy, dopasowuje parametry.
- Walidacyjne (validation) – na nich sprawdzasz, jak model radzi sobie w trakcie strojenia ustawień (np. głębokości drzewa, liczby sąsiadów).
- Testowe (test) – są trzymane „w zamrażarce” i ujawniane dopiero na końcu, żeby dać uczciwy obraz tego, jak model zadziała na nowych, nieznanych danych.
Na początku często łączy się walidację z testem i robi po prostu podział train/test. Bardziej zaawansowane projekty korzystają z walidacji krzyżowej. Najważniejsza idea jest jednak prosta: model nie może być oceniany tylko na danych, na których się uczył.
Overfitting i underfitting – kiedy model umie za dużo albo za mało
Dwa słowa, które prędko się pojawią przy każdym pierwszym projekcie machine learning w Pythonie, to overfitting i underfitting.
Overfitting to sytuacja, gdy model jest aż za bardzo dopasowany do danych treningowych. Zapamiętuje szczegóły, szum, wyjątki, przez co błyszczy na znanych przykładach, a na nowych radzi sobie marnie. To jak uczeń, który nauczył się testu na pamięć, ale lekkie przeredagowanie pytania zupełnie go wykoleja.
Underfitting działa odwrotnie – model jest zbyt prosty, by złapać prawdziwy wzorzec w danych. Jest jak uczeń, który przeczytał tylko nagłówki rozdziałów i nie rozumie szczegółów. Wyniki są słabe zarówno na danych treningowych, jak i testowych.
Celem jest złapanie złotego środka: model na tyle złożony, by uchwycić zależności, ale na tyle uogólniający, by nie przyklejał się do każdego przypadku osobno. Walidacja krzyżowa, regularizacja i zdrowy rozsądek co do liczby parametrów pomagają utrzymać równowagę.
Model i parametry na przykładzie regresji liniowej
Model można rozumieć jako pewien przepis matematyczny, który przekształca cechy wejściowe w przewidywanie. W najprostszym wydaniu – regresji liniowej – jest to suma cech pomnożonych przez odpowiednie wagi (parametry) plus stała. Intuicyjnie: każda cecha ma pewien „wpływ”, który model stara się wycenić na podstawie danych.
Parametry są więc tym, co model zmienia podczas treningu. Ty ustawiasz jedynie hiperparametry: wybierasz rodzaj modelu (np. regresja liniowa, drzewo decyzyjne, las losowy), jego maksymalną głębokość, liczbę iteracji czy siłę regularizacji. Reszta dzieje się automatycznie w funkcji uczącej.

Narzędzia, które naprawdę wystarczą na start
Dlaczego Python stał się językiem numer jeden w ML
Python ma kilka cech, które sprawiają, że idealnie nadaje się na pierwszy język do uczenia maszynowego. Jest prosty składniowo – kod często wygląda jak rozszerzony pseudokod, a nie labirynt nawiasów. To ważne, bo na początku chcesz skupić się na danych i modelach, a nie na walce z językiem.
Drugą przewagą jest ogromny ekosystem bibliotek i społeczności. Masz gotowe klocki do obliczeń numerycznych, analizy danych, wizualizacji i budowy modeli. Zamiast implementować regresję liniową od podstaw, wywołujesz jedną klasę ze scikit-learn i skupiasz się na sensowniejszych problemach: jakości danych, interpretacji, eksperymentach.
Najważniejsze biblioteki: NumPy, pandas, Matplotlib/Seaborn, scikit-learn
Na start naprawdę wystarczy kilka podstawowych pakietów:
- NumPy – fundament obliczeń numerycznych. Oferuje szybkie tablice (ndarray) i operacje wektorowe. W ML rzadko używasz go wprost, ale wiele bibliotek na nim bazuje.
- pandas – kluczowy element: praca na tabelach (DataFrame), wczytywanie CSV, Excela, prosty cleaning, grupowanie. To w pandas spędzisz ogromną część czasu.
- Matplotlib i Seaborn – do tworzenia wykresów. Matplotlib jest bardziej low-level, Seaborn daje ładniejsze wizualizacje przy mniejszej ilości kodu.
- scikit-learn – „szwajcarski scyzoryk” do klasycznego uczenia maszynowego w Pythonie. Zawiera gotowe implementacje większości typowych modeli, narzędzia do podziału danych, skalowania, walidacji krzyżowej i metryk.
Przy tych bibliotekach możesz robić większość klasycznych projektów ML: regresję, klasyfikację, clustering, proste systemy rekomendacji, podstawową analizę danych.
Wybór środowiska: Jupyter Notebook vs klasyczne IDE
Na początku kluczowy jest komfort eksperymentowania. Pod tym względem Jupyter Notebook i JupyterLab są bardzo wygodne. Możesz uruchamiać kod blokami (komórkami), od razu pod spodem widzieć wynik, mieszać kod z komentarzami i wykresami. Uczenie się na przykładach, notatki i testowanie hipotez idą wtedy dużo szybciej.
Klasyczne IDE (PyCharm, VS Code) lepiej sprawdzają się przy większych projektach i długich skryptach, ale na pierwsze modele ML nie są konieczne. Dobrym kompromisem jest VS Code z rozszerzeniem do Jupytera, które łączy światy notatników i „normalnego” edytora.
Instalacja krok po kroku: Anaconda czy venv i pip
Istnieją dwie dominanty, jeśli chodzi o szybki start:
Po trzecie, łatwiej rozmawia się z zespołami technicznymi. Rozumiesz, co znaczy „overfitting”, „walidacja krzyżowa”, „zbalansowany zbiór danych”. Nawet podstawowy poziom pozwala uczestniczyć w merytorycznych rozmowach zamiast patrzeć na wykresy jak na abstrakcyjną sztukę współczesną. Jeśli do tego dołożysz praktyczne wskazówki: Informatyka, masz spójne podstawy zarówno w klasycznym IT, jak i w ML.
- Anaconda – dystrybucja Pythona z wieloma bibliotekami (w tym ML) wbudowanymi od razu. Instaluje się trochę dłużej i zajmuje więcej miejsca, ale na starcie oszczędza konfigurowanie pakietów. Jupyter jest od razu pod ręką.
- venv + pip – czyste środowisko wirtualne Pythona, do którego instalujesz tylko to, czego potrzebujesz, np.
pip install numpy pandas scikit-learn matplotlib seaborn jupyter. Daje większą kontrolę nad wersjami i jest bliżej standardów pracy w większych projektach.
Dla zupełnie początkujących Anaconda bywa wygodna, bo redukuje liczbę problemów konfiguracyjnych. Jeśli jednak masz już minimalne doświadczenie z Pythonem, własne środowisko wirtualne (venv) jest czytelne i schludne.
„Piaskownica” do nauki: katalogi, notatniki, wersjonowanie
Porządek w projektach – jak zorganizować sobie naukę
Nawet mały projekt ML potrafi szybko zmienić się w chaos plików i wersji. Lepiej od razu wyrobić kilka prostych nawyków: osobny katalog na każdy projekt, sensowne nazwy notatników i zapisywanie kolejnych podejść zamiast ciągłego nadpisywania jednego pliku.
Praktyczny układ może wyglądać tak:
projekty_ml/
ceny_mieszkan/
data/
raw/
processed/
notebooks/
01_eksploracja.ipynb
02_pierwszy_model.ipynb
models/
reports/
Nie chodzi o sztywny standard, raczej o to, żeby jutro wiedzieć, gdzie coś wczoraj zapisałeś. W katalogu notebooks trzymaj eksperymenty, w data surowe i przetworzone dane, a w models ewentualnie zapisane modele (np. pliki .pkl z joblib).
Dobrym dodatkiem jest proste wersjonowanie kodu, np. przez Git. Na start wystarczy lokalne repozytorium i kilka commitów z opisem zmian: „dodano feature X”, „zmiana metryki na MAE”. Dzięki temu łatwiej wrócić do działającej wersji, gdy kolejne eksperymenty zaczną się sypać.
Pierwszy kontakt z danymi – skąd je wziąć i co z nimi zrobić
Skąd brać dane do nauki i prostych projektów
Nie ma uczenia maszynowego bez danych. Na szczęście na początku nie musisz ich samodzielnie zbierać – istnieje sporo gotowych zbiorów, które idealnie nadają się do ćwiczeń.
- Wbudowane zbiory w scikit-learn – np.
load_iris,load_wine,load_breast_cancer. Małe, czyste, dobre do zrozumienia koncepcji, ale często zbyt „ładne” jak na realne życie. - Kaggle – serwis z konkursami i setkami otwartych datasetów. Znajdziesz tam zarówno klasyki (Titanic, domy w Bostonie), jak i dane biznesowe, tekstowe czy obrazowe.
- Otwarte dane publiczne – portale rządowe, miejskie (np. dane o komunikacji, nieruchomościach, demografii). To już często „prawdziwe” dane: niepełne, brudne, ale przez to bardzo pouczające.
- API i własne źródła – statusy z Twittera, dane z własnej aplikacji, logi serwera. To już krok w stronę bardziej samodzielnych projektów.
Na sam początek dobrze jest sięgnąć po zbiór, który ma jasny cel (przewidzieć cenę, sklasyfikować e-maile itp.) i jest w miarę mały, tak żeby mieścił się w pamięci i nie wymagał kombinowania z infrastrukturą.
Wczytywanie danych w pandas – minimalne „API”, które warto znać
Większość pierwszych projektów zacznie się od czegoś w tym stylu:
import pandas as pd
df = pd.read_csv("data/raw/ceny_mieszkan.csv")
pandas traktuje takie dane jako DataFrame – tabelę z nazwanymi kolumnami. Podstawowe operacje, które szybko staną się odruchowe:
df.head()– podgląd pierwszych kilku wierszy, żeby zobaczyć, co w ogóle wczytałeś.df.info()– typy kolumn, liczba niepustych wartości, rozmiar danych.df.describe()– podstawowe statystyki liczbowe: średnia, odchylenie, min, max, kwartyle.df["kolumna"]lubdf[["kol1", "kol2"]]– wybór jednej lub kilku kolumn.
Już ten zestaw pozwala zorientować się, z czym masz do czynienia: ile jest obserwacji, jakie kolumny są tekstowe, a jakie liczbowe, czy brakuje danych.
Inspekcja danych – szybkie pytania, które warto zadać na start
Zanim zaczniesz liczyć dokładność modelu, wypada obejrzeć dane jak człowiek. Pomaga w tym kilka prostych pytań:
- Ile jest wierszy i kolumn?
- Czy są brakujące wartości i w których kolumnach?
- Jak wyglądają rozkłady kluczowych zmiennych – czy są skrajne wartości, długie „ogonki”?
- Czy etykiety są zbalansowane, np. podobna liczba przypadków klasy 0 i 1?
Część z tego możesz ogarnąć „na oko” w notatniku, część policzyć kodem:
df.isna().sum() # liczba braków w każdej kolumnie
df["target"].value_counts() # rozkład klas dla klasyfikacji
Kilka minut takiej inspekcji często ujawnia problemy, które później męczyłyby cię godzinami przy dziwnie zachowującym się modelu.

Eksploracja i wizualizacja – zanim uruchomisz jakikolwiek model
Dlaczego warto „pogadać” z danymi przed modelowaniem
Exploratory Data Analysis (EDA) to po prostu etap, w którym sprawdzasz, jak dane się zachowują, bez wyciągania daleko idących wniosków. W praktyce oznacza to robienie prostych wykresów, tabel przestawnych, sprawdzanie korelacji. Dzięki temu widzisz, czy relacje, które później ma złapać model, w ogóle mają sens.
Bez takiego rozeznania łatwo traci się czas: model niby czegoś się uczy, ale tak naprawdę goni szum albo artefakt błędu pomiarowego w jednej kolumnie.
Podstawowe wykresy w Matplotlib i Seaborn
Do pierwszej eksploracji wystarczy kilka typów wykresów. Ich interpretacja jest znacznie ważniejsza niż piękny styl.
- Histogram – rozkład jednej zmiennej ciągłej (np. ceny, metrażu). Ujawnia skośność, nietypowe wartości.
- Boxplot – pokazuje medianę, kwartyle i obserwacje odstające. Świetny do porównywania rozkładu między grupami.
- Scatter plot – wykres punktowy dwóch zmiennych liczbowych. Dobrze pokazuje proste zależności liniowe i nieliniowe.
- Countplot – słupki pokazujące liczebność kategorii (np. typ zabudowy, dzielnica).
Przykładowy kod z Seabornem:
import seaborn as sns
import matplotlib.pyplot as plt
# Histogram ceny
sns.histplot(df["cena"], kde=True)
plt.show()
# Zależność ceny od metrażu
sns.scatterplot(data=df, x="metraz", y="cena")
plt.show()
Taki zestaw dwóch–trzech wykresów często wystarcza, żeby złapać pierwszą intuicję: czy zależności są w miarę gładkie, czy może dane tworzą kilka odrębnych „chmurek”, sugerując potrzebę bardziej złożonego podejścia.
Korelacje i zależności – co mówią liczby
Oprócz wykresów warto spojrzeć na korelacje, czyli miarę siły liniowej zależności między dwiema zmiennymi liczbowymi. W pandas wystarczy:
corr = df.corr(numeric_only=True)
Macierz korelacji można zwizualizować jako mapę ciepła:
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=False, cmap="coolwarm", center=0)
plt.show()
Duże wartości dodatnie (blisko 1) oznaczają silną dodatnią zależność, duże ujemne (blisko -1) – silną ujemną. Dwie niemal identyczne kolumny zwykle nie wnoszą wiele nowej informacji, ale mogą komplikować model (np. zwiększać ryzyko przeuczenia w modelach liniowych).
Proste grupowania i agregacje – szybkie odpowiedzi na konkretne pytania
Często chcesz sprawdzić różnice między grupami: średnia cena w poszczególnych dzielnicach, średnia długość pobytu w szpitalu w zależności od płci, itd. W pandas służy do tego groupby:
df.groupby("dzielnica")["cena"].mean().sort_values(ascending=False)
Taki jednowierszowy kod potrafi od razu pokazać efekt, który później podchwyci model. Dodatkowo, różnice między grupami możesz od razu zwizualizować słupkami:
sns.barplot(data=df, x="dzielnica", y="cena")
plt.xticks(rotation=45)
plt.show()
Przygotowanie danych – etap, którego większość nie docenia
Dlaczego „czyszczenie” danych zajmuje tyle czasu
Dane, z którymi spotkasz się poza książkowymi przykładami, rzadko są idealne. Brakuje wartości, typy są pomylone (liczby zapisane jako tekst), występują literówki w kategoriach, a czasem kolumny mają w sobie kilka informacji naraz. Uporządkowanie tego wszystkiego to zwykle większość pracy w projekcie ML.
W praktyce lepiej jest poświęcić więcej czasu na dobre przygotowanie danych niż na wymyślne modele. Prosty model na dobrze ogarniętych danych potrafi wygrać z wyrafinowaną architekturą na przypadkowo zlepionych kolumnach.
Radzenie sobie z brakującymi wartościami
Braki danych (NaN) to pierwszy przeciwnik, na którego natrafisz. Sposób reakcji zależy od tego, jak dużo ich jest i w których kolumnach leżą.
W tym miejscu przyda się jeszcze jeden praktyczny punkt odniesienia: Ranking menedżerów haseł: Bitwarden, 1Password, KeePass w praktyce.
- Całkowite usunięcie wierszy – gdy braków jest naprawdę mało i losowo rozłożone. W pandas:
df.dropna(). - Uzupełnianie prostą statystyką – np. średnią, medianą (dla liczb) lub najczęstszą kategorią (dla tekstu). W pandas:
df["kolumna"].fillna(df["kolumna"].median()). - Specjalna kategoria „brak” – dla zmiennych kategorycznych czasem sensowniej jest oznaczyć brak jako osobną wartość, np. „nieznane”.
W scikit-learn masz do tego gotową klasę SimpleImputer, którą można wpiąć w cały pipeline (o pipeline za chwilę):
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
X_num_imputed = imputer.fit_transform(X_num)
Kluczowa rzecz: imputer uczysz tylko na zbiorze treningowym, a potem stosujesz do walidacyjnego i testowego, żeby nie „podejrzeć” przyszłości.
Przekształcanie zmiennych kategorycznych na liczby
Modele scikit-learn oczekują zwykle danych liczbowych. Tekstowe kategorie (np. dzielnica, typ budynku) trzeba więc zakodować. Najprostsze podejścia to:
- One-hot encoding – zamiana kategorii na zestaw kolumn 0/1, po jednej kolumnie na kategorię. W praktyce:
get_dummiesw pandas lubOneHotEncoderw scikit-learn. - Ordinal encoding – przypisanie kolejnym kategoriom liczb 0, 1, 2… Użyteczne, gdy istnieje naturalny porządek (np. „niski”, „średni”, „wysoki”).
Przykład z pandas:
X_cat = pd.get_dummies(df[["dzielnica", "typ_budynku"]], drop_first=True)
W pipeline’ie scikit-learn często używa się OneHotEncoder w połączeniu z ColumnTransformer, żeby przekształcać różne kolumny różnymi metodami.
Skalowanie cech – kiedy ma sens, a kiedy nie
Niektóre modele (np. regresja liniowa, kNN, SVM) są wrażliwe na skale poszczególnych cech. Metraż w metrach i cena w tysiącach mogą powodować, że jedna cecha zdominuje inne, jeśli ich nie przeskalujesz. Do wyrównania skali używa się:
- StandardScaler – odejmuje średnią i dzieli przez odchylenie standardowe, wynik ma zwykle rozkład zbliżony do normalnego.
- MinMaxScaler – przekształca dane do zakresu [0, 1].
W scikit-learn:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_num_scaled = scaler.fit_transform(X_num)
Drzewa decyzyjne i oparte na nich lasy losowe czy gradient boosting zwykle nie wymagają skalowania – potrafią dobrze radzić sobie z oryginalnymi jednostkami.
Podział na cechy i etykietę oraz train/test w praktyce
Gdy dane są już w miarę uporządkowane, trzeba wydzielić cechy (X) i etykietę (y), a potem podzielić wszystko na zbiory treningowy i testowy. W praktyce wygląda to zwykle tak:
from sklearn.model_selection import train_test_split
X = df.drop(columns=["cena"])
y = df["cena"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
Parametr random_state ustawia „ziarno losowości”, dzięki czemu zawsze dostaniesz taki sam podział – łatwiej wtedy porównywać różne modele i dzielić się kodem z innymi.
Budowanie prostego pipeline’u w scikit-learn
Warto połączyć kroki przygotowania danych i modelowania w jedno spójne „pudełko” – pipeline. Dzięki temu unikasz sytuacji, w której na zbiorze testowym robisz trochę inne przekształcenia niż na treningowym.
Przykładowy pipeline dla mieszaniny zmiennych liczbowych i kategorycznych może wyglądać tak:
Przykładowy ColumnTransformer + Pipeline krok po kroku
Skoro masz już rozdzielone kolumny na liczbowe i kategoryczne, można złożyć z nich jedną całość przy użyciu ColumnTransformer. W ten sposób każda grupa cech dostaje swój „mini-pipeline”, a na końcu całość trafia do modelu.
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor
# Załóżmy, że tak zdefiniowałeś listy kolumn:
num_cols = ["metraz", "liczba_pokoi", "rok_budowy"]
cat_cols = ["dzielnica", "typ_budynku"]
# Pipeline dla cech liczbowych
numeric_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
# Pipeline dla cech kategorycznych
categorical_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(handle_unknown="ignore"))
])
# Połączenie w ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_transformer, num_cols),
("cat", categorical_transformer, cat_cols),
]
)
# Pełny pipeline z modelem
model = Pipeline(steps=[
("preprocessor", preprocessor),
("regressor", RandomForestRegressor(
n_estimators=200,
random_state=42
))
])
# Trenowanie
model.fit(X_train, y_train)
Taki obiekt model możesz od razu używać do predykcji:
y_pred = model.predict(X_test)Z zewnątrz nie interesuje cię, które dokładnie kroki dzieją się w środku. Ten sam pipeline możesz potem zapisać, wczytać i wykorzystać produkcyjnie, bez ręcznego powtarzania transformacji.
Jak unikać „przecieku informacji” (data leakage)
Cichy zabójca wielu pierwszych projektów ML to niewidoczny wyciek informacji. Dzieje się to wtedy, gdy dane z przyszłości (zbioru testowego) wpływają na to, jak uczysz model.
Kilka typowych sytuacji:
- Skalowanie na całym zbiorze – wyliczasz średnią i odchylenie na wszystkich danych, a potem trenujesz na części z nich. Test staje się przez to „łatwiejszy”, niż będzie w realnym świecie.
- Imputacja na całym zbiorze – medianę dla braków liczysz z całości, zamiast tylko ze zbioru treningowego.
- Wycieki czasowe – używasz informacji, które w prawdziwym scenariuszu byłyby znane dopiero po zdarzeniu (np. finalny status pacjenta przy przewidywaniu decyzji lekarza).
Pipeline rozwiązuje dwa pierwsze problemy w naturalny sposób: wszystkie transformacje wyliczają parametry na X_train w trakcie fit, a do X_test stosowane są tylko w trybie transform. Trzeci wymaga od ciebie zdrowego rozsądku i przemyślenia kolejności zdarzeń w danych.
Weryfikacja modelu – pierwsze porządne sprawdzenie jakości
Po trenowaniu modelu przychodzi moment prawdy: jak dobrze radzi sobie na danych, których nie widział? Tu wchodzą miary jakości dopasowane do rodzaju problemu.
Podstawowe metryki dla regresji
Jeśli przewidujesz wartość liczbową (np. cenę mieszkania), interesuje cię, jak bardzo model się myli. Najczęściej używane miary to:
- MAE (Mean Absolute Error) – średni błąd bezwzględny. Intuicyjne, bo w tej samej jednostce, co przewidywana wartość (np. zł).
- MSE / RMSE (Mean Squared Error / Root MSE) – błąd średniokwadratowy i jego pierwiastek. Silniej karze duże pomyłki.
- R² – współczynnik determinacji. Mówi, jaka część zmienności danych wyjaśniana jest przez model (1 – idealnie, 0 – tyle, co średnia).
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print("MAE:", mae)
print("RMSE:", rmse)
print("R²:", r2)Przy pierwszych projektach dobrze jest tłumaczyć sobie liczby na język biznesowy: „średnio mylimy się o X zł” brzmi dużo konkretniej niż „R² to 0.78”.
Podstawowe metryki dla klasyfikacji
Jeśli przewidujesz klasę (np. czy klient odejdzie, czy nie), proce nt trafień (accuracy) to za mało. W praktyce często ważniejsze jest, kogo model myli.
Z podstawowego zestawu przydają się:
- Accuracy – odsetek poprawnych predykcji. Przy niezbalansowanych danych bywa złudnie wysoki.
- Precision – z wszystkich przewidzianych „tak”, ile faktycznie było „tak”. Mierzy „czystość” pozytywnej klasy.
- Recall – z wszystkich prawdziwych „tak”, ile model wykrył. Mierzy wrażliwość na przypadki pozytywne.
- F1-score – średnia harmoniczna precision i recall.
from sklearn.metrics import classification_report, confusion_matrix
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))
Macierz pomyłek (confusion_matrix) to prosta tabelka, która pokazuje, ile przykładów trafiło do której klasy. Dobrze na nią popatrzeć, zanim zaczniesz „ścigać” kolejne procenty accuracy.
Walidacja krzyżowa – bardziej rzetelny pomiar
Pojedynczy podział na train/test bywa zdradliwy. Załóż, że test „przypadkiem” trafił wyjątkowo łatwy lub wyjątkowo trudny fragment danych. Wynik może cię wprowadzić w błąd.
Na koniec warto zerknąć również na: Od regresji do XGBoost: kiedy który model ma sens? — to dobre domknięcie tematu.
Rozwiązaniem jest walidacja krzyżowa (cross-validation), czyli wielokrotne trenowanie modelu na różnych kawałkach danych i uśrednienie wyniku.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(
model,
X,
y,
cv=5, # 5-krotna walidacja
scoring="neg_mean_absolute_error"
)
print("MAE CV:", -scores.mean(), "+/-", scores.std())Scikit-learn stosuje konwencję, że niższe jest lepsze, więc dla błędów zwraca wartości ujemne; dlatego przed interpretacją bierzesz minus.
Prosty baseline – z czym porównywać model
Wielu początkujących pomija ważny krok: sprawdzenie, czy zbudowany model jest lepszy niż „głupi” punkt odniesienia (baseline). Taki punkt odniesienia to np.:
- w regresji – zawsze przewiduj średnią lub medianę z treningu,
- w klasyfikacji – zawsze przewiduj najczęstszą klasę.
Jeśli twój skomplikowany model ledwo przebija taki baseline, to sygnał, że problem jest trudny albo dane nie niosą wiele informacji.
import numpy as np
# Baseline dla regresji: przewidywanie mediany
median_price = y_train.median()
y_pred_baseline = np.full_like(y_test, fill_value=median_price, dtype=float)
mae_baseline = mean_absolute_error(y_test, y_pred_baseline)
print("MAE baseline (mediana):", mae_baseline)Pierwsze porównanie modeli – bez wchodzenia w setki hiperparametrów
Zanim zaczniesz stroić dziesiątki parametrów jednego modelu, sensowniej jest zestawić kilka prostych algorytmów i zobaczyć, który ogólnie lepiej pasuje do danego typu danych.
Dla regresji (np. cen mieszkań) możesz wypróbować:
- LinearRegression – prosty model liniowy, często dobry punkt startowy.
- RandomForestRegressor – las losowy, nie wymaga skalowania cech, dobrze radzi sobie z nieliniowościami.
- GradientBoostingRegressor lub HistGradientBoostingRegressor – nieco bardziej zaawansowane drzewa wzmacniane.
Wzorzec pracy będzie podobny: ten sam preprocessor, inny końcowy krok w pipeline. Dzięki temu porównujesz algorytmy uczciwie – na identycznie przygotowanych danych.
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor, HistGradientBoostingRegressor
models = {
"lin_reg": LinearRegression(),
"rf": RandomForestRegressor(n_estimators=200, random_state=42),
"hgb": HistGradientBoostingRegressor(random_state=42)
}
for name, reg in models.items():
pipe = Pipeline(steps=[
("preprocessor", preprocessor),
("regressor", reg)
])
scores = cross_val_score(
pipe, X, y,
cv=5,
scoring="neg_mean_absolute_error"
)
print(name, "MAE:", -scores.mean())Już taki prosty przegląd potrafi oszczędzić sporo czasu. Zamiast godzinami dostrajać liniową regresję, może się okazać, że las losowy działa znacznie lepiej bez większego kombinowania.
Podstawy strojenia hiperparametrów – GridSearch i RandomizedSearch
Gdy wybierzesz wstępnie jeden lub dwa modele, przychodzi pora na strojenie hiperparametrów, czyli ustawień, których model sam nie uczy z danych (np. głębokość drzew, liczba drzew w lesie).
Najprostsze narzędzia scikit-learn to:
- GridSearchCV – przegląda wszystkie kombinacje zadanych parametrów.
- RandomizedSearchCV – losuje ograniczoną liczbę kombinacji z większej przestrzeni.
Przy niewielkich zbiorach można zacząć od siatki (gridu), ale nawet wtedy rozsądnie jest trzymać liczbę kombinacji w ryzach.
from sklearn.model_selection import GridSearchCV
param_grid = {
"regressor__n_estimators": [100, 200, 300],
"regressor__max_depth": [None, 5, 10],
"regressor__min_samples_split": [2, 5]
}
rf = RandomForestRegressor(random_state=42)
pipe = Pipeline(steps=[
("preprocessor", preprocessor),
("regressor", rf)
])
grid_search = GridSearchCV(
pipe,
param_grid,
cv=5,
scoring="neg_mean_absolute_error",
n_jobs=-1
)
grid_search.fit(X_train, y_train)
print("Najlepsze parametry:", grid_search.best_params_)
print("Najlepszy MAE:", -grid_search.best_score_)
Zwróć uwagę na nazwy parametrów: regressor__n_estimators oznacza, że zmieniasz parametr n_estimators kroku o nazwie regressor w pipeline. Podwójny podkreślnik to standardowa konwencja w scikit-learn.
Znaczenie cech – co tak naprawdę „widzi” model
Sam wynik liczbowy (MAE, accuracy) mówi, jak dobrze model działa, ale nie odpowiada na pytanie: dlaczego. Nawet prosty rzut oka na znaczenie cech potrafi ujawnić, że model trzyma się głównie jednej kolumny.
Dla drzew i lasów losowych możesz skorzystać z feature_importances_:
best_model = grid_search.best_estimator_
reg = best_model.named_steps["regressor"]
importances = reg.feature_importances_
Problem w tym, że po ColumnTransformer i OneHotEncoder liczba cech rośnie, a ich nazwy się zmieniają. Żeby je odzyskać, trzeba odpytać encoder:
import numpy as np
pre = best_model.named_steps["preprocessor"]
# Nazwy cech liczbowych
num_features = num_cols
# Nazwy cech kategorycznych po one-hot
cat_encoder = pre.named_transformers_["cat"].named_steps["encoder"]
cat_features = list(cat_encoder.get_feature_names_out(cat_cols))
feature_names = np.array(num_features + cat_features)
# Posortuj od najważniejszych
idx = np.argsort(importances)[::-1]
for i in idx[:20]:
print(feature_names[i], ":", importances[i])Takie zestawienie bywa bardzo pouczające. W jednym z realnych projektów dotyczących predykcji cen mieszkań okazało się, że model uznał kod pocztowy za kluczowy czynnik, przebijający nawet metraż. To z kolei prowadzi do pytań, czy nie lepiej agregować lokalizację na bardziej ogólny poziom (dzielnica, rejon miasta).
Prosty eksperyment end-to-end – mini-projekt do samodzielnego przerobienia
Teoria teorią, ale najszybszy postęp przychodzi, gdy przejdziesz przez cały cykl na prawdziwym zbiorze. Dobrym kandydatem na start są otwarte dane z serwisu Kaggle.
Przykładowy mini-projekt krok po kroku może wyglądać tak:
- Wybierz mały zbiór danych regresyjnych (np. ceny domów, zużycie energii).
- Załaduj dane do pandas, obejrzyj
head(),info(), podstawowe statystyki. - Usuń ewidentnie bezużyteczne kolumny (identyfikatory, losowe opisy tekstowe).
- Podziel kolumny na liczbowe i kategoryczne.
- Zbuduj
ColumnTransformer+ pipeline z prostym modelem. - Zmierz jakość na walidacji krzyżowej i na zbiorze testowym.
- Porównaj 2–3 różne algorytmy, zmieniając tylko końcówkę pipeline.
- Dodaj kilka prostych cech (np. wiek budynku = rok_budowy przekształcony na „obecny rok” minus „rok budowy”) i zobacz, czy metryki się poprawiają.
Taki pełny przebieg – od danych surowych do w miarę sensownego modelu i interpretacji – daje dużo więcej niż samo czytanie o algorytmach. Po jednym–dwóch takich projektach zaczynasz instynktownie wyczuwać, kiedy masz problem z danymi, a kiedy z modelem.






