Releases: bbstats/chimeraboost
Release list
0.21.0
Changed
- pandas is no longer a dependency. The categorical machinery (factorize, predict-time code mapping, gdiff group means) is now numpy/numba only, bit-identical to the pandas implementation (the gdiff group sums replicate pandas' Kahan-compensated groupby exactly). DataFrames still work as input — they are consumed through their own
to_numpy/columnsattributes. Installs pull four packages instead of five. - Bagged predict no longer redoes the categorical transform per member. Members factorize each categorical (and combo) column once per call through a shared cache and remap only the unique values through their own fit-time maps; the input conversion and validation also run once instead of once per member. Predictions are bit-identical. 50k rows,
n_ensembles=8, 4 string categoricals: binary predict 1.21 s → 0.40 s, multiclass 1.78 s → 0.74 s, regression 1.35 s → 0.49 s; single-model predict is unchanged at big batches and ~9× faster at 1-row calls (0.19 ms vs 1.7 ms).
Fixed
- Bagged members no longer keep their fit-time thread cap at predict. A parallel
n_ensemblesfit divides the thread budget across workers, but members retained that sliver permanently — an 8-member bag walked each forest on 1–2 threads. Members now get the parent'sthread_countback after fitting. Bit-identical; bagged predict ~3–5× faster on an 8-core box (numeric 0.80 s → 0.24 s at 50k rows).
Full details in CHANGELOG.md.
0.20.0 — cross features for categorical columns
Group-centered categorical crosses. The cross-feature race now also auditions columns of the form value minus the average of that value within this row's category (salary minus the department's average salary), so "above this row's own group's baseline" becomes one split instead of a per-category staircase. Target-free (no leakage machinery), weight-aware, kept per dataset only when validation loss agrees. On the real high-cardinality suite: +0.25% mean, employee_salaries +2.5%; the headline Grinsztajn suite is bit-identical by construction; the independent OpenML check was flat.
Faster selection fits. Audition fits no longer compute a training-set loss nobody reads: predictions bit-identical, a 50k-row binary fit ~15% faster, Grinsztajn suite fit time ~3% lower (single model now 4.8× LightGBM, was 5.1×).
Full validation record: benchmarks/CATCROSS_PLAN.md (PR #24). TabArena-Lite re-read post-release: Elo 1278, rank 31/68 (was 1264, within CI).
0.19.0
Added
- With
verbose=True, fit prints a notice whenearly_stopping=Truesilently holds outvalidation_fractionof the training rows as the validation set, so the effective training size is visible. Defaultverbose=Falseunchanged.
Changed
- Classifier
leaf_estimation_iterationsdefault is nowNone(auto) instead of a concrete3; it resolves to 3 and fits bit-identically. An A/B across the decision suites confirmed 3 is the right effective value (helps small/categorical binary, inert where linear leaves take over or for multiclass) —Nonejust stops the API from advertising a refinement count that is dead in those regimes.
Fixed
sample_weightis now honored everywhere, not just in the gradient step: the ordered-target categorical encoder, the quantile bin borders, and the early-stopping validation metric on the auto-split (or bagged out-of-bag) holdout. Previously a weight-0 row still shaped encodings and bin edges and scored the early-stopping metric at full weight — up to an order-of-magnitude model degradation in an adversarial repro.sample_weight=Noneand uniform weights stay bitwise-identical to before.eval_setnow accepts an optional third(X, y, sample_weight)element.- The inert-knob warnings for
leaf_estimation_iterationsnow fire for any explicitly-set value> 1that will be ignored on the path about to run; the auto default stays quiet.
v0.18.1
Maintenance release: audit-driven robustness fixes plus an early-stopping correctness fix. No default or accuracy changes on the benchmark suites — all fits stay benchmark-identical.
Fixed
- Early stopping truncates to the best iteration when
n_estimatorsruns out before patience fires. Previously every tree built past the best validation round was kept, contradicting the documentedbest_iteration_contract (~5% validation RMSE cost on budget-exhausted fits in an adversarial repro). Patience stops, early-stopping-off, and callback stops are unchanged. - Thread hygiene:
thread_countnow applies to predict as well as fit, and the process-global numba thread setting is restored afterwards. - Pickles no longer carry the packed-forest predict cache (~2x smaller payloads); it rebuilds lazily on first predict. Loaded models predict bit-identically.
- Loud failures replace silent misbehavior for unseen
eval_setlabels, class-stranding early-stopping splits,fit(X, y, w)positional misuse, and inertordered_boosting/leaf_estimation_iterationssettings. cat_featuresaccepts a numpy integer array; classifierdepth=Noneresolves to the default 6; bagged members retain the parent's feature names.
Full details in the CHANGELOG.
0.18.0
Quantized-gradient histograms are now the default (quantize_gradients=True): the split search runs on ~15-bit quantized gradient/hessian pairs packed one-integer-per-sample into integer histograms (the LightGBM-4 quantized-training idea, adapted). Summed single-model fit time fell 26% on the Grinsztajn suite and 20% on the high-cardinality suite (bagged Ens8 arm likewise -19%) at benchmark-flat accuracy; leaf values are always computed from the unquantized float gradients. Deterministic per random_state; quantize_gradients=False restores exact float64 histograms. Also: tree growth now runs one fused kernel launch per level (bit-identical; biggest on small fits). Full changelog in CHANGELOG.md.
0.17.0 — multiclass cross features
Cross features now cover multiclass classification: the same validation-selected difference/product columns regression and binary get (top numeric pairs of the base fit, raced audition at selection_rounds, judged on softmax validation log loss). Auto-on under the same gates (>= 2000 rows, >= 2 numeric features); explicit cross_features=True no longer raises on multiclass. Regression and binary paths are bit-identical to 0.16.1.
Full experiment record: benchmarks/M1_PLAN.md.
0.16.1
Faster fits at identical output: internal model selection no longer recomputes preprocessing. The variant auditions, the cross-feature candidate, and the winner refit inside one fit previously each reran the full preprocessing pipeline (target encoding, quantile borders, binning); they now share it. Models are bit-identical to 0.16.0 (verified by numerical-identity goldens and exact prediction ties on all 73 benchmark datasets). Biggest on categorical-heavy data (17-32% faster single-model fits measured there); bagged ensembles save it in every member. See CHANGELOG.md.
0.16.0
Bagging program — the opt-in n_ensembles mode is now a tuned, first-class accuracy mode.
Changed
- Members train on 80% row subsamples without replacement (new
max_samples, default0.8;1.0restores the classic bootstrap). A bootstrap gives a member only ~63% unique rows at full compute; 80% without replacement is more effective data and less work. Grinsztajn 54W-5L +0.94% with a 23-0 Brier sweep at 0.87x fit; high-card Brier 8-0 at 0.73x. - Tuned bagged-member defaults: inside a bag,
learning_rate None → 0.15andcolsample None → 0.85(single-model resolution unchanged). Announced at fit; recorded inmember_params_; explicit values always win. Recommended sizen_ensembles=8(stronger than 5 at similar cost; 2 remains anti-recommended). - Members fit in parallel by default (
ensemble_n_jobs1 → -1): workers split the thread budget, so a bagged fit uses the same cores a single fit would. Models identical to sequential (verified on 73 datasets); 1.2–2x faster wall-clock.
Fixed
ensemble_n_jobs=-1previously gave every worker the full thread budget (oversubscribing); the budget is now divided across workers.
The single-model default is unchanged.
0.15.0 — faster fits: raced selection auditions
Highlights
selection_rounds(new, default100): ~1.5x faster fits at the same accuracy. The internal selection fits (constant/linear leaf variants and the pre-cross-features base fit) now run as capped auditions judged on their best validation loss within a shared budget; only the winner continues to full early stopping. An audition that early-stops before the cap is reused as the finished model — that path is bit-identical to the previous behavior.selection_rounds=Nonerestores the old run-everything-to-full-early-stopping selection.- Grinsztajn benchmark suite: fit time 351→235 s with accuracy columns flat; headline slowdown vs the fastest competitor improves 7.9x → 6.0x at unchanged blended strength (see
images/pareto.png). - TabArena-Lite (report-only holdout): Elo 1256 (+54/−57), statistically unchanged, on a fresh 51-task run.
- Measured trade-off, documented in the CHANGELOG: on a minority of regression datasets the 100-round audition can pick the leaf variant a full run would have rejected (typically 0.5–1.5% there).
Full details in CHANGELOG.md.
0.14.2 — predict 1.35–1.63× faster, bit-identical
Cuts the 0.14.2 release. The diff here is just the version bump and CHANGELOG
date stamp -the changes being released are already on main (merge a5810fa).
What's in this release
Predict is 1.35–1.63× faster end-to-end, with predictions unchanged to the
last bit. Two changes:
- Row-major fused predict kernels (
_predict_forest_rm,
_predict_forest_linear_rm) — predict consumes the binner's row-major
output directly, so each sample's bins sit in one or two cache lines for
the whole forest walk and the per-predict feature-major transpose copy is
gone. Fit-side kernels keep the feature-major layout (histograms want it);
SHAP keeps the feature-major kernels for its background walk. - No more whole-matrix copy for all-numeric input —
FeaturePreprocessorextracted the numeric block as
X[:, num_features_]even when every column is numeric, a full
fancy-index copy on each transform (~18% of end-to-end predict on large
batches). Plainasarraynow covers that case; mixed/categorical inputs
keep the gather.
Numbers (fit 200k / predict 2M×30 / 200 trees / 12 threads, min-of-5)
| config | 0.14.1 | 0.14.2 | speedup |
|---|---|---|---|
| classifier, linear leaves (default) | 2.140 s | 1.588 s | 1.35× |
| classifier, constant leaves | 1.244 s | 0.764 s | 1.63× |
| regressor | 1.236 s | 0.757 s | 1.63× |
Against the field on the same benchmark (benchmarks/scaling_predict.py):
default binary predict is now 1.26 Mrows/s — 1.30× LightGBM, 3.0×
sklearn-HGB, 0.50× XGBoost; constant-leaf paths reach ~2.6 Mrows/s, on par
with XGBoost. CatBoost's SIMD-fused C++ inference remains ~10× faster.
Why "bit-identical" is safe to claim
- Exact-equality kernel test (
test_rowmajor_predict_kernels_match_feature_major_exactly):
row-major vs feature-major kernels compared withnp.array_equalacross
depth-0 trees, constant and linear leaf blocks, NaN center bins, n ∈ {0, 1, many}. - Regenerated golden baseline changed only machine-relative timing ratios —
every accuracy metric field is byte-identical. - Full suite: 378 passed.
warmup()compiles the new kernels automatically
(same numba argument types), so the 0.14.1 fresh-worker JIT fix carries over.
After merging
-
git tag v0.14.2 && git push origin v0.14.2 - build + upload to PyPI
🤖 Generated with Claude Code