Use matrix for gradient. (#9508)

- Use the `linalg::Matrix` for storing gradients.
- New API for the custom objective.
- Custom objective for multi-class/multi-target is now required to return the correct shape.
- Custom objective for Python can accept arrays with any strides. (row-major, column-major)
This commit is contained in:
Jiaming Yuan
2023-08-24 05:29:52 +08:00
committed by GitHub
parent 6103dca0bb
commit 972730cde0
77 changed files with 1052 additions and 651 deletions

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (c) by Contributors 2020
/**
* Copyright 2020-2023, XGBoost Contributors
*/
#include <gtest/gtest.h>
#include <memory>
@@ -12,9 +12,7 @@
#include "../helpers.h"
#include "../../../src/common/survival_util.h"
namespace xgboost {
namespace common {
namespace xgboost::common {
TEST(Objective, DeclareUnifiedTest(AFTObjConfiguration)) {
auto ctx = MakeCUDACtx(GPUIDX);
std::unique_ptr<ObjFunction> objective(ObjFunction::Create("survival:aft", &ctx));
@@ -65,14 +63,14 @@ static inline void CheckGPairOverGridPoints(
preds[i] = std::log(std::pow(2.0, i * (log_y_high - log_y_low) / (num_point - 1) + log_y_low));
}
HostDeviceVector<GradientPair> out_gpair;
linalg::Matrix<GradientPair> out_gpair;
obj->GetGradient(HostDeviceVector<bst_float>(preds), info, 1, &out_gpair);
const auto& gpair = out_gpair.HostVector();
const auto gpair = out_gpair.HostView();
CHECK_EQ(num_point, expected_grad.size());
CHECK_EQ(num_point, expected_hess.size());
for (int i = 0; i < num_point; ++i) {
EXPECT_NEAR(gpair[i].GetGrad(), expected_grad[i], ftol);
EXPECT_NEAR(gpair[i].GetHess(), expected_hess[i], ftol);
EXPECT_NEAR(gpair(i).GetGrad(), expected_grad[i], ftol);
EXPECT_NEAR(gpair(i).GetHess(), expected_hess[i], ftol);
}
}
@@ -169,5 +167,4 @@ TEST(Objective, DeclareUnifiedTest(AFTObjGPairIntervalCensoredLabels)) {
0.2757f, 0.1776f, 0.1110f, 0.0682f, 0.0415f, 0.0251f, 0.0151f, 0.0091f, 0.0055f, 0.0033f });
}
} // namespace common
} // namespace xgboost
} // namespace xgboost::common

View File

@@ -74,35 +74,35 @@ void TestNDCGGPair(Context const* ctx) {
info.labels = linalg::Tensor<float, 2>{{0, 1, 0, 1}, {4, 1}, GPUIDX};
info.group_ptr_ = {0, 2, 4};
info.num_row_ = 4;
HostDeviceVector<GradientPair> gpairs;
linalg::Matrix<GradientPair> gpairs;
obj->GetGradient(predts, info, 0, &gpairs);
ASSERT_EQ(gpairs.Size(), predts.Size());
{
predts = {1, 0, 1, 0};
HostDeviceVector<GradientPair> gpairs;
linalg::Matrix<GradientPair> gpairs;
obj->GetGradient(predts, info, 0, &gpairs);
for (size_t i = 0; i < gpairs.Size(); ++i) {
ASSERT_GT(gpairs.HostSpan()[i].GetHess(), 0);
for (std::size_t i = 0; i < gpairs.Size(); ++i) {
ASSERT_GT(gpairs.HostView()(i).GetHess(), 0);
}
ASSERT_LT(gpairs.HostSpan()[1].GetGrad(), 0);
ASSERT_LT(gpairs.HostSpan()[3].GetGrad(), 0);
ASSERT_LT(gpairs.HostView()(1).GetGrad(), 0);
ASSERT_LT(gpairs.HostView()(3).GetGrad(), 0);
ASSERT_GT(gpairs.HostSpan()[0].GetGrad(), 0);
ASSERT_GT(gpairs.HostSpan()[2].GetGrad(), 0);
ASSERT_GT(gpairs.HostView()(0).GetGrad(), 0);
ASSERT_GT(gpairs.HostView()(2).GetGrad(), 0);
info.weights_ = {2, 3};
HostDeviceVector<GradientPair> weighted_gpairs;
linalg::Matrix<GradientPair> weighted_gpairs;
obj->GetGradient(predts, info, 0, &weighted_gpairs);
auto const& h_gpairs = gpairs.ConstHostSpan();
auto const& h_weighted_gpairs = weighted_gpairs.ConstHostSpan();
auto const& h_gpairs = gpairs.HostView();
auto const& h_weighted_gpairs = weighted_gpairs.HostView();
for (size_t i : {0ul, 1ul}) {
ASSERT_FLOAT_EQ(h_weighted_gpairs[i].GetGrad(), h_gpairs[i].GetGrad() * 2.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs[i].GetHess(), h_gpairs[i].GetHess() * 2.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs(i).GetGrad(), h_gpairs(i).GetGrad() * 2.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs(i).GetHess(), h_gpairs(i).GetHess() * 2.0f);
}
for (size_t i : {2ul, 3ul}) {
ASSERT_FLOAT_EQ(h_weighted_gpairs[i].GetGrad(), h_gpairs[i].GetGrad() * 3.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs[i].GetHess(), h_gpairs[i].GetHess() * 3.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs(i).GetGrad(), h_gpairs(i).GetGrad() * 3.0f);
ASSERT_FLOAT_EQ(h_weighted_gpairs(i).GetHess(), h_gpairs(i).GetHess() * 3.0f);
}
}
@@ -125,7 +125,7 @@ void TestUnbiasedNDCG(Context const* ctx) {
std::sort(h_label.begin(), h_label.end(), std::greater<>{});
HostDeviceVector<float> predt(p_fmat->Info().num_row_, 1.0f);
HostDeviceVector<GradientPair> out_gpair;
linalg::Matrix<GradientPair> out_gpair;
obj->GetGradient(predt, p_fmat->Info(), 0, &out_gpair);
Json config{Object{}};

View File

@@ -42,20 +42,21 @@ void TestGPUMakePair() {
auto d = dummy.View(ctx.gpu_id);
linalg::Vector<GradientPair> dgpair;
auto dg = dgpair.View(ctx.gpu_id);
cuda_impl::KernelInputs args{d,
d,
d,
d,
p_cache->DataGroupPtr(&ctx),
p_cache->CUDAThreadsGroupPtr(),
rank_idx,
info.labels.View(ctx.gpu_id),
predt.ConstDeviceSpan(),
{},
dg,
nullptr,
y_sorted_idx,
0};
cuda_impl::KernelInputs args{
d,
d,
d,
d,
p_cache->DataGroupPtr(&ctx),
p_cache->CUDAThreadsGroupPtr(),
rank_idx,
info.labels.View(ctx.gpu_id),
predt.ConstDeviceSpan(),
linalg::MatrixView<GradientPair>{common::Span<GradientPair>{}, {0}, 0},
dg,
nullptr,
y_sorted_idx,
0};
return args;
};

View File

@@ -122,8 +122,8 @@ TEST(Objective, DeclareUnifiedTest(LogisticRegressionBasic)) {
EXPECT_NEAR(obj->ProbToMargin(0.1f), -2.197f, 0.01f);
EXPECT_NEAR(obj->ProbToMargin(0.5f), 0, 0.01f);
EXPECT_NEAR(obj->ProbToMargin(0.9f), 2.197f, 0.01f);
EXPECT_ANY_THROW(obj->ProbToMargin(10))
<< "Expected error when base_score not in range [0,1f] for LogisticRegression";
EXPECT_ANY_THROW((void)obj->ProbToMargin(10))
<< "Expected error when base_score not in range [0,1f] for LogisticRegression";
// test PredTransform
HostDeviceVector<bst_float> io_preds = {0, 0.1f, 0.5f, 0.9f, 1};
@@ -282,9 +282,9 @@ TEST(Objective, DeclareUnifiedTest(TweedieRegressionGPair)) {
TEST(Objective, CPU_vs_CUDA) {
Context ctx = MakeCUDACtx(GPUIDX);
ObjFunction* obj = ObjFunction::Create("reg:squarederror", &ctx);
HostDeviceVector<GradientPair> cpu_out_preds;
HostDeviceVector<GradientPair> cuda_out_preds;
std::unique_ptr<ObjFunction> obj{ObjFunction::Create("reg:squarederror", &ctx)};
linalg::Matrix<GradientPair> cpu_out_preds;
linalg::Matrix<GradientPair> cuda_out_preds;
constexpr size_t kRows = 400;
constexpr size_t kCols = 100;
@@ -300,7 +300,7 @@ TEST(Objective, CPU_vs_CUDA) {
info.labels.Reshape(kRows);
auto& h_labels = info.labels.Data()->HostVector();
for (size_t i = 0; i < h_labels.size(); ++i) {
h_labels[i] = 1 / (float)(i+1);
h_labels[i] = 1 / static_cast<float>(i+1);
}
{
@@ -314,19 +314,17 @@ TEST(Objective, CPU_vs_CUDA) {
obj->GetGradient(preds, info, 0, &cuda_out_preds);
}
auto& h_cpu_out = cpu_out_preds.HostVector();
auto& h_cuda_out = cuda_out_preds.HostVector();
auto h_cpu_out = cpu_out_preds.HostView();
auto h_cuda_out = cuda_out_preds.HostView();
float sgrad = 0;
float shess = 0;
for (size_t i = 0; i < kRows; ++i) {
sgrad += std::pow(h_cpu_out[i].GetGrad() - h_cuda_out[i].GetGrad(), 2);
shess += std::pow(h_cpu_out[i].GetHess() - h_cuda_out[i].GetHess(), 2);
sgrad += std::pow(h_cpu_out(i).GetGrad() - h_cuda_out(i).GetGrad(), 2);
shess += std::pow(h_cpu_out(i).GetHess() - h_cuda_out(i).GetHess(), 2);
}
ASSERT_NEAR(sgrad, 0.0f, kRtEps);
ASSERT_NEAR(shess, 0.0f, kRtEps);
delete obj;
}
#endif