如何在 MCU 上实现 DS-CNN KWS

网友投稿 355 2022-11-23

如何在 MCU 上实现 DS-CNN KWS

为什么选择 KWS?

语音命令管道要求

为了在这些产品上提供语音激活,开发人员将语音命令管道拆分或将语音命令限制为几个非常简单的词,例如“开”和“关”。在资源有限的消费产品上,开发人员使用神经网络推理引擎实现 KWS 功能,该引擎能够为 Alexa、Siri 或 Google 的简单命令或命令激活短语提供所需的高精度、低延迟响应(图 1)。

尽管如此,设备资源可用性和推理引擎资源需求之间的冲突仍使开发人员试图将这些方法应用于更小的可穿戴设备和物联网设备设计的尝试。尽管经典的 CNN 相对容易理解且易于训练,但这些模型仍然可能是资源密集型的。随着 CNN 模型在识别图像方面的准确度大幅提高,CNN 的大小和复杂度也显着增加。

结果是非常精确的 CNN 模型,需要数十亿计算密集型通用矩阵乘法 (GEMM) 操作进行训练。一旦经过训练,相应的推理模型可能会占用数百兆内存,并且需要非常大量的 GEMM 操作才能进行单个推理。

对于电池供电的可穿戴设备和物联网设备,有效的 KWS 推理模型必须能够在有限的内存中运行且处理要求低。此外,由于 KWS 推理引擎必须在“始终开启”模式下运行才能执行其功能,因此它必须能够以最低功耗运行。

在越来越有吸引力的可穿戴设备和物联网设备领域,神经网络的潜力与有限资源之间的这种二分法引起了机器学习专家的极大关注。结果是开发了优化基本 CNN 模型的技术,并出现了替代神经网络架构,能够弥合小型资源受限设备的性能要求和资源能力之间的差距。

小尺寸型号

除了网络修剪和参数量化等简化技术外,还有其他降低资源需求的方法可以修改 CNN 架构本身的拓扑结构。在这些替代架构中,深度可分离卷积神经网络提供了一种特别有效的方法来创建能够在通用 MCU 上运行的小型、资源高效模型。

新方法将过滤和特征生成分为两个独立的阶段,统称为深度可分离卷积。第一阶段执行深度卷积,该卷积在输入的每个通道上充当空间滤波器(图 2,中间)。因为第一阶段不创建新特征(深度神经网络架构的核心目标),所以第二阶段执行逐点 1 x 1 卷积(图 2,底部),它结合第一阶段的输出以生成新特征。

这种 DS-CNN 架构在用于移动和嵌入式视觉应用的 Google MobileNet 模型中使用,减少了参数和相关操作的数量,从而产生更小的模型,需要显着更少的计算来获得准确的结果。3

与全卷积相比,在 MobileNet 模型中使用深度可分离卷积在行业标准 ImageNet 数据集上仅降低了 1% 的准确度,但使用了不到 12% 的乘加运算和所需的模型参数数量的 14%用于传统 ImageNet CNN 模型中的全卷积。

DS-CNN 实现

正如在 TensorBoard 中所展示的那样,MobileNet 架构用深度可分离卷积替换了传统 CNN 架构中除第一个完整卷积层之外的所有卷积层。

如前所述,这些阶段中的每一个都包括深度卷积和点卷积阶段,每个阶段都输入一个 batchnorm 内核以对输出结果进行归一化(图 3,左)。DS-CNN 模型使用一个特殊的 TensorFlow 融合 batchnorm 函数,它将多个选项组合到一个内核中。

此外,通过放大音频输入特征提取阶段(图 3,右),开发人员可以检查音频处理序列,包括音频解码、频谱图生成和 MFCC 滤波。MFCC 生成的特征通过一对重塑阶段来创建 MobileNet 分类器所需的张量形状。

ARM_status ARM_depthwise_separable_conv_HWC_q7_nonsquare

ARM_status ARM_convolve_1x1_HWC_q7_fast_nonsquare

该 API 还在专门为方形输入张量设计的版本中提供了这两个函数。

void DS_CNN::run_nn(q7_t* in_data, q7_t* out_data)

{

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

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 卷积函数(黄色突出显示)在硬件优化的 ARM CMSIS-NN 软件库中支持。(代码来源:ARM)

尽管 C++ DS-CNN 实现与前面展示的 TensorBoard DS-CNN 模型略有不同,但总体方法保持不变。在初始全卷积核之后,一系列深度可分离卷积核输入最终池化层和全连接层,以生成每个输出通道的预测值(对应于用于训练模型的 12 个类标签)。

KWS 应用程序将此模型与代码相结合,以提供对 STM32F746G-DISCO 开发板收集的实时音频流的推断。在这里,主函数初始化推理引擎,启用音频采样,然后进入由单个等待中断 (WFI) 调用组成的无限循环(清单 2)。

char output_class[12][8] = {“Silence”, “Unknown”,“yes”,“no”,“up”,“down”,

“left”,“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) {

__WFI();

}

}

/*

* The audio recording works with two ping-pong buffers.

* 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 应用程序的主例程(通过KWS_F746NG)实例化推理引擎,激活 STM32F746G-DISCO 开发板的音频子系统,并进入无限循环,等待中断调用执行推理的完成例程 ( run_kws())。(代码来源:ARM)

包含在此main例程中的回调函数提供完成例程,这些例程缓冲记录的数据并通过调用run_kws(). 该run_kws函数调用对推理引擎实例的调用以提取特征、对结果进行分类并提供预测,以指示记录的音频样本属于如前所述的原始训练中使用的 12 个类之一的概率。

推理引擎本身是通过一系列调用实例化的,从main实例化KWS_F746NG类的调用开始,该类本身是类的子KWS_DS_NN类。后一个类使用父类封装了前面显示的 C++ DS-CNN 模型,该类KWS实现了特定的推理引擎方法: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

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 应用程序中,KWS 模块在基本 DS-CNN 类上添加了执行推理操作所需的方法,包括特征提取、分类和生成由平均窗口平滑的结果。(代码来源:ARM)

所有这些软件复杂性都隐藏在一个简单的使用模型后面,其中主程序通过实例化推理引擎来启动该过程,并在音频输入可用时使用其完成程序执行推理。据 ARM 称,在 STM32F746G-DISCO 开发板上运行的这个示例 CMSIS-NN 实现只需要大约 12 毫秒 (ms) 即可完成一个推理周期,其中包括音频数据缓冲区复制、特征提取和 DS-CNN 模型执行。同样重要的是,完整的 KWS 应用程序只需要大约 70 KB 的内存。

结论

随着 KWS 功能作为一项要求变得越来越重要,资源有限的可穿戴设备和其他物联网设计的开发人员需要占用空间小的推理引擎。ARM CMSIS-NN 旨在利用 ARM Cortex-M7 MCU 中的 DSP 功能,为实现优化的神经网络架构(如 DS-CNN)奠定了基础,能够满足这些要求。

在基于 ARM Cortex-M7 MCU 的开发系统上运行的 KWS 推理引擎可以在资源有限的物联网设备轻松支持的内存占用中实现接近 10 次推理/秒的性能。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Hadoop之 hdfs 系统
下一篇:矩形重载连接器的性能特点_矩形重载连接器的优势
相关文章

 发表评论

暂时没有评论,来抢沙发吧~