|
13 | 13 | import xgboost.testing as tm |
14 | 14 |
|
15 | 15 | from .._typing import ArrayLike |
16 | | -from ..core import Booster, DMatrix, QuantileDMatrix |
| 16 | +from ..compat import import_cupy |
| 17 | +from ..core import Booster, DMatrix, ExtMemQuantileDMatrix, QuantileDMatrix |
17 | 18 | from ..objective import Objective, TreeObjective |
18 | 19 | from ..sklearn import XGBClassifier |
19 | 20 | from ..training import train |
| 21 | +from .data import IteratorForTest |
20 | 22 | from .updater import ResetStrategy |
21 | | -from .utils import Device |
| 23 | +from .utils import Device, assert_allclose |
22 | 24 |
|
23 | 25 |
|
24 | 26 | def run_multiclass(device: Device, learning_rate: Optional[float]) -> None: |
@@ -64,34 +66,42 @@ def run_multilabel(device: Device, learning_rate: Optional[float]) -> None: |
64 | 66 | assert proba.shape == y.shape |
65 | 67 |
|
66 | 68 |
|
67 | | -def run_reduced_grad(device: Device) -> None: |
68 | | - """Basic test for using reduced gradient for tree splits.""" |
69 | | - import cupy as cp |
| 69 | +class LsObj0(TreeObjective): |
| 70 | + """Split grad is the same as value grad.""" |
70 | 71 |
|
71 | | - class LsObj0(TreeObjective): |
72 | | - """Split grad is the same as value grad.""" |
| 72 | + def __call__( |
| 73 | + self, y_pred: ArrayLike, dtrain: DMatrix |
| 74 | + ) -> Tuple[ArrayLike, ArrayLike]: |
| 75 | + cp = import_cupy() |
73 | 76 |
|
74 | | - def __call__( |
75 | | - self, y_pred: ArrayLike, dtrain: DMatrix |
76 | | - ) -> Tuple[cp.ndarray, cp.ndarray]: |
77 | | - y_true = dtrain.get_label().reshape(y_pred.shape) |
78 | | - grad, hess = tm.ls_obj(y_true, y_pred, None) |
79 | | - return cp.array(grad), cp.array(hess) |
| 77 | + y_true = dtrain.get_label().reshape(y_pred.shape) |
| 78 | + grad, hess = tm.ls_obj(y_true, y_pred, None) |
| 79 | + return cp.array(grad), cp.array(hess) |
80 | 80 |
|
81 | | - def split_grad( |
82 | | - self, grad: ArrayLike, hess: ArrayLike |
83 | | - ) -> Tuple[ArrayLike, ArrayLike]: |
84 | | - return cp.array(grad), cp.array(hess) |
| 81 | + def split_grad( |
| 82 | + self, grad: ArrayLike, hess: ArrayLike |
| 83 | + ) -> Tuple[ArrayLike, ArrayLike]: |
| 84 | + cp = import_cupy() |
85 | 85 |
|
86 | | - class LsObj1(Objective): |
87 | | - """No split grad.""" |
| 86 | + return cp.array(grad), cp.array(hess) |
88 | 87 |
|
89 | | - def __call__( |
90 | | - self, y_pred: ArrayLike, dtrain: DMatrix |
91 | | - ) -> Tuple[cp.ndarray, cp.ndarray]: |
92 | | - y_true = dtrain.get_label().reshape(y_pred.shape) |
93 | | - grad, hess = tm.ls_obj(y_true, y_pred, None) |
94 | | - return cp.array(grad), cp.array(hess) |
| 88 | + |
| 89 | +class LsObj1(Objective): |
| 90 | + """No split grad.""" |
| 91 | + |
| 92 | + def __call__( |
| 93 | + self, y_pred: ArrayLike, dtrain: DMatrix |
| 94 | + ) -> Tuple[ArrayLike, ArrayLike]: |
| 95 | + cp = import_cupy() |
| 96 | + |
| 97 | + y_true = dtrain.get_label().reshape(y_pred.shape) |
| 98 | + grad, hess = tm.ls_obj(y_true, y_pred, None) |
| 99 | + return cp.array(grad), cp.array(hess) |
| 100 | + |
| 101 | + |
| 102 | +def run_reduced_grad(device: Device) -> None: |
| 103 | + """Basic test for using reduced gradient for tree splits.""" |
| 104 | + import cupy as cp |
95 | 105 |
|
96 | 106 | X, y = make_regression( # pylint: disable=unbalanced-tuple-unpacking |
97 | 107 | n_samples=1024, n_features=16, random_state=1994, n_targets=5 |
@@ -149,3 +159,85 @@ def split_grad( |
149 | 159 | run_test(LsObj2(False)) |
150 | 160 | with pytest.raises(AssertionError): |
151 | 161 | run_test(LsObj2(True)) |
| 162 | + |
| 163 | + |
| 164 | +def run_with_iter(device: Device) -> None: # pylint: disable=too-many-locals |
| 165 | + """Test vector leaf with external memory.""" |
| 166 | + if device == "cuda": |
| 167 | + from cupy import asarray |
| 168 | + else: |
| 169 | + from numpy import asarray |
| 170 | + |
| 171 | + n_batches = 4 |
| 172 | + n_rounds = 8 |
| 173 | + n_targets = 3 |
| 174 | + intercept = [0.5] * n_targets |
| 175 | + Xs = [] |
| 176 | + ys = [] |
| 177 | + for i in range(n_batches): |
| 178 | + X_i, y_i = make_regression( # pylint: disable=unbalanced-tuple-unpacking |
| 179 | + n_samples=4096, n_features=8, random_state=(i + 1), n_targets=n_targets |
| 180 | + ) |
| 181 | + Xs.append(asarray(X_i)) |
| 182 | + ys.append(asarray(y_i)) |
| 183 | + it = IteratorForTest(Xs, ys, None, cache="cache", on_host=True) |
| 184 | + Xy: DMatrix = ExtMemQuantileDMatrix(it, cache_host_ratio=1.0) |
| 185 | + |
| 186 | + evals_result_0: Dict[str, Dict] = {} |
| 187 | + booster_0 = train( |
| 188 | + { |
| 189 | + "device": device, |
| 190 | + "multi_strategy": "multi_output_tree", |
| 191 | + "learning_rate": 1.0, |
| 192 | + "base_score": intercept, |
| 193 | + }, |
| 194 | + Xy, |
| 195 | + num_boost_round=n_rounds, |
| 196 | + evals=[(Xy, "Train")], |
| 197 | + evals_result=evals_result_0, |
| 198 | + ) |
| 199 | + |
| 200 | + it = IteratorForTest(Xs, ys, None, cache=None) |
| 201 | + Xy = QuantileDMatrix(it) |
| 202 | + evals_result_1: Dict[str, Dict] = {} |
| 203 | + booster_1 = train( |
| 204 | + { |
| 205 | + "device": device, |
| 206 | + "multi_strategy": "multi_output_tree", |
| 207 | + "learning_rate": 1.0, |
| 208 | + "base_score": intercept, |
| 209 | + }, |
| 210 | + Xy, |
| 211 | + num_boost_round=n_rounds, |
| 212 | + evals=[(Xy, "Train")], |
| 213 | + evals_result=evals_result_1, |
| 214 | + ) |
| 215 | + np.testing.assert_allclose( |
| 216 | + evals_result_0["Train"]["rmse"], evals_result_1["Train"]["rmse"] |
| 217 | + ) |
| 218 | + assert tm.non_increasing(evals_result_0["Train"]["rmse"]) |
| 219 | + X, _, _ = it.as_arrays() |
| 220 | + assert_allclose(device, booster_0.inplace_predict(X), booster_1.inplace_predict(X)) |
| 221 | + |
| 222 | + it = IteratorForTest(Xs, ys, None, cache="cache", on_host=True) |
| 223 | + Xy = ExtMemQuantileDMatrix(it, cache_host_ratio=1.0) |
| 224 | + |
| 225 | + evals_result_2: Dict[str, Dict] = {} |
| 226 | + booster_2 = train( |
| 227 | + { |
| 228 | + "device": device, |
| 229 | + "multi_strategy": "multi_output_tree", |
| 230 | + "learning_rate": 1.0, |
| 231 | + "base_score": intercept, |
| 232 | + "debug_synchronize": True, |
| 233 | + }, |
| 234 | + Xy, |
| 235 | + evals=[(Xy, "Train")], |
| 236 | + obj=LsObj0(), |
| 237 | + num_boost_round=n_rounds, |
| 238 | + evals_result=evals_result_2, |
| 239 | + ) |
| 240 | + np.testing.assert_allclose( |
| 241 | + evals_result_0["Train"]["rmse"], evals_result_2["Train"]["rmse"] |
| 242 | + ) |
| 243 | + assert_allclose(device, booster_0.inplace_predict(X), booster_2.inplace_predict(X)) |
0 commit comments