-
Notifications
You must be signed in to change notification settings - Fork 16
Re-organizing examples #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hhsecond
wants to merge
19
commits into
master
Choose a base branch
from
ci
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
1403b7f
removing circle ci
8ce10e0
moving reference files
86ced83
master
eb01870
wip
d551e39
workflow
19f327a
ignoring test results
95469f9
redisai service
630c49d
working papermill
f91f98f
correction
9fd81df
redis connection
48a90a9
new notebooks
d9f9279
tests using pytest
1eabdba
pytorch example wip
81643d1
pytorch-example
999fef6
review feedback
4843209
tf example
734809d
only 3.7 support
1f1c6e2
test
2e16114
lfs
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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,51 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: ["master"] | ||
pull_request: | ||
|
||
jobs: | ||
tests: | ||
name: "Python ${{ matrix.python-version }}" | ||
runs-on: "ubuntu-latest" | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
python-version: ["3.7"] | ||
|
||
services: | ||
redisai: | ||
image: redislabs/redisai:edge-cpu-bionic | ||
ports: | ||
- 6379:6379 | ||
options: >- | ||
--health-cmd "redis-cli ping" | ||
--health-interval 10s | ||
--health-timeout 5s | ||
--health-retries 5 | ||
steps: | ||
- name: Checkout Code | ||
uses: "actions/checkout@v2" | ||
with: | ||
lfs: true | ||
- name: Setup Python | ||
uses: "actions/setup-python@v2" | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Cache dependencies | ||
uses: actions/cache@v2 | ||
with: | ||
path: /opt/venv | ||
key: /opt/venv-${{ hashFiles('**/requirements.txt') }} | ||
- name: Install dependencies | ||
if: steps.cache.outputs.cache-hit != 'true' | ||
run: | | ||
pip install --upgrade pip | ||
pip install -r requirements.txt | ||
- name: Run Tests | ||
env: | ||
REDIS_HOST: localhost | ||
REDIS_PORT: 6379 | ||
run: | | ||
pytest test.py |
This file contains hidden or 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 hidden or 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,294 @@ | ||
{ | ||
hhsecond marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "f0c2a6e5", | ||
"metadata": {}, | ||
"source": [ | ||
"# Image Classification with PyTorch\n", | ||
"Pytorch has been both researcher's and engineer's preferred choice of framework for DL development but when it comes to productionizing pytorch models, there still hasn't been a consensus on what to use. This guide run you through building a simple image classification model using Pytorch and then deploying that to RedisAI. Let's start with importing the necessary packages" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"id": "1e657632", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import torchvision.models as models\n", | ||
"import torch\n", | ||
"\n", | ||
"import json\n", | ||
"import time\n", | ||
"from redisai import Client\n", | ||
"import ml2rt\n", | ||
"from skimage import io\n", | ||
"\n", | ||
"import os\n", | ||
"from redisai import Client" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "43cd67a3", | ||
"metadata": {}, | ||
"source": [ | ||
"## Build Model\n", | ||
"For this example, we use a pretrained model from torchvision for image classification - the renowned resnet50. Since RedisAI is a C/C++ runtime, we'd need to export the torch model into [TorchScript](https://pytorch.org/docs/stable/jit.html). Here is how to do it but you can read more about TorchScript in the attached link" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"id": "56edea97", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"model = models.resnet50(pretrained=True)\n", | ||
"model.eval()\n", | ||
"\n", | ||
"scripted_model = torch.jit.script(model)\n", | ||
"torch.jit.save(scripted_model, 'resnet50.pt')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "75c8faa0", | ||
"metadata": {}, | ||
"source": [ | ||
"## Setup RedisAI\n", | ||
"This tutorial assumes you already have a RedisAI server running. The easiest way to setup one instance is using docker\n", | ||
"\n", | ||
"```\n", | ||
"docker run -p 6379:6379 redislabs/redisai:latest-cpu-x64-bionic\n", | ||
"```\n", | ||
"\n", | ||
"Take a look at this [quickstart](https://oss.redis.com/redisai/quickstart/) for more details. Here we setup the connection credentials and ping the server to verify we can talk " | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"id": "59b6599a", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"True" | ||
] | ||
}, | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"REDIS_HOST = os.getenv(\"REDIS_HOST\", \"localhost\")\n", | ||
"REDIS_PORT = int(os.getenv(\"REDIS_PORT\", 6379))\n", | ||
"con = Client(host=REDIS_HOST, port=REDIS_PORT)\n", | ||
"con.ping()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "f68e8993", | ||
"metadata": {}, | ||
"source": [ | ||
"## Load model\n", | ||
"Next step is to load the model we trained above into RedisAI for serving. We are using a convinent package [ml2rt](https://pypi.org/project/ml2rt/) here for loading but it's not a mandatory dependency if you want to keep your `requirements.txt` small. Take a look at the `load_model` function. This will give us a binary blob of the model we have built above. We need to send this to RedisAI and also inform which backend we'd like to use and which device this should run on. We'll set the model on a key so we can reference this key later\n", | ||
"\n", | ||
"Note: If you want to run on GPU, take a look at the above quick start to setup RedisAI on GPU" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 4, | ||
"id": "f7ddde68", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"'OK'" | ||
] | ||
}, | ||
"execution_count": 4, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"model = ml2rt.load_model(\"resnet50.pt\")\n", | ||
"con.modelstore(\"pytorch_model\", backend=\"TORCH\", device=\"CPU\", data=model)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "c635e5f4", | ||
"metadata": {}, | ||
"source": [ | ||
"## Load script\n", | ||
"Why do you need Script? It's very likely that your deep learning model would have a pre/post processing step, like changing the dimensionality of the input (adding batch dimension) or doing normalizatoin etc. You normally do this from your client code and send the processed data to model server. With script, you can club this into your model serving pipeline. Script is one of the powerful feature of RedisAI. RedisAI Scripts are built on top of [TorchScript](https://pytorch.org/docs/stable/jit.html) and it's recommended to take a look if TorcScript is new to you. Torchscript is a subset of python programming langauge i.e it looks and smells like python but all the python functionalities are not available in torchscript. Now if you are wondering what's the benefit of TorchScript in RedisAI, there are few\n", | ||
"\n", | ||
"- It runs on a highly effecient C++ runtime\n", | ||
"- It can pipeline your preprocessing and postprocessing jobs, right where your model and data resides. So no back and forth of huge data blobs between your model server and pre/post processing scripts\n", | ||
"- It can run in a single redis pipeline or in RedisAI Dag which makes serving channel implementation smooth\n", | ||
"- You can use it with any framework, not just pytorch\n", | ||
"\n", | ||
"You can load the script from a file (`ml2rt.load_script` does this for you) which is probably your workflow normally since you save the script in a file but here we pass the string into the `scriptstore` method" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 5, | ||
"id": "50bb90b1", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"'OK'" | ||
] | ||
}, | ||
"execution_count": 5, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"script = \"\"\"\n", | ||
"def pre_process(tensors: List[Tensor], keys: List[str], args: List[str]):\n", | ||
" image = tensors[0]\n", | ||
" mean = torch.zeros(3).float().to(image.device)\n", | ||
" std = torch.zeros(3).float().to(image.device)\n", | ||
" mean[0], mean[1], mean[2] = 0.485, 0.456, 0.406\n", | ||
" std[0], std[1], std[2] = 0.229, 0.224, 0.225\n", | ||
" mean = mean.unsqueeze(1).unsqueeze(1)\n", | ||
" std = std.unsqueeze(1).unsqueeze(1)\n", | ||
" temp = image.float().div(255).permute(2, 0, 1)\n", | ||
" return temp.sub(mean).div(std).unsqueeze(0)\n", | ||
"\n", | ||
"\n", | ||
"def post_process(tensors: List[Tensor], keys: List[str], args: List[str]):\n", | ||
" output = tensors[0]\n", | ||
" return output.max(1)[1]\n", | ||
"\"\"\"\n", | ||
"con.scriptstore(\"processing_script\", device=\"CPU\", script=script, entry_points=(\"pre_process\", \"post_process\"))" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "5b16d972", | ||
"metadata": {}, | ||
"source": [ | ||
"## Load the image and final classes\n", | ||
"Here we load the input image and the final classes to find the predicted output" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 6, | ||
"id": "fe95d716", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"image = io.imread(\"data/cat.jpg\")\n", | ||
"class_idx = json.load(open(\"data/imagenet_classes.json\"))" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "9440afb8", | ||
"metadata": {}, | ||
"source": [ | ||
"## Run the model serving pipeline\n", | ||
"Here we run the serving pipeline one by one and finally fetch the results out. The pipeline is organized into 5 steps\n", | ||
"\n", | ||
"```\n", | ||
"Setting Input -> Pre-processing Script -> Running Model -> Post-processing Script -> Fetching Output\n", | ||
"```" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 7, | ||
"id": "f24ce05d", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"281 tabby, tabby catamount\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"con.tensorset('image', image)\n", | ||
"con.scriptexecute('processing_script', 'pre_process', inputs='image', outputs='processed')\n", | ||
"con.modelexecute('pytorch_model', 'processed', 'model_out')\n", | ||
"con.scriptexecute('processing_script', 'post_process', inputs='model_out', outputs='final')\n", | ||
"final = con.tensorget('final')\n", | ||
"print(final[0], class_idx[str(final[0])])" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "7b75d0ba", | ||
"metadata": {}, | ||
"source": [ | ||
"## Running with DAG\n", | ||
"Although this looks good, each of these calls has a network overhead of going back and forth and sometimes it's better to run everything as a single execution and that's what you can do with RedisAI DAG. DAGs are much more powerful than that but let's discuss that in another tutorial. Here we first setup a dag object and track all the operations we did above in the dag. Note that none of these tracking steps sends a request to RedisAI server. Once the dag object is ready with all the paths, you can trigger `dag.execute()` to initiate the DAG execution in RedisAI backend" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 8, | ||
"id": "40e02215", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"281 tabby, tabby catamount\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"dag = con.dag(routing='default')\n", | ||
"dag.tensorset('image', image)\n", | ||
"dag.scriptexecute('processing_script', 'pre_process', inputs='image', outputs='processed')\n", | ||
"dag.modelexecute('pytorch_model', 'processed', 'model_out')\n", | ||
"dag.scriptexecute('processing_script', 'post_process', inputs='model_out', outputs='final')\n", | ||
"dag.tensorget('final')\n", | ||
"\n", | ||
"final = dag.execute()[-1]\n", | ||
"print(final[0], class_idx[str(final[0])])" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3 (ipykernel)", | ||
"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.7.6" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.