Обучите сеть параллельно с пользовательским учебным циклом

В этом примере показано, как настроить пользовательский учебный цикл, чтобы обучить сеть параллельно. В этом примере параллельные рабочие обучаются на фрагментах полного мини-пакета. Если у вас есть графический процессор, то обучение происходит на графическом процессоре. Во время обучения, DataQueue объект передает информацию о процессе обучения обратно клиенту MATLAB.

Загрузите набор данных

Загрузите набор данных цифры и создайте datastore изображений для набора данных. Разделите datastore в обучение и протестируйте хранилища данных рандомизированным способом.

digitDatasetPath = fullfile(matlabroot,'toolbox','nnet','nndemos', ...
    'nndatasets','DigitDataset');
imds = imageDatastore(digitDatasetPath, ...
    'IncludeSubfolders',true, ...
    'LabelSource','foldernames');

[imdsTrain,imdsTest] = splitEachLabel(imds,0.9,"randomized");

Определите различные классы в наборе обучающих данных.

classes = categories(imdsTrain.Labels);
numClasses = numel(classes);

Сеть Define

Задайте свою сетевую архитектуру и превратите ее в график слоев при помощи layerGraph функция. Эта сетевая архитектура включает слои нормализации партии., которые отслеживают среднее значение и статистику отклонения набора данных. Когда обучение параллельно, объедините статистику от всех рабочих в конце каждого шага итерации, чтобы гарантировать, что сетевое состояние отражает целый мини-пакет. В противном случае сетевое состояние может отличаться через рабочих. Если вы - учебные рекуррентные нейронные сети с сохранением информации (RNNs), например, с помощью данных о последовательности, которые были разделены в меньшие последовательности, чтобы обучить нейронные сети содержащий LSTM или слои ГРУ, необходимо также управлять состоянием между рабочими.

layers = [
    imageInputLayer([28 28 1],'Name','input','Normalization','none')
    convolution2dLayer(5,20,'Name','conv1')
    batchNormalizationLayer('Name','bn1')
    reluLayer('Name','relu1')
    convolution2dLayer(3,20,'Padding',1,'Name','conv2')
    batchNormalizationLayer('Name','bn2')
    reluLayer('Name','relu2')
    convolution2dLayer(3,20,'Padding',1,'Name','conv3')
    batchNormalizationLayer('Name','bn3')
    reluLayer('Name','relu3')
    fullyConnectedLayer(numClasses,'Name','fc')];

lgraph = layerGraph(layers);

Создайте dlnetwork объект от графика слоев. dlnetwork объекты допускают обучение с пользовательскими циклами.

dlnet = dlnetwork(lgraph)
dlnet = 
  dlnetwork with properties:

         Layers: [11×1 nnet.cnn.layer.Layer]
    Connections: [10×2 table]
     Learnables: [14×3 table]
          State: [6×3 table]
     InputNames: {'input'}
    OutputNames: {'fc'}
    Initialized: 1

Настройте параллельную среду

Определите, доступны ли графические процессоры для MATLAB, чтобы использовать с canUseGPU функция.

  • Если существуют доступные графические процессоры, то обучаются на графических процессорах. Создайте параллельный пул со столькими же рабочих сколько графические процессоры.

  • Если нет никаких доступных графических процессоров, то не обучаются на центральных процессорах. Создайте параллельный пул с количеством по умолчанию рабочих.

if canUseGPU
    executionEnvironment = "gpu";
    numberOfGPUs = gpuDeviceCount("available");
    pool = parpool(numberOfGPUs);
else
    executionEnvironment = "cpu";
    pool = parpool;
end

Получите количество рабочих в параллельном пуле. Позже в этом примере, вы делите рабочую нагрузку согласно этому номеру.

N = pool.NumWorkers;

Обучите модель

Задайте опции обучения.

numEpochs = 20;
miniBatchSize = 128;
velocity = [];

Для обучения графического процессора методические рекомендации должны увеличить мини-пакетный размер линейно с количеством графических процессоров, для того, чтобы сохранить рабочую нагрузку на каждом графическом процессоре постоянной. Для более связанного совета смотрите Обучение с помощью Нескольких графических процессоров (Deep Learning Toolbox).

if executionEnvironment == "gpu"
    miniBatchSize = miniBatchSize .* N
end
miniBatchSize = 512

Вычислите мини-пакетный размер для каждого рабочего путем деления полного мини-пакетного размера равномерно между рабочими. Распределите остаток на первых рабочих.

workerMiniBatchSize = floor(miniBatchSize ./ repmat(N,1,N));
remainder = miniBatchSize - sum(workerMiniBatchSize);
workerMiniBatchSize = workerMiniBatchSize + [ones(1,remainder) zeros(1,N-remainder)]
workerMiniBatchSize = 1×4

   128   128   128   128

Инициализируйте график процесса обучения.

% Set up the training plot
figure
lineLossTrain = animatedline('Color',[0.85 0.325 0.098]);
ylim([0 inf])
xlabel("Iteration")
ylabel("Loss")
grid on

Чтобы передать данные обратно от рабочих во время обучения, создайте DataQueue объект. Используйте afterEach настраивать функцию, displayTrainingProgress, чтобы вызвать каждый раз, рабочий отправляет данные. displayTrainingProgress функция поддержки, заданная в конце этого примера, который отображает информацию о процессе обучения, которая прибывает от рабочих.

Q = parallel.pool.DataQueue;
displayFcn = @(x) displayTrainingProgress(x,lineLossTrain);
afterEach(Q,displayFcn);

Обучите модель с помощью пользовательского параллельного учебного цикла, как детализировано в следующих шагах. Чтобы выполнить код одновременно на всех рабочих, используйте spmd блок. В spmd блок, labindex дает индекс выполняющегося в данного момента рабочего код.

Перед обучением разделите datastore для каждого рабочего при помощи partition функция и набор ReadSize к мини-пакетному размеру рабочего.

В течение каждой эпохи, сброса и перестановки datastore с reset и shuffle функции. Для каждой итерации в эпоху:

  • Убедитесь, что у всех рабочих есть доступные данные прежде, чем начать обрабатывать его параллельно путем выполнения глобального and операция (gop) на результате hasdata функция.

  • Считайте мини-пакет из datastore при помощи read функция, и конкатенирует полученные изображения в четырехмерный массив изображений. Нормируйте изображения так, чтобы пиксели приняли значения между 0 и 1.

  • Преобразуйте метки в матрицу фиктивных переменных, которая помещает метки против наблюдений. Фиктивные переменные содержат 1 для метки наблюдения и 0 в противном случае.

  • Преобразуйте мини-пакет данных к dlarray объект с базовым одним типом и указывает, что размерность маркирует 'SSCB' (пространственный, пространственный, канал, пакет). Для обучения графического процессора преобразуйте данные в gpuArray.

  • Вычислите градиенты и потерю сети на каждом рабочем путем вызова dlfeval на modelGradients функция. dlfeval функция выполняет функцию помощника modelGradients с автоматическим включенным дифференцированием, таким образом, modelGradients может вычислить градиенты относительно потери автоматическим способом. modelGradients задан в конце примера и возвращает потерю и градиенты, учитывая сеть, мини-пакет данных и истинные метки.

  • Чтобы получить полную потерю, агрегируйте потери на всех рабочих. Этим примером использование пересекает энтропию для функции потерь и агрегированную потерю, является сумма всех потерь. Перед агрегацией нормируйте каждую потерю путем умножения пропорцией полного мини-пакета, что рабочий продолжает работать. Используйте gplus добавить все потери вместе и реплицировать результаты через рабочих.

  • Чтобы агрегировать и обновить градиенты всех рабочих, используйте dlupdate функция с aggregateGradients функция. aggregateGradients функция, определяемая поддержки в конце этого примера. Эта функция использует gplus добавить вместе и реплицировать градиенты через рабочих, после нормализации согласно пропорции полного мини-пакета, что каждый рабочий продолжает работать.

  • Агрегируйте состояние сети на всех рабочих, использующих aggregateState функция. aggregateState функция, определяемая поддержки в конце этого примера. batchnormalization слои в сети отслеживают среднее значение и отклонение данных. Поскольку полный мини-пакет распространен через несколько рабочих, агрегируйте сетевое состояние после каждой итерации, чтобы вычислить среднее значение и отклонение целого мини-пакета.

  • После вычисления итоговых градиентов обновите сетевые настраиваемые параметры с sgdmupdate функция.

  • Передайте информацию о процессе обучения обратно клиенту при помощи send функция с DataQueue. Используйте только одного рабочего, чтобы отправить данные, потому что у всех рабочих есть та же информация о потере. Гарантировать, что данные находятся на центральном процессоре, так, чтобы клиентская машина без графического процессора могла получить доступ к нему, gather использования на dlarray прежде, чем отправить его.

start = tic;
spmd
    % Partition datastore.
    workerImds = partition(imdsTrain,N,labindex);
    workerImds.ReadSize = workerMiniBatchSize(labindex);
    
    workerVelocity = velocity;
   
    iteration = 0;
    
    for epoch = 1:numEpochs
        % Reset and shuffle the datastore.
        reset(workerImds);
        workerImds = shuffle(workerImds);
        
        % Loop over mini-batches.
        while gop(@and,hasdata(workerImds))
            iteration = iteration + 1;
            
            % Read a mini-batch of data.
            [workerXBatch,workerTBatch] = read(workerImds);
            workerXBatch = cat(4,workerXBatch{:});
            workerNumObservations = numel(workerTBatch.Label);

            % Normalize the images.
            workerXBatch =  single(workerXBatch) ./ 255;
            
            % Convert the labels to dummy variables.
            workerY = zeros(numClasses,workerNumObservations,'single');
            for c = 1:numClasses
                workerY(c,workerTBatch.Label==classes(c)) = 1;
            end
            
            % Convert the mini-batch of data to dlarray.
            dlworkerX = dlarray(workerXBatch,'SSCB');
            
            % If training on GPU, then convert data to gpuArray.
            if executionEnvironment == "gpu"
                dlworkerX = gpuArray(dlworkerX);
            end
            
            % Evaluate the model gradients and loss on the worker.
            [workerGradients,dlworkerLoss,workerState] = dlfeval(@modelGradients,dlnet,dlworkerX,workerY);
            
            % Aggregate the losses on all workers.
            workerNormalizationFactor = workerMiniBatchSize(labindex)./miniBatchSize;
            loss = gplus(workerNormalizationFactor*extractdata(dlworkerLoss));
            
            % Aggregate the network state on all workers
            dlnet.State = aggregateState(workerState,workerNormalizationFactor);
            
            % Aggregate the gradients on all workers.
            workerGradients.Value = dlupdate(@aggregateGradients,workerGradients.Value,{workerNormalizationFactor});
            
            % Update the network parameters using the SGDM optimizer.
            [dlnet.Learnables,workerVelocity] = sgdmupdate(dlnet.Learnables,workerGradients,workerVelocity);
        end
        
       % Display training progress information.
       if labindex == 1
           data = [epoch loss iteration toc(start)];
           send(Q,gather(data)); 
       end
    end
end

Тестовая модель

После того, как вы обучите сеть, можно протестировать ее точность.

Загрузите тестовые изображения в память при помощи readall на тестовом datastore конкатенируйте их и нормируйте их.

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

После того, как обучение завершено, у всех рабочих есть завершенное то же самое, обучил сеть. Получите любого из них.

dlnetFinal = dlnet{1};

Классифицировать изображения с помощью dlnetwork объект, используйте predict функция на dlarray.

dlYPredScores = predict(dlnetFinal,dlarray(XTest,'SSCB'));

От предсказанных баллов найдите класс с самым высоким счетом с max функция. Прежде чем вы сделаете это, извлеките данные из dlarray с extractdata функция.

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

Чтобы получить точность классификации модели, сравните предсказания на наборе тестов против истинных меток.

accuracy = mean(YPred==YTest)
accuracy = 0.9910

Функции градиентов модели

Задайте функцию, modelGradients, вычислить градиенты потери относительно настраиваемых параметров сети. Эта функция вычисляет сетевые выходные параметры для мини-пакетного X с forward и softmax и вычисляет потерю, учитывая истинные выходные параметры, с помощью перекрестной энтропии. Когда вы вызываете эту функцию с dlfeval, автоматическое дифференцирование включено, и dlgradient может вычислить градиенты потери относительно learnables автоматически.

function [dlgradients,dlloss,state] = modelGradients(dlnet,dlX,dlY)
    [dlYPred,state] = forward(dlnet,dlX);
    dlYPred = softmax(dlYPred);
    
    dlloss = crossentropy(dlYPred,dlY);
    dlgradients = dlgradient(dlloss,dlnet.Learnables);
end

Отобразите функцию процесса обучения

Задайте функцию, чтобы отобразить информацию о процессе обучения, которая прибывает от рабочих. DataQueue в этом примере вызывает эту функцию каждый раз, когда рабочий отправляет данные.

function displayTrainingProgress (data,line)
     addpoints(line,double(data(3)),double(data(2)))
     D = duration(0,0,data(4),'Format','hh:mm:ss');
     title("Epoch: " + data(1) + ", Elapsed: " + string(D))
     drawnow
end

Совокупная функция градиентов

Задайте функцию, которая агрегировала градиенты на всех рабочих путем добавления их вместе. gplus добавляет вместе и реплицирует все градиенты в рабочих. Прежде, чем добавить их вместе, нормируйте их путем умножения их на фактор, который представляет пропорцию полного мини-пакета, что рабочий продолжает работать. Получать содержимое dlarrayUse extractdata.

function gradients = aggregateGradients(dlgradients,factor)
    gradients = extractdata(dlgradients);
    gradients = gplus(factor*gradients);
end

Совокупная функция состояния

Задайте функцию, которая агрегировала сетевое состояние на всех рабочих. Сетевое состояние содержит обученную статистику нормализации партии. набора данных. Поскольку каждый рабочий только видит фрагмент мини-пакета, агрегируйте сетевое состояние так, чтобы статистические данные были представительными для статистики через все данные. Для каждого мини-пакета объединенное среднее значение вычисляется как взвешенное среднее среднего значения через рабочих для каждой итерации. Объединенное отклонение вычисляется согласно следующей формуле:

sc2=1Mj=1Nmj[sj2+(xj-xc)2]

где Nобщее количество рабочих, Mобщее количество наблюдений в мини-пакете, mj количество наблюдений, обработанных на jрабочий th, xj и sj2 среднее значение и статистика отклонения, вычисленная на того рабочего, и xc объединенное среднее значение через всех рабочих.

function state = aggregateState(state,factor)

    numrows = size(state,1);
    
    for j = 1:numrows
        isBatchNormalizationState = state.Parameter(j) =="TrainedMean"...
            && state.Parameter(j+1) =="TrainedVariance"...
            && state.Layer(j) == state.Layer(j+1);
        
        if isBatchNormalizationState
            meanVal = state.Value{j};
            varVal = state.Value{j+1};
            
            % Calculate combined mean
            combinedMean = gplus(factor*meanVal);
                   
            % Caclulate combined variance terms to sum
            combinedVarTerm = factor.*(varVal + (meanVal - combinedMean).^2);        
            
            % Update state
            state.Value(j) = {combinedMean};
            state.Value(j+1) = {gplus(combinedVarTerm)};
           
        end
    end
end

Смотрите также

| | | | | | | (Deep Learning Toolbox) | (Deep Learning Toolbox) | (Deep Learning Toolbox) | (Deep Learning Toolbox)

Связанные примеры

Больше о