Initial support for multioutput regression. (#7514)
* Add num target model parameter, which is configured from input labels. * Change elementwise metric and indexing for weights. * Add demo. * Add tests.
This commit is contained in:
@@ -92,6 +92,7 @@ TEST(CAPI, ConfigIO) {
|
||||
labels[i] = i;
|
||||
}
|
||||
p_dmat->Info().labels.Data()->HostVector() = labels;
|
||||
p_dmat->Info().labels.Reshape(kRows);
|
||||
|
||||
std::shared_ptr<Learner> learner { Learner::Create(mat) };
|
||||
|
||||
@@ -126,6 +127,7 @@ TEST(CAPI, JsonModelIO) {
|
||||
labels[i] = i;
|
||||
}
|
||||
p_dmat->Info().labels.Data()->HostVector() = labels;
|
||||
p_dmat->Info().labels.Reshape(kRows);
|
||||
|
||||
std::shared_ptr<Learner> learner { Learner::Create(mat) };
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
#include <xgboost/linalg.h>
|
||||
|
||||
#include <numeric>
|
||||
#include "../../../src/data/array_interface.h"
|
||||
|
||||
#include "../../../src/common/linalg_op.h"
|
||||
#include "../../../src/data/array_interface.h"
|
||||
|
||||
namespace xgboost {
|
||||
inline void TestMetaInfoStridedData(int32_t device) {
|
||||
|
||||
@@ -144,15 +144,26 @@ void CheckRankingObjFunction(std::unique_ptr<xgboost::ObjFunction> const& obj,
|
||||
CheckObjFunctionImpl(obj, preds, labels, weights, info, out_grad, out_hess);
|
||||
}
|
||||
|
||||
xgboost::bst_float GetMetricEval(xgboost::Metric * metric,
|
||||
xgboost::bst_float GetMetricEval(xgboost::Metric* metric,
|
||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||
std::vector<xgboost::bst_float> labels,
|
||||
std::vector<xgboost::bst_float> weights,
|
||||
std::vector<xgboost::bst_uint> groups) {
|
||||
return GetMultiMetricEval(
|
||||
metric, preds,
|
||||
xgboost::linalg::Tensor<float, 2>{labels.begin(), labels.end(), {labels.size()}, -1}, weights,
|
||||
groups);
|
||||
}
|
||||
|
||||
double GetMultiMetricEval(xgboost::Metric* metric,
|
||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||
xgboost::linalg::Tensor<float, 2> const& labels,
|
||||
std::vector<xgboost::bst_float> weights,
|
||||
std::vector<xgboost::bst_uint> groups) {
|
||||
xgboost::MetaInfo info;
|
||||
info.num_row_ = labels.size();
|
||||
info.labels =
|
||||
xgboost::linalg::Tensor<float, 2>{labels.begin(), labels.end(), {labels.size()}, -1};
|
||||
info.num_row_ = labels.Shape(0);
|
||||
info.labels.Reshape(labels.Shape()[0], labels.Shape()[1]);
|
||||
info.labels.Data()->Copy(*labels.Data());
|
||||
info.weights_.HostVector() = weights;
|
||||
info.group_ptr_ = groups;
|
||||
|
||||
@@ -344,13 +355,14 @@ RandomDataGenerator::GenerateDMatrix(bool with_label, bool float_label,
|
||||
RandomDataGenerator gen(rows_, 1, 0);
|
||||
if (!float_label) {
|
||||
gen.Lower(0).Upper(classes).GenerateDense(out->Info().labels.Data());
|
||||
out->Info().labels.Reshape(out->Info().labels.Size());
|
||||
out->Info().labels.Reshape(this->rows_);
|
||||
auto& h_labels = out->Info().labels.Data()->HostVector();
|
||||
for (auto& v : h_labels) {
|
||||
v = static_cast<float>(static_cast<uint32_t>(v));
|
||||
}
|
||||
} else {
|
||||
gen.GenerateDense(out->Info().labels.Data());
|
||||
out->Info().labels.Reshape(this->rows_);
|
||||
}
|
||||
}
|
||||
if (device_ >= 0) {
|
||||
|
||||
@@ -91,6 +91,12 @@ xgboost::bst_float GetMetricEval(
|
||||
std::vector<xgboost::bst_float> weights = std::vector<xgboost::bst_float>(),
|
||||
std::vector<xgboost::bst_uint> groups = std::vector<xgboost::bst_uint>());
|
||||
|
||||
double GetMultiMetricEval(xgboost::Metric* metric,
|
||||
xgboost::HostDeviceVector<xgboost::bst_float> const& preds,
|
||||
xgboost::linalg::Tensor<float, 2> const& labels,
|
||||
std::vector<xgboost::bst_float> weights = {},
|
||||
std::vector<xgboost::bst_uint> groups = {});
|
||||
|
||||
namespace xgboost {
|
||||
bool IsNear(std::vector<xgboost::bst_float>::const_iterator _beg1,
|
||||
std::vector<xgboost::bst_float>::const_iterator _end1,
|
||||
|
||||
@@ -40,6 +40,9 @@ inline void CheckDeterministicMetricElementWise(StringView name, int32_t device)
|
||||
} // anonymous namespace
|
||||
} // namespace xgboost
|
||||
|
||||
namespace xgboost {
|
||||
namespace metric {
|
||||
|
||||
TEST(Metric, DeclareUnifiedTest(RMSE)) {
|
||||
auto lparam = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||
xgboost::Metric * metric = xgboost::Metric::Create("rmse", &lparam);
|
||||
@@ -276,3 +279,27 @@ TEST(Metric, DeclareUnifiedTest(PoissionNegLogLik)) {
|
||||
|
||||
xgboost::CheckDeterministicMetricElementWise(xgboost::StringView{"mphe"}, GPUIDX);
|
||||
}
|
||||
|
||||
TEST(Metric, DeclareUnifiedTest(MultiRMSE)) {
|
||||
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 lparam = xgboost::CreateEmptyGenericParam(GPUIDX);
|
||||
std::unique_ptr<Metric> metric{Metric::Create("rmse", &lparam)};
|
||||
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);
|
||||
}
|
||||
} // namespace metric
|
||||
} // namespace xgboost
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
#include "xgboost/json.h"
|
||||
#include "../../src/common/io.h"
|
||||
#include "../../src/common/random.h"
|
||||
#include "../../src/common/linalg_op.h"
|
||||
|
||||
namespace xgboost {
|
||||
|
||||
TEST(Learner, Basic) {
|
||||
using Arg = std::pair<std::string, std::string>;
|
||||
auto args = {Arg("tree_method", "exact")};
|
||||
@@ -278,6 +278,7 @@ TEST(Learner, GPUConfiguration) {
|
||||
labels[i] = i;
|
||||
}
|
||||
p_dmat->Info().labels.Data()->HostVector() = labels;
|
||||
p_dmat->Info().labels.Reshape(kRows);
|
||||
{
|
||||
std::unique_ptr<Learner> learner {Learner::Create(mat)};
|
||||
learner->SetParams({Arg{"booster", "gblinear"},
|
||||
@@ -424,4 +425,28 @@ TEST(Learner, FeatureInfo) {
|
||||
ASSERT_TRUE(std::equal(out_types.begin(), out_types.end(), types.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Learner, MultiTarget) {
|
||||
size_t constexpr kRows{128}, kCols{10}, kTargets{3};
|
||||
auto m = RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix();
|
||||
m->Info().labels.Reshape(kRows, kTargets);
|
||||
linalg::ElementWiseKernelHost(m->Info().labels.HostView(), omp_get_max_threads(),
|
||||
[](auto i, auto) { return i; });
|
||||
|
||||
{
|
||||
std::unique_ptr<Learner> learner{Learner::Create({m})};
|
||||
learner->Configure();
|
||||
|
||||
Json model{Object()};
|
||||
learner->SaveModel(&model);
|
||||
ASSERT_EQ(get<String>(model["learner"]["learner_model_param"]["num_target"]),
|
||||
std::to_string(kTargets));
|
||||
}
|
||||
{
|
||||
std::unique_ptr<Learner> learner{Learner::Create({m})};
|
||||
learner->SetParam("objective", "multi:softprob");
|
||||
// unsupported objective.
|
||||
EXPECT_THROW({ learner->Configure(); }, dmlc::Error);
|
||||
}
|
||||
}
|
||||
} // namespace xgboost
|
||||
|
||||
Reference in New Issue
Block a user