Dlaczego akurat XGBoost, LightGBM i CatBoost? Kontekst i zastosowania
Pozycja „wielkiej trójki” w świecie danych tabelarycznych
W praktyce komercyjnej i konkursowej, gdy na biurku ląduje zbiór danych tabelarycznych (kolumny, wiersze, liczby, kategorie), bardzo często pierwsza odpowiedź brzmi: gradient boosting na drzewach. Przez długi czas domyślnym wyborem był XGBoost, później dołączył LightGBM, a następnie CatBoost – i to one stały się rdzeniem większości zwycięskich rozwiązań na Kaggle oraz w wielu projektach produkcyjnych.
Biblioteki te łączy rdzeń algorytmiczny (gradient boosting), ale różnią się realizacją i filozofią: XGBoost stawia na kontrolę i stabilność, LightGBM na ekstremalną szybkość i skalę, CatBoost na wygodę oraz bezpośrednią obsługę cech kategorycznych. W rezultacie to właśnie porównanie XGBoost vs LightGBM vs CatBoost na tych samych danych daje najbardziej praktyczny wgląd w to, jak dobrać narzędzie do konkretnego projektu.
Wspólny mianownik jest prosty: dane tabelaryczne z mieszanką cech numerycznych i kategorycznych, ograniczony budżet czasowy i potrzeba uzyskania dobrej jakości predykcji bez nadmiernie skomplikowanych modeli typu deep learning. Tu gradient boosting w praktyce zwykle wypada lepiej niż klasyczne modele liniowe czy pojedyncze drzewa decyzyjne.
Typowe zadania: klasyfikacja, regresja, ranking
Wszystkie trzy biblioteki obsługują najpopularniejsze typy zadań:
- klasyfikacja binarna – scoring kredytowy, wykrywanie churnu, predykcja konwersji, fraud detection,
- klasyfikacja wieloklasowa – segmentacja klientów, przewidywanie typu produktu, rozpoznawanie kategorii,
- regresja – przewidywanie wartości koszyka, czasu dostawy, popytu, cen nieruchomości,
- ranking – rekomendacje, wyszukiwarki wewnętrzne, sortowanie ofert w marketplace.
XGBoost i LightGBM są często wybierane przy dużych, nierównoważonych zbiorach danych, gdzie liczy się wydajność trenowania modeli boosting oraz elastyczna konfiguracja metryk (np. AUC, logloss). CatBoost silnie akcentuje klasyfikację i regresję na danych z wieloma kolumnami kategorycznymi, gdzie jego natywna obsługa cech kategorycznych realnie upraszcza pipeline.
Dlaczego boosting wygrywa z modelami liniowymi i prostymi drzewami
Na danych tabelarycznych gradient boosting często przewyższa modele liniowe z kilku powodów. Po pierwsze, modeluje nieliniowości i interakcje między cechami bez ręcznego tworzenia dziesiątek cech z interakcji. Po drugie, dobrze radzi sobie z mieszanką skal – nie wymaga standaryzacji jak wiele algorytmów liniowych czy metody oparte na odległości. Po trzecie, dzięki sumie wielu drzew, może łagodnie, krok po kroku, dopasowywać się do trudnych fragmentów rozkładu danych.
W porównaniu do pojedynczego drzewa decyzyjnego, boosting:
- jest znacznie stabilniejszy – mniejsza wrażliwość na konkretne podziały danych treningowych,
- osiąga lepszą dokładność – wiele małych drzew korygujących błędy daje lepszy kompromis bias–variance,
- zapewnia bogatsze narzędzia regularyzacji i kontroli złożoności niż pojedyncze drzewo.
Główne kryteria porównania w realnym projekcie
Porównując XGBoost, LightGBM i CatBoost na tych samych danych, praktyk zwykle patrzy na cztery wymiary:
- jakość predykcji – metryka biznesowa (np. AUC, RMSE, logloss), ale też stabilność wyników w walidacji krzyżowej,
- czas trenowania i predykcji – ważne przy częstym retrenowaniu modeli lub dużych wolumenach danych,
- łatwość użycia – od preprocessingu (szczególnie cechy kategoryczne) po integrację z istniejącym pipeline’em,
- interpretowalność i narzędzia analityczne – feature importance, SHAP, obsługa braków danych, monitoring w produkcji.
Do tego dochodzi aspekt „ekosystemu”: dokumentacja, liczba przykładów, wsparcie społeczności, integracja z frameworkami MLOps. XGBoost i LightGBM są szeroko obecne w narzędziach AutoML; CatBoost bywa wybierany tam, gdzie zespół ma silną przewagę danych kategorycznych i potrzebuje szybkich iteracji bez skomplikowanego kodowania.
Krótka powtórka z gradient boosting – co naprawdę się dzieje pod spodem
Intuicja działania: korekta błędów krok po kroku
Gradient boosting można opisać w jednym zdaniu: model jest budowany iteracyjnie, a każde nowe drzewo uczy się korygować błędy poprzednich drzew. Na starcie mamy bardzo prostą predykcję (np. średnia wartości wyjściowej w regresji lub logit z proporcji klas w klasyfikacji). Następnie:
- obliczane są reszty lub gradienty funkcji straty (czyli: w którą stronę i jak mocno trzeba poprawić predykcje),
- trenowane jest małe drzewo regresyjne, które próbuje przybliżyć te reszty,
- nowe przewidywania są dodawane do modelu z wagą określoną przez learning rate,
- proces powtarzamy setki, czasem tysiące razy.
Powstaje suma wielu prostych modeli (drzew), z których każdy jest stosunkowo słaby, ale jako zespół tworzą mocny model. Kluczowe jest to, że każde kolejne drzewo „widzi” błędy poprzednich i może skupić się na trudno przewidywalnych obserwacjach.
Kluczowe pojęcia: słabi uczący, learning rate, liczba iteracji
Trzy parametry w gradient boostingu są absolutnie podstawowe:
- słaby uczący (weak learner) – najczęściej małe drzewko o ograniczonej głębokości (
max_depth) lub z małą liczbą liści; w XGBoost i LightGBM konfigurujemy m.in.max_depth,min_child_weight,num_leaves, - learning rate (często
eta) – współczynnik „kroku”, z jakim każde nowe drzewo koryguje model; mniejszy learning rate oznacza wolniejsze uczenie, ale potencjalnie lepszą generalizację, - liczba iteracji / drzew (
n_estimators,num_boost_round) – ile razy dodamy nowe drzewo; zbyt mało to niedouczenie, zbyt dużo – ryzyko przeuczenia.
Te parametry są powiązane. Mały learning rate zwykle wymaga większej liczby drzew, aby osiągnąć podobną jakość predykcji, ale w zamian daje większą kontrolę nad procesem uczenia i mniej gwałtowne dopasowanie do szumu. W praktyce tuning hyperparametrów boosting często zaczyna się właśnie od tych elementów.
Boosting vs bagging: dwa podejścia do łączenia drzew
Boosting (XGBoost, LightGBM, CatBoost) i bagging (np. Random Forest) często są mylone, choć ich filozofie są różne.
- Bagging: wiele drzew trenowanych niezależnie na losowych próbkach danych, a predykcja to średnia/większość głosów. Zmniejsza wariancję przez uśrednianie, dobrze radzi sobie z przeuczeniem pojedynczego drzewa, ale nie skupia się aktywnie na najtrudniejszych przykładach.
- Boosting: sekwencyjne dodawanie drzew, z których każde stara się poprawić błędy poprzedników. Koncentruje się na obserwacjach, które są trudniejsze do przewidzenia, i zmniejsza bias, ale jest bardziej podatny na overfitting w gradient boosting.
Z punktu widzenia praktyka: bagging jest bardziej „plug and play”, rzadziej wymaga agresywnego tuningu, ale ma mniejszy sufit jakościowy dla danych tablicowych. Boosting może dać wyraźnie lepsze wyniki, lecz wymaga starannej walidacji, regularyzacji i kontroli złożoności drzew.
Konsekwencje dla przeuczenia i tuningu
Gradient boosting bardzo łatwo adaptuje się do detali danych treningowych. Jeśli:
- drzewa są zbyt głębokie,
- learning rate jest za duży,
- liczba iteracji jest zbyt wysoka,
to model szybko nauczy się szumu zamiast sygnału. W praktyce:
- stosuje się wczesne stopowanie (early stopping) na zbiorze walidacyjnym,
- ogranicza się złożoność drzew (głębokość, liczba liści, minimalna liczba próbek w liściu),
- używa się podpróbowania obserwacji i cech (
subsample,colsample_bytree,feature_fraction).
Stąd tak duży nacisk na tuning hyperparametrów boosting przy zastosowaniach produkcyjnych. Różne biblioteki mają inne domyślne ustawienia tych mechanizmów – i to wpływa na wyniki, szczególnie w pierwszych „baseline’ach”.
Kluczowe różnice architektoniczne między XGBoost, LightGBM i CatBoost
XGBoost: punkt odniesienia i klasyczna architektura
XGBoost był pierwszym szeroko adoptowanym, wysoko zoptymalizowanym frameworkiem gradient boostingu na drzewach. Do dziś stanowi punkt odniesienia przy analizie wyników: jeśli nowa metoda nie przebija XGBoost, trudno mówić o postępie.
Kilka elementów architektury XGBoost jest szczególnie istotnych:
- exact vs approximate split finding – XGBoost może znajdować najlepsze podziały w węzłach drzew metodą dokładną (dokładne sortowanie) lub przybliżoną (histogramy). Pozwala to balansować między dokładnością a czasem trenowania,
- obsługa braków danych – XGBoost ma wbudowany mechanizm traktowania braków jako osobnej gałęzi podziału, co ogranicza konieczność agresywnej imputacji,
- bogata regularyzacja – w funkcji celu pojawia się kara za liczbę liści i ich wartości (parametry
gamma,lambda,alpha), co umożliwia precyzyjne kontrolowanie złożoności modelu.
W praktyce XGBoost jest bardzo elastyczny, ale mniej nastawiony na ekstremalną szybkość niż LightGBM. Daje za to dobre, przewidywalne wyniki na wielu typach danych, a jego API jest stabilne i dobrze udokumentowane.
LightGBM: histogramy, leaf-wise i prędkość
LightGBM powstał jako odpowiedź na rosnące rozmiary danych oraz potrzebę szybszego trenowania przy zachowaniu wysokiej jakości predykcji. Jego kluczowe cechy architektoniczne to:
- histogram-based split finding – cechy numeryczne są dyskretyzowane na biny (koszyki), a podziały są szukane na poziomie histogramów, co znacznie przyspiesza uczenie i zmniejsza zużycie pamięci,
- wzrost leaf-wise (best-first) z ograniczeniem głębokości – zamiast symetrycznego wzrostu na poziomach (level-wise), LightGBM rozwija zawsze ten liść, którego podział daje największy spadek straty; efektem są drzewa często bardzo „wąskie” w jednych gałęziach i płytkie w innych,
- zaawansowane optymalizacje pamięciowe – m.in. gradient-based one-side sampling (GOSS), exclusive feature bundling (EFB), dzięki którym LightGBM dobrze sprawdza się również przy dużej liczbie cech.
Leaf-wise growth daje potencjalnie większą moc modelowania, ale jednocześnie zwiększa ryzyko przeuczenia, gdy parametry nie są kontrolowane. Dla początkujących użytkowników jest to jeden z powodów, dla których domyślne ustawienia LightGBM czasem dają bardzo dobre wyniki na treningu, ale gorsze na walidacji, jeśli zabraknie odpowiedniej regularyzacji.
CatBoost: cechy kategoryczne i ordered boosting
CatBoost został zaprojektowany z myślą o danych, w których dominują cechy kategoryczne. Najważniejsza cecha od strony użytkownika: obsługa cech kategorycznych „z pudełka”, bez konieczności jawnego one-hot czy target encodingu po stronie użytkownika.
Kluczowe elementy architektury:
- kodowanie kategorii – CatBoost używa statystyk opartych na docelowej zmiennej (target statistics), ale z zastosowaniem technik zapobiegających wyciekowi informacji (np. ordered target encoding, permutacje danych). Kodowanie jest częścią procesu uczenia, co upraszcza pipeline.
- ordered boosting – zamiast klasycznego boostingu, gdzie każdy kolejny model widzi pełne etykiety treningowe, CatBoost wykorzystuje specjalny schemat, który ma ograniczyć bias wynikający z korzystania z informacji „przyszłościowych” podczas wyliczania statystyk.
- stabilniejsze domyślne parametry – domyślna konfiguracja często daje sensowny kompromis między jakością a ryzykiem przeuczenia, szczególnie przy problemach z dużą liczbą kategorii.
Z perspektywy praktycznej CatBoost jest atrakcyjny, gdy:
- liczba cech kategorycznych jest znaczna,
- wiele z nich ma wysoką krotność (wiele unikalnych wartości),
- chcemy szybko zbudować prototyp bez tworzenia złożonych schematów kodowania kategorii.

Przygotowanie wspólnego eksperymentu – te same dane, te same kryteria
Porównanie XGBoost, LightGBM i CatBoost ma sens tylko wtedy, gdy wszystkie trzy modele grają „na tym samym boisku”. Chodzi nie tylko o identyczny zbiór danych, ale też ten sam sposób walidacji, tę samą metrykę i zbliżone ograniczenia czasowe. Inaczej łatwo wyciągnąć wnioski, które są efektem przypadku, a nie realnych różnic między bibliotekami.
Co porównujemy: jakość, czas, stabilność
W praktycznym teście interesują trzy kategorie wyników:
- jakość predykcji – np. AUC, logloss, RMSE w zależności od zadania; mierzone na zbiorze walidacyjnym lub w cross‑walidacji,
- koszt czasowy – czas trenowania i czas predykcji na rozsądnie dużym zbiorze (np. dziesiątki/setki tysięcy wierszy),
- stabilność – wrażliwość na zmianę losowego podziału danych, seedów, a także na niewielkie zmiany hyperparametrów.
W tle pojawia się też pytanie praktyczne: który model jest łatwiej „dopieścić” do dobrego poziomu w ograniczonym czasie eksperymentowania. To nie zawsze widać w jednej tabelce wyników, ale pojawia się przy realnej pracy nad projektem.
Stałe elementy eksperymentu
Żeby ograniczyć liczbę zmiennych, sensownie jest „zamrozić” kilka elementów setupu:
- ten sam podział danych – np. 70% train, 15% validation, 15% test, z tym samym losowaniem (
random_state), - ta sama metryka główna – np. ROC AUC dla klasyfikacji binarnej, MAPE lub RMSE dla regresji,
- ta sama forma walidacji – podział hold‑out lub K‑fold (często 5‑fold) o identycznych indeksach foldów dla wszystkich modeli,
- te same ograniczenia czasowe – np. maksymalny czas trenowania jednego modelu albo maksymalna liczba iteracji wczesnego stopowania.
Bez takiej dyscypliny łatwo dojść do sytuacji, w której np. LightGBM ma „lepszy wynik”, ponieważ dostał dwa razy więcej iteracji lub dłużej się uczył.
Przykładowy scenariusz: klasyfikacja binarna na danych tablicowych
Dobrym polem testowym jest klasyfikacja binarna z mieszanką cech numerycznych i kategorycznych. Typowe przypadki to:
- ocena ryzyka kredytowego (default / no default),
- model odchodów klientów (churn / no churn),
- detekcja fraudów (fraud / normalna transakcja).
Wspólny mianownik: stosunkowo niewiele próbek w porównaniu z typowymi problemami deep learning (od kilkunastu tysięcy do kilku milionów), sporo zmiennych kategorycznych, jakościowe dane wejściowe z brakami i outlierami.
Dane wejściowe i preprocessing – co trzeba zrobić „przed boostowaniem”
Boosting na drzewach dobrze radzi sobie z surowymi danymi tablicowymi, ale nie jest odporny na każdy bałagan. Zanim model stanie się konkurencją dla prostego baseline’u, dane muszą zostać choć w minimalnym stopniu uporządkowane.
Porządkowanie podstaw: target, indeks, duplikaty
Pierwszy krok to upewnienie się, że sama rama danych ma sens:
- target jest dobrze zdefiniowany (0/1 lub sensownie zakodowany dla klasyfikacji wieloklasowej),
- nie ma powielonych identycznych wierszy, które sztucznie pompują wynik walidacji,
- nie ma wycieków informacji – kolumn, które wprost zawierają wynik (np. „label_encoded_by_hand” na podstawie całych danych).
To nie jest techniczny detal, tylko kluczowe źródło błędów. Jeżeli jedna biblioteka będzie trenowana na zbiorze z ukrytym wyciekiem, wszystkie wyniki porównania przestają być wiarygodne.
Braki danych: różne podejścia bibliotek
XGBoost, LightGBM i CatBoost mają własne strategie radzenia sobie z brakami:
- XGBoost – obsługuje
NaNwprost, tworząc „domyślną” gałąź w drzewie (zachowanie konfigurowalne), - LightGBM – również potrafi przetwarzać wartości brakujące bez wcześniejszej imputacji,
- CatBoost – umie z nimi pracować, szczególnie w połączeniu z własnym kodowaniem cech kategorycznych.
Pojawia się pytanie: imputować wspólnie czy zdać się na mechanizmy wbudowane? Jeśli celem jest czyste porównanie, rozsądnym podejściem jest:
- zostawienie braków jako
NaNdla XGBoost i LightGBM, - analogiczne przekazanie ich CatBoostowi,
- ewentualnie przetestowanie prostej, wspólnej imputacji (np. medianą dla numerycznych, osobną kategorią dla kategorycznych) w osobnym eksperymencie.
Ważne, aby nie stosować innych schematów imputacji dla poszczególnych modeli w tym samym porównaniu – to zmienia dane wejściowe.
Cechy numeryczne: minimalne czyszczenie, bez agresywnej normalizacji
Drzewa decyzyjne nie wymagają skalowania cech do [0,1] czy standaryzacji do rozkładu normalnego. Boosting z natury tworzy progi typu x < c, więc względna skala między cechami nie jest krytyczna tak, jak w regresji liniowej czy sieciach neuronowych.
Przydaje się natomiast:
- naprawa oczywistych błędów (np. wiek 999),
- przycięcie ekstremalnych outlierów lub ich osobne zakodowanie jako flagi (np.
is_extreme_amount), - zastąpienie pojedynczych absurdalnych wartości rozsądnymi limitami (winsoryzacja) – ale możliwie prostymi regułami.
Zbyt agresywne przekształcenia mogą utrudnić interpretację modelu i wprowadzić dodatkową wariancję między eksperymentami.
Cechy kategoryczne: wspólny mianownik vs „magia” CatBoost
Tu widać największą różnicę między bibliotekami:
- XGBoost i LightGBM w typowych API (sklearn, Python) oczekują cech numerycznych. Kategorie zwykle trzeba zakodować ręcznie: one‑hot, target encoding, frequency encoding itp.,
- CatBoost przyjmuje kolumny typu string / kategoria i koduje je sam, wykorzystując własne statystyki.
Jeżeli zależy na wyrównaniu szans, można przyjąć dwa scenariusze:
- Scenariusz „realistyczny” – CatBoost dostaje surowe kategorie, XGBoost/LightGBM – rozsądny, manualnie dobrany encoding (np. kombinacja target/frequency encodingu). To oddaje typową sytuację produkcyjną.
- Scenariusz „laboratoryjny” – wszystkie modele korzystają z tego samego zakodowanego zbioru (np. one‑hot). CatBoost w tym wariancie traci swoją przewagę, ale porównanie staje się bardziej neutralne.
W jednym artykule można pokazać oba podejścia: laboratoryjne dla „suchego” porównania architektury i realistyczne, żeby zobaczyć, jak CatBoost zachowuje się przy naturalnym przepływie pracy.
Redukcja liczby cech: kiedy jest uzasadniona
Boosting dobrze znosi wysoką liczbę zmiennych, ale powyżej pewnego progu zaczyna się walka z czasem i pamięcią. Trzy praktyczne techniki:
- wstępna filtracja cech o zerowej/niemal zerowej wariancji,
- odrzucenie mocno skorelowanych duplikatów (np. dwóch niemal identycznych wskaźników finansowych),
- usunięcie cech, które mają ekstremalnie dużo braków, jeśli ich imputacja jest arbitralna.
Część selekcji można też oddać samym modelom (feature importance), ale wtedy pojawia się ryzyko „uczenia się” procesu selekcji pod konkretną bibliotekę. Dla porównania trzech frameworków lepiej zyskać wspólny, oczyszczony zestaw wejściowy.
Ustawienia startowe – sensowne „baseline” dla XGBoost, LightGBM i CatBoost
Po oczyszczeniu danych potrzebny jest pierwszy, wspólny punkt odniesienia: konfiguracje, które nie są jeszcze wytuningowane, ale też nie są losowo dobranymi domyślnymi parametrami. Co wiemy na starcie? Te biblioteki mają inne domyślne ustawienia, inne strategie budowy drzew i inne mechanizmy regularyzacji. Czego nie wiemy? Jak blisko optimum da się dojść przy niewielkim wysiłku w doborze hyperparametrów.
Założenia dla baseline’u
Kilka prostych zasad, które pomagają zachować porównywalność:
- ten sam learning rate (np. 0.05 lub 0.1),
- podobna liczba drzew, kontrolowana przez wczesne stopowanie (np. maks. 2000 iteracji,
early_stopping_rounds=100), - porównywalna złożoność pojedynczego drzewa – np. średnia głębokość 6–8, zbliżona liczba liści,
- ograniczenie interakcji – wyłączenie zaawansowanych opcji tylko w jednej bibliotece (np. GOSS w LightGBM) w baseline, aby nie wprowadzać dodatkowej przewagi.
Ten punkt startowy nie musi być idealny, ale powinien być rozsądny. Później można dobudować etap lekkiego tuningu wokół każdego baseline’u.
Baseline dla XGBoost: klasyczny, zachowawczy setup
Dla problemu klasyfikacji binarnej, prosta konfiguracja może wyglądać następująco (w stylu API sklearn):
xgb_params = {
"n_estimators": 2000,
"learning_rate": 0.05,
"max_depth": 6,
"min_child_weight": 1.0,
"subsample": 0.8,
"colsample_bytree": 0.8,
"gamma": 0.0,
"reg_lambda": 1.0,
"reg_alpha": 0.0,
"objective": "binary:logistic",
"eval_metric": "auc",
"tree_method": "hist", # dla większych zbiorów
"random_state": 42,
"n_jobs": -1
}
Kilka interpretacji:
max_depth=6– umiarkowanie głębokie drzewa, zwykle bezpieczny start,subsampleicolsample_bytreena poziomie 0.8 – lekkie podpróbkowanie poprawia generalizację i przyspiesza trening,tree_method="hist"– histogramowy wariant dzielenia cech, zbliżający się filozofią do LightGBM.
Podczas trenowania stosuje się wczesne stopowanie na zbiorze walidacyjnym:
model_xgb = XGBClassifier(**xgb_params)
model_xgb.fit(
X_train, y_train,
eval_set=[(X_valid, y_valid)],
early_stopping_rounds=100,
verbose=False
)
Baseline dla LightGBM: leaf-wise pod kontrolą
LightGBM z domyślnym trybem leaf‑wise potrafi bardzo szybko zejść z błędem na zbiorze treningowym, ale bywa agresywny. W baseline warto go lekko „uspokoić”:
lgb_params = {
"n_estimators": 2000,
"learning_rate": 0.05,
"num_leaves": 31, # domyślna, umiarkowana złożoność
"max_depth": -1, # brak twardego limitu, ale num_leaves ogranicza drzewo
"min_data_in_leaf": 20,
"feature_fraction": 0.8,
"bagging_fraction": 0.8,
"bagging_freq": 1,
"objective": "binary",
"metric": "auc",
"boosting_type": "gbdt", # klasyczny gradient boosting
"random_state": 42,
"n_jobs": -1
}
Trenowanie z wczesnym stopowaniem:
model_lgb = lgb.LGBMClassifier(**lgb_params)
model_lgb.fit(
X_train, y_train,
eval_set=[(X_valid, y_valid)],
eval_metric="auc",
early_stopping_rounds=100,
verbose=False
)
W tym układzie:
num_leaves=31ogranicza liczbę liści, a więc i złożoność drzewa,feature_fractionibagging_fractionrealizują subsampling cech i obserwacji podobny do parametrusubsampleicolsample_bytreew XGBoost,boosting_type="gbdt"utrzymuje klasyczny schemat boostingu; alternatywy (dart, goss) można zostawić na osobny eksperyment.
Baseline dla CatBoost: wykorzystanie natywnych kategorii
CatBoost ma inne API i podejście do konfiguracji. W baseline zakłada się, że cechy kategoryczne są przekazane w postaci surowej (np. stringi lub kolumny typu category w Pandas), a model dostaje je jako indeksy kolumn:
Parametry wyjściowe CatBoost: rozsądny punkt startowy
Przykładowa konfiguracja dla klasyfikacji binarnej, przy założeniu, że mamy listę indeksów kolumn kategorycznych cat_features:
from catboost import CatBoostClassifier
cb_params = {
"iterations": 2000,
"learning_rate": 0.05,
"depth": 6,
"l2_leaf_reg": 3.0,
"loss_function": "Logloss",
"eval_metric": "AUC",
"random_seed": 42,
"logging_level": "Silent",
"thread_count": -1,
"border_count": 128, # liczba progów dla cech numerycznych
"bagging_temperature": 1.0, # klasyczny sampling
"use_best_model": True
}
model_cb = CatBoostClassifier(**cb_params)
model_cb.fit(
X_train,
y_train,
eval_set=(X_valid, y_valid),
cat_features=cat_features,
verbose=False
)
Kilka punktów orientacyjnych:
depth=6tworzy drzewa o podobnej złożoności do XGBoost/LightGBM z umiarkowaną głębokością,l2_leaf_regkontroluje regularizację wag liści; wartości 3–10 zwykle dają stabilne wyniki na start,border_countdecyduje o liczbie progów binarnych dla cech numerycznych (im więcej, tym drobniejsze „krojenie” osi cechy, ale większy koszt obliczeń),use_best_model=Truepowoduje, że model po treningu przyjmuje parametry z najlepszej iteracji na zbiorze walidacyjnym, pełniąc rolę wczesnego stopowania.
CatBoost z założenia korzysta z porządkowego kodowania kategorii (ordered target statistics), więc nie ma potrzeby ręcznej implementacji target encodingu. Przy przejściu na scenariusz „laboratoryjny”, w którym wszystkie modele dostają one‑hot, parametry można zostawić te same, ale cat_features zostanie puste, a wszystkie kolumny będą traktowane jako numeryczne.
Wspólne zasady ewaluacji baseline’u
Aby porównanie baseline’ów było rzetelne, sam sposób pomiaru jakości musi być zbliżony. Co wiemy na tym etapie? Mamy:
- wspólny zbiór treningowy i walidacyjny,
- podobny schemat wczesnego stopowania (okno ok. 100 iteracji),
- zbieżne metryki (np. AUC dla klasyfikacji binarnej).
Kroki techniczne są proste, ale kilka detali wyraźnie wpływa na wynik:
- wszystkie modele uczone są na tym samym train/valid split (ten sam seed, ten sam podział),
- metryka liczona jest na tym samym zbiorze walidacyjnym i – jeśli to możliwe – na niezależnym zbiorze testowym,
- porównywana jest zarówno metryka końcowa, jak i liczba faktycznie wykorzystanych iteracji (boosting rounds).
Przykładowe, zunifikowane obliczenie AUC na zbiorze testowym w Pythonie:
from sklearn.metrics import roc_auc_score
pred_xgb = model_xgb.predict_proba(X_test)[:, 1]
pred_lgb = model_lgb.predict_proba(X_test)[:, 1]
pred_cb = model_cb.predict_proba(X_test)[:, 1]
auc_xgb = roc_auc_score(y_test, pred_xgb)
auc_lgb = roc_auc_score(y_test, pred_lgb)
auc_cb = roc_auc_score(y_test, pred_cb)
Czas treningu i zużycie pamięci można zmierzyć z grubsza za pomocą prostych narzędzi (logi, time.time(), monitorowanie RAM), ale w podstawowym porównaniu zwykle wystarczy zanotowanie czasu ściennego (wall‑clock) na tej samej maszynie.

Eksperyment porównawczy: ten sam pipeline, trzy implementacje
Struktura eksperymentu: od podziału danych po metryki
Schemat porównania można ująć w kilku krokach, trzymając się jednego, spójnego pipeline’u:
- Podział danych na train, validation, test z ustalonym random seedem.
- Preprocessing (imputacja, kodowanie, redukcja cech) wykonany wyłącznie na części treningowej, a następnie zastosowany do walidacji i testu.
- Trening trzech modeli z baseline’owymi parametrami i wczesnym stopowaniem.
- Pomiar jakości i czasu na zbiorze walidacyjnym i testowym.
Dobrą praktyką jest zablokowanie źródeł losowości we wszystkich bibliotekach (parametry random_state, random_seed, np.random.seed), aby uniknąć sytuacji, w której część różnic wynika z losowego doboru próbek w baggingu.
Przykładowy kod „klejący” cały eksperyment
Przy użyciu prostego podziału train/valid/test z sklearn całość może wyglądać tak:
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
RANDOM_STATE = 42
# 1. Podział na train + temp, potem temp na valid i test
X_train, X_temp, y_train, y_temp = train_test_split(
X, y, test_size=0.3, random_state=RANDOM_STATE, stratify=y
)
X_valid, X_test, y_valid, y_test = train_test_split(
X_temp, y_temp, test_size=0.5, random_state=RANDOM_STATE, stratify=y_temp
)
# 2. Preprocessing dopasowany na train, zastosowany na valid i test
preprocessor.fit(X_train)
X_train_p = preprocessor.transform(X_train)
X_valid_p = preprocessor.transform(X_valid)
X_test_p = preprocessor.transform(X_test)
# 3. Trening modeli (przy założeniu, że parametry xgb_params, lgb_params, cb_params są przygotowane)
model_xgb = XGBClassifier(**xgb_params)
model_xgb.fit(
X_train_p, y_train,
eval_set=[(X_valid_p, y_valid)],
early_stopping_rounds=100,
verbose=False
)
model_lgb = lgb.LGBMClassifier(**lgb_params)
model_lgb.fit(
X_train_p, y_train,
eval_set=[(X_valid_p, y_valid)],
eval_metric="auc",
early_stopping_rounds=100,
verbose=False
)
model_cb = CatBoostClassifier(**cb_params)
model_cb.fit(
X_train_p, y_train,
eval_set=(X_valid_p, y_valid),
cat_features=cat_features, # indeksy liczonych względem X_train_p
verbose=False
)
# 4. Ewaluacja na teście
pred_xgb = model_xgb.predict_proba(X_test_p)[:, 1]
pred_lgb = model_lgb.predict_proba(X_test_p)[:, 1]
pred_cb = model_cb.predict_proba(X_test_p)[:, 1]
auc_xgb = roc_auc_score(y_test, pred_xgb)
auc_lgb = roc_auc_score(y_test, pred_lgb)
auc_cb = roc_auc_score(y_test, pred_cb)
W praktyce część kodu otaczającego modele (przygotowanie danych, mierzenie czasu, logowanie metryk) dobrze jest zamknąć w powtarzalnych funkcjach lub klasach, tak aby wymiana modelu nie wymagała przepisywania całego pipeline’u.
Scenariusz „realistyczny” vs „laboratoryjny” w jednym kodzie
Łatwo popełnić błąd i porównać „jabłka do gruszek”: CatBoost z natywnymi kategoriami kontra XGBoost/LightGBM z prostym one‑hotem. Rozwiązaniem jest wyraźne rozdzielenie dwóch wariantów eksperymentu w kodzie:
- osobny pipeline preprocessingowy dla scenariusza realistycznego (np. target/frequency encoding dla XGBoost/LightGBM, surowe kategorie dla CatBoost),
- osobny pipeline dla scenariusza laboratoryjnego (wspólne one‑hot lub inny wspólny encoding, bez korzystania z natywnych mechanizmów CatBoosta).
Przykładowe, schematyczne rozdzielenie:
def build_realistic_pipelines():
# pipeline_oh: encodowanie ręczne (dla XGB/LGB)
# pipeline_cb: minimalny preprocessing numeryczny (dla CatBoost)
...
def build_lab_pipeline():
# pipeline_lab: wspólny one-hot / ordinal encoding dla wszystkich
...
pipe_xgb_real, pipe_lgb_real, pipe_cb_real = build_realistic_pipelines()
pipe_all_lab = build_lab_pipeline()
Dzięki temu w raportach można wprost odnieść się do dwóch wyników dla każdego modelu: „jak sprawdza się w naturalnym dla siebie środowisku” oraz „jak wypada przy identycznie przygotowanych danych”.
Lekki tuning hyperparametrów: ile „darmowego” zysku da się wycisnąć
Zakres poszukiwań: świadome, a nie losowe kręcenie gałkami
Baseline pokazuje punkt startowy, ale trudno na jego podstawie odpowiedzieć na pytanie: czy różnice między modelami są istotne, czy wynikają z konserwatywnych ustawień? Czego nie wiemy? Jak bardzo każdy z frameworków reaguje na drobne korekty kluczowych parametrów.
W lekkim tuningu nie chodzi o wielodniowe poszukiwanie optimum, lecz o przeskanowanie sensownego, wąskiego zakresu wokół baseline’u:
- learning rate – np. 0.02, 0.05, 0.1, z dostosowaną liczbą iteracji,
- głębokość / liczba liści – np. 4, 6, 8,
- siła regularizacji –
reg_lambda,reg_alpha,l2_leaf_regna kilku poziomach, - subsampling – lekkie zmiany wokół 0.7–0.9.
Z perspektywy porównania trzech bibliotek to wystarczy, by zobaczyć, który model szybko „łapie formę”, a który wymaga precyzyjniejszego strojenia.
Przykład prostego przeszukiwania siatką dla XGBoost
Użycie GridSearchCV wprost na pełnych parametrach boostingowych potrafi być bardzo wolne. Szybszym wariantem jest ręczne przeiterowanie po kilku kombinacjach i wykorzystanie wczesnego stopowania wewnątrz pętli:
from itertools import product
learning_rates = [0.02, 0.05, 0.1]
max_depths = [4, 6, 8]
subsamples = [0.7, 0.8]
best_auc = -1
best_params = None
for lr, depth, subs in product(learning_rates, max_depths, subsamples):
params = xgb_params.copy()
params.update({
"learning_rate": lr,
"max_depth": depth,
"subsample": subs
})
model = XGBClassifier(**params)
model.fit(
X_train_p, y_train,
eval_set=[(X_valid_p, y_valid)],
early_stopping_rounds=100,
verbose=False
)
pred = model.predict_proba(X_valid_p)[:, 1]
auc = roc_auc_score(y_valid, pred)
if auc > best_auc:
best_auc = auc
best_params = params
Analogiczną strategię można zastosować do LightGBM (zmieniając num_leaves, feature_fraction) i CatBoosta (przemieszczając depth, l2_leaf_reg, bagging_temperature). Kluczem jest korzystanie z wczesnego stopowania, które skraca trening konfiguracji ewidentnie gorszych.
Bayesowski tuning lub optuna: kiedy przyspieszyć poszukiwania
Przy większej liczbie parametrów i ograniczonym budżecie obliczeniowym wygodniejszy bywa tuning bayesowski (np. z użyciem optuna):
import optuna
def objective_xgb(trial):
params = xgb_params.copy()
params.update({
"learning_rate": trial.suggest_float("learning_rate", 0.02, 0.2, log=True),
"max_depth": trial.suggest_int("max_depth", 3, 10),
"subsample": trial.suggest_float("subsample", 0.6, 0.9),
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.6, 0.9),
"reg_lambda": trial.suggest_float("reg_lambda", 0.5, 5.0, log=True),
})
model = XGBClassifier(**params)
model.fit(
X_train_p, y_train,
eval_set=[(X_valid_p, y_valid)],
early_stopping_rounds=100,
verbose=False
)
pred = model.predict_proba(X_valid_p)[:, 1]
auc = roc_auc_score(y_valid, pred)
return auc
study = optuna.create_study(direction="maximize")
study.optimize(objective_xgb, n_trials=30)
Takie narzędzie można wykorzystać symetrycznie dla trzech bibliotek, modyfikując zakresy parametrów. Efektem nie będzie absolutne optimum, ale uczciwe „dokręcenie śrub” na porównywalnym budżecie (np. 30–50 prób na model).
Porównanie wyników: jakość, czas, stabilność
Metryki jakościowe: AUC to nie wszystko
W klasyfikacji binarnej AUC jest wygodna, bo nie wymaga wyboru progu i dobrze sprawdza się przy nierównomiernych klasach. W wielu zastosowaniach praktycznych liczą się jednak też inne wskaźniki:
- logloss – użyteczny, gdy istotna jest jakość probabilistyczna predykcji (np. scoring ryzyka),
- precision/recall przy kilku istotnych progach (np. w detekcji fraudów),
- calibration – zgodność przewidywanych prawdopodobieństw z częstością faktycznych zdarzeń.
Trzy biblioteki zwykle osiągają zbliżone wyniki AUC, ale różnice mogą ujawnić się właśnie w kalibracji lub zachowaniu w „ogonie” rozkładu (bardzo wysokie lub bardzo niskie prawdopodobieństwa).
Czas treningu i predykcji: który model jest szybszy w praktyce
Przy identycznych danych i podobnej liczbie iteracji:
- LightGBM często wygrywa czasem treningu na dużych, rzadkich macierzach (np. po one‑hocie),
- mieszanką różnych typów cech,
- umiarkowanymi rozmiarami datasetów,
- nieliniowościami i interakcjami bez ręcznego tworzenia cech.
- zmniejsza wrażliwość na losowy podział danych treningowych,
- lepiej kontroluje złożoność modelu (głębokość drzew, liczba liści, regularyzacja),
- uzyskuje wyższe metryki jakości przy podobnym nakładzie pracy.
- złożoność drzew: max_depth, num_leaves, min_child_weight / min_data_in_leaf – kontrolują, jak „szczegółowe” są pojedyncze drzewa,
- tempo uczenia: learning_rate (eta) – mniejsza wartość oznacza wolniejsze, ale często stabilniejsze uczenie,
- liczba iteracji: n_estimators / num_boost_round – ile drzew dodajemy do modelu.
- early stopping na osobnym zbiorze walidacyjnym,
- ograniczenie głębokości drzew i liczby liści,
- podpróbkowanie wierszy i kolumn (subsample, colsample_bytree, feature_fraction),
- niższy learning rate i odpowiednio większą liczbę drzew.
Najczęściej zadawane pytania (FAQ)
Kiedy wybrać XGBoost, a kiedy LightGBM lub CatBoost?
XGBoost zwykle sprawdza się tam, gdzie potrzebna jest duża kontrola nad procesem trenowania i stabilne wyniki na nierównoważonych danych, np. w scoringu kredytowym czy fraud detection. Ma rozbudowany zestaw parametrów regularyzacji i dobrze udokumentowany ekosystem.
LightGBM bywa pierwszym wyborem przy bardzo dużych zbiorach danych lub konieczności częstego retrenowania modelu. Jest zaprojektowany pod kątem szybkości i skalowania, bez wyraźnej utraty jakości względem XGBoost.
CatBoost jest szczególnie przydatny, gdy dominują cechy kategoryczne, a zespół nie chce inwestować dużo czasu w ręczne kodowanie (one‑hot, target encoding). Model natywnie obsługuje cechy kategoryczne, co upraszcza pipeline i zmniejsza ryzyko błędów w preprocessingu.
Co lepsze na dane tabelaryczne: XGBoost, LightGBM, CatBoost czy sieci neuronowe?
Dla typowych danych tabelarycznych (kolumny numeryczne i kategoryczne, zróżnicowane skale) gradient boosting na drzewach (XGBoost, LightGBM, CatBoost) najczęściej wygrywa z klasycznymi sieciami neuronowymi pod względem stosunku wysiłek → jakość. Sieci głębokie wymagają zwykle więcej danych, starannej normalizacji i bardziej skomplikowanej inżynierii cech.
Boosting lepiej radzi sobie z:
W specjalistycznych zastosowaniach (obrazy, tekst, sygnały) sieci neuronowe przejmują jednak prowadzenie, bo tam struktura danych jest inna.
Czy CatBoost zawsze jest najlepszy przy cechach kategorycznych?
CatBoost ma natywną obsługę cech kategorycznych i często upraszcza pracę przy datasetach z wieloma kolumnami typu „kraj”, „produkt”, „kampania”, „kanał sprzedaży”. Eliminuje potrzebę ręcznego kodowania i zwykle daje stabilne wyniki bez skomplikowanego tuningu.
Nie oznacza to jednak, że zawsze wygrywa z XGBoost czy LightGBM. W projektach o bardzo dużej skali, z agresywnie optymalizowanym pipeline’em, dobrze przygotowane kodowanie cech (np. target encoding + XGBoost) może dać podobną lub lepszą jakość. Pytanie kontrolne brzmi: czy kluczowa jest maksymalna jakość, czy prostota i szybkość iteracji?
Dlaczego gradient boosting często wygrywa z regresją liniową i pojedynczym drzewem?
Regresja liniowa zakłada liniową zależność między cechami a wynikiem. Na realnych danych biznesowych (popyt, ceny, ryzyko) zależności są zwykle nieliniowe i zawierają interakcje między cechami. Gradient boosting z wielu małych drzew uczy te nieliniowości bez ręcznego dodawania dziesiątek cech z interakcji.
W porównaniu z pojedynczym drzewem decyzyjnym, boosting:
Efekt w praktyce: mniej „dziwnych” podziałów i skoków w predykcjach, bardziej stabilne wyniki walidacji.
Boosting vs Random Forest – co wybrać do klasyfikacji binarnej?
Random Forest (bagging) jest bardziej odporny na przeuczenie bez intensywnego tuningu i dobrze sprawdza się jako model bazowy. Każde drzewo uczy się niezależnie, na losowych próbkach danych, a predykcja to uśrednienie wyników. To stabilne, dość „bezpieczne” podejście.
Boosting (XGBoost, LightGBM, CatBoost) konstrukcyjnie celuje w wyższą jakość na danych tabelarycznych: kolejne drzewa korygują błędy poprzednich, skupiając się na trudnych przykładach. Wymaga jednak lepszej walidacji, regularyzacji i częściej wczesnego stopowania. Jeśli kluczowe jest maksimum AUC/logloss, boosting będzie częstym wyborem; jeśli liczy się prostota i mniejsza podatność na błędy w tuningu, Random Forest może być rozsądną alternatywą.
Jakie parametry są najważniejsze przy tuningu XGBoost, LightGBM i CatBoost?
We wszystkich trzech bibliotekach kluczowe są trzy grupy parametrów:
Te parametry są ze sobą powiązane: niższy learning rate wymaga zwykle większej liczby drzew, a zbyt głębokie drzewa przy wysokim learning rate to prosty przepis na przeuczenie.
W tle działają też parametry podpróbowania (subsample, colsample_bytree, feature_fraction), które pomagają dodatkowo ograniczyć overfitting i przyspieszyć trenowanie. Pytanie praktyczne brzmi: gdzie jest granica, przy której dokładanie kolejnych drzew nie poprawia już metryki walidacyjnej?
Jak uniknąć przeuczenia modeli boostingowych na danych tabelarycznych?
Modele boostingowe są „agresywne” w dopasowywaniu się do danych, dlatego potrzebują kilku bezpieczników. W praktyce stosuje się:
Warto też pilnować rzetelnej walidacji krzyżowej, szczególnie przy nierównoważonych klasach. Jeśli metryka na treningu rośnie, a na walidacji zaczyna spadać – to zwykle wyraźny sygnał, że model zaczął uczyć się szumu, a nie sygnału.






