exponenta event banner

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

В этом примере показано, как создать нейронную сеть глубокого обучения с остаточными связями и обучить ее 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 объект, используемый для обучения сети. Во время обучения хранилище данных случайным образом разворачивает обучающие изображения вдоль вертикальной оси и случайным образом переводит их до четырех пикселей по горизонтали и вертикали. Увеличение объема данных помогает предотвратить переоборудование сети и запоминание точных деталей обучающих изображений.

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

  • Заключительный раздел с глобальным средним объединением, полностью подключенными уровнями, уровнями 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' на второй вход '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 - ширина сети, определяемая как количество фильтров в первых 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] Хэ, Каймин, Сянъу Чжан, Шаоцин Жэнь и Цзянь Сунь. «Глубокое остаточное обучение для распознавания изображения». В трудах конференции IEEE по компьютерному зрению и распознаванию образов, стр. 770-778. 2016.

См. также

| | |

Связанные темы