MIGraphX推理框架第八章-动态Shape

第七章介绍了MIGraphX的性能优化,可以在此跳转进行 回顾

第八章-动态Shape

    • 动态shape
      • 动态shape的限制
      • 支持动态Shape的模型
      • 不支持动态shape的解决方案

动态shape

在实际业务中,我们会遇到有多种输入shape的模型,比如CV领域的目标检测模型MTCNN,SSD和YOLO,在MIGraphX中实现动态shape主要包含下面几个步骤:

  • 设置环境变量:export MIGRAPHX_DYNAMIC_SHAPE=1
  • 设置模型的最大输入shape,在推理过程中,输入shape不能超过该最大值,否则报错
  • 调用program类的reshape方法实现动态shape

c++代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace cv;
using namespace cv::dnn;
using namespace migraphx;
int main(int argc, char *argv[]) {
  // 设置最大输入shape
  migraphx::onnx_options onnx_options;
  onnx_options.map_input_dims["input"] = {2, 3, 512,
                                          512};  // input表示输入节点名
  // 加载模型
  migraphx::program net = migraphx::parse_onnx("Test.onnx", onnx_options);
  std::pair inputAttribute =
      *(net.get_parameter_shapes().begin());
  std::string inputName = inputAttribute.first;
  migraphx::shape inputShape = inputAttribute.second;
  int N = inputShape.lens()[0];
  int C = inputShape.lens()[1];
  int H = inputShape.lens()[2];
  int W = inputShape.lens()[3];
  printf("input name:%s\n", inputName.c_str());
  printf("input shape:%d,%d,%d,%d\n", N, C, H, W);
  // 编译
  migraphx::compile_options options;
  options.device_id = 0;        // 设置GPU设备,默认为0号设备
  options.offload_copy = true;  // 设置offload_copy
  net.compile(migraphx::gpu::target{}, options);
  // 设置动态输入,这里添加了2个不同的输入shape
  std::vector> inputShapes;
  inputShapes.push_back({2, 3, 16, 16});
  inputShapes.push_back({2, 3, 32, 32});
  cv::Mat srcImage = cv::imread("Test.jpg", 1);
  for (int i = 0; i < inputShapes.size(); ++i) {
    // 设置输入shape并执行reshape
    std::unordered_map> inputShapeMap;
    inputShapeMap[inputName] = inputShapes[i];
    net.reshape(inputShapeMap);
    std::vector srcImages;
    for (int j = 0; j < inputShapes[i][0]; ++j) {
      srcImages.push_back(srcImage);
    }
    // 预处理并转换为NCHW
    cv::Mat inputBlob;
    cv::dnn::blobFromImages(srcImages, inputBlob, 0.0078125,
                            cv::Size(inputShapes[i][3], inputShapes[i][2]),
                            cv::Scalar(127.5, 127.5, 127.5), false, false);
    // 输入数据
    migraphx::parameter_map inputData;
    inputData[inputName] =
        migraphx::argument{migraphx::shape(inputShape.type(), inputShapes[i]),
                           (float *)inputBlob.data};
    // 推理
    std::vector results = net.eval(inputData);
    // 获取输出节点的属性
    migraphx::argument result = results[0];  // 获取第一个输出节点的数据
    migraphx::shape outputShape = result.get_shape();  // 输出节点的shape
    std::vector outputSize =
        outputShape.lens();  // 每一维大小,维度顺
    序为(N, C, H, W)
    int numberOfOutput = outputShape.elements();  // 输出节点元素的个数
    float *resultData = (float *)result.data();   // 输出节点数据指针
    // 打印输出
    printf("output size:%d\n", numberOfOutput);
    for (int i = 0; i < numberOfOutput; ++i) {
      printf("%f,", resultData[i]);
    }
    printf("\n");
  }
  return 0;
}

python代码:

import cv2
import numpy as np
import migraphx
def ReadImage(pathOfImage, inputShape):
    srcImage = cv2.imread(pathOfImage, cv2.IMREAD_COLOR)# numpy类型, HWC# resize并转换为CHW
resizedImage = cv2.resize(srcImage, (inputShape[3], inputShape[2]))
resizedImage_Float = resizedImage.astype("float32")# 转换为float32
srcImage_CHW = np.transpose(resizedImage_Float, (2, 0, 1))# 转换为CHW# 预处理
mean = np.array([127.5, 127.5, 127.5])
scale = np.array([0.0078125, 0.0078125, 0.0078125])
inputData = np.zeros(inputShape).astype("float32")# NCHW
for i in range(srcImage_CHW.shape[0]):
    inputData[0, i, : , : ] = (srcImage_CHW[i, : , : ] - mean[i]) * scale[i]# 复制到batch中的其他图像
for i in range(inputData.shape[0]):
    if i != 0:
    inputData[i, : , : , : ] = inputData[0, : , : , : ]
return inputData
if __name__ == '__main__': #设置最大输入shape
maxInput = {
    "input": [2, 3, 512, 512]
}#
加载模型
model = migraphx.parse_onnx("Test.onnx", map_input_dims = maxInput)
inputName = model.get_parameter_names()[0]
inputShape = model.get_parameter_shapes()[inputName].lens()
print("inputName:{0} \ninputShape:{1}".format(inputName, inputShape))# 编译
model.compile(t = migraphx.get_target("gpu"), device_id = 0)# 设置动态输入, 这里添加了2个不同的输入shape
inputShapes = [
    [2, 3, 16, 16],
    [2, 3, 32, 32]
]
for inputShape in inputShapes:
    inputShapeMap = {
        inputName: inputShape
    }
model.reshape(inputs = inputShapeMap)# 执行reshape# 预处理并转换为NCHW
pathOfImage = "Test.jpg"
image = ReadImage(pathOfImage, inputShape)# 推理
results = model.run({
    inputName: migraphx.argument(image)
})# 获取输出节点属性
result = results[0]# 获取第一个输出节点的数据, migraphx.argument类型
outputShape = result.get_shape()# 输出节点的shape, migraphx.shape类型
outputSize = outputShape.lens()# 表示每一维大小, 维度顺序为(N, C, H, W), list类型
numberOfOutput = outputShape.elements()# 输出节点元素的个数# 转换为numpy
result = np.array(results[0])
print(result)

动态shape的限制

目前MIGraphX的动态shape具有如下限制:

  1. 对于包含有全连接层的模型(在Pytorch中对应的是nn.Linear层),比如分类模型ResNet50,如果需要在H和W维度实现动态,需要保证输入到全连接层的数据在C,H,W维度上大小保持一致,可以在全连接层前面加入全局池化层
  2. 对于包含有LSTM的模型,batchsize大小只能设置为1

支持动态Shape的模型

支持N,H,W维度变化的模型 仅支持N维度变化的模型 仅支持H,W维度变化的模型
AlexNet ShuffleNet CRNN-LSTM
VGG16 SqueezeNet
VGG19 EfficientNet-B3
GoogLeNet EfficientNet-B5
InceptionV3 EfficientNet-B7
ResNet50 YOLOV2
DenseNet YOLOV3
MobileNetV1 YOLOV5
MobileNetV2 UNet
MobileNetV3 PaddleOCR
MTCNN
SSD-VGG16
RetinaNet
RetinaFace
DBNET
FCN

不支持动态shape的解决方案

如果在实际使用的时候发现MIGraphX不能支持某个模型的动态shape,可以有如下解决方案:

  1. 将输入图像resize到一个固定大小,通常这种做法会影响精度,如果对精度要求不高可以考虑该方案
  2. 将不同大小的图像填充到一个固定大小,可以使用0来填充,比如将128x128的图像用0填充到512x512

MIGraphX推理框架第八章-动态Shape_第1张图片

你可能感兴趣的:(#,MIGraphX推理框架,人工智能,深度学习,c++,python,linux,MIGraphX,推理框架)