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

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

Можно использовать Deep Learning Toolbox в тандеме с пакетом поддержки Библиотеки Квантования Модели Deep Learning Toolbox, чтобы уменьшать объем потребляемой памяти глубокой нейронной сети путем квантования весов, смещений и активаций слоев свертки к 8-битным масштабированным целочисленным типам данных. Затем можно использовать MATLAB Coder™, чтобы сгенерировать оптимизированный код для квантованной сети. Сгенерированный код использует в своих интересах процессор SIMD ARM® при помощи библиотеки ARM Compute. Сгенерированный код может быть интегрирован в ваш проект как исходный код, статические или динамические библиотеки или исполняемые файлы, которые можно развернуть во множество платформ центрального процессора ARM, таких как Raspberry Pi™.

В этом примере показано, как сгенерировать Код С++ для сверточной нейронной сети, которая использует ARM, Вычисляют Библиотеку, и выполняет расчеты вывода в 8-битных целых числах.

Этот пример не поддерживается для MATLAB Online.

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

Пример: классифицируйте изображения Используя SqueezeNet

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

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

Этот пример состоит из четырех шагов:

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

  2. Квантуйте модифицированную сеть SqueezeNet.

  3. Сгенерируйте код для квантованной сети при помощи codegen команды. Сгенерированный код работает на цели Raspberry Pi через выполнение PIL.

  4. Выполните сгенерированный MEX PIL на Raspberry Pi.

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

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

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

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

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

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

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

Это - findLayersToReplace Функция помощника:

type findLayersToReplace.m
% findLayersToReplace(lgraph) finds the single classification layer and the
% preceding learnable (fully connected or convolutional) layer of the layer
% graph lgraph.

function [learnableLayer,classLayer] = findLayersToReplace(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
        error('Layer graph must have a single learnable layer preceding the classification layer.')
    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

Чтобы использовать эту функцию, чтобы заменить последние слои, запустите эти команды:

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, но каждое изображение в хранилищах данных изображений имеет differnet размер. Используйте увеличенный 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);

Обучите сеть, которая состоит из переданных и новых слоев.

netTransfer = trainNetwork(augimdsTrain,lgraph,options);
classNames = netTransfer.Layers(end).Classes;
save('mySqueezenet.mat','netTransfer');

Квантуйте сеть

Создайте dlquantizer возразите и задайте сеть, чтобы квантовать.

quantObj = dlquantizer(netTransfer, 'ExecutionEnvironment', 'CPU');

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

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

Сгенерируйте MEX-функцию PIL

В этом примере вы генерируете код для функции точки входа predict_int8. Эта функция использует coder.loadDeepLearningNetwork функционируйте, чтобы загрузить модель глубокого обучения и создать и настроить класс CNN. Затем функция точки входа предсказывает ответы при помощи predict (Deep Learning Toolbox) функция.

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

Чтобы сгенерировать MEX-функцию PIL, создайте объект настройки кода для статической библиотеки и установите режим верификации на 'PIL'. Установите выходной язык на C++.

 cfg = coder.config('lib', 'ecoder', true);
 cfg.VerificationMode = 'PIL';
 cfg.TargetLang = 'C++';

Создайте объект настройки глубокого обучения для библиотеки ARM Compute и задайте версию библиотеки. В данном примере предположите, что ARM Вычисляет Библиотеку в оборудовании Raspberry Pi, версия 20.02.1.

 dlcfg = coder.DeepLearningConfig('arm-compute');
 dlcfg.ArmComputeVersion = '20.02.1';

Установите свойства dlcfg сгенерировать код для низкого вывода precision/INT8.

 dlcfg.CalibrationResultFile = 'squeezenetQuantObj.mat'; 
 dlcfg.DataType = 'int8';

6. Установите DeepLearningConfig свойство cfg к dlcfg.

 cfg.DeepLearningConfig = dlcfg;

7. Используйте Пакет поддержки MATLAB для функции Raspberry Pi, raspi, создать связь с Raspberry Pi. В следующем коде, замене:

  • raspiname с именем вашего Raspberry Pi

  • username с вашим именем пользователя

  • password с вашим паролем

%  r = raspi('raspiname','username','password');

8. Создайте coder.Hardware объект для Raspberry Pi и присоединения это к объекту настройки генерации кода.

% hw = coder.hardware('Raspberry Pi');
% cfg.Hardware = hw;

9. Сгенерируйте MEX-функцию PIL при помощи codegen команда

% codegen -config cfg predict_int8 -args {coder.Constant('mySqueezenet.mat'), ones(227,227,3,'uint8')}

Запустите сгенерированную MEX-функцию PIL на Raspberry Pi

Входное изображение, как ожидают, будет иметь тот же размер как входной размер сети. Считайте изображение, что вы хотите классифицировать и изменить размер его к входному размеру сети. Это изменение размеров немного изменяет соотношение сторон изображения.

% testImage = imread("MerchDataTest.jpg");
% testImage = imresize(testImage,inputSize(1:2));

Сравнить предсказания predict Deep Learning Toolbox функционируйте и generared PIL MEX-функция predict_int8_pil, вызовите оба этих funcions на входном изображении отдельно.

% predictScores(:,1) =  predict(netTransfer,testImage)';
% predictScores(:,2) = predict_int8_pil('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','arm-compute 8-bit integer');
% sgtitle('Predictions using Squeezenet')
% saveas(gcf,'SqueeznetPredictionComparison.jpg');
% close(gcf);
imshow('SqueeznetPredictionComparison.jpg');

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

Приложения

Функции

Объекты

Похожие темы