1
$\begingroup$

i am looking for a cost sensitive function that will have weights according to individual row feature (like amount) this way i can penalize more FN which has large amount vs. low dollar amount. took the raw solution from the excellent article : https://maxhalford.github.io/blog/lightgbm-focal-loss/ and incorporated it into the objective function (correctly ?) , but i wonder how should i do the same for the evaluation function.

here is the code:

class FocalLoss_with_amt:

def __init__(self, gamma, alpha=None):
    self.alpha = alpha
    self.gamma = gamma

def at(self, y):
    if self.alpha is None:
        return np.ones_like(y)
    return np.where(y, self.alpha, 1 - self.alpha)

def pt(self, y, p):
    p = np.clip(p, 1e-15, 1 - 1e-15)
    return np.where(y, p, 1 - p)

def __call__(self, y_true, y_pred):
    at = self.at(y_true)
    pt = self.pt(y_true, y_pred)
    return -at * (1 - pt) ** self.gamma * np.log(pt)

def grad(self, y_true, y_pred):
    y =  2 * y_true - 1  # {0, 1} -> {-1, 1}
    at = self.at(y_true)
    pt = self.pt(y_true, y_pred)
    g = self.gamma
    return at * y * (1 - pt) ** g * (g * pt * np.log(pt) + pt - 1)

def hess(self, y_true, y_pred):
    y =  2 * y_true - 1  # {0, 1} -> {-1, 1}
    at = self.at(y_true)
    pt = self.pt(y_true, y_pred)
    g = self.gamma

    u = at * y * (1 - pt) ** g
    du = -at * y * g * (1 - pt) ** (g - 1)
    v = g * pt * np.log(pt) + pt - 1
    dv = g * np.log(pt) + g + 1

    return (du * v + u * dv) * y * (pt * (1 - pt))

def init_score(self, y_true):
    res = optimize.minimize_scalar(
        lambda p: self(y_true, p).sum(),
        bounds=(0, 1),
        method='bounded'
    )
    p = res.x
    log_odds = np.log(p / (1 - p))
    return log_odds

def lgb_obj(self, preds, train_data):
    amt = train_data.get_weight()
    y = train_data.get_label()
    p = special.expit(preds)
    return amt*self.grad(y, p), amt*self.hess(y, p)

def lgb_eval(self, preds, train_data):
    amt = train_data.get_weight()
    y = train_data.get_label()
    p = special.expit(preds)
    is_higher_better = False
    return 'focal_loss', self(y, p).mean(), is_higher_better

fl_amt = FocalLoss_with_amt(alpha=0.27242, gamma=2.478478446)

d_train.set_weight(weight=np.log(X_train['amount']))
d_val.set_weight(weight=np.log(X_val['amount']))


model_amt=lgb.train(best, d_train, 10000, valid_sets=[d_val], fobj=fl_amt.lgb_obj,
                   feval=fl_amt.lgb_eval,early_stopping_rounds=40, verbose_eval=500)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.