Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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: 3 additions & 3 deletions doc/sources/input-types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ Just like |sklearn| estimators, estimators from the |sklearnex| are able to work
- Pandas :external+pandas:doc:`DataFrame and Series <user_guide/dsintro>` classes.
- Other DataFrame classes recognize by data validators from |sklearn|, such as the ones from `Polars <https://pola.rs>`__.

In addition, |sklearnex| also supports |dpnp_array| arrays (with and without array API mode) in estimators with
:ref:`GPU support <sklearn_algorithms_gpu>` (see also :doc:`array_api`).
In addition, |sklearnex| also supports |dpnp_array| arrays in estimators with
:ref:`GPU support <sklearn_algorithms_gpu>` when array API mode is enabled (see also :doc:`array_api`).

|sklearnex| currently does not offer accelerated routines for input types not listed here - when
receiving an unsupported class, estimators will either convert to a supported class under some
Expand All @@ -51,7 +51,7 @@ enabled but the input is unsupported).
- If a SYCL queue is used for :ref:`target_offload` for a device without ``float64`` support but data is ``float64``,
data will be converted to ``float32``.
- If a |dpnp_array| array on GPU is used as input without :doc:`array_api` being enabled, then data will be transferred
to CPU for validations and then back to GPU for computations (see :doc:`oneapi-gpu` for details).
to CPU and the result returned as a NumPy array (see :doc:`oneapi-gpu` for details).
- If a |dpnp_array| array on GPU is passed to an estimator with :ref:`GPU support <sklearn_algorithms_gpu>` but the
requested operation is not supported on GPU, and if array API is not enabled or |sklearn| does not support array API
for the requested operation, then data might be transferred to CPU and the operation done there (see :doc:`config-contexts`).
27 changes: 1 addition & 26 deletions doc/sources/oneapi-gpu.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,31 +265,6 @@ See :doc:`array_api` for details, instructions, and limitations. Example:
DPNP Arrays
~~~~~~~~~~~

As a special case, GPU arrays from |dpnp| can be used without enabling array API in estimators with :ref:`array API support <array_api_estimators>`, but note that using this alternative without array API enabled always involves data movement to host and back, thus not being the most efficient route in computational terms and **not recommended**.

Example:

.. code-block:: python

import numpy as np
import dpnp
from sklearnex import config_context
from sklearnex.linear_model import LinearRegression

rng = np.random.default_rng(seed=123)
X_np = rng.standard_normal(size=(100, 10), dtype=np.float32)
y_np = rng.standard_normal(size=100, dtype=np.float32)

X = dpnp.array(X_np, device="gpu")
y = dpnp.array(y_np, device="gpu")

model = LinearRegression()
model.fit(X, y)


Note that, if array API had been enabled, the snippet above would use the data as-is on the device where it resides, but without array API, it implies data movements using the SYCL queue contained by those objects.

.. note::
All the input data for an algorithm must reside on the same device when not using array API.
GPU arrays from |dpnp| are array-API-compliant and are used like any other array API framework: array API dispatch must be enabled through scikit-learn's ``config_context(array_api_dispatch=True)`` for the data to be consumed as-is on the device where it resides. Without array API enabled, |dpnp| arrays are treated as unsupported device inputs and moved to host for computation, with results returned as NumPy arrays.

When a |dpnp_array| on GPU is passed as input but the operation is not supported on GPU, the operation may be executed on CPU instead - see :doc:`config-contexts` for more details.
12 changes: 8 additions & 4 deletions examples/sklearnex/basic_statistics_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from dpctl import SyclQueue
from mpi4py import MPI

from sklearnex import config_context
from sklearnex.spmd.basic_statistics import BasicStatistics as BasicStatisticsSpmd


Expand Down Expand Up @@ -57,8 +58,11 @@ def generate_data(par, size, seed=777):
gtr_mean = np.mean(weighted_data, axis=0)
gtr_std = np.std(weighted_data, axis=0)

bss = BasicStatisticsSpmd(["mean", "standard_deviation"])
bss.fit(dpnp_data, dpnp_weights)
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
bss = BasicStatisticsSpmd(["mean", "standard_deviation"])
bss.fit(dpnp_data, dpnp_weights)

print(f"Computed mean on rank {rank}:\n", bss.mean_)
print(f"Computed std on rank {rank}:\n", bss.standard_deviation_)
print(f"Computed mean on rank {rank}:\n", bss.mean_)
print(f"Computed std on rank {rank}:\n", bss.standard_deviation_)
8 changes: 6 additions & 2 deletions examples/sklearnex/covariance_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import numpy as np
from mpi4py import MPI

from sklearnex import config_context
from sklearnex.spmd.covariance import EmpiricalCovariance


Expand All @@ -37,6 +38,9 @@ def get_data(data_seed):
X = get_data(rank)
dpnp_X = dpnp.asarray(X, usm_type="device", sycl_queue=q)

cov = EmpiricalCovariance().fit(dpnp_X)
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
cov = EmpiricalCovariance().fit(dpnp_X)

print(f"Computed covariance values on rank {rank}:\n", cov.covariance_)
print(f"Computed covariance values on rank {rank}:\n", cov.covariance_)
8 changes: 6 additions & 2 deletions examples/sklearnex/dbscan_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from mpi4py import MPI
from sklearn.datasets import load_digits

from sklearnex import config_context
from sklearnex.spmd.cluster import DBSCAN


Expand Down Expand Up @@ -57,6 +58,9 @@ def get_test_data(size):

dpnp_X = dpnp.asarray(X, usm_type="device", sycl_queue=queue)

model = DBSCAN(eps=3, min_samples=2).fit(dpnp_X)
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
model = DBSCAN(eps=3, min_samples=2).fit(dpnp_X)

print(f"Labels on rank {rank} (slice of 2):\n", model.labels_[:2])
print(f"Labels on rank {rank} (slice of 2):\n", model.labels_[:2])
44 changes: 25 additions & 19 deletions examples/sklearnex/incremental_basic_statistics_dpnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,41 @@
import dpctl
import dpnp

from sklearnex import config_context
from sklearnex.basic_statistics import IncrementalBasicStatistics

# We create GPU SyclQueue and then put data to dpnp arrays using
# the queue. It allows us to do computation on GPU.

queue = dpctl.SyclQueue("gpu")

incbs = IncrementalBasicStatistics(result_options=["mean", "max", "sum"])
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
incbs = IncrementalBasicStatistics(result_options=["mean", "max", "sum"])

# We do partial_fit for each batch and then print final result.
X_1 = dpnp.asarray([[0, 1], [0, 1]], sycl_queue=queue)
result = incbs.partial_fit(X_1)
# We do partial_fit for each batch and then print final result.
X_1 = dpnp.asarray([[0, 1], [0, 1]], sycl_queue=queue)
result = incbs.partial_fit(X_1)

X_2 = dpnp.asarray([[1, 2]], sycl_queue=queue)
result = incbs.partial_fit(X_2)
X_2 = dpnp.asarray([[1, 2]], sycl_queue=queue)
result = incbs.partial_fit(X_2)

X_3 = dpnp.asarray([[1, 1], [1, 2], [2, 3]], sycl_queue=queue)
result = incbs.partial_fit(X_3)
X_3 = dpnp.asarray([[1, 1], [1, 2], [2, 3]], sycl_queue=queue)
result = incbs.partial_fit(X_3)

print(f"Mean:\n{result.mean_}")
print(f"Max:\n{result.max_}")
print(f"Sum:\n{result.sum_}")
print(f"Mean:\n{result.mean_}")
print(f"Max:\n{result.max_}")
print(f"Sum:\n{result.sum_}")

# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
incbs = IncrementalBasicStatistics(result_options=["mean", "max", "sum"], batch_size=3)
X = dpnp.asarray([[0, 1], [0, 1], [1, 2], [1, 1], [1, 2], [2, 3]], sycl_queue=queue)
result = incbs.fit(X)
# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
incbs = IncrementalBasicStatistics(
result_options=["mean", "max", "sum"], batch_size=3
)
X = dpnp.asarray([[0, 1], [0, 1], [1, 2], [1, 1], [1, 2], [2, 3]], sycl_queue=queue)
result = incbs.fit(X)

print(f"Mean:\n{result.mean_}")
print(f"Max:\n{result.max_}")
print(f"Sum:\n{result.sum_}")
print(f"Mean:\n{result.mean_}")
print(f"Max:\n{result.max_}")
print(f"Sum:\n{result.sum_}")
18 changes: 11 additions & 7 deletions examples/sklearnex/incremental_covariance_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import numpy as np
from mpi4py import MPI

from sklearnex import config_context
from sklearnex.spmd.covariance import IncrementalEmpiricalCovariance


Expand All @@ -45,14 +46,17 @@ def get_local_data(data, comm):
X_local = get_local_data(X, comm)
X_split = np.array_split(X_local, num_batches)

cov = IncrementalEmpiricalCovariance()
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
cov = IncrementalEmpiricalCovariance()

# Partial fit is called for each batch on each GPU
# Partial fit is called for each batch on each GPU

for i in range(num_batches):
dpnp_X = dpnp.asarray(X_split[i], usm_type="device", sycl_queue=q)
cov.partial_fit(dpnp_X)
for i in range(num_batches):
dpnp_X = dpnp.asarray(X_split[i], usm_type="device", sycl_queue=q)
cov.partial_fit(dpnp_X)

# Finalization of results is performed in a lazy way after requesting results like in non-SPMD incremental estimators.
# Finalization of results is performed in a lazy way after requesting results like in non-SPMD incremental estimators.

print(f"Computed covariance values on rank {comm.Get_rank()}:\n", cov.covariance_)
print(f"Computed covariance values on rank {comm.Get_rank()}:\n", cov.covariance_)
64 changes: 35 additions & 29 deletions examples/sklearnex/incremental_linear_regression_dpnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,45 @@
import dpctl
import dpnp

from sklearnex import config_context
from sklearnex.linear_model import IncrementalLinearRegression

# We create GPU SyclQueue and then put data to dpnp arrays using
# the queue. It allows us to do computation on GPU.

queue = dpctl.SyclQueue("gpu")

inclin = IncrementalLinearRegression()

# We do partial_fit for each batch and then print final result.
X_1, y_1 = dpnp.asarray([[0, 1], [1, 2]], sycl_queue=queue), dpnp.asarray(
[2, 4], sycl_queue=queue
)
result = inclin.partial_fit(X_1, y_1)

X_2, y_2 = dpnp.asarray([[2, 3]], sycl_queue=queue), dpnp.asarray([6], sycl_queue=queue)
result = inclin.partial_fit(X_2, y_2)

X_3, y_3 = dpnp.asarray([[0, 2], [1, 3], [2, 4]], sycl_queue=queue), dpnp.asarray(
[3, 5, 7], sycl_queue=queue
)
result = inclin.partial_fit(X_3, y_3)

print(f"Coefs:\n{result.coef_}")
print(f"Intercept:\n{result.intercept_}")

# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
inclin = IncrementalLinearRegression(batch_size=3)
X, y = dpnp.asarray(
[[0, 1], [1, 2], [2, 3], [0, 2], [1, 3], [2, 4]], sycl_queue=queue
), dpnp.asarray([2, 4, 6, 3, 5, 7], sycl_queue=queue)
result = inclin.fit(X, y)

print(f"Coefs:\n{result.coef_}")
print(f"Intercept:\n{result.intercept_}")
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
inclin = IncrementalLinearRegression()

# We do partial_fit for each batch and then print final result.
X_1, y_1 = dpnp.asarray([[0, 1], [1, 2]], sycl_queue=queue), dpnp.asarray(
[2, 4], sycl_queue=queue
)
result = inclin.partial_fit(X_1, y_1)

X_2, y_2 = dpnp.asarray([[2, 3]], sycl_queue=queue), dpnp.asarray(
[6], sycl_queue=queue
)
result = inclin.partial_fit(X_2, y_2)

X_3, y_3 = dpnp.asarray([[0, 2], [1, 3], [2, 4]], sycl_queue=queue), dpnp.asarray(
[3, 5, 7], sycl_queue=queue
)
result = inclin.partial_fit(X_3, y_3)

print(f"Coefs:\n{result.coef_}")
print(f"Intercept:\n{result.intercept_}")

# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
inclin = IncrementalLinearRegression(batch_size=3)
X, y = dpnp.asarray(
[[0, 1], [1, 2], [2, 3], [0, 2], [1, 3], [2, 4]], sycl_queue=queue
), dpnp.asarray([2, 4, 6, 3, 5, 7], sycl_queue=queue)
result = inclin.fit(X, y)

print(f"Coefs:\n{result.coef_}")
print(f"Intercept:\n{result.intercept_}")
51 changes: 28 additions & 23 deletions examples/sklearnex/incremental_pca_dpnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import dpnp

# Import estimator via sklearnex's patch mechanism from sklearn
from sklearnex import patch_sklearn, sklearn_is_patched
from sklearnex import config_context, patch_sklearn, sklearn_is_patched

# IncrementalPCA is currently in preview module, so extra flag is required
patch_sklearn(preview=True)
Expand All @@ -39,32 +39,37 @@
# the queue. It allows us to do computation on GPU.
queue = dpctl.SyclQueue("gpu")

incpca = IncrementalPCA()
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
incpca = IncrementalPCA()

# We do partial_fit for each batch and then print final result.
X_1 = dpnp.asarray([[-1, -1], [-2, -1]], sycl_queue=queue)
result = incpca.partial_fit(X_1)
# We do partial_fit for each batch and then print final result.
X_1 = dpnp.asarray([[-1, -1], [-2, -1]], sycl_queue=queue)
result = incpca.partial_fit(X_1)

X_2 = dpnp.asarray([[-3, -2], [1, 1]], sycl_queue=queue)
result = incpca.partial_fit(X_2)
X_2 = dpnp.asarray([[-3, -2], [1, 1]], sycl_queue=queue)
result = incpca.partial_fit(X_2)

X_3 = dpnp.asarray([[2, 1], [3, 2]], sycl_queue=queue)
result = incpca.partial_fit(X_3)
X_3 = dpnp.asarray([[2, 1], [3, 2]], sycl_queue=queue)
result = incpca.partial_fit(X_3)

X = dpnp.concat((X_1, X_2, X_3))
transformed_X = incpca.transform(X)
X = dpnp.concat((X_1, X_2, X_3))
transformed_X = incpca.transform(X)

print(f"Principal components:\n{result.components_}")
print(f"Explained variance ratio:\n{result.explained_variance_ratio_}")
print(f"Transformed data:\n{transformed_X}")
print(f"Principal components:\n{result.components_}")
print(f"Explained variance ratio:\n{result.explained_variance_ratio_}")
print(f"Transformed data:\n{transformed_X}")

# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
incpca = IncrementalPCA(batch_size=3)
X = dpnp.asarray([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
result = incpca.fit(X)
transformed_X = incpca.transform(X)
# We put the whole data to fit method, it is split automatically and then
# partial_fit is called for each batch.
incpca = IncrementalPCA(batch_size=3)
X = dpnp.asarray(
[[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]], sycl_queue=queue
)
result = incpca.fit(X)
transformed_X = incpca.transform(X)

print(f"Principal components:\n{result.components_}")
print(f"Explained variance ratio:\n{result.explained_variance_ratio_}")
print(f"Transformed data:\n{transformed_X}")
print(f"Principal components:\n{result.components_}")
print(f"Explained variance ratio:\n{result.explained_variance_ratio_}")
print(f"Transformed data:\n{transformed_X}")
20 changes: 12 additions & 8 deletions examples/sklearnex/kmeans_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from mpi4py import MPI
from sklearn.datasets import load_digits

from sklearnex import config_context
from sklearnex.spmd.cluster import KMeans


Expand Down Expand Up @@ -53,15 +54,18 @@ def get_test_data(size):

dpnp_X = dpnp.asarray(X, usm_type="device", sycl_queue=queue)

model = KMeans(n_clusters=10).fit(dpnp_X)
# Array API dispatch keeps dpnp data on device throughout the computation.
# The SCIPY_ARRAY_API environment variable must also be set to enable this.
with config_context(array_api_dispatch=True):
model = KMeans(n_clusters=10).fit(dpnp_X)

print(f"Number of iterations on {rank}:\n", model.n_iter_)
print(f"Labels on rank {rank} (slice of 2):\n", model.labels_[:2])
print(f"Centers on rank {rank} (slice of 2):\n", model.cluster_centers_[:2, :])
print(f"Number of iterations on {rank}:\n", model.n_iter_)
print(f"Labels on rank {rank} (slice of 2):\n", model.labels_[:2])
print(f"Centers on rank {rank} (slice of 2):\n", model.cluster_centers_[:2, :])

X_test, _ = get_test_data(size)
dpnp_X_test = dpnp.asarray(X_test, usm_type="device", sycl_queue=queue)
X_test, _ = get_test_data(size)
dpnp_X_test = dpnp.asarray(X_test, usm_type="device", sycl_queue=queue)

result = model.predict(dpnp_X_test)
result = model.predict(dpnp_X_test)

print(f"Result labels on rank {rank} (slice of 5):\n", result[:5])
print(f"Result labels on rank {rank} (slice of 5):\n", result[:5])
Loading
Loading