Reproduire l'objectif LightGBM avec python

L'une des fonctionnalités de LightGBM, une bibliothèque populaire de kaggle, est qu'elle s'optimise bien en donnant le gradient de la fonction objectif que vous souhaitez minimiser. Choisir correctement cette fonction d'objectif est l'une des astuces pour faire un bon modèle, et par défaut plusieurs objectifs est implémenté. Cependant, LightGBM vous permet de passer une fonction pour calculer le dégradé en python. Dans cet article, je voudrais présenter le type d'implémentation effectué en créant un objectif équivalent avec python tout en faisant référence à l'implémentation officielle de LightGBM.

Conditions préalables

Dans cet article, on suppose que lgb.Dataset est passé à lgb.train pour l'entraînement à la régression / classification à deux classes. Lors de la création d'un objectif de classification multi-classes, [l'article de M. Tawara (effectuer une régression multi-tâches (ha ???) de force avec LightGBM)](https://tawara.hatenablog.com/entry/2020/05/ 14/120016) Je pense que c'est facile à comprendre si vous lisez. De plus, je ne sais pas pourquoi je veux un diplôme et un hess, alors veuillez vous référer à d'autres documents à ce sujet.

Imitez le comportement officiel

Quand objectif = "l2"

Commencera le sujet principal. Puisque la partie principale de lightGBM est implémentée en C ++ pour accélérer, la partie Objective est également écrite en C ++. Lisez le code (https://github.com/microsoft/LightGBM/blob/master/src/objective/regression_objective.hpp) pour le comportement lorsque objective = "l2". La partie qui calcule le dégradé est implémentée dans GetGradients ().

cpp:github.com/microsoft/LightGBM/blob/master/src/objective/regression_objective.hpp


  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>(score[i] - label_[i]);
        hessians[i] = 1.0f;
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>((score[i] - label_[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(weights_[i]);
      }
    }
  }

Ce n'est pas pratique car ce sera lent, mais si vous reproduisez cela avec python, ce sera comme ça.

def l2_loss(pred, data):
    true = data.get_label()
    grad = pred - true
    hess = np.ones(len(grad))   
    return grad, hess

Quand objectif = "poisson"

Cet objectif minimise la perte de poisson. Quand je lis la métrique sur la perte de poisson, c'est comme suit.

cpp:github.com/microsoft/LightGBM/blob/master/src/metric/regression_metric.hpp


class PoissonMetric: public RegressionMetric<PoissonMetric> {
 public:
  explicit PoissonMetric(const Config& config) :RegressionMetric<PoissonMetric>(config) {
  }

  inline static double LossOnPoint(label_t label, double score, const Config&) {
    const double eps = 1e-10f;
    if (score < eps) {
      score = eps;
    }
    return score - label * std::log(score);
  }
  inline static const char* Name() {
    return "poisson";
  }
};

Et quand je lis objectif, il a la mise en œuvre suivante.

cpp:github.com/microsoft/LightGBM/blob/master/src/objective/regression_objective.hpp


  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>(std::exp(score[i]) - label_[i]);
        hessians[i] = static_cast<score_t>(std::exp(score[i] + max_delta_step_));
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>((std::exp(score[i]) - label_[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(std::exp(score[i] + max_delta_step_) * weights_[i]);
      }
    }
  }

... as-tu remarqué? En fait, le score de cet objectif n'est pas la valeur prédite telle quelle, mais la valeur de la partie exposante x de e lorsqu'elle est exprimée par score = e ^ x. Essayez d'entrer la formule dans Wolfram Alpha Tu comprendras. Par conséquent, lors de la création d'un objectif de type poisson (d'autres comme gamma et tweedie) avec objectif, la métrique doit également être calculée avec la valeur prédite = e ^ (pred).

def poisson_metric(pred, data):
    true = data.get_label()
    loss = np.exp(pred) - true*pred
    return "poisson", np.mean(loss), False

def poisson_object(pred, data):
    poisson_max_delta_step = 0.7
    true = data.get_label()
    grad = np.exp(pred) - true
    hess = exp(pred + poisson_max_delta_step)
    return grad, hess

Quand objectif = "binaire"

J'aimerais voir l'objectif au moment du classement de Niclas en poussant mal. La métrique au moment du binaire est la suivante.

cpp:github.com/microsoft/LightGBM/blob/master/src/metric/binary_metric.hpp


class BinaryLoglossMetric: public BinaryMetric<BinaryLoglossMetric> {
 public:
  explicit BinaryLoglossMetric(const Config& config) :BinaryMetric<BinaryLoglossMetric>(config) {}

  inline static double LossOnPoint(label_t label, double prob) {
    if (label <= 0) {
      if (1.0f - prob > kEpsilon) {
        return -std::log(1.0f - prob);
      }
    } else {
      if (prob > kEpsilon) {
        return -std::log(prob);
      }
    }
    return -std::log(kEpsilon);
  }

  inline static const char* Name() {
    return "binary_logloss";
  }
};

Notez que l'objectif est sigmoid = 1, label_val = [-1, 1], label_weights = [1, 1] lorsque is_unbalance = False.

cpp:github.com/microsoft/LightGBM/blob/master/src/objective/binary_objective.hpp


  void GetGradients(const double* score, score_t* gradients, score_t* hessians) const override {
    if (!need_train_) {
      return;
    }
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        // get label and label weights
        const int is_pos = is_pos_(label_[i]);
        const int label = label_val_[is_pos];
        const double label_weight = label_weights_[is_pos];
        // calculate gradients and hessians
        const double response = -label * sigmoid_ / (1.0f + std::exp(label * sigmoid_ * score[i]));
        const double abs_response = fabs(response);
        gradients[i] = static_cast<score_t>(response * label_weight);
        hessians[i] = static_cast<score_t>(abs_response * (sigmoid_ - abs_response) * label_weight);
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        // get label and label weights
        const int is_pos = is_pos_(label_[i]);
        const int label = label_val_[is_pos];
        const double label_weight = label_weights_[is_pos];
        // calculate gradients and hessians
        const double response = -label * sigmoid_ / (1.0f + std::exp(label * sigmoid_ * score[i]));
        const double abs_response = fabs(response);
        gradients[i] = static_cast<score_t>(response * label_weight  * weights_[i]);
        hessians[i] = static_cast<score_t>(abs_response * (sigmoid_ - abs_response) * label_weight * weights_[i]);
      }
    }
  }

Comme dans le cas de poisson, le score est prédit valeur = sigmoïde (score), donc le gradient est comme ça. Vérification avec Wolfram Alpha comme avant [when label = 0](https://ja.wolframalpha.com/input/?i=d%2Fdx+log%281+-+%281%2F%281+%2B + e% 5E% 28-x% 29% 29% 29% 29), quand étiquette = 1 % 2F% 281 +% 2B + e% 5E% 28-x% 29% 29% 29% 29), donc si vous écrivez objectif en python, ce sera comme suit.

def binary_metric(pred, data):
    true = data.get_label()
    loss = -(true * np.log(1/(1+np.exp(-pred))) + (1 - true) * np.log(1 - 1/(1+np.exp(-pred))))
    return "binary", np.mean(loss), False

def binary_objective(pred, data):
    true = data.get_label()
    label = 2*true - 1
    response = -label / (1 + np.exp(label * pred))
    abs_response = np.abs(response)
    grad = response
    hess = abs_response * (1 - abs_response)
    return grad, hess

en conclusion

Cette fois, j'ai reproduit l'implémentation officielle de lightGBM avec python. Comprendre les bases introduites cette fois facilitera la création de votre propre objectif personnalisé. Je voudrais vous présenter l'objectif que j'ai mis en œuvre dans le concours dans un autre article, alors n'hésitez pas à me contacter.

Recommended Posts

Reproduire l'objectif LightGBM avec python
FizzBuzz en Python3
Essayez de reproduire un film couleur avec Python
Grattage avec Python
Statistiques avec python
Grattage avec Python
Python avec Go
Twilio avec Python
Intégrer avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Bingo avec python
Zundokokiyoshi avec python
Excel avec Python
Micro-ordinateur avec Python
Cast avec python
Communication série avec Python
Zip, décompressez avec python
Django 1.11 a démarré avec Python3.6
Jugement des nombres premiers avec Python
Communication de socket avec Python
Analyse de données avec python 2
Essayez de gratter avec Python.
Apprendre Python avec ChemTHEATER 03
Recherche séquentielle avec Python
"Orienté objet" appris avec python
Exécutez Python avec VBA
Manipuler yaml avec python
Résolvez AtCoder 167 avec python
Communication série avec python
[Python] Utiliser JSON avec Python
Apprendre Python avec ChemTHEATER 05-1
Apprenez Python avec ChemTHEATER
Exécutez prepDE.py avec python3
1.1 Premiers pas avec Python
Collecter des tweets avec Python
Binarisation avec OpenCV / Python
3. 3. Programmation IA avec Python
Méthode Kernel avec Python
Non bloquant avec Python + uWSGI
Grattage avec Python + PhantomJS
Publier des tweets avec python
Conduisez WebDriver avec python
Utiliser mecab avec Python 3
[Python] Redirection avec CGIHTTPServer
Analyse vocale par python
Pensez à yaml avec python
Utiliser Kinesis avec Python
Premiers pas avec Python
Utiliser DynamoDB avec Python
Getter Zundko avec python
Gérez Excel avec python
Loi d'Ohm avec Python
Jugement des nombres premiers avec python
Exécutez Blender avec python
Résoudre des maths avec Python
Python à partir de Windows 7
Carte thermique par Python + matplotlib