用泰勒分数修剪图像分类网络
这个例子展示了如何使用泰勒修剪减小深度神经网络的大小。通过使用taylorPrunableNetwork
函数去除卷积层滤波器,可以减小整个网络的大小,提高推理速度。
网络修剪是一个功能强大的模型压缩工具,它可以帮助识别可以删除的冗余,而对最终网络输出的影响很小。修剪在迁移学习中特别有用,在迁移学习中,网络经常被过度参数化。
本例使用在CIFAR-10数据集上训练的残差网络。有关更多信息,请参见训练残差网络用于图像分类.
加载预先训练的网络和数据
下载CIFAR-10数据集[1]downloadCIFARData
函数,作为支持文件附加到本示例中。要访问此文件,请将示例作为实时脚本打开。该数据集包含6万张图像。每张图片的尺寸是32x32,有三个颜色通道(RGB)。数据集的大小是175 MB。根据您的互联网连接,下载过程可能需要时间。
Datadir = tempdir;downloadCIFARData (datadir);
加载经过训练的网络进行修剪。
负载(“cifarnet - 20 - 16. -垫”,“trainedNet”);
将CIFAR-10训练和测试图像加载为4-D数组。训练集包含50,000张图像,测试集包含10,000张图像。将图像转换为anaugmentedImageDatastore
用于培训和验证。
[XTrain,TTrain,XTest,TTest] = loadCIFARData(datadir);inputSize = trainedNet.Layers(1).InputSize;augimdsTrain = augmentedImageDatastore(inputSize,XTrain,TTrain);augimdsTest = augmentedImageDatastore(inputSize,XTest,TTest);类=类别(TTest);
根据测试数据计算训练网络的准确性。
YTest =分类(trainedNet,augimdsTest);accuracyOfTrainedNet = mean(YTest == TTest)*100
accuracyOfTrainedNet = 90.2400
要实现自定义修剪循环,请将网络转换为dlnetwork
对象。
layerG = layerGraph(trainedNet);layerG = removeLayers(layerG,layerG. outputnames);net = dlnetwork(layerG);
分析网络。如网络图所示,转换去掉了网络的分类层。深度网络分析器显示网络中可学习参数的总数。
analyzeNetwork(净)
删除网络
使用t修剪网络aylorPrunableNetwork
函数。该网络基于泰勒展开[2][3]为网络中的每个卷积滤波器计算一个重要值。修剪是迭代的:每次循环运行,直到满足停止条件,该函数删除少量最不重要的卷积滤波器,并更新网络架构。
指定修剪和微调选项
设置修剪选项。
maxPruningIterations
设置要用于修剪过程的最大迭代次数maxToPrune
设置为修剪周期的每次迭代中要修剪的过滤器的最大数量
maxPruningIterations = 30;maxToPrune = 8;
设置微调选项。
learnRate = 1e-2;动量= 0.9;miniBatchSize = 256;numMinibatchUpdates = 50;validationFrequency = 1;
使用自定义修剪循环的修剪网络
从原始网络创建一个Taylor prable网络。
prunableNet = taylorPrunableNetwork(net);maxPrunableFilters = prunableNet.NumPrunables;
创建一个minibatchqueue
对象,该对象在训练期间处理和管理小批量图像。对于每个小批次:
使用自定义的小批量预处理功能
preprocessMiniBatch
(在本例末尾定义)将标签转换为单热编码变量。用尺寸标签格式化图像数据
“SSCB”
(空间,空间,渠道,批处理)。默认情况下,minibatchqueue
对象将数据转换为dlarray
具有基础类型的对象单
.不要向类标签添加格式。如果有GPU,请使用GPU进行训练。默认情况下,
minibatchqueue
对象将每个输出转换为gpuArray
如果有可用的GPU。使用GPU需要并行计算工具箱™和支持的GPU设备。有关支持的设备的信息,请参见GPU计算要求(并行计算工具箱).
mbqTrain = minibatchqueue(augimdsTrain,...MiniBatchSize = MiniBatchSize,...MiniBatchFcn = @preprocessMiniBatchTraining...OutputAsDlarray = [1 1],...OutputEnvironment = [“汽车”,“汽车”),...PartialMiniBatch =“回归”,...MiniBatchFormat = [“SSCB”,""]);mbqTest = minibatchqueue(augimdtest);...MiniBatchSize = MiniBatchSize,...MiniBatchFcn = @preprocessMiniBatchTraining...OutputAsDlarray = [1 1],...OutputEnvironment = [“汽车”,“汽车”),...PartialMiniBatch =“回归”,...MiniBatchFormat = [“SSCB”,""]);
初始化训练进度图。
图(“位置”,[10,10,700,700]) tl = tiledlayout(3,1);lossAx = nexttile;lineLossFinetune = animatedline(Color=[0.85 0.325 0.098]);Ylim ([0 inf]) xlabel(“微调”迭代) ylabel (“损失”网格)在标题(“修剪过程中的小批量丢失”) xTickPos = [];accuracyAx = nexttile;lineAccuracyPruning = animatedline(Color=[0.098 0.325 0.85],LineWidth=2,Marker=“o”);Ylim ([50 100]) xlabel(“修剪迭代”) ylabel (“准确性”网格)在addpoints (lineAccuracyPruning 0 accuracyOfTrainedNet)标题(“修剪后的验证精度”) numPrunablesAx = nexttile;lineNumPrunables = animatedline(Color=[0.4660 0.6740 0.1880],LineWidth=2,Marker=“^”);Ylim ([200 700]) xlabel(“修剪迭代”) ylabel (“Prunable过滤器”网格)在addpoints (lineNumPrunables 0双(maxPrunableFilters))标题(“修剪后可prable卷积滤波器个数”)
通过反复微调网络和删除低评分过滤器来修剪网络。
对于每个修剪迭代。使用以下步骤:
的卷积滤波器的微调网络和累积泰勒分数
numMinibatchUpdates
修剪网络使用
updatePrunables
删除功能maxToPrune
卷积滤波器的个数计算验证精度
要对网络进行微调,可以遍历训练数据的小批量。对于微调迭代中的每个小批,使用以下步骤:
评估修剪损失、修剪激活的梯度、修剪激活、模型梯度和状态
dlfeval
而且modelLossPruning
功能。更新网络状态。
更新网络参数
sgdmupdate
函数。更新prable网络的Taylor分数
updateScore
函数。显示培训进度。
开始= tic;迭代= 0;为pruningIteration = 1:maxPruningIterations% Shuffle数据。洗牌(mbqTrain);在每次修剪中重置SGDM求解器的速度参数%的迭代。速度= [];在小批量上循环。fineTuningIteration = 0;而hasdata(mbqTrain)迭代=迭代+ 1;fineTuningIteration = fineTuningIteration + 1;读取小批数据。[X, T] = next(mbqTrain);评估修剪激活,修剪的梯度%激活,模型梯度,状态和损失使用dlfeval和% modelLossPruning函数。[loss,pruningActivations, pruningGradients, netGradients, state] =...(@modelLossPruning, prunableNet, X, T);更新网络状态。prunableNet。国家=国家;使用SGDM优化器更新网络参数。[prunableNet, velocity] = sgdmupdate(prunableNet, netGradients, velocity, learnRate,动量);计算一阶泰勒分数并累积分数。%以前的小批量数据。prunableNet = updateScore(prunableNet, pruningactivation, pruningGradients);显示培训进度。D = duration(0,0,toc(start),格式=“hh: mm: ss”);addpoints(lineLossFinetune, iteration, double(loss))“处理修剪迭代:”+ pruningIteration +"的"maxPruningIterations +...,运行时间:"+字符串(D))同步精度和numPrunables图的x轴与损失图。xlim(numPrunablesAx,lossAx.XLim)绘制现在到达numMinibatchUpdates时停止微调循环。如果(fineTuningIteration > numMinibatchUpdates)打破结束结束%修剪过滤器基于以前计算的泰勒分数。prunableNet = updatePrunables(prunableNet, MaxToPrune = MaxToPrune);在修剪迭代的子集中显示验证数据集上的结果。isLastPruningIteration = pruningIteration == maxPruningIterations;如果(mod(pruningIteration, validationFrequency) == 0 || isLastPruningIteration) accuracy = modelAccuracy(prunableNet, mbqTest, classes, augimdste . numobservations);添加点(lineNumPrunables,iteration,double(prunableNet.NumPrunables))结束在每次修剪迭代结束时设置x轴刻度值。xTickPos = [xTickPos,迭代];% #好< AGROW >xticks(lossAx,xTickPos) xticks(accuracyAx,[0,xTickPos]) xticks(numPrunablesAx,[0,xTickPos]) xticklabels(accuracyAx,[“Unpruned”字符串(1:pruningIteration)]) xticklabels (numPrunablesAx, (“Unpruned”, (1: pruningIteration)]) drawnow字符串结束
与典型训练中损耗随迭代次数的增加而减少不同,在对卷积滤波器进行修剪时,由于网络结构的变化,会增加损耗,降低验证精度。为了进一步提高网络的准确性,可以对网络进行再训练。
修剪完成后,将taylorPrunableNetwork
回到dlnetwork
再培训。
prunedNet = dlnetwork(prunableNet);
修剪后再培训网络
修剪后重新训练网络,以恢复精度上的任何损失。再训练网络使用trainNetwork
函数,将修剪后的网络从dlnetwork转换为alayerGraph
.您还可以使用自定义训练循环来训练网络。有关更多信息,请参见使用自定义训练循环训练网络.
提取
layerGraph
从dlnetwork
.将从原始网络中删除的分类层添加到
layerGraph
修剪过的网络。培训
layerGraph
网络。
prunedLayerGraph = layerGraph(prunedNet);outputLayerName = string(trainedNet.OutputNames{1});outputLayerIdx = {trainedNet.Layers。Name} == outputLayerName;prunedLayerGraph = addLayers(prunedLayerGraph,trainedNet.Layers(outputLayerIdx));prunedLayerGraph = connectLayers(prunedLayerGraph,prunedNet.OutputNames{1},outputLayerName);
将选项设置为带有动量的随机梯度下降的默认设置。将最大再训练时间设置为10,并以初始学习率0.01开始训练。
options = trainingOptions(“个”,...MaxEpochs = 10,...MiniBatchSize = 256,...InitialLearnRate = 1e-2,...LearnRateSchedule =“分段”,...learnratdropfactor = 0.1,...learnratdropperiod = 2,...L2Regularization = 0.02,...验证数据= augimdtest,...ValidationFrequency = 200,...Verbose = false,...洗牌=“every-epoch”,...情节=“训练进步”);
训练网络。
prunedDAGNet = trainNetwork(augimdsTrain,prunedLayerGraph,options);
比较原始网络和修剪网络
确定修剪对每个层的影响。
[originalNetFilters,layerNames] = numConvLayerFilters(trainedNet);prunedNetFilters = numConvLayerFilters(prunedDAGNet);
可视化原始网络和修剪网络中的过滤器数量。
图(“位置”,[10,900,900]) bar([originalNetFilters,prunedNetFilters]) xlabel(“层”) ylabel (“过滤器数量”)标题(“每层滤镜数量”) xticks(1:(numel(layerNames))) xticklabels(layerNames) xtickangle(90) ax = gca;斧子。TickLabelInterpreter =“没有”;传奇(“原始网络过滤器”,“修剪网络过滤器”,“位置”,“southoutside”)
两个网络中过滤器数量的巨大差异表明许多不太重要的过滤器在哪里被修剪了。
接下来,比较原始网络和修剪后网络的准确性。
YPredOriginal =分类(trainedNet,augimdsTest);accuOriginal = mean(YPredOriginal == TTest)
accuOriginal = 0.9024
YPredPruned =分类(prunedDAGNet,augimdsTest);accuPruned = mean(YPredPruned == TTest)
accuPruned = 0.8736
剪枝会对不同类的分类产生不一样的影响,并给模型引入偏差,从精度值上看可能并不明显。要在类级别评估修剪的影响,可以使用混淆矩阵图。
图混淆图(TTest,YPredOriginal,Normalization = .“row-normalized”);标题(“原始网络”)
figure混淆图(TTest,YPredPruned,Normalization = .“row-normalized”);标题(“删除网络”)
接下来,估计原始网络和剪枝网络的模型参数,了解剪枝对整个网络学习能力和大小的影响。
analyzeNetworkMetrics (trainedNet prunedDAGNet、accuOriginal accuPruned)
ans =3×3表网络学习品。网络内存(MB)准确性 __________________ ___________________________ ________ 原始网络2.7169 e + 05年1.0364 - 0.9024修剪网络1.2128 e + 05年0.46266 0.8736 -55.36 -55.36 -3.1915百分比变化
该表比较了原始网络和修剪后网络的大小和分类精度。网络内存的减少和相似的精度值表明一个良好的修剪操作。有关显示如何使用量化进一步减小网络规模以进行部署的示例,请参见量化残差网络训练的图像分类和生成CUDA代码.
辅助函数
评估模型精度
的modelAccuracy
函数将网络作为输入(dlnetwork
),minibatchque
对象、类和观察数,并返回精度。
函数accuracy = modelAccuracy(net, mbq, classes, numObservations)这个函数在小批处理'mbq'上计算网络(dlnetwork)的模型精度。totalCorrect = 0;Classes = int32(categories (Classes));重置(兆贝可);而hasdata(mbq) [dlX, Y] = next(mbq);dlYPred = extractdata(predict(net, dlX));YPred = onehotdecode(dlYPred,classes,1)';YReal = onehotdecode(Y,classes,1)';miniBatchCorrect = nnz(YPred == YReal);totalCorrect = totalCorrect + miniBatchCorrect;结束accuracy = totalCorrect / numObservations * 100;结束
模型梯度函数的微调和修剪
的modelLossPruning
函数以a作为输入deep.prune.TaylorPrunableNetwork
对象prunableNet
,一个小批量的输入数据X
对应的标签T,并返回损失,损失相对于修剪激活的梯度,修剪激活,损失相对于可学习参数的梯度prunableNet
还有网络状态。方法可自动计算梯度dlgradient
函数。
函数[loss,pruningGradient, pruningactivation,netGradients,state] = modelLossPruning(prunableNet, X, T) [dlYPred,state, pruningactivation] = forward(prunableNet,X);损失= crossentropy(dlYPred,T);[pruningGradient,netGradients] = dlgradient(loss, pruningactivation,prunableNet.Learnables);结束
小批量预处理功能
的preprocessMiniBatchTraining
函数预处理一小批预测器和标签,以便在训练期间进行损失计算。
函数[X,T] = preprocessMiniBatchTraining(XCell,TCell)%连接。X = cat(4,XCell{1:end});从单元格中提取标签数据并连接。T = cat(2,TCell{1:end});单热编码标签。T = onehotencode(T,1);结束
计算卷积层中滤波器的数量
的numConvLayerFilters
函数返回每个卷积层中的过滤器数量。
函数[nFilters, convNames] = numConvLayerFilters(net) numLayers = numel(net. layers);convNames = [];nFilters = [];检查卷积层并提取过滤器的数量。为cnt = 1:numLayers如果isa (net.Layers(问),“nnet.cnn.layer.Convolution2DLayer”) sizeW = size(net.Layers(cnt).Weights);nFilters = [nFilters;sizeW(结束)];convNames = [convNames;]字符串(net.Layers(问). name)];结束结束结束
评估原始网络和修剪网络的网络统计数据
的analyzeNetworkMetrics
函数将输入分别作为原始网络、裁剪网络、原始网络的精度和裁剪网络的精度,以表格的形式返回测试数据上的网络学习量、网络内存和精度等不同的统计量。
函数[statistics] = analyzeNetworkMetrics(originalNet,prunedNet,accuracyOriginal,accuracyPruned) originalNetMetrics = estimateNetworkMetrics(originalNet);prunedNetMetrics = estimateNetworkMetrics(prunedNet);原始网络和修剪网络的准确率perChangeAccu = 100*(accuracyPruned - accuracyOriginal)/accuracyOriginal;AccuracyForNetworks = [accuracyOriginal;accuracyPruned;perChangeAccu];%两个网络的总可学习内容originalNetLearnables = sum(1:end,“NumberOfLearnables”) .NumberOfLearnables);prunedNetLearnables = sum(1:end,“NumberOfLearnables”) .NumberOfLearnables);percentageChangeLearnables = 100*(prunedNetLearnables - originalNetLearnables)/originalNetLearnables;LearnablesForNetwork = [originalNetLearnables;prunedNetLearnables;percentageChangeLearnables];近似参数内存approxOriginalMemory = sum(originalNetMetrics(1:end,“ParameterMemory (MB)”).(“ParameterMemory (MB)”));approxPrunedMemory = sum(1:end,“ParameterMemory (MB)”).(“ParameterMemory (MB)”));percentagechangmemory = 100*(approxPrunedMemory - approxOriginalMemory)/approxOriginalMemory;NetworkMemory = [approxOriginalMemory;]approxPrunedMemory;percentageChangeMemory];创建汇总表统计=表(LearnablesForNetwork,NetworkMemory,AccuracyForNetworks,...“VariableNames”, (“网络可学的”,“大约。网络内存(MB)",“准确性”),...“RowNames”, {“原始网络”,“删除网络”,的百分比变化});结束
参考文献
亚历克斯·克里哲夫斯基,2009年。“从微小的图像中学习多层特征。”https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf.
Molchanov, Pavlo, Stephen Tyree, Tero Karras, Timo Aila和Jan kaautz。修剪卷积神经网络的资源高效推理。预印本,2017年6月8日提交。https://arxiv.org/abs/1611.06440。
[3]莫尔查诺夫,帕夫洛,阿伦·马尔雅,斯蒂芬·泰瑞,Iuri Frosio和简·考茨。神经网络修剪的重要性估计。2019年IEEE/CVF计算机视觉和模式识别(CVPR)会议,11256-64。美国加利福尼亚州长滩:IEEE, 2019。https://doi.org/10.1109/CVPR.2019.01152。