Re-implement ROC-AUC. (#6747)
* Re-implement ROC-AUC. * Binary * MultiClass * LTR * Add documents. This PR resolves a few issues: - Define a value when the dataset is invalid, which can happen if there's an empty dataset, or when the dataset contains only positive or negative values. - Define ROC-AUC for multi-class classification. - Define weighted average value for distributed setting. - A correct implementation for learning to rank task. Previous implementation is just binary classification with averaging across groups, which doesn't measure ordered learning to rank.
This commit is contained in:
@@ -42,6 +42,7 @@ def local_cuda_cluster(request, pytestconfig):
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption('--use-rmm-pool', action='store_true', default=False, help='Use RMM pool')
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if config.getoption('--use-rmm-pool'):
|
||||
blocklist = [
|
||||
@@ -53,3 +54,9 @@ def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
if any(item.nodeid.startswith(x) for x in blocklist):
|
||||
item.add_marker(skip_mark)
|
||||
|
||||
# mark dask tests as `mgpu`.
|
||||
mgpu_mark = pytest.mark.mgpu
|
||||
for item in items:
|
||||
if item.nodeid.startswith("python-gpu/test_gpu_with_dask.py"):
|
||||
item.add_marker(mgpu_mark)
|
||||
|
||||
47
tests/python-gpu/test_gpu_eval_metrics.py
Normal file
47
tests/python-gpu/test_gpu_eval_metrics.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import sys
|
||||
import xgboost
|
||||
import pytest
|
||||
|
||||
sys.path.append("tests/python")
|
||||
import test_eval_metrics as test_em # noqa
|
||||
|
||||
|
||||
class TestGPUEvalMetrics:
|
||||
cpu_test = test_em.TestEvalMetrics()
|
||||
|
||||
@pytest.mark.parametrize("n_samples", [4, 100, 1000])
|
||||
def test_roc_auc_binary(self, n_samples):
|
||||
self.cpu_test.run_roc_auc_binary("gpu_hist", n_samples)
|
||||
|
||||
@pytest.mark.parametrize("n_samples", [4, 100, 1000])
|
||||
def test_roc_auc_multi(self, n_samples):
|
||||
self.cpu_test.run_roc_auc_multi("gpu_hist", n_samples)
|
||||
|
||||
@pytest.mark.parametrize("n_samples", [4, 100, 1000])
|
||||
def test_roc_auc_ltr(self, n_samples):
|
||||
import numpy as np
|
||||
|
||||
rng = np.random.RandomState(1994)
|
||||
n_samples = n_samples
|
||||
n_features = 10
|
||||
X = rng.randn(n_samples, n_features)
|
||||
y = rng.randint(0, 16, size=n_samples)
|
||||
group = np.array([n_samples // 2, n_samples // 2])
|
||||
|
||||
Xy = xgboost.DMatrix(X, y, group=group)
|
||||
|
||||
cpu = xgboost.train(
|
||||
{"tree_method": "hist", "eval_metric": "auc", "objective": "rank:ndcg"},
|
||||
Xy,
|
||||
num_boost_round=10,
|
||||
)
|
||||
cpu_auc = float(cpu.eval(Xy).split(":")[1])
|
||||
|
||||
gpu = xgboost.train(
|
||||
{"tree_method": "gpu_hist", "eval_metric": "auc", "objective": "rank:ndcg"},
|
||||
Xy,
|
||||
num_boost_round=10,
|
||||
)
|
||||
gpu_auc = float(gpu.eval(Xy).split(":")[1])
|
||||
|
||||
np.testing.assert_allclose(cpu_auc, gpu_auc)
|
||||
@@ -5,6 +5,10 @@ import itertools
|
||||
import shutil
|
||||
import urllib.request
|
||||
import zipfile
|
||||
import sys
|
||||
sys.path.append("tests/python")
|
||||
|
||||
import testing as tm # noqa
|
||||
|
||||
|
||||
class TestRanking:
|
||||
@@ -15,9 +19,9 @@ class TestRanking:
|
||||
"""
|
||||
from sklearn.datasets import load_svmlight_files
|
||||
# download the test data
|
||||
cls.dpath = 'demo/rank/'
|
||||
cls.dpath = os.path.join(tm.PROJECT_ROOT, "demo/rank/")
|
||||
src = 'https://s3-us-west-2.amazonaws.com/xgboost-examples/MQ2008.zip'
|
||||
target = cls.dpath + '/MQ2008.zip'
|
||||
target = os.path.join(cls.dpath, "MQ2008.zip")
|
||||
|
||||
if os.path.exists(cls.dpath) and os.path.exists(target):
|
||||
print("Skipping dataset download...")
|
||||
@@ -79,8 +83,8 @@ class TestRanking:
|
||||
Cleanup test artifacts from download and unpacking
|
||||
:return:
|
||||
"""
|
||||
os.remove(cls.dpath + "MQ2008.zip")
|
||||
shutil.rmtree(cls.dpath + "MQ2008")
|
||||
os.remove(os.path.join(cls.dpath, "MQ2008.zip"))
|
||||
shutil.rmtree(os.path.join(cls.dpath, "MQ2008"))
|
||||
|
||||
@classmethod
|
||||
def __test_training_with_rank_objective(cls, rank_objective, metric_name, tolerance=1e-02):
|
||||
|
||||
@@ -17,6 +17,8 @@ if sys.platform.startswith("win"):
|
||||
|
||||
sys.path.append("tests/python")
|
||||
from test_with_dask import run_empty_dmatrix_reg # noqa
|
||||
from test_with_dask import run_empty_dmatrix_auc # noqa
|
||||
from test_with_dask import run_auc # noqa
|
||||
from test_with_dask import run_boost_from_prediction # noqa
|
||||
from test_with_dask import run_dask_classifier # noqa
|
||||
from test_with_dask import run_empty_dmatrix_cls # noqa
|
||||
@@ -286,6 +288,15 @@ class TestDistributedGPU:
|
||||
run_empty_dmatrix_reg(client, parameters)
|
||||
run_empty_dmatrix_cls(client, parameters)
|
||||
|
||||
def test_empty_dmatrix_auc(self, local_cuda_cluster: LocalCUDACluster) -> None:
|
||||
with Client(local_cuda_cluster) as client:
|
||||
n_workers = len(_get_client_workers(client))
|
||||
run_empty_dmatrix_auc(client, "gpu_hist", n_workers)
|
||||
|
||||
def test_auc(self, local_cuda_cluster: LocalCUDACluster) -> None:
|
||||
with Client(local_cuda_cluster) as client:
|
||||
run_auc(client, "gpu_hist")
|
||||
|
||||
def test_data_initialization(self, local_cuda_cluster: LocalCUDACluster) -> None:
|
||||
with Client(local_cuda_cluster) as client:
|
||||
X, y, _ = generate_array()
|
||||
|
||||
Reference in New Issue
Block a user