6

I built and saved a very simple model in Python using the high-level Estimator API (DNNClassifier). It takes 2 floats and outputs one of two classes. I'm trying to load it in Ruby using the tensorflow.rb gem, and make a prediction with it. This should be very similar to the CAPTCHA example provided by tensorflow.rb.

I saved it using export_saved_model. Here is the Python code that trained the model. It can correctly predict the classes.

import numpy as np import pandas as pd import tensorflow as tf

dataframe = pd.read_csv("remediations_import.csv", sep=",")
dataframe = dataframe.reindex(
    np.random.permutation(dataframe.index))

STEPS = 5000
BATCH_SIZE = 10
SAVED_MODEL_FOLDER = 'saved_model'
FEATURE_1 = 'Float_input_1'
FEATURE_2 = 'Float_input_2'
LABEL = "Human_label"

my_feature_data = dataframe[[FEATURE_1, FEATURE_2]]
targets = dataframe[LABEL]

feature_columns = [tf.feature_column.numeric_column(FEATURE_1),
                   tf.feature_column.numeric_column(FEATURE_2)]

features = {key: np.array(value) for key, value in dict(my_feature_data).items()}
training_input_fn = tf.estimator.inputs.numpy_input_fn(x=features,
                                                       y=targets.as_matrix(),
                                                       batch_size=BATCH_SIZE,
                                                       shuffle=True)

classifier = tf.estimator.DNNClassifier(
    feature_columns=feature_columns,
    hidden_units=[10, 10],
    n_classes=2,
    model_dir=SAVED_MODEL_FOLDER)

classifier.train(
    input_fn=training_input_fn,
    steps=STEPS
)

print("Model training finished.")

# Save the model under pb format
features = {'Float_input_1': tf.placeholder(tf.float32, shape=[1]),
            'Float_input_2': tf.placeholder(tf.float32, shape=[1])}
serving_input_receiver_fn = tf.estimator.export.build_raw_serving_input_receiver_fn(features,
                                                                                    default_batch_size=None)
classifier.export_savedmodel(SAVED_MODEL_FOLDER + '_pb', serving_input_receiver_fn, strip_default_attrs=True)

# Predict
new_features = pd.DataFrame({FEATURE_1: [0.97, 0.03], FEATURE_2: [1.00, -1.00]})
# Convert pandas data into a dict of numpy arrays.
features = {key: np.array(value) for key, value in dict(new_features).items()}
prediction_input_fn = tf.estimator.inputs.numpy_input_fn(x=features,
                                                         num_epochs=1,
                                                         shuffle=False)

predictions_results = classifier.predict(input_fn=prediction_input_fn)
predictions = np.array([item['class_ids'][0] for item in predictions_results])
print(predictions)

Looking at my saved model using saved_model_cli shows this:

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['Float_input_1'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1)
        name: Placeholder:0
    inputs['Float_input_2'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1)
        name: Placeholder_1: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['logistic'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: dnn/head/predictions/logistic:0
    outputs['logits'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: dnn/logits/BiasAdd:0
    outputs['probabilities'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 2)
        name: dnn/head/predictions/probabilities:0
  Method name is: tensorflow/serving/predict

My tensorflow.rb Ruby code to get a prediction:

saved_model = Tensorflow::Savedmodel.new
saved_model.LoadSavedModel('saved_model_pb', ['serve'], nil)

input = [0.97, 1.00]
feature1_output = saved_model.graph.operation('Placeholder').output(0)
feature2_output = 
saved_model.graph.operation('Placeholder_1').output(0)
classes = saved_model.graph.operation('dnn/head/predictions/str_classes').output(0)
# probabilities = saved_model.graph.operation('dnn/head/predictions/probabilities').output(0)

feature1_tensor = Tensorflow::Tensor.new(input[0])
feature2_tensor = Tensorflow::Tensor.new(input[1])
feeds_tensor_to_output_hash = {feature1_output => feature1_tensor,
                               feature2_output => feature2_tensor}
out_tensor = saved_model.session.run(feeds_tensor_to_output_hash, [classes], [])

puts out_tensor

This fails with an ArgumentError without an error message:

2018-05-22 17:07:08.443115: I tensorflow/cc/saved_model/loader.cc:242] Loading SavedModel with tags: { serve }; from: saved_model_pb/saved_model
2018-05-22 17:07:08.447053: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.2 AVX AVX2 FMA
2018-05-22 17:07:08.452575: I tensorflow/cc/saved_model/loader.cc:161] Restoring SavedModel bundle.
2018-05-22 17:07:08.453012: I tensorflow/cc/saved_model/loader.cc:171] The specified SavedModel has no variables; no checkpoints were restored.
2018-05-22 17:07:08.453024: I tensorflow/cc/saved_model/loader.cc:196] Running LegacyInitOp on SavedModel bundle.
2018-05-22 17:07:08.475575: I tensorflow/cc/saved_model/loader.cc:291] SavedModel load for tags { serve }; Status: success. Took 33095 microseconds.
rake aborted!
ArgumentError: 
.rbenv/versions/2.2.5/lib/ruby/gems/2.2.0/bundler/gems/tensorflow.rb-eb3f5bf4f0fd/lib/tensorflow/session.rb:57:in `Session_run'
.rbenv/versions/2.2.5/lib/ruby/gems/2.2.0/bundler/gems/tensorflow.rb-eb3f5bf4f0fd/lib/tensorflow/session.rb:57:in `run'
lib/tasks/ml.rake:380:in `block (2 levels) in <top (required)>'

Any idea what I'm doing wrong? The CAPTCHA example and model work fine. Is my model saved correctly? Here is the saved model that I used.

Wiwiweb
  • 1,076
  • 2
  • 8
  • 24
  • Probably you need run a global variable initialiser? https://stackoverflow.com/questions/44299666/when-global-variables-initializer-is-actually-required. Not sure what is Ruby equivalent – Tarun Lalwani Jun 06 '18 at 07:46

1 Answers1

0

It turned out to be a type mismatch. I saved the model to accept float32 variables. tensorflow.rb created input tensors from Ruby's float type, which is float64.

Changing this:

# Save the model under pb format
features = {'Float_input_1': tf.placeholder(tf.float32, shape=[1]),
            'Float_input_2': tf.placeholder(tf.float32, shape=[1])}

To this:

# Save the model under pb format
features = {'Float_input_1': tf.placeholder(tf.float64, shape=[1]),
            'Float_input_2': tf.placeholder(tf.float64, shape=[1])}

Fixed the problem.

Wiwiweb
  • 1,076
  • 2
  • 8
  • 24