В этом примере демонстрируется создание кода для определения ключевых слов с использованием сети двунаправленной долговременной памяти (BiLSTM) и извлечения функции частотного кепстрального коэффициента (MFCC) на Pi™ Raspberry. MATLAB ® Coder™ с поддержкой глубокого обучения позволяет создавать автономные исполняемые файлы на Raspberry Pi. Обмен данными между файлом MATLAB ® (.mlx) и созданным исполняемым файлом осуществляется по асинхронному протоколу пользовательских дейтаграмм (UDP). Входящий речевой сигнал отображается с помощью timescope. Маска отображается синим прямоугольником вокруг пятнистых экземпляров ключевого слова YES. Для получения более подробной информации об извлечении функций MFCC и обучении работе с сетью глубокого обучения посетите раздел Ключевые слова - определение шума с использованием сетей MFCC и LSTM.
Интерфейс кодера MATLAB ® для пакета поддержки глубокого обучения
Процессор ARM, поддерживающий расширение NEON
Вычислительная библиотека ARM версии 20.02.1 (на целевом оборудовании ARM)
Переменные среды для компиляторов и библиотек
Поддерживаемые версии библиотек и сведения о настройке переменных среды см. в разделе Предварительные условия для глубокого обучения с помощью кодера MATLAB (MATLAB Coder).
Частота выборки предварительно обученной сети составляет 16 кГц. Задайте длину окна как 512 выборки, с длиной перекрытия 384 выборки и длина перехода, определяемая как разница между длиной окна и длиной перекрытия. Определите скорость, с которой оценивается маска. Маска генерируется один раз для каждого numHopsPerUpdate аудиокадры.
fs = 16e3; windowLength = 512; overlapLength = 384; hopLength = windowLength - overlapLength; numHopsPerUpdate = 16; maskLength = hopLength * numHopsPerUpdate;
Создание audioFeatureExtractor объект для выполнения извлечения функции MFCC.
afe = audioFeatureExtractor('SampleRate',fs, ... 'Window',hann(windowLength,'periodic'), ... 'OverlapLength',overlapLength, ... 'mfcc',true, ... 'mfccDelta',true, ... 'mfccDeltaDelta',true);
Загрузить и загрузить подготовленную сеть, а также среднее (M) и стандартное отклонение (S) векторы, используемые для стандартизации функций.
url = 'http://ssd.mathworks.com/supportfiles/audio/KeywordSpotting.zip'; downloadNetFolder = './'; netFolder = fullfile(downloadNetFolder,'KeywordSpotting'); if ~exist(netFolder,'dir') disp('Downloading pretrained network and audio files (4 files - 7 MB) ...') unzip(url,downloadNetFolder) end load(fullfile(netFolder,'KWSNet.mat'),"KWSNet","M","S");
Звонить generateMATLABFunction на audioFeatureExtractor объект для создания функции извлечения элемента.
generateMATLABFunction(afe,'generateKeywordFeatures','IsStreaming',true);
Определите object™ системы чтения аудиоустройств для считывания звука с микрофона. Установите длину кадра равной длине транзитного участка. Это позволяет вычислять новый набор функций для каждого нового звукового кадра, принимаемого от микрофона.
frameLength = hopLength; adr = audioDeviceReader('SampleRate',fs, ... 'SamplesPerFrame',frameLength,'OutputDataType','single');
Создайте временную область для визуализации речевых сигналов и оценочной маски.
scope = timescope('SampleRate',fs, ... 'TimeSpanSource','property', ... 'TimeSpan',5, ... 'TimeSpanOverrunAction','Scroll', ... 'BufferLength',fs*5*2, ... 'ShowLegend',true, ... 'ChannelNames',{'Speech','Keyword Mask'}, ... 'YLimits',[-1.2 1.2], ... 'Title','Keyword Spotting');
Инициализируют буфер для аудиоданных, буфер для вычисленных характеристик и буфер для построения графика входного звука и выходной речевой маски.
dataBuff = dsp.AsyncBuffer(windowLength); featureBuff = dsp.AsyncBuffer(numHopsPerUpdate); plotBuff = dsp.AsyncBuffer(numHopsPerUpdate*windowLength);
Выполнение определения ключевых слов для речи, полученной от микрофона. Для бесконечного запуска цикла установите timeLimit кому Inf. Чтобы остановить моделирование, закройте scope.
show(scope); timeLimit = 20; tic while toc < timeLimit && isVisible(scope) data = adr(); write(dataBuff,data); write(plotBuff,data); frame = read(dataBuff,windowLength,overlapLength); features = generateKeywordFeatures(frame,fs); write(featureBuff,features.'); if featureBuff.NumUnreadSamples == numHopsPerUpdate featureMatrix = read(featureBuff); featureMatrix(~isfinite(featureMatrix)) = 0; featureMatrix = (featureMatrix - M)./S; [keywordNet,v] = classifyAndUpdateState(KWSNet,featureMatrix.'); v = double(v) - 1; v = repmat(v,hopLength,1); v = v(:); v = mode(v); v = repmat(v,numHopsPerUpdate * hopLength,1); data = read(plotBuff); scope([data,v]); drawnow limitrate; end end hide(scope)
helperKeywordSpottingRaspi поддерживающая функция инкапсулирует процесс извлечения признаков и предсказания сети, продемонстрированный ранее. Для обеспечения совместимости извлечения признака с генерацией кода извлечение признака обрабатывается сгенерированным generateKeywordFeatures функция. Чтобы сделать сеть совместимой с генерацией кода, функция поддержки использует coder.loadDeepLearningNetwork (Кодер MATLAB) для загрузки сети.
Поддерживающая функция использует dsp.UDPReceiver Системный объект для приема захваченного звука от MATLAB ® и использует dsp.UDPSender Системный объект для передачи входного речевого сигнала вместе с оценочной маской, предсказанной сетью, в MATLAB ®. Аналогично, в сценарии MATLAB ® live используется dsp.UDPSender Системный объект для передачи захваченного речевого сигнала исполняемому файлу, работающему на Raspberry Pi и dsp.UDPReceiver Объект системы для приема речевого сигнала и оценочной маски от Raspberry Pi.
Замените hostIPAddress с адресом вашей машины. Ваш Raspberry Pi посылает входной речевой сигнал и оценочную маску на указанный IP-адрес.
hostIPAddress = coder.Constant('172.18.230.30');Создайте объект конфигурации создания кода для создания исполняемой программы. Укажите целевой язык как C++.
cfg = coder.config('exe'); cfg.TargetLang = 'C++';
Создайте объект конфигурации для создания кода глубокого обучения с помощью вычислительной библиотеки ARM, которая находится на Raspberry Pi. Укажите архитектуру Raspberry Pi и присоедините объект конфигурации глубокого обучения к объекту конфигурации генерации кода.
dlcfg = coder.DeepLearningConfig('arm-compute'); dlcfg.ArmArchitecture = 'armv7'; dlcfg.ArmComputeVersion = '20.02.1'; cfg.DeepLearningConfig = dlcfg;
Используйте функцию Raspberry Pi Support Package, raspi, чтобы создать соединение с вашим Raspberry Pi. В следующем коде замените:
raspiname с именем вашего Малинового Пи
pi с именем пользователя
password с вашим паролем
r = raspi('raspiname','pi','password');
Создать coder.hardware (MATLAB Coder) объект для Raspberry Pi и присоедините его к объекту конфигурации генерации кода.
hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;Укажите папку сборки на Raspberry Pi.
buildDir = '~/remoteBuildDir';
cfg.Hardware.BuildDir = buildDir;Создайте основной файл C++, необходимый для создания автономного исполняемого файла.
cfg.GenerateExampleMain = 'GenerateCodeAndCompile';Создать код C++ для helperKeywordSpottingRaspi на твоей Малиновой Пи.
codegen -config cfg helperKeywordSpottingRaspi -args {hostIPAddress} -report
Deploying code. This may take a few minutes. Warning: Function 'helperKeywordSpottingRaspi' does not terminate due to an infinite loop. Warning in ==> helperKeywordSpottingRaspi Line: 78 Column: 1 Code generation successful (with warnings): View report
Создайте команду, чтобы открыть helperKeywordSpottingRaspi заявка на Малиновый Пи. Использовать system отправить команду на ваш Малиновый Пи.
applicationName = 'helperKeywordSpottingRaspi'; applicationDirPaths = raspi.utils.getRemoteBuildDirectory('applicationName',applicationName); targetDirPath = applicationDirPaths{1}.directory; exeName = strcat(applicationName,'.elf'); command = ['cd ',targetDirPath,'; ./',exeName,' &> 1 &']; system(r,command);
Создать dsp.UDPSender Системный объект для отправки звука, записанного в MATLAB ®, в Raspberry Pi. Обновить targetIPAddress для твоего Малинового Пи. Raspberry Pi получает захваченное аудио с того же порта с помощью dsp.UDPReceiver Системный объект.
targetIPAddress = '172.18.240.234'; UDPSend = dsp.UDPSender('RemoteIPPort',26000,'RemoteIPAddress',targetIPAddress);
Создать dsp.UDPReceiver Системный объект для получения речевых данных и предсказанной речевой маски от вашего Raspberry Pi. Каждый пакет UDP, полученный от Raspberry Pi, состоит из maskLength маска и образцы речи. Максимальная длина сообщения для dsp.UDPReceiver объект - 65507 байт. Вычислите размер буфера для размещения максимального количества пакетов UDP.
sizeOfFloatInBytes = 4; speechDataLength = maskLength; numElementsPerUDPPacket = maskLength + speechDataLength; maxUDPMessageLength = floor(65507/sizeOfFloatInBytes); numPackets = floor(maxUDPMessageLength/numElementsPerUDPPacket); bufferSize = numPackets*numElementsPerUDPPacket*sizeOfFloatInBytes; UDPReceive = dsp.UDPReceiver("LocalIPPort",21000, ... "MessageDataType","single", ... "MaximumMessageLength",1+numElementsPerUDPPacket, ... "ReceiveBufferSize",bufferSize);
Укажите ключевое слово до тех пор, пока область времени открыта или пока не будет достигнут предел времени. Чтобы остановить обнаружение в реальном времени до достижения предела времени, закройте область времени.
tic; show(scope); timelimit = 20; while toc < timelimit && isVisible(scope) x = adr(); UDPSend(x); data = UDPReceive(); if ~isempty(data) mask = data(1:maskLength); dataForPlot = data(maskLength + 1 : numElementsPerUDPPacket); scope([dataForPlot,mask]); end drawnow limitrate; end
Деблокируйте системные объекты и завершите автономный исполняемый файл.
hide(scope) release(UDPSend) release(UDPReceive) release(scope) release(adr) stopExecutable(codertarget.raspi.raspberrypi,exeName)
Чтобы оценить время выполнения, затраченное автономным исполняемым файлом на Raspberry Pi, используйте рабочий процесс PIL (процессор в цикле). Для выполнения профилирования PIL создайте функцию PIL для поддерживающей функции. profileKeywordSpotting. profileKeywordSpotting эквивалентно helperKeywordSpottingRaspi, за исключением того, что первый возвращает речь и предсказанную речевую маску, в то время как второй посылает те же параметры с использованием UDP. Время, затрачиваемое на вызовы UDP, составляет менее 1 мс, что относительно мало по сравнению с общим временем выполнения.
Создайте объект конфигурации генерации кода для создания функции PIL.
cfg = coder.config('lib','ecoder',true); cfg.VerificationMode = 'PIL';
Настройте вычислительную библиотеку и архитектуру ARM.
dlcfg = coder.DeepLearningConfig('arm-compute'); cfg.DeepLearningConfig = dlcfg ; cfg.DeepLearningConfig.ArmArchitecture = 'armv7'; cfg.DeepLearningConfig.ArmComputeVersion = '20.02.1';
Настройте подключение к целевому оборудованию.
if (~exist('r','var')) r = raspi('raspiname','pi','password'); end hw = coder.hardware('Raspberry Pi'); cfg.Hardware = hw;
Задайте каталог построения и целевой язык.
buildDir = '~/remoteBuildDir'; cfg.Hardware.BuildDir = buildDir; cfg.TargetLang = 'C++';
Включите профилирование и создайте код PIL. Файл MEX с именем profileKeywordSpotting_pil создается в текущей папке.
cfg.CodeExecutionProfiling = true; codegen -config cfg profileKeywordSpotting -args {pinknoise(hopLength,1,'single')} -report
Deploying code. This may take a few minutes. ### Connectivity configuration for function 'profileKeywordSpotting': 'Raspberry Pi' Location of the generated elf : /home/pi/remoteBuildDir/MATLAB_ws/R2021a/E/sandbox/sporwal/Examples/ExampleManager/sporwal.Bdoc21a.j1572571/deeplearning_shared-ex18742368/codegen/lib/profileKeywordSpotting/pil Code generation successful: View report
Вызовите сгенерированную функцию PIL несколько раз, чтобы получить среднее время выполнения.
numPredictCalls = 10; totalCalls = numHopsPerUpdate * numPredictCalls; x = pinknoise(hopLength,1,'single'); for k = 1:totalCalls [maskReceived,inputSignal,plotFlag] = profileKeywordSpotting_pil(x); end
### Starting application: 'codegen\lib\profileKeywordSpotting\pil\profileKeywordSpotting.elf'
To terminate execution: clear profileKeywordSpotting_pil
### Launching application profileKeywordSpotting.elf...
Execution profiling data is available for viewing. Open Simulation Data Inspector.
Execution profiling report available after termination.
Завершите выполнение PIL.
clear profileKeywordSpotting_pil### Host application produced the following standard output (stdout) and standard error (stderr) messages:
Execution profiling report: report(getCoderExecutionProfile('profileKeywordSpotting'))
Создание отчета о профиле выполнения для оценки времени выполнения.
executionProfile = getCoderExecutionProfile('profileKeywordSpotting'); report(executionProfile, ... 'Units','Seconds', ... 'ScaleFactor','1e-03', ... 'NumericFormat','%0.4f')
ans = 'E:\sandbox\sporwal\Examples\ExampleManager\sporwal.Bdoc21a.j1572571\deeplearning_shared-ex18742368\codegen\lib\profileKeywordSpotting\html\orphaned\ExecutionProfiling_303d853869fa4b88.html'


Обработка первого кадра заняла ~ 20 мс из-за накладных расходов на инициализацию. Всплески во временном графике в каждом 16-м кадре (numHopsPerUpdate) соответствуют ресурсоемкой функции прогнозирования, называемой каждым 16-м кадром. Максимальное время выполнения составляет ~ 30 мс, что ниже бюджета 128 мс для потоковой передачи в реальном времени. Производительность измеряется на Raspberry Pi 4 Model B Rev 1.1.