IBM Developer - Data Asset Exchange
Curated free and open datasets under open data licenses for enterprise data science.
Link to download: https://developer.ibm.com/exchanges/data/all/fashion-mnist/
The model is developed using the Tensorflow framework.
Each training and test example is assigned to one of the following labels:
Label | Description |
---|---|
0 | T-shirt/top |
1 | Trouser |
2 | Pullover |
3 | Dress |
4 | Coat |
5 | Sandal |
6 | Shirt |
7 | Sneaker |
8 | Bag |
9 | Ankle boot |
- Docker
- Python IDE or code editors
- Pre-trained model weights stored in a downloadable location
- List of required python packages
- Input pre-processing code
- Prediction/Inference code
- Output variables
- Output post-processing code
- Fork the Template and Clone the Repository
- Update Dockerfile
- Update Package Requirements
- Update API and Model Metadata
- Update Scripts
- Build the model Docker image
- Run the model server
-
Login to GitHub and go to MAX Skeleton
-
Click on
Use this template
and provide a name for the repo. -
Clone the newly created repository using the below command:
$ git clone https://github.com/......
- Clone the MAX Skeleton repository using the below the command:
$ git clone https://github.com/IBM/MAX-Skeleton.git
Open the Dockerfile file and update the following:
-
ARG model_bucket=
with the link to the model file public storage that can be downloadedFor demo purpose, we have uploaded the trained model weights to IBM Cloud Object Storage
-
ARG model_file=
with the model file name.For testing purpose, update as below:
ARG model_bucket=https://max-assets-dev.s3.us-south.cloud-object-storage.appdomain.cloud/max-demo/1.0.0
ARG model_file=assets.tar.gz
- When building the Dockerfile, the integrity of the downloaded model file will be verified. If you're using the model file provided by us, the checksum in the
md5sums.txt
file has to be replaced with the following:
b44f32e1ca3392312bc9d9f8a2ada8a1 assets/fashion_mnist.h5
Add required python packages for running the model prediction to requirements.txt.
Following packages are required for this model:
numpy==1.14.1
Pillow==5.4.1
h5py==2.9.0
tensorflow==1.14
-
In
config.py
, update the API metadata.- API_TITLE
- API_DESC
- API_VERSION
-
Set
MODEL_PATH = 'asssets/fashion_mnist.h5'
NOTE: Model files are always downloaded to
assets
folder inside docker. -
In
core/model.py
, fill in theMODEL_META_DATA
- Model id
- Model name (e.g. MAX-Fashion-MNIST)
- Description of the model (e.g. Classify clothing and fashion items)
- Model type based on what the model does (e.g. Digit recognition)
- Source to the model belongs
- Model license (e.g. Apache 2.0)
All you need to start wrapping your model is pre-processing, prediction and post-processing code.
-
In
core/model.py
, load the model under the__init__()
method of theModelWrapper
class.Here, the saved model (
.h5
format) can be loaded using the command below.global sess global graph sess = tf.Session() graph = tf.get_default_graph() set_session(sess) self.model = tf.keras.models.load_model(path)
In order for the above to function, we will have to add the following dependency to the top of the file.
import tensorflow as tf from tensorflow.keras.backend import set_session
-
In
core/model.py
, input pre-processing functions should be placed under the_pre_process
function.Here, the input image needs to be read and converted into an array of acceptable shape.
# Open the input image img = Image.open(io.BytesIO(inp)) print('reading image..', img.size) # Convert the PIL image instance into numpy array and # get in proper dimension. image = tf.keras.preprocessing.image.img_to_array(img) print('image array shape..', image.shape) image = np.expand_dims(image, axis=0) print('image array shape..', image.shape) return image
In order for the above to function, we will have to add the following dependency to the top of the file.
import io import numpy as np from PIL import Image
-
Following pre-processing, we will feed the input to the model. Place the inference code under the
_predict
method incore/model.py
. The model will return a list of class probabilities, corresponding to the likelihood of the input image to belong to respective class. There are 10 classes (digit 0 to 9), sopredict_result
will contain 10 values.with graph.as_default(): set_session(sess) predict_result = self.model.predict(x) return predict_result
-
Following inference, a post-processing step is needed to reformat the output of the
_predict
method. It's important to make sense of the results before returning the output to the user. Any post-processing code will go under the_post_process
method incore/model.py
.In order to make sense of the predicted class digits, we will add the
CLASS_DIGIT_TO_LABEL
variable to theconfig.py
file. This will serve as a mapping between class digits and labels to make the output more understandable to the user.CLASS_DIGIT_TO_LABEL = { 0: "T-shirt/top", 1: "Trouser", 2: "Pullover", 3: "Dress", 4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker", 8: "Bag", 9: "Ankle boot" }
We will import this map at the top of the
core/model.py
file.from config import DEFAULT_MODEL_PATH, CLASS_DIGIT_TO_LABEL
The class with the highest probability will be assigned to the input image. Here, we will use our imported
CLASS_DIGIT_TO_LABEL
variable to map the class digit to the corresponding label.# Extract prediction probability using `amax` and # digit prediction using `argmax` return [{'probability': np.amax(result), 'prediction': CLASS_DIGIT_TO_LABEL[np.argmax(result)]}]
-
The predicted class and it's probability are the expected output. In order to return these values in the API, we need to add these two fields to
label_prediction
inapi/predict.py
.label_prediction = MAX_API.model('LabelPrediction', { 'prediction': fields.String(required=True), 'probability': fields.Float(required=True) })
In addition, the output response has two fields
status
andpredictions
need to be updated as follows. This defines the format the API expects the output to be in.predict_response = MAX_API.model('ModelPredictResponse', { 'status': fields.String(required=True, description='Response status message'), 'predictions': fields.List(fields.Nested(label_prediction), description='Predicted labels and probabilities') })
NOTE: These fields can vary depending on the model.
-
Finally, assign the output from the post-processing step to the appropriate response field in
api/predict.py
to link the processed model output to the API.# Assign result result['predictions'] = preds
If you're using your own file, please generate the md5 checksum for your own model file, and replace it with the value on the left. If you want to skip this step, feel free to remove the entire RUN-statement from the Dockerfile.
To build the docker image locally, run:
$ docker build -t max-mnist .
If you want to print debugging messages make sure to set DEBUG=True
in config.py
.
To run the docker image, which automatically starts the model serving API, run:
$ docker run -it -p 5000:5000 max-mnist
-
Add test images to the
samples/
directory. In our implementation, we have added a picture of theT-shirt/top
category and named it1.jpeg
. -
Add a few integration tests using pytest in tests/test.py to verify that your model works.
Example:
-
Update model endpoint and sample input file path.
model_endpoint = 'http://localhost:5000/model/predict' file_path = 'samples/1.jpeg' with open(file_path, 'rb') as file: file_form = {'file': (file_path, file, 'image/jpeg')} r = requests.post(url=model_endpoint, files=file_form)
-
Check if the prediction is
T-shirt/top
.assert response['predictions'][0]['prediction'] == "T-shirt/top"
-
-
To enable Travis CI testing uncomment the docker commands and pytest command in
.travis.yml
.
Follow the tutorial in the deploy-to-kubernetes.md tutorial.