Beslisbomen en gradient boosting
Wat is een beslisboom?
Een beslisboom is een reeks ja-of-neevragen over één rij data, met op het einde een antwoord. Heeft deze klant al meer dan drie keer bij ons besteld? Zo ja, staat er minder dan 10.000 euro op de offerte? Zo ja, voorspel 68 procent kans dat we ze binnenhalen. Je kan het geheel op één blad afdrukken en elke voorspelling van boven naar onder volgen. Daarom noemt de documentatie van scikit-learn een boom een white box model.
De vragen kiest de computer, niet jij. Het algoritme bekijkt elke kolom en elk mogelijk afkappunt daarin, neemt de splitsing die gewonnen offertes het best van verloren offertes scheidt, en herhaalt dat binnen elke tak tot er niets zinnigs meer te splitsen valt of tot een grens die jij instelt.
Waarom één boom niet volstaat
Laat je een boom vrij groeien, dan splitst hij door tot er in bijna elk blad nog één trainingsrij zit. Op dat punt heeft hij de trainingsdata vanbuiten geleerd in plaats van een patroon gevonden. Snoei je hem terug tot vijf of zes niveaus, dan gebeurt het omgekeerde: klanten die echt van elkaar verschillen belanden in hetzelfde blad met dezelfde score.
Daarnaast is er de wankelheid. scikit-learn zet het zelf bij de nadelen: kleine wijzigingen in de data kunnen een volledig andere boom opleveren. Train je volgende maand opnieuw met driehonderd rijen extra, dan kan de kolom bovenaan veranderd zijn. Een model dat zo prettig leest maar elk kwartaal een ander verhaal vertelt, leg je beter niet onder je verkoopproces.
Van één boom naar een bos, en van een bos naar boosting
De oplossing is niet meer op één boom rekenen. Er zijn twee manieren om er veel te combineren, en het verschil daartussen is het hele verhaal.
Bagging, oftewel een random forest. Train enkele honderden bomen tegelijk, elk op een willekeurige greep uit de rijen met teruglegging, en laat elke boom bij elke splitsing maar een willekeurig deel van de kolommen bekijken. De bomen zitten er dan elk in een andere richting naast. Neem het gemiddelde van hun antwoorden en een flink deel van die fouten heft elkaar op. scikit-learn beschrijft de bedoeling van die dubbele willekeur precies zo: de fouten van de individuele bomen loskoppelen, zodat middelen er een stuk van wegneemt.
Boosting. Train één kleine boom. Kijk wat hij nog fout heeft. Train een tweede boom die niets anders doet dan die fout voorspellen, en tel zijn uitkomst op bij die van de eerste. Ga zo een paar honderd rondes door, en tel elke boom in kleine stapjes bij, geregeld door een learning rate, zodat geen enkele boom het antwoord ver kan wegtrekken.
Wat het woord gradient hier betekent
Niets waar je wiskunde voor nodig hebt. Na elke ronde heeft het model voor elke trainingsrij een voorspelling en jij kent het echte antwoord. Per rij weet je dus twee dingen: welke kant die voorspelling op moet, en hoe ver. Dat paar is de gradient, en de volgende boom wordt getraind om net dat te voorspellen. Voorspel je een getal en meet je de fout als het kwadraat van het verschil, dan is de gradient exact wat er overblijft: het echte cijfer min de huidige voorspelling. Vandaar dat boosting vaak uitgelegd wordt als bomen die de restjes voorspellen.
Random forest tegenover gradient boosting
Vergelijk de twee op één as: hoe de bomen samengevoegd worden. In een random forest groeien ze naast elkaar, zien ze elkaar nooit, en tellen ze allemaal even zwaar mee in de eindstem. Bij gradient boosting groeien ze na elkaar en bestaat elke boom enkel door de fouten van de vorige, dus is het geheel een optelsom waarin de volgorde meetelt.
Een random forest krijg je daardoor moeilijk stuk: er bomen bij zetten maakt hem niet slechter, en de standaardinstellingen volstaan meestal. Gradient boosting eindigt op dezelfde tabel een paar punten hoger, maar gaat overfitten als je te veel rondes laat lopen of de bomen te diep laat worden, dus er hoort een validatieset en een stopregel bij. Train eerst het bos als ondergrens en kijk daarna hoeveel boosting daar bovenop legt.
De drie implementaties die je tegenkomt
Drie open source bibliotheken domineren deze hoek van machine learning, en alle drie doen ze gradient boosting op beslisbomen. Ze verschillen in hoe ze de bomen laten groeien en in wat ze met categorische kolommen doen.
XGBoost geeft in zijn eigen tutorial de helderste samenvatting van de methode: hou vast wat al geleerd is, en voeg telkens één nieuwe boom toe. Wat het onderscheidt, is dat de straf op te complexe bomen in de doelfunctie zelf zit die het algoritme optimaliseert, en er niet achteraf tegenaan geplakt is. Dezelfde pagina merkt op dat de meeste boompakketten daar losser mee omgaan of het overslaan. Categorische kolommen ondersteunt XGBoost rechtstreeks sinds versie 1.5, aan te zetten met de vlag enable_categorical.
LightGBM verdeelt continue waarden vooraf in een vast aantal bakjes en zoekt splitsingen over die bakjes, wat hem snel en zuinig met geheugen maakt. Bovendien groeit hij leaf-wise: in plaats van een niveau af te maken voor hij aan het volgende begint, splitst hij het blad dat de grootste foutdaling belooft, waar dat blad ook zit. De eigen tuninggids is eerlijk over de prijs daarvan en waarschuwt dat leaf-wise groei kan overfitten zonder de juiste parameters, met num_leaves en min_data_in_leaf als de twee die hem tegenhouden. De bibliotheek is bij Microsoft begonnen en verhuisde in maart 2026 naar een eigen GitHub-organisatie.
CatBoost komt van Yandex, en de manier waarop hij categorische kolommen aanpakt is de reden dat mensen ernaar grijpen. In plaats van een kolom met tweehonderd postcodes om te zetten in tweehonderd nieuwe kolommen, vervangt hij elke categorie door een cijfer afgeleid uit de doelkolom, berekend met alleen de rijen die eerder komen in een willekeurige volgorde. Een rij telt dus nooit mee voor haar eigen omzetting, en net dat verhindert dat de truc het antwoord in de feature laat lekken.
Welke van de drie je kiest, weegt minder zwaar dan het internet doet uitschijnen: op een tabel van enkele duizenden tot enkele honderdduizenden rijen eindigen ze meestal binnen een punt of twee van elkaar. In scikit-learn zit trouwens ook HistGradientBoostingClassifier, met dezelfde aanpak met bakjes en zonder dat je iets extra installeert.
Waarom ze op bedrijfstabellen beter zijn dan deep learning
Een team van Inria, het Franse nationale onderzoeksinstituut voor digitale wetenschap en technologie, zette in 2022 boommodellen naast neurale netwerken op 45 tabeldatasets, met een heel brede zoektocht naar hyperparameters per model zodat geen van beide kon winnen op afstelwerk alleen. De boommodellen bleven voorop op middelgrote data van rond de tienduizend rijen. De drie redenen die het onderzoek geeft, beschrijven allemaal de data die een KMO effectief heeft: kolommen die niets bijdragen, waar een neuraal netwerk van de wijs raakt terwijl een boom er gewoon nooit op splitst; harde drempels zoals een korting boven vijftien procent of een factuur ouder dan dertig dagen, waar bomen per definitie op snijden terwijl neurale netwerken van vloeiende verbanden houden; en kolommen die elk iets aparts betekenen, waar door elkaar mengen juist is voor de pixels van een foto en verkeerd voor een tabel.
Naast dat nauwkeurigheidsargument staan de eigenschappen die bepalen of een KMO ooit een model in productie krijgt. Deze modellen werken vanaf een paar honderd rijen tot in de miljoenen, nemen numerieke en categorische kolommen in dezelfde tabel aan zonder voorbewerkingsketen ervoor, en lossen lege waarden op door bij elke splitsing te leren of de rijen met een gat links of rechts horen. Ze trainen in seconden tot enkele minuten op een laptop, dus je probeert er twintig ideeën mee uit op een namiddag. En ze geven je een rangschikking van welke kolommen meegewogen hebben, en dat is het stuk dat je bij een verkoopverantwoordelijke op tafel kan leggen.
Waar een taalmodel hier past
Niet in deze job. Een taalmodel heeft jouw geschiedenis nooit gezien, leert geen drempel uit vijfduizend rijen die je in een prompt plakt, en kost per gescoorde rij een veelvoud. Waar het wel zijn plaats verdient, is één stap eerder: vrije tekst omzetten in een kolom. Het onderwerp van het laatste supportticket, de toon van de laatste mail van de klant, of een aanbestedingsdocument een onderhoudscontract vermeldt. Die kolom sluit dan aan bij de tabel waar het boostingmodel op traint.
Uitgewerkt voorbeeld: welke offertes worden getekend
Een installatiebedrijf stuurt zo'n 1.400 offertes per jaar en wint er ongeveer dertig procent van. Vier jaar geschiedenis geeft ruwweg 5.600 rijen, waarvan er rond de 1.700 getekend zijn. De zaakvoerder wil weten welke openstaande offertes deze week een opvolgtelefoontje verdienen. De doelkolom: is deze offerte binnen 60 dagen na verzending aanvaard, ja of nee. Twaalf features, allemaal gekend op de dag dat de offerte buitenging:
Totaalbedrag van de offerte in euro
Aantal lijnen op de offerte
Toegekend kortingspercentage
Dagen tussen de vraag van de klant en het versturen van de offerte
Of er een plaatsbezoek geweest is voor het opmaken
Nieuwe klant of bestaande klant
Aantal eerdere bestellingen van die klant
Waarde in euro van die eerdere bestellingen
Sector van de klant
Provincie
Verkoper die ze verstuurd heeft
Maand van verzending
Een LightGBM-model met vijfvoudige cross-validation traint in minder dan tien seconden op een gewone laptop en komt uit op een AUC van ongeveer 0,74. Dat is geen indrukwekkend cijfer en dat hoeft ook niet. Het betekent dat als je willekeurig een gewonnen en een verloren offerte naast elkaar legt, het model de gewonnen ongeveer drie keer op vier de hoogste score geeft. Op een opvolglijst van dertig namen per week is dat het verschil tussen lukraak bellen en bellen met een reden.
De feature importance eerlijk lezen
Bovenaan de grafiek staat het aantal dagen tussen vraag en offerte, daarna het totaalbedrag, dan of er een plaatsbezoek geweest is.
Belang is geen oorzaak. Het model heeft gevonden dat snel verstuurde offertes vaker getekend worden. Het heeft niet gevonden dat sneller offreren tot tekenen leidt. Het omgekeerde verhaal is waarschijnlijker: een klant die klaar is om te kopen duwt op een offerte en krijgt er binnen twee dagen een, terwijl een vage vraag veertien dagen in iemands mailbox blijft liggen. Beide patronen leveren dezelfde kolom bovenaan dezelfde grafiek op. Het enige wat de twee uit elkaar haalt, is iets veranderen en meten: offreer volgend kwartaal de helft van de aanvragen binnen 48 uur, hoe warm ze ook aanvoelen, en vergelijk.
Let op kolommen met veel verschillende waarden. Het belangcijfer dat standaard uit een boommodel komt, wordt op de trainingsdata berekend en geeft, zoals de documentatie van scikit-learn waarschuwt, voorrang aan kolommen met veel unieke waarden. Een provinciekolom met elf waarden komt er eerlijk uit. Een postcodekolom met vierhonderd waarden zou alleen al door dat aantal naar de top klimmen. Permutation importance, waarbij je één kolom door elkaar schudt en meet hoeveel de score zakt op data waar het model niet op getraind is, is de versie die je wel mag vertrouwen.
Wees voorzichtig met de verkoperskolom. Staat één verkoper hoog, dan heeft het model misschien geleerd dat die persoon de vaste klanten opvolgt die toch al gingen tekenen. Dat zegt iets over hoe het werk verdeeld is, niet over de verkoper.
Waar moet je op letten bij gradient boosting
Overfitting als niets het model tegenhoudt. Elke extra boom past net iets beter op de trainingsdata, of er nu nog iets te leren valt of niet. Hou data achter die het model nooit ziet, of gebruik cross-validation, en stop zodra de score daarop niet meer verbetert.
Een gelekte kolom. Een boostingmodel vindt elke kolom die stiekem het antwoord bevat, en sneller dan een mens dat zou doen. In het offertevoorbeeld wordt een veld ordernummer enkel ingevuld bij offertes die aanvaard zijn. Laat je het staan, dan haalt het model een AUC van 0,99 en is het waardeloos. Een verdacht goede score is eerst een leakage-check en pas daarna een reden om te vieren.
Voorspellingen buiten het bereik dat het model kent. Een boom voorspelt door een rij in een blad te laten vallen en het gemiddelde van dat blad terug te geven, dus verder rekenen dan zijn data kan hij niet. scikit-learn zegt het onomwonden: de voorspellingen zijn stuksgewijs constant, dus bomen zijn niet goed in extrapoleren. Is de grootste offerte in vier jaar 60.000 euro, dan krijgt een nieuwe offerte van 250.000 euro dezelfde behandeling als een van 60.000.
Categorieën die het model tijdens de training nooit gezien heeft. Een nieuwe verkoper, een nieuwe sectorcode, een leverancier van vorige maand. LightGBM behandelt zo'n categorie bij het voorspellen als een lege waarde, verstandig als standaardkeuze, maar die rijen krijgen wel een algemeen antwoord tot er wat geschiedenis achter zit.
Te weinig rijen voor het aantal kolommen. Driehonderd offertes en veertig features geeft een model dat de offertes zelf vanbuiten kent. Snoei de lijst terug tot de tien à twaalf kolommen waar de business echt in gelooft, en hou de bomen ondiep.