主要内容

生成INT8树莓派上的深度学习网络代码

这个例子之前被命名为“树莓派上量化深度学习网络的代码生成”。

此示例通过将卷积层的权重、偏差和激活量化为8位缩放整数数据类型,为卷积深度神经网络生成优化的c++代码。量化是通过提供校准结果文件产生的校准(深度学习工具箱)函数codegen命令。

代码生成不支持量化深度神经网络生成金宝app数字转换(深度学习工具箱)函数。

深度学习使用的神经网络架构包含许多处理层,包括卷积层。深度学习模型通常在大量标记数据集上工作。在这些模型上执行推理需要大量的计算,消耗大量的内存。神经网络使用内存来存储输入数据、参数(权重)和来自每一层的激活,因为输入在网络中传播。在MATLAB中训练的深度神经网络使用单精度浮点数据类型。即使是规模很小的网络也需要相当数量的内存和硬件来执行这些浮点算术操作。这些限制可能会阻碍将深度学习模型部署到计算能力低、内存资源较小的设备上。通过使用较低的精度来存储权重和激活,可以减少网络的内存需求。

您可以将深度学习工具箱与深度学习工具箱模型量化库支持包结合使用,通过将卷积层的权重、偏差和激活量化为8位缩放整数数据类型来减少深度神经网络的内存占用。金宝app然后,您可以使用MATLAB Coder™为该网络生成优化的代码。生成的代码通过使用ARM计算库利用ARM®处理器SIMD。生成的代码可以作为源代码、静态或动态库或可执行文件集成到项目中,可以部署到各种ARM CPU平台(如树莓派™)。

这个例子展示了如何为卷积神经网络生成c++代码,该网络使用ARM计算库并在8位整数中执行推理计算。

此示例不支持MATLAB Online。金宝app

第三方的先决条件

示例:使用SqueezeNet对图像进行分类

在本例中,您使用MATLAB Coder为深度卷积神经网络生成优化的c++代码并对图像进行分类。生成的代码对卷积层使用8位整数数据类型执行推理计算。本例使用预训练的squeezenet(深度学习工具箱)卷积神经网络。

SqueezeNet已经在包含1000个对象类别图像的ImageNet数据集上进行了训练。该网络已经为广泛的图像学习了丰富的特征表示。该网络将图像作为输入,并输出图像中对象的标签以及每个对象类别的概率。

这个例子包括四个步骤:

  1. 修改SqueezeNet神经网络,使用迁移学习对包含五个对象类别的图像进行分类。

  2. 使用校准函数使用样本输入来练习网络,并收集范围信息以产生校准结果文件。

  3. 使用codegen命令和校准结果文件生成网络优化代码。生成的代码通过PIL执行在Raspberry Pi目标上运行。

  4. 在树莓派上执行生成的PIL MEX。

使用SqueezeNet进行迁移学习

为了对一组新图像进行分类,必须使用迁移学习对预训练的SqueezeNet卷积神经网络进行微调。在迁移学习中,你使用一个预先训练好的网络,并将其作为学习新任务的起点。使用迁移学习对网络进行微调通常比从头训练随机初始化权重的网络更快更容易。通过使用少量的训练图像,可以快速地将学习到的特征转移到新的任务中。

负荷训练数据

解压缩并将新映像加载为映像数据存储。的imageDatastore函数根据文件夹名称自动标记图像,并将数据存储为ImageDatastore对象。图像数据存储使您能够存储大量图像数据,包括不适合内存的数据,并在卷积神经网络训练期间有效地读取批量图像。

解压缩(“MerchData.zip”);imds = imageDatastore(“MerchData”...“IncludeSubfolders”,真的,...“LabelSource”“foldernames”);[imdsTrain,imdsValidation] = splitEachLabel(imds,0.7,“随机”);numTrainImages = numel(imdsTrain.Labels);idx = randperm(numTrainImages,4);Img = imtile(imds,“帧”, idx);图imshow(img)标题(“来自训练集的随机图像”);

图中包含一个轴对象。标题为Random Images from Training Dataset的axes对象包含一个image类型的对象。

负荷预训练网络

加载预训练的SqueezeNet网络。

网= squeezenet;

的对象包含了DAGNetwork对象。第一层是图像输入层,接收大小为227 × 227 × 3的输入图像,其中3是颜色通道的数量。使用analyzeNetwork(深度学习工具箱)功能,用于显示网络体系结构的交互式可视化,检测网络中的错误和问题,并显示有关网络层的详细信息。层信息包括层激活和可学习参数的大小,可学习参数的总数,循环层状态参数的大小。

inputSize = net.Layers(1).InputSize;analyzeNetwork(净);

更换最终图层

网络的卷积层提取图像特征,最后的可学习层和最终的分类层使用这些特征对输入图像进行分类。这两层,“conv10”而且“ClassificationLayer_predictions”在SqueezeNet中,包含关于如何将网络提取到类概率、损失值和预测标签的特征结合起来的信息。

为了重新训练一个预先训练好的网络来分类新图像,用适应新数据集的新层替换这两个层。您可以手动或使用helper函数完成此操作findLayersToReplace自动找到这些层。

这是findLayersToReplace辅助功能:

类型findLayersToReplace.m
function [learnableLayer,classLayer] = findLayersToReplace(lgraph) % findLayersToReplace(lgraph)查找单层分类层和层%前面的可学习(全连接或卷积)层。if ~isa(lgraph,'nnet.cnn.LayerGraph')错误('参数必须是一个LayerGraph对象。')end %获取源、目标和层名。src = string(lgraph.Connections.Source);dst = string(lgraph.Connections.Destination);layerNames = string({lgraph.Layers.Name}');找到分类层。层图必须有一个单一的%分类层。isClassificationLayer = arrayfun(@(l)…(isa (l, nnet.cnn.layer.ClassificationOutputLayer) | isa (l ' nnet.layer.ClassificationLayer ')),…lgraph.Layers); if sum(isClassificationLayer) ~= 1 error('Layer graph must have a single classification layer.') end classLayer = lgraph.Layers(isClassificationLayer); % Traverse the layer graph in reverse starting from the classification % layer. If the network branches, throw an error. currentLayerIdx = find(isClassificationLayer); while true if numel(currentLayerIdx) ~= 1 error('Layer graph must have a single learnable layer preceding the classification layer.') end currentLayerType = class(lgraph.Layers(currentLayerIdx)); isLearnableLayer = ismember(currentLayerType, ... ['nnet.cnn.layer.FullyConnectedLayer','nnet.cnn.layer.Convolution2DLayer']); if isLearnableLayer learnableLayer = lgraph.Layers(currentLayerIdx); return end currentDstIdx = find(layerNames(currentLayerIdx) == dst); currentLayerIdx = find(src(currentDstIdx) == layerNames); %#ok end end

要使用这个函数来替换最后的层,运行这些命令:

lgraph = layerGraph(net);[learnableLayer,classLayer] = findLayersToReplace(lgraph);numClasses = numel(categories(imdsTrain.Labels));newConvLayer = convolution2dLayer([1,1],numClasses,“WeightLearnRateFactor”...10日,“BiasLearnRateFactor”10“名称”“new_conv”);lgraph =替换层(lgraph,“conv10”, newConvLayer);newclassificationlayer = classificationLayer(“名字”“new_classoutput”);lgraph =替换层(lgraph,“ClassificationLayer_predictions”, newClassificatonLayer);

列车网络的

网络要求所有输入图像的大小为227 × 227 × 3,但是图像数据存储中的每个图像都有不同的大小。使用增强图像数据存储来自动调整训练图像的大小。指定要在训练图像上执行的这些附加增强操作:随机翻转训练图像的垂直轴,并随机将其水平和垂直平移至30像素。数据增强有助于防止网络过度拟合和记忆训练图像的确切细节。

pixelRange = [-30 30];imageAugmenter = imageDataAugmenter(...“RandXReflection”,真的,...“RandXTranslation”pixelRange,...“RandYTranslation”, pixelRange);augimdsTrain = augmentedimagedastore (inputSize(1:2)),imdsTrain,...“DataAugmentation”, imageAugmenter);

若要自动调整验证图像的大小,而不执行进一步的数据增强,请使用增强图像数据存储,而不指定任何额外的预处理操作。

augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);

指定培训选项。对于迁移学习,保留预训练网络早期层的特征(转移层权重)。为了降低迁移层的学习速度,可以将初始学习率设置为较小的值。在前面的步骤中,您增加了卷积层的学习率因子,以加快新的最终层的学习速度。这种学习率设置的组合导致只在新层中快速学习,而在其他层中学习较慢。在执行迁移学习时,您不需要训练许多epoch。epoch是整个训练数据集上的一个完整的训练周期。将迷你批处理大小指定为11,以便在每个epoch中考虑所有数据。在训练过程中,软件会在每次训练后验证网络ValidationFrequency迭代。

选项= trainingOptions(“个”...“MiniBatchSize”11...“MaxEpochs”7...“InitialLearnRate”2的军医,...“洗牌”“every-epoch”...“ValidationData”augimdsValidation,...“ValidationFrequency”,3,...“详细”、假);

训练由转移层和新层组成的网络。

netTransfer = trainNetwork(augimdsTrain,lgraph,options);classNames = netTransfer.Layers(end).Classes;保存(“mySqueezenet.mat”“netTransfer”);

生成网络校准结果文件

创建一个dlquantizer对象并指定网络。

quantObj = dlquantizer(netTransfer,“ExecutionEnvironment”“CPU”);

使用校准使用样本输入练习网络并收集距离信息的功能。的校准函数对网络进行练习,并收集网络的卷积层和全连接层中权重和偏差的动态范围,以及网络各层中激活的动态范围。函数返回一个表。表的每一行都包含优化网络的一个可学习参数的范围信息。

calResults = quantObj.calibrate(augimdsTrain);
试图校准主机图形处理器错误的消息:无法找到支持的图形处理器设备。金宝app有关GPU支持的更多信息,请参见不同版本的GPU支持。金宝app恢复使用主机CPU。
保存(“squeezenetCalResults.mat”“calResults”);保存(“squeezenetQuantObj.mat”“quantObj”);

生成PIL MEX函数

在本例中,为入口点函数生成代码predict_int8.此函数使用coder.loadDeepLearningNetwork函数来加载深度学习模型,并构造和设置CNN类。然后用入口点函数预测响应预测(深度学习工具箱)函数。

类型predict_int8.m
function out = predict_int8(netFile, in)持久化mynet;if isempty(mynet) mynet = code . loaddeeplearningnetwork (netFile);End out = predict(mynet,in);结束

要生成PIL MEX函数,为静态库创建一个代码配置对象,并将验证模式设置为“公益诉讼”.将目标语言设置为c++。

CFG = code .config(“自由”“是”,真正的);cfg。VerificationMode =“公益诉讼”;cfg。TargetLang =“c++”

为ARM Compute库创建一个深度学习配置对象。指定库版本和arm架构。在这个例子中,假设树莓派硬件中的ARM计算库是20.02.1版本。

DLCFG =编码器。DeepLearningConfig (“arm-compute”);dlcfg。ArmComputeVersion =“20.02.1”;dlcfg。ArmArchitecture =v7的

设置的属性dlcfg用于生成低精度/INT8推断的代码。

dlcfg。CalibrationResultFile =“squeezenetQuantObj.mat”;dlcfg。数据类型=“int8”

6.设置DeepLearningConfig的属性cfgdlcfg

cfg。DeepLearningConfig = dlcfg;

7.使用树莓派函数的MATLAB支金宝app持包raspi来创建与树莓派的连接。在下面的代码中,替换:

  • raspiname树莓派的名字

  • 用户名用你的用户名

  • 密码用你的密码

% r = raspi('raspiname','username','password');

8.创建一个编码器。硬件对象,并将其附加到代码生成配置对象。

% hw =编码器。硬件(覆盆子π);% cfg。硬件= hw;

9.方法生成PIL MEX函数codegen命令

% codegen -config cfg predict_int8 -args {code . constant (' mysqueeezenet .mat'), ones(227,227,3,'uint8')}

在树莓派上运行generate PIL MEX Function

输入图像的大小期望与网络的输入大小相同。读取要分类的图像,并将其调整为网络的输入大小。这种调整会略微改变图像的纵横比。

% testImage = imread("MerchDataTest.jpg");% testImage = imresize(testImage,inputSize(1:2));

比较深度学习工具箱的预测结果预测函数和生成的PIL MEX函数predict_int8_pil,分别在输入图像上调用这两个函数。

% predictScores(:,1) = predict(netTransfer,testImage)';% predictScores(:,2) = predict_int8_pil('mySqueezenet.mat',testImage);

以直方图的形式显示预测的标签及其相关概率。

% h =数字;% h.位置(3)= 2*h.位置(3);% ax1 = subplot(1,2,1);% ax2 = subplot(1,2,2);%的形象(ax₁,testImage);% barh (ax2 predictScores)%包含(ax2,“概率”)% yticklabels (ax2一会)% ax2。XLim = [0 1.1];% ax2。YAxisLocation = 'left';% legend('Matlab Single','arm-compute 8位整数');% sgtitle(“使用Squeezenet进行预测”)% saveas (gcf SqueeznetPredictionComparison.jpg);%关闭(gcf);imshow (“SqueeznetPredictionComparison.jpg”);

图中包含一个轴对象。axis对象包含一个image类型的对象。

另请参阅

应用程序

功能

对象

相关的话题