首页 > 其他分享 >在STM32上运行KWS之三 模型搭建与训练测试

在STM32上运行KWS之三 模型搭建与训练测试

时间:2024-12-11 16:10:20浏览次数:5  
标签:模型 之三 torch STM32 KWS 64 model self Conv2d

此篇文章在2023年2月6日被记录

搭建一个怎样的模型

KWS模型结构属于比较简单的模型结构,但是为了少走弯路,我计划使用现成的结构,我从这个演示视频参考而来:点击我跳转,这个KWS项目运行在AT32F403上,其网络模型结构为一个64个特征的普通卷积层,然后重复四次的DS-CNN卷积,在每次卷积后都进行一次relu,最后进行一次池化,用来减少全连接层的参数。
模型部分代码如下:

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=64,
                                     kernel_size=(4, 10), padding=(1, 4), stride=(2, 2))
        self.fc1 = torch.nn.Linear(in_features=12*5*64, out_features=6)
        self.avgpool = torch.nn.AvgPool2d(
            kernel_size=(1*4), stride=(1, 1), padding=(0, 0))
        self.depthwise = torch.nn.Conv2d(in_channels=64, out_channels=64, kernel_size=(
            3, 3), padding=(1, 1), stride=(1, 1), groups=64)
        self.pointwise = torch.nn.Conv2d(
            in_channels=64, out_channels=64, kernel_size=1)
        self.pooling = torch.nn.MaxPool2d(kernel_size=(2, 1))

    def dw_conv(self, x):
        x = self.depthwise(x)
        x = F.relu(x)
        x = self.pointwise(x)
        x = F.relu(x)
        return x

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.dw_conv(x)
        x = self.dw_conv(x)
        x = self.dw_conv(x)
        x = self.dw_conv(x)
        #x = self.avgpool(x)
        x = self.pooling(x)
        x = x.view(int(x.size(0)), -1)
        x = self.fc1(x)
        return x

为什么使用更换CNN为DS-CNN(深度可分离卷积)

对于一张5×5像素、三通道(shape为5×5×3),经过3×3卷积核的卷积层(假设输出通道数为4,则卷积核shape为3×3×3×4,最终输出4个Feature Map,如果有same padding则尺寸与输入层相同(5×5),如果没有则为尺寸变为3×3,可以看到参数数量成几何倍的增长,计算量也呈几何倍的增长,示意图如下:

img

更换为DS-CNN,分别对三个通道上提取特征,得到三张特征图,在对三张图进行逐点卷积,有几个卷积核就输出几张特征图,与常规来讲,降低了大量不必要的特征图,节省了参数数量与计算量,示意图如下:

img

img

回顾一下,常规卷积的参数个数为:
N_std = 4 × 3 × 3 × 3 = 108
Separable Convolution的参数由两部分相加得到:
N_depthwise = 3 × 3 × 3 = 27
N_pointwise = 1 × 1 × 3 × 4 = 12
N_separable = N_depthwise + N_pointwise = 39
相同的输入,同样是得到4张Feature map,Separable Convolution的参数个数是常规卷积的约1/3。因此,在参数量相同的前提下,采用Separable Convolution的神经网络层数可以做的更深,计算量和对内存占用更少,这在MCU设备上是相当重要的。

模型训练

我们已经知道了我们使用怎样的模型结构,就可以直接来训练了,迭代50次看看效果,并且保存loss值最小的模型到本地(其实不建议这么做,出来的模型可能是过拟合的模型)

*建立模型
train_model = Net()
*构建损失
criterion = torch.nn.CrossEntropyLoss()
*构建优化器
optimizer = torch.optim.RMSprop(train_model.parameters(),lr=0.001,alpha=0.9)
def train(model , epochs):
    epoches = epochs
    best_loss_val = 1
    targets = torch.tensor([0, 1, 2, 3, 4,5])
    for epoch in range(epoches):
        for i in range(0, 1700):
            inputs = torch.Tensor(([background_mfcc[i]],[one_mfcc[i]], [two_mfcc[i]], [
                                  three_mfcc[i]], [four_mfcc[i]], [five_mfcc[i]]))
            outputs = model(inputs)
            loss = criterion(outputs, targets)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            loss_val = loss.data.numpy()
        if loss_val<best_loss_val:
            torch.save(model, 'best_model.pth')
            best_loss_val = loss_val
            print("saved as \"best_model.pth\"")
            print("best_loss_val:",best_loss_val)
        print('Epoch: ', epoch, '| train loss: %.10f' % loss_val)
train(train_model,50)

训练完成后生成了best_model.pth到本地,我们可以测试这个模型

模型测试

读取保存在本地的模型文件,使用300个测试样本进行测试,分别打印每个样本的错误数量,准确率,以及模型结构和参数大小

def test(model):
    nothing = 0
    one = 0
    two = 0
    three = 0
    four = 0
    five = 0
    for i in range(1700, 2000):
        inputs = torch.Tensor(([background_mfcc[i]],[one_mfcc[i]], [two_mfcc[i]], [
                                  three_mfcc[i]], [four_mfcc[i]], [five_mfcc[i]]))
        outputs = model(inputs)
        pred_y = torch.max(outputs, 1)[1].data.numpy().squeeze()
        if pred_y[0] != 0 :
            nothing +=1
        if pred_y[1] != 1 :
            one +=1
        if pred_y[2] != 2 :
            two +=1
        if pred_y[3] != 3 :
            three +=1
        if pred_y[4] != 4 :
            four +=1
        if pred_y[5] != 5 :
            five +=1
    print(nothing,one,two,three,four,five)
    print((1-nothing/300),(1-one/300),(1-two/300),(1-three/300),(1-four/300),(1-five/300))
test_model = torch.load('best_model.pth')
test(test_model)
summary(test_model, input_size=[(1, 49, 10)],device="cpu")

打印的log:

0 10 19 38 12 33   测试过程中,每个样本有多少个错误
1.0 0.9666666666666667 0.9366666666666666 0.8733333333333333 0.96 0.89  测试结果准确率,可以看出来基本都在90%左右

        Layer (type)               Output Shape         Param #
            Conv2d-1            [-1, 64, 24, 5]           2,624
            Conv2d-2            [-1, 64, 24, 5]             640
            Conv2d-3            [-1, 64, 24, 5]           4,160
            Conv2d-4            [-1, 64, 24, 5]             640
            Conv2d-5            [-1, 64, 24, 5]           4,160
            Conv2d-6            [-1, 64, 24, 5]             640
            Conv2d-7            [-1, 64, 24, 5]           4,160
            Conv2d-8            [-1, 64, 24, 5]             640
            Conv2d-9            [-1, 64, 24, 5]           4,160
        MaxPool2d-10            [-1, 64, 12, 5]               0
           Linear-11                    [-1, 6]          23,046
Total params: 44,870
Trainable params: 44,870
Non-trainable params: 0
Input size (MB): 0.00
Forward/backward pass size (MB): 0.56
Params size (MB): 0.17
Estimated Total Size (MB): 0.73

转换为ONNX模型

开放神经网络交换(Open Neural Network Exchange)简称ONNX,是微软和Facebook提出用来表示深度学习模型的开放格式。所谓开放就是ONNX定义了一组和环境,平台均无关的标准格式,来增强各种AI模型的可交互性。换句话说,无论你使用何种训练框架训练模型(比如TensorFlow/Pytorch/OneFlow/Paddle),在训练完毕后你都可以将这些框架的模型统一转换为ONNX这种统一的格式进行存储。注意ONNX文件不仅仅存储了神经网络模型的权重,同时也存储了模型的结构信息以及网络中每一层的输入输出和一些其它的辅助信息。后文中运行在STM32上,在cube中载入模型时可以选择onnx模型,我们来把我们的best_model.pth转换为best_model.onnx

def convert_onnx(model): 

    # set the model to inference mode 
    model.eval() 

    # Let's create a dummy input tensor  
    dummy_input = torch.randn(1, 1, 49, 10, requires_grad=True)  

    # Export the model   
    torch.onnx.export(model,         # model being run 
         dummy_input,       # model input (or a tuple for multiple inputs) 
         "best_model.onnx",       # where to save the model  
         export_params=True,  # store the trained parameter weights inside the model file 
         opset_version=11,    # the ONNX version to export the model to 
         do_constant_folding=True,  # whether to execute constant folding for optimization 
         input_names = ['input'],   # the model's input names 
         output_names = ['output'], # the model's output names 
         dynamic_axes={'input' : {0 : 'batch_size'},    # variable length axes 
                                'output' : {0 : 'batch_size'}}) 
    print(" ") 
    print('Model has been converted to ONNX')
convert_onnx(test_model)

转换成ONNX模型格式,可以在这个网站更直观的查看我们的模型结构以及参数,可以看到完全符合我们的预期:

img

本章节结束,下一章节将阐述使用cube.ai在STM32上运行测试,以及优化时间

标签:模型,之三,torch,STM32,KWS,64,model,self,Conv2d
From: https://www.cnblogs.com/shumei52/p/18599854

相关文章

  • 在STM32上运行KWS之一 工作简述
    此篇文章在2023年2月1日被记录前景提要公司有一个新产品,产品定义上有一个很愚蠢的交互设计,耳机中有人打电话过来后,会有提示音播放是否接听,此时用户想要接听,随便说什么话都可以接通(甚至噪音都可以),如果不想接通就闭嘴,因此我来了兴趣,能否在单片机上运行简单的语音关键词识别简......
  • 神经网络入门之三 C环境部署
    此篇文章在2022年12月5日被记录入门神经网络三C环境部署为什么要部署到C环境上面两篇文章中,我们详细讲解了采集样本与样本训练,生成了一个模型文件,经过测试我们的模型成功率在90%以上,但是我们想将这个模型利用起来,实时检测我们的运动姿态,在ESP32上运行torch显然是不现实......
  • hal库点亮stm32f103c8t6最小板自带小灯
    hal库点亮stm32f103c8t6最小板自带小灯1.打开STM32CubeMX2.点击创建最小工程3.输入STM32F103C8T6,选中此芯片4.搜索框里面搜索PC13,这个是最小板上面自带的5.设置引脚模式6.创建命名工程,然后选择纯英文路径7.选择版本8.基本小配置9.生成工程代码,并打开......
  • STM32中使用低功耗定时器延时
    此篇文章在2022年5月19日被记录上文说了STM32L4的几种低功耗模式,将其应用起来作为一个低功耗的延时方案。为什么使用低功耗定时器,在追求长时间续航时,单片机有时需要切换到低功耗模式或者停止模式下,在这种模式下,系统主时钟关闭,有一些依赖于系统主时钟的应用程序,可能会发生出现......
  • STM32低功耗入门
    此篇文章在2022年5月19日被记录STM32低功耗介绍:STM32L4系列单片机总共有9中低功耗模式:1、Sleepmode:CPU时钟关闭,IO口保持与运行状态相同的状态。可由wake事件,NVIC,SysTick,外部中断等,无唤醒时间,唤醒后执行唤醒源中断回调函数(和标准库中断服务函数功能一样,但意义不同),然后按原来......
  • 基于STM32单片机的智能点滴输液报警器液位检测电机无线WiFi手机APP设计DR-01非接触液
    25-040-点滴检测+药水液位+电机控制+上下限+按键+声光提醒+TFT彩屏+WiFi产品功能描述:本系统由STM32F103C8T6单片机核心板、TFT液晶显示电路、无线无线WIFI/、点滴检测模块、步进电机控制电路、DR-01非接触液位传感器检测电路、蜂鸣器声光报警、按键电路、电源电路组成。【1......
  • STM32单片机芯片与内部13 TIM-通用定时器TIM2345 高级定时器TIM18-定时计数功能、库函
    目录一、通用定时器库函数工程模板1、TIM_TimeBaseInitTypeDef2、时钟3、初始化4、中断服务函数二、通用定时器库函数API1、初始化封装2、中断服务函数封装三、高级定时器库函数工程模板1、TIM_TimeBaseInitTypeDef2、时钟3、初始化4、中断服务函数四、高级定时......
  • STM32单片机芯片与内部12 TIM-基本定时器TIM67 -定时计数功能、库函数配置、HAL库配置
    目录一、功能二、库函数工程模板1、NVIC_InitTypeDef与TIM_TimeBaseInitTypeDef2、时钟使能3、初始化4、清除中断5、开启/关闭中断6、使能/失能计数器三、库函数API1、初始化的封装2、中断服务函数四、HAL库工程模板1、TIM_HandleTypeDef2、TIM_MasterConfigType......
  • 分享一个对STM32 ADC单次转换模式、连续转换模式扫描模式的理解
    目录不启动SCAN模式在单次转换模式下:启动SCAN模式下在连续转换模式下此随笔出自对STM32ADC单次转换模式、连续转换模式扫描模式的理解举例用ADC1规则通道的顺序为CH0,CH1,CH2,CH3不启动SCAN模式在单次转换模式下:启动ADC1,则1.开始转换CHO(ADCSQR的第一通道)2.转换完成......
  • STM32开发环境笔记
             ......