Fix regression in RandomForestClassifier binary classification predictions - #28685
Conversation
…ghts_are_all_positive_ logic PR #27552 removed the weights_are_all_positive_ flag which determined whether to use the > 0.5 threshold with 1-score complement (write_additional_scores 0/1) or the > 0 threshold with -score complement (write_additional_scores 2/3). For RandomForestClassifier models where all leaf weights are non-negative (representing probabilities), removing this distinction caused: - Wrong probabilities (negative values instead of 1-p complement) - Wrong labels (when score is between 0 and 0.5) The LOGISTIC post_transform case (which issue #27533 was about) handles both paths identically (applying sigmoid to score and -score), so restoring this flag does not break the fix for #27533. Fixes #27919
|
Copilot can you add a non regression test (it should fail on |
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
The fix correctly restores the weights_are_all_positive_ distinction so binary classifiers with non-negative leaf weights (RandomForest-style) use the > 0.5 threshold with the 1 - score complement (codes 0/1), while mixed/negative-weight models keep > 0 with -score (codes 2/3). This matches the add_second_class contract in write_scores, and the LOGISTIC path is unaffected. Index access in the new loop is safe given the size invariants enforced in the base Init, and the aggregator constructor's member-init order matches declaration order.
One consistency suggestion is left inline. Otherwise this is a well-scoped regression fix with targeted tests covering the < 0.5, > 0.5, and 0.5-boundary cases.
…nvention Use target_class_weights_as_tensor.empty() selector with static_cast to match the weight extraction pattern used elsewhere in Init().
Review — PR #28685 (Fix regression in RandomForestClassifier binary classification)Verdict: approve. This is a clean regression fix for issue #28557 (sklearn What it fixesPR #27552 collapsed two distinct binary-classification score-conversion paths into one:
After #27552, every binary case took the mixed-weights path. For RF where a leaf weight of 0.3 is a probability, the "complement" became What's right
Comments1. Boundary semantics — confirm with the spec. Test 3 pins 2. The fix only addresses binary classification. The 3. 4. Test naming nit. 5. The test rebuilds the full attribute set three times. A small lambda or helper would tidy this up, but for three tests it's fine as-is. Don't refactor. 6. The fix is narrow but the surface is wide. Things that look good
Bottom lineSolid regression fix; correctly identifies the dropped invariant, restores it without re-breaking the original #27533 fix, and pins it down with tests that would catch a future regression. Approve. 85/85 CI green after the consistency commit. |
|
Thanks a lot! |
Description
Restores the
weights_are_all_positive_flag inTreeEnsembleClassifierthat was removed in PR #27552.This flag controls how binary classification scores are converted to two-class probabilities in
_set_score_binary:> 0.5, complement via1 - score(write_additional_scores= 0/1)> 0, complement via-score(write_additional_scores= 2/3)Without this distinction, RandomForest models produce negative "probabilities" and incorrect labels when the score falls in (0, 0.5).
The LOGISTIC post_transform path (the #27533 fix) is unaffected because
write_scoresappliessigmoid(score)/sigmoid(-score)identically for both cases 0/1 and 2/3.Motivation and Context
PR #27552 fixed #27533 (LOGISTIC transform with negative weights) but inadvertently broke all binary
TreeEnsembleClassifiermodels with non-negative weights and non-LOGISTIC post_transform — notably sklearnRandomForestClassifierconversions via skl2onnx.