Besøg: Kategoriske funktioner og kodning i beslutningstræer

Fuld kildekode til eksperimentet og plottene kan findes her: https://github.com/Laurae2/CategoricalAnalysis

Når du har kategoriske funktioner, og du bruger beslutningstræer, har du ofte et stort problem: hvordan skal man håndtere kategoriske funktioner?

Ofte ser du følgende ...:

  • Udsættelse af problemet: Brug en maskinlæringsmodel, der håndterer kategoriske funktioner, den største løsning!
  • Løs problemet nu: designmatrix, en-hot kodning, binær kodning ...

Normalt vil du vil tackle problemet nu, fordi hvis du udsætter problemet, betyder det, at du allerede har fundet løsningen:

  • Du udsætter ikke problemer, fordi de i Data Science ophobes hurtigt som helvede (held og lykke med at huske ethvert problem, der er opstået, og kom derefter tilbage en måned senere uden at tænke over dem og recitere dem hver).
  • Du udsætter ikke problemer uden at kende det potentielle middel bagefter (ellers kan du have en fungerende pipeline, men ingen løsning til at løse det!).

Så hvad er der? Lad os gå tilbage til det grundlæggende ved beslutningstræer og kodning, så kan vi teste nogle gode ting ... i tre dele:

  • Implementeringer af maskinlæring: specifikationer er forskellige
  • Eksempler på måder at kode kategoriske funktioner på
  • Benchmarking Encodings versus vanilla Categorical features

Beslutningstræer og kodning

Specifikationer for maskinlæring

Når du bruger beslutningstræsmodeller og kategoriske funktioner, har du for det meste tre typer modeller:

  1. Modeller, der håndterer kategoriske funktioner KORREKT. Du kaster bare de kategoriske funktioner på modellen i det passende format (f.eks .: som faktorer i R), OG maskinindlæringsmodellen behandler kategoriske funktioner korrekt som kategorier. BEDSTE SAG, fordi det passer til dine behov.
  2. Modeller, der håndterer kategoriske funktioner Ukorrekt. Du kaster bare de kategoriske funktioner på modellen i det passende format (f.eks .: som faktorer i R), MEN maskinindlæringsmodellen behandler kategoriske funktioner forkert ved at udføre troldmandsbehandling for at omdanne dem til noget anvendeligt (som en hot-kodning), medmindre du er opmærksom på det. VÆRSTE SAG EVER, fordi det ikke gør, hvad du forventede at gøre.
  3. Modeller, der IKKE håndterer kategoriske funktioner overhovedet. Du skal forbehandle manuelt de kategoriske funktioner for at have dem i et passende format til maskinindlæringsmodellen (normalt: numeriske funktioner). Men hvordan forvandler du dem (også kaldet ENCODE) dem?

Vi vil specifikt målrette mod den tredje type model, fordi det er det, vi vil vurdere. Der er mange metoder til at kode kategoriske funktioner. Vi vil tjekke tre af dem: numerisk kodning, en-hot-kodning og binær kodning.

Kategorisk kodningsspecifikation

Kategorisk kodning henviser til omdannelse af en kategorisk funktion til en eller flere numeriske funktioner. Du kan bruge enhver matematisk metode eller logisk metode, du ønsker at omdanne den kategoriske funktion, himlen er grænsen for denne opgave.

  • Numerisk kodning
Konverterer du dine kategoriske funktioner for hånd, eller laver du arbejdet med en computer?

Numerisk kodning er meget enkel: Tildel et vilkårligt nummer til hver kategori.

Der er ingen raketvidenskab til transformationen, undtagen måske ... hvordan tildeler du det vilkårlige nummer? Er der en enkel måde?

Det typiske tilfælde er at lade dit foretrukne programmeringssprog gøre arbejdet.

For eksempel kan du gøre sådan i R ...

my_data $ cat_feature <- as.numeric (as.factor (my_data $ cat_feature))

Sådan som dette:

as.numeric (as.factor (c ( "Louise",
                       "Gabriel",
                       "Emma",
                       "Adam",
                       "Alice",
                       "Raphael",
                       "Chloe",
                       "Louis",
                       "Jeanne",
                       "Arthur")))
Dette fungerer, dette er ikke brainer, og det koder for den måde, det vil deterministisk (tjek bestillingen, så ser du).
  • En-varm kodning
One-Hot Encoding er bare en designmatrix med den første faktor, der holdes. En designmatrix fjerner den første faktor for at undgå matrixinversionsproblemet i lineære regressioner.

Har du nogensinde hørt om One-Hot Encoding og dens magi? Her har du det: dette er en designmatrix, hvor du beholder den første faktor i stedet for at fjerne den (hvor enkel!).

For at gøre det klart, skal du bare kontrollere billedet, da det taler for sig selv bedre end 1.000 ord.

Ud over at tænke over, hvad One-Hot Encoding gør, vil du bemærke noget meget hurtigt:

  • Du har så mange kolonner, som du har kardinaliteter (værdier) i den kategoriske variabel.
  • Du har en masse nuller og kun få 1'er! (en 1 pr. ny funktion)

Derfor skal du vælge mellem to repræsentationer af One-Hot Encoding:

  • Tæt repræsentation: 0'er gemmes i hukommelsen, hvilket balloner RAM bruger meget, hvis du har mange kardinaliteter. Men i det mindste er støtten til en sådan repræsentation typisk… over hele verden.
  • Sparsom gengivelse: 0'erne gemmes ikke i hukommelsen, hvilket gør RAM-effektiviteten meget bedre, selvom du har millioner af kardinaliteter. Held og lykke med at finde støtte til sparsomme matrixer til maskinlæring, fordi det ikke er udbredt (tænk: xgboost, LightGBM osv.).

Igen lader du normalt dit foretrukne programmeringssprog gøre arbejdet. Gå ikke gennem hver kategorisk værdi og tildel en kolonne, da dette overhovedet IKKE er en effektiv. Det er ikke svært, ikke?

Eksempel i R, "en linje" !:

model.matrix (~ kat + 0,
             data = data.frame (
               cat = as.factor (c ("Louise",
                                 "Gabriel",
                                 "Emma",
                                 "Adam",
                                 "Alice",
                                 "Raphael",
                                 "Chloe",
                                 "Louis",
                                 "Jeanne",
                                 "Arthur"))))
Tæt One-Hot-kodning i R-eksempel. Som sædvanlig er den specifikke rækkefølge identisk med den numeriske version, fordi as.factor vælger ordren vilkårligt!

Hvis du er ved at løbe tør for den tilgængelige hukommelse, hvad med at arbejde med sparse matrixer? At gøre det i R er ingen hjerner i “én linje”!

bibliotek (Matrix)
sparse.model.matrix (~ kat + 0,
                    data = data.frame (
                      cat = as.factor (c ("Louise",
                                        "Gabriel",
                                        "Emma",
                                        "Adam",
                                        "Alice",
                                        "Raphael",
                                        "Chloe",
                                        "Louis",
                                        "Jeanne",
                                        "Arthur"))))
Sparse One-Hot Encoding i R. Der er ingen forskel i den tætte version, bortset fra at vi ender med en sparsom matrix (dgCMatrix: sparse kolonnekomprimeret matrix).
  • Binær kodning
Kraft fra binære filer!

Målet med binær kodning ... er at bruge binær kodning til at hash kardinaliteterne til binære værdier.

Ved at bruge strømloven for binær kodning er vi i stand til at gemme N-kardinaliteter ved hjælp af funktionerne loft (log (N + 1) / log (2)).

Det betyder, at vi kun kan gemme 4294967295 kardinaliteter ved hjælp af kun 32 funktioner med binær kodning! Er det ikke fantastisk at ikke have disse 4294697295 funktioner fra One-Hot Encoding? (hvordan skal du lære 4 milliarder funktioner i et beslutningstræ ...? du har brug for en dybde på 32, og den kan ikke læses ...)

Stadig så let i (base) R, behøver du bare at tro, at du er begrænset til et specificeret antal bits (vil du nogensinde nå 4294967296 kardinaliteter? Hvis ja, slippe af med nogle kategorier, fordi du har for mange af dem ...):

my_data <- c ("Louise",
             "Gabriel",
             "Emma",
             "Adam",
             "Alice",
             "Raphael",
             "Chloe",
             "Louis",
             "Jeanne",
             "Arthur")
matrix (
  as.integer (intToBits (as.integer (as.factor (my_data)))),
  ncol = 32,
  nrow = længde (my_data),
  byrow = SAND
) [, 1: loft (log (længde (unik (my_data)) + 1) / log (2))]
Binær kodning i base R.

Ugh, formlen er lidt større end forventet. Men du får ideen:

Tre nøgleaktioner, der skal udføres til binær kodning.
  • Funktion 1: konverter my_data til faktor, derefter til heltal (“numerisk”), derefter til numerisk binær repræsentation (som en vektor med længde 32 for hver observation) og derefter til heltal (“numerisk”).
  • Funktion 2: konverter det "numeriske" til en matrix med 32 kolonner og det samme antal rækker som antallet af originale observationer.
  • Funktion 3: ved hjælp af invers fra den binære strømegenskab (loft (log (N + 1) / log (2))), fjern alle de ubrugte kolonner (kolonnerne med nuller).

Der er naturligvis lettere måder at gøre dette på. Men jeg gør dette eksempel for at vise, at du kan gøre dette i base R. Intet behov for smarte pakke ting.

Benchmarking Performance af kodning

Vi vil benchmarke ydeevnen for fire typer kodning:

  • Kategorisk kodning (rå, som den er)
  • Numerisk kodning
  • En-varm kodning
  • Binær kodning

Vi vil bruge rpart som beslutningstræ indlæringsmodel, da den også er uafhængig af tilfældige frø.

Det eksperimentelle design er følgende:

  • Vi opretter datasæt af en kategorisk funktion med 8 til 8192 kardinaliteter (trin med magt på 2).
  • Vi bruger 25% eller 50% af kardinaliteter som positive mærker til at vurdere beslutningstræets ydeevne. Dette betyder et forhold på 1: 3 eller 1: 1.
  • Vi kører 250 gange hver kombination af kardinaliteter og procentdel af positive labels for at få en bedre forventet værdi (gennemsnit) af ydeevnen.
  • For at fremskynde beregningerne bruger vi 6 parallelle tråde, da One-Hot Encoding er beregningsintensivt.
  • Rpart-funktionen er begrænset til en maksimal dybde på 30 til praktisk anvendelse og bruges med følgende parametre:
rpart (etiket ~.,
      data = my_data,
      metode = "klasse",
      parms = liste (split = "information"),
      kontrol = rpart.control (minsplit = 1,
                              minbucket = 1,
                              cp = 1e-15,
                              maxcompete = 1,
                              maxsurrogate = 1,
                              brugesurrogat = 0,
                              xval = 1,
                              surrogatestyle = 1,
                              maksimal dybde = 30))

Advarsel: husk, at vi gør dette på et syntetisk datasæt med perfekte regler. Du kan muligvis få modstridende ydelser på den virkelige verdens datasæt.

Sneak Peak: hvordan ser beslutningstræerne ud?

For eksempel skyld med 1024 kategorier og 25% positive labels.

  • Kategorisk kodning
  • Numerisk kodning
  • En-varm kodning
  • Binær kodning

Man kan forstå det som følgende:

  • Kategorisk kodning godt ... ligestillingsregler, så det er let at opdele.
  • Numerisk kodning kræver opdeling af sig selv, hvis den splittes 30 gange i træk på den samme gren, er den forbi. Hvis en split fryses bagefter, er det også slut for den gren. Derfor masser af RNG-formning!
  • Én varm kodning kræver .. lige så mange opdelinger som der er kategorier, hvilket betyder en vanvittig masse og så meget at den stopper meget hurtigt, fordi hvis du udfører en opdeling, vil en del af opdelingen blive frosset (fordi det er et perfekt regeldatasæt) => det giver denne rulletrappe-lignende form og dermed meget dårligt ydeevne.
  • Binær kodning har under 30 funktioner i alle mine tilfælde, derfor skal hvert træ være i stand til at skildre alle reglerne (teori er sandt, praksis er forkert, fordi du har brug for opdelinger for ikke at lukke sig selv, hvilket ikke er muligt i teorien, men muligt i praksis) => det giver denne højre-halede træform. Når ubalancen stiger, øges ydelsen, fordi den kræver en lavere forventet værdi (gennemsnit) af opdelinger for at udføre en perfekt opdeling end en perfekt afbalanceret sag.

Kodningers generelle nøjagtighed

Uden at se dybden i nøjagtigheden vil vi hurtigt se på den generelle nøjagtighed af de fire kodninger, vi har.

På billedet kan vi tydeligt bemærke en tendens:

  • Kategorisk kodning er den klare vinder med en nøjagtig 100% nøjagtighed til enhver tid.
  • Numerisk kodning udfører et fremragende stykke arbejde, så længe antallet af kardinaliteter ikke er for stort: ​​fra 1.024 kardinaliteter falder dens nøjagtighed drastisk. Dets nøjagtighed over alle test er omkring 92%.
  • One-Hot Encoding udfører et fremragende stykke arbejde som numerisk kodning, bortset fra at det falder meget hurtigt: fra 128 kardinaliteter, hvilket udfører konsistens værre end binær kodning. Dets nøjagtighed i alle løb er ca. 80%.
  • Binær kodning er meget konsistent i ydelse, men ikke perfekt, selv med 8.192 kardinaliteter. Dets nøjagtige nøjagtighed er cirka 91%.

Afbalancering af kodningernes nøjagtighed

For at se nærmere på detaljerne opdeler vi balanceforholdet (25% og 50%) af den positive etiket for at kontrollere for uoverensstemmelser.

Vi bemærker helt klart en større tendens:

  • Jo mere datasættet er ubalanceret, desto mere nøjagtige bliver kodningerne.
  • Omvendt: Jo mere datasættet er afbalanceret, desto mindre nøjagtige bliver kodningerne.

Vi kan også bemærke, at vores numeriske kodning bliver værre hurtigere versus binær kodning, når datasættet bliver mere afbalanceret: det bliver værre fra 1024 kardinaliteter på et perfekt afbalanceret datasæt, mens det kun bliver værre fra 2048 kardinaliteter på et 1: 3 ubalanceret datasæt.

For One-Hot Encoding er det endnu mere udtalt: værre 256 kardinaliteter på et perfekt afbalanceret datasæt til 128 kardinaliteter på et 1: 3 ubalanceret datasæt.

Derudover synes der ikke at være nogen grund til at bruge One-Hot Encoding over Numeric Encoding i henhold til billedet.

Derudover er der tre specifikke tendenser, vi kan fange:

  • Numerisk kodning giver ikke ensartede resultater, da resultaterne flyver lidt rundt (sammenlign kassefritstørrelserne, og du vil se det). Det ser ud til, at det kræver meget mere kardinaliteter for at konvergere til en stabil præstation. Det skildrer også mere konsistens “efterhånden som balanceringen bliver mere ubalanceret”.
  • One-Hot Encoding er ekstremt konsistent. Så meget konsistent er der ikke meget at sige om, hvis du vil tilnærme den forudsigelige kraft af en kategorisk funktion, når du er alene.
  • Binær kodning bliver konsistent, når kardinaliteten øges, samtidig med at den opretholder en stabil ydelse. Det skildrer også mere konsistens “efterhånden som balanceringen bliver mere ubalanceret”. Måske har nogen et matematisk bevis på konvergens mod en bestemt grænse i betragtning af kardinaliteten og balanceforholdet?

Træningstid

Træningstiden gives her som et eksempel på tætte data ved hjælp af rpart. Hver model blev kørt 250 gange med medianen til:

  • 25 gange pr. Kørsel til kategorisk kodning.
  • 25 gange pr. Kørsel til numerisk kodning.
  • 1 gang pr. Kørsel til One-Hot Encoding (for lang…).
  • 10 gange pr. Kørsel til binær kodning.

Det viser, hvorfor du skal undgå One-Hot Encoding på rpart, da træningstiden for beslutningstræet bogstaveligt talt eksploderer !:

Data domineres af langsom One-Hot Encoding.

Uden en skaleringsproblem med en varm kodning har vi følgende:

En mere retfærdig sammenligning.

Som vi tydeligt kan bemærke, at hvis du er klar til at bruge lidt mere tid på beregninger, er numeriske og binære kodninger fine.

Konklusion (tl; dr)

En simpel CV med et billede og to tekstlinjer:

  • Kategoriske træk med store kardinaliteter (over 1000): Binær
  • Kategoriske træk med små kardinaliteter (mindre end 1000): Numerisk
Der synes ikke at være nogen grund til at bruge One-Hot Encoding over Numeric Encoding.