Repository navigation
BUG: compute Probit loglike, score and hessian in log space - #10229
Merged
Merged
Conversation
Probit evaluated log(clip(cdf(z), eps, 1)), which is flat at log(eps) for q * XB below about -8 while log(Phi(z)) keeps falling, and used the clipped cdf in the pdf / cdf Mills ratio for the score. hessian did not clip and returned nan below z = -38. With an extreme misclassified observation this gave a wrong llf, a wrong-signed score, a nan Hessian and spurious bfgs optima with nan bse. Use log_ndtr for log(Phi(z)) and exp(logpdf - log_ndtr) for the Mills ratio in loglike, loglikeobs, score, score_obs, score_factor, hessian, hessian_factor and _deriv_score_obs_dendog. This is the Probit counterpart of the Logit overflow fix in gh-3923.
|
Thanks, the AI Disclosure section looks complete. |
Member
|
LGTM. Just want to see the other CI tools pass. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NumPy's guide.
AI Disclosure
Contributions must comply with the statsmodels AI Policy.
Tool(s): Claude (Claude Code).
Used for: locating the bug (probing Probit against scipy references after the Logit BUG/ENH: overflow in Logit #3923 fix), drafting the implementation and the two regression tests, running the verification (scipy references, complex-step and finite-difference derivative checks, min-version scipy 1.8.1 check), and drafting this description and the linked issue.
I have personally read, understood, and can explain every line of this diff, and
have verified the statistical/numerical correctness of the change.
Summary
Probitevaluated its log-likelihood aslog(clip(cdf(z), FLOAT_EPS, 1)), which is flat atlog(eps) = -36.04forq * XBbelow about -8 while the truelog(Phi(z))keeps falling (-53 at z = -10, -805 at z = -40). The score used the same clippedcdfin the Mills ratiopdf / cdf, wherepdfunderflows first, so extreme observations contributed ~0 score instead of a value growing like|z|;hessianandhessian_factordid not clip at all and returned nan below z = -38. With a single extreme misclassified observation in the sample this gave a wrongllf, a wrong-signed score, a nan Hessian, andfit(method="bfgs")stopping at a spurious optimum with nanbse(details and repro in #10228). This is the Probit counterpart of theLogitoverflow fixed in gh-3923.The fix computes everything in log space with the exact scipy pieces:
_norm_logcdf(z) = log_ndtr(z)replaceslog(clip(cdf))inloglikeandloglikeobs._norm_mills(z) = exp(norm._logpdf(z) - log_ndtr(z))replaces the clippedpdf / cdfinscore,score_obs,score_factor,hessianandhessian_factor, so the inverse Mills ratio stays finite and accurate wherecdfunderflows (it behaves like-zthere)._deriv_score_obs_dendogusesexp(logpdf - log_ndtr(z) - log_ndtr(-z))forpdf / (cdf * (1 - cdf)), via1 - Phi(z) = Phi(-z).log_ndtraccepts complex input (checked on scipy 1.8.1, the minimum supported version), so complex-step differentiation of the newloglikestill works and agrees with the analytic score. In the normal range the new values agree with the old ones to ~1e-14, and all existing Probit tests pass unchanged.Tests
test_probit_loglikeobs_extreme_linpredmirrors the gh-3923 Logit test (z from -1e4 to 1e4: finiteloglikeobs,score_obs,hessian; agreement withscipy.stats.norm.logcdfat 1e-14 and with the Mills ratio reference at 1e-12).test_probit_extreme_observation_fituses the outlier sample: exactloglike/score/hessianagainst scipy references, complex-step and finite-difference derivative checks, and bothnewtonandbfgsconverging to the same optimum as a directscipy.optimize.minimizeof the exact log-likelihood with finitebse. Both tests fail on main and pass with the fix. The reference values are scipy'slog_ndtr/norm.logcdfandnorm.logpdf, which are the same quantities R'spnorm(log.p = TRUE)anddnorm(log = TRUE)compute.pytest statsmodels/discrete/testsandstatsmodels/tools/tests/test_numdiff.pypass;ruff checkis clean. No release note added since the recent BUG merges did not touchdocs/source/release/; happy to add one wherever you want it.