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:
@@ -22,6 +22,7 @@
|
||||
#include "../common/charconv.h" // for from_chars, to_chars, NumericLimits, from_ch...
|
||||
#include "../common/hist_util.h" // for HistogramCuts
|
||||
#include "../common/io.h" // for FileExtension, LoadSequentialFile, MemoryBuf...
|
||||
#include "../common/linalg_op.h" // for ElementWiseTransformHost
|
||||
#include "../common/threading_utils.h" // for OmpGetNumThreads, ParallelFor
|
||||
#include "../data/adapter.h" // for ArrayAdapter, DenseAdapter, RecordBatchesIte...
|
||||
#include "../data/ellpack_page.h" // for EllpackPage
|
||||
@@ -68,6 +69,7 @@ XGB_DLL void XGBoostVersion(int* major, int* minor, int* patch) {
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(DMLC_CXX11_THREAD_LOCAL, "XGBoost depends on thread-local storage.");
|
||||
using GlobalConfigAPIThreadLocalStore = dmlc::ThreadLocalStore<XGBAPIThreadLocalEntry>;
|
||||
|
||||
#if !defined(XGBOOST_USE_CUDA)
|
||||
@@ -717,8 +719,7 @@ XGB_DLL int XGDMatrixGetUIntInfo(const DMatrixHandle handle,
|
||||
API_END();
|
||||
}
|
||||
|
||||
XGB_DLL int XGDMatrixNumRow(const DMatrixHandle handle,
|
||||
xgboost::bst_ulong *out) {
|
||||
XGB_DLL int XGDMatrixNumRow(DMatrixHandle handle, xgboost::bst_ulong *out) {
|
||||
API_BEGIN();
|
||||
CHECK_HANDLE();
|
||||
auto p_m = CastDMatrixHandle(handle);
|
||||
@@ -727,8 +728,7 @@ XGB_DLL int XGDMatrixNumRow(const DMatrixHandle handle,
|
||||
API_END();
|
||||
}
|
||||
|
||||
XGB_DLL int XGDMatrixNumCol(const DMatrixHandle handle,
|
||||
xgboost::bst_ulong *out) {
|
||||
XGB_DLL int XGDMatrixNumCol(DMatrixHandle handle, xgboost::bst_ulong *out) {
|
||||
API_BEGIN();
|
||||
CHECK_HANDLE();
|
||||
auto p_m = CastDMatrixHandle(handle);
|
||||
@@ -970,28 +970,71 @@ XGB_DLL int XGBoosterUpdateOneIter(BoosterHandle handle,
|
||||
API_END();
|
||||
}
|
||||
|
||||
XGB_DLL int XGBoosterBoostOneIter(BoosterHandle handle,
|
||||
DMatrixHandle dtrain,
|
||||
bst_float *grad,
|
||||
bst_float *hess,
|
||||
xgboost::bst_ulong len) {
|
||||
XGB_DLL int XGBoosterBoostOneIter(BoosterHandle handle, DMatrixHandle dtrain, bst_float *grad,
|
||||
bst_float *hess, xgboost::bst_ulong len) {
|
||||
API_BEGIN();
|
||||
CHECK_HANDLE();
|
||||
HostDeviceVector<GradientPair> tmp_gpair;
|
||||
auto* bst = static_cast<Learner*>(handle);
|
||||
auto* dtr =
|
||||
static_cast<std::shared_ptr<DMatrix>*>(dtrain);
|
||||
tmp_gpair.Resize(len);
|
||||
std::vector<GradientPair>& tmp_gpair_h = tmp_gpair.HostVector();
|
||||
if (len > 0) {
|
||||
xgboost_CHECK_C_ARG_PTR(grad);
|
||||
xgboost_CHECK_C_ARG_PTR(hess);
|
||||
}
|
||||
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
||||
tmp_gpair_h[i] = GradientPair(grad[i], hess[i]);
|
||||
}
|
||||
error::DeprecatedFunc(__func__, "2.1.0", "XGBoosterTrainOneIter");
|
||||
auto *learner = static_cast<Learner *>(handle);
|
||||
auto ctx = learner->Ctx()->MakeCPU();
|
||||
|
||||
bst->BoostOneIter(0, *dtr, &tmp_gpair);
|
||||
auto t_grad = linalg::MakeTensorView(&ctx, common::Span{grad, len}, len);
|
||||
auto t_hess = linalg::MakeTensorView(&ctx, common::Span{hess, len}, len);
|
||||
|
||||
auto s_grad = linalg::ArrayInterfaceStr(t_grad);
|
||||
auto s_hess = linalg::ArrayInterfaceStr(t_hess);
|
||||
|
||||
return XGBoosterTrainOneIter(handle, dtrain, 0, s_grad.c_str(), s_hess.c_str());
|
||||
API_END();
|
||||
}
|
||||
|
||||
namespace xgboost {
|
||||
// copy user-supplied CUDA gradient arrays
|
||||
void CopyGradientFromCUDAArrays(Context const *, ArrayInterface<2, false> const &,
|
||||
ArrayInterface<2, false> const &, linalg::Matrix<GradientPair> *)
|
||||
#if !defined(XGBOOST_USE_CUDA)
|
||||
{
|
||||
common::AssertGPUSupport();
|
||||
}
|
||||
#else
|
||||
; // NOLINT
|
||||
#endif
|
||||
} // namespace xgboost
|
||||
|
||||
XGB_DLL int XGBoosterTrainOneIter(BoosterHandle handle, DMatrixHandle dtrain, int iter,
|
||||
char const *grad, char const *hess) {
|
||||
API_BEGIN();
|
||||
CHECK_HANDLE();
|
||||
xgboost_CHECK_C_ARG_PTR(grad);
|
||||
xgboost_CHECK_C_ARG_PTR(hess);
|
||||
auto p_fmat = CastDMatrixHandle(dtrain);
|
||||
ArrayInterface<2, false> i_grad{StringView{grad}};
|
||||
ArrayInterface<2, false> i_hess{StringView{hess}};
|
||||
StringView msg{"Mismatched shape between the gradient and hessian."};
|
||||
CHECK_EQ(i_grad.Shape(0), i_hess.Shape(0)) << msg;
|
||||
CHECK_EQ(i_grad.Shape(1), i_hess.Shape(1)) << msg;
|
||||
linalg::Matrix<GradientPair> gpair;
|
||||
auto grad_is_cuda = ArrayInterfaceHandler::IsCudaPtr(i_grad.data);
|
||||
auto hess_is_cuda = ArrayInterfaceHandler::IsCudaPtr(i_hess.data);
|
||||
CHECK_EQ(i_grad.Shape(0), p_fmat->Info().num_row_)
|
||||
<< "Mismatched size between the gradient and training data.";
|
||||
CHECK_EQ(grad_is_cuda, hess_is_cuda) << "gradient and hessian should be on the same device.";
|
||||
auto *learner = static_cast<Learner *>(handle);
|
||||
auto ctx = learner->Ctx();
|
||||
if (!grad_is_cuda) {
|
||||
gpair.Reshape(i_grad.Shape(0), i_grad.Shape(1));
|
||||
auto const shape = gpair.Shape();
|
||||
auto h_gpair = gpair.HostView();
|
||||
DispatchDType(i_grad, DeviceOrd::CPU(), [&](auto &&t_grad) {
|
||||
DispatchDType(i_hess, DeviceOrd::CPU(), [&](auto &&t_hess) {
|
||||
common::ParallelFor(h_gpair.Size(), ctx->Threads(),
|
||||
detail::CustomGradHessOp{t_grad, t_hess, h_gpair});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
CopyGradientFromCUDAArrays(ctx, i_grad, i_hess, &gpair);
|
||||
}
|
||||
learner->BoostOneIter(iter, p_fmat, &gpair);
|
||||
API_END();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
/**
|
||||
* Copyright 2019-2023 by XGBoost Contributors
|
||||
*/
|
||||
#include "../common/api_entry.h" // XGBAPIThreadLocalEntry
|
||||
#include <thrust/transform.h> // for transform
|
||||
|
||||
#include "../common/api_entry.h" // for XGBAPIThreadLocalEntry
|
||||
#include "../common/cuda_context.cuh" // for CUDAContext
|
||||
#include "../common/threading_utils.h"
|
||||
#include "../data/array_interface.h" // for DispatchDType, ArrayInterface
|
||||
#include "../data/device_adapter.cuh"
|
||||
#include "../data/proxy_dmatrix.h"
|
||||
#include "c_api_error.h"
|
||||
@@ -13,7 +17,6 @@
|
||||
#include "xgboost/learner.h"
|
||||
|
||||
namespace xgboost {
|
||||
|
||||
void XGBBuildInfoDevice(Json *p_info) {
|
||||
auto &info = *p_info;
|
||||
|
||||
@@ -55,6 +58,27 @@ void XGBoostAPIGuard::RestoreGPUAttribute() {
|
||||
// If errors, do nothing, assuming running on CPU only machine.
|
||||
cudaSetDevice(device_id_);
|
||||
}
|
||||
|
||||
void CopyGradientFromCUDAArrays(Context const *ctx, ArrayInterface<2, false> const &grad,
|
||||
ArrayInterface<2, false> const &hess,
|
||||
linalg::Matrix<GradientPair> *out_gpair) {
|
||||
auto grad_dev = dh::CudaGetPointerDevice(grad.data);
|
||||
auto hess_dev = dh::CudaGetPointerDevice(hess.data);
|
||||
CHECK_EQ(grad_dev, hess_dev) << "gradient and hessian should be on the same device.";
|
||||
auto &gpair = *out_gpair;
|
||||
gpair.SetDevice(grad_dev);
|
||||
gpair.Reshape(grad.Shape(0), grad.Shape(1));
|
||||
auto d_gpair = gpair.View(grad_dev);
|
||||
auto cuctx = ctx->CUDACtx();
|
||||
|
||||
DispatchDType(grad, DeviceOrd::CUDA(grad_dev), [&](auto &&t_grad) {
|
||||
DispatchDType(hess, DeviceOrd::CUDA(hess_dev), [&](auto &&t_hess) {
|
||||
CHECK_EQ(t_grad.Size(), t_hess.Size());
|
||||
thrust::for_each_n(cuctx->CTP(), thrust::make_counting_iterator(0ul), t_grad.Size(),
|
||||
detail::CustomGradHessOp{t_grad, t_hess, d_gpair});
|
||||
});
|
||||
});
|
||||
}
|
||||
} // namespace xgboost
|
||||
|
||||
using namespace xgboost; // NOLINT
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Copyright (c) 2015-2022 by Contributors
|
||||
/**
|
||||
* Copyright 2015-2023, XGBoost Contributors
|
||||
* \file c_api_error.h
|
||||
* \brief Error handling for C API.
|
||||
*/
|
||||
@@ -35,8 +35,8 @@
|
||||
} \
|
||||
return 0; // NOLINT(*)
|
||||
|
||||
#define CHECK_HANDLE() if (handle == nullptr) \
|
||||
LOG(FATAL) << "DMatrix/Booster has not been initialized or has already been disposed.";
|
||||
#define CHECK_HANDLE() \
|
||||
if (handle == nullptr) ::xgboost::detail::EmptyHandle();
|
||||
|
||||
/*!
|
||||
* \brief Set the last error message needed by C API
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string>
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <tuple> // for make_tuple
|
||||
#include <utility> // for move
|
||||
#include <vector>
|
||||
|
||||
#include "xgboost/c_api.h"
|
||||
@@ -16,7 +18,7 @@
|
||||
#include "xgboost/feature_map.h" // for FeatureMap
|
||||
#include "xgboost/json.h"
|
||||
#include "xgboost/learner.h"
|
||||
#include "xgboost/linalg.h" // ArrayInterfaceHandler
|
||||
#include "xgboost/linalg.h" // ArrayInterfaceHandler, MakeTensorView, ArrayInterfaceStr
|
||||
#include "xgboost/logging.h"
|
||||
#include "xgboost/string_view.h" // StringView
|
||||
|
||||
@@ -287,6 +289,19 @@ inline std::shared_ptr<DMatrix> CastDMatrixHandle(DMatrixHandle const handle) {
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
inline void EmptyHandle() {
|
||||
LOG(FATAL) << "DMatrix/Booster has not been initialized or has already been disposed.";
|
||||
}
|
||||
|
||||
inline xgboost::Context const *BoosterCtx(BoosterHandle handle) {
|
||||
if (handle == nullptr) {
|
||||
EmptyHandle();
|
||||
}
|
||||
auto *learner = static_cast<xgboost::Learner *>(handle);
|
||||
CHECK(learner);
|
||||
return learner->Ctx();
|
||||
}
|
||||
|
||||
template <typename PtrT, typename I, typename T>
|
||||
void MakeSparseFromPtr(PtrT const *p_indptr, I const *p_indices, T const *p_data,
|
||||
std::size_t nindptr, std::string *indptr_str, std::string *indices_str,
|
||||
@@ -334,6 +349,40 @@ void MakeSparseFromPtr(PtrT const *p_indptr, I const *p_indices, T const *p_data
|
||||
Json::Dump(jindices, indices_str);
|
||||
Json::Dump(jdata, data_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make array interface for other language bindings.
|
||||
*/
|
||||
template <typename G, typename H>
|
||||
auto MakeGradientInterface(Context const *ctx, G const *grad, H const *hess, std::size_t n_samples,
|
||||
std::size_t n_targets) {
|
||||
auto t_grad =
|
||||
linalg::MakeTensorView(ctx, common::Span{grad, n_samples * n_targets}, n_samples, n_targets);
|
||||
auto t_hess =
|
||||
linalg::MakeTensorView(ctx, common::Span{hess, n_samples * n_targets}, n_samples, n_targets);
|
||||
auto s_grad = linalg::ArrayInterfaceStr(t_grad);
|
||||
auto s_hess = linalg::ArrayInterfaceStr(t_hess);
|
||||
return std::make_tuple(s_grad, s_hess);
|
||||
}
|
||||
|
||||
template <typename G, typename H>
|
||||
struct CustomGradHessOp {
|
||||
linalg::MatrixView<G> t_grad;
|
||||
linalg::MatrixView<H> t_hess;
|
||||
linalg::MatrixView<GradientPair> d_gpair;
|
||||
|
||||
CustomGradHessOp(linalg::MatrixView<G> t_grad, linalg::MatrixView<H> t_hess,
|
||||
linalg::MatrixView<GradientPair> d_gpair)
|
||||
: t_grad{std::move(t_grad)}, t_hess{std::move(t_hess)}, d_gpair{std::move(d_gpair)} {}
|
||||
|
||||
XGBOOST_DEVICE void operator()(std::size_t i) {
|
||||
auto [m, n] = linalg::UnravelIndex(i, t_grad.Shape(0), t_grad.Shape(1));
|
||||
auto g = t_grad(m, n);
|
||||
auto h = t_hess(m, n);
|
||||
// from struct of arrays to array of structs.
|
||||
d_gpair(m, n) = GradientPair{static_cast<float>(g), static_cast<float>(h)};
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace xgboost
|
||||
#endif // XGBOOST_C_API_C_API_UTILS_H_
|
||||
|
||||
@@ -384,7 +384,7 @@ inline bool ArrayInterfaceHandler::IsCudaPtr(void const *) { return false; }
|
||||
* numpy has the proper support even though it's in the __cuda_array_interface__
|
||||
* protocol defined by numba.
|
||||
*/
|
||||
template <int32_t D, bool allow_mask = (D == 1)>
|
||||
template <std::int32_t D, bool allow_mask = (D == 1)>
|
||||
class ArrayInterface {
|
||||
static_assert(D > 0, "Invalid dimension for array interface.");
|
||||
|
||||
@@ -588,7 +588,7 @@ class ArrayInterface {
|
||||
};
|
||||
|
||||
template <std::int32_t D, typename Fn>
|
||||
void DispatchDType(ArrayInterface<D> const array, std::int32_t device, Fn fn) {
|
||||
void DispatchDType(ArrayInterface<D> const array, DeviceOrd device, Fn fn) {
|
||||
// Only used for cuDF at the moment.
|
||||
CHECK_EQ(array.valid.Capacity(), 0);
|
||||
auto dispatch = [&](auto t) {
|
||||
|
||||
@@ -448,7 +448,7 @@ void CopyTensorInfoImpl(Context const& ctx, Json arr_interface, linalg::Tensor<T
|
||||
auto t_out = p_out->View(Context::kCpuId);
|
||||
CHECK(t_out.CContiguous());
|
||||
auto const shape = t_out.Shape();
|
||||
DispatchDType(array, Context::kCpuId, [&](auto&& in) {
|
||||
DispatchDType(array, DeviceOrd::CPU(), [&](auto&& in) {
|
||||
linalg::ElementWiseTransformHost(t_out, ctx.Threads(), [&](auto i, auto) {
|
||||
return std::apply(in, linalg::UnravelIndex<D>(i, shape));
|
||||
});
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "../common/error_msg.h"
|
||||
|
||||
namespace xgboost::gbm {
|
||||
|
||||
DMLC_REGISTRY_FILE_TAG(gblinear);
|
||||
|
||||
// training parameters
|
||||
@@ -142,7 +141,7 @@ class GBLinear : public GradientBooster {
|
||||
this->updater_->SaveConfig(&j_updater);
|
||||
}
|
||||
|
||||
void DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair, PredictionCacheEntry*,
|
||||
void DoBoost(DMatrix* p_fmat, linalg::Matrix<GradientPair>* in_gpair, PredictionCacheEntry*,
|
||||
ObjFunction const*) override {
|
||||
monitor_.Start("DoBoost");
|
||||
|
||||
@@ -232,9 +231,8 @@ class GBLinear : public GradientBooster {
|
||||
std::fill(contribs.begin(), contribs.end(), 0);
|
||||
}
|
||||
|
||||
std::vector<std::string> DumpModel(const FeatureMap& fmap,
|
||||
bool with_stats,
|
||||
std::string format) const override {
|
||||
[[nodiscard]] std::vector<std::string> DumpModel(const FeatureMap& fmap, bool with_stats,
|
||||
std::string format) const override {
|
||||
return model_.DumpModel(fmap, with_stats, format);
|
||||
}
|
||||
|
||||
@@ -263,7 +261,7 @@ class GBLinear : public GradientBooster {
|
||||
}
|
||||
}
|
||||
|
||||
bool UseGPU() const override {
|
||||
[[nodiscard]] bool UseGPU() const override {
|
||||
if (param_.updater == "gpu_coord_descent") {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -167,8 +167,8 @@ void GBTree::Configure(Args const& cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCopyGradient(HostDeviceVector<GradientPair> const*, bst_group_t, bst_group_t,
|
||||
HostDeviceVector<GradientPair>*)
|
||||
void GPUCopyGradient(Context const*, linalg::Matrix<GradientPair> const*, bst_group_t,
|
||||
linalg::Matrix<GradientPair>*)
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
; // NOLINT
|
||||
#else
|
||||
@@ -177,16 +177,19 @@ void GPUCopyGradient(HostDeviceVector<GradientPair> const*, bst_group_t, bst_gro
|
||||
}
|
||||
#endif
|
||||
|
||||
void CopyGradient(HostDeviceVector<GradientPair> const* in_gpair, int32_t n_threads,
|
||||
bst_group_t n_groups, bst_group_t group_id,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
if (in_gpair->DeviceIdx() != Context::kCpuId) {
|
||||
GPUCopyGradient(in_gpair, n_groups, group_id, out_gpair);
|
||||
void CopyGradient(Context const* ctx, linalg::Matrix<GradientPair> const* in_gpair,
|
||||
bst_group_t group_id, linalg::Matrix<GradientPair>* out_gpair) {
|
||||
out_gpair->SetDevice(ctx->Device());
|
||||
out_gpair->Reshape(in_gpair->Shape(0), 1);
|
||||
if (ctx->IsCUDA()) {
|
||||
GPUCopyGradient(ctx, in_gpair, group_id, out_gpair);
|
||||
} else {
|
||||
std::vector<GradientPair> &tmp_h = out_gpair->HostVector();
|
||||
const auto& gpair_h = in_gpair->ConstHostVector();
|
||||
common::ParallelFor(out_gpair->Size(), n_threads,
|
||||
[&](auto i) { tmp_h[i] = gpair_h[i * n_groups + group_id]; });
|
||||
auto const& in = *in_gpair;
|
||||
auto target_gpair = in.Slice(linalg::All(), group_id);
|
||||
auto h_tmp = out_gpair->HostView();
|
||||
auto h_in = in.HostView().Slice(linalg::All(), group_id);
|
||||
CHECK_EQ(h_tmp.Size(), h_in.Size());
|
||||
common::ParallelFor(h_in.Size(), ctx->Threads(), [&](auto i) { h_tmp(i) = h_in(i); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +218,7 @@ void GBTree::UpdateTreeLeaf(DMatrix const* p_fmat, HostDeviceVector<float> const
|
||||
}
|
||||
}
|
||||
|
||||
void GBTree::DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair,
|
||||
void GBTree::DoBoost(DMatrix* p_fmat, linalg::Matrix<GradientPair>* in_gpair,
|
||||
PredictionCacheEntry* predt, ObjFunction const* obj) {
|
||||
if (model_.learner_model_param->IsVectorLeaf()) {
|
||||
CHECK(tparam_.tree_method == TreeMethod::kHist || tparam_.tree_method == TreeMethod::kAuto)
|
||||
@@ -263,12 +266,12 @@ void GBTree::DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair,
|
||||
}
|
||||
} else {
|
||||
CHECK_EQ(in_gpair->Size() % n_groups, 0U) << "must have exactly ngroup * nrow gpairs";
|
||||
HostDeviceVector<GradientPair> tmp(in_gpair->Size() / n_groups, GradientPair(),
|
||||
in_gpair->DeviceIdx());
|
||||
linalg::Matrix<GradientPair> tmp{{in_gpair->Shape(0), static_cast<std::size_t>(1ul)},
|
||||
ctx_->Ordinal()};
|
||||
bool update_predict = true;
|
||||
for (bst_target_t gid = 0; gid < n_groups; ++gid) {
|
||||
node_position.clear();
|
||||
CopyGradient(in_gpair, ctx_->Threads(), n_groups, gid, &tmp);
|
||||
CopyGradient(ctx_, in_gpair, gid, &tmp);
|
||||
TreesOneGroup ret;
|
||||
BoostNewTrees(&tmp, p_fmat, gid, &node_position, &ret);
|
||||
UpdateTreeLeaf(p_fmat, predt->predictions, obj, gid, node_position, &ret);
|
||||
@@ -289,7 +292,7 @@ void GBTree::DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair,
|
||||
this->CommitModel(std::move(new_trees));
|
||||
}
|
||||
|
||||
void GBTree::BoostNewTrees(HostDeviceVector<GradientPair>* gpair, DMatrix* p_fmat, int bst_group,
|
||||
void GBTree::BoostNewTrees(linalg::Matrix<GradientPair>* gpair, DMatrix* p_fmat, int bst_group,
|
||||
std::vector<HostDeviceVector<bst_node_t>>* out_position,
|
||||
TreesOneGroup* ret) {
|
||||
std::vector<RegTree*> new_trees;
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost Contributors
|
||||
*/
|
||||
#include "../common/device_helpers.cuh"
|
||||
#include "xgboost/linalg.h"
|
||||
#include "xgboost/span.h"
|
||||
#include <thrust/iterator/counting_iterator.h> // for make_counting_iterator
|
||||
|
||||
#include "../common/cuda_context.cuh"
|
||||
#include "../common/device_helpers.cuh" // for MakeTransformIterator
|
||||
#include "xgboost/base.h" // for GradientPair
|
||||
#include "xgboost/linalg.h" // for Matrix
|
||||
|
||||
namespace xgboost::gbm {
|
||||
void GPUCopyGradient(HostDeviceVector<GradientPair> const *in_gpair,
|
||||
bst_group_t n_groups, bst_group_t group_id,
|
||||
HostDeviceVector<GradientPair> *out_gpair) {
|
||||
auto mat = linalg::TensorView<GradientPair const, 2>(
|
||||
in_gpair->ConstDeviceSpan(),
|
||||
{in_gpair->Size() / n_groups, static_cast<size_t>(n_groups)},
|
||||
in_gpair->DeviceIdx());
|
||||
auto v_in = mat.Slice(linalg::All(), group_id);
|
||||
out_gpair->Resize(v_in.Size());
|
||||
auto d_out = out_gpair->DeviceSpan();
|
||||
dh::LaunchN(v_in.Size(), [=] __device__(size_t i) { d_out[i] = v_in(i); });
|
||||
void GPUCopyGradient(Context const *ctx, linalg::Matrix<GradientPair> const *in_gpair,
|
||||
bst_group_t group_id, linalg::Matrix<GradientPair> *out_gpair) {
|
||||
auto v_in = in_gpair->View(ctx->Device()).Slice(linalg::All(), group_id);
|
||||
out_gpair->SetDevice(ctx->Device());
|
||||
out_gpair->Reshape(v_in.Size(), 1);
|
||||
auto d_out = out_gpair->View(ctx->Device());
|
||||
auto cuctx = ctx->CUDACtx();
|
||||
auto it = dh::MakeTransformIterator<GradientPair>(
|
||||
thrust::make_counting_iterator(0ul), [=] XGBOOST_DEVICE(std::size_t i) { return v_in(i); });
|
||||
thrust::copy(cuctx->CTP(), it, it + v_in.Size(), d_out.Values().data());
|
||||
}
|
||||
|
||||
void GPUDartPredictInc(common::Span<float> out_predts,
|
||||
|
||||
@@ -183,8 +183,8 @@ class GBTree : public GradientBooster {
|
||||
/**
|
||||
* @brief Carry out one iteration of boosting.
|
||||
*/
|
||||
void DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair,
|
||||
PredictionCacheEntry* predt, ObjFunction const* obj) override;
|
||||
void DoBoost(DMatrix* p_fmat, linalg::Matrix<GradientPair>* in_gpair, PredictionCacheEntry* predt,
|
||||
ObjFunction const* obj) override;
|
||||
|
||||
[[nodiscard]] bool UseGPU() const override { return tparam_.tree_method == TreeMethod::kGPUHist; }
|
||||
|
||||
@@ -326,7 +326,7 @@ class GBTree : public GradientBooster {
|
||||
}
|
||||
|
||||
protected:
|
||||
void BoostNewTrees(HostDeviceVector<GradientPair>* gpair, DMatrix* p_fmat, int bst_group,
|
||||
void BoostNewTrees(linalg::Matrix<GradientPair>* gpair, DMatrix* p_fmat, int bst_group,
|
||||
std::vector<HostDeviceVector<bst_node_t>>* out_position,
|
||||
std::vector<std::unique_ptr<RegTree>>* ret);
|
||||
|
||||
|
||||
@@ -1282,14 +1282,14 @@ class LearnerImpl : public LearnerIO {
|
||||
monitor_.Start("GetGradient");
|
||||
GetGradient(predt.predictions, train->Info(), iter, &gpair_);
|
||||
monitor_.Stop("GetGradient");
|
||||
TrainingObserver::Instance().Observe(gpair_, "Gradients");
|
||||
TrainingObserver::Instance().Observe(*gpair_.Data(), "Gradients");
|
||||
|
||||
gbm_->DoBoost(train.get(), &gpair_, &predt, obj_.get());
|
||||
monitor_.Stop("UpdateOneIter");
|
||||
}
|
||||
|
||||
void BoostOneIter(int iter, std::shared_ptr<DMatrix> train,
|
||||
HostDeviceVector<GradientPair>* in_gpair) override {
|
||||
linalg::Matrix<GradientPair>* in_gpair) override {
|
||||
monitor_.Start("BoostOneIter");
|
||||
this->Configure();
|
||||
|
||||
@@ -1299,6 +1299,9 @@ class LearnerImpl : public LearnerIO {
|
||||
|
||||
this->ValidateDMatrix(train.get(), true);
|
||||
|
||||
CHECK_EQ(this->learner_model_param_.OutputLength(), in_gpair->Shape(1))
|
||||
<< "The number of columns in gradient should be equal to the number of targets/classes in "
|
||||
"the model.";
|
||||
auto& predt = prediction_container_.Cache(train, ctx_.gpu_id);
|
||||
gbm_->DoBoost(train.get(), in_gpair, &predt, obj_.get());
|
||||
monitor_.Stop("BoostOneIter");
|
||||
@@ -1461,18 +1464,18 @@ class LearnerImpl : public LearnerIO {
|
||||
}
|
||||
|
||||
private:
|
||||
void GetGradient(HostDeviceVector<bst_float> const& preds, MetaInfo const& info, int iteration,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
out_gpair->Resize(preds.Size());
|
||||
collective::ApplyWithLabels(info, out_gpair->HostPointer(),
|
||||
void GetGradient(HostDeviceVector<bst_float> const& preds, MetaInfo const& info,
|
||||
std::int32_t iter, linalg::Matrix<GradientPair>* out_gpair) {
|
||||
out_gpair->Reshape(info.num_row_, this->learner_model_param_.OutputLength());
|
||||
collective::ApplyWithLabels(info, out_gpair->Data()->HostPointer(),
|
||||
out_gpair->Size() * sizeof(GradientPair),
|
||||
[&] { obj_->GetGradient(preds, info, iteration, out_gpair); });
|
||||
[&] { obj_->GetGradient(preds, info, iter, out_gpair); });
|
||||
}
|
||||
|
||||
/*! \brief random number transformation seed. */
|
||||
static int32_t constexpr kRandSeedMagic = 127;
|
||||
// gradient pairs
|
||||
HostDeviceVector<GradientPair> gpair_;
|
||||
linalg::Matrix<GradientPair> gpair_;
|
||||
/*! \brief Temporary storage to prediction. Useful for storing data transformed by
|
||||
* objective function */
|
||||
PredictionContainer output_predictions_;
|
||||
|
||||
@@ -45,30 +45,31 @@ class CoordinateUpdater : public LinearUpdater {
|
||||
out["coordinate_param"] = ToJson(cparam_);
|
||||
}
|
||||
|
||||
void Update(HostDeviceVector<GradientPair> *in_gpair, DMatrix *p_fmat,
|
||||
gbm::GBLinearModel *model, double sum_instance_weight) override {
|
||||
void Update(linalg::Matrix<GradientPair> *in_gpair, DMatrix *p_fmat, gbm::GBLinearModel *model,
|
||||
double sum_instance_weight) override {
|
||||
auto gpair = in_gpair->Data();
|
||||
tparam_.DenormalizePenalties(sum_instance_weight);
|
||||
const int ngroup = model->learner_model_param->num_output_group;
|
||||
// update bias
|
||||
for (int group_idx = 0; group_idx < ngroup; ++group_idx) {
|
||||
auto grad = GetBiasGradientParallel(group_idx, ngroup, in_gpair->ConstHostVector(), p_fmat,
|
||||
auto grad = GetBiasGradientParallel(group_idx, ngroup, gpair->ConstHostVector(), p_fmat,
|
||||
ctx_->Threads());
|
||||
auto dbias = static_cast<float>(tparam_.learning_rate *
|
||||
CoordinateDeltaBias(grad.first, grad.second));
|
||||
model->Bias()[group_idx] += dbias;
|
||||
UpdateBiasResidualParallel(ctx_, group_idx, ngroup, dbias, &in_gpair->HostVector(), p_fmat);
|
||||
UpdateBiasResidualParallel(ctx_, group_idx, ngroup, dbias, &gpair->HostVector(), p_fmat);
|
||||
}
|
||||
// prepare for updating the weights
|
||||
selector_->Setup(ctx_, *model, in_gpair->ConstHostVector(), p_fmat, tparam_.reg_alpha_denorm,
|
||||
selector_->Setup(ctx_, *model, gpair->ConstHostVector(), p_fmat, tparam_.reg_alpha_denorm,
|
||||
tparam_.reg_lambda_denorm, cparam_.top_k);
|
||||
// update weights
|
||||
for (int group_idx = 0; group_idx < ngroup; ++group_idx) {
|
||||
for (unsigned i = 0U; i < model->learner_model_param->num_feature; i++) {
|
||||
int fidx =
|
||||
selector_->NextFeature(ctx_, i, *model, group_idx, in_gpair->ConstHostVector(), p_fmat,
|
||||
selector_->NextFeature(ctx_, i, *model, group_idx, gpair->ConstHostVector(), p_fmat,
|
||||
tparam_.reg_alpha_denorm, tparam_.reg_lambda_denorm);
|
||||
if (fidx < 0) break;
|
||||
this->UpdateFeature(fidx, group_idx, &in_gpair->HostVector(), p_fmat, model);
|
||||
this->UpdateFeature(fidx, group_idx, &gpair->HostVector(), p_fmat, model);
|
||||
}
|
||||
}
|
||||
monitor_.Stop("UpdateFeature");
|
||||
|
||||
@@ -93,17 +93,18 @@ class GPUCoordinateUpdater : public LinearUpdater { // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void Update(HostDeviceVector<GradientPair> *in_gpair, DMatrix *p_fmat,
|
||||
gbm::GBLinearModel *model, double sum_instance_weight) override {
|
||||
void Update(linalg::Matrix<GradientPair> *in_gpair, DMatrix *p_fmat, gbm::GBLinearModel *model,
|
||||
double sum_instance_weight) override {
|
||||
tparam_.DenormalizePenalties(sum_instance_weight);
|
||||
monitor_.Start("LazyInitDevice");
|
||||
this->LazyInitDevice(p_fmat, *(model->learner_model_param));
|
||||
monitor_.Stop("LazyInitDevice");
|
||||
|
||||
monitor_.Start("UpdateGpair");
|
||||
|
||||
// Update gpair
|
||||
if (ctx_->gpu_id >= 0) {
|
||||
this->UpdateGpair(in_gpair->ConstHostVector());
|
||||
if (ctx_->IsCUDA()) {
|
||||
this->UpdateGpair(in_gpair->Data()->ConstHostVector());
|
||||
}
|
||||
monitor_.Stop("UpdateGpair");
|
||||
|
||||
@@ -111,15 +112,15 @@ class GPUCoordinateUpdater : public LinearUpdater { // NOLINT
|
||||
this->UpdateBias(model);
|
||||
monitor_.Stop("UpdateBias");
|
||||
// prepare for updating the weights
|
||||
selector_->Setup(ctx_, *model, in_gpair->ConstHostVector(), p_fmat, tparam_.reg_alpha_denorm,
|
||||
tparam_.reg_lambda_denorm, coord_param_.top_k);
|
||||
selector_->Setup(ctx_, *model, in_gpair->Data()->ConstHostVector(), p_fmat,
|
||||
tparam_.reg_alpha_denorm, tparam_.reg_lambda_denorm, coord_param_.top_k);
|
||||
monitor_.Start("UpdateFeature");
|
||||
for (uint32_t group_idx = 0; group_idx < model->learner_model_param->num_output_group;
|
||||
++group_idx) {
|
||||
for (auto i = 0U; i < model->learner_model_param->num_feature; i++) {
|
||||
auto fidx =
|
||||
selector_->NextFeature(ctx_, i, *model, group_idx, in_gpair->ConstHostVector(), p_fmat,
|
||||
tparam_.reg_alpha_denorm, tparam_.reg_lambda_denorm);
|
||||
selector_->NextFeature(ctx_, i, *model, group_idx, in_gpair->Data()->ConstHostVector(),
|
||||
p_fmat, tparam_.reg_alpha_denorm, tparam_.reg_lambda_denorm);
|
||||
if (fidx < 0) break;
|
||||
this->UpdateFeature(fidx, group_idx, model);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include <xgboost/linear_updater.h>
|
||||
#include "coordinate_common.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace linear {
|
||||
namespace xgboost::linear {
|
||||
|
||||
DMLC_REGISTRY_FILE_TAG(updater_shotgun);
|
||||
|
||||
@@ -32,30 +31,31 @@ class ShotgunUpdater : public LinearUpdater {
|
||||
out["linear_train_param"] = ToJson(param_);
|
||||
}
|
||||
|
||||
void Update(HostDeviceVector<GradientPair> *in_gpair, DMatrix *p_fmat,
|
||||
gbm::GBLinearModel *model, double sum_instance_weight) override {
|
||||
auto &gpair = in_gpair->HostVector();
|
||||
void Update(linalg::Matrix<GradientPair> *in_gpair, DMatrix *p_fmat, gbm::GBLinearModel *model,
|
||||
double sum_instance_weight) override {
|
||||
auto gpair = in_gpair->Data();
|
||||
param_.DenormalizePenalties(sum_instance_weight);
|
||||
const int ngroup = model->learner_model_param->num_output_group;
|
||||
|
||||
// update bias
|
||||
for (int gid = 0; gid < ngroup; ++gid) {
|
||||
auto grad = GetBiasGradientParallel(gid, ngroup, in_gpair->ConstHostVector(), p_fmat,
|
||||
auto grad = GetBiasGradientParallel(gid, ngroup, gpair->ConstHostVector(), p_fmat,
|
||||
ctx_->Threads());
|
||||
auto dbias = static_cast<bst_float>(param_.learning_rate *
|
||||
CoordinateDeltaBias(grad.first, grad.second));
|
||||
model->Bias()[gid] += dbias;
|
||||
UpdateBiasResidualParallel(ctx_, gid, ngroup, dbias, &in_gpair->HostVector(), p_fmat);
|
||||
UpdateBiasResidualParallel(ctx_, gid, ngroup, dbias, &gpair->HostVector(), p_fmat);
|
||||
}
|
||||
|
||||
// lock-free parallel updates of weights
|
||||
selector_->Setup(ctx_, *model, in_gpair->ConstHostVector(), p_fmat, param_.reg_alpha_denorm,
|
||||
selector_->Setup(ctx_, *model, gpair->ConstHostVector(), p_fmat, param_.reg_alpha_denorm,
|
||||
param_.reg_lambda_denorm, 0);
|
||||
auto &h_gpair = gpair->HostVector();
|
||||
for (const auto &batch : p_fmat->GetBatches<CSCPage>(ctx_)) {
|
||||
auto page = batch.GetView();
|
||||
const auto nfeat = static_cast<bst_omp_uint>(batch.Size());
|
||||
common::ParallelFor(nfeat, ctx_->Threads(), [&](auto i) {
|
||||
int ii = selector_->NextFeature(ctx_, i, *model, 0, in_gpair->ConstHostVector(), p_fmat,
|
||||
int ii = selector_->NextFeature(ctx_, i, *model, 0, gpair->ConstHostVector(), p_fmat,
|
||||
param_.reg_alpha_denorm, param_.reg_lambda_denorm);
|
||||
if (ii < 0) return;
|
||||
const bst_uint fid = ii;
|
||||
@@ -63,7 +63,7 @@ class ShotgunUpdater : public LinearUpdater {
|
||||
for (int gid = 0; gid < ngroup; ++gid) {
|
||||
double sum_grad = 0.0, sum_hess = 0.0;
|
||||
for (auto &c : col) {
|
||||
const GradientPair &p = gpair[c.index * ngroup + gid];
|
||||
const GradientPair &p = h_gpair[c.index * ngroup + gid];
|
||||
if (p.GetHess() < 0.0f) continue;
|
||||
const bst_float v = c.fvalue;
|
||||
sum_grad += p.GetGrad() * v;
|
||||
@@ -77,7 +77,7 @@ class ShotgunUpdater : public LinearUpdater {
|
||||
w += dw;
|
||||
// update grad values
|
||||
for (auto &c : col) {
|
||||
GradientPair &p = gpair[c.index * ngroup + gid];
|
||||
GradientPair &p = h_gpair[c.index * ngroup + gid];
|
||||
if (p.GetHess() < 0.0f) continue;
|
||||
p += GradientPair(p.GetHess() * c.fvalue * dw, 0);
|
||||
}
|
||||
@@ -98,5 +98,4 @@ XGBOOST_REGISTER_LINEAR_UPDATER(ShotgunUpdater, "shotgun")
|
||||
"Update linear model according to shotgun coordinate descent "
|
||||
"algorithm.")
|
||||
.set_body([]() { return new ShotgunUpdater(); });
|
||||
} // namespace linear
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::linear
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Copyright 2019-2022 by Contributors
|
||||
/**
|
||||
* Copyright 2019-2023, XGBoost Contributors
|
||||
* \file aft_obj.cu
|
||||
* \brief Definition of AFT loss for survival analysis.
|
||||
* \author Avinash Barnwal, Hyunsu Cho and Toby Hocking
|
||||
@@ -41,11 +41,9 @@ class AFTObj : public ObjFunction {
|
||||
ObjInfo Task() const override { return ObjInfo::kSurvival; }
|
||||
|
||||
template <typename Distribution>
|
||||
void GetGradientImpl(const HostDeviceVector<bst_float> &preds,
|
||||
const MetaInfo &info,
|
||||
HostDeviceVector<GradientPair> *out_gpair,
|
||||
size_t ndata, int device, bool is_null_weight,
|
||||
float aft_loss_distribution_scale) {
|
||||
void GetGradientImpl(const HostDeviceVector<bst_float>& preds, const MetaInfo& info,
|
||||
linalg::Matrix<GradientPair>* out_gpair, size_t ndata, int device,
|
||||
bool is_null_weight, float aft_loss_distribution_scale) {
|
||||
common::Transform<>::Init(
|
||||
[=] XGBOOST_DEVICE(size_t _idx,
|
||||
common::Span<GradientPair> _out_gpair,
|
||||
@@ -66,16 +64,17 @@ class AFTObj : public ObjFunction {
|
||||
_out_gpair[_idx] = GradientPair(grad * w, hess * w);
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(ndata)}, this->ctx_->Threads(), device).Eval(
|
||||
out_gpair, &preds, &info.labels_lower_bound_, &info.labels_upper_bound_,
|
||||
out_gpair->Data(), &preds, &info.labels_lower_bound_, &info.labels_upper_bound_,
|
||||
&info.weights_);
|
||||
}
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, int /*iter*/,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
const size_t ndata = preds.Size();
|
||||
CHECK_EQ(info.labels_lower_bound_.Size(), ndata);
|
||||
CHECK_EQ(info.labels_upper_bound_.Size(), ndata);
|
||||
out_gpair->Resize(ndata);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(ndata, 1);
|
||||
const int device = ctx_->gpu_id;
|
||||
const float aft_loss_distribution_scale = param_.aft_loss_distribution_scale;
|
||||
const bool is_null_weight = info.weights_.Size() == 0;
|
||||
|
||||
@@ -27,8 +27,8 @@ class HingeObj : public ObjFunction {
|
||||
void Configure(Args const&) override {}
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float> &preds, const MetaInfo &info, int /*iter*/,
|
||||
HostDeviceVector<GradientPair> *out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float> &preds, const MetaInfo &info,
|
||||
std::int32_t /*iter*/, linalg::Matrix<GradientPair> *out_gpair) override {
|
||||
CHECK_NE(info.labels.Size(), 0U) << "label set cannot be empty";
|
||||
CHECK_EQ(preds.Size(), info.labels.Size())
|
||||
<< "labels are not correctly provided"
|
||||
@@ -41,7 +41,8 @@ class HingeObj : public ObjFunction {
|
||||
CHECK_EQ(info.weights_.Size(), ndata)
|
||||
<< "Number of weights should be equal to number of data points.";
|
||||
}
|
||||
out_gpair->Resize(ndata);
|
||||
CHECK_EQ(info.labels.Shape(1), 1) << "Multi-target for `binary:hinge` is not yet supported.";
|
||||
out_gpair->Reshape(ndata, 1);
|
||||
common::Transform<>::Init(
|
||||
[=] XGBOOST_DEVICE(size_t _idx,
|
||||
common::Span<GradientPair> _out_gpair,
|
||||
@@ -63,7 +64,7 @@ class HingeObj : public ObjFunction {
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(ndata)}, this->ctx_->Threads(),
|
||||
ctx_->gpu_id).Eval(
|
||||
out_gpair, &preds, info.labels.Data(), &info.weights_);
|
||||
out_gpair->Data(), &preds, info.labels.Data(), &info.weights_);
|
||||
}
|
||||
|
||||
void PredTransform(HostDeviceVector<bst_float> *io_preds) const override {
|
||||
|
||||
@@ -21,7 +21,7 @@ void FitIntercept::InitEstimation(MetaInfo const& info, linalg::Vector<float>* b
|
||||
}
|
||||
// Avoid altering any state in child objective.
|
||||
HostDeviceVector<float> dummy_predt(info.labels.Size(), 0.0f, this->ctx_->gpu_id);
|
||||
HostDeviceVector<GradientPair> gpair(info.labels.Size(), GradientPair{}, this->ctx_->gpu_id);
|
||||
linalg::Matrix<GradientPair> gpair(info.labels.Shape(), this->ctx_->gpu_id);
|
||||
|
||||
Json config{Object{}};
|
||||
this->SaveConfig(&config);
|
||||
|
||||
@@ -165,9 +165,8 @@ class LambdaRankObj : public FitIntercept {
|
||||
void CalcLambdaForGroup(std::int32_t iter, common::Span<float const> g_predt,
|
||||
linalg::VectorView<float const> g_label, float w,
|
||||
common::Span<std::size_t const> g_rank, bst_group_t g, Delta delta,
|
||||
common::Span<GradientPair> g_gpair) {
|
||||
std::fill_n(g_gpair.data(), g_gpair.size(), GradientPair{});
|
||||
auto p_gpair = g_gpair.data();
|
||||
linalg::VectorView<GradientPair> g_gpair) {
|
||||
std::fill_n(g_gpair.Values().data(), g_gpair.Size(), GradientPair{});
|
||||
|
||||
auto ti_plus = ti_plus_.HostView();
|
||||
auto tj_minus = tj_minus_.HostView();
|
||||
@@ -198,8 +197,8 @@ class LambdaRankObj : public FitIntercept {
|
||||
|
||||
std::size_t idx_high = g_rank[rank_high];
|
||||
std::size_t idx_low = g_rank[rank_low];
|
||||
p_gpair[idx_high] += pg;
|
||||
p_gpair[idx_low] += ng;
|
||||
g_gpair(idx_high) += pg;
|
||||
g_gpair(idx_low) += ng;
|
||||
|
||||
if (unbiased) {
|
||||
auto k = ti_plus.Size();
|
||||
@@ -225,12 +224,13 @@ class LambdaRankObj : public FitIntercept {
|
||||
MakePairs(ctx_, iter, p_cache_, g, g_label, g_rank, loop);
|
||||
if (sum_lambda > 0.0) {
|
||||
double norm = std::log2(1.0 + sum_lambda) / sum_lambda;
|
||||
std::transform(g_gpair.data(), g_gpair.data() + g_gpair.size(), g_gpair.data(),
|
||||
[norm](GradientPair const& g) { return g * norm; });
|
||||
std::transform(g_gpair.Values().data(), g_gpair.Values().data() + g_gpair.Size(),
|
||||
g_gpair.Values().data(), [norm](GradientPair const& g) { return g * norm; });
|
||||
}
|
||||
|
||||
auto w_norm = p_cache_->WeightNorm();
|
||||
std::transform(g_gpair.begin(), g_gpair.end(), g_gpair.begin(),
|
||||
std::transform(g_gpair.Values().data(), g_gpair.Values().data() + g_gpair.Size(),
|
||||
g_gpair.Values().data(),
|
||||
[&](GradientPair const& gpair) { return gpair * w * w_norm; });
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ class LambdaRankObj : public FitIntercept {
|
||||
}
|
||||
|
||||
void GetGradient(HostDeviceVector<float> const& predt, MetaInfo const& info, std::int32_t iter,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CHECK_EQ(info.labels.Size(), predt.Size()) << error::LabelScoreSize();
|
||||
|
||||
// init/renew cache
|
||||
@@ -339,7 +339,7 @@ class LambdaRankNDCG : public LambdaRankObj<LambdaRankNDCG, ltr::NDCGCache> {
|
||||
void CalcLambdaForGroupNDCG(std::int32_t iter, common::Span<float const> g_predt,
|
||||
linalg::VectorView<float const> g_label, float w,
|
||||
common::Span<std::size_t const> g_rank,
|
||||
common::Span<GradientPair> g_gpair,
|
||||
linalg::VectorView<GradientPair> g_gpair,
|
||||
linalg::VectorView<double const> inv_IDCG,
|
||||
common::Span<double const> discount, bst_group_t g) {
|
||||
auto delta = [&](auto y_high, auto y_low, std::size_t rank_high, std::size_t rank_low,
|
||||
@@ -351,7 +351,7 @@ class LambdaRankNDCG : public LambdaRankObj<LambdaRankNDCG, ltr::NDCGCache> {
|
||||
}
|
||||
|
||||
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
|
||||
const MetaInfo& info, HostDeviceVector<GradientPair>* out_gpair) {
|
||||
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
|
||||
if (ctx_->IsCUDA()) {
|
||||
cuda_impl::LambdaRankGetGradientNDCG(
|
||||
ctx_, iter, predt, info, GetCache(), ti_plus_.View(ctx_->gpu_id),
|
||||
@@ -363,8 +363,10 @@ class LambdaRankNDCG : public LambdaRankObj<LambdaRankNDCG, ltr::NDCGCache> {
|
||||
bst_group_t n_groups = p_cache_->Groups();
|
||||
auto gptr = p_cache_->DataGroupPtr(ctx_);
|
||||
|
||||
out_gpair->Resize(info.num_row_);
|
||||
auto h_gpair = out_gpair->HostSpan();
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, 1);
|
||||
|
||||
auto h_gpair = out_gpair->HostView();
|
||||
auto h_predt = predt.ConstHostSpan();
|
||||
auto h_label = info.labels.HostView();
|
||||
auto h_weight = common::MakeOptionalWeights(ctx_, info.weights_);
|
||||
@@ -378,7 +380,8 @@ class LambdaRankNDCG : public LambdaRankObj<LambdaRankNDCG, ltr::NDCGCache> {
|
||||
std::size_t cnt = gptr[g + 1] - gptr[g];
|
||||
auto w = h_weight[g];
|
||||
auto g_predt = h_predt.subspan(gptr[g], cnt);
|
||||
auto g_gpair = h_gpair.subspan(gptr[g], cnt);
|
||||
auto g_gpair =
|
||||
h_gpair.Slice(linalg::Range(static_cast<std::size_t>(gptr[g]), gptr[g] + cnt), 0);
|
||||
auto g_label = h_label.Slice(make_range(g), 0);
|
||||
auto g_rank = rank_idx.subspan(gptr[g], cnt);
|
||||
|
||||
@@ -420,7 +423,7 @@ void LambdaRankGetGradientNDCG(Context const*, std::int32_t, HostDeviceVector<fl
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double>, linalg::VectorView<double>,
|
||||
HostDeviceVector<GradientPair>*) {
|
||||
linalg::Matrix<GradientPair>*) {
|
||||
common::AssertGPUSupport();
|
||||
}
|
||||
|
||||
@@ -470,7 +473,7 @@ void MAPStat(Context const* ctx, linalg::VectorView<float const> label,
|
||||
class LambdaRankMAP : public LambdaRankObj<LambdaRankMAP, ltr::MAPCache> {
|
||||
public:
|
||||
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
|
||||
const MetaInfo& info, HostDeviceVector<GradientPair>* out_gpair) {
|
||||
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
|
||||
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the MAP objective.";
|
||||
if (ctx_->IsCUDA()) {
|
||||
return cuda_impl::LambdaRankGetGradientMAP(
|
||||
@@ -482,8 +485,11 @@ class LambdaRankMAP : public LambdaRankObj<LambdaRankMAP, ltr::MAPCache> {
|
||||
auto gptr = p_cache_->DataGroupPtr(ctx_).data();
|
||||
bst_group_t n_groups = p_cache_->Groups();
|
||||
|
||||
out_gpair->Resize(info.num_row_);
|
||||
auto h_gpair = out_gpair->HostSpan();
|
||||
CHECK_EQ(info.labels.Shape(1), 1) << "multi-target for learning to rank is not yet supported.";
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
|
||||
auto h_gpair = out_gpair->HostView();
|
||||
auto h_label = info.labels.HostView().Slice(linalg::All(), 0);
|
||||
auto h_predt = predt.ConstHostSpan();
|
||||
auto rank_idx = p_cache_->SortedIdx(ctx_, h_predt);
|
||||
@@ -514,7 +520,7 @@ class LambdaRankMAP : public LambdaRankObj<LambdaRankMAP, ltr::MAPCache> {
|
||||
auto cnt = gptr[g + 1] - gptr[g];
|
||||
auto w = h_weight[g];
|
||||
auto g_predt = h_predt.subspan(gptr[g], cnt);
|
||||
auto g_gpair = h_gpair.subspan(gptr[g], cnt);
|
||||
auto g_gpair = h_gpair.Slice(linalg::Range(gptr[g], gptr[g] + cnt), 0);
|
||||
auto g_label = h_label.Slice(make_range(g));
|
||||
auto g_rank = rank_idx.subspan(gptr[g], cnt);
|
||||
|
||||
@@ -545,7 +551,7 @@ void LambdaRankGetGradientMAP(Context const*, std::int32_t, HostDeviceVector<flo
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double>, linalg::VectorView<double>,
|
||||
HostDeviceVector<GradientPair>*) {
|
||||
linalg::Matrix<GradientPair>*) {
|
||||
common::AssertGPUSupport();
|
||||
}
|
||||
} // namespace cuda_impl
|
||||
@@ -557,7 +563,7 @@ void LambdaRankGetGradientMAP(Context const*, std::int32_t, HostDeviceVector<flo
|
||||
class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::RankingCache> {
|
||||
public:
|
||||
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
|
||||
const MetaInfo& info, HostDeviceVector<GradientPair>* out_gpair) {
|
||||
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
|
||||
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the pairwise objective.";
|
||||
if (ctx_->IsCUDA()) {
|
||||
return cuda_impl::LambdaRankGetGradientPairwise(
|
||||
@@ -569,8 +575,10 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
|
||||
auto gptr = p_cache_->DataGroupPtr(ctx_);
|
||||
bst_group_t n_groups = p_cache_->Groups();
|
||||
|
||||
out_gpair->Resize(info.num_row_);
|
||||
auto h_gpair = out_gpair->HostSpan();
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
|
||||
auto h_gpair = out_gpair->HostView();
|
||||
auto h_label = info.labels.HostView().Slice(linalg::All(), 0);
|
||||
auto h_predt = predt.ConstHostSpan();
|
||||
auto h_weight = common::MakeOptionalWeights(ctx_, info.weights_);
|
||||
@@ -585,7 +593,7 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
|
||||
auto cnt = gptr[g + 1] - gptr[g];
|
||||
auto w = h_weight[g];
|
||||
auto g_predt = h_predt.subspan(gptr[g], cnt);
|
||||
auto g_gpair = h_gpair.subspan(gptr[g], cnt);
|
||||
auto g_gpair = h_gpair.Slice(linalg::Range(gptr[g], gptr[g] + cnt), 0);
|
||||
auto g_label = h_label.Slice(make_range(g));
|
||||
auto g_rank = rank_idx.subspan(gptr[g], cnt);
|
||||
|
||||
@@ -611,7 +619,7 @@ void LambdaRankGetGradientPairwise(Context const*, std::int32_t, HostDeviceVecto
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double const>, // input bias ratio
|
||||
linalg::VectorView<double>, linalg::VectorView<double>,
|
||||
HostDeviceVector<GradientPair>*) {
|
||||
linalg::Matrix<GradientPair>*) {
|
||||
common::AssertGPUSupport();
|
||||
}
|
||||
} // namespace cuda_impl
|
||||
|
||||
@@ -93,7 +93,7 @@ struct GetGradOp {
|
||||
// obtain group segment data.
|
||||
auto g_label = args.labels.Slice(linalg::Range(data_group_begin, data_group_begin + n_data), 0);
|
||||
auto g_predt = args.predts.subspan(data_group_begin, n_data);
|
||||
auto g_gpair = args.gpairs.subspan(data_group_begin, n_data).data();
|
||||
auto g_gpair = args.gpairs.Slice(linalg::Range(data_group_begin, data_group_begin + n_data));
|
||||
auto g_rank = args.d_sorted_idx.subspan(data_group_begin, n_data);
|
||||
|
||||
auto [i, j] = make_pair(idx, g);
|
||||
@@ -128,8 +128,8 @@ struct GetGradOp {
|
||||
auto ngt = GradientPair{common::TruncateWithRounding(gr.GetGrad(), ng.GetGrad()),
|
||||
common::TruncateWithRounding(gr.GetHess(), ng.GetHess())};
|
||||
|
||||
dh::AtomicAddGpair(g_gpair + idx_high, pgt);
|
||||
dh::AtomicAddGpair(g_gpair + idx_low, ngt);
|
||||
dh::AtomicAddGpair(&g_gpair(idx_high), pgt);
|
||||
dh::AtomicAddGpair(&g_gpair(idx_low), ngt);
|
||||
}
|
||||
|
||||
if (unbiased && need_update) {
|
||||
@@ -266,16 +266,16 @@ void CalcGrad(Context const* ctx, MetaInfo const& info, std::shared_ptr<ltr::Ran
|
||||
*/
|
||||
auto d_weights = common::MakeOptionalWeights(ctx, info.weights_);
|
||||
auto w_norm = p_cache->WeightNorm();
|
||||
thrust::for_each_n(ctx->CUDACtx()->CTP(), thrust::make_counting_iterator(0ul), d_gpair.size(),
|
||||
[=] XGBOOST_DEVICE(std::size_t i) {
|
||||
thrust::for_each_n(ctx->CUDACtx()->CTP(), thrust::make_counting_iterator(0ul), d_gpair.Size(),
|
||||
[=] XGBOOST_DEVICE(std::size_t i) mutable {
|
||||
auto g = dh::SegmentId(d_gptr, i);
|
||||
auto sum_lambda = thrust::get<2>(d_max_lambdas[g]);
|
||||
// Normalization
|
||||
if (sum_lambda > 0.0) {
|
||||
double norm = std::log2(1.0 + sum_lambda) / sum_lambda;
|
||||
d_gpair[i] *= norm;
|
||||
d_gpair(i, 0) *= norm;
|
||||
}
|
||||
d_gpair[i] *= (d_weights[g] * w_norm);
|
||||
d_gpair(i, 0) *= (d_weights[g] * w_norm);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ void Launch(Context const* ctx, std::int32_t iter, HostDeviceVector<float> const
|
||||
linalg::VectorView<double const> ti_plus, // input bias ratio
|
||||
linalg::VectorView<double const> tj_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
linalg::Matrix<GradientPair>* out_gpair) {
|
||||
// boilerplate
|
||||
std::int32_t device_id = ctx->gpu_id;
|
||||
dh::safe_cuda(cudaSetDevice(device_id));
|
||||
@@ -296,8 +296,8 @@ void Launch(Context const* ctx, std::int32_t iter, HostDeviceVector<float> const
|
||||
|
||||
info.labels.SetDevice(device_id);
|
||||
preds.SetDevice(device_id);
|
||||
out_gpair->SetDevice(device_id);
|
||||
out_gpair->Resize(preds.Size());
|
||||
out_gpair->SetDevice(ctx->Device());
|
||||
out_gpair->Reshape(preds.Size(), 1);
|
||||
|
||||
CHECK(p_cache);
|
||||
|
||||
@@ -308,8 +308,9 @@ void Launch(Context const* ctx, std::int32_t iter, HostDeviceVector<float> const
|
||||
|
||||
auto label = info.labels.View(ctx->gpu_id);
|
||||
auto predts = preds.ConstDeviceSpan();
|
||||
auto gpairs = out_gpair->DeviceSpan();
|
||||
thrust::fill_n(ctx->CUDACtx()->CTP(), gpairs.data(), gpairs.size(), GradientPair{0.0f, 0.0f});
|
||||
auto gpairs = out_gpair->View(ctx->Device());
|
||||
thrust::fill_n(ctx->CUDACtx()->CTP(), gpairs.Values().data(), gpairs.Size(),
|
||||
GradientPair{0.0f, 0.0f});
|
||||
|
||||
auto const d_threads_group_ptr = p_cache->CUDAThreadsGroupPtr();
|
||||
auto const d_gptr = p_cache->DataGroupPtr(ctx);
|
||||
@@ -371,7 +372,7 @@ void LambdaRankGetGradientNDCG(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> ti_plus, // input bias ratio
|
||||
linalg::VectorView<double const> tj_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
linalg::Matrix<GradientPair>* out_gpair) {
|
||||
// boilerplate
|
||||
std::int32_t device_id = ctx->gpu_id;
|
||||
dh::safe_cuda(cudaSetDevice(device_id));
|
||||
@@ -440,7 +441,7 @@ void LambdaRankGetGradientMAP(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> ti_plus, // input bias ratio
|
||||
linalg::VectorView<double const> tj_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
linalg::Matrix<GradientPair>* out_gpair) {
|
||||
std::int32_t device_id = ctx->gpu_id;
|
||||
dh::safe_cuda(cudaSetDevice(device_id));
|
||||
|
||||
@@ -479,7 +480,7 @@ void LambdaRankGetGradientPairwise(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> ti_plus, // input bias ratio
|
||||
linalg::VectorView<double const> tj_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair) {
|
||||
linalg::Matrix<GradientPair>* out_gpair) {
|
||||
std::int32_t device_id = ctx->gpu_id;
|
||||
dh::safe_cuda(cudaSetDevice(device_id));
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ struct KernelInputs {
|
||||
|
||||
linalg::MatrixView<float const> labels;
|
||||
common::Span<float const> predts;
|
||||
common::Span<GradientPair> gpairs;
|
||||
linalg::MatrixView<GradientPair> gpairs;
|
||||
|
||||
linalg::VectorView<GradientPair const> d_roundings;
|
||||
double const *d_cost_rounding;
|
||||
@@ -79,8 +79,8 @@ struct MakePairsOp {
|
||||
/**
|
||||
* \brief Make pair for the topk pair method.
|
||||
*/
|
||||
XGBOOST_DEVICE std::tuple<std::size_t, std::size_t> WithTruncation(std::size_t idx,
|
||||
bst_group_t g) const {
|
||||
[[nodiscard]] XGBOOST_DEVICE std::tuple<std::size_t, std::size_t> WithTruncation(
|
||||
std::size_t idx, bst_group_t g) const {
|
||||
auto thread_group_begin = args.d_threads_group_ptr[g];
|
||||
auto idx_in_thread_group = idx - thread_group_begin;
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ void LambdaRankGetGradientNDCG(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> t_plus, // input bias ratio
|
||||
linalg::VectorView<double const> t_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair);
|
||||
linalg::Matrix<GradientPair>* out_gpair);
|
||||
|
||||
/**
|
||||
* \brief Generate statistic for MAP used for calculating \Delta Z in lambda mart.
|
||||
@@ -168,7 +168,7 @@ void LambdaRankGetGradientMAP(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> t_plus, // input bias ratio
|
||||
linalg::VectorView<double const> t_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair);
|
||||
linalg::Matrix<GradientPair>* out_gpair);
|
||||
|
||||
void LambdaRankGetGradientPairwise(Context const* ctx, std::int32_t iter,
|
||||
HostDeviceVector<float> const& predt, const MetaInfo& info,
|
||||
@@ -176,7 +176,7 @@ void LambdaRankGetGradientPairwise(Context const* ctx, std::int32_t iter,
|
||||
linalg::VectorView<double const> ti_plus, // input bias ratio
|
||||
linalg::VectorView<double const> tj_minus, // input bias ratio
|
||||
linalg::VectorView<double> li, linalg::VectorView<double> lj,
|
||||
HostDeviceVector<GradientPair>* out_gpair);
|
||||
linalg::Matrix<GradientPair>* out_gpair);
|
||||
|
||||
void LambdaRankUpdatePositionBias(Context const* ctx, linalg::VectorView<double const> li_full,
|
||||
linalg::VectorView<double const> lj_full,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Copyright 2015-2022 by XGBoost Contributors
|
||||
/**
|
||||
* Copyright 2015-2023, XGBoost Contributors
|
||||
* \file multi_class.cc
|
||||
* \brief Definition of multi-class classification objectives.
|
||||
* \author Tianqi Chen
|
||||
@@ -48,13 +48,8 @@ class SoftmaxMultiClassObj : public ObjFunction {
|
||||
|
||||
ObjInfo Task() const override { return ObjInfo::kClassification; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds,
|
||||
const MetaInfo& info,
|
||||
int iter,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
// Remove unused parameter compiler warning.
|
||||
(void) iter;
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, std::int32_t,
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
if (info.labels.Size() == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -77,7 +72,7 @@ class SoftmaxMultiClassObj : public ObjFunction {
|
||||
label_correct_.Resize(1);
|
||||
label_correct_.SetDevice(device);
|
||||
|
||||
out_gpair->Resize(preds.Size());
|
||||
out_gpair->Reshape(info.num_row_, static_cast<std::uint64_t>(nclass));
|
||||
label_correct_.Fill(1);
|
||||
|
||||
const bool is_null_weight = info.weights_.Size() == 0;
|
||||
@@ -115,7 +110,7 @@ class SoftmaxMultiClassObj : public ObjFunction {
|
||||
gpair[idx * nclass + k] = GradientPair(p * wt, h);
|
||||
}
|
||||
}, common::Range{0, ndata}, ctx_->Threads(), device)
|
||||
.Eval(out_gpair, info.labels.Data(), &preds, &info.weights_, &label_correct_);
|
||||
.Eval(out_gpair->Data(), info.labels.Data(), &preds, &info.weights_, &label_correct_);
|
||||
|
||||
std::vector<int>& label_correct_h = label_correct_.HostVector();
|
||||
for (auto const flag : label_correct_h) {
|
||||
|
||||
@@ -27,13 +27,12 @@
|
||||
|
||||
#endif // defined(XGBOOST_USE_CUDA)
|
||||
|
||||
namespace xgboost {
|
||||
namespace obj {
|
||||
namespace xgboost::obj {
|
||||
class QuantileRegression : public ObjFunction {
|
||||
common::QuantileLossParam param_;
|
||||
HostDeviceVector<float> alpha_;
|
||||
|
||||
bst_target_t Targets(MetaInfo const& info) const override {
|
||||
[[nodiscard]] bst_target_t Targets(MetaInfo const& info) const override {
|
||||
auto const& alpha = param_.quantile_alpha.Get();
|
||||
CHECK_EQ(alpha.size(), alpha_.Size()) << "The objective is not yet configured.";
|
||||
if (info.ShouldHaveLabels()) {
|
||||
@@ -50,7 +49,7 @@ class QuantileRegression : public ObjFunction {
|
||||
|
||||
public:
|
||||
void GetGradient(HostDeviceVector<float> const& preds, const MetaInfo& info, std::int32_t iter,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
if (iter == 0) {
|
||||
CheckInitInputs(info);
|
||||
}
|
||||
@@ -65,10 +64,11 @@ class QuantileRegression : public ObjFunction {
|
||||
|
||||
auto labels = info.labels.View(ctx_->gpu_id);
|
||||
|
||||
out_gpair->SetDevice(ctx_->gpu_id);
|
||||
out_gpair->Resize(n_targets * info.num_row_);
|
||||
auto gpair =
|
||||
linalg::MakeTensorView(ctx_, out_gpair, info.num_row_, n_alphas, n_targets / n_alphas);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
CHECK_EQ(info.labels.Shape(1), 1)
|
||||
<< "Multi-target for quantile regression is not yet supported.";
|
||||
out_gpair->Reshape(info.num_row_, n_targets);
|
||||
auto gpair = out_gpair->View(ctx_->Device());
|
||||
|
||||
info.weights_.SetDevice(ctx_->gpu_id);
|
||||
common::OptionalWeights weight{ctx_->IsCPU() ? info.weights_.ConstHostSpan()
|
||||
@@ -85,15 +85,16 @@ class QuantileRegression : public ObjFunction {
|
||||
ctx_, gpair, [=] XGBOOST_DEVICE(std::size_t i, GradientPair const&) mutable {
|
||||
auto [sample_id, quantile_id, target_id] =
|
||||
linalg::UnravelIndex(i, n_samples, alpha.size(), n_targets / alpha.size());
|
||||
assert(target_id == 0);
|
||||
|
||||
auto d = predt(i) - labels(sample_id, target_id);
|
||||
auto h = weight[sample_id];
|
||||
if (d >= 0) {
|
||||
auto g = (1.0f - alpha[quantile_id]) * weight[sample_id];
|
||||
gpair(sample_id, quantile_id, target_id) = GradientPair{g, h};
|
||||
gpair(sample_id, quantile_id) = GradientPair{g, h};
|
||||
} else {
|
||||
auto g = (-alpha[quantile_id] * weight[sample_id]);
|
||||
gpair(sample_id, quantile_id, target_id) = GradientPair{g, h};
|
||||
gpair(sample_id, quantile_id) = GradientPair{g, h};
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -192,7 +193,7 @@ class QuantileRegression : public ObjFunction {
|
||||
param_.Validate();
|
||||
this->alpha_.HostVector() = param_.quantile_alpha.Get();
|
||||
}
|
||||
ObjInfo Task() const override { return {ObjInfo::kRegression, true, true}; }
|
||||
[[nodiscard]] ObjInfo Task() const override { return {ObjInfo::kRegression, true, true}; }
|
||||
static char const* Name() { return "reg:quantileerror"; }
|
||||
|
||||
void SaveConfig(Json* p_out) const override {
|
||||
@@ -206,8 +207,8 @@ class QuantileRegression : public ObjFunction {
|
||||
alpha_.HostVector() = param_.quantile_alpha.Get();
|
||||
}
|
||||
|
||||
const char* DefaultEvalMetric() const override { return "quantile"; }
|
||||
Json DefaultMetricConfig() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override { return "quantile"; }
|
||||
[[nodiscard]] Json DefaultMetricConfig() const override {
|
||||
CHECK(param_.GetInitialised());
|
||||
Json config{Object{}};
|
||||
config["name"] = String{this->DefaultEvalMetric()};
|
||||
@@ -223,5 +224,4 @@ XGBOOST_REGISTER_OBJECTIVE(QuantileRegression, QuantileRegression::Name())
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
DMLC_REGISTRY_FILE_TAG(quantile_obj_gpu);
|
||||
#endif // defined(XGBOOST_USE_CUDA)
|
||||
} // namespace obj
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::obj
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
#include "xgboost/tree_model.h" // RegTree
|
||||
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
#include "../common/cuda_context.cuh" // for CUDAContext
|
||||
#include "../common/device_helpers.cuh"
|
||||
#include "../common/linalg_op.cuh"
|
||||
#endif // defined(XGBOOST_USE_CUDA)
|
||||
|
||||
namespace xgboost {
|
||||
namespace obj {
|
||||
namespace xgboost::obj {
|
||||
namespace {
|
||||
void CheckRegInputs(MetaInfo const& info, HostDeviceVector<bst_float> const& preds) {
|
||||
CheckInitInputs(info);
|
||||
@@ -68,33 +68,60 @@ class RegLossObj : public FitIntercept {
|
||||
HostDeviceVector<float> additional_input_;
|
||||
|
||||
public:
|
||||
// 0 - label_correct flag, 1 - scale_pos_weight, 2 - is_null_weight
|
||||
RegLossObj(): additional_input_(3) {}
|
||||
void ValidateLabel(MetaInfo const& info) {
|
||||
auto label = info.labels.View(ctx_->Ordinal());
|
||||
auto valid = ctx_->DispatchDevice(
|
||||
[&] {
|
||||
return std::all_of(linalg::cbegin(label), linalg::cend(label),
|
||||
[](float y) -> bool { return Loss::CheckLabel(y); });
|
||||
},
|
||||
[&] {
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
auto cuctx = ctx_->CUDACtx();
|
||||
auto it = dh::MakeTransformIterator<bool>(
|
||||
thrust::make_counting_iterator(0ul), [=] XGBOOST_DEVICE(std::size_t i) -> bool {
|
||||
auto [m, n] = linalg::UnravelIndex(i, label.Shape());
|
||||
return Loss::CheckLabel(label(m, n));
|
||||
});
|
||||
return dh::Reduce(cuctx->CTP(), it, it + label.Size(), true, thrust::logical_and<>{});
|
||||
#else
|
||||
common::AssertGPUSupport();
|
||||
return false;
|
||||
#endif // defined(XGBOOST_USE_CUDA)
|
||||
});
|
||||
if (!valid) {
|
||||
LOG(FATAL) << Loss::LabelErrorMsg();
|
||||
}
|
||||
}
|
||||
// 0 - scale_pos_weight, 1 - is_null_weight
|
||||
RegLossObj(): additional_input_(2) {}
|
||||
|
||||
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {
|
||||
param_.UpdateAllowUnknown(args);
|
||||
}
|
||||
|
||||
ObjInfo Task() const override { return Loss::Info(); }
|
||||
[[nodiscard]] ObjInfo Task() const override { return Loss::Info(); }
|
||||
|
||||
bst_target_t Targets(MetaInfo const& info) const override {
|
||||
[[nodiscard]] bst_target_t Targets(MetaInfo const& info) const override {
|
||||
// Multi-target regression.
|
||||
return std::max(static_cast<size_t>(1), info.labels.Shape(1));
|
||||
return std::max(static_cast<std::size_t>(1), info.labels.Shape(1));
|
||||
}
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds,
|
||||
const MetaInfo &info, int,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info,
|
||||
std::int32_t iter, linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CheckRegInputs(info, preds);
|
||||
if (iter == 0) {
|
||||
ValidateLabel(info);
|
||||
}
|
||||
|
||||
size_t const ndata = preds.Size();
|
||||
out_gpair->Resize(ndata);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
auto device = ctx_->gpu_id;
|
||||
additional_input_.HostVector().begin()[0] = 1; // Fill the label_correct flag
|
||||
|
||||
bool is_null_weight = info.weights_.Size() == 0;
|
||||
auto scale_pos_weight = param_.scale_pos_weight;
|
||||
additional_input_.HostVector().begin()[1] = scale_pos_weight;
|
||||
additional_input_.HostVector().begin()[2] = is_null_weight;
|
||||
additional_input_.HostVector().begin()[0] = scale_pos_weight;
|
||||
additional_input_.HostVector().begin()[1] = is_null_weight;
|
||||
|
||||
const size_t nthreads = ctx_->Threads();
|
||||
bool on_device = device >= 0;
|
||||
@@ -102,7 +129,8 @@ class RegLossObj : public FitIntercept {
|
||||
// for better performance.
|
||||
const size_t n_data_blocks = std::max(static_cast<size_t>(1), (on_device ? ndata : nthreads));
|
||||
const size_t block_size = ndata / n_data_blocks + !!(ndata % n_data_blocks);
|
||||
auto const n_targets = std::max(info.labels.Shape(1), static_cast<size_t>(1));
|
||||
auto const n_targets = this->Targets(info);
|
||||
out_gpair->Reshape(info.num_row_, n_targets);
|
||||
|
||||
common::Transform<>::Init(
|
||||
[block_size, ndata, n_targets] XGBOOST_DEVICE(
|
||||
@@ -117,8 +145,8 @@ class RegLossObj : public FitIntercept {
|
||||
GradientPair* out_gpair_ptr = _out_gpair.data();
|
||||
const size_t begin = data_block_idx*block_size;
|
||||
const size_t end = std::min(ndata, begin + block_size);
|
||||
const float _scale_pos_weight = _additional_input[1];
|
||||
const bool _is_null_weight = _additional_input[2];
|
||||
const float _scale_pos_weight = _additional_input[0];
|
||||
const bool _is_null_weight = _additional_input[1];
|
||||
|
||||
for (size_t idx = begin; idx < end; ++idx) {
|
||||
bst_float p = Loss::PredTransform(preds_ptr[idx]);
|
||||
@@ -127,26 +155,17 @@ class RegLossObj : public FitIntercept {
|
||||
if (label == 1.0f) {
|
||||
w *= _scale_pos_weight;
|
||||
}
|
||||
if (!Loss::CheckLabel(label)) {
|
||||
// If there is an incorrect label, the host code will know.
|
||||
_additional_input[0] = 0;
|
||||
}
|
||||
out_gpair_ptr[idx] = GradientPair(Loss::FirstOrderGradient(p, label) * w,
|
||||
Loss::SecondOrderGradient(p, label) * w);
|
||||
}
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(n_data_blocks)}, nthreads, device)
|
||||
.Eval(&additional_input_, out_gpair, &preds, info.labels.Data(),
|
||||
.Eval(&additional_input_, out_gpair->Data(), &preds, info.labels.Data(),
|
||||
&info.weights_);
|
||||
|
||||
auto const flag = additional_input_.HostVector().begin()[0];
|
||||
if (flag == 0) {
|
||||
LOG(FATAL) << Loss::LabelErrorMsg();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
const char* DefaultEvalMetric() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override {
|
||||
return Loss::DefaultEvalMetric();
|
||||
}
|
||||
|
||||
@@ -160,7 +179,7 @@ class RegLossObj : public FitIntercept {
|
||||
.Eval(io_preds);
|
||||
}
|
||||
|
||||
float ProbToMargin(float base_score) const override {
|
||||
[[nodiscard]] float ProbToMargin(float base_score) const override {
|
||||
return Loss::ProbToMargin(base_score);
|
||||
}
|
||||
|
||||
@@ -215,21 +234,21 @@ class PseudoHuberRegression : public FitIntercept {
|
||||
|
||||
public:
|
||||
void Configure(Args const& args) override { param_.UpdateAllowUnknown(args); }
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
bst_target_t Targets(MetaInfo const& info) const override {
|
||||
return std::max(static_cast<size_t>(1), info.labels.Shape(1));
|
||||
[[nodiscard]] ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
[[nodiscard]] bst_target_t Targets(MetaInfo const& info) const override {
|
||||
return std::max(static_cast<std::size_t>(1), info.labels.Shape(1));
|
||||
}
|
||||
|
||||
void GetGradient(HostDeviceVector<bst_float> const& preds, const MetaInfo& info, int /*iter*/,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CheckRegInputs(info, preds);
|
||||
auto slope = param_.huber_slope;
|
||||
CHECK_NE(slope, 0.0) << "slope for pseudo huber cannot be 0.";
|
||||
auto labels = info.labels.View(ctx_->gpu_id);
|
||||
|
||||
out_gpair->SetDevice(ctx_->gpu_id);
|
||||
out_gpair->Resize(info.labels.Size());
|
||||
auto gpair = linalg::MakeVec(out_gpair);
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
auto gpair = out_gpair->View(ctx_->Device());
|
||||
|
||||
preds.SetDevice(ctx_->gpu_id);
|
||||
auto predt = linalg::MakeVec(&preds);
|
||||
@@ -252,7 +271,7 @@ class PseudoHuberRegression : public FitIntercept {
|
||||
});
|
||||
}
|
||||
|
||||
const char* DefaultEvalMetric() const override { return "mphe"; }
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override { return "mphe"; }
|
||||
|
||||
void SaveConfig(Json* p_out) const override {
|
||||
auto& out = *p_out;
|
||||
@@ -292,15 +311,15 @@ class PoissonRegression : public FitIntercept {
|
||||
param_.UpdateAllowUnknown(args);
|
||||
}
|
||||
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
[[nodiscard]] ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds,
|
||||
const MetaInfo &info, int,
|
||||
HostDeviceVector<GradientPair> *out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, int,
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CHECK_NE(info.labels.Size(), 0U) << "label set cannot be empty";
|
||||
CHECK_EQ(preds.Size(), info.labels.Size()) << "labels are not correctly provided";
|
||||
size_t const ndata = preds.Size();
|
||||
out_gpair->Resize(ndata);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
auto device = ctx_->gpu_id;
|
||||
label_correct_.Resize(1);
|
||||
label_correct_.Fill(1);
|
||||
@@ -328,7 +347,7 @@ class PoissonRegression : public FitIntercept {
|
||||
expf(p + max_delta_step) * w};
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(ndata)}, this->ctx_->Threads(), device).Eval(
|
||||
&label_correct_, out_gpair, &preds, info.labels.Data(), &info.weights_);
|
||||
&label_correct_, out_gpair->Data(), &preds, info.labels.Data(), &info.weights_);
|
||||
// copy "label correct" flags back to host
|
||||
std::vector<int>& label_correct_h = label_correct_.HostVector();
|
||||
for (auto const flag : label_correct_h) {
|
||||
@@ -349,10 +368,10 @@ class PoissonRegression : public FitIntercept {
|
||||
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
|
||||
PredTransform(io_preds);
|
||||
}
|
||||
bst_float ProbToMargin(bst_float base_score) const override {
|
||||
[[nodiscard]] float ProbToMargin(bst_float base_score) const override {
|
||||
return std::log(base_score);
|
||||
}
|
||||
const char* DefaultEvalMetric() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override {
|
||||
return "poisson-nloglik";
|
||||
}
|
||||
|
||||
@@ -383,16 +402,15 @@ XGBOOST_REGISTER_OBJECTIVE(PoissonRegression, "count:poisson")
|
||||
class CoxRegression : public FitIntercept {
|
||||
public:
|
||||
void Configure(Args const&) override {}
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
[[nodiscard]] ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds,
|
||||
const MetaInfo &info, int,
|
||||
HostDeviceVector<GradientPair> *out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, int,
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CHECK_NE(info.labels.Size(), 0U) << "label set cannot be empty";
|
||||
CHECK_EQ(preds.Size(), info.labels.Size()) << "labels are not correctly provided";
|
||||
const auto& preds_h = preds.HostVector();
|
||||
out_gpair->Resize(preds_h.size());
|
||||
auto& gpair = out_gpair->HostVector();
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
auto gpair = out_gpair->HostView();
|
||||
const std::vector<size_t> &label_order = info.LabelAbsSort(ctx_);
|
||||
|
||||
const omp_ulong ndata = static_cast<omp_ulong>(preds_h.size()); // NOLINT(*)
|
||||
@@ -440,8 +458,8 @@ class CoxRegression : public FitIntercept {
|
||||
}
|
||||
|
||||
const double grad = exp_p*r_k - static_cast<bst_float>(y > 0);
|
||||
const double hess = exp_p*r_k - exp_p*exp_p * s_k;
|
||||
gpair.at(ind) = GradientPair(grad * w, hess * w);
|
||||
const double hess = exp_p * r_k - exp_p * exp_p * s_k;
|
||||
gpair(ind) = GradientPair(grad * w, hess * w);
|
||||
|
||||
last_abs_y = abs_y;
|
||||
last_exp_p = exp_p;
|
||||
@@ -457,10 +475,10 @@ class CoxRegression : public FitIntercept {
|
||||
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
|
||||
PredTransform(io_preds);
|
||||
}
|
||||
bst_float ProbToMargin(bst_float base_score) const override {
|
||||
[[nodiscard]] float ProbToMargin(bst_float base_score) const override {
|
||||
return std::log(base_score);
|
||||
}
|
||||
const char* DefaultEvalMetric() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override {
|
||||
return "cox-nloglik";
|
||||
}
|
||||
|
||||
@@ -480,16 +498,16 @@ XGBOOST_REGISTER_OBJECTIVE(CoxRegression, "survival:cox")
|
||||
class GammaRegression : public FitIntercept {
|
||||
public:
|
||||
void Configure(Args const&) override {}
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
[[nodiscard]] ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float> &preds,
|
||||
const MetaInfo &info, int,
|
||||
HostDeviceVector<GradientPair> *out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, std::int32_t,
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CHECK_NE(info.labels.Size(), 0U) << "label set cannot be empty";
|
||||
CHECK_EQ(preds.Size(), info.labels.Size()) << "labels are not correctly provided";
|
||||
const size_t ndata = preds.Size();
|
||||
auto device = ctx_->gpu_id;
|
||||
out_gpair->Resize(ndata);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
label_correct_.Resize(1);
|
||||
label_correct_.Fill(1);
|
||||
|
||||
@@ -514,7 +532,7 @@ class GammaRegression : public FitIntercept {
|
||||
_out_gpair[_idx] = GradientPair((1 - y / expf(p)) * w, y / expf(p) * w);
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(ndata)}, this->ctx_->Threads(), device).Eval(
|
||||
&label_correct_, out_gpair, &preds, info.labels.Data(), &info.weights_);
|
||||
&label_correct_, out_gpair->Data(), &preds, info.labels.Data(), &info.weights_);
|
||||
|
||||
// copy "label correct" flags back to host
|
||||
std::vector<int>& label_correct_h = label_correct_.HostVector();
|
||||
@@ -536,10 +554,10 @@ class GammaRegression : public FitIntercept {
|
||||
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
|
||||
PredTransform(io_preds);
|
||||
}
|
||||
bst_float ProbToMargin(bst_float base_score) const override {
|
||||
[[nodiscard]] float ProbToMargin(bst_float base_score) const override {
|
||||
return std::log(base_score);
|
||||
}
|
||||
const char* DefaultEvalMetric() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override {
|
||||
return "gamma-nloglik";
|
||||
}
|
||||
void SaveConfig(Json* p_out) const override {
|
||||
@@ -578,15 +596,15 @@ class TweedieRegression : public FitIntercept {
|
||||
metric_ = os.str();
|
||||
}
|
||||
|
||||
ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
[[nodiscard]] ObjInfo Task() const override { return ObjInfo::kRegression; }
|
||||
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds,
|
||||
const MetaInfo &info, int,
|
||||
HostDeviceVector<GradientPair> *out_gpair) override {
|
||||
void GetGradient(const HostDeviceVector<bst_float>& preds, const MetaInfo& info, std::int32_t,
|
||||
linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CHECK_NE(info.labels.Size(), 0U) << "label set cannot be empty";
|
||||
CHECK_EQ(preds.Size(), info.labels.Size()) << "labels are not correctly provided";
|
||||
const size_t ndata = preds.Size();
|
||||
out_gpair->Resize(ndata);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
|
||||
auto device = ctx_->gpu_id;
|
||||
label_correct_.Resize(1);
|
||||
@@ -619,7 +637,7 @@ class TweedieRegression : public FitIntercept {
|
||||
_out_gpair[_idx] = GradientPair(grad * w, hess * w);
|
||||
},
|
||||
common::Range{0, static_cast<int64_t>(ndata), 1}, this->ctx_->Threads(), device)
|
||||
.Eval(&label_correct_, out_gpair, &preds, info.labels.Data(), &info.weights_);
|
||||
.Eval(&label_correct_, out_gpair->Data(), &preds, info.labels.Data(), &info.weights_);
|
||||
|
||||
// copy "label correct" flags back to host
|
||||
std::vector<int>& label_correct_h = label_correct_.HostVector();
|
||||
@@ -639,11 +657,11 @@ class TweedieRegression : public FitIntercept {
|
||||
.Eval(io_preds);
|
||||
}
|
||||
|
||||
bst_float ProbToMargin(bst_float base_score) const override {
|
||||
[[nodiscard]] float ProbToMargin(bst_float base_score) const override {
|
||||
return std::log(base_score);
|
||||
}
|
||||
|
||||
const char* DefaultEvalMetric() const override {
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override {
|
||||
return metric_.c_str();
|
||||
}
|
||||
|
||||
@@ -672,19 +690,19 @@ XGBOOST_REGISTER_OBJECTIVE(TweedieRegression, "reg:tweedie")
|
||||
class MeanAbsoluteError : public ObjFunction {
|
||||
public:
|
||||
void Configure(Args const&) override {}
|
||||
ObjInfo Task() const override { return {ObjInfo::kRegression, true, true}; }
|
||||
bst_target_t Targets(MetaInfo const& info) const override {
|
||||
return std::max(static_cast<size_t>(1), info.labels.Shape(1));
|
||||
[[nodiscard]] ObjInfo Task() const override { return {ObjInfo::kRegression, true, true}; }
|
||||
[[nodiscard]] bst_target_t Targets(MetaInfo const& info) const override {
|
||||
return std::max(static_cast<std::size_t>(1), info.labels.Shape(1));
|
||||
}
|
||||
|
||||
void GetGradient(HostDeviceVector<bst_float> const& preds, const MetaInfo& info, int /*iter*/,
|
||||
HostDeviceVector<GradientPair>* out_gpair) override {
|
||||
void GetGradient(HostDeviceVector<float> const& preds, const MetaInfo& info,
|
||||
std::int32_t /*iter*/, linalg::Matrix<GradientPair>* out_gpair) override {
|
||||
CheckRegInputs(info, preds);
|
||||
auto labels = info.labels.View(ctx_->gpu_id);
|
||||
|
||||
out_gpair->SetDevice(ctx_->gpu_id);
|
||||
out_gpair->Resize(info.labels.Size());
|
||||
auto gpair = linalg::MakeVec(out_gpair);
|
||||
out_gpair->SetDevice(ctx_->Device());
|
||||
out_gpair->Reshape(info.num_row_, this->Targets(info));
|
||||
auto gpair = out_gpair->View(ctx_->Device());
|
||||
|
||||
preds.SetDevice(ctx_->gpu_id);
|
||||
auto predt = linalg::MakeVec(&preds);
|
||||
@@ -692,14 +710,14 @@ class MeanAbsoluteError : public ObjFunction {
|
||||
common::OptionalWeights weight{ctx_->IsCPU() ? info.weights_.ConstHostSpan()
|
||||
: info.weights_.ConstDeviceSpan()};
|
||||
|
||||
linalg::ElementWiseKernel(ctx_, labels, [=] XGBOOST_DEVICE(size_t i, float const y) mutable {
|
||||
linalg::ElementWiseKernel(ctx_, labels, [=] XGBOOST_DEVICE(std::size_t i, float y) mutable {
|
||||
auto sign = [](auto x) {
|
||||
return (x > static_cast<decltype(x)>(0)) - (x < static_cast<decltype(x)>(0));
|
||||
};
|
||||
auto sample_id = std::get<0>(linalg::UnravelIndex(i, labels.Shape()));
|
||||
auto [sample_id, target_id] = linalg::UnravelIndex(i, labels.Shape());
|
||||
auto grad = sign(predt(i) - y) * weight[sample_id];
|
||||
auto hess = weight[sample_id];
|
||||
gpair(i) = GradientPair{grad, hess};
|
||||
gpair(sample_id, target_id) = GradientPair{grad, hess};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -748,7 +766,7 @@ class MeanAbsoluteError : public ObjFunction {
|
||||
p_tree);
|
||||
}
|
||||
|
||||
const char* DefaultEvalMetric() const override { return "mae"; }
|
||||
[[nodiscard]] const char* DefaultEvalMetric() const override { return "mae"; }
|
||||
|
||||
void SaveConfig(Json* p_out) const override {
|
||||
auto& out = *p_out;
|
||||
@@ -763,5 +781,4 @@ class MeanAbsoluteError : public ObjFunction {
|
||||
XGBOOST_REGISTER_OBJECTIVE(MeanAbsoluteError, "reg:absoluteerror")
|
||||
.describe("Mean absoluate error.")
|
||||
.set_body([]() { return new MeanAbsoluteError(); });
|
||||
} // namespace obj
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::obj
|
||||
|
||||
@@ -66,14 +66,13 @@ inline void FitStump(Context const*, linalg::TensorView<GradientPair const, 2>,
|
||||
#endif // !defined(XGBOOST_USE_CUDA)
|
||||
} // namespace cuda_impl
|
||||
|
||||
void FitStump(Context const* ctx, MetaInfo const& info, HostDeviceVector<GradientPair> const& gpair,
|
||||
void FitStump(Context const* ctx, MetaInfo const& info, linalg::Matrix<GradientPair> const& gpair,
|
||||
bst_target_t n_targets, linalg::Vector<float>* out) {
|
||||
out->SetDevice(ctx->gpu_id);
|
||||
out->Reshape(n_targets);
|
||||
auto n_samples = gpair.Size() / n_targets;
|
||||
|
||||
gpair.SetDevice(ctx->gpu_id);
|
||||
auto gpair_t = linalg::MakeTensorView(ctx, &gpair, n_samples, n_targets);
|
||||
gpair.SetDevice(ctx->Device());
|
||||
auto gpair_t = gpair.View(ctx->Device());
|
||||
ctx->IsCPU() ? cpu_impl::FitStump(ctx, info, gpair_t, out->HostView())
|
||||
: cuda_impl::FitStump(ctx, gpair_t, out->View(ctx->gpu_id));
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ XGBOOST_DEVICE inline double CalcUnregularizedWeight(T sum_grad, T sum_hess) {
|
||||
/**
|
||||
* @brief Fit a tree stump as an estimation of base_score.
|
||||
*/
|
||||
void FitStump(Context const* ctx, MetaInfo const& info, HostDeviceVector<GradientPair> const& gpair,
|
||||
void FitStump(Context const* ctx, MetaInfo const& info, linalg::Matrix<GradientPair> const& gpair,
|
||||
bst_target_t n_targets, linalg::Vector<float>* out);
|
||||
} // namespace tree
|
||||
} // namespace xgboost
|
||||
|
||||
@@ -269,17 +269,18 @@ class GlobalApproxUpdater : public TreeUpdater {
|
||||
out["hist_train_param"] = ToJson(hist_param_);
|
||||
}
|
||||
|
||||
void InitData(TrainParam const ¶m, HostDeviceVector<GradientPair> const *gpair,
|
||||
void InitData(TrainParam const ¶m, linalg::Matrix<GradientPair> const *gpair,
|
||||
linalg::Matrix<GradientPair> *sampled) {
|
||||
*sampled = linalg::Empty<GradientPair>(ctx_, gpair->Size(), 1);
|
||||
sampled->Data()->Copy(*gpair);
|
||||
auto in = gpair->HostView().Values();
|
||||
std::copy(in.data(), in.data() + in.size(), sampled->HostView().Values().data());
|
||||
|
||||
SampleGradient(ctx_, param, sampled->HostView());
|
||||
}
|
||||
|
||||
[[nodiscard]] char const *Name() const override { return "grow_histmaker"; }
|
||||
|
||||
void Update(TrainParam const *param, HostDeviceVector<GradientPair> *gpair, DMatrix *m,
|
||||
void Update(TrainParam const *param, linalg::Matrix<GradientPair> *gpair, DMatrix *m,
|
||||
common::Span<HostDeviceVector<bst_node_t>> out_position,
|
||||
const std::vector<RegTree *> &trees) override {
|
||||
CHECK(hist_param_.GetInitialised());
|
||||
|
||||
@@ -91,7 +91,7 @@ class ColMaker: public TreeUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
void Update(TrainParam const *param, HostDeviceVector<GradientPair> *gpair, DMatrix *dmat,
|
||||
void Update(TrainParam const *param, linalg::Matrix<GradientPair> *gpair, DMatrix *dmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> /*out_position*/,
|
||||
const std::vector<RegTree *> &trees) override {
|
||||
if (collective::IsDistributed()) {
|
||||
@@ -106,10 +106,11 @@ class ColMaker: public TreeUpdater {
|
||||
// rescale learning rate according to size of trees
|
||||
interaction_constraints_.Configure(*param, dmat->Info().num_row_);
|
||||
// build tree
|
||||
CHECK_EQ(gpair->Shape(1), 1) << MTNotImplemented();
|
||||
for (auto tree : trees) {
|
||||
CHECK(ctx_);
|
||||
Builder builder(*param, colmaker_param_, interaction_constraints_, ctx_, column_densities_);
|
||||
builder.Update(gpair->ConstHostVector(), dmat, tree);
|
||||
builder.Update(gpair->Data()->ConstHostVector(), dmat, tree);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -760,16 +760,18 @@ class GPUHistMaker : public TreeUpdater {
|
||||
dh::GlobalMemoryLogger().Log();
|
||||
}
|
||||
|
||||
void Update(TrainParam const* param, HostDeviceVector<GradientPair>* gpair, DMatrix* dmat,
|
||||
void Update(TrainParam const* param, linalg::Matrix<GradientPair>* gpair, DMatrix* dmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> out_position,
|
||||
const std::vector<RegTree*>& trees) override {
|
||||
monitor_.Start("Update");
|
||||
|
||||
CHECK_EQ(gpair->Shape(1), 1) << MTNotImplemented();
|
||||
auto gpair_hdv = gpair->Data();
|
||||
// build tree
|
||||
try {
|
||||
std::size_t t_idx{0};
|
||||
for (xgboost::RegTree* tree : trees) {
|
||||
this->UpdateTree(param, gpair, dmat, tree, &out_position[t_idx]);
|
||||
this->UpdateTree(param, gpair_hdv, dmat, tree, &out_position[t_idx]);
|
||||
this->hist_maker_param_.CheckTreesSynchronized(tree);
|
||||
++t_idx;
|
||||
}
|
||||
@@ -887,7 +889,7 @@ class GPUGlobalApproxMaker : public TreeUpdater {
|
||||
}
|
||||
~GPUGlobalApproxMaker() override { dh::GlobalMemoryLogger().Log(); }
|
||||
|
||||
void Update(TrainParam const* param, HostDeviceVector<GradientPair>* gpair, DMatrix* p_fmat,
|
||||
void Update(TrainParam const* param, linalg::Matrix<GradientPair>* gpair, DMatrix* p_fmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> out_position,
|
||||
const std::vector<RegTree*>& trees) override {
|
||||
monitor_.Start("Update");
|
||||
@@ -898,7 +900,7 @@ class GPUGlobalApproxMaker : public TreeUpdater {
|
||||
auto hess = dh::ToSpan(hess_);
|
||||
|
||||
gpair->SetDevice(ctx_->Device());
|
||||
auto d_gpair = gpair->ConstDeviceSpan();
|
||||
auto d_gpair = gpair->Data()->ConstDeviceSpan();
|
||||
auto cuctx = ctx_->CUDACtx();
|
||||
thrust::transform(cuctx->CTP(), dh::tcbegin(d_gpair), dh::tcend(d_gpair), dh::tbegin(hess),
|
||||
[=] XGBOOST_DEVICE(GradientPair const& g) { return g.GetHess(); });
|
||||
@@ -912,7 +914,7 @@ class GPUGlobalApproxMaker : public TreeUpdater {
|
||||
|
||||
std::size_t t_idx{0};
|
||||
for (xgboost::RegTree* tree : trees) {
|
||||
this->UpdateTree(gpair, p_fmat, tree, &out_position[t_idx]);
|
||||
this->UpdateTree(gpair->Data(), p_fmat, tree, &out_position[t_idx]);
|
||||
this->hist_maker_param_.CheckTreesSynchronized(tree);
|
||||
++t_idx;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class TreePruner : public TreeUpdater {
|
||||
[[nodiscard]] bool CanModifyTree() const override { return true; }
|
||||
|
||||
// update the tree, do pruning
|
||||
void Update(TrainParam const* param, HostDeviceVector<GradientPair>* gpair, DMatrix* p_fmat,
|
||||
void Update(TrainParam const* param, linalg::Matrix<GradientPair>* gpair, DMatrix* p_fmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> out_position,
|
||||
const std::vector<RegTree*>& trees) override {
|
||||
pruner_monitor_.Start("PrunerUpdate");
|
||||
|
||||
@@ -492,7 +492,7 @@ class QuantileHistMaker : public TreeUpdater {
|
||||
|
||||
[[nodiscard]] char const *Name() const override { return "grow_quantile_histmaker"; }
|
||||
|
||||
void Update(TrainParam const *param, HostDeviceVector<GradientPair> *gpair, DMatrix *p_fmat,
|
||||
void Update(TrainParam const *param, linalg::Matrix<GradientPair> *gpair, DMatrix *p_fmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> out_position,
|
||||
const std::vector<RegTree *> &trees) override {
|
||||
if (trees.front()->IsMultiTarget()) {
|
||||
@@ -511,8 +511,7 @@ class QuantileHistMaker : public TreeUpdater {
|
||||
}
|
||||
|
||||
bst_target_t n_targets = trees.front()->NumTargets();
|
||||
auto h_gpair =
|
||||
linalg::MakeTensorView(ctx_, gpair->HostSpan(), p_fmat->Info().num_row_, n_targets);
|
||||
auto h_gpair = gpair->HostView();
|
||||
|
||||
linalg::Matrix<GradientPair> sample_out;
|
||||
auto h_sample_out = h_gpair;
|
||||
|
||||
@@ -31,11 +31,14 @@ class TreeRefresher : public TreeUpdater {
|
||||
[[nodiscard]] char const *Name() const override { return "refresh"; }
|
||||
[[nodiscard]] bool CanModifyTree() const override { return true; }
|
||||
// update the tree, do pruning
|
||||
void Update(TrainParam const *param, HostDeviceVector<GradientPair> *gpair, DMatrix *p_fmat,
|
||||
void Update(TrainParam const *param, linalg::Matrix<GradientPair> *gpair, DMatrix *p_fmat,
|
||||
common::Span<HostDeviceVector<bst_node_t>> /*out_position*/,
|
||||
const std::vector<RegTree *> &trees) override {
|
||||
if (trees.size() == 0) return;
|
||||
const std::vector<GradientPair> &gpair_h = gpair->ConstHostVector();
|
||||
if (trees.size() == 0) {
|
||||
return;
|
||||
}
|
||||
CHECK_EQ(gpair->Shape(1), 1) << MTNotImplemented();
|
||||
const std::vector<GradientPair> &gpair_h = gpair->Data()->ConstHostVector();
|
||||
// thread temporal space
|
||||
std::vector<std::vector<GradStats> > stemp;
|
||||
std::vector<RegTree::FVec> fvec_temp;
|
||||
|
||||
@@ -31,7 +31,7 @@ class TreeSyncher : public TreeUpdater {
|
||||
|
||||
[[nodiscard]] char const* Name() const override { return "prune"; }
|
||||
|
||||
void Update(TrainParam const*, HostDeviceVector<GradientPair>*, DMatrix*,
|
||||
void Update(TrainParam const*, linalg::Matrix<GradientPair>*, DMatrix*,
|
||||
common::Span<HostDeviceVector<bst_node_t>> /*out_position*/,
|
||||
const std::vector<RegTree*>& trees) override {
|
||||
if (collective::GetWorldSize() == 1) return;
|
||||
|
||||
Reference in New Issue
Block a user