1

I am using Google Collab, training a sequence model (NASNet) with a custom output. I export my model to an h5 using the model.save() method. I use a separate ipynb to load my h5 and convert it to a pb, however, my resulting pb lacks a named output node, and I cannot get predictions from my resulting model.

My model sequence definition:

model = tf.keras.Sequential([
  base_model,
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(num_labels, activation = 'sigmoid', name="output")])

model.summary()

I am able to make compile my model, train, and make predictions in collab.

I export my model like so:

model.save(model_path)

To convert to Tensorflow PB, I use a new ipynb and am able to 'successfully' run the conversion (ie, it doesn't fail).

The conversion code seems widely used elsewhere. Here it is:

import tensorflow as tf
import tensorflow.keras.backend as K

K.set_learning_phase(0)

restored_model = tf.keras.models.load_model(model_path)
print(restored_model.outputs)
print(restored_model.inputs)

def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
    """
    Freezes the state of a session into a pruned computation graph.

    Creates a new computation graph where variable nodes are replaced by
    constants taking their current value in the session. The new graph will be
    pruned so subgraphs that are not necessary to compute the requested
    outputs are removed.
    @param session The TensorFlow session to be frozen.
    @param keep_var_names A list of variable names that should not be frozen,
                          or None to freeze all the variables in the graph.
    @param output_names Names of the relevant graph outputs.
    @param clear_devices Remove the device directives from the graph for better portability.
    @return The frozen graph definition.
    """
    from tensorflow.python.framework.graph_util import convert_variables_to_constants
    graph = session.graph
    with graph.as_default():
        freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
        output_names = output_names or []
        output_names += [v.op.name for v in tf.global_variables()]
        # Graph -> GraphDef ProtoBuf
        input_graph_def = graph.as_graph_def()
        if clear_devices:
            for node in input_graph_def.node:
                node.device = ""
        frozen_graph = convert_variables_to_constants(session, input_graph_def,
                                                      output_names, freeze_var_names)
        return frozen_graph




frozen_graph = freeze_session(K.get_session(),
                              output_names=[out.op.name for out in restored_model.outputs], 
                             clear_devices=True)

tf.train.write_graph(frozen_graph, "/tmp", model_name+".pb", as_text=False)

output_model_name = model_name + ".pb"
output_model_path = "/tmp/" + output_model_name

This resulting pb appears well formed at first glance, but Netron and other tooling indicates it has no final output node in the graph.

I am able to verify my tf.keras h5 model has inputs and outputs in my first ipynb via:

print(model.input.op.name)
print(model.output.op.name)

Which returns:

NASNet_input
output/Identity

And when I load my saved h5 in prep to convert to Tensorflow protobuff, I also check the inout and output names:

print(restored_model.outputs)
print(restored_model.inputs)

Which returns in TF naming convention:

[<tf.Tensor 'output/Sigmoid:0' shape=(?, 229) dtype=float32>]
[<tf.Tensor 'NASNet_input:0' shape=(?, 224, 224, 3) dtype=float32>]

So clearly my h5 appears to have an output node named 'output' - as confirmed in Netron when I inspect the H5.

Why does my conversion code appear to be removing the output node in the pb graph?

Thank you!

vade
  • 702
  • 4
  • 22

1 Answers1

0

So, it appears that tf 2.0 resolve this bug (see https://github.com/tensorflow/tensorflow/issues/26809) , which is unhelpful because 1.14 can export to a valid protobuf (which I need, I can't use the SaveModel new format in 2.0 since coreML converters can't read it, and onnx converters die on this saved model for some other reason) - so this entire endeavor appears to be a dead end.

Can one seriously not export TF 2.0 Keras model to a Protobuffers compatible with 1.14 runtime?

vade
  • 702
  • 4
  • 22
  • Were you able to get to the bottom of this yourself. I'm in a similar situation and haven't found anything online to help. The .h5 file shows a nice graph in [Netron](https://netron.app/), but when I convert it to pb (or just save it as a pb instead of h5) it is completely different in Netron. Just wondering if you got anywhere with this. [Here](https://stackoverflow.com/questions/64794378/input-output-nodes-in-tensorflow-model-are-unrecognizable-from-original-build) is my inquire on this. – Josh Nov 12 '20 at 15:23
  • Yea, I switched to PyTorch. I know its not helpful but christ TF is a mess. – vade Nov 21 '20 at 18:47