ML Atlas

02 · Dane · 4 min czytania · aktualizacja

Jak zakodować zmienne kategoryczne: one-hot, label encoding czy target encoding?

W skrócie

Model liczy na liczbach, więc kategorie trzeba zakodować. One-hot nie wprowadza fałszywego porządku, kodowanie liczbami tak, a target encoding grozi wyciekiem.

Co to jest

Kodowanie zmiennych kategorycznych to zamiana wartości takich jak „Southampton”, „czerwony” czy „klient premium” na liczby, które model potrafi przetworzyć. Najczęstsze sposoby to kodowanie porządkowe (label/ordinal encoding, każda kategoria dostaje liczbę całkowitą), one-hot (osobna kolumna 0/1 dla każdej kategorii) i target encoding (kategoria zastąpiona średnią zmiennej celu w tej kategorii).

Wybór kodowania nie jest formalnością. Każde z nich mówi modelowi coś innego o relacjach między kategoriami — a model w to uwierzy.

Intuicja: jeśli kolorom nadamy numery czerwony = 1, zielony = 2, niebieski = 3, to dla regresji liniowej zielony stanie się „średnią” czerwonego i niebieskiego.

Mechanizm — dlaczego tak działa

Model liniowy mnoży cechę przez jedną wagę. Przy kodowaniu porządkowym efekt kategorii musi więc rosnąć lub maleć równymi krokami w kolejności numerów. Jeśli prawdziwa zależność jest inna, model jej nie odwzoruje — nie dlatego, że jest za słaby, tylko dlatego, że kodowanie mu to uniemożliwia. Dla zmiennych naprawdę porządkowych (wykształcenie, klasa biletu) to założenie bywa rozsądne; dla nominalnych jest arbitralne.

One-hot daje każdej kategorii własną kolumnę i własną wagę, więc nie narzuca porządku ani odstępów. Dla k kategorii powstaje k kolumn (albo k − 1, gdy jedną usuwamy jako poziom odniesienia, by uniknąć idealnej współliniowości z wyrazem wolnym — tzw. pułapki zmiennych fikcyjnych). Koszt: przy setkach czy tysiącach kategorii (kody pocztowe, ID produktów) tabela robi się ogromna i rzadka, a rzadkie kategorie mają za mało przykładów, by oszacować ich wagi.

Target encoding (Micci-Barreca, 2001) rozwiązuje problem wielu kategorii: każdą zastępuje jedna liczba, np. odsetek sukcesów w tej kategorii. Pułapką jest wyciek: jeśli średnią liczymy na tych samych wierszach, na których uczymy model, kategoria z jednym przykładem dostaje dokładnie jego etykietę. Model widzi wtedy odpowiedź w cechach. Rozwiązania to wygładzanie w stronę średniej globalnej i liczenie średnich poza foldem (out-of-fold). CatBoost robi to w kolejności „wcześniejszych” wierszy (ordered target statistics).

Drzewa decyzyjne są mniej wrażliwe na kodowanie porządkowe, bo mogą wyciąć dowolny przedział liczb kilkoma podziałami — choć arbitralna kolejność wymusza więcej podziałów. Sieci neuronowe przy wielu kategoriach używają zanurzeń (embeddings): każda kategoria dostaje uczony wektor, a podobne kategorie mogą trafić blisko siebie.

Na przykładzie

Na Titanicu (889 pasażerów ze znanym portem) przeżywalność zależy od portu zaokrętowania: Cherbourg 55,4%, Queenstown 39,0%, Southampton 33,7%. Regresja logistyczna z samym portem, oceniona 10-krotną walidacją krzyżową, daje przy one-hot trafność 63,8% i log loss 0,652 (model bez cech: 0,665). Jej przewidywane prawdopodobieństwa — 55,1%, 39,2% i 33,8% — praktycznie odtwarzają odsetki w grupach.

Ten sam model przy kodowaniu S = 0, C = 1, Q = 2 osiąga tylko 60,0% trafności i log loss 0,660. Jedna waga musi opisać monotoniczny trend, a Cherbourg z najwyższą przeżywalnością utknął w środku skali. Co ciekawe, kodowanie C = 0, Q = 1, S = 2 daje wynik równy one-hot — bo przypadkiem ułożyło porty w kolejności malejącej przeżywalności. Kodowanie porządkowe działa więc tylko wtedy, gdy trafimy w porządek, który istnieje w danych.

Dane: Titanic

W praktyce

  • OneHotEncoder(handle_unknown='ignore') w scikit-learn; drop='first' tylko dla modeli bez regularyzacji, gdzie współliniowość szkodzi.
  • OrdinalEncoder(categories=[[...]]) dla zmiennych porządkowych — zawsze z jawnie podaną kolejnością, nie alfabetyczną.
  • TargetEncoder (scikit-learn od 1.3) liczy średnie z walidacją krzyżową wewnątrz fit_transform, co ogranicza wyciek.
  • W pandas szybki one-hot: pd.get_dummies(df, columns=[...]) — ale w produkcji lepszy enkoder z biblioteki, który pamięta kategorie z treningu.
  • Rzadkie kategorie łącz w „inne” (min_frequency w OneHotEncoder).
  • Typowy błąd: dopasowanie enkodera na całym zbiorze lub target encoding bez walidacji krzyżowej.

Najczęstsze pytania

Kiedy wystarczy label encoding?
Gdy zmienna jest porządkowa i zakładasz w przybliżeniu równe odstępy, albo gdy używasz drzew (las losowy, gradient boosting), które radzą sobie z arbitralną numeracją kosztem dodatkowych podziałów. Dla modeli liniowych, kNN i sieci przy zmiennych nominalnych to zły wybór.
Co zrobić z kategorią, której nie było w treningu?
One-hot z `handle_unknown='ignore'` zakoduje ją jako same zera, czyli „żadna ze znanych”. Target encoding przypisze jej średnią globalną. Warto mieć też jawną kategorię „inne”.
Ile kategorii to „za dużo” dla one-hot?
Nie ma sztywnej granicy. Problem zaczyna się, gdy wiele kategorii ma pojedyncze przykłady — wtedy wagi są szacowane z szumu. Wtedy pomagają łączenie rzadkich kategorii, target encoding lub zanurzenia.

Źródła

  • Micci-Barreca D. (2001). „A preprocessing scheme for high-cardinality categorical attributes in classification and prediction problems”. ACM SIGKDD Explorations Newsletter, 3(1), 27–32.
  • Prokhorenkova L., Gusev G., Vorobev A., Dorogush A. V., Gulin A. (2018). „CatBoost: unbiased boosting with categorical features”. NeurIPS 2018.
  • James G., Witten D., Hastie T., Tibshirani R. „An Introduction to Statistical Learning”, 2nd ed., 2021, rozdz. 3.3.1 (Qualitative Predictors).
  • Dokumentacja scikit-learn: Encoding categorical features, https://scikit-learn.org/stable/modules/preprocessing.html#encoding-categorical-features

Zobacz też