exponenta event banner

Обучение и развертывание полностью свернутых сетей для семантической сегментации

В этом примере показано, как обучить и развернуть полностью сверточную сеть семантической сегментации на графическом процессоре NVIDIA ® с помощью графического процессора Coder™.

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

Чтобы проиллюстрировать процедуру обучения, этот пример обучает FCN-8s [1], один тип сверточной нейронной сети (CNN), предназначенной для сегментации семантического изображения. Другие типы сетей для семантической сегментации включают полностью сверточные сети, такие как SegNet и U-Net. Эту процедуру обучения можно применить и к этим сетям.

В этом примере для обучения используется набор данных CamVid [2] Кембриджского университета. Этот набор данных представляет собой совокупность изображений, содержащих виды на уровне улиц, полученные за рулем. Набор данных предоставляет пиксельные метки для 32 семантических классов, включая автомобильный, пешеходный и дорожный.

Предварительные условия для сторонних производителей

Необходимый

  • Графический процессор NVIDIA с поддержкой CUDA ® и совместимый драйвер.

Дополнительный

Проверка среды графического процессора

Используйте coder.checkGpuInstall Функция (GPU Coder) для проверки правильности настройки компиляторов и библиотек, необходимых для выполнения этого примера.

envCfg = coder.gpuEnvConfig('host');
envCfg.DeepLibTarget = 'cudnn';
envCfg.DeepCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

Установка

В этом примере создается полностью сверточная сеть семантической сегментации с весами, инициализированными из VGG-16 сети. Функция vgg16 проверяет наличие модели Deep Learning Toolbox для пакета поддержки VGG-16 Network и возвращает предварительно подготовленную модель VGG-16.

vgg16();

Загрузите предварительно подготовленную версию FCN. Эта предварительно подготовленная модель позволяет выполнять весь пример, не дожидаясь завершения обучения. Флаг doTraining определяет, используется ли в примере обученная сеть примера или предварительно обученная сеть FCN для генерации кода.

doTraining = false;
if ~doTraining
    pretrainedURL = 'https://www.mathworks.com/supportfiles/gpucoder/cnn_models/fcn/FCN8sCamVid.mat';
    disp('Downloading pretrained FCN (448 MB)...');
    websave('FCN8sCamVid.mat',pretrainedURL);
end
Downloading pretrained FCN (448 MB)...

Загрузить набор данных CamVid

Загрузите набор данных CamVid с этих URL-адресов.

imageURL = 'http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/files/701_StillsRaw_full.zip';
labelURL = 'http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/data/LabeledApproved_full.zip';

outputFolder = fullfile(pwd,'CamVid');

if ~exist(outputFolder, 'dir')
   
    mkdir(outputFolder)
    labelsZip = fullfile(outputFolder,'labels.zip');
    imagesZip = fullfile(outputFolder,'images.zip');   
    
    disp('Downloading 16 MB CamVid dataset labels...'); 
    websave(labelsZip, labelURL);
    unzip(labelsZip, fullfile(outputFolder,'labels'));
    
    disp('Downloading 557 MB CamVid dataset images...');  
    websave(imagesZip, imageURL);       
    unzip(imagesZip, fullfile(outputFolder,'images'));    
end

Время загрузки данных зависит от подключения к Интернету. Выполнение примера не продолжается до завершения операции загрузки. Либо загрузите набор данных на локальный диск с помощью веб-браузера. Затем используйте переменную outputFolder, чтобы указать расположение загруженного файла.

Загрузить изображения CamVid

Использовать imageDatastore для загрузки изображений CamVid. imageDatastore позволяет эффективно загружать большую коллекцию образов на диск.

imgDir = fullfile(outputFolder,'images','701_StillsRaw_full');
imds = imageDatastore(imgDir);

Отображение одного из изображений.

I = readimage(imds,25);
I = histeq(I);
imshow(I)

Загрузка изображений с меткой пикселя CamVid

Использовать pixelLabelDatastore (Computer Vision Toolbox) для загрузки данных изображения пиксельной метки CamVid. PixelLabelDatastore инкапсулирует данные пиксельной метки и идентификатор метки в соответствие имени класса.

Следуя методике обучения, описанной в документе SegNet [3], сгруппируйте 32 оригинальных класса в CamVid в 11 классов. Укажите эти классы.

classes = [
    "Sky"
    "Building"
    "Pole"
    "Road"
    "Pavement"
    "Tree"
    "SignSymbol"
    "Fence"
    "Car"
    "Pedestrian"
    "Bicyclist"
    ];

Чтобы свести 32 класса к 11 классам, несколько классов из исходного набора данных группируются вместе. Например, «Вагон» - это сочетание «Вагон», «СУВПиккупГрузовик», «Truck_Bus,» «Поезд» и «OtherMoving». Возвращает сгруппированные идентификаторы меток с помощью функции поддержки camvidPixelLabelID.

labelIDs = camvidPixelLabelIDs();

Используйте классы и идентификаторы меток для создания pixelLabelDatastore.

labelDir = fullfile(outputFolder,'labels');
pxds = pixelLabelDatastore(labelDir,classes,labelIDs);

Считывание и отображение одного из помеченных пикселями изображений путем наложения его поверх изображения.

C = readimage(pxds,25);
cmap = camvidColorMap;
B = labeloverlay(I,C,'ColorMap',cmap);
imshow(B)
pixelLabelColorbar(cmap,classes);

Области без наложения цветов не имеют пиксельных меток и не используются во время обучения.

Анализ статистики набора данных

Чтобы просмотреть распределение меток классов в наборе данных CamVid, используйте countEachLabel(Панель инструментов компьютерного зрения). Эта функция подсчитывает количество пикселей по метке класса.

tbl = countEachLabel(pxds)
tbl=11×3 table
         Name         PixelCount    ImagePixelCount
    ______________    __________    _______________

    {'Sky'       }    7.6801e+07      4.8315e+08   
    {'Building'  }    1.1737e+08      4.8315e+08   
    {'Pole'      }    4.7987e+06      4.8315e+08   
    {'Road'      }    1.4054e+08      4.8453e+08   
    {'Pavement'  }    3.3614e+07      4.7209e+08   
    {'Tree'      }    5.4259e+07       4.479e+08   
    {'SignSymbol'}    5.2242e+06      4.6863e+08   
    {'Fence'     }    6.9211e+06       2.516e+08   
    {'Car'       }    2.4437e+07      4.8315e+08   
    {'Pedestrian'}    3.4029e+06      4.4444e+08   
    {'Bicyclist' }    2.5912e+06      2.6196e+08   

Визуализация количества пикселей по классам.

frequency = tbl.PixelCount/sum(tbl.PixelCount);

bar(1:numel(classes),frequency)
xticks(1:numel(classes)) 
xticklabels(tbl.Name)
xtickangle(45)
ylabel('Frequency')

В идеале все классы имеют равное количество наблюдений. Классы в CamVid разбалансированы, что является обычным вопросом в автомобильных наборах данных уличных сцен. Такие сцены имеют больше пикселей неба, здания и дороги, чем пиксели пешеходов и велосипедистов, потому что небо, здания и дороги покрывают большую площадь изображения. В случае неправильной обработки этот дисбаланс может нанести ущерб процессу обучения, поскольку обучение смещено в пользу доминирующих классов. Далее в этом примере для решения этой проблемы используется взвешивание класса.

Изменение размера данных CamVid

Изображения в наборе данных CamVid 720 на 960. Чтобы сократить время обучения и использование памяти, измените размер изображений и пиксельных меток до 360 на 480 с помощью функций resityCamVidImages и resityCamVidPixelLabels.

imageFolder = fullfile(outputFolder,'imagesResized',filesep);
imds = resizeCamVidImages(imds,imageFolder);

labelFolder = fullfile(outputFolder,'labelsResized',filesep);
pxds = resizeCamVidPixelLabels(pxds,labelFolder);

Подготовка обучающих и тестовых наборов

SegNet обучается с использованием 60% изображений из набора данных. Остальные изображения используются для тестирования. Следующий код случайным образом разбивает данные изображения и метки пикселя на обучающий набор и тестовый набор.

[imdsTrain,imdsTest,pxdsTrain,pxdsTest] = partitionCamVidData(imds,pxds);

Разделение 60/40 приводит к следующему количеству тренировочных и тестовых изображений:

numTrainingImages = numel(imdsTrain.Files)
numTrainingImages = 421
numTestingImages = numel(imdsTest.Files)
numTestingImages = 280

Создать сеть

Использовать fcnLayers (Computer Vision Toolbox) для создания полностью сверточных сетевых уровней, инициализированных с помощью весов VGG-16. fcnLayers функция выполняет сетевые преобразования для переноса весов из VGG-16 и добавляет дополнительные уровни, необходимые для семантической сегментации. Выходные данные fcnLayers функция является объектом LeyGraph, представляющим FCN. Объект LeyGraph инкапсулирует сетевые слои и соединения между ними.

imageSize = [360 480];
numClasses = numel(classes);
lgraph = fcnLayers(imageSize,numClasses);

Размер изображения выбирается на основе размера изображений в наборе данных. Количество классов выбирается на основе классов в CamVid.

Классы сальдо с использованием взвешивания классов

Классы в CamVid не сбалансированы. Чтобы улучшить обучение, можно использовать количество меток пикселей, вычисленное ранее countEachLabel (Computer Vision Toolbox) и вычислите средние весовые коэффициенты класса частот [3].

imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq) ./ imageFreq;

Укажите веса классов с помощью pixelClassificationLayer(Панель инструментов компьютерного зрения).

pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights)
pxLayer = 
  PixelClassificationLayer with properties:

            Name: 'labels'
         Classes: [11×1 categorical]
    ClassWeights: [11×1 double]
      OutputSize: 'auto'

   Hyperparameters
    LossFunction: 'crossentropyex'

Обновите сеть SegNet, содержащую новый pixelLayer, удалив текущий pixelLayer и добавив новый слой. Текущему pixelLayer присвоено имя pixelLabels. Удалите его с помощью removeLayers добавьте новую функцию, используя addLayers и подключите новый уровень к остальной части сети с помощью connectLayers функция.

lgraph = removeLayers(lgraph,'pixelLabels');
lgraph = addLayers(lgraph, pxLayer);
lgraph = connectLayers(lgraph,'softmax','labels');

Выбор параметров обучения

Алгоритм оптимизации для обучения - Адам, который выводится из адаптивной оценки момента. Используйте trainingOptions для указания гиперпараметров, используемых для Адама.

options = trainingOptions('adam', ...
    'InitialLearnRate',1e-3, ...
    'MaxEpochs',100, ...  
    'MiniBatchSize',4, ...
    'Shuffle','every-epoch', ...
    'CheckpointPath', tempdir, ...
    'VerboseFrequency',2);

«MiniBatchSize» из четырех элементов уменьшает использование памяти во время обучения. Это значение можно увеличить или уменьшить в зависимости от объема памяти графического процессора в системе.

Для параметра CheckstartPath задано временное расположение. Эта пара имя-значение позволяет сохранять контрольные точки сети в конце каждого периода обучения. Если обучение прервано из-за сбоя системы или отключения питания, вы можете возобновить обучение с сохраненной контрольной точки. Убедитесь, что в расположении, указанном с помощью команды «» CheckstartPath «», достаточно места для хранения контрольных точек сети.

Увеличение объема данных

Увеличение объема данных предоставляет больше примеров для сети, поскольку это помогает повысить точность сети. Здесь для увеличения данных используют случайное левое/правое отражение и случайное X/Y-преобразование +/- 10 пикселей. Используйте imageDataAugmenter для задания этих параметров увеличения данных.

augmenter = imageDataAugmenter('RandXReflection',true,...
    'RandXTranslation',[-10 10],'RandYTranslation',[-10 10]);

The imageDataAugmenter поддерживает несколько других типов увеличения данных. Выбор среди них требует эмпирического анализа и является другим уровнем гиперпараметрической настройки.

Начать обучение

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

pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain, ...
    'DataAugmentation',augmenter);

Если doTraining true, начать обучение с помощью trainNetwork функция.

Обучение было проверено на NVIDIA™ Titan Xp с 12 ГБ памяти GPU. Если на графическом процессоре меньше памяти, может не хватить памяти. Если в системе недостаточно памяти, попробуйте опустить MiniBatchSize свойство в trainingOptions на 1. Обучение в этой сети занимает около 5 часов или более в зависимости от оборудования графического процессора.

if doTraining    
    [net, info] = trainNetwork(pximds,lgraph,options);
    save('FCN8sCamVid.mat','net');
end

Сохранить сетевой объект DAG как MAT-файл с именем FCN8sCamVid.mat. Этот MAT-файл используется при создании кода.

Создание кода MEX

Функция fcn_predict.m принимает входные данные изображения и выполняет прогнозирование изображения с использованием сети глубокого обучения, сохраненной в FCN8sCamVid.mat файл. Функция загружает сетевой объект из FCN8sCamVid.mat в постоянный переменный mynet и повторно использует постоянный объект при последующих вызовах прогнозирования.

type('fcn_predict.m')
function out = fcn_predict(in)
%#codegen
% Copyright 2018-2019 The MathWorks, Inc.

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('FCN8sCamVid.mat');
end

% pass in input
out = predict(mynet,in);

Создайте объект конфигурации графического процессора для целевого языка MEX, задающего язык назначения C++. Используйте coder.DeepLearningConfig (Кодер графического процессора) для создания cuDNN глубокий объект конфигурации обучения и назначить его DeepLearningConfig свойства объекта конфигурации кода графического процессора. Запустить codegen (Кодер MATLAB), указывающий размер ввода [360, 480, 3]. Этот размер соответствует входному уровню FCN.

cfg = coder.gpuConfig('mex');
cfg.TargetLang = 'C++';
cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
codegen -config cfg fcn_predict -args {ones(360,480,3,'uint8')} -report
Code generation successful: View report

Выполнить сгенерированный MEX

Загрузка и отображение входного изображения.

im = imread('testImage.png');
imshow(im);

Выполнить прогнозирование путем вызова fcn_predict_mex на входном изображении.

predict_scores = fcn_predict_mex(im);

predict_scores переменная является трехмерной матрицей, имеющей 11 каналов, соответствующих баллам пиксельного прогнозирования для каждого класса. Вычислите канал, используя максимальную оценку прогнозирования, чтобы получить пиксельные метки.

[~,argmax] = max(predict_scores,[],3);

Наложение сегментированных меток на входное изображение и отображение сегментированной области.

classes = [
    "Sky"
    "Building"
    "Pole"
    "Road"
    "Pavement"
    "Tree"
    "SignSymbol"
    "Fence"
    "Car"
    "Pedestrian"
    "Bicyclist"
    ];

cmap = camvidColorMap();
SegmentedImage = labeloverlay(im,argmax,'ColorMap',cmap);
figure
imshow(SegmentedImage);
pixelLabelColorbar(cmap,classes);

Очистка

Очистить статический сетевой объект, загруженный в память.

clear mex;

Ссылки

[1] Долго, J., Э. Шелхэмер и Т. Даррелл. «Полностью сверточные сети для семантической сегментации». Материалы Конференции IEEE по компьютерному зрению и распознаванию образов, 2015 год, стр. 3431-3440.

[2] Бростоу, G. J., J. Fauqueur и R. Cipolla. «Классы семантических объектов в видео: база данных истинности земли высокой четкости». Буквы распознавания образов. Том 30, выпуск 2, 2009, стр. 88-97.

[3] Бадринараянан, В., А. Кендалл и Р. Чиполла. «SegNet: архитектура глубокого сверточного кодера-декодера для сегментации изображений». arXiv препринт arXiv:1511.00561, 2015.