19

我正在尝试使用 TensorRT 部署经过训练的 U-Net。该模型是使用 Keras 训练的(使用 Tensorflow 作为后端)。代码和这个很相似:https ://github.com/zhixuhao/unet/blob/master/model.py

当我将模型转换为 UFF 格式时,使用如下代码:

import uff
import os
uff_fname = os.path.join("./models/", "model_" + idx + ".uff")
uff_model = uff.from_tensorflow_frozen_model(
    frozen_file = os.path.join('./models', trt_fname), output_nodes = output_names, 
    output_filename = uff_fname
)

我会收到以下警告:

Warning: No conversion function registered for layer: ResizeNearestNeighbor yet.
Converting up_sampling2d_32_12/ResizeNearestNeighbor as custom op: ResizeNearestNeighbor
Warning: No conversion function registered for layer: DataFormatVecPermute yet.
Converting up_sampling2d_32_12/Shape-0-0-VecPermuteNCHWToNHWC-LayoutOptimizer as custom op: DataFormatVecPermute

我试图通过用上采样(双线性插值)和转置卷积替换上采样层来避免这种情况。但是转换器会给我带来类似的错误。我检查了https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html似乎还不支持所有这些操作。

我想知道这个问题是否有任何解决方法?TensorRT 是否有任何其他格式/框架喜欢并支持上采样?或者是否可以用其他一些支持的操作来替换它?

我还在某处看到可以添加自定义操作来替换 TensorRT 不支持的操作。虽然我不太确定工作流程会如何。如果有人可以指出自定义图层的示例,那也将非常有帮助。

先感谢您!

4

3 回答 3

2

正如您已经提到的,这些警告是因为TensorRT尚不支持这些操作。不幸的是,没有简单的方法可以解决这个问题。您要么必须修改图表(即使在训练之后)以仅使用组合支持的操作;或者自己编写这些操作作为自定义层

但是,有一种更好的方法可以在 C++ 中的其他设备上运行推理。您可以将TensorFlow 与 TensorRT 混合使用。TensorRT 将分析它支持的操作图并将它们转换为 TensorRT 节点,图的其余部分将照常由 TensorFlow 处理。更多信息在这里。这个解决方案比自己重写操作要快得多。唯一复杂的部分是从目标设备上的源代码构建 TensorFlow 并生成动态库 tensorflow_cc。最近有许多指南和支持将 TensorFlow 移植到各种架构,例如ARM

于 2019-07-23T03:56:54.747 回答
2

2019 年 9 月 28 日更新

Nvidia大约两周前发布了TensorRT 6.0.1 ,并添加了一个名为“IResizeLayer”的新 API。该层支持“最近”插值,因此可用于实现上采样。不再需要使用自定义层/插件!

原答案:

感谢您在此处发布的所有答案和建议!

最后,我们直接在 TensorRT C++ API 中实现了网络,并从 .h5 模型文件中加载了权重。我们还没有时间来分析和完善解决方案,但根据我们输入的测试图像,推断似乎正在发挥作用。

这是我们采用的工作流程:

第 1 步:对上采样层进行编码。

在我们的 U-Net 模型中,所有上采样层的比例因子为 (2, 2),并且它们都使用 ResizeNearestNeighbor 插值。本质上,原始张量中 (x,y) 处的像素值将变为四个像素:(2x, 2y)、(2x+1, 2y)、(2x, 2y+1) 和 (2x+1, 2y+1) ) 在新张量中。这可以很容易地编码为 CUDA 内核函数。

一旦我们得到了上采样内核,我们需要用 TensorRT API 包装它,特别是IPluginV2Ext 类。开发者参考对需要实现哪些功能进行了一些描述。我想说 enqueue() 是最重要的函数,因为 CUDA 内核在那里执行。

TensorRT Samples 文件夹中也有示例。对于我的版本,这些资源很有帮助:

第 2 步:使用 TensorRT API 对网络的其余部分进行编码

网络的其余部分应该非常简单。只需从TensorRT 网络定义中找到调用不同的“addxxxLayer”函数即可。

要记住的一件事:根据您使用的 TRT 版本,添加填充的方式可能会有所不同。我认为最新版本(5.1.5)允许开发人员添加参数,addConvolution()以便可以选择适当的填充模式。

我的模型是使用 Keras 训练的,默认的填充模式是如果填充的总数不均匀,则右侧和底部获得更多的填充。查看此Stack Overflow 链接了解详细信息。5.1.5 中有一种模式代表了这种填充方案。

如果您使用的是旧版本(5.1.2.2),则需要在卷积层之前将填充作为单独的层添加,该层有两个参数:pre-padding 和 post-padding。

此外,TensorRT 中的所有东西都是 NCHW

有用的样本:

  • TensorRT-5.1.2.2/samples/sampleMNISTAP

第 3 步:加载权重

TensorRT 需要 [out_c, in_c, filter_h, filter_w] 格式的权重,存档文档中提到了这一点。Keras 具有 [filter_h, filter_w, c_in, c_out] 格式的权重。

我们通过调用model.save_weights('weight.h5')Python 得到了一个纯权重文件。然后我们可以使用 h5py 将权重读入 Numpy 数组,执行转置并将转置后的权重保存为新文件。我们还使用 h5py 确定了组和数据集名称。使用HDF5 C++ API将权重加载到 C++ 代码中时使用此信息。

我们逐层比较了 C++ 代码和 Python 代码之间的输出。对于我们的 U-Net,直到第三个块(在 2 个池化之后)之前,所有激活图都是相同的。之后,像素值之间存在微小差异。绝对百分比误差是 10^-8,所以我们认为它没有那么糟糕。我们仍在完善 C++ 实现。

再次感谢我们在这篇文章中得到的所有建议和答案。希望我们的解决方案也能有所帮助!

于 2019-08-23T16:21:39.140 回答
1

嘿,我做过类似的事情,我想说解决这个问题的最好方法是将你的模型导出到.onnx一个像这样的好模型,如果你检查onnx 的支持矩阵,支持上采样: 在此处输入图像描述

然后你可以使用https://github.com/onnx/onnx-tensorrt将 onnx-model 转换为 tensorrt,我有这个来转换我在 pytorch 中训练并具有上采样的网络。repoonnx-tensorrt更活跃一些,如果您检查 pr 选项卡,您可以检查其他人编写自定义层并从那里分叉。

于 2019-07-26T13:17:51.353 回答