针对不同批量大小在 Intel 目标上进行深度学习代码生成
此示例说明如何使用 codegen
命令为在 Intel® 处理器上使用深度学习的图像分类应用程序生成代码。生成的代码使用了用于深度神经网络的 Intel 数学核心函数库 (MKL-DNN)。此示例由两部分组成:
第一部分说明如何生成接受一批图像作为输入的 MEX 函数。
第二部分说明如何生成接受一批图像作为输入的可执行文件。
前提条件
支持 Intel Advanced Vector Extensions 2 (Intel AVX2) 指令的 Intel 处理器
用于深度神经网络的 Intel 数学核心函数库 (MKL-DNN)
编译器和库的环境变量。有关支持的编译器版本的信息,请参阅支持的编译器。有关设置环境变量的信息,请参阅使用 MATLAB Coder 进行深度学习的前提条件 (MATLAB Coder)。
此示例可在 Linux®、Windows® 和 Mac® 平台上运行,但不支持在 MATLAB Online 中运行。
下载输入视频文件
下载示例视频文件。
if ~exist('./object_class.avi', 'file') url = 'https://www.mathworks.com/supportfiles/gpucoder/media/object_class.avi.zip'; websave('object_class.avi.zip',url); unzip('object_class.avi.zip'); end
定义 resnet_predict
函数
此示例在 Intel 桌面上使用 DAG 网络 ResNet-50 显示图像分类。Deep Learning Toolbox Model for ResNet-50 Network 支持包是适用于 MATLAB 的预训练 ResNet-50 模型的一部分。
resnet_predict
函数将 ResNet-50 网络加载到一个持久性网络对象中,然后对输入执行预测。对该函数的后续调用会重用该持久性网络对象。
type resnet_predict
% Copyright 2020 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
函数生成 MEX 函数,请将 codegen
与针对 MKL-DNN 库的深度学习配置对象结合使用。将该深度学习配置对象附加到传递给 codegen
的 MEX 代码生成配置对象。运行 codegen
命令,并将输入指定为 [224,224,3,|batchSize|] 大小的四维矩阵。该值对应于 ResNet-50 网络的输入层大小。
batchSize = 5; cfg = coder.config('mex'); cfg.TargetLang = 'C++'; cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn'); codegen -config cfg resnet_predict -args {ones(224,224,3,batchSize,'single')} -report
Code generation successful: To view the report, open('codegen\mex\resnet_predict\html\report.mldatx')
对一批图像执行预测
假设已下载 Object_class.avi 视频文件。创建 videoReader 对象并使用 videoReader read 函数读取五个帧。由于 batchSize 设置为 5,因此读取 5 个图像。将该批输入图像的大小调整为 resnet50 所需的大小,即 ResNet50 网络预期的大小。
videoReader = VideoReader('Object_class.avi'); imBatch = read(videoReader,[1 5]); imBatch = imresize(imBatch, [224,224]);
调用生成的 resnet_predict_mex
函数,该函数为您提供的输入输出分类结果。
predict_scores = resnet_predict_mex(single(imBatch));
获取批量图像中每个图像的前 5 个概率分数及其标签。
[val,indx] = sort(transpose(predict_scores), 'descend'); scores = val(1:5,:)*100; net = resnet50; classnames = net.Layers(end).ClassNames; for i = 1:batchSize labels = classnames(indx(1:5,i)); disp(['Top 5 predictions on image, ', num2str(i)]); for j=1:5 disp([labels{j},' ',num2str(scores(j,i), '%2.2f'),'%']) end end
将针对第一个图像的前五个预测分数映射到 synset
字典中的单词。
fid = fopen('synsetWords.txt'); synsetOut = textscan(fid,'%s', 'delimiter', '\n'); synsetOut = synsetOut{1}; fclose(fid); [val,indx] = sort(transpose(predict_scores), 'descend'); scores = val(1:5,1)*100; top5labels = synsetOut(indx(1:5,1));
在图像上显示排名前五的分类标签。
outputImage = zeros(224,400,3, 'uint8'); for k = 1:3 outputImage(:,177:end,k) = imBatch(:,:,k,1); 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_exe
入口函数
要从 MATLAB 代码生成可执行文件,请定义一个新的入口函数 resnet_predict_exe
。此函数与之前的入口函数 resent_predict
相似,但还包含用于预处理和后处理的代码。resnet_predict_exe
使用的 API 与平台无关。此函数接受视频和批量大小作为输入参数。这些参数是编译时常量。
type resnet_predict_exe
% Copyright 2020 The MathWorks, Inc. function resnet_predict_exe(inputVideo,batchSize) %#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 % Create video reader and video player objects % videoReader = VideoReader(inputVideo); depVideoPlayer = vision.DeployableVideoPlayer; % Read the classification label names % synsetOut = readImageClassLabels('synsetWords.txt'); i=1; % Read frames until end of video file % while ~(i+batchSize > (videoReader.NumFrames+1)) % Read and resize batch of frames as specified by input argument% reSizedImagesBatch = readImageInputBatch(videoReader,batchSize,i); % run predict on resized input images % predict_scores = mynet.predict(reSizedImagesBatch); % overlay the prediction scores on images and display % overlayResultsOnImages(predict_scores,synsetOut,reSizedImagesBatch,batchSize,depVideoPlayer) i = i+ batchSize; end release(depVideoPlayer); end function synsetOut = readImageClassLabels(classLabelsFile) % Read the classification label names from the file % % Inputs : % classLabelsFile - supplied by user % % Outputs : % synsetOut - cell array filled with 1000 image class labels synsetOut = cell(1000,1); fid = fopen(classLabelsFile); for i = 1:1000 synsetOut{i} = fgetl(fid); end fclose(fid); end function reSizedImagesBatch = readImageInputBatch(videoReader,batchSize,i) % Read and resize batch of frames as specified by input argument% % % Inputs : % videoReader - Object used for reading the images from video file % batchSize - Number of images in batch to process. Supplied by user % i - index to track frames read from video file % % Outputs : % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize img = read(videoReader,[i (i+batchSize-1)]); reSizedImagesBatch = coder.nullcopy(ones(224,224,3,batchSize,'like',img)); resizeTo = coder.const([224,224]); reSizedImagesBatch(:,:,:,:) = imresize(img,resizeTo); end function overlayResultsOnImages(predict_scores,synsetOut,reSizedImagesBatch,batchSize,depVideoPlayer) % Read and resize batch of frames as specified by input argument% % % Inputs : % predict_scores - classification results for given network % synsetOut - cell array filled with 1000 image class labels % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize % batchSize - Number of images in batch to process. Supplied by user % depVideoPlayer - Object for displaying results % % Outputs : % Predicted results overlayed on input images % sort the predicted scores % [val,indx] = sort(transpose(predict_scores), 'descend'); for j = 1:batchSize scores = val(1:5,j)*100; outputImage = zeros(224,400,3, 'uint8'); for k = 1:3 outputImage(:,177:end,k) = reSizedImagesBatch(:,:,k,j); end % Overlay the results on image % scol = 1; srow = 1; outputImage = insertText(outputImage, [scol, srow], 'Classification with ResNet-50', 'TextColor', [255 255 255],'FontSize',20, 'BoxColor', [0 0 0]); srow = srow + 30; for k = 1:5 scoreStr = sprintf('%2.2f',scores(k)); outputImage = insertText(outputImage, [scol, srow], [synsetOut{indx(k,j)},' ',scoreStr,'%'], 'TextColor', [255 255 255],'FontSize',15, 'BoxColor', [0 0 0]); srow = srow + 25; end depVideoPlayer(outputImage); end end
resnet_predict_exe
函数的结构体
函数 resnet_predict_exe
包含四个子部分,分别用于执行下列操作:
从提供的输入文本文件中读取分类标签
读取输入的一批图像,并根据网络需要调整其大小
对输入图像批量运行推断
在图像上叠加结果
有关上述每个步骤的详细信息,请参阅后续章节。
readImageClassLabels
函数
此函数接受 synsetWords.txt
文件作为输入参数。它读取分类标签并填充元胞数组。
function synsetOut = readImageClassLabels(classLabelsFile) % Read the classification label names from the file % % Inputs : % classLabelsFile - supplied by user % % Outputs : % synsetOut - cell array filled with 1000 image class labels
synsetOut = cell(1000,1); fid = fopen(classLabelsFile); for i = 1:1000 synsetOut{i} = fgetl(fid); end fclose(fid); end
readImageInputBatch
函数
此函数从作为输入参数传递给函数的视频输入文件中读取图像并调整其大小。它读取指定的输入图像,并将其大小调整为 224×224×3,这是 resnet50 网络预期的大小。
function reSizedImagesBatch = readImageInputBatch(videoReader,batchSize,i) % Read and resize batch of frames as specified by input argument% % % Inputs : % videoReader - Object used for reading the images from video file % batchSize - Number of images in batch to process. Supplied by user % i - index to track frames read from video file % % Outputs : % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize
img = read(videoReader,[i (i+batchSize-1)]); reSizedImagesBatch = coder.nullcopy(ones(224,224,3,batchSize,'like',img)); resizeTo = coder.const([224,224]); reSizedImagesBatch(:,:,:,:) = imresize(img,resizeTo); end
mynet.predict
函数
此函数接受调整大小后的图像批量作为输入,并返回预测结果。
% run predict on resized input images % predict_scores = mynet.predict(reSizedImagesBatch);
overlayResultsOnImages
函数
此函数接受预测结果,并按降序对它们进行排序。它在输入图像上叠加这些结果并显示它们。
function overlayResultsOnImages(predict_scores,synsetOut,reSizedImagesBatch,batchSize,depVideoPlayer) % Read and resize batch of frames as specified by input argument% % % Inputs : % predict_scores - classification results for given network % synsetOut - cell array filled with 1000 image class labels % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize % batchSize - Number of images in batch to process. Supplied by user % depVideoPlayer - Object for displaying results % % Outputs : % Predicted results overlayed on input images
% sort the predicted scores % [val,indx] = sort(transpose(predict_scores), 'descend');
for j = 1:batchSize scores = val(1:5,j)*100; outputImage = zeros(224,400,3, 'uint8'); for k = 1:3 outputImage(:,177:end,k) = reSizedImagesBatch(:,:,k,j); end
% Overlay the results on image % scol = 1; srow = 1; outputImage = insertText(outputImage, [scol, srow], 'Classification with ResNet-50', 'TextColor', [255 255 255],'FontSize',20, 'BoxColor', [0 0 0]); srow = srow + 30; for k = 1:5 scoreStr = sprintf('%2.2f',scores(k)); outputImage = insertText(outputImage, [scol, srow], [synsetOut{indx(k,j)},' ',scoreStr,'%'], 'TextColor', [255 255 255],'FontSize',15, 'BoxColor', [0 0 0]); srow = srow + 25; end
depVideoPlayer(outputImage); end end
编译和运行可执行文件
创建一个用于生成可执行文件的代码配置对象。将一个深度学习配置对象与之关联。设置 batchSize
和 inputVideoFile
变量。
如果不打算创建自定义 C++ 主函数,而是使用生成的示例 C++ 主函数,请将 GenerateExampleMain
参数设置为 'GenerateCodeAndCompile'
。此外,禁用 cfg.EnableOpenMP 以确保在从桌面终端运行可执行文件时不存在对 openmp 库的依存关系。
cfg = coder.config('exe'); cfg.TargetLang = 'C++'; cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn'); batchSize = 5; inputVideoFile = 'object_class.avi'; cfg.GenerateExampleMain = 'GenerateCodeAndCompile'; cfg.EnableOpenMP = 0;
运行 codegen
命令以编译可执行文件。在 MATLAB 命令行中或在桌面终端上运行生成的可执行文件 resnet_predict_exe。
codegen -config cfg resnet_predict_exe -args {coder.Constant(inputVideoFile), coder.Constant(batchSize)} -report system('./resnet_predict_exe')