Make sure metrics work with column-wise distributed training (#9020)
This commit is contained in:
parent
191d0aa5cf
commit
ba9d24ff7b
@ -116,8 +116,10 @@ double MultiClassOVR(Context const *ctx, common::Span<float const> predts, MetaI
|
|||||||
|
|
||||||
// we have 2 averages going in here, first is among workers, second is among
|
// we have 2 averages going in here, first is among workers, second is among
|
||||||
// classes. allreduce sums up fp/tp auc for each class.
|
// classes. allreduce sums up fp/tp auc for each class.
|
||||||
collective::Allreduce<collective::Operation::kSum>(results.Values().data(),
|
if (info.IsRowSplit()) {
|
||||||
results.Values().size());
|
collective::Allreduce<collective::Operation::kSum>(results.Values().data(),
|
||||||
|
results.Values().size());
|
||||||
|
}
|
||||||
double auc_sum{0};
|
double auc_sum{0};
|
||||||
double tp_sum{0};
|
double tp_sum{0};
|
||||||
for (size_t c = 0; c < n_classes; ++c) {
|
for (size_t c = 0; c < n_classes; ++c) {
|
||||||
@ -290,7 +292,9 @@ class EvalAUC : public MetricNoCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::array<double, 2> results{auc, static_cast<double>(valid_groups)};
|
std::array<double, 2> results{auc, static_cast<double>(valid_groups)};
|
||||||
collective::Allreduce<collective::Operation::kSum>(results.data(), results.size());
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(results.data(), results.size());
|
||||||
|
}
|
||||||
auc = results[0];
|
auc = results[0];
|
||||||
valid_groups = static_cast<uint32_t>(results[1]);
|
valid_groups = static_cast<uint32_t>(results[1]);
|
||||||
|
|
||||||
@ -319,7 +323,9 @@ class EvalAUC : public MetricNoCache {
|
|||||||
}
|
}
|
||||||
double local_area = fp * tp;
|
double local_area = fp * tp;
|
||||||
std::array<double, 2> result{auc, local_area};
|
std::array<double, 2> result{auc, local_area};
|
||||||
collective::Allreduce<collective::Operation::kSum>(result.data(), result.size());
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(result.data(), result.size());
|
||||||
|
}
|
||||||
std::tie(auc, local_area) = common::UnpackArr(std::move(result));
|
std::tie(auc, local_area) = common::UnpackArr(std::move(result));
|
||||||
if (local_area <= 0) {
|
if (local_area <= 0) {
|
||||||
// the dataset across all workers have only positive or negative sample
|
// the dataset across all workers have only positive or negative sample
|
||||||
|
|||||||
@ -198,7 +198,7 @@ class PseudoErrorLoss : public MetricNoCache {
|
|||||||
return std::make_tuple(v, wt);
|
return std::make_tuple(v, wt);
|
||||||
});
|
});
|
||||||
double dat[2]{result.Residue(), result.Weights()};
|
double dat[2]{result.Residue(), result.Weights()};
|
||||||
if (collective::IsDistributed()) {
|
if (info.IsRowSplit()) {
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
}
|
}
|
||||||
return EvalRowMAPE::GetFinal(dat[0], dat[1]);
|
return EvalRowMAPE::GetFinal(dat[0], dat[1]);
|
||||||
@ -367,7 +367,9 @@ struct EvalEWiseBase : public MetricNoCache {
|
|||||||
});
|
});
|
||||||
|
|
||||||
double dat[2]{result.Residue(), result.Weights()};
|
double dat[2]{result.Residue(), result.Weights()};
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
|
}
|
||||||
return Policy::GetFinal(dat[0], dat[1]);
|
return Policy::GetFinal(dat[0], dat[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +441,9 @@ class QuantileError : public MetricNoCache {
|
|||||||
if (info.num_row_ == 0) {
|
if (info.num_row_ == 0) {
|
||||||
// empty DMatrix on distributed env
|
// empty DMatrix on distributed env
|
||||||
double dat[2]{0.0, 0.0};
|
double dat[2]{0.0, 0.0};
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
|
}
|
||||||
CHECK_GT(dat[1], 0);
|
CHECK_GT(dat[1], 0);
|
||||||
return dat[0] / dat[1];
|
return dat[0] / dat[1];
|
||||||
}
|
}
|
||||||
@ -477,7 +481,9 @@ class QuantileError : public MetricNoCache {
|
|||||||
return std::make_tuple(l, w);
|
return std::make_tuple(l, w);
|
||||||
});
|
});
|
||||||
double dat[2]{result.Residue(), result.Weights()};
|
double dat[2]{result.Residue(), result.Weights()};
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
|
}
|
||||||
CHECK_GT(dat[1], 0);
|
CHECK_GT(dat[1], 0);
|
||||||
return dat[0] / dat[1];
|
return dat[0] / dat[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,7 +181,9 @@ struct EvalMClassBase : public MetricNoCache {
|
|||||||
dat[0] = result.Residue();
|
dat[0] = result.Residue();
|
||||||
dat[1] = result.Weights();
|
dat[1] = result.Weights();
|
||||||
}
|
}
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
|
}
|
||||||
return Derived::GetFinal(dat[0], dat[1]);
|
return Derived::GetFinal(dat[0], dat[1]);
|
||||||
}
|
}
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@ -244,7 +244,7 @@ struct EvalRank : public MetricNoCache, public EvalRankConfig {
|
|||||||
exc.Rethrow();
|
exc.Rethrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collective::IsDistributed()) {
|
if (collective::IsDistributed() && info.IsRowSplit()) {
|
||||||
double dat[2]{sum_metric, static_cast<double>(ngroups)};
|
double dat[2]{sum_metric, static_cast<double>(ngroups)};
|
||||||
// approximately estimate the metric using mean
|
// approximately estimate the metric using mean
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
@ -401,9 +401,11 @@ class EvalRankWithCache : public Metric {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
double Finalize(double score, double sw) {
|
double Finalize(MetaInfo const& info, double score, double sw) {
|
||||||
std::array<double, 2> dat{score, sw};
|
std::array<double, 2> dat{score, sw};
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat.data(), dat.size());
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat.data(), dat.size());
|
||||||
|
}
|
||||||
if (sw > 0.0) {
|
if (sw > 0.0) {
|
||||||
score = score / sw;
|
score = score / sw;
|
||||||
}
|
}
|
||||||
@ -430,7 +432,7 @@ class EvalNDCG : public EvalRankWithCache<ltr::NDCGCache> {
|
|||||||
std::shared_ptr<ltr::NDCGCache> p_cache) override {
|
std::shared_ptr<ltr::NDCGCache> p_cache) override {
|
||||||
if (ctx_->IsCUDA()) {
|
if (ctx_->IsCUDA()) {
|
||||||
auto ndcg = cuda_impl::NDCGScore(ctx_, info, preds, minus_, p_cache);
|
auto ndcg = cuda_impl::NDCGScore(ctx_, info, preds, minus_, p_cache);
|
||||||
return Finalize(ndcg.Residue(), ndcg.Weights());
|
return Finalize(info, ndcg.Residue(), ndcg.Weights());
|
||||||
}
|
}
|
||||||
|
|
||||||
// group local ndcg
|
// group local ndcg
|
||||||
@ -476,7 +478,7 @@ class EvalNDCG : public EvalRankWithCache<ltr::NDCGCache> {
|
|||||||
sum_w = std::accumulate(weights.weights.cbegin(), weights.weights.cend(), 0.0);
|
sum_w = std::accumulate(weights.weights.cbegin(), weights.weights.cend(), 0.0);
|
||||||
}
|
}
|
||||||
auto ndcg = std::accumulate(linalg::cbegin(ndcg_gloc), linalg::cend(ndcg_gloc), 0.0);
|
auto ndcg = std::accumulate(linalg::cbegin(ndcg_gloc), linalg::cend(ndcg_gloc), 0.0);
|
||||||
return Finalize(ndcg, sum_w);
|
return Finalize(info, ndcg, sum_w);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -489,7 +491,7 @@ class EvalMAPScore : public EvalRankWithCache<ltr::MAPCache> {
|
|||||||
std::shared_ptr<ltr::MAPCache> p_cache) override {
|
std::shared_ptr<ltr::MAPCache> p_cache) override {
|
||||||
if (ctx_->IsCUDA()) {
|
if (ctx_->IsCUDA()) {
|
||||||
auto map = cuda_impl::MAPScore(ctx_, info, predt, minus_, p_cache);
|
auto map = cuda_impl::MAPScore(ctx_, info, predt, minus_, p_cache);
|
||||||
return Finalize(map.Residue(), map.Weights());
|
return Finalize(info, map.Residue(), map.Weights());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto gptr = p_cache->DataGroupPtr(ctx_);
|
auto gptr = p_cache->DataGroupPtr(ctx_);
|
||||||
@ -532,7 +534,7 @@ class EvalMAPScore : public EvalRankWithCache<ltr::MAPCache> {
|
|||||||
sw += weight[i];
|
sw += weight[i];
|
||||||
}
|
}
|
||||||
auto sum = std::accumulate(map_gloc.cbegin(), map_gloc.cend(), 0.0);
|
auto sum = std::accumulate(map_gloc.cbegin(), map_gloc.cend(), 0.0);
|
||||||
return Finalize(sum, sw);
|
return Finalize(info, sum, sw);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -212,7 +212,9 @@ struct EvalEWiseSurvivalBase : public MetricNoCache {
|
|||||||
info.labels_upper_bound_, preds);
|
info.labels_upper_bound_, preds);
|
||||||
|
|
||||||
double dat[2]{result.Residue(), result.Weights()};
|
double dat[2]{result.Residue(), result.Weights()};
|
||||||
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
if (info.IsRowSplit()) {
|
||||||
|
collective::Allreduce<collective::Operation::kSum>(dat, 2);
|
||||||
|
}
|
||||||
return Policy::GetFinal(dat[0], dat[1]);
|
return Policy::GetFinal(dat[0], dat[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -167,18 +167,20 @@ xgboost::bst_float GetMetricEval(xgboost::Metric* metric,
|
|||||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||||
std::vector<xgboost::bst_float> labels,
|
std::vector<xgboost::bst_float> labels,
|
||||||
std::vector<xgboost::bst_float> weights,
|
std::vector<xgboost::bst_float> weights,
|
||||||
std::vector<xgboost::bst_uint> groups) {
|
std::vector<xgboost::bst_uint> groups,
|
||||||
|
xgboost::DataSplitMode data_split_mode) {
|
||||||
return GetMultiMetricEval(
|
return GetMultiMetricEval(
|
||||||
metric, preds,
|
metric, preds,
|
||||||
xgboost::linalg::Tensor<float, 2>{labels.begin(), labels.end(), {labels.size()}, -1}, weights,
|
xgboost::linalg::Tensor<float, 2>{labels.begin(), labels.end(), {labels.size()}, -1}, weights,
|
||||||
groups);
|
groups, data_split_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
double GetMultiMetricEval(xgboost::Metric* metric,
|
double GetMultiMetricEval(xgboost::Metric* metric,
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||||
xgboost::linalg::Tensor<float, 2> const& labels,
|
xgboost::linalg::Tensor<float, 2> const& labels,
|
||||||
std::vector<xgboost::bst_float> weights,
|
std::vector<xgboost::bst_float> weights,
|
||||||
std::vector<xgboost::bst_uint> groups) {
|
std::vector<xgboost::bst_uint> groups,
|
||||||
|
xgboost::DataSplitMode data_split_mode) {
|
||||||
std::shared_ptr<xgboost::DMatrix> p_fmat{xgboost::RandomDataGenerator{0, 0, 0}.GenerateDMatrix()};
|
std::shared_ptr<xgboost::DMatrix> p_fmat{xgboost::RandomDataGenerator{0, 0, 0}.GenerateDMatrix()};
|
||||||
auto& info = p_fmat->Info();
|
auto& info = p_fmat->Info();
|
||||||
info.num_row_ = labels.Shape(0);
|
info.num_row_ = labels.Shape(0);
|
||||||
@ -186,6 +188,7 @@ double GetMultiMetricEval(xgboost::Metric* metric,
|
|||||||
info.labels.Data()->Copy(*labels.Data());
|
info.labels.Data()->Copy(*labels.Data());
|
||||||
info.weights_.HostVector() = weights;
|
info.weights_.HostVector() = weights;
|
||||||
info.group_ptr_ = groups;
|
info.group_ptr_ = groups;
|
||||||
|
info.data_split_mode = data_split_mode;
|
||||||
|
|
||||||
return metric->Evaluate(preds, p_fmat);
|
return metric->Evaluate(preds, p_fmat);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,18 @@
|
|||||||
#define GPUIDX -1
|
#define GPUIDX -1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__CUDACC__)
|
||||||
|
#define DeclareUnifiedDistributedTest(name) MGPU ## name
|
||||||
|
#else
|
||||||
|
#define DeclareUnifiedDistributedTest(name) name
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__CUDACC__)
|
||||||
|
#define WORLD_SIZE_FOR_TEST (xgboost::common::AllVisibleGPUs())
|
||||||
|
#else
|
||||||
|
#define WORLD_SIZE_FOR_TEST (3)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
class ObjFunction;
|
class ObjFunction;
|
||||||
class Metric;
|
class Metric;
|
||||||
@ -92,13 +104,15 @@ xgboost::bst_float GetMetricEval(
|
|||||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||||
std::vector<xgboost::bst_float> labels,
|
std::vector<xgboost::bst_float> labels,
|
||||||
std::vector<xgboost::bst_float> weights = std::vector<xgboost::bst_float>(),
|
std::vector<xgboost::bst_float> weights = std::vector<xgboost::bst_float>(),
|
||||||
std::vector<xgboost::bst_uint> groups = std::vector<xgboost::bst_uint>());
|
std::vector<xgboost::bst_uint> groups = std::vector<xgboost::bst_uint>(),
|
||||||
|
xgboost::DataSplitMode data_split_Mode = xgboost::DataSplitMode::kRow);
|
||||||
|
|
||||||
double GetMultiMetricEval(xgboost::Metric* metric,
|
double GetMultiMetricEval(xgboost::Metric* metric,
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||||
xgboost::linalg::Tensor<float, 2> const& labels,
|
xgboost::linalg::Tensor<float, 2> const& labels,
|
||||||
std::vector<xgboost::bst_float> weights = {},
|
std::vector<xgboost::bst_float> weights = {},
|
||||||
std::vector<xgboost::bst_uint> groups = {});
|
std::vector<xgboost::bst_uint> groups = {},
|
||||||
|
xgboost::DataSplitMode data_split_Mode = xgboost::DataSplitMode::kRow);
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
|
|
||||||
@ -496,4 +510,17 @@ void RunWithInMemoryCommunicator(int32_t world_size, Function&& function, Args&&
|
|||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeclareUnifiedDistributedTest(MetricTest) : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
int world_size_;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
world_size_ = WORLD_SIZE_FOR_TEST;
|
||||||
|
if (world_size_ <= 1) {
|
||||||
|
GTEST_SKIP() << "Skipping MGPU test with # GPUs = " << world_size_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
@ -1,261 +1,68 @@
|
|||||||
|
#include "test_auc.h"
|
||||||
|
|
||||||
#include <xgboost/metric.h>
|
#include <xgboost/metric.h>
|
||||||
#include "../helpers.h"
|
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
namespace metric {
|
namespace metric {
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(BinaryAUC)) {
|
TEST(Metric, DeclareUnifiedTest(BinaryAUC)) { VerifyBinaryAUC(); }
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
|
||||||
std::unique_ptr<Metric> uni_ptr {Metric::Create("auc", &ctx)};
|
|
||||||
Metric * metric = uni_ptr.get();
|
|
||||||
ASSERT_STREQ(metric->Name(), "auc");
|
|
||||||
|
|
||||||
// Binary
|
TEST(Metric, DeclareUnifiedTest(MultiClassAUC)) { VerifyMultiClassAUC(); }
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1.0f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {1, 0}), 0.0f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 0}, {0, 1}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {1, 1}, {0, 1}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 0}, {1, 0}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {1, 1}, {1, 0}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {1, 0, 0}, {0, 0, 1}), 0.25f, 1e-10);
|
|
||||||
|
|
||||||
// Invalid dataset
|
TEST(Metric, DeclareUnifiedTest(RankingAUC)) { VerifyRankingAUC(); }
|
||||||
auto p_fmat = EmptyDMatrix();
|
|
||||||
MetaInfo& info = p_fmat->Info();
|
|
||||||
info.labels = linalg::Tensor<float, 2>{{0.0f, 0.0f}, {2}, -1};
|
|
||||||
float auc = metric->Evaluate({1, 1}, p_fmat);
|
|
||||||
ASSERT_TRUE(std::isnan(auc));
|
|
||||||
*info.labels.Data() = HostDeviceVector<float>{};
|
|
||||||
auc = metric->Evaluate(HostDeviceVector<float>{}, p_fmat);
|
|
||||||
ASSERT_TRUE(std::isnan(auc));
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1, 0, 1}, {0, 1, 0, 1}), 1.0f, 1e-10);
|
TEST(Metric, DeclareUnifiedTest(PRAUC)) { VerifyPRAUC(); }
|
||||||
|
|
||||||
// AUC with instance weights
|
TEST(Metric, DeclareUnifiedTest(MultiClassPRAUC)) { VerifyMultiClassPRAUC(); }
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.9f, 0.1f, 0.4f, 0.3f},
|
|
||||||
{0, 0, 1, 1},
|
|
||||||
{1.0f, 3.0f, 2.0f, 4.0f}),
|
|
||||||
0.75f, 0.001f);
|
|
||||||
|
|
||||||
// regression test case
|
TEST(Metric, DeclareUnifiedTest(RankingPRAUC)) { VerifyRankingPRAUC(); }
|
||||||
ASSERT_NEAR(GetMetricEval(
|
|
||||||
metric,
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), BinaryAUCRowSplit) {
|
||||||
{0.79523796, 0.5201713, 0.79523796, 0.24273258, 0.53452194,
|
RunWithInMemoryCommunicator(world_size_, &VerifyBinaryAUC, DataSplitMode::kRow);
|
||||||
0.53452194, 0.24273258, 0.5201713, 0.79523796, 0.53452194,
|
|
||||||
0.24273258, 0.53452194, 0.79523796, 0.5201713, 0.24273258,
|
|
||||||
0.5201713, 0.5201713, 0.53452194, 0.5201713, 0.53452194},
|
|
||||||
{0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0}),
|
|
||||||
0.5, 1e-10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MultiClassAUC)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), BinaryAUCColumnSplit) {
|
||||||
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyBinaryAUC, DataSplitMode::kCol);
|
||||||
std::unique_ptr<Metric> uni_ptr{
|
|
||||||
Metric::Create("auc", &ctx)};
|
|
||||||
auto metric = uni_ptr.get();
|
|
||||||
|
|
||||||
// MultiClass
|
|
||||||
// 3x3
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{
|
|
||||||
1.0f, 0.0f, 0.0f, // p_0
|
|
||||||
0.0f, 1.0f, 0.0f, // p_1
|
|
||||||
0.0f, 0.0f, 1.0f // p_2
|
|
||||||
},
|
|
||||||
{0, 1, 2}),
|
|
||||||
1.0f, 1e-10);
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{
|
|
||||||
1.0f, 0.0f, 0.0f, // p_0
|
|
||||||
0.0f, 1.0f, 0.0f, // p_1
|
|
||||||
0.0f, 0.0f, 1.0f // p_2
|
|
||||||
},
|
|
||||||
{0, 1, 2},
|
|
||||||
{1.0f, 1.0f, 1.0f}),
|
|
||||||
1.0f, 1e-10);
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{
|
|
||||||
1.0f, 0.0f, 0.0f, // p_0
|
|
||||||
0.0f, 1.0f, 0.0f, // p_1
|
|
||||||
0.0f, 0.0f, 1.0f // p_2
|
|
||||||
},
|
|
||||||
{2, 1, 0}),
|
|
||||||
0.5f, 1e-10);
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{
|
|
||||||
1.0f, 0.0f, 0.0f, // p_0
|
|
||||||
0.0f, 1.0f, 0.0f, // p_1
|
|
||||||
0.0f, 0.0f, 1.0f // p_2
|
|
||||||
},
|
|
||||||
{2, 0, 1}),
|
|
||||||
0.25f, 1e-10);
|
|
||||||
|
|
||||||
// invalid dataset
|
|
||||||
float auc = GetMetricEval(metric,
|
|
||||||
{
|
|
||||||
1.0f, 0.0f, 0.0f, // p_0
|
|
||||||
0.0f, 1.0f, 0.0f, // p_1
|
|
||||||
0.0f, 0.0f, 1.0f // p_2
|
|
||||||
},
|
|
||||||
{0, 1, 1}); // no class 2.
|
|
||||||
EXPECT_TRUE(std::isnan(auc)) << auc;
|
|
||||||
|
|
||||||
HostDeviceVector<float> predts{
|
|
||||||
0.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 0.0f, 0.0f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
};
|
|
||||||
std::vector<float> labels {1.0f, 0.0f, 2.0f, 1.0f};
|
|
||||||
auc = GetMetricEval(metric, predts, labels, {1.0f, 2.0f, 3.0f, 4.0f});
|
|
||||||
ASSERT_GT(auc, 0.714);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(RankingAUC)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassAUCRowSplit) {
|
||||||
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassAUC, DataSplitMode::kRow);
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("auc", &ctx)};
|
|
||||||
|
|
||||||
// single group
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), {0.7f, 0.2f, 0.3f, 0.6f},
|
|
||||||
{1.0f, 0.8f, 0.4f, 0.2f}, /*weights=*/{},
|
|
||||||
{0, 4}),
|
|
||||||
0.5f, 1e-10);
|
|
||||||
|
|
||||||
// multi group
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1, 2, 0, 1, 2},
|
|
||||||
{0, 1, 2, 0, 1, 2}, /*weights=*/{}, {0, 3, 6}),
|
|
||||||
1.0f, 1e-10);
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1, 2, 0, 1, 2},
|
|
||||||
{0, 1, 2, 0, 1, 2}, /*weights=*/{1.0f, 2.0f},
|
|
||||||
{0, 3, 6}),
|
|
||||||
1.0f, 1e-10);
|
|
||||||
|
|
||||||
// AUC metric for grouped datasets - exception scenarios
|
|
||||||
ASSERT_TRUE(std::isnan(
|
|
||||||
GetMetricEval(metric.get(), {0, 1, 2}, {0, 0, 0}, {}, {0, 2, 3})));
|
|
||||||
|
|
||||||
// regression case
|
|
||||||
HostDeviceVector<float> predt{0.33935383, 0.5149714, 0.32138085, 1.4547751,
|
|
||||||
1.2010975, 0.42651367, 0.23104341, 0.83610827,
|
|
||||||
0.8494239, 0.07136688, 0.5623144, 0.8086237,
|
|
||||||
1.5066161, -4.094787, 0.76887935, -2.4082742};
|
|
||||||
std::vector<bst_group_t> groups{0, 7, 16};
|
|
||||||
std::vector<float> labels{1., 0., 0., 1., 2., 1., 0., 0.,
|
|
||||||
0., 0., 0., 0., 1., 0., 1., 0.};
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), std::move(predt), labels,
|
|
||||||
/*weights=*/{}, groups),
|
|
||||||
0.769841f, 1e-6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(PRAUC)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassAUCColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassAUC, DataSplitMode::kCol);
|
||||||
|
|
||||||
xgboost::Metric *metric = xgboost::Metric::Create("aucpr", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "aucpr");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 0, 1, 1}, {0, 0, 1, 1}), 1, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0.1f, 0.9f, 0.1f, 0.9f}, {0, 0, 1, 1}),
|
|
||||||
0.5f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(
|
|
||||||
metric,
|
|
||||||
{0.4f, 0.2f, 0.9f, 0.1f, 0.2f, 0.4f, 0.1f, 0.1f, 0.2f, 0.1f},
|
|
||||||
{0, 0, 0, 0, 0, 1, 0, 0, 1, 1}),
|
|
||||||
0.2908445f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(
|
|
||||||
metric, {0.87f, 0.31f, 0.40f, 0.42f, 0.25f, 0.66f, 0.95f,
|
|
||||||
0.09f, 0.10f, 0.97f, 0.76f, 0.69f, 0.15f, 0.20f,
|
|
||||||
0.30f, 0.14f, 0.07f, 0.58f, 0.61f, 0.08f},
|
|
||||||
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1}),
|
|
||||||
0.2769199f, 0.001f);
|
|
||||||
auto auc = GetMetricEval(metric, {0, 1}, {});
|
|
||||||
ASSERT_TRUE(std::isnan(auc));
|
|
||||||
|
|
||||||
// AUCPR with instance weights
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.29f, 0.52f, 0.11f, 0.21f, 0.219f, 0.93f, 0.493f,
|
|
||||||
0.17f, 0.47f, 0.13f, 0.43f, 0.59f, 0.87f, 0.007f},
|
|
||||||
{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0},
|
|
||||||
{1, 2, 7, 4, 5, 2.2f, 3.2f, 5, 6, 1, 2, 1.1f, 3.2f,
|
|
||||||
4.5f}), // weights
|
|
||||||
0.694435f, 0.001f);
|
|
||||||
|
|
||||||
// Both groups contain only pos or neg samples.
|
|
||||||
auc = GetMetricEval(metric,
|
|
||||||
{0, 0.1f, 0.3f, 0.5f, 0.7f},
|
|
||||||
{1, 1, 0, 0, 0},
|
|
||||||
{},
|
|
||||||
{0, 2, 5});
|
|
||||||
ASSERT_TRUE(std::isnan(auc));
|
|
||||||
delete metric;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MultiClassPRAUC)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingAUCRowSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRankingAUC, DataSplitMode::kRow);
|
||||||
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
|
|
||||||
|
|
||||||
float auc = 0;
|
|
||||||
std::vector<float> labels {1.0f, 0.0f, 2.0f};
|
|
||||||
HostDeviceVector<float> predts{
|
|
||||||
0.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 0.0f, 0.0f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
};
|
|
||||||
auc = GetMetricEval(metric.get(), predts, labels, {});
|
|
||||||
EXPECT_EQ(auc, 1.0f);
|
|
||||||
|
|
||||||
auc = GetMetricEval(metric.get(), predts, labels, {1.0f, 1.0f, 1.0f});
|
|
||||||
EXPECT_EQ(auc, 1.0f);
|
|
||||||
|
|
||||||
predts.HostVector() = {
|
|
||||||
0.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 0.0f, 0.0f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
};
|
|
||||||
labels = {1.0f, 0.0f, 2.0f, 1.0f};
|
|
||||||
auc = GetMetricEval(metric.get(), predts, labels, {1.0f, 2.0f, 3.0f, 4.0f});
|
|
||||||
ASSERT_GT(auc, 0.699);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(RankingPRAUC)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingAUCColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRankingAUC, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PRAUCRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyPRAUC, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<float> labels {1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PRAUCColumnSplit) {
|
||||||
std::vector<uint32_t> groups {0, 2, 6};
|
RunWithInMemoryCommunicator(world_size_, &VerifyPRAUC, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
float auc = 0;
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassPRAUCRowSplit) {
|
||||||
auc = GetMetricEval(metric.get(), {1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, labels, {}, groups);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassPRAUC, DataSplitMode::kRow);
|
||||||
EXPECT_EQ(auc, 1.0f);
|
}
|
||||||
|
|
||||||
auc = GetMetricEval(metric.get(), {1.0f, 0.5f, 0.8f, 0.3f, 0.2f, 1.0f}, labels, {}, groups);
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassPRAUCColumnSplit) {
|
||||||
EXPECT_EQ(auc, 1.0f);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassPRAUC, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
auc = GetMetricEval(metric.get(), {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingPRAUCRowSplit) {
|
||||||
{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, {}, groups);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRankingPRAUC, DataSplitMode::kRow);
|
||||||
ASSERT_TRUE(std::isnan(auc));
|
}
|
||||||
|
|
||||||
// Incorrect label
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingPRAUCColumnSplit) {
|
||||||
ASSERT_THROW(GetMetricEval(metric.get(), {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
RunWithInMemoryCommunicator(world_size_, &VerifyRankingPRAUC, DataSplitMode::kCol);
|
||||||
{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 3.0f}, {}, groups),
|
|
||||||
dmlc::Error);
|
|
||||||
|
|
||||||
// AUCPR with groups and no weights
|
|
||||||
EXPECT_NEAR(GetMetricEval(
|
|
||||||
metric.get(), {0.87f, 0.31f, 0.40f, 0.42f, 0.25f, 0.66f, 0.95f,
|
|
||||||
0.09f, 0.10f, 0.97f, 0.76f, 0.69f, 0.15f, 0.20f,
|
|
||||||
0.30f, 0.14f, 0.07f, 0.58f, 0.61f, 0.08f},
|
|
||||||
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1},
|
|
||||||
{}, // weights
|
|
||||||
{0, 2, 5, 9, 14, 20}), // group info
|
|
||||||
0.556021f, 0.001f);
|
|
||||||
}
|
}
|
||||||
} // namespace metric
|
} // namespace metric
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
249
tests/cpp/metric/test_auc.h
Normal file
249
tests/cpp/metric/test_auc.h
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (c) 2023 by XGBoost Contributors
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xgboost/metric.h>
|
||||||
|
|
||||||
|
#include "../helpers.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace metric {
|
||||||
|
|
||||||
|
inline void VerifyBinaryAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<Metric> uni_ptr{Metric::Create("auc", &ctx)};
|
||||||
|
Metric* metric = uni_ptr.get();
|
||||||
|
ASSERT_STREQ(metric->Name(), "auc");
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1.0f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {1, 0}, {}, {}, data_split_mode), 0.0f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 0}, {0, 1}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {1, 1}, {0, 1}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 0}, {1, 0}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {1, 1}, {1, 0}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {1, 0, 0}, {0, 0, 1}, {}, {}, data_split_mode), 0.25f, 1e-10);
|
||||||
|
|
||||||
|
// Invalid dataset
|
||||||
|
auto p_fmat = EmptyDMatrix();
|
||||||
|
MetaInfo& info = p_fmat->Info();
|
||||||
|
info.labels = linalg::Tensor<float, 2>{{0.0f, 0.0f}, {2}, -1};
|
||||||
|
float auc = metric->Evaluate({1, 1}, p_fmat);
|
||||||
|
ASSERT_TRUE(std::isnan(auc));
|
||||||
|
*info.labels.Data() = HostDeviceVector<float>{};
|
||||||
|
auc = metric->Evaluate(HostDeviceVector<float>{}, p_fmat);
|
||||||
|
ASSERT_TRUE(std::isnan(auc));
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1, 0, 1}, {0, 1, 0, 1}, {}, {}, data_split_mode), 1.0f,
|
||||||
|
1e-10);
|
||||||
|
|
||||||
|
// AUC with instance weights
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0.9f, 0.1f, 0.4f, 0.3f}, {0, 0, 1, 1},
|
||||||
|
{1.0f, 3.0f, 2.0f, 4.0f}, {}, data_split_mode),
|
||||||
|
0.75f, 0.001f);
|
||||||
|
|
||||||
|
// regression test case
|
||||||
|
ASSERT_NEAR(GetMetricEval(metric, {0.79523796, 0.5201713, 0.79523796, 0.24273258, 0.53452194,
|
||||||
|
0.53452194, 0.24273258, 0.5201713, 0.79523796, 0.53452194,
|
||||||
|
0.24273258, 0.53452194, 0.79523796, 0.5201713, 0.24273258,
|
||||||
|
0.5201713, 0.5201713, 0.53452194, 0.5201713, 0.53452194},
|
||||||
|
{0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {}, {},
|
||||||
|
data_split_mode),
|
||||||
|
0.5, 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMultiClassAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<Metric> uni_ptr{Metric::Create("auc", &ctx)};
|
||||||
|
auto metric = uni_ptr.get();
|
||||||
|
|
||||||
|
// MultiClass
|
||||||
|
// 3x3
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{
|
||||||
|
1.0f, 0.0f, 0.0f, // p_0
|
||||||
|
0.0f, 1.0f, 0.0f, // p_1
|
||||||
|
0.0f, 0.0f, 1.0f // p_2
|
||||||
|
},
|
||||||
|
{0, 1, 2}, {}, {}, data_split_mode),
|
||||||
|
1.0f, 1e-10);
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{
|
||||||
|
1.0f, 0.0f, 0.0f, // p_0
|
||||||
|
0.0f, 1.0f, 0.0f, // p_1
|
||||||
|
0.0f, 0.0f, 1.0f // p_2
|
||||||
|
},
|
||||||
|
{0, 1, 2}, {1.0f, 1.0f, 1.0f}, {}, data_split_mode),
|
||||||
|
1.0f, 1e-10);
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{
|
||||||
|
1.0f, 0.0f, 0.0f, // p_0
|
||||||
|
0.0f, 1.0f, 0.0f, // p_1
|
||||||
|
0.0f, 0.0f, 1.0f // p_2
|
||||||
|
},
|
||||||
|
{2, 1, 0}, {}, {}, data_split_mode),
|
||||||
|
0.5f, 1e-10);
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{
|
||||||
|
1.0f, 0.0f, 0.0f, // p_0
|
||||||
|
0.0f, 1.0f, 0.0f, // p_1
|
||||||
|
0.0f, 0.0f, 1.0f // p_2
|
||||||
|
},
|
||||||
|
{2, 0, 1}, {}, {}, data_split_mode),
|
||||||
|
0.25f, 1e-10);
|
||||||
|
|
||||||
|
// invalid dataset
|
||||||
|
float auc = GetMetricEval(metric,
|
||||||
|
{
|
||||||
|
1.0f, 0.0f, 0.0f, // p_0
|
||||||
|
0.0f, 1.0f, 0.0f, // p_1
|
||||||
|
0.0f, 0.0f, 1.0f // p_2
|
||||||
|
},
|
||||||
|
{0, 1, 1}, {}, {}, data_split_mode); // no class 2.
|
||||||
|
EXPECT_TRUE(std::isnan(auc)) << auc;
|
||||||
|
|
||||||
|
HostDeviceVector<float> predts{
|
||||||
|
0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
std::vector<float> labels{1.0f, 0.0f, 2.0f, 1.0f};
|
||||||
|
auc = GetMetricEval(metric, predts, labels, {1.0f, 2.0f, 3.0f, 4.0f}, {}, data_split_mode);
|
||||||
|
ASSERT_GT(auc, 0.714);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyRankingAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("auc", &ctx)};
|
||||||
|
|
||||||
|
// single group
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), {0.7f, 0.2f, 0.3f, 0.6f}, {1.0f, 0.8f, 0.4f, 0.2f},
|
||||||
|
/*weights=*/{}, {0, 4}, data_split_mode),
|
||||||
|
0.5f, 1e-10);
|
||||||
|
|
||||||
|
// multi group
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1, 2, 0, 1, 2}, {0, 1, 2, 0, 1, 2}, /*weights=*/{},
|
||||||
|
{0, 3, 6}, data_split_mode),
|
||||||
|
1.0f, 1e-10);
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1, 2, 0, 1, 2}, {0, 1, 2, 0, 1, 2},
|
||||||
|
/*weights=*/{1.0f, 2.0f}, {0, 3, 6}, data_split_mode),
|
||||||
|
1.0f, 1e-10);
|
||||||
|
|
||||||
|
// AUC metric for grouped datasets - exception scenarios
|
||||||
|
ASSERT_TRUE(std::isnan(
|
||||||
|
GetMetricEval(metric.get(), {0, 1, 2}, {0, 0, 0}, {}, {0, 2, 3}, data_split_mode)));
|
||||||
|
|
||||||
|
// regression case
|
||||||
|
HostDeviceVector<float> predt{
|
||||||
|
0.33935383, 0.5149714, 0.32138085, 1.4547751, 1.2010975, 0.42651367, 0.23104341, 0.83610827,
|
||||||
|
0.8494239, 0.07136688, 0.5623144, 0.8086237, 1.5066161, -4.094787, 0.76887935, -2.4082742};
|
||||||
|
std::vector<bst_group_t> groups{0, 7, 16};
|
||||||
|
std::vector<float> labels{1., 0., 0., 1., 2., 1., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0.};
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), std::move(predt), labels,
|
||||||
|
/*weights=*/{}, groups, data_split_mode),
|
||||||
|
0.769841f, 1e-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
|
xgboost::Metric* metric = xgboost::Metric::Create("aucpr", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "aucpr");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 0, 1, 1}, {0, 0, 1, 1}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
EXPECT_NEAR(
|
||||||
|
GetMetricEval(metric, {0.1f, 0.9f, 0.1f, 0.9f}, {0, 0, 1, 1}, {}, {}, data_split_mode), 0.5f,
|
||||||
|
0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0.4f, 0.2f, 0.9f, 0.1f, 0.2f, 0.4f, 0.1f, 0.1f, 0.2f, 0.1f},
|
||||||
|
{0, 0, 0, 0, 0, 1, 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.2908445f, 0.001f);
|
||||||
|
EXPECT_NEAR(
|
||||||
|
GetMetricEval(metric, {0.87f, 0.31f, 0.40f, 0.42f, 0.25f, 0.66f, 0.95f, 0.09f, 0.10f, 0.97f,
|
||||||
|
0.76f, 0.69f, 0.15f, 0.20f, 0.30f, 0.14f, 0.07f, 0.58f, 0.61f, 0.08f},
|
||||||
|
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1}, {}, {},
|
||||||
|
data_split_mode),
|
||||||
|
0.2769199f, 0.001f);
|
||||||
|
auto auc = GetMetricEval(metric, {0, 1}, {}, {}, {}, data_split_mode);
|
||||||
|
ASSERT_TRUE(std::isnan(auc));
|
||||||
|
|
||||||
|
// AUCPR with instance weights
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.29f, 0.52f, 0.11f, 0.21f, 0.219f, 0.93f, 0.493f, 0.17f, 0.47f, 0.13f,
|
||||||
|
0.43f, 0.59f, 0.87f, 0.007f},
|
||||||
|
{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0},
|
||||||
|
{1, 2, 7, 4, 5, 2.2f, 3.2f, 5, 6, 1, 2, 1.1f, 3.2f, 4.5f}, // weights
|
||||||
|
{}, data_split_mode),
|
||||||
|
0.694435f, 0.001f);
|
||||||
|
|
||||||
|
// Both groups contain only pos or neg samples.
|
||||||
|
auc = GetMetricEval(metric, {0, 0.1f, 0.3f, 0.5f, 0.7f}, {1, 1, 0, 0, 0}, {}, {0, 2, 5},
|
||||||
|
data_split_mode);
|
||||||
|
ASSERT_TRUE(std::isnan(auc));
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMultiClassPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
|
||||||
|
|
||||||
|
float auc = 0;
|
||||||
|
std::vector<float> labels{1.0f, 0.0f, 2.0f};
|
||||||
|
HostDeviceVector<float> predts{
|
||||||
|
0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
auc = GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode);
|
||||||
|
EXPECT_EQ(auc, 1.0f);
|
||||||
|
|
||||||
|
auc = GetMetricEval(metric.get(), predts, labels, {1.0f, 1.0f, 1.0f}, {}, data_split_mode);
|
||||||
|
EXPECT_EQ(auc, 1.0f);
|
||||||
|
|
||||||
|
predts.HostVector() = {
|
||||||
|
0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
labels = {1.0f, 0.0f, 2.0f, 1.0f};
|
||||||
|
auc = GetMetricEval(metric.get(), predts, labels, {1.0f, 2.0f, 3.0f, 4.0f}, {}, data_split_mode);
|
||||||
|
ASSERT_GT(auc, 0.699);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyRankingPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
|
||||||
|
|
||||||
|
std::vector<float> labels{1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
std::vector<uint32_t> groups{0, 2, 6};
|
||||||
|
|
||||||
|
float auc = 0;
|
||||||
|
auc = GetMetricEval(metric.get(), {1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, labels, {}, groups,
|
||||||
|
data_split_mode);
|
||||||
|
EXPECT_EQ(auc, 1.0f);
|
||||||
|
|
||||||
|
auc = GetMetricEval(metric.get(), {1.0f, 0.5f, 0.8f, 0.3f, 0.2f, 1.0f}, labels, {}, groups,
|
||||||
|
data_split_mode);
|
||||||
|
EXPECT_EQ(auc, 1.0f);
|
||||||
|
|
||||||
|
auc = GetMetricEval(metric.get(), {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
||||||
|
{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, {}, groups, data_split_mode);
|
||||||
|
ASSERT_TRUE(std::isnan(auc));
|
||||||
|
|
||||||
|
// Incorrect label
|
||||||
|
ASSERT_THROW(GetMetricEval(metric.get(), {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
||||||
|
{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 3.0f}, {}, groups, data_split_mode),
|
||||||
|
dmlc::Error);
|
||||||
|
|
||||||
|
// AUCPR with groups and no weights
|
||||||
|
EXPECT_NEAR(
|
||||||
|
GetMetricEval(metric.get(),
|
||||||
|
{0.87f, 0.31f, 0.40f, 0.42f, 0.25f, 0.66f, 0.95f, 0.09f, 0.10f, 0.97f,
|
||||||
|
0.76f, 0.69f, 0.15f, 0.20f, 0.30f, 0.14f, 0.07f, 0.58f, 0.61f, 0.08f},
|
||||||
|
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1}, {}, // weights
|
||||||
|
{0, 2, 5, 9, 14, 20}, // group info
|
||||||
|
data_split_mode),
|
||||||
|
0.556021f, 0.001f);
|
||||||
|
}
|
||||||
|
} // namespace metric
|
||||||
|
} // namespace xgboost
|
||||||
@ -1,347 +1,108 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2018-2023 by XGBoost contributors
|
* Copyright 2018-2023 by XGBoost contributors
|
||||||
*/
|
*/
|
||||||
#include <xgboost/json.h>
|
#include "test_elementwise_metric.h"
|
||||||
#include <xgboost/metric.h>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "../../../src/common/linalg_op.h"
|
|
||||||
#include "../helpers.h"
|
|
||||||
|
|
||||||
namespace xgboost {
|
|
||||||
namespace {
|
|
||||||
inline void CheckDeterministicMetricElementWise(StringView name, int32_t device) {
|
|
||||||
auto ctx = CreateEmptyGenericParam(device);
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create(name.c_str(), &ctx)};
|
|
||||||
|
|
||||||
HostDeviceVector<float> predts;
|
|
||||||
size_t n_samples = 2048;
|
|
||||||
|
|
||||||
auto p_fmat = EmptyDMatrix();
|
|
||||||
MetaInfo& info = p_fmat->Info();
|
|
||||||
info.labels.Reshape(n_samples, 1);
|
|
||||||
info.num_row_ = n_samples;
|
|
||||||
auto &h_labels = info.labels.Data()->HostVector();
|
|
||||||
auto &h_predts = predts.HostVector();
|
|
||||||
|
|
||||||
SimpleLCG lcg;
|
|
||||||
SimpleRealUniformDistribution<float> dist{0.0f, 1.0f};
|
|
||||||
|
|
||||||
h_labels.resize(n_samples);
|
|
||||||
h_predts.resize(n_samples);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n_samples; ++i) {
|
|
||||||
h_predts[i] = dist(&lcg);
|
|
||||||
h_labels[i] = dist(&lcg);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = metric->Evaluate(predts, p_fmat);
|
|
||||||
for (size_t i = 0; i < 8; ++i) {
|
|
||||||
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // anonymous namespace
|
|
||||||
} // namespace xgboost
|
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
namespace metric {
|
namespace metric {
|
||||||
|
TEST(Metric, DeclareUnifiedTest(RMSE)) { VerifyRMSE(); }
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(RMSE)) {
|
TEST(Metric, DeclareUnifiedTest(RMSLE)) { VerifyRMSLE(); }
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("rmse", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "rmse");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.6403f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
2.8284f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.6708f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"rmse"}, GPUIDX);
|
TEST(Metric, DeclareUnifiedTest(MAE)) { VerifyMAE(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(MAPE)) { VerifyMAPE(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(MPHE)) { VerifyMPHE(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(LogLoss)) { VerifyLogLoss(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(Error)) { VerifyError(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(PoissonNegLogLik)) { VerifyPoissonNegLogLik(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(MultiRMSE)) { VerifyMultiRMSE(); }
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(Quantile)) { VerifyQuantile(); }
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSERowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyRMSE, DataSplitMode::kRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(RMSLE)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSEColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRMSE, DataSplitMode::kCol);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("rmsle", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "rmsle");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
|
||||||
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f}),
|
|
||||||
0.4063f, 1e-4);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
|
||||||
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
{ 0, -1, 1, -9, 9}),
|
|
||||||
0.6212f, 1e-4);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
|
||||||
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
{ 0, 1, 2, 9, 8}),
|
|
||||||
0.2415f, 1e-4);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"rmsle"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MAE)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSLERowSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRMSLE, DataSplitMode::kRow);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("mae", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "mae");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.5f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
8.0f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.54f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"mae"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MAPE)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSLEColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyRMSLE, DataSplitMode::kCol);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("mape", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "mape");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {150, 300}, {100, 200}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{50, 400, 500, 4000},
|
|
||||||
{100, 200, 500, 1000}),
|
|
||||||
1.125f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{50, 400, 500, 4000},
|
|
||||||
{100, 200, 500, 1000},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
-26.5f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{50, 400, 500, 4000},
|
|
||||||
{100, 200, 500, 1000},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
1.3250f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"mape"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MPHE)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAERowSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAE, DataSplitMode::kRow);
|
||||||
std::unique_ptr<xgboost::Metric> metric{xgboost::Metric::Create("mphe", &ctx)};
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "mphe");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(),
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.1751f, 1e-4);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(),
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
3.4037f, 1e-4);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(),
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.1922f, 1e-4);
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"mphe"}, GPUIDX);
|
|
||||||
|
|
||||||
metric->Configure({{"huber_slope", "0.1"}});
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(),
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.0461686f, 1e-4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(LogLoss)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAEColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAE, DataSplitMode::kCol);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("logloss", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "logloss");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.5f, 1e-17f, 1.0f+1e-17f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.1996f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
1.2039f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
21.9722f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
1.3138f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"logloss"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(Error)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPERowSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAPE, DataSplitMode::kRow);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("error", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "error");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.5f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
10.0f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.55f, 0.001f);
|
|
||||||
|
|
||||||
EXPECT_ANY_THROW(xgboost::Metric::Create("error@abc", &ctx));
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
metric = xgboost::Metric::Create("error@0.5f", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
EXPECT_STREQ(metric->Name(), "error");
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
metric = xgboost::Metric::Create("error@0.1", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "error@0.1");
|
|
||||||
EXPECT_STREQ(metric->Name(), "error@0.1");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{-0.1f, -0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.25f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{-0.1f, -0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
9.0f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{-0.1f, -0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
0.45f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"error@0.5"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(PoissionNegLogLik)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPEColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAPE, DataSplitMode::kCol);
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("poisson-nloglik", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "poisson-nloglik");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.5f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.5f, 1e-17f, 1.0f+1e-17f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.6263f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
1.1019f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ -1, 1, 9, -9}),
|
|
||||||
13.3750f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1},
|
|
||||||
{ 1, 2, 9, 8}),
|
|
||||||
1.5783f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
|
|
||||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"poisson-nloglik"}, GPUIDX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MultiRMSE)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MPHERowSplit) {
|
||||||
size_t n_samples = 32, n_targets = 8;
|
RunWithInMemoryCommunicator(world_size_, &VerifyMPHE, DataSplitMode::kRow);
|
||||||
linalg::Tensor<float, 2> y{{n_samples, n_targets}, GPUIDX};
|
|
||||||
auto &h_y = y.Data()->HostVector();
|
|
||||||
std::iota(h_y.begin(), h_y.end(), 0);
|
|
||||||
|
|
||||||
HostDeviceVector<float> predt(n_samples * n_targets, 0);
|
|
||||||
|
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("rmse", &ctx)};
|
|
||||||
metric->Configure({});
|
|
||||||
|
|
||||||
auto loss = GetMultiMetricEval(metric.get(), predt, y);
|
|
||||||
std::vector<float> weights(n_samples, 1);
|
|
||||||
auto loss_w = GetMultiMetricEval(metric.get(), predt, y, weights);
|
|
||||||
|
|
||||||
std::transform(h_y.cbegin(), h_y.cend(), h_y.begin(), [](auto &v) { return v * v; });
|
|
||||||
auto ret = std::sqrt(std::accumulate(h_y.cbegin(), h_y.cend(), 1.0, std::plus<>{}) / h_y.size());
|
|
||||||
ASSERT_FLOAT_EQ(ret, loss);
|
|
||||||
ASSERT_FLOAT_EQ(ret, loss_w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(Quantile)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MPHEColumnSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyMPHE, DataSplitMode::kCol);
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("quantile", &ctx)};
|
}
|
||||||
|
|
||||||
HostDeviceVector<float> predts{0.1f, 0.9f, 0.1f, 0.9f};
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), LogLossRowSplit) {
|
||||||
std::vector<float> labels{0.5f, 0.5f, 0.9f, 0.1f};
|
RunWithInMemoryCommunicator(world_size_, &VerifyLogLoss, DataSplitMode::kRow);
|
||||||
std::vector<float> weights{0.2f, 0.4f,0.6f, 0.8f};
|
}
|
||||||
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.0]"}});
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), LogLossColumnSplit) {
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights), 0.400f, 0.001f);
|
RunWithInMemoryCommunicator(world_size_, &VerifyLogLoss, DataSplitMode::kCol);
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.2]"}});
|
}
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights), 0.376f, 0.001f);
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.4]"}});
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights), 0.352f, 0.001f);
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.8]"}});
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights), 0.304f, 0.001f);
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[1.0]"}});
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights), 0.28f, 0.001f);
|
|
||||||
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.0]"}});
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), ErrorRowSplit) {
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels), 0.3f, 0.001f);
|
RunWithInMemoryCommunicator(world_size_, &VerifyError, DataSplitMode::kRow);
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.2]"}});
|
}
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels), 0.3f, 0.001f);
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.4]"}});
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), ErrorColumnSplit) {
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels), 0.3f, 0.001f);
|
RunWithInMemoryCommunicator(world_size_, &VerifyError, DataSplitMode::kCol);
|
||||||
metric->Configure(Args{{"quantile_alpha", "[0.8]"}});
|
}
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels), 0.3f, 0.001f);
|
|
||||||
metric->Configure(Args{{"quantile_alpha", "[1.0]"}});
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PoissonNegLogLikRowSplit) {
|
||||||
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels), 0.3f, 0.001f);
|
RunWithInMemoryCommunicator(world_size_, &VerifyPoissonNegLogLik, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PoissonNegLogLikColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyPoissonNegLogLik, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiRMSERowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiRMSE, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiRMSEColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiRMSE, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), QuantileRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyQuantile, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), QuantileColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyQuantile, DataSplitMode::kCol);
|
||||||
}
|
}
|
||||||
} // namespace metric
|
} // namespace metric
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
385
tests/cpp/metric/test_elementwise_metric.h
Normal file
385
tests/cpp/metric/test_elementwise_metric.h
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-2023 by XGBoost contributors
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <xgboost/json.h>
|
||||||
|
#include <xgboost/metric.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "../../../src/common/linalg_op.h"
|
||||||
|
#include "../helpers.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace metric {
|
||||||
|
|
||||||
|
inline void CheckDeterministicMetricElementWise(StringView name, int32_t device) {
|
||||||
|
auto ctx = CreateEmptyGenericParam(device);
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create(name.c_str(), &ctx)};
|
||||||
|
|
||||||
|
HostDeviceVector<float> predts;
|
||||||
|
size_t n_samples = 2048;
|
||||||
|
|
||||||
|
auto p_fmat = EmptyDMatrix();
|
||||||
|
MetaInfo& info = p_fmat->Info();
|
||||||
|
info.labels.Reshape(n_samples, 1);
|
||||||
|
info.num_row_ = n_samples;
|
||||||
|
auto &h_labels = info.labels.Data()->HostVector();
|
||||||
|
auto &h_predts = predts.HostVector();
|
||||||
|
|
||||||
|
SimpleLCG lcg;
|
||||||
|
SimpleRealUniformDistribution<float> dist{0.0f, 1.0f};
|
||||||
|
|
||||||
|
h_labels.resize(n_samples);
|
||||||
|
h_predts.resize(n_samples);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n_samples; ++i) {
|
||||||
|
h_predts[i] = dist(&lcg);
|
||||||
|
h_labels[i] = dist(&lcg);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = metric->Evaluate(predts, p_fmat);
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("rmse", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "rmse");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.6403f, 0.001f);
|
||||||
|
auto expected = 2.8284f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected = sqrt(8.0f * collective::GetWorldSize());
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.6708f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"rmse"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyRMSLE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("rmsle", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "rmsle");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
||||||
|
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, {}, {}, data_split_mode),
|
||||||
|
0.4063f, 1e-4);
|
||||||
|
auto expected = 0.6212f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected = sqrt(0.3859f * collective::GetWorldSize());
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
||||||
|
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
|
||||||
|
{ 0, -1, 1, -9, 9}, {}, data_split_mode),
|
||||||
|
expected, 1e-4);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.2f, 0.4f, 0.8f, 1.6f},
|
||||||
|
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
|
||||||
|
{ 0, 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.2415f, 1e-4);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"rmsle"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMAE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("mae", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "mae");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.5f, 0.001f);
|
||||||
|
auto expected = 8.0f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.54f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"mae"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMAPE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("mape", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "mape");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {150, 300}, {100, 200}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{50, 400, 500, 4000},
|
||||||
|
{100, 200, 500, 1000}, {}, {}, data_split_mode),
|
||||||
|
1.125f, 0.001f);
|
||||||
|
auto expected = -26.5f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{50, 400, 500, 4000},
|
||||||
|
{100, 200, 500, 1000},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{50, 400, 500, 4000},
|
||||||
|
{100, 200, 500, 1000},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
1.3250f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"mape"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMPHE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<xgboost::Metric> metric{xgboost::Metric::Create("mphe", &ctx)};
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "mphe");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(),
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.1751f, 1e-4);
|
||||||
|
auto expected = 3.40375f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(),
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 1e-4);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(),
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.1922f, 1e-4);
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"mphe"}, GPUIDX);
|
||||||
|
|
||||||
|
metric->Configure({{"huber_slope", "0.1"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(),
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.0461686f, 1e-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyLogLoss(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("logloss", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "logloss");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.5f, 1e-17f, 1.0f+1e-17f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.1996f, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
1.2039f, 0.001f);
|
||||||
|
auto expected = 21.9722f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
1.3138f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"logloss"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyError(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("error", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "error");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.5f, 0.001f);
|
||||||
|
auto expected = 10.0f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.55f, 0.001f);
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(xgboost::Metric::Create("error@abc", &ctx));
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
metric = xgboost::Metric::Create("error@0.5f", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
EXPECT_STREQ(metric->Name(), "error");
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
metric = xgboost::Metric::Create("error@0.1", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "error@0.1");
|
||||||
|
EXPECT_STREQ(metric->Name(), "error@0.1");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{-0.1f, -0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.25f, 0.001f);
|
||||||
|
expected = 9.0f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{-0.1f, -0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{-0.1f, -0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
0.45f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"error@0.5"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyPoissonNegLogLik(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("poisson-nloglik", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "poisson-nloglik");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0.5f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.5f, 1e-17f, 1.0f+1e-17f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.6263f, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
1.1019f, 0.001f);
|
||||||
|
auto expected = 13.3750f;
|
||||||
|
if (collective::IsDistributed() && data_split_mode == DataSplitMode::kRow) {
|
||||||
|
expected *= collective::GetWorldSize();
|
||||||
|
}
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ -1, 1, 9, -9}, {}, data_split_mode),
|
||||||
|
expected, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1},
|
||||||
|
{ 1, 2, 9, 8}, {}, data_split_mode),
|
||||||
|
1.5783f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
|
||||||
|
CheckDeterministicMetricElementWise(StringView{"poisson-nloglik"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMultiRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
size_t n_samples = 32, n_targets = 8;
|
||||||
|
linalg::Tensor<float, 2> y{{n_samples, n_targets}, GPUIDX};
|
||||||
|
auto &h_y = y.Data()->HostVector();
|
||||||
|
std::iota(h_y.begin(), h_y.end(), 0);
|
||||||
|
|
||||||
|
HostDeviceVector<float> predt(n_samples * n_targets, 0);
|
||||||
|
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("rmse", &ctx)};
|
||||||
|
metric->Configure({});
|
||||||
|
|
||||||
|
auto loss = GetMultiMetricEval(metric.get(), predt, y, {}, {}, data_split_mode);
|
||||||
|
std::vector<float> weights(n_samples, 1);
|
||||||
|
auto loss_w = GetMultiMetricEval(metric.get(), predt, y, weights, {}, data_split_mode);
|
||||||
|
|
||||||
|
std::transform(h_y.cbegin(), h_y.cend(), h_y.begin(), [](auto &v) { return v * v; });
|
||||||
|
auto ret = std::sqrt(std::accumulate(h_y.cbegin(), h_y.cend(), 1.0, std::plus<>{}) / h_y.size());
|
||||||
|
ASSERT_FLOAT_EQ(ret, loss);
|
||||||
|
ASSERT_FLOAT_EQ(ret, loss_w);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyQuantile(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("quantile", &ctx)};
|
||||||
|
|
||||||
|
HostDeviceVector<float> predts{0.1f, 0.9f, 0.1f, 0.9f};
|
||||||
|
std::vector<float> labels{0.5f, 0.5f, 0.9f, 0.1f};
|
||||||
|
std::vector<float> weights{0.2f, 0.4f, 0.6f, 0.8f};
|
||||||
|
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.0]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights, {}, data_split_mode), 0.400f,
|
||||||
|
0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.2]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights, {}, data_split_mode), 0.376f,
|
||||||
|
0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.4]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights, {}, data_split_mode), 0.352f,
|
||||||
|
0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.8]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights, {}, data_split_mode), 0.304f,
|
||||||
|
0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[1.0]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, weights, {}, data_split_mode), 0.28f,
|
||||||
|
0.001f);
|
||||||
|
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.0]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode), 0.3f, 0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.2]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode), 0.3f, 0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.4]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode), 0.3f, 0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[0.8]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode), 0.3f, 0.001f);
|
||||||
|
metric->Configure(Args{{"quantile_alpha", "[1.0]"}});
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric.get(), predts, labels, {}, {}, data_split_mode), 0.3f, 0.001f);
|
||||||
|
}
|
||||||
|
} // namespace metric
|
||||||
|
} // namespace xgboost
|
||||||
@ -1,87 +1,29 @@
|
|||||||
// Copyright by Contributors
|
// Copyright by Contributors
|
||||||
#include <xgboost/metric.h>
|
#include "test_multiclass_metric.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "../helpers.h"
|
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
inline void CheckDeterministicMetricMultiClass(StringView name, int32_t device) {
|
namespace metric {
|
||||||
auto ctx = CreateEmptyGenericParam(device);
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create(name.c_str(), &ctx)};
|
|
||||||
|
|
||||||
HostDeviceVector<float> predts;
|
TEST(Metric, DeclareUnifiedTest(MultiClassError)) { VerifyMultiClassError(); }
|
||||||
auto p_fmat = EmptyDMatrix();
|
|
||||||
MetaInfo& info = p_fmat->Info();
|
|
||||||
auto &h_predts = predts.HostVector();
|
|
||||||
|
|
||||||
SimpleLCG lcg;
|
TEST(Metric, DeclareUnifiedTest(MultiClassLogLoss)) { VerifyMultiClassLogLoss(); }
|
||||||
|
|
||||||
size_t n_samples = 2048, n_classes = 4;
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassErrorRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassError, DataSplitMode::kRow);
|
||||||
info.labels.Reshape(n_samples);
|
|
||||||
auto &h_labels = info.labels.Data()->HostVector();
|
|
||||||
h_predts.resize(n_samples * n_classes);
|
|
||||||
|
|
||||||
{
|
|
||||||
SimpleRealUniformDistribution<float> dist{0.0f, static_cast<float>(n_classes)};
|
|
||||||
for (size_t i = 0; i < n_samples; ++i) {
|
|
||||||
h_labels[i] = dist(&lcg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SimpleRealUniformDistribution<float> dist{0.0f, 1.0f};
|
|
||||||
for (size_t i = 0; i < n_samples * n_classes; ++i) {
|
|
||||||
h_predts[i] = dist(&lcg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = metric->Evaluate(predts, p_fmat);
|
|
||||||
for (size_t i = 0; i < 8; ++i) {
|
|
||||||
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassErrorColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassError, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassLogLossRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassLogLoss, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassLogLossColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyMultiClassLogLoss, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
} // namespace metric
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|
||||||
inline void TestMultiClassError(int device) {
|
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(device);
|
|
||||||
ctx.gpu_id = device;
|
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("merror", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "merror");
|
|
||||||
EXPECT_ANY_THROW(GetMetricEval(metric, {0}, {0, 0}));
|
|
||||||
EXPECT_NEAR(GetMetricEval(
|
|
||||||
metric, {1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 2}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
|
|
||||||
{0, 1, 2}),
|
|
||||||
0.666f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MultiClassError)) {
|
|
||||||
TestMultiClassError(GPUIDX);
|
|
||||||
xgboost::CheckDeterministicMetricMultiClass(xgboost::StringView{"merror"}, GPUIDX);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void TestMultiClassLogLoss(int device) {
|
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(device);
|
|
||||||
ctx.gpu_id = device;
|
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("mlogloss", &ctx);
|
|
||||||
metric->Configure({});
|
|
||||||
ASSERT_STREQ(metric->Name(), "mlogloss");
|
|
||||||
EXPECT_ANY_THROW(GetMetricEval(metric, {0}, {0, 0}));
|
|
||||||
EXPECT_NEAR(GetMetricEval(
|
|
||||||
metric, {1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 2}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
|
|
||||||
{0, 1, 2}),
|
|
||||||
2.302f, 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MultiClassLogLoss)) {
|
|
||||||
TestMultiClassLogLoss(GPUIDX);
|
|
||||||
xgboost::CheckDeterministicMetricMultiClass(xgboost::StringView{"mlogloss"}, GPUIDX);
|
|
||||||
}
|
|
||||||
|
|||||||
91
tests/cpp/metric/test_multiclass_metric.h
Normal file
91
tests/cpp/metric/test_multiclass_metric.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright by Contributors
|
||||||
|
#include <xgboost/metric.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../helpers.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace metric {
|
||||||
|
|
||||||
|
inline void CheckDeterministicMetricMultiClass(StringView name, int32_t device) {
|
||||||
|
auto ctx = CreateEmptyGenericParam(device);
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create(name.c_str(), &ctx)};
|
||||||
|
|
||||||
|
HostDeviceVector<float> predts;
|
||||||
|
auto p_fmat = EmptyDMatrix();
|
||||||
|
MetaInfo& info = p_fmat->Info();
|
||||||
|
auto &h_predts = predts.HostVector();
|
||||||
|
|
||||||
|
SimpleLCG lcg;
|
||||||
|
|
||||||
|
size_t n_samples = 2048, n_classes = 4;
|
||||||
|
|
||||||
|
info.labels.Reshape(n_samples);
|
||||||
|
auto &h_labels = info.labels.Data()->HostVector();
|
||||||
|
h_predts.resize(n_samples * n_classes);
|
||||||
|
|
||||||
|
{
|
||||||
|
SimpleRealUniformDistribution<float> dist{0.0f, static_cast<float>(n_classes)};
|
||||||
|
for (size_t i = 0; i < n_samples; ++i) {
|
||||||
|
h_labels[i] = dist(&lcg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SimpleRealUniformDistribution<float> dist{0.0f, 1.0f};
|
||||||
|
for (size_t i = 0; i < n_samples * n_classes; ++i) {
|
||||||
|
h_predts[i] = dist(&lcg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = metric->Evaluate(predts, p_fmat);
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TestMultiClassError(int device, DataSplitMode data_split_mode) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(device);
|
||||||
|
ctx.gpu_id = device;
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("merror", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "merror");
|
||||||
|
EXPECT_ANY_THROW(GetMetricEval(metric, {0}, {0, 0}, {}, {}, data_split_mode));
|
||||||
|
EXPECT_NEAR(GetMetricEval(
|
||||||
|
metric, {1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 2}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
|
||||||
|
{0, 1, 2}, {}, {}, data_split_mode),
|
||||||
|
0.666f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMultiClassError(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
TestMultiClassError(GPUIDX, data_split_mode);
|
||||||
|
CheckDeterministicMetricMultiClass(StringView{"merror"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TestMultiClassLogLoss(int device, DataSplitMode data_split_mode) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(device);
|
||||||
|
ctx.gpu_id = device;
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("mlogloss", &ctx);
|
||||||
|
metric->Configure({});
|
||||||
|
ASSERT_STREQ(metric->Name(), "mlogloss");
|
||||||
|
EXPECT_ANY_THROW(GetMetricEval(metric, {0}, {0, 0}, {}, {}, data_split_mode));
|
||||||
|
EXPECT_NEAR(GetMetricEval(
|
||||||
|
metric, {1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 2}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
|
||||||
|
{0, 1, 2}, {}, {}, data_split_mode),
|
||||||
|
2.302f, 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMultiClassLogLoss(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
TestMultiClassLogLoss(GPUIDX, data_split_mode);
|
||||||
|
CheckDeterministicMetricMultiClass(StringView{"mlogloss"}, GPUIDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace metric
|
||||||
|
} // namespace xgboost
|
||||||
@ -11,16 +11,20 @@
|
|||||||
#include <memory> // for unique_ptr
|
#include <memory> // for unique_ptr
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "test_rank_metric.h"
|
||||||
#include "../helpers.h" // for GetMetricEval, CreateEmptyGe...
|
#include "../helpers.h" // for GetMetricEval, CreateEmptyGe...
|
||||||
#include "xgboost/base.h" // for bst_float, kRtEps
|
#include "xgboost/base.h" // for bst_float, kRtEps
|
||||||
#include "xgboost/host_device_vector.h" // for HostDeviceVector
|
#include "xgboost/host_device_vector.h" // for HostDeviceVector
|
||||||
#include "xgboost/json.h" // for Json, String, Object
|
#include "xgboost/json.h" // for Json, String, Object
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace metric {
|
||||||
|
|
||||||
#if !defined(__CUDACC__)
|
#if !defined(__CUDACC__)
|
||||||
TEST(Metric, AMS) {
|
TEST(Metric, AMS) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
||||||
EXPECT_ANY_THROW(xgboost::Metric::Create("ams", &ctx));
|
EXPECT_ANY_THROW(Metric::Create("ams", &ctx));
|
||||||
xgboost::Metric* metric = xgboost::Metric::Create("ams@0.5f", &ctx);
|
Metric* metric = Metric::Create("ams@0.5f", &ctx);
|
||||||
ASSERT_STREQ(metric->Name(), "ams@0.5");
|
ASSERT_STREQ(metric->Name(), "ams@0.5");
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
@ -29,7 +33,7 @@ TEST(Metric, AMS) {
|
|||||||
0.29710f, 0.001f);
|
0.29710f, 0.001f);
|
||||||
|
|
||||||
delete metric;
|
delete metric;
|
||||||
metric = xgboost::Metric::Create("ams@0", &ctx);
|
metric = Metric::Create("ams@0", &ctx);
|
||||||
ASSERT_STREQ(metric->Name(), "ams@0");
|
ASSERT_STREQ(metric->Name(), "ams@0");
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
|
||||||
|
|
||||||
@ -37,172 +41,44 @@ TEST(Metric, AMS) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(Precision)) {
|
TEST(Metric, DeclareUnifiedTest(Precision)) { VerifyPrecision(); }
|
||||||
// When the limit for precision is not given, it takes the limit at
|
|
||||||
// std::numeric_limits<unsigned>::max(); hence all values are very small
|
|
||||||
// NOTE(AbdealiJK): Maybe this should be fixed to be num_row by default.
|
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
|
||||||
xgboost::Metric * metric = xgboost::Metric::Create("pre", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "pre");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0, 1e-7);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0, 1e-7);
|
|
||||||
|
|
||||||
delete metric;
|
TEST(Metric, DeclareUnifiedTest(NDCG)) { VerifyNDCG(); }
|
||||||
metric = xgboost::Metric::Create("pre@2", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "pre@2");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.5f, 1e-7);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.5f, 0.001f);
|
|
||||||
|
|
||||||
EXPECT_ANY_THROW(GetMetricEval(metric, {0, 1}, {}));
|
TEST(Metric, DeclareUnifiedTest(MAP)) { VerifyMAP(); }
|
||||||
|
|
||||||
delete metric;
|
TEST(Metric, DeclareUnifiedTest(NDCGExpGain)) { VerifyNDCGExpGain(); }
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PrecisionRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyPrecision, DataSplitMode::kRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace xgboost {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PrecisionColumnSplit) {
|
||||||
namespace metric {
|
RunWithInMemoryCommunicator(world_size_, &VerifyPrecision, DataSplitMode::kCol);
|
||||||
TEST(Metric, DeclareUnifiedTest(NDCG)) {
|
|
||||||
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
|
||||||
Metric * metric = xgboost::Metric::Create("ndcg", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "ndcg");
|
|
||||||
EXPECT_ANY_THROW(GetMetricEval(metric, {0, 1}, {}));
|
|
||||||
ASSERT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
{}), 1, 1e-10);
|
|
||||||
ASSERT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.6509f, 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("ndcg@2", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "ndcg@2");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.3868f, 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("ndcg@-", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "ndcg-");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
{}), 0, 1e-10);
|
|
||||||
ASSERT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1.f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.6509f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("ndcg-", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "ndcg-");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
{}), 0, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1.f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.6509f, 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("ndcg@2-", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "ndcg@2-");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1.f, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
1.f - 0.3868f, 1.f - 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(MAP)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGRowSplit) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyNDCG, DataSplitMode::kRow);
|
||||||
Metric * metric = xgboost::Metric::Create("map", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "map");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1, kRtEps);
|
|
||||||
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.5f, 0.001f);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
std::vector<xgboost::bst_float>{}), 1, 1e-10);
|
|
||||||
|
|
||||||
// Rank metric with group info
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.2f, 0.8f, 0.4f, 1.7f},
|
|
||||||
{1, 1, 1, 0, 1, 0}, // Labels
|
|
||||||
{}, // Weights
|
|
||||||
{0, 2, 5, 6}), // Group info
|
|
||||||
0.8611f, 0.001f);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("map@-", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "map-");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
{}), 0, 1e-10);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("map-", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "map-");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
|
||||||
{}), 0, 1e-10);
|
|
||||||
|
|
||||||
delete metric;
|
|
||||||
metric = xgboost::Metric::Create("map@2", &ctx);
|
|
||||||
ASSERT_STREQ(metric->Name(), "map@2");
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 1, 1e-10);
|
|
||||||
EXPECT_NEAR(GetMetricEval(metric,
|
|
||||||
{0.1f, 0.9f, 0.1f, 0.9f},
|
|
||||||
{ 0, 0, 1, 1}),
|
|
||||||
0.25f, 0.001f);
|
|
||||||
delete metric;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(NDCGExpGain)) {
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGColumnSplit) {
|
||||||
Context ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
RunWithInMemoryCommunicator(world_size_, &VerifyNDCG, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
auto p_fmat = xgboost::RandomDataGenerator{0, 0, 0}.GenerateDMatrix();
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPRowSplit) {
|
||||||
MetaInfo& info = p_fmat->Info();
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAP, DataSplitMode::kRow);
|
||||||
info.labels = linalg::Matrix<float>{{10.0f, 0.0f, 0.0f, 1.0f, 5.0f}, {5}, ctx.gpu_id};
|
}
|
||||||
info.num_row_ = info.labels.Shape(0);
|
|
||||||
info.group_ptr_.resize(2);
|
|
||||||
info.group_ptr_[0] = 0;
|
|
||||||
info.group_ptr_[1] = info.num_row_;
|
|
||||||
HostDeviceVector<float> predt{{0.1f, 0.2f, 0.3f, 4.0f, 70.0f}};
|
|
||||||
|
|
||||||
std::unique_ptr<Metric> metric{Metric::Create("ndcg", &ctx)};
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPColumnSplit) {
|
||||||
Json config{Object{}};
|
RunWithInMemoryCommunicator(world_size_, &VerifyMAP, DataSplitMode::kCol);
|
||||||
config["name"] = String{"ndcg"};
|
}
|
||||||
config["lambdarank_param"] = Object{};
|
|
||||||
config["lambdarank_param"]["ndcg_exp_gain"] = String{"true"};
|
|
||||||
config["lambdarank_param"]["lambdarank_num_pair_per_sample"] = String{"32"};
|
|
||||||
metric->LoadConfig(config);
|
|
||||||
|
|
||||||
auto ndcg = metric->Evaluate(predt, p_fmat);
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGExpGainRowSplit) {
|
||||||
ASSERT_NEAR(ndcg, 0.409738f, kRtEps);
|
RunWithInMemoryCommunicator(world_size_, &VerifyNDCGExpGain, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
config["lambdarank_param"]["ndcg_exp_gain"] = String{"false"};
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGExpGainColumnSplit) {
|
||||||
metric->LoadConfig(config);
|
RunWithInMemoryCommunicator(world_size_, &VerifyNDCGExpGain, DataSplitMode::kCol);
|
||||||
|
|
||||||
ndcg = metric->Evaluate(predt, p_fmat);
|
|
||||||
ASSERT_NEAR(ndcg, 0.695694f, kRtEps);
|
|
||||||
|
|
||||||
predt.HostVector() = info.labels.Data()->HostVector();
|
|
||||||
ndcg = metric->Evaluate(predt, p_fmat);
|
|
||||||
ASSERT_NEAR(ndcg, 1.0, kRtEps);
|
|
||||||
}
|
}
|
||||||
} // namespace metric
|
} // namespace metric
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
191
tests/cpp/metric/test_rank_metric.h
Normal file
191
tests/cpp/metric/test_rank_metric.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2016-2023 by XGBoost Contributors
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <gtest/gtest.h> // for Test, EXPECT_NEAR, ASSERT_STREQ
|
||||||
|
#include <xgboost/context.h> // for Context
|
||||||
|
#include <xgboost/data.h> // for MetaInfo, DMatrix
|
||||||
|
#include <xgboost/linalg.h> // for Matrix
|
||||||
|
#include <xgboost/metric.h> // for Metric
|
||||||
|
|
||||||
|
#include <algorithm> // for max
|
||||||
|
#include <memory> // for unique_ptr
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "../helpers.h" // for GetMetricEval, CreateEmptyGe...
|
||||||
|
#include "xgboost/base.h" // for bst_float, kRtEps
|
||||||
|
#include "xgboost/host_device_vector.h" // for HostDeviceVector
|
||||||
|
#include "xgboost/json.h" // for Json, String, Object
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace metric {
|
||||||
|
|
||||||
|
inline void VerifyPrecision(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
// When the limit for precision is not given, it takes the limit at
|
||||||
|
// std::numeric_limits<unsigned>::max(); hence all values are very small
|
||||||
|
// NOTE(AbdealiJK): Maybe this should be fixed to be num_row by default.
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
xgboost::Metric * metric = xgboost::Metric::Create("pre", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "pre");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0, 1e-7);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0, 1e-7);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("pre@2", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "pre@2");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 0.5f, 1e-7);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.5f, 0.001f);
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(GetMetricEval(metric, {0, 1}, {}, {}, {}, data_split_mode));
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyNDCG(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = CreateEmptyGenericParam(GPUIDX);
|
||||||
|
Metric * metric = xgboost::Metric::Create("ndcg", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "ndcg");
|
||||||
|
EXPECT_ANY_THROW(GetMetricEval(metric, {0, 1}, {}, {}, {}, data_split_mode));
|
||||||
|
ASSERT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
{}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
ASSERT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.6509f, 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("ndcg@2", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "ndcg@2");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.3868f, 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("ndcg@-", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "ndcg-");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
{}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
ASSERT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1.f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.6509f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("ndcg-", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "ndcg-");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
{}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1.f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.6509f, 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("ndcg@2-", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "ndcg@2-");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1.f, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
1.f - 0.3868f, 1.f - 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyMAP(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
Metric * metric = xgboost::Metric::Create("map", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "map");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1, kRtEps);
|
||||||
|
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.5f, 0.001f);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
std::vector<xgboost::bst_float>{}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
|
||||||
|
// Rank metric with group info
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.2f, 0.8f, 0.4f, 1.7f},
|
||||||
|
{1, 1, 1, 0, 1, 0}, // Labels
|
||||||
|
{}, // Weights
|
||||||
|
{0, 2, 5, 6}, // Group info
|
||||||
|
data_split_mode),
|
||||||
|
0.8611f, 0.001f);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("map@-", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "map-");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
{}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("map-", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "map-");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
xgboost::HostDeviceVector<xgboost::bst_float>{},
|
||||||
|
{}, {}, {}, data_split_mode), 0, 1e-10);
|
||||||
|
|
||||||
|
delete metric;
|
||||||
|
metric = xgboost::Metric::Create("map@2", &ctx);
|
||||||
|
ASSERT_STREQ(metric->Name(), "map@2");
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1, 1e-10);
|
||||||
|
EXPECT_NEAR(GetMetricEval(metric,
|
||||||
|
{0.1f, 0.9f, 0.1f, 0.9f},
|
||||||
|
{ 0, 0, 1, 1}, {}, {}, data_split_mode),
|
||||||
|
0.25f, 0.001f);
|
||||||
|
delete metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VerifyNDCGExpGain(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
|
Context ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
|
auto p_fmat = xgboost::RandomDataGenerator{0, 0, 0}.GenerateDMatrix();
|
||||||
|
MetaInfo& info = p_fmat->Info();
|
||||||
|
info.labels = linalg::Matrix<float>{{10.0f, 0.0f, 0.0f, 1.0f, 5.0f}, {5}, ctx.gpu_id};
|
||||||
|
info.num_row_ = info.labels.Shape(0);
|
||||||
|
info.group_ptr_.resize(2);
|
||||||
|
info.group_ptr_[0] = 0;
|
||||||
|
info.group_ptr_[1] = info.num_row_;
|
||||||
|
info.data_split_mode = data_split_mode;
|
||||||
|
HostDeviceVector<float> predt{{0.1f, 0.2f, 0.3f, 4.0f, 70.0f}};
|
||||||
|
|
||||||
|
std::unique_ptr<Metric> metric{Metric::Create("ndcg", &ctx)};
|
||||||
|
Json config{Object{}};
|
||||||
|
config["name"] = String{"ndcg"};
|
||||||
|
config["lambdarank_param"] = Object{};
|
||||||
|
config["lambdarank_param"]["ndcg_exp_gain"] = String{"true"};
|
||||||
|
config["lambdarank_param"]["lambdarank_num_pair_per_sample"] = String{"32"};
|
||||||
|
metric->LoadConfig(config);
|
||||||
|
|
||||||
|
auto ndcg = metric->Evaluate(predt, p_fmat);
|
||||||
|
ASSERT_NEAR(ndcg, 0.409738f, kRtEps);
|
||||||
|
|
||||||
|
config["lambdarank_param"]["ndcg_exp_gain"] = String{"false"};
|
||||||
|
metric->LoadConfig(config);
|
||||||
|
|
||||||
|
ndcg = metric->Evaluate(predt, p_fmat);
|
||||||
|
ASSERT_NEAR(ndcg, 0.695694f, kRtEps);
|
||||||
|
|
||||||
|
predt.HostVector() = info.labels.Data()->HostVector();
|
||||||
|
ndcg = metric->Evaluate(predt, p_fmat);
|
||||||
|
ASSERT_NEAR(ndcg, 1.0, kRtEps);
|
||||||
|
}
|
||||||
|
} // namespace metric
|
||||||
|
} // namespace xgboost
|
||||||
@ -46,9 +46,8 @@ inline void CheckDeterministicMetricElementWise(StringView name, int32_t device)
|
|||||||
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
ASSERT_EQ(metric->Evaluate(predts, p_fmat), result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(AFTNegLogLik)) {
|
void VerifyAFTNegLogLik(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,10 +58,11 @@ TEST(Metric, DeclareUnifiedTest(AFTNegLogLik)) {
|
|||||||
MetaInfo& info = p_fmat->Info();
|
MetaInfo& info = p_fmat->Info();
|
||||||
info.num_row_ = 4;
|
info.num_row_ = 4;
|
||||||
info.labels_lower_bound_.HostVector()
|
info.labels_lower_bound_.HostVector()
|
||||||
= { 100.0f, 0.0f, 60.0f, 16.0f };
|
= { 100.0f, 0.0f, 60.0f, 16.0f };
|
||||||
info.labels_upper_bound_.HostVector()
|
info.labels_upper_bound_.HostVector()
|
||||||
= { 100.0f, 20.0f, std::numeric_limits<bst_float>::infinity(), 200.0f };
|
= { 100.0f, 20.0f, std::numeric_limits<bst_float>::infinity(), 200.0f };
|
||||||
info.weights_.HostVector() = std::vector<bst_float>();
|
info.weights_.HostVector() = std::vector<bst_float>();
|
||||||
|
info.data_split_mode = data_split_mode;
|
||||||
HostDeviceVector<bst_float> preds(4, std::log(64));
|
HostDeviceVector<bst_float> preds(4, std::log(64));
|
||||||
|
|
||||||
struct TestCase {
|
struct TestCase {
|
||||||
@ -70,15 +70,15 @@ TEST(Metric, DeclareUnifiedTest(AFTNegLogLik)) {
|
|||||||
bst_float reference_value;
|
bst_float reference_value;
|
||||||
};
|
};
|
||||||
for (const auto& test_case : std::vector<TestCase>{ {"normal", 2.1508f}, {"logistic", 2.1804f},
|
for (const auto& test_case : std::vector<TestCase>{ {"normal", 2.1508f}, {"logistic", 2.1804f},
|
||||||
{"extreme", 2.0706f} }) {
|
{"extreme", 2.0706f} }) {
|
||||||
std::unique_ptr<Metric> metric(Metric::Create("aft-nloglik", &ctx));
|
std::unique_ptr<Metric> metric(Metric::Create("aft-nloglik", &ctx));
|
||||||
metric->Configure({ {"aft_loss_distribution", test_case.dist_type},
|
metric->Configure({ {"aft_loss_distribution", test_case.dist_type},
|
||||||
{"aft_loss_distribution_scale", "1.0"} });
|
{"aft_loss_distribution_scale", "1.0"} });
|
||||||
EXPECT_NEAR(metric->Evaluate(preds, p_fmat), test_case.reference_value, 1e-4);
|
EXPECT_NEAR(metric->Evaluate(preds, p_fmat), test_case.reference_value, 1e-4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Metric, DeclareUnifiedTest(IntervalRegressionAccuracy)) {
|
void VerifyIntervalRegressionAccuracy(DataSplitMode data_split_mode = DataSplitMode::kRow) {
|
||||||
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
auto ctx = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||||
|
|
||||||
auto p_fmat = EmptyDMatrix();
|
auto p_fmat = EmptyDMatrix();
|
||||||
@ -87,6 +87,7 @@ TEST(Metric, DeclareUnifiedTest(IntervalRegressionAccuracy)) {
|
|||||||
info.labels_lower_bound_.HostVector() = { 20.0f, 0.0f, 60.0f, 16.0f };
|
info.labels_lower_bound_.HostVector() = { 20.0f, 0.0f, 60.0f, 16.0f };
|
||||||
info.labels_upper_bound_.HostVector() = { 80.0f, 20.0f, 80.0f, 200.0f };
|
info.labels_upper_bound_.HostVector() = { 80.0f, 20.0f, 80.0f, 200.0f };
|
||||||
info.weights_.HostVector() = std::vector<bst_float>();
|
info.weights_.HostVector() = std::vector<bst_float>();
|
||||||
|
info.data_split_mode = data_split_mode;
|
||||||
HostDeviceVector<bst_float> preds(4, std::log(60.0f));
|
HostDeviceVector<bst_float> preds(4, std::log(60.0f));
|
||||||
|
|
||||||
std::unique_ptr<Metric> metric(Metric::Create("interval-regression-accuracy", &ctx));
|
std::unique_ptr<Metric> metric(Metric::Create("interval-regression-accuracy", &ctx));
|
||||||
@ -102,6 +103,27 @@ TEST(Metric, DeclareUnifiedTest(IntervalRegressionAccuracy)) {
|
|||||||
|
|
||||||
CheckDeterministicMetricElementWise(StringView{"interval-regression-accuracy"}, GPUIDX);
|
CheckDeterministicMetricElementWise(StringView{"interval-regression-accuracy"}, GPUIDX);
|
||||||
}
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(AFTNegLogLik)) { VerifyAFTNegLogLik(); }
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), AFTNegLogLikRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyAFTNegLogLik, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), AFTNegLogLikColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyAFTNegLogLik, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Metric, DeclareUnifiedTest(IntervalRegressionAccuracy)) { VerifyIntervalRegressionAccuracy(); }
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), IntervalRegressionAccuracyRowSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyIntervalRegressionAccuracy, DataSplitMode::kRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeclareUnifiedDistributedTest(MetricTest), IntervalRegressionAccuracyColumnSplit) {
|
||||||
|
RunWithInMemoryCommunicator(world_size_, &VerifyIntervalRegressionAccuracy, DataSplitMode::kCol);
|
||||||
|
}
|
||||||
|
|
||||||
// Test configuration of AFT metric
|
// Test configuration of AFT metric
|
||||||
TEST(AFTNegLogLikMetric, DeclareUnifiedTest(Configuration)) {
|
TEST(AFTNegLogLikMetric, DeclareUnifiedTest(Configuration)) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user