主要内容

与定制训练循环并行的训练网络

这个例子展示了如何设置一个自定义训练循环来并行训练一个网络。在本例中,并行工作者在整个小批的部分上进行训练。如果你有一个GPU,那么训练就在GPU上进行。在培训期间,DataQueue对象将训练进度信息发送回MATLAB客户端。

负载数据集

加载数字数据集并为该数据集创建一个图像数据存储。以随机的方式将数据存储分为训练数据存储和测试数据存储。创建一个augmentedImageDatastore包含训练数据。

digitDatasetPath = fullfile(toolboxdir(“nnet”),“nndemos”“nndatasets”“DigitDataset”);imds = imageDatastore(digitDatasetPath,IncludeSubfolders = true,LabelSource =“foldernames”);[imdsTrain,imdsTest] = splitEachLabel(imds,0.9,“随机”);inputSize = [28 28 1];augimdsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain);

确定训练集中不同的类。

classes = categories(imdsTrain.Labels);numClasses = nummel(类);

定义网络

定义您的网络架构。该网络体系结构包括批归一化层,用于跟踪数据集的均值和方差统计信息。并行训练时,在每个迭代步骤结束时,结合所有worker的统计数据,以确保网络状态反映整个小批。否则,网络状态可能会在各个工作者之间发散。例如,如果你正在训练有状态循环神经网络(rnn),使用被分割成更小序列的序列数据来训练包含LSTM或GRU层的网络,你还必须管理工作者之间的状态。

layers = [imageInputLayer(inputSize,归一化=“没有”) convolution2dLayer(5,20) batchNormalizationLayer relullayer (3,20,Padding=1) batchNormalizationLayer reluLayer convolution2dLayer(3,20,Padding=1) batchNormalizationLayer reluledlayer fullyConnectedLayer(numClasses) softmaxLayer];

创建一个dlnetwork对象。dlnetwork对象允许使用自定义循环进行训练。

Net = dlnetwork(layers)
net = dlnetwork with properties: Layers: [12×1 nnet.cnn.layer.Layer] Connections: [11×2 table] Learnables: [14×3 table] State: [6×3 table] InputNames: {'imageinput'} OutputNames: {'softmax'} Initialized: 1使用summary查看summary。

设置并行环境

确定是否有图形处理器可供MATLAB使用canUseGPU函数。

  • 如果有可用的图形处理器,则在图形处理器上进行训练。创建一个具有和gpu一样多的worker的并行池。

  • 如果没有可用的gpu,则在cpu上进行训练。创建一个具有默认worker数量的并行池。

如果canUseGPU executionEnvironment =“图形”;numberofgpu = gpuDeviceCount(“可用”);pool = parpool(numberofgpu);其他的executionEnvironment =“cpu”;Pool = parpool;结束
使用'Processes'配置文件启动并行池(parpool)…连接到并行池(工人数量:4)。

获得并行池中工人的数量。在本例的后面,您将根据这个数字划分工作负载。

numWorkers = pool.NumWorkers;

火车模型

指定培训选项。

numEpochs = 20;miniBatchSize = 128;速度= [];

对于GPU训练,推荐的做法是随着GPU的数量线性地扩大迷你批处理的大小,以保持每个GPU上的工作负载不变。有关更多相关建议,请参见基于MATLAB的多gpu深度学习

如果executionEnvironment = =“图形”miniBatchSize = miniBatchSize .* numWorkers结束
miniBatchSize = 512

通过在每个工人中平均分配整个小批量大小来计算每个工人的小批量大小。将剩余的分配给第一批工人。

workerMiniBatchSize = floor(miniBatchSize ./ repmat(numWorkers,1,numWorkers));剩余= miniBatchSize - sum(workerMiniBatchSize);workerMiniBatchSize = workerMiniBatchSize + [ones(1,余数)0 (1,numworkers -余数)]
workerMiniBatchSize =1×4128 128 128 128 128

该网络包含批处理归一化层,用于跟踪网络所训练数据的平均值和方差。由于每个工作人员在每次迭代中处理每个小批的一部分,因此必须在所有工作人员中聚合平均值和方差。在网络状态属性中找到批归一化层的均值和方差状态参数的指标。

batchNormLayers = arrayfun(@(l)isa(l,“nnet.cnn.layer.BatchNormalizationLayer”), net.Layers);batchNormLayersNames = string({net.Layers(batchNormLayers).Name});state = net.State;isBatchNormalizationStateMean = ismember(state. layer,batchNormLayersNames) &状态。参数= =“TrainedMean”;isBatchNormalizationStateVariance = ismember(state. layer,batchNormLayersNames) &状态。参数= =“TrainedVariance”

初始化TrainingProgressMonitor对象。因为计时器在创建监视器对象时开始,所以请确保创建的对象接近训练循环。

monitor = trainingProgressMonitor(指标=“TrainingLoss”信息= [“时代”“工人”),包含=“迭代”);

创造一个Dataqueue当按下“停止对接”按钮时,要求工人发出停止培训的标志。

spmdstopTrainingEventQueue = parallel.pool.DataQueue;结束stopTrainingQueue = stopTrainingEventQueue{1};

为了在培训期间从工人那里发回数据,创建一个DataQueue对象。使用afterEach为了建立一个函数,displayTrainingProgress,在worker每次发送数据时调用。displayTrainingProgress在本例末尾万博1manbetx定义的支持函数是否显示更新TrainingProgressMonitor对象显示来自工人的培训进度信息,并在停止按钮被按下时向工人发送一个标志。

dataQueue = parallel.pool.DataQueue;displayFcn = @(x) displayTrainingProgress(x,numEpochs,numWorkers,monitor,stopTrainingQueue);afterEach (dataQueue displayFcn)

使用自定义并行训练循环训练模型,具体步骤如下。要在所有worker上同时执行代码,请使用spmd块。在spmd块,spmdIndex给出当前执行代码的工作线程的索引。

在培训之前,为每个worker分区数据存储分区函数。使用分区数据存储创建一个minibatchqueue每个工人身上。对于每个小批量:

  • 使用自定义小批量预处理功能preprocessMiniBatch(在本例结束时定义)来规范化数据,将标签转换为单热编码变量,并确定小批中的观察数。

  • 用尺寸标签格式化图像数据“SSCB”(空间,空间,通道,批次)。默认情况下,minibatchqueue对象将数据转换为dlarray具有基础类型的对象.不要向类标签或观察数添加格式。

  • 如果有GPU,可以在GPU上进行训练。默认情况下,minibatchqueue对象将每个输出转换为gpuArray如果GPU可用。使用GPU需要并行计算工具箱™和受支持的GPU设备。万博1manbetx有关受支持设备的信息,请参见万博1manbetxGPU计算要求(并行计算工具箱)(并行计算工具箱)。

方法对每个纪元重置和洗牌数据存储重置而且洗牌功能。对于纪元中的每个迭代:

  • 在开始并行处理数据之前,通过执行全局操作,确保所有工作者都有可用的数据而且操作使用spmdreduce关于结果的hasdata函数。

  • 中读取一个小批minibatchqueue通过使用下一个函数。

  • 通过调用计算每个worker上的网络损失和梯度dlfevalmodelLoss函数。的dlfeval函数求helper函数的值modelLoss启用了自动微分,所以modelLoss可以自动计算关于损失的梯度。modelLoss在示例的末尾定义,并返回给定网络、小批量数据和true标签的损失和梯度。

  • 为了得到总的损失,把所有工人的损失加起来。本例使用交叉熵作为损失函数,而聚合损失是所有损失的总和。在聚合之前,通过乘以工作人员正在处理的整个小批的比例,将每个损失归一化。使用spmdPlus将所有损失加在一起,并在所有员工中复制结果。

  • 要汇总和更新所有工作的梯度,请使用dlupdate函数与aggregateGradients函数。aggregateGradients是在本示例万博1manbetx末尾定义的支持函数。此函数使用spmdPlus根据每个工作人员正在处理的整个迷你批的比例进行标准化,在各个工作人员之间添加和复制梯度。

  • 方法聚合所有工作者的网络状态aggregateState函数。aggregateState是在本示例万博1manbetx末尾定义的支持函数。网络中的批归一化层跟踪数据的均值和方差。由于完整的迷你批分布在多个工作者之间,因此在每次迭代之后聚合网络状态以计算整个迷你批的平均值和方差。

  • 计算最终梯度后,使用sgdmupdate函数。

在每个纪元结束后,检查停止按钮,并将培训进度信息发送回客户端发送函数与Dataqueue对象。您只需要使用一个worker发送回数据,因为所有的worker都有相同的丢失信息。要确保数据位于CPU上,并且没有GPU的客户端机器可以访问它,请使用收集dlarray在发送给客户端之前。当工作者之间的通信发生在每个纪元之后时,单击停止在当前纪元结束时停止训练。如果你想要停止按钮在每次迭代结束时停止训练,可以检查是否停止按钮被按下,并以增加的通信开销为代价,在每次迭代中将训练进度信息发送回客户机。

spmd重置并洗牌数据存储。重置(augimdsTrain);augimdsTrain = shuffle(augimdsTrain);分区数据存储。workerImds = partition(augimdsTrain,numWorkers,spmdIndex);在每个worker上使用分区的数据存储创建minibatchqueueworkerMbq = minibatchqueue(workerImds,3,MiniBatchSize = workerMiniBatchSize (spmdIndex),MiniBatchFcn = @preprocessMiniBatch,MiniBatchFormat = [“SSCB”""""]);workerVelocity =速度;Epoch = 0;迭代= 0;stopRequest = false;epoch < numEpochs && ~stopRequest epoch = epoch + 1;洗牌(workerMbq);在小批上循环spmreduce (@and,hasdata(workerMbq)) && ~stopRequest迭代=迭代+ 1;读取一个小批数据[workerX,workerT,workerNumObservations] = next(workerMbq);评估模型损失和工人的梯度。[workerLoss,workerGradients,workerState] = dlfeval(@modelLoss,net,workerX,workerT);所有工人损失的总和workerNormalizationFactor = workerMiniBatchSize(spmdIndex)./miniBatchSize;损失= spmdPlus(workerNormalizationFactor*extractdata(workerLoss));聚合所有worker上的网络状态网状态= aggregateState(workerState,workerNormalizationFactor,isBatchNormalizationStateMean isBatchNormalizationStateVariance);聚合所有工人的梯度workerGradients。Value = dlupdate(@aggregateGradients,workerGradients.Value,{workerNormalizationFactor});使用SGDM优化器更新网络参数[net,workerVelocity] = sgdmupdate(net,workerGradients,workerVelocity);结束%停止训练,如果已点击停止按钮stopRequest = spmdPlus(stopTrainingEventQueue.QueueLength);向客户端发送培训进度信息。如果spmdIndex == 1 data = [epoch loss iteration];发送(dataQueue,收集(数据));结束结束结束

测试模型

训练网络之后,就可以测试它的准确性了。

使用将测试图像加载到内存中readall在测试数据存储上,将它们连接起来,并将它们规范化。

XTest = readall(imdsTest);XTest = cat(4,XTest{:});XTest = single(XTest) ./ 255;TTest = imdsTest.Labels;

培训结束后,所有员工都拥有同样完整的培训网络。检索其中任何一个。

netFinal = net{1};

对图像进行分类dlnetwork对象时,使用预测函数在dlarray

YTest =预测(netFinal,dlarray(XTest,“SSCB”));

从预测分数中,找出得分最高的班级马克斯函数。在此之前,从dlarrayextractdata函数。

[~,idx] = max(extractdata(YTest),[],1);YTest = classes(idx);

为了获得模型的分类精度,将测试集上的预测与真实标签进行比较。

精度=平均值(YTest==TTest)
准确度= 0.9440

迷你批量预处理功能

preprocessMiniBatch函数使用以下步骤预处理一小批预测器和标签:

  1. 确定小批中的观察数

  2. 对图像进行预处理preprocessMiniBatchPredictors函数。

  3. 从传入的单元格数组中提取标签数据,并沿着第二次维度连接到分类数组中。

  4. One-hot将分类标签编码为数字数组。编码到第一个维度会产生一个与网络输出形状匹配的编码数组。

函数[X,Y,numObs] = preprocessMiniBatch(XCell,YCell) numObs = num (YCell);预处理预测器。X = preprocessMiniBatchPredictors(XCell);从单元格和级联中提取标签数据。Y = cat(2,YCell{1:end});单热编码标签。Y = onehotencode(Y,1);结束

小批量预测预处理函数

preprocessMiniBatchPredictors函数通过从输入单元格数组中提取图像数据并将其连接到数值数组来预处理一小批预测器。对于灰度输入,在第四个维度上的连接将为每个图像添加第三个维度,以用作单个通道维度。然后对数据进行规范化。

函数X = preprocessMiniBatchPredictors(XCell)%连接。X = cat(4,XCell{1:end});%正常化。X = X ./ 255;结束

模型损失函数

定义一个函数,modelLoss,以计算损失相对于网络的可学习参数的梯度。这个函数计算一个小批处理的网络输出X向前在给定真实输出的情况下,用交叉熵计算损失。当你用dlfeval,启用自动区分,和dlgradient可以计算损失的梯度相对于可学习的自动。

函数[Y,state] = forward(net,X);损失=交叉熵(Y,T);gradients = dlgradient(loss,net.Learnables);结束

显示培训进度功能

定义一个函数来显示来自工人的培训进度信息,并检查是否停止按钮已被单击。如果停止按钮被点击,一个标志被发送给工人,表明培训应该停止。的DataQueue在本例中,每当worker发送数据时调用此函数。

函数displayTrainingProgress(data,numEpochs,numWorkers,monitor,stopTrainingQueue) epoch = data(1);损失=数据(2);迭代=数据(3);recordMetrics(监控、迭代TrainingLoss =损失);updateInfo(监视、时代=时代+“的”+ nummepochs, Workers= numWorkers);班长。进度= 100 * epoch/numEpochs;如果班长。停止发送(年代topTrainingQueue,true);结束结束

聚合梯度函数

定义一个函数,通过将所有worker的梯度相加来聚合它们。spmdPlus添加在一起,并复制所有的工人梯度。在将它们相加之前,通过将它们乘以一个表示工作人员正在处理的整个迷你批的比例的因子来规范化它们。类的内容dlarrayuseextractdata

函数gradients = aggregateGradients(gradients,factor) gradients = extractdata(gradients);梯度= spmdPlus(因子*梯度);结束

聚合状态函数

定义一个函数来聚合所有worker上的网络状态。网络状态包含训练过的数据集的批归一化统计信息。由于每个工作人员只看到小批处理的一部分,因此需要聚合网络状态,以便统计信息能够代表所有数据的统计信息。对于每个小批,合并的平均值被计算为每个迭代中工人的平均值的加权平均值。组合方差按下式计算:

年代 c 2 1 j 1 N j 年代 j 2 + x j - x c 2

在哪里 N 是工人总数, 为小批量观测的总数, j 是否处理了观测数据的数量 j th工人, x j 而且 年代 j 2 是否对该工人进行了均值和方差统计 x c 是所有工人的综合平均值。

函数状态= aggregateState(状态,因子,isBatchNormalizationStateMean,isBatchNormalizationStateVariance) stateMeans = state.Value(isBatchNormalizationStateMean);stateVariances = state.Value(isBatchNormalizationStateVariance);j = 1:numel(stateMeans) meanVal = stateMeans{j};varVal = stateVariances{j};计算组合平均值combinedMean = spmdPlus(factor*meanVal);计算组合方差项求和varTerm = factor。*(varVal + (meanVal - combinedMean).^2);%更新状态stateMeans{j} = combinedMean;stateVariances{j} = spmdPlus(varTerm);结束state.Value(isBatchNormalizationStateMean) = stateMeans;state.Value(isBatchNormalizationStateVariance) = stateVariances;结束

另请参阅

|||||||||

相关的话题