Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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