2

I am trying to call a prediction on a custom trained TensorFlow model deployed to GCP ML engine. When I am trying to call a prediction on the model it is returning the following error message "Expected float32 got base64"

  1. I've used transfer learning and the TensorFlow's retrain.py script to train my model on my images, following the official documentation
python retrain.py --image_dir ~/training_images saved_model_dir /saved_model_directory
  1. I've tested the prediction locally using TensorFlow's label_img.py script, the prediction worked locally for my images
python label_image.py --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt --input_layer=Placeholder --output_layer=final_result \
  1. I've exported my model to use with Tensorflow Serving as described in the documentation of the retrain.py script.
python retrain.py --image_dir ~/training_images --saved_model_dir /saved_model_directory
  1. I've uploaded the model to Firebase, GCP validated and accepted my model, I was able to trigger my model.

  2. When trying to call an online prediction I am receiving the " Expected float32" error.

 test.json ={"image_bytes": {"b64": "/9j/4AAQSkZJ.......=="}}

 gcloud ml-engine predict \
        --model my_model \
        --version v1 \
        --json-instances ./test.json

Do I need to modify retrain.py to make my saved model accept base64 or is there any other solution for the problem?

I've already checked the following answer, but unfortunately it does not solved my problem: How to pass base64 encoded image to Tensorflow prediction?

gogasca
  • 9,283
  • 6
  • 80
  • 125
Tamás Králik
  • 201
  • 1
  • 8

2 Answers2

1

The problem is that retrain.py exports a model whose input is expecting an already decoded and resized image in the form of floats (see this line), but you are passing it raw, undecoded image data.

There are two solutions.

  1. Create a JSON request in the expected format (floats). This is an easy fix but could have performance implications (sending float32 data as JSON can be inefficient).
  2. Alter the model to accept raw image data as input. This requires some reworking of the model.

For (1), you would send a JSON file similar to:

{"images": [[[0.0, 0.0, 0.0], [0,0,0], [...]], [...], ...]}

Of course, you'd probably construct that using some client library

(2) is a little more involved. This sample can guide you on how to do that.

rhaertel80
  • 8,254
  • 1
  • 31
  • 47
  • Thanks for the answer, I've went with the first solution just to check if my online prediction is working. It worked, but it was not too efficient (as you told). The final solution was to first upload the image to the server, convert it on the backend, and feed the converted image to the model. – Tamás Králik Jan 28 '19 at 15:13
  • i encountered the same problem and i tried the first solution (sending float32 data as JSON) but i got an error while creating the JSON request : ` raise TypeError(repr(o) + " is not JSON serializable") TypeError: array([[[0.85098046, 0.85098046, 0.85098046], .... [0.48627454, 0.48627454, 0.48627454]]], dtype=float32) is not JSON serializable` i tried to encode it myself and convert it to a JSON format following this [link](https://stackoverflow.com/questions/8230315/how-to-json-serialize-sets) but no luck.. any tips on how to do that? – user 007 Mar 04 '19 at 09:26
  • call tolist() on your ndarray. Also, be sure to get the request format correct. See [this](https://stackoverflow.com/a/49177909/1399222) answer for some tips – rhaertel80 Mar 05 '19 at 20:10
1

While sending a float32 array in json will work, you'll find that it is extremely slow due to network latency. If at all possible, you'd want to use base64 encoded strings.

To do this, one very simple change you can make to the export script is to change the image input name to end with _bytes. When this occurs, tensorflow serving will decode your base64 encoded image string automatically for you. Essentially update this line https://github.com/tensorflow/hub/blob/a96bbd73abbecfad8c5517684cf3655b48bab39b/examples/image_retraining/retrain.py#L963

to inputs={'image_bytes': in_image},

This is probably one of the most useful but lesser known behavior of tensorflow serving when dealing with image inputs.

You can also use tf.io.decode and write your own decoding function when exporting the model.

Finally, your json payload would look something like this

{"inputs": {"image_bytes": {"b64": "/9j/4AAQSkZJ.......=="}}}

Or, if you prefer the instances format

{"instances": [{"image_bytes": {"b64": "/9j/4AAQSkZJ.......=="}}]}
khuang834
  • 931
  • 1
  • 9
  • 12