Skip to content

blackbox: warn when LimeTabular ignores user-supplied mode (#477)#667

Open
jbbqqf wants to merge 1 commit intointerpretml:mainfrom
jbbqqf:feat/477-lime-mode-warn-override
Open

blackbox: warn when LimeTabular ignores user-supplied mode (#477)#667
jbbqqf wants to merge 1 commit intointerpretml:mainfrom
jbbqqf:feat/477-lime-mode-warn-override

Conversation

@jbbqqf
Copy link
Copy Markdown

@jbbqqf jbbqqf commented May 9, 2026

Summary

LimeTabular(model=..., data=..., mode="classification") silently drops
the user-supplied mode argument and runs LIME in "regression" mode
instead. The override is necessary (the wrapper unifies classifier
predict functions to a scalar so LIME can treat them uniformly), but
silence makes it confusing — users were left wondering why their
keyword had no effect (issue #477). Emit a UserWarning when the
override actually changes anything, so the behaviour stays the same
but the surprise is gone.

Fixes #477Why the "mode" in lime is fixed to "regression"?

Context

In _lime.py the wrapper does:

# rewrite these even if the user specified them
kwargs = kwargs.copy()
kwargs["mode"] = "regression"
kwargs["feature_names"] = self.feature_names_in_

The forced "regression" is intentional: unify_predict_fn(predict_fn, X, 1 if n_classes == 2 else -1) turns a classifier into something that returns
a scalar probability, and LIME-in-classification-mode would expect a 2D
probability array. Running LIME in regression mode is what makes the
wrapper work for both regression and binary classification.

That is fine. What is not fine is dropping the user's mode kwarg
without a peep: LimeTabular(..., mode="classification") looked like it
would do something and did nothing. This PR keeps the override but
emits a UserWarning only when the user actually passed a different
value. The silent path (no mode, or explicit mode="regression")
stays warning-free so we don't nag people who follow the documented
API.

Changes

  • python/interpret-core/interpret/blackbox/_lime.py
    • import warnings
    • before applying the override, check whether kwargs["mode"] is
      already "regression". If not, emit a UserWarning mentioning the
      user's value and explaining why the wrapper forces regression mode.
    • the inline comment on the override is rewritten to call out the
      invariant explicitly (citing issue Why the "mode" in lime is fixed to "regression"? #477) so a future maintainer
      doesn't have to re-derive why this guard exists.
  • python/interpret-core/tests/blackbox/test_lime.py (new)
    • three pytest cases: warning on mode="classification", no warning
      when mode is omitted, no warning when the user explicitly passes
      mode="regression". The first case fails on origin/main.

Reproduce BEFORE/AFTER yourself (copy-paste)

# --- one-time setup ---
git clone https://github.com/interpretml/interpret.git /tmp/repro && cd /tmp/repro
python -m venv .venv && source .venv/bin/activate
pip install -e "python/interpret-core[lime]"

cat > /tmp/repro_477.py <<'PY'
import warnings
import numpy as np
from interpret.blackbox import LimeTabular

rng = np.random.default_rng(0)
data = rng.standard_normal((20, 3)).astype(np.float64)

def predict_fn(X):
    return 1.0 / (1.0 + np.exp(-X.sum(axis=1)))

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    LimeTabular(model=predict_fn, data=data, mode="classification")

mode_warnings = [w for w in caught if issubclass(w.category, UserWarning)]
print(f"warnings emitted: {len(mode_warnings)}")
for w in mode_warnings:
    print(f"  - {w.message}")
PY

# --- BEFORE (origin/main) ---
git checkout origin/main
python /tmp/repro_477.py
# Expected: "warnings emitted: 0" — the override is silent

# --- AFTER (this PR) ---
git fetch https://github.com/jbbqqf/interpret.git feat/477-lime-mode-warn-override
git checkout FETCH_HEAD
python /tmp/repro_477.py
# Expected: "warnings emitted: 1" with a message mentioning both
#           "regression" and "classification"

What I ran locally

  • python -m pytest -vv tests/blackbox/test_lime.py3 passed on
    this branch; 1 failed, 2 passed on origin/main (the failing
    case is the warning assertion — exactly the divergence this PR
    introduces).
  • ruff check interpret-core/interpret/blackbox/_lime.py → 1 error,
    identical to origin/main (PLC0415 on the existing from lime…
    inline import; pre-existing, not touched by this PR).
  • ruff format --check on the touched files → already formatted.

Edge cases tested

# Scenario Input Expected Verified by
1 mode="classification" user kwarg differs from internal default UserWarning emitted, message mentions both modes test_user_supplied_mode_classification_warns
2 mode omitted default path zero warnings about mode= test_no_warning_when_mode_omitted
3 mode="regression" (explicit) user matches internal default zero warnings — no override happening test_no_warning_when_mode_regression

Risk / blast radius

The override behaviour is unchanged; existing user code keeps working.
The only new artefact is a UserWarning on a path that was previously
silent. Anyone who was unknowingly relying on the silent-override
behaviour will now see a single warning explaining what happened, then
the wrapper continues exactly as before.

Release note

LimeTabular now warns when the user-supplied `mode` kwarg is overridden
to "regression" (issue #477). The override behaviour itself is
unchanged.

PR drafted with assistance from Claude Code. The change was reviewed
manually against interpretml/interpret's source and the cited issue.
The reproducer block above was used during development; it is the same
one a reviewer can paste verbatim.

…ml#477)

LimeTabular always runs the underlying lime explainer in
mode="regression", regardless of whether the wrapped model is a
regressor or a binary classifier — unify_predict_fn turns classifier
output into a scalar probability so LIME can treat both cases
uniformly. The override is intentional and necessary for the wrapper to
work, but interpretml#477 showed it is silent and surprising: a user passing
mode="classification" got their kwarg dropped on the floor with no
indication.

Issue a UserWarning whenever the user-supplied mode differs from
"regression". Default-path users (no mode kwarg, or explicit
mode="regression") see no warning. The override behaviour itself is
unchanged — the goal is purely to reduce confusion.

Add tests/blackbox/test_lime.py covering all three branches: warn on
classification, quiet on default, quiet on explicit "regression". The
warning case fails on origin/main (no warning emitted) and passes on
this branch.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: jbbqqf <jbaptiste.braun@gmail.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.93%. Comparing base (6c79a67) to head (be4b3f8).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #667      +/-   ##
==========================================
+ Coverage   66.86%   66.93%   +0.07%     
==========================================
  Files          77       77              
  Lines       11724    11728       +4     
==========================================
+ Hits         7839     7850      +11     
+ Misses       3885     3878       -7     
Flag Coverage Δ
bdist_linux_311_python ?
bdist_linux_312_python ?
bdist_linux_313_python ?
bdist_linux_314_python ?
bdist_linuxarm_311_python 66.72% <100.00%> (+0.10%) ⬆️
bdist_linuxarm_312_python 64.83% <100.00%> (-1.80%) ⬇️
bdist_linuxarm_313_python ?
bdist_linuxarm_314_python ?
bdist_mac_311_python ?
bdist_mac_312_python ?
bdist_mac_313_python 66.10% <100.00%> (-0.66%) ⬇️
bdist_mac_314_python ?
bdist_win_311_python ?
bdist_win_312_python ?
bdist_win_313_python ?
bdist_win_314_python ?
sdist_linux_311_python ?
sdist_linux_312_python ?
sdist_linux_313_python ?
sdist_linux_314_python ?
sdist_linuxarm_311_python 64.77% <100.00%> (-1.78%) ⬇️
sdist_linuxarm_312_python ?
sdist_linuxarm_313_python ?
sdist_linuxarm_314_python 64.67% <100.00%> (-1.79%) ⬇️
sdist_mac_311_python 66.78% <100.00%> (+0.10%) ⬆️
sdist_mac_312_python ?
sdist_mac_313_python ?
sdist_mac_314_python 65.92% <100.00%> (-0.66%) ⬇️
sdist_win_311_python ?
sdist_win_312_python ?
sdist_win_313_python ?
sdist_win_314_python ?

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.

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.

Why the "mode" in lime is fixed to "regression"?

1 participant