Глубокое обучение использует архитектуры нейронных сетей, которые содержат множество уровней обработки, включая сверточные уровни. Модели глубокого обучения обычно работают с большими наборами помеченных данных. Обучение этим моделям и выполнение вывода является вычислительной интенсивностью, требующей значительного объема памяти. Нейронные сети используют память для хранения входных данных, параметров (весов) и активаций от каждого уровня по мере распространения входных данных по сети. Большинство предварительно подготовленных нейронных сетей и нейронных сетей, обученных с помощью Deep Learning, Toolbox™ использовать типы данных с плавающей запятой с одной точностью. Даже сети небольшого размера требуют значительного объема памяти и оборудования для выполнения этих арифметических операций с плавающей запятой. Эти ограничения могут препятствовать развертыванию моделей глубокого обучения на устройствах с низкой вычислительной мощностью и меньшими ресурсами памяти. Использование более низкой точности для хранения весов и активизаций позволяет уменьшить требования к памяти сети.
Вы можете использовать Deep Learning Toolbox совместно с пакетом поддержки Deep Learning Toolbox Model Quantization Library, чтобы уменьшить объем памяти глубокой нейронной сети за счет квантования весов, смещений и активизаций слоев свертки до 8-битных масштабированных целочисленных типов данных. Затем можно использовать Coder™ графического процессора для генерации оптимизированного кода CUDA ® для квантованной сети. Созданный код использует преимущества библиотеки глубоких нейронных сетей NVIDIA ® CUDA (cuDNN) или TensorRT™ высокопроизводительной библиотеки выводов. созданный код может быть интегрирован в проект в виде исходного кода, статических или динамических библиотек или исполняемых файлов, которые можно развернуть на различных платформах графического процессора NVIDIA.
В этом примере кодер графического процессора используется для генерации кода CUDA для квантованной глубокой сверточной нейронной сети и классификации изображения. В примере используется предварительно подготовленное squeezenet (Deep Learning Toolbox) сверточная нейронная сеть для демонстрации обучения передаче, квантования и генерации кода CUDA для квантованной сети.
SqueeeNet был обучен более чем миллиону изображений и может классифицировать изображения на 1000 категорий объектов (таких как клавиатура, кофейная кружка, карандаш и многие животные). Сеть изучила богатые представления функций для широкого спектра изображений. Сеть принимает изображение в качестве входного и выводит метку для объекта в изображении вместе с вероятностями для каждой из категорий объектов.
Предварительные условия для сторонних производителей
Необходимый
Графический процессор NVIDIA ® с поддержкой CUDA и совместимый драйвер.
Дополнительный
Для построений, отличных от MEX, таких как статические, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.
Инструментарий NVIDIA CUDA.
Библиотека NVIDIA cuDNN.
Переменные среды для компиляторов и библиотек. Дополнительные сведения см. в разделах Аппаратное обеспечение сторонних производителей и Настройка необходимых продуктов.
Передача обучения с помощью SqueeENet
Чтобы выполнить классификацию на новом наборе изображений, вы точно настроите предварительно обученную сверточную нейронную сеть SqueeENet путем обучения передаче. При обучении передаче можно использовать предварительно подготовленную сеть и использовать ее в качестве отправной точки для изучения новой задачи. Точная настройка сети с обучением переносу обычно намного быстрее и проще, чем обучение сети с произвольно инициализированными весами с нуля. Вы можете быстро перенести изученные функции на новую задачу, используя меньшее количество обучающих изображений.
Загрузка данных обучения
Распакуйте и загрузите новые образы как хранилище данных образов. imageDatastore функция автоматически помечает изображения на основе имен папок и сохраняет данные в виде ImageDatastore объект. Хранилище данных изображения позволяет хранить большие данные изображения, включая данные, которые не помещаются в память, и эффективно считывать партии изображений во время обучения сверточной нейронной сети. Разделите данные на наборы данных обучения и проверки. Используйте 70% изображений для обучения и 30% для проверки. splitEachLabel разделяет imds в два новых хранилища данных.
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');

Загрузить предварительно обученную сеть
Загрузите предварительно обученную сеть SqueeEcNet. Если необходимые пакеты поддержки не установлены, программа предоставляет ссылку для загрузки.
net = squeezenet;
Объект net содержит DAGNetwork объект. Первый слой, изображение ввело слой, требует входных изображений размера 227 на 227 на 3, где 3 количество цветных каналов. Вы можете использовать analyzeNetwork Функция (Deep Learning Toolbox) используется для отображения интерактивной визуализации сетевой архитектуры, обнаружения ошибок и проблем в сети, а также для отображения подробной информации о сетевых уровнях. Информация о слое включает в себя размеры активизаций слоя и обучаемых параметров, общее количество обучаемых параметров и размеры параметров состояния повторяющихся слоев.
inputSize = net.Layers(1).InputSize;
Заменить конечные слои
Сверточные уровни сетевого извлеченного изображения отличаются тем, что последний обучаемый уровень и конечный уровень классификации используют для классификации входного изображения. Эти два слоя, 'conv10' и 'ClassificationLayer_predictions' в SqueeEnet, содержат информацию о том, как объединить функции, извлекаемые сетью, в вероятностях классов, значении потерь и прогнозируемых метках.
Для переподготовки заранее обученной сети для классификации новых изображений замените эти два слоя новыми слоями, адаптированными к новому набору данных. Это можно сделать вручную или с помощью функции помощника 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);
Чтобы автоматически изменять размер изображений проверки без дальнейшего увеличения данных, используйте хранилище данных дополненного изображения без указания дополнительных операций предварительной обработки.
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 функцию точки входа, которая:
Использует 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 команда. codegen команда генерирует код CUDA из predict_int8.m Функция начальной точки MATLAB.
inputs = {coder.Constant('mySqueezenet.mat'),ones(inputSize,'uint8')};
codegen -config cfg -args inputs predict_int8Code generation successful.
После успешного создания кода можно просмотреть полученный отчет о создании кода, щелкнув Просмотр отчета в окне команд MATLAB. Отчет отображается в окне Просмотр отчетов. Если генератор кода обнаруживает ошибки или предупреждения во время генерации кода, отчет описывает проблемы и предоставляет ссылки на проблемный код MATLAB.
Запуск сгенерированного MEX
Изображение, которое требуется классифицировать, должно иметь тот же размер, что и входной размер сети. Прочитайте изображение, которое требуется классифицировать, и измените его размер до входного размера сети. Это изменение размера немного изменяет пропорции изображения.
testImage = imread("MerchDataTest.jpg");
testImage = imresize(testImage,inputSize(1:2));Вызовите SqueeENet для прогнозирования входного изображения.
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-битного целочисленного квантования при нацеливании на библиотеку глубоких нейронных сетей NVIDIA CUDA (cuDNN).
leakyReluLayer
clippedReluLayer
globalAveragePooling2dLayer
codegen | coder.loadDeepLearningNetwork | calibrate (инструментарий глубокого обучения) | dlquantizationOptions (инструментарий для глубокого обучения) | dlquantizer (инструментарий для глубокого обучения) | validate (инструментарий для глубокого обучения)