使用一類新興的高效算法,任何開發(fā)人員現(xiàn)在都可以在低功耗、資源受限的系統(tǒng)上部署復(fù)雜的關(guān)鍵字識別功能。這個由兩部分組成的系列的第一部分展示了如何使用 FPGA 來做到這一點。在第二部分中,我們將展示如何使用 MCU 來做到這一點。
關(guān)鍵字發(fā)現(xiàn) (KWS) 技術(shù)已成為可穿戴設(shè)備、物聯(lián)網(wǎng)設(shè)備和其他智能產(chǎn)品越來越重要的功能。機器學(xué)習(xí)方法可以提供卓越的 KWS 準(zhǔn)確性,但這些產(chǎn)品的功能和性能限制直到最近限制了大型企業(yè)或經(jīng)驗豐富的機器學(xué)習(xí)專家對機器學(xué)習(xí) KWS 解決方案的使用。
然而,開發(fā)人員越來越需要在可穿戴設(shè)備和其他物聯(lián)網(wǎng)設(shè)備中在能夠在這些設(shè)備的資源限制內(nèi)運行的更高效的 KWS 引擎上實現(xiàn)語音激活的 KWS 功能。深度可分離卷積神經(jīng)網(wǎng)絡(luò) (DS-CNN) 架構(gòu)修改了傳統(tǒng)的 CNN 以提供所需的效率。
使用硬件優(yōu)化的神經(jīng)網(wǎng)絡(luò)庫,開發(fā)人員可以實現(xiàn)需要最少資源的基于 MCU 的 DS-CNN 推理引擎。本文描述了 DS-CNN 架構(gòu),并展示了開發(fā)人員如何在 MCU 上實現(xiàn) DS-CNN KWS。
為什么選擇 KWS?
消費者對使用“Alexa”、“Hey Siri”或“Ok Google”等短語的智能手機和家用電器上的語音激活功能的接受度已迅速演變?yōu)閷缀跛袨橛脩艚换ザO(shè)計的產(chǎn)品的語音服務(wù)的廣泛需求。在這些服務(wù)的基礎(chǔ)上,準(zhǔn)確的語音識別依賴于各種人工智能方法來識別口語單詞,并將單詞和詞組解釋為適合應(yīng)用程序的命令。
然而,快速準(zhǔn)確地完成整個語音命令序列所需的資源開始超過低成本線路供電消費集線器的能力,更不用說電池供電的個人電子產(chǎn)品了。
語音命令管道要求
為了在這些產(chǎn)品上提供語音激活,開發(fā)人員將語音命令管道拆分或?qū)⒄Z音命令限制為幾個非常簡單的詞,例如“開”和“關(guān)”。在資源有限的消費產(chǎn)品上,開發(fā)人員使用神經(jīng)網(wǎng)絡(luò)推理引擎實現(xiàn) KWS 功能,該引擎能夠為 Alexa、Siri 或 Google 的簡單命令或命令激活短語提供所需的高精度、低延遲響應(yīng)(圖 1)。
在這里,設(shè)計將輸入音頻流數(shù)字化,提取語音特征,并將這些特征傳遞給神經(jīng)網(wǎng)絡(luò)以識別關(guān)鍵字。
??
圖 1:使用 KWS 進(jìn)行語音激活使用處理管道從音頻輸入信號中提取頻域特征,并對提取的特征進(jìn)行分類,以預(yù)測輸入信號對應(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)過驗證的能力,根據(jù)神經(jīng)網(wǎng)絡(luò)訓(xùn)練期間使用的標(biāo)簽之一對口語進(jìn)行準(zhǔn)確分類。2對于更復(fù)雜的語音接口,命令處理管道超出了設(shè)備本身。在 KWS 推理引擎檢測到激活關(guān)鍵字或短語后,產(chǎn)品將數(shù)字化的音頻流傳遞給基于云的服務(wù),能夠更有效地處理復(fù)雜的語音處理和命令識別操作。
盡管如此,設(shè)備資源可用性和推理引擎資源需求之間的沖突仍使開發(fā)人員試圖將這些方法應(yīng)用于更小的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備設(shè)計的嘗試。盡管經(jīng)典的 CNN 相對容易理解且易于訓(xùn)練,但這些模型仍然可能是資源密集型的。隨著 CNN 模型在識別圖像方面的準(zhǔn)確度大幅提高,CNN 的大小和復(fù)雜度也顯著增加。
結(jié)果是非常精確的 CNN 模型,需要數(shù)十億計算密集型通用矩陣乘法 (GEMM) 操作進(jìn)行訓(xùn)練。一旦經(jīng)過訓(xùn)練,相應(yīng)的推理模型可能會占用數(shù)百兆內(nèi)存,并且需要非常大量的 GEMM 操作才能進(jìn)行單個推理。
對于電池供電的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備,有效的 KWS 推理模型必須能夠在有限的內(nèi)存中運行且處理要求低。此外,由于 KWS 推理引擎必須在“始終開啟”模式下運行才能執(zhí)行其功能,因此它必須能夠以最低功耗運行。
在越來越有吸引力的可穿戴設(shè)備和物聯(lián)網(wǎng)設(shè)備領(lǐng)域,神經(jīng)網(wǎng)絡(luò)的潛力與有限資源之間的這種二分法引起了機器學(xué)習(xí)專家的極大關(guān)注。結(jié)果是開發(fā)了優(yōu)化基本 CNN 模型的技術(shù),并出現(xiàn)了替代神經(jīng)網(wǎng)絡(luò)架構(gòu),能夠彌合小型資源受限設(shè)備的性能要求和資源能力之間的差距。
小尺寸型號
在創(chuàng)建小型模型的技術(shù)中,機器學(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 位浮點數(shù),甚至早期 CNN 中的 16 位和 8 位浮點數(shù)減少到 1 位價值觀。如第 1 部分所述,Lattice Semiconductor機器學(xué)習(xí)SensAI?平臺使用這種高效的 BNN 架構(gòu)作為 1 毫瓦 (mW) KWS 解決方案的基礎(chǔ),該解決方案在其基于 FPGA 的iCE40 UltraPlus移動開發(fā)平臺,或 MDP。
除了網(wǎng)絡(luò)修剪和參數(shù)量化等簡化技術(shù)外,還有其他降低資源需求的方法可以修改 CNN 架構(gòu)本身的拓?fù)浣Y(jié)構(gòu)。在這些替代架構(gòu)中,深度可分離卷積神經(jīng)網(wǎng)絡(luò)提供了一種特別有效的方法來創(chuàng)建能夠在通用 MCU 上運行的小型、資源高效模型。
在早期工作的基礎(chǔ)上,谷歌機器學(xué)習(xí)專家找到了一種通過關(guān)注卷積層本身來提高 CNN 效率的方法。在傳統(tǒng)的 CNN 中,每個卷積層過濾輸入特征并在一個步驟中將它們組合成一組新的特征(圖 2,頂部)。
??
圖 2:與全卷積(上)不同,深度可分離卷積首先使用 D K xD K濾波器(中)分別過濾 M 個輸入通道中的每一個,并使用逐點 1 x 1 卷積來創(chuàng)建 N 個新特征。(圖片來源:谷歌)
新方法將過濾和特征生成分為兩個獨立的階段,統(tǒng)稱為深度可分離卷積。第一階段執(zhí)行深度卷積,該卷積在輸入的每個通道上充當(dāng)空間濾波器(圖 2,中間)。因為第一階段不創(chuàng)建新特征(深度神經(jīng)網(wǎng)絡(luò)架構(gòu)的核心目標(biāo)),所以第二階段執(zhí)行逐點 1 x 1 卷積(圖 2,底部),它結(jié)合第一階段的輸出以生成新特征。
這種 DS-CNN 架構(gòu)在用于移動和嵌入式視覺應(yīng)用的 Google MobileNet 模型中使用,減少了參數(shù)和相關(guān)操作的數(shù)量,從而產(chǎn)生更小的模型,需要顯著更少的計算來獲得準(zhǔn)確的結(jié)果。3
與全卷積相比,在 MobileNet 模型中使用深度可分離卷積在行業(yè)標(biāo)準(zhǔn) ImageNet 數(shù)據(jù)集上僅降低了 1% 的準(zhǔn)確度,但使用了不到 12% 的乘加運算和所需的模型參數(shù)數(shù)量的 14%用于傳統(tǒng) ImageNet CNN 模型中的全卷積。
盡管 DS-CNN 最初是為圖像識別而開發(fā)的,但這些相同的模型可以通過將音頻輸入流轉(zhuǎn)換為頻譜圖來提供一組可用的特征來提供音頻識別。實際上,音頻前端將音頻流轉(zhuǎn)換為 DS-CNN 可以分類的一組特征。對于語音處理,前端產(chǎn)生的特征通常采用梅爾頻率倒譜系數(shù)(MFCC)的形式,它更接近人類的聽覺特征,同時顯著降低了傳遞給 DS-CNN 分類器的特征集的維數(shù)。這正是 ARM ML-KWS-for-MCU開源軟件存儲庫中使用的方法。
DS-CNN 實現(xiàn)
ARM KWS 存儲庫旨在演示在 Arm Cortex ? -M 系列 MCU 上的 KWS 實施,在包括傳統(tǒng) CNN、DS-CNN 等在內(nèi)的多種架構(gòu)中提供了大量預(yù)訓(xùn)練的 TensorFlow 模型。使用谷歌速度命令數(shù)據(jù)集4訓(xùn)練,模型將音頻輸入分類為 12 個可能的類別之一:“是”、“否”、“上”、“下”、“左”、“右”、“開”、“ 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 模型上運行 TensorFlow 的import_pb_to_tensorboard Python 實用程序后,開發(fā)人員可以使用 TensorBoard 可視化模型的基于 MobileNet 的架構(gòu)。
??
圖 3:在 TensorBoard 中顯示的 Arm 預(yù)訓(xùn)練 KWS 模型將熟悉的 MobileNet DS-CNN 模型(左紅色輪廓)與使用梅爾頻率倒譜系數(shù) (MFCC) 的頻域特征提取階段(右擴展)相結(jié)合. (圖片來源:Digi-Key Electronics)
正如在 TensorBoard 中所展示的那樣,MobileNet 架構(gòu)用深度可分離卷積替換了傳統(tǒng) CNN 架構(gòu)中除第一個完整卷積層之外的所有卷積層。
如前所述,這些階段中的每一個都包括深度卷積和點卷積階段,每個階段都輸入一個 batchnorm 內(nèi)核以對輸出結(jié)果進(jìn)行歸一化(圖 3,左)。DS-CNN 模型使用一個特殊的 TensorFlow 融合 batchnorm 函數(shù),它將多個選項組合到一個內(nèi)核中。
此外,通過放大音頻輸入特征提取階段(圖 3,右),開發(fā)人員可以檢查音頻處理序列,包括音頻解碼、頻譜圖生成和 MFCC 濾波。MFCC 生成的特征通過一對重塑階段來創(chuàng)建 MobileNet 分類器所需的張量形狀。
可以想象,開發(fā)人員可以在基于 MCU 的系統(tǒng)(包括Raspberry Pi )上運行來自 TensorFlow 或其他機器學(xué)習(xí)框架的訓(xùn)練模型。5使用這種方法,開發(fā)人員可以量化經(jīng)過訓(xùn)練的模型,以生成能夠在這些系統(tǒng)上運行的較小版本。但是,如果沒有圖形處理單元 (GPU) 或其他硬件支持 GEMM 加速,推理延遲可能會令用戶對語音激活性能的預(yù)期失望。
ARM 通過其對 ARM Cortex 微控制器軟件接口標(biāo)準(zhǔn) (CMSIS) 的神經(jīng)網(wǎng)絡(luò) (NN) 擴展提供了一種替代方法。CMSIS-NN 提供一整套 CNN 功能,充分利用 ARM Cortex-M7 處理器(例如STMicroelectronics 的 STM32F7 MCU 系列中的那些)中內(nèi)置的 DSP 擴展。除了傳統(tǒng)的 CNN 函數(shù)外,CMSIS-NN 應(yīng)用程序編程接口 (API) 還支持深度可分離卷積,其中一對函數(shù)對應(yīng)于 DS-CNN 架構(gòu)下的深度和逐點 1 x 1 卷積階段:
ARM_status ARM_depthwise_separable_conv_HWC_q7_nonsquare
ARM_status ARM_convolve_1x1_HWC_q7_fast_nonsquare
該 API 還在專門為方形輸入張量設(shè)計的版本中提供了這兩個函數(shù)。
ARM 在示例代碼中使用這些函數(shù),該示例代碼演示了一個完整的基于 DS-CNN 的 KWS 應(yīng)用程序,該應(yīng)用程序在 STMicroelectronics STM32F746G-DISCO開發(fā)板上運行,該開發(fā)板圍繞STM32F746NGH6 MCU 構(gòu)建。在示例代碼的核心,原生 CMSIS-NN C++ 模塊實現(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); // 逐點卷積 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 軟件存儲庫包含一個 C++ DS-CNN 模型,其中一個完整的卷積層后跟幾個深度可分離卷積(框),每個都使用深度卷積和 1 x 1 卷積函數(shù)(黃色突出顯示)在硬件優(yōu)化的 ARM CMSIS-NN 軟件庫中支持。(代碼來源:ARM)
盡管 C++ DS-CNN 實現(xiàn)與前面展示的 TensorBoard DS-CNN 模型略有不同,但總體方法保持不變。在初始全卷積核之后,一系列深度可分離卷積核輸入最終池化層和全連接層,以生成每個輸出通道的預(yù)測值(對應(yīng)于用于訓(xùn)練模型的 12 個類標(biāo)簽)。
KWS 應(yīng)用程序?qū)⒋四P团c代碼相結(jié)合,以提供對 STM32F746G-DISCO 開發(fā)板收集的實時音頻流的推斷。在這里,主函數(shù)初始化推理引擎,啟用音頻采樣,然后進(jìn)入由單個等待中斷 (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 軟件存儲庫中,DS-CNN KWS 應(yīng)用程序的主例程(通過KWS_F746NG)實例化推理引擎,激活 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)用對推理引擎實例的調(diào)用以提取特征、對結(jié)果進(jìn)行分類并提供預(yù)測,以指示記錄的音頻樣本屬于如前所述的原始訓(xùn)練中使用的 12 個類之一的概率。
推理引擎本身是通過一系列調(diào)用實例化的,從main實例化KWS_F746NG類的調(diào)用開始,該類本身是類的子KWS_DS_NN類。后一個類使用父類封裝了前面顯示的 C++ DS-CNN 模型,該類KWS實現(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ù)雜性都隱藏在一個簡單的使用模型后面,其中主程序通過實例化推理引擎來啟動該過程,并在音頻輸入可用時使用其完成程序執(zhí)行推理。據(jù) ARM 稱,在 STM32F746G-DISCO 開發(fā)板上運行的這個示例 CMSIS-NN 實現(xiàn)只需要大約 12 毫秒 (ms) 即可完成一個推理周期,其中包括音頻數(shù)據(jù)緩沖區(qū)復(fù)制、特征提取和 DS-CNN 模型執(zhí)行。同樣重要的是,完整的 KWS 應(yīng)用程序只需要大約 70 KB 的內(nèi)存。
結(jié)論
隨著 KWS 功能作為一項要求變得越來越重要,資源有限的可穿戴設(shè)備和其他物聯(lián)網(wǎng)設(shè)計的開發(fā)人員需要占用空間小的推理引擎。ARM CMSIS-NN 旨在利用 ARM Cortex-M7 MCU 中的 DSP 功能,為實現(xiàn)優(yōu)化的神經(jīng)網(wǎng)絡(luò)架構(gòu)(如 DS-CNN)奠定了基礎(chǔ),能夠滿足這些要求。
在基于 ARM Cortex-M7 MCU 的開發(fā)系統(tǒng)上運行的 KWS 推理引擎可以在資源有限的物聯(lián)網(wǎng)設(shè)備輕松支持的內(nèi)存占用中實現(xiàn)接近 10 次推理/秒的性能。
評論
查看更多