diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..5c71e130e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,18 @@
+* text=auto
+
+*.c text eol=lf
+*.h text eol=lf
+*.cc text eol=lf
+*.cuh text eol=lf
+*.cu text eol=lf
+*.py text eol=lf
+*.txt text eol=lf
+*.R text eol=lf
+*.scala text eol=lf
+*.java text eol=lf
+
+*.sh text eol=lf
+
+*.rst text eol=lf
+*.md text eol=lf
+*.csv text eol=lf
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ac50b744b..ab2a58fe9 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -156,40 +156,3 @@ jobs:
xgboost \
cpp \
include src python-package
-
- sphinx:
- runs-on: ubuntu-latest
- name: Build docs using Sphinx
- steps:
- - uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v2.5.0
- with:
- submodules: 'true'
- - uses: actions/setup-python@7f80679172b057fc5e90d70d197929d454754a5a # v4.3.0
- with:
- python-version: "3.8"
- architecture: 'x64'
- - name: Install system packages
- run: |
- sudo apt-get install -y --no-install-recommends graphviz doxygen ninja-build
- python -m pip install wheel setuptools awscli
- python -m pip install -r doc/requirements.txt
- - name: Extract branch name
- shell: bash
- run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
- id: extract_branch
- if: github.ref == 'refs/heads/master' || contains(github.ref, 'refs/heads/release_')
- - name: Run Sphinx
- run: |
- make -C doc html
- env:
- SPHINX_GIT_BRANCH: ${{ steps.extract_branch.outputs.branch }}
- READTHEDOCS: "True"
-
- - name: Publish
- run: |
- tar cvjf ${{ steps.extract_branch.outputs.branch }}.tar.bz2 doxygen/doc_doxygen/
- python -m awscli s3 cp ./${{ steps.extract_branch.outputs.branch }}.tar.bz2 s3://xgboost-docs/doxygen/ --acl public-read
- if: github.ref == 'refs/heads/master' || contains(github.ref, 'refs/heads/release_')
- env:
- AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_IAM_S3_UPLOADER }}
- AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_IAM_S3_UPLOADER }}
diff --git a/README.md b/README.md
index 219831114..2fae68ac5 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
eXtreme Gradient Boosting
+
eXtreme Gradient Boosting
===========
[](https://xgboost-ci.net/blue/organizations/jenkins/xgboost/activity)
[](https://github.com/dmlc/xgboost/actions)
diff --git a/demo/guide-python/multioutput_regression.py b/demo/guide-python/multioutput_regression.py
index 375377e4e..078ec6b7d 100644
--- a/demo/guide-python/multioutput_regression.py
+++ b/demo/guide-python/multioutput_regression.py
@@ -7,6 +7,12 @@ The demo is adopted from scikit-learn:
https://scikit-learn.org/stable/auto_examples/ensemble/plot_random_forest_regression_multioutput.html#sphx-glr-auto-examples-ensemble-plot-random-forest-regression-multioutput-py
See :doc:`/tutorials/multioutput` for more information.
+
+.. note::
+
+ The feature is experimental. For the `multi_output_tree` strategy, many features are
+ missing.
+
"""
import argparse
@@ -40,11 +46,18 @@ def gen_circle() -> Tuple[np.ndarray, np.ndarray]:
return X, y
-def rmse_model(plot_result: bool):
+def rmse_model(plot_result: bool, strategy: str):
"""Draw a circle with 2-dim coordinate as target variables."""
X, y = gen_circle()
# Train a regressor on it
- reg = xgb.XGBRegressor(tree_method="hist", n_estimators=64)
+ reg = xgb.XGBRegressor(
+ tree_method="hist",
+ n_estimators=128,
+ n_jobs=16,
+ max_depth=8,
+ multi_strategy=strategy,
+ subsample=0.6,
+ )
reg.fit(X, y, eval_set=[(X, y)])
y_predt = reg.predict(X)
@@ -52,7 +65,7 @@ def rmse_model(plot_result: bool):
plot_predt(y, y_predt, "multi")
-def custom_rmse_model(plot_result: bool) -> None:
+def custom_rmse_model(plot_result: bool, strategy: str) -> None:
"""Train using Python implementation of Squared Error."""
# As the experimental support status, custom objective doesn't support matrix as
@@ -88,9 +101,10 @@ def custom_rmse_model(plot_result: bool) -> None:
{
"tree_method": "hist",
"num_target": y.shape[1],
+ "multi_strategy": strategy,
},
dtrain=Xy,
- num_boost_round=100,
+ num_boost_round=128,
obj=squared_log,
evals=[(Xy, "Train")],
evals_result=results,
@@ -107,6 +121,16 @@ if __name__ == "__main__":
parser.add_argument("--plot", choices=[0, 1], type=int, default=1)
args = parser.parse_args()
# Train with builtin RMSE objective
- rmse_model(args.plot == 1)
+ # - One model per output.
+ rmse_model(args.plot == 1, "one_output_per_tree")
+
+ # - One model for all outputs, this is still working in progress, many features are
+ # missing.
+ rmse_model(args.plot == 1, "multi_output_tree")
+
# Train with custom objective.
- custom_rmse_model(args.plot == 1)
+ # - One model per output.
+ custom_rmse_model(args.plot == 1, "one_output_per_tree")
+ # - One model for all outputs, this is still working in progress, many features are
+ # missing.
+ custom_rmse_model(args.plot == 1, "multi_output_tree")
diff --git a/demo/guide-python/sklearn_examples.py b/demo/guide-python/sklearn_examples.py
index 5890987f9..cf33e959a 100644
--- a/demo/guide-python/sklearn_examples.py
+++ b/demo/guide-python/sklearn_examples.py
@@ -2,6 +2,9 @@
Collection of examples for using sklearn interface
==================================================
+For an introduction to XGBoost's scikit-learn estimator interface, see
+:doc:`/python/sklearn_estimator`.
+
Created on 1 Apr 2015
@author: Jamie Hall
diff --git a/doc/c++.rst b/doc/c++.rst
index 4a045fc42..ce30bbefa 100644
--- a/doc/c++.rst
+++ b/doc/c++.rst
@@ -8,5 +8,5 @@ As a result it's changing quite often and we don't maintain its stability. Alon
plugin system (see ``plugin/example`` in XGBoost's source tree), users can utilize some
existing c++ headers for gaining more access to the internal of XGBoost.
-* `C++ interface documentation (latest master branch) `_
+* `C++ interface documentation (latest master branch) <./dev/files.html>`_
* `C++ interface documentation (last stable release) `_
diff --git a/doc/c.rst b/doc/c.rst
index 02581b874..d63e779e1 100644
--- a/doc/c.rst
+++ b/doc/c.rst
@@ -10,7 +10,7 @@ simply look at function comments in ``include/xgboost/c_api.h``. The reference i
to sphinx with the help of breathe, which doesn't contain links to examples but might be
easier to read. For the original doxygen pages please visit:
-* `C API documentation (latest master branch) `_
+* `C API documentation (latest master branch) <./dev/c__api_8h.html>`_
* `C API documentation (last stable release) `_
***************
diff --git a/doc/conf.py b/doc/conf.py
index 7d585e420..73fe48acc 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -13,53 +13,106 @@
# serve to show the default.
import os
import re
+import shutil
import subprocess
import sys
+import tarfile
import urllib.request
+import warnings
from subprocess import call
from urllib.error import HTTPError
from sh.contrib import git
-git_branch = os.getenv('SPHINX_GIT_BRANCH', default=None)
+CURR_PATH = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
+PROJECT_ROOT = os.path.normpath(os.path.join(CURR_PATH, os.path.pardir))
+TMP_DIR = os.path.join(CURR_PATH, "tmp")
+DOX_DIR = "doxygen"
+
+
+def run_doxygen():
+ """Run the doxygen make command in the designated folder."""
+ curdir = os.path.normpath(os.path.abspath(os.path.curdir))
+ if os.path.exists(TMP_DIR):
+ print(f"Delete directory {TMP_DIR}")
+ shutil.rmtree(TMP_DIR)
+ else:
+ print(f"Create directory {TMP_DIR}")
+ os.mkdir(TMP_DIR)
+ try:
+ os.chdir(PROJECT_ROOT)
+ if not os.path.exists(DOX_DIR):
+ os.mkdir(DOX_DIR)
+ os.chdir(os.path.join(PROJECT_ROOT, DOX_DIR))
+ print(
+ "Build doxygen at {}".format(
+ os.path.join(PROJECT_ROOT, DOX_DIR, "doc_doxygen")
+ )
+ )
+ subprocess.check_call(["cmake", "..", "-DBUILD_C_DOC=ON", "-GNinja"])
+ subprocess.check_call(["ninja", "doc_doxygen"])
+
+ src = os.path.join(PROJECT_ROOT, DOX_DIR, "doc_doxygen", "html")
+ dest = os.path.join(TMP_DIR, "dev")
+ print(f"Copy directory {src} -> {dest}")
+ shutil.copytree(src, dest)
+ except OSError as e:
+ sys.stderr.write("doxygen execution failed: %s" % e)
+ finally:
+ os.chdir(curdir)
+
+
+def is_readthedocs_build():
+ if os.environ.get("READTHEDOCS", None) == "True":
+ return True
+ warnings.warn(
+ "Skipping Doxygen build... You won't have documentation for C/C++ functions. "
+ "Set environment variable READTHEDOCS=True if you want to build Doxygen. "
+ "(If you do opt in, make sure to install Doxygen, Graphviz, CMake, and C++ compiler "
+ "on your system.)"
+ )
+ return False
+
+
+if is_readthedocs_build():
+ run_doxygen()
+
+
+git_branch = os.getenv("SPHINX_GIT_BRANCH", default=None)
if not git_branch:
# If SPHINX_GIT_BRANCH environment variable is not given, run git
# to determine branch name
git_branch = [
- re.sub(r'origin/', '', x.lstrip(' ')) for x in str(
- git.branch('-r', '--contains', 'HEAD')).rstrip('\n').split('\n')
+ re.sub(r"origin/", "", x.lstrip(" "))
+ for x in str(git.branch("-r", "--contains", "HEAD")).rstrip("\n").split("\n")
]
- git_branch = [x for x in git_branch if 'HEAD' not in x]
+ git_branch = [x for x in git_branch if "HEAD" not in x]
else:
git_branch = [git_branch]
-print('git_branch = {}'.format(git_branch[0]))
+print("git_branch = {}".format(git_branch[0]))
try:
filename, _ = urllib.request.urlretrieve(
- 'https://s3-us-west-2.amazonaws.com/xgboost-docs/{}.tar.bz2'.format(
- git_branch[0]))
- call(
- 'if [ -d tmp ]; then rm -rf tmp; fi; mkdir -p tmp/jvm; cd tmp/jvm; tar xvf {}'
- .format(filename),
- shell=True)
+ f"https://s3-us-west-2.amazonaws.com/xgboost-docs/{git_branch[0]}.tar.bz2"
+ )
+ if not os.path.exists(TMP_DIR):
+ print(f"Create directory {TMP_DIR}")
+ os.mkdir(TMP_DIR)
+ jvm_doc_dir = os.path.join(TMP_DIR, "jvm")
+ if os.path.exists(jvm_doc_dir):
+ print(f"Delete directory {jvm_doc_dir}")
+ shutil.rmtree(jvm_doc_dir)
+ print(f"Create directory {jvm_doc_dir}")
+ os.mkdir(jvm_doc_dir)
+
+ with tarfile.open(filename, "r:bz2") as t:
+ t.extractall(jvm_doc_dir)
except HTTPError:
- print('JVM doc not found. Skipping...')
-try:
- filename, _ = urllib.request.urlretrieve(
- 'https://s3-us-west-2.amazonaws.com/xgboost-docs/doxygen/{}.tar.bz2'.
- format(git_branch[0]))
- call(
- 'mkdir -p tmp/dev; cd tmp/dev; tar xvf {}; mv doc_doxygen/html/* .; rm -rf doc_doxygen'
- .format(filename),
- shell=True)
-except HTTPError:
- print('C API doc not found. Skipping...')
+ print("JVM doc not found. Skipping...")
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-CURR_PATH = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
-PROJECT_ROOT = os.path.normpath(os.path.join(CURR_PATH, os.path.pardir))
libpath = os.path.join(PROJECT_ROOT, "python-package/")
sys.path.insert(0, libpath)
sys.path.insert(0, CURR_PATH)
@@ -82,50 +135,56 @@ release = xgboost.__version__
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones
extensions = [
- 'matplotlib.sphinxext.plot_directive',
- 'sphinx.ext.autodoc',
- 'sphinx.ext.napoleon',
- 'sphinx.ext.mathjax',
- 'sphinx.ext.intersphinx',
+ "matplotlib.sphinxext.plot_directive",
+ "sphinxcontrib.jquery",
+ "sphinx.ext.autodoc",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.mathjax",
+ "sphinx.ext.intersphinx",
"sphinx_gallery.gen_gallery",
- 'breathe',
- 'recommonmark'
+ "breathe",
+ "recommonmark",
]
sphinx_gallery_conf = {
# path to your example scripts
"examples_dirs": ["../demo/guide-python", "../demo/dask", "../demo/aft_survival"],
# path to where to save gallery generated output
- "gallery_dirs": ["python/examples", "python/dask-examples", "python/survival-examples"],
+ "gallery_dirs": [
+ "python/examples",
+ "python/dask-examples",
+ "python/survival-examples",
+ ],
"matplotlib_animations": True,
}
autodoc_typehints = "description"
-graphviz_output_format = 'png'
-plot_formats = [('svg', 300), ('png', 100), ('hires.png', 300)]
+graphviz_output_format = "png"
+plot_formats = [("svg", 300), ("png", 100), ("hires.png", 300)]
plot_html_show_source_link = False
plot_html_show_formats = False
# Breathe extension variables
-DOX_DIR = "doxygen"
-breathe_projects = {
- "xgboost": os.path.join(PROJECT_ROOT, DOX_DIR, "doc_doxygen/xml")
-}
+breathe_projects = {}
+if is_readthedocs_build():
+ breathe_projects = {
+ "xgboost": os.path.join(PROJECT_ROOT, DOX_DIR, "doc_doxygen/xml")
+ }
breathe_default_project = "xgboost"
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
-source_suffix = ['.rst', '.md']
+source_suffix = [".rst", ".md"]
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -134,7 +193,7 @@ master_doc = 'index'
# Usually you set "language" from the command line for these cases.
language = "en"
-autoclass_content = 'both'
+autoclass_content = "both"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -144,8 +203,10 @@ autoclass_content = 'both'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-html_extra_path = ['./tmp']
+exclude_patterns = ["_build"]
+html_extra_path = []
+if is_readthedocs_build():
+ html_extra_path = [TMP_DIR]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@@ -163,7 +224,7 @@ html_extra_path = ['./tmp']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -186,27 +247,24 @@ html_logo = "https://raw.githubusercontent.com/dmlc/dmlc.github.io/master/img/lo
html_css_files = ["css/custom.css"]
-html_sidebars = {
- '**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
-}
+html_sidebars = {"**": ["logo-text.html", "globaltoc.html", "searchbox.html"]}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
# Output file base name for HTML help builder.
-htmlhelp_basename = project + 'doc'
+htmlhelp_basename = project + "doc"
# -- Options for LaTeX output ---------------------------------------------
-latex_elements = {
-}
+latex_elements = {}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, '%s.tex' % project, project, author, 'manual'),
+ (master_doc, "%s.tex" % project, project, author, "manual"),
]
intersphinx_mapping = {
@@ -221,30 +279,5 @@ intersphinx_mapping = {
}
-# hook for doxygen
-def run_doxygen():
- """Run the doxygen make command in the designated folder."""
- curdir = os.path.normpath(os.path.abspath(os.path.curdir))
- try:
- os.chdir(PROJECT_ROOT)
- if not os.path.exists(DOX_DIR):
- os.mkdir(DOX_DIR)
- os.chdir(os.path.join(PROJECT_ROOT, DOX_DIR))
- subprocess.check_call(["cmake", "..", "-DBUILD_C_DOC=ON", "-GNinja"])
- subprocess.check_call(["ninja", "doc_doxygen"])
- except OSError as e:
- sys.stderr.write("doxygen execution failed: %s" % e)
- finally:
- os.chdir(curdir)
-
-
-def generate_doxygen_xml(app):
- """Run the doxygen make commands if we're on the ReadTheDocs server"""
- read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'
- if read_the_docs_build:
- run_doxygen()
-
-
def setup(app):
- app.add_css_file('custom.css')
- app.connect("builder-inited", generate_doxygen_xml)
+ app.add_css_file("custom.css")
diff --git a/doc/parameter.rst b/doc/parameter.rst
index 99d6f0585..1e703dacd 100644
--- a/doc/parameter.rst
+++ b/doc/parameter.rst
@@ -226,6 +226,18 @@ Parameters for Tree Booster
list is a group of indices of features that are allowed to interact with each other.
See :doc:`/tutorials/feature_interaction_constraint` for more information.
+* ``multi_strategy``, [default = ``one_output_per_tree``]
+
+ .. versionadded:: 2.0.0
+
+ .. note:: This parameter is working-in-progress.
+
+ - The strategy used for training multi-target models, including multi-target regression
+ and multi-class classification. See :doc:`/tutorials/multioutput` for more information.
+
+ - ``one_output_per_tree``: One model for each target.
+ - ``multi_output_tree``: Use multi-target trees.
+
.. _cat-param:
Parameters for Categorical Feature
@@ -408,8 +420,17 @@ Specify the learning task and the corresponding learning objective. The objectiv
- ``ndcg``: `Normalized Discounted Cumulative Gain `_
- ``map``: `Mean Average Precision `_
- - ``ndcg@n``, ``map@n``: 'n' can be assigned as an integer to cut off the top positions in the lists for evaluation.
- - ``ndcg-``, ``map-``, ``ndcg@n-``, ``map@n-``: In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions.
+
+ The `average precision` is defined as:
+
+ .. math::
+
+ AP@l = \frac{1}{min{(l, N)}}\sum^l_{k=1}P@k \cdot I_{(k)}
+
+ where :math:`I_{(k)}` is an indicator function that equals to :math:`1` when the document at :math:`k` is relevant and :math:`0` otherwise. The :math:`P@k` is the precision at :math:`k`, and :math:`N` is the total number of relevant documents. Lastly, the `mean average precision` is defined as the weighted average across all queries.
+
+ - ``ndcg@n``, ``map@n``: :math:`n` can be assigned as an integer to cut off the top positions in the lists for evaluation.
+ - ``ndcg-``, ``map-``, ``ndcg@n-``, ``map@n-``: In XGBoost, the NDCG and MAP evaluate the score of a list without any positive samples as :math:`1`. By appending "-" to the evaluation metric name, we can ask XGBoost to evaluate these scores as :math:`0` to be consistent under some conditions.
- ``poisson-nloglik``: negative log-likelihood for Poisson regression
- ``gamma-nloglik``: negative log-likelihood for gamma regression
- ``cox-nloglik``: negative partial log-likelihood for Cox proportional hazards regression
diff --git a/doc/python/index.rst b/doc/python/index.rst
index 60608700b..fd34e0d43 100644
--- a/doc/python/index.rst
+++ b/doc/python/index.rst
@@ -10,6 +10,7 @@ Contents
.. toctree::
python_intro
+ sklearn_estimator
python_api
callbacks
model
diff --git a/doc/python/python_api.rst b/doc/python/python_api.rst
index b27542a8b..0cbf63456 100644
--- a/doc/python/python_api.rst
+++ b/doc/python/python_api.rst
@@ -41,6 +41,7 @@ Learning API
Scikit-Learn API
----------------
+
.. automodule:: xgboost.sklearn
.. autoclass:: xgboost.XGBRegressor
:members:
diff --git a/doc/python/python_intro.rst b/doc/python/python_intro.rst
index c36db91ff..505556383 100644
--- a/doc/python/python_intro.rst
+++ b/doc/python/python_intro.rst
@@ -305,7 +305,8 @@ Scikit-Learn interface
----------------------
XGBoost provides an easy to use scikit-learn interface for some pre-defined models
-including regression, classification and ranking.
+including regression, classification and ranking. See :doc:`/python/sklearn_estimator`
+for more info.
.. code-block:: python
diff --git a/doc/python/sklearn_estimator.rst b/doc/python/sklearn_estimator.rst
new file mode 100644
index 000000000..9748dbebd
--- /dev/null
+++ b/doc/python/sklearn_estimator.rst
@@ -0,0 +1,162 @@
+##########################################
+Using the Scikit-Learn Estimator Interface
+##########################################
+
+**Contents**
+
+.. contents::
+ :backlinks: none
+ :local:
+
+********
+Overview
+********
+
+In addition to the native interface, XGBoost features a sklearn estimator interface that
+conforms to `sklearn estimator guideline
+`__. It
+supports regression, classification, and learning to rank. Survival training for the
+sklearn estimator interface is still working in progress.
+
+You can find some some quick start examples at
+:ref:`sphx_glr_python_examples_sklearn_examples.py`. The main advantage of using sklearn
+interface is that it works with most of the utilites provided by sklearn like
+:py:func:`sklearn.model_selection.cross_validate`. Also, many other libraries recognize
+the sklearn estimator interface thanks to its popularity.
+
+With the sklearn estimator interface, we can train a classification model with only a
+couple lines of Python code. Here's an example for training a classification model:
+
+.. code-block:: python
+
+ from sklearn.datasets import load_breast_cancer
+ from sklearn.model_selection import train_test_split
+
+ import xgboost as xgb
+
+ X, y = load_breast_cancer(return_X_y=True)
+ X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=94)
+
+ # Use "hist" for constructing the trees, with early stopping enabled.
+ clf = xgb.XGBClassifier(tree_method="hist", early_stopping_rounds=2)
+ # Fit the model, test sets are used for early stopping.
+ clf.fit(X_train, y_train, eval_set=[(X_test, y_test)])
+ # Save model into JSON format.
+ clf.save_model("clf.json")
+
+
+The ``tree_method`` parameter specifies the method to use for constructing the trees, and
+the early_stopping_rounds parameter enables early stopping. Early stopping can help
+prevent overfitting and save time during training.
+
+**************
+Early Stopping
+**************
+
+As demonstrated in the previous example, early stopping can be enabled by the parameter
+``early_stopping_rounds``. Alternatively, there's a callback function that can be used
+:py:class:`xgboost.callback.EarlyStopping` to specify more details about the behavior of
+early stopping, including whether XGBoost should return the best model instead of the full
+stack of trees:
+
+.. code-block:: python
+
+ early_stop = xgb.callback.EarlyStopping(
+ rounds=2, metric_name='logloss', data_name='Validation_0', save_best=True
+ )
+ clf = xgb.XGBClassifier(tree_method="hist", callbacks=[early_stop])
+ clf.fit(X_train, y_train, eval_set=[(X_test, y_test)])
+
+At present, XGBoost doesn't implement data spliting logic within the estimator and relies
+on the ``eval_set`` parameter of the :py:meth:`xgboost.XGBModel.fit` method. If you want
+to use early stopping to prevent overfitting, you'll need to manually split your data into
+training and testing sets using the :py:func:`sklearn.model_selection.train_test_split`
+function from the `sklearn` library. Some other machine learning algorithms, like those in
+`sklearn`, include early stopping as part of the estimator and may work with cross
+validation. However, using early stopping during cross validation may not be a perfect
+approach because it changes the model's number of trees for each validation fold, leading
+to different model. A better approach is to retrain the model after cross validation using
+the best hyperparameters along with early stopping. If you want to experiment with idea of
+using cross validation with early stopping, here is a snippet to begin with:
+
+.. code-block:: python
+
+ from sklearn.base import clone
+ from sklearn.datasets import load_breast_cancer
+ from sklearn.model_selection import StratifiedKFold, cross_validate
+
+ import xgboost as xgb
+
+ X, y = load_breast_cancer(return_X_y=True)
+
+
+ def fit_and_score(estimator, X_train, X_test, y_train, y_test):
+ """Fit the estimator on the train set and score it on both sets"""
+ estimator.fit(X_train, y_train, eval_set=[(X_test, y_test)])
+
+ train_score = estimator.score(X_train, y_train)
+ test_score = estimator.score(X_test, y_test)
+
+ return estimator, train_score, test_score
+
+
+ cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=94)
+
+ clf = xgb.XGBClassifier(tree_method="hist", early_stopping_rounds=3)
+
+ resutls = {}
+
+ for train, test in cv.split(X, y):
+ X_train = X[train]
+ X_test = X[test]
+ y_train = y[train]
+ y_test = y[test]
+ est, train_score, test_score = fit_and_score(
+ clone(clf), X_train, X_test, y_train, y_test
+ )
+ resutls[est] = (train_score, test_score)
+
+
+***********************************
+Obtaining the native booster object
+***********************************
+
+The sklearn estimator interface primarily facilitates training and doesn't implement all
+features available in XGBoost. For instance, in order to have cached predictions,
+:py:class:`xgboost.DMatrix` needs to be used with :py:meth:`xgboost.Booster.predict`. One
+can obtain the booster object from the sklearn interface using
+:py:meth:`xgboost.XGBModel.get_booster`:
+
+.. code-block:: python
+
+ booster = clf.get_booster()
+ print(booster.num_boosted_rounds())
+
+
+**********
+Prediction
+**********
+
+When early stopping is enabled, prediction functions including the
+:py:meth:`xgboost.XGBModel.predict`, :py:meth:`xgboost.XGBModel.score`, and
+:py:meth:`xgboost.XGBModel.apply` methods will use the best model automatically. Meaning
+the :py:attr:`xgboost.XGBModel.best_iteration` is used to specify the range of trees used
+in prediction.
+
+To have cached results for incremental prediction, please use the
+:py:meth:`xgboost.Booster.predict` method instead.
+
+
+**************************
+Number of parallel threads
+**************************
+
+When working with XGBoost and other sklearn tools, you can specify how many threads you
+want to use by using the ``n_jobs`` parameter. By default, XGBoost uses all the available
+threads on your computer, which can lead to some interesting consequences when combined
+with other sklearn functions like :py:func:`sklearn.model_selection.cross_validate`. If
+both XGBoost and sklearn are set to use all threads, your computer may start to slow down
+significantly due to something called "thread thrashing". To avoid this, you can simply
+set the ``n_jobs`` parameter for XGBoost to `None` (which uses all threads) and the
+``n_jobs`` parameter for sklearn to `1`. This way, both programs will be able to work
+together smoothly without causing any unnecessary computer strain.
diff --git a/doc/tutorials/c_api_tutorial.rst b/doc/tutorials/c_api_tutorial.rst
index ca121e1d2..090743a0f 100644
--- a/doc/tutorials/c_api_tutorial.rst
+++ b/doc/tutorials/c_api_tutorial.rst
@@ -134,7 +134,7 @@ c. Assertion technique: It works both in C/ C++. If expression evaluates to 0 (f
// do something with booster
//free the memory
- XGBoosterFree(booster)
+ XGBoosterFree(booster);
DMatrixHandle DMatrixHandle_param;
@@ -156,7 +156,7 @@ c. Assertion technique: It works both in C/ C++. If expression evaluates to 0 (f
.. code-block:: c
BoosterHandle booster;
- XGBoosterSetParam(booster, "paramter_name", "0.1");
+ XGBoosterSetParam(booster, "parameter_name", "0.1");
**************************************************************
diff --git a/doc/tutorials/dask.rst b/doc/tutorials/dask.rst
index c010aa0e2..c66c6131f 100644
--- a/doc/tutorials/dask.rst
+++ b/doc/tutorials/dask.rst
@@ -190,9 +190,9 @@ Scikit-Learn wrapper object:
booster = cls.get_booster()
-**********************
-Scikit-Learn interface
-**********************
+********************************
+Scikit-Learn Estimator Interface
+********************************
As mentioned previously, there's another interface that mimics the scikit-learn estimators
with higher level of of abstraction. The interface is easier to use compared to the
@@ -488,12 +488,13 @@ with dask and optuna.
Troubleshooting
***************
-.. versionadded:: 1.6.0
-In some environments XGBoost might fail to resolve the IP address of the scheduler, a
-symptom is user receiving ``OSError: [Errno 99] Cannot assign requested address`` error
-during training. A quick workaround is to specify the address explicitly. To do that
-dask config is used:
+- In some environments XGBoost might fail to resolve the IP address of the scheduler, a
+ symptom is user receiving ``OSError: [Errno 99] Cannot assign requested address`` error
+ during training. A quick workaround is to specify the address explicitly. To do that
+ dask config is used:
+
+ .. versionadded:: 1.6.0
.. code-block:: python
@@ -511,10 +512,20 @@ dask config is used:
reg = dxgb.DaskXGBRegressor()
-Please note that XGBoost requires a different port than dask. By default, on a unix-like
-system XGBoost uses the port 0 to find available ports, which may fail if a user is
-running in a restricted docker environment. In this case, please open additional ports in
-the container and specify it as in the above snippet.
+- Please note that XGBoost requires a different port than dask. By default, on a unix-like
+ system XGBoost uses the port 0 to find available ports, which may fail if a user is
+ running in a restricted docker environment. In this case, please open additional ports
+ in the container and specify it as in the above snippet.
+
+- If you encounter a NCCL system error while training with GPU enabled, which usually
+ includes the error message `NCCL failure: unhandled system error`, you can specify its
+ network configuration using one of the environment variables listed in the `NCCL
+ document `__ such as
+ the ``NCCL_SOCKET_IFNAME``. In addition, you can use ``NCCL_DEBUG`` to obtain debug
+ logs.
+
+- MIG (Multi-Instance GPU) is not yet supported by NCCL. You will receive an error message
+ that includes `Multiple processes within a communication group ...` upon initialization.
************
IPv6 Support
@@ -564,6 +575,69 @@ computations, one can explicitly wait for results of input data before construct
Also dask's `diagnostics dashboard `_ can be used to
monitor what operations are currently being performed.
+*******************
+Reproducible Result
+*******************
+
+In a single node mode, we can always expect the same training result between runs as along
+as the underlying platforms are the same. However, it's difficult to obtain reproducible
+result in a distributed environment, since the tasks might get different machine
+allocation or have different amount of available resources during different
+sessions. There are heuristics and guidelines on how to achieve it but no proven method
+for guaranteeing such deterministic behavior. The Dask interface in XGBoost tries to
+provide reproducible result with best effort. This section highlights some known criteria
+and try to share some insights into the issue.
+
+There are primarily two different tasks for XGBoost the carry out, training and
+inference. Inference is reproducible given the same software and hardware along with the
+same run-time configurations. The remaining of this section will focus on training.
+
+Many of the challenges come from the fact that we are using approximation algorithms, The
+sketching algorithm used to find histogram bins is an approximation to the exact quantile
+algorithm, the `AUC` metric in a distributed environment is an approximation to the exact
+`AUC` score, and floating-point number is an approximation to real number. Floating-point
+is an issue as its summation is not associative, meaning :math:`(a + b) + c` does not
+necessarily equal to :math:`a + (b + c)`, even though this property holds true for real
+number. As a result, whenever we change the order of a summation, the result can
+differ. This imposes the requirement that, in order to have reproducible output from
+XGBoost, the entire pipeline needs to be reproducible.
+
+- The software stack is the same for each runs. This goes without saying. XGBoost might
+ generate different outputs between different versions. This is expected as we might
+ change the default value of hyper-parameter, or the parallel strategy that generates
+ different floating-point result. We guarantee the correctness the algorithms, but there
+ are lots of wiggle room for the final output. The situation is similar for many
+ dependencies, for instance, the random number generator might differ from platform to
+ platform.
+
+- The hardware stack is the same for each runs. This includes the number of workers, and
+ the amount of available resources on each worker. XGBoost can generate different results
+ using different number of workers. This is caused by the approximation issue mentioned
+ previously.
+
+- Similar to the hardware constraint, the network topology is also a factor in final
+ output. If we change topology the workers might be ordered differently, leading to
+ different ordering of floating-point operations.
+
+- The random seed used in various place of the pipeline.
+
+- The partitioning of data needs to be reproducible. This is related to the available
+ resources on each worker. Dask might partition the data differently for each run
+ according to its own scheduling policy. For instance, if there are some additional tasks
+ in the cluster while you are running the second training session for XGBoost, some of
+ the workers might have constrained memory and Dask may not push the training data for
+ XGBoost to that worker. This change in data partitioning can lead to different output
+ models. If you are using a shared Dask cluster, then the result is likely to vary
+ between runs.
+
+- The operations performed on dataframes need to be reproducible. There are some
+ operations like `DataFrame.merge` not being deterministic on parallel hardwares like GPU
+ where the order of the index might differ from run to run.
+
+It's expected to have different results when training the model in a distributed
+environment than training the model using a single node due to aforementioned criteria.
+
+
************
Memory Usage
************
diff --git a/doc/tutorials/multioutput.rst b/doc/tutorials/multioutput.rst
index 280fb106f..983002aed 100644
--- a/doc/tutorials/multioutput.rst
+++ b/doc/tutorials/multioutput.rst
@@ -11,7 +11,11 @@ can be simultaneously classified as both sci-fi and comedy. For detailed explan
terminologies related to different multi-output models please refer to the
:doc:`scikit-learn user guide `.
-Internally, XGBoost builds one model for each target similar to sklearn meta estimators,
+**********************************
+Training with One-Model-Per-Target
+**********************************
+
+By default, XGBoost builds one model for each target similar to sklearn meta estimators,
with the added benefit of reusing data and other integrated features like SHAP. For a
worked example of regression, see
:ref:`sphx_glr_python_examples_multioutput_regression.py`. For multi-label classification,
@@ -36,3 +40,26 @@ dense matrix for labels.
The feature is still under development with limited support from objectives and metrics.
+
+*************************
+Training with Vector Leaf
+*************************
+
+.. versionadded:: 2.0
+
+.. note::
+
+ This is still working-in-progress, and many features are missing.
+
+XGBoost can optionally build multi-output trees with the size of leaf equals to the number
+of targets when the tree method `hist` is used. The behavior can be controlled by the
+``multi_strategy`` training parameter, which can take the value `one_output_per_tree` (the
+default) for building one model per-target or `multi_output_tree` for building
+multi-output trees.
+
+.. code-block:: python
+
+ clf = xgb.XGBClassifier(tree_method="hist", multi_strategy="multi_output_tree")
+
+See :ref:`sphx_glr_python_examples_multioutput_regression.py` for a worked example with
+regression.
diff --git a/include/xgboost/cache.h b/include/xgboost/cache.h
index 781f45b1c..32e1b21ac 100644
--- a/include/xgboost/cache.h
+++ b/include/xgboost/cache.h
@@ -116,6 +116,18 @@ class DMatrixCache {
* \param cache_size Maximum size of the cache.
*/
explicit DMatrixCache(std::size_t cache_size) : max_size_{cache_size} {}
+
+ DMatrixCache& operator=(DMatrixCache&& that) {
+ CHECK(lock_.try_lock());
+ lock_.unlock();
+ CHECK(that.lock_.try_lock());
+ that.lock_.unlock();
+ std::swap(this->container_, that.container_);
+ std::swap(this->queue_, that.queue_);
+ std::swap(this->max_size_, that.max_size_);
+ return *this;
+ }
+
/**
* \brief Cache a new DMatrix if it's not in the cache already.
*
@@ -149,6 +161,26 @@ class DMatrixCache {
}
return container_.at(key).value;
}
+ /**
+ * \brief Re-initialize the item in cache.
+ *
+ * Since the shared_ptr is used to hold the item, any reference that lives outside of
+ * the cache can no-longer be reached from the cache.
+ *
+ * We use reset instead of erase to avoid walking through the whole cache for renewing
+ * a single item. (the cache is FIFO, needs to maintain the order).
+ */
+ template
+ std::shared_ptr ResetItem(std::shared_ptr m, Args const&... args) {
+ std::lock_guard guard{lock_};
+ CheckConsistent();
+ auto key = Key{m.get(), std::this_thread::get_id()};
+ auto it = container_.find(key);
+ CHECK(it != container_.cend());
+ it->second = {m, std::make_shared(args...)};
+ CheckConsistent();
+ return it->second.value;
+ }
/**
* \brief Get a const reference to the underlying hash map. Clear expired caches before
* returning.
diff --git a/include/xgboost/data.h b/include/xgboost/data.h
index ec78c588d..57f8a0e36 100644
--- a/include/xgboost/data.h
+++ b/include/xgboost/data.h
@@ -171,6 +171,15 @@ class MetaInfo {
*/
void Extend(MetaInfo const& that, bool accumulate_rows, bool check_column);
+ /**
+ * @brief Synchronize the number of columns across all workers.
+ *
+ * Normally we just need to find the maximum number of columns across all workers, but
+ * in vertical federated learning, since each worker loads its own list of columns,
+ * we need to sum them.
+ */
+ void SynchronizeNumberOfColumns();
+
private:
void SetInfoFromHost(Context const& ctx, StringView key, Json arr);
void SetInfoFromCUDA(Context const& ctx, StringView key, Json arr);
@@ -325,6 +334,10 @@ class SparsePage {
* \brief Check wether the column index is sorted.
*/
bool IsIndicesSorted(int32_t n_threads) const;
+ /**
+ * \brief Reindex the column index with an offset.
+ */
+ void Reindex(uint64_t feature_offset, int32_t n_threads);
void SortRows(int32_t n_threads);
@@ -559,17 +572,18 @@ class DMatrix {
* \brief Creates a new DMatrix from an external data adapter.
*
* \tparam AdapterT Type of the adapter.
- * \param [in,out] adapter View onto an external data.
- * \param missing Values to count as missing.
- * \param nthread Number of threads for construction.
- * \param cache_prefix (Optional) The cache prefix for external memory.
- * \param page_size (Optional) Size of the page.
+ * \param [in,out] adapter View onto an external data.
+ * \param missing Values to count as missing.
+ * \param nthread Number of threads for construction.
+ * \param cache_prefix (Optional) The cache prefix for external memory.
+ * \param data_split_mode (Optional) Data split mode.
*
* \return a Created DMatrix.
*/
template
static DMatrix* Create(AdapterT* adapter, float missing, int nthread,
- const std::string& cache_prefix = "");
+ const std::string& cache_prefix = "",
+ DataSplitMode data_split_mode = DataSplitMode::kRow);
/**
* \brief Create a new Quantile based DMatrix used for histogram based algorithm.
diff --git a/include/xgboost/gbm.h b/include/xgboost/gbm.h
index d00f9ceaf..07758a524 100644
--- a/include/xgboost/gbm.h
+++ b/include/xgboost/gbm.h
@@ -9,7 +9,6 @@
#define XGBOOST_GBM_H_
#include
-#include
#include
#include
#include
diff --git a/include/xgboost/json_io.h b/include/xgboost/json_io.h
index e11545b04..3a73d170a 100644
--- a/include/xgboost/json_io.h
+++ b/include/xgboost/json_io.h
@@ -1,5 +1,5 @@
-/*!
- * Copyright (c) by Contributors 2019-2022
+/**
+ * Copyright 2019-2023, XGBoost Contributors
*/
#ifndef XGBOOST_JSON_IO_H_
#define XGBOOST_JSON_IO_H_
@@ -17,44 +17,26 @@
#include
namespace xgboost {
-namespace detail {
-// Whether char is signed is undefined, as a result we might or might not need
-// static_cast and std::to_string.
-template ::value>* = nullptr>
-std::string CharToStr(Char c) {
- static_assert(std::is_same::value);
- return std::string{c};
-}
-
-template ::value>* = nullptr>
-std::string CharToStr(Char c) {
- static_assert(std::is_same::value);
- return (c <= static_cast(127) ? std::string{c} : std::to_string(c));
-}
-} // namespace detail
-
-/*
+/**
* \brief A json reader, currently error checking and utf-8 is not fully supported.
*/
class JsonReader {
+ public:
+ using Char = std::int8_t;
+
protected:
- size_t constexpr static kMaxNumLength =
- std::numeric_limits::max_digits10 + 1;
+ size_t constexpr static kMaxNumLength = std::numeric_limits::max_digits10 + 1;
struct SourceLocation {
private:
- size_t pos_ { 0 }; // current position in raw_str_
+ std::size_t pos_{0}; // current position in raw_str_
public:
SourceLocation() = default;
- size_t Pos() const { return pos_; }
+ size_t Pos() const { return pos_; }
- void Forward() {
- pos_++;
- }
- void Forward(uint32_t n) {
- pos_ += n;
- }
+ void Forward() { pos_++; }
+ void Forward(uint32_t n) { pos_ += n; }
} cursor_;
StringView raw_str_;
@@ -62,7 +44,7 @@ class JsonReader {
protected:
void SkipSpaces();
- char GetNextChar() {
+ Char GetNextChar() {
if (XGBOOST_EXPECT((cursor_.Pos() == raw_str_.size()), false)) {
return -1;
}
@@ -71,24 +53,24 @@ class JsonReader {
return ch;
}
- char PeekNextChar() {
+ Char PeekNextChar() {
if (cursor_.Pos() == raw_str_.size()) {
return -1;
}
- char ch = raw_str_[cursor_.Pos()];
+ Char ch = raw_str_[cursor_.Pos()];
return ch;
}
/* \brief Skip spaces and consume next character. */
- char GetNextNonSpaceChar() {
+ Char GetNextNonSpaceChar() {
SkipSpaces();
return GetNextChar();
}
/* \brief Consume next character without first skipping empty space, throw when the next
* character is not the expected one.
*/
- char GetConsecutiveChar(char expected_char) {
- char result = GetNextChar();
+ Char GetConsecutiveChar(char expected_char) {
+ Char result = GetNextChar();
if (XGBOOST_EXPECT(result != expected_char, false)) { Expect(expected_char, result); }
return result;
}
@@ -96,7 +78,7 @@ class JsonReader {
void Error(std::string msg) const;
// Report expected character
- void Expect(char c, char got) {
+ void Expect(Char c, Char got) {
std::string msg = "Expecting: \"";
msg += c;
msg += "\", got: \"";
@@ -105,7 +87,7 @@ class JsonReader {
} else if (got == 0) {
msg += "\\0\"";
} else {
- msg += detail::CharToStr(got) + " \"";
+ msg += std::to_string(got) + " \"";
}
Error(msg);
}
diff --git a/include/xgboost/learner.h b/include/xgboost/learner.h
index 1d4e35a94..08e1ded09 100644
--- a/include/xgboost/learner.h
+++ b/include/xgboost/learner.h
@@ -286,8 +286,8 @@ struct LearnerModelParamLegacy;
* \brief Strategy for building multi-target models.
*/
enum class MultiStrategy : std::int32_t {
- kComposite = 0,
- kMonolithic = 1,
+ kOneOutputPerTree = 0,
+ kMultiOutputTree = 1,
};
/**
@@ -317,7 +317,7 @@ struct LearnerModelParam {
/**
* \brief Strategy for building multi-target models.
*/
- MultiStrategy multi_strategy{MultiStrategy::kComposite};
+ MultiStrategy multi_strategy{MultiStrategy::kOneOutputPerTree};
LearnerModelParam() = default;
// As the old `LearnerModelParamLegacy` is still used by binary IO, we keep
@@ -338,7 +338,7 @@ struct LearnerModelParam {
void Copy(LearnerModelParam const& that);
[[nodiscard]] bool IsVectorLeaf() const noexcept {
- return multi_strategy == MultiStrategy::kMonolithic;
+ return multi_strategy == MultiStrategy::kMultiOutputTree;
}
[[nodiscard]] bst_target_t OutputLength() const noexcept { return this->num_output_group; }
[[nodiscard]] bst_target_t LeafLength() const noexcept {
diff --git a/include/xgboost/linalg.h b/include/xgboost/linalg.h
index 91aeb189c..65e9de6ba 100644
--- a/include/xgboost/linalg.h
+++ b/include/xgboost/linalg.h
@@ -30,11 +30,11 @@
// decouple it from xgboost.
#ifndef LINALG_HD
-#if defined(__CUDA__) || defined(__NVCC__) || defined(__HIP_PLATFORM_AMD__)
+#if defined(__CUDA__) || defined(__NVCC__)
#define LINALG_HD __host__ __device__
#else
#define LINALG_HD
-#endif // defined (__CUDA__) || defined(__NVCC__) || defined(__HIP_PLATFORM_AMD__)
+#endif // defined (__CUDA__) || defined(__NVCC__)
#endif // LINALG_HD
namespace xgboost::linalg {
@@ -118,9 +118,9 @@ using IndexToTag = std::conditional_t>::value,
template
LINALG_HD constexpr auto UnrollLoop(Fn fn) {
-#if defined(__CUDA_ARCH__) || defined(__HIP_PLATFORM_AMD__)
+#if defined __CUDA_ARCH__
#pragma unroll n
-#endif // defined __CUDA_ARCH__ || defined(__HIP_PLATFORM_AMD__)
+#endif // defined __CUDA_ARCH__
for (int32_t i = 0; i < n; ++i) {
fn(i);
}
@@ -136,7 +136,7 @@ int32_t NativePopc(T v) {
inline LINALG_HD int Popc(uint32_t v) {
#if defined(__CUDA_ARCH__)
return __popc(v);
-#elif defined(__GNUC__) || defined(__clang__) || defined(__HIP_PLATFORM_AMD__)
+#elif defined(__GNUC__) || defined(__clang__)
return __builtin_popcount(v);
#elif defined(_MSC_VER)
return __popcnt(v);
@@ -148,7 +148,7 @@ inline LINALG_HD int Popc(uint32_t v) {
inline LINALG_HD int Popc(uint64_t v) {
#if defined(__CUDA_ARCH__)
return __popcll(v);
-#elif defined(__GNUC__) || defined(__clang__) || defined(__HIP_PLATFORM_AMD__)
+#elif defined(__GNUC__) || defined(__clang__)
return __builtin_popcountll(v);
#elif defined(_MSC_VER) && _defined(_M_X64)
return __popcnt64(v);
@@ -530,17 +530,17 @@ class TensorView {
/**
* \brief Number of items in the tensor.
*/
- LINALG_HD std::size_t Size() const { return size_; }
+ [[nodiscard]] LINALG_HD std::size_t Size() const { return size_; }
/**
* \brief Whether this is a contiguous array, both C and F contiguous returns true.
*/
- LINALG_HD bool Contiguous() const {
+ [[nodiscard]] LINALG_HD bool Contiguous() const {
return data_.size() == this->Size() || this->CContiguous() || this->FContiguous();
}
/**
* \brief Whether it's a c-contiguous array.
*/
- LINALG_HD bool CContiguous() const {
+ [[nodiscard]] LINALG_HD bool CContiguous() const {
StrideT stride;
static_assert(std::is_same::value);
// It's contiguous if the stride can be calculated from shape.
@@ -550,7 +550,7 @@ class TensorView {
/**
* \brief Whether it's a f-contiguous array.
*/
- LINALG_HD bool FContiguous() const {
+ [[nodiscard]] LINALG_HD bool FContiguous() const {
StrideT stride;
static_assert(std::is_same::value);
// It's contiguous if the stride can be calculated from shape.
diff --git a/include/xgboost/tree_model.h b/include/xgboost/tree_model.h
index dc24e882d..61dd94302 100644
--- a/include/xgboost/tree_model.h
+++ b/include/xgboost/tree_model.h
@@ -29,11 +29,6 @@
namespace xgboost {
class Json;
-#if defined(XGBOOST_USE_HIP)
-#define XGBOOST_NODISCARD
-#else
-#define XGBOOST_NODISCARD [[nodiscard]]
-#endif
// FIXME(trivialfis): Once binary IO is gone, make this parameter internal as it should
// not be configured by users.
/*! \brief meta parameters of the tree */
@@ -64,7 +59,7 @@ struct TreeParam : public dmlc::Parameter {
// Swap byte order for all fields. Useful for transporting models between machines with different
// endianness (big endian vs little endian)
- XGBOOST_NODISCARD TreeParam ByteSwap() const {
+ [[nodiscard]] TreeParam ByteSwap() const {
TreeParam x = *this;
dmlc::ByteSwap(&x.deprecated_num_roots, sizeof(x.deprecated_num_roots), 1);
dmlc::ByteSwap(&x.num_nodes, sizeof(x.num_nodes), 1);
@@ -117,7 +112,7 @@ struct RTreeNodeStat {
}
// Swap byte order for all fields. Useful for transporting models between machines with different
// endianness (big endian vs little endian)
- XGBOOST_NODISCARD RTreeNodeStat ByteSwap() const {
+ [[nodiscard]] RTreeNodeStat ByteSwap() const {
RTreeNodeStat x = *this;
dmlc::ByteSwap(&x.loss_chg, sizeof(x.loss_chg), 1);
dmlc::ByteSwap(&x.sum_hess, sizeof(x.sum_hess), 1);
@@ -183,51 +178,33 @@ class RegTree : public Model {
}
/*! \brief index of left child */
- XGBOOST_DEVICE XGBOOST_NODISCARD int LeftChild() const {
- return this->cleft_;
- }
+ [[nodiscard]] XGBOOST_DEVICE int LeftChild() const { return this->cleft_; }
/*! \brief index of right child */
- XGBOOST_DEVICE XGBOOST_NODISCARD int RightChild() const {
- return this->cright_;
- }
+ [[nodiscard]] XGBOOST_DEVICE int RightChild() const { return this->cright_; }
/*! \brief index of default child when feature is missing */
- XGBOOST_DEVICE XGBOOST_NODISCARD int DefaultChild() const {
+ [[nodiscard]] XGBOOST_DEVICE int DefaultChild() const {
return this->DefaultLeft() ? this->LeftChild() : this->RightChild();
}
/*! \brief feature index of split condition */
- XGBOOST_DEVICE XGBOOST_NODISCARD unsigned SplitIndex() const {
+ [[nodiscard]] XGBOOST_DEVICE unsigned SplitIndex() const {
return sindex_ & ((1U << 31) - 1U);
}
/*! \brief when feature is unknown, whether goes to left child */
- XGBOOST_DEVICE XGBOOST_NODISCARD bool DefaultLeft() const {
- return (sindex_ >> 31) != 0;
- }
+ [[nodiscard]] XGBOOST_DEVICE bool DefaultLeft() const { return (sindex_ >> 31) != 0; }
/*! \brief whether current node is leaf node */
- XGBOOST_DEVICE XGBOOST_NODISCARD bool IsLeaf() const {
- return cleft_ == kInvalidNodeId;
- }
+ [[nodiscard]] XGBOOST_DEVICE bool IsLeaf() const { return cleft_ == kInvalidNodeId; }
/*! \return get leaf value of leaf node */
- XGBOOST_DEVICE XGBOOST_NODISCARD float LeafValue() const {
- return (this->info_).leaf_value;
- }
+ [[nodiscard]] XGBOOST_DEVICE float LeafValue() const { return (this->info_).leaf_value; }
/*! \return get split condition of the node */
- XGBOOST_DEVICE XGBOOST_NODISCARD SplitCondT SplitCond() const {
- return (this->info_).split_cond;
- }
+ [[nodiscard]] XGBOOST_DEVICE SplitCondT SplitCond() const { return (this->info_).split_cond; }
/*! \brief get parent of the node */
- XGBOOST_DEVICE XGBOOST_NODISCARD int Parent() const {
- return parent_ & ((1U << 31) - 1);
- }
+ [[nodiscard]] XGBOOST_DEVICE int Parent() const { return parent_ & ((1U << 31) - 1); }
/*! \brief whether current node is left child */
- XGBOOST_DEVICE XGBOOST_NODISCARD bool IsLeftChild() const {
- return (parent_ & (1U << 31)) != 0;
- }
+ [[nodiscard]] XGBOOST_DEVICE bool IsLeftChild() const { return (parent_ & (1U << 31)) != 0; }
/*! \brief whether this node is deleted */
- XGBOOST_DEVICE XGBOOST_NODISCARD bool IsDeleted() const {
- return sindex_ == kDeletedNodeMarker;
- }
+ [[nodiscard]] XGBOOST_DEVICE bool IsDeleted() const { return sindex_ == kDeletedNodeMarker; }
/*! \brief whether current node is root */
- XGBOOST_DEVICE XGBOOST_NODISCARD bool IsRoot() const { return parent_ == kInvalidNodeId; }
+ [[nodiscard]] XGBOOST_DEVICE bool IsRoot() const { return parent_ == kInvalidNodeId; }
/*!
* \brief set the left child
* \param nid node id to right child
@@ -284,7 +261,7 @@ class RegTree : public Model {
info_.leaf_value == b.info_.leaf_value;
}
- XGBOOST_NODISCARD Node ByteSwap() const {
+ [[nodiscard]] Node ByteSwap() const {
Node x = *this;
dmlc::ByteSwap(&x.parent_, sizeof(x.parent_), 1);
dmlc::ByteSwap(&x.cleft_, sizeof(x.cleft_), 1);
@@ -342,15 +319,13 @@ class RegTree : public Model {
this->ChangeToLeaf(rid, value);
}
- /*! \brief model parameter */
- TreeParam param;
RegTree() {
- param.Init(Args{});
- nodes_.resize(param.num_nodes);
- stats_.resize(param.num_nodes);
- split_types_.resize(param.num_nodes, FeatureType::kNumerical);
- split_categories_segments_.resize(param.num_nodes);
- for (int i = 0; i < param.num_nodes; i++) {
+ param_.Init(Args{});
+ nodes_.resize(param_.num_nodes);
+ stats_.resize(param_.num_nodes);
+ split_types_.resize(param_.num_nodes, FeatureType::kNumerical);
+ split_categories_segments_.resize(param_.num_nodes);
+ for (int i = 0; i < param_.num_nodes; i++) {
nodes_[i].SetLeaf(0.0f);
nodes_[i].SetParent(kInvalidNodeId);
}
@@ -359,10 +334,10 @@ class RegTree : public Model {
* \brief Constructor that initializes the tree model with shape.
*/
explicit RegTree(bst_target_t n_targets, bst_feature_t n_features) : RegTree{} {
- param.num_feature = n_features;
- param.size_leaf_vector = n_targets;
+ param_.num_feature = n_features;
+ param_.size_leaf_vector = n_targets;
if (n_targets > 1) {
- this->p_mt_tree_.reset(new MultiTargetTree{¶m});
+ this->p_mt_tree_.reset(new MultiTargetTree{¶m_});
}
}
@@ -376,17 +351,17 @@ class RegTree : public Model {
}
/*! \brief get const reference to nodes */
- XGBOOST_NODISCARD const std::vector& GetNodes() const { return nodes_; }
+ [[nodiscard]] const std::vector& GetNodes() const { return nodes_; }
/*! \brief get const reference to stats */
- XGBOOST_NODISCARD const std::vector& GetStats() const { return stats_; }
+ [[nodiscard]] const std::vector& GetStats() const { return stats_; }
/*! \brief get node statistics given nid */
RTreeNodeStat& Stat(int nid) {
return stats_[nid];
}
/*! \brief get node statistics given nid */
- XGBOOST_NODISCARD const RTreeNodeStat& Stat(int nid) const {
+ [[nodiscard]] const RTreeNodeStat& Stat(int nid) const {
return stats_[nid];
}
@@ -406,7 +381,7 @@ class RegTree : public Model {
bool operator==(const RegTree& b) const {
return nodes_ == b.nodes_ && stats_ == b.stats_ &&
- deleted_nodes_ == b.deleted_nodes_ && param == b.param;
+ deleted_nodes_ == b.deleted_nodes_ && param_ == b.param_;
}
/* \brief Iterate through all nodes in this tree.
*
@@ -439,7 +414,7 @@ class RegTree : public Model {
*
* \param b The other tree.
*/
- XGBOOST_NODISCARD bool Equal(const RegTree& b) const;
+ [[nodiscard]] bool Equal(const RegTree& b) const;
/**
* \brief Expands a leaf node into two additional leaf nodes.
@@ -464,7 +439,9 @@ class RegTree : public Model {
bst_float loss_change, float sum_hess, float left_sum,
float right_sum,
bst_node_t leaf_right_child = kInvalidNodeId);
-
+ /**
+ * \brief Expands a leaf node into two additional leaf nodes for a multi-target tree.
+ */
void ExpandNode(bst_node_t nidx, bst_feature_t split_index, float split_cond, bool default_left,
linalg::VectorView base_weight,
linalg::VectorView left_weight,
@@ -490,25 +467,54 @@ class RegTree : public Model {
bst_float base_weight, bst_float left_leaf_weight,
bst_float right_leaf_weight, bst_float loss_change, float sum_hess,
float left_sum, float right_sum);
-
- XGBOOST_NODISCARD bool HasCategoricalSplit() const {
- return !split_categories_.empty();
- }
+ /**
+ * \brief Whether this tree has categorical split.
+ */
+ [[nodiscard]] bool HasCategoricalSplit() const { return !split_categories_.empty(); }
/**
* \brief Whether this is a multi-target tree.
*/
- XGBOOST_NODISCARD bool IsMultiTarget() const { return static_cast(p_mt_tree_); }
- XGBOOST_NODISCARD bst_target_t NumTargets() const { return param.size_leaf_vector; }
- XGBOOST_NODISCARD auto GetMultiTargetTree() const {
+ [[nodiscard]] bool IsMultiTarget() const { return static_cast(p_mt_tree_); }
+ /**
+ * \brief The size of leaf weight.
+ */
+ [[nodiscard]] bst_target_t NumTargets() const { return param_.size_leaf_vector; }
+ /**
+ * \brief Get the underlying implementaiton of multi-target tree.
+ */
+ [[nodiscard]] auto GetMultiTargetTree() const {
CHECK(IsMultiTarget());
return p_mt_tree_.get();
}
+ /**
+ * \brief Get the number of features.
+ */
+ [[nodiscard]] bst_feature_t NumFeatures() const noexcept { return param_.num_feature; }
+ /**
+ * \brief Get the total number of nodes including deleted ones in this tree.
+ */
+ [[nodiscard]] bst_node_t NumNodes() const noexcept { return param_.num_nodes; }
+ /**
+ * \brief Get the total number of valid nodes in this tree.
+ */
+ [[nodiscard]] bst_node_t NumValidNodes() const noexcept {
+ return param_.num_nodes - param_.num_deleted;
+ }
+ /**
+ * \brief number of extra nodes besides the root
+ */
+ [[nodiscard]] bst_node_t NumExtraNodes() const noexcept {
+ return param_.num_nodes - 1 - param_.num_deleted;
+ }
+ /* \brief Count number of leaves in tree. */
+ [[nodiscard]] bst_node_t GetNumLeaves() const;
+ [[nodiscard]] bst_node_t GetNumSplitNodes() const;
/*!
* \brief get current depth
* \param nid node id
*/
- XGBOOST_NODISCARD std::int32_t GetDepth(bst_node_t nid) const {
+ [[nodiscard]] std::int32_t GetDepth(bst_node_t nid) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->Depth(nid);
}
@@ -519,6 +525,9 @@ class RegTree : public Model {
}
return depth;
}
+ /**
+ * \brief Set the leaf weight for a multi-target tree.
+ */
void SetLeaf(bst_node_t nidx, linalg::VectorView weight) {
CHECK(IsMultiTarget());
return this->p_mt_tree_->SetLeaf(nidx, weight);
@@ -528,27 +537,15 @@ class RegTree : public Model {
* \brief get maximum depth
* \param nid node id
*/
- XGBOOST_NODISCARD int MaxDepth(int nid) const {
+ [[nodiscard]] int MaxDepth(int nid) const {
if (nodes_[nid].IsLeaf()) return 0;
- return std::max(MaxDepth(nodes_[nid].LeftChild())+1,
- MaxDepth(nodes_[nid].RightChild())+1);
+ return std::max(MaxDepth(nodes_[nid].LeftChild()) + 1, MaxDepth(nodes_[nid].RightChild()) + 1);
}
/*!
* \brief get maximum depth
*/
- int MaxDepth() {
- return MaxDepth(0);
- }
-
- /*! \brief number of extra nodes besides the root */
- XGBOOST_NODISCARD int NumExtraNodes() const {
- return param.num_nodes - 1 - param.num_deleted;
- }
-
- /* \brief Count number of leaves in tree. */
- XGBOOST_NODISCARD bst_node_t GetNumLeaves() const;
- XGBOOST_NODISCARD bst_node_t GetNumSplitNodes() const;
+ int MaxDepth() { return MaxDepth(0); }
/*!
* \brief dense feature vector that can be taken by RegTree
@@ -575,20 +572,20 @@ class RegTree : public Model {
* \brief returns the size of the feature vector
* \return the size of the feature vector
*/
- XGBOOST_NODISCARD size_t Size() const;
+ [[nodiscard]] size_t Size() const;
/*!
* \brief get ith value
* \param i feature index.
* \return the i-th feature value
*/
- XGBOOST_NODISCARD bst_float GetFvalue(size_t i) const;
+ [[nodiscard]] bst_float GetFvalue(size_t i) const;
/*!
* \brief check whether i-th entry is missing
* \param i feature index.
* \return whether i-th value is missing.
*/
- XGBOOST_NODISCARD bool IsMissing(size_t i) const;
- XGBOOST_NODISCARD bool HasMissing() const;
+ [[nodiscard]] bool IsMissing(size_t i) const;
+ [[nodiscard]] bool HasMissing() const;
private:
@@ -619,34 +616,34 @@ class RegTree : public Model {
* \param format the format to dump the model in
* \return the string of dumped model
*/
- XGBOOST_NODISCARD std::string DumpModel(const FeatureMap& fmap, bool with_stats,
+ [[nodiscard]] std::string DumpModel(const FeatureMap& fmap, bool with_stats,
std::string format) const;
/*!
* \brief Get split type for a node.
* \param nidx Index of node.
* \return The type of this split. For leaf node it's always kNumerical.
*/
- XGBOOST_NODISCARD FeatureType NodeSplitType(bst_node_t nidx) const { return split_types_.at(nidx); }
+ [[nodiscard]] FeatureType NodeSplitType(bst_node_t nidx) const { return split_types_.at(nidx); }
/*!
* \brief Get split types for all nodes.
*/
- XGBOOST_NODISCARD std::vector const& GetSplitTypes() const {
+ [[nodiscard]] std::vector const& GetSplitTypes() const {
return split_types_;
}
- XGBOOST_NODISCARD common::Span GetSplitCategories() const {
+ [[nodiscard]] common::Span GetSplitCategories() const {
return split_categories_;
}
/*!
* \brief Get the bit storage for categories
*/
- XGBOOST_NODISCARD common::Span NodeCats(bst_node_t nidx) const {
+ [[nodiscard]] common::Span NodeCats(bst_node_t nidx) const {
auto node_ptr = GetCategoriesMatrix().node_ptr;
auto categories = GetCategoriesMatrix().categories;
auto segment = node_ptr[nidx];
auto node_cats = categories.subspan(segment.beg, segment.size);
return node_cats;
}
- XGBOOST_NODISCARD auto const& GetSplitCategoriesPtr() const { return split_categories_segments_; }
+ [[nodiscard]] auto const& GetSplitCategoriesPtr() const { return split_categories_segments_; }
/**
* \brief CSR-like matrix for categorical splits.
@@ -665,7 +662,7 @@ class RegTree : public Model {
common::Span node_ptr;
};
- XGBOOST_NODISCARD CategoricalSplitMatrix GetCategoriesMatrix() const {
+ [[nodiscard]] CategoricalSplitMatrix GetCategoriesMatrix() const {
CategoricalSplitMatrix view;
view.split_type = common::Span(this->GetSplitTypes());
view.categories = this->GetSplitCategories();
@@ -673,55 +670,55 @@ class RegTree : public Model {
return view;
}
- XGBOOST_NODISCARD bst_feature_t SplitIndex(bst_node_t nidx) const {
+ [[nodiscard]] bst_feature_t SplitIndex(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->SplitIndex(nidx);
}
return (*this)[nidx].SplitIndex();
}
- XGBOOST_NODISCARD float SplitCond(bst_node_t nidx) const {
+ [[nodiscard]] float SplitCond(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->SplitCond(nidx);
}
return (*this)[nidx].SplitCond();
}
- XGBOOST_NODISCARD bool DefaultLeft(bst_node_t nidx) const {
+ [[nodiscard]] bool DefaultLeft(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->DefaultLeft(nidx);
}
return (*this)[nidx].DefaultLeft();
}
- XGBOOST_NODISCARD bool IsRoot(bst_node_t nidx) const {
+ [[nodiscard]] bool IsRoot(bst_node_t nidx) const {
if (IsMultiTarget()) {
return nidx == kRoot;
}
return (*this)[nidx].IsRoot();
}
- XGBOOST_NODISCARD bool IsLeaf(bst_node_t nidx) const {
+ [[nodiscard]] bool IsLeaf(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->IsLeaf(nidx);
}
return (*this)[nidx].IsLeaf();
}
- XGBOOST_NODISCARD bst_node_t Parent(bst_node_t nidx) const {
+ [[nodiscard]] bst_node_t Parent(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->Parent(nidx);
}
return (*this)[nidx].Parent();
}
- XGBOOST_NODISCARD bst_node_t LeftChild(bst_node_t nidx) const {
+ [[nodiscard]] bst_node_t LeftChild(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->LeftChild(nidx);
}
return (*this)[nidx].LeftChild();
}
- XGBOOST_NODISCARD bst_node_t RightChild(bst_node_t nidx) const {
+ [[nodiscard]] bst_node_t RightChild(bst_node_t nidx) const {
if (IsMultiTarget()) {
return this->p_mt_tree_->RightChild(nidx);
}
return (*this)[nidx].RightChild();
}
- XGBOOST_NODISCARD bool IsLeftChild(bst_node_t nidx) const {
+ [[nodiscard]] bool IsLeftChild(bst_node_t nidx) const {
if (IsMultiTarget()) {
CHECK_NE(nidx, kRoot);
auto p = this->p_mt_tree_->Parent(nidx);
@@ -729,7 +726,7 @@ class RegTree : public Model {
}
return (*this)[nidx].IsLeftChild();
}
- XGBOOST_NODISCARD bst_node_t Size() const {
+ [[nodiscard]] bst_node_t Size() const {
if (IsMultiTarget()) {
return this->p_mt_tree_->Size();
}
@@ -740,6 +737,8 @@ class RegTree : public Model {
template
void LoadCategoricalSplit(Json const& in);
void SaveCategoricalSplit(Json* p_out) const;
+ /*! \brief model parameter */
+ TreeParam param_;
// vector of nodes
std::vector nodes_;
// free node space, used during training process
@@ -757,20 +756,20 @@ class RegTree : public Model {
// allocate a new node,
// !!!!!! NOTE: may cause BUG here, nodes.resize
bst_node_t AllocNode() {
- if (param.num_deleted != 0) {
+ if (param_.num_deleted != 0) {
int nid = deleted_nodes_.back();
deleted_nodes_.pop_back();
nodes_[nid].Reuse();
- --param.num_deleted;
+ --param_.num_deleted;
return nid;
}
- int nd = param.num_nodes++;
- CHECK_LT(param.num_nodes, std::numeric_limits::max())
+ int nd = param_.num_nodes++;
+ CHECK_LT(param_.num_nodes, std::numeric_limits::max())
<< "number of nodes in the tree exceed 2^31";
- nodes_.resize(param.num_nodes);
- stats_.resize(param.num_nodes);
- split_types_.resize(param.num_nodes, FeatureType::kNumerical);
- split_categories_segments_.resize(param.num_nodes);
+ nodes_.resize(param_.num_nodes);
+ stats_.resize(param_.num_nodes);
+ split_types_.resize(param_.num_nodes, FeatureType::kNumerical);
+ split_categories_segments_.resize(param_.num_nodes);
return nd;
}
// delete a tree node, keep the parent field to allow trace back
@@ -785,7 +784,7 @@ class RegTree : public Model {
deleted_nodes_.push_back(nid);
nodes_[nid].MarkDelete();
- ++param.num_deleted;
+ ++param_.num_deleted;
}
};
diff --git a/jvm-packages/pom.xml b/jvm-packages/pom.xml
index 852cf7f69..a5d219040 100644
--- a/jvm-packages/pom.xml
+++ b/jvm-packages/pom.xml
@@ -37,7 +37,7 @@
3.1.1
2.12.8
2.12
- 3.3.4
+ 3.3.5
5
OFF
OFF
@@ -118,7 +118,7 @@
org.apache.maven.plugins
maven-release-plugin
- 2.5.3
+ 3.0.0
true
false
@@ -427,7 +427,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.22.2
+ 3.0.0
false
false
diff --git a/jvm-packages/xgboost4j-example/README.md b/jvm-packages/xgboost4j-example/README.md
index 4718f212f..50f268e83 100644
--- a/jvm-packages/xgboost4j-example/README.md
+++ b/jvm-packages/xgboost4j-example/README.md
@@ -1,30 +1,30 @@
-XGBoost4J Code Examples
-=======================
-
-## Java API
-* [Basic walkthrough of wrappers](src/main/java/ml/dmlc/xgboost4j/java/example/BasicWalkThrough.java)
-* [Customize loss function, and evaluation metric](src/main/java/ml/dmlc/xgboost4j/java/example/CustomObjective.java)
-* [Boosting from existing prediction](src/main/java/ml/dmlc/xgboost4j/java/example/BoostFromPrediction.java)
-* [Predicting using first n trees](src/main/java/ml/dmlc/xgboost4j/java/example/PredictFirstNtree.java)
-* [Generalized Linear Model](src/main/java/ml/dmlc/xgboost4j/java/example/GeneralizedLinearModel.java)
-* [Cross validation](src/main/java/ml/dmlc/xgboost4j/java/example/CrossValidation.java)
-* [Predicting leaf indices](src/main/java/ml/dmlc/xgboost4j/java/example/PredictLeafIndices.java)
-* [External Memory](src/main/java/ml/dmlc/xgboost4j/java/example/ExternalMemory.java)
-* [Early Stopping](src/main/java/ml/dmlc/xgboost4j/java/example/EarlyStopping.java)
-
-## Scala API
-
-* [Basic walkthrough of wrappers](src/main/scala/ml/dmlc/xgboost4j/scala/example/BasicWalkThrough.scala)
-* [Customize loss function, and evaluation metric](src/main/scala/ml/dmlc/xgboost4j/scala/example/CustomObjective.scala)
-* [Boosting from existing prediction](src/main/scala/ml/dmlc/xgboost4j/scala/example/BoostFromPrediction.scala)
-* [Predicting using first n trees](src/main/scala/ml/dmlc/xgboost4j/scala/example/PredictFirstNTree.scala)
-* [Generalized Linear Model](src/main/scala/ml/dmlc/xgboost4j/scala/example/GeneralizedLinearModel.scala)
-* [Cross validation](src/main/scala/ml/dmlc/xgboost4j/scala/example/CrossValidation.scala)
-* [Predicting leaf indices](src/main/scala/ml/dmlc/xgboost4j/scala/example/PredictLeafIndices.scala)
-* [External Memory](src/main/scala/ml/dmlc/xgboost4j/scala/example/ExternalMemory.scala)
-
-## Spark API
-* [Distributed Training with Spark](src/main/scala/ml/dmlc/xgboost4j/scala/example/spark/SparkMLlibPipeline.scala)
-
-## Flink API
-* [Distributed Training with Flink](src/main/scala/ml/dmlc/xgboost4j/scala/example/flink/DistTrainWithFlink.scala)
+XGBoost4J Code Examples
+=======================
+
+## Java API
+* [Basic walkthrough of wrappers](src/main/java/ml/dmlc/xgboost4j/java/example/BasicWalkThrough.java)
+* [Customize loss function, and evaluation metric](src/main/java/ml/dmlc/xgboost4j/java/example/CustomObjective.java)
+* [Boosting from existing prediction](src/main/java/ml/dmlc/xgboost4j/java/example/BoostFromPrediction.java)
+* [Predicting using first n trees](src/main/java/ml/dmlc/xgboost4j/java/example/PredictFirstNtree.java)
+* [Generalized Linear Model](src/main/java/ml/dmlc/xgboost4j/java/example/GeneralizedLinearModel.java)
+* [Cross validation](src/main/java/ml/dmlc/xgboost4j/java/example/CrossValidation.java)
+* [Predicting leaf indices](src/main/java/ml/dmlc/xgboost4j/java/example/PredictLeafIndices.java)
+* [External Memory](src/main/java/ml/dmlc/xgboost4j/java/example/ExternalMemory.java)
+* [Early Stopping](src/main/java/ml/dmlc/xgboost4j/java/example/EarlyStopping.java)
+
+## Scala API
+
+* [Basic walkthrough of wrappers](src/main/scala/ml/dmlc/xgboost4j/scala/example/BasicWalkThrough.scala)
+* [Customize loss function, and evaluation metric](src/main/scala/ml/dmlc/xgboost4j/scala/example/CustomObjective.scala)
+* [Boosting from existing prediction](src/main/scala/ml/dmlc/xgboost4j/scala/example/BoostFromPrediction.scala)
+* [Predicting using first n trees](src/main/scala/ml/dmlc/xgboost4j/scala/example/PredictFirstNTree.scala)
+* [Generalized Linear Model](src/main/scala/ml/dmlc/xgboost4j/scala/example/GeneralizedLinearModel.scala)
+* [Cross validation](src/main/scala/ml/dmlc/xgboost4j/scala/example/CrossValidation.scala)
+* [Predicting leaf indices](src/main/scala/ml/dmlc/xgboost4j/scala/example/PredictLeafIndices.scala)
+* [External Memory](src/main/scala/ml/dmlc/xgboost4j/scala/example/ExternalMemory.scala)
+
+## Spark API
+* [Distributed Training with Spark](src/main/scala/ml/dmlc/xgboost4j/scala/example/spark/SparkMLlibPipeline.scala)
+
+## Flink API
+* [Distributed Training with Flink](src/main/scala/ml/dmlc/xgboost4j/scala/example/flink/DistTrainWithFlink.scala)
diff --git a/jvm-packages/xgboost4j-flink/pom.xml b/jvm-packages/xgboost4j-flink/pom.xml
index e48feb876..b8b757eae 100644
--- a/jvm-packages/xgboost4j-flink/pom.xml
+++ b/jvm-packages/xgboost4j-flink/pom.xml
@@ -51,7 +51,7 @@
org.apache.hadoop
hadoop-common
- 3.3.4
+ 3.3.5
diff --git a/jvm-packages/xgboost4j-gpu/pom.xml b/jvm-packages/xgboost4j-gpu/pom.xml
index 4d35d2e76..1da88c3cc 100644
--- a/jvm-packages/xgboost4j-gpu/pom.xml
+++ b/jvm-packages/xgboost4j-gpu/pom.xml
@@ -41,13 +41,13 @@
com.typesafe.akka
akka-actor_${scala.binary.version}
- 2.7.0
+ 2.6.20
compile
com.typesafe.akka
akka-testkit_${scala.binary.version}
- 2.7.0
+ 2.6.20
test
diff --git a/jvm-packages/xgboost4j-gpu/src/test/java/ml/dmlc/xgboost4j/gpu/java/BoosterTest.java b/jvm-packages/xgboost4j-gpu/src/test/java/ml/dmlc/xgboost4j/gpu/java/BoosterTest.java
index 49d17b6be..25705fd1b 100644
--- a/jvm-packages/xgboost4j-gpu/src/test/java/ml/dmlc/xgboost4j/gpu/java/BoosterTest.java
+++ b/jvm-packages/xgboost4j-gpu/src/test/java/ml/dmlc/xgboost4j/gpu/java/BoosterTest.java
@@ -84,9 +84,10 @@ public class BoosterTest {
};
try (Table tmpTable = Table.readCSV(schema, opts, new File(trainingDataPath))) {
- ColumnVector[] df = new ColumnVector[12];
- for (int i = 0; i < 12; ++i) {
- df[i] = tmpTable.getColumn(i);
+ ColumnVector[] df = new ColumnVector[10];
+ // exclude the first two columns, they are label bounds and contain inf.
+ for (int i = 2; i < 12; ++i) {
+ df[i - 2] = tmpTable.getColumn(i);
}
try (Table X = new Table(df);) {
ColumnVector[] labels = new ColumnVector[1];
diff --git a/jvm-packages/xgboost4j-spark-gpu/src/test/scala/ml/dmlc/xgboost4j/scala/rapids/spark/GpuXGBoostClassifierSuite.scala b/jvm-packages/xgboost4j-spark-gpu/src/test/scala/ml/dmlc/xgboost4j/scala/rapids/spark/GpuXGBoostClassifierSuite.scala
index fc26b2985..7e24fe0dd 100644
--- a/jvm-packages/xgboost4j-spark-gpu/src/test/scala/ml/dmlc/xgboost4j/scala/rapids/spark/GpuXGBoostClassifierSuite.scala
+++ b/jvm-packages/xgboost4j-spark-gpu/src/test/scala/ml/dmlc/xgboost4j/scala/rapids/spark/GpuXGBoostClassifierSuite.scala
@@ -21,7 +21,7 @@ import java.io.File
import ml.dmlc.xgboost4j.scala.spark.{XGBoostClassificationModel, XGBoostClassifier}
import org.apache.spark.ml.feature.VectorAssembler
-import org.apache.spark.sql.functions.{col, udf}
+import org.apache.spark.sql.functions.{col, udf, when}
import org.apache.spark.sql.types.{FloatType, StructField, StructType}
class GpuXGBoostClassifierSuite extends GpuTestSuite {
@@ -47,7 +47,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
"num_round" -> 10, "num_workers" -> 1, "tree_method" -> "gpu_hist",
"features_cols" -> featureNames, "label_col" -> labelName)
val Array(originalDf, testDf) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
// Get a model
val model = new XGBoostClassifier(xgbParam)
.fit(originalDf)
@@ -64,7 +65,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
"num_round" -> 10, "num_workers" -> 1, "tree_method" -> "gpu_hist",
"features_cols" -> featureNames, "label_col" -> labelName)
val Array(originalDf, testDf) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
val getWeightFromF1 = udf({ f1: Float => if (f1.toInt % 2 == 0) 1.0f else 0.001f })
val dfWithWeight = originalDf.withColumn("weight", getWeightFromF1(col("f1")))
@@ -87,7 +89,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
val xgbParam = Map("eta" -> 0.1f, "max_depth" -> 2, "objective" -> "binary:logistic",
"num_round" -> 10, "num_workers" -> 1)
val Array(rawInput, testDf) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
val classifier = new XGBoostClassifier(xgbParam)
.setFeaturesCol(featureNames)
@@ -122,7 +125,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
val xgbParam = Map("eta" -> 0.1f, "max_depth" -> 2, "objective" -> "binary:logistic",
"num_round" -> 10, "num_workers" -> 1)
val Array(rawInput, _) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
val vectorAssembler = new VectorAssembler()
.setHandleInvalid("keep")
@@ -144,7 +148,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
// transform on GPU
withGpuSparkSession() { spark =>
val Array(_, testDf) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
// Since CPU model does not know the information about the features cols that GPU transform
// pipeline requires. End user needs to setFeaturesCol(features: Array[String]) in the model
@@ -174,7 +179,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
val xgbParam = Map("eta" -> 0.1f, "max_depth" -> 2, "objective" -> "binary:logistic",
"num_round" -> 10, "num_workers" -> 1)
val Array(rawInput, _) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
val classifier = new XGBoostClassifier(xgbParam)
.setFeaturesCol(featureNames)
@@ -190,7 +196,8 @@ class GpuXGBoostClassifierSuite extends GpuTestSuite {
// transform on CPU
withCpuSparkSession() { spark =>
val Array(_, rawInput) = spark.read.option("header", "true").schema(schema)
- .csv(dataPath).randomSplit(Array(0.7, 0.3), seed = 1)
+ .csv(dataPath).withColumn("f2", when(col("f2").isin(Float.PositiveInfinity), 0))
+ .randomSplit(Array(0.7, 0.3), seed = 1)
val featureColName = "feature_col"
val vectorAssembler = new VectorAssembler()
diff --git a/jvm-packages/xgboost4j-spark/src/test/resources/rank.test.csv b/jvm-packages/xgboost4j-spark/src/test/resources/rank.test.csv
index 83bf8b080..729732e5b 100644
--- a/jvm-packages/xgboost4j-spark/src/test/resources/rank.test.csv
+++ b/jvm-packages/xgboost4j-spark/src/test/resources/rank.test.csv
@@ -1,66 +1,66 @@
-0,10.0229017899,7.30178495562,0.118115020017,1
-0,9.93639621859,9.93102159291,0.0435030004396,1
-0,10.1301737265,0.00411765220572,2.4165878053,1
-1,9.87828587087,0.608588414992,0.111262590883,1
-0,10.1373430048,0.47764012225,0.991553052194,1
-0,10.0523814718,4.72152505167,0.672978832666,1
-0,10.0449715742,8.40373928536,0.384457573667,1
-1,996.398498791,941.976309154,0.230269231292,2
-0,1005.11269468,900.093680877,0.265031528873,2
-0,997.160349441,891.331101688,2.19362017313,2
-0,993.754139031,44.8000165317,1.03868009875,2
-1,994.831299184,241.959208453,0.667631827024,2
-0,995.948333283,7.94326917112,0.750490877118,3
-0,989.733981273,7.52077625436,0.0126335967282,3
-0,1003.54086516,6.48177510564,1.19441696788,3
-0,996.56177804,9.71959812613,1.33082465111,3
-0,1005.61382467,0.234339369309,1.17987797356,3
-1,980.215758708,6.85554542926,2.63965085259,3
-1,987.776408872,2.23354609991,0.841885278028,3
-0,1006.54260396,8.12142049834,2.26639471174,3
-0,1009.87927639,6.40028519044,0.775155669615,3
-0,9.95006244393,928.76896718,234.948458244,4
-1,10.0749152258,255.294574476,62.9728604166,4
-1,10.1916541988,312.682867085,92.299413677,4
-0,9.95646724484,742.263188416,53.3310473654,4
-0,9.86211293222,996.237023866,2.00760301168,4
-1,9.91801019468,303.971783709,50.3147230679,4
-0,996.983996934,9.52188222766,1.33588120981,5
-0,995.704388126,9.49260524915,0.908498516541,5
-0,987.86480767,0.0870786716821,0.108859297837,5
-0,1000.99561307,2.85272694575,0.171134518956,5
-0,1011.05508066,7.55336771768,1.04950084825,5
-1,985.52199365,0.763305780608,1.7402424375,5
-0,10.0430321467,813.185427181,4.97728254185,6
-0,10.0812334228,258.297288417,0.127477670549,6
-0,9.84210504292,887.205815261,0.991689193955,6
-1,9.94625332613,0.298622762132,0.147881353231,6
-0,9.97800659954,727.619819757,0.0718361141866,6
-1,9.8037938472,957.385549617,0.0618862028941,6
-0,10.0880634741,185.024638577,1.7028095095,6
-0,9.98630799154,109.10631473,0.681117359751,6
-0,9.91671416638,166.248076588,122.538291094,7
-0,10.1206910464,88.1539468531,141.189859069,7
-1,10.1767160518,1.02960996847,172.02256237,7
-0,9.93025147233,391.196641942,58.040338247,7
-0,9.84850936037,474.63346537,17.5627875397,7
-1,9.8162731343,61.9199554213,30.6740972851,7
-0,10.0403482984,987.50416929,73.0472906209,7
-1,997.019228359,133.294717663,0.0572254083186,8
-0,973.303999107,1.79080888849,0.100478717048,8
-0,1008.28808825,342.282350685,0.409806485495,8
-0,1014.55621524,0.680510407082,0.929530602495,8
-1,1012.74370325,823.105266455,0.0894693730585,8
-0,1003.63554038,727.334432075,0.58206275756,8
-0,10.1560432436,740.35938307,11.6823378533,9
-0,9.83949099701,512.828227154,138.206666681,9
-1,10.1837395682,179.287126088,185.479062365,9
-1,9.9761881495,12.1093388336,9.1264604171,9
-1,9.77402180766,318.561317743,80.6005221355,9
-0,1011.15705381,0.215825852155,1.34429667906,10
-0,1005.60353229,727.202346126,1.47146041005,10
-1,1013.93702961,58.7312725205,0.421041560754,10
-0,1004.86813074,757.693204258,0.566055205344,10
-0,999.996324692,813.12386828,0.864428279513,10
-0,996.55255931,918.760056995,0.43365051974,10
-1,1004.1394132,464.371823646,0.312492288321,10
+0,10.0229017899,7.30178495562,0.118115020017,1
+0,9.93639621859,9.93102159291,0.0435030004396,1
+0,10.1301737265,0.00411765220572,2.4165878053,1
+1,9.87828587087,0.608588414992,0.111262590883,1
+0,10.1373430048,0.47764012225,0.991553052194,1
+0,10.0523814718,4.72152505167,0.672978832666,1
+0,10.0449715742,8.40373928536,0.384457573667,1
+1,996.398498791,941.976309154,0.230269231292,2
+0,1005.11269468,900.093680877,0.265031528873,2
+0,997.160349441,891.331101688,2.19362017313,2
+0,993.754139031,44.8000165317,1.03868009875,2
+1,994.831299184,241.959208453,0.667631827024,2
+0,995.948333283,7.94326917112,0.750490877118,3
+0,989.733981273,7.52077625436,0.0126335967282,3
+0,1003.54086516,6.48177510564,1.19441696788,3
+0,996.56177804,9.71959812613,1.33082465111,3
+0,1005.61382467,0.234339369309,1.17987797356,3
+1,980.215758708,6.85554542926,2.63965085259,3
+1,987.776408872,2.23354609991,0.841885278028,3
+0,1006.54260396,8.12142049834,2.26639471174,3
+0,1009.87927639,6.40028519044,0.775155669615,3
+0,9.95006244393,928.76896718,234.948458244,4
+1,10.0749152258,255.294574476,62.9728604166,4
+1,10.1916541988,312.682867085,92.299413677,4
+0,9.95646724484,742.263188416,53.3310473654,4
+0,9.86211293222,996.237023866,2.00760301168,4
+1,9.91801019468,303.971783709,50.3147230679,4
+0,996.983996934,9.52188222766,1.33588120981,5
+0,995.704388126,9.49260524915,0.908498516541,5
+0,987.86480767,0.0870786716821,0.108859297837,5
+0,1000.99561307,2.85272694575,0.171134518956,5
+0,1011.05508066,7.55336771768,1.04950084825,5
+1,985.52199365,0.763305780608,1.7402424375,5
+0,10.0430321467,813.185427181,4.97728254185,6
+0,10.0812334228,258.297288417,0.127477670549,6
+0,9.84210504292,887.205815261,0.991689193955,6
+1,9.94625332613,0.298622762132,0.147881353231,6
+0,9.97800659954,727.619819757,0.0718361141866,6
+1,9.8037938472,957.385549617,0.0618862028941,6
+0,10.0880634741,185.024638577,1.7028095095,6
+0,9.98630799154,109.10631473,0.681117359751,6
+0,9.91671416638,166.248076588,122.538291094,7
+0,10.1206910464,88.1539468531,141.189859069,7
+1,10.1767160518,1.02960996847,172.02256237,7
+0,9.93025147233,391.196641942,58.040338247,7
+0,9.84850936037,474.63346537,17.5627875397,7
+1,9.8162731343,61.9199554213,30.6740972851,7
+0,10.0403482984,987.50416929,73.0472906209,7
+1,997.019228359,133.294717663,0.0572254083186,8
+0,973.303999107,1.79080888849,0.100478717048,8
+0,1008.28808825,342.282350685,0.409806485495,8
+0,1014.55621524,0.680510407082,0.929530602495,8
+1,1012.74370325,823.105266455,0.0894693730585,8
+0,1003.63554038,727.334432075,0.58206275756,8
+0,10.1560432436,740.35938307,11.6823378533,9
+0,9.83949099701,512.828227154,138.206666681,9
+1,10.1837395682,179.287126088,185.479062365,9
+1,9.9761881495,12.1093388336,9.1264604171,9
+1,9.77402180766,318.561317743,80.6005221355,9
+0,1011.15705381,0.215825852155,1.34429667906,10
+0,1005.60353229,727.202346126,1.47146041005,10
+1,1013.93702961,58.7312725205,0.421041560754,10
+0,1004.86813074,757.693204258,0.566055205344,10
+0,999.996324692,813.12386828,0.864428279513,10
+0,996.55255931,918.760056995,0.43365051974,10
+1,1004.1394132,464.371823646,0.312492288321,10
diff --git a/jvm-packages/xgboost4j-spark/src/test/resources/rank.train.csv b/jvm-packages/xgboost4j-spark/src/test/resources/rank.train.csv
index ebe232b51..bec3b034c 100644
--- a/jvm-packages/xgboost4j-spark/src/test/resources/rank.train.csv
+++ b/jvm-packages/xgboost4j-spark/src/test/resources/rank.train.csv
@@ -1,149 +1,149 @@
-0,985.574005058,320.223538037,0.621236086198,1
-0,1010.52917943,635.535543082,2.14984030531,1
-0,1012.91900422,132.387300057,0.488761066665,1
-0,990.829194034,135.102081162,0.747701610673,1
-0,1007.05103629,154.289183562,0.464118249201,1
-0,994.9573036,317.483732878,0.0313685555674,1
-0,987.8071541,731.349178363,0.244616944245,1
-1,10.0349544469,2.29750906143,36.4949974282,2
-0,9.92953881383,5.39134047297,120.041297548,2
-0,10.0909866713,9.06191026312,138.807825798,2
-1,10.2090970614,0.0784495944448,58.207703565,2
-0,9.85695905893,9.99500727713,56.8610243778,2
-1,10.0805758547,0.0410805760559,222.102302076,2
-0,10.1209914486,9.9729127088,171.888238763,2
-0,10.0331939798,0.853339303793,311.181328375,3
-0,9.93901762951,2.72757449146,78.4859514413,3
-0,10.0752365346,9.18695328235,49.8520256553,3
-1,10.0456548902,0.270936043122,123.462958597,3
-0,10.0568923673,0.82997113263,44.9391426001,3
-0,9.8214143472,0.277538931578,15.4217659578,3
-0,9.95258604431,8.69564346094,255.513470671,3
-0,9.91934976357,7.72809741413,82.171591817,3
-0,10.043239582,8.64168255553,38.9657919329,3
-1,10.0236147929,0.0496662263659,4.40889812286,3
-1,1001.85585324,3.75646886071,0.0179224994842,4
-0,1014.25578571,0.285765311201,0.510329864983,4
-1,1002.81422786,9.77676280375,0.433705951912,4
-1,998.072711553,2.82100686538,0.889829076909,4
-0,1003.77395036,2.55916592114,0.0359402151496,4
-1,10.0807877782,4.98513959013,47.5266363559,5
-0,10.0015013081,9.94302478763,78.3697486277,5
-1,10.0441936789,0.305091816635,56.8213984987,5
-0,9.94257106618,7.23909568913,442.463339039,5
-1,9.86479307916,6.41701315844,55.1365304834,5
-0,10.0428628516,9.98466447697,0.391632812588,5
-0,9.94445884566,9.99970945878,260.438436534,5
-1,9.84641392823,225.78051312,1.00525978847,6
-1,9.86907690608,26.8971083147,0.577959255991,6
-0,10.0177314626,0.110585342313,2.30545043031,6
-0,10.0688190907,412.023866234,1.22421542264,6
-0,10.1251769646,13.8212202925,0.129171734504,6
-0,10.0840758802,407.359097187,0.477000870705,6
-0,10.1007458705,987.183625145,0.149385677415,6
-0,9.86472656059,169.559640615,0.147221652519,6
-0,9.94207419238,507.290053755,0.41996207214,6
-0,9.9671005502,1.62610457716,0.408173666788,6
-0,1010.57126596,9.06673707562,0.672092284372,7
-0,1001.6718262,9.53203990055,4.7364050044,7
-0,995.777341384,4.43847316256,2.07229073634,7
-0,1002.95701386,5.51711016665,1.24294450546,7
-0,1016.0988238,0.626468941906,0.105627919134,7
-0,1013.67571419,0.042315529666,0.717619310322,7
-1,994.747747892,6.01989364024,0.772910130015,7
-1,991.654593872,7.35575736952,1.19822091548,7
-0,1008.47101732,8.28240754909,0.229582481359,7
-0,1000.81975227,1.52448354056,0.096441660362,7
-0,10.0900922344,322.656649307,57.8149073088,8
-1,10.0868337371,2.88652339174,54.8865514572,8
-0,10.0988984137,979.483832657,52.6809830901,8
-0,9.97678959238,665.770979738,481.069628909,8
-0,9.78554312773,257.309358658,47.7324475232,8
-0,10.0985967566,935.896512941,138.937052808,8
-0,10.0522252319,876.376299607,6.00373510669,8
-1,9.88065229501,9.99979825653,0.0674603696149,9
-0,10.0483244098,0.0653852316381,0.130679349938,9
-1,9.99685215607,1.76602542774,0.2551321159,9
-0,9.99750159428,1.01591534436,0.145445506504,9
-1,9.97380908941,0.940048645571,0.411805696316,9
-0,9.99977678382,6.91329929641,5.57858201258,9
-0,978.876096381,933.775364741,0.579170824236,10
-0,998.381016406,220.940470582,2.01491778565,10
-0,987.917644594,8.74667873567,0.364006099758,10
-0,1000.20994892,25.2945450565,3.5684398964,10
-0,1014.57141264,675.593540733,0.164174055535,10
-0,998.867283535,765.452750642,0.818425293238,10
-0,10.2143092481,273.576539531,137.111774354,11
-0,10.0366658918,842.469052609,2.32134375927,11
-0,10.1281202091,395.654057342,35.4184893063,11
-0,10.1443721289,960.058461049,272.887070637,11
-0,10.1353234784,535.51304462,2.15393842032,11
-1,10.0451640374,216.733858424,55.6533298016,11
-1,9.94254592171,44.5985537358,304.614176871,11
-0,10.1319257181,613.545504487,5.42391587912,11
-0,1020.63622468,997.476744201,0.509425590461,12
-0,986.304585519,822.669937965,0.605133561808,12
-1,1012.66863221,26.7185759069,0.0875458784828,12
-0,995.387656321,81.8540176995,0.691999430068,12
-0,1020.6587198,848.826964547,0.540159430526,12
-1,1003.81573853,379.84350931,0.0083682925194,12
-0,1021.60921516,641.376951467,1.12339054807,12
-0,1000.17585041,122.107138713,1.09906375372,12
-1,987.64802348,5.98448541152,0.124241987204,12
-1,9.94610136583,346.114985897,0.387708236565,13
-0,9.96812192337,313.278109696,0.00863026595671,13
-0,10.0181739194,36.7378924562,2.92179879835,13
-0,9.89000102695,164.273723971,0.685222591968,13
-0,10.1555212436,320.451459462,2.01341536261,13
-0,10.0085727613,999.767117646,0.462294934168,13
-1,9.93099658724,5.17478203909,0.213855205032,13
-0,10.0629454957,663.088181857,0.049022351462,13
-0,10.1109732417,734.904569784,1.6998450094,13
-0,1006.6015266,505.023453703,1.90870566777,14
-0,991.865769489,245.437343115,0.475109744256,14
-0,998.682734072,950.041057232,1.9256314201,14
-0,1005.02207209,2.9619314197,0.0517146822357,14
-0,1002.54526214,860.562681899,0.915687092848,14
-0,1000.38847359,808.416525088,0.209690673808,14
-1,992.557818382,373.889409453,0.107571728577,14
-0,1002.07722137,997.329626371,1.06504260496,14
-0,1000.40504333,949.832139189,0.539159980327,14
-0,10.1460179902,8.86082969819,135.953842715,15
-1,9.98529296553,2.87366448495,1.74249892194,15
-0,9.88942676744,9.4031821056,149.473066381,15
-1,10.0192953341,1.99685737576,1.79502473397,15
-0,10.0110654379,8.13112593726,87.7765628103,15
-0,997.148677047,733.936190093,1.49298494242,16
-0,1008.70465919,957.121652078,0.217414013634,16
-1,997.356154278,541.599587807,0.100855972216,16
-0,999.615897283,943.700501824,0.862874175879,16
-1,997.36859077,0.200859940848,0.13601892182,16
-0,10.0423255624,1.73855202168,0.956695338485,17
-1,9.88440755486,9.9994600678,0.305080529665,17
-0,10.0891026412,3.28031719474,0.364450973697,17
-0,9.90078644258,8.77839663617,0.456660574479,17
-1,9.79380029711,8.77220326156,0.527292005175,17
-0,9.93613887011,9.76270841268,1.40865693823,17
-0,10.0009239007,7.29056178263,0.498015866607,17
-0,9.96603319905,5.12498000925,0.517492532783,17
-0,10.0923827222,2.76652583955,1.56571226159,17
-1,10.0983782035,587.788120694,0.031756483687,18
-1,9.91397225464,994.527496819,3.72092164978,18
-0,10.1057472738,2.92894440088,0.683506438532,18
-0,10.1014053354,959.082038017,1.07039624129,18
-0,10.1433253044,322.515119317,0.51408278993,18
-1,9.82832510699,637.104433908,0.250272776427,18
-0,1000.49729075,2.75336888111,0.576634423274,19
-1,984.90338088,0.0295435794035,1.26273339929,19
-0,1001.53811442,4.64164410861,0.0293389959504,19
-1,995.875898395,5.08223403205,0.382330566779,19
-0,996.405937252,6.26395190757,0.453645816611,19
-0,10.0165140779,340.126072514,0.220794603312,20
-0,9.93482824816,951.672000448,0.124406293612,20
-0,10.1700278554,0.0140985961008,0.252452256311,20
-0,9.99825079542,950.382643896,0.875382402062,20
-0,9.87316410028,686.788257829,0.215886999825,20
-0,10.2893240654,89.3947931451,0.569578232133,20
-0,9.98689192703,0.430107535413,2.99869831728,20
-0,10.1365175107,972.279245093,0.0865099386744,20
-0,9.90744703306,50.810461183,3.00863325197,20
+0,985.574005058,320.223538037,0.621236086198,1
+0,1010.52917943,635.535543082,2.14984030531,1
+0,1012.91900422,132.387300057,0.488761066665,1
+0,990.829194034,135.102081162,0.747701610673,1
+0,1007.05103629,154.289183562,0.464118249201,1
+0,994.9573036,317.483732878,0.0313685555674,1
+0,987.8071541,731.349178363,0.244616944245,1
+1,10.0349544469,2.29750906143,36.4949974282,2
+0,9.92953881383,5.39134047297,120.041297548,2
+0,10.0909866713,9.06191026312,138.807825798,2
+1,10.2090970614,0.0784495944448,58.207703565,2
+0,9.85695905893,9.99500727713,56.8610243778,2
+1,10.0805758547,0.0410805760559,222.102302076,2
+0,10.1209914486,9.9729127088,171.888238763,2
+0,10.0331939798,0.853339303793,311.181328375,3
+0,9.93901762951,2.72757449146,78.4859514413,3
+0,10.0752365346,9.18695328235,49.8520256553,3
+1,10.0456548902,0.270936043122,123.462958597,3
+0,10.0568923673,0.82997113263,44.9391426001,3
+0,9.8214143472,0.277538931578,15.4217659578,3
+0,9.95258604431,8.69564346094,255.513470671,3
+0,9.91934976357,7.72809741413,82.171591817,3
+0,10.043239582,8.64168255553,38.9657919329,3
+1,10.0236147929,0.0496662263659,4.40889812286,3
+1,1001.85585324,3.75646886071,0.0179224994842,4
+0,1014.25578571,0.285765311201,0.510329864983,4
+1,1002.81422786,9.77676280375,0.433705951912,4
+1,998.072711553,2.82100686538,0.889829076909,4
+0,1003.77395036,2.55916592114,0.0359402151496,4
+1,10.0807877782,4.98513959013,47.5266363559,5
+0,10.0015013081,9.94302478763,78.3697486277,5
+1,10.0441936789,0.305091816635,56.8213984987,5
+0,9.94257106618,7.23909568913,442.463339039,5
+1,9.86479307916,6.41701315844,55.1365304834,5
+0,10.0428628516,9.98466447697,0.391632812588,5
+0,9.94445884566,9.99970945878,260.438436534,5
+1,9.84641392823,225.78051312,1.00525978847,6
+1,9.86907690608,26.8971083147,0.577959255991,6
+0,10.0177314626,0.110585342313,2.30545043031,6
+0,10.0688190907,412.023866234,1.22421542264,6
+0,10.1251769646,13.8212202925,0.129171734504,6
+0,10.0840758802,407.359097187,0.477000870705,6
+0,10.1007458705,987.183625145,0.149385677415,6
+0,9.86472656059,169.559640615,0.147221652519,6
+0,9.94207419238,507.290053755,0.41996207214,6
+0,9.9671005502,1.62610457716,0.408173666788,6
+0,1010.57126596,9.06673707562,0.672092284372,7
+0,1001.6718262,9.53203990055,4.7364050044,7
+0,995.777341384,4.43847316256,2.07229073634,7
+0,1002.95701386,5.51711016665,1.24294450546,7
+0,1016.0988238,0.626468941906,0.105627919134,7
+0,1013.67571419,0.042315529666,0.717619310322,7
+1,994.747747892,6.01989364024,0.772910130015,7
+1,991.654593872,7.35575736952,1.19822091548,7
+0,1008.47101732,8.28240754909,0.229582481359,7
+0,1000.81975227,1.52448354056,0.096441660362,7
+0,10.0900922344,322.656649307,57.8149073088,8
+1,10.0868337371,2.88652339174,54.8865514572,8
+0,10.0988984137,979.483832657,52.6809830901,8
+0,9.97678959238,665.770979738,481.069628909,8
+0,9.78554312773,257.309358658,47.7324475232,8
+0,10.0985967566,935.896512941,138.937052808,8
+0,10.0522252319,876.376299607,6.00373510669,8
+1,9.88065229501,9.99979825653,0.0674603696149,9
+0,10.0483244098,0.0653852316381,0.130679349938,9
+1,9.99685215607,1.76602542774,0.2551321159,9
+0,9.99750159428,1.01591534436,0.145445506504,9
+1,9.97380908941,0.940048645571,0.411805696316,9
+0,9.99977678382,6.91329929641,5.57858201258,9
+0,978.876096381,933.775364741,0.579170824236,10
+0,998.381016406,220.940470582,2.01491778565,10
+0,987.917644594,8.74667873567,0.364006099758,10
+0,1000.20994892,25.2945450565,3.5684398964,10
+0,1014.57141264,675.593540733,0.164174055535,10
+0,998.867283535,765.452750642,0.818425293238,10
+0,10.2143092481,273.576539531,137.111774354,11
+0,10.0366658918,842.469052609,2.32134375927,11
+0,10.1281202091,395.654057342,35.4184893063,11
+0,10.1443721289,960.058461049,272.887070637,11
+0,10.1353234784,535.51304462,2.15393842032,11
+1,10.0451640374,216.733858424,55.6533298016,11
+1,9.94254592171,44.5985537358,304.614176871,11
+0,10.1319257181,613.545504487,5.42391587912,11
+0,1020.63622468,997.476744201,0.509425590461,12
+0,986.304585519,822.669937965,0.605133561808,12
+1,1012.66863221,26.7185759069,0.0875458784828,12
+0,995.387656321,81.8540176995,0.691999430068,12
+0,1020.6587198,848.826964547,0.540159430526,12
+1,1003.81573853,379.84350931,0.0083682925194,12
+0,1021.60921516,641.376951467,1.12339054807,12
+0,1000.17585041,122.107138713,1.09906375372,12
+1,987.64802348,5.98448541152,0.124241987204,12
+1,9.94610136583,346.114985897,0.387708236565,13
+0,9.96812192337,313.278109696,0.00863026595671,13
+0,10.0181739194,36.7378924562,2.92179879835,13
+0,9.89000102695,164.273723971,0.685222591968,13
+0,10.1555212436,320.451459462,2.01341536261,13
+0,10.0085727613,999.767117646,0.462294934168,13
+1,9.93099658724,5.17478203909,0.213855205032,13
+0,10.0629454957,663.088181857,0.049022351462,13
+0,10.1109732417,734.904569784,1.6998450094,13
+0,1006.6015266,505.023453703,1.90870566777,14
+0,991.865769489,245.437343115,0.475109744256,14
+0,998.682734072,950.041057232,1.9256314201,14
+0,1005.02207209,2.9619314197,0.0517146822357,14
+0,1002.54526214,860.562681899,0.915687092848,14
+0,1000.38847359,808.416525088,0.209690673808,14
+1,992.557818382,373.889409453,0.107571728577,14
+0,1002.07722137,997.329626371,1.06504260496,14
+0,1000.40504333,949.832139189,0.539159980327,14
+0,10.1460179902,8.86082969819,135.953842715,15
+1,9.98529296553,2.87366448495,1.74249892194,15
+0,9.88942676744,9.4031821056,149.473066381,15
+1,10.0192953341,1.99685737576,1.79502473397,15
+0,10.0110654379,8.13112593726,87.7765628103,15
+0,997.148677047,733.936190093,1.49298494242,16
+0,1008.70465919,957.121652078,0.217414013634,16
+1,997.356154278,541.599587807,0.100855972216,16
+0,999.615897283,943.700501824,0.862874175879,16
+1,997.36859077,0.200859940848,0.13601892182,16
+0,10.0423255624,1.73855202168,0.956695338485,17
+1,9.88440755486,9.9994600678,0.305080529665,17
+0,10.0891026412,3.28031719474,0.364450973697,17
+0,9.90078644258,8.77839663617,0.456660574479,17
+1,9.79380029711,8.77220326156,0.527292005175,17
+0,9.93613887011,9.76270841268,1.40865693823,17
+0,10.0009239007,7.29056178263,0.498015866607,17
+0,9.96603319905,5.12498000925,0.517492532783,17
+0,10.0923827222,2.76652583955,1.56571226159,17
+1,10.0983782035,587.788120694,0.031756483687,18
+1,9.91397225464,994.527496819,3.72092164978,18
+0,10.1057472738,2.92894440088,0.683506438532,18
+0,10.1014053354,959.082038017,1.07039624129,18
+0,10.1433253044,322.515119317,0.51408278993,18
+1,9.82832510699,637.104433908,0.250272776427,18
+0,1000.49729075,2.75336888111,0.576634423274,19
+1,984.90338088,0.0295435794035,1.26273339929,19
+0,1001.53811442,4.64164410861,0.0293389959504,19
+1,995.875898395,5.08223403205,0.382330566779,19
+0,996.405937252,6.26395190757,0.453645816611,19
+0,10.0165140779,340.126072514,0.220794603312,20
+0,9.93482824816,951.672000448,0.124406293612,20
+0,10.1700278554,0.0140985961008,0.252452256311,20
+0,9.99825079542,950.382643896,0.875382402062,20
+0,9.87316410028,686.788257829,0.215886999825,20
+0,10.2893240654,89.3947931451,0.569578232133,20
+0,9.98689192703,0.430107535413,2.99869831728,20
+0,10.1365175107,972.279245093,0.0865099386744,20
+0,9.90744703306,50.810461183,3.00863325197,20
diff --git a/jvm-packages/xgboost4j-tester/generate_pom.py b/jvm-packages/xgboost4j-tester/generate_pom.py
index ff651a4f7..edc9759bd 100644
--- a/jvm-packages/xgboost4j-tester/generate_pom.py
+++ b/jvm-packages/xgboost4j-tester/generate_pom.py
@@ -51,13 +51,13 @@ pom_template = """
com.typesafe.akka
akka-actor_${{scala.binary.version}}
- 2.7.0
+ 2.6.20
compile
com.typesafe.akka
akka-testkit_${{scala.binary.version}}
- 2.7.0
+ 2.6.20
test
diff --git a/jvm-packages/xgboost4j/pom.xml b/jvm-packages/xgboost4j/pom.xml
index dcc4bf60c..946b11108 100644
--- a/jvm-packages/xgboost4j/pom.xml
+++ b/jvm-packages/xgboost4j/pom.xml
@@ -34,13 +34,13 @@
com.typesafe.akka
akka-actor_${scala.binary.version}
- 2.7.0
+ 2.6.20
compile
com.typesafe.akka
akka-testkit_${scala.binary.version}
- 2.7.0
+ 2.6.20
test
diff --git a/plugin/updater_oneapi/predictor_oneapi.cc b/plugin/updater_oneapi/predictor_oneapi.cc
index eafe83e19..25a14186c 100755
--- a/plugin/updater_oneapi/predictor_oneapi.cc
+++ b/plugin/updater_oneapi/predictor_oneapi.cc
@@ -1,448 +1,447 @@
-/*!
- * Copyright by Contributors 2017-2020
- */
-#include
-#include
-#include
-
-#include "xgboost/base.h"
-#include "xgboost/data.h"
-#include "xgboost/predictor.h"
-#include "xgboost/tree_model.h"
-#include "xgboost/tree_updater.h"
-#include "xgboost/logging.h"
-#include "xgboost/host_device_vector.h"
-
-#include "../../src/data/adapter.h"
-#include "../../src/common/math.h"
-#include "../../src/gbm/gbtree_model.h"
-
-#include "CL/sycl.hpp"
-
-namespace xgboost {
-namespace predictor {
-
-DMLC_REGISTRY_FILE_TAG(predictor_oneapi);
-
-/*! \brief Element from a sparse vector */
-struct EntryOneAPI {
- /*! \brief feature index */
- bst_feature_t index;
- /*! \brief feature value */
- bst_float fvalue;
- /*! \brief default constructor */
- EntryOneAPI() = default;
- /*!
- * \brief constructor with index and value
- * \param index The feature or row index.
- * \param fvalue The feature value.
- */
- EntryOneAPI(bst_feature_t index, bst_float fvalue) : index(index), fvalue(fvalue) {}
-
- EntryOneAPI(const Entry& entry) : index(entry.index), fvalue(entry.fvalue) {}
-
- /*! \brief reversely compare feature values */
- inline static bool CmpValue(const EntryOneAPI& a, const EntryOneAPI& b) {
- return a.fvalue < b.fvalue;
- }
- inline bool operator==(const EntryOneAPI& other) const {
- return (this->index == other.index && this->fvalue == other.fvalue);
- }
-};
-
-struct DeviceMatrixOneAPI {
- DMatrix* p_mat; // Pointer to the original matrix on the host
- cl::sycl::queue qu_;
- size_t* row_ptr;
- size_t row_ptr_size;
- EntryOneAPI* data;
-
- DeviceMatrixOneAPI(DMatrix* dmat, cl::sycl::queue qu) : p_mat(dmat), qu_(qu) {
- size_t num_row = 0;
- size_t num_nonzero = 0;
- for (auto &batch : dmat->GetBatches()) {
- const auto& data_vec = batch.data.HostVector();
- const auto& offset_vec = batch.offset.HostVector();
- num_nonzero += data_vec.size();
- num_row += batch.Size();
- }
-
- row_ptr = cl::sycl::malloc_shared(num_row + 1, qu_);
- data = cl::sycl::malloc_shared(num_nonzero, qu_);
-
- size_t data_offset = 0;
- for (auto &batch : dmat->GetBatches()) {
- const auto& data_vec = batch.data.HostVector();
- const auto& offset_vec = batch.offset.HostVector();
- size_t batch_size = batch.Size();
- if (batch_size > 0) {
- std::copy(offset_vec.data(), offset_vec.data() + batch_size,
- row_ptr + batch.base_rowid);
- if (batch.base_rowid > 0) {
- for(size_t i = 0; i < batch_size; i++)
- row_ptr[i + batch.base_rowid] += batch.base_rowid;
- }
- std::copy(data_vec.data(), data_vec.data() + offset_vec[batch_size],
- data + data_offset);
- data_offset += offset_vec[batch_size];
- }
- }
- row_ptr[num_row] = data_offset;
- row_ptr_size = num_row + 1;
- }
-
- ~DeviceMatrixOneAPI() {
- if (row_ptr) {
- cl::sycl::free(row_ptr, qu_);
- }
- if (data) {
- cl::sycl::free(data, qu_);
- }
- }
-};
-
-struct DeviceNodeOneAPI {
- DeviceNodeOneAPI()
- : fidx(-1), left_child_idx(-1), right_child_idx(-1) {}
-
- union NodeValue {
- float leaf_weight;
- float fvalue;
- };
-
- int fidx;
- int left_child_idx;
- int right_child_idx;
- NodeValue val;
-
- DeviceNodeOneAPI(const RegTree::Node& n) { // NOLINT
- this->left_child_idx = n.LeftChild();
- this->right_child_idx = n.RightChild();
- this->fidx = n.SplitIndex();
- if (n.DefaultLeft()) {
- fidx |= (1U << 31);
- }
-
- if (n.IsLeaf()) {
- this->val.leaf_weight = n.LeafValue();
- } else {
- this->val.fvalue = n.SplitCond();
- }
- }
-
- bool IsLeaf() const { return left_child_idx == -1; }
-
- int GetFidx() const { return fidx & ((1U << 31) - 1U); }
-
- bool MissingLeft() const { return (fidx >> 31) != 0; }
-
- int MissingIdx() const {
- if (MissingLeft()) {
- return this->left_child_idx;
- } else {
- return this->right_child_idx;
- }
- }
-
- float GetFvalue() const { return val.fvalue; }
-
- float GetWeight() const { return val.leaf_weight; }
-};
-
-class DeviceModelOneAPI {
- public:
- cl::sycl::queue qu_;
- DeviceNodeOneAPI* nodes;
- size_t* tree_segments;
- int* tree_group;
- size_t tree_beg_;
- size_t tree_end_;
- int num_group;
-
- DeviceModelOneAPI() : nodes(nullptr), tree_segments(nullptr), tree_group(nullptr) {}
-
- ~DeviceModelOneAPI() {
- Reset();
- }
-
- void Reset() {
- if (nodes)
- cl::sycl::free(nodes, qu_);
- if (tree_segments)
- cl::sycl::free(tree_segments, qu_);
- if (tree_group)
- cl::sycl::free(tree_group, qu_);
- }
-
- void Init(const gbm::GBTreeModel& model, size_t tree_begin, size_t tree_end, cl::sycl::queue qu) {
- qu_ = qu;
- CHECK_EQ(model.param.size_leaf_vector, 0);
- Reset();
-
- tree_segments = cl::sycl::malloc_shared((tree_end - tree_begin) + 1, qu_);
- int sum = 0;
- tree_segments[0] = sum;
- for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
- sum += model.trees[tree_idx]->GetNodes().size();
- tree_segments[tree_idx - tree_begin + 1] = sum;
- }
-
- nodes = cl::sycl::malloc_shared(sum, qu_);
- for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
- auto& src_nodes = model.trees[tree_idx]->GetNodes();
- for (size_t node_idx = 0; node_idx < src_nodes.size(); node_idx++)
- nodes[node_idx + tree_segments[tree_idx - tree_begin]] = src_nodes[node_idx];
- }
-
- tree_group = cl::sycl::malloc_shared(model.tree_info.size(), qu_);
- for (size_t tree_idx = 0; tree_idx < model.tree_info.size(); tree_idx++)
- tree_group[tree_idx] = model.tree_info[tree_idx];
-
- tree_beg_ = tree_begin;
- tree_end_ = tree_end;
- num_group = model.learner_model_param->num_output_group;
- }
-};
-
-float GetFvalue(int ridx, int fidx, EntryOneAPI* data, size_t* row_ptr, bool& is_missing) {
- // Binary search
- auto begin_ptr = data + row_ptr[ridx];
- auto end_ptr = data + row_ptr[ridx + 1];
- EntryOneAPI* previous_middle = nullptr;
- while (end_ptr != begin_ptr) {
- auto middle = begin_ptr + (end_ptr - begin_ptr) / 2;
- if (middle == previous_middle) {
- break;
- } else {
- previous_middle = middle;
- }
-
- if (middle->index == fidx) {
- is_missing = false;
- return middle->fvalue;
- } else if (middle->index < fidx) {
- begin_ptr = middle;
- } else {
- end_ptr = middle;
- }
- }
- is_missing = true;
- return 0.0;
-}
-
-float GetLeafWeight(int ridx, const DeviceNodeOneAPI* tree, EntryOneAPI* data, size_t* row_ptr) {
- DeviceNodeOneAPI n = tree[0];
- int node_id = 0;
- bool is_missing;
- while (!n.IsLeaf()) {
- float fvalue = GetFvalue(ridx, n.GetFidx(), data, row_ptr, is_missing);
- // Missing value
- if (is_missing) {
- n = tree[n.MissingIdx()];
- } else {
- if (fvalue < n.GetFvalue()) {
- node_id = n.left_child_idx;
- n = tree[n.left_child_idx];
- } else {
- node_id = n.right_child_idx;
- n = tree[n.right_child_idx];
- }
- }
- }
- return n.GetWeight();
-}
-
-class PredictorOneAPI : public Predictor {
- protected:
- void InitOutPredictions(const MetaInfo& info,
- HostDeviceVector* out_preds,
- const gbm::GBTreeModel& model) const {
- CHECK_NE(model.learner_model_param->num_output_group, 0);
- size_t n = model.learner_model_param->num_output_group * info.num_row_;
- const auto& base_margin = info.base_margin_.HostVector();
- out_preds->Resize(n);
- std::vector& out_preds_h = out_preds->HostVector();
- if (base_margin.size() == n) {
- CHECK_EQ(out_preds->Size(), n);
- std::copy(base_margin.begin(), base_margin.end(), out_preds_h.begin());
- } else {
- if (!base_margin.empty()) {
- std::ostringstream oss;
- oss << "Ignoring the base margin, since it has incorrect length. "
- << "The base margin must be an array of length ";
- if (model.learner_model_param->num_output_group > 1) {
- oss << "[num_class] * [number of data points], i.e. "
- << model.learner_model_param->num_output_group << " * " << info.num_row_
- << " = " << n << ". ";
- } else {
- oss << "[number of data points], i.e. " << info.num_row_ << ". ";
- }
- oss << "Instead, all data points will use "
- << "base_score = " << model.learner_model_param->base_score;
- LOG(WARNING) << oss.str();
- }
- std::fill(out_preds_h.begin(), out_preds_h.end(),
- model.learner_model_param->base_score);
- }
- }
-
- void DevicePredictInternal(DeviceMatrixOneAPI* dmat, HostDeviceVector* out_preds,
- const gbm::GBTreeModel& model, size_t tree_begin,
- size_t tree_end) {
- if (tree_end - tree_begin == 0) {
- return;
- }
- model_.Init(model, tree_begin, tree_end, qu_);
-
- auto& out_preds_vec = out_preds->HostVector();
-
- DeviceNodeOneAPI* nodes = model_.nodes;
- cl::sycl::buffer out_preds_buf(out_preds_vec.data(), out_preds_vec.size());
- size_t* tree_segments = model_.tree_segments;
- int* tree_group = model_.tree_group;
- size_t* row_ptr = dmat->row_ptr;
- EntryOneAPI* data = dmat->data;
- int num_features = dmat->p_mat->Info().num_col_;
- int num_rows = dmat->row_ptr_size - 1;
- int num_group = model.learner_model_param->num_output_group;
-
- qu_.submit([&](cl::sycl::handler& cgh) {
- auto out_predictions = out_preds_buf.get_access(cgh);
- cgh.parallel_for(cl::sycl::range<1>(num_rows), [=](cl::sycl::id<1> pid) {
- int global_idx = pid[0];
- if (global_idx >= num_rows) return;
- if (num_group == 1) {
- float sum = 0.0;
- for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
- const DeviceNodeOneAPI* tree = nodes + tree_segments[tree_idx - tree_begin];
- sum += GetLeafWeight(global_idx, tree, data, row_ptr);
- }
- out_predictions[global_idx] += sum;
- } else {
- for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
- const DeviceNodeOneAPI* tree = nodes + tree_segments[tree_idx - tree_begin];
- int out_prediction_idx = global_idx * num_group + tree_group[tree_idx];
- out_predictions[out_prediction_idx] += GetLeafWeight(global_idx, tree, data, row_ptr);
- }
- }
- });
- }).wait();
- }
-
- public:
- explicit PredictorOneAPI(Context const* generic_param) :
- Predictor::Predictor{generic_param}, cpu_predictor(Predictor::Create("cpu_predictor", generic_param)) {
- cl::sycl::default_selector selector;
- qu_ = cl::sycl::queue(selector);
- }
-
- // ntree_limit is a very problematic parameter, as it's ambiguous in the context of
- // multi-output and forest. Same problem exists for tree_begin
- void PredictBatch(DMatrix* dmat, PredictionCacheEntry* predts,
- const gbm::GBTreeModel& model, int tree_begin,
- uint32_t const ntree_limit = 0) override {
- if (this->device_matrix_cache_.find(dmat) ==
- this->device_matrix_cache_.end()) {
- this->device_matrix_cache_.emplace(
- dmat, std::unique_ptr(
- new DeviceMatrixOneAPI(dmat, qu_)));
- }
- DeviceMatrixOneAPI* device_matrix = device_matrix_cache_.find(dmat)->second.get();
-
- // tree_begin is not used, right now we just enforce it to be 0.
- CHECK_EQ(tree_begin, 0);
- auto* out_preds = &predts->predictions;
- CHECK_GE(predts->version, tree_begin);
- if (out_preds->Size() == 0 && dmat->Info().num_row_ != 0) {
- CHECK_EQ(predts->version, 0);
- }
- if (predts->version == 0) {
- // out_preds->Size() can be non-zero as it's initialized here before any tree is
- // built at the 0^th iterator.
- this->InitOutPredictions(dmat->Info(), out_preds, model);
- }
-
- uint32_t const output_groups = model.learner_model_param->num_output_group;
- CHECK_NE(output_groups, 0);
- // Right now we just assume ntree_limit provided by users means number of tree layers
- // in the context of multi-output model
- uint32_t real_ntree_limit = ntree_limit * output_groups;
- if (real_ntree_limit == 0 || real_ntree_limit > model.trees.size()) {
- real_ntree_limit = static_cast(model.trees.size());
- }
-
- uint32_t const end_version = (tree_begin + real_ntree_limit) / output_groups;
- // When users have provided ntree_limit, end_version can be lesser, cache is violated
- if (predts->version > end_version) {
- CHECK_NE(ntree_limit, 0);
- this->InitOutPredictions(dmat->Info(), out_preds, model);
- predts->version = 0;
- }
- uint32_t const beg_version = predts->version;
- CHECK_LE(beg_version, end_version);
-
- if (beg_version < end_version) {
- DevicePredictInternal(device_matrix, out_preds, model,
- beg_version * output_groups,
- end_version * output_groups);
- }
-
- // delta means {size of forest} * {number of newly accumulated layers}
- uint32_t delta = end_version - beg_version;
- CHECK_LE(delta, model.trees.size());
- predts->Update(delta);
-
- CHECK(out_preds->Size() == output_groups * dmat->Info().num_row_ ||
- out_preds->Size() == dmat->Info().num_row_);
- }
-
- void InplacePredict(dmlc::any const &x, const gbm::GBTreeModel &model,
- float missing, PredictionCacheEntry *out_preds,
- uint32_t tree_begin, unsigned tree_end) const override {
- cpu_predictor->InplacePredict(x, model, missing, out_preds, tree_begin, tree_end);
- }
-
- void PredictInstance(const SparsePage::Inst& inst,
- std::vector* out_preds,
- const gbm::GBTreeModel& model, unsigned ntree_limit) override {
- cpu_predictor->PredictInstance(inst, out_preds, model, ntree_limit);
- }
-
- void PredictLeaf(DMatrix* p_fmat, std::vector* out_preds,
- const gbm::GBTreeModel& model, unsigned ntree_limit) override {
- cpu_predictor->PredictLeaf(p_fmat, out_preds, model, ntree_limit);
- }
-
- void PredictContribution(DMatrix* p_fmat, std::vector* out_contribs,
- const gbm::GBTreeModel& model, uint32_t ntree_limit,
- std::vector* tree_weights,
- bool approximate, int condition,
- unsigned condition_feature) override {
- cpu_predictor->PredictContribution(p_fmat, out_contribs, model, ntree_limit, tree_weights, approximate, condition, condition_feature);
- }
-
- void PredictInteractionContributions(DMatrix* p_fmat, std::vector* out_contribs,
- const gbm::GBTreeModel& model, unsigned ntree_limit,
- std::vector* tree_weights,
- bool approximate) override {
- cpu_predictor->PredictInteractionContributions(p_fmat, out_contribs, model, ntree_limit, tree_weights, approximate);
- }
-
- private:
- cl::sycl::queue qu_;
- DeviceModelOneAPI model_;
-
- std::mutex lock_;
- std::unique_ptr cpu_predictor;
-
- std::unordered_map>
- device_matrix_cache_;
-};
-
-XGBOOST_REGISTER_PREDICTOR(PredictorOneAPI, "oneapi_predictor")
-.describe("Make predictions using DPC++.")
-.set_body([](Context const* generic_param) {
- return new PredictorOneAPI(generic_param);
- });
-} // namespace predictor
-} // namespace xgboost
+/*!
+ * Copyright by Contributors 2017-2020
+ */
+#include // for any
+#include
+#include
+#include
+
+#include "../../src/common/math.h"
+#include "../../src/data/adapter.h"
+#include "../../src/gbm/gbtree_model.h"
+#include "CL/sycl.hpp"
+#include "xgboost/base.h"
+#include "xgboost/data.h"
+#include "xgboost/host_device_vector.h"
+#include "xgboost/logging.h"
+#include "xgboost/predictor.h"
+#include "xgboost/tree_model.h"
+#include "xgboost/tree_updater.h"
+
+namespace xgboost {
+namespace predictor {
+
+DMLC_REGISTRY_FILE_TAG(predictor_oneapi);
+
+/*! \brief Element from a sparse vector */
+struct EntryOneAPI {
+ /*! \brief feature index */
+ bst_feature_t index;
+ /*! \brief feature value */
+ bst_float fvalue;
+ /*! \brief default constructor */
+ EntryOneAPI() = default;
+ /*!
+ * \brief constructor with index and value
+ * \param index The feature or row index.
+ * \param fvalue The feature value.
+ */
+ EntryOneAPI(bst_feature_t index, bst_float fvalue) : index(index), fvalue(fvalue) {}
+
+ EntryOneAPI(const Entry& entry) : index(entry.index), fvalue(entry.fvalue) {}
+
+ /*! \brief reversely compare feature values */
+ inline static bool CmpValue(const EntryOneAPI& a, const EntryOneAPI& b) {
+ return a.fvalue < b.fvalue;
+ }
+ inline bool operator==(const EntryOneAPI& other) const {
+ return (this->index == other.index && this->fvalue == other.fvalue);
+ }
+};
+
+struct DeviceMatrixOneAPI {
+ DMatrix* p_mat; // Pointer to the original matrix on the host
+ cl::sycl::queue qu_;
+ size_t* row_ptr;
+ size_t row_ptr_size;
+ EntryOneAPI* data;
+
+ DeviceMatrixOneAPI(DMatrix* dmat, cl::sycl::queue qu) : p_mat(dmat), qu_(qu) {
+ size_t num_row = 0;
+ size_t num_nonzero = 0;
+ for (auto &batch : dmat->GetBatches()) {
+ const auto& data_vec = batch.data.HostVector();
+ const auto& offset_vec = batch.offset.HostVector();
+ num_nonzero += data_vec.size();
+ num_row += batch.Size();
+ }
+
+ row_ptr = cl::sycl::malloc_shared(num_row + 1, qu_);
+ data = cl::sycl::malloc_shared(num_nonzero, qu_);
+
+ size_t data_offset = 0;
+ for (auto &batch : dmat->GetBatches()) {
+ const auto& data_vec = batch.data.HostVector();
+ const auto& offset_vec = batch.offset.HostVector();
+ size_t batch_size = batch.Size();
+ if (batch_size > 0) {
+ std::copy(offset_vec.data(), offset_vec.data() + batch_size,
+ row_ptr + batch.base_rowid);
+ if (batch.base_rowid > 0) {
+ for(size_t i = 0; i < batch_size; i++)
+ row_ptr[i + batch.base_rowid] += batch.base_rowid;
+ }
+ std::copy(data_vec.data(), data_vec.data() + offset_vec[batch_size],
+ data + data_offset);
+ data_offset += offset_vec[batch_size];
+ }
+ }
+ row_ptr[num_row] = data_offset;
+ row_ptr_size = num_row + 1;
+ }
+
+ ~DeviceMatrixOneAPI() {
+ if (row_ptr) {
+ cl::sycl::free(row_ptr, qu_);
+ }
+ if (data) {
+ cl::sycl::free(data, qu_);
+ }
+ }
+};
+
+struct DeviceNodeOneAPI {
+ DeviceNodeOneAPI()
+ : fidx(-1), left_child_idx(-1), right_child_idx(-1) {}
+
+ union NodeValue {
+ float leaf_weight;
+ float fvalue;
+ };
+
+ int fidx;
+ int left_child_idx;
+ int right_child_idx;
+ NodeValue val;
+
+ DeviceNodeOneAPI(const RegTree::Node& n) { // NOLINT
+ this->left_child_idx = n.LeftChild();
+ this->right_child_idx = n.RightChild();
+ this->fidx = n.SplitIndex();
+ if (n.DefaultLeft()) {
+ fidx |= (1U << 31);
+ }
+
+ if (n.IsLeaf()) {
+ this->val.leaf_weight = n.LeafValue();
+ } else {
+ this->val.fvalue = n.SplitCond();
+ }
+ }
+
+ bool IsLeaf() const { return left_child_idx == -1; }
+
+ int GetFidx() const { return fidx & ((1U << 31) - 1U); }
+
+ bool MissingLeft() const { return (fidx >> 31) != 0; }
+
+ int MissingIdx() const {
+ if (MissingLeft()) {
+ return this->left_child_idx;
+ } else {
+ return this->right_child_idx;
+ }
+ }
+
+ float GetFvalue() const { return val.fvalue; }
+
+ float GetWeight() const { return val.leaf_weight; }
+};
+
+class DeviceModelOneAPI {
+ public:
+ cl::sycl::queue qu_;
+ DeviceNodeOneAPI* nodes;
+ size_t* tree_segments;
+ int* tree_group;
+ size_t tree_beg_;
+ size_t tree_end_;
+ int num_group;
+
+ DeviceModelOneAPI() : nodes(nullptr), tree_segments(nullptr), tree_group(nullptr) {}
+
+ ~DeviceModelOneAPI() {
+ Reset();
+ }
+
+ void Reset() {
+ if (nodes)
+ cl::sycl::free(nodes, qu_);
+ if (tree_segments)
+ cl::sycl::free(tree_segments, qu_);
+ if (tree_group)
+ cl::sycl::free(tree_group, qu_);
+ }
+
+ void Init(const gbm::GBTreeModel& model, size_t tree_begin, size_t tree_end, cl::sycl::queue qu) {
+ qu_ = qu;
+ CHECK_EQ(model.param.size_leaf_vector, 0);
+ Reset();
+
+ tree_segments = cl::sycl::malloc_shared((tree_end - tree_begin) + 1, qu_);
+ int sum = 0;
+ tree_segments[0] = sum;
+ for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
+ sum += model.trees[tree_idx]->GetNodes().size();
+ tree_segments[tree_idx - tree_begin + 1] = sum;
+ }
+
+ nodes = cl::sycl::malloc_shared(sum, qu_);
+ for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
+ auto& src_nodes = model.trees[tree_idx]->GetNodes();
+ for (size_t node_idx = 0; node_idx < src_nodes.size(); node_idx++)
+ nodes[node_idx + tree_segments[tree_idx - tree_begin]] = src_nodes[node_idx];
+ }
+
+ tree_group = cl::sycl::malloc_shared(model.tree_info.size(), qu_);
+ for (size_t tree_idx = 0; tree_idx < model.tree_info.size(); tree_idx++)
+ tree_group[tree_idx] = model.tree_info[tree_idx];
+
+ tree_beg_ = tree_begin;
+ tree_end_ = tree_end;
+ num_group = model.learner_model_param->num_output_group;
+ }
+};
+
+float GetFvalue(int ridx, int fidx, EntryOneAPI* data, size_t* row_ptr, bool& is_missing) {
+ // Binary search
+ auto begin_ptr = data + row_ptr[ridx];
+ auto end_ptr = data + row_ptr[ridx + 1];
+ EntryOneAPI* previous_middle = nullptr;
+ while (end_ptr != begin_ptr) {
+ auto middle = begin_ptr + (end_ptr - begin_ptr) / 2;
+ if (middle == previous_middle) {
+ break;
+ } else {
+ previous_middle = middle;
+ }
+
+ if (middle->index == fidx) {
+ is_missing = false;
+ return middle->fvalue;
+ } else if (middle->index < fidx) {
+ begin_ptr = middle;
+ } else {
+ end_ptr = middle;
+ }
+ }
+ is_missing = true;
+ return 0.0;
+}
+
+float GetLeafWeight(int ridx, const DeviceNodeOneAPI* tree, EntryOneAPI* data, size_t* row_ptr) {
+ DeviceNodeOneAPI n = tree[0];
+ int node_id = 0;
+ bool is_missing;
+ while (!n.IsLeaf()) {
+ float fvalue = GetFvalue(ridx, n.GetFidx(), data, row_ptr, is_missing);
+ // Missing value
+ if (is_missing) {
+ n = tree[n.MissingIdx()];
+ } else {
+ if (fvalue < n.GetFvalue()) {
+ node_id = n.left_child_idx;
+ n = tree[n.left_child_idx];
+ } else {
+ node_id = n.right_child_idx;
+ n = tree[n.right_child_idx];
+ }
+ }
+ }
+ return n.GetWeight();
+}
+
+class PredictorOneAPI : public Predictor {
+ protected:
+ void InitOutPredictions(const MetaInfo& info,
+ HostDeviceVector* out_preds,
+ const gbm::GBTreeModel& model) const {
+ CHECK_NE(model.learner_model_param->num_output_group, 0);
+ size_t n = model.learner_model_param->num_output_group * info.num_row_;
+ const auto& base_margin = info.base_margin_.HostVector();
+ out_preds->Resize(n);
+ std::vector& out_preds_h = out_preds->HostVector();
+ if (base_margin.size() == n) {
+ CHECK_EQ(out_preds->Size(), n);
+ std::copy(base_margin.begin(), base_margin.end(), out_preds_h.begin());
+ } else {
+ if (!base_margin.empty()) {
+ std::ostringstream oss;
+ oss << "Ignoring the base margin, since it has incorrect length. "
+ << "The base margin must be an array of length ";
+ if (model.learner_model_param->num_output_group > 1) {
+ oss << "[num_class] * [number of data points], i.e. "
+ << model.learner_model_param->num_output_group << " * " << info.num_row_
+ << " = " << n << ". ";
+ } else {
+ oss << "[number of data points], i.e. " << info.num_row_ << ". ";
+ }
+ oss << "Instead, all data points will use "
+ << "base_score = " << model.learner_model_param->base_score;
+ LOG(WARNING) << oss.str();
+ }
+ std::fill(out_preds_h.begin(), out_preds_h.end(),
+ model.learner_model_param->base_score);
+ }
+ }
+
+ void DevicePredictInternal(DeviceMatrixOneAPI* dmat, HostDeviceVector* out_preds,
+ const gbm::GBTreeModel& model, size_t tree_begin,
+ size_t tree_end) {
+ if (tree_end - tree_begin == 0) {
+ return;
+ }
+ model_.Init(model, tree_begin, tree_end, qu_);
+
+ auto& out_preds_vec = out_preds->HostVector();
+
+ DeviceNodeOneAPI* nodes = model_.nodes;
+ cl::sycl::buffer out_preds_buf(out_preds_vec.data(), out_preds_vec.size());
+ size_t* tree_segments = model_.tree_segments;
+ int* tree_group = model_.tree_group;
+ size_t* row_ptr = dmat->row_ptr;
+ EntryOneAPI* data = dmat->data;
+ int num_features = dmat->p_mat->Info().num_col_;
+ int num_rows = dmat->row_ptr_size - 1;
+ int num_group = model.learner_model_param->num_output_group;
+
+ qu_.submit([&](cl::sycl::handler& cgh) {
+ auto out_predictions = out_preds_buf.get_access(cgh);
+ cgh.parallel_for(cl::sycl::range<1>(num_rows), [=](cl::sycl::id<1> pid) {
+ int global_idx = pid[0];
+ if (global_idx >= num_rows) return;
+ if (num_group == 1) {
+ float sum = 0.0;
+ for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
+ const DeviceNodeOneAPI* tree = nodes + tree_segments[tree_idx - tree_begin];
+ sum += GetLeafWeight(global_idx, tree, data, row_ptr);
+ }
+ out_predictions[global_idx] += sum;
+ } else {
+ for (int tree_idx = tree_begin; tree_idx < tree_end; tree_idx++) {
+ const DeviceNodeOneAPI* tree = nodes + tree_segments[tree_idx - tree_begin];
+ int out_prediction_idx = global_idx * num_group + tree_group[tree_idx];
+ out_predictions[out_prediction_idx] += GetLeafWeight(global_idx, tree, data, row_ptr);
+ }
+ }
+ });
+ }).wait();
+ }
+
+ public:
+ explicit PredictorOneAPI(Context const* generic_param) :
+ Predictor::Predictor{generic_param}, cpu_predictor(Predictor::Create("cpu_predictor", generic_param)) {
+ cl::sycl::default_selector selector;
+ qu_ = cl::sycl::queue(selector);
+ }
+
+ // ntree_limit is a very problematic parameter, as it's ambiguous in the context of
+ // multi-output and forest. Same problem exists for tree_begin
+ void PredictBatch(DMatrix* dmat, PredictionCacheEntry* predts,
+ const gbm::GBTreeModel& model, int tree_begin,
+ uint32_t const ntree_limit = 0) override {
+ if (this->device_matrix_cache_.find(dmat) ==
+ this->device_matrix_cache_.end()) {
+ this->device_matrix_cache_.emplace(
+ dmat, std::unique_ptr(
+ new DeviceMatrixOneAPI(dmat, qu_)));
+ }
+ DeviceMatrixOneAPI* device_matrix = device_matrix_cache_.find(dmat)->second.get();
+
+ // tree_begin is not used, right now we just enforce it to be 0.
+ CHECK_EQ(tree_begin, 0);
+ auto* out_preds = &predts->predictions;
+ CHECK_GE(predts->version, tree_begin);
+ if (out_preds->Size() == 0 && dmat->Info().num_row_ != 0) {
+ CHECK_EQ(predts->version, 0);
+ }
+ if (predts->version == 0) {
+ // out_preds->Size() can be non-zero as it's initialized here before any tree is
+ // built at the 0^th iterator.
+ this->InitOutPredictions(dmat->Info(), out_preds, model);
+ }
+
+ uint32_t const output_groups = model.learner_model_param->num_output_group;
+ CHECK_NE(output_groups, 0);
+ // Right now we just assume ntree_limit provided by users means number of tree layers
+ // in the context of multi-output model
+ uint32_t real_ntree_limit = ntree_limit * output_groups;
+ if (real_ntree_limit == 0 || real_ntree_limit > model.trees.size()) {
+ real_ntree_limit = static_cast(model.trees.size());
+ }
+
+ uint32_t const end_version = (tree_begin + real_ntree_limit) / output_groups;
+ // When users have provided ntree_limit, end_version can be lesser, cache is violated
+ if (predts->version > end_version) {
+ CHECK_NE(ntree_limit, 0);
+ this->InitOutPredictions(dmat->Info(), out_preds, model);
+ predts->version = 0;
+ }
+ uint32_t const beg_version = predts->version;
+ CHECK_LE(beg_version, end_version);
+
+ if (beg_version < end_version) {
+ DevicePredictInternal(device_matrix, out_preds, model,
+ beg_version * output_groups,
+ end_version * output_groups);
+ }
+
+ // delta means {size of forest} * {number of newly accumulated layers}
+ uint32_t delta = end_version - beg_version;
+ CHECK_LE(delta, model.trees.size());
+ predts->Update(delta);
+
+ CHECK(out_preds->Size() == output_groups * dmat->Info().num_row_ ||
+ out_preds->Size() == dmat->Info().num_row_);
+ }
+
+ void InplacePredict(std::any const& x, const gbm::GBTreeModel& model, float missing,
+ PredictionCacheEntry* out_preds, uint32_t tree_begin,
+ unsigned tree_end) const override {
+ cpu_predictor->InplacePredict(x, model, missing, out_preds, tree_begin, tree_end);
+ }
+
+ void PredictInstance(const SparsePage::Inst& inst,
+ std::vector* out_preds,
+ const gbm::GBTreeModel& model, unsigned ntree_limit) override {
+ cpu_predictor->PredictInstance(inst, out_preds, model, ntree_limit);
+ }
+
+ void PredictLeaf(DMatrix* p_fmat, std::vector* out_preds,
+ const gbm::GBTreeModel& model, unsigned ntree_limit) override {
+ cpu_predictor->PredictLeaf(p_fmat, out_preds, model, ntree_limit);
+ }
+
+ void PredictContribution(DMatrix* p_fmat, std::vector* out_contribs,
+ const gbm::GBTreeModel& model, uint32_t ntree_limit,
+ std::vector* tree_weights,
+ bool approximate, int condition,
+ unsigned condition_feature) override {
+ cpu_predictor->PredictContribution(p_fmat, out_contribs, model, ntree_limit, tree_weights, approximate, condition, condition_feature);
+ }
+
+ void PredictInteractionContributions(DMatrix* p_fmat, std::vector* out_contribs,
+ const gbm::GBTreeModel& model, unsigned ntree_limit,
+ std::vector* tree_weights,
+ bool approximate) override {
+ cpu_predictor->PredictInteractionContributions(p_fmat, out_contribs, model, ntree_limit, tree_weights, approximate);
+ }
+
+ private:
+ cl::sycl::queue qu_;
+ DeviceModelOneAPI model_;
+
+ std::mutex lock_;
+ std::unique_ptr cpu_predictor;
+
+ std::unordered_map>
+ device_matrix_cache_;
+};
+
+XGBOOST_REGISTER_PREDICTOR(PredictorOneAPI, "oneapi_predictor")
+.describe("Make predictions using DPC++.")
+.set_body([](Context const* generic_param) {
+ return new PredictorOneAPI(generic_param);
+ });
+} // namespace predictor
+} // namespace xgboost
diff --git a/plugin/updater_oneapi/regression_loss_oneapi.h b/plugin/updater_oneapi/regression_loss_oneapi.h
index 4759f5c3f..b0299ff7f 100755
--- a/plugin/updater_oneapi/regression_loss_oneapi.h
+++ b/plugin/updater_oneapi/regression_loss_oneapi.h
@@ -1,145 +1,145 @@
-/*!
- * Copyright 2017-2020 XGBoost contributors
- */
-#ifndef XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
-#define XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
-
-#include
-#include
-#include
-
-#include "CL/sycl.hpp"
-
-namespace xgboost {
-namespace obj {
-
-/*!
- * \brief calculate the sigmoid of the input.
- * \param x input parameter
- * \return the transformed value.
- */
-inline float SigmoidOneAPI(float x) {
- return 1.0f / (1.0f + cl::sycl::exp(-x));
-}
-
-// common regressions
-// linear regression
-struct LinearSquareLossOneAPI {
- static bst_float PredTransform(bst_float x) { return x; }
- static bool CheckLabel(bst_float x) { return true; }
- static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
- return predt - label;
- }
- static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
- return 1.0f;
- }
- static bst_float ProbToMargin(bst_float base_score) { return base_score; }
- static const char* LabelErrorMsg() { return ""; }
- static const char* DefaultEvalMetric() { return "rmse"; }
-
- static const char* Name() { return "reg:squarederror_oneapi"; }
-};
-
-// TODO: DPC++ does not fully support std math inside offloaded kernels
-struct SquaredLogErrorOneAPI {
- static bst_float PredTransform(bst_float x) { return x; }
- static bool CheckLabel(bst_float label) {
- return label > -1;
- }
- static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
- predt = std::max(predt, (bst_float)(-1 + 1e-6)); // ensure correct value for log1p
- return (cl::sycl::log1p(predt) - cl::sycl::log1p(label)) / (predt + 1);
- }
- static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
- predt = std::max(predt, (bst_float)(-1 + 1e-6));
- float res = (-cl::sycl::log1p(predt) + cl::sycl::log1p(label) + 1) /
- cl::sycl::pow(predt + 1, (bst_float)2);
- res = std::max(res, (bst_float)1e-6f);
- return res;
- }
- static bst_float ProbToMargin(bst_float base_score) { return base_score; }
- static const char* LabelErrorMsg() {
- return "label must be greater than -1 for rmsle so that log(label + 1) can be valid.";
- }
- static const char* DefaultEvalMetric() { return "rmsle"; }
-
- static const char* Name() { return "reg:squaredlogerror_oneapi"; }
-};
-
-// logistic loss for probability regression task
-struct LogisticRegressionOneAPI {
- // duplication is necessary, as __device__ specifier
- // cannot be made conditional on template parameter
- static bst_float PredTransform(bst_float x) { return SigmoidOneAPI(x); }
- static bool CheckLabel(bst_float x) { return x >= 0.0f && x <= 1.0f; }
- static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
- return predt - label;
- }
- static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
- const bst_float eps = 1e-16f;
- return std::max(predt * (1.0f - predt), eps);
- }
- template
- static T PredTransform(T x) { return SigmoidOneAPI(x); }
- template
- static T FirstOrderGradient(T predt, T label) { return predt - label; }
- template
- static T SecondOrderGradient(T predt, T label) {
- const T eps = T(1e-16f);
- return std::max(predt * (T(1.0f) - predt), eps);
- }
- static bst_float ProbToMargin(bst_float base_score) {
- CHECK(base_score > 0.0f && base_score < 1.0f)
- << "base_score must be in (0,1) for logistic loss, got: " << base_score;
- return -logf(1.0f / base_score - 1.0f);
- }
- static const char* LabelErrorMsg() {
- return "label must be in [0,1] for logistic regression";
- }
- static const char* DefaultEvalMetric() { return "rmse"; }
-
- static const char* Name() { return "reg:logistic_oneapi"; }
-};
-
-// logistic loss for binary classification task
-struct LogisticClassificationOneAPI : public LogisticRegressionOneAPI {
- static const char* DefaultEvalMetric() { return "logloss"; }
- static const char* Name() { return "binary:logistic_oneapi"; }
-};
-
-// logistic loss, but predict un-transformed margin
-struct LogisticRawOneAPI : public LogisticRegressionOneAPI {
- // duplication is necessary, as __device__ specifier
- // cannot be made conditional on template parameter
- static bst_float PredTransform(bst_float x) { return x; }
- static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
- predt = SigmoidOneAPI(predt);
- return predt - label;
- }
- static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
- const bst_float eps = 1e-16f;
- predt = SigmoidOneAPI(predt);
- return std::max(predt * (1.0f - predt), eps);
- }
- template
- static T PredTransform(T x) { return x; }
- template
- static T FirstOrderGradient(T predt, T label) {
- predt = SigmoidOneAPI(predt);
- return predt - label;
- }
- template
- static T SecondOrderGradient(T predt, T label) {
- const T eps = T(1e-16f);
- predt = SigmoidOneAPI(predt);
- return std::max(predt * (T(1.0f) - predt), eps);
- }
- static const char* DefaultEvalMetric() { return "logloss"; }
-
- static const char* Name() { return "binary:logitraw_oneapi"; }
-};
-
-} // namespace obj
-} // namespace xgboost
-
-#endif // XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
+/*!
+ * Copyright 2017-2020 XGBoost contributors
+ */
+#ifndef XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
+#define XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
+
+#include
+#include
+#include
+
+#include "CL/sycl.hpp"
+
+namespace xgboost {
+namespace obj {
+
+/*!
+ * \brief calculate the sigmoid of the input.
+ * \param x input parameter
+ * \return the transformed value.
+ */
+inline float SigmoidOneAPI(float x) {
+ return 1.0f / (1.0f + cl::sycl::exp(-x));
+}
+
+// common regressions
+// linear regression
+struct LinearSquareLossOneAPI {
+ static bst_float PredTransform(bst_float x) { return x; }
+ static bool CheckLabel(bst_float x) { return true; }
+ static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
+ return predt - label;
+ }
+ static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
+ return 1.0f;
+ }
+ static bst_float ProbToMargin(bst_float base_score) { return base_score; }
+ static const char* LabelErrorMsg() { return ""; }
+ static const char* DefaultEvalMetric() { return "rmse"; }
+
+ static const char* Name() { return "reg:squarederror_oneapi"; }
+};
+
+// TODO: DPC++ does not fully support std math inside offloaded kernels
+struct SquaredLogErrorOneAPI {
+ static bst_float PredTransform(bst_float x) { return x; }
+ static bool CheckLabel(bst_float label) {
+ return label > -1;
+ }
+ static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
+ predt = std::max(predt, (bst_float)(-1 + 1e-6)); // ensure correct value for log1p
+ return (cl::sycl::log1p(predt) - cl::sycl::log1p(label)) / (predt + 1);
+ }
+ static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
+ predt = std::max(predt, (bst_float)(-1 + 1e-6));
+ float res = (-cl::sycl::log1p(predt) + cl::sycl::log1p(label) + 1) /
+ cl::sycl::pow(predt + 1, (bst_float)2);
+ res = std::max(res, (bst_float)1e-6f);
+ return res;
+ }
+ static bst_float ProbToMargin(bst_float base_score) { return base_score; }
+ static const char* LabelErrorMsg() {
+ return "label must be greater than -1 for rmsle so that log(label + 1) can be valid.";
+ }
+ static const char* DefaultEvalMetric() { return "rmsle"; }
+
+ static const char* Name() { return "reg:squaredlogerror_oneapi"; }
+};
+
+// logistic loss for probability regression task
+struct LogisticRegressionOneAPI {
+ // duplication is necessary, as __device__ specifier
+ // cannot be made conditional on template parameter
+ static bst_float PredTransform(bst_float x) { return SigmoidOneAPI(x); }
+ static bool CheckLabel(bst_float x) { return x >= 0.0f && x <= 1.0f; }
+ static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
+ return predt - label;
+ }
+ static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
+ const bst_float eps = 1e-16f;
+ return std::max(predt * (1.0f - predt), eps);
+ }
+ template
+ static T PredTransform(T x) { return SigmoidOneAPI(x); }
+ template
+ static T FirstOrderGradient(T predt, T label) { return predt - label; }
+ template
+ static T SecondOrderGradient(T predt, T label) {
+ const T eps = T(1e-16f);
+ return std::max(predt * (T(1.0f) - predt), eps);
+ }
+ static bst_float ProbToMargin(bst_float base_score) {
+ CHECK(base_score > 0.0f && base_score < 1.0f)
+ << "base_score must be in (0,1) for logistic loss, got: " << base_score;
+ return -logf(1.0f / base_score - 1.0f);
+ }
+ static const char* LabelErrorMsg() {
+ return "label must be in [0,1] for logistic regression";
+ }
+ static const char* DefaultEvalMetric() { return "rmse"; }
+
+ static const char* Name() { return "reg:logistic_oneapi"; }
+};
+
+// logistic loss for binary classification task
+struct LogisticClassificationOneAPI : public LogisticRegressionOneAPI {
+ static const char* DefaultEvalMetric() { return "logloss"; }
+ static const char* Name() { return "binary:logistic_oneapi"; }
+};
+
+// logistic loss, but predict un-transformed margin
+struct LogisticRawOneAPI : public LogisticRegressionOneAPI {
+ // duplication is necessary, as __device__ specifier
+ // cannot be made conditional on template parameter
+ static bst_float PredTransform(bst_float x) { return x; }
+ static bst_float FirstOrderGradient(bst_float predt, bst_float label) {
+ predt = SigmoidOneAPI(predt);
+ return predt - label;
+ }
+ static bst_float SecondOrderGradient(bst_float predt, bst_float label) {
+ const bst_float eps = 1e-16f;
+ predt = SigmoidOneAPI(predt);
+ return std::max(predt * (1.0f - predt), eps);
+ }
+ template
+ static T PredTransform(T x) { return x; }
+ template
+ static T FirstOrderGradient(T predt, T label) {
+ predt = SigmoidOneAPI(predt);
+ return predt - label;
+ }
+ template
+ static T SecondOrderGradient(T predt, T label) {
+ const T eps = T(1e-16f);
+ predt = SigmoidOneAPI(predt);
+ return std::max(predt * (T(1.0f) - predt), eps);
+ }
+ static const char* DefaultEvalMetric() { return "logloss"; }
+
+ static const char* Name() { return "binary:logitraw_oneapi"; }
+};
+
+} // namespace obj
+} // namespace xgboost
+
+#endif // XGBOOST_OBJECTIVE_REGRESSION_LOSS_ONEAPI_H_
diff --git a/plugin/updater_oneapi/regression_obj_oneapi.cc b/plugin/updater_oneapi/regression_obj_oneapi.cc
index 4a1bd7229..3ee5741e7 100755
--- a/plugin/updater_oneapi/regression_obj_oneapi.cc
+++ b/plugin/updater_oneapi/regression_obj_oneapi.cc
@@ -1,182 +1,182 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "xgboost/host_device_vector.h"
-#include "xgboost/json.h"
-#include "xgboost/parameter.h"
-#include "xgboost/span.h"
-
-#include "../../src/common/transform.h"
-#include "../../src/common/common.h"
-#include "./regression_loss_oneapi.h"
-
-#include "CL/sycl.hpp"
-
-namespace xgboost {
-namespace obj {
-
-DMLC_REGISTRY_FILE_TAG(regression_obj_oneapi);
-
-struct RegLossParamOneAPI : public XGBoostParameter {
- float scale_pos_weight;
- // declare parameters
- DMLC_DECLARE_PARAMETER(RegLossParamOneAPI) {
- DMLC_DECLARE_FIELD(scale_pos_weight).set_default(1.0f).set_lower_bound(0.0f)
- .describe("Scale the weight of positive examples by this factor");
- }
-};
-
-template
-class RegLossObjOneAPI : public ObjFunction {
- protected:
- HostDeviceVector label_correct_;
-
- public:
- RegLossObjOneAPI() = default;
-
- void Configure(const std::vector >& args) override {
- param_.UpdateAllowUnknown(args);
-
- cl::sycl::default_selector selector;
- qu_ = cl::sycl::queue(selector);
- }
-
- void GetGradient(const HostDeviceVector& preds,
- const MetaInfo &info,
- int iter,
- HostDeviceVector* out_gpair) override {
- if (info.labels_.Size() == 0U) {
- LOG(WARNING) << "Label set is empty.";
- }
- CHECK_EQ(preds.Size(), info.labels_.Size())
- << " " << "labels are not correctly provided"
- << "preds.size=" << preds.Size() << ", label.size=" << info.labels_.Size() << ", "
- << "Loss: " << Loss::Name();
-
- size_t const ndata = preds.Size();
- out_gpair->Resize(ndata);
-
- // TODO: add label_correct check
- label_correct_.Resize(1);
- label_correct_.Fill(1);
-
- bool is_null_weight = info.weights_.Size() == 0;
-
- cl::sycl::buffer preds_buf(preds.HostPointer(), preds.Size());
- cl::sycl::buffer labels_buf(info.labels_.HostPointer(), info.labels_.Size());
- cl::sycl::buffer out_gpair_buf(out_gpair->HostPointer(), out_gpair->Size());
- cl::sycl::buffer weights_buf(is_null_weight ? NULL : info.weights_.HostPointer(),
- is_null_weight ? 1 : info.weights_.Size());
-
- cl::sycl::buffer additional_input_buf(1);
- {
- auto additional_input_acc = additional_input_buf.get_access();
- additional_input_acc[0] = 1; // Fill the label_correct flag
- }
-
- auto scale_pos_weight = param_.scale_pos_weight;
- if (!is_null_weight) {
- CHECK_EQ(info.weights_.Size(), ndata)
- << "Number of weights should be equal to number of data points.";
- }
-
- qu_.submit([&](cl::sycl::handler& cgh) {
- auto preds_acc = preds_buf.get_access(cgh);
- auto labels_acc = labels_buf.get_access(cgh);
- auto weights_acc = weights_buf.get_access(cgh);
- auto out_gpair_acc = out_gpair_buf.get_access(cgh);
- auto additional_input_acc = additional_input_buf.get_access(cgh);
- cgh.parallel_for<>(cl::sycl::range<1>(ndata), [=](cl::sycl::id<1> pid) {
- int idx = pid[0];
- bst_float p = Loss::PredTransform(preds_acc[idx]);
- bst_float w = is_null_weight ? 1.0f : weights_acc[idx];
- bst_float label = labels_acc[idx];
- 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_acc[0] = 0;
- }
- out_gpair_acc[idx] = GradientPair(Loss::FirstOrderGradient(p, label) * w,
- Loss::SecondOrderGradient(p, label) * w);
- });
- }).wait();
-
- int flag = 1;
- {
- auto additional_input_acc = additional_input_buf.get_access();
- flag = additional_input_acc[0];
- }
-
- if (flag == 0) {
- LOG(FATAL) << Loss::LabelErrorMsg();
- }
-
- }
-
- public:
- const char* DefaultEvalMetric() const override {
- return Loss::DefaultEvalMetric();
- }
-
- void PredTransform(HostDeviceVector *io_preds) override {
- size_t const ndata = io_preds->Size();
-
- cl::sycl::buffer io_preds_buf(io_preds->HostPointer(), io_preds->Size());
-
- qu_.submit([&](cl::sycl::handler& cgh) {
- auto io_preds_acc = io_preds_buf.get_access(cgh);
- cgh.parallel_for<>(cl::sycl::range<1>(ndata), [=](cl::sycl::id<1> pid) {
- int idx = pid[0];
- io_preds_acc[idx] = Loss::PredTransform(io_preds_acc[idx]);
- });
- }).wait();
- }
-
- float ProbToMargin(float base_score) const override {
- return Loss::ProbToMargin(base_score);
- }
-
- void SaveConfig(Json* p_out) const override {
- auto& out = *p_out;
- out["name"] = String(Loss::Name());
- out["reg_loss_param"] = ToJson(param_);
- }
-
- void LoadConfig(Json const& in) override {
- FromJson(in["reg_loss_param"], ¶m_);
- }
-
- protected:
- RegLossParamOneAPI param_;
-
- cl::sycl::queue qu_;
-};
-
-// register the objective functions
-DMLC_REGISTER_PARAMETER(RegLossParamOneAPI);
-
-// TODO: Find a better way to dispatch names of DPC++ kernels with various template parameters of loss function
-XGBOOST_REGISTER_OBJECTIVE(SquaredLossRegressionOneAPI, LinearSquareLossOneAPI::Name())
-.describe("Regression with squared error with DPC++ backend.")
-.set_body([]() { return new RegLossObjOneAPI(); });
-XGBOOST_REGISTER_OBJECTIVE(SquareLogErrorOneAPI, SquaredLogErrorOneAPI::Name())
-.describe("Regression with root mean squared logarithmic error with DPC++ backend.")
-.set_body([]() { return new RegLossObjOneAPI(); });
-XGBOOST_REGISTER_OBJECTIVE(LogisticRegressionOneAPI, LogisticRegressionOneAPI::Name())
-.describe("Logistic regression for probability regression task with DPC++ backend.")
-.set_body([]() { return new RegLossObjOneAPI(); });
-XGBOOST_REGISTER_OBJECTIVE(LogisticClassificationOneAPI, LogisticClassificationOneAPI::Name())
-.describe("Logistic regression for binary classification task with DPC++ backend.")
-.set_body([]() { return new RegLossObjOneAPI(); });
-XGBOOST_REGISTER_OBJECTIVE(LogisticRawOneAPI, LogisticRawOneAPI::Name())
-.describe("Logistic regression for classification, output score "
- "before logistic transformation with DPC++ backend.")
-.set_body([]() { return new RegLossObjOneAPI(); });
-
-} // namespace obj
-} // namespace xgboost
+#include
+#include
+#include
+#include
+#include
+
+#include "xgboost/host_device_vector.h"
+#include "xgboost/json.h"
+#include "xgboost/parameter.h"
+#include "xgboost/span.h"
+
+#include "../../src/common/transform.h"
+#include "../../src/common/common.h"
+#include "./regression_loss_oneapi.h"
+
+#include "CL/sycl.hpp"
+
+namespace xgboost {
+namespace obj {
+
+DMLC_REGISTRY_FILE_TAG(regression_obj_oneapi);
+
+struct RegLossParamOneAPI : public XGBoostParameter {
+ float scale_pos_weight;
+ // declare parameters
+ DMLC_DECLARE_PARAMETER(RegLossParamOneAPI) {
+ DMLC_DECLARE_FIELD(scale_pos_weight).set_default(1.0f).set_lower_bound(0.0f)
+ .describe("Scale the weight of positive examples by this factor");
+ }
+};
+
+template
+class RegLossObjOneAPI : public ObjFunction {
+ protected:
+ HostDeviceVector label_correct_;
+
+ public:
+ RegLossObjOneAPI() = default;
+
+ void Configure(const std::vector >& args) override {
+ param_.UpdateAllowUnknown(args);
+
+ cl::sycl::default_selector selector;
+ qu_ = cl::sycl::queue(selector);
+ }
+
+ void GetGradient(const HostDeviceVector& preds,
+ const MetaInfo &info,
+ int iter,
+ HostDeviceVector* out_gpair) override {
+ if (info.labels_.Size() == 0U) {
+ LOG(WARNING) << "Label set is empty.";
+ }
+ CHECK_EQ(preds.Size(), info.labels_.Size())
+ << " " << "labels are not correctly provided"
+ << "preds.size=" << preds.Size() << ", label.size=" << info.labels_.Size() << ", "
+ << "Loss: " << Loss::Name();
+
+ size_t const ndata = preds.Size();
+ out_gpair->Resize(ndata);
+
+ // TODO: add label_correct check
+ label_correct_.Resize(1);
+ label_correct_.Fill(1);
+
+ bool is_null_weight = info.weights_.Size() == 0;
+
+ cl::sycl::buffer preds_buf(preds.HostPointer(), preds.Size());
+ cl::sycl::buffer labels_buf(info.labels_.HostPointer(), info.labels_.Size());
+ cl::sycl::buffer out_gpair_buf(out_gpair->HostPointer(), out_gpair->Size());
+ cl::sycl::buffer weights_buf(is_null_weight ? NULL : info.weights_.HostPointer(),
+ is_null_weight ? 1 : info.weights_.Size());
+
+ cl::sycl::buffer additional_input_buf(1);
+ {
+ auto additional_input_acc = additional_input_buf.get_access();
+ additional_input_acc[0] = 1; // Fill the label_correct flag
+ }
+
+ auto scale_pos_weight = param_.scale_pos_weight;
+ if (!is_null_weight) {
+ CHECK_EQ(info.weights_.Size(), ndata)
+ << "Number of weights should be equal to number of data points.";
+ }
+
+ qu_.submit([&](cl::sycl::handler& cgh) {
+ auto preds_acc = preds_buf.get_access(cgh);
+ auto labels_acc = labels_buf.get_access(cgh);
+ auto weights_acc = weights_buf.get_access(cgh);
+ auto out_gpair_acc = out_gpair_buf.get_access(cgh);
+ auto additional_input_acc = additional_input_buf.get_access(cgh);
+ cgh.parallel_for<>(cl::sycl::range<1>(ndata), [=](cl::sycl::id<1> pid) {
+ int idx = pid[0];
+ bst_float p = Loss::PredTransform(preds_acc[idx]);
+ bst_float w = is_null_weight ? 1.0f : weights_acc[idx];
+ bst_float label = labels_acc[idx];
+ 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_acc[0] = 0;
+ }
+ out_gpair_acc[idx] = GradientPair(Loss::FirstOrderGradient(p, label) * w,
+ Loss::SecondOrderGradient(p, label) * w);
+ });
+ }).wait();
+
+ int flag = 1;
+ {
+ auto additional_input_acc = additional_input_buf.get_access();
+ flag = additional_input_acc[0];
+ }
+
+ if (flag == 0) {
+ LOG(FATAL) << Loss::LabelErrorMsg();
+ }
+
+ }
+
+ public:
+ const char* DefaultEvalMetric() const override {
+ return Loss::DefaultEvalMetric();
+ }
+
+ void PredTransform(HostDeviceVector *io_preds) override {
+ size_t const ndata = io_preds->Size();
+
+ cl::sycl::buffer io_preds_buf(io_preds->HostPointer(), io_preds->Size());
+
+ qu_.submit([&](cl::sycl::handler& cgh) {
+ auto io_preds_acc = io_preds_buf.get_access(cgh);
+ cgh.parallel_for<>(cl::sycl::range<1>(ndata), [=](cl::sycl::id<1> pid) {
+ int idx = pid[0];
+ io_preds_acc[idx] = Loss::PredTransform(io_preds_acc[idx]);
+ });
+ }).wait();
+ }
+
+ float ProbToMargin(float base_score) const override {
+ return Loss::ProbToMargin(base_score);
+ }
+
+ void SaveConfig(Json* p_out) const override {
+ auto& out = *p_out;
+ out["name"] = String(Loss::Name());
+ out["reg_loss_param"] = ToJson(param_);
+ }
+
+ void LoadConfig(Json const& in) override {
+ FromJson(in["reg_loss_param"], ¶m_);
+ }
+
+ protected:
+ RegLossParamOneAPI param_;
+
+ cl::sycl::queue qu_;
+};
+
+// register the objective functions
+DMLC_REGISTER_PARAMETER(RegLossParamOneAPI);
+
+// TODO: Find a better way to dispatch names of DPC++ kernels with various template parameters of loss function
+XGBOOST_REGISTER_OBJECTIVE(SquaredLossRegressionOneAPI, LinearSquareLossOneAPI::Name())
+.describe("Regression with squared error with DPC++ backend.")
+.set_body([]() { return new RegLossObjOneAPI(); });
+XGBOOST_REGISTER_OBJECTIVE(SquareLogErrorOneAPI, SquaredLogErrorOneAPI::Name())
+.describe("Regression with root mean squared logarithmic error with DPC++ backend.")
+.set_body([]() { return new RegLossObjOneAPI(); });
+XGBOOST_REGISTER_OBJECTIVE(LogisticRegressionOneAPI, LogisticRegressionOneAPI::Name())
+.describe("Logistic regression for probability regression task with DPC++ backend.")
+.set_body([]() { return new RegLossObjOneAPI(); });
+XGBOOST_REGISTER_OBJECTIVE(LogisticClassificationOneAPI, LogisticClassificationOneAPI::Name())
+.describe("Logistic regression for binary classification task with DPC++ backend.")
+.set_body([]() { return new RegLossObjOneAPI(); });
+XGBOOST_REGISTER_OBJECTIVE(LogisticRawOneAPI, LogisticRawOneAPI::Name())
+.describe("Logistic regression for classification, output score "
+ "before logistic transformation with DPC++ backend.")
+.set_body([]() { return new RegLossObjOneAPI(); });
+
+} // namespace obj
+} // namespace xgboost
diff --git a/python-package/xgboost/callback.py b/python-package/xgboost/callback.py
index 5be6a058a..6569f7e3d 100644
--- a/python-package/xgboost/callback.py
+++ b/python-package/xgboost/callback.py
@@ -324,7 +324,7 @@ class EarlyStopping(TrainingCallback):
es = xgboost.callback.EarlyStopping(
rounds=2,
- abs_tol=1e-3,
+ min_delta=1e-3,
save_best=True,
maximize=False,
data_name="validation_0",
diff --git a/python-package/xgboost/sklearn.py b/python-package/xgboost/sklearn.py
index 3204f5a2a..52175981a 100644
--- a/python-package/xgboost/sklearn.py
+++ b/python-package/xgboost/sklearn.py
@@ -312,6 +312,19 @@ __model_doc = f"""
needs to be set to have categorical feature support. See :doc:`Categorical Data
` and :ref:`cat-param` for details.
+ multi_strategy : Optional[str]
+
+ .. versionadded:: 2.0.0
+
+ .. note:: This parameter is working-in-progress.
+
+ The strategy used for training multi-target models, including multi-target
+ regression and multi-class classification. See :doc:`/tutorials/multioutput` for
+ more information.
+
+ - ``one_output_per_tree``: One model for each target.
+ - ``multi_output_tree``: Use multi-target trees.
+
eval_metric : Optional[Union[str, List[str], Callable]]
.. versionadded:: 1.6.0
@@ -355,18 +368,21 @@ __model_doc = f"""
.. versionadded:: 1.6.0
- Activates early stopping. Validation metric needs to improve at least once in
- every **early_stopping_rounds** round(s) to continue training. Requires at least
- one item in **eval_set** in :py:meth:`fit`.
+ - Activates early stopping. Validation metric needs to improve at least once in
+ every **early_stopping_rounds** round(s) to continue training. Requires at
+ least one item in **eval_set** in :py:meth:`fit`.
- The method returns the model from the last iteration (not the best one). If
- there's more than one item in **eval_set**, the last entry will be used for early
- stopping. If there's more than one metric in **eval_metric**, the last metric
- will be used for early stopping.
+ - The method returns the model from the last iteration, not the best one, use a
+ callback :py:class:`xgboost.callback.EarlyStopping` if returning the best
+ model is preferred.
- If early stopping occurs, the model will have three additional fields:
- :py:attr:`best_score`, :py:attr:`best_iteration` and
- :py:attr:`best_ntree_limit`.
+ - If there's more than one item in **eval_set**, the last entry will be used for
+ early stopping. If there's more than one metric in **eval_metric**, the last
+ metric will be used for early stopping.
+
+ - If early stopping occurs, the model will have three additional fields:
+ :py:attr:`best_score`, :py:attr:`best_iteration` and
+ :py:attr:`best_ntree_limit`.
.. note::
@@ -466,7 +482,9 @@ Parameters
doc.extend([get_doc(i) for i in items])
if end_note:
doc.append(end_note)
- full_doc = [header + "\n\n"]
+ full_doc = [
+ header + "\nSee :doc:`/python/sklearn_estimator` for more information.\n"
+ ]
full_doc.extend(doc)
cls.__doc__ = "".join(full_doc)
return cls
@@ -624,6 +642,7 @@ class XGBModel(XGBModelBase):
feature_types: Optional[FeatureTypes] = None,
max_cat_to_onehot: Optional[int] = None,
max_cat_threshold: Optional[int] = None,
+ multi_strategy: Optional[str] = None,
eval_metric: Optional[Union[str, List[str], Callable]] = None,
early_stopping_rounds: Optional[int] = None,
callbacks: Optional[List[TrainingCallback]] = None,
@@ -670,6 +689,7 @@ class XGBModel(XGBModelBase):
self.feature_types = feature_types
self.max_cat_to_onehot = max_cat_to_onehot
self.max_cat_threshold = max_cat_threshold
+ self.multi_strategy = multi_strategy
self.eval_metric = eval_metric
self.early_stopping_rounds = early_stopping_rounds
self.callbacks = callbacks
@@ -1131,10 +1151,10 @@ class XGBModel(XGBModelBase):
base_margin: Optional[ArrayLike] = None,
iteration_range: Optional[Tuple[int, int]] = None,
) -> ArrayLike:
- """Predict with `X`. If the model is trained with early stopping, then `best_iteration`
- is used automatically. For tree models, when data is on GPU, like cupy array or
- cuDF dataframe and `predictor` is not specified, the prediction is run on GPU
- automatically, otherwise it will run on CPU.
+ """Predict with `X`. If the model is trained with early stopping, then
+ :py:attr:`best_iteration` is used automatically. For tree models, when data is
+ on GPU, like cupy array or cuDF dataframe and `predictor` is not specified, the
+ prediction is run on GPU automatically, otherwise it will run on CPU.
.. note:: This function is only thread safe for `gbtree` and `dart`.
@@ -1209,8 +1229,8 @@ class XGBModel(XGBModelBase):
ntree_limit: int = 0,
iteration_range: Optional[Tuple[int, int]] = None,
) -> np.ndarray:
- """Return the predicted leaf every tree for each sample. If the model is trained with
- early stopping, then `best_iteration` is used automatically.
+ """Return the predicted leaf every tree for each sample. If the model is trained
+ with early stopping, then :py:attr:`best_iteration` is used automatically.
Parameters
----------
@@ -1620,7 +1640,9 @@ class XGBClassifier(XGBModel, XGBClassifierBase):
base_margin: Optional[ArrayLike] = None,
iteration_range: Optional[Tuple[int, int]] = None,
) -> np.ndarray:
- """Predict the probability of each `X` example being of a given class.
+ """Predict the probability of each `X` example being of a given class. If the
+ model is trained with early stopping, then :py:attr:`best_iteration` is used
+ automatically.
.. note:: This function is only thread safe for `gbtree` and `dart`.
@@ -1646,6 +1668,7 @@ class XGBClassifier(XGBModel, XGBClassifierBase):
prediction :
a numpy array of shape array-like of shape (n_samples, n_classes) with the
probability of each data example being of a given class.
+
"""
# custom obj: Do nothing as we don't know what to do.
# softprob: Do nothing, output is proba.
@@ -2107,11 +2130,13 @@ class XGBRanker(XGBModel, XGBRankerMixIn):
return super().apply(X, ntree_limit, iteration_range)
def score(self, X: ArrayLike, y: ArrayLike) -> float:
- """Evaluate score for data using the last evaluation metric.
+ """Evaluate score for data using the last evaluation metric. If the model is
+ trained with early stopping, then :py:attr:`best_iteration` is used
+ automatically.
Parameters
----------
- X : pd.DataFrame|cudf.DataFrame
+ X : Union[pd.DataFrame, cudf.DataFrame]
Feature matrix. A DataFrame with a special `qid` column.
y :
diff --git a/python-package/xgboost/testing/__init__.py b/python-package/xgboost/testing/__init__.py
index 3b33e8774..20a4c681e 100644
--- a/python-package/xgboost/testing/__init__.py
+++ b/python-package/xgboost/testing/__init__.py
@@ -10,7 +10,6 @@ import os
import platform
import socket
import sys
-import zipfile
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
from io import StringIO
@@ -28,7 +27,6 @@ from typing import (
TypedDict,
Union,
)
-from urllib import request
import numpy as np
import pytest
@@ -37,6 +35,13 @@ from scipy import sparse
import xgboost as xgb
from xgboost.core import ArrayLike
from xgboost.sklearn import SklObjective
+from xgboost.testing.data import (
+ get_california_housing,
+ get_cancer,
+ get_digits,
+ get_sparse,
+ memory,
+)
hypothesis = pytest.importorskip("hypothesis")
@@ -44,13 +49,8 @@ hypothesis = pytest.importorskip("hypothesis")
from hypothesis import strategies
from hypothesis.extra.numpy import arrays
-joblib = pytest.importorskip("joblib")
datasets = pytest.importorskip("sklearn.datasets")
-Memory = joblib.Memory
-
-memory = Memory("./cachedir", verbose=0)
-
PytestSkip = TypedDict("PytestSkip", {"condition": bool, "reason": str})
@@ -352,137 +352,6 @@ class TestDataset:
return self.name
-@memory.cache
-def get_california_housing() -> Tuple[np.ndarray, np.ndarray]:
- data = datasets.fetch_california_housing()
- return data.data, data.target
-
-
-@memory.cache
-def get_digits() -> Tuple[np.ndarray, np.ndarray]:
- data = datasets.load_digits()
- return data.data, data.target
-
-
-@memory.cache
-def get_cancer() -> Tuple[np.ndarray, np.ndarray]:
- return datasets.load_breast_cancer(return_X_y=True)
-
-
-@memory.cache
-def get_sparse() -> Tuple[np.ndarray, np.ndarray]:
- rng = np.random.RandomState(199)
- n = 2000
- sparsity = 0.75
- X, y = datasets.make_regression(n, random_state=rng)
- flag = rng.binomial(1, sparsity, X.shape)
- for i in range(X.shape[0]):
- for j in range(X.shape[1]):
- if flag[i, j]:
- X[i, j] = np.nan
- return X, y
-
-
-@memory.cache
-def get_ames_housing() -> Tuple[np.ndarray, np.ndarray]:
- """
- Number of samples: 1460
- Number of features: 20
- Number of categorical features: 10
- Number of numerical features: 10
- """
- from sklearn.datasets import fetch_openml
-
- X, y = fetch_openml(data_id=42165, as_frame=True, return_X_y=True)
-
- categorical_columns_subset: List[str] = [
- "BldgType", # 5 cats, no nan
- "GarageFinish", # 3 cats, nan
- "LotConfig", # 5 cats, no nan
- "Functional", # 7 cats, no nan
- "MasVnrType", # 4 cats, nan
- "HouseStyle", # 8 cats, no nan
- "FireplaceQu", # 5 cats, nan
- "ExterCond", # 5 cats, no nan
- "ExterQual", # 4 cats, no nan
- "PoolQC", # 3 cats, nan
- ]
-
- numerical_columns_subset: List[str] = [
- "3SsnPorch",
- "Fireplaces",
- "BsmtHalfBath",
- "HalfBath",
- "GarageCars",
- "TotRmsAbvGrd",
- "BsmtFinSF1",
- "BsmtFinSF2",
- "GrLivArea",
- "ScreenPorch",
- ]
-
- X = X[categorical_columns_subset + numerical_columns_subset]
- X[categorical_columns_subset] = X[categorical_columns_subset].astype("category")
- return X, y
-
-
-@memory.cache
-def get_mq2008(
- dpath: str,
-) -> Tuple[
- sparse.csr_matrix,
- np.ndarray,
- np.ndarray,
- sparse.csr_matrix,
- np.ndarray,
- np.ndarray,
- sparse.csr_matrix,
- np.ndarray,
- np.ndarray,
-]:
- from sklearn.datasets import load_svmlight_files
-
- src = "https://s3-us-west-2.amazonaws.com/xgboost-examples/MQ2008.zip"
- target = dpath + "/MQ2008.zip"
- if not os.path.exists(target):
- request.urlretrieve(url=src, filename=target)
-
- with zipfile.ZipFile(target, "r") as f:
- f.extractall(path=dpath)
-
- (
- x_train,
- y_train,
- qid_train,
- x_test,
- y_test,
- qid_test,
- x_valid,
- y_valid,
- qid_valid,
- ) = load_svmlight_files(
- (
- dpath + "MQ2008/Fold1/train.txt",
- dpath + "MQ2008/Fold1/test.txt",
- dpath + "MQ2008/Fold1/vali.txt",
- ),
- query_id=True,
- zero_based=False,
- )
-
- return (
- x_train,
- y_train,
- qid_train,
- x_test,
- y_test,
- qid_test,
- x_valid,
- y_valid,
- qid_valid,
- )
-
-
# pylint: disable=too-many-arguments,too-many-locals
@memory.cache
def make_categorical(
@@ -737,20 +606,7 @@ _unweighted_datasets_strategy = strategies.sampled_from(
TestDataset(
"calif_housing-l1", get_california_housing, "reg:absoluteerror", "mae"
),
- TestDataset("digits", get_digits, "multi:softmax", "mlogloss"),
TestDataset("cancer", get_cancer, "binary:logistic", "logloss"),
- TestDataset(
- "mtreg",
- lambda: datasets.make_regression(n_samples=128, n_features=2, n_targets=3),
- "reg:squarederror",
- "rmse",
- ),
- TestDataset(
- "mtreg-l1",
- lambda: datasets.make_regression(n_samples=128, n_features=2, n_targets=3),
- "reg:absoluteerror",
- "mae",
- ),
TestDataset("sparse", get_sparse, "reg:squarederror", "rmse"),
TestDataset("sparse-l1", get_sparse, "reg:absoluteerror", "mae"),
TestDataset(
@@ -763,37 +619,71 @@ _unweighted_datasets_strategy = strategies.sampled_from(
)
-@strategies.composite
-def _dataset_weight_margin(draw: Callable) -> TestDataset:
- data: TestDataset = draw(_unweighted_datasets_strategy)
- if draw(strategies.booleans()):
- data.w = draw(
- arrays(np.float64, (len(data.y)), elements=strategies.floats(0.1, 2.0))
- )
- if draw(strategies.booleans()):
- num_class = 1
- if data.objective == "multi:softmax":
- num_class = int(np.max(data.y) + 1)
- elif data.name.startswith("mtreg"):
- num_class = data.y.shape[1]
+def make_datasets_with_margin(
+ unweighted_strategy: strategies.SearchStrategy,
+) -> Callable:
+ """Factory function for creating strategies that generates datasets with weight and
+ base margin.
- data.margin = draw(
- arrays(
- np.float64,
- (data.y.shape[0] * num_class),
- elements=strategies.floats(0.5, 1.0),
+ """
+
+ @strategies.composite
+ def weight_margin(draw: Callable) -> TestDataset:
+ data: TestDataset = draw(unweighted_strategy)
+ if draw(strategies.booleans()):
+ data.w = draw(
+ arrays(np.float64, (len(data.y)), elements=strategies.floats(0.1, 2.0))
)
- )
- assert data.margin is not None
- if num_class != 1:
- data.margin = data.margin.reshape(data.y.shape[0], num_class)
+ if draw(strategies.booleans()):
+ num_class = 1
+ if data.objective == "multi:softmax":
+ num_class = int(np.max(data.y) + 1)
+ elif data.name.startswith("mtreg"):
+ num_class = data.y.shape[1]
- return data
+ data.margin = draw(
+ arrays(
+ np.float64,
+ (data.y.shape[0] * num_class),
+ elements=strategies.floats(0.5, 1.0),
+ )
+ )
+ assert data.margin is not None
+ if num_class != 1:
+ data.margin = data.margin.reshape(data.y.shape[0], num_class)
+
+ return data
+
+ return weight_margin
-# A strategy for drawing from a set of example datasets
-# May add random weights to the dataset
-dataset_strategy = _dataset_weight_margin()
+# A strategy for drawing from a set of example datasets. May add random weights to the
+# dataset
+dataset_strategy = make_datasets_with_margin(_unweighted_datasets_strategy)()
+
+
+_unweighted_multi_datasets_strategy = strategies.sampled_from(
+ [
+ TestDataset("digits", get_digits, "multi:softmax", "mlogloss"),
+ TestDataset(
+ "mtreg",
+ lambda: datasets.make_regression(n_samples=128, n_features=2, n_targets=3),
+ "reg:squarederror",
+ "rmse",
+ ),
+ TestDataset(
+ "mtreg-l1",
+ lambda: datasets.make_regression(n_samples=128, n_features=2, n_targets=3),
+ "reg:absoluteerror",
+ "mae",
+ ),
+ ]
+)
+
+# A strategy for drawing from a set of multi-target/multi-class datasets.
+multi_dataset_strategy = make_datasets_with_margin(
+ _unweighted_multi_datasets_strategy
+)()
def non_increasing(L: Sequence[float], tolerance: float = 1e-4) -> bool:
diff --git a/python-package/xgboost/testing/data.py b/python-package/xgboost/testing/data.py
index 4f79d7358..477d0cf3d 100644
--- a/python-package/xgboost/testing/data.py
+++ b/python-package/xgboost/testing/data.py
@@ -1,10 +1,20 @@
"""Utilities for data generation."""
-from typing import Any, Generator, Tuple, Union
+import os
+import zipfile
+from typing import Any, Generator, List, Tuple, Union
+from urllib import request
import numpy as np
+import pytest
+from numpy.random import Generator as RNG
+from scipy import sparse
+import xgboost
from xgboost.data import pandas_pyarrow_mapper
+joblib = pytest.importorskip("joblib")
+memory = joblib.Memory("./cachedir", verbose=0)
+
def np_dtypes(
n_samples: int, n_features: int
@@ -179,3 +189,154 @@ def pd_arrow_dtypes() -> Generator:
dtype=pd.ArrowDtype(pa.bool_()),
)
yield orig, df
+
+
+def check_inf(rng: RNG) -> None:
+ """Validate there's no inf in X."""
+ X = rng.random(size=32).reshape(8, 4)
+ y = rng.random(size=8)
+ X[5, 2] = np.inf
+
+ with pytest.raises(ValueError, match="Input data contains `inf`"):
+ xgboost.QuantileDMatrix(X, y)
+
+ with pytest.raises(ValueError, match="Input data contains `inf`"):
+ xgboost.DMatrix(X, y)
+
+
+@memory.cache
+def get_california_housing() -> Tuple[np.ndarray, np.ndarray]:
+ """Fetch the California housing dataset from sklearn."""
+ datasets = pytest.importorskip("sklearn.datasets")
+ data = datasets.fetch_california_housing()
+ return data.data, data.target
+
+
+@memory.cache
+def get_digits() -> Tuple[np.ndarray, np.ndarray]:
+ """Fetch the digits dataset from sklearn."""
+ datasets = pytest.importorskip("sklearn.datasets")
+ data = datasets.load_digits()
+ return data.data, data.target
+
+
+@memory.cache
+def get_cancer() -> Tuple[np.ndarray, np.ndarray]:
+ """Fetch the breast cancer dataset from sklearn."""
+ datasets = pytest.importorskip("sklearn.datasets")
+ return datasets.load_breast_cancer(return_X_y=True)
+
+
+@memory.cache
+def get_sparse() -> Tuple[np.ndarray, np.ndarray]:
+ """Generate a sparse dataset."""
+ datasets = pytest.importorskip("sklearn.datasets")
+ rng = np.random.RandomState(199)
+ n = 2000
+ sparsity = 0.75
+ X, y = datasets.make_regression(n, random_state=rng)
+ flag = rng.binomial(1, sparsity, X.shape)
+ for i in range(X.shape[0]):
+ for j in range(X.shape[1]):
+ if flag[i, j]:
+ X[i, j] = np.nan
+ return X, y
+
+
+@memory.cache
+def get_ames_housing() -> Tuple[np.ndarray, np.ndarray]:
+ """
+ Number of samples: 1460
+ Number of features: 20
+ Number of categorical features: 10
+ Number of numerical features: 10
+ """
+ datasets = pytest.importorskip("sklearn.datasets")
+ X, y = datasets.fetch_openml(data_id=42165, as_frame=True, return_X_y=True)
+
+ categorical_columns_subset: List[str] = [
+ "BldgType", # 5 cats, no nan
+ "GarageFinish", # 3 cats, nan
+ "LotConfig", # 5 cats, no nan
+ "Functional", # 7 cats, no nan
+ "MasVnrType", # 4 cats, nan
+ "HouseStyle", # 8 cats, no nan
+ "FireplaceQu", # 5 cats, nan
+ "ExterCond", # 5 cats, no nan
+ "ExterQual", # 4 cats, no nan
+ "PoolQC", # 3 cats, nan
+ ]
+
+ numerical_columns_subset: List[str] = [
+ "3SsnPorch",
+ "Fireplaces",
+ "BsmtHalfBath",
+ "HalfBath",
+ "GarageCars",
+ "TotRmsAbvGrd",
+ "BsmtFinSF1",
+ "BsmtFinSF2",
+ "GrLivArea",
+ "ScreenPorch",
+ ]
+
+ X = X[categorical_columns_subset + numerical_columns_subset]
+ X[categorical_columns_subset] = X[categorical_columns_subset].astype("category")
+ return X, y
+
+
+@memory.cache
+def get_mq2008(
+ dpath: str,
+) -> Tuple[
+ sparse.csr_matrix,
+ np.ndarray,
+ np.ndarray,
+ sparse.csr_matrix,
+ np.ndarray,
+ np.ndarray,
+ sparse.csr_matrix,
+ np.ndarray,
+ np.ndarray,
+]:
+ """Fetch the mq2008 dataset."""
+ datasets = pytest.importorskip("sklearn.datasets")
+ src = "https://s3-us-west-2.amazonaws.com/xgboost-examples/MQ2008.zip"
+ target = os.path.join(dpath, "MQ2008.zip")
+ if not os.path.exists(target):
+ request.urlretrieve(url=src, filename=target)
+
+ with zipfile.ZipFile(target, "r") as f:
+ f.extractall(path=dpath)
+
+ (
+ x_train,
+ y_train,
+ qid_train,
+ x_test,
+ y_test,
+ qid_test,
+ x_valid,
+ y_valid,
+ qid_valid,
+ ) = datasets.load_svmlight_files(
+ (
+ os.path.join(dpath, "MQ2008/Fold1/train.txt"),
+ os.path.join(dpath, "MQ2008/Fold1/test.txt"),
+ os.path.join(dpath, "MQ2008/Fold1/vali.txt"),
+ ),
+ query_id=True,
+ zero_based=False,
+ )
+
+ return (
+ x_train,
+ y_train,
+ qid_train,
+ x_test,
+ y_test,
+ qid_test,
+ x_valid,
+ y_valid,
+ qid_valid,
+ )
diff --git a/python-package/xgboost/testing/params.py b/python-package/xgboost/testing/params.py
index 3af3306da..e6ba73e1f 100644
--- a/python-package/xgboost/testing/params.py
+++ b/python-package/xgboost/testing/params.py
@@ -4,8 +4,8 @@ from typing import cast
import pytest
-hypothesis = pytest.importorskip("hypothesis")
-from hypothesis import strategies # pylint:disable=wrong-import-position
+strategies = pytest.importorskip("hypothesis.strategies")
+
exact_parameter_strategy = strategies.fixed_dictionaries(
{
@@ -41,6 +41,26 @@ hist_parameter_strategy = strategies.fixed_dictionaries(
and (cast(int, x["max_depth"]) > 0 or x["grow_policy"] == "lossguide")
)
+hist_multi_parameter_strategy = strategies.fixed_dictionaries(
+ {
+ "max_depth": strategies.integers(1, 11),
+ "max_leaves": strategies.integers(0, 1024),
+ "max_bin": strategies.integers(2, 512),
+ "multi_strategy": strategies.sampled_from(
+ ["multi_output_tree", "one_output_per_tree"]
+ ),
+ "grow_policy": strategies.sampled_from(["lossguide", "depthwise"]),
+ "min_child_weight": strategies.floats(0.5, 2.0),
+ # We cannot enable subsampling as the training loss can increase
+ # 'subsample': strategies.floats(0.5, 1.0),
+ "colsample_bytree": strategies.floats(0.5, 1.0),
+ "colsample_bylevel": strategies.floats(0.5, 1.0),
+ }
+).filter(
+ lambda x: (cast(int, x["max_depth"]) > 0 or cast(int, x["max_leaves"]) > 0)
+ and (cast(int, x["max_depth"]) > 0 or x["grow_policy"] == "lossguide")
+)
+
cat_parameter_strategy = strategies.fixed_dictionaries(
{
"max_cat_to_onehot": strategies.integers(1, 128),
diff --git a/python-package/xgboost/testing/ranking.py b/python-package/xgboost/testing/ranking.py
index fe4fc8404..7c75012c2 100644
--- a/python-package/xgboost/testing/ranking.py
+++ b/python-package/xgboost/testing/ranking.py
@@ -48,7 +48,12 @@ def run_ranking_qid_df(impl: ModuleType, tree_method: str) -> None:
def neg_mse(*args: Any, **kwargs: Any) -> float:
return -float(mean_squared_error(*args, **kwargs))
- ranker = xgb.XGBRanker(n_estimators=3, eval_metric=neg_mse, tree_method=tree_method)
+ ranker = xgb.XGBRanker(
+ n_estimators=3,
+ eval_metric=neg_mse,
+ tree_method=tree_method,
+ disable_default_eval_metric=True,
+ )
ranker.fit(df, y, eval_set=[(valid_df, y)])
score = ranker.score(valid_df, y)
assert np.isclose(score, ranker.evals_result()["validation_0"]["neg_mse"][-1])
diff --git a/src/c_api/c_api_utils.h b/src/c_api/c_api_utils.h
index c0ee65e00..d0bf00cad 100644
--- a/src/c_api/c_api_utils.h
+++ b/src/c_api/c_api_utils.h
@@ -55,6 +55,7 @@ inline void CalcPredictShape(bool strict_shape, PredictionType type, size_t rows
*out_dim = 2;
shape.resize(*out_dim);
shape.front() = rows;
+ // chunksize can be 1 if it's softmax
shape.back() = std::min(groups, chunksize);
}
break;
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index 739a84968..a34010cd0 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -14,7 +14,7 @@
// clang with libstdc++ works as well
#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__sun) && !defined(sun) && \
- !defined(__APPLE__) && __has_include()
+ !defined(__APPLE__) && __has_include() && __has_include()
#define GCC_HAS_PARALLEL 1
#endif // GLIC_VERSION
diff --git a/src/common/device_helpers.cuh b/src/common/device_helpers.cuh
index b1d165c42..956c9cf04 100644
--- a/src/common/device_helpers.cuh
+++ b/src/common/device_helpers.cuh
@@ -121,17 +121,20 @@ namespace dh {
#ifdef XGBOOST_USE_NCCL
#define safe_nccl(ans) ThrowOnNcclError((ans), __FILE__, __LINE__)
-inline ncclResult_t ThrowOnNcclError(ncclResult_t code, const char *file,
- int line) {
+inline ncclResult_t ThrowOnNcclError(ncclResult_t code, const char *file, int line) {
if (code != ncclSuccess) {
std::stringstream ss;
- ss << "NCCL failure :" << ncclGetErrorString(code);
+ ss << "NCCL failure: " << ncclGetErrorString(code) << ".";
+ ss << " " << file << "(" << line << ")\n";
if (code == ncclUnhandledCudaError) {
// nccl usually preserves the last error so we can get more details.
auto err = cudaPeekAtLastError();
- ss << " " << thrust::system_error(err, thrust::cuda_category()).what();
+ ss << " CUDA error: " << thrust::system_error(err, thrust::cuda_category()).what() << "\n";
+ } else if (code == ncclSystemError) {
+ ss << " This might be caused by a network configuration issue. Please consider specifying "
+ "the network interface for NCCL via environment variables listed in its reference: "
+ "`https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/env.html`.\n";
}
- ss << " " << file << "(" << line << ")";
LOG(FATAL) << ss.str();
}
diff --git a/src/common/device_helpers.hip.h b/src/common/device_helpers.hip.h
index 365126465..38bc29f91 100644
--- a/src/common/device_helpers.hip.h
+++ b/src/common/device_helpers.hip.h
@@ -2,6 +2,9 @@
* Copyright 2017-2023 XGBoost contributors
*/
#pragma once
+
+#if defined(XGBOOST_USE_CUDA)
+
#include // thrust::upper_bound
#include
#include
@@ -95,20 +98,23 @@ XGBOOST_DEV_INLINE T atomicAdd(T *addr, T v) { // NOLINT
}
namespace dh {
-#ifdef XGBOOST_USE_NCCL
+#ifdef XGBOOST_USE_RCCL
#define safe_nccl(ans) ThrowOnNcclError((ans), __FILE__, __LINE__)
-inline ncclResult_t ThrowOnNcclError(ncclResult_t code, const char *file,
- int line) {
+inline ncclResult_t ThrowOnNcclError(ncclResult_t code, const char *file, int line) {
if (code != ncclSuccess) {
std::stringstream ss;
- ss << "NCCL failure :" << ncclGetErrorString(code);
+ ss << "RCCL failure: " << ncclGetErrorString(code) << ".";
+ ss << " " << file << "(" << line << ")\n";
if (code == ncclUnhandledCudaError) {
// nccl usually preserves the last error so we can get more details.
auto err = hipPeekAtLastError();
- ss << " " << thrust::system_error(err, thrust::hip_category()).what();
+ ss << " CUDA error: " << thrust::system_error(err, thrust::cuda_category()).what() << "\n";
+ } else if (code == ncclSystemError) {
+ ss << " This might be caused by a network configuration issue. Please consider specifying "
+ "the network interface for NCCL via environment variables listed in its reference: "
+ "`https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/env.html`.\n";
}
- ss << " " << file << "(" << line << ")";
LOG(FATAL) << ss.str();
}
diff --git a/src/common/error_msg.h b/src/common/error_msg.h
index 48a2c92a4..3dbb7f52c 100644
--- a/src/common/error_msg.h
+++ b/src/common/error_msg.h
@@ -20,5 +20,9 @@ constexpr StringView GroupSize() {
constexpr StringView LabelScoreSize() {
return "The size of label doesn't match the size of prediction.";
}
+
+constexpr StringView InfInData() {
+ return "Input data contains `inf` or a value too large, while `missing` is not set to `inf`";
+}
} // namespace xgboost::error
#endif // XGBOOST_COMMON_ERROR_MSG_H_
diff --git a/src/common/hist_util.h b/src/common/hist_util.h
index c09e5c71a..d95d405eb 100644
--- a/src/common/hist_util.h
+++ b/src/common/hist_util.h
@@ -7,23 +7,22 @@
#ifndef XGBOOST_COMMON_HIST_UTIL_H_
#define XGBOOST_COMMON_HIST_UTIL_H_
-#include
-
#include
+#include // for uint32_t
#include
#include