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

В этом примере показано, как обучить и развернуть полностью сверточную сеть семантической сегментации на графическом процессоре 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

Загрузите набор данных 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 чтобы загрузить пиксель 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

Изображения в наборе данных 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');

Выберите Training Options

Алгоритмом оптимизации для обучения является Адам, который выведен из адаптивной оценки момента. Используйте 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-файл используется во время генерации кода.

Выполните генерацию кода 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;

Ссылки

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