multiclass_metric.hpp 5.67 KB
Newer Older
1
2
3
4
/*!
 * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
5
6
7
8
#ifndef LIGHTGBM_METRIC_MULTICLASS_METRIC_HPP_
#define LIGHTGBM_METRIC_MULTICLASS_METRIC_HPP_

#include <LightGBM/metric.h>
Guolin Ke's avatar
Guolin Ke committed
9
10
#include <LightGBM/utils/log.h>

11
#include <string>
12
#include <cmath>
13
#include <vector>
14
15
16
17
18
19
20
21

namespace LightGBM {
/*!
* \brief Metric for multiclass task.
* Use static class "PointWiseLossCalculator" to calculate loss point-wise
*/
template<typename PointWiseLossCalculator>
class MulticlassMetric: public Metric {
Nikita Titov's avatar
Nikita Titov committed
22
 public:
Guolin Ke's avatar
Guolin Ke committed
23
  explicit MulticlassMetric(const Config& config) {
Guolin Ke's avatar
Guolin Ke committed
24
    num_class_ = config.num_class;
25
26
27
28
29
  }

  virtual ~MulticlassMetric() {
  }

Guolin Ke's avatar
Guolin Ke committed
30
31
  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back(PointWiseLossCalculator::Name());
32
33
34
35
36
37
    num_data_ = num_data;
    // get label
    label_ = metadata.label();
    // get weights
    weights_ = metadata.weights();
    if (weights_ == nullptr) {
38
      sum_weights_ = static_cast<double>(num_data_);
39
40
41
42
43
44
45
    } else {
      sum_weights_ = 0.0f;
      for (data_size_t i = 0; i < num_data_; ++i) {
        sum_weights_ += weights_[i];
      }
    }
  }
46

Guolin Ke's avatar
Guolin Ke committed
47
  const std::vector<std::string>& GetName() const override {
48
    return name_;
49
50
  }

51
  double factor_to_bigger_better() const override {
52
    return -1.0f;
53
  }
54

Guolin Ke's avatar
Guolin Ke committed
55
  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
56
    double sum_loss = 0.0;
Guolin Ke's avatar
Guolin Ke committed
57
58
59
    int num_tree_per_iteration = num_class_;
    int num_pred_per_row = num_class_;
    if (objective != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
60
      num_tree_per_iteration = objective->NumModelPerIteration();
Guolin Ke's avatar
Guolin Ke committed
61
62
      num_pred_per_row = objective->NumPredictOneRow();
    }
63
64
65
66
    if (objective != nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
Guolin Ke's avatar
Guolin Ke committed
67
          std::vector<double> raw_score(num_tree_per_iteration);
68
69
          for (int k = 0; k < num_tree_per_iteration; ++k) {
            size_t idx = static_cast<size_t>(num_data_) * k + i;
Guolin Ke's avatar
Guolin Ke committed
70
            raw_score[k] = static_cast<double>(score[idx]);
71
          }
Guolin Ke's avatar
Guolin Ke committed
72
73
          std::vector<double> rec(num_pred_per_row);
          objective->ConvertOutput(raw_score.data(), rec.data());
74
75
76
77
78
79
          // add loss
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], rec);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
Guolin Ke's avatar
Guolin Ke committed
80
          std::vector<double> raw_score(num_tree_per_iteration);
81
82
          for (int k = 0; k < num_tree_per_iteration; ++k) {
            size_t idx = static_cast<size_t>(num_data_) * k + i;
Guolin Ke's avatar
Guolin Ke committed
83
            raw_score[k] = static_cast<double>(score[idx]);
84
          }
Guolin Ke's avatar
Guolin Ke committed
85
86
          std::vector<double> rec(num_pred_per_row);
          objective->ConvertOutput(raw_score.data(), rec.data());
87
88
          // add loss
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], rec) * weights_[i];
89
90
91
        }
      }
    } else {
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          std::vector<double> rec(num_tree_per_iteration);
          for (int k = 0; k < num_tree_per_iteration; ++k) {
            size_t idx = static_cast<size_t>(num_data_) * k + i;
            rec[k] = static_cast<double>(score[idx]);
          }
          // add loss
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], rec);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          std::vector<double> rec(num_tree_per_iteration);
          for (int k = 0; k < num_tree_per_iteration; ++k) {
            size_t idx = static_cast<size_t>(num_data_) * k + i;
            rec[k] = static_cast<double>(score[idx]);
          }
          // add loss
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], rec) * weights_[i];
113
114
115
        }
      }
    }
116
117
    double loss = sum_loss / sum_weights_;
    return std::vector<double>(1, loss);
118
119
  }

Nikita Titov's avatar
Nikita Titov committed
120
 private:
121
122
123
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
124
  const label_t* label_;
125
  /*! \brief Pointer of weighs */
126
  const label_t* weights_;
127
  /*! \brief Sum weights */
128
  double sum_weights_;
129
  /*! \brief Name of this test set */
130
  std::vector<std::string> name_;
Guolin Ke's avatar
Guolin Ke committed
131
  int num_class_;
132
133
134
135
};

/*! \brief L2 loss for multiclass task */
class MultiErrorMetric: public MulticlassMetric<MultiErrorMetric> {
Nikita Titov's avatar
Nikita Titov committed
136
 public:
Guolin Ke's avatar
Guolin Ke committed
137
  explicit MultiErrorMetric(const Config& config) :MulticlassMetric<MultiErrorMetric>(config) {}
138

139
  inline static double LossOnPoint(label_t label, std::vector<double>& score) {
140
    size_t k = static_cast<size_t>(label);
141
142
143
144
    for (size_t i = 0; i < score.size(); ++i) {
      if (i != k && score[i] >= score[k]) {
        return 1.0f;
      }
145
    }
Guolin Ke's avatar
Guolin Ke committed
146
    return 0.0f;
147
148
149
  }

  inline static const char* Name() {
150
    return "multi_error";
151
152
153
154
  }
};

/*! \brief Logloss for multiclass task */
Guolin Ke's avatar
Guolin Ke committed
155
class MultiSoftmaxLoglossMetric: public MulticlassMetric<MultiSoftmaxLoglossMetric> {
Nikita Titov's avatar
Nikita Titov committed
156
 public:
Guolin Ke's avatar
Guolin Ke committed
157
  explicit MultiSoftmaxLoglossMetric(const Config& config) :MulticlassMetric<MultiSoftmaxLoglossMetric>(config) {}
158

159
  inline static double LossOnPoint(label_t label, std::vector<double>& score) {
160
161
    size_t k = static_cast<size_t>(label);
    if (score[k] > kEpsilon) {
162
      return static_cast<double>(-std::log(score[k]));
163
164
165
166
    } else {
      return -std::log(kEpsilon);
    }
  }
167

168
  inline static const char* Name() {
169
    return "multi_logloss";
170
171
172
173
174
  }
};

}  // namespace LightGBM
#endif   // LightGBM_METRIC_MULTICLASS_METRIC_HPP_