在实际业务中,我们会遇到有多种输入shape的模型,比如CV领域的目标检测模型MTCNN,SSD和YOLO,在MIGraphX中实现动态shape主要包含下面几个步骤:
export MIGRAPHX_DYNAMIC_SHAPE=1
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)
目前MIGraphX的动态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 |
如果在实际使用的时候发现MIGraphX不能支持某个模型的动态shape,可以有如下解决方案: