Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Scikit-learn - tropiciel gatunków

Wyobraź sobie, @name, że wracasz z wyprawy z notatnikiem pełnym pomiarów: długość kła, rozpiętość skrzydeł, waga. Chcesz, by komputer sam rozpoznawał, jakie to zwierzę. Dokładnie tym zajmuje się klasyczne uczenie maszynowe - a jego najpopularniejszym narzędziem w Pythonie jest scikit-learn.

1pip install scikit-learn

Jeden rytuał, wiele modeli

Zanim poznasz konkretne modele, zapamiętaj najważniejszą rzecz z tej lekcji. Każdy model w scikit-learn - niezależnie czy to drzewo, las, czy sieć sąsiadów - mówi tymi samymi trzema czasownikami:

  • fit(X, y)
    - ucz się: pokaż modelowi pomiary
    X
    i prawidłowe odpowiedzi
    y
    .
  • predict(X)
    - rozpoznaj: dla nowych pomiarów zwróć przewidywany gatunek.
  • score(X, y)
    - oceń: sprawdź, jaki procent zgadł poprawnie.

To jak wspólny język tropicieli: gdy nauczysz się go raz, każdy nowy model jest tylko innym tropicielem mówiącym tą samą mową. Poniższe modele różnią się sposobem myślenia, ale obsługujesz je identycznie.

Drzewo decyzyjne - tropiciel zadający pytania

Najprostszy tropiciel to ten, który zadaje serię pytań: "Czy waży ponad 100 kg? Czy ma grzywę?". Tak działa drzewo decyzyjne - dzieli zwierzęta pytaniami, aż zostanie jeden gatunek.

1from sklearn.tree import DecisionTreeClassifier
2from sklearn.datasets import load_iris
3
4iris = load_iris()
5X, y = iris.data, iris.target
6
7tree = DecisionTreeClassifier(max_depth=3, random_state=42)
8tree.fit(X_train, y_train)
9
10accuracy = tree.score(X_test, y_test)

Zwróć uwagę, że pojawiły się nasze trzy czasowniki:

fit
uczy,
score
ocenia. Parametr
max_depth=3
ogranicza liczbę pytań w łańcuchu - im głębsze drzewo, tym bardziej dopasowuje się do danych treningowych, ale tym łatwiej się "przeucza" i gubi na nowych zwierzętach. To pierwszy kompromis ML, który poczujesz w praktyce.

Wielką zaletą drzewa jest to, że możesz je zobaczyć - dosłownie narysować jego pytania:

1from sklearn.tree import plot_tree
2import matplotlib.pyplot as plt
3
4plt.figure(figsize=(20, 10))
5plot_tree(tree, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
6plt.show()

Ten rysunek to Twój najlepszy przyjaciel przy tłumaczeniu decyzji modelu innym - każda gałąź to jedno pytanie, które model zadał.

Las losowy - narada wielu tropicieli

Jedno drzewo bywa kapryśne. A gdyby zapytać stu tropicieli i przyjąć odpowiedź większości? To właśnie las losowy (

RandomForest
) - buduje setki różnych drzew i głosuje.

1from sklearn.ensemble import RandomForestClassifier
2
3forest = RandomForestClassifier(
4    n_estimators=100,    # liczba drzew w naradzie
5    max_depth=10,
6    min_samples_split=5,
7    random_state=42
8)
9forest.fit(X_train, y_train)
10
11importance = forest.feature_importances_
12for name, imp in zip(feature_names, importance):
13    print(f"{name}: {imp:.4f}")

n_estimators=100
to liczba tropicieli w naradzie - więcej drzew zwykle znaczy stabilniejszą odpowiedź, kosztem czasu. Las daje też prezent, którego drzewo nie miało tak łatwo:
feature_importances_
mówi, które pomiary najbardziej pomagały rozpoznać gatunek. Zapamiętaj tę własność - w praktyce to ona podpowiada, co w ogóle warto mierzyć.

SVM - tropiciel wytyczający granicę

Inny sposób myślenia: zamiast pytań, narysuj na mapie pomiarów granicę oddzielającą gatunki. SVM (Support Vector Machine) szuka takiej linii, by margines między grupami był jak najszerszy.

1from sklearn.svm import SVC
2
3svm = SVC(
4    kernel='rbf',        # kształt granicy: 'linear', 'poly', 'rbf'
5    C=1.0,
6    gamma='scale',
7    probability=True     # pozwala pytać o prawdopodobieństwo
8)
9svm.fit(X_train, y_train)
10
11proba = svm.predict_proba(X_test)

Parametr

kernel
decyduje, czy granica jest prosta (
linear
), czy potrafi się wyginać (
rbf
). Włączenie
probability=True
daje coś cennego:
predict_proba
zwróci nie tylko gatunek, ale i pewność modelu - np. "80% lew, 20% gepard". To przydaje się, gdy chcesz odrzucać niepewne rozpoznania zamiast zgadywać na siłę.

KNN - tropiciel patrzący na sąsiadów

Najbardziej intuicyjny tropiciel nie uczy się prawie wcale. KNN (K-Nearest Neighbors) dla nowego zwierzęcia po prostu patrzy, do jakich znanych okazów jest najbardziej podobne, i przyjmuje ich gatunek.

1from sklearn.neighbors import KNeighborsClassifier
2
3knn = KNeighborsClassifier(
4    n_neighbors=5,       # ilu sąsiadów pytamy
5    weights='uniform',
6    metric='euclidean'
7)
8knn.fit(X_train, y_train)
9predictions = knn.predict(X_test)

n_neighbors=5
znaczy "spójrz na 5 najbliższych znanych zwierząt i przyjmij większość". Uwaga na pułapkę: KNN mierzy odległości, więc jeśli jeden pomiar jest w tonach, a inny w milimetrach, ten pierwszy zdominuje wynik. Dlatego za chwilę poznasz skalowanie danych - dla KNN jest ono obowiązkowe.

Regresja - gdy odpowiedzią jest liczba

Do tej pory zgadywaliśmy gatunek (kategorię). A gdy chcesz przewidzieć liczbę - wagę zwierzęcia na podstawie wymiarów? To regresja, i korzysta z tych samych trzech czasowników.

1from sklearn.linear_model import LinearRegression, Ridge, Lasso
2
3linear = LinearRegression()
4linear.fit(X_train, y_train)
5
6ridge = Ridge(alpha=1.0)    # kara za zbyt duże współczynniki (L2)
7ridge.fit(X_train, y_train)
8
9lasso = Lasso(alpha=0.1)    # potrafi wyzerować nieważne cechy (L1)
10lasso.fit(X_train, y_train)
11
12print(f"Coefficients: {linear.coef_}")
13print(f"Intercept: {linear.intercept_}")

LinearRegression
szuka prostej zależności między pomiarami a wynikiem.
Ridge
i
Lasso
to jej ostrożniejsze wersje - dokładają karę za przesadę, żeby model nie dopasował się zbyt kurczowo do danych treningowych. Różnica:
Lasso
potrafi całkiem wyzerować nieprzydatne cechy, więc bywa też narzędziem do wyboru, które pomiary w ogóle zatrzymać.

Gdy zależność nie jest prosta, ten sam las co przy klasyfikacji ma wersję dla liczb:

1from sklearn.ensemble import RandomForestRegressor
2
3rf_reg = RandomForestRegressor(n_estimators=100, random_state=42)
4rf_reg.fit(X_train, y_train)
5predictions = rf_reg.predict(X_test)

Zauważ, że kod jest bliźniaczo podobny do klasyfikatora - zmienił się tylko koniec nazwy (

Regressor
zamiast
Classifier
). To znów ten sam wspólny język: opanowawszy jeden model, drugi rozpoznajesz w locie.

Skalowanie - wyrównaj jednostki przed treningiem

Wróćmy do pułapki z KNN. Modele mierzące odległości gubią się, gdy cechy mają różne skale. Skalowanie sprowadza wszystkie pomiary do wspólnej miary, zanim model je zobaczy.

1from sklearn.preprocessing import StandardScaler, MinMaxScaler, LabelEncoder, OneHotEncoder
2
3scaler = StandardScaler()          # średnia 0, odchylenie 1
4X_scaled = scaler.fit_transform(X_train)
5X_test_scaled = scaler.transform(X_test)
6
7minmax = MinMaxScaler()            # ściśnij do zakresu 0-1
8X_normalized = minmax.fit_transform(X_train)
9
10le = LabelEncoder()                # 'lion'/'cheetah' -> 0/1/2
11y_encoded = le.fit_transform(['lion', 'elephant', 'cheetah'])
12
13ohe = OneHotEncoder(sparse_output=False)  # kategorie -> kolumny 0/1
14species_onehot = ohe.fit_transform(species_array.reshape(-1, 1))

Jest tu jedna zasada, którą łatwo złamać, a która psuje wyniki: skaler uczysz tylko na danych treningowych (

fit_transform
), a dane testowe jedynie przekształcasz (
transform
). Inaczej model "podejrzy" dane testowe i ocena będzie zawyżona. Dwa dolne narzędzia zamieniają nazwy gatunków na liczby, bo modele rozumieją tylko liczby -
LabelEncoder
dla odpowiedzi,
OneHotEncoder
dla cech kategorycznych.

Pipeline - spakuj wszystko w jeden rytuał

Skoro przed każdym modelem trzeba skalować dane, łatwo o pomyłkę: przeskalować trening inaczej niż test, albo zapomnieć o kroku. Pipeline skleja przygotowanie danych i model w jeden obiekt, który sam pilnuje kolejności.

1from sklearn.pipeline import Pipeline
2from sklearn.preprocessing import StandardScaler
3from sklearn.ensemble import RandomForestClassifier
4
5pipeline = Pipeline([
6    ('scaler', StandardScaler()),
7    ('classifier', RandomForestClassifier(n_estimators=100))
8])
9
10pipeline.fit(X_train, y_train)
11predictions = pipeline.predict(X_test)

Najważniejsze:

pipeline
też mówi naszymi trzema czasownikami -
fit
na całym łańcuchu naraz skaluje i trenuje, a
predict
automatycznie skaluje nowe dane tym samym skalerem. Dzięki temu pułapka ze skalowaniem znika sama, bo nie da się już zrobić tego w złej kolejności.

Pamiętaj z tej lekcji jedną rzecz: w scikit-learn nie uczysz się dziesięciu różnych narzędzi, tylko jednego wspólnego języka -

fit
,
predict
,
score
- którym mówią wszystkie modele. Reszta to wybór tropiciela pasującego do zadania.

Przejdź do CodeWorlds