"""Exponential distribution
"""
from collections import namedtuple
import numpy as np
from scipy.stats import expon
from xgboost_distribution.distributions.base import BaseDistribution
from xgboost_distribution.distributions.utils import check_all_ge_zero, safe_exp
Params = namedtuple("Params", ("scale"))
[docs]class Exponential(BaseDistribution):
"""Exponential distribution with log score
Definition:
f(x) = 1 / scale * e^(-x / scale)
We reparameterize scale -> log(scale) = a to ensure scale >= 0. Gradient:
d/da -log[f(x)] = d/da -log[1/e^a e^(-x / e^a)]
= 1 - x e^-a
= 1 - x / scale
The Fisher information = 1 / scale^2, when reparameterized:
1 / scale^2 = I ( d/d(scale) log(scale) )^2 = I ( 1/ scale )^2
Hence we find: I = 1
"""
@property
def params(self):
return Params._fields
[docs] def check_target(self, y):
check_all_ge_zero(y)
[docs] def gradient_and_hessian(self, y, params, natural_gradient=True):
"""Gradient and diagonal hessian"""
(scale,) = self.predict(params)
grad = np.zeros(shape=(len(y), 1), dtype="float32")
grad[:, 0] = 1 - y / scale
if natural_gradient:
fisher_matrix = np.ones(shape=(len(y), 1, 1), dtype="float32")
grad = np.linalg.solve(fisher_matrix, grad)
hess = np.ones(shape=(len(y), 1), dtype="float32") # constant hessian
else:
hess = -(grad - 1)
return grad, hess
[docs] def loss(self, y, params):
(scale,) = self.predict(params)
return "Exponential-NLL", -expon.logpdf(y, scale=scale)
[docs] def predict(self, params):
scale = safe_exp(params)
return Params(scale=scale)
[docs] def starting_params(self, y):
return Params(scale=np.log(np.mean(y)))