Classifier.cpp 10.8 KB
Newer Older
liucong's avatar
liucong committed
1
2
#include <Classifier.h>

liucong's avatar
liucong committed
3
4
5
#include <migraphx/onnx.hpp>
#include <migraphx/gpu/target.hpp>
#include <migraphx/quantization.hpp>
6
#include <hip/hip_runtime_api.h>
liucong's avatar
liucong committed
7
8
#include <Filesystem.h>
#include <SimpleLog.h>
liucong's avatar
liucong committed
9
10
#include <algorithm>
#include <CommonUtility.h>
liucong's avatar
liucong committed
11
12
13
14

namespace migraphxSamples
{

liucong's avatar
liucong committed
15
Classifier::Classifier() {}
liucong's avatar
liucong committed
16

liucong's avatar
liucong committed
17
Classifier::~Classifier() { configurationFile.release(); }
liucong's avatar
liucong committed
18

liucong's avatar
liucong committed
19
cv::Mat Classifier::Preprocess(const std::vector<cv::Mat>& srcImages)
liucong's avatar
liucong committed
20
21
22
{
    // 数据预处理
    std::vector<cv::Mat> image;
liucong's avatar
liucong committed
23
    for(int i = 0; i < srcImages.size(); ++i)
liucong's avatar
liucong committed
24
    {
liucong's avatar
liucong committed
25
        // BGR转换为RGB
liucong's avatar
liucong committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        cv::Mat imgRGB;
        cv::cvtColor(srcImages[i], imgRGB, cv::COLOR_BGR2RGB);

        // 调整大小,使短边为256,保持长宽比
        cv::Mat shrink;
        float ratio = (float)256 / min(imgRGB.cols, imgRGB.rows);
        if(imgRGB.rows > imgRGB.cols)
        {
            cv::resize(imgRGB, shrink, cv::Size(256, int(ratio * imgRGB.rows)), 0, 0);
        }
        else
        {
            cv::resize(imgRGB, shrink, cv::Size(int(ratio * imgRGB.cols), 256), 0, 0);
        }

        // 裁剪中心窗口为224*224
liucong's avatar
liucong committed
42
43
        int start_x = shrink.cols / 2 - 224 / 2;
        int start_y = shrink.rows / 2 - 224 / 2;
liucong's avatar
liucong committed
44
45
46
47
48
49
50
51
        cv::Rect rect(start_x, start_y, 224, 224);
        cv::Mat images = shrink(rect);
        image.push_back(images);
    }

    // normalize并转换为NCHW
    cv::Mat inputBlob;
    Image2BlobParams image2BlobParams;
liucong's avatar
liucong committed
52
53
54
55
    image2BlobParams.scalefactor = cv::Scalar(1 / 58.395, 1 / 57.12, 1 / 57.375);
    image2BlobParams.mean        = cv::Scalar(123.675, 116.28, 103.53);
    image2BlobParams.swapRB      = false;
    blobFromImagesWithParams(image, inputBlob, image2BlobParams);
liucong's avatar
liucong committed
56
57
58
    return inputBlob;
}

liucong's avatar
liucong committed
59
60
ErrorCode
Classifier::Initialize(InitializationParameterOfClassifier initializationParameterOfClassifier)
liucong's avatar
liucong committed
61
62
{
    // 读取配置文件
liucong's avatar
liucong committed
63
    std::string configFilePath = initializationParameterOfClassifier.configFilePath;
liucong's avatar
liucong committed
64
    if(!Exists(configFilePath))
liucong's avatar
liucong committed
65
66
67
68
    {
        LOG_ERROR(stdout, "no configuration file!\n");
        return CONFIG_FILE_NOT_EXIST;
    }
liucong's avatar
liucong committed
69
    if(!configurationFile.open(configFilePath, cv::FileStorage::READ))
liucong's avatar
liucong committed
70
    {
liucong's avatar
liucong committed
71
72
        LOG_ERROR(stdout, "fail to open configuration file\n");
        return FAIL_TO_OPEN_CONFIG_FILE;
liucong's avatar
liucong committed
73
74
75
76
    }
    LOG_INFO(stdout, "succeed to open configuration file\n");

    // 获取配置文件参数
liucong's avatar
liucong committed
77
78
79
80
81
    cv::FileNode netNode  = configurationFile["Classifier"];
    std::string modelPath = (std::string)netNode["ModelPath"];
    useInt8               = (bool)(int)netNode["UseInt8"];
    useFP16               = (bool)(int)netNode["UseFP16"];
    useoffloadcopy        = (bool)(int)netNode["Useoffloadcopy"];
liucong's avatar
liucong committed
82
83

    // 加载模型
liucong's avatar
liucong committed
84
    if(!Exists(modelPath))
liucong's avatar
liucong committed
85
    {
liucong's avatar
liucong committed
86
        LOG_ERROR(stdout, "%s not exist!\n", modelPath.c_str());
liucong's avatar
liucong committed
87
88
        return MODEL_NOT_EXIST;
    }
89
    net = migraphx::parse_onnx(modelPath);
liucong's avatar
liucong committed
90
    LOG_INFO(stdout, "succeed to load model: %s\n", GetFileName(modelPath).c_str());
liucong's avatar
liucong committed
91

liucong's avatar
liucong committed
92
    // 获取模型输入/输出节点信息
liucong's avatar
liucong committed
93
94
95
96
97
98
99
100
101
102
103
    std::unordered_map<std::string, migraphx::shape> inputs  = net.get_inputs();
    std::unordered_map<std::string, migraphx::shape> outputs = net.get_outputs();
    inputName                                                = inputs.begin()->first;
    inputShape                                               = inputs.begin()->second;
    outputName                                               = outputs.begin()->first;
    outputShape                                              = outputs.begin()->second;
    int N                                                    = inputShape.lens()[0];
    int C                                                    = inputShape.lens()[1];
    int H                                                    = inputShape.lens()[2];
    int W                                                    = inputShape.lens()[3];
    inputSize                                                = cv::Size(W, H);
liucong's avatar
liucong committed
104
105
106
107
108
109
110
111

    // 设置模型为GPU模式
    migraphx::target gpuTarget = migraphx::gpu::target{};

    // 量化
    if(useInt8)
    {
        // 创建量化校准数据,建议使用测试集中的多张典型图像
liucong's avatar
liucong committed
112
        cv::Mat srcImage = cv::imread("../Resource/Images/ImageNet_test.jpg", 1);
liucong's avatar
liucong committed
113
        std::vector<cv::Mat> srcImages;
liucong's avatar
liucong committed
114
        for(int i = 0; i < inputShape.lens()[0]; ++i)
liucong's avatar
liucong committed
115
116
117
        {
            srcImages.push_back(srcImage);
        }
liucong's avatar
liucong committed
118
119

        // 数据预处理
liucong's avatar
liucong committed
120
        cv::Mat inputBlob = Preprocess(srcImages);
liucong's avatar
liucong committed
121
        std::unordered_map<std::string, migraphx::argument> inputData;
liucong's avatar
liucong committed
122
123
124
        inputData[inputName] = migraphx::argument{inputShape, (float*)inputBlob.data};
        std::vector<std::unordered_map<std::string, migraphx::argument>> calibrationData = {
            inputData};
liucong's avatar
liucong committed
125
126
127
128

        // INT8量化
        migraphx::quantize_int8(net, gpuTarget, calibrationData);
    }
liucong's avatar
liucong committed
129
    else if(useFP16)
liucong's avatar
liucong committed
130
131
132
133
134
135
    {
        migraphx::quantize_fp16(net);
    }

    // 编译模型
    migraphx::compile_options options;
liucong's avatar
liucong committed
136
    options.device_id = 0; // 设置GPU设备,默认为0号设备
liucong's avatar
liucong committed
137
138
    if(useoffloadcopy)
    {
liucong's avatar
liucong committed
139
        options.offload_copy = true;
liucong's avatar
liucong committed
140
141
142
    }
    else
    {
liucong's avatar
liucong committed
143
        options.offload_copy = false;
liucong's avatar
liucong committed
144
    }
liucong's avatar
liucong committed
145
146
    net.compile(gpuTarget, options);
    LOG_INFO(stdout, "succeed to compile model: %s\n", GetFileName(modelPath).c_str());
147
148
149
150
151

    // offloadcopy为false的时候,分配输入和输出内存
    if(!useoffloadcopy)
    {
        // 分配device输入内存
liucong's avatar
liucong committed
152
        inputBuffer_Device = nullptr;
153
154
155
156
        hipMalloc(&inputBuffer_Device, inputShape.bytes());
        programParameters[inputName] = migraphx::argument{inputShape, inputBuffer_Device};

        // 分配device和host输出内存
liucong's avatar
liucong committed
157
        outputBuffer_Device = nullptr;
158
159
        hipMalloc(&outputBuffer_Device, outputShape.bytes());
        programParameters[outputName] = migraphx::argument{outputShape, outputBuffer_Device};
liucong's avatar
liucong committed
160
161
        outputBuffer_Host             = nullptr; // host内存
        outputBuffer_Host             = malloc(outputShape.bytes());
162
    }
liucong's avatar
liucong committed
163

liucong's avatar
liucong committed
164
    // warm up
liucong's avatar
liucong committed
165
166
167
    if(useoffloadcopy)
    {
        std::unordered_map<std::string, migraphx::argument> inputData;
liucong's avatar
liucong committed
168
        inputData[inputName] = migraphx::argument{inputShape};
liucong's avatar
liucong committed
169
170
171
172
        net.eval(inputData);
    }
    else
    {
liucong's avatar
liucong committed
173
        migraphx::argument inputData = migraphx::argument{inputShape};
174
175
        hipMemcpy(inputBuffer_Device, inputData.data(), inputShape.bytes(), hipMemcpyHostToDevice);
        net.eval(programParameters);
liucong's avatar
liucong committed
176
    }
liucong's avatar
liucong committed
177
178

    // log
liucong's avatar
liucong committed
179
180
181
182
183
    LOG_INFO(stdout, "InputSize:%dx%d\n", inputSize.width, inputSize.height);
    LOG_INFO(stdout, "InputName:%s\n", inputName.c_str());
    LOG_INFO(stdout, "UseInt8:%d\n", (int)useInt8);
    LOG_INFO(stdout, "UseFP16:%d\n", (int)useFP16);
    LOG_INFO(stdout, "Useoffloadcopy:%d\n", (int)useoffloadcopy);
liucong's avatar
liucong committed
184

liucong's avatar
liucong committed
185
    return SUCCESS;
186
187
}

liucong's avatar
liucong committed
188
189
ErrorCode Classifier::Classify(const std::vector<cv::Mat>& srcImages,
                               std::vector<std::vector<ResultOfPrediction>>& predictions)
190
{
liucong's avatar
liucong committed
191
    if(srcImages.size() == 0 || srcImages[0].empty() || srcImages[0].depth() != CV_8U)
192
193
194
195
    {
        LOG_ERROR(stdout, "image error!\n");
        return IMAGE_ERROR;
    }
liucong's avatar
liucong committed
196

liucong's avatar
liucong committed
197
    // 数据预处理
198
    cv::Mat inputBlob = Preprocess(srcImages);
liucong's avatar
liucong committed
199

200
    // 当offload为true时,不需要内存拷贝
liucong's avatar
liucong committed
201
202
203
    if(useoffloadcopy)
    {
        std::unordered_map<std::string, migraphx::argument> inputData;
liucong's avatar
liucong committed
204
        inputData[inputName] = migraphx::argument{inputShape, (float*)inputBlob.data};
liucong's avatar
liucong committed
205

liucong's avatar
liucong committed
206
        // 推理
liucong's avatar
liucong committed
207
        std::vector<migraphx::argument> results = net.eval(inputData);
liucong's avatar
liucong committed
208

209
        // 获取输出节点的属性
liucong's avatar
liucong committed
210
211
212
213
214
215
        migraphx::argument result    = results[0];         // 获取第一个输出节点的数据
        migraphx::shape outputShapes = result.get_shape(); // 输出节点的shape
        std::vector<std::size_t> outputSize =
            outputShapes.lens();                      // 每一维大小,维度顺序为(N,C,H,W)
        int numberOfOutput = outputShapes.elements(); // 输出节点元素的个数
        float* logits      = (float*)result.data();   // 输出节点数据指针
216
217

        // 获取每张图像的预测结果
liucong's avatar
liucong committed
218
219
        int numberOfClasses = numberOfOutput / srcImages.size();
        for(int i = 0; i < srcImages.size(); ++i)
220
        {
liucong's avatar
liucong committed
221
            int startIndex = numberOfClasses * i;
222
223

            std::vector<float> logit;
liucong's avatar
liucong committed
224
            for(int j = 0; j < numberOfClasses; ++j)
225
            {
liucong's avatar
liucong committed
226
                logit.push_back(logits[startIndex + j]);
227
            }
liucong's avatar
liucong committed
228

229
            std::vector<ResultOfPrediction> resultOfPredictions;
liucong's avatar
liucong committed
230
            for(int j = 0; j < numberOfClasses; ++j)
231
232
            {
                ResultOfPrediction prediction;
liucong's avatar
liucong committed
233
234
                prediction.label      = j;
                prediction.confidence = logit[j];
235
236
237
238
239
240

                resultOfPredictions.push_back(prediction);
            }

            predictions.push_back(resultOfPredictions);
        }
liucong's avatar
liucong committed
241
    }
liucong's avatar
liucong committed
242
    else // 当offload为false时,需要内存拷贝
liucong's avatar
liucong committed
243
244
    {

245
        migraphx::argument inputData = migraphx::argument{inputShape, (float*)inputBlob.data};
liucong's avatar
liucong committed
246

247
248
        // 拷贝到device输入内存
        hipMemcpy(inputBuffer_Device, inputData.data(), inputShape.bytes(), hipMemcpyHostToDevice);
liucong's avatar
liucong committed
249
250

        // 推理
liucong's avatar
liucong committed
251
        std::vector<migraphx::argument> results = net.eval(programParameters);
liucong's avatar
liucong committed
252

253
        // 获取输出节点的属性
liucong's avatar
liucong committed
254
255
256
257
258
        migraphx::argument result    = results[0];         // 获取第一个输出节点的数据
        migraphx::shape outputShapes = result.get_shape(); // 输出节点的shape
        std::vector<std::size_t> outputSize =
            outputShapes.lens();                      // 每一维大小,维度顺序为(N,C,H,W)
        int numberOfOutput = outputShapes.elements(); // 输出节点元素的个数
liucong's avatar
liucong committed
259

260
        // 将device输出数据拷贝到分配好的host输出内存
liucong's avatar
liucong committed
261
262
263
264
        hipMemcpy(outputBuffer_Host,
                  outputBuffer_Device,
                  outputShapes.bytes(),
                  hipMemcpyDeviceToHost); // 直接使用事先分配好的输出内存拷贝
liucong's avatar
liucong committed
265

266
        // 获取每张图像的预测结果
liucong's avatar
liucong committed
267
        int numberOfClasses = numberOfOutput / srcImages.size();
liucong's avatar
liucong committed
268
        std::vector<float> logit;
liucong's avatar
liucong committed
269
        for(int i = 0; i < srcImages.size(); ++i)
liucong's avatar
liucong committed
270
        {
liucong's avatar
liucong committed
271
272
            int startIndex = numberOfClasses * i;
            for(int j = 0; j < numberOfClasses; ++j)
273
            {
liucong's avatar
liucong committed
274
                logit.push_back(((float*)outputBuffer_Host)[startIndex + j]);
275
            }
liucong's avatar
liucong committed
276

277
            std::vector<ResultOfPrediction> resultOfPredictions;
liucong's avatar
liucong committed
278
            for(int j = 0; j < numberOfClasses; ++j)
279
280
            {
                ResultOfPrediction prediction;
liucong's avatar
liucong committed
281
282
                prediction.label      = j;
                prediction.confidence = logit[j];
283
284
285

                resultOfPredictions.push_back(prediction);
            }
liucong's avatar
liucong committed
286

287
            predictions.push_back(resultOfPredictions);
liucong's avatar
liucong committed
288
289
        }

290
291
292
293
        // 释放
        hipFree(inputBuffer_Device);
        hipFree(outputBuffer_Device);
        free(outputBuffer_Host);
liucong's avatar
liucong committed
294
295
296
297
298
    }

    return SUCCESS;
}

liucong's avatar
liucong committed
299
} // namespace migraphxSamples