В глубоком обучении используются нейронные сетевые архитектуры, которые содержат много слоев обработки, включая сверточные слои. Модели глубокого обучения обычно работают с большими наборами маркированных данных. Выполнение вывода на этих моделях является вычислительно интенсивным, потребляя значительный объем памяти. Нейронные сети используют память для хранения входных данных, параметров (весов) и активаций с каждого слоя, когда вход распространяется через сеть. Глубокие нейронные сети, обученные в MATLAB, используют типы данных с одной точностью с плавающей точкой. Даже малые по размеру сети требуют значительного объема памяти и оборудования для выполнения этих арифметических операций с плавающей точкой. Эти ограничения могут препятствовать развертыванию моделей глубокого обучения на устройствах с низкой вычислительной степенью и меньшими ресурсами памяти. Используя более низкую точность для хранения весов и активаций, можно уменьшить требования сети к памяти.
Можно использовать Deep Learning Toolbox в паре с пакетом поддержки Deep Learning Toolbox Model Quantization Library, чтобы уменьшить площадь памяти глубокой нейронной сети путем квантования весов, смещений и активаций слоев свертки до 8-битных масштабированных целочисленных типов данных. Затем можно использовать MATLAB Coder™, чтобы сгенерировать оптимизированный код для квантованной сети. Сгенерированный код использует преимущества процессора ARM ® SIMD при помощи библиотеки ARM Compute. Сгенерированный код может быть интегрирован в ваш проект как исходный код, статические или динамические библиотеки или исполняемые файлы, которые можно развернуть на различных платформах ARM CPU, таких как Raspberry Pi™.
В этом примере показано, как сгенерировать код С++ для сверточной нейронной сети, которая использует ARM Compute Library и выполняет расчеты вывода в 8-битных целых числах.
Этот пример не поддерживается для MATLAB Online.
Необходимые условия для третьих лиц
Оборудование Raspberry Pi
ARM Compute Library (на целевом оборудовании ARM)
Переменные окружения для компиляторов и библиотек. Информацию о поддерживаемых версиях компиляторов и библиотек см. в разделе Оборудование и программное обеспечение сторонних производителей. Для настройки переменных окружений смотрите Окружение Переменные.
В этом примере вы используете MATLAB Coder, чтобы сгенерировать оптмизированный код С++ для квантованной глубокой сверточной нейронной сети и классифицировать изображение. В примере используется предварительно обученная squeezenet
(Deep Learning Toolbox) сверточная нейронная сеть.
SqueezeNet обучен на наборе данных ImageNet, содержащем изображения 1000 категорий объектов. Сеть изучила представления богатых функций для широкой области значений изображений. Сеть принимает изображение как вход и выводит метку для объекта в изображении вместе с вероятностями для каждой из категорий объектов.
Этот пример состоит из четырех шагов:
Измените нейронную сеть SqueezeNet, чтобы классифицировать меньшее подмножество изображений, содержащих пять категорий объектов, используя передачу обучения..
Квантуйте измененную сеть SqueezeNet.
Сгенерируйте код для квантованной сети с помощью команды codegen. Сгенерированный код запускается на цели Raspberry Pi посредством выполнения PIL.
Выполните сгенерированный PIL MEX на Raspberry Pi.
Чтобы выполнить классификацию на новом наборе изображений, необходимо подстроить предварительно обученную сверточную нейронную сеть SqueezeNet с помощью передачи обучения. В передачу обучения вы берете предварительно обученную сеть и используете ее как начальная точка, чтобы узнать новую задачу. Подстройка сети с помощью передачи обучения обычно намного быстрее и проще, чем обучение сети со случайным образом инициализированными весами с нуля. Можно быстро перенести выученные функции в новую задачу с помощью меньшего количества обучающих изображений.
Разархивируйте и загружайте новые изображения как image datastore. The imageDatastore
функция автоматически помечает изображения на основе имен папок и сохраняет данные как ImageDatastore
объект. image 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);
Сверточные слои сети извлекают изображение, функции последний выучиваемый слой и конечный слой классификации используют для классификации входа изображения. Эти два слоя, 'conv10'
и
'ClassificationLayer_predictions'
в SqueezeNet содержат информацию о том, как объединить функции, которые сеть извлекает в вероятности классов, значение потерь и предсказанные метки.
Чтобы переобучить предварительно обученную сеть для классификации новых изображений, замените эти два слоя новыми слоями, адаптированными к новому набору данных. Вы можете сделать это вручную или использовать функцию helper 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, но каждое изображение в хранилищах данных изображений имеет дифференцированный размер. Используйте хранилище данных дополненных изображений, чтобы автоматически изменить размер обучающих изображений. Задайте эти дополнительные операции увеличения, которые будут выполняться на обучающих изображениях: случайным образом разверните обучающие изображения вокруг вертикальной оси и случайным образом переведите их до 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
функция для упражнения сети с выборочными входами и сбора информации о области значений. The calibrate
функция реализует сеть и собирает динамические области значений весов и смещений в свертках и полносвязных слоях сети и динамические области значений активаций во всех слоях сети. Функция возвращает таблицу. Каждая строка таблицы содержит информацию о области значений для настраиваемого параметра оптимизированной сети.
calResults = quantObj.calibrate(augimdsTrain); save('squeezenetCalResults.mat','calResults'); save('squeezenetQuantObj.mat','quantObj');
В этом примере вы генерируете код для функции точки входа 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
Чтобы сгенерировать функцию PIL MEX, создайте объект строения кода для статической библиотеки и установите режим верификации равным 'PIL'
. Установите целевой язык на C++.
cfg = coder.config('lib', 'ecoder', true); cfg.VerificationMode = 'PIL'; cfg.TargetLang = 'C++';
Создайте объект строения глубокого обучения для библиотеки ARM Compute и укажите версию библиотеки. В данном примере предположим, что ARM Compute Library в оборудовании 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. Сгенерируйте функцию PIL MEX при помощи codegen
команда
% codegen -config cfg predict_int8 -args {coder.Constant('mySqueezenet.mat'), ones(227,227,3,'uint8')}
Предполагается, что вход изображения совпадает с размером входа сети. Считайте изображение, которое вы хотите классифицировать, и измените его размер на вход сети. Это изменение размера немного изменяет соотношение сторон изображения.
% testImage = imread("MerchDataTest.jpg"); % testImage = imresize(testImage,inputSize(1:2));
Сравнение предсказаний Deep Learning Toolbox predict
функция и сгенерированная функция PIL MEX predict_int8_pil
вызовите оба этих функциона на вход изображении отдельно.
% 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');
codegen
| coder.loadDeepLearningNetwork
| calibrate
(Deep Learning Toolbox) | dlquantizationOptions
(Deep Learning Toolbox) | dlquantizer
(Deep Learning Toolbox) | validate
(Deep Learning Toolbox)