run SSD_MobileNetV2 (Tensorflow object detection API) on TensorRT

I am working on inferencing using trained tensorflow SSD_mobilenetV2 models (from Tensorflow/models/research/object_detection), following the example given by sampleUffSSD (TensorRT-4.0.1.6). The conversion from the frozen graph to UFF file is successful, however, when running ./sample_uff_ssd, erros occur:
ERROR: UFFParser: Parser error: BoxPredictor_0/Reshape: Reshape: -1 dimension specified more than 1 time
In the frozen graph from ssd_inception_v2_coco_2018_01_28, which works well with sampleUffSSD, BoxPredictor_0/Reshape:0 has a shape of (?, 1083, 1, 4).
In the forzen graph from the ssd_mobilenetV2 model built by tensorflow object detection API, however, BoxPredictor_0/Reshape:0 has another shape of (?, ?, 1, 4). In the code [https://github.com/tensorflow/models/blob/03612984e9f7565fed185977d251bbc23665396e/research/object_detection/predictors/heads/box_head.py#L183], four shape parameter is given by [batch_size, -1, 1, self._box_code_size] (box_encodings = tf.reshape(box_encodings,[batch_size, -1, 1, self._box_code_size]). So it seems to me that, batch_size is also recognized as “-1” by the UFF converter, so the input shape of the reshape operation becomes [-1, -1, 1, self._box_code_size], which leads to the error: Reshape: -1 dimension specified more than 1 time

The possible solution I can think about is that, when building the graph, a value instead of “-1” (e.g. 1083 for BoxPredictor_0) should be used. This however, required extra work to be integrated into the object detection API, and we might need to do the same for classPredictor reshape operation in different scales.

Could someone please help with this?

Hello,

can you please point me to the exact SSD_mobilenetV2 trained model you are using?

thanks

Thanks NVES for your swift reply. It is built from tensorflow object detection api (https://github.com/tensorflow/models/tree/master/research/object_detection) by the config file pasted:

The frozen graph, converted uff, config.py, list of lables are all available here: Dropbox - trained_tf_model_ssd_mobilenet_v2 - Simplify your life

model {
  ssd {
    num_classes: 1
    image_resizer {
      fixed_shape_resizer {
        height: 300
        width: 300
      }
    }
    feature_extractor {
      type: "ssd_mobilenet_v2"
      depth_multiplier: 1.0
      min_depth: 16
      conv_hyperparams {
        regularizer {
          l2_regularizer {
            weight: 3.99999989895e-05
          }
        }
        initializer {
          truncated_normal_initializer {
            mean: 0.0
            stddev: 0.0299999993294
          }
        }
        activation: RELU_6
        batch_norm {
          decay: 0.999700009823
          center: true
          scale: true
          epsilon: 0.0010000000475
          train: true
        }
      }
    }
    box_coder {
      faster_rcnn_box_coder {
        y_scale: 10.0
        x_scale: 10.0
        height_scale: 5.0
        width_scale: 5.0
      }
    }
    matcher {
      argmax_matcher {
        matched_threshold: 0.5
        unmatched_threshold: 0.5
        ignore_thresholds: false
        negatives_lower_than_unmatched: true
        force_match_for_each_row: true
      }
    }
    similarity_calculator {
      iou_similarity {
      }
    }
    box_predictor {
      convolutional_box_predictor {
        conv_hyperparams {
          regularizer {
            l2_regularizer {
              weight: 3.99999989895e-05
            }
          }
          initializer {
            truncated_normal_initializer {
              mean: 0.0
              stddev: 0.0299999993294
            }
          }
          activation: RELU_6
          batch_norm {
            decay: 0.999700009823
            center: true
            scale: true
            epsilon: 0.0010000000475
            train: true
          }
        }
        min_depth: 0
        max_depth: 0
        num_layers_before_predictor: 0
        use_dropout: false
        dropout_keep_probability: 0.800000011921
        kernel_size: 1
        box_code_size: 4
        apply_sigmoid_to_scores: false
      }
    }
    anchor_generator {
      ssd_anchor_generator {
        num_layers: 6
        min_scale: 0.20000000298
        max_scale: 0.949999988079
        aspect_ratios: 1.0
        aspect_ratios: 2.0
        aspect_ratios: 0.5
        aspect_ratios: 3.0
        aspect_ratios: 0.333299994469
      }
    }
    post_processing {
      batch_non_max_suppression {
        score_threshold: 9.99999993923e-09
        iou_threshold: 0.20000000298
        max_detections_per_class: 100
        max_total_detections: 100
      }
      score_converter: SIGMOID
    }
    normalize_loss_by_num_matches: true
    loss {
      localization_loss {
        weighted_smooth_l1 {
        }
      }
      classification_loss {
        weighted_sigmoid {
        }
      }
      hard_example_miner {
        num_hard_examples: 3000
        iou_threshold: 0.800000011921
        loss_type: CLASSIFICATION
        max_negatives_per_positive: 3
        min_negatives_per_image: 3
      }
      classification_weight: 1.0
      localization_weight: 2.0
    }
  }
}
train_config {
  batch_size: 96
  data_augmentation_options {
    random_adjust_brightness {
      max_delta: 0.10000000149
    }
  }
  data_augmentation_options {
    random_adjust_contrast {
      min_delta: 0.899999976158
      max_delta: 1.10000002384
    }
  }
  data_augmentation_options {
    random_adjust_hue {
      max_delta: 0.0299999993294
    }
  }
  data_augmentation_options {
    random_adjust_saturation {
      min_delta: 0.949999988079
      max_delta: 1.04999995232
    }
  }
  data_augmentation_options {
    random_horizontal_flip {
    }
  }
  data_augmentation_options {
    random_vertical_flip {
    }
  }
  data_augmentation_options {
    random_image_scale_crop_pad {
      min_scale_ratio: 0.5
      max_scale_ratio: 1.0
    }
  }
  optimizer {
    rms_prop_optimizer {
      learning_rate {
        manual_step_learning_rate {
          initial_learning_rate: 0.0010000000475
          schedule {
            step: 80000
            learning_rate: 0.000199999994948
          }
          schedule {
            step: 150000
            learning_rate: 3.99999989895e-05
          }
        }
      }
      momentum_optimizer_value: 0.899999976158
      decay: 0.899999976158
      epsilon: 1.0
    }
  }
  fine_tune_checkpoint: "/path/model.ckpt-150000"
  num_steps: 200000
  fine_tune_checkpoint_type: "detection"
}
train_input_reader {
  label_map_path: "/path/tt_label_map.pbtxt"
  shuffle: true
  num_readers: 100
  tf_record_input_reader {
    input_path: "/path/tt_train.record-?????-of-00100"
  }
}
eval_config {
  num_examples: 2040
  max_evals: 1
  use_moving_averages: false
}
eval_input_reader {
  label_map_path: "/path/tt_label_map.pbtxt"
  shuffle: false
  num_epochs: 1
  tf_record_input_reader {
    input_path: "/path/tt_val.tfrecord"
  }
}

Hello,

Per engineering, the UFF Parser fix should be available in the next release. However, it may be possible to hack the TF graph and change the batch dimension to anything other than -1. That, combined with the attached config file, would bypass the error.

regards,
NVIDIA Enterprise Support
config.zip (889 Bytes)

Thanks for your help. I fixed the batch size==2 and it works now! Look forward to the updates in the next release!

NVES - is there any information regarding when this fix will be released? Thanks.

Hi - how did you manage to “hack the TF graph”? when you say that you fixed the batch size do you mean that you retrained or changed it in the graphdef somehow?

Thanks in advance for any pointers you can give.

Hi, you can still use the model you trained, but before generating the frozen graph, replace “batch size” with the batch size you want (e.g. 1 or 2) in the following lines in predictors/headers/box_head.py and predictors/headers/class_head.py:

class_predictions_with_background = tf.reshape(
    class_predictions_with_background, [1, -1, num_class_slots])
box_encodings = tf.reshape(box_encodings,
    [1, -1, 1, self._box_code_size])

Then run “export_inference_graph.py” to generate the frozen graph.

I tested the generated graph in tensorflow python/CAPI and it works, which means this “fixing batch size” was successful. However, when using it with sampleUFFSSD.cpp, although a forward pass was enabled, it output garbage. I am still trying to find the reason.

@NVES:

Do you have any suggestions on this? Please find the frozen graph with fixed batch size 1 (frozen_inference_graph_batch_size_1.pb) and config file (config_mobilenet.py) in the following link:

BTW, in the modified “sampleUFFSSD.cpp” I only set batch size to 1 (N = 1), and redefined the path to test image and .uff file. Is there anything else that I should change too?

Thanks for your help!

And another issue confused me is the conversion of relu6 layer.

In the document “4.1.2. Example 2: Adding A Custom Layer That Is Not Supported In UFF Using C++”, it is suggested that relu6 should be replaced by a customized layer, and declared in config.py. However, in the example of sampleUffSSD, it seems that relu6 is directly taken care of by convert-to-uff. So can we assume that relu6 is supported by tensorRT now?

@NEVS,

Hi, just figured out why my ssd_mobilenetv2 output garbage – I converted my .jpg to .ppm using opencv, which added an additional line to the header information of generated .ppm file, and this extra line led to incorrect image reading.

So I can confirm that with the config.py file provided by @NEVS, and the way I used to generate the frozen graph:

Exactly the same inference outputs could be expected from tensorflow object detection API and tensorRT for ssd_mobilenetv2.

Thanks a lot for your help.

Hey,
Thanks for your help @NEVS & @xjtulyj! I have gone through all the steps of exporting the frozen graph.
What version of graphsurgeon are you guys using. For the above given config file, I get a python error that says,

AttributeError: module 'graphsurgeon' has no attribute 'create_plugin_node'

I am using graphsurgeon 0.2.0 and I don’t see this method in the docs given in https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/tensorrt_401/tensorrt-api/python_api/graphsurgeon/graphsurgeon.html.

If anyone can help me or show me what I am missing, it’ll be really helpful.
Thanks!

Hi,

create_plugin_node is available in Tensorrt 5. I have tested on tensorrt 5.0.2.6 and tensorrt 5.0.4 it both works.

Thanks a lot @xjtuljy!
Will upgrade to TensorRT 5 and check it out.

For TensorRT 4 if config.py file got modified in the following way it should also work:

  1. following the example config.py to use create_node:
concat_priorbox = gs.create_node("concat_priorbox", dtype=tf.float32, axis=2)
concat_box_loc = gs.create_node("concat_box_loc")
concat_box_conf = gs.create_node("concat_box_conf")
  1. remove “Squeeze” node from the dynamic_graph to avoid error “Assertion `isPlugin(layerName)’ failed”
Squeeze = dynamic_graph.find_node_inputs_by_name(dynamic_graph.graph_outputs[0], 'Squeeze')
    dynamic_graph.forward_inputs(Squeeze)

This point was mentioned in a previous discussion in this forum but I cannot find it now so I’m not referencing the original post.

I tested it with TensorRT 4.0.1.6 and it worked.

Hi xjtuljy,
Thanks a lot for your help. I did install TRT5 but its still giving me a segmentation fault error while running the executable. Trying to debug that for now. Also, I’m using a custom trained ssd_mobilenet_v1_coco model. Will keep you updated with the progress.

Thanks again! You’ve been really helpful. :)

Thanks for your help @NEVS & @xjtulyj!I successfully convert the frozen graph to UFF file.
But there’s a problem that bothers me. I hope you can give me some advice:

When the uff file and the frozen graph.pb file predict the same picture,the confidence of predicted boxes change greatly .

in frozen_inference_graph.uff ,the results(top10) as like this:

s:0.997611
  s:0.985859
  s:0.982447
  s:0.980549
  s:0.974113
  s:0.965314
  s:0.964978
  s:0.960484
  s:0.953297
  s:0.951944

but in frozen_inference_graph.pb,the results(top10) as like this:

0.9945557
  0.0252087  
  0.01583725 
  0.0116272  
  0.01065959 
  0.01036237
  0.00991684 
  0.00956967 
  0.00916798 
  0.00816284 
 0.00802886

When I use the official config file,when running ./sample_uff_ssd, the erros was same with @xjtulyj,but successfully when I use @NEVS provided config file.
I noticed the difference between official config file and the @NEVS provided.
In official config:

NMS = gs.create_plugin_node(name="NMS", op="NMS_TRT",
    shareLocation=1,
    varianceEncodedInTarget=0,
    backgroundLabelId=0,
    confidenceThreshold=1e-8,
    nmsThreshold=0.6,
    topK=100,
    keepTopK=100,
    numClasses=91,
    inputOrder=[0, 2, 1],
    confSigmoid=1,
    isNormalized=1,
    <b>scoreConverter="SIGMOID"</b>)

In @@NEVS provided:

NMS = gs.create_plugin_node(name="NMS", op="NMS_TRT",
    shareLocation=1,
    varianceEncodedInTarget=0,
    backgroundLabelId=0,
    confidenceThreshold=1e-8,
    nmsThreshold=0.6,
    topK=100,
    keepTopK=100,
    numClasses=2,
    inputOrder=[0, 2, 1],
    confSigmoid=1,
    isNormalized=1)

So,I guess that is it the missing function of scoreConverter in @NEVS provided config file that causes the change of scores?
@NEVS,Can you tell me how resovle this problem,and What role did scoreConverter=“SIGMOID” play in the official config file?

Hi 315369731,

I checked my config.py file I actually have

scoreConverter="SIGMOID"

I cannot remember why I added this to @NEVS’s file, but I succeeded in converting .pb to .uff using this config.py. And with correct interpolation to resize and generate .ppm files (use cv2.INTER_LINEAR since tf uses it to scale the input), in my case the scores for the top several outputs of trt are extactly the same as tf. But I didn’t check that many topK outputs as you did, since in my case there are maximally 4 detected objects in one image. Please also notice that nmsThreshold also affects trt outputs and can make it inconsistent with tf outputs (but the highest score should be the same if the right interpolation is used).

Hi!
@NEVS
@xjtulyj
As for the questions I mentioned earlier, I have some new findings:

I tested it with two different models I trained.The models built also from tensorflow object detection api (https://github.com/tensorflow/models/tree/master/research/object_detection) . Abnormal phenomena on scores only arises in the first model.I guess the reason for this is the use of score_converter: SOFTMAX in the first model.But in the second model I used the score_converter: SIGMOID,in this case the scores for the top several outputs of trt are extactly the same as tf,This phenomenon is the same as @xjtulyj mentioned.I don’t know what you think about it.
If I want to use the first model, how can I solve the problem caused by using the score_converter: SOFTMAX?

the first model config file is as follows:

model {
  ssd {
    num_classes: 4
    image_resizer {
      fixed_shape_resizer {
        height: 300
        width: 300
      }
    }
    feature_extractor {
      type: "ssd_mobilenet_v2"
      depth_multiplier: 1.0
      min_depth: 16
      conv_hyperparams {
        regularizer {
          l2_regularizer {
            weight: 3.9999998989515007e-05
          }
        }
        initializer {
          truncated_normal_initializer {
            mean: 0.0
            stddev: 0.029999999329447746
          }
        }
        activation: RELU_6
        batch_norm {
          decay: 0.9997000098228455
          center: true
          scale: true
          epsilon: 0.0010000000474974513
          train: true
        }
      }
    }
    box_coder {
      faster_rcnn_box_coder {
        y_scale: 10.0
        x_scale: 10.0
        height_scale: 5.0
        width_scale: 5.0
      }
    }
    matcher {
      argmax_matcher {
        matched_threshold: 0.5
        unmatched_threshold: 0.5
        ignore_thresholds: false
        negatives_lower_than_unmatched: true
        force_match_for_each_row: true
      }
    }
    similarity_calculator {
      iou_similarity {
      }
    }
    box_predictor {
      convolutional_box_predictor {
        conv_hyperparams {
          regularizer {
            l2_regularizer {
              weight: 3.9999998989515007e-05
            }
          }
          initializer {
            truncated_normal_initializer {
              mean: 0.0
              stddev: 0.029999999329447746
            }
          }
          activation: RELU_6
          batch_norm {
            decay: 0.9997000098228455
            center: true
            scale: true
            epsilon: 0.0010000000474974513
            train: true
          }
        }
        min_depth: 0
        max_depth: 0
        num_layers_before_predictor: 0
        use_dropout: false
        dropout_keep_probability: 0.800000011920929
        kernel_size: 1
        box_code_size: 4
        apply_sigmoid_to_scores: false
      }
    }
    anchor_generator {
      ssd_anchor_generator {
        num_layers: 6
        min_scale: 0.20000000298023224
        max_scale: 0.949999988079071
        aspect_ratios: 1.0
        aspect_ratios: 2.0
        aspect_ratios: 0.5
        aspect_ratios: 3.0
        aspect_ratios: 0.33329999446868896
      }
    }
    post_processing {
      batch_non_max_suppression {
        score_threshold: 9.99999993922529e-09
        iou_threshold: 0.6000000238418579
        max_detections_per_class: 100
        max_total_detections: 100
      }
      <b>score_converter: SOFTMAX</b>
    }
    normalize_loss_by_num_matches: true
    loss {
      localization_loss {
        weighted_smooth_l1 {
        }
      }
      classification_loss {
        weighted_softmax {
        }
      }
      hard_example_miner {
        num_hard_examples: 3000
        iou_threshold: 0.9900000095367432
        loss_type: CLASSIFICATION
        max_negatives_per_positive: 3
        min_negatives_per_image: 3
      }
      classification_weight: 1.0
      localization_weight: 1.0
    }
  }
}
train_config {
  batch_size: 24
  data_augmentation_options {
    ssd_random_crop {
    }
  }
  optimizer {
    rms_prop_optimizer {
      learning_rate {
        exponential_decay_learning_rate {
          initial_learning_rate: 0.004000000189989805
          decay_steps: 40000
          decay_factor: 0.949999988079071
        }
      }
      momentum_optimizer_value: 0.8999999761581421
      decay: 0.8999999761581421
      epsilon: 1.0
    }
  }
  num_steps: 800000
}
train_input_reader {
  label_map_path: "data/udlr.pbtxt"
  tf_record_input_reader {
    input_path: "data/udlr.record"
  }
}
eval_config {
  num_examples: 8000
  max_evals: 10
  use_moving_averages: false
}
eval_input_reader {
  label_map_path: "data/udlr.pbtxt"
  shuffle: false
  num_readers: 1
  tf_record_input_reader {
    input_path: "data/val_all.record"
  }
}

the second model config file is as follows:

model {
  ssd {
    num_classes: 4
    image_resizer {
      fixed_shape_resizer {
        height: 300
        width: 300
      }
    }
    feature_extractor {
      type: "ssd_mobilenet_v2"
      depth_multiplier: 1.0
      min_depth: 16
      conv_hyperparams {
        regularizer {
          l2_regularizer {
            weight: 3.9999998989515007e-05
          }
        }
        initializer {
          truncated_normal_initializer {
            mean: 0.0
            stddev: 0.029999999329447746
          }
        }
        activation: RELU_6
        batch_norm {
          decay: 0.9997000098228455
          center: true
          scale: true
          epsilon: 0.0010000000474974513
          train: true
        }
      }
    }
    box_coder {
      faster_rcnn_box_coder {
        y_scale: 10.0
        x_scale: 10.0
        height_scale: 5.0
        width_scale: 5.0
      }
    }
    matcher {
      argmax_matcher {
        matched_threshold: 0.5
        unmatched_threshold: 0.5
        ignore_thresholds: false
        negatives_lower_than_unmatched: true
        force_match_for_each_row: true
      }
    }
    similarity_calculator {
      iou_similarity {
      }
    }
    box_predictor {
      convolutional_box_predictor {
        conv_hyperparams {
          regularizer {
            l2_regularizer {
              weight: 3.9999998989515007e-05
            }
          }
          initializer {
            truncated_normal_initializer {
              mean: 0.0
              stddev: 0.029999999329447746
            }
          }
          activation: RELU_6
          batch_norm {
            decay: 0.9997000098228455
            center: true
            scale: true
            epsilon: 0.0010000000474974513
            train: true
          }
        }
        min_depth: 0
        max_depth: 0
        num_layers_before_predictor: 0
        use_dropout: false
        dropout_keep_probability: 0.800000011920929
        kernel_size: 1
        box_code_size: 4
        apply_sigmoid_to_scores: false
      }
    }
    anchor_generator {
      ssd_anchor_generator {
        num_layers: 6
        min_scale: 0.20000000298023224
        max_scale: 0.949999988079071
        aspect_ratios: 1.0
        aspect_ratios: 2.0
        aspect_ratios: 0.5
        aspect_ratios: 3.0
        aspect_ratios: 0.33329999446868896
      }
    }
    post_processing {
      batch_non_max_suppression {
        score_threshold: 9.99999993922529e-09
        iou_threshold: 0.6000000238418579
        max_detections_per_class: 100
        max_total_detections: 100
      }
      score_converter: SIGMOID
    }
    normalize_loss_by_num_matches: true
    loss {
      localization_loss {
        weighted_smooth_l1 {
        }
      }
      classification_loss {
        weighted_sigmoid {
        }
      }
      hard_example_miner {
        num_hard_examples: 3000
        iou_threshold: 0.9900000095367432
        loss_type: CLASSIFICATION
        max_negatives_per_positive: 3
        min_negatives_per_image: 3
      }
      classification_weight: 1.0
      localization_weight: 1.0
    }
  }
}
train_config {
  batch_size: 24
  data_augmentation_options {
    ssd_random_crop {
    }
  }
  optimizer {
    rms_prop_optimizer {
      learning_rate {
        exponential_decay_learning_rate {
          initial_learning_rate: 0.004000000189989805
          decay_steps: 40000
          decay_factor: 0.949999988079071
        }
      }
      momentum_optimizer_value: 0.8999999761581421
      decay: 0.8999999761581421
      epsilon: 1.0
    }
  }
  num_steps: 800000
}
train_input_reader {
  label_map_path: "data/udlr.pbtxt"
  tf_record_input_reader {
    input_path: "data/udlr.record"
  }
}
eval_config {
  num_examples: 8000
  max_evals: 10
  use_moving_averages: false
}
eval_input_reader {
  label_map_path: "data/udlr.pbtxt"
  shuffle: false
  num_readers: 1
  tf_record_input_reader {
    input_path: "data/val_all.record"
  }
}

@xjtuljy

I download the frozen_inference_graph_batch_size_1.pb file and try to convert it into uff file using command ‘g=uff.from_tensorflow_frozen_model(’./frozen_inference_graph_batch_size_1.pb’)'. looks like it’s still not right (see below). I’m confused how can you successfully convert the detection .pb into uff? And what’s the ‘config.py’ used for?

Converting Preprocessor/map/while/TensorArrayWrite_1/TensorArrayWriteV3/Enter as custom op: Enter
Traceback (most recent call last):
File “”, line 1, in
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/conversion_helpers.py”, line 233, in from_tensorflow_frozen_model
return from_tensorflow(graphdef, output_nodes, preprocessor, **kwargs)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/conversion_helpers.py”, line 181, in from_tensorflow
debug_mode=debug_mode)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 94, in convert_tf2uff_graph
uff_graph, input_replacements, debug_mode=debug_mode)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 79, in convert_tf2uff_node
op, name, tf_node, inputs, uff_graph, tf_nodes=tf_nodes, debug_mode=debug_mode)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 41, in convert_layer
fields = cls.parse_tf_attrs(tf_node.attr)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 222, in parse_tf_attrs
return {key: cls.parse_tf_attr_value(val) for key, val in attrs.items() if val is not None and val.WhichOneof(‘value’) is not None}
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 222, in
return {key: cls.parse_tf_attr_value(val) for key, val in attrs.items() if val is not None and val.WhichOneof(‘value’) is not None}
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 218, in parse_tf_attr_value
return cls.convert_tf2uff_field(code, val)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 190, in convert_tf2uff_field
return TensorFlowToUFFConverter.convert_tf2numpy_dtype(val)
File “/usr/lib/python2.7/dist-packages/uff/converters/tensorflow/converter.py”, line 103, in convert_tf2numpy_dtype
return tf.as_dtype(dtype).as_numpy_dtype
File “/usr/local/lib/python2.7/dist-packages/tensorflow/python/framework/dtypes.py”, line 128, in as_numpy_dtype
return _TF_TO_NP[self._type_enum]
KeyError: 20