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


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

    动态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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    动态shape的限制

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

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

    支持动态Shape的模型

    支持N,H,W维度变化的模型仅支持N维度变化的模型仅支持H,W维度变化的模型
    AlexNetShuffleNetCRNN-LSTM
    VGG16SqueezeNet
    VGG19EfficientNet-B3
    GoogLeNetEfficientNet-B5
    InceptionV3EfficientNet-B7
    ResNet50YOLOV2
    DenseNetYOLOV3
    MobileNetV1YOLOV5
    MobileNetV2UNet
    MobileNetV3PaddleOCR
    MTCNN
    SSD-VGG16
    RetinaNet
    RetinaFace
    DBNET
    FCN

    不支持动态shape的解决方案

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

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

    在这里插入图片描述

  • 相关阅读:
    xilinx xdc 约束及时序收敛分析
    yolov7模型训练环境安装
    企业云性能监控
    PyTorch学习笔记-常用函数与数据加载
    DBW*的trace文件过大的bug
    Genesis公链:夯实Web 3.0发展底座
    C++11标准模板(STL)- 算法(std::iter_swap)
    高通导航器软件开发包使用指南(14)
    健身用什么耳机比较好、五款适合健身房运动的耳机推荐
    论文-分布式-并发控制-并发控制问题的解决方案
  • 原文地址:https://blog.csdn.net/qq_28356373/article/details/134383443