В этом примере показано, как обучить и развернуть полностью сверточную сеть семантической сегментации на графическом процессоре NVIDIA ® с помощью графического процессора Coder™.
Семантическая сеть сегментации классифицирует каждый пиксель в изображении, в результате чего изображение сегментируется по классу. Приложения для семантической сегментации включают сегментацию дорог для автономного вождения и сегментацию раковых клеток для медицинской диагностики. Дополнительные сведения см. в разделе Начало работы с семантической сегментацией с помощью глубокого обучения (панель инструментов компьютерного зрения).
Чтобы проиллюстрировать процедуру обучения, этот пример обучает FCN-8s [1], один тип сверточной нейронной сети (CNN), предназначенной для сегментации семантического изображения. Другие типы сетей для семантической сегментации включают полностью сверточные сети, такие как SegNet и U-Net. Эту процедуру обучения можно применить и к этим сетям.
В этом примере для обучения используется набор данных CamVid [2] Кембриджского университета. Этот набор данных представляет собой совокупность изображений, содержащих виды на уровне улиц, полученные за рулем. Набор данных предоставляет пиксельные метки для 32 семантических классов, включая автомобильный, пешеходный и дорожный.
Графический процессор NVIDIA с поддержкой CUDA ® и совместимый драйвер.
Инструментарий NVIDIA CUDA.
Библиотека NVIDIA cuDNN.
Переменные среды для компиляторов и библиотек. Сведения о поддерживаемых версиях компиляторов и библиотек см. в разделе Аппаратное обеспечение сторонних производителей (GPU Coder). Сведения о настройке переменных среды см. в разделе Настройка необходимых продуктов (кодер графического процессора).
Используйте 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 с этих 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, чтобы указать расположение загруженного файла.
Использовать imageDatastore для загрузки изображений CamVid. imageDatastore позволяет эффективно загружать большую коллекцию образов на диск.
imgDir = fullfile(outputFolder,'images','701_StillsRaw_full'); imds = imageDatastore(imgDir);
Отображение одного из изображений.
I = readimage(imds,25); I = histeq(I); imshow(I)

Использовать 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 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-файл используется при создании кода.
Функция 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
Загрузка и отображение входного изображения.
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.