Skip to content
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

Integrate AWS Chalice #116

Merged
merged 46 commits into from Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7e335a1
add chalice class
aniketmaurya Aug 13, 2021
6585e24
update
aniketmaurya Aug 13, 2021
5bd3a20
fix text_clf function
aniketmaurya Aug 13, 2021
0c4f0da
update gh-label
aniketmaurya Aug 13, 2021
1a86ffd
update gh-label
aniketmaurya Aug 14, 2021
499a373
rename model_server to base
aniketmaurya Aug 14, 2021
57daf84
build docs
aniketmaurya Aug 14, 2021
cd8bb9d
dist -> build
aniketmaurya Aug 14, 2021
4b99733
add resize method
aniketmaurya Aug 14, 2021
86e11b5
update
aniketmaurya Aug 14, 2021
f6a7f64
ignore models
aniketmaurya Aug 14, 2021
01698b4
refactor
aniketmaurya Aug 14, 2021
5ee615b
update
aniketmaurya Aug 14, 2021
34e9877
add example
aniketmaurya Aug 14, 2021
e6aca29
add example
aniketmaurya Aug 14, 2021
98f23c0
fix email
aniketmaurya Aug 14, 2021
562441f
fixes
aniketmaurya Aug 14, 2021
688fd5b
fixes
aniketmaurya Aug 14, 2021
8c7d920
fix example
aniketmaurya Aug 14, 2021
8861a72
model.eval
aniketmaurya Aug 14, 2021
f0a0882
remove .show
aniketmaurya Aug 14, 2021
dd452b4
add test
aniketmaurya Aug 14, 2021
375b577
Format code with black
deepsource-autofix[bot] Aug 14, 2021
c3667a6
fixes
aniketmaurya Aug 14, 2021
a8be8f1
merge
aniketmaurya Aug 14, 2021
5c2c360
rename job
aniketmaurya Aug 14, 2021
05d061d
Autofix issues in 4 files
deepsource-autofix[bot] Aug 14, 2021
95d6bf1
fixes
aniketmaurya Aug 14, 2021
648659c
fix missing test
aniketmaurya Aug 14, 2021
384950e
add timm dep
aniketmaurya Aug 14, 2021
49da652
remove loguru direct import
aniketmaurya Aug 14, 2021
0b0b72b
cache pip :package:
aniketmaurya Aug 14, 2021
5379987
fix tests
aniketmaurya Aug 14, 2021
7115f5e
fix tests
aniketmaurya Aug 14, 2021
4dc2705
fix tests
aniketmaurya Aug 14, 2021
80c4417
add test
aniketmaurya Aug 14, 2021
d11efe4
add test
aniketmaurya Aug 14, 2021
0600714
add test
aniketmaurya Aug 14, 2021
9465b6f
fix
aniketmaurya Aug 14, 2021
6e8ec8a
fixes
aniketmaurya Aug 14, 2021
b8005cf
Format code with black
deepsource-autofix[bot] Aug 14, 2021
11dcaf1
remove abc method run
aniketmaurya Aug 14, 2021
62709c0
Merge branch 'feature/chalice' of github.com:aniketmaurya/chitra into…
aniketmaurya Aug 14, 2021
46f923d
update
aniketmaurya Aug 14, 2021
fedd539
fix tests
aniketmaurya Aug 14, 2021
16322b6
refactor
aniketmaurya Aug 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/labeler.yml
@@ -1,9 +1,16 @@
# Add 'docs' to any changes within 'docs' folder or any subfolders
# https://stackoverflow.com/questions/34691809/regex-match-folder-and-all-subfolders
documentation:
- docs/**/*

example:
- example/**/*
- examples($|/.*)

test:
- tests/**/*

serve:
- chitra/serve($|/.*)

cli:
- chitra/cli($|/.*)
17 changes: 16 additions & 1 deletion .github/workflows/main.yml
@@ -1,4 +1,4 @@
name: Python Main CI
name: pytest
on:
push:
branches: [ master ]
Expand All @@ -12,6 +12,11 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest ]
include:
- os: ubuntu-latest
path: ~/.cache/pip
- os: macos-latest
path: ~/Library/Caches/pip
env:
OS: ${{ matrix.os }}
PYTHON: '3.7'
Expand All @@ -27,6 +32,15 @@ jobs:
with:
python-version: 3.7

- name: Cache pip
uses: actions/cache@v2
with:
path: ${{ matrix.path }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- name: Installation
run: |
python --version
Expand All @@ -35,6 +49,7 @@ jobs:
pip install ".[serve]"
pip install ".[converter]"
pip install ".[test]"
pip install ".[example]"
pip list
shell: bash

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -138,3 +138,7 @@ checklink/cookies.txt

# .gitconfig is now autogenerated
.gitconfig

*.pth
*.ckpt
*.h5
10 changes: 4 additions & 6 deletions Makefile
@@ -1,6 +1,4 @@
SRC = $(wildcard ./*.ipynb)

all: chitra docs
.PHONY: build_docs clean style build pypi

build_docs:
cp README.md docs/index.md
Expand All @@ -18,7 +16,7 @@ coverage: ## Run tests with coverage
coverage xml

clean:
rm -rf dist
rm -rf dist/
find . -type f -name "*.DS_Store" -ls -delete
find . | grep -E "(__pycache__|\.pyc|\.pyo)" | xargs rm -rf
find . | grep -E ".pytest_cache" | xargs rm -rf
Expand All @@ -29,10 +27,10 @@ style:
black chitra tests examples
isort chitra tests examples

dist: clean
build: style clean
flit build

pypi: dist
pypi: build
flit publish

push:
Expand Down
2 changes: 1 addition & 1 deletion chitra/__init__.py
@@ -1,4 +1,4 @@
"""Deep Learning library for Model Building, Interpretability, Visualization, API Building & Deployment."""

__version__ = "0.1.1"
__version__ = "0.2.0a1"
__license__ = "Apache License 2.0"
17 changes: 17 additions & 0 deletions chitra/image.py
@@ -1,3 +1,4 @@
import io
import os
from io import BytesIO
from pathlib import Path
Expand Down Expand Up @@ -89,6 +90,9 @@ def _load_image(data: DATA_FORMATS, cache: bool):
if isinstance(data, Image.Image):
return data

if isinstance(data, bytes):
return Image.open(io.BytesIO(data))

if isinstance(data, (tf.Tensor, torch.Tensor)):
data = data.numpy().astype("uint8")

Expand Down Expand Up @@ -147,6 +151,19 @@ def draw_boxes(
self.numpy()[..., :3], color=color, size=marker_size
)

def resize(self, *args, **kwargs) -> Image.Image:
"""
Calls PIL.Image.resize method and passes the arguments
Args:
*args:
**kwargs:

Returns:
resized PIL.Image
"""
self.image = self.image.resize(*args, **kwargs)
return self.image

def resize_image_with_bbox(self, size: List[int]):
old_size = self.shape
self.image = self.image.resize(size)
Expand Down
2 changes: 1 addition & 1 deletion chitra/serve/__init__.py
@@ -1,3 +1,3 @@
from chitra.serve.api import API, create_api
from chitra.serve.app import GradioApp
from chitra.serve.model_server import ModelServer
from chitra.serve.base import ModelServer
2 changes: 1 addition & 1 deletion chitra/serve/api.py
Expand Up @@ -5,8 +5,8 @@

from chitra.__about__ import documentation_url
from chitra.serve import schema
from chitra.serve.base import ModelServer
from chitra.serve.constants import IMAGE_CLF, OBJECT_DETECTION, QNA, TXT_CLF
from chitra.serve.model_server import ModelServer


class API(ModelServer):
Expand Down
17 changes: 12 additions & 5 deletions chitra/serve/app.py
Expand Up @@ -5,11 +5,14 @@

from chitra.__about__ import documentation_url
from chitra.serve import constants as const
from chitra.serve.model_server import ModelServer
from chitra.serve.base import ModelServer


class GradioApp(ModelServer):
API_TYPES = {"VISION": (const.IMAGE_CLF, const.OBJECT_DETECTION)}
API_TYPES = {
"VISION": (const.IMAGE_CLF, const.OBJECT_DETECTION),
"NLP": (const.TXT_CLF,),
}

def __init__(
self,
Expand Down Expand Up @@ -47,8 +50,12 @@ def setup(
self,
**kwargs,
):

self.api_type_func[const.IMAGE_CLF] = self.image_classification
if self.api_type in (const.IMAGE_CLF, const.OBJECT_DETECTION):
self.api_type_func[self.api_type] = self.single_x_classification
elif self.api_type == const.TXT_CLF:
self.api_type_func[self.api_type] = self.single_x_classification
else:
raise NotImplementedError(f"api_type={self.api_type} not implemented yet!")

if not self.input_types:
self.input_types = self.get_input_type(**kwargs)
Expand All @@ -67,7 +74,7 @@ def get_input_type(self, **kwargs):
)
raise NotImplementedError(f"{self.api_type} API Type is not implemented yet!")

def image_classification(self, x: np.ndarray):
def single_x_classification(self, x: np.ndarray):
data_processor = self.data_processor

if data_processor.preprocess_fn:
Expand Down
15 changes: 11 additions & 4 deletions chitra/serve/model_server.py → chitra/serve/base.py
@@ -1,3 +1,4 @@
import abc
import itertools
from typing import Callable, List, Optional

Expand All @@ -21,10 +22,19 @@ def __init__(
model: Callable,
preprocess_fn=None,
postprocess_fn=None,
preprocess_conf: Optional[dict] = None,
postprocess_conf: Optional[dict] = None,
**kwargs,
):
if not preprocess_conf:
preprocess_conf = {}
if not postprocess_conf:
postprocess_conf = {}

self.api_type = api_type.upper()
self.model = model
self.preprocess_conf = preprocess_conf
self.postprocess_conf = postprocess_conf
self.data_processor: Optional[DataProcessor] = self.set_data_processor(
preprocess_fn, postprocess_fn
)
Expand All @@ -50,11 +60,8 @@ def set_default_processor(self) -> DataProcessor:
elif api_type in ModelServer.API_TYPES.get("NLP"):
self.data_processor = DefaultTextProcessor.nlp
else:
raise UserWarning(
raise NotImplementedError(
f"{api_type} is not implemented! Available types are -\
{ModelServer.get_available_api_types()}"
)
return self.data_processor

def run(self, *_, **__):
raise NotImplementedError
1 change: 1 addition & 0 deletions chitra/serve/cloud/__init__.py
@@ -0,0 +1 @@
from .aws_serverless import ChaliceServer
68 changes: 68 additions & 0 deletions chitra/serve/cloud/aws_serverless.py
@@ -0,0 +1,68 @@
from typing import Callable, List, Optional

from chalice import Chalice, Rate

from chitra.logging import logger
from chitra.serve.cloud.base import CloudServer

S3 = "s3"
GCS = "gcs"

RATE_UNIT = {"m": Rate.MINUTES, "h": Rate.HOURS, "d": Rate.DAYS}


class ChaliceServer(CloudServer):
INVOKE_METHODS = ("route",)

def __init__(
self,
api_type: str,
model_path: str,
model_loader: Callable,
preprocess_fn: Callable = None,
postprocess_fn: Callable = None,
**kwargs,
):
super().__init__(
api_type,
model_path=model_path,
model_loader=model_loader,
preprocess_fn=preprocess_fn,
postprocess_fn=postprocess_fn,
**kwargs,
)

self.app = Chalice(app_name=kwargs.get("name", "chitra-server"))

@staticmethod
def index():
return {"hello": "world"}

def predict(self) -> dict:

data_processor = self.data_processor
x = self.app.current_request.raw_body
logger.debug(f"raw body type={type(x)}")
if data_processor.preprocess_fn:
x = data_processor.preprocess(x, **self.preprocess_conf)
x = self.model(x)
if data_processor.postprocess_fn:
x = data_processor.postprocess(x, **self.postprocess_conf)
return x

def run(self, invoke_method: str, content_types: Optional[List] = None, **kwargs):
invoke_method = invoke_method.lower()
if not content_types:
content_types = []

if invoke_method not in self.INVOKE_METHODS:
raise NotImplementedError(
f"invoke method={invoke_method} not implemented yet. Please select {self.INVOKE_METHODS}"
)

if invoke_method == "route":
route_path = kwargs.get("path", "/predict")
self.app.route("/", methods=["GET"])(self.index)
self.app.route(route_path, methods=["POST"], content_types=content_types)(
self.predict
)
47 changes: 47 additions & 0 deletions chitra/serve/cloud/base.py
@@ -0,0 +1,47 @@
import abc
import io
from abc import ABC
from typing import Callable, Optional

import smart_open

from chitra.serve.base import ModelServer


class CloudServer(ModelServer, ABC):
def __init__(
self,
api_type: str,
model_path: str,
model_loader: Callable,
preprocess_fn: Optional[Callable] = None,
postprocess_fn: Optional[Callable] = None,
**kwargs
):
raw_model = self.download_model(model_path, **kwargs)
model = model_loader(raw_model)

super().__init__(
api_type,
model,
preprocess_fn=preprocess_fn,
postprocess_fn=postprocess_fn,
**kwargs
)

@staticmethod
def download_model(path: str, **kwargs) -> io.BytesIO:
"""
Download model from cloud
ref: http://5.9.10.113/67706477/load-pytorch-model-from-s3-bucket
Args:
path:
**kwargs:

Returns:

"""

with smart_open.open(path, mode="rb", **kwargs) as fr:
data = io.BytesIO(fr.read())
return data
2 changes: 1 addition & 1 deletion docs/index.md
Expand Up @@ -42,7 +42,7 @@ Easily create UI for Machine Learning models or Rest API backend that can be dep

### Using pip (recommended)

`pip install -U chitra==0.1.0`
`pip install -U chitra`

### From source

Expand Down