From b25fd5931cd05b5e196d826452eee9d71d82d447 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 21 May 2024 23:46:06 +0900 Subject: [PATCH 1/4] fix ML example --- .../machinelearning-python-bentoml/README.md | 142 ++++++++++++------ .../iris_classifier.py | 17 ++- .../machinelearning-python-bentoml/main.py | 14 +- 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/code-samples/community/serving/machinelearning-python-bentoml/README.md b/code-samples/community/serving/machinelearning-python-bentoml/README.md index b3975314fd..b1e46fe522 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/README.md +++ b/code-samples/community/serving/machinelearning-python-bentoml/README.md @@ -39,15 +39,18 @@ as API endpoint with Knative Serving. API, which is the entry point for accessing this machine learning service. ```python - from bentoml import env, artifacts, api, BentoService - from bentoml.handlers import DataframeHandler - from bentoml.artifact import SklearnModelArtifact + import bentoml + import joblib - @env(auto_pip_dependencies=True) - @artifacts([SklearnModelArtifact('model')]) - class IrisClassifier(BentoService): - @api(DataframeHandler) + @bentoml.service + class IrisClassifier: + iris_model = bentoml.models.get("iris_classifier:latest") + + def __init__(self): + self.model = joblib.load(self.iris_model.path_of("model.pkl")) + + @bentoml.api def predict(self, df): return self.artifacts.model.predict(df) ``` @@ -58,10 +61,11 @@ as API endpoint with Knative Serving. given data and then save the model with BentoML to local disk. ```python + import joblib from sklearn import svm from sklearn import datasets - from iris_classifier import IrisClassifier + import bentoml if __name__ == "__main__": # Load training data @@ -72,14 +76,9 @@ as API endpoint with Knative Serving. clf = svm.SVC(gamma='scale') clf.fit(X, y) - # Create a iris classifier service instance - iris_classifier_service = IrisClassifier() - - # Pack the newly trained model artifact - iris_classifier_service.pack('model', clf) - - # Save the prediction service to disk for model serving - saved_path = iris_classifier_service.save() + with bentoml.models.create("iris_classifier") as bento_model: + joblib.dump(clf, bento_model.path_of("model.pkl")) + print(f"Model saved: {bento_model}") ``` 1. Run the `main.py` file to train and save the model: @@ -91,48 +90,97 @@ as API endpoint with Knative Serving. 1. Use BentoML CLI to check saved model's information. ```bash - bentoml get IrisClassifier:latest + bentoml get iris_classifier:latest ``` Example: ```bash - > bentoml get IrisClassifier:latest + > bentoml get iris_classifier:latest -o json { - "name": "IrisClassifier", - "version": "20200305171229_0A1411", - "uri": { - "type": "LOCAL", - "uri": "/Users/bozhaoyu/bentoml/repository/IrisClassifier/20200305171229_0A1411" + "service": "iris_classifier:IrisClassifier", + "name": "iris_classifier", + "version": "ar67rxqxqcrqi7ol", + "bentoml_version": "1.2.16", + "creation_time": "2024-05-21T14:40:20.737900+00:00", + "labels": { + "owner": "bentoml-team", + "project": "gallery" }, - "bentoServiceMetadata": { + "models": [], + "runners": [], + "entry_service": "IrisClassifier", + "services": [ + { + "name": "IrisClassifier", + "service": "", + "models": [ + { + "tag": "iris_sklearn:ml5evdaxpwrqi7ol", + "module": "", + "creation_time": "2024-05-21T14:21:17.070059+00:00" + } + ], + "dependencies": [], + "config": {} + } + ], + "envs": [], + "schema": { "name": "IrisClassifier", - "version": "20200305171229_0A1411", - "createdAt": "2020-03-06T01:12:49.431011Z", - "env": { - "condaEnv": "name: bentoml-IrisClassifier\nchannels:\n- defaults\ndependencies:\n- python=3.7.3\n- pip\n", - "pipDependencies": "bentoml==0.6.2\nscikit-learn", - "pythonVersion": "3.7.3" - }, - "artifacts": [ - { - "name": "model", - "artifactType": "SklearnModelArtifact" - } - ], - "apis": [ + "type": "service", + "routes": [ { "name": "predict", - "handlerType": "DataframeHandler", - "docs": "BentoService API", - "handlerConfig": { - "orient": "records", - "typ": "frame", - "input_dtypes": null, - "output_orient": "records" + "route": "/predict", + "batchable": false, + "input": { + "properties": { + "df": { + "title": "Df" + } + }, + "required": [ + "df" + ], + "title": "Input", + "type": "object" + }, + "output": { + "title": "AnyIODescriptor" } } ] + }, + "apis": [], + "docker": { + "distro": "debian", + "python_version": "3.11", + "cuda_version": null, + "env": null, + "system_packages": null, + "setup_script": null, + "base_image": null, + "dockerfile_template": null + }, + "python": { + "requirements_txt": "./requirements.txt", + "packages": null, + "lock_packages": true, + "pack_git_packages": true, + "index_url": null, + "no_index": null, + "trusted_host": null, + "find_links": null, + "extra_index_url": null, + "pip_args": null, + "wheels": null + }, + "conda": { + "environment_yml": null, + "channels": null, + "dependencies": null, + "pip": null } } ``` @@ -141,7 +189,7 @@ as API endpoint with Knative Serving. BentoML CLI command to start an API server locally and test it with the `curl` command. ```bash - bentoml serve IrisClassifier:latest + bentoml serve iris_classifier:latest ``` In another terminal window, make `curl` request with sample data to the API server @@ -166,7 +214,7 @@ a Dockerfile is automatically generated when saving the model. ```bash # jq might not be installed on your local system, please follow jq install # instruction at https://stedolan.github.io/jq/download/ - saved_path=$(bentoml get IrisClassifier:latest -q | jq -r ".uri.uri") + saved_path=$(bentoml get iris_classifier:latest -q -o json | jq -r ".uri.uri") # Build and push the container on your local machine. docker buildx build --platform linux/arm64,linux/amd64 -t "{username}/iris-classifier" --push $saved_path diff --git a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py index c94c386f84..3fa0c1f734 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py +++ b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py @@ -1,11 +1,14 @@ -from bentoml import env, artifacts, api, BentoService -from bentoml.handlers import DataframeHandler -from bentoml.artifact import SklearnModelArtifact +import bentoml +import joblib -@env(auto_pip_dependencies=True) -@artifacts([SklearnModelArtifact('model')]) -class IrisClassifier(BentoService): - @api(DataframeHandler) +@bentoml.service +class IrisClassifier: + iris_model = bentoml.models.get("iris_sklearn:latest") + + def __init__(self): + self.model = joblib.load(self.iris_model.path_of("model.pkl")) + + @bentoml.api def predict(self, df): return self.artifacts.model.predict(df) diff --git a/code-samples/community/serving/machinelearning-python-bentoml/main.py b/code-samples/community/serving/machinelearning-python-bentoml/main.py index b5bb8c0c72..7ab891d346 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/main.py +++ b/code-samples/community/serving/machinelearning-python-bentoml/main.py @@ -1,7 +1,8 @@ +import joblib from sklearn import svm from sklearn import datasets -from iris_classifier import IrisClassifier +import bentoml if __name__ == "__main__": # Load training data @@ -12,11 +13,6 @@ clf = svm.SVC(gamma='scale') clf.fit(X, y) - # Create a iris classifier service instance - iris_classifier_service = IrisClassifier() - - # Pack the newly trained model artifact - iris_classifier_service.pack('model', clf) - - # Save the prediction service to disk for model serving - saved_path = iris_classifier_service.save() + with bentoml.models.create("iris_classifier") as bento_model: + joblib.dump(clf, bento_model.path_of("model.pkl")) + print(f"Model saved: {bento_model}") From 623853be67cd4909d4d957958ea1b89a70bade84 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 21 May 2024 23:59:30 +0900 Subject: [PATCH 2/4] fix dockerize --- .../serving/machinelearning-python-bentoml/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/code-samples/community/serving/machinelearning-python-bentoml/README.md b/code-samples/community/serving/machinelearning-python-bentoml/README.md index b1e46fe522..2c2a796848 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/README.md +++ b/code-samples/community/serving/machinelearning-python-bentoml/README.md @@ -212,12 +212,8 @@ a Dockerfile is automatically generated when saving the model. username and run the following commands. ```bash - # jq might not be installed on your local system, please follow jq install - # instruction at https://stedolan.github.io/jq/download/ - saved_path=$(bentoml get iris_classifier:latest -q -o json | jq -r ".uri.uri") - # Build and push the container on your local machine. - docker buildx build --platform linux/arm64,linux/amd64 -t "{username}/iris-classifier" --push $saved_path + bentoml containerize iris_classifier:latest -t "{username}/iris-classifier" --push ``` 1. In `service.yaml`, replace `{username}` with your Docker hub username: From 19f1eb32a3690ce52faffcbdb56446e77f517879 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 22 May 2024 00:46:06 +0900 Subject: [PATCH 3/4] type variable df --- .../machinelearning-python-bentoml/iris_classifier.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py index 3fa0c1f734..324208bf47 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py +++ b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py @@ -1,4 +1,8 @@ +import numpy as np import bentoml +from pydantic import Field +from bentoml.validators import Shape +from typing_extensions import Annotated import joblib @@ -10,5 +14,10 @@ def __init__(self): self.model = joblib.load(self.iris_model.path_of("model.pkl")) @bentoml.api - def predict(self, df): + def predict( + self, + df: Annotated[np.ndarray, Shape((-1, 4))] = Field( + default=[[5.2, 2.3, 5.0, 0.7]] + ), + ) -> np.ndarray: return self.artifacts.model.predict(df) From bdcff2407b28e47dbb8dffd300acd31a663b2f85 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 22 May 2024 01:11:06 +0900 Subject: [PATCH 4/4] fix code --- .../serving/machinelearning-python-bentoml/iris_classifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py index 324208bf47..dc880d5309 100644 --- a/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py +++ b/code-samples/community/serving/machinelearning-python-bentoml/iris_classifier.py @@ -20,4 +20,4 @@ def predict( default=[[5.2, 2.3, 5.0, 0.7]] ), ) -> np.ndarray: - return self.artifacts.model.predict(df) + return self.model.predict(df)