Skip to content

Normalize forest sample weights#3674

Open
ethanglaser wants to merge 8 commits into
uxlfoundation:mainfrom
ethanglaser:dev/eglaser-forest-fix
Open

Normalize forest sample weights#3674
ethanglaser wants to merge 8 commits into
uxlfoundation:mainfrom
ethanglaser:dev/eglaser-forest-fix

Conversation

@ethanglaser

@ethanglaser ethanglaser commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes test deselected in uxlfoundation/scikit-learn-intelex#3231
Accompanies uxlfoundation/scikit-learn-intelex#3292
Combined CI: http://intel-ci.intel.com/f170e0af-9e7a-f1a7-82dc-d4f5ef20c6a0


Checklist:

Completeness and readability

  • I have commented my code, particularly in hard-to-understand areas.
  • I have updated the documentation to reflect the changes or created a separate PR with updates and provided its number in the description, if necessary.
  • Git commit message contains an appropriate signed-off-by string (see CONTRIBUTING.md for details).
  • I have resolved any merge conflicts that might occur with the base branch.

Testing

  • I have run it locally and tested the changes extensively.
  • All CI jobs are green or I have provided justification why they aren't.
  • I have extended testing suite if new functionality was introduced in this PR.

Performance

  • I have measured performance for affected algorithms using scikit-learn_bench and provided at least a summary table with measured data, if performance change is expected.
  • I have provided justification why performance and/or quality metrics have changed or why changes are not expected.
  • I have extended the benchmarking suite and provided a corresponding scikit-learn_bench PR if new measurable functionality was introduced in this PR.

@ethanglaser ethanglaser marked this pull request as ready for review June 25, 2026 23:04
Copilot AI review requested due to automatic review settings June 25, 2026 23:04
@ethanglaser ethanglaser changed the title Dev/eglaser forest fix Normalize forest sample weights Jun 25, 2026
@ethanglaser

Copy link
Copy Markdown
Contributor Author

/azp run CI

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI 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.

Pull request overview

This PR adjusts decision forest training to reduce sensitivity to the absolute scale of sample weights by introducing a weight-rescaling step (max weight scaled to 1) and applying it in both classification and regression forest training paths. It also aligns an internal regression split-selection accumulator type with the higher-precision intermediate type used in the surrounding impurity math.

Changes:

  • Added normalizeWeights() helper to rescale input sample weights before training.
  • Applied weight normalization in both classification and regression *TrainBatchKernel::compute() implementations.
  • Updated a regression split-selection variable (vBest) to use intermSummFPType for consistency with intermediate computations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
cpp/daal/src/algorithms/dtrees/forest/df_train_dense_default_impl.i Adds normalizeWeights() helper and required include to create a normalized weights table.
cpp/daal/src/algorithms/dtrees/forest/regression/df_regression_train_dense_default_impl.i Uses normalized weights in training and adjusts split-selection accumulator precision.
cpp/daal/src/algorithms/dtrees/forest/classification/df_classification_train_dense_default_impl.i Uses normalized weights in training.

Comment on lines +59 to +60
const size_t nRows = weights->getNumberOfRows();
if (!nRows) return empty;
Comment on lines +67 to +86
algorithmFPType maxWeight = 0;
for (size_t i = 0; i < nRows; ++i)
{
if (src[i] > maxWeight) maxWeight = src[i];
}
if (!(maxWeight > 0)) return empty;

services::SharedPtr<HomogenNumericTableCPU<algorithmFPType, cpu> > normalized =
HomogenNumericTableCPU<algorithmFPType, cpu>::create(1, nRows, &s);
if (!s) return empty;

WriteOnlyRows<algorithmFPType, cpu> dstBlock(normalized.get(), 0, nRows);
s |= dstBlock.status();
if (!s) return empty;
algorithmFPType * dst = dstBlock.get();

for (size_t i = 0; i < nRows; ++i)
{
dst[i] = src[i] / maxWeight;
}
ImpurityData right;
IndexType iBest = -1;
algorithmFPType vBest;
intermSummFPType vBest;

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.

If you're going to do this, then it makes more sense to make normalizedWeights of intermSummFPType. It also would need to be modified in a lot of other places to be consistent.

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.

The normalized table can't be intermSummFPType - it's read back as algorithmFPType by _helper.init/ReadRows, so a float32 fit needs a float32 table

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.

Then please change the dtype in all other places that calculate similar quantities to be consistent.

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.

I made one other change that I think is related to what you are eluding to, otherwise you're going to need to be more specific as I am not very familiar with these files.

Comment thread cpp/daal/src/algorithms/dtrees/forest/df_train_dense_default_impl.i
}
if (!(maxWeight > 0)) return empty;

services::SharedPtr<HomogenNumericTableCPU<algorithmFPType, cpu> > normalized =

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.

Does this need to be a oneDAL table? It's just a one-dimensional array which could be passed as a unique or shared pointer.

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.

Downstream functionality (compute, computeForSpecificHelper, TrainBatchTask) all consume NumericTables so it sets up well for those

if (!s) return empty;
algorithmFPType * dst = dstBlock.get();

for (size_t i = 0; i < nRows; ++i)

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.

It could use ?rscl (preferrably) or ?scal from MKL.

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.

Updated

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.

On a second look, if it's going to copy beforehand, maybe it'd make more sense to replace with a vectorized loop. Could also set a pragma for the alignment, given that the new array is allocated through oneDAL.

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.

I believe your MKL suggestion would be preferable

@ethanglaser

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants