Skip to content

Commit

Permalink
Merge pull request #152 from bojiang/torch
Browse files Browse the repository at this point in the history
chore(torch): add pytorch yolov5 example with torchhub usage
  • Loading branch information
yetone authored Aug 2, 2022
2 parents f630bde + 50af52b commit 9709dc1
Show file tree
Hide file tree
Showing 14 changed files with 1,119 additions and 4 deletions.
8 changes: 4 additions & 4 deletions pytorch_mnist/pytorch_mnist_demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"source": [
"# BentoML PyTorch MNIST Tutorial\n",
"\n",
"Link to source code: https://github.com/bentoml/gallery/tree/main/pytorch_mnist_demo/\n",
"Link to source code: https://github.com/bentoml/gallery/tree/main/pytorch_mnist/\n",
"\n",
"Install required dependencies:"
]
Expand Down Expand Up @@ -494,9 +494,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "tf2-py3.7",
"language": "python",
"name": "python3"
"name": "tf2-py3.7"
},
"language_info": {
"codemirror_mode": {
Expand All @@ -508,7 +508,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
"version": "3.7.13"
},
"name": "pytorch_mnist.ipynb"
},
Expand Down
4 changes: 4 additions & 0 deletions pytorch_yolov5_torchhub/.bentoignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__/
*.py[cod]
*$py.class
.ipynb_checkpoints
1 change: 1 addition & 0 deletions pytorch_yolov5_torchhub/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MNIST/
674 changes: 674 additions & 0 deletions pytorch_yolov5_torchhub/LICENSE

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions pytorch_yolov5_torchhub/bentofile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
service: "service:svc"
description: "file: ./README.md"
labels:
owner: bentoml-team
stage: demo
include:
- "*.py"
- "/home/agent/.cache/torch/hub/ultralytics_yolov5_master/models/common.py"
exclude:
- "tests/"
- "locustfile.py"
python:
requirements_txt: ./requirements.txt
15 changes: 15 additions & 0 deletions pytorch_yolov5_torchhub/locustfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from locust import HttpUser, task, between

with open("yolov5/data/images/bus.jpg", "rb") as f:
test_image_bytes = f.read()


class PyTorchMNISTLoadTestUser(HttpUser):

wait_time = between(0.3, 1.7)

@task
def predict_image(self):
files = {"upload_files": ("bus.jpg", test_image_bytes, "image/png")}
self.client.post("/predict_image", files=files)

299 changes: 299 additions & 0 deletions pytorch_yolov5_torchhub/pytorch_yolov5_torchhub_demo.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a682ea0b",
"metadata": {},
"source": [
"# BentoML PyTorch MNIST Tutorial\n",
"\n",
"Link to source code: https://github.com/bentoml/gallery/tree/main/pytorch_yolov5_torchhub/\n",
"\n",
"Install required dependencies:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ad00863",
"metadata": {},
"outputs": [],
"source": [
"!pip install -r requirements.txt"
]
},
{
"cell_type": "markdown",
"id": "45393b74",
"metadata": {},
"source": [
"## Load the pre-trained model from Torch Hub\n",
"\n",
"take `ultralytics/yolov5` as the example"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee440480-fec6-4da1-a38b-e70fd5dab70f",
"metadata": {},
"outputs": [],
"source": [
"!git clone 'https://github.com/ultralytics/yolov5.git'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6ae5cf59-8423-49d3-a2d4-d5837ce94c60",
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"\n",
"# Model\n",
"original_model = torch.hub.load('./yolov5', 'yolov5s', pretrained=True, source=\"local\")\n",
"\n",
"\n",
"class WrapperModel(torch.nn.Module):\n",
" def __init__(self, model):\n",
" super().__init__()\n",
" self.model = model\n",
" \n",
" def forward(self, imgs):\n",
" outputs = self.model(imgs)\n",
"\n",
" # convert outputs to a json serializable list\n",
" results = []\n",
" for det in outputs.pred:\n",
" detections = []\n",
" for i in det:\n",
" d = {}\n",
" d['obj'] = outputs.names[int(i[5])]\n",
" d['position'] = i[:4].tolist()\n",
" d['prob'] = i[4].tolist()\n",
" detections.append(d)\n",
" results.append(detections)\n",
"\n",
" return results\n",
"\n",
"\n",
"model = WrapperModel(original_model)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0950faa2-256f-46ef-b7c1-62737a85ff32",
"metadata": {},
"outputs": [],
"source": [
"import PIL.Image\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3c821e28-1001-4aae-91b0-5684d458be61",
"metadata": {},
"outputs": [],
"source": [
"# Images\n",
"imgs = [\n",
" np.array(PIL.Image.open('yolov5/data/images/bus.jpg')),\n",
" np.array(PIL.Image.open('yolov5/data/images/zidane.jpg')),\n",
"] # batch of images\n",
"\n",
"model(imgs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f2a8b2ec-875b-43d7-9d23-c9fa4c938a8d",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "38888f0a",
"metadata": {},
"source": [
"## Training and Saving the model\n",
"\n",
"Then we define a simple PyTorch network and some helper functions"
]
},
{
"cell_type": "markdown",
"id": "92d9b23c",
"metadata": {},
"source": [
"### saving the model with some metadata"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "048518a2-9a87-4672-8807-2e180534bab7",
"metadata": {},
"outputs": [],
"source": [
"import bentoml\n",
"\n",
"bentoml.pytorch.save_model(\n",
" \"pytorch_yolov5\",\n",
" model,\n",
" signatures={\"__call__\": {\"batchable\": True, \"batchdim\": 0}},\n",
")"
]
},
{
"cell_type": "markdown",
"id": "bdf35e55",
"metadata": {},
"source": [
"## Create a BentoML Service for serving the model\n",
"\n",
"Note: using `%%writefile` here because `bentoml.Service` instance must be created in a separate `.py` file\n",
"\n",
"Even though we have only one model, we can create as many api endpoints as we want. Here we create two end points `predict_ndarray` and `predict_image`"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f3e2f590",
"metadata": {},
"outputs": [],
"source": [
"%%writefile service.py\n",
"\n",
"import sys\n",
"import os\n",
"import typing as t\n",
"\n",
"import numpy as np\n",
"import PIL.Image\n",
"\n",
"import bentoml\n",
"from bentoml.io import Image\n",
"from bentoml.io import JSON\n",
"\n",
"\n",
"yolo_runner = bentoml.pytorch.get(\"pytorch_yolov5\").to_runner()\n",
"\n",
"svc = bentoml.Service(\n",
" name=\"pytorch_yolo_demo\",\n",
" runners=[yolo_runner],\n",
")\n",
"\n",
"\n",
"sys.path.append('yolov5')\n",
"\n",
"@svc.api(input=Image(), output=JSON())\n",
"async def predict_image(img: PIL.Image.Image) -> list:\n",
" assert isinstance(img, PIL.Image.Image)\n",
" return await yolo_runner.async_run([np.array(img)])\n"
]
},
{
"cell_type": "markdown",
"id": "590147aa",
"metadata": {},
"source": [
"Start a dev model server to test out the service defined above"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "29173871",
"metadata": {},
"outputs": [],
"source": [
"!bentoml serve service.py:svc"
]
},
{
"cell_type": "markdown",
"id": "606c1b36",
"metadata": {},
"source": [
"Now you can use something like:\n",
"\n",
"`curl -H \"Content-Type: multipart/form-data\" -F'fileobj=@yolov5/data/images/bus.jpg;type=image/png' http://127.0.0.1:3000/predict_image`\n",
" \n",
"to send an image to the digit recognition service"
]
},
{
"cell_type": "markdown",
"id": "c7f03564",
"metadata": {},
"source": [
"## Build a Bento for distribution and deployment"
]
},
{
"cell_type": "markdown",
"id": "36306933",
"metadata": {},
"source": [
"Starting a dev server with the Bento build:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7f01436c-a244-4504-bf05-1bbdef5d815a",
"metadata": {},
"outputs": [],
"source": [
"!bentoml build"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec4b9dff",
"metadata": {},
"outputs": [],
"source": [
"!bentoml serve pytorch_yolo_demo:latest"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f05fae93",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "torch-py3.7",
"language": "python",
"name": "torch-py3.7"
},
"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.7.13"
},
"name": "pytorch_mnist.ipynb"
},
"nbformat": 4,
"nbformat_minor": 5
}
20 changes: 20 additions & 0 deletions pytorch_yolov5_torchhub/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Usage: pip install -r requirements.txt

# Bento requirements
pydantic



# YOLOv5 requirements
matplotlib>=3.2.2
numpy>=1.18.5
opencv-python>=4.1.1
Pillow>=7.1.2
PyYAML>=5.3.1
requests>=2.23.0
scipy>=1.4.1
torch>=1.7.0
torchvision>=0.8.1
tqdm>=4.64.0
protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
seaborn
Loading

0 comments on commit 9709dc1

Please sign in to comment.