Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ conda env create -n sklearn -f envs/conda-env-sklearn.yml
conda env create -n rapids --solver=libmamba -f envs/conda-env-rapids.yml
```

GPU benchmarks using `sklearnex`'s `device: gpu` rely on scikit-learn's Array API support (`array_api_dispatch`), which requires `SCIPY_ARRAY_API=1` to be set in the environment before running:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like it could be set inside the python process instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could but I don't think this is a great way to handle it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ethanglaser What would be the downside?


```bash
export SCIPY_ARRAY_API=1
```

## 🚀 How To Use Scikit-learn_bench

### Benchmarks Runner
Expand Down
2 changes: 1 addition & 1 deletion configs/BENCH-CONFIG-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Configs have the three highest parameter keys:
| `data`:`distributed_split` | None | None, `rank_based` | Split type used to distribute data between machines in distributed algorithm. `None` type means usage of all data without split on all machines. `rank_based` type splits the data equally between machines with split sequence based on rank id from MPI. |
|<h3>Algorithm parameters</h3>||||
| `algorithm`:`library` | None | | Python module containing measured entity (class or function). |
| `algorithm`:`device` | `default` | `default`, `cpu`, `gpu` | Device selected for computation. |
| `algorithm`:`device` | `default` | `default`, `cpu`, `gpu` | Device selected for computation. `sklearnex`+`gpu` cases enable sklearn's `array_api_dispatch` and use `dpnp` data by default (see `sklearn_context` below); requires `SCIPY_ARRAY_API=1` to be set in the environment. |

## Benchmark-Specific Parameters

Expand Down
18 changes: 12 additions & 6 deletions configs/common/sklearn.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
{ "library": "sklearnex", "device": "cpu" }
]
},
"sklearn-ex[cpu,gpu] implementations": {
"algorithm": [
{ "library": "sklearn", "device": "cpu" },
{ "library": "sklearnex", "device": ["cpu", "gpu"] }
]
},
"sklearn-ex[cpu,gpu] implementations": [
{ "algorithm": { "library": "sklearn", "device": "cpu" } },
{ "algorithm": { "library": "sklearnex", "device": "cpu" } },
{
"algorithm": {
"library": "sklearnex",
"device": "gpu",
"sklearn_context": { "array_api_dispatch": true }
},
"data": { "format": "dpnp" }
}
],
"sklearnex spmd implementation": {
"algorithm": {
"library": "sklearnex.spmd",
Expand Down
4 changes: 4 additions & 0 deletions sklbench/benchmarks/sklearn_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ def get_subset_metrics_of_estimator(
and getattr(estimator_instance, "probability") == False
):
y_pred_proba = convert_to_numpy(estimator_instance.predict_proba(x))
# GPU/float32 predict_proba rows may drift outside roc_auc_score's

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed in sklearnex instead. There is a code branch like this for random forests. If it happens for other classes, needs to be added for them too.

# sum-to-one tolerance for multiclass, so renormalize before scoring
if y_pred_proba.shape[1] > 2:
y_pred_proba = y_pred_proba / y_pred_proba.sum(axis=1, keepdims=True)
metrics.update(
{
"ROC AUC": float(
Expand Down
17 changes: 15 additions & 2 deletions sklbench/datasets/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from ..utils.logger import logger


def convert_data(data, dformat: str, order: str, dtype: str, device: str = None):
def convert_data(data, dformat: str, order: str, dtype: str, device: str = None, sycl_queue=None):
if isinstance(data, csr_matrix) and dformat != "csr_matrix":
data = data.toarray()
if dtype == "preserve":
Expand All @@ -46,6 +46,11 @@ def convert_data(data, dformat: str, order: str, dtype: str, device: str = None)
elif dformat == "dpnp":
import dpnp

# Pin every subset to one shared queue: sklearnex builds its internal
# arrays (e.g. the take() indices in KNN predict) on the device's default
# queue, and array_api_dispatch requires all arrays share one queue object.
if sycl_queue is not None:
return dpnp.asarray(data, dtype=dtype, order=order, sycl_queue=sycl_queue)
return dpnp.array(data, dtype=dtype, order=order, device=device)
elif dformat == "dpctl":
warnings.warn(
Expand Down Expand Up @@ -143,6 +148,14 @@ def split_and_transform_data(bench_case, data, data_description):

device = get_bench_case_value(bench_case, "algorithm:device", None)
common_data_format = get_bench_case_value(bench_case, "data:format", "pandas")

# Resolve one queue for the device up front so all dpnp subsets share it;
# dpnp.array(device=...) per subset can otherwise land on distinct queues.
sycl_queue = None
if common_data_format == "dpnp" and device is not None:
import dpnp

sycl_queue = dpnp.array([], device=device).sycl_queue
common_data_order = get_bench_case_value(bench_case, "data:order", "F")
common_data_dtype = get_bench_case_value(bench_case, "data:dtype", "float32")

Expand Down Expand Up @@ -177,7 +190,7 @@ def split_and_transform_data(bench_case, data, data_description):
data_dtype = required_label_dtype

converted_data = convert_data(
subset_content, data_format, data_order, data_dtype, device
subset_content, data_format, data_order, data_dtype, device, sycl_queue
)
data_dict[subset_name] = converted_data
if not is_label:
Expand Down
Loading