В этом примере показано, как обучить и развернуть полностью сверточную сеть семантической сегментации на графическом процессоре NVIDIA ® с помощью GPU Coder™.
Семантическая сеть сегментации классифицирует каждый пиксель в изображении, получая к изображение, которое сегментировано по классам. Приложения для семантической сегментации включают сегментацию дорог для автономного управления автомобилем и сегментацию раковых камер для медицинского диагностирования. Дополнительные сведения см. в разделе Начало работы с семантической сегментацией с использованием глубокого обучения (Computer Vision Toolbox).
Чтобы проиллюстрировать процедуру обучения, этот пример обучает FCN-8s [1], один тип сверточной нейронной сети (CNN), предназначенной для семантической сегментации изображений. Другие типы сетей для семантической сегментации включают полностью сверточные сети, такие как SegNet и U-Net. Можно применить эту процедуру обучения и к этим сетям.
Этот пример использует набор данных CamVid [2] из Кембриджского университета для обучения. Этот набор данных представляет собой набор изображений, содержащих представления уличного уровня, полученные во время вождения. Набор данных обеспечивает пиксельные метки уровня для 32 семантических классов, включая автомобиль, пешехода и дорогу.
Графический процессор NVIDIA и совместимый драйвер с поддержкой CUDA ®.
Инструментарий NVIDIA CUDA.
Библиотека NVIDIA cuDNN.
Переменные окружения для компиляторов и библиотек. Информацию о поддерживаемых версиях компиляторов и библиотек см. в разделе Оборудование сторонних производителей (GPU Coder). Для настройки переменных окружения смотрите Настройка обязательных продуктов (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) для загрузки данных о пиксельных метках изображение. pixelLabelDatastore инкапсулирует данные о пиксельных метках и идентификатор метки в отображение имен классов.
Следуя методу обучения, описанному в документе SegNet [3], сгруппируйте 32 исходных класса в классах CamVid по 11. Задайте эти классы.
classes = [ "Sky" "Building" "Pole" "Road" "Pavement" "Tree" "SignSymbol" "Fence" "Car" "Pedestrian" "Bicyclist" ];
Чтобы уменьшить 32 класса до 11 классов, несколько классов из исходного набора данных сгруппированы вместе. Например, «Car» является комбинацией «Car», «SUVPickupTruck», «Truck_Bus,» «Train» и «OtherMoving». Возвращает сгруппированные идентификаторы меток с помощью вспомогательной функции camvid Pixel Label ID s.
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
(Computer Vision Toolbox). Эта функция отсчитывает количество пикселей по меткам классов.
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
(Computer Vision Toolbox), чтобы создать полностью сверточные сетевые слои, инициализированные при помощи VGG-16 весов. The fcnLayers
функция выполняет преобразования сети для передачи весов от VGG-16 и добавляет дополнительные слои, необходимые для семантической сегментации. Выходные выходы fcnLayers
функция является объектом LayerGraph, представляющим FCN. Объект LayerGraph инкапсулирует слои сети и соединения между слоями.
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
(Computer Vision Toolbox).
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
function, добавить новый при помощи addLayers
function, и соединить новый слой с остальной частью сети с помощью 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
(Computer Vision Toolbox) функция. The pixelLabelImageDatastore
функция считывает пакеты обучающих данных, применяет увеличение данных и отправляет дополненные данные в алгоритм настройки.
pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain, ... 'DataAugmentation',augmenter);
Если на doTraining
флаг true, запустите обучение с помощью trainNetwork
функция.
Обучение было проверено на NVIDIA™ Titan 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 для целевого языка MEX на C++. Используйте coder.DeepLearningConfig
(GPU Coder) функция для создания uDNN
c объект строения глубокого обучения и присвоение его
DeepLearningConfig
свойство объекта строения кода GPU. Запуск codegen
(MATLAB Coder), задающая размер входного сигнала [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);
The 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] Лонг, Дж., Э. Шелхамер и Т. Даррелл. «Полностью сверточные сети для семантической сегментации». Материалы Конференции IEEE по компьютерному зрению и распознаванию шаблонов, 2015, стр. 3431-3440.
[2] Brostow, G. J., J. Fauqueur, and R. Cipolla. Semantic object classes in video: A high-definition основная истина database (неопр.) (недоступная ссылка). Распознавание Букв. Том 30, Выпуск 2, 2009, стр. 88-97.
[3] Badrinarayanan, V., A. Kendall, and R. Cipolla. «SegNet: Архитектура глубокого сверточного энкодера-декодера для сегментации изображений». arXiv preprint arXiv:1511.00561, 2015.