1
votes

I am trying to train a model with Amazon Sagemaker and I want serve it using with Tensorflow serving. To achieve that, I am downloading the model to a Tensorflow serving docker and I am trying to serve it from there.

The Sagemaker's training and evaluating stages are completed without errors, but when I load my model to the Tensorflow serving server and try to invoke it I get Tensorflow serving errors that suggest that my model has no defined inputs. It can be seen that the Tensorflow serving server that the model is being served.

For debugging purposes, I tried to serve it with Sagemaker but all I got was a vague error message saying I have an error invoking the endpoint.

I think that the problem is that I am not defining well either the serving_input_fn or invoking it wrong or both. Can anyone help?

Tensorflow serving server invocation curl:

curl -d '{"instances": [{"col3": 1.0}]}' -X POST http://localhost:8501/v1/models/test_model:predict

The error I receive from Tensorflow serving:

{ "error": "Failed to process element: 0 key: col3 of \'instances\' list. Error: Invalid argument: JSON object: does not have named input: col3" }%    

Sagemaker's training python file:

import os
import tensorflow as tf
from tensorflow.python.ops import nn


TRAIN_FILENAME = 'test.csv'
TEST_FILENAME = 'train.csv'

NODES_IN_LAYER = 6
LAYERS_NUM = 10
NUM_LINES_TO_SKIP = 1

CSV_COLUMNS = ['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7', 'col8', 'label']
RECORDS_DEFAULTS = [[0], [0], [0.0], [0.0], [0], [0.0], [0.0], [0], [0.0]]

BATCH_SIZE = 32

FEATURE_SPEC = {
    'col3': tf.FixedLenFeature(dtype=tf.float32, shape=[]),
}


def estimator_fn(run_config, params):
    feature_columns = [
        tf.feature_column.numeric_column('col3')]
    return tf.estimator.DNNRegressor(feature_columns=feature_columns,
                                     hidden_units=[NODES_IN_LAYER] * LAYERS_NUM,
                                     activation_fn=nn.tanh,
                                     config=run_config)


def serving_input_fn(params):
    return tf.estimator.export.build_raw_serving_input_receiver_fn(FEATURE_SPEC)


def train_input_fn(training_dir, params):
    """Returns input function that would feed the model during training"""
    return _generate_input_fn(training_dir, TRAIN_FILENAME)


def eval_input_fn(training_dir, params):
    """Returns input function that would feed the model during evaluation"""
    return _generate_input_fn(training_dir, TEST_FILENAME)


def parse_csv(line):
    columns = tf.decode_csv(line, record_defaults=RECORDS_DEFAULTS)
    line_features = dict(zip(CSV_COLUMNS, columns))
    line_label = line_features.pop('label')
    return {'col3': line_features.pop('col3')}, line_label


def _generate_input_fn(training_dir, training_filename):
    filename = os.path.join(training_dir, training_filename)
    dataset = tf.data.TextLineDataset(filename)
    dataset = dataset.skip(NUM_LINES_TO_SKIP).map(parse_csv).batch(BATCH_SIZE)
    return dataset
4

4 Answers

1
votes

Make a regress call instead of predict

curl -d '{"examples": [{"col3": 1.0}]}' -X POST http://localhost:8501/v1/models/test_model:regress

Docs: https://github.com/tensorflow/serving/blob/master/tensorflow_serving/g3doc/api_rest.md#make-rest-api-calls-to-modelserver

1
votes

This error occurs when there is mismatch between input of the model and the input you feed.

The best way is to check the input of the serving model by making a get request like:

http://<ip>:8501/v1/models/bilstm/metadata

It would return output like

{
    "model_spec": {
        "name": "bilstm",
        "signature_name": "",
        "version": "1"
    },
    "metadata": {
        "signature_def": {
            "signature_def": {
                "serving_default": {
                    "inputs": {
                        "sequence_length": {
                            "dtype": "DT_INT32",
                            "tensor_shape": {
                                "dim": [
                                    {
                                        "size": "-1",
                                        "name": ""
                                    }
                                ],
                                "unknown_rank": false
                            },
                            "name": "sequence_lengths:0"
                        },
                        "word_ids": {
                            "dtype": "DT_INT32",
                            "tensor_shape": {
                                "dim": [
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "-1",
                                        "name": ""
                                    }
                                ],
                                "unknown_rank": false
                            },
                            "name": "word_ids:0"
                        },
                        "lr": {
                            "dtype": "DT_FLOAT",
                            "tensor_shape": {
                                "dim": [],
                                "unknown_rank": false
                            },
                            "name": "lr:0"
                        },
                        "word_lengths": {
                            "dtype": "DT_INT32",
                            "tensor_shape": {
                                "dim": [
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "-1",
                                        "name": ""
                                    }
                                ],
                                "unknown_rank": false
                            },
                            "name": "word_lengths:0"
                        },
                        "char_ids": {
                            "dtype": "DT_INT32",
                            "tensor_shape": {
                                "dim": [
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "-1",
                                        "name": ""
                                    }
                                ],
                                "unknown_rank": false
                            },
                            "name": "char_ids:0"
                        },
                        "dropout": {
                            "dtype": "DT_FLOAT",
                            "tensor_shape": {
                                "dim": [],
                                "unknown_rank": false
                            },
                            "name": "dropout:0"
                        }
                    },
                    "outputs": {
                        "scores": {
                            "dtype": "DT_FLOAT",
                            "tensor_shape": {
                                "dim": [
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "-1",
                                        "name": ""
                                    },
                                    {
                                        "size": "30",
                                        "name": ""
                                    }
                                ],
                                "unknown_rank": false
                            },
                            "name": "bi-lstm-crf/output_ff/BiasAdd:0"
                        }
                    },
                    "method_name": "tensorflow/serving/predict"
                }
            }
        }
    }
}
0
votes

The serving_input_fn defines the name of the tensor(s) expected in the input, as well as the shape. So in your case, the request should be a dict of {'col3': []}.

There's also a problem currently with the deserialization behavior currently for dicts when using json, described in this issue: https://github.com/aws/sagemaker-tensorflow-container/issues/71

This pull request should fix that issue once it goes out: https://github.com/aws/sagemaker-tensorflow-container/pull/76

0
votes

Try to inspect your exported model first using the saved_model_cli to make sure your inputs and outputs are as expected:

saved_model_cli show --dir . --tag_set serve --signature_def serving_default

You're using a canned Estimator, so you should see something like this:

The given SavedModel SignatureDef contains the following input(s):
  inputs['examples'] tensor_info:
      dtype: DT_STRING
      shape: (-1)
      name: input_example_tensor:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['output'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: groupwise_dnn_v2/accumulate_scores/truediv:0

where the input is a ProtoBuf Example and the output is a batch of regression scalars.

Now you can try to query the model using the CLI:

saved_model_cli run \
    --dir . \
    --tag_set serve \
    --signature_def predict \
    --input_examples 'examples=[{"col3":[1.0]},{"col3":[2.0]},{"col3":[3.0]}]'

If you can query your model from the CLI, then that can help eliminate some variables in your problem.