-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #152 from bojiang/torch
chore(torch): add pytorch yolov5 example with torchhub usage
- Loading branch information
Showing
14 changed files
with
1,119 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
.ipynb_checkpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
MNIST/ |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
299
pytorch_yolov5_torchhub/pytorch_yolov5_torchhub_demo.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.