Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] Experimental support for l1 error. #7812

Merged
merged 127 commits into from Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
ad0dcee
Initial commit.
trivialfis Apr 1, 2022
73aef42
refresh.
trivialfis Apr 1, 2022
f0d949a
Start looking into quantile reg.
trivialfis Apr 1, 2022
3def77b
init.
trivialfis Apr 1, 2022
4f5cd8c
quantile.
trivialfis Apr 1, 2022
b216a38
percentile.
trivialfis Apr 7, 2022
ef5a141
Fixes.
trivialfis Apr 8, 2022
d19e667
Test.
trivialfis Apr 8, 2022
823648c
Use in boosting.
trivialfis Apr 8, 2022
07523df
Make sure it's used.
trivialfis Apr 8, 2022
fd1729c
Use transform iter.
trivialfis Apr 8, 2022
033ac6f
fixes.
trivialfis Apr 8, 2022
6b53665
Fix.
trivialfis Apr 8, 2022
dc1015c
cleanup.
trivialfis Apr 8, 2022
076f810
Subsample.
trivialfis Apr 8, 2022
609ceb3
Start working on GPU.
trivialfis Apr 8, 2022
f5732dc
Work on GPU.
trivialfis Apr 8, 2022
9513cf4
Copy the row index.
trivialfis Apr 9, 2022
d9ac306
Comp.
trivialfis Apr 9, 2022
eca5afb
Start working on device quantile.
trivialfis Apr 9, 2022
8480886
Compute target.
trivialfis Apr 12, 2022
9ff0a01
Work on GPU partitioner.
trivialfis Apr 12, 2022
2a5f8bd
rle.
trivialfis Apr 12, 2022
9e7cdac
use it in obj.
trivialfis Apr 12, 2022
b1be3fe
fixes
trivialfis Apr 12, 2022
bf4c5b8
fix.
trivialfis Apr 12, 2022
34680ac
Cleanup & fix.
trivialfis Apr 13, 2022
2df0a50
Commented code.
trivialfis Apr 13, 2022
f0101a6
Start working on weighted.
trivialfis Apr 13, 2022
3904578
Start working on weighted.
trivialfis Apr 13, 2022
82af757
Move.
trivialfis Apr 13, 2022
5083125
Refactor.
trivialfis Apr 13, 2022
cc0defd
GPU weighted.
trivialfis Apr 13, 2022
c722b36
Fix.
trivialfis Apr 13, 2022
7c50061
Refactor.
trivialfis Apr 13, 2022
055fa2f
hist.
trivialfis Apr 13, 2022
5ea25d0
Format.
trivialfis Apr 13, 2022
ce80cb3
Cleanup.
trivialfis Apr 13, 2022
6fbc58a
working on sampling.
trivialfis Apr 13, 2022
ebd500c
Fix.
trivialfis Apr 13, 2022
ec82f8e
Drop interpolation altogether.
trivialfis Apr 13, 2022
245e202
Drop on GPU.
trivialfis Apr 13, 2022
a0f9757
start poc for refactor.
trivialfis Apr 13, 2022
925e93c
Use iterator for non-weight quantile.
trivialfis Apr 14, 2022
29bd9a6
fix test.
trivialfis Apr 14, 2022
10b2108
Fix test.
trivialfis Apr 14, 2022
aea2ead
Start working on test.
trivialfis Apr 14, 2022
13b5f28
Avoid computing residule.
trivialfis Apr 14, 2022
965060c
Use iter for weighted.
trivialfis Apr 14, 2022
b6257bd
Use permutation iterator.
trivialfis Apr 14, 2022
321558f
Allreduce.
trivialfis Apr 14, 2022
05a8133
Disable multi-target.
trivialfis Apr 14, 2022
f1670af
Fix regen.
trivialfis Apr 14, 2022
9cf36fe
Restore ext handling.
trivialfis Apr 14, 2022
3fa3bf8
Guard.
trivialfis Apr 14, 2022
df562e2
Empty matrix test.
trivialfis Apr 14, 2022
0f486c0
Fix empty dmatrix on CPU.
trivialfis Apr 14, 2022
4d77a90
Unify the cache.
trivialfis Apr 14, 2022
3d51200
Merge remote-tracking branch 'jiamingy/adaptive-tree' into adaptive-tree
trivialfis Apr 14, 2022
48a6882
Add test.
trivialfis Apr 14, 2022
9c87c53
Set device.
trivialfis Apr 14, 2022
2c8a204
Extract the code.
trivialfis Apr 14, 2022
399fcbe
small cleanup.
trivialfis Apr 14, 2022
5c3d82f
GPU sampling test.
trivialfis Apr 14, 2022
cb0df4b
Fix empty leaf.
trivialfis Apr 14, 2022
64e9d5a
Change name.
trivialfis Apr 14, 2022
6905afb
failed cases.
trivialfis Apr 14, 2022
94c3fab
Fix empty.
trivialfis Apr 14, 2022
75c5aff
debug.
trivialfis Apr 15, 2022
f953ab3
Fix.
trivialfis Apr 15, 2022
1ce049c
Fix.
trivialfis Apr 15, 2022
d9d51e4
Cleanup.
trivialfis Apr 15, 2022
bd6069d
Merge remote-tracking branch 'jiamingy/adaptive-tree' into adaptive-tree
trivialfis Apr 15, 2022
cdf93e2
Cleanups.
trivialfis Apr 15, 2022
914c19a
Cleanup.
trivialfis Apr 15, 2022
0a5525e
Fix test.
trivialfis Apr 15, 2022
8af41ad
Fix test.
trivialfis Apr 15, 2022
aca3304
Fill missing leaf.
trivialfis Apr 15, 2022
705d738
another sample.
trivialfis Apr 15, 2022
a23361f
Fix external memory.
trivialfis Apr 15, 2022
4948886
Note.
trivialfis Apr 15, 2022
30783fd
Return NAN.
trivialfis Apr 15, 2022
81014b2
Cleanup.
trivialfis Apr 15, 2022
3ab84a2
Fix subsample.
trivialfis Apr 15, 2022
598c0e4
rename.
trivialfis Apr 15, 2022
19934f3
Remove target.
trivialfis Apr 15, 2022
9dff694
note.
trivialfis Apr 15, 2022
6bbba67
test.
trivialfis Apr 15, 2022
e58a339
CPU build.
trivialfis Apr 15, 2022
3faab6c
Json schema.
trivialfis Apr 15, 2022
35b64dc
Fix tests.
trivialfis Apr 15, 2022
1105b6f
Skip tests.
trivialfis Apr 15, 2022
3a25702
Skip test.
trivialfis Apr 15, 2022
4a7359c
Cleanup.
trivialfis Apr 16, 2022
e9a434b
Merge remote-tracking branch 'jiaming/adaptive-tree' into adaptive-tree
trivialfis Apr 16, 2022
b2b79f4
Make optional.
trivialfis Apr 16, 2022
5cc1a4a
Allreduce.
trivialfis Apr 16, 2022
493795b
Test.
trivialfis Apr 16, 2022
980219f
Header.
trivialfis Apr 16, 2022
286f3a9
Fix.
trivialfis Apr 16, 2022
c485d0a
Workaround.
trivialfis Apr 16, 2022
56d8beb
Fix set device.
trivialfis Apr 16, 2022
02e04b6
Small cleanup.
trivialfis Apr 17, 2022
26dcc40
Skip if updater doesn't support this feature.
trivialfis Apr 17, 2022
467348b
Pass in position.
trivialfis Apr 22, 2022
423520c
Copy position on host.
trivialfis Apr 22, 2022
4ac1211
Compile.
trivialfis Apr 22, 2022
b66c262
Fix n leaf.
trivialfis Apr 22, 2022
6b4f6f4
Sampling.
trivialfis Apr 22, 2022
2cebd45
approx test.
trivialfis Apr 22, 2022
13f772b
Compile on GPU.
trivialfis Apr 22, 2022
d83a973
Cleanup.
trivialfis Apr 22, 2022
a4ce314
Refactor.
trivialfis Apr 22, 2022
91c24bb
empty.
trivialfis Apr 22, 2022
f86ca05
Cleanup.
trivialfis Apr 22, 2022
fd9fcc2
Cleanup.
trivialfis Apr 22, 2022
244c216
Skip updaters that don't support partitioner.
trivialfis Apr 22, 2022
b940311
Forbid external memory.
trivialfis Apr 22, 2022
fe1f36f
Small cleanups.
trivialfis Apr 22, 2022
a81359f
Fix test.
trivialfis Apr 22, 2022
0d328f1
tidy.
trivialfis Apr 23, 2022
a0d883b
Use nan.
trivialfis Apr 23, 2022
5aa21bc
Remove unused code.
trivialfis Apr 25, 2022
1144fd3
Remove optimization.
trivialfis Apr 25, 2022
8f96187
Make it a check.
trivialfis Apr 25, 2022
1cf3f02
Revert some cleanups.
trivialfis Apr 25, 2022
a83c813
Naming after the refactor.
trivialfis Apr 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions amalgamation/xgboost-all0.cc
Expand Up @@ -24,6 +24,7 @@
#include "../src/objective/rank_obj.cc"
#include "../src/objective/hinge.cc"
#include "../src/objective/aft_obj.cc"
#include "../src/objective/adaptive.cc"

// gbms
#include "../src/gbm/gbm.cc"
Expand Down
9 changes: 8 additions & 1 deletion doc/model.schema
Expand Up @@ -400,7 +400,6 @@
"reg_loss_param"
]
},

{
"type": "object",
"properties": {
Expand Down Expand Up @@ -433,6 +432,14 @@
"tweedie_regression_param"
]
},
{
"properties": {
"name": {
"const": "reg:absoluteerror"
}
},
"type": "object"
},
{
"type": "object",
"properties": {
Expand Down
1 change: 1 addition & 0 deletions doc/parameter.rst
Expand Up @@ -349,6 +349,7 @@ Specify the learning task and the corresponding learning objective. The objectiv
- ``reg:squaredlogerror``: regression with squared log loss :math:`\frac{1}{2}[log(pred + 1) - log(label + 1)]^2`. All input labels are required to be greater than -1. Also, see metric ``rmsle`` for possible issue with this objective.
- ``reg:logistic``: logistic regression.
- ``reg:pseudohubererror``: regression with Pseudo Huber loss, a twice differentiable alternative to absolute loss.
- ``reg:absoluteerror``: Regression with L1 error. When tree model is used, leaf value is refreshed after tree construction.
- ``binary:logistic``: logistic regression for binary classification, output probability
- ``binary:logitraw``: logistic regression for binary classification, output score before logistic transformation
- ``binary:hinge``: hinge loss for binary classification. This makes predictions of 0 or 1, rather than producing probabilities.
Expand Down
5 changes: 2 additions & 3 deletions include/xgboost/gbm.h
Expand Up @@ -90,9 +90,8 @@ class GradientBooster : public Model, public Configurable {
* \param prediction The output prediction cache entry that needs to be updated.
* the booster may change content of gpair
*/
virtual void DoBoost(DMatrix* p_fmat,
HostDeviceVector<GradientPair>* in_gpair,
PredictionCacheEntry*) = 0;
virtual void DoBoost(DMatrix* p_fmat, HostDeviceVector<GradientPair>* in_gpair,
PredictionCacheEntry*, ObjFunction const* obj) = 0;

/*!
* \brief generate predictions for given feature matrix
Expand Down
8 changes: 6 additions & 2 deletions include/xgboost/linalg.h
Expand Up @@ -670,9 +670,13 @@ class Tensor {
* See \ref TensorView for parameters of this constructor.
*/
template <typename I, int32_t D>
explicit Tensor(I const (&shape)[D], int32_t device) {
explicit Tensor(I const (&shape)[D], int32_t device)
: Tensor{common::Span<I const, D>{shape}, device} {}

template <typename I, size_t D>
explicit Tensor(common::Span<I const, D> shape, int32_t device) {
// No device unroll as this is a host only function.
std::copy(shape, shape + D, shape_);
std::copy(shape.data(), shape.data() + D, shape_);
for (auto i = D; i < kDim; ++i) {
shape_[i] = 1;
}
Expand Down
20 changes: 19 additions & 1 deletion include/xgboost/objective.h
@@ -1,5 +1,5 @@
/*!
* Copyright 2014-2019 by Contributors
* Copyright 2014-2022 by Contributors
* \file objective.h
* \brief interface of objective function used by xgboost.
* \author Tianqi Chen, Kailong Chen
Expand All @@ -22,6 +22,8 @@

namespace xgboost {

class RegTree;

/*! \brief interface of objective function */
class ObjFunction : public Configurable {
protected:
Expand Down Expand Up @@ -88,6 +90,22 @@ class ObjFunction : public Configurable {
return 1;
}

/**
* \brief Update the leaf values after a tree is built. Needed for objectives with 0
* hessian.
*
* Note that the leaf update is not well defined for distributed training as XGBoost
* computes only an average of quantile between workers. This breaks when some leaf
* have no sample assigned in a local worker.
*
* \param position The leaf index for each rows.
* \param info MetaInfo providing labels and weights.
* \param prediction Model prediction after transformation.
* \param p_tree Tree that needs to be updated.
*/
virtual void UpdateTreeLeaf(HostDeviceVector<bst_node_t> const& position, MetaInfo const& info,
HostDeviceVector<float> const& prediction, RegTree* p_tree) const {}

/*!
* \brief Create an objective function according to name.
* \param tparam Generic parameters.
Expand Down
9 changes: 7 additions & 2 deletions include/xgboost/task.h
Expand Up @@ -33,13 +33,18 @@ struct ObjInfo {
} task;
// Does the objective have constant hessian value?
bool const_hess{false};
bool zero_hess{false};

explicit ObjInfo(Task t) : task{t} {}
ObjInfo(Task t, bool khess) : task{t}, const_hess{khess} {}
ObjInfo(Task t) : task{t} {} // NOLINT
ObjInfo(Task t, bool khess, bool zhess) : task{t}, const_hess{khess}, zero_hess(zhess) {}

XGBOOST_DEVICE bool UseOneHot() const {
return (task != ObjInfo::kRegression && task != ObjInfo::kBinary);
}
/**
* \brief Use adaptive tree if the objective doesn't have valid hessian value.
*/
XGBOOST_DEVICE bool UpdateTreeLeaf() const { return zero_hess; }
};
} // namespace xgboost
#endif // XGBOOST_TASK_H_
15 changes: 11 additions & 4 deletions include/xgboost/tree_updater.h
Expand Up @@ -49,18 +49,25 @@ class TreeUpdater : public Configurable {
* existing trees.
*/
virtual bool CanModifyTree() const { return false; }
/*!
* \brief Wether the out_position in `Update` is valid. This determines whether adaptive
* tree can be used.
*/
virtual bool HasNodePosition() const { return false; }
trivialfis marked this conversation as resolved.
Show resolved Hide resolved
/*!
* \brief perform update to the tree models
* \param gpair the gradient pair statistics of the data
* \param data The data matrix passed to the updater.
* \param trees references the trees to be updated, updater will change the content of trees
* \param out_position The leaf index for each row. The index is negated if that row is
* removed during sampling. So the 3th node is ~3.
* \param out_trees references the trees to be updated, updater will change the content of trees
* note: all the trees in the vector are updated, with the same statistics,
* but maybe different random seeds, usually one tree is passed in at a time,
* there can be multiple trees when we train random forest style model
*/
virtual void Update(HostDeviceVector<GradientPair>* gpair,
DMatrix* data,
const std::vector<RegTree*>& trees) = 0;
virtual void Update(HostDeviceVector<GradientPair>* gpair, DMatrix* data,
common::Span<HostDeviceVector<bst_node_t>> out_position,
const std::vector<RegTree*>& out_trees) = 0;

/*!
* \brief determines whether updater has enough knowledge about a given dataset
Expand Down
10 changes: 3 additions & 7 deletions plugin/example/custom_obj.cc
@@ -1,5 +1,5 @@
/*!
* Copyright 2015-2019 by Contributors
* Copyright 2015-2022 by Contributors
* \file custom_metric.cc
* \brief This is an example to define plugin of xgboost.
* This plugin defines the additional metric function.
Expand Down Expand Up @@ -31,13 +31,9 @@ DMLC_REGISTER_PARAMETER(MyLogisticParam);
// Implement the interface.
class MyLogistic : public ObjFunction {
public:
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {
param_.UpdateAllowUnknown(args);
}
void Configure(const Args& args) override { param_.UpdateAllowUnknown(args); }

struct ObjInfo Task() const override {
return {ObjInfo::kRegression, false};
}
ObjInfo Task() const override { return ObjInfo::kRegression; }

void GetGradient(const HostDeviceVector<bst_float> &preds,
const MetaInfo &info,
Expand Down
99 changes: 93 additions & 6 deletions src/common/common.h
@@ -1,5 +1,5 @@
/*!
* Copyright 2015-2018 by Contributors
* Copyright 2015-2022 by XGBoost Contributors
* \file common.h
* \brief Common utilities
*/
Expand All @@ -14,12 +14,12 @@
#include <exception>
#include <functional>
#include <limits>
#include <type_traits>
#include <vector>
#include <string>
#include <sstream>
#include <numeric>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#if defined(__CUDACC__)
#include <thrust/system/cuda/error.h>
Expand Down Expand Up @@ -164,6 +164,67 @@ class Range {
Iterator end_;
};

/**
* \brief Transform iterator that takes an index and calls transform operator.
*
* This is CPU-only right now as taking host device function as operator complicates the
* code. For device side one can use `thrust::transform_iterator` instead.
*/
template <typename Fn>
class IndexTransformIter {
size_t iter_{0};
Fn fn_;

public:
using iterator_category = std::random_access_iterator_tag; // NOLINT
using value_type = std::result_of_t<Fn(size_t)>; // NOLINT
using difference_type = detail::ptrdiff_t; // NOLINT
using reference = std::add_lvalue_reference_t<value_type>; // NOLINT
using pointer = std::add_pointer_t<value_type>; // NOLINT

public:
/**
* \param op Transform operator, takes a size_t index as input.
*/
explicit IndexTransformIter(Fn &&op) : fn_{op} {}
IndexTransformIter(IndexTransformIter const &) = default;

value_type operator*() const { return fn_(iter_); }

auto operator-(IndexTransformIter const &that) const { return iter_ - that.iter_; }

IndexTransformIter &operator++() {
iter_++;
return *this;
}
IndexTransformIter operator++(int) {
auto ret = *this;
++(*this);
return ret;
}
IndexTransformIter &operator+=(difference_type n) {
iter_ += n;
return *this;
}
IndexTransformIter &operator-=(difference_type n) {
(*this) += -n;
return *this;
}
IndexTransformIter operator+(difference_type n) const {
auto ret = *this;
return ret += n;
}
IndexTransformIter operator-(difference_type n) const {
auto ret = *this;
return ret -= n;
}
};

template <typename Fn>
auto MakeIndexTransformIter(Fn&& fn) {
return IndexTransformIter<Fn>(std::forward<Fn>(fn));
}

int AllVisibleGPUs();

inline void AssertGPUSupport() {
Expand Down Expand Up @@ -191,13 +252,39 @@ std::vector<Idx> ArgSort(Container const &array, Comp comp = std::less<V>{}) {

struct OptionalWeights {
Span<float const> weights;
float dft{1.0f};
float dft{1.0f}; // fixme: make this compile time constant

explicit OptionalWeights(Span<float const> w) : weights{w} {}
explicit OptionalWeights(float w) : dft{w} {}

XGBOOST_DEVICE float operator[](size_t i) const { return weights.empty() ? dft : weights[i]; }
};

/**
* Last index of a group in a CSR style of index pointer.
*/
template <typename Indexable>
XGBOOST_DEVICE size_t LastOf(size_t group, Indexable const &indptr) {
return indptr[group + 1] - 1;
}

/**
* \brief Run length encode on CPU, input must be sorted.
*/
template <typename Iter, typename Idx>
void RunLengthEncode(Iter begin, Iter end, std::vector<Idx> *p_out) {
auto &out = *p_out;
out = std::vector<Idx>{0};
size_t n = std::distance(begin, end);
for (size_t i = 1; i < n; ++i) {
if (begin[i] != begin[i - 1]) {
out.push_back(i);
}
}
if (out.back() != n) {
out.push_back(n);
}
}
} // namespace common
} // namespace xgboost
#endif // XGBOOST_COMMON_COMMON_H_
40 changes: 39 additions & 1 deletion src/common/device_helpers.cuh
@@ -1,5 +1,5 @@
/*!
* Copyright 2017-2021 XGBoost contributors
* Copyright 2017-2022 XGBoost contributors
*/
#pragma once
#include <thrust/device_ptr.h>
Expand Down Expand Up @@ -1537,6 +1537,43 @@ void SegmentedArgSort(xgboost::common::Span<U> values,
sorted_idx.size_bytes(), cudaMemcpyDeviceToDevice));
}

/**
* \brief Different from the above one, this one can handle cases where segment doesn't
* start from 0, but as a result it uses comparison sort.
*/
template <typename SegIt, typename ValIt>
void SegmentedArgSort(SegIt seg_begin, SegIt seg_end, ValIt val_begin, ValIt val_end,
dh::device_vector<size_t> *p_sorted_idx) {
using Tup = thrust::tuple<int32_t, float>;
auto &sorted_idx = *p_sorted_idx;
size_t n = std::distance(val_begin, val_end);
sorted_idx.resize(n);
dh::Iota(dh::ToSpan(sorted_idx));
dh::device_vector<Tup> keys(sorted_idx.size());
auto key_it = dh::MakeTransformIterator<Tup>(thrust::make_counting_iterator(0ul),
[=] XGBOOST_DEVICE(size_t i) -> Tup {
int32_t leaf_idx;
if (i < *seg_begin) {
leaf_idx = -1;
} else {
leaf_idx = dh::SegmentId(seg_begin, seg_end, i);
}
auto residue = val_begin[i];
return thrust::make_tuple(leaf_idx, residue);
});
dh::XGBCachingDeviceAllocator<char> caching;
thrust::copy(thrust::cuda::par(caching), key_it, key_it + keys.size(), keys.begin());

dh::XGBDeviceAllocator<char> alloc;
thrust::stable_sort_by_key(thrust::cuda::par(alloc), keys.begin(), keys.end(), sorted_idx.begin(),
[=] XGBOOST_DEVICE(Tup const &l, Tup const &r) {
if (thrust::get<0>(l) != thrust::get<0>(r)) {
return thrust::get<0>(l) < thrust::get<0>(r); // segment index
}
return thrust::get<1>(l) < thrust::get<1>(r); // residue
});
}

class CUDAStreamView;

class CUDAEvent {
Expand Down Expand Up @@ -1600,5 +1637,6 @@ class CUDAStream {
}

CUDAStreamView View() const { return CUDAStreamView{stream_}; }
void Sync() { this->View().Sync(); }
};
} // namespace dh
3 changes: 2 additions & 1 deletion src/common/linalg_op.cuh
Expand Up @@ -13,6 +13,7 @@ namespace xgboost {
namespace linalg {
template <typename T, int32_t D, typename Fn>
void ElementWiseKernelDevice(linalg::TensorView<T, D> t, Fn&& fn, cudaStream_t s = nullptr) {
dh::safe_cuda(cudaSetDevice(t.DeviceIdx()));
static_assert(std::is_void<std::result_of_t<Fn(size_t, T&)>>::value,
"For function with return, use transform instead.");
if (t.Contiguous()) {
Expand Down Expand Up @@ -40,7 +41,7 @@ void ElementWiseTransformDevice(linalg::TensorView<T, D> t, Fn&& fn, cudaStream_
}

template <typename T, int32_t D, typename Fn>
void ElementWiseKernel(GenericParameter const* ctx, linalg::TensorView<T, D> t, Fn&& fn) {
void ElementWiseKernel(Context const* ctx, linalg::TensorView<T, D> t, Fn&& fn) {
ctx->IsCPU() ? ElementWiseKernelHost(t, ctx->Threads(), fn) : ElementWiseKernelDevice(t, fn);
}
} // namespace linalg
Expand Down