Skip to content

Commit

Permalink
Use double precision in metric calculation. (#7364)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivialfis authored Nov 2, 2021
1 parent 239dbb3 commit 0f7a9b4
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 224 deletions.
5 changes: 2 additions & 3 deletions include/xgboost/metric.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ class Metric : public Configurable {
* the average statistics across all the node,
* this is only supported by some metrics
*/
virtual bst_float Eval(const HostDeviceVector<bst_float>& preds,
const MetaInfo& info,
bool distributed) = 0;
virtual double Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info, bool distributed) = 0;
/*! \return name of metric */
virtual const char* Name() const = 0;
/*! \brief virtual destructor */
Expand Down
1 change: 1 addition & 0 deletions src/learner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,7 @@ class LearnerImpl : public LearnerIO {
this->Configure();

std::ostringstream os;
os.precision(std::numeric_limits<double>::max_digits10);
os << '[' << iter << ']' << std::setiosflags(std::ios::fixed);
if (metrics_.size() == 0 && tparam_.disable_default_eval_metric <= 0) {
auto warn_default_eval_metric = [](const std::string& objective, const std::string& before,
Expand Down
147 changes: 75 additions & 72 deletions src/metric/auc.cc

Large diffs are not rendered by default.

155 changes: 77 additions & 78 deletions src/metric/auc.cu

Large diffs are not rendered by default.

47 changes: 24 additions & 23 deletions src/metric/auc.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,59 +23,60 @@ namespace metric {
/***********
* ROC AUC *
***********/
XGBOOST_DEVICE inline float TrapezoidArea(float x0, float x1, float y0, float y1) {
XGBOOST_DEVICE inline double TrapezoidArea(double x0, double x1, double y0, double y1) {
return std::abs(x0 - x1) * (y0 + y1) * 0.5f;
}

struct DeviceAUCCache;

std::tuple<float, float, float>
std::tuple<double, double, double>
GPUBinaryROCAUC(common::Span<float const> predts, MetaInfo const &info,
int32_t device, std::shared_ptr<DeviceAUCCache> *p_cache);

float GPUMultiClassROCAUC(common::Span<float const> predts,
MetaInfo const &info, int32_t device,
std::shared_ptr<DeviceAUCCache> *cache,
size_t n_classes);
double GPUMultiClassROCAUC(common::Span<float const> predts,
MetaInfo const &info, int32_t device,
std::shared_ptr<DeviceAUCCache> *cache,
size_t n_classes);

std::pair<float, uint32_t>
std::pair<double, uint32_t>
GPURankingAUC(common::Span<float const> predts, MetaInfo const &info,
int32_t device, std::shared_ptr<DeviceAUCCache> *cache);

/**********
* PR AUC *
**********/
std::tuple<float, float, float>
std::tuple<double, double, double>
GPUBinaryPRAUC(common::Span<float const> predts, MetaInfo const &info,
int32_t device, std::shared_ptr<DeviceAUCCache> *p_cache);

float GPUMultiClassPRAUC(common::Span<float const> predts, MetaInfo const &info,
int32_t device, std::shared_ptr<DeviceAUCCache> *cache,
size_t n_classes);
double GPUMultiClassPRAUC(common::Span<float const> predts,
MetaInfo const &info, int32_t device,
std::shared_ptr<DeviceAUCCache> *cache,
size_t n_classes);

std::pair<float, uint32_t>
std::pair<double, uint32_t>
GPURankingPRAUC(common::Span<float const> predts, MetaInfo const &info,
int32_t device, std::shared_ptr<DeviceAUCCache> *cache);

namespace detail {
XGBOOST_DEVICE inline float CalcH(float fp_a, float fp_b, float tp_a,
float tp_b) {
XGBOOST_DEVICE inline double CalcH(double fp_a, double fp_b, double tp_a,
double tp_b) {
return (fp_b - fp_a) / (tp_b - tp_a);
}

XGBOOST_DEVICE inline float CalcB(float fp_a, float h, float tp_a, float total_pos) {
XGBOOST_DEVICE inline double CalcB(double fp_a, double h, double tp_a, double total_pos) {
return (fp_a - h * tp_a) / total_pos;
}

XGBOOST_DEVICE inline float CalcA(float h) { return h + 1; }
XGBOOST_DEVICE inline double CalcA(double h) { return h + 1; }

XGBOOST_DEVICE inline float CalcDeltaPRAUC(float fp_prev, float fp,
float tp_prev, float tp,
float total_pos) {
float pr_prev = tp_prev / total_pos;
float pr = tp / total_pos;
XGBOOST_DEVICE inline double CalcDeltaPRAUC(double fp_prev, double fp,
double tp_prev, double tp,
double total_pos) {
double pr_prev = tp_prev / total_pos;
double pr = tp / total_pos;

float h{0}, a{0}, b{0};
double h{0}, a{0}, b{0};

if (tp == tp_prev) {
a = 1.0;
Expand All @@ -86,7 +87,7 @@ XGBOOST_DEVICE inline float CalcDeltaPRAUC(float fp_prev, float fp,
b = detail::CalcB(fp_prev, h, tp_prev, total_pos);
}

float area = 0;
double area = 0;
if (b != 0.0) {
area = (pr - pr_prev -
b / a * (std::log(a * pr + b) - std::log(a * pr_prev + b))) /
Expand Down
34 changes: 16 additions & 18 deletions src/metric/elementwise_metric.cu
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ class ElementWiseMetricsReduction {
thrust::cuda::par(alloc),
begin, end,
[=] XGBOOST_DEVICE(size_t idx) {
bst_float weight = is_null_weight ? 1.0f : s_weights[idx];
float weight = is_null_weight ? 1.0f : s_weights[idx];

bst_float residue = d_policy.EvalRow(s_label[idx], s_preds[idx]);
float residue = d_policy.EvalRow(s_label[idx], s_preds[idx]);
residue *= weight;
return PackedReduceResult{ residue, weight };
},
Expand Down Expand Up @@ -141,7 +141,7 @@ struct EvalRowRMSE {
bst_float diff = label - pred;
return diff * diff;
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? std::sqrt(esum) : std::sqrt(esum / wsum);
}
};
Expand All @@ -155,7 +155,7 @@ struct EvalRowRMSLE {
bst_float diff = std::log1p(label) - std::log1p(pred);
return diff * diff;
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? std::sqrt(esum) : std::sqrt(esum / wsum);
}
};
Expand All @@ -168,7 +168,7 @@ struct EvalRowMAE {
XGBOOST_DEVICE bst_float EvalRow(bst_float label, bst_float pred) const {
return std::abs(label - pred);
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand All @@ -180,7 +180,7 @@ struct EvalRowMAPE {
XGBOOST_DEVICE bst_float EvalRow(bst_float label, bst_float pred) const {
return std::abs((label - pred) / label);
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand All @@ -202,7 +202,7 @@ struct EvalRowLogLoss {
}
}

static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand All @@ -215,7 +215,7 @@ struct EvalRowMPHE {
bst_float diff = label - pred;
return std::sqrt( 1 + diff * diff) - 1;
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand Down Expand Up @@ -244,13 +244,12 @@ struct EvalError {
}
}

XGBOOST_DEVICE bst_float EvalRow(
bst_float label, bst_float pred) const {
XGBOOST_DEVICE bst_float EvalRow(bst_float label, bst_float pred) const {
// assume label is in [0,1]
return pred > threshold_ ? 1.0f - label : label;
}

static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}

Expand All @@ -270,7 +269,7 @@ struct EvalPoissonNegLogLik {
return common::LogGamma(y + 1.0f) + py - std::log(py) * y;
}

static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand All @@ -291,7 +290,7 @@ struct EvalGammaDeviance {
return std::log(predt / label) + label / predt - 1;
}

static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
if (wsum <= 0) {
wsum = kRtEps;
}
Expand All @@ -317,7 +316,7 @@ struct EvalGammaNLogLik {
// general form for exponential family.
return -((y * theta - b) / a + c);
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}
};
Expand All @@ -343,7 +342,7 @@ struct EvalTweedieNLogLik {
bst_float b = std::exp((2 - rho_) * std::log(p)) / (2 - rho_);
return -a + b;
}
static bst_float GetFinal(bst_float esum, bst_float wsum) {
static double GetFinal(double esum, double wsum) {
return wsum == 0 ? esum : esum / wsum;
}

Expand All @@ -360,9 +359,8 @@ struct EvalEWiseBase : public Metric {
explicit EvalEWiseBase(char const* policy_param) :
policy_{policy_param}, reducer_{policy_} {}

bst_float Eval(const HostDeviceVector<bst_float>& preds,
const MetaInfo& info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK_EQ(preds.Size(), info.labels_.Size())
<< "label and prediction size not match, "
<< "hint: use merror or mlogloss for multi-class classification";
Expand Down
7 changes: 3 additions & 4 deletions src/metric/multiclass_metric.cu
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,8 @@ class MultiClassMetricsReduction {
*/
template<typename Derived>
struct EvalMClassBase : public Metric {
bst_float Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
bool distributed) override {
double Eval(const HostDeviceVector<float> &preds, const MetaInfo &info,
bool distributed) override {
if (info.labels_.Size() == 0) {
CHECK_EQ(preds.Size(), 0);
} else {
Expand Down Expand Up @@ -206,7 +205,7 @@ struct EvalMClassBase : public Metric {
* \param esum the sum statistics returned by EvalRow
* \param wsum sum of weight
*/
inline static bst_float GetFinal(bst_float esum, bst_float wsum) {
inline static double GetFinal(double esum, double wsum) {
return esum / wsum;
}

Expand Down
25 changes: 10 additions & 15 deletions src/metric/rank_metric.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,8 @@ struct EvalAMS : public Metric {
name_ = os.str();
}

bst_float Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK(!distributed) << "metric AMS do not support distributed evaluation";
using namespace std; // NOLINT(*)

Expand Down Expand Up @@ -163,9 +162,8 @@ struct EvalRank : public Metric, public EvalRankConfig {
std::unique_ptr<xgboost::Metric> rank_gpu_;

public:
bst_float Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK_EQ(preds.Size(), info.labels_.Size())
<< "label size predict size not match";

Expand Down Expand Up @@ -222,14 +220,12 @@ struct EvalRank : public Metric, public EvalRankConfig {
}

if (distributed) {
bst_float dat[2];
dat[0] = static_cast<bst_float>(sum_metric);
dat[1] = static_cast<bst_float>(ngroups);
double dat[2]{sum_metric, static_cast<double>(ngroups)};
// approximately estimate the metric using mean
rabit::Allreduce<rabit::op::Sum>(dat, 2);
return dat[0] / dat[1];
} else {
return static_cast<bst_float>(sum_metric) / ngroups;
return sum_metric / ngroups;
}
}

Expand Down Expand Up @@ -335,9 +331,9 @@ struct EvalMAP : public EvalRank {
return sumap;
} else {
if (this->minus) {
return 0.0f;
return 0.0;
} else {
return 1.0f;
return 1.0;
}
}
}
Expand All @@ -347,9 +343,8 @@ struct EvalMAP : public EvalRank {
struct EvalCox : public Metric {
public:
EvalCox() = default;
bst_float Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK(!distributed) << "Cox metric does not support distributed evaluation";
using namespace std; // NOLINT(*)

Expand Down
5 changes: 2 additions & 3 deletions src/metric/rank_metric.cu
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ DMLC_REGISTRY_FILE_TAG(rank_metric_gpu);
template <typename EvalMetricT>
struct EvalRankGpu : public Metric, public EvalRankConfig {
public:
bst_float Eval(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
// Sanity check is done by the caller
std::vector<unsigned> tgptr(2, 0);
tgptr[1] = static_cast<unsigned>(preds.Size());
Expand Down
12 changes: 5 additions & 7 deletions src/metric/survival_metric.cu
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,8 @@ template <typename Policy> struct EvalEWiseSurvivalBase : public Metric {
CHECK(tparam_);
}

bst_float Eval(const HostDeviceVector<bst_float>& preds,
const MetaInfo& info,
bool distributed) override {
double Eval(const HostDeviceVector<float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK_EQ(preds.Size(), info.labels_lower_bound_.Size());
CHECK_EQ(preds.Size(), info.labels_upper_bound_.Size());
CHECK(tparam_);
Expand All @@ -221,7 +220,7 @@ template <typename Policy> struct EvalEWiseSurvivalBase : public Metric {
if (distributed) {
rabit::Allreduce<rabit::op::Sum>(dat, 2);
}
return static_cast<bst_float>(Policy::GetFinal(dat[0], dat[1]));
return Policy::GetFinal(dat[0], dat[1]);
}

const char* Name() const override {
Expand All @@ -241,9 +240,8 @@ struct AFTNLogLikDispatcher : public Metric {
return "aft-nloglik";
}

bst_float Eval(const HostDeviceVector<bst_float>& preds,
const MetaInfo& info,
bool distributed) override {
double Eval(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
bool distributed) override {
CHECK(metric_) << "AFT metric must be configured first, with distribution type and scale";
return metric_->Eval(preds, info, distributed);
}
Expand Down
5 changes: 4 additions & 1 deletion tests/python/test_with_sklearn.py
Original file line number Diff line number Diff line change
Expand Up @@ -1331,8 +1331,11 @@ def merror(y_true: np.ndarray, predt: np.ndarray):
)
clf.fit(X, y, eval_set=[(X, y)])
internal = clf.evals_result()

np.testing.assert_allclose(
custom["validation_0"]["merror"], internal["validation_0"]["merror"]
custom["validation_0"]["merror"],
internal["validation_0"]["merror"],
atol=1e-6
)

clf = xgb.XGBRFClassifier(
Expand Down

0 comments on commit 0f7a9b4

Please sign in to comment.