Skip to content

Add per-feature smoothing_rounds support (#626)#672

Open
ugbotueferhire wants to merge 2 commits into
interpretml:mainfrom
ugbotueferhire:feature/per-feature-smoothing-rounds
Open

Add per-feature smoothing_rounds support (#626)#672
ugbotueferhire wants to merge 2 commits into
interpretml:mainfrom
ugbotueferhire:feature/per-feature-smoothing-rounds

Conversation

@ugbotueferhire
Copy link
Copy Markdown
Contributor

Extend smoothing_rounds and interaction_smoothing_rounds to accept a sequence of ints, one per term, in addition to the existing scalar form. Per Paul's spec on the issue thread: the smoothing phase as a whole runs while any term still has rounds remaining; a term whose counter has reached zero falls back to normal gain-based updates for the remaining smoothing iterations.

  • _boost.py: replace the scalar smoothing_rounds counter with a per-term np.int64 array. Scalars are broadcast at the top of the loop, so the inner control flow is uniform. The RandomSplits flag, the make_progress predicate, the early-stopping skip, and the end-of-cycle decrement all become per-term checks.
  • _ebm.py: validate int | Sequence[int] in _normalize_smoothing_rounds. At the mains call site, gather the per-feature list down to the post-exclude term_features order. At the interactions call site, only accept a list when interactions is an explicit list (FAST-discovered interactions cannot be aligned to a user-supplied per-interaction parameter), and align via boost_groups_user_idx to handle dedup.
  • Update docstrings on ExplainableBoostingClassifier/Regressor and document that DP-EBM ignores the list form.

Tests cover scalar/list equivalence, per-feature zero disables smoothing for those features without breaking other features' smoothing, exclude alignment, validation errors (wrong length, negatives, non-integers, empty), and the interactions-list constraint.

Extend smoothing_rounds and interaction_smoothing_rounds to accept a
sequence of ints, one per term, in addition to the existing scalar form.
Per Paul's spec on the issue thread: the smoothing phase as a whole runs
while any term still has rounds remaining; a term whose counter has
reached zero falls back to normal gain-based updates for the remaining
smoothing iterations.

* _boost.py: replace the scalar smoothing_rounds counter with a per-term
  np.int64 array. Scalars are broadcast at the top of the loop, so the
  inner control flow is uniform. The RandomSplits flag, the
  make_progress predicate, the early-stopping skip, and the end-of-cycle
  decrement all become per-term checks.
* _ebm.py: validate int | Sequence[int] in _normalize_smoothing_rounds.
  At the mains call site, gather the per-feature list down to the
  post-exclude term_features order. At the interactions call site, only
  accept a list when interactions is an explicit list (FAST-discovered
  interactions cannot be aligned to a user-supplied per-interaction
  parameter), and align via boost_groups_user_idx to handle dedup.
* Update docstrings on ExplainableBoostingClassifier/Regressor and
  document that DP-EBM ignores the list form.

Tests cover scalar/list equivalence, per-feature zero disables smoothing
for those features without breaking other features' smoothing, exclude
alignment, validation errors (wrong length, negatives, non-integers,
empty), and the interactions-list constraint.

Signed-off-by: ugbotueferhire <ugbotueferhire@gmail.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.46%. Comparing base (7fbb088) to head (6d7a3f6).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #672      +/-   ##
==========================================
+ Coverage   67.20%   67.46%   +0.25%     
==========================================
  Files          77       77              
  Lines       11735    11796      +61     
==========================================
+ Hits         7887     7958      +71     
+ Misses       3848     3838      -10     
Flag Coverage Δ
bdist_linux_311_python 67.20% <100.00%> (+0.25%) ⬆️
bdist_linux_312_python 67.19% <100.00%> (+0.24%) ⬆️
bdist_linux_313_python 67.21% <100.00%> (+0.27%) ⬆️
bdist_linux_314_python 67.12% <100.00%> (+0.27%) ⬆️
bdist_linuxarm_311_python 67.23% <100.00%> (+0.27%) ⬆️
bdist_linuxarm_312_python 67.23% <100.00%> (+0.27%) ⬆️
bdist_linuxarm_313_python 67.21% <100.00%> (+0.25%) ⬆️
bdist_linuxarm_314_python 67.12% <100.00%> (+0.25%) ⬆️
bdist_mac_311_python 67.36% <100.00%> (+0.25%) ⬆️
bdist_mac_312_python 67.37% <100.00%> (+0.27%) ⬆️
bdist_mac_313_python 67.37% <100.00%> (+0.26%) ⬆️
bdist_mac_314_python 67.27% <100.00%> (+0.25%) ⬆️
bdist_win_311_python 67.36% <100.00%> (+0.23%) ⬆️
bdist_win_312_python 67.37% <100.00%> (+0.25%) ⬆️
bdist_win_313_python 67.39% <100.00%> (+0.27%) ⬆️
bdist_win_314_python 67.30% <100.00%> (+0.27%) ⬆️
sdist_linux_311_python 67.14% <100.00%> (+0.25%) ⬆️
sdist_linux_312_python 67.16% <100.00%> (+0.27%) ⬆️
sdist_linux_313_python 67.16% <100.00%> (+0.27%) ⬆️
sdist_linux_314_python 67.05% <100.00%> (+0.25%) ⬆️
sdist_linuxarm_311_python 67.15% <100.00%> (+0.23%) ⬆️
sdist_linuxarm_312_python 67.15% <100.00%> (+0.25%) ⬆️
sdist_linuxarm_313_python 67.16% <100.00%> (+0.26%) ⬆️
sdist_linuxarm_314_python 67.04% <100.00%> (+0.24%) ⬆️
sdist_mac_311_python 67.29% <100.00%> (+0.27%) ⬆️
sdist_mac_312_python 67.29% <100.00%> (+0.27%) ⬆️
sdist_mac_313_python 67.29% <100.00%> (+0.27%) ⬆️
sdist_mac_314_python 67.18% <100.00%> (+0.25%) ⬆️
sdist_win_311_python 67.39% <100.00%> (+0.27%) ⬆️
sdist_win_312_python 67.37% <100.00%> (+0.25%) ⬆️
sdist_win_313_python 67.39% <100.00%> (+0.27%) ⬆️
sdist_win_314_python 67.28% <100.00%> (+0.23%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Codecov flagged the validation helper's defensive branches as untested.
Add tests for: None input (treated as 0), float scalar (accepted when
integer-valued, rejected when fractional or negative), 2D array, object-
dtype array, unsupported type (set), per-feature list with exclude='mains'
(empty term_features path), and explicit interactions all excluded
(empty boost_groups path).

Signed-off-by: ugbotueferhire <ugbotueferhire@gmail.com>
@ugbotueferhire
Copy link
Copy Markdown
Contributor Author

hello @paulbkoch how are you doing,
I made submitted a PR will be awaiting your feedback :)

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant