使用一類新興的高效算法,任何開發(fā)人員現(xiàn)在都可以在低功耗、資源受限的系統(tǒng)上部署復(fù)雜的關(guān)鍵字識(shí)別功能。這個(gè)由兩部分組成的系列的第一部分展示了如何使用 FPGA 來做到這一點(diǎn)。在第二部分中,我們將展示如何使用 MCU 來做到這一點(diǎn)。
關(guān)鍵字發(fā)現(xiàn) (KWS) 技術(shù)已成為可穿戴設(shè)備、物聯(lián)網(wǎng)設(shè)備和其他智能產(chǎn)品越來越重要的功能。機(jī)器學(xué)習(xí)方法可以提供卓越的 KWS 準(zhǔn)確性,但這些產(chǎn)品的功能和性能限制直到最近限制了大型企業(yè)或經(jīng)驗(yàn)豐富的機(jī)器學(xué)習(xí)專家對(duì)機(jī)器學(xué)習(xí) KWS 解決方案的使用。
然而,開發(fā)人員越來越需要在可穿戴設(shè)備和其他物聯(lián)網(wǎng)設(shè)備中在能夠在這些設(shè)備的資源限制內(nèi)運(yùn)行的更高效的 KWS 引擎上實(shí)現(xiàn)語音激活的 KWS 功能。深度可分離卷積神經(jīng)網(wǎng)絡(luò) (DS-CNN) 架構(gòu)修改了傳統(tǒng)的 CNN 以提供所需的效率。
使用硬件優(yōu)化的神經(jīng)網(wǎng)絡(luò)庫,開發(fā)人員可以實(shí)現(xiàn)需要最少資源的基于 MCU 的 DS-CNN 推理引擎。本文描述了 DS-CNN 架構(gòu),并展示了開發(fā)人員如何在 MCU 上實(shí)現(xiàn) DS-CNN KWS。
為什么選擇 KWS?
消費(fèi)者對(duì)使用“Alexa”、“Hey Siri”或“Ok Google”等短語的智能手機(jī)和家用電器上的語音激活功能的接受度已迅速演變?yōu)閷?duì)幾乎所有為用戶交互而設(shè)計(jì)的產(chǎn)品的語音服務(wù)的廣泛需求。在這些服務(wù)的基礎(chǔ)上,準(zhǔn)確的語音識(shí)別依賴于各種人工智能方法來識(shí)別口語單詞,并將單詞和詞組解釋為適合應(yīng)用程序的命令。
然而,快速準(zhǔn)確地完成整個(gè)語音命令序列所需的資源開始超過低成本線路供電消費(fèi)集線器的能力,更不用說電池供電的個(gè)人電子產(chǎn)品了。
語音命令管道要求
為了在這些產(chǎn)品上提供語音激活,開發(fā)人員將語音命令管道拆分或?qū)⒄Z音命令限制為幾個(gè)非常簡(jiǎn)單的詞,例如“開”和“關(guān)”。在資源有限的消費(fèi)產(chǎn)品上,開發(fā)人員使用神經(jīng)網(wǎng)絡(luò)推理引擎實(shí)現(xiàn) KWS 功能,該引擎能夠?yàn)?Alexa、Siri 或 Google 的簡(jiǎn)單命令或命令激活短語提供所需的高精度、低延遲響應(yīng)(圖 1)。
在這里,設(shè)計(jì)將輸入音頻流數(shù)字化,提取語音特征,并將這些特征傳遞給神經(jīng)網(wǎng)絡(luò)以識(shí)別關(guān)鍵字。
??
圖 1:使用 KWS 進(jìn)行語音激活使用處理管道從音頻輸入信號(hào)中提取頻域特征,并對(duì)提取的特征進(jìn)行分類,以預(yù)測(cè)輸入信號(hào)對(duì)應(yīng)于用于訓(xùn)練神經(jīng)網(wǎng)絡(luò)的標(biāo)簽之一的概率。(圖片來源:Arm?)
通過將幅度調(diào)制的音頻輸入流轉(zhuǎn)換為頻譜圖中的特征,開發(fā)人員可以利用卷積神經(jīng)網(wǎng)絡(luò) (CNN) 模型經(jīng)過驗(yàn)證的能力,根據(jù)神經(jīng)網(wǎng)絡(luò)訓(xùn)練期間使用的標(biāo)簽之一對(duì)口語進(jìn)行準(zhǔn)確分類。2對(duì)于更復(fù)雜的語音接口,命令處理管道超出了設(shè)備本身。在 KWS 推理引擎檢測(cè)到激活關(guān)鍵字或短語后,產(chǎn)品將數(shù)字化的音頻流傳遞給基于云的服務(wù),能夠更有效地處理復(fù)雜的語音處理和命令識(shí)別操作。
盡管如此,設(shè)備資源可用性和推理引擎資源需求之間的沖突仍使開發(fā)人員試圖將這些方法應(yīng)用于更小的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備設(shè)計(jì)的嘗試。盡管經(jīng)典的 CNN 相對(duì)容易理解且易于訓(xùn)練,但這些模型仍然可能是資源密集型的。隨著 CNN 模型在識(shí)別圖像方面的準(zhǔn)確度大幅提高,CNN 的大小和復(fù)雜度也顯著增加。
結(jié)果是非常精確的 CNN 模型,需要數(shù)十億計(jì)算密集型通用矩陣乘法 (GEMM) 操作進(jìn)行訓(xùn)練。一旦經(jīng)過訓(xùn)練,相應(yīng)的推理模型可能會(huì)占用數(shù)百兆內(nèi)存,并且需要非常大量的 GEMM 操作才能進(jìn)行單個(gè)推理。
對(duì)于電池供電的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備,有效的 KWS 推理模型必須能夠在有限的內(nèi)存中運(yùn)行且處理要求低。此外,由于 KWS 推理引擎必須在“始終開啟”模式下運(yùn)行才能執(zhí)行其功能,因此它必須能夠以最低功耗運(yùn)行。
在越來越有吸引力的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備領(lǐng)域,神經(jīng)網(wǎng)絡(luò)的潛力與有限資源之間的這種二分法引起了機(jī)器學(xué)習(xí)專家的極大關(guān)注。結(jié)果是開發(fā)了優(yōu)化基本 CNN 模型的技術(shù),并出現(xiàn)了替代神經(jīng)網(wǎng)絡(luò)架構(gòu),能夠彌合小型資源受限設(shè)備的性能要求和資源能力之間的差距。
小尺寸型號(hào)
在創(chuàng)建小型模型的技術(shù)中,機(jī)器學(xué)習(xí)專家已經(jīng)應(yīng)用了網(wǎng)絡(luò)修剪和參數(shù)量化等優(yōu)化方法來生成 CNN 變體,這些變體能夠提供幾乎與完整 CNN 一樣準(zhǔn)確的結(jié)果,但只使用了一小部分資源。這些降低精度的神經(jīng)網(wǎng)絡(luò)的成功為二值化神經(jīng)網(wǎng)絡(luò) (BNN) 架構(gòu)鋪平了道路,該架構(gòu)將模型參數(shù)從 32 位浮點(diǎn)數(shù),甚至早期 CNN 中的 16 位和 8 位浮點(diǎn)數(shù)減少到 1 位價(jià)值觀。如第 1 部分所述,Lattice Semiconductor機(jī)器學(xué)習(xí)SensAI?平臺(tái)使用這種高效的 BNN 架構(gòu)作為 1 毫瓦 (mW) KWS 解決方案的基礎(chǔ),該解決方案在其基于 FPGA 的iCE40 UltraPlus移動(dòng)開發(fā)平臺(tái),或 MDP。
除了網(wǎng)絡(luò)修剪和參數(shù)量化等簡(jiǎn)化技術(shù)外,還有其他降低資源需求的方法可以修改 CNN 架構(gòu)本身的拓?fù)浣Y(jié)構(gòu)。在這些替代架構(gòu)中,深度可分離卷積神經(jīng)網(wǎng)絡(luò)提供了一種特別有效的方法來創(chuàng)建能夠在通用 MCU 上運(yùn)行的小型、資源高效模型。
在早期工作的基礎(chǔ)上,谷歌機(jī)器學(xué)習(xí)專家找到了一種通過關(guān)注卷積層本身來提高 CNN 效率的方法。在傳統(tǒng)的 CNN 中,每個(gè)卷積層過濾輸入特征并在一個(gè)步驟中將它們組合成一組新的特征(圖 2,頂部)。
??
圖 2:與全卷積(上)不同,深度可分離卷積首先使用 D K xD K濾波器(中)分別過濾 M 個(gè)輸入通道中的每一個(gè),并使用逐點(diǎn) 1 x 1 卷積來創(chuàng)建 N 個(gè)新特征。(圖片來源:谷歌)
新方法將過濾和特征生成分為兩個(gè)獨(dú)立的階段,統(tǒng)稱為深度可分離卷積。第一階段執(zhí)行深度卷積,該卷積在輸入的每個(gè)通道上充當(dāng)空間濾波器(圖 2,中間)。因?yàn)榈谝浑A段不創(chuàng)建新特征(深度神經(jīng)網(wǎng)絡(luò)架構(gòu)的核心目標(biāo)),所以第二階段執(zhí)行逐點(diǎn) 1 x 1 卷積(圖 2,底部),它結(jié)合第一階段的輸出以生成新特征。
這種 DS-CNN 架構(gòu)在用于移動(dòng)和嵌入式視覺應(yīng)用的 Google MobileNet 模型中使用,減少了參數(shù)和相關(guān)操作的數(shù)量,從而產(chǎn)生更小的模型,需要顯著更少的計(jì)算來獲得準(zhǔn)確的結(jié)果。3
與全卷積相比,在 MobileNet 模型中使用深度可分離卷積在行業(yè)標(biāo)準(zhǔn) ImageNet 數(shù)據(jù)集上僅降低了 1% 的準(zhǔn)確度,但使用了不到 12% 的乘加運(yùn)算和所需的模型參數(shù)數(shù)量的 14%用于傳統(tǒng) ImageNet CNN 模型中的全卷積。
盡管 DS-CNN 最初是為圖像識(shí)別而開發(fā)的,但這些相同的模型可以通過將音頻輸入流轉(zhuǎn)換為頻譜圖來提供一組可用的特征來提供音頻識(shí)別。實(shí)際上,音頻前端將音頻流轉(zhuǎn)換為 DS-CNN 可以分類的一組特征。對(duì)于語音處理,前端產(chǎn)生的特征通常采用梅爾頻率倒譜系數(shù)(MFCC)的形式,它更接近人類的聽覺特征,同時(shí)顯著降低了傳遞給 DS-CNN 分類器的特征集的維數(shù)。這正是 ARM ML-KWS-for-MCU開源軟件存儲(chǔ)庫中使用的方法。
DS-CNN 實(shí)現(xiàn)
ARM KWS 存儲(chǔ)庫旨在演示在 Arm Cortex ? -M 系列 MCU 上的 KWS 實(shí)施,在包括傳統(tǒng) CNN、DS-CNN 等在內(nèi)的多種架構(gòu)中提供了大量預(yù)訓(xùn)練的 TensorFlow 模型。使用谷歌速度命令數(shù)據(jù)集4訓(xùn)練,模型將音頻輸入分類為 12 個(gè)可能的類別之一:“是”、“否”、“上”、“下”、“左”、“右”、“開”、“ Off”、“Stop”、“Go”、“silence”(不說話)和“unknown”(代表 Google 數(shù)據(jù)集中包含的其他詞)。
開發(fā)人員可以立即使用這些預(yù)訓(xùn)練模型來比較這些替代神經(jīng)網(wǎng)絡(luò)架構(gòu)的推理性能并檢查它們的內(nèi)部結(jié)構(gòu)。例如,在 ARM 的預(yù)訓(xùn)練 DS-CNN 模型上運(yùn)行 TensorFlow 的import_pb_to_tensorboard Python 實(shí)用程序后,開發(fā)人員可以使用 TensorBoard 可視化模型的基于 MobileNet 的架構(gòu)。
??
圖 3:在 TensorBoard 中顯示的 Arm 預(yù)訓(xùn)練 KWS 模型將熟悉的 MobileNet DS-CNN 模型(左紅色輪廓)與使用梅爾頻率倒譜系數(shù) (MFCC) 的頻域特征提取階段(右擴(kuò)展)相結(jié)合. (圖片來源:Digi-Key Electronics)
正如在 TensorBoard 中所展示的那樣,MobileNet 架構(gòu)用深度可分離卷積替換了傳統(tǒng) CNN 架構(gòu)中除第一個(gè)完整卷積層之外的所有卷積層。
如前所述,這些階段中的每一個(gè)都包括深度卷積和點(diǎn)卷積階段,每個(gè)階段都輸入一個(gè) batchnorm 內(nèi)核以對(duì)輸出結(jié)果進(jìn)行歸一化(圖 3,左)。DS-CNN 模型使用一個(gè)特殊的 TensorFlow 融合 batchnorm 函數(shù),它將多個(gè)選項(xiàng)組合到一個(gè)內(nèi)核中。
此外,通過放大音頻輸入特征提取階段(圖 3,右),開發(fā)人員可以檢查音頻處理序列,包括音頻解碼、頻譜圖生成和 MFCC 濾波。MFCC 生成的特征通過一對(duì)重塑階段來創(chuàng)建 MobileNet 分類器所需的張量形狀。
可以想象,開發(fā)人員可以在基于 MCU 的系統(tǒng)(包括Raspberry Pi )上運(yùn)行來自 TensorFlow 或其他機(jī)器學(xué)習(xí)框架的訓(xùn)練模型。5使用這種方法,開發(fā)人員可以量化經(jīng)過訓(xùn)練的模型,以生成能夠在這些系統(tǒng)上運(yùn)行的較小版本。但是,如果沒有圖形處理單元 (GPU) 或其他硬件支持 GEMM 加速,推理延遲可能會(huì)令用戶對(duì)語音激活性能的預(yù)期失望。
ARM 通過其對(duì) ARM Cortex 微控制器軟件接口標(biāo)準(zhǔn) (CMSIS) 的神經(jīng)網(wǎng)絡(luò) (NN) 擴(kuò)展提供了一種替代方法。CMSIS-NN 提供一整套 CNN 功能,充分利用 ARM Cortex-M7 處理器(例如STMicroelectronics 的 STM32F7 MCU 系列中的那些)中內(nèi)置的 DSP 擴(kuò)展。除了傳統(tǒng)的 CNN 函數(shù)外,CMSIS-NN 應(yīng)用程序編程接口 (API) 還支持深度可分離卷積,其中一對(duì)函數(shù)對(duì)應(yīng)于 DS-CNN 架構(gòu)下的深度和逐點(diǎn) 1 x 1 卷積階段:
ARM_status ARM_depthwise_separable_conv_HWC_q7_nonsquare
ARM_status ARM_convolve_1x1_HWC_q7_fast_nonsquare
該 API 還在專門為方形輸入張量設(shè)計(jì)的版本中提供了這兩個(gè)函數(shù)。
ARM 在示例代碼中使用這些函數(shù),該示例代碼演示了一個(gè)完整的基于 DS-CNN 的 KWS 應(yīng)用程序,該應(yīng)用程序在 STMicroelectronics STM32F746G-DISCO開發(fā)板上運(yùn)行,該開發(fā)板圍繞STM32F746NGH6 MCU 構(gòu)建。在示例代碼的核心,原生 CMSIS-NN C++ 模塊實(shí)現(xiàn)了 CS-DNN(清單 1)。
void DS_CNN::run_nn(q7_t* in_data, q7_t* out_data)
{
//CONV1 : regular convolution
ARM_convolve_HWC_q7_basic_nonsquare(in_data, CONV1_IN_X, CONV1_IN_Y, 1, conv1_wt, CONV1_OUT_CH, CONV1_KX, CONV1_KY, CONV1_PX, CONV1_PY, CONV1_SX, CONV1_SY, conv1_bias, CONV1_BIAS_LSHIFT, CONV1_OUT_RSHIFT, buffer1, CONV1_OUT_X, CONV1_OUT_Y, (q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer1,CONV1_OUT_X*CONV1_OUT_Y*CONV1_OUT_CH);
// conv2:ds + pw conv // depthwise可分開的cons(批次范圍折疊為conv wts/bias) arm_depthwise_separible_conv_hwc_hwc_hwc_hwc_q7_nonsquare(buffer1,conv2_in_in_x,conv2_in_y,conv2_in_y,
conv1_out_conve_c__cond_ss_s_ds_s_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_ds_d, conv2_ds_bias,CONV2_DS_BIAS_LSHIFT,CONV2_DS_OUT_RSHIFT,buffer2,CONV2_OUT_X,CONV2_OUT_Y,(q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer2,CONV2_OUT_X*CONV2_OUT_Y*CONV2_OUT_CH); // 逐點(diǎn)卷積 ARM_convolve_1x1_HWC_q7_fast_nonsquare(buffer2, CONV2_OUT_X, CONV2_OUT_Y, CONV1_OUT_CH, conv2_pw_wt, CONV2_OUT_CH, 1, 1, 0, 0, 1, 1, conv2_pw_bias, CONV2_PW_BIAS_LSHIFT,
CONV2_PW_OUT_RSHIFT, buffer1, CONV2_OUT_X, CONV2_bufferY, (NULL), CONV2_buff_Y, (NULL)_ ARM_relu_q7(buffer1,CONV2_OUT_X*CONV2_OUT_Y*CONV2_OUT_CH);
//CONV3 : DS + PW conv
//Depthwise separable conv (batch norm params folded into conv wts/bias)
ARM_depthwise_separable_conv_HWC_q7_nonsquare(buffer1,CONV3_IN_X,CONV3_IN_Y,CONV2_OUT_CH,conv3_ds_wt,CONV2_OUT_CH,CONV3_DS_KX,CONV3_DS_KY,CONV3_DS_PX,CONV3_DS_PY,CONV3_DS_SX,CONV3_DS_SY,conv3_ds_bias,CONV3_DS_BIAS_LSHIFT,CONV3_DS_OUT_RSHIFT,buffer2,CONV3_OUT_X,CONV3_OUT_Y,(q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer2,CONV3_OUT_X*CONV3_OUT_Y*CONV3_OUT_CH);
//Pointwise conv
ARM_convolve_1x1_HWC_q7_fast_nonsquare(buffer2, CONV3_OUT_X, CONV3_OUT_Y, CONV2_OUT_CH, conv3_pw_wt, CONV3_OUT_CH, 1, 1, 0, 0, 1, 1, conv3_pw_bias, CONV3_PW_BIAS_LSHIFT, CONV3_PW_OUT_RSHIFT, buffer1, CONV3_OUT_X, CONV3_OUT_Y, (q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer1,CONV3_OUT_X*CONV3_OUT_Y*CONV3_OUT_CH);
//CONV4 : DS + PW conv
//Depthwise separable conv (batch norm params folded into conv wts/bias)
ARM_depthwise_separable_conv_HWC_q7_nonsquare(buffer1,CONV4_IN_X,CONV4_IN_Y,CONV3_OUT_CH,conv4_ds_wt,CONV3_OUT_CH,CONV4_DS_KX,CONV4_DS_KY,CONV4_DS_PX,CONV4_DS_PY,CONV4_DS_SX,CONV4_DS_SY,conv4_ds_bias,CONV4_DS_BIAS_LSHIFT,CONV4_DS_OUT_RSHIFT,buffer2,CONV4_OUT_X,CONV4_OUT_Y,(q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer2,CONV4_OUT_X*CONV4_OUT_Y*CONV4_OUT_CH);
//Pointwise conv
ARM_convolve_1x1_HWC_q7_fast_nonsquare(buffer2, CONV4_OUT_X, CONV4_OUT_Y, CONV3_OUT_CH, conv4_pw_wt, CONV4_OUT_CH, 1, 1, 0, 0, 1, 1, conv4_pw_bias, CONV4_PW_BIAS_LSHIFT, CONV4_PW_OUT_RSHIFT, buffer1, CONV4_OUT_X, CONV4_OUT_Y, (q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer1,CONV4_OUT_X*CONV4_OUT_Y*CONV4_OUT_CH);
//CONV5 : DS + PW conv
//Depthwise separable conv (batch norm params folded into conv wts/bias)
ARM_depthwise_separable_conv_HWC_q7_nonsquare(buffer1,CONV5_IN_X,CONV5_IN_Y,CONV4_OUT_CH,conv5_ds_wt,CONV4_OUT_CH,CONV5_DS_KX,CONV5_DS_KY,CONV5_DS_PX,CONV5_DS_PY,CONV5_DS_SX,CONV5_DS_SY,conv5_ds_bias,CONV5_DS_BIAS_LSHIFT,CONV5_DS_OUT_RSHIFT,buffer2,CONV5_OUT_X,CONV5_OUT_Y,(q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer2,CONV5_OUT_X*CONV5_OUT_Y*CONV5_OUT_CH);
//Pointwise conv
ARM_convolve_1x1_HWC_q7_fast_nonsquare(buffer2, CONV5_OUT_X, CONV5_OUT_Y, CONV4_OUT_CH, conv5_pw_wt, CONV5_OUT_CH, 1, 1, 0, 0, 1, 1, conv5_pw_bias, CONV5_PW_BIAS_LSHIFT, CONV5_PW_OUT_RSHIFT, buffer1, CONV5_OUT_X, CONV5_OUT_Y, (q15_t*)col_buffer, NULL);
ARM_relu_q7(buffer1,CONV5_OUT_X*CONV5_OUT_Y*CONV5_OUT_CH);
//Average pool
ARM_avepool_q7_HWC_nonsquare (buffer1,CONV5_OUT_X,CONV5_OUT_Y,CONV5_OUT_CH,CONV5_OUT_X,CONV5_OUT_Y,0,0,1,1,1,1,NULL,buffer2, 2);
ARM_fully_connected_q7(buffer2, final_fc_wt, CONV5_OUT_CH, OUT_DIM, FINAL_FC_BIAS_LSHIFT, FINAL_FC_OUT_RSHIFT, final_fc_bias, out_data, (q15_t*)col_buffer);
}
清單 1:ARM ML-KWS-for-MCU 軟件存儲(chǔ)庫包含一個(gè) C++ DS-CNN 模型,其中一個(gè)完整的卷積層后跟幾個(gè)深度可分離卷積(框),每個(gè)都使用深度卷積和 1 x 1 卷積函數(shù)(黃色突出顯示)在硬件優(yōu)化的 ARM CMSIS-NN 軟件庫中支持。(代碼來源:ARM)
盡管 C++ DS-CNN 實(shí)現(xiàn)與前面展示的 TensorBoard DS-CNN 模型略有不同,但總體方法保持不變。在初始全卷積核之后,一系列深度可分離卷積核輸入最終池化層和全連接層,以生成每個(gè)輸出通道的預(yù)測(cè)值(對(duì)應(yīng)于用于訓(xùn)練模型的 12 個(gè)類標(biāo)簽)。
KWS 應(yīng)用程序?qū)⒋四P团c代碼相結(jié)合,以提供對(duì) STM32F746G-DISCO 開發(fā)板收集的實(shí)時(shí)音頻流的推斷。在這里,主函數(shù)初始化推理引擎,啟用音頻采樣,然后進(jìn)入由單個(gè)等待中斷 (WFI) 調(diào)用組成的無限循環(huán)(清單 2)。
char output_class[12][8] = {“Silence”, “Unknown”,“yes”,“no”,“up”,“down”,
“l(fā)eft”,“right”,“on”,“off”,“stop”,“go”};
int main()
{
pc.baud(9600);
kws = new KWS_F746NG(recording_win,averaging_window_len);
init_plot();
kws-》start_kws();
T.start();
while (1) {
/* A dummy loop to wait for the interrupts. Feature extraction and
neural network inference are done in the interrupt service routine. */
__WFI();
}
}
/*
* The audio recording works with two ping-pong buffers.
* The data for each window will be tranfered by the DMA, which sends
* sends an interrupt after the transfer is completed.
*/
// Manages the DMA Transfer complete interrupt.
void BSP_AUDIO_IN_TransferComplete_CallBack(void)
{
ARM_copy_q7((q7_t *)kws-》audio_buffer_in + kws-》audio_block_size*4, (q7_t *)kws-》audio_buffer_out + kws-》audio_block_size*4, kws-》audio_block_size*4);
if(kws-》frame_len != kws-》frame_shift) {
//copy the last (frame_len - frame_shift) audio data to the start
ARM_copy_q7((q7_t *)(kws-》audio_buffer)+2*(kws-》audio_buffer_size-(kws-》frame_len-kws-》frame_shift)), (q7_t *)kws-》audio_buffer, 2*(kws-》frame_len-kws-》frame_shift));
}
// copy the new recording data
for (int i=0;i《kws-》audio_block_size;i++) {
kws-》audio_buffer[kws-》frame_len-kws-》frame_shift+i] = kws-》audio_buffer_in[2*kws-》audio_block_size+i*2];
}
run_kws();
return;
}
// Manages the DMA Half Transfer complete interrupt.
void BSP_AUDIO_IN_HalfTransfer_CallBack(void)
{
ARM_copy_q7((q7_t *)kws-》audio_buffer_in, (q7_t *)kws-》audio_buffer_out, kws-》audio_block_size*4);
if(kws-》frame_len!=kws-》frame_shift) {
//copy the last (frame_len - frame_shift) audio data to the start
ARM_copy_q7((q7_t *)(kws-》audio_buffer)+2*(kws-》audio_buffer_size-(kws-》frame_len-kws-》frame_shift)), (q7_t *)kws-》audio_buffer, 2*(kws-》frame_len-kws-》frame_shift));
}
// copy the new recording data
for (int i=0;i《kws-》audio_block_size;i++) {
kws-》audio_buffer[kws-》frame_len-kws-》frame_shift+i] = kws-》audio_buffer_in[i*2];
}
run_kws();
return;
}
void run_kws()
{
kws-》extract_features(); //extract mfcc features
kws-》classify(); //classify using dnn
kws-》average_predictions();
plot_mfcc();
plot_waveform();
int max_ind = kws-》get_top_class(kws-》averaged_output);
if(kws-》averaged_output[max_ind]》detection_threshold*128/100)
sprintf(lcd_output_string,“%d%% %s”,((int)kws-》averaged_output[max_ind]*100/128),output_class[max_ind]);
lcd.ClearStringLine(8);
lcd.DisplayStringAt(0, LINE(8), (uint8_t *) lcd_output_string, CENTER_MODE);
}
清單 2:在 ARM ML-KWS-for-MCU 軟件存儲(chǔ)庫中,DS-CNN KWS 應(yīng)用程序的主例程(通過KWS_F746NG)實(shí)例化推理引擎,激活 STM32F746G-DISCO 開發(fā)板的音頻子系統(tǒng),并進(jìn)入無限循環(huán),等待中斷調(diào)用執(zhí)行推理的完成例程 ( run_kws())。(代碼來源:ARM)
包含在此main例程中的回調(diào)函數(shù)提供完成例程,這些例程緩沖記錄的數(shù)據(jù)并通過調(diào)用run_kws(). 該run_kws函數(shù)調(diào)用對(duì)推理引擎實(shí)例的調(diào)用以提取特征、對(duì)結(jié)果進(jìn)行分類并提供預(yù)測(cè),以指示記錄的音頻樣本屬于如前所述的原始訓(xùn)練中使用的 12 個(gè)類之一的概率。
推理引擎本身是通過一系列調(diào)用實(shí)例化的,從main實(shí)例化KWS_F746NG類的調(diào)用開始,該類本身是類的子KWS_DS_NN類。后一個(gè)類使用父類封裝了前面顯示的 C++ DS-CNN 模型,該類KWS實(shí)現(xiàn)了特定的推理引擎方法:extract_features()、classify()等(清單 3)。
#include “kws.h”
KWS::KWS()
{
}
KWS::~KWS()
{
delete mfcc;
delete mfcc_buffer;
delete output;
delete predictions;
delete averaged_output;
}
void KWS::init_kws()
{
num_mfcc_features = nn-》get_num_mfcc_features();
num_frames = nn-》get_num_frames();
frame_len = nn-》get_frame_len();
frame_shift = nn-》get_frame_shift();
int mfcc_dec_bits = nn-》get_in_dec_bits();
num_out_classes = nn-》get_num_out_classes();
mfcc = new MFCC(num_mfcc_features, frame_len, mfcc_dec_bits);
mfcc_buffer = new q7_t[num_frames*num_mfcc_features];
output = new q7_t[num_out_classes];
averaged_output = new q7_t[num_out_classes];
predictions = new q7_t[sliding_window_len*num_out_classes];
audio_block_size = recording_win*frame_shift;
audio_buffer_size = audio_block_size + frame_len - frame_shift;
}
void KWS::extract_features()
{
if(num_frames》recording_win) {
//move old features left
memmove(mfcc_buffer,mfcc_buffer+(recording_win*num_mfcc_features),(num_frames-recording_win)*num_mfcc_features);
}
//compute features only for the newly recorded audio
int32_t mfcc_buffer_head = (num_frames-recording_win)*num_mfcc_features;
for (uint16_t f = 0; f 《 recording_win; f++) {
mfcc-》mfcc_compute(audio_buffer+(f*frame_shift),&mfcc_buffer[mfcc_buffer_head]);
mfcc_buffer_head += num_mfcc_features;
}
}
void KWS::classify()
{
nn-》run_nn(mfcc_buffer, output);
// Softmax
ARM_softmax_q7(output,num_out_classes,output);
}
int KWS::get_top_class(q7_t* prediction)
{
int max_ind=0;
int max_val=-128;
for(int i=0;i《num_out_classes;i++) {
if(max_val《prediction[i]) {
max_val = prediction[i];
max_ind = i;
}
}
return max_ind;
}
void KWS::average_predictions()
{
//shift right old predictions
ARM_copy_q7((q7_t *)predictions, (q7_t *)(predictions+num_out_classes), (sliding_window_len-1)*num_out_classes);
//add new predictions
ARM_copy_q7((q7_t *)output, (q7_t *)predictions, num_out_classes);
//compute averages
int sum;
for(int j=0;j《num_out_classes;j++) {
sum=0;
for(int i=0;i《sliding_window_len;i++)
sum += predictions[i*num_out_classes+j];
averaged_output[j] = (q7_t)(sum/sliding_window_len);
}
}
清單 3:在 ARM DS-CNN KWS 應(yīng)用程序中,KWS 模塊在基本 DS-CNN 類上添加了執(zhí)行推理操作所需的方法,包括特征提取、分類和生成由平均窗口平滑的結(jié)果。(代碼來源:ARM)
所有這些軟件復(fù)雜性都隱藏在一個(gè)簡(jiǎn)單的使用模型后面,其中主程序通過實(shí)例化推理引擎來啟動(dòng)該過程,并在音頻輸入可用時(shí)使用其完成程序執(zhí)行推理。據(jù) ARM 稱,在 STM32F746G-DISCO 開發(fā)板上運(yùn)行的這個(gè)示例 CMSIS-NN 實(shí)現(xiàn)只需要大約 12 毫秒 (ms) 即可完成一個(gè)推理周期,其中包括音頻數(shù)據(jù)緩沖區(qū)復(fù)制、特征提取和 DS-CNN 模型執(zhí)行。同樣重要的是,完整的 KWS 應(yīng)用程序只需要大約 70 KB 的內(nèi)存。
結(jié)論
隨著 KWS 功能作為一項(xiàng)要求變得越來越重要,資源有限的可穿戴設(shè)備和其他物聯(lián)網(wǎng)設(shè)計(jì)的開發(fā)人員需要占用空間小的推理引擎。ARM CMSIS-NN 旨在利用 ARM Cortex-M7 MCU 中的 DSP 功能,為實(shí)現(xiàn)優(yōu)化的神經(jīng)網(wǎng)絡(luò)架構(gòu)(如 DS-CNN)奠定了基礎(chǔ),能夠滿足這些要求。
在基于 ARM Cortex-M7 MCU 的開發(fā)系統(tǒng)上運(yùn)行的 KWS 推理引擎可以在資源有限的物聯(lián)網(wǎng)設(shè)備輕松支持的內(nèi)存占用中實(shí)現(xiàn)接近 10 次推理/秒的性能。
評(píng)論
查看更多