pytorch 为ONNX运行时推理预分配动态形状Tensor内存?

cwdobuhd  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(388)

我目前正在试用onnxruntime-gpu,我希望使用NVIDIA DALI在GPU上执行图像预处理。一切正常,我能够预处理我的图像,但问题是,我希望保留设备上的所有数据,以免从设备和主机来回复制数据造成瓶颈。
onnxruntime库允许IO绑定来将输入和输出绑定到设备。问题是这是令人难以置信的静态,这使得在为不同形状的输出Tensor预分配内存时出现问题。例如,我使用的RetinaNet生成不同大小的预测,我似乎无法处理。
对于预处理,我使用以下代码:

class ImagePipeline(Pipeline):
    def __init__(self, file_list, batch_size, num_threads, device_id):
        super(ImagePipeline, self).__init__(batch_size, num_threads, device_id)
        self.input = ops.readers.File(file_root="", file_list=file_list)
        self.decode = ops.decoders.Image(device="mixed", output_type=types.RGB)
        self.resize = ops.Resize(device="gpu", resize_x=800, resize_y=800)
        
        self.normalize = ops.CropMirrorNormalize(
            device="gpu",
            dtype=types.FLOAT,
            output_layout=types.NCHW,
            crop=(800, 800),
            mean=[0.485 * 255, 0.456 * 255, 0.406 * 255],
            std=[0.229 * 255, 0.224 * 255, 0.225 * 255],
        )

    def define_graph(self):
            inputs, labels = self.input()
            images = self.decode(inputs)
            images = self.resize(images)
            images = self.normalize(images)
            return images, labels

这可以正确地创建shape(BATCH_SIZE,800,800)图像的批处理。为了对这些批处理运行推理,我使用了以下代码片段:

def run_with_torch_tensors_on_device(x: torch.Tensor, CURR_SIZE: int, torch_type: torch.dtype = torch.float) -> torch.Tensor:
    binding = session.io_binding()
    x_tensor = x.contiguous()
    z_tensor = torch.zeros(CURR_SIZE, 4, dtype=torch_type, device=DEVICE).contiguous()

    binding.bind_input(
        name=session.get_inputs()[0].name,
        device_type=DEVICE_NAME,
        device_id=DEVICE_INDEX,
        element_type=np.float32,
        shape=tuple(x_tensor.shape),
        buffer_ptr=x_tensor.data_ptr())
    
    binding.bind_output(
        name=session.get_outputs()[0].name,
        device_type=DEVICE_NAME,
        device_id=DEVICE_INDEX,
        element_type=np.int64,
        shape=tuple(x_tensor.shape),
        buffer_ptr=z_tensor.data_ptr())

    session.run_with_iobinding(binding)

    return z_tensor.squeeze(0)

这就是问题发生的地方。我不能创建正确形状的z_tensors。我使用www.example.com上预先训练的视网膜网络https://pytorch.org/vision/main/models/generated/torchvision.models.detection.retinanet_resnet50_fpn_v2.html#torchvision.models.detection.retinanet_resnet50_fpn_v2。
我找到了一个解决方法,如下所示:

def run_with_data_on_device(x):
    x_ortvalue = ort.OrtValue.ortvalue_from_numpy(x)
    io_binding = session.io_binding()
    io_binding.bind_input(name=session.get_inputs()[0].name, device_type=x_ortvalue.device_name(), device_id=0, element_type=x.dtype, shape=x_ortvalue.shape(), buffer_ptr=x_ortvalue.data_ptr())
    io_binding.bind_output(name=session.get_outputs()[-1].name, device_type=DEVICE_NAME, device_id=DEVICE_INDEX, element_type=x.dtype, shape=x_ortvalue.shape())
    session.run_with_iobinding(io_binding)

    z = io_binding.get_outputs()

    return z[0]

但这自然会导致往返主机的问题,这是不必要的......我是否忽略了一些明显的问题?为什么我不能将z_tensor初始化为(None,None)并具有动态形状的输出Tensor?

ql3eal8s

ql3eal8s1#

IO绑定的API:https://onnxruntime.ai/docs/api/python/api_summary.html#iobinding
实际上你可以只绑定输出变量name,因为其他参数都是可选的。如果是这样,内存将由onnxruntime分配。这对动态输出形状的情况很有帮助。
get_outputs()返回设备中的OrtValues,copy_outputs_to_cpu()可以将数据复制到CPU。
页面中也有很多示例,请参见“设备上的数据”部分中的第一个示例:https://onnxruntime.ai/docs/api/python/api_summary.html#data-on-device

相关问题