Hot Swap Models Tutorial

How to swap models in a pipeline step while the pipeline is deployed.

This tutorial and the assets can be downloaded as part of the Wallaroo Tutorials repository.

Model Hot Swap Tutorial

One of the biggest challenges facing organizations once they have a model trained is deploying the model: Getting all of the resources together, MLOps configured and systems prepared to allow inferences to run.

The next biggest challenge? Replacing the model while keeping the existing production systems running.

This tutorial demonstrates how Wallaroo model hot swap can update a pipeline step with a new model with one command. This lets organizations keep their production systems running while changing a ML model, with the change taking only milliseconds, and any inference requests in that time are processed after the hot swap is completed.

This example and sample data comes from the Machine Learning Group’s demonstration on Credit Card Fraud detection.

This tutorial provides the following:

  • ccfraud.onnx: A pre-trained ML model used to detect potential credit card fraud.
  • xgboost_ccfraud.onnx: A pre-trained ML model used to detect potential credit card fraud originally converted from an XGBoost model. This will be used to swap with the ccfraud.onnx.
  • smoke_test.json: A data file used to verify that the model will return a low possibility of credit card fraud.
  • high_fraud.json: A data file used to verify that the model will return a high possibility of credit card fraud.
  • Sample inference data files: Data files used for inference examples with the following number of records:
    • cc_data_5.json: 5 records.
    • cc_data_1k.json: 1,000 records.
    • cc_data_10k.json: 10,000 records.
    • cc_data_40k.json: Over 40,000 records.

Reference

For more information about Wallaroo and related features, see the Wallaroo Documentation Site.

Steps

The following steps demonstrate the following:

  • Connect to a Wallaroo instance.
  • Create a workspace and pipeline.
  • Upload both models to the workspace.
  • Deploy the pipe with the ccfraud.onnx model as a pipeline step.
  • Perform sample inferences.
  • Hot swap and replace the existing model with the xgboost_ccfraud.onnx while keeping the pipeline deployed.
  • Conduct additional inferences to demonstrate the model hot swap was successful.
  • Undeploy the pipeline and return the resources back to the Wallaroo instance.

Load the Libraries

Load the Python libraries used to connect and interact with the Wallaroo instance.

import wallaroo
from wallaroo.object import EntityNotFoundError

import pandas as pd

# used to display dataframe information without truncating
pd.set_option('display.max_colwidth', None)  

Arrow Support

As of the 2023.1 release, Wallaroo provides support for dataframe and Arrow for inference inputs. This tutorial allows users to adjust their experience based on whether they have enabled Arrow support in their Wallaroo instance or not.

If Arrow support has been enabled, arrowEnabled=True. If disabled or you’re not sure, set it to arrowEnabled=False

The examples below will be shown in an arrow enabled environment.

import os
# Only set the below to make the OS environment ARROW_ENABLED to TRUE.  Otherwise, leave as is.
# os.environ["ARROW_ENABLED"]="True"

if "ARROW_ENABLED" not in os.environ or os.environ["ARROW_ENABLED"].casefold() == "False".casefold():
    arrowEnabled = False
else:
    arrowEnabled = True
print(arrowEnabled)
True

Open a Connection to Wallaroo

The first step is to connect to Wallaroo through the Wallaroo client.

This is accomplished using the wallaroo.Client(api_endpoint, auth_endpoint, auth_type command) command that connects to the Wallaroo instance services.

The Client method takes the following parameters:

  • api_endpoint (String): The URL to the Wallaroo instance API service.
  • auth_endpoint (String): The URL to the Wallaroo instance Keycloak service.
  • auth_type command (String): The authorization type. In this case, SSO.

The URLs are based on the Wallaroo Prefix and Wallaroo Suffix for the Wallaroo instance. For more information, see the DNS Integration Guide. In the example below, replace “YOUR PREFIX” and “YOUR SUFFIX” with the Wallaroo Prefix and Suffix, respectively.

If connecting from within the Wallaroo instance’s JupyterHub service, then only wl = wallaroo.Client() is required.

Once run, the wallaroo.Client command provides a URL to grant the SDK permission to your specific Wallaroo environment. When displayed, enter the URL into a browser and confirm permissions. Depending on the configuration of the Wallaroo instance, the user will either be presented with a login request to the Wallaroo instance or be authenticated through a broker such as Google, Github, etc. To use the broker, select it from the list under the username/password login forms. For more information on Wallaroo authentication configurations, see the Wallaroo Authentication Configuration Guides.

# Internal Login

wl = wallaroo.Client()

# Remote Login

# wallarooPrefix = "YOUR PREFIX"
# wallarooSuffix = "YOUR SUFFIX"

# wl = wallaroo.Client(api_endpoint=f"https://{wallarooPrefix}.api.{wallarooSuffix}", 
#                     auth_endpoint=f"https://{wallarooPrefix}.keycloak.{wallarooSuffix}", 
#                     auth_type="sso")

Set the Variables

The following variables are used in the later steps for creating the workspace, pipeline, and uploading the models. Modify them according to your organization’s requirements.

Just for the sake of this tutorial, we’ll use the SDK below to create our workspace , assign as our current workspace, then display all of the workspaces we have at the moment. We’ll also set up for our models and pipelines down the road, so we have one spot to change names to whatever fits your organization’s standards best.

To allow this tutorial to be run multiple times or by multiple users in the same Wallaroo instance, a random 4 character prefix will be added to the workspace, pipeline, and model.

import string
import random

# make a random 4 character prefix
prefix= ''.join(random.choice(string.ascii_lowercase) for i in range(4))

workspace_name = f'{prefix}hotswapworkspace'
pipeline_name = f'{prefix}hotswappipeline'
original_model_name = f'{prefix}ccfraudoriginal'
original_model_file_name = './ccfraud.onnx'
replacement_model_name = f'{prefix}ccfraudreplacement'
replacement_model_file_name = './xgboost_ccfraud.onnx'
def get_workspace(name):
    workspace = None
    for ws in wl.list_workspaces():
        if ws.name() == name:
            workspace= ws
    if(workspace == None):
        workspace = wl.create_workspace(name)
    return workspace

def get_pipeline(name):
    try:
        pipeline = wl.pipelines_by_name(pipeline_name)[0]
    except EntityNotFoundError:
        pipeline = wl.build_pipeline(pipeline_name)
    return pipeline

Create the Workspace

We will create a workspace based on the variable names set above, and set the new workspace as the current workspace. This workspace is where new pipelines will be created in and store uploaded models for this session.

Once set, the pipeline will be created.

workspace = get_workspace(workspace_name)

wl.set_current_workspace(workspace)

pipeline = get_pipeline(pipeline_name)
pipeline
name ggwzhotswappipeline
created 2023-02-27 17:33:53.541871+00:00
last_updated 2023-02-27 17:33:53.541871+00:00
deployed (none)
tags
versions ad943ff6-1a38-4304-a243-6958ba118df2
steps

Upload Models

We can now upload both of the models. In a later step, only one model will be added as a pipeline step, where the pipeline will submit inference requests to the pipeline.

original_model = wl.upload_model(original_model_name , original_model_file_name)
replacement_model = wl.upload_model(replacement_model_name , replacement_model_file_name)
wl.list_models()
Name # of Versions Owner ID Last Updated Created At
ggwzccfraudreplacement 1 "" 2023-02-27 17:33:55.531013+00:00 2023-02-27 17:33:55.531013+00:00
ggwzccfraudoriginal 1 "" 2023-02-27 17:33:55.147884+00:00 2023-02-27 17:33:55.147884+00:00

Add Model to Pipeline Step

With the models uploaded, we will add the original model as a pipeline step, then deploy the pipeline so it is available for performing inferences.

pipeline.add_model_step(original_model)
pipeline
name ggwzhotswappipeline
created 2023-02-27 17:33:53.541871+00:00
last_updated 2023-02-27 17:33:53.541871+00:00
deployed (none)
tags
versions ad943ff6-1a38-4304-a243-6958ba118df2
steps
pipeline.deploy()
name ggwzhotswappipeline
created 2023-02-27 17:33:53.541871+00:00
last_updated 2023-02-27 17:33:58.263239+00:00
deployed True
tags
versions 3078dffa-4e10-41ef-85bc-e7a0de5afa82, ad943ff6-1a38-4304-a243-6958ba118df2
steps ggwzccfraudoriginal
pipeline.status()
{'status': 'Running',
 'details': [],
 'engines': [{'ip': '10.244.0.30',
   'name': 'engine-7bb46d756-2v7j7',
   'status': 'Running',
   'reason': None,
   'details': [],
   'pipeline_statuses': {'pipelines': [{'id': 'ggwzhotswappipeline',
      'status': 'Running'}]},
   'model_statuses': {'models': [{'name': 'ggwzccfraudoriginal',
      'version': '55fe0137-2e0b-4c7d-9cf2-6521b526339e',
      'sha': 'bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507',
      'status': 'Running'}]}}],
 'engine_lbs': [{'ip': '10.244.0.29',
   'name': 'engine-lb-ddd995646-wjg4k',
   'status': 'Running',
   'reason': None,
   'details': []}],
 'sidekicks': []}

Verify the Model

The pipeline is deployed with our model. The following will verify that the model is operating correctly. The high_fraud.json file contains data that the model should process as a high likelihood of being a fraudulent transaction.

if arrowEnabled is True:
    result = pipeline.infer_from_file('./data/smoke_test.df.json')
else:
    result = pipeline.infer_from_file('./data/smoke_test.json')
display(result)
time out.dense_1 check_failures metadata.last_model
0 1677519249926 [0.0014974177] [] {"model_name":"ggwzccfraudoriginal","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}

Replace the Model

The pipeline is currently deployed and is able to handle inferences. The model will now be replaced without having to undeploy the pipeline. This is done using the pipeline method replace_with_model_step(index, model). Steps start at 0, so the method called below will replace step 0 in our pipeline with the replacement model.

As an exercise, this deployment can be performed while inferences are actively being submitted to the pipeline to show how quickly the swap takes place.

pipeline.replace_with_model_step(0, replacement_model).deploy()
name ggwzhotswappipeline
created 2023-02-27 17:33:53.541871+00:00
last_updated 2023-02-27 17:34:11.049248+00:00
deployed True
tags
versions a620354f-291e-4a98-b5f7-9d8bf165b1df, 3078dffa-4e10-41ef-85bc-e7a0de5afa82, ad943ff6-1a38-4304-a243-6958ba118df2
steps ggwzccfraudoriginal
# Display the pipeline
pipeline.status()
{'status': 'Running',
 'details': [],
 'engines': [{'ip': '10.244.0.30',
   'name': 'engine-7bb46d756-2v7j7',
   'status': 'Running',
   'reason': None,
   'details': [],
   'pipeline_statuses': {'pipelines': [{'id': 'ggwzhotswappipeline',
      'status': 'Running'}]},
   'model_statuses': {'models': [{'name': 'ggwzccfraudoriginal',
      'version': '55fe0137-2e0b-4c7d-9cf2-6521b526339e',
      'sha': 'bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507',
      'status': 'Running'}]}}],
 'engine_lbs': [{'ip': '10.244.0.29',
   'name': 'engine-lb-ddd995646-wjg4k',
   'status': 'Running',
   'reason': None,
   'details': []}],
 'sidekicks': []}

Verify the Swap

To verify the swap, we’ll submit a set of inferences to the pipeline using the new model. We’ll display just the first 5 rows for space reasons.

if arrowEnabled is True:
    result = pipeline.infer_from_file('./data/cc_data_1k.df.json')
    display(result.loc[0:4,:])
else:
    result = pipeline.infer_from_file('./data/cc_data_1k.json')
    display(result)
time out.dense_1 check_failures metadata.last_model
0 1677519255319 [0.99300325] [] {"model_name":"ggwzccfraudreplacement","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}
1 1677519255319 [0.99300325] [] {"model_name":"ggwzccfraudreplacement","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}
2 1677519255319 [0.99300325] [] {"model_name":"ggwzccfraudreplacement","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}
3 1677519255319 [0.99300325] [] {"model_name":"ggwzccfraudreplacement","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}
4 1677519255319 [0.0010916889] [] {"model_name":"ggwzccfraudreplacement","model_sha":"bc85ce596945f876256f41515c7501c399fd97ebcb9ab3dd41bf03f8937b4507"}

Undeploy the Pipeline

With the tutorial complete, the pipeline is undeployed to return the resources back to the Wallaroo instance.

pipeline.undeploy()
name ggwzhotswappipeline
created 2023-02-27 17:33:53.541871+00:00
last_updated 2023-02-27 17:34:11.049248+00:00
deployed False
tags
versions a620354f-291e-4a98-b5f7-9d8bf165b1df, 3078dffa-4e10-41ef-85bc-e7a0de5afa82, ad943ff6-1a38-4304-a243-6958ba118df2
steps ggwzccfraudoriginal