Evaluator.h 14.0 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
Z
zhangjinchao01 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#pragma once

Y
Yu Yang 已提交
17
#include <fstream>
Z
zhangjinchao01 已提交
18 19
#include "ModelConfig.pb.h"
#include "paddle/parameter/Argument.h"
Y
Yu Yang 已提交
20 21
#include "paddle/pserver/ParameterClient2.h"
#include "paddle/utils/ClassRegistrar.h"
Y
Stash  
Yu Yang 已提交
22
#include "paddle/utils/Error.h"
Z
zhangjinchao01 已提交
23 24 25 26

namespace paddle {

class NeuralNetwork;
Q
qijun 已提交
27 28 29 30
/**
 * @def REGISTER_EVALUATOR
 * @brief Macro for registering evaluator class
 */
Z
zhangjinchao01 已提交
31 32 33 34 35

#define REGISTER_EVALUATOR(__type_name, __class_name)                \
  static InitFunction __reg_type_##__type_name([]() {                \
    Evaluator::registrar_.registerClass<__class_name>(#__type_name); \
  })
Q
qijun 已提交
36 37 38 39 40 41
/**
 * @brief Base class for Evaluator
 * Evaluating the performance of a model is very important.
 * It indicates how successful the scores(predictions) of a datasets
 * has been by a trained model.
 */
Z
zhangjinchao01 已提交
42 43 44 45 46 47 48 49 50 51 52
class Evaluator {
public:
  static Evaluator* create(const EvaluatorConfig& config);

  Evaluator() : numSamples_(0), totalScore_(0) {}

  virtual ~Evaluator() {}

  virtual void init(const EvaluatorConfig& config) { config_ = config; }

  /**
Q
qijun 已提交
53
   * @brief start to evaluate some data
Z
zhangjinchao01 已提交
54 55 56 57 58 59 60
   */
  virtual void start() {
    numSamples_ = 0;
    totalScore_ = 0;
  }

  /**
Q
qijun 已提交
61
   * @brief Process a batch of data.
Z
zhangjinchao01 已提交
62 63 64 65
   */
  virtual void eval(const NeuralNetwork& nn);

  /**
Q
qijun 已提交
66 67 68 69
   * @brief Process a batch of data.
   * @return the score for the batch if it make sense to sum the score across
   * batches.
   * @note Otherwise evaluator should return 0 and override finish() and
Z
zhangjinchao01 已提交
70 71 72 73 74
   * printStats() to do the right calculation.
   */
  virtual real evalImp(std::vector<Argument>& arguments) = 0;

  /**
Q
qijun 已提交
75
   * @brief Update the number of processed samples
Z
zhangjinchao01 已提交
76 77 78 79 80
   */
  virtual void updateSamplesNum(const std::vector<Argument>& arguments) {
    numSamples_ += arguments[0].getBatchSize();
  }

81
  /// finish() should be called before distributeEval
Z
zhangjinchao01 已提交
82 83 84 85 86 87 88 89 90 91 92 93
  virtual void distributeEval(ParameterClient2* client) {
    LOG(FATAL) << "Not implemeted";
  }

  void mergeResultsOfAllClients(ParameterClient2* client) {
    double data[2] = {totalScore_, numSamples_};
    client->reduce(data, data, 2, FLAGS_trainer_id, 0);
    totalScore_ = data[0];
    numSamples_ = data[1];
  }

  /**
Q
qijun 已提交
94
   * @brief finish the evaluation.
Z
zhangjinchao01 已提交
95 96 97
   */
  virtual void finish() {}

Q
qijun 已提交
98 99 100 101
  /**
   * @brief print the statistics of evaluate result
   * @note finish() should be called before printStats
   */
Y
Yu Yang 已提交
102
  virtual void printStats(std::ostream& os) const {
Z
zhangjinchao01 已提交
103 104 105 106 107
    os << config_.name() << "="
       << (numSamples_ ? totalScore_ / numSamples_ : 0);
  }

  friend std::ostream& operator<<(std::ostream& os,
Y
Yu Yang 已提交
108
                                  const Evaluator& evaluator) {
Z
zhangjinchao01 已提交
109 110 111 112
    evaluator.printStats(os);
    return os;
  }

113
  friend std::ostream&& operator<<(std::ostream&& os,  // NOLINT
Y
Yu Yang 已提交
114
                                   const Evaluator& evaluator) {
Z
zhangjinchao01 已提交
115 116 117 118 119 120
    evaluator.printStats(os);
    return std::move(os);
  }

  static ClassRegistrar<Evaluator> registrar_;

Y
Yu Yang 已提交
121 122 123 124 125 126 127
  /**
   * @brief getNames will return all field names of current evaluator.
   *
   * The format of name is `evaluator_name.evaluator_fields`. If the evaluator
   * has multiple field, the name could be `evaluator_name.field1`. For example
   * the PrecisionRecallEvaluator contains `precision`, `recall` fields. The get
   * names will return `precision_recall_evaluator.precision`,
128
   * `precision_recall_evaluator.recal`, etc.
Y
Yu Yang 已提交
129 130 131 132 133 134 135
   *
   * Also, if current Evaluator is a combined evaluator. getNames will return
   * all names of all evaluators inside the combined evaluator.
   *
   * @param names [out]: the field names of current evaluator.
   * @note Never clear the names parameter inside getNames.
   */
Y
Stash  
Yu Yang 已提交
136 137 138 139
  virtual void getNames(std::vector<std::string>* names) {
    names->push_back(config_.name());
  }

Y
Yu Yang 已提交
140 141 142 143
  /**
   * @brief getValue will return the current evaluate value of one field.
   *
   * @param name: The field name of current evaluator.
144
   * @param err [out]: The error state.
Y
Yu Yang 已提交
145 146 147
   *
   * @return The evaluate value(metric).
   */
Y
Yu Yang 已提交
148
  virtual real getValue(const std::string& name, Error* err) const {
Y
Yu Yang 已提交
149
    if (name != config_.name()) {
Y
Yu Yang 已提交
150
      *err = Error("no such name of evaluator %s", name.c_str());
Y
Stash  
Yu Yang 已提交
151 152 153 154 155
      return .0f;
    }
    return this->getValueImpl();
  }

Y
Yu Yang 已提交
156 157 158 159 160 161 162 163 164 165 166
  /**
   * @brief getType will return the evaluator type by field name.
   *
   * Evaluate Type is the current type of evaluator in string. Such as 'auc',
   * 'precision_recall'. In combined evaluator, different name may get different
   * evaluate type because it could be evaluated by different evaluator inside.
   *
   * @param name: The field name of current Evaluator.
   * @param err: The error state. nullptr means don't care.
   * @return the evaluator type string.
   */
Y
Yu Yang 已提交
167
  virtual std::string getType(const std::string& name, Error* err) const {
168
    if (name != config_.name()) {
Y
Yu Yang 已提交
169
      *err = Error("no such name of evaluator %s", name.c_str());
Y
Stash  
Yu Yang 已提交
170 171 172 173 174 175
      return std::string();
    }
    return this->getTypeImpl();
  }

protected:
Y
Yu Yang 已提交
176 177 178 179 180 181
  /**
   * @brief getValueImpl The simplest way to define getValue result. If this
   * evaluator doesn't contain multiple fields, and do not throw any error, just
   * implemented this method to get the evaluate result(metric).
   * @return Evaluate result(metric).
   */
Y
Yu Yang 已提交
182 183 184
  virtual real getValueImpl() const {
    return numSamples_ != .0 ? totalScore_ / numSamples_ : .0;
  }
Y
Stash  
Yu Yang 已提交
185

Y
Yu Yang 已提交
186 187 188 189 190 191
  /**
   * @brief getTypeImpl The simplest way to define getType result. If this
   * evaluator doesn't combine many evaluators, the get type should only return
   * itself type.
   * @return Evaluator type.
   */
Y
Stash  
Yu Yang 已提交
192 193
  virtual std::string getTypeImpl() const { return "base"; }

Z
zhangjinchao01 已提交
194 195 196 197 198 199
protected:
  EvaluatorConfig config_;
  double numSamples_;
  double totalScore_;
};

Y
Yu Yang 已提交
200 201 202 203 204
/**
 * @brief The NotGetableEvaluator class is the base class of evaluator that
 * cannot get value in runtime. The most NotGetableEvaluator is Printer
 * Evaluator, which is only used to debug network configuration.
 */
Y
Yu Yang 已提交
205 206 207 208 209 210
class NotGetableEvaluator : public Evaluator {
  // Evaluator interface
public:
  void getNames(std::vector<std::string>* names) {}

  real getValue(const std::string& name, Error* err) const {
211
    *err = Error("Not implemented");
Y
Yu Yang 已提交
212 213
    return .0f;
  }
C
caoying03 已提交
214

Y
Yu Yang 已提交
215
  std::string getType(const std::string& name, Error* err) const {
216
    *err = Error("Not implemented");
Y
Yu Yang 已提交
217 218 219 220
    return "";
  }
};

Z
zhangjinchao01 已提交
221 222 223 224 225 226 227 228 229 230 231
class DummyEvaluator : public Evaluator {
public:
  DummyEvaluator() {}
  virtual void init(const EvaluatorConfig&) {}
  virtual void start() {}
  virtual void eval(const NeuralNetwork&) {}
  virtual real evalImp(std::vector<Argument>& arguments) {
    (void)arguments;
    return -1;
  }
  virtual void finish() {}
Y
Yu Yang 已提交
232
  virtual void printStats(std::ostream&) const {}
Y
Stash  
Yu Yang 已提交
233 234 235 236

  // Evaluator interface
protected:
  std::string getTypeImpl() const;
Z
zhangjinchao01 已提交
237
};
Q
qijun 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
/**
 * @brief evaluate AUC using colIdx-th column as prediction.
 * The AUC(Area Under the Curve) is a common evaluation metric
 * for binary classification problems. It computes the area under
 * the receiver operating characteristic(ROC) curve.
 *
 * @note colIdx-th column
 *
 * - colIdx = 0: the 0-th column.
 * - colIdx > 0: the colIdx-th column.
 * - colIdx < 0: the last colIdx-th column.
 *
 * The config file api is auc_evaluator.
 *
 */
Z
zhangjinchao01 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265
class AucEvaluator : public Evaluator {
public:
  AucEvaluator(int32_t colIdx)
      : colIdx_(colIdx),
        realColumnIdx_(0),
        cpuOutput_(nullptr),
        cpuLabel_(nullptr),
        cpuWeight_(nullptr) {}

  virtual void start();

  virtual real evalImp(std::vector<Argument>& arguments);

Y
Yu Yang 已提交
266
  virtual void printStats(std::ostream& os) const {
Z
zhangjinchao01 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    os << config_.name() << "=" << calcAuc();
  }

  virtual void distributeEval(ParameterClient2* client);

private:
  static const uint32_t kBinNum_ = (1 << 24) - 1;
  static const int kNegativeLabel_ = 0;
  double statPos_[kBinNum_ + 1];
  double statNeg_[kBinNum_ + 1];
  int32_t colIdx_;
  uint32_t realColumnIdx_;
  MatrixPtr cpuOutput_;
  IVectorPtr cpuLabel_;
  MatrixPtr cpuWeight_;

  AucEvaluator() {}

285 286 287
  inline static double trapezoidArea(double X1,
                                     double X2,
                                     double Y1,
Z
zhangjinchao01 已提交
288 289 290 291
                                     double Y2) {
    return (X1 > X2 ? (X1 - X2) : (X2 - X1)) * (Y1 + Y2) / 2.0;
  }

Y
Yu Yang 已提交
292
  double calcAuc() const;
Y
Stash  
Yu Yang 已提交
293 294 295 296 297

  // Evaluator interface
protected:
  real getValueImpl() const;
  std::string getTypeImpl() const;
Z
zhangjinchao01 已提交
298 299 300
};

/**
Q
qijun 已提交
301 302 303 304 305
 * @brief RankAucEvaluator calculates the AUC of each list (i.e., titles
 * under the same query), and averages them. Each list should be organized
 * as a sequence. The inputs of this evaluator is [output, click, pv]. If pv
 * is not provided, it will be set to 1. The types of click and pv are
 * dense value.
Z
zhangjinchao01 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
 */
class RankAucEvaluator : public Evaluator {
public:
  // evaluate ranking AUC
  virtual void start();

  virtual void updateSamplesNum(const std::vector<Argument>& arguments);

  virtual real evalImp(std::vector<Argument>& arguments);

  virtual void distributeEval(ParameterClient2* client) {
    mergeResultsOfAllClients(client);
  }

private:
  MatrixPtr output_;
  MatrixPtr click_;
  MatrixPtr pv_;
  std::vector<std::pair<real, int>> outputPair_;

326 327 328
  double calcRankAuc(real* outputData,
                     real* clickData,
                     real* pvData,
Z
zhangjinchao01 已提交
329
                     size_t size);
Y
Yu Yang 已提交
330 331 332 333

  // Evaluator interface
protected:
  std::string getTypeImpl() const;
Z
zhangjinchao01 已提交
334
};
C
caoying03 已提交
335

Q
qijun 已提交
336 337 338 339 340 341 342 343 344 345
/**
 * @brief precision, recall and f1 score Evaluator
 * \f[
 * precision = \frac{tp}{tp+tn} \\
 * recall=\frac{tp}{tp+fn} \\
 * f1=2*\frac{precsion*recall}{precision+recall}
 * \f]
 *
 * The config file api is precision_recall_evaluator.
 */
Z
zhangjinchao01 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358
class PrecisionRecallEvaluator : public Evaluator {
public:
  // Evaluate precision, recall and F1 score
  PrecisionRecallEvaluator()
      : isMultiBinaryLabel_(false),
        cpuOutput_(nullptr),
        cpuLabel_(nullptr),
        cpuWeight_(nullptr) {}

  virtual void start();

  virtual real evalImp(std::vector<Argument>& arguments);

Y
Yu Yang 已提交
359
  virtual void printStats(std::ostream& os) const;
Z
zhangjinchao01 已提交
360 361 362

  virtual void distributeEval(ParameterClient2* client);

C
caoying03 已提交
363 364 365 366 367 368
  void getNames(std::vector<std::string>* names);

  real getValue(const std::string& name, Error* err) const;

  std::string getType(const std::string& name, Error* err) const;

Z
zhangjinchao01 已提交
369
  struct StatsInfo {
370 371 372 373 374 375 376 377
    /// numbers of true positives
    double TP;
    /// numbers of true negatives
    double TN;
    /// numbers of false positives
    double FP;
    /// numbers of false negatives
    double FN;
Z
zhangjinchao01 已提交
378 379 380 381 382 383 384 385 386 387 388 389

    StatsInfo() : TP(0.0), TN(0.0), FP(0.0), FN(0.0) {}
  };

private:
  bool isMultiBinaryLabel_;
  std::vector<StatsInfo> statsInfo_;

  MatrixPtr cpuOutput_;
  IVectorPtr cpuLabel_;
  MatrixPtr cpuWeight_;

390 391 392 393 394 395 396 397 398 399 400 401 402
  struct PrintStatsInfo {
    double precision;
    double recall;
    double f1;
    double macroAvgPrecision;
    double macroAvgRecall;
    double macroAvgF1Score;
    double microAvgPrecision;
    double microAvgRecall;
    double microAvgF1Score;
  };

  bool getStatsInfo(PrintStatsInfo* info) const;
Y
Yu Yang 已提交
403

404 405
  void calcStatsInfo(const MatrixPtr& output,
                     const IVectorPtr& label,
Z
zhangjinchao01 已提交
406 407
                     const MatrixPtr& weight);

408 409
  void calcStatsInfoMulti(const MatrixPtr& output,
                          const MatrixPtr& label,
Z
zhangjinchao01 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
                          const MatrixPtr& weight);

  inline static double calcPrecision(double TP, double FP) {
    if (TP > 0.0 || FP > 0.0) {
      return TP / (TP + FP);
    } else {
      return 1.0;
    }
  }

  inline static double calcRecall(double TP, double FN) {
    if (TP > 0.0 || FN > 0.0) {
      return TP / (TP + FN);
    } else {
      return 1.0;
    }
  }

  inline static double calcF1Score(double precision, double recall) {
    if (precision > 0.0 || recall > 0.0) {
      return 2 * precision * recall / (precision + recall);
    } else {
      return 0;
    }
  }
Y
Yu Yang 已提交
435 436 437 438

  mutable std::unordered_map<std::string, real> values_;

  void storeLocalValues() const;
Z
zhangjinchao01 已提交
439 440
};

Q
qijun 已提交
441 442 443 444
/*
 * @brief positive-negative pair rate Evaluator
 *
 * The config file api is pnpair_evaluator.
Z
zhangjinchao01 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
 */
class PnpairEvaluator : public Evaluator {
public:
  PnpairEvaluator()
      : cpuOutput_(nullptr),
        cpuLabel_(nullptr),
        cpuInfo_(nullptr),
        cpuWeight_(nullptr) {}

  virtual void start();
  virtual real evalImp(std::vector<Argument>& arguments);

  struct PredictionResult {
    PredictionResult(real __out, int __label, int __queryid, real __weight)
        : out(__out), label(__label), queryid(__queryid), weight(__weight) {}
    real out;
    int label;
    int queryid;
    real weight;
  };
  std::vector<PredictionResult> predictArray_;
  void printPredictResults() {
    std::ofstream fs(FLAGS_predict_file);
    CHECK(fs) << "Fail to open " << FLAGS_predict_file;
    for (auto& res : predictArray_) {
      fs << res.out << " " << res.label << " " << res.queryid << std::endl;
    }
  }

474 475 476 477 478 479
  void stat(size_t start,
            size_t end,
            PredictionResult* answers,
            double& pos,
            double& neg,
            double& spe);
Z
zhangjinchao01 已提交
480 481 482 483
  void calc(std::vector<PredictionResult>& predictArray);

  virtual void finish() { calc(predictArray_); }

Y
Yu Yang 已提交
484
  virtual void printStats(std::ostream& os) const {
Y
Yu Yang 已提交
485
    os << " pos/neg=" << this->getValueImpl();
Z
zhangjinchao01 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
  }

  virtual void distributeEval(ParameterClient2* client) {
    client->reduce(pairArray_, pairArray_, kPairArrayNum_, FLAGS_trainer_id, 0);
    LOG(INFO) << " distribute eval calc total pos pair: " << pairArray_[0]
              << " calc total neg pair: " << pairArray_[1];
  }

private:
  static const uint32_t kPairArrayNum_ = 2;
  double pairArray_[kPairArrayNum_];
  MatrixPtr cpuOutput_;
  IVectorPtr cpuLabel_;
  IVectorPtr cpuInfo_;
  MatrixPtr cpuWeight_;
Y
Yu Yang 已提交
501 502 503 504 505 506 507

  // Evaluator interface
protected:
  real getValueImpl() const {
    return pairArray_[0] / ((pairArray_[1] <= 0) ? 1.0 : pairArray_[1]);
  }
  std::string getTypeImpl() const;
Z
zhangjinchao01 已提交
508 509 510
};

}  // namespace paddle