I've completed the starting tutorial for TensorFlow (https://www.tensorflow.org/get_started/get_started_for_beginners) and made some minor changes to the code to adapt it to my application. The feature columns for my case are the following:
transaction_column = tf.feature_column.categorical_column_with_vocabulary_list(key='Transaction', vocabulary_list=["buy", "rent"])
localization_column = tf.feature_column.categorical_column_with_vocabulary_list(key='Localization', vocabulary_list=["barcelona", "girona"])
dimensions_feature_column = tf.feature_column.numeric_column("Dimensions")
buy_price_feature_column = tf.feature_column.numeric_column("BuyPrice")
rent_price_feature_column = tf.feature_column.numeric_column("RentPrice")
my_feature_columns = [
tf.feature_column.indicator_column(transaction_column),
tf.feature_column.indicator_column(localization_column),
tf.feature_column.bucketized_column(source_column = dimensions_feature_column,
boundaries = [50, 75, 100]),
tf.feature_column.numeric_column(key='Rooms'),
tf.feature_column.numeric_column(key='Toilets'),
tf.feature_column.bucketized_column(source_column = buy_price_feature_column,
boundaries = [1, 180000, 200000, 225000, 250000, 275000, 300000]),
tf.feature_column.bucketized_column(source_column = rent_price_feature_column,
boundaries = [1, 700, 1000, 1300])
]
After that, I've saved the model so that it can be used in Cloud ML Engine to do predictions. To export the model I've added the following code (after evaluating the model):
feature_spec = tf.feature_column.make_parse_example_spec(my_feature_columns)
export_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec)
servable_model_dir = "modeloutput"
servable_model_path = classifier.export_savedmodel(servable_model_dir, export_input_fn)
After running the code I get the proper model files in my "modeloutput" directory and I create the model in the Cloud (as explained in https://cloud.google.com/ml-engine/docs/tensorflow/getting-started-training-prediction#deploy_a_model_to_support_prediction, "Deploy a model to support prediction")
Once the model version is created I simply try to launch an online prediction using this model with the following command on the Cloud Shell:
gcloud ml-engine predict --model $MODEL_NAME --version v1 --json-instances ../prediction.json
Where $MODEL_NAME is my model's name and prediction.json is a JSON file with the following content:
{"inputs":[
{
"Transaction":"rent",
"Localization":"girona",
"Dimensions":90,
"Rooms":4,
"Toilets":2,
"BuyPrice":0,
"RentPrice":1100
}
]
}
However, the prediction fails and I get the following error message:
"error": "Prediction failed: Error processing input: Expected string, got {u'BuyPrice': 0, u'Transaction': u'rent', u'Rooms': 4, u'Localization': u'girona', u'Toilets': 2, u'RentPrice': 1100, u'Dimensions': 90} of type 'dict' instead."
The error is clear, a string is expected instead of a dictionary. If I check my SavedModel SignatureDef I get the following information:
The given SavedModel SignatureDef contains the following input(s):
inputs['inputs'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: input_example_tensor:0
The given SavedModel SignatureDef contains the following output(s):
outputs['classes'] tensor_info:
dtype: DT_STRING
shape: (-1, 12)
name: dnn/head/Tile:0
outputs['scores'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 12)
name: dnn/head/predictions/probabilities:0
Method name is: tensorflow/serving/classify
It's clear that the dtype expected for the input is a string (DT_STRING) but I don't know how to format my input data so that the prediction succeeds. I've tried to write the input JSON in many different ways but I keep getting errors. If I look at how the prediction is performed in the tutorial (https://www.tensorflow.org/get_started/get_started_for_beginners), I think it's clear that the predict input is passed as a dictionary (predict_x in the tutorial code).
So, where am I wrong? How can I make the prediction with this input data?
Thank you for your time.
EDIT BASED ON ANSWER ------
Following the second suggestion by @Lak, I've updated the code to export the model so it now looks like this:
export_input_fn = serving_input_fn
servable_model_dir = "savedmodeloutput"
servable_model_path = classifier.export_savedmodel(servable_model_dir,
export_input_fn)
...
def serving_input_fn():
feature_placeholders = {
'Transaction': tf.placeholder(tf.string, [None]),
'Localization': tf.placeholder(tf.string, [None]),
'Dimensions': tf.placeholder(tf.float32, [None]),
'Rooms': tf.placeholder(tf.int32, [None]),
'Toilets': tf.placeholder(tf.int32, [None]),
'BuyPrice': tf.placeholder(tf.float32, [None]),
'RentPrice': tf.placeholder(tf.float32, [None])
}
features = {
key: tf.expand_dims(tensor, -1)
for key, tensor in feature_placeholders.items()
}
return tf.estimator.export.ServingInputReceiver(features, feature_placeholders)
After that, I've created a new model and fed it the following JSON to get the prediction:
{
"Transaction":"rent",
"Localization":"girona",
"Dimensions":90.0,
"Rooms":4,
"Toilets":2,
"BuyPrice":0.0,
"RentPrice":1100.0
}
Note that I removed "inputs" from the JSON structure as I received the error "Unexpected tensor name: inputs" when doing the prediction. However, now I get a new, and uglier error:
"error": "Prediction failed: Error during model execution: AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"NodeDef mentions attr 'T' not in Op index:int64>; NodeDef: dnn/input_from_feature_columns/input_layer/Transaction_indicator/to_sparse_input/indices = WhereT=DT_BOOL, _output_shapes=[[?,2]], _device=\"/job:localhost/replica:0/task:0/device:CPU:0\". (Check whether your GraphDef-interpreting binary is up to date with your GraphDef-generating binary.).\n\t [[Node: dnn/input_from_feature_columns/input_layer/Transaction_indicator/to_sparse_input/indices = WhereT=DT_BOOL, _output_shapes=[[?,2]], _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"]]\")"
I've checked again the SignatureDef and I get the following information:
The given SavedModel SignatureDef contains the following input(s):
inputs['Toilets'] tensor_info:
dtype: DT_INT32
shape: (-1)
name: Placeholder_4:0
inputs['Rooms'] tensor_info:
dtype: DT_INT32
shape: (-1)
name: Placeholder_3:0
inputs['Localization'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: Placeholder_1:0
inputs['RentPrice'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: Placeholder_6:0
inputs['BuyPrice'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: Placeholder_5:0
inputs['Dimensions'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: Placeholder_2:0
inputs['Transaction'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: Placeholder:0
The given SavedModel SignatureDef contains the following output(s):
outputs['class_ids'] tensor_info:
dtype: DT_INT64
shape: (-1, 1)
name: dnn/head/predictions/ExpandDims:0
outputs['classes'] tensor_info:
dtype: DT_STRING
shape: (-1, 1)
name: dnn/head/predictions/str_classes:0
outputs['logits'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 12)
name: dnn/logits/BiasAdd:0
outputs['probabilities'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 12)
name: dnn/head/predictions/probabilities:0
Method name is: tensorflow/serving/predict
Have I gone wrong in some of the steps? Thanks!
NEW UPDATE
I've run a local prediction and it has been succesfully executed, receiving the expected prediction result. Command used:
gcloud ml-engine local predict --model-dir $MODEL_DIR --json-instances=../prediction.json
where MODEL_DIR is the directory containing the files generated on the model training. So the problem seems to be in exporting the model. Somehow the model exported and used later for prediction is not correct. I've read something about TensorFlow versions possibly being the origin of the problem, but I don't understand. Isn't my whole code executed with the same TF version? Any ideas regarding this point?
Thanks!