Skip to content

Add class_weight='balanced_subsample' to RandomForest and ExtraTrees - #8181

Closed
switch527 wants to merge 3 commits into
NVIDIA:mainfrom
switch527:fea-rf-balanced-subsample
Closed

Add class_weight='balanced_subsample' to RandomForest and ExtraTrees#8181
switch527 wants to merge 3 commits into
NVIDIA:mainfrom
switch527:fea-rf-balanced-subsample

Conversation

@switch527

@switch527 switch527 commented May 29, 2026

Copy link
Copy Markdown
Contributor

Closes #8146. Stacked on #8132 (RF sample_weight + classifier class_weight) and #8143 (ExtraTrees estimators). Review the third commit for the balanced_subsample-specific surface; the first two commits are the dependencies and will collapse on merge.

Adds class_weight='balanced_subsample' to RandomForestClassifier and ExtraTreesClassifier, completing sklearn class_weight parity. The per-tree compute and the HasTreeClassWeight kernel template gating only make sense on top of #8132 + #8143.

The mode computes class weights on-device per tree from the bootstrap's class distribution and folds them into the histogram kernel's per-row weight under a compile-time HasTreeClassWeight template parameter. The split-finding kernel and the leaf-prediction kernel both apply the multiplier; without the leaf-side fix, leaf probabilities collapse to the unweighted majority and minority recall goes the wrong way (caught by the Phase 6 minority-recall test). 12 new explicit instantiations gated to weighted-classifier .cu files via a #define INSTANTIATE_TREE_CLASS_WEIGHT block in the impl headers; weighted-regression files don't define the macro so tree_class_weight[floating-point label] never tries to compile.

bootstrap=False has no per-tree bootstrap, so a private @property _effective_class_weight collapses the mode to 'balanced' and fit() emits a UserWarning once. The property is the source of truth for both Python and Cython reads; self.class_weight stays untouched so clone() round-trips. sklearn falls back silently here; cuml is explicit per the property pattern.

feature_importances_ under balanced_subsample does not match sklearn's formula shape: cuml weights the importance at the impurity term via the kernel multiplier, sklearn weights it at the count term via weighted_n_node_samples. Both apply the per-tree class weight once. Closing the formula gap would require threading tree_class_weight into the node-count kernels and is out of scope here.

Three pre-PR tests that asserted balanced_subsample raises (test_rf_classifier_class_weight_balanced_subsample_raises[*], test_class_weight_balanced_subsample_rejected) are replaced by positive forms: minority-recall lift on a 90:10 imbalanced fixture, bootstrap=False collapse to 'balanced' via cp.testing.assert_array_equal on predictions, sklearn from_sklearn round-trip, and a feature_importances well-formedness check. The bootstrap=False UserWarning has paired positive + negative tests. test_sklearn_import_export.py parametrize lists for RFC and ETC now include 'balanced_subsample'.

Binary size delta: +97 KB on libcuml.so for the 12 new instantiations and the per-tree compute kernel.

@copy-pr-bot

copy-pr-bot Bot commented May 29, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

switch527 added 3 commits May 28, 2026 20:16
Thread true per-sample `sample_weight` through the RandomForest C++ tree builder and add classifier `class_weight`, bringing `RandomForestClassifier` and `RandomForestRegressor` in line with scikit-learn's weighting API. Weights enter split finding and leaf statistics directly. The unweighted path stays byte-identical to the prior behavior; the weighted path uses a deterministic pool-based cross-block reduction.
Closes NVIDIA#8133. Adds `ExtraTreesClassifier` and `ExtraTreesRegressor` to `cuml.ensemble`. Both subclass the existing RandomForest estimators, route through a new `SPLITTER_RANDOM` C++ path, and default to `bootstrap=False` to match scikit-learn's ExtraTrees defaults. `sample_weight` and `class_weight` reuse the parent estimators' machinery.

The C++ side is a direct-single-threshold split kernel that skips the per-bin histogram. Splitter-aware dispatch in `Builder::computeSplit` keeps the `SPLITTER_BEST` arm byte-identical; the `SPLITTER_RANDOM` arm uses a smaller per-block scratch and reuses the deterministic pool primitive introduced by NVIDIA#8132.

Adjacent fix: `BaseRandomForestModel._params_to_cpu` coerces `max_samples` to `None` when `bootstrap=False` so the sklearn round-trip succeeds. Affects RandomForest too; sklearn rejects that combination anyway.

Test coverage adds a new C++ gtest binary plus Python sklearn-parity, round-trip, and accel-integration tests for both estimators.

Stacks on NVIDIA#8132 since the weighted bin types and pool primitive live there. The Python entry point routes through the existing RandomForest code path unchanged; `_splitter` is a class attribute, not a user-facing parameter.
Closes NVIDIA#8146. Computes per-tree class weights on-device from each
bootstrap's class distribution and folds them into the split-finding
and leaf-prediction histogram kernels under a HasTreeClassWeight
template parameter. bootstrap=False collapses to 'balanced' via a
private _effective_class_weight property and emits a UserWarning;
self.class_weight stays untouched so clone() round-trips.
@switch527
switch527 force-pushed the fea-rf-balanced-subsample branch from 048e3fa to 44013e1 Compare May 29, 2026 03:49
@switch527

Copy link
Copy Markdown
Contributor Author

Rebased onto current main on top of rebased #8132 and #8143. No conflicts.

@switch527
switch527 marked this pull request as ready for review May 29, 2026 04:15
@switch527
switch527 requested review from a team as code owners May 29, 2026 04:15
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added ExtraTreesClassifier and ExtraTreesRegressor models for extra trees ensemble learning.
    • Added sample_weight support for Random Forest and Extra Trees training.
    • Added class_weight support for Random Forest classifiers, including 'balanced_subsample' mode.
  • Enhancements

    • Extended classifier and regressor score() methods to support weighted metrics.
    • Improved splitter strategy selection for tree-based models.

Walkthrough

Implements ExtraTrees (random splitter) and end-to-end weighted training: new enums/params, weighted bins/objectives, builder/kernel updates (including random-split kernels), RF integration with per-tree class weights, Python bindings and new ExtraTrees estimators, plus comprehensive C++/Python tests and benchmarks. Build/CMake updated to compile new kernels.

Changes

Core Tree/Forest Contracts

Layer / File(s) Summary
Enums and RF params; DecisionTree params/signatures; validation
cpp/include/cuml/ensemble/randomforest.hpp, cpp/include/cuml/tree/decisiontree.hpp, cpp/src/decisiontree/decisiontree.cu, cpp/src/decisiontree/decisiontree.cuh
Adds Splitter/ClassWeightMode enums, extends params and fit signatures for sample_weight, and validates splitter and n_bins.

Weighted Bins and Objectives

Layer / File(s) Summary
Weighted bins and weighted/unweighted objective gains
cpp/src/decisiontree/batched-levelalgo/bins.cuh, cpp/src/decisiontree/batched-levelalgo/objectives.cuh
Introduces weighted bin types and weighted objective classes; adds GainFromSideStats across objectives.

Builder and Kernels

Layer / File(s) Summary
Builder and NodeQueue weighted support and dispatch
cpp/src/decisiontree/batched-levelalgo/builder.cuh
Adds weighted-mode queue/builder, left-weighted counts, workload tuple, random vs best splitter dispatch, and leaf prediction updates.
Dataset tree_class_weight field
cpp/src/decisiontree/batched-levelalgo/dataset.h, cpp/src/decisiontree/batched-levelalgo/kernels/builder_kernels.cuh
Adds optional per-tree class weights and POOL_SIZE constant.
Kernel declarations and implementations (compute and random)
cpp/src/decisiontree/batched-levelalgo/kernels/*builder*_kernels*.cuh
Implements weighted-left kernel, updates compute/leaf kernels with class-weight template, adds randomSplit kernel/launcher.
Kernel TU registrations and CMake
cpp/CMakeLists.txt, cpp/src/decisiontree/batched-levelalgo/kernels/*
Registers all random_* and weighted_* kernel translation units and adds them to build.

RandomForest Integration

Layer / File(s) Summary
RF fit overloads, set_rf_params, treelite signatures
cpp/src/randomforest/randomforest.cu
Passes sample_weight through fit paths; adds splitter/class_weight_mode to params; updates treelite instantiations; weighted feature importances.
Per-tree class-weight kernel and RF::fit changes
cpp/src/randomforest/randomforest.cuh
Computes per-tree class weights from bootstrap; RF::fit validates and forwards weights; captures exceptions.

Python Bindings and ExtraTrees

Layer / File(s) Summary
Cython RF plumbing for splitter/class_weight/sample_weight
python/cuml/cuml/ensemble/randomforest_common.pyx
Adds enums/params, forwards sample_weight, maps splitter strings, and sets cfg_class_weight_mode.
Sklearn overrides: pass sample_weight; expose ExtraTrees proxies
python/cuml/cuml/accel/_overrides/sklearn/ensemble.py
Forwards sample_weight to GPU and adds ExtraTrees proxies/exports.
Package exports and benchmarks for ExtraTrees
python/cuml/cuml/ensemble/__init__.py, python/cuml/cuml/__init__.py, python/cuml/cuml/benchmark/*
Exports ExtraTrees and adds benchmark entries/fixtures.
ExtraTreesClassifier/Regressor Python classes
python/cuml/cuml/ensemble/extra_trees_*
Adds ExtraTrees estimators as RF subclasses with _splitter="random".

C++ Tests

Layer / File(s) Summary
CMake test target and RNG reference generator/header
cpp/tests/CMakeLists.txt, cpp/tests/sg/*reference*
Adds ExtraTrees test target and RNG oracle generator/header.
ExtraTrees RNG/gain/fitting tests
cpp/tests/sg/extratrees_test.cu
Validates RNG, gains, smoke fits, and constant-feature handling.
RF sample_weight invariants and weighted objectives tests
cpp/tests/sg/rf_test.cu
Weighted vs duplicated equivalence, determinism, invariants, smem error, weighted objective unit tests.

Python Tests, Dask, and Compatibility

Layer / File(s) Summary
Dask RF fit signature and tests
python/cuml/cuml/dask/ensemble/randomforest*.py, python/cuml/tests/dask/*
Adds keyword-only sample_weight (unsupported) and tests positional binding and NotImplemented.
Integration tests for ExtraTrees and RF weighting
python/cuml/cuml_accel_tests/integration/*
Adds ExtraTrees and RF sample_weight integration tests.
sklearn compatibility matrix and xfail updates
python/cuml/tests/test_sklearn_compatibility.py, python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
Includes ExtraTrees in matrix; adjusts expected-failure lists.
Python unit tests for ExtraTrees
python/cuml/tests/test_extratrees.py
Covers defaults, cloning, splitter errors, parity, weighting effects, OOB, and n_bins constraints.
Python unit tests for RF weighting and scoring
python/cuml/tests/test_random_forest.py
Extensive class/sample weight tests, importances, validation, and score weighting.
sklearn import/export tests for RF/ExtraTrees
python/cuml/tests/test_sklearn_import_export.py
Parametrizes class_weight; tests ExtraTrees round-trip and scoring.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested labels

improvement, non-breaking

Suggested reviewers

  • tarang-jain
  • csadorf
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@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

🧹 Nitpick comments (3)
python/cuml/cuml_accel_tests/integration/test_et_classifier.py (1)

46-53: ⚡ Quick win

Add explicit balanced_subsample coverage in class_weight parametrization.

Line 46 currently skips the new mode introduced by this PR, so the integration suite can miss regressions on the core behavior.

✅ Suggested test update
-@pytest.mark.parametrize("class_weight", [None, "balanced", {0: 1, 1: 2}])
+@pytest.mark.parametrize(
+    "class_weight",
+    [None, "balanced", "balanced_subsample", {0: 1, 1: 2}],
+)
 def test_et_class_weight(classification_data, class_weight):

As per coding guidelines **/*test*.{py,cpp,hpp}: Update unit tests when implementing code changes.

🤖 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 `@python/cuml/cuml_accel_tests/integration/test_et_classifier.py` around lines
46 - 53, The test test_et_class_weight for ExtraTreesClassifier doesn't include
the new "balanced_subsample" mode; update the pytest.mark.parametrize on
class_weight in test_et_class_weight to include "balanced_subsample" alongside
None, "balanced", and the dict {0:1, 1:2} so the ExtraTreesClassifier
class_weight behavior (constructor/fit/predict) is exercised for the new mode
and the integration suite catches regressions.
cpp/src/decisiontree/batched-levelalgo/builder.cuh (1)

21-31: ⚡ Quick win

Include <tuple> explicitly for the new std::make_tuple usage.

updateWorkloadInfo() and doSplit() now depend on std::make_tuple(), but this header still builds only if some transitive include happens to pull in <tuple>. Please add the direct include here so the file stays self-contained.

Suggested fix
 `#include` <deque>
 `#include` <memory>
+#include <tuple>
 `#include` <type_traits>
 `#include` <utility>
🤖 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/builder.cuh` around lines 21 - 31, The
file is missing a direct include of <tuple>, causing builds to rely on
transitive includes for new uses of std::make_tuple in updateWorkloadInfo and
doSplit; add `#include` <tuple> at the top of builder.cuh (near the other STL
includes) so the file is self-contained and both updateWorkloadInfo and doSplit
can use std::make_tuple reliably.
cpp/src/decisiontree/batched-levelalgo/objectives.cuh (1)

753-762: 💤 Low value

Potential division by zero in SetLeafVector.

If total sums to zero (all weighted_sum values are zero due to edge cases or floating-point accumulation), this division produces undefined behavior. Per coding guidelines, add an epsilon check.

This pattern appears in all six weighted objective SetLeafVector methods (lines 753-762, 871-879, 961-966, 1054-1058, 1147-1151, 1238-1242).

🛡️ Suggested epsilon guard
 static DI void SetLeafVector(BinT const* shist, int nclasses, DataT* out)
 {
   double total = 0.0;
   for (int i = 0; i < nclasses; i++) {
     total += shist[i].weighted_sum;
   }
+  // Guard against degenerate all-zero-weight leaf
+  if (total <= 0.0) total = 1.0;
   for (int i = 0; i < nclasses; i++) {
     out[i] = DataT(shist[i].weighted_sum) / DataT(total);
   }
 }

As per coding guidelines: "Add epsilon checks for division by zero or near-zero values".

🤖 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 753 -
762, The SetLeafVector function may divide by zero when total (sum of
shist[i].weighted_sum) is zero; update SetLeafVector (and the other five
weighted SetLeafVector overloads) to compute total, compare it against a small
epsilon (e.g., 1e-12) and, if total <= epsilon, populate out[] with a safe
default (suggest using uniform probabilities DataT(1)/DataT(nclasses)),
otherwise proceed with out[i] = DataT(shist[i].weighted_sum) / DataT(total);
reference symbols: SetLeafVector, BinT, DataT, shist, nclasses, out.
🤖 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 `@python/cuml/cuml/ensemble/randomforest_common.pyx`:
- Around line 494-500: Validate sample_weight before taking its pointer: ensure
sample_weight (the input used with cp.ascontiguousarray and assigned to
sample_weight_dev/sample_weight_ptr) is a 1-D array with length equal to the
number of rows in X (e.g., X.shape[0]); if not, raise a ValueError describing
the mismatch. Convert to a CuPy array first (cp.asarray), check ndim == 1 and
size == X.shape[0], then call cp.ascontiguousarray(..., dtype=X.dtype) and take
sample_weight_dev.data.ptr only after those checks pass.

---

Nitpick comments:
In `@cpp/src/decisiontree/batched-levelalgo/builder.cuh`:
- Around line 21-31: The file is missing a direct include of <tuple>, causing
builds to rely on transitive includes for new uses of std::make_tuple in
updateWorkloadInfo and doSplit; add `#include` <tuple> at the top of builder.cuh
(near the other STL includes) so the file is self-contained and both
updateWorkloadInfo and doSplit can use std::make_tuple reliably.

In `@cpp/src/decisiontree/batched-levelalgo/objectives.cuh`:
- Around line 753-762: The SetLeafVector function may divide by zero when total
(sum of shist[i].weighted_sum) is zero; update SetLeafVector (and the other five
weighted SetLeafVector overloads) to compute total, compare it against a small
epsilon (e.g., 1e-12) and, if total <= epsilon, populate out[] with a safe
default (suggest using uniform probabilities DataT(1)/DataT(nclasses)),
otherwise proceed with out[i] = DataT(shist[i].weighted_sum) / DataT(total);
reference symbols: SetLeafVector, BinT, DataT, shist, nclasses, out.

In `@python/cuml/cuml_accel_tests/integration/test_et_classifier.py`:
- Around line 46-53: The test test_et_class_weight for ExtraTreesClassifier
doesn't include the new "balanced_subsample" mode; update the
pytest.mark.parametrize on class_weight in test_et_class_weight to include
"balanced_subsample" alongside None, "balanced", and the dict {0:1, 1:2} so the
ExtraTreesClassifier class_weight behavior (constructor/fit/predict) is
exercised for the new mode and the integration suite catches regressions.
🪄 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: 603930db-0c4b-460c-8f0f-a8d9440a71c3

📥 Commits

Reviewing files that changed from the base of the PR and between ebc227b and 44013e1.

📒 Files selected for processing (77)
  • cpp/CMakeLists.txt
  • cpp/include/cuml/ensemble/randomforest.hpp
  • cpp/include/cuml/tree/decisiontree.hpp
  • cpp/src/decisiontree/batched-levelalgo/bins.cuh
  • cpp/src/decisiontree/batched-levelalgo/builder.cuh
  • cpp/src/decisiontree/batched-levelalgo/dataset.h
  • cpp/src/decisiontree/batched-levelalgo/kernels/builder_kernels.cuh
  • cpp/src/decisiontree/batched-levelalgo/kernels/builder_kernels_impl.cuh
  • cpp/src/decisiontree/batched-levelalgo/kernels/builder_random_kernels_impl.cuh
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_entropy-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_entropy-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_gamma-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_gamma-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_gini-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_gini-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_inverse_gaussian-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_inverse_gaussian-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_mse-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_mse-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_poisson-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/random_poisson-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_entropy-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_entropy-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_gamma-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_gamma-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_gini-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_gini-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_inverse_gaussian-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_inverse_gaussian-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_mse-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_mse-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_poisson-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_poisson-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_entropy-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_entropy-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_gamma-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_gamma-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_gini-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_gini-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_inverse_gaussian-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_inverse_gaussian-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_mse-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_mse-float.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_poisson-double.cu
  • cpp/src/decisiontree/batched-levelalgo/kernels/weighted_random_poisson-float.cu
  • cpp/src/decisiontree/batched-levelalgo/objectives.cuh
  • cpp/src/decisiontree/decisiontree.cu
  • cpp/src/decisiontree/decisiontree.cuh
  • cpp/src/randomforest/randomforest.cu
  • cpp/src/randomforest/randomforest.cuh
  • cpp/tests/CMakeLists.txt
  • cpp/tests/sg/extratrees_rng_reference.h
  • cpp/tests/sg/extratrees_rng_reference_gen.py
  • cpp/tests/sg/extratrees_test.cu
  • cpp/tests/sg/rf_test.cu
  • python/cuml/cuml/__init__.py
  • python/cuml/cuml/accel/_overrides/sklearn/ensemble.py
  • python/cuml/cuml/benchmark/algorithms.py
  • python/cuml/cuml/benchmark/automated/bench_extra_trees.py
  • python/cuml/cuml/dask/ensemble/randomforestclassifier.py
  • python/cuml/cuml/dask/ensemble/randomforestregressor.py
  • python/cuml/cuml/ensemble/__init__.py
  • python/cuml/cuml/ensemble/extra_trees_classifier.py
  • python/cuml/cuml/ensemble/extra_trees_regressor.py
  • python/cuml/cuml/ensemble/randomforest_common.pyx
  • python/cuml/cuml/ensemble/randomforestclassifier.py
  • python/cuml/cuml/ensemble/randomforestregressor.py
  • python/cuml/cuml_accel_tests/integration/test_et_classifier.py
  • python/cuml/cuml_accel_tests/integration/test_et_regressor.py
  • python/cuml/cuml_accel_tests/integration/test_rf_classifier.py
  • python/cuml/cuml_accel_tests/integration/test_rf_regressor.py
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
  • python/cuml/tests/dask/test_dask_random_forest.py
  • python/cuml/tests/test_extratrees.py
  • python/cuml/tests/test_random_forest.py
  • python/cuml/tests/test_sklearn_compatibility.py
  • python/cuml/tests/test_sklearn_import_export.py

Comment on lines +494 to +500
# None -> NULL pointer -> unweighted C++ path unchanged.
sample_weight_dev = None
cdef uintptr_t sample_weight_ptr = 0
if sample_weight is not None:
sample_weight_dev = cp.ascontiguousarray(
sample_weight, dtype=X.dtype)
sample_weight_ptr = sample_weight_dev.data.ptr

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate sample_weight before taking its raw pointer.

cp.ascontiguousarray() will accept a mis-sized or multi-dimensional input, and the C++ fit path will still read it as n_rows contiguous weights. That can silently train on the wrong weights or walk past the intended buffer. Please enforce a 1-D array with exactly one entry per row here.

Suggested fix
         sample_weight_dev = None
         cdef uintptr_t sample_weight_ptr = 0
         if sample_weight is not None:
+            if sample_weight.ndim != 1 or sample_weight.shape[0] != X.shape[0]:
+                raise ValueError(
+                    "sample_weight must be 1-dimensional with one entry per row"
+                )
             sample_weight_dev = cp.ascontiguousarray(
                 sample_weight, dtype=X.dtype)
             sample_weight_ptr = sample_weight_dev.data.ptr
🤖 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 `@python/cuml/cuml/ensemble/randomforest_common.pyx` around lines 494 - 500,
Validate sample_weight before taking its pointer: ensure sample_weight (the
input used with cp.ascontiguousarray and assigned to
sample_weight_dev/sample_weight_ptr) is a 1-D array with length equal to the
number of rows in X (e.g., X.shape[0]); if not, raise a ValueError describing
the mismatch. Convert to a CuPy array first (cp.asarray), check ndim == 1 and
size == X.shape[0], then call cp.ascontiguousarray(..., dtype=X.dtype) and take
sample_weight_dev.data.ptr only after those checks pass.

@chyunsu3 chyunsu3 added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels May 29, 2026
@switch527
switch527 marked this pull request as draft May 30, 2026 16:56
@switch527 switch527 closed this Jun 1, 2026
@switch527

Copy link
Copy Markdown
Contributor Author

Covered by the new PR structure for class weight and extra trees

@switch527
switch527 deleted the fea-rf-balanced-subsample branch June 1, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CMake CUDA/C++ Cython / Python Cython or Python issue 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.

[FEA] Add class_weight='balanced_subsample' to RandomForest and ExtraTrees classifiers

4 participants