NGBoost - Natural Gradient Boosting for probabilistic prediction.
gradient_boosting, nothing here wraps an external boosting library: the base learners are TuiML's own DecisionTreeRegressor.Classes
class algorithms.gradient_boosting.ngboost.NGBoostRegressor(_NGBoostBase, Regressor)
NGBoost fits a whole predictive distribution, not just a mean.
__init__( self, dist: str = 'normal', scoring: str = 'log', n_estimators: int = 100, learning_rate: float = 0.1, max_depth: int = 3, min_samples_split: int = 2, min_samples_leaf: int = 1, natural_gradient: bool = True, minibatch_frac: float = 1.0, tol: float = 1e-05, random_state: Optional[int] = None, verbose: bool = False, )
Overview
- Initialise every sample with the marginal MLE of the distribution.
- At each stage compute the natural gradient
-
Fit one
DecisionTreeRegressorper
- Line-search the stage scaling that minimises the mean score, shrink it
learning_rate, and take the step.Theory
For a proper scoring rule S the induced Riemannian metric is
which for the log score is the Fisher information. With the Normal parameterised as \theta = (\mu, \log \sigma) and z = (y - \mu)/\sigma, the negative log-likelihood has gradient
and Fisher information
so the natural gradient collapses to the strikingly simple, scale-free
The CRPS alternative,
is also proper, and its own metric is used when scoring="crps".
Parameters
dist
"normal", "lognormal" (requires strictly positive targets) or "exponential" (requires non-negative targets).
scoring
"log" is the negative log-likelihood; "crps" is the continuous ranked probability score and is available for dist="normal" and "lognormal".
n_estimators
learning_rate
max_depth
min_samples_split
min_samples_leaf
natural_gradient
False to recover ordinary (non-invariant) gradient boosting of the same score.
minibatch_frac
tol
random_state
verbose
Attributes
dist_
init_params_
estimators_
scalings_
learning_rate times the line-searched scaling.
train_score_
n_estimators_
n_estimators when the tol early stop triggers).
n_features_in_
fit().
Notes
Complexity:
- Fitting: O(M \cdot k \cdot n p \log n) for M stages and
- Prediction: O(M \cdot k \cdot d) per sample for depth
When to use NGBoostRegressor:
- When a calibrated predictive interval matters as much as the point
- When the noise is heteroscedastic, so a single global error bar would
- Prefer a plain boosted regressor when only the conditional mean is
References
See Also
>>> import numpy as np
>>> from tuiml.algorithms.gradient_boosting import NGBoostRegressor
>>> rng = np.random.default_rng(0)
>>> X = rng.uniform(-3, 3, size=(300, 1))
>>> # Noise grows with x: a single global error bar cannot describe this.
>>> y = X[:, 0] ** 2 + rng.normal(0, 0.2 + 0.5 * np.abs(X[:, 0]))
>>> model = NGBoostRegressor(n_estimators=60, random_state=0).fit(X, y)
>>> params = model.predict_dist(X)
>>> sorted(params)
['loc', 'scale']
>>> # The fitted scale tracks the true |x|-driven noise.
>>> bool(np.corrcoef(params["scale"], np.abs(X[:, 0]))[0, 1] > 0.7)
True
>>> lower, upper = model.predict_interval(X, alpha=0.05).T
>>> bool(np.mean((y >= lower) & (y <= upper)) > 0.85)
True
Methods
predict_dist
(self, X: np.ndarray) -> Dict[str, np.ndarray]
predict_dist
(self, X: np.ndarray) -> Dict[str, np.ndarray]
Return the predicted distribution parameters.
Parameters
X
Returns
params
{"loc", "scale"} for normal and lognormal (the log-normal's parameters describe :math:`\log y`), {"scale"} for exponential. Each value has shape (n_samples,).
predict_interval
(self, X: np.ndarray, alpha: float=0.05) -> np.ndarray
predict_interval
(self, X: np.ndarray, alpha: float=0.05) -> np.ndarray
Return equal-tailed prediction intervals.
Parameters
X
alpha
alpha=0.05 gives a nominal 95% interval.
Returns
interval
score_samples
(self, X: np.ndarray, y: np.ndarray) -> np.ndarray
score_samples
(self, X: np.ndarray, y: np.ndarray) -> np.ndarray
Return the per-sample value of the fitted scoring rule.
Parameters
X
y
Returns
score
class algorithms.gradient_boosting.ngboost.NGBoostClassifier(_NGBoostBase, Classifier)
NGBoost for categorical targets: boosting on natural-gradient logits.
NGBoostRegressor. The predictive distribution is categorical over K classes, parameterised by K - 1 logits against a reference class, and the boosting stages follow the natural gradient of the log score — the ordinary gradient premultiplied by the inverse Fisher information of the multinomial. Binary problems are the K = 2 case and reduce to a Bernoulli with a single logit.__init__( self, n_estimators: int = 100, learning_rate: float = 0.1, max_depth: int = 3, min_samples_split: int = 2, min_samples_leaf: int = 1, natural_gradient: bool = True, minibatch_frac: float = 1.0, tol: float = 1e-05, random_state: Optional[int] = None, verbose: bool = False, )
Overview
- Initialise every sample with the marginal class log-odds.
- Compute the natural gradient of the negative log-likelihood per sample.
-
Fit one
DecisionTreeRegressorper logit. -
Line-search the stage scaling, shrink by
learning_rate, step.
Theory
With class 0 as reference, p = \mathrm{softmax}(0, \eta) and
The reduced Fisher information is non-singular and its inverse is available in closed form,
so the natural gradient needs no linear solve. For K = 2 this collapses to (p - y) / (p(1 - p)), the Bernoulli case.
Parameters
n_estimators
learning_rate
max_depth
min_samples_split
min_samples_leaf
natural_gradient
minibatch_frac
tol
random_state
verbose
Attributes
classes_
fit().
dist_
init_params_
estimators_
scalings_
train_score_
n_estimators_
n_features_in_
fit().
Notes
Complexity:
- Fitting: O(M (K-1) n p \log n) for M stages.
- Prediction: O(M (K-1) d) per sample.
- When well-behaved class probabilities matter more than raw accuracy.
- When the natural-gradient step's parameterisation invariance is wanted
- Prefer a plain boosted classifier when only the argmax is consumed.
References
See Also
>>> import numpy as np
>>> from tuiml.algorithms.gradient_boosting import NGBoostClassifier
>>> rng = np.random.default_rng(0)
>>> X = rng.normal(size=(200, 3))
>>> y = (X[:, 0] + 0.5 * X[:, 1] > 0).astype(int)
>>> model = NGBoostClassifier(n_estimators=40, random_state=0).fit(X, y)
>>> model.classes_.tolist()
[0, 1]
>>> proba = model.predict_proba(X)
>>> proba.shape
(200, 2)
>>> bool(np.allclose(proba.sum(axis=1), 1.0))
True
>>> float((model.predict(X) == y).mean()) > 0.9
True
Methods
predict_dist
(self, X: np.ndarray) -> Dict[str, np.ndarray]
predict_dist
(self, X: np.ndarray) -> Dict[str, np.ndarray]
Return the predicted distribution parameters.
Parameters
X
Returns
params
{"proba": array of shape (n_samples, n_classes)} — the parameters of the predictive categorical distribution.
predict_interval
(self, X: np.ndarray, alpha: float=0.05) -> np.ndarray
predict_interval
(self, X: np.ndarray, alpha: float=0.05) -> np.ndarray
Return the categorical analogue of a prediction interval.
Parameters
X
alpha
Returns
mask
mask[i, k] is True when class classes_[k] belongs to the credible set of sample i. Every row contains at least the most probable class.