Skip to content

Commit

Permalink
Merge branch 'main' into target-latency
Browse files Browse the repository at this point in the history
  • Loading branch information
sauyon authored Apr 19, 2023
2 parents a8e17f4 + b80d4b9 commit a4c3eac
Show file tree
Hide file tree
Showing 24 changed files with 1,446 additions and 81 deletions.
76 changes: 76 additions & 0 deletions .github/workflows/frameworks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
fastai: ${{ steps.filter.outputs.fastai }}
keras: ${{ steps.filter.outputs.keras }}
lightgbm: ${{ steps.filter.outputs.lightgbm }}
detectron: ${{ steps.filter.outputs.detectron }}
easyocr: ${{ steps.filter.outputs.easyocr }}
mlflow: ${{ steps.filter.outputs.mlflow }}
onnx: ${{ steps.filter.outputs.onnx }}
picklable_model: ${{ steps.filter.outputs.picklable_model }}
Expand Down Expand Up @@ -64,11 +66,21 @@ jobs:
- src/bentoml/diffusers.py
- src/bentoml/_internal/frameworks/diffusers.py
- tests/integration/frameworks/models/diffusers.py
detectron:
- *related
- src/bentoml/detectron.py
- src/bentoml/_internal/frameworks/detectron.py
- tests/integration/frameworks/models/detectron.py
lightgbm:
- *related
- src/bentoml/lightgbm.py
- src/bentoml/_internal/frameworks/lightgbm.py
- tests/integration/frameworks/models/lightgbm.py
easyocr:
- *related
- src/bentoml/easyocr.py
- src/bentoml/_internal/frameworks/easyocr.py
- tests/integration/frameworks/models/easyocr.py
mlflow:
- *related
- src/bentoml/mlflow.py
Expand Down Expand Up @@ -213,7 +225,71 @@ jobs:
run: |
OPTS=(--cov-config pyproject.toml --cov src/bentoml --cov-append --framework diffusers)
coverage run -m pytest tests/integration/frameworks/test_frameworks.py "${OPTS[@]}"
detectron_integration_tests:
needs: diff
if: ${{ (github.event_name == 'pull_request' && needs.diff.outputs.detectron == 'true') || github.event_name == 'push' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # fetch all tags and branches
- name: Setup python
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Get pip cache dir
id: cache-dir
run: |
echo ::set-output name=dir::$(pip cache dir)
- name: Cache pip dependencies
uses: actions/cache@v3
id: cache-pip
with:
path: ${{ steps.cache-dir.outputs.dir }}
key: ${{ runner.os }}-tests-${{ hashFiles('requirements/tests-requirements.txt') }}
- name: Install dependencies
run: |
sudo apt-get install -y git
pip install .
pip install torch requests
pip install git+https://github.com/facebookresearch/detectron2.git
pip install -r requirements/tests-requirements.txt
- name: Run tests and generate coverage report
run: |
OPTS=(--cov-config pyproject.toml --cov src/bentoml --cov-append --framework detectron)
coverage run -m pytest tests/integration/frameworks/test_frameworks.py "${OPTS[@]}"
easyocr_integration_tests:
needs: diff
if: ${{ (github.event_name == 'pull_request' && needs.diff.outputs.easyocr == 'true') || github.event_name == 'push' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # fetch all tags and branches
- name: Setup python
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Get pip cache dir
id: cache-dir
run: |
echo ::set-output name=dir::$(pip cache dir)
- name: Cache pip dependencies
uses: actions/cache@v3
id: cache-pip
with:
path: ${{ steps.cache-dir.outputs.dir }}
key: ${{ runner.os }}-tests-${{ hashFiles('requirements/tests-requirements.txt') }}
- name: Install dependencies
run: |
pip install .
pip install easyocr torch requests Pillow
pip install -r requirements/tests-requirements.txt
- name: Run tests and generate coverage report
run: |
OPTS=(--cov-config pyproject.toml --cov src/bentoml --cov-append --framework easyocr)
coverage run -m pytest tests/integration/frameworks/test_frameworks.py "${OPTS[@]}"
flax_integration_tests:
needs: diff
if: ${{ (github.event_name == 'pull_request' && needs.diff.outputs.flax == 'true') || github.event_name == 'push' }}
Expand Down
154 changes: 154 additions & 0 deletions docs/source/frameworks/detectron.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
==========
Detectron2
==========

`Detectron2 <https://github.com/facebookresearch/detectron2>`_ is Facebook AI Research's (FAIR) next generation library
that provides state-of-the-art detection and segmentation algorithms. It is the successor of Detectron and maskrcnn-benchmark.
It supports a number of computer vision research projects and production applications in Facebook.

This guide will provide an overview of how to save and load Detectron2 models with BentoML.

Compatibility
-------------

Since the official way to install Detectron2 from their `installation guide <https://detectron2.readthedocs.io/en/latest/tutorials/install.html>`_ is from the repository git source,
The current implementation has been tested with Detectron2 since this :github:`commit <facebookresearch/detectron2/commit/4e447553eb32b6e3784df0b8fca286935107b2fd>`

Saving a Detectron2 model
-------------------------

The following example is excerpted from `Detectron2's official demo <https://github.com/facebookresearch/detectron2/blob/main/demo/demo.py>`_.

We will be using a Masked-RCNN trained on the COCO datasets.

.. code-block:: python
import detectron2.config as Cf
import detectron2.model_zoo as ModelZoo
import detectron2.modeling as Modeling
cfg = Cf.get_cfg()
cfg.merge_from_file(ModelZoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5
cfg.MODEL.WEIGHTS = ModelZoo.get_checkpoint_url(model_url)
cfg = cfg.clone()
cfg.MODEL.DEVICE = "cpu" # Change device to run on CPU
model = Modeling.build_model(cfg)
model.eval()
To run an example inputs, use the following image from the COCO dataset:

.. code-block:: python
import requests
import PIL.Image
import numpy as np
from detectron2.data import transforms as Transforms
url = "http://images.cocodataset.org/val2017/000000439715.jpg"
augmentation = Transforms.ResizeShortestEdge([800,800], 1333)
im = np.asarray(PIL.Image.open(requests.get(url, stream=True).raw).convert("RGB"))
arr = augmentation.get_transform(im).apply_image(im)
# Running prediction
outputs = model({'image': torch.as_tensor(arr.transpose(2, 0, 1))})
To save the above model, pass in both the model and the config instance to :obj:`~bentoml.detectron.save_model`:

.. code-block:: python
bentoml.detectron.save_model("coco-masked-rcnn", model, config=cfg)
Detectron2 also provides a :obj:`~detectron2.engine.defaults.DefaultPredictor` class that can be used to run inference on the model quickly.

BentoML also supports saving this predictor with ``bentoml.detectron.save_model``. Do the following:

.. code-block:: python
from detectron2.engine import DefaultPredictor
predictor = DefaultPredictor(cfg)
bentoml.detectron.save_model("coco-masked-rcnn-predictor", predictor)
.. note::

:bdg-info:`Remarks:` External python classes or utility functions required by the Detectron models/custom models
must be referenced in ``<module>.<class>`` format, and such modules should be passed to ``bentoml.detectron.save_model`` via ``external_modules``.

For example:

.. code-block:: python
import dit
predictor = dit.get_predictor()
bentoml.detectron.save_model("dit-predictor", predictor, external_modules=[dit])
This is due to a limitation from PyTorch model serialisation, where PyTorch requires the model's source code to restore it.

The signatures used for creating a Runner is ``{"__call__": {"batchable": False}}``. This means by default, BentoML’s `Adaptive Batching <guides/batching:Adaptive Batching>`_ is disabled when using :obj:`~bentoml.pytorch.save_model()`. If you want to utilize adaptive batching behavior and know your model's dynamic batching dimension, make sure to pass in ``signatures`` as follow:

.. code-block:: python
bentoml.detectron.save_model("dit-predictor", predictor, signatures={"__call__": {"batch_dim": 0, "batchable": True}})
.. admonition:: About ``custom_objects``

BentoML will save the Detectron ``config`` ConfigNode instance into the ``custom_objects`` attribute via ``config``. Make sure when using ``custom_objects`` to not overwrite this naming.


Building a Service
------------------

Create a BentoML service with the previously saved ``coco-masked-rcnn-predictor`` model using the :code:`bentoml.detectron` framework APIs.

.. code-block:: python
:caption: `service.py`
import bentoml
import PIL.Image
import numpy as np
from detectron2.data import MetadataCatalog
from detectron2.utils.visualizer import ColorMode
from detectron2.utils.visualizer import Visualizer
bentomodel = bentoml.detectron.get("coco-masked-rcnn-predictor")
cfg = model.custom_objects['config']
runner = bentomodel.to_runner()
svc = bentoml.Service(name="masked-rcnn", runners=[runner])
@svc.api(input=bentoml.io.Image(), output=bentoml.io.Image())
async def predict(im: PIL.Image.Image) -> PIL.Image.Image:
md = MetadataCatalog.get(cfg.DATASETS.TEST[0])
tensor = np.array(im)
output = await predictor.async_run(tensor)
instances = output['instances']
v = Visualizer(
tensor[:, :, ::-1], md, scale=1.0, instance_mode=ColorMode.SEGMENTATION
)
res = v.draw_instance_predictions(output.to("cpu"))
return Image.fromarray(res.get_image()[:, :, ::-1])
Adaptive Batching
-----------------

Most Detectron models can accept batched data as input. If batched interence is supported, it is recommended to enable batching to take advantage of
the adaptive batching capability to improve the throughput and efficiency of the model. Enable adaptive batching by overriding the :code:`signatures`
argument with the method name and providing :code:`batchable` and :code:`batch_dim` configurations when saving the model to the model store.

.. seealso::

See :ref:`Adaptive Batching <guides/batching:Adaptive Batching>` to learn more.


.. note::

You can find more examples for **Detectron** in our :examples:`bentoml/examples <>` directory.

.. currentmodule:: bentoml.detectron
121 changes: 121 additions & 0 deletions docs/source/frameworks/easyocr.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
=======
EasyOCR
=======

EasyOCR is a ready-to-use OCR with 80+ supported languages. It helps you to quickly convert and transcribe text from images. This guide provides an overiew of using `EasyOCR <https://www.jaided.ai/easyocr/>`_ with BentoML.

Compatibility
-------------

BentoML has been validated to work with EasyOCR version 1.6.2 and higher.

Save/Load a EasyOCR Reader with BentoML
---------------------------------------

First, create a reader instance with the `language codes <https://www.jaided.ai/easyocr/>`_ for your usecase.

.. code-block:: python
import easyocr
reader = easyocr.Reader(['en'])
Save this reader instance using :obj:`~bentoml.easyocr.save_model()` to save this to the BentoML model store

.. code-block:: python
import bentoml
bento_model = bentoml.easyocr.save_model('en-reader', reader)
To verify that the saved model is working, load it back with :obj:`~bentoml.easyocr.load_model()`:

.. code-block:: python
loaded_model = bentoml.easyocr.load_model('en-reader')
rs = loaded_model.readtext('image.jpg')
.. note:: GPU behaviour

GPU can be passed through ``easyocr.Reader`` constructor as ``gpu=True``. This means in order to use GPU, the reader instance must be created with a machine with GPU before saving it to BentoML.

Building a Service
------------------

.. seealso::

:ref:`Building a Service <concepts/service:Service and APIs>`: more information on creating a
prediction service with BentoML.

Create a ``service.py`` file separate from your training code that will be used to define the
BentoML service:

.. code-block:: python
import bentoml
import PIL.Image
import numpy as np
# create a runner from the saved Booster
runner = bentoml.easyocr.get("en-reader").to_runner()
# create a BentoML service
svc = bentoml.Service("ocr", runners=[runner])
# define a new endpoint on the BentoML service
@svc.api(input=bentoml.io.Image(), output=bentoml.io.Text())
async def transcript_text(input: PIL.Image.Image) -> str:
# use 'runner.predict.run(input)' instead of 'booster.predict'
return await runner.readtext.async_run(np.asarray(input))
Take note of the name of the service (``svc`` in this example) and the name of the file.

You should also have a ``bentofile.yaml`` alongside the service file that specifies that
information, as well as the fact that it depends on XGBoost. This can be done using either
``python`` (if using pip), or ``conda``:

.. tab-set::

.. tab-item:: pip

.. code-block:: yaml
service: "service:svc"
python:
packages:
- easyocr
- bentoml
.. tab-item:: conda

.. code-block:: yaml
service: "service:svc"
conda:
channels:
- conda-forge
dependencies:
- easyocr
Using Runners
~~~~~~~~~~~~~

.. seealso::

:ref:`concepts/runner:Using Runners`: a general introduction to the Runner concept and its usage.

A runner for a Reader is created like so:

.. code-block:: python
bentoml.easyocr.get("model_name:model_version").to_runner()
``runner.readtext.run`` is generally a drop-in replacement for ``reader.readtext``.

Runners must to be initialized in order for their ``run`` methods to work. This is done by BentoML
internally when you serve a bento with ``bentoml serve``. See the :ref:`runner debugging guide
<concepts/service:Debugging Runners>` for more information about initializing runners locally.


.. currentmodule:: bentoml.easyocr
Loading

0 comments on commit a4c3eac

Please sign in to comment.