TropComplique / FaceBoxes-tensorflow

A fast face detector
MIT License
179 stars 66 forks source link

Converting to tf-coreml #19

Open mrgloom opened 5 years ago

mrgloom commented 5 years ago

I'm trying to find out endpoints of model like is done here: https://github.com/tf-coreml/tf-coreml/blob/master/examples/ssd_example.ipynb

Cause using default input/output node names produce error:

input_tensor_shapes {'image_tensor:0': [1, 256, 256, 3]}
output_tensor_names ['boxes:0', 'scores:0', 'num_boxes:0']

Loading the TF graph...
Graph Loaded.
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-15-3268ebb54f92> in <module>
     18         mlmodel_path=coreml_model_file,
     19         input_name_shape_dict=input_tensor_shapes,
---> 20         output_feature_names=output_tensor_names)

/usr/local/lib/python3.6/site-packages/tfcoreml/_tf_coreml_converter.py in convert(tf_model_path, mlmodel_path, output_feature_names, input_name_shape_dict, image_input_names, is_bgr, red_bias, green_bias, blue_bias, gray_bias, image_scale, class_labels, predicted_feature_name, predicted_probabilities_output, add_custom_layers, custom_conversion_functions)
    584       predicted_probabilities_output=predicted_probabilities_output,
    585       add_custom_layers=add_custom_layers,
--> 586       custom_conversion_functions=custom_conversion_functions)

/usr/local/lib/python3.6/site-packages/tfcoreml/_tf_coreml_converter.py in _convert_pb_to_mlmodel(tf_model_path, mlmodel_path, output_feature_names, input_name_shape_dict, image_input_names, is_bgr, red_bias, green_bias, blue_bias, gray_bias, image_scale, class_labels, predicted_feature_name, predicted_probabilities_output, add_custom_layers, custom_conversion_functions)
    165   print('Graph Loaded.')
    166   # Sort the ops in topological order and check whether the graph has cycles, if yes, error out
--> 167   OPS = _topological_sort_ops(OPS)
    168 
    169   SHAPE_DICT = {} #Tensor name --> shape ({str: list})

/usr/local/lib/python3.6/site-packages/tfcoreml/_tf_graph_transform.py in _topological_sort_ops(ops)
    192       node = _get_unvisited_child(G, stack[-1], not_visited)
    193       if node != -1:
--> 194         _push_stack(stack, node, in_stack)
    195       else:
    196         node = stack.pop()

/usr/local/lib/python3.6/site-packages/tfcoreml/_tf_graph_transform.py in _push_stack(stack, node, in_stack)
     36   stack.append(node)
     37   if node in in_stack:
---> 38     raise ValueError('Graph has cycles.')
     39   else:
     40     in_stack[node] = True

ValueError: Graph has cycles.
Screenshot 2019-03-14 at 14 39 35

On graph last block before nms is preprocessing, but I'm not sure what is output_tensor_names should be used.

I have tried to print node dimensions:

with tf.Graph().as_default() as graph:
    tf.import_graph_def(original_gdef, name='')
    for op in graph.get_operations():
        if 'postprocessing' in op.name:
            print('-'*60)
            print('str(op.name)', str(op.name))
            print('len(op.values())', len(op.values()))
            try:
                for i in range(len(op.values())):
                    print('op.values()[i].get_shape().as_list()', op.values()[i].get_shape().as_list())
            except:
                for i in range(len(op.values())):
                    print('op.values()[i].get_shape()', op.values()[i].get_shape())

Output:

------------------------------------------------------------
str(op.name) postprocessing/Shape
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice/stack
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice/stack_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice/stack_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/Shape_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_1/stack
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_1/stack_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_1/stack_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [1]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_1
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/ExpandDims/dim
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/ExpandDims
len(op.values()) 1
op.values()[i].get_shape().as_list() [1, 1364, 4]
------------------------------------------------------------
str(op.name) postprocessing/Tile/multiples/1
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/Tile/multiples/2
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/Tile/multiples
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/Tile
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 1364, 4]
------------------------------------------------------------
str(op.name) postprocessing/Reshape/shape
len(op.values()) 1
op.values()[i].get_shape().as_list() [2]
------------------------------------------------------------
str(op.name) postprocessing/Reshape
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 4]
------------------------------------------------------------
str(op.name) postprocessing/Reshape_1/shape
len(op.values()) 1
op.values()[i].get_shape().as_list() [2]
------------------------------------------------------------
str(op.name) postprocessing/Reshape_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 4]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/unstack
len(op.values()) 4
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/sub
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/sub_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/mul/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/mul
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/add
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/mul_1/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/mul_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_center_coordinates/add_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/unstack_1
len(op.values()) 4
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_1/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_2/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_3/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/truediv_3
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/Exp
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/mul
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/Exp_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/mul_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/mul_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/add
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/mul_3
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/add_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/sub
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_1/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/sub_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_2/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/add
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_3/x
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/mul_3
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/to_minmax_coordinates/add_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None]
------------------------------------------------------------
str(op.name) postprocessing/decode_predictions/stack
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 4]
------------------------------------------------------------
str(op.name) postprocessing/Reshape_2/shape/2
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/Reshape_2/shape
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/Reshape_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value/Minimum/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value/Minimum
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/truediv
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value_1/Minimum/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value_1/Minimum
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value_1/y
len(op.values()) 1
op.values()[i].get_shape().as_list() []
------------------------------------------------------------
str(op.name) postprocessing/clip_by_value_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 4]
------------------------------------------------------------
str(op.name) postprocessing/Softmax
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 2]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_2/stack
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_2/stack_1
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_2/stack_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [3]
------------------------------------------------------------
str(op.name) postprocessing/strided_slice_2
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None]

But I can't find out any meaningfull node name:

These looks like bboxes:

str(op.name) postprocessing/Tile
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 1364, 4]

str(op.name) postprocessing/ExpandDims
len(op.values()) 1
op.values()[i].get_shape().as_list() [1, 1364, 4]

Is it true that for 256x256 image we have 1364 bboxes?

With same approach I found node:

------------------------------------------------------------
str(op.name) postprocessing/Softmax
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, None, 2]

But not sure why it have shape [None, None, 2]?

Also here is postprocessing scope: https://github.com/TropComplique/FaceBoxes-tensorflow/blob/545ec4f4f3c55c3592ee189ed56a11a3fd017194/src/detector.py#L70

It should have output boxes with [batch_size, num_anchors, 4] shape and scores with [batch_size, num_anchors] shape.

mrgloom commented 5 years ago

I have ended with layers after prediction_layers scope, looks like it's maximum that tf-coreml can support in terms of operations.

I can't figure out how to get tensor names in tensorboard, so I have added print to get tensor names and the run python save.py:

        for i in range(len(box_encodings)):
            print('DEBUG: box_encodings[i]', box_encodings[i])

        for i in range(len(class_predictions_with_background)):
            print('DEBUG: class_predictions_with_background[i]', class_predictions_with_background[i])

Here in the code: https://github.com/TropComplique/FaceBoxes-tensorflow/blob/545ec4f4f3c55c3592ee189ed56a11a3fd017194/src/detector.py#L284

# V1: after feature extractor
# [None, None, None, 128], [None, None, None, 256], [None, None, None, 256]
# output_node_names = ['inception3/concat', 'conv3_2/Relu', 'conv4_2/Relu']

# V2: real netwrok outputs
# ValueError: Graph has cycles.
# output_node_names = ['boxes', 'scores', 'num_boxes']

# V3: before NMS
# NotImplementedError: Unsupported Ops of type: Unpack,Pack
# [batch_size, num_anchors, 4], [batch_size, num_anchors]
# output_node_names = ['postprocessing/clip_by_value_1', 'postprocessing/strided_slice_2']

# V4: after reshaping scope
# AssertionError: Reshape: Currently only supported if target shape is rank 2, 3 or 4
# output_node_names = ['reshaping/concat', 'reshaping/concat_1']

# V5: after prediction_layers scope
output_node_names = ['prediction_layers/box_encoding_predictor_0/BiasAdd',
                     'prediction_layers/box_encoding_predictor_1/BiasAdd',
                     'prediction_layers/box_encoding_predictor_2/BiasAdd',
                     'prediction_layers/class_predictor_0/BiasAdd',
                     'prediction_layers/class_predictor_1/BiasAdd',
                     'prediction_layers/class_predictor_2/BiasAdd']

For 256x256 input image shapes of output tensors are:

------------------------------------------------------------
str(op.name) image_tensor
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 256, 256, 3]
------------------------------------------------------------
str(op.name) prediction_layers/box_encoding_predictor_0/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 8, 8, 84]
------------------------------------------------------------
str(op.name) prediction_layers/class_predictor_0/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 8, 8, 42]
------------------------------------------------------------
str(op.name) prediction_layers/box_encoding_predictor_1/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 4, 4, 4]
------------------------------------------------------------
str(op.name) prediction_layers/class_predictor_1/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 4, 4, 2]
------------------------------------------------------------
str(op.name) prediction_layers/box_encoding_predictor_2/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 2, 2, 4]
------------------------------------------------------------
str(op.name) prediction_layers/class_predictor_2/BiasAdd
len(op.values()) 1
op.values()[i].get_shape().as_list() [None, 2, 2, 2]