主要内容

在GPU上并行运行自定义训练循环

您可以通过在GPU上运行、并行使用多个GPU或在集群上运行来加速自定义训练循环。

建议使用一个或多个图形处理器进行训练。如果没有GPU,只能使用单个CPU或多个CPU。在训练和推断方面,cpu通常比gpu慢得多。运行在单个GPU上通常比运行在多个CPU内核上提供更好的性能。

请注意

本主题向您展示如何在gpu、并行和云上执行自定义训练。来了解并行工作流和GPU工作流trainNetwork功能,请参见:

使用GPU或并行选项需要并行计算工具箱™。使用GPU还需要支持的GPU设备。金宝app有关受支持设备的信息,请参见金宝appGPU支金宝app持版本(并行计算工具箱).使用远程集群也需要MATLAB®并行服务器™

GPU网络训练

默认情况下,自定义训练循环在CPU上运行。自动区分使用dlgradient而且dlfeval金宝app当数据在GPU上时,支持在GPU上运行。要在GPU上运行自定义训练循环,只需将数据转换为gpuArray(并行计算工具箱)在培训。

你可以使用minibatchqueue在培训期间管理您的数据。minibatchqueue自动为训练准备数据,包括自定义预处理和将数据转换为dlarray而且gpuArray.默认情况下,minibatchqueue返回GPU上所有的迷你批处理变量(如果有的话)。属性可以选择在GPU上返回哪些变量OutputEnvironment财产。

作为一个例子,展示如何使用minibatchqueue在GPU上进行训练,参见使用自定义训练循环训练网络

或者,您可以手动将数据转换为gpuArray在训练循环中。

要方便地指定执行环境,请创建变量executionEnvironment它包含了“cpu”“图形”,或“汽车”

executionEnvironment =“汽车”

在训练期间,读取一个小批处理后,检查执行环境选项并将数据转换为gpuArray如果有必要的话)。的canUseGPU函数检查可用的gpu。

如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlX = gpuArray(dlX);结束

并行训练单一网络

当你并行训练时,每个worker使用一个迷你批处理的一部分同时训练网络。这意味着您必须根据每个工人处理的小批的比例,在每次迭代之后组合梯度、损失和任何状态参数。

您可以在本地机器上并行训练,也可以在远程集群上并行训练,例如,在云中。在所需的资源中启动一个并行池,并在worker之间划分数据。在训练期间,在每次迭代后结合梯度、损失和状态,以便每个worker上的可学习参数同步更新。有关显示如何并行执行自定义训练的示例,请参见与定制训练循环并行的训练网络

设置并行环境

建议使用一个或多个图形处理器进行训练。如果没有GPU,只能使用单个CPU或多个CPU。在训练和推断方面,cpu通常比gpu慢得多。运行在单个GPU上通常比运行在多个CPU内核上提供更好的性能。

在训练之前设置要使用的并行环境。使用所需的资源启动并行池。对于使用多个gpu进行培训,启动一个并行池,其中包含尽可能多的可用gpu。为了获得最佳性能,MATLAB自动为每个worker分配不同的GPU。

如果您正在使用本地机器,则可以使用canUseGPU而且gpuDeviceCount(并行计算工具箱)以确定是否有可用的图形处理器。例如,要检查gpu的可用性,并启动一个具有与可用gpu一样多的worker的并行池,使用以下代码:

如果canUseGPU executionEnvironment =“图形”;numberofgpu = gpuDeviceCount(“可用”);pool = parpool(numberofgpu);其他的executionEnvironment =“cpu”;Pool = parpool;结束

如果您正在使用一个远程集群(例如,云中的一个集群)运行,则启动一个并行池,其中的工作人员数量等于每台机器的gpu数量乘以机器数量。

有关选择特定gpu的详细信息,请参见选择特定gpu用于培训

指定小批量大小和分区数据

指定在培训期间要使用的小批大小。对于GPU训练,推荐的做法是随着GPU的数量线性地扩大迷你批处理的大小,以保持每个GPU上的工作负载不变。例如,如果您正在单个GPU上使用64个迷你批处理大小进行训练,并且希望扩展到使用四个相同类型的GPU进行训练,则可以将迷你批处理大小增加到256,以便每个GPU每次迭代处理64个观测值。

您可以使用下面的代码按工作人员的数量来扩大小批大小,其中N是并行池中工人的数量。

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

如果您想要使用一个不能被并行池中的worker数量整除的迷你批处理大小,那么请将剩余的批处理分配给各个worker。

workerMiniBatchSize = floor(miniBatchSize ./ repmat(N,1,N));剩余= miniBatchSize - sum(workerMiniBatchSize);workerMiniBatchSize = workerMiniBatchSize + [ones(1,余数)0 (1,n -余数)]

在训练开始时,洗牌你的数据。对数据进行分区,以便每个工作人员都可以访问迷你批处理的一部分。要对数据存储进行分区,请使用分区函数。

你可以使用minibatchqueue管理培训期间每个员工的数据。minibatchqueue自动为训练准备数据,包括自定义预处理和将数据转换为dlarray而且gpuArray.使用分区数据存储在每个worker上创建一个minibatchqueue。设置MiniBatchSize属性使用为每个工人计算的小批大小。

在每次训练迭代的开始,使用共和党(并行计算工具箱)函数检查所有workerminibatchqueue对象可以返回数据。如果任何工作人员耗尽了数据,培训就会停止。如果您的整体小批大小不能被工人的数量整除,并且您没有丢弃部分小批,那么一些工人可能会在其他工人之前耗尽数据。

把你的训练代码写在spmd(并行计算工具箱)块,以便在每个工人上执行训练循环。

spmd重置并洗牌数据存储。重置(augimdsTrain);augimdsTrain = shuffle(augimdsTrain);分区数据存储。workerImds =分区(augimdsTrain,N,labindex);在每个worker上使用分区的数据存储创建minibatchqueueworkerMbq = minibatchqueue(workerImds,...“MiniBatchSize”workerMiniBatchSize (labindex),...“MiniBatchFcn”, @preprocessMiniBatch);...epoch = 1:numEpochs在每个worker上重置和洗牌minibatchqueue。洗牌(workerMbq);在小批上循环。共和党(@and hasdata (workerMbq))%自定义培训循环...结束...结束结束

总梯度

为了确保每个worker上的网络从所有数据中学习,而不仅仅是从该worker上的数据中学习,可以聚合梯度,并使用聚合的梯度来更新每个worker上的网络。

例如,假设您正在训练网络dlnet,使用模型梯度函数modelGradients.你的训练循环包含以下代码,用于评估每个工人的梯度、损失和统计数据:

[workerGradients,dlworkerLoss,workerState] = dlfeval(@modelGradients,dlnet,dlworkerX,workerY);
dlworkerX而且workerY分别是每个工人的预测值和真实反应。

要聚合梯度,请使用加权和。定义一个辅助函数来对梯度求和。

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

在训练循环中,使用dlupdate将函数应用于每个可学习参数的梯度。

workerGradients。Value = dlupdate(@aggregateGradients,workerGradients.Value,{workerNormalizationFactor});

总损耗和准确度

例如,为了找到网络损失和准确度,在训练期间绘制它们以监控训练进度,将所有工人的损失和准确度的值汇总在一起。通常,聚合值是每个工人的值之和,并根据每个工人使用的小批的比例进行加权。为了汇总每次迭代的损失和精度,计算每个工人和使用的权重因子gplus(并行计算工具箱)把每个工人的价值加起来。

workerNormalizationFactor = workerMiniBatchSize(labindex)./miniBatchSize;损失= gplus(workerNormalizationFactor*extractdata(dlworkerLoss));精度= gplus(workerNormalizationFactor*extractdata(dlworkerAccuracy));

总统计

如果您的网络包含跟踪训练数据统计信息的层,例如批处理归一化层,那么您必须在每次训练迭代之后汇总所有worker的统计信息。这样做可以确保网络学习到代表整个训练集的统计数据。

您可以在训练之前识别包含统计信息的层。例如,如果您正在使用dlnetwork使用批处理规范化层,可以使用以下代码查找相关层。

batchNormLayers = arrayfun(@(l)isa(l,“nnet.cnn.layer.BatchNormalizationLayer”), dlnet.Layers);batchNormLayersNames = string({dlnet.Layers(batchNormLayers).Name});state = dlnet.State;isBatchNormalizationStateMean = ismember(state. layer,batchNormLayersNames) &状态。参数= =“TrainedMean”;isBatchNormalizationStateVariance = ismember(state. layer,batchNormLayersNames) &状态。参数= =“TrainedVariance”
定义一个辅助函数来聚合您正在使用的统计数据。批处理归一化层跟踪输入数据的平均值和方差。你可以用加权平均数把所有工人的平均数加起来。计算汇总方差 年代 c 2 ,用下式的公式。

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

N是工人总数,为小批量观测的总数,j是否处理了观测数据的数量jth工人, 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 = gplus(factor*meanVal);计算组合方差项求和varTerm = factor。*(varVal + (meanVal - combinedMean).^2);%更新状态stateMeans{j} = combinedMean;stateVariances{j} = gplus(varTerm);结束state.Value(isBatchNormalizationStateMean) = stateMeans;state.Value(isBatchNormalizationStateVariance) = stateVariances;结束

在训练循环中,使用helper函数用组合的均值和方差更新批处理归一化层的状态。

dlnet。状态= aggregateState(workerState,workerNormalizationFactor,...isBatchNormalizationStateMean isBatchNormalizationStateVariance);

训练期间的情节结果

如果希望在培训期间绘制结果,可以使用类将工人数据发送到客户机DataQueue对象。

为了方便地指定绘图应该是打开还是关闭,请创建变量情节它包含了“训练进步”“没有”

情节=“训练进步”

在培训之前,初始化DataQueue和动画线使用animatedline函数。

如果情节= =“训练进步”图lineLossTrain = animatedline(“颜色”,[0.85 0.325 0.098]);Ylim ([0 inf]) xlabel(“迭代”) ylabel (“损失”网格)结束
创建DataQueue对象。使用afterEach调用helper函数displayTrainingProgress每次数据都从worker发送到客户端。
Q = parallel.pool.DataQueue;displayFcn = @(x) displayTrainingProgress(x,lineLossTrain);afterEach (Q, displayFcn);
displayTrainingProgressHelper函数包含用于向动画线添加点和显示训练周期和持续时间的代码。
函数displayTrainingProgress (data,line) addpoints(line,double(data(3)),double(data(2))) D = duration(0,0,data(4),“格式”“hh: mm: ss”);标题(”时代:“+数据(1,消失:"+字符串(D))现在绘制结束

在训练循环中,在每个纪元结束时,使用DataQueue将工人的培训数据发送给客户端。在每次迭代结束时,每个worker的聚合损失是相同的,因此您可以从单个worker发送数据。

显示培训进度信息。如果Labindex == 1 data = [epoch loss iteration toc(start)];发送(Q,收集(数据));结束

并行训练多个网络

要并行地训练多个网络,请在所需的资源和使用中启动一个并行池parfor(并行计算工具箱)对每个工人进行单一网络培训。

您可以在本地运行,也可以使用远程集群。使用远程集群需要MATLAB并行服务器.有关管理集群资源的详细信息,请参见发现集群并使用集群概要文件(并行计算工具箱).如果您有多个gpu,并且想要排除一些训练,您可以选择用于训练的gpu。有关选择特定gpu的详细信息,请参见选择特定gpu用于培训

您可以修改每个worker上的网络或训练参数,以并行执行参数扫描。例如,在网络的数组。dlnetwork对象,您可以使用以下形式的代码使用相同的数据训练多个不同的网络。

parpool (“本地”, numNetworks);parforidx = 1:numNetworks迭代= 0;速度= [];为每个员工分配一个网络Dlnet =网络(idx)%遍历epoch。epoch = 1:numEpochs% Shuffle数据。洗牌(兆贝可);在小批上循环。Hasdata (mbq)迭代=迭代+ 1;%自定义培训循环...结束结束将训练好的网络发送回客户端。。trainedNetworks{idx} = dlnet;结束
parfor完成后,trainedNetworks包含由工作人员训练的结果网络。

训练期间的情节结果

要监控工人的培训进度,您可以使用DataQueue从工人那里发回数据。

为了方便地指定绘图应该是打开还是关闭,请创建变量情节它包含了“训练进步”“没有”

情节=“训练进步”

在培训之前,初始化DataQueue动画线条使用animatedline函数。为你正在训练的每个网络创建一个子图。

如果情节= =“训练进步”F =数字;f.可见=真实;1:numNetworks subplot(numNetworks,1,i) xlabel(“迭代”);ylabel (“损失”);Lines (i) = animatedline;结束结束
创建DataQueue对象。使用afterEach调用helper函数displayTrainingProgress每次数据都从worker发送到客户端。
Q = parallel.pool.DataQueue;displayFcn = @(x) displayTrainingProgress(x,lines);afterEach (Q, displayFcn);
displayTrainingProgressHelper函数包含用于向动画线添加点的代码。
函数displayTrainingProgress (data,lines) addpoints(lines(1),double(data(4)),double(data(3))) D = duration(0,0,data(5),“格式”“hh: mm: ss”);标题(”时代:“+数据(2,消失:"+字符串(D))现在绘制limitratenocallbacks结束

在训练循环中,在每次迭代结束时,使用DataQueue将工人的培训数据发送给客户端。发送parfor循环索引以及培训信息,以便将点添加到每个工人的正确线上。

显示培训进度信息。Data = [idx epoch loss iteration toc(start)];发送(Q,收集(数据));

使用实验管理器并行训练

您可以使用实验管理器并行运行您的自定义训练循环。您可以同时运行多个试验,也可以使用并行资源一次运行单个试验。

要同时运行多个试验,每次试验使用一个并行工作器,请设置您的自定义训练实验并启用使用并行在进行实验前选择。

若要使用多个并行工作线程一次运行单个试验,请在实验训练函数中定义并行环境并使用spmd块来并行训练网络。有关与自定义训练循环并行训练单个网络的详细信息,请参见并行训练单一网络

有关使用实验管理器并行训练的更多信息,请参见使用实验管理器并行训练

另请参阅

(并行计算工具箱)|(并行计算工具箱)|(并行计算工具箱)||

相关的话题