Определение ключевого слова в шумовой генерации кода на Raspberry Pi

Этот пример демонстрирует генерацию кода для определения ключевого слова с помощью сети Bidirectional Long Short-Term Memory (BiLSTM) и извлечения признаков частоты mel cepstral коэффициента (MFCC) на Raspberry Pi™. MATLAB® Coder™ с Поддержкой Глубокого обучения включает генерацию независимого исполняемого файла (.elf) файл на Raspberry Pi. Связь между MATLAB® (.mlx) файл и сгенерированным исполняемым файлом происходит по асинхронному Пользовательскому дейтаграммному протоколу (UDP). Входящий речевой сигнал отображен с помощью timescope. Маска показывается синим прямоугольником, окружающим определенные экземпляры ключевого слова, YES. Для получения дополнительной информации об извлечении признаков MFCC и обучении нейронной сети для глубокого обучения, посетите Определение Ключевого слова в Шуме Используя MFCC и Сети LSTM.

Требования в качестве примера

  • MATLAB® Coder Interface для пакета поддержки глубокого обучения

  • Процессор ARM, который поддерживает расширение NEON

  • ARM Вычисляет версию 20.02.1 Библиотеки (на целевом оборудовании ARM)

  • Переменные окружения для компиляторов и библиотек

Для поддерживаемых версий библиотек и для получения информации о подготовке переменных окружения, смотрите Необходимые условия для Глубокого обучения для MATLAB Coder (MATLAB Coder).

Определение ключевого слова предварительно обученной сети Используя MATLAB® и передачу потокового аудио от микрофона

Частотой дискретизации предварительно обученной сети является 16 kHz. Установите длину окна на 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');

Создайте Time Scope, чтобы визуализировать речевые сигналы и оцененную маску.

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 Coder) функция, чтобы загрузить сеть.

Функция поддержки использует dsp.UDPReceiver Системный объект, чтобы получить записанное аудио от MATLAB® и использования dsp.UDPSender Системный объект, чтобы отправить входную речь сигнализирует наряду с предполагаемой маской, предсказанной сетью к MATLAB®. Точно так же live скрипт MATLAB® использует dsp.UDPSender Системный объект, чтобы отправить полученную речь сигнализирует к исполняемому файлу, работающему на Raspberry Pi и dsp.UDPReceiver Системный объект, чтобы получить речь сигнализирует и оцененная маска от Raspberry Pi.

Сгенерируйте исполняемый файл на 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, raspi, создать связь с вашим Raspberry Pi. В следующем коде, замене:

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

  • 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';

Сгенерируйте Код С++ для helperKeywordSpottingRaspi на вашем Raspberry Pi.

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 приложение на Raspberry Pi. Использование системы отправить команду в ваш Raspberry Pi.

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. 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 байты. Вычислите buffer size, чтобы вместить максимальное количество пакетов 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)

Оцените время выполнения Используя альтернативный рабочий процесс функции PIL

Чтобы оценить время выполнения, потраченное независимым исполняемым файлом на 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

Оцените время выполнения Raspberry Pi

Вызовите сгенерированную функцию 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 мс для потоковой передачи в реальном времени. Уровень измерен на версии Модели B Raspberry Pi 4 1.1.