Skip to content

Commit

Permalink
Cleaning up various housekeeping issues (#31)
Browse files Browse the repository at this point in the history
* Move samples images to new 'samples' folder

* new COS and max-base

* Use prod bucket

* Add license headers

* Test pip install from reqs file

* Fix flake8 issues

* Ignore qa

* Change 'assets' to 'samples' in README

* Update README.md

* Add tutorial button

* Add license header to Dockerfile
  • Loading branch information
Nick Pentreath authored and splovyt committed Jul 3, 2019
1 parent 5e291ba commit 0c76916
Show file tree
Hide file tree
Showing 21 changed files with 192 additions and 410 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ install:
- docker run -it -d -p 5000:5000 max-scene-classifier

before_script:
- pip install pytest requests
- pip install -r test-requirements.txt
- sleep 30

script:
- flake8 . --max-line-length=127
- pytest tests/test.py
22 changes: 19 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
FROM codait/max-base:v1.1.1

ARG model_bucket=http://max-assets.s3.us.cloud-object-storage.appdomain.cloud/scene-classifier/1.0
#
# Copyright 2018-2019 IBM Corp. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

FROM codait/max-base:v1.1.3

ARG model_bucket=https://s3.us-south.cloud-object-storage.appdomain.cloud/max-assets-prod/max-scene-classifier/1.0.1
ARG model_file=assets.tar.gz

WORKDIR /workspace
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[![Build Status](https://travis-ci.org/IBM/MAX-Scene-Classifier.svg?branch=master)](https://travis-ci.org/IBM/MAX-Scene-Classifier) [![Website Status](https://img.shields.io/website/http/max-scene-classifier.max.us-south.containers.appdomain.cloud/swagger.json.svg?label=api+demo)](http://max-scene-classifier.max.us-south.containers.appdomain.cloud/)

[<img src="docs/deploy-max-to-ibm-cloud-with-kubernetes-button.png" width="400px">](http://ibm.biz/max-to-ibm-cloud-tutorial)

# IBM Code Model Asset Exchange: Scene Classifier

This repository contains code to instantiate and deploy an image classification model. This model recognizes the 365 different classes of scene/location in the [Places365-Standard subset of the Places2 Dataset](http://places2.csail.mit.edu/). The model is based on the [Places365-CNN Model](https://github.com/CSAILVision/places365) and consists of a pre-trained deep convolutional net using the ResNet architecture, trained on the [ImageNet-2012](http://www.image-net.org/challenges/LSVRC/2012/) data set. The pre-trained model is then fine-tuned on the Places365-Standard dataset. The input to the model is a 224x224 image, and the output is a list of estimated class probabilities.

The specific model variant used in this repository is the [PyTorch Places365 ResNet18 Model](https://github.com/CSAILVision/places365#pre-trained-cnn-models-on-places365-standard). The model files are hosted on [IBM Cloud Object Storage](http://max-assets.s3.us.cloud-object-storage.appdomain.cloud/scene-classifier/1.0/assets.tar.gz). The code in this repository deploys the model as a web service in a Docker container. This repository was developed as part of the [IBM Code Model Asset Exchange](https://developer.ibm.com/code/exchanges/models/).
The specific model variant used in this repository is the [PyTorch Places365 ResNet18 Model](https://github.com/CSAILVision/places365#pre-trained-cnn-models-on-places365-standard). The model files are hosted on [IBM Cloud Object Storage](https://s3.us-south.cloud-object-storage.appdomain.cloud/max-assets-prod/max-scene-classifier/1.0.1/assets.tar.gz). The code in this repository deploys the model as a web service in a Docker container. This repository was developed as part of the [IBM Code Model Asset Exchange](https://developer.ibm.com/code/exchanges/models/) and the public API is powered by [IBM Cloud](https://ibm.biz/Bdz2XM).

## Model Metadata
| Domain | Application | Industry | Framework | Training Data | Input Data Format |
Expand All @@ -27,7 +29,7 @@ using Places Database"](http://places.csail.mit.edu/places_NIPS14.pdf), Advances
| This repository | [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) | [LICENSE](LICENSE) |
| Model Weights | [CC BY License](https://creativecommons.org/licenses/by/4.0/) | [Places365-CNN](https://github.com/CSAILVision/places365)|
| Model Code (3rd party) | [MIT](https://opensource.org/licenses/MIT) | [Places365-CNN](https://github.com/CSAILVision/places365)|
| Test assets | Various | [Asset README](assets/README.md) |
| Test assets | Various | [Asset README](samples/README.md) |

## Pre-requisites:

Expand Down Expand Up @@ -63,6 +65,8 @@ $ kubectl apply -f https://raw.githubusercontent.com/IBM/MAX-Scene-Classifier/ma

The model will be available internally at port `5000`, but can also be accessed externally through the `NodePort`.

A more elaborate tutorial on how to deploy this MAX model to production on [IBM Cloud](https://ibm.biz/Bdz2XM) can be found [here](http://ibm.biz/max-to-ibm-cloud-tutorial).

## Run Locally

1. [Build the Model](#1-build-the-model)
Expand Down Expand Up @@ -105,14 +109,14 @@ $ docker run -it -p 5000:5000 max-scene-classifier

The API server automatically generates an interactive Swagger documentation page. Go to `http://localhost:5000` to load it. From there you can explore the API and also create test requests.

Use the `model/predict` endpoint to load a test image (you can use one of the test images from the `assets` folder) and get predicted labels for the image from the API.
Use the `model/predict` endpoint to load a test image (you can use one of the test images from the `samples` folder) and get predicted labels for the image from the API.

![Swagger Doc Screenshot](docs/swagger-screenshot.png)

You can also test it on the command line, for example:

```
$ curl -F "image=@assets/aquarium.jpg" -XPOST http://localhost:5000/model/predict
$ curl -F "image=@samples/aquarium.jpg" -XPOST http://localhost:5000/model/predict
```

You should see a JSON response like that below:
Expand Down
19 changes: 17 additions & 2 deletions api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
from .metadata import ModelMetadataAPI
from .predict import ModelPredictAPI, ModelLabelsAPI
#
# Copyright 2018-2019 IBM Corp. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from .metadata import ModelMetadataAPI # noqa
from .predict import ModelPredictAPI, ModelLabelsAPI # noqa
19 changes: 18 additions & 1 deletion api/metadata.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
#
# Copyright 2018-2019 IBM Corp. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from core.model import ModelWrapper

from maxfw.core import MAX_API, MetadataAPI, METADATA_SCHEMA


class ModelMetadataAPI(MetadataAPI):

@MAX_API.marshal_with(METADATA_SCHEMA)
def get(self):
"""Return the metadata associated with the model"""
return ModelWrapper.MODEL_META_DATA
return ModelWrapper.MODEL_META_DATA
30 changes: 24 additions & 6 deletions api/predict.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
#
# Copyright 2018-2019 IBM Corp. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from core.model import ModelWrapper, read_image

from maxfw.core import MAX_API, PredictAPI, MetadataAPI

from flask_restplus import Namespace, Resource, fields
from flask_restplus import fields
from werkzeug.datastructures import FileStorage


Expand All @@ -20,6 +36,7 @@
'labels': fields.List(fields.Nested(model_label), description='Class labels that can be predicted by the model')
})


class ModelLabelsAPI(MetadataAPI):
'''API for getting information about available class labels'''

Expand All @@ -35,7 +52,7 @@ def get(self):
return result


# === Predict API
# === Predict API

label_prediction = MAX_API.model('LabelPrediction', {
'label_id': fields.String(required=False, description='Label identifier'),
Expand All @@ -50,8 +67,9 @@ def get(self):

# set up parser for image input data
image_parser = MAX_API.parser()
image_parser.add_argument('image', type=FileStorage, location='files', required=True,
help='An image file (encoded as PNG or JPG/JPEG)')
image_parser.add_argument('image', type=FileStorage, location='files', required=True,
help='An image file (encoded as PNG or JPG/JPEG)')


class ModelPredictAPI(PredictAPI):

Expand All @@ -66,9 +84,9 @@ def post(self):
image_data = args['image'].read()
image = read_image(image_data)
preds = model_wrapper.predict(image)

label_preds = [{'label_id': p[0], 'label': p[1], 'probability': p[2]} for p in [x for x in preds]]
result['predictions'] = label_preds
result['status'] = 'ok'

return result
return result
18 changes: 17 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
#
# Copyright 2018-2019 IBM Corp. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from maxfw.core import MAXApp
from api import ModelMetadataAPI, ModelPredictAPI, ModelLabelsAPI
from config import API_TITLE, API_DESC, API_VERSION
Expand All @@ -6,4 +22,4 @@
max_app.add_api(ModelMetadataAPI, '/metadata')
max_app.add_api(ModelLabelsAPI, '/labels')
max_app.add_api(ModelPredictAPI, '/predict')
max_app.run()
max_app.run()
17 changes: 0 additions & 17 deletions assets/README.md

This file was deleted.

Loading

0 comments on commit 0c76916

Please sign in to comment.