diff --git a/demo/c-api/basic/c-api-demo.c b/demo/c-api/basic/c-api-demo.c index 15a224e9ec61..e7dfa23b9058 100644 --- a/demo/c-api/basic/c-api-demo.c +++ b/demo/c-api/basic/c-api-demo.c @@ -53,15 +53,7 @@ int main() { // configure the training // available parameters are described here: // https://xgboost.readthedocs.io/en/latest/parameter.html - safe_xgboost(XGBoosterSetParam(booster, "tree_method", use_gpu ? "gpu_hist" : "hist")); - if (use_gpu) { - // set the GPU to use; - // this is not necessary, but provided here as an illustration - safe_xgboost(XGBoosterSetParam(booster, "gpu_id", "0")); - } else { - // avoid evaluating objective and metric on a GPU - safe_xgboost(XGBoosterSetParam(booster, "gpu_id", "-1")); - } + safe_xgboost(XGBoosterSetParam(booster, "device", use_gpu ? "cuda" : "cpu")); safe_xgboost(XGBoosterSetParam(booster, "objective", "binary:logistic")); safe_xgboost(XGBoosterSetParam(booster, "min_child_weight", "1")); diff --git a/demo/gpu_acceleration/README.md b/demo/gpu_acceleration/README.md deleted file mode 100644 index a49cd0c188f4..000000000000 --- a/demo/gpu_acceleration/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# GPU Acceleration Demo - -`cover_type.py` shows how to train a model on the [forest cover type](https://archive.ics.uci.edu/ml/datasets/covertype) dataset using GPU acceleration. The forest cover type dataset has 581,012 rows and 54 features, making it time consuming to process. We compare the run-time and accuracy of the GPU and CPU histogram algorithms. - -`shap.ipynb` demonstrates using GPU acceleration to compute SHAP values for feature importance. diff --git a/demo/gpu_acceleration/README.rst b/demo/gpu_acceleration/README.rst new file mode 100644 index 000000000000..77bd221d1807 --- /dev/null +++ b/demo/gpu_acceleration/README.rst @@ -0,0 +1,8 @@ +:orphan: + +GPU Acceleration Demo +===================== + +This is a collection of demonstration scripts to showcase the basic usage of GPU. Please +see :doc:`/gpu/index` for more info. There are other demonstrations for distributed GPU +training using dask or spark. diff --git a/demo/gpu_acceleration/cover_type.py b/demo/gpu_acceleration/cover_type.py index 1f2322d054ff..a582aaad3c60 100644 --- a/demo/gpu_acceleration/cover_type.py +++ b/demo/gpu_acceleration/cover_type.py @@ -1,41 +1,49 @@ +""" +Using xgboost on GPU devices +============================ + +Shows how to train a model on the `forest cover type +`_ dataset using GPU +acceleration. The forest cover type dataset has 581,012 rows and 54 features, making it +time consuming to process. We compare the run-time and accuracy of the GPU and CPU +histogram algorithms. + +In addition, The demo showcases using GPU with other GPU-related libraries including +cupy and cuml. These libraries are not strictly required. + +""" import time +import cupy as cp +from cuml.model_selection import train_test_split from sklearn.datasets import fetch_covtype -from sklearn.model_selection import train_test_split import xgboost as xgb # Fetch dataset using sklearn -cov = fetch_covtype() -X = cov.data -y = cov.target +X, y = fetch_covtype(return_X_y=True) +X = cp.array(X) +y = cp.array(y) +y -= y.min() # Create 0.75/0.25 train/test split -X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, train_size=0.75, - random_state=42) +X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=0.25, train_size=0.75, random_state=42 +) # Specify sufficient boosting iterations to reach a minimum num_round = 3000 # Leave most parameters as default -param = {'objective': 'multi:softmax', # Specify multiclass classification - 'num_class': 8, # Number of possible output classes - 'tree_method': 'gpu_hist' # Use GPU accelerated algorithm - } - -# Convert input data from numpy to XGBoost format -dtrain = xgb.DMatrix(X_train, label=y_train) -dtest = xgb.DMatrix(X_test, label=y_test) - -gpu_res = {} # Store accuracy result -tmp = time.time() +clf = xgb.XGBClassifier(device="cuda", n_estimators=num_round) # Train model -xgb.train(param, dtrain, num_round, evals=[(dtest, 'test')], evals_result=gpu_res) -print("GPU Training Time: %s seconds" % (str(time.time() - tmp))) +start = time.time() +clf.fit(X_train, y_train, eval_set=[(X_test, y_test)]) +gpu_res = clf.evals_result() +print("GPU Training Time: %s seconds" % (str(time.time() - start))) # Repeat for CPU algorithm -tmp = time.time() -param['tree_method'] = 'hist' -cpu_res = {} -xgb.train(param, dtrain, num_round, evals=[(dtest, 'test')], evals_result=cpu_res) -print("CPU Training Time: %s seconds" % (str(time.time() - tmp))) +clf = xgb.XGBClassifier(device="cpu", n_estimators=num_round) +start = time.time() +cpu_res = clf.evals_result() +print("CPU Training Time: %s seconds" % (str(time.time() - start))) diff --git a/demo/gpu_acceleration/shap.ipynb b/demo/gpu_acceleration/shap.ipynb deleted file mode 100644 index 7f1ee87d51a1..000000000000 --- a/demo/gpu_acceleration/shap.ipynb +++ /dev/null @@ -1,211 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - ".. _california_housing_dataset:\n", - "\n", - "California Housing dataset\n", - "--------------------------\n", - "\n", - "**Data Set Characteristics:**\n", - "\n", - " :Number of Instances: 20640\n", - "\n", - " :Number of Attributes: 8 numeric, predictive attributes and the target\n", - "\n", - " :Attribute Information:\n", - " - MedInc median income in block\n", - " - HouseAge median house age in block\n", - " - AveRooms average number of rooms\n", - " - AveBedrms average number of bedrooms\n", - " - Population block population\n", - " - AveOccup average house occupancy\n", - " - Latitude house block latitude\n", - " - Longitude house block longitude\n", - "\n", - " :Missing Attribute Values: None\n", - "\n", - "This dataset was obtained from the StatLib repository.\n", - "http://lib.stat.cmu.edu/datasets/\n", - "\n", - "The target variable is the median house value for California districts.\n", - "\n", - "This dataset was derived from the 1990 U.S. census, using one row per census\n", - "block group. A block group is the smallest geographical unit for which the U.S.\n", - "Census Bureau publishes sample data (a block group typically has a population\n", - "of 600 to 3,000 people).\n", - "\n", - "It can be downloaded/loaded using the\n", - ":func:`sklearn.datasets.fetch_california_housing` function.\n", - "\n", - ".. topic:: References\n", - "\n", - " - Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,\n", - " Statistics and Probability Letters, 33 (1997) 291-297\n", - "\n", - "Wall time: 28.9 s\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "import xgboost as xgb\n", - "from sklearn.datasets import fetch_california_housing\n", - "\n", - "# Fetch dataset using sklearn\n", - "data = fetch_california_housing()\n", - "print( data.DESCR)\n", - "X = data.data\n", - "y = data.target\n", - "\n", - "num_round = 500\n", - "\n", - "param = {\n", - " \"eta\": 0.05,\n", - " \"max_depth\": 10,\n", - " \"tree_method\": \"gpu_hist\",\n", - "}\n", - "\n", - "# GPU accelerated training\n", - "dtrain = xgb.DMatrix(X, label=y, feature_names=data.feature_names)\n", - "%time model = xgb.train(param, dtrain,num_round)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Wall time: 3.73 s\n" - ] - } - ], - "source": [ - "%%time\n", - "# Compute shap values using GPU with xgboost\n", - "# model.set_param({\"predictor\":\"cpu_predictor\"})\n", - "model.set_param({\"predictor\": \"gpu_predictor\"})\n", - "shap_values = model.predict(dtrain, pred_contribs=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Wall time: 49.3 s\n" - ] - } - ], - "source": [ - "%%time\n", - "# Compute shap interaction values using GPU\n", - "shap_interaction_values = model.predict(dtrain, pred_interactions=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Wall time: 3.69 s\n" - ] - }, - { - "data": { - "image/png": "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G6PdjKOyYMECXn755Wg3Q0REROLMq6++yvz58xs0bTzvKzUV7XM1jMInEREREREREYkZTz75JJ999lnMzk8aT+GTiIiIiIiIiIhEjCfaDRAREYll27ZtY968eRQXFzNw4ECmTp2Kx+OhtLSUV155ha1bt+L3++nRowdTp07F6/UC8MUXX/Dhhx9y6NAhUlNTOe200xg2bBgAn3/+OYsWLeLgwYN07dqVc845h8zMzMOe+5lnnqF///6MHj26+raHH36YSZMmMWjQIObNm8eaNWsoLy+nXbt2nHXWWeTk5Bw2n02bNvHyyy9zyy23VN82a9Yszj33XHr37o1t2yxatIgVK1ZQVlZG7969mTp1KikpKU29OEVERKQJzJo1ixNPPJGVK1eyf/9+hg4dyumnn86rr77K5s2b6datG9OmTav+Ll+3bh3vvfcexcXFdO7cmSlTppCVlQXAjh07mDt3Lvv27aNfv35YVvgJ1tavX8/8+fMpKCggKyuLqVOn0qlTpyO2saysjHnz5vHNN9+QkJDAyJEjmTBhApZlsWDBAvbv38+FF14IQEFBAbNmzeLOO+/kgw8+ID8/n61bt/LWW28xYsQIvv/973PXXXdx9tlns3TpUsrLyxkxYgSTJ08+6vmF0j5X5Cl8EhERqceqVau47LLLSExM5LnnnuOjjz7itNNOw7ZtRowYwbRp0/D7/bz22mu8+eab5Obm4vP5mDdvHtdccw0dOnSguLiY0tJSANauXcvChQuZPn067du35+OPP2bOnDlcffXVhz33cccdx/Lly6t3hPbs2UNhYSH9+vUDoGvXrpxyyikkJyezdOlSXnrpJW6++WY8nsZ9vS9btoy1a9dy1VVXkZqayrx583jjjTe4+OKLj3HpiYiISKSsWbOGyy+/HL/fz6OPPsrOnTs599xzycrK4tlnn2XZsmVMmjSJffv2MXv2bHJzc+nZsydLly7l+eef5/rrrwcgLy+PMWPGMHr0aNatW8fs2bMZP348YIKp1157jenTp5Odnc3KlSt5/vnnueGGG464vzFv3jzKysq46aabKC0t5emnnyYtLY0TTjih3sedfvrpbNmyhWHDhh027Zo1a/jpT3+Kz+fjqaeeokOHDsc0vwDtc0Weut2JiIjUY/To0WRkZJCSksLEiRNZtWoVAKmpqQwePJiEhASSkpKYOHEimzZt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jK2XpqTwSURERERERESa3N13382yZcv4xz/+Qa9evZr0cVOmTCE9PR2A8ePHc/zxx3PuuedW379t27Zja7w0KYVPIiIiIiIiItKkli9fzr333stll13GpZde2uSP69WrF++//z6TJ08mMTGRzZs3c9FFF5GZmQlAQkLCMb8GaTqWbdvRboOIiIiIiIiIxK/DgoUnn3ySq666iuTk5OpucyUlJdi2jdvtJjk5mW3btpGRkdEkjwNT7RQYH+rBBx/khhtuqKu9Vl13SGSo8klEREREREREIqKsrIxDhw5x6NAhAsUvVVVV1ddPP/10Bg4cyK9//etGPQ7g448/prKyEjAB1fXXXw9AYmIiF154YXO9RGkAhU8iIiIiIiIi0qSuvPJKbNsO+8vJyQHgoosuwrZtMjMz+fbbb1m3bh07duxo1OMAbrjhBjp06MCwYcPo3Lkzr732GgB//vOfyc7OjsKrlroofBIREYlBxcXFdnFxsfrGi4iIiNThjDPOoG3btqxfvx6ASZMm8frrr9fX3U6iRGM+iYiIxKBA8JSenq4xCURERCTWxVuwoP2rZqbKJxERERERERERiRiFTyIiIiIiIiIiEjEKn0REREREREREJGIUPomIiIiIiIiISMQofBIRERERERERkYhR+CQiIiIiIiIiIhGj8ElEREREREREjsWuaDegEeKprS2Gwqc4Y1nWWZZlrbMsa4NlWbfVcv+VlmXtsSzrC+fvJ9FopzSeZVn/tixrt2VZq+u437Is62/Oul9pWdYJzd1GOToNWLeTLMsqDNlu72zuNsrRsSyru2VZH1iWtcayrK8sy7qplmm07capBq5fbb9xyLKsZMuyPrEs60tn3d5dyzRJlmW94Gy7yyzL6tn8LZWj0cD1q33mOGZZltuyrM8ty3q9lvu07UZHZ8Bqij/LsjyWZX1RXFxMcXExNe67yrKsvc72/aVlWdccxXN0jvCykFp4ot0AaTjLstzAQ8BkYCvwqWVZc23b/rrGpC/Ytn1DszdQjtWTwN+Bp+q4/2ygn/N3EvCw819i35PUv24BFtq2PbV5miNNqBK41bbtzyzLSgdWWJb1bo3PZW278ash6xe0/cajcuA027YPWpaVAHxsWdY827aXhkxzNXDAtu2+lmXlAjOBS6LRWGm0hqxf0D5zPLsJWAN4a7lP2278C6zfEXXcr203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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# We can use the shap package\n", - "import shap\n", - "\n", - "\n", - "# shap will call the GPU accelerated version as long as the predictor parameter is set to \"gpu_predictor\"\n", - "model.set_param({\"predictor\": \"gpu_predictor\"})\n", - "explainer = shap.TreeExplainer(model)\n", - "%time shap_values = explainer.shap_values(X)\n", - "\n", - "# visualize the first prediction's explanation\n", - "shap.force_plot(\n", - " explainer.expected_value,\n", - " shap_values[0, :],\n", - " X[0, :],\n", - " feature_names=data.feature_names,\n", - " matplotlib=True\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Show a summary of feature importance\n", - "shap.summary_plot(shap_values, X, plot_type=\"bar\", feature_names=data.feature_names)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/gpu_acceleration/tree_shap.py b/demo/gpu_acceleration/tree_shap.py new file mode 100644 index 000000000000..d591307e0c1b --- /dev/null +++ b/demo/gpu_acceleration/tree_shap.py @@ -0,0 +1,55 @@ +""" +Use GPU to speedup SHAP value computation +========================================= + +Demonstrates using GPU acceleration to compute SHAP values for feature importance. + +""" +import shap +from sklearn.datasets import fetch_california_housing + +import xgboost as xgb + +# Fetch dataset using sklearn +data = fetch_california_housing() +print(data.DESCR) +X = data.data +y = data.target + +num_round = 500 + +param = { + "eta": 0.05, + "max_depth": 10, + "tree_method": "hist", + "device": "cuda", +} + +# GPU accelerated training +dtrain = xgb.DMatrix(X, label=y, feature_names=data.feature_names) +model = xgb.train(param, dtrain, num_round) + +# Compute shap values using GPU with xgboost +model.set_param({"device": "cuda"}) +shap_values = model.predict(dtrain, pred_contribs=True) + +# Compute shap interaction values using GPU +shap_interaction_values = model.predict(dtrain, pred_interactions=True) + + +# shap will call the GPU accelerated version as long as the device parameter is set to +# "cuda" +explainer = shap.TreeExplainer(model) +shap_values = explainer.shap_values(X) + +# visualize the first prediction's explanation +shap.force_plot( + explainer.expected_value, + shap_values[0, :], + X[0, :], + feature_names=data.feature_names, + matplotlib=True, +) + +# Show a summary of feature importance +shap.summary_plot(shap_values, X, plot_type="bar", feature_names=data.feature_names) diff --git a/demo/nvflare/horizontal/custom/trainer.py b/demo/nvflare/horizontal/custom/trainer.py index f65f800f0240..b1ec942117d8 100644 --- a/demo/nvflare/horizontal/custom/trainer.py +++ b/demo/nvflare/horizontal/custom/trainer.py @@ -70,8 +70,7 @@ def _do_training(self, fl_ctx: FLContext): param = {'max_depth': 2, 'eta': 1, 'objective': 'binary:logistic'} if self._use_gpus: self.log_info(fl_ctx, f'Training with GPU {rank}') - param['tree_method'] = 'gpu_hist' - param['gpu_id'] = rank + param['device'] = f"cuda:{rank}" # Specify validations set to watch performance watchlist = [(dtest, 'eval'), (dtrain, 'train')] diff --git a/demo/rmm_plugin/README.md b/demo/rmm_plugin/README.md deleted file mode 100644 index bf6e7f12d67f..000000000000 --- a/demo/rmm_plugin/README.md +++ /dev/null @@ -1,47 +0,0 @@ -Using XGBoost with RAPIDS Memory Manager (RMM) plugin (EXPERIMENTAL) -==================================================================== -[RAPIDS Memory Manager (RMM)](https://github.com/rapidsai/rmm) library provides a collection of -efficient memory allocators for NVIDIA GPUs. It is now possible to use XGBoost with memory -allocators provided by RMM, by enabling the RMM integration plugin. - -The demos in this directory highlights one RMM allocator in particular: **the pool sub-allocator**. -This allocator addresses the slow speed of `cudaMalloc()` by allocating a large chunk of memory -upfront. Subsequent allocations will draw from the pool of already allocated memory and thus avoid -the overhead of calling `cudaMalloc()` directly. See -[this GTC talk slides](https://on-demand.gputechconf.com/gtc/2015/presentation/S5530-Stephen-Jones.pdf) -for more details. - -Before running the demos, ensure that XGBoost is compiled with the RMM plugin enabled. To do this, -run CMake with option `-DPLUGIN_RMM=ON` (`-DUSE_CUDA=ON` also required): -``` -cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON -make -j4 -``` -CMake will attempt to locate the RMM library in your build environment. You may choose to build -RMM from the source, or install it using the Conda package manager. If CMake cannot find RMM, you -should specify the location of RMM with the CMake prefix: -``` -# If using Conda: -cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX -# If using RMM installed with a custom location -cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON -DCMAKE_PREFIX_PATH=/path/to/rmm -``` - -# Informing XGBoost about RMM pool - -When XGBoost is compiled with RMM, most of the large size allocation will go through RMM -allocators, but some small allocations in performance critical areas are using a different -caching allocator so that we can have better control over memory allocation behavior. -Users can override this behavior and force the use of rmm for all allocations by setting -the global configuration ``use_rmm``: - -``` python -with xgb.config_context(use_rmm=True): - clf = xgb.XGBClassifier(tree_method="gpu_hist") -``` - -Depending on the choice of memory pool size or type of allocator, this may have negative -performance impact. - -* [Using RMM with a single GPU](./rmm_singlegpu.py) -* [Using RMM with a local Dask cluster consisting of multiple GPUs](./rmm_mgpu_with_dask.py) diff --git a/demo/rmm_plugin/README.rst b/demo/rmm_plugin/README.rst new file mode 100644 index 000000000000..4742507d240d --- /dev/null +++ b/demo/rmm_plugin/README.rst @@ -0,0 +1,51 @@ +Using XGBoost with RAPIDS Memory Manager (RMM) plugin (EXPERIMENTAL) +==================================================================== + +`RAPIDS Memory Manager (RMM) `__ library provides a +collection of efficient memory allocators for NVIDIA GPUs. It is now possible to use +XGBoost with memory allocators provided by RMM, by enabling the RMM integration plugin. + +The demos in this directory highlights one RMM allocator in particular: **the pool +sub-allocator**. This allocator addresses the slow speed of ``cudaMalloc()`` by +allocating a large chunk of memory upfront. Subsequent allocations will draw from the pool +of already allocated memory and thus avoid the overhead of calling ``cudaMalloc()`` +directly. See `this GTC talk slides +`_ for +more details. + +Before running the demos, ensure that XGBoost is compiled with the RMM plugin enabled. To do this, +run CMake with option ``-DPLUGIN_RMM=ON`` (``-DUSE_CUDA=ON`` also required): + +.. code-block:: sh + + cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON + make -j$(nproc) + +CMake will attempt to locate the RMM library in your build environment. You may choose to build +RMM from the source, or install it using the Conda package manager. If CMake cannot find RMM, you +should specify the location of RMM with the CMake prefix: + +.. code-block:: sh + + # If using Conda: + cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX + # If using RMM installed with a custom location + cmake .. -DUSE_CUDA=ON -DUSE_NCCL=ON -DPLUGIN_RMM=ON -DCMAKE_PREFIX_PATH=/path/to/rmm + +******************************** +Informing XGBoost about RMM pool +******************************** + +When XGBoost is compiled with RMM, most of the large size allocation will go through RMM +allocators, but some small allocations in performance critical areas are using a different +caching allocator so that we can have better control over memory allocation behavior. +Users can override this behavior and force the use of rmm for all allocations by setting +the global configuration ``use_rmm``: + +.. code-block:: python + + with xgb.config_context(use_rmm=True): + clf = xgb.XGBClassifier(tree_method="hist", device="cuda") + +Depending on the choice of memory pool size or type of allocator, this may have negative +performance impact. diff --git a/demo/rmm_plugin/rmm_mgpu_with_dask.py b/demo/rmm_plugin/rmm_mgpu_with_dask.py index be2aa83a7e4a..2384b209eb62 100644 --- a/demo/rmm_plugin/rmm_mgpu_with_dask.py +++ b/demo/rmm_plugin/rmm_mgpu_with_dask.py @@ -1,3 +1,7 @@ +""" +Using rmm with Dask +=================== +""" import dask from dask.distributed import Client from dask_cuda import LocalCUDACluster @@ -11,25 +15,33 @@ def main(client): # xgb.set_config(use_rmm=True) X, y = make_classification(n_samples=10000, n_informative=5, n_classes=3) - # In pratice one should prefer loading the data with dask collections instead of using - # `from_array`. + # In pratice one should prefer loading the data with dask collections instead of + # using `from_array`. X = dask.array.from_array(X) y = dask.array.from_array(y) dtrain = xgb.dask.DaskDMatrix(client, X, label=y) - params = {'max_depth': 8, 'eta': 0.01, 'objective': 'multi:softprob', 'num_class': 3, - 'tree_method': 'gpu_hist', 'eval_metric': 'merror'} - output = xgb.dask.train(client, params, dtrain, num_boost_round=100, - evals=[(dtrain, 'train')]) - bst = output['booster'] - history = output['history'] - for i, e in enumerate(history['train']['merror']): - print(f'[{i}] train-merror: {e}') + params = { + "max_depth": 8, + "eta": 0.01, + "objective": "multi:softprob", + "num_class": 3, + "tree_method": "hist", + "eval_metric": "merror", + "device": "cuda", + } + output = xgb.dask.train( + client, params, dtrain, num_boost_round=100, evals=[(dtrain, "train")] + ) + bst = output["booster"] + history = output["history"] + for i, e in enumerate(history["train"]["merror"]): + print(f"[{i}] train-merror: {e}") -if __name__ == '__main__': - # To use RMM pool allocator with a GPU Dask cluster, just add rmm_pool_size option to - # LocalCUDACluster constructor. - with LocalCUDACluster(rmm_pool_size='2GB') as cluster: +if __name__ == "__main__": + # To use RMM pool allocator with a GPU Dask cluster, just add rmm_pool_size option + # to LocalCUDACluster constructor. + with LocalCUDACluster(rmm_pool_size="2GB") as cluster: with Client(cluster) as client: main(client) diff --git a/demo/rmm_plugin/rmm_singlegpu.py b/demo/rmm_plugin/rmm_singlegpu.py index 50d4a7ea3d14..b4dccd8058ac 100644 --- a/demo/rmm_plugin/rmm_singlegpu.py +++ b/demo/rmm_plugin/rmm_singlegpu.py @@ -1,3 +1,7 @@ +""" +Using rmm on a single node device +================================= +""" import rmm from sklearn.datasets import make_classification @@ -16,7 +20,8 @@ "eta": 0.01, "objective": "multi:softprob", "num_class": 3, - "tree_method": "gpu_hist", + "tree_method": "hist", + "device": "cuda", } # XGBoost will automatically use the RMM pool allocator bst = xgb.train(params, dtrain, num_boost_round=100, evals=[(dtrain, "train")]) diff --git a/doc/.gitignore b/doc/.gitignore index 61e15164ccaf..26725cafb936 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -6,3 +6,5 @@ doxygen parser.py *.pyc web-data +# generated by doxygen +tmp \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index f8926e73b243..68ec39181ba0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -19,7 +19,6 @@ import tarfile import urllib.request import warnings -from subprocess import call from urllib.error import HTTPError from sh.contrib import git @@ -148,12 +147,20 @@ def is_readthedocs_build(): sphinx_gallery_conf = { # path to your example scripts - "examples_dirs": ["../demo/guide-python", "../demo/dask", "../demo/aft_survival"], + "examples_dirs": [ + "../demo/guide-python", + "../demo/dask", + "../demo/aft_survival", + "../demo/gpu_acceleration", + "../demo/rmm_plugin" + ], # path to where to save gallery generated output "gallery_dirs": [ "python/examples", "python/dask-examples", "python/survival-examples", + "python/gpu-examples", + "python/rmm-examples", ], "matplotlib_animations": True, } diff --git a/doc/gpu/index.rst b/doc/gpu/index.rst index 4489c142733c..a11b753fe195 100644 --- a/doc/gpu/index.rst +++ b/doc/gpu/index.rst @@ -23,20 +23,19 @@ The GPU algorithms currently work with CLI, Python, R, and JVM packages. See :do :caption: Python example params = dict() - params["device"] = "cuda:0" + params["device"] = "cuda" params["tree_method"] = "hist" Xy = xgboost.QuantileDMatrix(X, y) xgboost.train(params, Xy) .. code-block:: python - :caption: With Scikit-Learn interface + :caption: With the Scikit-Learn interface XGBRegressor(tree_method="hist", device="cuda") - GPU-Accelerated SHAP values ============================= -XGBoost makes use of `GPUTreeShap `_ as a backend for computing shap values when the GPU predictor is selected. +XGBoost makes use of `GPUTreeShap `_ as a backend for computing shap values when the GPU is used. .. code-block:: python @@ -44,12 +43,12 @@ XGBoost makes use of `GPUTreeShap `_ as shap_values = booster.predict(dtrain, pred_contribs=True) shap_interaction_values = model.predict(dtrain, pred_interactions=True) -See examples `here `__. +See :ref:`sphx_glr_python_gpu-examples_tree_shap.py` for a worked example. Multi-node Multi-GPU Training ============================= -XGBoost supports fully distributed GPU training using `Dask `_, ``Spark`` and ``PySpark``. For getting started with Dask see our tutorial :doc:`/tutorials/dask` and worked examples `here `__, also Python documentation :ref:`dask_api` for complete reference. For usage with ``Spark`` using Scala see :doc:`/jvm/xgboost4j_spark_gpu_tutorial`. Lastly for distributed GPU training with ``PySpark``, see :doc:`/tutorials/spark_estimator`. +XGBoost supports fully distributed GPU training using `Dask `_, ``Spark`` and ``PySpark``. For getting started with Dask see our tutorial :doc:`/tutorials/dask` and worked examples :doc:`/python/dask-examples/index`, also Python documentation :ref:`dask_api` for complete reference. For usage with ``Spark`` using Scala see :doc:`/jvm/xgboost4j_spark_gpu_tutorial`. Lastly for distributed GPU training with ``PySpark``, see :doc:`/tutorials/spark_estimator`. Memory usage @@ -67,7 +66,8 @@ If you are getting out-of-memory errors on a big dataset, try the or :py:class:` CPU-GPU Interoperability ======================== -XGBoost models trained on GPUs can be used on CPU-only systems to generate predictions. For information about how to save and load an XGBoost model, see :doc:`/tutorials/saving_model`. + +The model can be used on any device regardless of the one used to train it. For instance, a model trained using GPU can still work on a CPU-only machine and vice versa. For more information about model serialization, see :doc:`/tutorials/saving_model`. Developer notes diff --git a/doc/install.rst b/doc/install.rst index 51f0d0d609be..bf90a913bd31 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -189,7 +189,7 @@ This will check out the latest stable version from the Maven Central. For the latest release version number, please check `release page `_. -To enable the GPU algorithm (``tree_method='gpu_hist'``), use artifacts ``xgboost4j-gpu_2.12`` and ``xgboost4j-spark-gpu_2.12`` instead (note the ``gpu`` suffix). +To enable the GPU algorithm (``device='cuda'``), use artifacts ``xgboost4j-gpu_2.12`` and ``xgboost4j-spark-gpu_2.12`` instead (note the ``gpu`` suffix). .. note:: Windows not supported in the JVM package @@ -325,4 +325,4 @@ The SNAPSHOT JARs are hosted by the XGBoost project. Every commit in the ``maste You can browse the file listing of the Maven repository at https://s3-us-west-2.amazonaws.com/xgboost-maven-repo/list.html. -To enable the GPU algorithm (``tree_method='gpu_hist'``), use artifacts ``xgboost4j-gpu_2.12`` and ``xgboost4j-spark-gpu_2.12`` instead (note the ``gpu`` suffix). +To enable the GPU algorithm (``device='cuda'``), use artifacts ``xgboost4j-gpu_2.12`` and ``xgboost4j-spark-gpu_2.12`` instead (note the ``gpu`` suffix). diff --git a/doc/parameter.rst b/doc/parameter.rst index 382cddd4f85c..2072c4b7551f 100644 --- a/doc/parameter.rst +++ b/doc/parameter.rst @@ -34,6 +34,20 @@ General Parameters - Which booster to use. Can be ``gbtree``, ``gblinear`` or ``dart``; ``gbtree`` and ``dart`` use tree based models while ``gblinear`` uses linear functions. +* ``device`` [default= ``cpu``] + + .. versionadded:: 2.0.0 + + - Device for XGBoost to run. User can set it to one of the following values: + + + ``cpu``: Use CPU. + + ``cuda``: Use a GPU (CUDA device). + + ``cuda:``: ```` is an integer that specifies the ordinal of the GPU (which GPU do you want to use if you have more than one devices). + + ``gpu``: Default GPU device selection from the list of available and supported devices. Only ``cuda`` devices are supported currently. + + ``gpu:``: Default GPU device selection from the list of available and supported devices. Only ``cuda`` devices are supported currently. + + For more information about GPU acceleration, see :doc:`/gpu/index`. + * ``verbosity`` [default=1] - Verbosity of printing messages. Valid values are 0 (silent), 1 (warning), 2 (info), 3 @@ -44,7 +58,7 @@ General Parameters * ``validate_parameters`` [default to ``false``, except for Python, R and CLI interface] - When set to True, XGBoost will perform validation of input parameters to check whether - a parameter is used or not. + a parameter is used or not. A warning is emitted when there's unknown parameter. * ``nthread`` [default to maximum number of threads available if not set] @@ -55,18 +69,6 @@ General Parameters - Flag to disable default metric. Set to 1 or ``true`` to disable. -* ``device`` [default= ``cpu``] - - .. versionadded:: 2.0.0 - - - Device for XGBoost to run. User can set it to one of the following values: - - + ``cpu``: Use CPU. - + ``cuda``: Use a GPU (CUDA device). - + ``cuda:``: ```` is an integer that specifies the ordinal of the GPU (which GPU do you want to use if you have more than one devices). - + ``gpu``: Default GPU device selection from the list of available and supported devices. Only ``cuda`` devices are supported currently. - + ``gpu:``: Default GPU device selection from the list of available and supported devices. Only ``cuda`` devices are supported currently. - Parameters for Tree Booster =========================== * ``eta`` [default=0.3, alias: ``learning_rate``] @@ -160,7 +162,7 @@ Parameters for Tree Booster - ``grow_colmaker``: non-distributed column-based construction of trees. - ``grow_histmaker``: distributed tree construction with row-based data splitting based on global proposal of histogram counting. - ``grow_quantile_histmaker``: Grow tree using quantized histogram. - - ``grow_gpu_hist``: Grow tree with GPU. Same as setting ``tree_method`` to ``hist`` and use ``device=cuda``. + - ``grow_gpu_hist``: Grow tree with GPU. Enabled when ``tree_method`` is set to ``hist`` along with ``device=cuda``. - ``sync``: synchronizes trees in all distributed nodes. - ``refresh``: refreshes tree's statistics and/or leaf values based on the current data. Note that no random subsampling of data rows is performed. - ``prune``: prunes the splits where loss < min_split_loss (or gamma) and nodes that have depth greater than ``max_depth``. diff --git a/doc/python/.gitignore b/doc/python/.gitignore index bb0916d77fbd..f3097dfc2987 100644 --- a/doc/python/.gitignore +++ b/doc/python/.gitignore @@ -1,3 +1,5 @@ examples dask-examples -survival-examples \ No newline at end of file +survival-examples +gpu-examples +rmm-examples \ No newline at end of file diff --git a/doc/python/index.rst b/doc/python/index.rst index fd34e0d43c13..079c91bfeaa2 100644 --- a/doc/python/index.rst +++ b/doc/python/index.rst @@ -17,3 +17,5 @@ Contents examples/index dask-examples/index survival-examples/index + gpu-examples/index + rmm-examples/index diff --git a/doc/treemethod.rst b/doc/treemethod.rst index 8ecddc066843..4dfb107a0c30 100644 --- a/doc/treemethod.rst +++ b/doc/treemethod.rst @@ -124,7 +124,7 @@ Following table summarizes some differences in supported features between 4 tree `T` means supported while `F` means unsupported. +------------------+-----------+---------------------+---------------------+------------------------+ -| | Exact | Approx | Hist | GPU Hist | +| | Exact | Approx | Hist | Hist (GPU) | +==================+===========+=====================+=====================+========================+ | grow_policy | Depthwise | depthwise/lossguide | depthwise/lossguide | depthwise/lossguide | +------------------+-----------+---------------------+---------------------+------------------------+ @@ -141,5 +141,5 @@ Following table summarizes some differences in supported features between 4 tree Features/parameters that are not mentioned here are universally supported for all 4 tree methods (for instance, column sampling and constraints). The `P` in external memory means -partially supported. Please note that both categorical data and external memory are +special handling. Please note that both categorical data and external memory are experimental. diff --git a/doc/tutorials/categorical.rst b/doc/tutorials/categorical.rst index bcea77ddf9ed..2a84080cfcc2 100644 --- a/doc/tutorials/categorical.rst +++ b/doc/tutorials/categorical.rst @@ -35,8 +35,8 @@ parameter ``enable_categorical``: .. code:: python - # Supported tree methods are `gpu_hist`, `approx`, and `hist`. - clf = xgb.XGBClassifier(tree_method="gpu_hist", enable_categorical=True) + # Supported tree methods are `approx` and `hist`. + clf = xgb.XGBClassifier(tree_method="hist", enable_categorical=True, device="cuda") # X is the dataframe we created in previous snippet clf.fit(X, y) # Must use JSON/UBJSON for serialization, otherwise the information is lost. diff --git a/doc/tutorials/external_memory.rst b/doc/tutorials/external_memory.rst index 832d13eddd08..811db6bd561a 100644 --- a/doc/tutorials/external_memory.rst +++ b/doc/tutorials/external_memory.rst @@ -81,7 +81,7 @@ constructor. it = Iterator(["file_0.svm", "file_1.svm", "file_2.svm"]) Xy = xgboost.DMatrix(it) - # Other tree methods including ``hist`` and ``gpu_hist`` also work, but has some caveats + # The ``approx`` also work, but with low performance. GPU implementation is different from CPU. # as noted in following sections. booster = xgboost.train({"tree_method": "hist"}, Xy) @@ -118,15 +118,15 @@ to reduce the overhead of file reading. GPU Version (GPU Hist tree method) ********************************** -External memory is supported by GPU algorithms (i.e. when ``tree_method`` is set to -``gpu_hist``). However, the algorithm used for GPU is different from the one used for +External memory is supported by GPU algorithms (i.e. when ``device`` is set to +``cuda``). However, the algorithm used for GPU is different from the one used for CPU. When training on a CPU, the tree method iterates through all batches from external memory for each step of the tree construction algorithm. On the other hand, the GPU algorithm uses a hybrid approach. It iterates through the data during the beginning of -each iteration and concatenates all batches into one in GPU memory. To reduce overall -memory usage, users can utilize subsampling. The GPU hist tree method supports -`gradient-based sampling`, enabling users to set a low sampling rate without compromising -accuracy. +each iteration and concatenates all batches into one in GPU memory for performance +reasons. To reduce overall memory usage, users can utilize subsampling. The GPU hist tree +method supports `gradient-based sampling`, enabling users to set a low sampling rate +without compromising accuracy. .. code-block:: python diff --git a/doc/tutorials/monotonic.rst b/doc/tutorials/monotonic.rst index 4ed7fa273c24..e663d1109689 100644 --- a/doc/tutorials/monotonic.rst +++ b/doc/tutorials/monotonic.rst @@ -83,13 +83,14 @@ Some other examples: - ``(0,-1)``: No constraint on the first predictor and a decreasing constraint on the second. -**Note for the 'hist' tree construction algorithm**. -If ``tree_method`` is set to either ``hist``, ``approx`` or ``gpu_hist``, enabling -monotonic constraints may produce unnecessarily shallow trees. This is because the -``hist`` method reduces the number of candidate splits to be considered at each -split. Monotonic constraints may wipe out all available split candidates, in which case no -split is made. To reduce the effect, you may want to increase the ``max_bin`` parameter to -consider more split candidates. +.. note:: + + **Note for the 'hist' tree construction algorithm**. If ``tree_method`` is set to + either ``hist`` or ``approx``, enabling monotonic constraints may produce unnecessarily + shallow trees. This is because the ``hist`` method reduces the number of candidate + splits to be considered at each split. Monotonic constraints may wipe out all available + split candidates, in which case no split is made. To reduce the effect, you may want to + increase the ``max_bin`` parameter to consider more split candidates. ******************* diff --git a/doc/tutorials/param_tuning.rst b/doc/tutorials/param_tuning.rst index 5ede195f3b2e..5ef8df003c21 100644 --- a/doc/tutorials/param_tuning.rst +++ b/doc/tutorials/param_tuning.rst @@ -38,10 +38,6 @@ There are in general two ways that you can control overfitting in XGBoost: - This includes ``subsample`` and ``colsample_bytree``. - You can also reduce stepsize ``eta``. Remember to increase ``num_round`` when you do so. -*************************** -Faster training performance -*************************** -There's a parameter called ``tree_method``, set it to ``hist`` or ``gpu_hist`` for faster computation. ************************* Handle Imbalanced Dataset diff --git a/doc/tutorials/rf.rst b/doc/tutorials/rf.rst index b68204e63f21..014c67060169 100644 --- a/doc/tutorials/rf.rst +++ b/doc/tutorials/rf.rst @@ -50,13 +50,14 @@ Here is a sample parameter dictionary for training a random forest on a GPU usin xgboost:: params = { - 'colsample_bynode': 0.8, - 'learning_rate': 1, - 'max_depth': 5, - 'num_parallel_tree': 100, - 'objective': 'binary:logistic', - 'subsample': 0.8, - 'tree_method': 'gpu_hist' + "colsample_bynode": 0.8, + "learning_rate": 1, + "max_depth": 5, + "num_parallel_tree": 100, + "objective": "binary:logistic", + "subsample": 0.8, + "tree_method": "hist", + "device": "cuda", } A random forest model can then be trained as follows:: diff --git a/doc/tutorials/saving_model.rst b/doc/tutorials/saving_model.rst index 5d9ba1d553dd..54c217249010 100644 --- a/doc/tutorials/saving_model.rst +++ b/doc/tutorials/saving_model.rst @@ -174,7 +174,7 @@ Will print out something similar to (not actual output as it's too long for demo "gbtree_train_param": { "num_parallel_tree": "1", "process_type": "default", - "tree_method": "gpu_hist", + "tree_method": "hist", "updater": "grow_gpu_hist", "updater_seq": "grow_gpu_hist" }, diff --git a/python-package/xgboost/sklearn.py b/python-package/xgboost/sklearn.py index 46a3ffa4aec1..9e155c20aeb9 100644 --- a/python-package/xgboost/sklearn.py +++ b/python-package/xgboost/sklearn.py @@ -278,9 +278,15 @@ def task(i: int) -> float: without bias. device : Optional[str] - Device ordinal. + + .. versionadded:: 2.0.0 + + Device ordinal, available options are `cpu`, `cuda`, and `gpu`. + validate_parameters : Optional[bool] + Give warnings for unknown parameter. + enable_categorical : bool .. versionadded:: 1.5.0 diff --git a/python-package/xgboost/spark/estimator.py b/python-package/xgboost/spark/estimator.py index f11a0eda856b..b73dfba6c770 100644 --- a/python-package/xgboost/spark/estimator.py +++ b/python-package/xgboost/spark/estimator.py @@ -144,8 +144,13 @@ class SparkXGBRegressor(_SparkXGBEstimator): .. deprecated:: 2.0.0 Use `device` instead. + device: + + .. versionadded:: 2.0.0 + Device for XGBoost workers, available options are `cpu`, `cuda`, and `gpu`. + force_repartition: Boolean value to specify if forcing the input dataset to be repartitioned before XGBoost training. @@ -319,8 +324,13 @@ class SparkXGBClassifier(_SparkXGBEstimator, HasProbabilityCol, HasRawPrediction .. deprecated:: 2.0.0 Use `device` instead. + device: + + .. versionadded:: 2.0.0 + Device for XGBoost workers, available options are `cpu`, `cuda`, and `gpu`. + force_repartition: Boolean value to specify if forcing the input dataset to be repartitioned before XGBoost training. @@ -497,8 +507,13 @@ class SparkXGBRanker(_SparkXGBEstimator): .. deprecated:: 2.0.0 Use `device` instead. + device: + + .. versionadded:: 2.0.0 + Device for XGBoost workers, available options are `cpu`, `cuda`, and `gpu`. + force_repartition: Boolean value to specify if forcing the input dataset to be repartitioned before XGBoost training. diff --git a/src/data/data.cc b/src/data/data.cc index d305749eefb5..7c76c6d25335 100644 --- a/src/data/data.cc +++ b/src/data/data.cc @@ -724,11 +724,15 @@ void MetaInfo::SynchronizeNumberOfColumns() { namespace { template void CheckDevice(std::int32_t device, HostDeviceVector const& v) { - CHECK(v.DeviceIdx() == Context::kCpuId || device == Context::kCpuId || v.DeviceIdx() == device) - << "Data is resided on a different device than `gpu_id`. " - << "Device that data is on: " << v.DeviceIdx() << ", " - << "`gpu_id` for XGBoost: " << device; + bool valid = + v.DeviceIdx() == Context::kCpuId || device == Context::kCpuId || v.DeviceIdx() == device; + if (!valid) { + LOG(FATAL) << "Invalid device ordinal. Data is associated with a different device ordinal than " + "the booster. The device ordinal of the data is: " + << v.DeviceIdx() << "; the device ordinal of the Booster is: " << device; + } } + template void CheckDevice(std::int32_t device, linalg::Tensor const& v) { CheckDevice(device, *v.Data()); diff --git a/src/gbm/gbtree.cc b/src/gbm/gbtree.cc index 0806c13a7c7f..8b456af66931 100644 --- a/src/gbm/gbtree.cc +++ b/src/gbm/gbtree.cc @@ -42,22 +42,22 @@ DMLC_REGISTRY_FILE_TAG(gbtree); namespace { /** @brief Map the `tree_method` parameter to the `updater` parameter. */ -std::string MapTreeMethodToUpdaters(Context const* ctx_, TreeMethod tree_method) { +std::string MapTreeMethodToUpdaters(Context const* ctx, TreeMethod tree_method) { // Choose updaters according to tree_method parameters + if (ctx->IsCUDA()) { + common::AssertGPUSupport(); + } switch (tree_method) { case TreeMethod::kAuto: // Use hist as default in 2.0 case TreeMethod::kHist: { - return ctx_->DispatchDevice([] { return "grow_quantile_histmaker"; }, - [] { - common::AssertGPUSupport(); - return "grow_gpu_hist"; - }); + return ctx->DispatchDevice([] { return "grow_quantile_histmaker"; }, + [] { return "grow_gpu_hist"; }); } case TreeMethod::kApprox: - CHECK(ctx_->IsCPU()) << "The `approx` tree method is not supported on GPU."; + CHECK(ctx->IsCPU()) << "The `approx` tree method is not supported on GPU."; return "grow_histmaker"; case TreeMethod::kExact: - CHECK(ctx_->IsCPU()) << "The `exact` tree method is not supported on GPU."; + CHECK(ctx->IsCPU()) << "The `exact` tree method is not supported on GPU."; return "grow_colmaker,prune"; case TreeMethod::kGPUHist: { common::AssertGPUSupport(); @@ -150,6 +150,7 @@ void GBTree::Configure(Args const& cfg) { CHECK(tparam_.tree_method == TreeMethod::kHist || tparam_.tree_method == TreeMethod::kAuto) << "Only the hist tree method is supported for building multi-target trees with vector " "leaf."; + CHECK(ctx_->IsCPU()) << "GPU is not yet supported for vector leaf."; } LOG(DEBUG) << "Using tree method: " << static_cast(tparam_.tree_method); diff --git a/tests/ci_build/lint_python.py b/tests/ci_build/lint_python.py index ca5d56e4c39b..5a5745ffba15 100644 --- a/tests/ci_build/lint_python.py +++ b/tests/ci_build/lint_python.py @@ -29,10 +29,12 @@ class LintersPaths: "tests/python-gpu/load_pickle.py", "tests/python-gpu/test_gpu_pickling.py", "tests/python-gpu/test_gpu_eval_metrics.py", + "tests/python-gpu/test_gpu_with_sklearn.py", "tests/test_distributed/test_with_spark/", "tests/test_distributed/test_gpu_with_spark/", # demo "demo/dask/", + "demo/rmm_plugin", "demo/json-model/json_parser.py", "demo/guide-python/cat_in_the_dat.py", "demo/guide-python/categorical.py", diff --git a/tests/python-gpu/test_from_cupy.py b/tests/python-gpu/test_from_cupy.py index 71667fa7bc86..b811ba0906ff 100644 --- a/tests/python-gpu/test_from_cupy.py +++ b/tests/python-gpu/test_from_cupy.py @@ -234,7 +234,7 @@ def test_specified_device(self): cp.cuda.runtime.setDevice(0) dtrain = dmatrix_from_cupy(np.float32, xgb.QuantileDMatrix, np.nan) with pytest.raises( - xgb.core.XGBoostError, match="Data is resided on a different device" + xgb.core.XGBoostError, match="Invalid device ordinal" ): xgb.train( {'tree_method': 'gpu_hist', 'gpu_id': 1}, dtrain, num_boost_round=10 diff --git a/tests/python-gpu/test_gpu_with_sklearn.py b/tests/python-gpu/test_gpu_with_sklearn.py index c9d3ab4ebff7..530d3e9dfbab 100644 --- a/tests/python-gpu/test_gpu_with_sklearn.py +++ b/tests/python-gpu/test_gpu_with_sklearn.py @@ -2,6 +2,7 @@ import os import sys import tempfile +from concurrent.futures import ThreadPoolExecutor import numpy as np import pytest @@ -23,18 +24,19 @@ def test_gpu_binary_classification(): from sklearn.model_selection import KFold digits = load_digits(n_class=2) - y = digits['target'] - X = digits['data'] + y = digits["target"] + X = digits["data"] kf = KFold(n_splits=2, shuffle=True, random_state=rng) for cls in (xgb.XGBClassifier, xgb.XGBRFClassifier): for train_index, test_index in kf.split(X, y): xgb_model = cls( - random_state=42, tree_method='gpu_hist', - n_estimators=4, gpu_id='0').fit(X[train_index], y[train_index]) + random_state=42, tree_method="gpu_hist", n_estimators=4, gpu_id="0" + ).fit(X[train_index], y[train_index]) preds = xgb_model.predict(X[test_index]) labels = y[test_index] - err = sum(1 for i in range(len(preds)) - if int(preds[i] > 0.5) != labels[i]) / float(len(preds)) + err = sum( + 1 for i in range(len(preds)) if int(preds[i] > 0.5) != labels[i] + ) / float(len(preds)) assert err < 0.1 @@ -133,7 +135,7 @@ def test_classififer(): X, y = load_digits(return_X_y=True) y *= 10 - clf = xgb.XGBClassifier(tree_method="gpu_hist", n_estimators=1) + clf = xgb.XGBClassifier(tree_method="hist", n_estimators=1, device="cuda") # numpy with pytest.raises(ValueError, match=r"Invalid classes.*"): @@ -161,3 +163,46 @@ def test_ranking_qid_df(): import cudf run_ranking_qid_df(cudf, "gpu_hist") + + +@pytest.mark.skipif(**tm.no_cupy()) +@pytest.mark.mgpu +def test_device_ordinal() -> None: + import cupy as cp + + n_devices = 2 + + def worker(ordinal: int, correct_ordinal: bool) -> None: + if correct_ordinal: + cp.cuda.runtime.setDevice(ordinal) + else: + cp.cuda.runtime.setDevice((ordinal + 1) % n_devices) + + X, y, w = tm.make_regression(4096, 12, use_cupy=True) + reg = xgb.XGBRegressor(device=f"cuda:{ordinal}", tree_method="hist") + + if correct_ordinal: + reg.fit( + X, y, sample_weight=w, eval_set=[(X, y)], sample_weight_eval_set=[w] + ) + assert tm.non_increasing(reg.evals_result()["validation_0"]["rmse"]) + return + + with pytest.raises(ValueError, match="Invalid device ordinal"): + reg.fit( + X, y, sample_weight=w, eval_set=[(X, y)], sample_weight_eval_set=[w] + ) + + with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + futures = [] + n_trials = 32 + for i in range(n_trials): + fut = executor.submit( + worker, ordinal=i % n_devices, correct_ordinal=i % 3 != 0 + ) + futures.append(fut) + + for fut in futures: + fut.result() + + cp.cuda.runtime.setDevice(0)