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-learnZanim 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.
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ł.
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ć.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łę.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.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.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.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.