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

Этот пример показывает, как создать глубокую нейронную сеть обучения с остаточными связями и обучить ее на 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 с активациями. The netWidth параметром является ширина сети, заданная как количество фильтров в сверточных слоях на первом этапе сети. Первые сверточные модули на втором и третьем этапах понижают пространственные размерности в два раза. Чтобы сохранить количество расчетов, требуемых в каждом сверточном слое, примерно одинаковым по всей сети, увеличьте количество фильтров в два раза каждый раз, когда вы выполняете пространственную понижающую дискретизацию.

  • Заключительный раздел с глобальным средним объединением, полносвязными, softmax и классификационными слоями.

Использование convolutionalUnit(numF,stride,tag) для создания сверточного модуля. numF количество сверточных фильтров в каждом слое, stride - шаг первого сверточного слоя модуля, и tag представляет собой символьный массив для подготовки к именам слоев. The 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' слой ко второму входу '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 данных произвольной глубины и ширины, используйте вспомогательную функцию lestualCIFARlgraph.

lgraph = residualCIFARlgraph(netWidth,numUnits,unitType) создает график слоев для CIFAR-10 данных с остаточными соединениями.

  • netWidth - ширина сети, заданная как количество фильтров в первых сверточных слоях сети 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] Крижевский, Алекс. «Изучение нескольких слоев функций из крошечных изображений». (2009). https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf

[2] He, Kaiming, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. «Глубокое остаточное обучение для распознавания изображений». В материалах конференции IEEE по компьютерному зрению и распознаванию шаблонов, стр. 770-778. 2016.

См. также

| | |

Похожие темы