Генерация кода для квантованных нейронных сетей для глубокого обучения

Глубокое обучение использует архитектуры нейронной сети, которые содержат много слоев обработки, включая сверточные слои. Модели глубокого обучения обычно работают над большими наборами маркированных данных. Обучение эти модели и выполняющий вывод в вычислительном отношении интенсивно, используя существенное количество памяти. Нейронные сети используют память, чтобы сохранить входные данные, параметры (веса) и активации от каждого слоя, когда вход распространяет через сеть. Большинство предварительно обученных нейронных сетей и нейронных сетей, обученных при помощи Deep Learning Toolbox™, использует типы данных с плавающей запятой с одинарной точностью. Даже сети, которые малы в размере, требуют, чтобы значительный объем памяти и оборудование выполнили эти арифметические операции с плавающей точкой. Эти ограничения могут запретить развертывание моделей глубокого обучения к устройствам, которые имеют низкую вычислительную силу и меньшие ресурсы памяти. При помощи более низкой точности, чтобы сохранить веса и активации, можно уменьшать требования к памяти сети.

Можно использовать Deep Learning Toolbox в тандеме с пакетом поддержки Библиотеки Квантования Модели Deep Learning Toolbox, чтобы уменьшать объем потребляемой памяти глубокой нейронной сети путем квантования весов, смещений и активаций слоев свертки к 8-битным масштабированным целочисленным типам данных. Затем можно использовать GPU Coder™, чтобы сгенерировать оптимизированный код CUDA® для квантованной сети. Сгенерированный код использует в своих интересах библиотеку глубокой нейронной сети NVIDIA® CUDA (cuDNN) или высокоэффективную библиотеку вывода TensorRT™. сгенерированный код может быть интегрирован в ваш проект как исходный код, статические или динамические библиотеки или исполняемые файлы, что можно развернуть в разнообразие NVIDIA платформы графического процессора.

Классифицируйте изображения на графическом процессоре Используя квантованную сеть

В этом примере вы используете GPU Coder, чтобы сгенерировать код CUDA для квантованной глубокой сверточной нейронной сети и классифицировать изображение. Пример использует предварительно обученный squeezenet Сверточная нейронная сеть (Deep Learning Toolbox), чтобы продемонстрировать передачу обучения, квантование и генерацию кода CUDA для квантованной сети.

SqueezeNet был обучен на более чем миллионе изображений и может классифицировать изображения в 1 000 категорий объектов (таких как клавиатура, кофейная кружка, карандаш и многие животные). Сеть изучила богатые представления функции для широкого спектра изображений. Сеть берет изображение в качестве входа и выводит метку для объекта в изображении вместе с вероятностями для каждой из категорий объектов.

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

Необходимый

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

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

Для сборок неMEX, таких как статические, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.

Передача обучения Используя SqueezeNet

Чтобы выполнить классификацию на новом наборе изображений, вы подстраиваете предварительно обученную сверточную нейронную сеть SqueezeNet, переводом учась. В передаче обучения можно взять предварительно обученную сеть и использовать ее в качестве начальной точки, чтобы изучить новую задачу. Подстройка сети с передачей обучения обычно намного быстрее и легче, чем обучение сети со случайным образом инициализированными весами с нуля. Можно быстро передать изученные функции новой задаче с помощью меньшего числа учебных изображений.

Загрузите обучающие данные

Разархивируйте и загрузите новые изображения как datastore изображений. imageDatastore функционируйте автоматически помечает изображения на основе имен папок и хранит данные как ImageDatastore объект. 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;

Замените последние слои

Сверточные слои сетевого извлечения отображают функции что последний learnable слой и итоговое использование слоя классификации, чтобы классифицировать входное изображение. Эти два слоя, 'conv10' и 'ClassificationLayer_predictions' в SqueezeNet содержите информацию о том, как сочетать функции, которые сеть извлекает в вероятности класса, значение потерь и предсказанные метки.

Чтобы переобучить предварительно обученную сеть, чтобы классифицировать новые изображения, замените эти два слоя на новые слои, адаптированные к новому набору данных. Можно сделать это вручную или использовать функцию помощника 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, но изображения в хранилищах данных изображений имеют различные размеры. Используйте увеличенный datastore изображений, чтобы автоматически изменить размер учебных изображений. Задайте дополнительные операции увеличения, чтобы выполнить на учебных изображениях: случайным образом инвертируйте учебные изображения вдоль вертикальной оси, и случайным образом переведите их до 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 функционируйте, чтобы осуществить сеть с демонстрационными входными параметрами и собрать информацию области значений. calibrate функционируйте осуществляет сеть и собирает динамические диапазоны весов и смещений в свертке и полносвязных слоях сети и динамические диапазоны активаций во всех слоях сети. Функция возвращает таблицу. Каждая строка таблицы содержит информацию об области значений для настраиваемого параметра оптимизированной сети.

calResults = calibrate(quantObj,augimdsTrain);
save('squeezenetCalResults.mat','calResults');
save('squeezenetQuantObj.mat','quantObj');

Можно использовать validate функция, чтобы квантовать настраиваемые параметры в слоях свертки сети и осуществить сеть. Функция использует метрическую функцию, заданную в dlquantizationOptions объект сравнить результаты сети до и после квантования.

valResults = validate(quantObj,augimdsValidation,quantOpts);

Создайте функцию точки входа

Напишите функцию точки входа в MATLAB что:

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 функция. Например, при генерации MEX CUDA с помощью 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 команда. 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. Отчет отображен в окне Report Viewer. Если генератор кода обнаруживает ошибки или предупреждения во время генерации кода, отчет описывает проблемы и обеспечивает ссылки на проблематичный код 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-битного целочисленного квантования при предназначении для библиотеки глубокой нейронной сети CUDA NVIDIA (cuDNN) библиотека.

    • leakyReluLayer

    • clippedReluLayer

    • globalAveragePooling2dLayer

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

Приложения

Функции

Объекты

Похожие темы