Обучите остаточную сеть для классификации изображений

В этом примере показано, как создать глубокую нейронную сеть с остаточными связями и обучить ее на данных CIFAR-10. Остаточные связи являются популярным элементом в архитектурах сверточной нейронной сети. Используя остаточные связи улучшает поток градиента через сеть и включает обучение более глубоких сетей.

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

Чтобы создать и обучить сеть со структурой графика, выполните эти шаги.

  • Создайте LayerGraph объект с помощью layerGraph. График слоев задает сетевую архитектуру. Можно создать пустой график слоев и затем добавить слои в него. Можно также создать график слоев непосредственно из массива слоев сети. В этом случае, layerGraph соединяет слои в массиве один за другим.

  • Добавьте слои в график слоев с помощью addLayers, и удалите слои из графика с помощью removeLayers.

  • Соедините слои с другими слоями с помощью connectLayers, и отключите слои от других слоев с помощью disconnectLayers.

  • Постройте сетевую архитектуру с помощью plot.

  • Обучите сеть с помощью trainNetwork. Обучившим сеть является DAGNetwork объект.

  • Выполните классификацию и предсказание на новых данных с помощью classify и predict.

В этом примере показано, как создать остаточную сеть с нуля. Можно также создать остаточные сети с помощью resnetLayers функция. Эта функция позволяет вам быстро создавать остаточные сети для задач классификации изображений.

Можно также загрузить предварительно обученные сети для классификации изображений. Для получения дополнительной информации смотрите Предварительно обученные Глубокие нейронные сети.

Подготовка данных

Загрузите набор данных 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');

Архитектура сети Define

Остаточная сетевая архитектура состоит из этих компонентов:

  • Основная ветвь со сверточным, нормализацией партии. и слоями 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.

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

| | | | |

Похожие темы