В этом примере показов, как создать и обучить нейронную сеть для глубокого обучения с помощью функций, а не графа слоев или dlnetwork
. Преимущество использования функций заключается в гибкости описания широкого спектра сетей. Недостатком является то, что вы должны выполнить больше шагов и тщательно подготовить свои данные. Этот пример использует изображения рукописных цифр с двумя целями классификации цифр и определения угла каждой цифры от вертикали.
The digitTrain4DArrayData
функция загружает изображения, их метки цифр и углы поворота от вертикали. Создание arrayDatastore
объекты для изображений, меток и углов, а затем используйте combine
функция, чтобы создать один datastore, который содержит все обучающие данные. Извлеките имена классов и количество недискретных ответов.
[XTrain,YTrain,anglesTrain] = digitTrain4DArrayData;
dsXTrain = arrayDatastore(XTrain,'IterationDimension',4);
dsYTrain = arrayDatastore(YTrain);
dsAnglesTrain = arrayDatastore(anglesTrain);
dsTrain = combine(dsXTrain,dsYTrain,dsAnglesTrain);
classNames = categories(YTrain);
numClasses = numel(classNames);
numResponses = size(anglesTrain,2);
numObservations = numel(YTrain);
Просмотрите некоторые изображения из обучающих данных.
idx = randperm(numObservations,64); I = imtile(XTrain(:,:,:,idx)); figure imshow(I)
Задайте следующую сеть, которая предсказывает и метки, и углы поворота.
Блок свертки-batchnorm-ReLU с 16 фильтрами 5 на 5.
Ветвь из двух блоков свертки-batchnorm каждый с 32 фильтрами 3 на 3 с операцией ReLU между
Пропускное соединение с блоком свертки-batchnorm с 32 свертками 1 на 1.
Объедините обе ветви с помощью сложения и последующей операции ReLU
Для вывода регрессии выход ветвь с полностью связанной операцией размера 1 (количество откликов).
Для вывода классификации выход ветвь с полностью связанной операцией размера 10 (количество классов) и операцией softmax.
Определите параметры для каждой из операций и включите их в struct. Используйте формат parameters.OperationName.ParameterName
где parameters
- struct, O perationName
- имя операции (для примера «conv1») и ParameterName
- имя параметра (для примера - «Веса»).
Создайте struct parameters
содержащие параметры модели. Инициализируйте обучаемые веса и смещения слоев с помощью initializeGlorot
и initializeZeros
функции , взятые в качестве примера,, соответственно. Инициализируйте параметры смещения и шкалы нормализации партии . с помощью initializeZeros
и initializeOnes
функции , взятые в качестве примера,, соответственно.
Чтобы выполнить обучение и вывод с использованием слоев нормализации партии ., необходимо также управлять состоянием сети. Перед предсказанием необходимо задать среднее значение набора данных и отклонения, выведенные из обучающих данных. Создайте struct state
содержит параметры состояния. Статистика нормализации партии . не должна быть dlarray
объекты. Инициализируйте нормализацию партии ., обученную средним и обученным состояниям отклонения, используя zeros
и ones
функций, соответственно.
Функции , взятые в качестве примера, инициализации присоединены к этому примеру как вспомогательные файлы.
Инициализируйте параметры для первого сверточного слоя.
filterSize = [5 5]; numChannels = 1; numFilters = 16; sz = [filterSize numChannels numFilters]; numOut = prod(filterSize) * numFilters; numIn = prod(filterSize) * numFilters; parameters.conv1.Weights = initializeGlorot(sz,numOut,numIn); parameters.conv1.Bias = initializeZeros([numFilters 1]);
Инициализируйте параметры и состояние для первого слоя нормализации партии ..
parameters.batchnorm1.Offset = initializeZeros([numFilters 1]); parameters.batchnorm1.Scale = initializeOnes([numFilters 1]); state.batchnorm1.TrainedMean = zeros(numFilters,1,'single'); state.batchnorm1.TrainedVariance = ones(numFilters,1,'single');
Инициализируйте параметры для второго сверточного слоя.
filterSize = [3 3]; numChannels = 16; numFilters = 32; sz = [filterSize numChannels numFilters]; numOut = prod(filterSize) * numFilters; numIn = prod(filterSize) * numFilters; parameters.conv2.Weights = initializeGlorot(sz,numOut,numIn); parameters.conv2.Bias = initializeZeros([numFilters 1]);
Инициализируйте параметры и состояние для второго слоя нормализации партии ..
parameters.batchnorm2.Offset = initializeZeros([numFilters 1]); parameters.batchnorm2.Scale = initializeOnes([numFilters 1]); state.batchnorm2.TrainedMean = zeros(numFilters,1,'single'); state.batchnorm2.TrainedVariance = ones(numFilters,1,'single');
Инициализируйте параметры для третьего сверточного слоя.
filterSize = [3 3]; numChannels = 32; numFilters = 32; sz = [filterSize numChannels numFilters]; numOut = prod(filterSize) * numFilters; numIn = prod(filterSize) * numFilters; parameters.conv3.Weights = initializeGlorot(sz,numOut,numIn); parameters.conv3.Bias = initializeZeros([numFilters 1]);
Инициализируйте параметры и состояние для третьего слоя нормализации партии ..
parameters.batchnorm3.Offset = initializeZeros([numFilters 1]); parameters.batchnorm3.Scale = initializeOnes([numFilters 1]); state.batchnorm3.TrainedMean = zeros(numFilters,1,'single'); state.batchnorm3.TrainedVariance = ones(numFilters,1,'single');
Инициализируйте параметры для сверточного слоя в пропускающем соединении.
filterSize = [1 1]; numChannels = 16; numFilters = 32; sz = [filterSize numChannels numFilters]; numOut = prod(filterSize) * numFilters; numIn = prod(filterSize) * numFilters; parameters.convSkip.Weights = initializeGlorot(sz,numOut,numIn); parameters.convSkip.Bias = initializeZeros([numFilters 1]);
Инициализируйте параметры и состояние для слоя нормализации партии . в пропускающем соединении.
parameters.batchnormSkip.Offset = initializeZeros([numFilters 1]); parameters.batchnormSkip.Scale = initializeOnes([numFilters 1]); state.batchnormSkip.TrainedMean = zeros([numFilters 1],'single'); state.batchnormSkip.TrainedVariance = ones([numFilters 1],'single');
Инициализируйте параметры для полносвязного слоя, соответствующего выходу классификации.
sz = [numClasses 6272]; numOut = numClasses; numIn = 6272; parameters.fc1.Weights = initializeGlorot(sz,numOut,numIn); parameters.fc1.Bias = initializeZeros([numClasses 1]);
Инициализируйте параметры для полносвязного слоя, соответствующего выходу регрессии.
sz = [numResponses 6272]; numOut = numResponses; numIn = 6272; parameters.fc2.Weights = initializeGlorot(sz,numOut,numIn); parameters.fc2.Bias = initializeZeros([numResponses 1]);
Просмотрите struct параметров.
parameters
parameters = struct with fields:
conv1: [1×1 struct]
batchnorm1: [1×1 struct]
conv2: [1×1 struct]
batchnorm2: [1×1 struct]
conv3: [1×1 struct]
batchnorm3: [1×1 struct]
convSkip: [1×1 struct]
batchnormSkip: [1×1 struct]
fc1: [1×1 struct]
fc2: [1×1 struct]
Просмотрите параметры для операции «conv1».
parameters.conv1
ans = struct with fields:
Weights: [5×5×1×16 dlarray]
Bias: [16×1 dlarray]
Просмотрите struct состояния.
state
state = struct with fields:
batchnorm1: [1×1 struct]
batchnorm2: [1×1 struct]
batchnorm3: [1×1 struct]
batchnormSkip: [1×1 struct]
Просмотрите параметры состояния для операции «batchnorm1».
state.batchnorm1
ans = struct with fields:
TrainedMean: [16×1 single]
TrainedVariance: [16×1 single]
Создайте функцию model
, перечисленный в конце примера, который вычисляет выходы модели глубокого обучения, описанной ранее.
Функция model
принимает параметры модели parameters
, входные данные dlX
, флаг doTraining
который определяет, должна ли модель возвращать выходы для обучения или предсказания, и состояние сети state
. Сеть выводит предсказания для меток, предсказания для углов и обновленное состояние сети.
Создайте функцию modelGradients
, перечисленный в конце примера, который берёт параметры модели, мини-пакет входных данных dlX
с соответствующими целями T1
и T2
содержит метки и углы, соответственно, и возвращает градиенты потерь относительно настраиваемых параметров, обновленного состояния сети и соответствующих потерь.
Задайте опции обучения. Обучайте на 20 эпох с мини-партией размером 128.
numEpochs = 20; miniBatchSize = 128;
Чтобы контролировать процесс обучения, можно построить график потерь обучения после каждой итерации. Создайте переменные графики, которые содержат «процесс обучения». Если вы не хотите строить график процесса обучения, задайте для этого значения значение «none».
plots = "training-progress";
Использование minibatchqueue
для обработки и управления мини-пакетами изображений. Для каждого мини-пакета:
Используйте пользовательскую функцию мини-пакетной предварительной обработки preprocessMiniBatch
(определено в конце этого примера), чтобы закодировать метки классов с одним «горячим» кодом.
Форматируйте данные изображения с помощью меток размерностей 'SSCB'
(пространственный, пространственный, канальный, пакетный). По умолчанию в minibatchqueue
объект преобразует данные в dlarray
объекты с базовым типом single
. Не добавляйте формат к меткам классов или углам.
Обучите на графическом процессоре, если он доступен. По умолчанию в minibatchqueue
объект преобразует каждый выход в gpuArray
при наличии графический процессор. Для использования графический процессор требуется Parallel Computing Toolbox™ и поддерживаемый графический процессор. Для получения информации о поддерживаемых устройствах смотрите Поддержку GPU by Release (Parallel Computing Toolbox).
mbq = minibatchqueue(dsTrain,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFcn', @preprocessMiniBatch,... 'MiniBatchFormat',{'SSCB','',''});
Для каждой эпохи перетасуйте данные и закольцовывайте по мини-пакетам данных. В конце каждой итерации отобразите процесс обучения. Для каждого мини-пакета:
Оцените градиенты модели и потери с помощью dlfeval
и modelGradients
функция.
Обновляйте параметры сети с помощью adamupdate
функция.
Инициализируйте параметры для Adam.
trailingAvg = []; trailingAvgSq = [];
Инициализируйте график процесса обучения.
if plots == "training-progress" figure lineLossTrain = animatedline('Color',[0.85 0.325 0.098]); ylim([0 inf]) xlabel("Iteration") ylabel("Loss") grid on end
Обучите модель.
iteration = 0; start = tic; % Loop over epochs. for epoch = 1:numEpochs % Shuffle data. shuffle(mbq) % Loop over mini-batches while hasdata(mbq) iteration = iteration + 1; [dlX,dlY1,dlY2] = next(mbq); % Evaluate the model gradients, state, and loss using dlfeval and the % modelGradients function. [gradients,state,loss] = dlfeval(@modelGradients, parameters, dlX, dlY1, dlY2, state); % Update the network parameters using the Adam optimizer. [parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients, ... trailingAvg,trailingAvgSq,iteration); % Display the training progress. if plots == "training-progress" D = duration(0,0,toc(start),'Format','hh:mm:ss'); addpoints(lineLossTrain,iteration,double(gather(extractdata(loss)))) title("Epoch: " + epoch + ", Elapsed: " + string(D)) drawnow end end end
Протестируйте классификационную точность модели путем сравнения предсказаний на тестовом наборе с истинными метками и углами. Управление набором тестовых данных с помощью minibatchqueue
объект с той же настройкой, что и обучающие данные.
[XTest,YTest,anglesTest] = digitTest4DArrayData; dsXTest = arrayDatastore(XTest,'IterationDimension',4); dsYTest = arrayDatastore(YTest); dsAnglesTest = arrayDatastore(anglesTest); dsTest = combine(dsXTest,dsYTest,dsAnglesTest); mbqTest = minibatchqueue(dsTest,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFcn', @preprocessMiniBatch,... 'MiniBatchFormat',{'SSCB','',''});
Чтобы предсказать метки и углы данных валидации, закольцовывайте мини-пакеты и используйте функцию модели с doTraining
значение опции установлено в false
. Сохраните предсказанные классы и углы. Сравните предсказанные и истинные классы и углы и сохраните результаты.
doTraining = false; classesPredictions = []; anglesPredictions = []; classCorr = []; angleDiff = []; % Loop over mini-batches. while hasdata(mbqTest) % Read mini-batch of data. [dlXTest,dlY1Test,dlY2Test] = next(mbqTest); % Make predictions using the predict function. [dlY1Pred,dlY2Pred] = model(parameters,dlXTest,doTraining,state); % Determine predicted classes. Y1PredBatch = onehotdecode(dlY1Pred,classNames,1); classesPredictions = [classesPredictions Y1PredBatch]; % Dermine predicted angles Y2PredBatch = extractdata(dlY2Pred); anglesPredictions = [anglesPredictions Y2PredBatch]; % Compare predicted and true classes Y1Test = onehotdecode(dlY1Test,classNames,1); classCorr = [classCorr Y1PredBatch == Y1Test]; % Compare predicted and true angles angleDiffBatch = Y2PredBatch - dlY2Test; angleDiff = [angleDiff extractdata(gather(angleDiffBatch))]; end
Оцените точность классификации.
accuracy = mean(classCorr)
accuracy = 0.9730
Оцените точность регрессии.
angleRMSE = sqrt(mean(angleDiff.^2))
angleRMSE = single
6.6909
Просмотрите некоторые изображения с их предсказаниями. Отображение прогнозируемых углов красного цвета и правильных меток зеленого цвета.
idx = randperm(size(XTest,4),9); figure for i = 1:9 subplot(3,3,i) I = XTest(:,:,:,idx(i)); imshow(I) hold on sz = size(I,1); offset = sz/2; thetaPred = anglesPredictions(idx(i)); plot(offset*[1-tand(thetaPred) 1+tand(thetaPred)],[sz 0],'r--') thetaValidation = anglesTest(idx(i)); plot(offset*[1-tand(thetaValidation) 1+tand(thetaValidation)],[sz 0],'g--') hold off label = string(classesPredictions(idx(i))); title("Label: " + label) end
Функция model
принимает параметры модели parameters
, входные данные dlX
, флаг doTraining
который определяет, должна ли модель возвращать выходы для обучения или предсказания, и состояние сети state
. Сеть выводит предсказания для меток, предсказания для углов и обновленное состояние сети.
function [dlY1,dlY2,state] = model(parameters,dlX,doTraining,state) % Convolution weights = parameters.conv1.Weights; bias = parameters.conv1.Bias; dlY = dlconv(dlX,weights,bias,'Padding','same'); % Batch normalization, ReLU offset = parameters.batchnorm1.Offset; scale = parameters.batchnorm1.Scale; trainedMean = state.batchnorm1.TrainedMean; trainedVariance = state.batchnorm1.TrainedVariance; if doTraining [dlY,trainedMean,trainedVariance] = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); % Update state state.batchnorm1.TrainedMean = trainedMean; state.batchnorm1.TrainedVariance = trainedVariance; else dlY = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); end dlY = relu(dlY); % Convolution, batch normalization (Skip connection) weights = parameters.convSkip.Weights; bias = parameters.convSkip.Bias; dlYSkip = dlconv(dlY,weights,bias,'Stride',2); offset = parameters.batchnormSkip.Offset; scale = parameters.batchnormSkip.Scale; trainedMean = state.batchnormSkip.TrainedMean; trainedVariance = state.batchnormSkip.TrainedVariance; if doTraining [dlYSkip,trainedMean,trainedVariance] = batchnorm(dlYSkip,offset,scale,trainedMean,trainedVariance); % Update state state.batchnormSkip.TrainedMean = trainedMean; state.batchnormSkip.TrainedVariance = trainedVariance; else dlYSkip = batchnorm(dlYSkip,offset,scale,trainedMean,trainedVariance); end % Convolution weights = parameters.conv2.Weights; bias = parameters.conv2.Bias; dlY = dlconv(dlY,weights,bias,'Padding','same','Stride',2); % Batch normalization, ReLU offset = parameters.batchnorm2.Offset; scale = parameters.batchnorm2.Scale; trainedMean = state.batchnorm2.TrainedMean; trainedVariance = state.batchnorm2.TrainedVariance; if doTraining [dlY,trainedMean,trainedVariance] = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); % Update state state.batchnorm2.TrainedMean = trainedMean; state.batchnorm2.TrainedVariance = trainedVariance; else dlY = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); end dlY = relu(dlY); % Convolution weights = parameters.conv3.Weights; bias = parameters.conv3.Bias; dlY = dlconv(dlY,weights,bias,'Padding','same'); % Batch normalization offset = parameters.batchnorm3.Offset; scale = parameters.batchnorm3.Scale; trainedMean = state.batchnorm3.TrainedMean; trainedVariance = state.batchnorm3.TrainedVariance; if doTraining [dlY,trainedMean,trainedVariance] = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); % Update state state.batchnorm3.TrainedMean = trainedMean; state.batchnorm3.TrainedVariance = trainedVariance; else dlY = batchnorm(dlY,offset,scale,trainedMean,trainedVariance); end % Addition, ReLU dlY = dlYSkip + dlY; dlY = relu(dlY); % Fully connect, softmax (labels) weights = parameters.fc1.Weights; bias = parameters.fc1.Bias; dlY1 = fullyconnect(dlY,weights,bias); dlY1 = softmax(dlY1); % Fully connect (angles) weights = parameters.fc2.Weights; bias = parameters.fc2.Bias; dlY2 = fullyconnect(dlY,weights,bias); end
The modelGradients
function, принимает параметры модели, мини-пакет входных данных dlX
с соответствующими целями T1
и T2
содержит метки и углы, соответственно, и возвращает градиенты потерь относительно настраиваемых параметров, обновленного состояния сети и соответствующих потерь.
function [gradients,state,loss] = modelGradients(parameters,dlX,T1,T2,state) doTraining = true; [dlY1,dlY2,state] = model(parameters,dlX,doTraining,state); lossLabels = crossentropy(dlY1,T1); lossAngles = mse(dlY2,T2); loss = lossLabels + 0.1*lossAngles; gradients = dlgradient(loss,parameters); end
The preprocessMiniBatch
функция предварительно обрабатывает данные с помощью следующих шагов:
Извлеките данные изображения из входящего массива ячеек и соедините в числовой массив. Конкатенация данных изображения по четвертому измерению добавляет третье измерение к каждому изображению, которое используется в качестве размерности одинарного канала.
Извлеките данные о метках и углах из входящих массивов ячеек и соедините вдоль второго измерения в категориальный массив и числовой массив, соответственно.
Однократное кодирование категориальных меток в числовые массивы. Кодирование в первую размерность создает закодированный массив, который совпадает с формой выходного сигнала сети.
function [X,Y,angle] = preprocessMiniBatch(XCell,YCell,angleCell) % Extract image data from cell and concatenate X = cat(4,XCell{:}); % Extract label data from cell and concatenate Y = cat(2,YCell{:}); % Extract angle data from cell and concatenate angle = cat(2,angleCell{:}); % One-hot encode labels Y = onehotencode(Y,1); end
batchnorm
| crossentropy
| dlarray
| dlconv
| dlfeval
| dlgradient
| fullyconnect
| minibatchqueue
| onehotdecode
| onehotencode
| relu
| sgdmupdate
| softmax