# Model Serialization and Persistence

This notebook demonstrates how to save and load cuML models using various serialization methods, including pickle, joblib, and cross-platform deployment strategies.

## Security Warning

**Only unpickle or deserialize models from trusted sources.**

The `pickle` module (and by extension `joblib`) **is not secure**. Malicious pickle data can execute arbitrary code during deserialization, potentially compromising your entire system.

> ⚠️ **Never unpickle data that could have come from an untrusted source, or that could have been tampered with.**

This warning applies to all serialization methods demonstrated in this notebook, including:

- `pickle.load()` and `pickle.loads()`
- `joblib.load()`
- Any file-based model loading

For more information, see the [Python pickle security documentation](https://docs.python.org/3/library/pickle.html).

## Single GPU Model Serialization

All single-GPU cuML estimators support serialization using standard Python libraries. This section demonstrates:

1. **Training a model** on synthetic data
2. **Saving the model** using pickle and joblib
3. **Loading the model** for future use

Trained single-GPU models can also be used for distributed inference on Dask clusters, as shown in the [Distributed Model Serialization](#Distributed-Model-Serialization) section.

```none
[1]:
```

**Recommendation:** Use Pickle protocol 5 for better performance with large arrays and models. Protocol 5 provides significant speed improvements for NumPy arrays and cuML models with large parameter sets.

```none
[2]:
```

**Important:** The model can be restored using pickle, but requires the same cuML version used for training. If you need to load models across different cuML versions, consider using the [scikit-learn conversion](#Converting-Between-cuML-and-scikit-learn-Models) approach instead.

```none
[3]:
```

```none
[3]:
```

## Using joblib for Model Serialization

joblib is an optimized alternative to pickle for machine learning models, offering:

- **Better performance** for large NumPy arrays and cuML models
- **Efficient compression** for models with many parameters
- **Memory mapping** for faster loading of large models
- **Optimized serialization** specifically designed for ML workloads

**Note:** While pickle and joblib files are often compatible, we recommend using the same library for both saving and loading to ensure reliability.

```none
[4]:
```

```none
[4]:
```

Then reload the model with joblib.

```none
[5]:
```

```none
[5]:
```

## Distributed Model Serialization

When working with distributed cuML models using Dask, the distributed estimator wrappers in `cuml.dask` are not designed to be pickled directly. Instead, cuML provides a specialized workflow:

### Workflow Steps

1. **Extract the combined model**: Use `get_combined_model()` to extract a single-GPU version of the trained distributed model
2. **Serialize the combined model**: Save the extracted model using pickle or joblib (same as any cuML model)
3. **Flexible inference**: Use the saved model in multiple ways:
   - **Single-GPU inference**: Load directly for single-GPU predictions
   - **Distributed inference**: Use `ParallelPostFit` from [Dask-ML](https://ml.dask.org/meta-estimators.html) to distribute inference across a Dask cluster

This approach allows you to choose the optimal resources for both training and inference phases.

```none
[6]:
```

```none
[7]:
```

Now we can save it with pickle like before, but we have to *combine* it into a non-distributed model first.

```none
[8]:
```

And we can reload this model just like before.

```none
[9]:
```

```none
[9]:
```

## Converting Between cuML and scikit-learn Models

Many cuML estimators provide `as_sklearn()` and `from_sklearn()` methods for seamless conversion between cuML and scikit-learn formats.

### Use Cases

- **Cross-platform deployment**: Train on GPU systems, deploy on CPU-only machines
- **Maximum compatibility**: Use standard scikit-learn serialization tools
- **Hybrid workflows**: Mix cuML and scikit-learn in the same pipeline
- **Legacy integration**: Convert existing scikit-learn models to cuML for GPU acceleration

This approach eliminates the need to install cuML on deployment machines while maintaining model compatibility.

```none
[10]:
```

We can convert this cuML model into a native scikit-learn estimator using the `as_sklearn()` method. This enables standard scikit-learn serialization and deployment on any Python environment.

```none
[11]:
```

The pickled scikit-learn model can be loaded and executed on any Python environment with only scikit-learn installed – no cuML or GPU required.

> ⚠️ **Security reminder:** Only load pickle files from trusted sources. See [Security Warning](#Security-Warning) above.
```none
[12]:
```

You can also reconstruct a cuML model from a scikit-learn model using `from_sklearn()`. This is particularly useful for:

- **Pre-trained models**: Convert existing scikit-learn models for GPU acceleration
- **Performance optimization**: Run faster inference on GPU hardware
- **Hybrid workflows**: Switch between CPU and GPU execution as needed

```none
[13]:
```

## Exporting Random Forest Models for CPU-Only Deployment

You can export cuML Random Forest models for deployment on machines without NVIDIA GPUs using the [Treelite](https://github.com/dmlc/treelite) library.

### Benefits

- **CPU-only deployment**: Run trained models on any machine
- **Optimized inference**: Treelite provides highly optimized CPU inference
- **Small footprint**: No cuML or GPU dependencies required
- **Production ready**: Efficient serialization and fast loading

### Export Process

1. **Convert to Treelite format**: Use `as_treelite()` to transform your cuML Random Forest model
2. **Serialize the model**: Call `.serialize()` to create a portable checkpoint file
3. **Deploy anywhere**: Install Treelite on the target machine and load the model for inference

```none
[14]:
```

### Deployment Steps

1. **Copy the checkpoint file**: Transfer `checkpoint.tl` to your target machine
2. **Install Treelite**: Run `pip install treelite` or `conda install -c conda-forge treelite`
   - No NVIDIA GPUs required
   - No cuML installation needed
3. **Load and use the model**: Run the code below on the target machine

```none
[15]:
```

## Exporting to ONNX

cuML models can be exported to the [ONNX](https://onnx.ai/) format using [sklearn-onnx](https://onnx.ai/sklearn-onnx/). Use `as_sklearn()` to convert the cuML model to a scikit-learn estimator, then pass it to `skl2onnx.convert_sklearn()`. The resulting `.onnx` file can be loaded with [ONNX Runtime](https://onnxruntime.ai/) for inference on both CPU and GPU, with no cuML dependency at inference time.

Not all estimators are supported by `sklearn-onnx` — see the [supported model list](https://onnx.ai/sklearn-onnx/supported.html) for details. UMAP, HDBSCAN, and some other estimators are not supported.

Under `cuml.accel`, proxy objects are recognized by `sklearn-onnx` directly without needing `as_sklearn()`. See the [cuml.accel ONNX example](../cuml-accel/examples/onnx_export/index.html.md) for details.

> Note: due to a known upstream incompatibility in `onnxruntime` version 1.26.0, predictions from a serialized `RandomForestClassifier` will be incorrect (see [microsoft/onnxruntime#28557](https://github.com/microsoft/onnxruntime/issues/28557)). We recommend using version 1.25.1 until the issue is resolved.

### Export Process

Train with cuML, convert to scikit-learn with `as_sklearn()`, then export to ONNX.

```none
[16]:
```

### Inference with ONNX Runtime

The saved `.onnx` file can be loaded on any machine with `onnxruntime` installed — no cuML or `sklearn-onnx` needed. Use `onnxruntime` for CPU inference, or `onnxruntime-gpu` for GPU inference.

> Note: due to a known upstream incompatibility in `onnxruntime` version 1.26.0, predictions from a serialized `RandomForestClassifier` will be incorrect (see [microsoft/onnxruntime#28557](https://github.com/microsoft/onnxruntime/issues/28557)). We recommend using version 1.25.1 until the issue is resolved.
```none
[17]:
```
