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!