Problem
Credit policy and model risk management routinely require monotone relationships: more delinquencies must never decrease estimated risk. Teachers can enforce this (monotone_constraints in XGBoost/LightGBM, monotonic_cst in sklearn's HistGradientBoostingRegressor).
CompileML's whitebox cannot. train_whitebox uses GradientBoostingRegressor, which does not accept monotonic_cst (verified on sklearn 1.8). So even when the teacher is monotone, the distilled student may not be — and nothing detects it.
This is made worse by our own scorecard feature: build_scorecard will happily print a bin table where 3 late payments scores better than 2. That is a scorecard a validation committee rejects on sight, and we currently make the violation more visible without offering a way to prevent it.
Note the naming collision to avoid: everything in compileml.bands called "monotone" refers to band monotonicity (bad rates rising across bands). This issue is about feature monotonicity, a different property.
Proposed approach
- Switch
train_whitebox to HistGradientBoostingRegressor, or add it as a selectable backend, so monotonic_cst can be passed through.
- Accept
monotone_constraints={"BILLS_PAID_LATE": +1, "INCOME": -1} (or a positional array) on train_whitebox and build_artifact.
- Record the constraints in the artifact under
runtime or metadata so they travel with the decision.
- Add a validation check that verifies the compiled scorecard is monotone in every constrained feature — checking the emitted bins, not just trusting the trainer.
Acceptance criteria
Files
src/compileml/compile/distill.py, src/compileml/compile/extract.py, src/compileml/artifact/build.py, src/compileml/validate/framework.py, src/compileml/scorecard/build.py
Scope
Substantial. The extractor work is the non-obvious part: HistGradientBoostingRegressor stores binned thresholds and needs its own extraction path with the same parity gate the existing extractors have.
Problem
Credit policy and model risk management routinely require monotone relationships: more delinquencies must never decrease estimated risk. Teachers can enforce this (
monotone_constraintsin XGBoost/LightGBM,monotonic_cstin sklearn'sHistGradientBoostingRegressor).CompileML's whitebox cannot.
train_whiteboxusesGradientBoostingRegressor, which does not acceptmonotonic_cst(verified on sklearn 1.8). So even when the teacher is monotone, the distilled student may not be — and nothing detects it.This is made worse by our own scorecard feature:
build_scorecardwill happily print a bin table where 3 late payments scores better than 2. That is a scorecard a validation committee rejects on sight, and we currently make the violation more visible without offering a way to prevent it.Note the naming collision to avoid: everything in
compileml.bandscalled "monotone" refers to band monotonicity (bad rates rising across bands). This issue is about feature monotonicity, a different property.Proposed approach
train_whiteboxtoHistGradientBoostingRegressor, or add it as a selectable backend, somonotonic_cstcan be passed through.monotone_constraints={"BILLS_PAID_LATE": +1, "INCOME": -1}(or a positional array) ontrain_whiteboxandbuild_artifact.runtimeormetadataso they travel with the decision.Acceptance criteria
compileml inspectvalidate_artifactfails when a recorded constraint is violated in the compiled modelHistGradientBoostingRegressoruses a different internal tree structure thanGradientBoostingRegressor—compileml/compile/extract.pywill need a branch)Files
src/compileml/compile/distill.py,src/compileml/compile/extract.py,src/compileml/artifact/build.py,src/compileml/validate/framework.py,src/compileml/scorecard/build.pyScope
Substantial. The extractor work is the non-obvious part:
HistGradientBoostingRegressorstores binned thresholds and needs its own extraction path with the same parity gate the existing extractors have.