tensorflow / tfjs

A WebGL accelerated JavaScript library for training and deploying ML models.
https://js.tensorflow.org
Apache License 2.0
18.45k stars 1.92k forks source link

tfjs_converter output format incorrect. #5808

Closed mkabatek closed 2 years ago

mkabatek commented 2 years ago

Hello i'm working with tfjs_converter on a saved model via the following command

tensorflowjs_converter --input_format=tf_saved_model --signature_name=serving_default --saved_model_tags=serve ./saved_model ./tfjs_model This seems to convert my model fine however there is some oddity in the outputs when looking at the output tensors from the saved model.json.

If I run the following command I get the following output tensors from the original saved model:

saved_model_cli show --dir ./saved_model --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
  The given SavedModel SignatureDef contains the following input(s):
  The given SavedModel SignatureDef contains the following output(s):
    outputs['__saved_model_init_op'] tensor_info:
        dtype: DT_INVALID
        shape: unknown_rank
        name: NoOp
  Method name is:

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['input_tensor'] tensor_info:
        dtype: DT_UINT8
        shape: (1, -1, -1, 3)
        name: serving_default_input_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['detection_anchor_indices'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 100)
        name: StatefulPartitionedCall:0
    outputs['detection_boxes'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 100, 4)
        name: StatefulPartitionedCall:1
    outputs['detection_classes'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 100)
        name: StatefulPartitionedCall:2
    outputs['detection_multiclass_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 100, 3)
        name: StatefulPartitionedCall:3
    outputs['detection_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 100)
        name: StatefulPartitionedCall:4
    outputs['num_detections'] tensor_info:
        dtype: DT_FLOAT
        shape: (1)
        name: StatefulPartitionedCall:5
    outputs['raw_detection_boxes'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 51150, 4)
        name: StatefulPartitionedCall:6
    outputs['raw_detection_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 51150, 3)
        name: StatefulPartitionedCall:7
  Method name is: tensorflow/serving/predict

Now using the converted tfjs model in node, I run the following command to get the outputNodes with the following output.

let model = await tf.loadGraphModel(handler);
console.log(model.outputNodes)
[
  'detection_multiclass_scores',
  'raw_detection_boxes',
  'Identity:0',
  'Identity_2:0',
  'detection_boxes',
  'raw_detection_scores',
  'num_detections',
  'Identity_4:0'
]

You can see that some of the output tensors didn't carry the names from the save model, they are also not ordered as the original saved model. Why do the output tensors: 'Identity:0', 'Identity_2:0', 'Identity_4:0' not have the same names as detection_anchor_indices, detection_scores, detection_classes. It's difficult to determine the output. This seems like a bug in the converter. Any insight on this issue is appreciated.

vladmandic commented 2 years ago

mapping output node names is a best effort and only added in recent versions, previously it was all just identity_n
but for mapping to work, tensors must have unique shape so they can be determined without errors. if two tensors have the same shape, there is no chance to know which one is which auto-magically

you can see that detection_anchor_indices and detection_classes have the same shape [1, 100]

its just how converter works, not a bug as such

and order of tensors is not something to ever rely on. that is not even a limitation.

mkabatek commented 2 years ago

@vladmandic thanks for that explanation that helps. Is there any way to prune or remove output tensors that may not be needed during the conversion using tensorflowjs_converter or a way to name or rename the outputs tensors or manually map them during the conversion?

The problem is that when using these outputs in tfjs knowing which output to use is important, if we have some arbitrary output names it makes interpreting the output difficult, unless I can rely on Identity_2:0 always mapping to detection_anchor_indices.

vladmandic commented 2 years ago

A) yes, you can rely on it since it's now defined in model.json B) you can run model.json through a prettyfier and then edit signature part to write down anything you want.

google-ml-butler[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you.

google-ml-butler[bot] commented 2 years ago

Closing as stale. Please @mention us if this needs more attention.

google-ml-butler[bot] commented 2 years ago

Are you satisfied with the resolution of your issue? Yes No