I followed the codelab TensorFlow For Poets for transfer learning using inception_v3. It generates retrained_graph.pb and retrained_labels.txt files, which can used to make predictions locally (running label_image.py).
Then, I wanted to deploy this model to Cloud ML Engine, so that I could make online predictions. For that, I had to export the retrained_graph.pb to SavedModel format. I managed to do it by following the indications in this answer from Google's @rhaertel80 and this python file from the Flowers Cloud ML Engine Tutorial. Here is my code:
import tensorflow as tf from tensorflow.contrib import layers from tensorflow.python.saved_model import builder as saved_model_builder from tensorflow.python.saved_model import signature_constants from tensorflow.python.saved_model import signature_def_utils from tensorflow.python.saved_model import tag_constants from tensorflow.python.saved_model import utils as saved_model_utils export_dir = '../tf_files/saved7' retrained_graph = '../tf_files/retrained_graph2.pb' label_count = 5 def build_signature(inputs, outputs): signature_inputs = { key: saved_model_utils.build_tensor_info(tensor) for key, tensor in inputs.items() } signature_outputs = { key: saved_model_utils.build_tensor_info(tensor) for key, tensor in outputs.items() } signature_def = signature_def_utils.build_signature_def( signature_inputs, signature_outputs, signature_constants.PREDICT_METHOD_NAME ) return signature_def class GraphReferences(object): def __init__(self): self.examples = None self.train = None self.global_step = None self.metric_updates = [] self.metric_values = [] self.keys = None self.predictions = [] self.input_jpeg = None class Model(object): def __init__(self, label_count): self.label_count = label_count def build_image_str_tensor(self): image_str_tensor = tf.placeholder(tf.string, shape=[None]) def decode_and_resize(image_str_tensor): return image_str_tensor image = tf.map_fn( decode_and_resize, image_str_tensor, back_prop=False, dtype=tf.string ) return image_str_tensor def build_prediction_graph(self, g): tensors = GraphReferences() tensors.examples = tf.placeholder(tf.string, name='input', shape=(None,)) tensors.input_jpeg = self.build_image_str_tensor() keys_placeholder = tf.placeholder(tf.string, shape=[None]) inputs = { 'key': keys_placeholder, 'image_bytes': tensors.input_jpeg } keys = tf.identity(keys_placeholder) outputs = { 'key': keys, 'prediction': g.get_tensor_by_name('final_result:0') } return inputs, outputs def export(self, output_dir): with tf.Session(graph=tf.Graph()) as sess: with tf.gfile.GFile(retrained_graph, "rb") as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) tf.import_graph_def(graph_def, name="") g = tf.get_default_graph() inputs, outputs = self.build_prediction_graph(g) signature_def = build_signature(inputs=inputs, outputs=outputs) signature_def_map = { signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature_def } builder = saved_model_builder.SavedModelBuilder(output_dir) builder.add_meta_graph_and_variables( sess, tags=[tag_constants.SERVING], signature_def_map=signature_def_map ) builder.save() model = Model(label_count) model.export(export_dir)
This code generates a saved_model.pb file, which I then used to create the Cloud ML Engine model. I can get predictions from this model using gcloud ml-engine predict --model my_model_name --json-instances request.json
, where the contents of request.json are:
{ "key": "0", "image_bytes": { "b64": "jpeg_image_base64_encoded" } }
However, no matter which jpeg I encode in the request, I always get the exact same wrong predictions:
I guess the problem is in the way the CloudML Prediction API passes the base64 encoded image bytes to the input tensor "DecodeJpeg/contents:0" of inception_v3 ("build_image_str_tensor()" method in the previous code). Any clue on how can I solve this issue and have my locally retrained model serving correct predictions on Cloud ML Engine?
(Just to make it clear, the problem is not in retrained_graph.pb, as it makes correct predictions when I run it locally; nor is it in request.json, because the same request file worked without problems when following the Flowers Cloud ML Engine Tutorial pointed above.)