Reproduzieren Sie LightGBM Objective mit Python

Eine der Funktionen von LightGBM, einer beliebten Bibliothek in Kaggle, ist die gute Optimierung, indem der Gradient der Zielfunktion angegeben wird, den Sie minimieren möchten. Die richtige Auswahl dieser Zielfunktion ist einer der Tricks, um ein gutes Modell zu erstellen, und standardmäßig ist viele Ziele implementiert. Mit LightGBM können Sie jedoch eine Funktion übergeben, um den Gradienten in Python zu berechnen. In diesem Artikel möchte ich vorstellen, welche Art der Implementierung durchgeführt wird, indem ein gleichwertiges Ziel mit Python erstellt wird, während auf die offizielle Implementierung von LightGBM verwiesen wird.

Voraussetzungen

In diesem Artikel wird davon ausgegangen, dass lgb.Dataset für das Training in Regression / Zwei-Klassen-Klassifizierung an lgb.train übergeben wird. Bei der Erstellung eines Ziels für die Klassifizierung mehrerer Klassen [Mr. Tawaras Artikel (zwangsweise Multi-Task-Regression (ha ???) mit LightGBM durchführen)](https://tawara.hatenablog.com/entry/2020/05/ 14/120016) Ich denke, es ist leicht zu verstehen, wenn Sie herumlesen. Ich bin mir auch nicht sicher, warum ich grad und hess will, also verweise bitte auf andere Materialien dazu.

Imitieren Sie das offizielle Verhalten

Wenn Ziel = "l2"

Beginnt das Hauptthema. Da der Kernteil von lightGBM zur Beschleunigung in C ++ implementiert ist, ist der Zielteil auch in C ++ geschrieben. Lesen Sie den Code (https://github.com/microsoft/LightGBM/blob/master/src/objective/regression_objective.hpp) für das Verhalten bei Objective = "l2". Der Teil, der den Gradienten berechnet, ist in GetGradients () implementiert.

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]);
      }
    }
  }

Es ist nicht praktisch, weil es langsam sein wird, aber wenn Sie dies mit Python reproduzieren, wird es so sein.

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

Wenn objektiv = "Poisson"

Dieses Ziel minimiert den Poissonverlust. Wenn ich die Metrik über Poisson-Verlust lese, ist sie wie folgt.

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";
  }
};

Und wenn ich objektiv lese, hat es die folgende Implementierung.

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]);
      }
    }
  }

... Hast du bemerkt? Tatsächlich ist die Punktzahl dieses Ziels nicht der vorhergesagte Wert, wie er ist, sondern der Wert des Exponententeils x von e, wenn er durch Punktzahl = e ^ x dargestellt wird, ist enthalten. Versuchen Sie, die Formel in Wolfram Alpha einzugeben Du wirst verstehen. Daher muss bei der Erstellung eines Ziels vom Poisson-Typ (andere wie Gamma und Tweedie) mit Ziel auch die Metrik mit dem vorhergesagten Wert = e ^ (pred) berechnet werden.

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

Wenn objektiv = "binär"

Ich würde das Ziel zum Zeitpunkt der Niclas-Klassifizierung gerne sehen, indem ich es schlecht treibe. Die Metrik zum Zeitpunkt der Binärdatei ist wie folgt.

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";
  }
};

Beachten Sie, dass das Ziel sigmoid = 1, label_val = [-1, 1], label_weights = [1, 1] ist, wenn 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]);
      }
    }
  }

Wie im Fall von Poisson wird die Punktzahl als Wert = Sigmoid (Punktzahl) vorhergesagt, so dass der Gradient wie folgt ist. Überprüfen mit Wolfram Alpha wie zuvor [wenn label = 0](https://ja.wolframalpha.com/input/?i=d%2Fdx+log%281+-+%281%2F%281+%2B + e% 5E% 28-x% 29% 29% 29% 29), wenn label = 1 % 2F% 281 +% 2B + e% 5E% 28-x% 29% 29% 29% 29) Wenn Sie also ein Ziel in Python schreiben, ist dies wie folgt.

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

abschließend

Dieses Mal habe ich die offizielle Implementierung von lightGBM mit Python reproduziert. Wenn Sie die diesmal eingeführten Grundlagen verstehen, können Sie leichter Ihr eigenes benutzerdefiniertes Ziel erstellen. Ich möchte das Ziel, das ich im Wettbewerb umgesetzt habe, in einem anderen Artikel vorstellen. Bitte zögern Sie nicht, mich zu kontaktieren.

Recommended Posts

Reproduzieren Sie LightGBM Objective mit Python
FizzBuzz in Python3
Versuchen Sie, Farbfilme mit Python zu reproduzieren
Scraping mit Python
Statistik mit Python
Scraping mit Python
Python mit Go
Twilio mit Python
In Python integrieren
Spielen Sie mit 2016-Python
AES256 mit Python
Getestet mit Python
Python beginnt mit ()
mit Syntax (Python)
Bingo mit Python
Zundokokiyoshi mit Python
Excel mit Python
Mikrocomputer mit Python
Mit Python besetzen
Serielle Kommunikation mit Python
Zip, entpacken mit Python
Django 1.11 wurde mit Python3.6 gestartet
Primzahlbeurteilung mit Python
Socket-Kommunikation mit Python
Datenanalyse mit Python 2
Versuchen Sie es mit Python.
Python lernen mit ChemTHEATER 03
Sequentielle Suche mit Python
"Objektorientiert" mit Python gelernt
Führen Sie Python mit VBA aus
Umgang mit Yaml mit Python
Löse AtCoder 167 mit Python
Serielle Kommunikation mit Python
[Python] Verwenden Sie JSON mit Python
Python lernen mit ChemTHEATER 05-1
Lerne Python mit ChemTHEATER
Führen Sie prepDE.py mit python3 aus
1.1 Erste Schritte mit Python
Tweets mit Python sammeln
Binarisierung mit OpenCV / Python
3. 3. KI-Programmierung mit Python
Kernel-Methode mit Python
Nicht blockierend mit Python + uWSGI
Scraping mit Python + PhantomJS
Tweets mit Python posten
Fahren Sie WebDriver mit Python
Verwenden Sie Mecab mit Python 3
[Python] Mit CGIHTTPServer umleiten
Sprachanalyse mit Python
Denken Sie an Yaml mit Python
Kinesis mit Python betreiben
Erste Schritte mit Python
Verwenden Sie DynamoDB mit Python
Zundko Getter mit Python
Behandle Excel mit Python
Ohmsches Gesetz mit Python
Primzahlbeurteilung mit Python
Führen Sie Blender mit Python aus
Löse Mathe mit Python
Python ab Windows 7
Heatmap von Python + matplotlib