Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ibm
GitHub Repository: ibm/watson-machine-learning-samples
Path: blob/master/cpd5.2/notebooks/python_sdk/deployments/python_function/Use Python function to recognize hand-written digits.ipynb
6412 views
Kernel: watsonx-ai-samples-py-312

Use Python function to recognize hand-written digits with ibm-watsonx-ai

Create and deploy a function that receives HTML canvas image data from a web app and then processes and sends that data to a model trained to recognize handwritten digits. See: MNIST function deployment tutorial

This notebook runs on Python 3.12.

Learning goals

The learning goals of this notebook are:

  • AI function definition

  • Store AI function

  • Deployment creation

Contents

This notebook contains the following parts:

  1. Setup

  2. Get an ID for a model deployment

  3. Get sample canvas data

  4. Create a deployable function

  5. Store and deploy the function

  6. Test the deployed function

  7. Clean up

  8. Summary

1. Set up the environment

Before you use the sample code in this notebook, you must perform the following setup tasks:

  • Contact your Cloud Pak for Data administrator and ask them for your account credentials

Install dependencies

Note: ibm-watsonx-ai documentation can be found here.

%pip install -U wget | tail -n 1 %pip install -U matplotlib | tail -n 1 %pip install -U ibm-watsonx-ai | tail -n 1
Successfully installed wget-3.2 Successfully installed contourpy-1.3.2 cycler-0.12.1 fonttools-4.58.4 kiwisolver-1.4.8 matplotlib-3.10.3 numpy-2.3.0 pillow-11.2.1 pyparsing-3.2.3 Successfully installed anyio-4.9.0 certifi-2025.6.15 charset_normalizer-3.4.2 h11-0.16.0 httpcore-1.0.9 httpx-0.28.1 ibm-cos-sdk-2.14.2 ibm-cos-sdk-core-2.14.2 ibm-cos-sdk-s3transfer-2.14.2 ibm-watsonx-ai-1.3.26 idna-3.10 jmespath-1.0.1 lomond-0.3.3 pandas-2.2.3 pytz-2025.2 requests-2.32.4 sniffio-1.3.1 tabulate-0.9.0 typing_extensions-4.14.0 tzdata-2025.2 urllib3-2.5.0

Define credentials

Authenticate the watsonx.ai Runtime service on IBM Cloud Pak for Data. You need to provide the admin's username and the platform url.

username = "PASTE YOUR USERNAME HERE" url = "PASTE THE PLATFORM URL HERE"

Use the admin's api_key to authenticate watsonx.ai Runtime services:

import getpass from ibm_watsonx_ai import Credentials credentials = Credentials( username=username, api_key=getpass.getpass("Enter your watsonx.ai API key and hit enter: "), url=url, instance_id="openshift", version="5.2", )

Alternatively you can use the admin's password:

import getpass from ibm_watsonx_ai import Credentials if "credentials" not in locals() or not credentials.api_key: credentials = Credentials( username=username, password=getpass.getpass("Enter your watsonx.ai password and hit enter: "), url=url, instance_id="openshift", version="5.2", )

Create APIClient instance

from ibm_watsonx_ai import APIClient client = APIClient(credentials)

Working with spaces

First of all, you need to create a space that will be used for your work. If you do not have space already created, you can use {PLATFORM_URL}/ml-runtime/spaces?context=icp4data to create one.

  • Click New Deployment Space

  • Create an empty space

  • Go to space Settings tab

  • Copy space_id and paste it below

Tip: You can also use SDK to prepare the space for your work. More information can be found here.

Action: Assign space ID below

space_id = "PASTE YOUR SPACE ID HERE"

You can use list method to print all existing spaces.

client.spaces.list(limit=10)

To be able to interact with all resources available in Watson Machine Learning, you need to set space which you will be using.

client.set.default_space(space_id)
'SUCCESS'

2. Get an ID for a model deployment

The deployed function created in this notebook is designed to send payload data to a TensorFlow model created with MNIST.

Option 1: Use your own, existing model deployment

If you already deployed a model while working through one of the following MNIST tutorials, you can use that model deployment:

Paste the model deployment ID in the following cell.

See: Looking up an online deployment ID

for x in client.deployments.get_details()["resources"]: if x["entity"]["name"] == "Scikit German Risk Deployment WML V4": deployment_id = x["metadata"]["id"]
model_deployment_id = ""

Option 2: Download, store, and deploy a sample model

You can deployed a sample model and get its deployment ID by running the code in the following four cells.

Download a sample model to the notebook working directory

import os import wget sample_saved_model_filename = "mnist-tf-model.tar.gz" url = f"https://github.com/IBM/watsonx-ai-samples/raw/master/cpd5.2/models/tensorflow/mnist/{sample_saved_model_filename}" if not os.path.isfile(sample_saved_model_filename): wget.download(url)

Look up software specification for the MNIST model

software_spec_id = client.software_specifications.get_id_by_name("runtime-25.1-py3.12")

Store the sample model in your watsonx.ai Runtime

metadata = { client.repository.ModelMetaNames.NAME: "Saved MNIST model", client.repository.ModelMetaNames.TYPE: "tensorflow_2.18", client.repository.ModelMetaNames.SOFTWARE_SPEC_UID: software_spec_id, } model_details = client.repository.store_model( model=sample_saved_model_filename, meta_props=metadata ) model_details
{'metadata': {'name': 'Saved MNIST model', 'space_id': '8a13841b-df99-4b4d-bf2a-161ad2e33980', 'resource_key': 'e7dd7bb9-8588-4c52-a85c-42fb37c1187e', 'id': '00e8e023-8153-4ef1-b499-14c59e6d884b', 'created_at': '2025-06-20T11:32:11Z', 'rov': {'member_roles': {'1000331001': {'user_iam_id': '1000331001', 'roles': ['OWNER']}}}, 'owner': '1000331001'}, 'entity': {'software_spec': {'id': 'f47ae1c3-198e-5718-b59d-2ea471561e9e'}, 'type': 'tensorflow_2.18'}}

Get published model ID

published_model_id = client.repository.get_model_id(model_details)

Deploy the stored model

metadata = { client.deployments.ConfigurationMetaNames.NAME: "MNIST saved model deployment", client.deployments.ConfigurationMetaNames.ONLINE: {}, } model_deployment_details = client.deployments.create( published_model_id, meta_props=metadata )
###################################################################################### Synchronous deployment creation for id: '00e8e023-8153-4ef1-b499-14c59e6d884b' started ###################################################################################### initializing Note: online_url is deprecated and will be removed in a future release. Use serving_urls instead. ........ ready ----------------------------------------------------------------------------------------------- Successfully finished deployment creation, deployment_id='e98b3f47-8f12-4701-ab4e-af075e4fb4b8' -----------------------------------------------------------------------------------------------

Get the ID of the model deployment just created

model_deployment_id = client.deployments.get_id(model_deployment_details) model_deployment_id
'e98b3f47-8f12-4701-ab4e-af075e4fb4b8'

3. Get sample canvas data

The deployed function created in this notebook is designed to accept RGBA image data from an HTML canvas object.

Run the following cells to download and view sample canvas data for testing the deployed function.

3.1 Download sample data file

Download the file containing the sample data

sample_canvas_data_filename = "mnist-html-canvas-image-data.json" url = f"https://github.com/IBM/watsonx-ai-samples/raw/master/cpd5.2/data/mnist/{sample_canvas_data_filename}" if not os.path.isfile(sample_canvas_data_filename): wget.download(url)

Load the sample data from the file into a variable

import json with open(sample_canvas_data_filename) as data_file: sample_canvas_data = json.load(data_file)

3.2 View sample data

View the raw contents of the sample data

print("Height (n): " + str(sample_canvas_data["height"]) + " pixels\n") print( "Num image data entries: " + str(len(sample_canvas_data["data"])) + " - (n * n * 4) elements - RGBA values\n" ) print( json.dumps(sample_canvas_data, indent=3)[:75] + "...\n" + json.dumps(sample_canvas_data, indent=3)[-50:] )
Height (n): 187 pixels Num image data entries: 139876 - (n * n * 4) elements - RGBA values { "data": [ 0, 0, 0, 0, 0, 0, ... 0, 0, 0 ], "height": 187 }

See what hand-drawn digit the sample data represents

import numpy as np import matplotlib.pyplot as plt rgba_arr = np.asarray(sample_canvas_data["data"]).astype("uint8") n = sample_canvas_data["height"] plt.figure() plt.imshow(rgba_arr.reshape(n, n, 4)) plt.xticks([]) plt.yticks([]) plt.show()
Image in a Jupyter notebook

4. Create a deployable function

The basics of creating and deploying functions in watsonx.ai.

4.1 Define the function

  1. Define a Python closure with an inner function named "score".

  2. Use default parameters to save your Watson Machine Learning credentials and the model deployment ID with the deployed function.

  3. Process the canvas data (reshape and normalize) and then send the processed data to the model deployment.

  4. Process the results from the model deployment so the deployed function returns simpler results.

  5. Implement error handling so the function will behave gracefully if there is an error.

parameters = { "credentials": credentials.to_dict(), "space_id": space_id, "model_deployment_id": model_deployment_id, } def my_deployable_function(params=parameters): import numpy as np from PIL import Image from ibm_watsonx_ai import APIClient def get_image_array(canvas_data): rgba_data, dimension = canvas_data["data"], canvas_data["height"] rgba_array = np.asarray(rgba_data).astype("uint8") return rgba_array.reshape(dimension, dimension, 4) def score(function_payload): try: # Read the payload received by the function canvas_data = function_payload["input_data"][0]["values"][0] # Create an array object with the required shape image_array = get_image_array(canvas_data) # Create an image object that can be resized img = Image.fromarray(image_array, "RGBA") # Resize the image to 28 x 28 pixels sm_img = img.resize((28, 28), Image.LANCZOS) # Get alpha channel of resized image alpha_arr = np.array(sm_img.split()[-1]).reshape((28, 28, 1)) # Create a payload to be sent to the model model_payload = {"input_data": [{"values": [alpha_arr]}]} client = APIClient( credentials=params["credentials"], space_id=params["space_id"] ) model_result = client.deployments.score( params["model_deployment_id"], model_payload ) return model_result except Exception as exc: return {"predictions": [{"values": [repr(exc)]}]} return score

4.2 Test locally

You can test your function in the notebook before deploying the function.

local_function = my_deployable_function() func_result = local_function({"input_data": [{"values": [sample_canvas_data]}]}) print(json.dumps(func_result, indent=2))
{ "predictions": [ { "id": "output_0", "values": [ [ 2.65692210632551e-06, 0.0010547457495704293, 3.4101554774679244e-05, 1.9494355001370423e-06, 0.9868835806846619, 0.00032408724655397236, 0.005543855484575033, 0.0006548759411089122, 0.004688874818384647, 0.0008112657815217972 ] ] } ] }

Visualize result

plt.bar(range(10), func_result["predictions"][0]["values"][0]) plt.title("Digit probability") plt.xlabel("Digit") plt.ylabel("Probability") plt.xticks(range(10)) plt.yscale("log") plt.show()
Image in a Jupyter notebook

5. Store and deploy the function

Before you can deploy the function, you must store the function in your watsonx.ai Runtime.

Look up software specification for the deployable function

software_spec_id = client.software_specifications.get_id_by_name("runtime-25.1-py3.12")

Store the deployable function in your watsonx.ai Runtime

meta_data = { client.repository.FunctionMetaNames.NAME: "MNIST deployable function", client.repository.FunctionMetaNames.SOFTWARE_SPEC_UID: software_spec_id, } function_details = client.repository.store_function( meta_props=meta_data, function=my_deployable_function )

Get published function ID

function_id = client.repository.get_function_id(function_details)

Deploy the stored function

metadata = { client.deployments.ConfigurationMetaNames.NAME: "MNIST function deployment", client.deployments.ConfigurationMetaNames.ONLINE: {}, } function_deployment_details = client.deployments.create( function_id, meta_props=metadata )
###################################################################################### Synchronous deployment creation for id: '8e8e71ad-7da1-4516-99a9-b5aa98c89e40' started ###################################################################################### initializing Note: online_url is deprecated and will be removed in a future release. Use serving_urls instead. ....... ready ----------------------------------------------------------------------------------------------- Successfully finished deployment creation, deployment_id='b23ea91c-93af-4809-a803-1daf930c5d6c' -----------------------------------------------------------------------------------------------

6. Test the deployed function

You can use the watsonx.ai Python client or REST API to send data to your function deployment for processing in exactly the same way you send data to model deployments for processing.

function_deployment_id = client.deployments.get_id(function_deployment_details) function_deployment_endpoint_url = client.deployments.get_scoring_href( function_deployment_details ) print(function_deployment_id) print(function_deployment_endpoint_url)
payload = {"input_data": [{"values": [sample_canvas_data]}]}

Score the deployment

result = client.deployments.score(function_deployment_id, payload) if "error" in result: print(result["error"]) else: print(result)
{'predictions': [{'id': 'output_0', 'values': [[2.65692210632551e-06, 0.0010547457495704293, 3.4101554774679244e-05, 1.9494355001370423e-06, 0.9868835806846619, 0.00032408724655397236, 0.005543855484575033, 0.0006548759411089122, 0.004688874818384647, 0.0008112657815217972]]}]}

Visualize result

plt.bar(range(10), func_result["predictions"][0]["values"][0]) plt.title("Digit probability") plt.xlabel("Digit") plt.ylabel("Probability") plt.xticks(range(10)) plt.yscale("log") plt.show()
Image in a Jupyter notebook

7. Clean up

If you want to clean up all created assets:

  • experiments

  • trainings

  • pipelines

  • model definitions

  • models

  • functions

  • deployments

please follow up this sample notebook.

8. Summary and next steps

In this notebook, you created a Python function that receives HTML canvas image data and then processes and sends that data to a model trained to recognize handwritten digits.

Check out our Online Documentation for more samples, tutorials, documentation, how-tos, and blog posts.

Authors

Sarah Packowski is a member of the IBM Watson Studio Content Design team in Canada.

Rafał Chrzanowski, Software Engineer Intern at watsonx.ai.

Copyright © 2018-2025 IBM. This notebook and its source code are released under the terms of the MIT License.