В глубоком обучении используются нейронные сетевые архитектуры, которые содержат много слоев обработки, включая сверточные слои. Модели глубокого обучения обычно работают с большими наборами маркированных данных. Настройка этих моделей и выполнение вывода является вычислительно интенсивным, потребляя значительный объем памяти. Нейронные сети используют память для хранения входных данных, параметров (весов) и активаций с каждого слоя, когда вход распространяется через сеть. Большинство предварительно обученных нейронных сетей и нейронных сетей, обученных с помощью Deep Learning Toolbox™, используют одноточные типы данных с плавающей точкой. Даже малые по размеру сети требуют значительного объема памяти и оборудования для выполнения этих арифметических операций с плавающей точкой. Эти ограничения могут препятствовать развертыванию моделей глубокого обучения на устройствах с низкой вычислительной степенью и меньшими ресурсами памяти. Используя более низкую точность для хранения весов и активаций, можно уменьшить требования сети к памяти.
Можно использовать Deep Learning Toolbox в паре с пакетом поддержки Deep Learning Toolbox Model Quantization Library, чтобы уменьшить площадь памяти глубокой нейронной сети путем квантования весов, смещений и активаций слоев свертки до 8-битных масштабированных целочисленных типов данных. Затем можно использовать GPU Coder™ для генерации оптимизированного CUDA® код для квантованной сети. Сгенерированный код использует преимущества NVIDIA® Библиотека глубоких нейронных сетей CUDA (cuDNN) или TensorRT™ библиотека вывода высокой эффективности. сгенерированный код может быть интегрирован в проект как исходный код, статические или динамические библиотеки или исполняемые файлы, которые можно развернуть на различных платформах графических процессоров NVIDIA.
В этом примере вы используете GPU Coder, чтобы сгенерировать код CUDA для квантованной глубокой сверточной нейронной сети и классифицировать изображение. В примере используется предварительно обученная squeezenet
(Deep Learning Toolbox) сверточная нейронная сеть для демонстрации передачи обучения, квантования и генерации кода CUDA для квантованной сети.
SqueezeNet обучен на более чем миллионе изображений и может классифицировать изображения в 1000 категорий объектов (таких как клавиатура, кофейная кружка, карандаш и многие животные). Сеть изучила представления богатых функций для широкой области значений изображений. Сеть принимает изображение как вход и выводит метку для объекта в изображении вместе с вероятностями для каждой из категорий объектов.
Необходимые условия для третьих лиц
Необходимый
CUDA включает графический процессор NVIDIA ® и совместимый драйвер.
Дополнительный
Для сборок, не являющихся MEX, таких как статические, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.
Инструментарий NVIDIA CUDA.
Библиотека NVIDIA cuDNN.
Переменные окружения для компиляторов и библиотек. Для получения дополнительной информации см. раздел «Оборудование сторонних производителей» и «Настройка продуктов для подготовки».
Передача обучения с помощью SqueezeNet
Чтобы выполнить классификацию на новом наборе изображений, вы подстраиваете предварительно обученную сверточную нейронную сеть SqueezeNet путем передачи обучения. В передачу обучения можно взять предварительно обученную сеть и использовать ее как начальная точка, чтобы узнать новую задачу. Подстройка сети с передачей обучения обычно намного быстрее и проще, чем обучение сети со случайным образом инициализированными весами с нуля. Можно быстро перенести выученные функции в новую задачу с помощью меньшего количества обучающих изображений.
Загрузка обучающих данных
Разархивируйте и загружайте новые изображения как image datastore. The imageDatastore
функция автоматически помечает изображения на основе имен папок и сохраняет данные как ImageDatastore
объект. image datastore позволяет вам хранить большие данные изображения, включая данные, которые не помещаются в памяти, и эффективно считывать пакеты изображений во время обучения сверточной нейронной сети. Разделите данные на наборы данных для обучения и валидации. Используйте 70% изображений для обучения и 30% для валидации. splitEachLabel
разделяет imds
datastore в два новых хранилища данных.
unzip('MerchData.zip'); imds = imageDatastore('MerchData', ... 'IncludeSubfolders',true, ... 'LabelSource','foldernames'); [imdsTrain,imdsValidation] = splitEachLabel(imds,0.7,'randomized'); numTrainImages = numel(imdsTrain.Labels); idx = randperm(numTrainImages,4); img = imtile(imds, 'Frames', idx); figure imshow(img) title('Random Images from Training Dataset');
Загрузка предварительно обученной сети
Загрузите предварительно обученную сеть SqueezeNet. Если у вас нет необходимых пакетов поддержки, программное обеспечение предоставляет ссылку для загрузки.
net = squeezenet;
Объект net
содержит DAGNetwork
объект. Первый слой, входной слой для изображений, требует входа изображений размера 227 227 3, где 3 количество цветовых каналов. Можно использовать analyzeNetwork
(Deep Learning Toolbox) функция для отображения интерактивной визуализации сетевой архитектуры, для обнаружения ошибок и проблем в сети, а также для отображения подробной информации о слоях сети. Информация о слое включает размеры активации слоя и настраиваемые параметры, общее количество настраиваемых параметров и размеры параметров состояния рекуррентных слоев.
inputSize = net.Layers(1).InputSize;
Замена конечных слоев
Сверточные слои сети извлекают изображение, функции последний выучиваемый слой и конечный слой классификации используют для классификации входа изображения. Эти два слоя, 'conv10'
и 'ClassificationLayer_predictions'
в SqueezeNet содержат информацию о том, как объединить функции, которые сеть извлекает в вероятности классов, значение потерь и предсказанные метки.
Чтобы переобучить предварительно обученную сеть для классификации новых изображений, замените эти два слоя новыми слоями, адаптированными к новому набору данных. Вы можете сделать это вручную или использовать функцию helper findLayersToReplace
для автоматического поиска этих слоев.
lgraph = layerGraph(net); [learnableLayer,classLayer] = findLayersToReplace(lgraph); numClasses = numel(categories(imdsTrain.Labels)); newConvLayer = convolution2dLayer([1, 1],numClasses,'WeightLearnRateFactor',... 10,'BiasLearnRateFactor',10,"Name",'new_conv'); lgraph = replaceLayer(lgraph,'conv10',newConvLayer); newClassificatonLayer = classificationLayer('Name','new_classoutput'); lgraph = replaceLayer(lgraph,'ClassificationLayer_predictions',newClassificatonLayer);
Обучите сеть
Сеть требует входа изображений размера 227 227 3, но изображения в хранилищах данных изображений имеют различные размеры. Используйте хранилище данных дополненных изображений, чтобы автоматически изменить размер обучающих изображений. Задайте дополнительные операции увеличения для выполнения на обучающих изображениях: случайным образом разверните обучающие изображения вдоль вертикальной оси и случайным образом переведите их до 30 пикселей горизонтально и вертикально. Увеличение количества данных помогает предотвратить сверхподбор кривой сети и запоминание точных деталей обучающих изображений.
pixelRange = [-30 30]; imageAugmenter = imageDataAugmenter( ... 'RandXReflection',true, ... 'RandXTranslation',pixelRange, ... 'RandYTranslation',pixelRange); augimdsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain, ... 'DataAugmentation',imageAugmenter);
Чтобы автоматически изменить размер изображений валидации, не выполняя дальнейшего увеличения данных, используйте хранилище datastore с дополненными изображениями, не задавая никаких дополнительных операций предварительной обработки.
augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);
Задайте опции обучения. Для передачи обучения сохраните функции из ранних слоев предварительно обученной сети (веса переданных слоев). Чтобы замедлить обучение в перенесенных слоях, установите начальную скорость обучения на небольшое значение. На предыдущем шаге вы увеличили коэффициенты скорости обучения для сверточного слоя, чтобы ускорить обучение в новых конечных слоях. Эта комбинация настроек скорости обучения приводит к быстрому обучению только в новых слоях и более медленному обучению в других слоях. При выполнении передачи обучения вам не нужно тренироваться на столько эпох. Эпоха - это полный цикл обучения на целом наборе обучающих данных. Задайте размер мини-пакета равным 11, чтобы в каждую эпоху вы учитывали все данные. Программное обеспечение проверяет сеть каждый ValidationFrequency
итерации во время обучения.
options = trainingOptions('sgdm', ... 'MiniBatchSize',11, ... 'MaxEpochs',7, ... 'InitialLearnRate',2e-4, ... 'Shuffle','every-epoch', ... 'ValidationData',augimdsValidation, ... 'ValidationFrequency',3, ... 'Verbose',false, ... 'Plots','training-progress');
Обучите сеть, которая состоит из переданного и нового слоев.
netTransfer = trainNetwork(augimdsTrain,lgraph,options);
classNames = netTransfer.Layers(end).Classes; save('mySqueezenet.mat','netTransfer');
Квантование сети
Создайте dlquantizer
объект и укажите сеть для квантования.
quantObj = dlquantizer(netTransfer);
Задайте метрическую функцию, которая будет использоваться для сравнения поведения сети до и после квантования.
type('hComputeModelAccuracy.m');
function accuracy = hComputeModelAccuracy(predictionScores, net, dataStore) %% Computes model-level accuracy statistics % Load ground truth tmp = readall(dataStore); groundTruth = tmp.response; % Compare with predicted label with actual ground truth predictionError = {}; for idx=1:numel(groundTruth) [~, idy] = max(predictionScores(idx,:)); yActual = net.Layers(end).Classes(idy); predictionError{end+1} = (yActual == groundTruth(idx)); %#ok end % Sum all prediction errors. predictionError = [predictionError{:}]; accuracy = sum(predictionError)/numel(predictionError); end
Задайте метрическую функцию в dlquantizationOptions
объект.
quantOpts = dlquantizationOptions('MetricFcn', ... {@(x)hComputeModelAccuracy(x,netTransfer,augimdsValidation)});
Используйте calibrate
функция для упражнения сети с выборочными входами и сбора информации о области значений. The calibrate
функция реализует сеть и собирает динамические области значений весов и смещений в свертках и полносвязных слоях сети и динамические области значений активаций во всех слоях сети. Функция возвращает таблицу. Каждая строка таблицы содержит информацию о области значений для настраиваемого параметра оптимизированной сети.
calResults = calibrate(quantObj,augimdsTrain); save('squeezenetCalResults.mat','calResults'); save('squeezenetQuantObj.mat','quantObj');
Можно использовать validate
функция для квантования настраиваемых параметров в слоях свертки сети и осуществления сети. Функция использует метрическую функцию, определенную в dlquantizationOptions
объект для сравнения результатов сети до и после квантования.
valResults = validate(quantObj,augimdsValidation,quantOpts);
Создайте функцию точки входа
Напишите функцию точки входа в MATLAB, которая:
Использует coder.loadDeepLearningNetwork
функция для загрузки модели глубокого обучения и создания и настройки класса CNN. Для получения дополнительной информации смотрите Загрузка предварительно обученных сетей для генерации кода.
Вызывает predict
функция для предсказания ответов.
type('predict_int8.m');
function out = predict_int8(netFile, in) persistent mynet; if isempty(mynet) mynet = coder.loadDeepLearningNetwork(netFile); end out = predict(mynet,in); end
Постоянный объект mynet
загружает DAGNetwork
объект. При первом вызове функции точки входа создается и настраивается постоянный объект. При последующих вызовах функции тот же объект повторно используется для вызова predict
на входах, избегая восстановления и перезагрузки объекта сети.
Примечание
Убедитесь, что все операции предварительной обработки, выполненные в шагах калибровки и валидации, включены в файл проекта.
Генерация кода при помощи codegen
Чтобы сконфигурировать настройки сборки, такие как имя выходного файла, расположение и тип, вы создаете объекты строения кодера. Чтобы создать объекты, используйте coder.gpuConfig
функция. Для примера при генерации CUDA MEX с помощью codegen
команда, использование cfg = coder.gpuConfig('mex');
Чтобы задать параметры генерации кода для cuDNN, установите DeepLearningConfig
свойство для coder.CuDNNConfig
объект, который вы создаете при помощи coder.DeepLearningConfig
.
cfg = coder.gpuConfig('mex'); cfg.TargetLang = 'C++'; cfg.GpuConfig.ComputeCapability = '6.1'; cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn'); cfg.DeepLearningConfig.AutoTuning = true; cfg.DeepLearningConfig.CalibrationResultFile = 'squeezenetQuantObj.mat'; cfg.DeepLearningConfig.DataType = 'int8';
Укажите местоположение MAT-файла, содержащего данные калибровки.
Задайте точность расчетов вывода в поддерживаемых слоях при помощи DataType
свойство. Для 8-битного целого числа используйте 'int8'
. Используйте ComputeCapability
свойство объекта строения кода, чтобы задать соответствующее вычислительное значение возможности.
Запуск codegen
команда. The codegen
команда генерирует код CUDA из predict_int8.m
Функция точки входа MATLAB.
inputs = {coder.Constant('mySqueezenet.mat'),ones(inputSize,'uint8')}; codegen -config cfg -args inputs predict_int8
Code generation successful.
Когда генерация кода успешна, можно просмотреть результат отчета генерации кода, нажав View Report в Командном Окне MATLAB. Отчет отображается в окне Средство Просмотра. Если генератор кода обнаруживает ошибки или предупреждения во время генерации кода, отчет описывает проблемы и предоставляет ссылки на проблемный код MATLAB.
Запуск сгенерированного MEX
Изображение, которое вы хотите классифицировать, должно иметь тот же размер, что и вход сигнала сети. Считайте изображение, которое вы хотите классифицировать, и измените его размер на вход сети. Это изменение размера немного изменяет соотношение сторон изображения.
testImage = imread("MerchDataTest.jpg");
testImage = imresize(testImage,inputSize(1:2));
Вызов SqueezeNet предсказать на вход изображении.
predictScores(:,1) = predict(netTransfer,testImage)';
predictScores(:,2) = predict_int8_mex('mySqueezenet.mat',testImage);
Отобразите предсказанные метки и связанные с ними вероятности в виде гистограммы.
h = figure; h.Position(3) = 2*h.Position(3); ax1 = subplot(1,2,1); ax2 = subplot(1,2,2); image(ax1,testImage); barh(ax2,predictScores) xlabel(ax2,'Probability') yticklabels(ax2,classNames) ax2.XLim = [0 1.1]; ax2.YAxisLocation = 'left'; legend('Matlab Single','cuDNN 8-bit integer'); sgtitle('Predictions using Squeezenet')
Вспомогательные функции
function [learnableLayer,classLayer] = findLayersToReplace(lgraph) % findLayersToReplace(lgraph) finds the single classification layer and the % preceding learnable (fully connected or convolutional) layer of the layer % graph lgraph. if ~isa(lgraph,'nnet.cnn.LayerGraph') error('Argument must be a LayerGraph object.') end % Get source, destination, and layer names. src = string(lgraph.Connections.Source); dst = string(lgraph.Connections.Destination); layerNames = string({lgraph.Layers.Name}'); % Find the classification layer. The layer graph must have a single % classification layer. isClassificationLayer = arrayfun(@(l) ... (isa(l,'nnet.cnn.layer.ClassificationOutputLayer') ... |isa(l,'nnet.layer.ClassificationLayer')), ... lgraph.Layers); if sum(isClassificationLayer) ~= 1 error('Layer graph must have a single classification layer.') end classLayer = lgraph.Layers(isClassificationLayer); % Traverse the layer graph in reverse starting from the classification % layer. If the network branches, throw an error. currentLayerIdx = find(isClassificationLayer); while true if numel(currentLayerIdx) ~= 1 msg = ['Layer graph must have a single learnable ' ... 'layer preceding the classification layer.']; error(msg); end currentLayerType = class(lgraph.Layers(currentLayerIdx)); isLearnableLayer = ismember(currentLayerType, ... ['nnet.cnn.layer.FullyConnectedLayer','nnet.cnn.layer.Convolution2DLayer']); if isLearnableLayer learnableLayer = lgraph.Layers(currentLayerIdx); return end currentDstIdx = find(layerNames(currentLayerIdx) == dst); currentLayerIdx = find(src(currentDstIdx) == layerNames); end end
При выполнении вывода в INT8
точность использования cuDNN версии 8.1.0, проблемы в библиотеке NVIDIA могут привести к значительному снижению эффективности.
Следующие слои не поддерживаются для 8-битного целочисленного квантования при нацеливании на библиотеку глубоких нейронных сетей (cuDNN) NVIDIA CUDA.
leakyReluLayer
clippedReluLayer
globalAveragePooling2dLayer
codegen
| coder.loadDeepLearningNetwork
| calibrate
(Deep Learning Toolbox) | dlquantizationOptions
(Deep Learning Toolbox) | dlquantizer
(Deep Learning Toolbox) | validate
(Deep Learning Toolbox)