В этом примере показано, как обучить сеть классификации изображений объектов с использованием циклического графика обучения и сбора снимков для повышения точности тестирования. В примере показано, как использовать функцию косинуса для графика скорости обучения, делать снимки сети во время обучения для создания ансамбля модели и добавлять L2-norm регуляризацию (распад веса) к потере тренировок.
Этот пример обучает остаточную сеть [1] на CIFAR-10 наборе данных [2] с пользовательской циклической скоростью обучения: для каждой итерации решатель использует скорость обучения, заданную смещенной косинусной функцией [3] alpha(t) = (alpha0/2)*cos(pi*mod(t-1,T/M)/(T/M)+1), где t - номер итерации, T - общее количество итераций обучения, alpha0 - начальная скорость обучения, и M - количество циклов/снимков файловой системы. Этот график обучения эффективно разделяет процесс обучения на M циклов. Каждый цикл начинается с большой скорости обучения, которая монотонно распадается, заставляя сеть исследовать различные локальные минимумы. В конце каждого учебного цикла вы берете снимок сети (то есть сохраняете модель на этой итерации) и позже усредняете прогнозы всех моделей снимков, также известных как сбор снимков [4], чтобы повысить окончательную точность тестирования.
Загрузите набор данных CIFAR-10 [2]. Набор данных содержит 60 000 изображений. Каждое изображение имеет размер 32 на 32 и имеет три цветовых канала (RGB). Размер набора данных составляет 175 МБ. В зависимости от подключения к Интернету процесс загрузки может занять некоторое время.
datadir = tempdir; downloadCIFARData(datadir);
Загрузите обучающие и тестовые образы CIFAR-10 в виде массивов 4-D. Обучающий набор содержит 50 000 изображений, а набор тестов - 10 000 изображений.
[XTrain,YTrain,XTest,YTest] = loadCIFARData(datadir); classes = categories(YTrain); numClasses = numel(classes);
Можно просмотреть случайную выборку обучающих изображений с помощью следующего кода.
figure;
idx = randperm(size(XTrain,4),20);
im = imtile(XTrain(:,:,:,idx),'ThumbnailSize',[96,96]);
imshow(im)
Создание augmentedImageDatastore объект, используемый для обучения сети. Во время обучения хранилище данных случайным образом разворачивает обучающие изображения вдоль вертикальной оси и случайным образом переводит их до четырех пикселей по горизонтали и вертикали. Увеличение объема данных помогает предотвратить переоборудование сети и запоминание точных деталей обучающих изображений.
imageSize = [32 32 3]; pixelRange = [-4 4]; imageAugmenter = imageDataAugmenter( ... 'RandXReflection',true, ... 'RandXTranslation',pixelRange, ... 'RandYTranslation',pixelRange); augimdsTrain = augmentedImageDatastore(imageSize,XTrain,YTrain, ... 'DataAugmentation',imageAugmenter);
Создайте остаточную сеть [1] с шестью стандартными сверточными блоками (по две единицы на этап) и шириной 16. Общая глубина сети составляет 2 * 6 + 2 = 14. Кроме того, укажите среднее изображение с помощью 'Mean' в слое ввода изображения.
netWidth = 16;
layers = [
imageInputLayer(imageSize,'Name','input','Mean', mean(XTrain,4))
convolution2dLayer(3,netWidth,'Padding','same','Name','convInp')
batchNormalizationLayer('Name','BNInp')
reluLayer('Name','reluInp')
convolutionalUnit(netWidth,1,'S1U1')
additionLayer(2,'Name','add11')
reluLayer('Name','relu11')
convolutionalUnit(netWidth,1,'S1U2')
additionLayer(2,'Name','add12')
reluLayer('Name','relu12')
convolutionalUnit(2*netWidth,2,'S2U1')
additionLayer(2,'Name','add21')
reluLayer('Name','relu21')
convolutionalUnit(2*netWidth,1,'S2U2')
additionLayer(2,'Name','add22')
reluLayer('Name','relu22')
convolutionalUnit(4*netWidth,2,'S3U1')
additionLayer(2,'Name','add31')
reluLayer('Name','relu31')
convolutionalUnit(4*netWidth,1,'S3U2')
additionLayer(2,'Name','add32')
reluLayer('Name','relu32')
averagePooling2dLayer(8,'Name','globalPool')
fullyConnectedLayer(10,'Name','fcFinal')
];
lgraph = layerGraph(layers);
lgraph = connectLayers(lgraph,'reluInp','add11/in2');
lgraph = connectLayers(lgraph,'relu11','add12/in2');
skip1 = [
convolution2dLayer(1,2*netWidth,'Stride',2,'Name','skipConv1')
batchNormalizationLayer('Name','skipBN1')];
lgraph = addLayers(lgraph,skip1);
lgraph = connectLayers(lgraph,'relu12','skipConv1');
lgraph = connectLayers(lgraph,'skipBN1','add21/in2');
lgraph = connectLayers(lgraph,'relu21','add22/in2');
skip2 = [
convolution2dLayer(1,4*netWidth,'Stride',2,'Name','skipConv2')
batchNormalizationLayer('Name','skipBN2')];
lgraph = addLayers(lgraph,skip2);
lgraph = connectLayers(lgraph,'relu22','skipConv2');
lgraph = connectLayers(lgraph,'skipBN2','add31/in2');
lgraph = connectLayers(lgraph,'relu31','add32/in2');Постройте график архитектуры ResNet.
figure; plot(lgraph)

Создать dlnetwork объект из графа слоев.
dlnet = dlnetwork(lgraph);
Создание вспомогательной функции modelGradients, перечисленных в конце примера. Функция принимает dlnetwork объект dlnet and мини-пакет входных данных dlX с соответствующими метками Y, и возвращает градиенты потерь относительно обучаемых параметров в dlnet. Эта функция также возвращает потери и состояние неочищаемых параметров сети в данной итерации.
Укажите параметры обучения. Поезд на 200 эпох с размером мини-партии 64.
numEpochs = 200; miniBatchSize = 64; numObservations = numel(YTrain); velocity = []; momentum = 0.9; weightDecay = 1e-4;
Укажите варианты обучения, специфичные для циклического уровня обучения. Alpha0 - начальный уровень обучения и numSnapshots - количество циклов или снимков, сделанных во время обучения.
alpha0 = 0.1;
numSnapshots = 5;
epochsPerSnapshot = numEpochs./numSnapshots;
iterationsPerSnapshot = ceil(numObservations./miniBatchSize)*numEpochs./numSnapshots;
modelPrefix = "SnapshotEpoch";Визуализация хода обучения на графике.
plots = "training-progress";Инициализируйте рисунок обучения.
if plots == "training-progress" [lossLine,learnRateLine] = plotLossAndLearnRate(); end
Использовать minibatchqueue обрабатывать и управлять мини-партиями изображений во время обучения. Для каждой мини-партии:
Использование пользовательской функции предварительной обработки мини-партии preprocessMiniBatch (определяется в конце этого примера) для одноконтактного кодирования меток класса.
Форматирование данных изображения с метками размеров 'SSCB' (пространственный, пространственный, канальный, пакетный). По умолчанию minibatchqueue объект преобразует данные в dlarray объекты с базовым типом single. Не добавляйте формат к меткам класса.
Обучение на GPU, если он доступен. По умолчанию minibatchqueue объект преобразует каждый вывод в gpuArray если графический процессор доступен. Для использования графического процессора требуется Toolbox™ параллельных вычислений и поддерживаемое устройство графического процессора. Сведения о поддерживаемых устройствах см. в разделе Поддержка графического процессора по выпуску (Parallel Computing Toolbox).
augimdsTrain.MiniBatchSize = miniBatchSize; mbqTrain = minibatchqueue(augimdsTrain,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFcn', @preprocessMiniBatch,... 'MiniBatchFormat',{'SSCB',''});
Обучение модели с помощью пользовательского цикла обучения. Для каждой эпохи тасуйте хранилище данных, закольцовывайте мини-пакеты данных и сохраните модель (снимок), если текущая эпоха кратна epochsPerSnapshot. В конце каждой эпохи отобразите ход обучения. Для каждой мини-партии:
Оценка градиентов и потерь модели с помощью dlfeval и modelGradients функция.
Обновите состояние неочищаемых параметров сети.
Определите скорость обучения для графика циклической скорости обучения.
Обновление параметров сети с помощью sgdmupdate функция.
Постройте график потерь и скорости обучения на каждой итерации.
Для этого примера обучение заняло около 14 часов на NVIDIA™ TITAN RTX.
iteration = 0; start = tic; % Loop over epochs. for epoch = 1:numEpochs % Shuffle data. shuffle(mbqTrain); % Save snapshot model. if ~mod(epoch,epochsPerSnapshot) save(modelPrefix + epoch + ".mat",'dlnet'); end % Loop over mini-batches. while hasdata(mbqTrain) iteration = iteration + 1; % Read mini-batch of data. [dlX,dlY] = next(mbqTrain); % Evaluate the model gradients and loss using dlfeval and the % modelGradients function. [gradients, loss, state] = dlfeval(@modelGradients,dlnet,dlX,dlY,weightDecay); % Update the state of nonlearnable parameters. dlnet.State = state; % Determine learning rate for cyclical learning rate schedule. learnRate = 0.5*alpha0*(cos((pi*mod(iteration-1,iterationsPerSnapshot)./iterationsPerSnapshot))+1); % Update the network parameters using the SGDM optimizer. [dlnet.Learnables, velocity] = sgdmupdate(dlnet.Learnables, gradients, velocity, learnRate, momentum); % Display the training progress. if plots == "training-progress" D = duration(0,0,toc(start),'Format','hh:mm:ss'); addpoints(lossLine,iteration,double(gather(extractdata(loss)))) addpoints(learnRateLine, iteration, learnRate); sgtitle("Epoch: " + epoch + ", Elapsed: " + string(D)) drawnow end end end

Объедините M снимков сети, сделанных во время обучения, чтобы сформировать окончательный ансамбль и проверить точность классификации модели. Ансамблевые предсказания соответствуют среднему показателю выхода полностью подключенного слоя от всех М отдельных моделей.
Протестируйте модель на тестовых данных, поставляемых с набором данных CIFAR-10. Управление набором тестовых данных с помощью minibatchqueue объект с той же настройкой, что и данные обучения.
augimdsTest = augmentedImageDatastore(imageSize,XTest,YTest); augimdsTest.MiniBatchSize = miniBatchSize; mbqTest = minibatchqueue(augimdsTest,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFcn', @preprocessMiniBatch,... 'MiniBatchFormat',{'SSCB',''});
Оцените точность каждой сети моментальных снимков. Используйте modelPredictions функция, определенная в конце этого примера для итерации по всем данным в наборе тестовых данных. Функция возвращает выходные данные полностью связанного слоя из модели, прогнозируемые классы и сравнение с истинным классом.
modelName = cell(numSnapshots+1,1); fcOutput = zeros(numClasses,numel(YTest),numSnapshots+1); classPredictions = cell(1,numSnapshots+1); modelAccuracy = zeros(numSnapshots+1,1); for m = 1:numSnapshots modelName{m} = modelPrefix + m*epochsPerSnapshot; load(modelName{m} + ".mat"); reset(mbqTest); [fcOutputTest,classPredTest,classCorrTest] = modelPredictions(dlnet,mbqTest,classes); fcOutput(:,:,m) = fcOutputTest; classPredictions{m} = classPredTest; modelAccuracy(m) = 100*mean(classCorrTest); disp(modelName{m} + " accuracy: " + modelAccuracy(m) + "%") end
SnapshotEpoch40 accuracy: 88.35% SnapshotEpoch80 accuracy: 89.93% SnapshotEpoch120 accuracy: 90.51% SnapshotEpoch160 accuracy: 90.33% SnapshotEpoch200 accuracy: 90.63%
Для определения выхода ансамблевых сетей вычислите среднее значение полностью подключенного выхода каждой сети моментальных снимков. Поиск прогнозируемых классов из сети ансамбля с помощью onehotdecode функция. Сравните с истинными классами, чтобы оценить точность ансамбля.
fcOutput(:,:,end) = mean(fcOutput(:,:,1:end-1),3);
classPredictions{end} = onehotdecode(softmax(fcOutput(:,:,end)),classes,1,'categorical');
classCorrEnsemble = classPredictions{end} == YTest';
modelAccuracy(end) = 100*mean(classCorrEnsemble);
modelName{end} = "Ensemble model";
disp("Ensemble accuracy: " + modelAccuracy(end) + "%")Ensemble accuracy: 91.59%
Постройте график точности на наборе тестовых данных для всех моделей снимков и модели ансамбля.
figure;bar(modelAccuracy); ylabel('Accuracy (%)'); xticklabels(modelName) xtickangle(45) title('Model accuracy')

modelGradients функция принимает dlnetwork объект dlnet, мини-пакет входных данных dlX, этикетки Y, и параметр для снижения веса. Функция возвращает градиенты, потери и состояние неочищаемых параметров. Для автоматического вычисления градиентов используйте dlgradient функция.
function [gradients,loss,state] = modelGradients(dlnet,dlX,Y,weightDecay) [dlYPred,state] = forward(dlnet,dlX); dlYPred = softmax(dlYPred); loss = crossentropy(dlYPred, Y); % L2-regularization (weight decay) allParams = dlnet.Learnables(dlnet.Learnables.Parameter == "Weights" | dlnet.Learnables.Parameter == "Scale",:).Value; l2Norm = cellfun(@(x) sum(x.^2,'All'),allParams,'UniformOutput',false); l2Norm = sum(cat(1,l2Norm{:})); loss = loss + weightDecay*0.5*l2Norm; gradients = dlgradient(loss,dlnet.Learnables); end
modelPredictions функция принимает в качестве входного значения a dlnetwork объект dlnet, a minibatchqueue входных данных mbqи вычисляет прогнозы модели путем итерации по всем данным в minibatchqueue. Функция использует onehotdecode функция, чтобы найти прогнозируемый класс с наивысшим баллом, а затем сравнивает прогноз с истинным классом. Функция возвращает выходные данные сети, предсказания классов и вектор единиц и нулей, представляющий правильные и неправильные предсказания.
function [rawPredictions,classPredictions,classCorr] = modelPredictions(dlnet,mbq,classes) rawPredictions = []; classPredictions = []; classCorr = []; while hasdata(mbq) [dlX,dlY] = next(mbq); % Make predictions dlYPred = predict(dlnet,dlX); rawPredictions = [rawPredictions extractdata(gather(dlYPred))]; % Convert network output to probabilities and determine predicted % classes dlYPred = softmax(dlYPred); YPredBatch = onehotdecode(dlYPred,classes,1); classPredictions = [classPredictions YPredBatch]; % Compare predicted and true classes Y = onehotdecode(dlY,classes,1); classCorr = [classCorr YPredBatch == Y]; end end
plotLossAndLearnRate функция инициализирует графики для отображения потерь и скорости обучения на каждой итерации во время обучения.
function [lossLine, learnRateLine] = plotLossAndLearnRate() figure subplot(2,1,1); lossLine = animatedline('Color',[0.85 0.325 0.098]); title('Loss'); xlabel('Iteration') ylabel('Loss') grid on subplot(2,1,2); learnRateLine = animatedline('Color',[0 0.447 0.741]); title('Learning rate'); xlabel('Iteration') ylabel('Learning rate') grid on end
convolutionalUnit(numF,stride,tag) функция создает массив слоев с двумя сверточными слоями и соответствующими уровнями пакетной нормализации и ReLU. numF - количество сверточных фильтров, stride - шаг первого сверточного слоя, и tag - тег, добавленный ко всем именам слоев.
function layers = convolutionalUnit(numF,stride,tag) layers = [ convolution2dLayer(3,numF,'Padding','same','Stride',stride,'Name',[tag,'conv1']) batchNormalizationLayer('Name',[tag,'BN1']) reluLayer('Name',[tag,'relu1']) convolution2dLayer(3,numF,'Padding','same','Name',[tag,'conv2']) batchNormalizationLayer('Name',[tag,'BN2'])]; end
preprocessMiniBatch функция выполняет предварительную обработку данных с помощью следующих шагов:
Извлеките данные изображения из входящего массива ячеек и объедините их в числовой массив. Конкатенация данных изображения над четвертым размером добавляет к каждому изображению третий размер, который будет использоваться в качестве размера одиночного канала.
Извлеките данные метки из входящих массивов ячеек и объедините их в категориальный массив вдоль второго измерения.
Одноконтурное кодирование категориальных меток в числовые массивы. Кодирование в первом измерении создает закодированный массив, который соответствует форме сетевого вывода.
function [X,Y] = preprocessMiniBatch(XCell,YCell) % Extract image data from cell and concatenate X = cat(4,XCell{:}); % Extract label data from cell and concatenate Y = cat(2,YCell{:}); % One-hot encode labels Y = onehotencode(Y,1); end
[1] Хэ, Каймин, Сянъу Чжан, Шаоцин Жэнь и Цзянь Сунь. «Глубокое остаточное обучение для распознавания изображения». В трудах конференции IEEE по компьютерному зрению и распознаванию образов, стр. 770-778. 2016.
[2] Крижевский, Алекс. «Изучение нескольких слоев элементов из крошечных изображений». (2009). https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf
[3] Лошчилов, Илья и Фрэнк Хаттер. «Sgdr: Стохастический градиентный спуск с тёплыми перезапусками». (2016). arXiv препринт arXiv:1608.03983.
[4] Хуан, Гао, Исюань Ли, Геофф Плейсс, Чжуан Лю, Джон Э. Хопкрофт и Килиан К. Вайнбергер. «Ансамбли снимков: Поезд 1, получить м бесплатно». (2017). arXiv препринт arXiv:1704.00109.
dlarray | dlfeval | dlgradient | dlnetwork | layerGraph | minibatchqueue | onehotdecode | onehotencode | sgdmupdate | sigmoid