From dfd6f18302507496ef70d2c6f7475574cda5c56f Mon Sep 17 00:00:00 2001 From: bojiang Date: Wed, 2 Nov 2022 15:26:29 +0800 Subject: [PATCH 1/8] doc(example): monitoring example for classification tasks --- .../task_classification/.bentoignore | 4 + .../monitoring/task_classification/README.md | 158 ++++++++++++++++++ .../task_classification/bentofile.yaml | 10 ++ .../task_classification/deployment.yaml | 2 + .../task_classification/requirements.txt | 3 + .../monitoring/task_classification/service.py | 43 +++++ .../monitoring/task_classification/train.py | 21 +++ 7 files changed, 241 insertions(+) create mode 100644 examples/monitoring/task_classification/.bentoignore create mode 100644 examples/monitoring/task_classification/README.md create mode 100644 examples/monitoring/task_classification/bentofile.yaml create mode 100644 examples/monitoring/task_classification/deployment.yaml create mode 100644 examples/monitoring/task_classification/requirements.txt create mode 100644 examples/monitoring/task_classification/service.py create mode 100644 examples/monitoring/task_classification/train.py diff --git a/examples/monitoring/task_classification/.bentoignore b/examples/monitoring/task_classification/.bentoignore new file mode 100644 index 0000000000..f4e455d1cb --- /dev/null +++ b/examples/monitoring/task_classification/.bentoignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +*$py.class +.ipynb_checkpoints diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md new file mode 100644 index 0000000000..39bf7e6bd0 --- /dev/null +++ b/examples/monitoring/task_classification/README.md @@ -0,0 +1,158 @@ +# BentoML monitoring example for classification tasks + +This is a sample project demonstrating basic monitoring usage of [BentoML](https://github.com/bentoml). + +In this project, we will train a classifier model using Scikit-learn and the Iris dataset, build +an prediction service for serving the trained model with monitoring enabled, and deploy the +model server as a docker image for production deployment. + +### Install Dependencies + +Install python packages required for running this project: +```bash +pip install -r ./requirements.txt +``` + +### Model Training + +Just a simple Iris classification example. + +```bash +import bentoml +from sklearn import svm, datasets + +# Load training data +iris = datasets.load_iris() +X, y = iris.data, iris.target + +# Model Training +clf = svm.SVC() +clf.fit(X, y) + +# Save model to BentoML local model store +bentoml.sklearn.save_model("iris_clf", clf) +``` + +### Serving the model +Copy this to a `service.py` file, and run your service with Bento Server locally: + +```bash +bentoml serve service.py:svc --reload +``` + +Open your web browser at http://127.0.0.1:3000 to view the Bento UI for sending test requests. + +You may also send request with `curl` command or any HTTP client, e.g.: + +```bash +curl -X POST -H "content-type: application/json" --data "[[5.9, 3, 5.1, 1.8]]" http://127.0.0.1:3000/classify +``` + + +Then you can find the exported data under the `./monitoring//data` directory. +Here's the example output: + +```json +{"timestamp": "2022-11-02T12:38:38.701396", "request_id": 8781503815303167270, "sepal length": 5.9, "sepal width": 3.0, "petal length": 1.4, "petal width": 0.2, "pred": "0"} +{"timestamp": "2022-11-02T12:38:48.345552", "request_id": 14419506828678509143, "sepal length": 4.9, "sepal width": 3.0, "petal length": 1.4, "petal width": 0.2, "pred": "0"} +``` + + +### Customizing the monitoring + +You can customize the monitoring by modifying the config file of bentoml. The default is: + +```yaml +monitoring: + enabled: true + type: default + options: + output_dir: ./monitoring +``` + +You can draft your own bentoml config file `deployment.yaml` and change the `output_dir` to any directory you want. You can also use other monitoring solutions by changing the `type` to your desired handler. For example, if you want to use the `arize` handler, you can change the config to: + +```yaml +monitoring: + enabled: true + type: bentoml_plugins.arize.ArizeMonitor + options: + api_key: + space_key: +``` + +Then you can specify the config file through environment variable `BENTOML_CONFIG`: +```bash +BENTOML_CONFIG=deployment.yaml bentoml serve service.py:svc +``` + + +### Containerized Serving with monitoring + +Bento is the distribution format in BentoML which captures all the source code, model files, config +files and dependency specifications required for running the service for production deployment. Think +of it as Docker/Container designed for machine learning models. + +To begin with building Bento, create a `bentofile.yaml` under your project directory: + +```yaml +service: "service.py:svc" +labels: + owner: bentoml-team + project: gallery +include: +- "*.py" +python: + packages: + - scikit-learn + - pandas +``` + +Next, run `bentoml build` from current directory to start the Bento build: + +``` +> bentoml build + +05/05/2022 19:19:16 INFO [cli] Building BentoML service "iris_classifier:5wtigdwm4kwzduqj" from build context "/Users/bentoml/workspace/gallery/quickstart" +05/05/2022 19:19:16 INFO [cli] Packing model "iris_clf:4i7wbngm4crhpuqj" from "/Users/bentoml/bentoml/models/iris_clf/4i7wbngm4crhpuqj" +05/05/2022 19:19:16 INFO [cli] Successfully saved Model(tag="iris_clf:4i7wbngm4crhpuqj", + path="/var/folders/bq/gdsf0kmn2k1bf880r_l238600000gn/T/tmp26dx354ubentoml_bento_iris_classifier/models/iris_clf/4i7wbngm4crhpuqj/") +05/05/2022 19:19:16 INFO [cli] Locking PyPI package versions.. +05/05/2022 19:19:17 INFO [cli] + ██████╗░███████╗███╗░░██╗████████╗░█████╗░███╗░░░███╗██╗░░░░░ + ██╔══██╗██╔════╝████╗░██║╚══██╔══╝██╔══██╗████╗░████║██║░░░░░ + ██████╦╝█████╗░░██╔██╗██║░░░██║░░░██║░░██║██╔████╔██║██║░░░░░ + ██╔══██╗██╔══╝░░██║╚████║░░░██║░░░██║░░██║██║╚██╔╝██║██║░░░░░ + ██████╦╝███████╗██║░╚███║░░░██║░░░╚█████╔╝██║░╚═╝░██║███████╗ + ╚═════╝░╚══════╝╚═╝░░╚══╝░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚══════╝ + +05/05/2022 19:19:17 INFO [cli] Successfully built Bento(tag="iris_classifier:5wtigdwm4kwzduqj") at "/Users/bentoml/bentoml/bentos/iris_classifier/5wtigdwm4kwzduqj/" +``` + +A new Bento is now built and saved to local Bento store. You can view and manage it via +`bentoml list`,`bentoml get` and `bentoml delete` CLI command. + +Then we will convert a Bento into a Docker image containing the HTTP model server. + +Make sure you have docker installed and docker deamon running, and run the following commnand: + +```bash +bentoml containerize iris_classifier:latest +``` + +This will build a new docker image with all source code, model files and dependencies in place, +and ready for production deployment. To start a container with this docker image locally, run: + +```bash +docker run -p 3000:3000 iris_classifier:invwzzsw7li6zckb2ie5eubhd --mount type=bind,source=,target=/bento/monitoring +``` + +## What's Next? + +- 👉 [Pop into our Slack community!](https://l.linklyhq.com/l/ktO8) We're happy to help with any issue you face or even just to meet you and hear what you're working on. +- Dive deeper into the [Core Concepts](https://docs.bentoml.org/en/latest/concepts/index.html) in BentoML +- Learn how to use BentoML with other ML Frameworks at [Frameworks Guide](https://docs.bentoml.org/en/latest/frameworks/index.html) or check out other [gallery projects](https://github.com/bentoml/BentoML/tree/main/examples) +- Learn more about model deployment options for Bento: + - [🦄️ Yatai](https://github.com/bentoml/Yatai): Model Deployment at scale on Kubernetes + - [🚀 bentoctl](https://github.com/bentoml/bentoctl): Fast model deployment on any cloud platform + diff --git a/examples/monitoring/task_classification/bentofile.yaml b/examples/monitoring/task_classification/bentofile.yaml new file mode 100644 index 0000000000..749288a228 --- /dev/null +++ b/examples/monitoring/task_classification/bentofile.yaml @@ -0,0 +1,10 @@ +service: "service.py:svc" +labels: + owner: bentoml-team + project: gallery +include: +- "*.py" +python: + packages: + - scikit-learn + - pandas diff --git a/examples/monitoring/task_classification/deployment.yaml b/examples/monitoring/task_classification/deployment.yaml new file mode 100644 index 0000000000..5302969a14 --- /dev/null +++ b/examples/monitoring/task_classification/deployment.yaml @@ -0,0 +1,2 @@ +monitoring: + enabled: true diff --git a/examples/monitoring/task_classification/requirements.txt b/examples/monitoring/task_classification/requirements.txt new file mode 100644 index 0000000000..98ac078404 --- /dev/null +++ b/examples/monitoring/task_classification/requirements.txt @@ -0,0 +1,3 @@ +scikit-learn +pandas +bentoml>=1.0.0 diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py new file mode 100644 index 0000000000..bdb6221284 --- /dev/null +++ b/examples/monitoring/task_classification/service.py @@ -0,0 +1,43 @@ +import numpy as np + +import bentoml +from bentoml.io import NumpyNdarray + +iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner() + +svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) + + +@svc.api( + input=NumpyNdarray.from_sample(np.array([[4.9, 3.0, 1.4, 0.2]], dtype=np.double)), + output=NumpyNdarray(), +) +async def classify(input_series: np.ndarray) -> np.ndarray: + with bentoml.monitor("iris_classifier_prediction") as mon: + mon.log( + data=input_series[0][0], + name="sepal length", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][1], + name="sepal width", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][2], + name="petal length", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][3], + name="petal width", + role="feature", + data_type="numerical", + ) + result = await iris_clf_runner.predict.async_run(input_series) + mon.log(result[0], name="pred", role="prediction", data_type="categorical") + return result diff --git a/examples/monitoring/task_classification/train.py b/examples/monitoring/task_classification/train.py new file mode 100644 index 0000000000..2f1bc08239 --- /dev/null +++ b/examples/monitoring/task_classification/train.py @@ -0,0 +1,21 @@ +import logging + +from sklearn import svm +from sklearn import datasets + +import bentoml + +logging.basicConfig(level=logging.WARN) + +if __name__ == "__main__": + # Load training data + iris = datasets.load_iris() + X, y = iris.data, iris.target + + # Model Training + clf = svm.SVC() + clf.fit(X, y) + + # Save model to BentoML local model store + saved_model = bentoml.sklearn.save_model("iris_clf", clf) + print(f"Model saved: {saved_model}") From 3edeac8293b33b1cde7d164024488d212f048932 Mon Sep 17 00:00:00 2001 From: bojiang <5886138+bojiang@users.noreply.github.com> Date: Thu, 3 Nov 2022 11:53:23 +0800 Subject: [PATCH 2/8] Update examples/monitoring/task_classification/README.md Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- examples/monitoring/task_classification/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md index 39bf7e6bd0..2a67918a97 100644 --- a/examples/monitoring/task_classification/README.md +++ b/examples/monitoring/task_classification/README.md @@ -15,7 +15,7 @@ pip install -r ./requirements.txt ### Model Training -Just a simple Iris classification example. +Create an Iris classifier and save it with `bentoml.sklearn`: ```bash import bentoml From fce8ecc8869a24b506f59bccd86f542401ff5f74 Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 12:03:38 +0800 Subject: [PATCH 3/8] add service code to README --- .../monitoring/task_classification/README.md | 54 ++++++++++++++++++- .../monitoring/task_classification/service.py | 10 +++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md index 2a67918a97..6e9357b141 100644 --- a/examples/monitoring/task_classification/README.md +++ b/examples/monitoring/task_classification/README.md @@ -34,7 +34,59 @@ bentoml.sklearn.save_model("iris_clf", clf) ``` ### Serving the model -Copy this to a `service.py` file, and run your service with Bento Server locally: +Draft a `service.py` file with monitoring data collection lines, and run your service with Bento Server locally: + +```python +import numpy as np + +import bentoml +from bentoml.io import NumpyNdarray + +CLASS_NAMES = ["setosa", "versicolor", "virginica"] + +iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner() +svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) + + +@svc.api( + input=NumpyNdarray.from_sample(np.array([[4.9, 3.0, 1.4, 0.2]], dtype=np.double)), + output=NumpyNdarray(), +) +async def classify(input_series: np.ndarray) -> np.ndarray: + with bentoml.monitor("iris_classifier_prediction") as mon: + mon.log( + data=input_series[0][0], + name="sepal length", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][1], + name="sepal width", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][2], + name="petal length", + role="feature", + data_type="numerical", + ) + mon.log( + input_series[0][3], + name="petal width", + role="feature", + data_type="numerical", + ) + result = await iris_clf_runner.predict.async_run(input_series) + mon.log( + CLASS_NAMES[result[0]], + name="pred", + role="prediction", + data_type="categorical", + ) + return result +``` ```bash bentoml serve service.py:svc --reload diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py index bdb6221284..fbd6102d6a 100644 --- a/examples/monitoring/task_classification/service.py +++ b/examples/monitoring/task_classification/service.py @@ -3,8 +3,9 @@ import bentoml from bentoml.io import NumpyNdarray -iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner() +CLASS_NAMES = ["setosa", "versicolor", "virginica"] +iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner() svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) @@ -39,5 +40,10 @@ async def classify(input_series: np.ndarray) -> np.ndarray: data_type="numerical", ) result = await iris_clf_runner.predict.async_run(input_series) - mon.log(result[0], name="pred", role="prediction", data_type="categorical") + mon.log( + CLASS_NAMES[result[0]], + name="pred", + role="prediction", + data_type="categorical", + ) return result From 1cc60913184cc40fcc217d0e6acf32fbc8276659 Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 19:25:43 +0800 Subject: [PATCH 4/8] update to single service --- .../monitoring/task_classification/README.md | 22 ++++++++++--------- .../monitoring/task_classification/service.py | 22 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md index 6e9357b141..7adae2074b 100644 --- a/examples/monitoring/task_classification/README.md +++ b/examples/monitoring/task_classification/README.md @@ -40,6 +40,7 @@ Draft a `service.py` file with monitoring data collection lines, and run your se import numpy as np import bentoml +from bentoml.io import Text from bentoml.io import NumpyNdarray CLASS_NAMES = ["setosa", "versicolor", "virginica"] @@ -49,43 +50,44 @@ svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) @svc.api( - input=NumpyNdarray.from_sample(np.array([[4.9, 3.0, 1.4, 0.2]], dtype=np.double)), - output=NumpyNdarray(), + input=NumpyNdarray.from_sample(np.array([4.9, 3.0, 1.4, 0.2], dtype=np.double)), + output=Text(), ) async def classify(input_series: np.ndarray) -> np.ndarray: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log( - data=input_series[0][0], + mon.log_batch( + data=input_series[0], name="sepal length", role="feature", data_type="numerical", ) mon.log( - input_series[0][1], + input_series[1], name="sepal width", role="feature", data_type="numerical", ) mon.log( - input_series[0][2], + input_series[2], name="petal length", role="feature", data_type="numerical", ) mon.log( - input_series[0][3], + input_series[3], name="petal width", role="feature", data_type="numerical", ) - result = await iris_clf_runner.predict.async_run(input_series) + result = await iris_clf_runner.predict.async_run([input_series])[0] + category = CLASS_NAMES[result] mon.log( - CLASS_NAMES[result[0]], + category, name="pred", role="prediction", data_type="categorical", ) - return result + return category ``` ```bash diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py index fbd6102d6a..736202c6f6 100644 --- a/examples/monitoring/task_classification/service.py +++ b/examples/monitoring/task_classification/service.py @@ -1,6 +1,7 @@ import numpy as np import bentoml +from bentoml.io import Text from bentoml.io import NumpyNdarray CLASS_NAMES = ["setosa", "versicolor", "virginica"] @@ -10,40 +11,41 @@ @svc.api( - input=NumpyNdarray.from_sample(np.array([[4.9, 3.0, 1.4, 0.2]], dtype=np.double)), - output=NumpyNdarray(), + input=NumpyNdarray.from_sample(np.array([4.9, 3.0, 1.4, 0.2], dtype=np.double)), + output=Text(), ) async def classify(input_series: np.ndarray) -> np.ndarray: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log( - data=input_series[0][0], + mon.log_batch( + data=input_series[0], name="sepal length", role="feature", data_type="numerical", ) mon.log( - input_series[0][1], + input_series[1], name="sepal width", role="feature", data_type="numerical", ) mon.log( - input_series[0][2], + input_series[2], name="petal length", role="feature", data_type="numerical", ) mon.log( - input_series[0][3], + input_series[3], name="petal width", role="feature", data_type="numerical", ) - result = await iris_clf_runner.predict.async_run(input_series) + result = await iris_clf_runner.predict.async_run([input_series])[0] + category = CLASS_NAMES[result] mon.log( - CLASS_NAMES[result[0]], + category, name="pred", role="prediction", data_type="categorical", ) - return result + return category From fc6166868c6aa676a8191f44fd4ddaee7befd36b Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 19:28:15 +0800 Subject: [PATCH 5/8] fix --- examples/monitoring/task_classification/README.md | 4 ++-- examples/monitoring/task_classification/service.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md index 7adae2074b..f91186df94 100644 --- a/examples/monitoring/task_classification/README.md +++ b/examples/monitoring/task_classification/README.md @@ -55,8 +55,8 @@ svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) ) async def classify(input_series: np.ndarray) -> np.ndarray: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log_batch( - data=input_series[0], + mon.log( + input_series[0], name="sepal length", role="feature", data_type="numerical", diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py index 736202c6f6..eec8d196d3 100644 --- a/examples/monitoring/task_classification/service.py +++ b/examples/monitoring/task_classification/service.py @@ -16,8 +16,8 @@ ) async def classify(input_series: np.ndarray) -> np.ndarray: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log_batch( - data=input_series[0], + mon.log( + input_series[0], name="sepal length", role="feature", data_type="numerical", From 0f85b4729efc00b47d4fd456175074feecf26d39 Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 19:29:54 +0800 Subject: [PATCH 6/8] amend --- examples/monitoring/task_classification/service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py index eec8d196d3..49630c80b2 100644 --- a/examples/monitoring/task_classification/service.py +++ b/examples/monitoring/task_classification/service.py @@ -40,7 +40,8 @@ async def classify(input_series: np.ndarray) -> np.ndarray: role="feature", data_type="numerical", ) - result = await iris_clf_runner.predict.async_run([input_series])[0] + results = await iris_clf_runner.predict.async_run([input_series]) + result = results[0] category = CLASS_NAMES[result] mon.log( category, From a26b6150ba474edd544b817be9400a02fc00a9df Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 20:02:56 +0800 Subject: [PATCH 7/8] add locustfile --- .../task_classification/locustfile.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/monitoring/task_classification/locustfile.py diff --git a/examples/monitoring/task_classification/locustfile.py b/examples/monitoring/task_classification/locustfile.py new file mode 100644 index 0000000000..d324b81260 --- /dev/null +++ b/examples/monitoring/task_classification/locustfile.py @@ -0,0 +1,33 @@ +import numpy as np +from locust import task +from locust import between +from locust import HttpUser +from sklearn import datasets + +test_data = datasets.load_iris().data +num_of_rows = test_data.shape[0] + + +class IrisHttpUser(HttpUser): + """ + Usage: + Run the iris_classifier service in production mode: + + bentoml serve-http iris_classifier:latest --production + + Start locust load testing client with: + + locust --class-picker -H http://localhost:3000 + + Open browser at http://0.0.0.0:8089, adjust desired number of users and spawn + rate for the load test from the Web UI and start swarming. + """ + + @task + def classify(self): + index = np.random.choice(num_of_rows - 1) + + input_data = test_data[index] + self.client.post("/classify", json=input_data.tolist()) + + wait_time = between(0.01, 2) From 491f3cf2be438347e86832b49533126a3e166c09 Mon Sep 17 00:00:00 2001 From: bojiang Date: Thu, 3 Nov 2022 20:05:39 +0800 Subject: [PATCH 8/8] styles --- .../monitoring/task_classification/README.md | 42 +++++-------------- .../monitoring/task_classification/service.py | 41 ++++-------------- 2 files changed, 19 insertions(+), 64 deletions(-) diff --git a/examples/monitoring/task_classification/README.md b/examples/monitoring/task_classification/README.md index f91186df94..00b4bdcc77 100644 --- a/examples/monitoring/task_classification/README.md +++ b/examples/monitoring/task_classification/README.md @@ -53,40 +53,18 @@ svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner]) input=NumpyNdarray.from_sample(np.array([4.9, 3.0, 1.4, 0.2], dtype=np.double)), output=Text(), ) -async def classify(input_series: np.ndarray) -> np.ndarray: +async def classify(features: np.ndarray) -> str: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log( - input_series[0], - name="sepal length", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[1], - name="sepal width", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[2], - name="petal length", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[3], - name="petal width", - role="feature", - data_type="numerical", - ) - result = await iris_clf_runner.predict.async_run([input_series])[0] + mon.log(features[0], name="sepal length", role="feature", data_type="numerical") + mon.log(features[1], name="sepal width", role="feature", data_type="numerical") + mon.log(features[2], name="petal length", role="feature", data_type="numerical") + mon.log(features[3], name="petal width", role="feature", data_type="numerical") + + results = await iris_clf_runner.predict.async_run([features]) + result = results[0] category = CLASS_NAMES[result] - mon.log( - category, - name="pred", - role="prediction", - data_type="categorical", - ) + + mon.log(category, name="pred", role="prediction", data_type="categorical") return category ``` diff --git a/examples/monitoring/task_classification/service.py b/examples/monitoring/task_classification/service.py index 49630c80b2..735324a8fc 100644 --- a/examples/monitoring/task_classification/service.py +++ b/examples/monitoring/task_classification/service.py @@ -14,39 +14,16 @@ input=NumpyNdarray.from_sample(np.array([4.9, 3.0, 1.4, 0.2], dtype=np.double)), output=Text(), ) -async def classify(input_series: np.ndarray) -> np.ndarray: +async def classify(features: np.ndarray) -> str: with bentoml.monitor("iris_classifier_prediction") as mon: - mon.log( - input_series[0], - name="sepal length", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[1], - name="sepal width", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[2], - name="petal length", - role="feature", - data_type="numerical", - ) - mon.log( - input_series[3], - name="petal width", - role="feature", - data_type="numerical", - ) - results = await iris_clf_runner.predict.async_run([input_series]) + mon.log(features[0], name="sepal length", role="feature", data_type="numerical") + mon.log(features[1], name="sepal width", role="feature", data_type="numerical") + mon.log(features[2], name="petal length", role="feature", data_type="numerical") + mon.log(features[3], name="petal width", role="feature", data_type="numerical") + + results = await iris_clf_runner.predict.async_run([features]) result = results[0] category = CLASS_NAMES[result] - mon.log( - category, - name="pred", - role="prediction", - data_type="categorical", - ) + + mon.log(category, name="pred", role="prediction", data_type="categorical") return category