В этом примере показано, как создать и обучить сеть глубокого обучения с помощью функций, а не графа уровня или dlnetwork. Преимуществом использования функций является гибкость описания широкого спектра сетей. Недостаток заключается в том, что необходимо выполнить дополнительные шаги и тщательно подготовить данные. В этом примере используются изображения рукописных цифр с двойными целями классификации цифр и определения угла каждой цифры по вертикали.
digitTrain4DArrayData функция загружает изображения, их цифровые метки и углы поворота от вертикали. Создать arrayDatastore объекты для изображений, меток и углов, а затем используйте combine для создания единого хранилища данных, содержащего все данные обучения. Извлеките имена классов и количество недискретных ответов.
[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)

Определите следующую сеть, которая предсказывает как метки, так и углы поворота.
Блок свертки-дозирования-ReLU с 16 фильтрами 5 на 5.
Ветвь из двух блоков свертки-последовательности с 32 фильтрами 3 на 3 с операцией ReLU между
Соединение пропуска с блоком свертки-последовательности с 32 свертками 1 на 1.
Объединение обеих ветвей с помощью добавления с последующей операцией ReLU
Для выхода регрессии ветвь с полностью связанной операцией размера 1 (количество откликов).
Для вывода классификации ветвь с полностью связанной операцией размера 10 (количество классов) и операцией softmax.

Определите параметры для каждой операции и включите их в структуру. Использовать формат parameters.OperationName.ParameterName где parameters - структура, OperationName - имя операции (например, «conv1») и ParameterName - имя параметра (например, «Веса»).
Создание структуры parameters содержащий параметры модели. Инициализируйте веса и смещения обучаемого слоя с помощью initializeGlorot и initializeZeros примерные функции соответственно. Инициализируйте параметры смещения и масштабирования пакетной нормализации с помощью initializeZeros и initializeOnes примерные функции соответственно.
Для выполнения обучения и вывода с использованием уровней пакетной нормализации необходимо также управлять состоянием сети. Перед прогнозированием необходимо указать среднее значение набора данных и дисперсию, полученные из данных обучения. Создание структуры 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]);
Просмотрите структуру параметров.
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]
Просмотр структуры состояния.
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;
Для контроля хода обучения можно построить график потерь обучения после каждой итерации. Создайте графики переменных, содержащие «training-progress». Если график хода обучения не требуется, установите для этого значения значение «none».
plots = "training-progress";Использовать minibatchqueue для обработки и управления мини-партиями изображений. Для каждой мини-партии:
Использование пользовательской функции предварительной обработки мини-партии preprocessMiniBatch (определяется в конце этого примера) для одноконтактного кодирования меток класса.
Форматирование данных изображения с метками размеров 'SSCB' (пространственный, пространственный, канальный, пакетный). По умолчанию minibatchqueue объект преобразует данные в dlarray объекты с базовым типом single. Не добавляйте формат к меткам класса или углам.
Обучение на GPU, если он доступен. По умолчанию minibatchqueue объект преобразует каждый вывод в gpuArray если графический процессор доступен. Для использования графического процессора требуется Toolbox™ параллельных вычислений и поддерживаемое устройство графического процессора. Сведения о поддерживаемых устройствах см. в разделе Поддержка графического процессора по выпуску (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
modelGradients функция, принимает параметры модели, мини-пакет входных данных 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
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