TF 对象检测:推理负载的 return 个子集

TF object detection: return subset of inference payload

问题

我正在使用 TF 的对象检测训练和部署实例分割模型 API。我能够成功地训练模型,将其打包成 TF Serving Docker 图像(截至 2020 年 10 月的 latest 标签),并通过 REST 接口处理推理请求。但是,从推理请求返回的数据量非常大(数百 Mb)。当推理请求和处理不在同一台机器上发生时,这是一个大问题,因为所有返回的数据都必须通过网络。

有没有办法 trim 减少输出数量(在模型导出期间或在 TF 服务图像中),以便在推理期间允许更快的往返时间?

详情

我正在使用 TF OD API(使用 TF2)训练 Mask RCNN 模型,它是 this config. I believe the full list of outputs is described in code here 的修改版本。我在推理过程中得到的项目列表也粘贴在下面。对于具有 100 个对象建议的模型,如果我将返回的推理作为 json 写入磁盘,则该信息约为 270 Mb。

inference_payload['outputs'].keys()

dict_keys(['detection_masks', 'rpn_features_to_crop', 'detection_anchor_indices', 'refined_box_encodings', 'final_anchors', 'mask_predictions', 'detection_classes', 'num_detections', 'rpn_box_predictor_features', 'class_predictions_with_background', 'proposal_boxes', 'raw_detection_boxes', 'rpn_box_encodings', 'box_classifier_features', 'raw_detection_scores', 'proposal_boxes_normalized', 'detection_multiclass_scores', 'anchors', 'num_proposals', 'detection_boxes', 'image_shape', 'rpn_objectness_predictions_with_background', 'detection_scores'])

我已经将推理请求中的图像编码为 base64,因此在通过网络时请求负载不会太大。只是相比之下,推理响应是巨大的。我只需要此响应中的 4 或 5 个项目,因此最好排除其余项目并避免通过网络传递如此大的位包。

我尝试过的事情

  1. 我尝试在导出过程中将 score_threshold 设置为更高的值 (code example here) 以减少输出数量。然而,这似乎只是门槛detection_scores。仍然返回所有无关的推理信息。
  2. 我还尝试通过添加要删除的键的名称来手动排除其中一些推理输出 here。这似乎也没有任何效果,我担心这是一个坏主意,因为在 scoring/evaluation.
  3. 期间可能需要其中一些密钥
  4. 我也在此处和 tensorflow/models 存储库上进行了搜索,但我什么也没找到。

我 运行 遇到了同样的问题。在 exporter_main_v2 代码中声明输出应该是:

and the following output nodes returned by the model.postprocess(..):
  * `num_detections`: Outputs float32 tensors of the form [batch]
      that specifies the number of valid boxes per image in the batch.
  * `detection_boxes`: Outputs float32 tensors of the form
      [batch, num_boxes, 4] containing detected boxes.
  * `detection_scores`: Outputs float32 tensors of the form
      [batch, num_boxes] containing class scores for the detections.
  * `detection_classes`: Outputs float32 tensors of the form
      [batch, num_boxes] containing classes for the detections.

我已经提交了一个关于 tensorflow 对象检测的问题 github 回购,我希望我们能从 tensorflow 开发团队得到反馈。

可以找到github问题here

我找到了一个棘手的解决方法。在导出过程中(here),删除了预测字典的一些组件。我在 non_tensor_predictions 列表中添加了其他项目,其中包含将在后处理步骤中删除的所有密钥。扩充此列表将我的推理输出从 ~200MB 减少到 ~12MB。

if self._number_of_stages == 3 块的完整代码:

    if self._number_of_stages == 3:

      non_tensor_predictions = [
          k for k, v in prediction_dict.items() if not isinstance(v, tf.Tensor)]

      # Add additional keys to delete during postprocessing
      non_tensor_predictions = non_tensor_predictions + ['raw_detection_scores', 'detection_multiclass_scores', 'anchors', 'rpn_objectness_predictions_with_background', 'detection_anchor_indices', 'refined_box_encodings', 'class_predictions_with_background', 'raw_detection_boxes', 'final_anchors', 'rpn_box_encodings', 'box_classifier_features']
      
      for k in non_tensor_predictions:
        tf.logging.info('Removing {0} from prediction_dict'.format(k))
        prediction_dict.pop(k)

      return prediction_dict

我认为在创建 TF 服务映像期间使用签名定义来处理此问题有一种更“正确”的方法,但这适用于快速但肮脏的修复。

如果您正在使用 exporter_main_v2.py 文件导出您的模型,您可以尝试这种 hack 方式来解决这个问题。

只需在 exporter_lib_v2.py 文件的 _run_inference_on_images 函数中添加以下代码:

    detections[classes_field] = (
        tf.cast(detections[classes_field], tf.float32) + label_id_offset)

############# START ##########
    ignored_model_output_names = ["raw_detection_boxes", "raw_detection_scores"]
    for key in ignored_model_output_names:
        if key in detections.keys(): del detections[key]
############# END ##########

    for key, val in detections.items():
      detections[key] = tf.cast(val, tf.float32)

因此生成的模型不会输出ignored_model_output_names的值。

如果这可以解决您的问题,请告诉我。

另一种方法是更改​​已保存模型的签名:

model = tf.saved_model.load(path.join("models", "efficientdet_d7_coco17_tpu-32", "saved_model"))

infer = model.signatures["serving_default"]
outputs = infer.structured_outputs
for o in ["raw_detection_boxes", "raw_detection_scores"]:
    outputs.pop(o)

tf.saved_model.save(
    model,
    export_dir="export",
    signatures={"serving_default" : infer},
    options=None
)