Skip to content

Commit f1f076c

Browse files
document serialization
1 parent 8bb05f0 commit f1f076c

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

doc/sources/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ See :ref:`oneapi_gpu` for other ways of executing on GPU.
170170
oneapi-gpu.rst
171171
config-contexts.rst
172172
array_api.rst
173+
serialization.rst
173174
distributed-mode.rst
174175
distributed_daal4py.rst
175176
non-scikit-algorithms.rst

doc/sources/oneapi-gpu.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ Running on GPU
8686

8787
|sklearnex| offers different options for running an algorithm on a specified device (e.g. a GPU):
8888

89+
90+
.. _target_offload:
91+
8992
Target offload option
9093
~~~~~~~~~~~~~~~~~~~~~
9194

@@ -130,6 +133,9 @@ Example:
130133
.. warning::
131134
When using ``target_offload``, operations on a fitted model must be executed under a context or global option with the same device or queue where the model was fitted - meaning: a model fitted on GPU cannot make predictions on CPU, and vice-versa. Note that upon serialization and subsequent deserialization of models, data is moved to the CPU.
132135

136+
.. hint::
137+
Serialization of model objects that used target offload will move data to CPU upon deserialization. See :doc:`serialization` for detail about serializing GPU models.
138+
133139
GPU arrays through array API
134140
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135141

@@ -192,6 +198,10 @@ See :doc:`array_api` for details, instructions, and limitations. Example:
192198
with config_context(array_api_dispatch=True):
193199
model.fit(X, y)
194200
201+
.. hint::
202+
If serialization of a GPU model is desired, use Torch tensors instead of DPNP arrays.
203+
See :doc:`serialization` for more information.
204+
195205
.. note::
196206
Not all estimator classes in the |sklearnex| support array API objects - see the list of :ref:`estimators with array API support <array_api_estimators>` for details.
197207

doc/sources/serialization.rst

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
.. Copyright contributors to the oneDAL project
2+
..
3+
.. Licensed under the Apache License, Version 2.0 (the "License");
4+
.. you may not use this file except in compliance with the License.
5+
.. You may obtain a copy of the License at
6+
..
7+
.. http://www.apache.org/licenses/LICENSE-2.0
8+
..
9+
.. Unless required by applicable law or agreed to in writing, software
10+
.. distributed under the License is distributed on an "AS IS" BASIS,
11+
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
.. See the License for the specific language governing permissions and
13+
.. limitations under the License.
14+
.. include:: substitutions.rst
15+
16+
==============================
17+
Model serialization (pickling)
18+
==============================
19+
20+
Serializing objects
21+
-------------------
22+
23+
Objects in Python are bound to the process that creates them. Usually, when it comes to statistical or machine learning models, one typically wants to save fitted models for later usage - for example, by fitting a model on a large machine, saving it to disk storage, and then serving it (making predictions on new data) on other smaller machines.
24+
25+
Just like other objects in Python, estimator objects from the |sklearnex| can be serialized / persisted / pickled using the built-in ``pickle`` module - for example:
26+
27+
.. code-block:: python
28+
29+
import pickle
30+
import numpy as np
31+
from sklearn.datasets import make_regression
32+
from sklearnex.linear_model import LinearRegression
33+
34+
X, y = make_regression()
35+
model = LinearRegression().fit(X, y)
36+
37+
model_file = "linear_model.pkl"
38+
with open(model_file, "wb") as output_file:
39+
pickle.dump(model, output_file)
40+
41+
with open(model_file, "rb") as input_file:
42+
model_deserialized = pickle.load(input_file)
43+
44+
np.testing.assert_array_equal(
45+
model_deserialized.predict(X),
46+
model.predict(X),
47+
)
48+
49+
.. hint:: Note that, while operations performed on CPU are usually deterministic and procedures involving random numbers allow controlling the seed, upon deserializing a model in a different machine, it is not guaranteed that outputs such as predictions on new data will be byte-by-byte reproducible due to differences in instructions sets supported by different CPUs, runtimes of backend libraries, and similar such nuances. Nevertheless, results from predictions of the same model on different machines with compatible environments (see next section) should be within numerical roundoff error.
50+
51+
Serialization requirements
52+
--------------------------
53+
54+
All estimator classes in the |sklearnex| that have a counterpart in |sklearn| (and thus participate in :ref:`patching <patching>`) inherit from that respective class from |sklearn|, and expose the same public attributes. Hence, in order to successfully serialize and deserialize a model from the |sklearnex|, it is necessary to satisfy all the requirements for serialization of |sklearn| objects, such as using the same |sklearn| version for serializing and deserializing the object - see :ref:`sklearn:pickle_persistence` for more details.
55+
56+
In addition to those requirements, additional conditions need to be met in order to ensure that serialization and deserialization of objects belonging to classes from the |sklearnex| will work correctly:
57+
58+
- The versions of both |sklearn| and the |sklearnex| must be the same for deserializing a given object as the versions used for serializing it.
59+
- The version of the :external+onedal:doc:`oneDAL <index>` backend used for the |sklearnex| (through Python package ``dal`` or ``daal`` depending on the installation medium) must be either the same or a higher minor version within the same major version series - for example, |onedal| version 2025.10 can deserialize models saved with 2025.8, but not the other way around, and version 2026.0 might not be able to deserialize models from 2025.x versions.
60+
- Other dependencies providing data classes that constitute object attributes, such as NumPy's arrays, must also be able to successfully serialize and deserialize in that same environment. Note that :ref:`array API classes <array_api>`, which might be used as object attributes when enabling this mode, might have tighter serialization requirements than NumPy.
61+
- The Python major version must be the same, and the minor version must be either the same or higher.
62+
63+
Just like in |sklearn|, in order to ensure that deserialized models work correctly, it is highly recommended to recreate the same environment that created the serialized model in terms of python versions, package versions, and configurations of packages (e.g. build variants in the case of conda-managed environments).
64+
65+
.. warning:: Note that, unlike objects from |sklearn|, objects from the |sklearnex| will not necessarily issue a warning when deserializing them with an incompatible library version.
66+
67+
Serialization of GPU models
68+
---------------------------
69+
70+
Be aware that if using the :ref:`target offload option <target_offload>` to fit models on GPU or on another SYCL device, upon deserialization of those models, the internal data behind them will be re-created on host (CPU), hence the deserialized models will become CPU/host ones and will not be able to make predictions on GPU data.
71+
72+
If persistence of GPU-only models is desired, one can instead use :ref:`array API classes with GPU support <array_api>`, which might have a different logic for serialization that preserves the device.
73+
74+
Currently, the only array API library with SYCL support known to provide serializable GPU arrays is `PyTorch <https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html>`__.
75+
76+
.. warning:: If serialization of models is desired, avoid usage of |dpnp| GPU arrays as they are not serilizable.
77+
78+
Example:
79+
80+
.. code-block:: python
81+
82+
import os
83+
os.environ["SCIPY_ARRAY_API"] = "1"
84+
85+
import pickle
86+
import torch
87+
import numpy as np
88+
from sklearn.datasets import make_regression
89+
from sklearnex import config_context
90+
from sklearnex.linear_model import LinearRegression
91+
92+
X_np, y_np = make_regression()
93+
X = torch.tensor(X_np, dtype=torch.float32, device="xpu")
94+
y = torch.tensor(y_np, dtype=torch.float32, device="xpu")
95+
96+
with config_context(array_api_dispatch=True):
97+
model = LinearRegression().fit(X, y)
98+
pred_fresh = model.predict(X)
99+
100+
assert isinstance(pred_fresh, torch.Tensor)
101+
102+
model_deserialized = pickle.loads( pickle.dumps(model) )
103+
with config_context(array_api_dispatch=True):
104+
pred_deserialized = model_deserialized.predict(X)
105+
106+
np.testing.assert_allclose(
107+
pred_fresh.cpu().numpy(),
108+
pred_deserialized.cpu().numpy(),
109+
)
110+
111+
Configurations are not serializable
112+
-----------------------------------
113+
114+
Be aware that serialization of model objects does not imply saving of global or local configurations. For example, a model that was fitted to :ref:`array API classes <array_api>` will have those same array API classes as attributes, but array API mode is not enabled by default in |sklearn| (and by extension, not in the |sklearnex| either). Hence, if the :ref:`global configuration <config_contexts>` was modified to enable array API support, the deserialized model might not be usable in a new Python process until that setting (array API) is enabled.
115+
116+
Likewise, other process-level internal settings, such as efficiency parameters that are modifiable through static class methods of estimators (currently undocumented), are not saved along with a model object, since they are not managed by it.

0 commit comments

Comments
 (0)