Module 5 · Machine Learning for Time Series Forecasting

Histogram vs GOSS Splitting

A boosted model builds hundreds of trees, and every tree has to search for its best splits. That search is where the time goes. LightGBM cuts it two ways: histograms test fewer thresholds, and GOSS looks at fewer rows. Turn the knobs and see what each one saves and what it costs.

One thing to know first

In gradient boosting each row carries a gradient: how wrong the current model is on that row, and in which direction. For squared error it is just the residual with the sign flipped. A good split puts rows that need pushing up on one side and rows that need pushing down on the other. Its quality, the gain, depends only on the gradient sums in each child: GL²/nL + GR²/nR − G²/n. Both tricks below are clever ways to get those sums cheaply.

Trick 1

Histogram split finding fewer thresholds to test

8
Splits to test, exact vs histogram–
Best split, exact vs histogram–
Gain the histogram keeps–
Same feature, 1 million rows–
Best exact split Best histogram split Gradient sum per bin

Exact. Sort the rows by the feature, then try every gap between two neighbors. That is up to n − 1 thresholds per feature, per node, per tree.

Histogram. Drop each row into a bin once, add up gradients per bin, and try only the bin edges. The bars are those sums. The best edge is usually right next to the best exact split, so almost no gain is lost. Push the bins down to 2 or 3 to see where it breaks.

Trick 2

GOSS: gradient-based one-side sampling fewer rows per tree

0.20 0.10 high
Rows used–
Weight on sampled rows–
Typical error, GOSS–
Typical error, random–
Always kept Sampled, upweighted Skipped this round

The idea. Rows with big gradients are where the model is most wrong, so they matter most for the next split. GOSS always keeps the top a share of them. From the rest it samples a b share and multiplies their gradients by (1 − a)/b, so the totals stay about right.

The error tiles. Each method estimates the total gradient with the same number of rows, 500 times. The tile shows the typical miss as a share of the total absolute gradient. Random sampling sometimes drops a big row and swings wildly. GOSS never does. Slide the concentration to low and the advantage disappears, because when every row is about equally wrong there is nothing special to keep.

Histogram vs GOSS two different savings

HistogramGOSS
What it cutsCandidate thresholdsRows per tree
HowBucket feature values, test only the bin edgesKeep the large gradient rows, sample and upweight the rest
In LightGBMAlways on, max_bin defaults to 255Optional, the default strategy is bagging
RiskToo few bins blur a fine splitUpweighted small rows add noise and can overfit on small data
Pays off whenAlmost always, it is cheap and nearly losslessData is large and a minority of rows carries most of the error
import lightgbm as lgb
model = lgb.LGBMRegressor(max_bin=255, data_sample_strategy="goss", top_rate=0.2, other_rate=0.1)

Older LightGBM versions turned GOSS on with boosting_type="goss" instead. GOSS cannot be combined with bagging, so leave bagging_fraction at 1.

Why speed matters here

Boosting is sequential. Tree 200 cannot start until tree 199 is done, so you cannot just throw more machines at it. Making each split search cheaper is how LightGBM and XGBoost's hist mode train in seconds instead of hours.

LightGBM's other tricks

It also grows trees leaf-wise, splitting whichever leaf gains most instead of a whole level at a time, and bundles sparse features that are rarely nonzero together. Leaf-wise growth is fast and accurate but needs num_leaves or max_depth to keep it from overfitting.

See it on real data

The Boosting lab runs both tricks inside a real model on the airline passengers data, using the model's actual residuals at every round.

And the parallel cousin

Bagging also samples rows, but uniformly and with replacement, to make trees different from each other. GOSS samples rows by importance, to make each tree cheaper.