В этом примере показано, как обучить и развернуть полностью сверточную сеть семантической сегментации на графическом процессоре NVIDIA® при помощи GPU Coder™.
Семантическая сеть сегментации классифицирует каждый пиксель в изображении, получая к изображение, которое сегментировано по классам. Приложения для семантической сегментации включают дорожную сегментацию для автономного управления автомобилем и сегментацию раковой клетки для медицинского диагностирования. Чтобы узнать больше, смотрите Начало работы С Семантической Сегментацией Используя Глубокое обучение (Computer Vision Toolbox).
Чтобы проиллюстрировать метод обучения, этот пример обучает FCN-8s [1], один тип сверточной нейронной сети (CNN), спроектированной для семантической сегментации изображений. Другие типы сетей для семантической сегментации включают полностью сверточные сети, такие как SegNet и U-Net. Можно применить этот метод обучения к тем сетям также.
Этот пример использует набор данных CamVid [2] из Кембриджского университета для обучения. Этот набор данных является набором изображений, содержащих представления уличного уровня, полученные при управлении. Набор данных обеспечивает метки пиксельного уровня для 32 семантических классов включая автомобиль, пешехода и дорогу.
CUDA® включил NVIDIA, графический процессор с вычисляет возможность 3.2 или выше.
NVIDIA инструментарий CUDA и драйвер.
Библиотека NVIDIA cuDNN.
Интерфейс GPU Coder для Библиотек Глубокого обучения поддерживает пакет. Чтобы установить этот пакет поддержки, используйте Add-On Explorer.
Модель Deep Learning Toolbox для пакета Сетевой поддержки VGG-16. Чтобы установить этот пакет поддержки, см. Модель Deep Learning Toolbox™ для Сети VGG-16.
Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Сторонние продукты. Для подготовки переменных окружения смотрите Подготовку Необходимых как условие продуктов.
Используйте coder.checkGpuInstall
функция, чтобы проверить, что компиляторы и библиотеки, необходимые для выполнения этого примера, настраиваются правильно.
envCfg = coder.gpuEnvConfig('host'); envCfg.DeepLibTarget = 'cudnn'; envCfg.DeepCodegen = 1; envCfg.Quiet = 1; coder.checkGpuInstall(envCfg);
Этот пример создает полностью сверточную сеть семантической сегментации с весами, инициализированными от сети VGG-16. Функция vgg16 проверяет на существование Модели Deep Learning Toolbox для пакета Сетевой поддержки VGG-16 и возвращает предварительно обученную модель 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
чтобы загрузить пиксель CamVid помечают данные изображения. PixelLabelDatastore инкапсулирует данные метки пикселя и идентификатор метки в сопоставление имен классов.
В соответствии с учебным методом, описанным в газете SegNet [3], сгруппируйте 32 исходных класса в CamVid к 11 классам. Задайте эти классы.
classes = [ "Sky" "Building" "Pole" "Road" "Pavement" "Tree" "SignSymbol" "Fence" "Car" "Pedestrian" "Bicyclist" ];
Чтобы уменьшать 32 класса в 11 классов, несколько классов от исходного набора данных группируются. Например, "Автомобиль" является комбинацией "Автомобиля", "SUVPickupTruck", "Truck_Bus", "Обучаются", и "OtherMoving". Возвратите сгруппированную метку IDs при помощи camvidPixelLabelIDs поддерживающий функции.
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 при помощи resizeCamVidImages и resizeCamVidPixelLabels поддерживающий функций.
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
создать полностью сверточные сетевые слои, инициализированные при помощи весов VGG-16. fcnLayers
функция выполняет трансформации сетей, чтобы передать веса от VGG-16 и добавляет дополнительные слои, требуемые для семантической сегментации. Выход fcnLayers
функция является объектом LayerGraph, представляющим FCN. Объект LayerGraph инкапсулирует сетевые слои и связи между слоями.
imageSize = [360 480]; numClasses = numel(classes); lgraph = fcnLayers(imageSize,numClasses);
Размер изображения выбран на основе размера изображений в наборе данных. Количество классов выбрано на основе классов в CamVid.
Классы в CamVid не сбалансированы. Чтобы улучшить обучение, можно использовать пиксельные количества метки, вычисленные ранее countEachLabel
функционируйте и вычислите веса класса медианной частоты [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, которая имеет новый pixelClassificationLayer путем удаления текущего pixelClassificationLayer и добавления нового слоя. Текущий pixelClassificationLayer называют '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' четыре уменьшает использование памяти в то время как обучение. Можно увеличить или уменьшить это значение на основе суммы памяти графического процессора в системе.
'CheckpointPath' установлен во временное местоположение. Эта пара "имя-значение" включает сохранение сетевых контрольных точек в конце каждой учебной эпохи. Если обучение прервано из-за системного отказа или отключения электроэнергии, можно возобновить обучение от сохраненной контрольной точки. Убедитесь, что местоположение, заданное 'CheckpointPath', имеет достаточно пробела, чтобы сохранить сетевые контрольные точки.
Увеличение данных предоставляет больше примеров сети, потому что это помогает улучшить точность сети. Здесь для увеличения данных используются случайное отражение "слева/справа" и случайный преобразование 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
флаг верен, запустите обучение при помощи trainNetwork
функция.
Обучение было проверено на Титане NVIDIA™ Xp с 12 Гбайт памяти графического процессора. Если ваш графический процессор имеет меньше памяти, у вас может закончиться память. Если у вас нет достаточной памяти в вашей системе, попытайтесь понизить 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);
Сгенерируйте объект GPU Configuration для цели MEX, устанавливающей выходной язык на C++. Используйте coder.DeepLearningConfig
функция, чтобы создать cuDNN
объект настройки глубокого обучения и присвоение это к DeepLearningConfig
свойство объекта настройки графического процессора кода. Запустите codegen
команда, задающая входной размер [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
переменная является 3D матрицей, имеющей 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] Brostow, G. J., J. Fauqueur, and R. Cipolla. "Semantic object classes in video: A high-definition ground truth database." Pattern Recognition Letters. Vol. 30, Issue 2, 2009, стр 88-97.
[3] Badrinarayanan, V., А. Кендалл и Р. Сиполла. "SegNet: Глубокая Сверточная Архитектура Декодера Энкодера для Сегментации Изображений". arXiv предварительно распечатывают arXiv:1511.00561, 2015.