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

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

Семантическая сеть сегментации классифицирует каждый пиксель в изображении, получая к изображение, которое сегментировано по классам. Приложения для семантической сегментации включают сегментацию дорог для автономного управления автомобилем и сегментацию раковой клетки для медицинского диагностирования. Чтобы узнать больше, смотрите Начало работы с Семантической Сегментацией Используя Глубокое обучение (Computer Vision Toolbox).

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

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

Сторонние необходимые условия

Необходимый

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

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

Проверьте среду графического процессора

Используйте 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

Загрузите набор данных 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 классов, несколько классов от исходного набора данных группируются. Например, "Автомобиль" является комбинацией "Автомобиля", "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 (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

Изображения в наборе данных 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. 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 (Deep Learning Toolbox) функция, добавляет новая при помощи addLayers (Deep Learning Toolbox) функция и подключение новый слой к остальной части сети при помощи connectLayers (Deep Learning Toolbox) функция.

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

Выберите Training Options

Алгоритмом оптимизации для обучения является Адам, который выведен из адаптивной оценки момента. Используйте trainingOptions (Deep Learning Toolbox) функция, чтобы задать гиперпараметры, используемые для Адама.

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

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

'CheckpointPath' установлен во временное местоположение. Эта пара "имя-значение" включает сохранение сетевых контрольных точек в конце каждой учебной эпохи. Если обучение прервано из-за системного отказа или отключения электроэнергии, можно возобновить обучение с сохраненной контрольной точки. Убедитесь, что местоположение, заданное 'CheckpointPath', имеет достаточно пробела, чтобы сохранить сетевые контрольные точки.

Увеличение данных

Увеличение данных используется, чтобы улучшить сетевую точность путем случайного преобразования исходных данных во время обучения. При помощи увеличения данных можно добавить больше разнообразия в обучающие данные, не увеличивая число помеченных обучающих выборок. Чтобы применить то же случайное преобразование, чтобы и отобразить и данные о пиксельных метках используют объединение datastore и преобразование. Во-первых, объедините imdsTrain и pxdsTrain.

dsTrain = combine(imdsTrain, pxdsTrain);

Затем используйте datastore, преобразовывают, чтобы применить желаемое увеличение данных, заданное в функции поддержки augmentImageAndLabelЗдесь для увеличения данных используются случайное отражение "слева/справа" и случайный преобразование X/Y на +/-10 пикселей.

xTrans = [-10 10];
yTrans = [-10 10];
dsTrain = transform(dsTrain, @(data)augmentImageAndLabel(data,xTrans,yTrans));

Обратите внимание на то, что увеличение данных не применяется к данным о валидации и тесту. Идеально, тест и данные о валидации должны быть представительными для исходных данных и оставлены немодифицированными для несмещенной оценки.

Запустите обучение

Запустите обучение с помощью trainNetwork если doTraining флаг верен. В противном случае загружает предварительно обученную сеть.

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

doTraining = false;
if doTraining    
    [net, info] = trainNetwork(dsTrain,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);

Сгенерируйте объект 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

Запустите сгенерированный MEX

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

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;

Вспомогательные Функции

function data = augmentImageAndLabel(data, xTrans, yTrans)
% Augment images and pixel label images using random reflection and
% translation.

for i = 1:size(data,1)
    
    tform = randomAffine2d(...
        'XReflection',true,...
        'XTranslation', xTrans, ...
        'YTranslation', yTrans);
    
    % Center the view at the center of image in the output space while
    % allowing translation to move the output image out of view.
    rout = affineOutputView(size(data{i,1}), tform, 'BoundsStyle', 'centerOutput');
    
    % Warp the image and pixel labels using the same transform.
    data{i,1} = imwarp(data{i,1}, tform, 'OutputView', rout);
    data{i,2} = imwarp(data{i,2}, tform, 'OutputView', rout);
    
end
end

Ссылки

[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.

Смотрите также

Функции

Объекты

Связанные примеры

Больше о

Для просмотра документации необходимо авторизоваться на сайте