Проблемы с копированием пользовательских двоичных журналов lightGBM - PullRequest
0 голосов
/ 20 марта 2020

Я хочу реализовать свои собственные функции проверки потерь и обучения с использованием lightGBM. В качестве отправной точки я хотел бы повторить реализацию sklearn. Но результаты моих пользовательских функций не дают одинаковую потерю проверки или одинаковые вероятности предсказания. Я использовал код, найденный в этом ответе: { ссылка }, а также вывел формулы для градиента и гессиана, выражая двоичный логлосс в терминах lo git, как видно из https://stats.stackexchange.com/a/205140 и https://stats.stackexchange.com/questions/157870/scikit-binomial-deviance-loss-function.

Мой код выглядит следующим образом:

import numpy as np
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.datasets import make_classification
from lightgbm import LGBMClassifier

train_test_seed = 42
test_size = 0.20
validation_size = 0.25

X, y = make_classification(n_samples=1000, n_features=10)

# Get training, testing and validation data
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=test_size, random_state=train_test_seed, stratify=y)
Xc_train, Xc_valid, yc_train, yc_valid = train_test_split(
    X_train, y_train, test_size=validation_size, stratify=y_train)


def sigmoid(x):
    # Numerically stable sigmoid
    answer = np.where(x >= 0,
                      1. / (1. + np.exp(-x)),
                      np.exp(x) / (1. + np.exp(x)))
    return answer


def my_eval(labels, preds):
    # validation loss function
    # Convert logits to probabilities
    preds = sigmoid(preds)
    # Compute loss
    logloss = labels*np.log(preds)+(1-labels)*np.log(1-preds)
    loss = -1.*logloss.mean()
    return "error", loss, False


def my_logloss(labels, preds):
    # Training evaluation, calculate gradients and hessians of loss
    # Convert logits to probabilities
    preds = sigmoid(preds)
    # Compute gradients and hessians
    grad = -(labels - preds)
    hess = preds * (1. - preds)
    return grad, hess

# Model with standard training loss
gbm = LGBMClassifier(learning_rate=0.05,
                     num_iterations=100,
                     max_bin=1000,
                     n_estimators=1000,
                     random_state=train_test_seed,
                     early_stopping_rounds=50,
                     scale_pos_weight=2.0,
                     importance_type='gain',
                     num_leaves=150,
                     objective='binary',
                     subsample=0.7,
                     colsample_bytree=0.6, )

# Fit standard model with standard binary logloss
gbm.fit(Xc_train, yc_train,  eval_set=[
       (Xc_valid, yc_valid)], verbose=10, eval_metric='binary_logloss')

# Model with my custom training loss
gbm_my = LGBMClassifier(learning_rate=0.05,
                        num_iterations=100,
                        max_bin=1000,
                        n_estimators=1000,
                        random_state=train_test_seed,
                        early_stopping_rounds=50,
                        scale_pos_weight=2.0,
                        importance_type='gain',
                        num_leaves=150,
                        objective=my_logloss,
                        subsample=0.7,
                        colsample_bytree=0.6, )

# Fit custom model with custom binary logloss
gbm_my.fit(Xc_train, yc_train,  eval_set=[
    (Xc_valid, yc_valid)], verbose=10, eval_metric=my_eval)

# Probabilities for both models
y_prob = gbm.predict_proba(X_test)
y_prob_test = sigmoid(gbm_my.predict_proba(X_test))

print(y_prob[:10])
print(y_prob_test[:10])

# Evaluate on test set
y_pred = gbm.predict(X_test)
# Predict gives the same results as sigmoid(predict_proba) >= 0.5
y_pred_test = gbm_my.predict(X_test)

# Confusion matrix for standard implementation
cm = confusion_matrix(y_test, y_pred)
print(cm)

# Confusion matrix for my custom implementation
cm_test = confusion_matrix(y_test, y_pred_test)
print(cm_test)

Но я все еще не получаю те же результаты от моей пользовательской реализации. Я получаю следующие потери проверки (напечатанные каждые 10 раундов), левое изображение - стандартная модель, а правое изображение - пользовательская модель:

validation loss standard model validation loss custom model

Кроме того, прогнозируемые вероятности для каждого класса различны (первый массив - стандартная модель, второй массив - пользовательская модель):

predicted probabilities

Наконец, это дает две разные матрицы путаницы (первая матрица - стандартная модель, вторая - пользовательская модель):

confusion matrices

Почему мой заказ реализация не дает те же результаты, что и реализация sklearn?

Код, который я реплицировал с Как реализовать пользовательские журналы с идентичным поведением двоичного объекта в LightGBM? имеет комментарий, говорящий " Кстати, log (sigmoid) также следует вычислять другим, численно устойчивым способом, мне просто лень это делать сейчас. См. https://timvieira.github.io/blog/post/2014/02/11/exp-normalize-trick/ ". Но я не нахожу в этом посте ничего о числовом стабильном журнале (сигмоиде), только о численно стабильном софтмаксе и сигмоиде. Я не могу добавить комментарий к вышеупомянутому комментарию, так как мне не хватает репутации.

Заранее благодарю за помощь!

Редактировать : Я попытался запустить LGBMClassifier только с параметром objective, но результаты между двумя моделями все еще различны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...