{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "erfOlc-T8kY3" }, "source": [ "# **BentoML Example: Image Segmentation with PaddleHub**\n", "**BentoML makes moving trained ML models to production easy:**\n", "\n", "\n", "\n", "* Package models trained with any ML framework and reproduce them for model serving in production\n", "* **Deploy anywhere** for online API serving or offline batch serving\n", "* High-Performance API model server with adaptive micro-batching support\n", "* Central hub for managing models and deployment process via Web UI and APIs\n", "* Modular and flexible design making it adaptable to your infrastrcuture\n", "\n", "BentoML is a framework for serving, managing, and deploying machine learning models. It is aiming to bridge the gap between Data Science and DevOps, and enable teams to deliver prediction services in a fast, repeatable, and scalable way.\n", "\n", "Before reading this example project, be sure to check out the [Getting started guide](https://github.com/bentoml/BentoML/blob/master/guides/quick-start/bentoml-quick-start-guide.ipynb) to learn about the basic concepts in BentoML.\n", "\n", "This notebook demonstrates how to use BentoML to turn a paddlepaddle model into a docker image containing a REST API server serving this model, how to use your ML service built with BentoML as a CLI tool, and how to distribute it a pypi package.\n", "\n", "The example is based on [this tutorial](https://www.paddlepaddle.org.cn/documentation/docs/en/1.5/beginners_guide/basics/fit_a_line/README.html), using dataset from the [UCI Machine Learning Repository](https://www.kaggle.com/schirmerchad/bostonhoustingmlnd)" ] }, { "cell_type": "markdown", "metadata": { "id": "ISK47vLPKCT9" }, "source": [ "This example notebook is base on the guide from PaddleHub: https://github.com/PaddlePaddle/PaddleHub/blob/release/v2.0/docs/docs_en/quick_experience/python_use_hub_en.md" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "54jFhiru8NWO" }, "outputs": [], "source": [ "%reload_ext autoreload\n", "%autoreload 2\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "XHOPuMGm-Nl2" }, "outputs": [], "source": [ "!pip3 install -q bentoml paddlepaddle paddlehub" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "KXz5IFU94P9D", "outputId": "ff7dd4b0-1835-4590-b041-06e725753e95" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/home/chvu/.local/lib/python3.8/site-packages/paddle/fluid/layers/utils.py:26: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\n", "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", " def convert_to_list(value, n, name, dtype=np.int):\n", "/usr/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl/html5lib/_trie/_base.py:3: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working\n", "\u001b[32m[2021-04-23 23:47:19,039] [ INFO]\u001b[0m - Module deeplabv3p_xception65_humanseg already installed in /home/chvu/.paddlehub/modules/deeplabv3p_xception65_humanseg\u001b[0m\n" ] } ], "source": [ "!hub install deeplabv3p_xception65_humanseg" ] }, { "cell_type": "markdown", "metadata": { "id": "bWx5VF_LLTef" }, "source": [ "## Prepare Input Data" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "yayroXhE-sos", "outputId": "4cd26532-19cc-42cc-b09c-4c0b80969e93" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2021-04-23 23:47:23-- https://paddlehub.bj.bcebos.com/resources/test_image.jpg\n", "Resolving paddlehub.bj.bcebos.com (paddlehub.bj.bcebos.com)... 103.235.46.61, 2409:8c00:6c21:10ad:0:ff:b00e:67d\n", "Connecting to paddlehub.bj.bcebos.com (paddlehub.bj.bcebos.com)|103.235.46.61|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 967120 (944K) [image/jpeg]\n", "Saving to: ‘test_image.jpg.1’\n", "\n", "test_image.jpg.1 100%[===================>] 944.45K 313KB/s in 3.0s \n", "\n", "2021-04-23 23:47:29 (313 KB/s) - ‘test_image.jpg.1’ saved [967120/967120]\n", "\n" ] } ], "source": [ "!wget https://paddlehub.bj.bcebos.com/resources/test_image.jpg" ] }, { "cell_type": "markdown", "metadata": { "id": "zcrHdbJxAHh0" }, "source": [ "## Create BentoService with PaddleHub Module Instantiation" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "s_T8YQRjALqg", "outputId": "45a9642d-a6d2-4403-9b09-c6ba1df93b97" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting paddlehub_service.py\n" ] } ], "source": [ "%%writefile paddlehub_service.py\n", "import paddlehub as hub\n", "import paddle\n", "import bentoml\n", "from bentoml import env, artifacts, api, BentoService, web_static_content\n", "import imageio\n", "from bentoml.adapters import ImageInput\n", "\n", "\n", "@env(infer_pip_packages=True)\n", "class PaddleHubService(bentoml.BentoService):\n", " def __init__(self):\n", " super(PaddleHubService, self).__init__()\n", " self.module = hub.Module(name=\"deeplabv3p_xception65_humanseg\")\n", "\n", " @api(input=ImageInput(), batch=True)\n", " def predict(self, images):\n", " results = self.module.segmentation(images=images, visualization=True)\n", " return [result['data'] for result in results]\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ESc4D_muCWNx", "outputId": "5b0a93b5-bc50-4ed5-b6d9-94954a8c5dc5" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/chvu/.local/lib/python3.8/site-packages/paddle/fluid/layers/utils.py:26: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\n", "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", " def convert_to_list(value, n, name, dtype=np.int):\n", "/usr/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl/html5lib/_trie/_base.py:3: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working\n", "\u001b[33m[2021-04-23 23:47:32,628] [ WARNING]\u001b[0m - The _initialize method in HubModule will soon be deprecated, you can use the __init__() to handle the initialization of the object\u001b[0m\n" ] } ], "source": [ "# Import the custom BentoService defined above\n", "from paddlehub_service import PaddleHubService\n", "import numpy as np\n", "import cv2\n", "\n", "bento_svc = PaddleHubService()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "ondQXpNCy_TV" }, "outputs": [], "source": [ "# Predict with the initialized module\n", "image = cv2.imread(\"test_image.jpg\")\n", "images = [image]\n", "segmentation_results = bento_svc.predict(images)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "jNnyhPQt59ey" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[WARNING 2021-04-23 23:48:05,357 image.py:721] Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Visualize the return data\n", "from matplotlib import pyplot as plt\n", "\n", "for result in segmentation_results:\n", " plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))\n", " plt.axis('off')\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "id": "kmJkYFPNRnmA", "outputId": "5fc5aa4d-e94b-4c4a-cfcf-c0e64bcae872" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Get the segmented image from the original image\n", "for result, original in zip(segmentation_results, images):\n", " result = cv2.cvtColor(result, cv2.COLOR_GRAY2RGB)\n", " original_mod = cv2.cvtColor(original, cv2.COLOR_RGB2RGBA)\n", " mask = result / 255\n", " *_, alpha = cv2.split(mask)\n", " mask = cv2.merge((mask, alpha))\n", " segmented_image = (original_mod * mask).clip(0, 255).astype(np.uint8)\n", " \n", " plt.imshow(cv2.cvtColor(segmented_image, cv2.COLOR_BGRA2RGBA))\n", " plt.axis('off')\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "pUM64JEKaRWt", "outputId": "94f496d7-b03d-4e17-f9f1-8b0b101649a6" }, "outputs": [], "source": [ "# Start a dev model server to test out everything\n", "bento_svc.start_dev_server()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "3valpr2oa_OM", "outputId": "62d13da5-8289-4429-e625-0c439c4f2c15" }, "outputs": [], "source": [ "!curl -i \\\n", " -F image=@test_image.jpg \\\n", " localhost:5000/predict" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "oCW5xuPebByD", "outputId": "8f2789eb-6110-4cc3-bef6-f63f047995d7" }, "outputs": [], "source": [ "# Stop the dev model server\n", "bento_svc.stop_dev_server()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "kCHUw-_Hy6tH", "outputId": "5cf693e7-a7e3-4079-b562-85c8007cc2b9" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2021-04-23 23:48:28,424] INFO - BentoService bundle 'PaddleHubService:20210423234828_2DF1FE' saved to: /home/chvu/bentoml/repository/PaddleHubService/20210423234828_2DF1FE\n" ] } ], "source": [ "# Save the BentoService for deployment\n", "saved_path = bento_svc.save()" ] }, { "cell_type": "markdown", "metadata": { "id": "IvUU0k0JCxYk" }, "source": [ "## REST API Model Serving" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CeJEIDyj_xGK" }, "outputs": [], "source": [ "!bentoml serve PaddleHubService:latest" ] }, { "cell_type": "markdown", "metadata": { "id": "FPoKbR6cCq8_" }, "source": [ "If you are running this notebook from Google Colab, you can start the dev server with --run-with-ngrok option, to gain acccess to the API endpoint via a public endpoint managed by ngrok:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "RodE8ooiCqRw", "outputId": "b9017c18-d625-4e0b-ec08-d856d7933e87" }, "outputs": [], "source": [ "!bentoml serve PaddleHubService:latest --run-with-ngrok" ] }, { "cell_type": "markdown", "metadata": { "id": "FMCrkYb5DDHB" }, "source": [ "## Make request to the REST server\n", "\n", "*After navigating to the location of this notebook, copy and paste the following code to your terminal and run it to make request*" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "fMyLXOIUDXSn" }, "outputs": [], "source": [ "curl -i \\\n", " --header \"Content-Type: image/jpeg\" \\\n", " --request POST \\\n", " --data-binary @test_image.jpg \\\n", " localhost:5000/predict" ] }, { "cell_type": "markdown", "metadata": { "id": "DlGTKeMnEEyE" }, "source": [ "## Launch inference job from CLI" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "CBqvdN9-iyQu", "outputId": "89a44b67-40ca-495d-ba34-933e1af3a26f" }, "outputs": [], "source": [ "!bentoml run PaddleHubService:latest predict --input-file test_image.jpg" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding Custom Web Static Content " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!curl https://raw.githubusercontent.com/bentoml/gallery/master/paddlehub/image-segmentation/static.tar.xz -o static.tar.xz\n", "!tar --xz -xf static.tar.xz\n", "!rm static.tar.xz" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting paddlehub_service.py\n" ] } ], "source": [ "%%writefile paddlehub_service.py\n", "import paddlehub as hub\n", "import bentoml\n", "from bentoml import env, artifacts, api, BentoService, web_static_content\n", "from bentoml.adapters import ImageInput\n", "import cv2\n", "import imageio\n", "import numpy as np\n", "\n", "@env(infer_pip_packages=True)\n", "@web_static_content('./static')\n", "class PaddleHubService(bentoml.BentoService):\n", " def __init__(self):\n", " super(PaddleHubService, self).__init__()\n", " self.module = hub.Module(name=\"deeplabv3p_xception65_humanseg\")\n", "\n", " @api(input=ImageInput(), batch=False)\n", " def test(self, image):\n", " results = self.module.segmentation(images=[image], visualization=True)\n", " \n", " # Post-processing to apply segmentation mask on original image\n", " segmentation_result = cv2.cvtColor(results[0]['data'], cv2.COLOR_GRAY2RGB)\n", " original_mod = cv2.cvtColor(image, cv2.COLOR_RGB2RGBA)\n", " mask = segmentation_result / 255\n", " *_, alpha = cv2.split(mask)\n", " mask = cv2.merge((mask, alpha))\n", " segmented_image = (original_mod * mask).clip(0, 255).astype(np.uint8)\n", " return segmented_image" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[33m[2021-04-23 23:50:04,066] [ WARNING]\u001b[0m - The _initialize method in HubModule will soon be deprecated, you can use the __init__() to handle the initialization of the object\u001b[0m\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[2021-04-23 23:50:09,070] INFO - BentoService bundle 'PaddleHubService:20210423235008_AB2ED0' saved to: /home/chvu/bentoml/repository/PaddleHubService/20210423235008_AB2ED0\n" ] } ], "source": [ "# Import the custom BentoService defined above\n", "from paddlehub_service import PaddleHubService\n", "\n", "svc = PaddleHubService()\n", "saved_path = svc.save()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2021-04-23 23:50:45,194] INFO - Getting latest version PaddleHubService:20210423235008_AB2ED0\n", "[2021-04-23 23:50:45,195] INFO - Starting BentoML API server in development mode..\n", "/home/chvu/.local/lib/python3.8/site-packages/paddle/fluid/layers/utils.py:26: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\n", "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", " def convert_to_list(value, n, name, dtype=np.int):\n", "/usr/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl/html5lib/_trie/_base.py:3: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working\n", "\u001b[33m[2021-04-23 23:50:52,871] [ WARNING]\u001b[0m - The _initialize method in HubModule will soon be deprecated, you can use the __init__() to handle the initialization of the object\u001b[0m\n", "W0423 23:50:52.871981 6337 analysis_predictor.cc:1059] Deprecated. Please use CreatePredictor instead.\n", " * Serving Flask app \"PaddleHubService\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n", "[INFO 2021-04-23 23:50:55,948 _internal.py:113] * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", "[INFO 2021-04-23 23:51:00,833 _internal.py:113] 127.0.0.1 - - [23/Apr/2021 23:51:00] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", "[INFO 2021-04-23 23:51:00,898 _internal.py:113] 127.0.0.1 - - [23/Apr/2021 23:51:00] \"\u001b[37mGET /css/styles.css?v=1.0 HTTP/1.1\u001b[0m\" 200 -\n", "[INFO 2021-04-23 23:51:00,916 _internal.py:113] 127.0.0.1 - - [23/Apr/2021 23:51:00] \"\u001b[37mGET /js/scripts.js HTTP/1.1\u001b[0m\" 200 -\n", "[INFO 2021-04-23 23:51:01,268 _internal.py:113] 127.0.0.1 - - [23/Apr/2021 23:51:01] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n", "^C\n" ] } ], "source": [ "!bentoml serve PaddleHubService:latest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Visit http://localhost:5000/ to serve the custom UI below. Note that the swagger docs is still accessible through http://localhost:5000/docs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Custom UI](webui.png)" ] }, { "cell_type": "markdown", "metadata": { "id": "6RA0JpPjDMt8" }, "source": [ "## Containerize model server with Docker\n", "\n", "One common way of distributing this model API server for production deployment, is via Docker containers. And BentoML provides a convenient way to do that.\n", "\n", "Note that docker is **not available in Google Colab**. You will need to download and run this notebook locally to try out this containerization with docker feature.\n", "\n", "If you already have docker configured, simply run the follow command to product a docker container serving the PaddeHub prediction service created above:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JKUGBMNWDJnr" }, "outputs": [], "source": [ "!bentoml containerize PaddleHubService:latest" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "0nyRChqMDwv4", "outputId": "da95549c-1901-484b-8489-d9b0c9706105" }, "outputs": [], "source": [ "!docker run --rm -p 5000:5000 PaddleHubService:latest" ] }, { "cell_type": "markdown", "metadata": { "id": "Jb-srm9RENeh" }, "source": [ "# **Deployment Options**\n", "\n", "If you are at a small team with limited engineering or DevOps resources, try out automated deployment with BentoML CLI, currently supporting AWS Lambda, AWS SageMaker, and Azure Functions:\n", "\n", "* [AWS Lambda Deployment Guide](https://docs.bentoml.org/en/latest/deployment/aws_lambda.html)\n", "* [AWS SageMaker Deployment Guide](https://docs.bentoml.org/en/latest/deployment/aws_sagemaker.html)\n", "* [Azure Functions Deployment Guide](https://docs.bentoml.org/en/latest/deployment/azure_functions.html)\n", "\n", "If the cloud platform you are working with is not on the list above, try out these step-by-step guide on manually deploying BentoML packaged model to cloud platforms:\n", "\n", "* [AWS ECS Deployment](https://docs.bentoml.org/en/latest/deployment/aws_ecs.html)\n", "* [Google Cloud Run Deployment](https://docs.bentoml.org/en/latest/deployment/google_cloud_run.html)\n", "* [Azure container instance Deployment](https://docs.bentoml.org/en/latest/deployment/azure_container_instance.html)\n", "* [Heroku Deployment](https://docs.bentoml.org/en/latest/deployment/heroku.html)\n", "\n", "Lastly, if you have a DevOps or ML Engineering team who's operating a Kubernetes or OpenShift cluster, use the following guides as references for implementating your deployment strategy:\n", "\n", "* [Kubernetes Deployment](https://docs.bentoml.org/en/latest/deployment/kubernetes.html)\n", "* [Knative Deployment](https://docs.bentoml.org/en/latest/deployment/knative.html)\n", "* [Kubeflow Deployment](https://docs.bentoml.org/en/latest/deployment/kubeflow.html)\n", "* [KFServing Deployment](https://docs.bentoml.org/en/latest/deployment/kfserving.html)\n", "* [Clipper.ai Deployment Guide](https://docs.bentoml.org/en/latest/deployment/clipper.html)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "PaddleHub_image-segmentation.ipynb", "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 1 }