## 1. 环境搭建
安装MIGraphX之前,需要先安装dtk,有两种方式安装dtk:
使用dtk镜像(推荐),镜像中已经安装好dtk环境,dtk镜像下载地址:[光合开发者社区](https://sourcefind.cn/#/image/dcu/dtk?activeName=overview "光合开发者社区"),根据需要选择合适的镜像。
下载dtk安装包,dtk安装包下载地址:[https://cancon.hpccube.com:65024/1/main](https://cancon.hpccube.com:65024/1/main "https://cancon.hpccube.com:65024/1/main"),根据需要选择合适的版本,然后将下载好的安装包安装到/opt目录下,最后创建一个软连接/opt/dtk,使得该软连接指向dtk的安装目录,注意:一定要创建软连接/opt/dtk,否则MIGraphX无法正常使用。
例如,如果是ubuntu系统,下载DTK-23.10.1-Ubuntu20.04.1-x86\_64.tar.gz,并解压:
```shell
tar -zxvf DTK-23.10.1-Ubuntu20.04.1-x86_64.tar.gz -C /opt
```

创建软连接:
```shell
ln -s /opt/dtk-23.10.1/ /opt/dtk
```

MIGraphX的安装包下载地址:[https://cancon.hpccube.com:65024/4/main/migraphx](https://cancon.hpccube.com:65024/4/main/migraphx "https://cancon.hpccube.com:65024/4/main/migraphx") ,选择合适的安装包下载,比如下载了DAS1.1中的MIGraphX4.3.0版本,可以通过如下命令安装:
```shell
bash migraphx-4.3.0+das1.1.git0a2480f.abi0.dtk2404-cp38-cp38-linux_x86_64.run
```

安装时会出现两次询问,第一次询问为
```plaintext
请输入安装路径(直接回车将安装到默认路径: /opt/dtk)
```

一般情况下,默认安装到/opt/dtk目录下即可。第二次询问为:
```plaintext
是否安装migraphx-diffusers?(安装需要环境pip能访问网络。默认不安装,直接回车将不进行安装) [y/n]:
```

migraphx-diffusers是一款支持文生图任务的Python插件,如果需要使用MIGraphX执行文生图任务,则建议安装该插件。如果安装过程报错,可能原因是设备无网络,无法安装migraphx-diffusers库所需的依赖。
安装完成后,需要设置环境变量:
```shell
source /opt/dtk/env.sh
```

如果需要在python中使用migraphx,还需要设置PYTHONPATH :
```shell
export PYTHONPATH=/opt/dtk/lib:$PYTHONPATH
```

## 2. 模型转换
使用MIGraphX进行推理前需要将训练好的ResNet50模型转换为ONNX格式,本示例使用如下的ResNet50模型:[https://download.pytorch.org/models/resnet50-19c8e357.pth](https://download.pytorch.org/models/resnet50-19c8e357.pth "https://download.pytorch.org/models/resnet50-19c8e357.pth"),下载该模型后使用如下代码可以转换为ONNX格式:
```python
# Pytorch模型文件
pathOfPytorchModel = "resnet50-19c8e357.pth"
# 创建ResNet50模型
net = torchvision.models.resnet50(pretrained=False)
# 定义输入
input = torch.randn(32,3,224,224)
# 生成的ONNX模型的路径
pathOfONNX = "ResNet50.onnx"
net.load_state_dict(torch.load(pathOfPytorchModel))
net.eval()
# 导出ONNX模型
torch.onnx.export(net,input,pathOfONNX,input_names = ["input"])
```

生成好ResNet50.onnx模型后就可以进行推理了。
## 3. c++ API 推理
使用C++ API加载ONNX模型进行推理主要包含三个步骤:
1. 加载ONNX模型
2. 编译模型
3. 执行推理
主要步骤示例代码如下:
```cpp
// 头文件
#include
#include
// 加载模型
migraphx::program net = migraphx::parse_onnx("path/to/your/onnx/model");
// 编译模型
migraphx::compile_options options;
net.compile(migraphx::gpu::target{},options);
// 加载数据
std::unordered_map inputData;
...
// 执行推理
std::vector results = net.eval(inputData);
```

下面以ResNet50.onnx模型为例说明如何使用C++ API进行分类模型的推理:
```cpp
#include
#include
#include
int main(int argc, char *argv[])
{
// 加载模型
migraphx::program net = migraphx::parse_onnx("ResNet50.onnx");
// 获取模型输入/输出节点信息
std::cout << "inputs:" << std::endl;
std::unordered_map inputs = net.get_inputs();
for (auto i : inputs)
{
std::cout << i.first << ":" << i.second << std::endl;
}
std::cout << "outputs:" << std::endl;
std::unordered_map outputs = net.get_outputs();
for (auto i : outputs)
{
std::cout << i.first << ":" << i.second << std::endl;
}
std::string inputName = inputs.begin()->first;
migraphx::shape inputShape = inputs.begin()->second;
int N = inputShape.lens()[0];
int C = inputShape.lens()[1];
int H = inputShape.lens()[2];
int W = inputShape.lens()[3];
// 编译模型
migraphx::compile_options options;
options.device_id = 0; // 设置GPU设备,默认为0号设备
options.offload_copy = true;
net.compile(migraphx::gpu::target{}, options);
// 数据预处理并转换为NCHW格式
int batchSize = N;
cv::Mat srcImage = cv::imread("Test.jpg");
std::vector srcImages;
for (int i = 0; i < batchSize; ++i)
{
srcImages.push_back(srcImage);
}
cv::Mat inputBlob;
cv::dnn::blobFromImages(srcImages, inputBlob, 0.0078125, cv::Size(W, H), cv::Scalar(127.5, 127.5, 127.5), false, false);
// 创建输入数据
std::unordered_map inputData;
inputData[inputName] = migraphx::argument{inputShape, 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(); // 输出节点数据指针
// 打印推理结果
for (int i = 0; i < numberOfOutput; ++i)
{
std::cout << resultData[i] << ",";
}
std::cout << std::endl;
return 0;
}
```

1. 首先通过parse\_onnx()函数加载onnx模型,加载好模型之后,可以通过program的get\_inputs()和get\_outputs()函数获取模型的输入和输出节点信息,两个函数返回值类型都是std::unordered\_map类型,每个输入节点或者输出节点都有一个节点名和shape,由于ResNet50模型只有一个输入,所以这里可以通过inputs.begin()获取模型的输入。
2. 如果需要采用FP16模式进行推理,可以通过quantize\_fp16()函数实现,具体实现见4.3小节,MIGraphX同时也支持int8推理,具体实现见4.4小节。
3. 加载onnx模型之后,需要使用compile()方法编译模型,这里将模型编译为GPU模式,如果需要编译为CPU模式,需要使用migraphx::cpu::target{},注意:如果你的输入数据在host端,则在设置编译选项的时候,需要设置offload\_copy为true。
4. 编译好模型之后,需要输入数据,输入数据需要经过预处理并转换为NCHW的格式,这里使用了OpenCV的blobFromImage函数将图像转换为了NCHW格式。并通过构造一个std::unordered\_map类型的对象保存输入数据,模型的每个输入都会对应一个输入数据,输入数据使用argument保存。
5. 最后通过program的eval()方法执行推理计算,推理结果是一个std::vector< migraphx::argument >类型,由于offload\_copy参数设置为true,所以推理的结果是host端数据,然后我们就可以通过argument提供的方法访问推理结果了。