이 페이지의 최신 내용은 아직 번역되지 않았습니다. 최신 내용은 영문으로 볼 수 있습니다.

Intel MKL-DNN을 사용한 심층 학습 예측

이 예제에서는 Intel® 프로세서에서 심층 학습을 사용하는 이미지 분류 응용 프로그램의 코드를 codegen을 사용하여 생성하는 방법을 보여줍니다. 생성된 코드는 Intel MKL-DNN(Math Kernel Library for Deep Neural Networks)을 사용합니다. 먼저 ResNet-50 이미지 분류 네트워크를 사용하여 예측을 실행하는 MEX 함수를 생성합니다. 그런 다음 정적 라이브러리를 빌드하고, ResNet-50 이미지 분류 네트워크를 사용하여 예측을 실행하는 메인 파일로 컴파일합니다.

선행 조건

  • Intel AVX2(Intel Advanced Vector Extensions 2) 명령을 지원하는 Xeon 프로세서

  • Intel MKL-DNN(Math Kernel Library for Deep Neural Networks)

  • OpenCV(Open Source Computer Vision Library) v3.1

  • Intel MKL-DNN 및 OpenCV 환경 변수

  • C++ 코드 생성을 위한 MATLAB® Coder™.

  • MATLAB Coder Interface for Deep Learning 지원 패키지

  • DAGNetwork 객체를 사용하기 위한 Deep Learning Toolbox™

  • 사전 훈련된 ResNet 네트워크를 사용하기 위한 Deep Learning Toolbox Model for ResNet-50 Network 지원 패키지.

자세한 내용은 Prerequisites for Deep Learning with MATLAB Coder (MATLAB Coder) 항목을 참조하십시오.

이 예제는 Linux® 및 Windows® 플랫폼에서 지원됩니다.

resnet_predict 함수

이 예제는 MKL-DNN을 사용한 이미지 분류를 보여주기 위해 DAG 네트워크 ResNet-50을 사용합니다. MATLAB에서 사용 가능한 사전 훈련된 ResNet-50 모델은 Deep Learning Toolbox Model for ResNet-50 Network 지원 패키지에서 제공됩니다. 지원 패키지를 다운로드하여 설치하려면 애드온 탐색기를 사용하십시오. 애드온 받기 (MATLAB) 항목을 참조하십시오.

resnet_predict 함수는 ResNet-50 네트워크를 영속 네트워크 객체로 불러옵니다. 이후 이 함수를 호출하면 이 영속 객체가 재사용됩니다.

type resnet_predict
% Copyright 2018 The MathWorks, Inc.

function out = resnet_predict(in) 
%#codegen

% A persistent object mynet is used to load the series network object.
% At the first call to this function, the persistent object is constructed and
% setup. When the function is called subsequent times, the same object is reused 
% to call predict on inputs, avoiding reconstructing and reloading the
% network object.

persistent mynet;

if isempty(mynet)
    % Call the function resnet50 that returns a DAG network
    % for ResNet-50 model.
    mynet = coder.loadDeepLearningNetwork('resnet50','resnet');
end

% pass in input   
out = mynet.predict(in);

resnet_predict 함수에 대한 MEX 코드 생성하기

resnet_predict.m 함수에서 MEX 함수를 생성하려면 MKL-DNN 라이브러리용으로 만들어진 심층 학습 구성 객체와 함께 codegen을 사용하십시오. codegen으로 전달하는 MEX 코드 생성 구성 객체에 심층 학습 구성 객체를 연결합니다.

 cfg = coder.config('mex');
 cfg.TargetLang = 'C++';
 cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn');
 codegen -config cfg resnet_predict -args {ones(224,224,3,'single')} -report
Code generation successful: To view the report, open('codegen\mex\resnet_predict\html\report.mldatx').

테스트 이미지에 대해 predict 호출하기

im = imread('peppers.png');
im = imresize(im, [224,224]);
imshow(im);
predict_scores = resnet_predict_mex(single(im));

상위 5개의 예측 점수를 synset 사전에 있는 단어에 매핑합니다.

fid = fopen('synsetWords.txt');
synsetOut = textscan(fid,'%s', 'delimiter', '\n');
synsetOut = synsetOut{1};
fclose(fid);
[val,indx] = sort(predict_scores, 'descend');
scores = val(1:5)*100;
top5labels = synsetOut(indx(1:5));

이미지에 상위 5개의 분류 레이블을 표시합니다.

outputImage = zeros(224,400,3, 'uint8');
for k = 1:3
    outputImage(:,177:end,k) = im(:,:,k);
end

scol = 1;
srow = 1;
outputImage = insertText(outputImage, [scol, srow], 'Classification with ResNet-50', 'TextColor', 'w','FontSize',20, 'BoxColor', 'black');
srow = srow + 30;
for k = 1:5
    outputImage = insertText(outputImage, [scol, srow], [top5labels{k},' ',num2str(scores(k), '%2.2f'),'%'], 'TextColor', 'w','FontSize',15, 'BoxColor', 'black');
    srow = srow + 25;
end

imshow(outputImage);

메모리에서 정적 네트워크 객체를 지웁니다.

clear mex;

resnet_predict 함수에 대한 정적 라이브러리 생성하기

resnet_predict.m 함수에서 정적 라이브러리를 생성하려면 MKL-DNN 라이브러리용으로 만들어진 심층 학습 구성 객체와 함께 codegen을 사용하십시오. codegen으로 전달하는 코드 생성 구성 객체에 심층 학습 구성 객체를 연결합니다.

cfg = coder.config('lib');
cfg.TargetLang = 'C++';
cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn');
codegen -config cfg resnet_predict -args {ones(224,224,3,'single')} -report
%
codegendir = fullfile(pwd, 'codegen', 'lib', 'resnet_predict');
Code generation successful: To view the report, open('codegen\lib\resnet_predict\html\report.mldatx').

main_resnet.cpp 파일

메인 파일은 codegen 명령에 의해 만들어진 정적 라이브러리에서 실행 파일을 생성하는 데 사용됩니다. 메인 파일은 입력 이미지를 읽어 들이고 이미지에 대해 예측을 실행한 다음 이미지에 분류 레이블을 표시합니다.

type main_resnet.cpp
/* Copyright 2018 The MathWorks, Inc. */

#include "resnet_predict.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace cv;

int readData(void* inputBuffer, char* inputImage) {

    Mat inpImage, intermImage;
    inpImage = imread(inputImage, 1);
    Size size(224, 224);
    resize(inpImage, intermImage, size);
    if (!intermImage.data) {
        printf(" No image data \n ");
        exit(1);
    }
    float* input = (float*)inputBuffer;

    for (int j = 0; j < 224 * 224; j++) {
        // BGR to RGB
        input[2 * 224 * 224 + j] = (float)(intermImage.data[j * 3 + 0]);
        input[1 * 224 * 224 + j] = (float)(intermImage.data[j * 3 + 1]);
        input[0 * 224 * 224 + j] = (float)(intermImage.data[j * 3 + 2]);
    }
    return 1;
}

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(_WIN64)

int cmpfunc(void* r, const void* a, const void* b) {
    float x = ((float*)r)[*(int*)b] - ((float*)r)[*(int*)a];
    return (x > 0 ? ceil(x) : floor(x));
}
#else

int cmpfunc(const void* a, const void* b, void* r) {
    float x = ((float*)r)[*(int*)b] - ((float*)r)[*(int*)a];
    return (x > 0 ? ceil(x) : floor(x));
}

#endif

void top(float* r, int* top5) {
    int t[1000];
    for (int i = 0; i < 1000; i++) {
        t[i] = i;
    }
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(_WIN64)
    qsort_s(t, 1000, sizeof(int), cmpfunc, r);
#else
    qsort_r(t, 1000, sizeof(int), cmpfunc, r);
#endif
    top5[0] = t[0];
    top5[1] = t[1];
    top5[2] = t[2];
    top5[3] = t[3];
    top5[4] = t[4];
    return;
}


int prepareSynset(char synsets[1000][100]) {
    FILE* fp1 = fopen("synsetWords.txt", "r");
    if (fp1 == 0) {
        return -1;
    }

    for (int i = 0; i < 1000; i++) {
        if (fgets(synsets[i], 100, fp1) != NULL)
            ;
        strtok(synsets[i], "\n");
    }
    fclose(fp1);
    return 0;
}

void writeData(float* output, char synsetWords[1000][100], Mat &frame) {
    int top5[5], j;
    
    top(output, top5);
    
    copyMakeBorder(frame, frame, 0, 0, 400, 0, BORDER_CONSTANT, CV_RGB(0,0,0));
    char strbuf[50];
    sprintf(strbuf, "%4.1f%% %s", output[top5[0]]*100, synsetWords[top5[0]]);
    putText(frame, strbuf, cvPoint(30,80), CV_FONT_HERSHEY_DUPLEX, 1.0, CV_RGB(220,220,220), 1);
    sprintf(strbuf, "%4.1f%% %s", output[top5[1]]*100, synsetWords[top5[1]]);
    putText(frame, strbuf, cvPoint(30,130), CV_FONT_HERSHEY_DUPLEX, 1.0, CV_RGB(220,220,220), 1);
    sprintf(strbuf, "%4.1f%% %s", output[top5[2]]*100, synsetWords[top5[2]]);
    putText(frame, strbuf, cvPoint(30,180), CV_FONT_HERSHEY_DUPLEX, 1.0, CV_RGB(220,220,220), 1);
    sprintf(strbuf, "%4.1f%% %s", output[top5[3]]*100, synsetWords[top5[3]]);
    putText(frame, strbuf, cvPoint(30,230), CV_FONT_HERSHEY_DUPLEX, 1.0, CV_RGB(220,220,220), 1);
    sprintf(strbuf, "%4.1f%% %s", output[top5[4]]*100, synsetWords[top5[4]]);
    putText(frame, strbuf, cvPoint(30,280), CV_FONT_HERSHEY_DUPLEX, 1.0, CV_RGB(220,220,220), 1);

}

// Main function
int main(int argc, char* argv[]) {
    int n = 1;
    char synsetWords[1000][100];
    
    namedWindow("Classification with ResNet-50",CV_WINDOW_NORMAL);
    resizeWindow("Classification with ResNet-50",440,224);

    Mat im;
    im = imread(argv[1], 1);
    
    float* ipfBuffer = (float*)calloc(sizeof(float), 224*224*3);
    
    float* opBuffer = (float*)calloc(sizeof(float), 1000);
    if (argc != 2) {
        printf("Input image missing \nSample Usage-./resnet_exe image.png\n");
        exit(1);
    }
    if (prepareSynset(synsetWords) == -1) {
        printf("ERROR: Unable to find synsetWords.txt\n");
        return -1;
    }

    //read input imaget to the ipfBuffer
    readData(ipfBuffer, argv[1]);
    
    //run prediction on image stored in ipfBuffer
    resnet_predict(ipfBuffer, opBuffer);
    
    //write predictions on input image
    writeData(opBuffer, synsetWords, im);

    //show predictions on input image
    imshow("Classification with ResNet-50", im);
    waitKey(5000);
    destroyWindow("Classification with ResNet-50");
    return 0;
}

실행 파일 빌드하고 실행하기

대상 플랫폼에 따라 실행 파일을 빌드합니다. 이 예제는 Windows 플랫폼의 경우 C++용 Microsoft® Visual Studio® 2017을 사용합니다.

if ispc
    setenv('MATLAB_ROOT', matlabroot);
    system('make_mkldnn_win17.bat');
    system('resnet.exe peppers.png');
else
    setenv('MATLAB_ROOT', matlabroot);
    system('make -f Makefile_mkldnn_linux.mk');
    system('./resnet_exe peppers.png');
end

입력 이미지 파일을 읽어 들일 때 사용되는 라이브러리 버전 간의 차이로 인해 MEX 함수의 결과와 정적 라이브러리 함수의 결과가 일치하지 않을 수 있습니다. MEX 함수로 전달되는 이미지는 MATLAB에서 제공하는 버전을 사용하여 읽힙니다. 정적 라이브러리 함수로 전달되는 이미지는 OpenCV가 사용하는 버전을 사용하여 읽힙니다.

관련 항목