Medial Code Documentation
Loading...
Searching...
No Matches
binary_objective.hpp
1#ifndef LIGHTGBM_OBJECTIVE_BINARY_OBJECTIVE_HPP_
2#define LIGHTGBM_OBJECTIVE_BINARY_OBJECTIVE_HPP_
3
4#include <LightGBM/objective_function.h>
5
6#include <cstring>
7#include <cmath>
8
9namespace LightGBM {
14public:
15 explicit BinaryLogloss(const Config& config, std::function<bool(label_t)> is_pos = nullptr) {
16 sigmoid_ = static_cast<double>(config.sigmoid);
17 if (sigmoid_ <= 0.0) {
18 Log::Fatal("Sigmoid parameter %f should be greater than zero", sigmoid_);
19 }
20 is_unbalance_ = config.is_unbalance;
21 scale_pos_weight_ = static_cast<double>(config.scale_pos_weight);
22 if (is_unbalance_ && std::fabs(scale_pos_weight_ - 1.0f) > 1e-6) {
23 Log::Fatal("Cannot set is_unbalance and scale_pos_weight at the same time");
24 }
25 is_pos_ = is_pos;
26 if (is_pos_ == nullptr) {
27 is_pos_ = [](label_t label) {return label > 0; };
28 }
29 }
30
31 explicit BinaryLogloss(const std::vector<std::string>& strs) {
32 sigmoid_ = -1;
33 for (auto str : strs) {
34 auto tokens = Common::Split(str.c_str(), ':');
35 if (tokens.size() == 2) {
36 if (tokens[0] == std::string("sigmoid")) {
37 Common::Atof(tokens[1].c_str(), &sigmoid_);
38 }
39 }
40 }
41 if (sigmoid_ <= 0.0) {
42 Log::Fatal("Sigmoid parameter %f should be greater than zero", sigmoid_);
43 }
44 }
45
47
48 void Init(const Metadata& metadata, data_size_t num_data) override {
49 num_data_ = num_data;
50 label_ = metadata.label();
51 weights_ = metadata.weights();
52 data_size_t cnt_positive = 0;
53 data_size_t cnt_negative = 0;
54 // REMOVEME: remove the warning after 2.4 version release
55 Log::Warning("Starting from the 2.1.2 version, default value for "
56 "the \"boost_from_average\" parameter in \"binary\" objective is true.\n"
57 "This may cause significantly different results comparing to the previous versions of LightGBM.\n"
58 "Try to set boost_from_average=false, if your old models produce bad results");
59 // count for positive and negative samples
60 #pragma omp parallel for schedule(static) reduction(+:cnt_positive, cnt_negative)
61 for (data_size_t i = 0; i < num_data_; ++i) {
62 if (is_pos_(label_[i])) {
63 ++cnt_positive;
64 } else {
65 ++cnt_negative;
66 }
67 }
68 need_train_ = true;
69 if (cnt_negative == 0 || cnt_positive == 0) {
70 Log::Warning("Contains only one class");
71 // not need to boost.
72 need_train_ = false;
73 }
74 Log::Info("Number of positive: %d, number of negative: %d", cnt_positive, cnt_negative);
75 // use -1 for negative class, and 1 for positive class
76 label_val_[0] = -1;
77 label_val_[1] = 1;
78 // weight for label
79 label_weights_[0] = 1.0f;
80 label_weights_[1] = 1.0f;
81 // if using unbalance, change the labels weight
82 if (is_unbalance_ && cnt_positive > 0 && cnt_negative > 0) {
83 if (cnt_positive > cnt_negative) {
84 label_weights_[1] = 1.0f;
85 label_weights_[0] = static_cast<double>(cnt_positive) / cnt_negative;
86 } else {
87 label_weights_[1] = static_cast<double>(cnt_negative) / cnt_positive;
88 label_weights_[0] = 1.0f;
89 }
90 }
91 label_weights_[1] *= scale_pos_weight_;
92 }
93
94 void GetGradients(const double* score, score_t* gradients, score_t* hessians) const override {
95 if (!need_train_) {
96 return;
97 }
98 if (weights_ == nullptr) {
99 #pragma omp parallel for schedule(static)
100 for (data_size_t i = 0; i < num_data_; ++i) {
101 // get label and label weights
102 const int is_pos = is_pos_(label_[i]);
103 const int label = label_val_[is_pos];
104 const double label_weight = label_weights_[is_pos];
105 // calculate gradients and hessians
106 const double response = -label * sigmoid_ / (1.0f + std::exp(label * sigmoid_ * score[i]));
107 const double abs_response = fabs(response);
108 gradients[i] = static_cast<score_t>(response * label_weight);
109 hessians[i] = static_cast<score_t>(abs_response * (sigmoid_ - abs_response) * label_weight);
110 }
111 } else {
112 #pragma omp parallel for schedule(static)
113 for (data_size_t i = 0; i < num_data_; ++i) {
114 // get label and label weights
115 const int is_pos = is_pos_(label_[i]);
116 const int label = label_val_[is_pos];
117 const double label_weight = label_weights_[is_pos];
118 // calculate gradients and hessians
119 const double response = -label * sigmoid_ / (1.0f + std::exp(label * sigmoid_ * score[i]));
120 const double abs_response = fabs(response);
121 gradients[i] = static_cast<score_t>(response * label_weight * weights_[i]);
122 hessians[i] = static_cast<score_t>(abs_response * (sigmoid_ - abs_response) * label_weight * weights_[i]);
123 }
124 }
125 }
126
127 // implement custom average to boost from (if enabled among options)
128 double BoostFromScore(int) const override {
129 double suml = 0.0f;
130 double sumw = 0.0f;
131 if (weights_ != nullptr) {
132 #pragma omp parallel for schedule(static) reduction(+:suml, sumw)
133 for (data_size_t i = 0; i < num_data_; ++i) {
134 suml += is_pos_(label_[i]) * weights_[i];
135 sumw += weights_[i];
136 }
137 } else {
138 sumw = static_cast<double>(num_data_);
139 #pragma omp parallel for schedule(static) reduction(+:suml)
140 for (data_size_t i = 0; i < num_data_; ++i) {
141 suml += is_pos_(label_[i]);
142 }
143 }
144 double pavg = suml / sumw;
145 pavg = std::min(pavg, 1.0 - kEpsilon);
146 pavg = std::max<double>(pavg, kEpsilon);
147 double initscore = std::log(pavg / (1.0f - pavg)) / sigmoid_;
148 Log::Info("[%s:%s]: pavg=%f -> initscore=%f", GetName(), __func__, pavg, initscore);
149 return initscore;
150 }
151
152 bool ClassNeedTrain(int /*class_id*/) const override {
153 return need_train_;
154 }
155
156 const char* GetName() const override {
157 return "binary";
158 }
159
160 void ConvertOutput(const double* input, double* output) const override {
161 output[0] = 1.0f / (1.0f + std::exp(-sigmoid_ * input[0]));
162 }
163
164 std::string ToString() const override {
165 std::stringstream str_buf;
166 str_buf << GetName() << " ";
167 str_buf << "sigmoid:" << sigmoid_;
168 return str_buf.str();
169 }
170
171 bool SkipEmptyClass() const override { return true; }
172
173 bool NeedAccuratePrediction() const override { return false; }
174
175private:
177 data_size_t num_data_;
179 const label_t* label_;
181 bool is_unbalance_;
183 double sigmoid_;
185 int label_val_[2];
187 double label_weights_[2];
189 const label_t* weights_;
190 double scale_pos_weight_;
191 std::function<bool(label_t)> is_pos_;
192 bool need_train_;
193};
194
195} // namespace LightGBM
196#endif // LightGBM_OBJECTIVE_BINARY_OBJECTIVE_HPP_
Objective function for binary classification.
Definition binary_objective.hpp:13
void GetGradients(const double *score, score_t *gradients, score_t *hessians) const override
calculating first order derivative of loss function
Definition binary_objective.hpp:94
bool NeedAccuratePrediction() const override
The prediction should be accurate or not. True will disable early stopping for prediction.
Definition binary_objective.hpp:173
void Init(const Metadata &metadata, data_size_t num_data) override
Initialize.
Definition binary_objective.hpp:48
This class is used to store some meta(non-feature) data for training data, e.g. labels,...
Definition dataset.h:36
const label_t * label() const
Get pointer of label.
Definition dataset.h:113
const label_t * weights() const
Get weights, if not exists, will return nullptr.
Definition dataset.h:146
The interface of Objective Function.
Definition objective_function.h:13
desc and descl2 fields must be written in reStructuredText format
Definition application.h:10
float score_t
Type of score, and gradients.
Definition meta.h:26
float label_t
Type of metadata, include weight and label.
Definition meta.h:33
int32_t data_size_t
Type of data size, it is better to use signed type.
Definition meta.h:14
Definition config.h:27