Skip to content

Prepare RF bins and objectives for weights support - #8247

Merged
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
RAMitchell:codex/rf-weights-support
Jun 11, 2026
Merged

Prepare RF bins and objectives for weights support#8247
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
RAMitchell:codex/rf-weights-support

Conversation

@RAMitchell

Copy link
Copy Markdown
Contributor

Summary

Refs #8093, #1279.
Builds on #8132 and #8233.
Related follow-ups: #8186, #8146.

This PR prepares the RF objective/bin layer for weighted training without threading sample_weight through the public estimator APIs yet.

  • Renames RF histogram bins to ClassificationBin and RegressionBin
  • Adds weighted bin variants that preserve integer sample counts while separately accumulating sample weight
  • Switches objective families to a weighted bool template parameter
  • Expands objective tests across weighted and unweighted regression/classification criteria
  • Adds weighted ground-truth checks for MSE, Poisson, Gamma, Inverse Gaussian, Entropy, and Gini

Notes

This is groundwork only. It does not yet route sample_weight through Python/Cython, sampling, or RF training kernels.

Testing

  • git diff --check
  • SG_RF_TEST.localnvforest --gtest_filter="*ObjectiveTest*"
    • 96 tests passed

A full normal ninja -C cpp/build-ninja-gcc12 SG_RF_TEST -j2 run was started after reconfiguring the local build cache to use the local nvforest artifact, but was paused before completion.

@RAMitchell
RAMitchell requested a review from a team as a code owner June 10, 2026 11:09
@RAMitchell
RAMitchell requested a review from Copilot June 10, 2026 11:09
@RAMitchell
RAMitchell requested a review from chyunsu3 June 10, 2026 11:09
@RAMitchell RAMitchell added non-breaking Non-breaking change improvement Improvement / enhancement to an existing function labels Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 35e21e9c-536d-4b7e-ab16-8ed872cb45d1

📥 Commits

Reviewing files that changed from the base of the PR and between 5373dd3 and 41270ce.

📒 Files selected for processing (2)
  • cpp/src/decisiontree/batched-levelalgo/objectives.cuh
  • cpp/tests/sg/rf_test.cu
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/src/decisiontree/batched-levelalgo/objectives.cuh

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added weighted-sample support for decision tree training (classification and regression), producing weight-aware splits and leaf predictions.
  • Refactor

    • Internal histogram/bin representation and objective computations rewritten to operate with weighted counts and aggregates.
  • Tests

    • Expanded tests to validate weighted objectives, edge cases, and parity with unweighted criteria.

Walkthrough

This PR introduces typed bin structures (ClassificationBin, WeightedClassificationBin, RegressionBin, WeightedRegressionBin) with atomic count/weight/label-sum updates, refactors objective functions to conditionally select bin types via a weighted template parameter and compute gains from bin accessors, updates kernel type aliases to use the new bins, and extends tests to validate weighted objectives and edge cases.

Changes

Weighted bins and objective refactoring

Layer / File(s) Summary
Typed bin data structures
cpp/src/decisiontree/batched-levelalgo/bins.cuh
Introduces BinCountT (64-bit unsigned integer) and four new bin types: ClassificationBin and WeightedClassificationBin store count and optional weight with atomic increments; RegressionBin and WeightedRegressionBin store count, label_sum, and optional weight with atomic updates and accessor methods for Count(), Weight(), and LabelSum().
Objective function template refactoring
cpp/src/decisiontree/batched-levelalgo/objectives.cuh
ClassificationObjectiveFunction and RegressionObjectiveFunction now accept a weighted_ template parameter and conditionally select between unweighted and weighted bin types via std::conditional_t. Gini, Entropy, MSE, Poisson, Gamma, and InverseGaussian gain/impurity computations are rewritten to derive normalization from bin Weight() and LabelSum() accessors instead of passed split-size parameters; leaf outputs and min-samples checks updated accordingly.
Kernel type alias updates
cpp/src/decisiontree/batched-levelalgo/kernels/{classification,regression}-{double,float}.cu
Classification and regression kernel files update their _BinT type aliases from CountBin/AggregateBin to ClassificationBin/RegressionBin, wiring kernels to use the new typed bin representation.
Test framework weighted support
cpp/tests/sg/rf_test.cu (framework and helpers)
ObjectiveTest framework extends histogram generation and objective validation to accept sample weights. Adds GenSampleWeights() and SplitOffset() helpers, replaces RNG from rand() to per-test std::mt19937_64, and refactors MSE, Inverse Gaussian, Gamma, Poisson, Entropy, and Gini helpers to compute weighted statistics (weighted means, weighted label sums, weight-sum-scaled gains).
Weighted objective test suites
cpp/tests/sg/rf_test.cu (test instantiations)
Adds new gtest parameterized test instantiations for weighted regression objectives (WeightedMSEObjectiveTest, WeightedPoissonObjectiveTest, WeightedGammaObjectiveTest, WeightedInverseGaussianObjectiveTest) and weighted classification objectives (WeightedEntropyObjectiveTest, WeightedGiniObjectiveTest), each with double and float precision variants linked to weighted_ = true objective templates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • rapidsai/cuml#8093: This PR's introduction of typed weighted bins and updates to kernels/objectives/tests relates to weighted training support for decision trees.

Possibly related PRs

  • rapidsai/cuml#8233: Both PRs modify the batched-levelalgo objective layer and kernel wiring to change split-gain computation and bin/objective selection.
  • rapidsai/cuml#8132: Both PRs refactor bin types and weighted objective semantics for the batched-level algorithm.

Suggested labels

CMake

Suggested reviewers

  • hcho3
  • viclafargue
  • dantegd
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main objective: preparing RF bins and objectives for weighted training support.
Description check ✅ Passed The description is comprehensive and related to the changeset, detailing the preparation work for weighted training in RF bins and objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cpp/src/decisiontree/batched-levelalgo/objectives.cuh (1)

52-117: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Zero-weight children can still reach the weighted normalization path.

Gain() still gates splits on Count(), but the weighted criteria and SetLeafVector() normalize by Weight(). A branch or leaf containing only zero-weight samples can therefore satisfy the count check with left_weight, right_weight, or total leaf weight equal to 0, which turns the inverse/mean computations here into NaN/Inf and can poison split selection or leaf predictions. Please add an explicit <= eps_ guard, or another zero-weight fallback, before any weight-based division in the weighted path. As per coding guidelines, "Add epsilon checks for division by zero or near-zero values" and "Handle numerical edge cases (near-zero eigenvalues, degenerate matrices, extreme values)".

Also applies to: 142-149, 154-163, 181-268, 296-312

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/decisiontree/batched-levelalgo/objectives.cuh` around lines 52 - 117,
GiniGain and EntropyGain compute invLeft/invRight/invLen using Weight() values
which can be zero or near-zero; before any division in GiniGain and EntropyGain
(and similar blocks referenced), check total_weight, left_weight and
right_weight against eps_ (e.g., if total_weight <= eps_ or left_weight <= eps_
or right_weight <= eps_) and early-return a safe fallback (such as DataT(0) or
the unweighted criterion) or handle the branch without performing inv*
divisions; apply the same epsilon-guarding pattern around any subsequent
weight-based divides in these functions so no raft::log or multiplications
receive Inf/NaN from 1/0.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/tests/sg/rf_test.cu`:
- Around line 1197-1227: GenHist() currently assumes every bin has size
bin_width and iterates to bin_end, which reads past data and writes an incorrect
count for the tail bin; clamp the tail by computing actual_bin_end =
std::min(bin_end, params.n_rows) (or equivalent) and use actual_count =
actual_bin_end - bin_begin for loops and for any count stored in
RegressionBin/WeightedRegressionBin::count (and when summing weights or
label_sum), replace uses of bin_width for the last bin with actual_count, and
ensure classification loops (the data[i] equality/weight accumulation) also
iterate to actual_bin_end so pdf_hist and downstream
Count()/min_samples_leaf/gain checks reflect the real sample count.

---

Outside diff comments:
In `@cpp/src/decisiontree/batched-levelalgo/objectives.cuh`:
- Around line 52-117: GiniGain and EntropyGain compute invLeft/invRight/invLen
using Weight() values which can be zero or near-zero; before any division in
GiniGain and EntropyGain (and similar blocks referenced), check total_weight,
left_weight and right_weight against eps_ (e.g., if total_weight <= eps_ or
left_weight <= eps_ or right_weight <= eps_) and early-return a safe fallback
(such as DataT(0) or the unweighted criterion) or handle the branch without
performing inv* divisions; apply the same epsilon-guarding pattern around any
subsequent weight-based divides in these functions so no raft::log or
multiplications receive Inf/NaN from 1/0.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: eeb03614-a149-4168-9af2-b7bea26fa4c5

📥 Commits

Reviewing files that changed from the base of the PR and between 22d63b9 and 5373dd3.

📒 Files selected for processing (7)
  • cpp/src/decisiontree/batched-levelalgo/bins.cuh
  • cpp/src/decisiontree/batched-levelalgo/kernels/classification-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/classification-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/regression-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/regression-float.cu
  • cpp/src/decisiontree/batched-levelalgo/objectives.cuh
  • cpp/tests/sg/rf_test.cu

Comment thread cpp/tests/sg/rf_test.cu Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@chyunsu3 chyunsu3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have separate bin types for regression and classification? The implementations for WeightedRegressionBin and WeightedClassificationBin appear to be almost identical.

CountBin(CountBin const&) = default;
HDI CountBin(double x_) : x(x_) {}
HDI CountBin() : x(0.0) {}
using BinCountT = unsigned long long int;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not uint64_t here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

64 bit atomics need unsigned long long int they don't work for uint64_t.

@RAMitchell

Copy link
Copy Markdown
Contributor Author

Unweighted classification needs only count. Regression needs label sum + count. So classification can be half the size.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment thread cpp/src/decisiontree/batched-levelalgo/objectives.cuh
@RAMitchell

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit bb50634 into NVIDIA:main Jun 11, 2026
102 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CUDA/C++ improvement Improvement / enhancement to an existing function non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants