I have seen many posts about restoring an already saved TF models here, but none could answer my question. Using TF 1.0.0
Specifically, I am interested in seeing the weights for inceptionv3 model which is publicly available in .pb file here. I managed to restore it back using a small chunk of Python code and can access the graphs high-level view in tensorboard:
from tensorflow.python.platform import gfile
INCEPTION_LOG_DIR = '/tmp/inception_v3_log'
if not os.path.exists(INCEPTION_LOG_DIR):
os.makedirs(INCEPTION_LOG_DIR)
with tf.Session() as sess:
model_filename = './model/tensorflow_inception_v3_stripped_optimized_quantized.pb'
with gfile.FastGFile(model_filename, 'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
_= tf.import_graph_def(graph_def,name='')
writer = tf.train.SummaryWriter(INCEPTION_LOG_DIR, graph_def)
writer=tf.summary.FileWriter(INCEPTION_LOG_DIR, graph_def)
writer.close()
However, I failed to access any layers' weights.
tensors= tf.import_graph_def(graph_def,name='')
returns empty, even if I add the arbitrary return_elements=. Does it have any weights at all? If yes, what is the appropriate procedure here? Thanks.
use this code to print your tensor's value :
with tf.Session() as sess:
print sess.run('your_tensor_name')
you can use this code to retrieve tensor names:
op = sess.graph.get_operations()
for m in op :
print(m.values())
There is a difference between restoring weights and printing them. The former one denotes that one would like to import the weight values from already saved ckpt files for retraining or inference while the latter may be for inspection. Also .pb file encodes model parameters as tf.constant() ops. As a result, the model parameters would not appear in tf.trainable_variables(), hence you can't use .pb directly to restore the weights. From your question I take that you just want to 'see' the weights for inspection.
Let us first load the graph from .pb file.
import tensorflow as tf
from tensorflow.python.platform import gfile
GRAPH_PB_PATH = './model/tensorflow_inception_v3_stripped_optimized_quantized.pb' #path to your .pb file
with tf.Session(config=config) as sess:
print("load graph")
with gfile.FastGFile(GRAPH_PB_PATH,'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
sess.graph.as_default()
tf.import_graph_def(graph_def, name='')
graph_nodes=[n for n in graph_def.node]
Now when you freeze a graph to .pb file your variables are converted to Const type and the weights which were trainabe variables would also be stored as Const in .pb file. graph_nodes contains all the nodes in graph. But we are interested in all the Const type nodes.
wts = [n for n in graph_nodes if n.op=='Const']
Each element of wts is of NodeDef type. It has several atributes such as name, op etc. The values can be extracted as follows -
from tensorflow.python.framework import tensor_util
for n in wts:
print "Name of the node - %s" % n.name
print "Value - "
print tensor_util.MakeNdarray(n.attr['value'].tensor)
Hope this solves your concern.
You can use this code to get the names of tensor.
[tensor.name for tensor in tf.get_default_graph().as_graph_def().node]
Just small utils to print .pb model weights:
import argparse
import tensorflow as tf
from tensorflow.python.framework import tensor_util
def print_pb_weights(pb_filepath):
graph_def = tf.GraphDef()
with tf.gfile.GFile(pb_filepath, "rb") as f:
graph_def.ParseFromString(f.read())
tf.import_graph_def(graph_def, name='')
for node in graph_def.node:
if node.op == 'Const':
print('-' * 60)
print('op:', node.op)
print('name:', node.name)
arr = tensor_util.MakeNdarray(node.attr['value'].tensor)
print('shape:', list(arr.shape))
print(arr)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('pb_filepath')
args = parser.parse_args()
print_pb_weights(args.pb_filepath)
Related
I have a tensorflow model as a frozen graph, which accepts an image tensor as an input. However, I'd like to add a new input image decoder node to this graph, so that the model also accepts an encoded byte string of a jpg image and eventually decodes the image by itself. I've tried this approach so far:
model = './frozen_graph.pb'
with tf.gfile.FastGFile(model, 'rb') as f:
# read graph
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
tf.import_graph_def(graph_def, name="")
g = tf.get_default_graph()
# fetch old input
old_input = g.get_tensor_by_name('image_tensor:0')
# define new input
new_input = graph_def.node.add()
new_input.name = 'encoded_image_string_tensor'
new_input.op = 'Substr'
# add new input attr
image = tf.image.decode_image(new_input, channels=3)
# link new input to old input
old_input.input = 'encoded_image_string_tensor' # must match with the name above
The above code returns this exception:
Expected string passed to parameter 'input' of op 'Substr', got name: "encoded_image_string_tensor" op: "Substr" of type 'NodeDef' instead.
I'm not quite sure if I can use the tf.image.decode_image within a graph, so maybe there's another way to solve this problem. Anybody got a hint?
Thanks to jdehesa, who gave me a good hint, I was able to solve this problem. With the input_map parameter I successfully mapped a new graph, which just decodes jpg images, to the input of my original graph (here: node.name='image_tensor:0'). Just make sure, that you rename the name_scope of the decoder graph (here: decoder).
After that, you can save the new concatenated graph by using the tensorflow SavedModelBuilder.
Here an example of an object detection network:
import tensorflow as tf
from tensorflow.python.saved_model import signature_constants
from tensorflow.python.saved_model import tag_constants
# The export path contains the name and the version of the model
model = 'path/to/model.pb'
export_path = './output/dir/'
sigs = {}
with tf.gfile.FastGFile(model, 'rb') as f:
with tf.name_scope('decoder'):
image_str_tensor = tf.placeholder(tf.string, shape=[None], name= 'encoded_image_string_tensor')
# The CloudML Prediction API always "feeds" the Tensorflow graph with
# dynamic batch sizes e.g. (?,). decode_jpeg only processes scalar
# strings because it cannot guarantee a batch of images would have
# the same output size. We use tf.map_fn to give decode_jpeg a scalar
# string from dynamic batches.
def decode_and_resize(image_str_tensor):
"""Decodes jpeg string, resizes it and returns a uint8 tensor."""
image = tf.image.decode_jpeg(image_str_tensor, channels=3)
# do additional image manipulation here (like resize etc...)
image = tf.cast(image, dtype=tf.uint8)
return image
image = tf.map_fn(decode_and_resize, image_str_tensor, back_prop=False, dtype=tf.uint8)
with tf.name_scope('net'):
# load .pb file
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
# concatenate decoder graph and original graph
tf.import_graph_def(graph_def, name="", input_map={'image_tensor:0':image})
g = tf.get_default_graph()
with tf.Session() as sess:
# load graph into session and save to new .pb file
# define model input
inp = g.get_tensor_by_name('decoder/encoded_image_string_tensor:0')
# define model outputs
num_detections = g.get_tensor_by_name('num_detections:0')
detection_scores = g.get_tensor_by_name('detection_scores:0')
detection_boxes = g.get_tensor_by_name('detection_boxes:0')
out = {'num_detections': num_detections, 'detection_scores': detection_scores, 'detection_boxes': detection_boxes}
builder = tf.saved_model.builder.SavedModelBuilder(export_path)
tensor_info_inputs = {
'inputs': tf.saved_model.utils.build_tensor_info(inp)}
tensor_info_outputs = {}
for k, v in out.items():
tensor_info_outputs[k] = tf.saved_model.utils.build_tensor_info(v)
# assign detection signature for tensorflow serving
detection_signature = (
tf.saved_model.signature_def_utils.build_signature_def(
inputs=tensor_info_inputs,
outputs=tensor_info_outputs,
method_name=signature_constants.PREDICT_METHOD_NAME))
# "build" graph
builder.add_meta_graph_and_variables(
sess, [tf.saved_model.tag_constants.SERVING],
signature_def_map={
'detection_signature':
detection_signature,
signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
detection_signature,
},
main_op=tf.tables_initializer()
)
# save graph
builder.save()
Sources I used to find the right solution:
Coding example
Scope names explanation
Tensorflow Github Issue #22162
Additionally:
If you struggle with finding the right input and output nodes, you can run this to display the graph:
graph_op = g.get_operations()
for i in graph_op:
print(i.node_def)
I'm trying to look at variable list of loaded .pb file, but for some reason it's empty.
Here is the code:
import tensorflow as tf
tf_model_path = './tf_coreml_ssd_resources/ssd_mobilenet_v1_android_export.pb'
with open(tf_model_path, 'rb') as f:
serialized = f.read()
tf.reset_default_graph()
original_gdef = tf.GraphDef()
original_gdef.ParseFromString(serialized)
# V1
with tf.Graph().as_default() as g:
print('type(g)', type(g)) # type(g) <class 'tensorflow.python.framework.ops.Graph'>
tf.import_graph_def(original_gdef, name='')
model_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
print('type(model_vars)', type(model_vars))
print('model_vars', model_vars)
# V2
graph = tf.import_graph_def(original_gdef, name='')
print('type(graph)', type(graph)) # why type(graph) <class 'NoneType'> ?
model_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
print('type(model_vars)', type(model_vars))
print('model_vars', model_vars)
Also why is in V2 case I get type(graph) <class 'NoneType'> ?
The GraphDef object serialized to the .pb file does not contain collections information. If you want to store a graph along with its metadata (including the collections), you should save a MetaGraphDef instead (see tf.train.export_meta_graph / tf.train.import_meta_graph).
In your V2 code, graph is None because tf.import_graph_def does not return anything, it just imports the nodes in the given graph definition into the current default graph.
As a side comment, note that graph collections are being deprecated in TensorFlow 2.x.
I have a problem with making batch inference using a tensorflow protobuf graph exported from a keras h5 model. Eventhough the exported pb graph can accept multiple inputs (samples), it always gives a single output regardless of the number of inputs. Here is a simple example to demonstrate the problem.
from keras.models import Model,load_model
from keras.layers import Dense, Input
from keras import backend as K
import tensorflow as tf
import numpy as np
import os
import os.path as osp
pinput = Input(shape=[10,], name='my_input')
poutput = Dense(1, activation='sigmoid')(pinput)
model = Model(inputs=[pinput], outputs=[poutput])
model.compile(loss='mean_squared_error',optimizer='sgd',metrics=['accuracy'])
data = np.random.random((100, 10))
labels = np.random.randint(2, size=(100, 1))
model.fit(data, labels, epochs=1, batch_size=32)
x = np.random.random((3, 10))
y = model.predict(x)
print y
####################################
# Save keras h5 to tensorflow pb
####################################
K.set_learning_phase(0)
#alias output names
numoutputs = 1
pred = [None]*numoutputs
pred_node_names = [None]*numoutputs
for i in range(numoutputs):
pred_node_names[i] = 'output'+'_'+str(i)
pred[i] = tf.identity(model.output[i], name=pred_node_names[i])
print('Output nodes names are: ', pred_node_names)
sess = K.get_session()
# Write the graph in human readable
f = 'graph_def_for_reference.pb.ascii'
tf.train.write_graph(sess.graph.as_graph_def(), '.', f, as_text=True)
input_graph_def = sess.graph.as_graph_def()
#freeze graph
from tensorflow.python.framework.graph_util import convert_variables_to_constants
output_names = pred_node_names
output_names += [v.op.name for v in tf.global_variables()]
constant_graph = convert_variables_to_constants(sess, input_graph_def,output_names)
# Write the graph in binary .pb file
from tensorflow.python.framework import graph_io
graph_io.write_graph(constant_graph, '.', 'model.pb', as_text=False)
def load_graph(frozen_graph_filename):
# We load the protobuf file from the disk and parse it to retrieve the
# unserialized graph_def
with tf.gfile.GFile(frozen_graph_filename, "rb") as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
# Then, we import the graph_def into a new Graph and returns it
with tf.Graph().as_default() as graph:
# The name var will prefix every op/nodes in your graph
# Since we load everything in a new graph, this is not needed
tf.import_graph_def(graph_def, name="prefix")
return graph
###################################
# Test batch inference with tf
###################################
graph = load_graph("model.pb")
for op in graph.get_operations():
print(op.name)
minput = graph.get_tensor_by_name('prefix/my_input:0')
moutput = graph.get_tensor_by_name('prefix/output_0:0')
with tf.Session(graph=graph) as sess:
y = sess.run(moutput, feed_dict={minput: x})
print y
The output of the run is
Epoch 1/1
100/100 [==============================] - 0s 661us/step - loss: 0.2655 - acc: 0.3900
[[0.62018263]
[0.41664478]
[0.40322617]]
('Output nodes names are: ', ['output_0'])
prefix/my_input
prefix/dense_1/kernel
prefix/dense_1/kernel/read
prefix/dense_1/bias
prefix/dense_1/bias/read
prefix/dense_1/MatMul
prefix/dense_1/BiasAdd
prefix/dense_1/Sigmoid
prefix/SGD/iterations
prefix/SGD/lr
prefix/SGD/momentum
prefix/SGD/decay
prefix/training/SGD/Variable
prefix/training/SGD/Variable_1
prefix/strided_slice/stack
prefix/strided_slice/stack_1
prefix/strided_slice/stack_2
prefix/strided_slice
prefix/output_0
[0.62018263]
You can see the keras h5 graphs gives 3 ouputs wile the tensorflow pb graph just gives the first output. What am I doing wrong? I would like to
modify the h5 to pb conversion process so that I can do batch inference using the pb grapth with the python and c++ tensorflow backends.
It turns out this is due to a bug I inherited from k2tf_convert
pred[i] = tf.identity(model.output[i], name=pred_node_names[i])
should be
pred[i] = tf.identity(model.outputs[i], name=pred_node_names[i])
It seems the keras model class has both 'output' and 'outputs' members that makes this bug hard to track.
I am using the sample to build a CNN as per this article: https://www.tensorflow.org/tutorials/layers
However, I am unable to find a sample to predict by feeding in a sample image. Any help here would be highly appreciated.
Below is what I have tried, and not able to find the output tensor name
img = <load from file>
sess = tf.Session()
saver = tf.train.import_meta_graph('/tmp/mnist_convnet_model/model.ckpt-2000.meta')
saver.restore(sess, tf.train.latest_checkpoint('/tmp/mnist_convnet_model/'))
input_place_holder = sess.graph.get_tensor_by_name("enqueue_input/Placeholder:0")
out_put = <not sure what the tensor output name in the graph>
current_input = img
result = sess.run(out_put, feed_dict={input_place_holder: current_input})
print(result)
You can use the inspect_checkpoint tool in Tensorflow to find the tensors inside a checkpoint file.
from tensorflow.python.tools.inspect_checkpoint import print_tensors_in_checkpoint_file
print_tensors_in_checkpoint_file(file_name="tmp/mnist_convnet_model/model.ckpt-2000.meta", tensor_name='')
There are nice instructions on how to save and restore in tensorflows programming guide. Here is a small example inspired from the latter link. Just make sure that the ./tmp dir exists
import tensorflow as tf
# Create some variables.
variable = tf.get_variable("variable_1", shape=[3], initializer=tf.zeros_initializer)
inc_v1=variable.assign(variable + 1)
# Operation to initialize variables if we do not restore from checkpoint
init_op = tf.global_variables_initializer()
# Create the saver
saver = tf.train.Saver()
with tf.Session() as sess:
# Setting to decide wether or not to restore
DO_RESTORE=True
# Where to save the data file
save_path="./tmp/model.ckpt"
if DO_RESTORE:
# If we want to restore, load the variables from the saved file
saver.restore(sess, save_path)
else:
# If we don't want to restore, then initialize variables
# using their specified initializers.
sess.run(init_op)
# Print the initial values of variable
initial_var_value=sess.run(variable)
print("Initial:", initial_var_value)
# Do some work with the model.
incremented=sess.run(inc_v1)
print("Incremented:", incremented)
# Save the variables to disk.
save_path = saver.save(sess, save_path)
print("Model saved in path: %s" % save_path)
I have a trained tensorflow model saved in form of a checkpoint, .data, .meta, and a .index file. The model uses batch normalization.
I tried converting it to .pb file using freeze_graph, which can be imported as from tensorflow.python.tools import freeze_graph. The input to this is also a .pb file but one which has only the graph structure.
I restore the model with the following code
sess = tf.Session()
saver = tf.train.import_meta_graph(r'.\path\to\model\VanillaCNN.0000.meta')
saver.restore(sess, tf.train.latest_checkpoint(r'.\path\to\model'))
graph = tf.get_default_graph()
Then a .pb file which contains the graph structure is created with
tf.train.write_graph(sess.graph_def, "", "model_proto.pb", False)
After this the I use freeze_graph to generate a .pb file which includes the graph structure as well as the weights.
The inputs to freeze_graph were
input_graph_path = r'.\path\to\model\model_proto.pb'
input_saver_def_path = ""
input_binary = False
input_checkpoint_path = r'.\path\to\model\VanillaCNN.0000'
output_node_names = "VanillaCNNoutput_10/layer_output"
restore_op_name = "save/restore_all"
filename_tensor_name = "save/Const:0"
output_graph_path = r'.\path\to\model\frozen_model.pb'
clear_devices = False
initializer_nodes=""
executed as
freeze_graph.freeze_graph(input_graph_path, input_saver_def_path,input_binary, input_checkpoint_path,output_node_names, restore_op_name,filename_tensor_name, output_graph_path,clear_devices,initializer_nodes)
This creates frozen_model.pb, when I try to load it back as below
def load_graph(frozen_graph_filename):
with tf.gfile.GFile(frozen_graph_filename, "rb") as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
with tf.Graph().as_default() as graph:
tf.import_graph_def(graph_def, input_map=None, return_elements=None, name="", op_dict=None, producer_op_list=None)
return graph
it throws the following error
ValueError: graph_def is invalid at node 'VanillaCNNconv_0/VanillaCNNconv_0/cond/Assign': Input tensor 'VanillaCNNconv_0/VanillaCNNconv_0/cond/Assign/Switch:1' Cannot convert a tensor of type float32 to an input of type float32_ref.
How can I fix this?