В этом примере показано, как создать глубокую нейронную сеть с остаточными связями и обучить ее на данных CIFAR-10. Остаточные связи являются популярным элементом в архитектуре сверточной нейронной сети. Используя остаточные связи улучшает поток градиента через сеть и включает обучение более глубоких сетей.
Для многих приложений, с помощью сети, которая состоит из простой последовательности слоев, достаточно. Однако некоторые приложения требуют сетей с более комплексной структурой графика, в которой слои могут иметь входные параметры от нескольких слоев и выходных параметров к нескольким слоям. Эти типы сетей часто называются сетями направленного графа без петель (DAG). Остаточная сеть является типом сети DAG, которая имеет невязку (или ярлык) связи, которые обходят основные сетевые слои. Остаточные связи позволяют градиентам параметра распространить более легко от выходного слоя до более ранних слоев сети, которая позволяет обучить более глубокие сети. Эта увеличенная сетевая глубина может привести к более высокой точности на более трудных задачах.
Чтобы создать и обучить сеть со структурой графика, выполните эти шаги.
Создайте LayerGraph
объект с помощью layerGraph
. График слоя задает сетевую архитектуру. Можно создать пустой график слоя и затем добавить слои в него. Можно также создать график слоя непосредственно из массива сетевых слоев. В этом случае, layerGraph
соединяет слои в массиве один за другим.
Добавьте слои в график слоя с помощью addLayers
, и удалите слои из графика с помощью removeLayers
.
Соедините слои с другими слоями с помощью connectLayers
, и отключите слои от других слоев с помощью disconnectLayers
.
Постройте сетевую архитектуру с помощью plot
.
Обучите сеть с помощью trainNetwork
. Обучившим сеть является DAGNetwork
объект.
Выполните классификацию и прогноз на новых данных с помощью classify
и predict
.
Можно также загрузить предварительно обученные сети для классификации изображений. Для получения дополнительной информации смотрите Предварительно обученные Глубокие нейронные сети.
Загрузите набор данных CIFAR-10 [1]. Набор данных содержит 60 000 изображений. Каждое изображение находится 32 32 в размере и имеет три цветовых канала (RGB). Размер набора данных составляет 175 Мбайт. В зависимости от вашего интернет-соединения может занять время процесс загрузки.
datadir = tempdir; downloadCIFARData(datadir);
Загрузите обучение CIFAR-10 и протестируйте изображения как 4-D массивы. Набор обучающих данных содержит 50 000 изображений, и набор тестов содержит 10 000 изображений. Используйте тестовые изображения CIFAR-10 в сетевой валидации.
[XTrain,YTrain,XValidation,YValidation] = loadCIFARData(datadir);
Можно отобразить случайную выборку учебных изображений с помощью следующего кода.
figure;
idx = randperm(size(XTrain,4),20);
im = imtile(XTrain(:,:,:,idx),'ThumbnailSize',[96,96]);
imshow(im)
Создайте augmentedImageDatastore
возразите, чтобы использовать в сетевом обучении. Во время обучения datastore случайным образом инвертирует учебные изображения вдоль вертикальной оси и случайным образом переводит их до четырех пикселей горизонтально и вертикально. Увеличение данных помогает препятствовать тому, чтобы сеть сверхсоответствовала и запомнила точные детали учебных изображений.
imageSize = [32 32 3]; pixelRange = [-4 4]; imageAugmenter = imageDataAugmenter( ... 'RandXReflection',true, ... 'RandXTranslation',pixelRange, ... 'RandYTranslation',pixelRange); augimdsTrain = augmentedImageDatastore(imageSize,XTrain,YTrain, ... 'DataAugmentation',imageAugmenter, ... 'OutputSizeMode','randcrop');
Остаточная сетевая архитектура состоит из этих компонентов:
Основная ветвь со сверточной, пакетной нормализацией и слои ReLU, соединенные последовательно.
Остаточные связи, которые обходят сверточные модули основной ветви. Выходные параметры остаточных связей и сверточных модулей добавляются поэлементные. Когда размер изменений активаций, остаточные связи должны также содержать сверточные слои 1 на 1. Остаточные связи позволяют градиентам параметра течь более легко от выходного слоя до более ранних слоев сети, которая позволяет обучить более глубокие сети.
Создайте основную ветвь
Запустите путем создания основной ветви сети. Основная ветвь содержит пять разделов.
Начальный раздел, содержащий изображение, ввел слой и начальную свертку с активацией.
Три этапа сверточных слоев с различными размерами элемента (32 32, 16 16, и 8 8). Каждый этап содержит сверточные модули N. В этой части примера, N = 2. Каждый сверточный модуль содержит два 3х3 сверточных слоя с активациями. netWidth
параметр является сетью width, заданной, когда количество просачивается сверточные слои в первой стадии сети. Первые сверточные модули на вторых и третьих стадиях прореживают пространственные размерности фактором два. Чтобы сохранить объем расчета требуемым в каждом сверточном слое примерно тем же самым в сети, увеличьте число фильтров по фактору двух каждых раз, когда вы выполняете пространственную субдискретизацию.
Итоговый раздел с глобальным средним объединением, полностью соединенным, softmax, и слои классификации.
Используйте convolutionalUnit(numF,stride,tag)
создать сверточный модуль. numF
количество сверточных фильтров в каждом слое, stride
шаг первого сверточного слоя модуля и tag
символьный массив должен предварительно ожидать к именам слоя. convolutionalUnit
функция задана в конце примера.
Дайте уникальные имена всем слоям. Слои в сверточных модулях имеют имена начиная с 'SjUk'
, где j
индекс этапа и k
индекс сверточного модуля в том этапе. Например, 'S2U1'
обозначает этап 2, модуль 1.
netWidth = 16; layers = [ imageInputLayer([32 32 3],'Name','input') 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') softmaxLayer('Name','softmax') classificationLayer('Name','classoutput') ];
Создайте график слоя из массива слоя. layerGraph
подключения все слои в layers
последовательно. Постройте график слоя.
lgraph = layerGraph(layers); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph);
Создайте остаточные связи
Добавьте остаточные связи вокруг сверточных модулей. Большинство остаточных связей не выполняет операций и просто добавляет поэлементный к выходным параметрам сверточных модулей.
Создайте остаточную связь из 'reluInp'
к 'add11'
слой. Поскольку вы задали количество входных параметров к слою сложения, чтобы быть два, когда вы создали слой, слой имеет два входных параметров с именами 'in1'
и 'in2'
. Последний слой первого сверточного модуля уже соединяется с 'in1'
входной параметр. Слой сложения затем суммирует выходные параметры первого сверточного модуля и 'reluInp'
слой.
Таким же образом соедините 'relu11'
слой к второму входу the 'add12'
слой. Проверяйте, что вы соединили слои правильно путем графического вывода графика слоя.
lgraph = connectLayers(lgraph,'reluInp','add11/in2'); lgraph = connectLayers(lgraph,'relu11','add12/in2'); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph);
Когда активации слоя в сверточных модулях изменяют размер (то есть, когда они прорежены пространственно и сверхдискретизированы в размерности канала), активации в остаточных связях должны также изменить размер. Измените размеры активации в остаточных связях при помощи сверточного слоя 1 на 1 вместе с его пакетным слоем нормализации.
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');
Измените размер активации в остаточной связи между вторыми и третьими стадиями другим сверточным слоем 1 на 1 вместе с его пакетным слоем нормализации.
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'); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph)
Чтобы создать график слоя с остаточными связями для данных CIFAR-10 произвольной глубины и ширины, используйте функцию поддержки residualCIFARlgraph.
lgraph = residualCIFARlgraph(netWidth,numUnits,unitType)
создает график слоя для данных CIFAR-10 с остаточными связями.
netWidth
сеть width, заданная, когда количество просачивается первые 3х3 сверточные слои сети.
numUnits
количество сверточных модулей в основной ветви сети. Поскольку сеть состоит из трех этапов, где каждый этап имеет то же количество сверточных модулей, numUnits
должно быть целочисленное кратное 3.
unitType
тип сверточного модуля, заданного как "standard"
или "bottleneck"
. Стандартный сверточный модуль состоит из двух 3х3 сверточных слоев. Узкое место сверточный модуль состоит из трех сверточных слоев: слой 1 на 1 для субдискретизации в размерности канала, 3х3 сверточный слой и слой 1 на 1 для повышающей дискретизации в размерности канала. Следовательно, узкое место сверточный модуль имеет на 50% более сверточные слои, чем стандартный модуль, но только половину количества пространственных 3х3 сверток. Два модульных типа имеют подобную вычислительную сложность, но общее количество распространения функций в остаточных связях в четыре раза больше при использовании модулей узкого места. Общая глубина, заданная как максимальное количество сверточных последовательных и полносвязные слоя, 2*numUnits
+ 2 для сетей со стандартными модулями и 3*numUnits
+ 2 для сетей с модулями узкого места.
Создайте остаточную сеть с девятью стандартными сверточными модулями (три модуля на этап) и ширина 16. Общая сетевая глубина 2*9+2 = 20.
numUnits = 9; netWidth = 16; lgraph = residualCIFARlgraph(netWidth,numUnits,"standard"); figure('Units','normalized','Position',[0.1 0.1 0.8 0.8]); plot(lgraph)
Задайте опции обучения. Обучите сеть в течение 80 эпох. Выберите темп обучения, который пропорционален мини-пакетному размеру, и уменьшайте темп обучения фактором 10 после 60 эпох. Подтвердите сеть однажды в эпоху с помощью данных о валидации.
miniBatchSize = 128; learnRate = 0.1*miniBatchSize/128; valFrequency = floor(size(XTrain,4)/miniBatchSize); options = trainingOptions('sgdm', ... 'InitialLearnRate',learnRate, ... 'MaxEpochs',80, ... 'MiniBatchSize',miniBatchSize, ... 'VerboseFrequency',valFrequency, ... 'Shuffle','every-epoch', ... 'Plots','training-progress', ... 'Verbose',false, ... 'ValidationData',{XValidation,YValidation}, ... 'ValidationFrequency',valFrequency, ... 'LearnRateSchedule','piecewise', ... 'LearnRateDropFactor',0.1, ... 'LearnRateDropPeriod',60);
Обучать сеть с помощью trainNetwork
, установите doTraining
отметьте к true
В противном случае загружает предварительно обученную сеть. Обучение сети на хорошем графическом процессоре занимает приблизительно два часа. Если у вас нет графического процессора, то обучение берет намного дольше.
doTraining = false; if doTraining trainedNet = trainNetwork(augimdsTrain,lgraph,options); else load('CIFARNet-20-16.mat','trainedNet'); end
Вычислите итоговую точность сети на наборе обучающих данных (без увеличения данных) и набор валидации.
[YValPred,probs] = classify(trainedNet,XValidation); validationError = mean(YValPred ~= YValidation); YTrainPred = classify(trainedNet,XTrain); trainError = mean(YTrainPred ~= YTrain); disp("Training error: " + trainError*100 + "%")
Training error: 2.862%
disp("Validation error: " + validationError*100 + "%")
Validation error: 9.76%
Постройте матрицу беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Сеть обычно путает кошек с собаками.
figure('Units','normalized','Position',[0.2 0.2 0.4 0.4]); cm = confusionchart(YValidation,YValPred); cm.Title = 'Confusion Matrix for Validation Data'; cm.ColumnSummary = 'column-normalized'; cm.RowSummary = 'row-normalized';
Можно отобразить случайную выборку девяти тестовых изображений вместе с их предсказанными классами и вероятностями тех классов с помощью следующего кода.
figure idx = randperm(size(XValidation,4),9); for i = 1:numel(idx) subplot(3,3,i) imshow(XValidation(:,:,:,idx(i))); prob = num2str(100*max(probs(idx(i),:)),3); predClass = char(YValPred(idx(i))); title([predClass,', ',prob,'%']) 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
[1] Krizhevsky, Алекс. "Изучая несколько слоев функций от крошечных изображений". (2009). https://www.cs.toronto.edu / ~ kriz/learning-features-2009-TR.pdf
[2] Он, Kaiming, Сянюй Чжан, Шаоцин Жэнь и Цзянь Сунь. "Глубокая невязка, учащаяся для распознавания изображений". В Продолжениях конференции по IEEE по компьютерному зрению и распознаванию образов, стр 770-778. 2016.
analyzeNetwork
| layerGraph
| trainNetwork
| trainingOptions