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

Этот пример показывает, как создать глубокую нейронную сеть с остаточными связями и обучить ее на данных 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');

Архитектура сети 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.

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

| | |

Похожие темы