前面的文章(飞桨paddlespeech语音唤醒推理C浮点实现)讲了飞桨paddlespeech语音唤醒推理的C浮点实现。但是嵌入式设备通常CPU频率低和memory小,在嵌入式设备上要想流畅的运行语音唤醒功能,通常用的是定点实现。于是我就在浮点实现(把卷积层和相应的batchNormal层合并成一个卷积层)的基础上做了定点实现。需要说明的是目前完成的是16bit的定点实现,后面会在此基础上做8bit的定点实现。

做定点实现主要包括两部分工作,一是模型参数的量化和定Q格式等,二是基于Q格式的定点实现。关于模型参数的量化,我曾写过相关的文章(深度学习中神经网络模型的量化),有兴趣的可以去看看。我用的是对称量化,这里简述一下这部分的工作。

1,  在python下根据paddlepaddle提供的API(named_parameters)得到模型每层的参数(weight & bias),同时看每层的weight和bias的绝对值的最大值,从而确定参数的Q格式,再以这个Q格式对weight 和bias做量化。

2,  在python下得到测试集里非常多个文件每层的输入和输出的绝对值的最大值,从而确定每层的输入和输出的Q格式。

至于代码的定点化,主要包括如下几点:

1,  卷积层的定点化

主要是做好乘累加以及输出的移位和防饱和处理。在文章(深度学习中神经网络模型的量化)里有详细描述,这里就不细讲了。

2,  sigmoid的定点化

调研了一下,sigmoid的定点化主要用查表法来实现。Sigmoid(x)在x<=-8时近似为0,在x>=8时近似为1,因此做表时在[-8,8)之间就可以了。 若表中有256个值,则表中x的间隔是16/256 = 0.0625。表中第一个值对应的是x=-8时sigmoid的值,第二个值对应的是x=-7.9375(-8 + 0.0635 = -7.9375)时sigmoid的值,以此类推。Sigmoid输出的取值范围是(0,1),因此用的Q格式是Q0.15。例如当x=0时,sigmoid(0) = 0.5,表示成Q0.15格式是0x4000。当x在[-8,8)范围内每隔0.0625的256个sigmoid值都算出来并换算成Q0.15格式,就得到表中的256个值了。

具体实现时参考率CMSIS_5的代码,如下图:

做表时把前128个值(x < 0时的)与后128个值(x>=0时的)做了位置上的互换。主要是因为处理时先对x定点化后的16位输入值做右移8位处理,就变成了8位的值,再变成unsigned char(U8)用于做表的索引。 U8(0) = 0, U8(127) = 127, 但U8(-128) = 128, U8(-127) = 129, ……, U8(-1) = 255。所以表中的位置前后部分就互换了。再看sigmoid层的输入与sigmoid函数的输入的关系。 假设sigmoid函数输入的16位定点值为0x1869,右移8位后为0x18,即为24。表中第24个代表的是x=1.5(24 * 0.0675 = 1.5)时的sigmoid值。我的sigmoid层的输入Q格式是Q7.8, 1.5用Q7.8表示就是0x0180, 而函数中要求的是0x18XX,所以需要把层的输入的值做左移4位处理。由于sigmoid函数只对[-8,8)内的值做处理,因此首先需要对层的输入值做[-8,8)的限幅处理。上面两步的代码如下图:

调sigmoid_q15()时把int_width设成3,就表示输入范围是[-8,8)。 由于输入的x值不一定正好落在表中的那些点上,如x = 0.0325就落在点0.0和点0.0625之间。 为了使sigmoid的输出值更准确,函数中用线性插值法求那些不落在点上的sigmoid值。我在文章(基于sinc的音频重采样(二):实现)中讲过线性插值法,有兴趣的可以去看看。要想sigmoid的输出值更准确,还可以扩大表里值的个数,比如变成512个值,代价是多用些memory。

3,  确定好评估的指标

我在文章(深度学习中神经网络模型的量化)中对评估指标有所描述。这里我选用的是欧氏距离(Euclidean Distance)。具体调试时浮点实现和定点实现并行运行。即算出的浮点的fbank值作为浮点实现模型的输入,将浮点的Fbank值根据定标转换成定点值作为定点实现模型的输入,然后每层的浮点实现和定点实现并行运行。浮点实现得到的结果是浮点值,定点实现得到的结果是定点值,再根据输出的Q格式转换成浮点值。最后再用欧氏距离对输出结果进行评估。下图给出了某一depthwise卷积层的实现代码。先做浮点的卷积层运算,结果保存在fbankFloat里,然后做定点的卷积层运算,结果保存在fbankFix里,再根据输出的Q格式将fbankFix转换成浮点值,最后算欧氏距离。欧氏距离越小越好。

下图给出了调试好后部分层的欧氏距离的值,都是很小的(图中0/1/2等表示卷积层ID)。

4,如何调试

模型定点化调试时要从第一层到最后一层一层一层的调试,只有当上一层的欧氏距离达标后再去调下一层。具体到调试某一层时,通过log找到那些浮点值与定点转浮点后的值差值较大的值,再到浮点实现和定点实现里打印出输入和运算后的具体值,分析具体原因。有可能是定点实现里移位防饱和等没做好,也有可能是参数量化没作对,还有可能是输入和输出的Q格式没定好导致误差偏大等。在定输入和输出的Q格式时,是根据绝对值的最大值来的。如果发现精度不够,有可能需要调整输入或输出的Q格式(小数位要多一位,依据是看超出定标最大值出现的次数,次数占比较小就可以)。

调试时是用一个音频文件去调。等模型调试完成后要在一个大的数据集上对定点实现做全面的评估,看唤醒率和误唤醒率的变化。我做完定点实现后在一个有两万五千多音频文件的数据集上做评估,跟浮点实现比,唤醒率下降了0.2%,误唤醒率上升了0.3%。说明定点化后性能没有出现明显的下降。

飞桨paddlespeech语音唤醒推理C定点实现的更多相关文章

  1. 飞桨paddlespeech语音唤醒推理C实现

    上篇(飞桨paddlespeech 语音唤醒初探)初探了paddlespeech下的语音唤醒方案,通过调试也搞清楚了里面的细节.因为是python 下的,不能直接部署,要想在嵌入式上部署需要有C下的推 ...

  2. 讯飞语音唤醒SDK集成流程

    唤醒功能,顾名思义,通过语音,唤醒服务,做我们想做的事情. 效果图(开启应用后说讯飞语音或者讯飞语点唤醒) 源码下载 地址:http://download.csdn.net/detail/q48788 ...

  3. android 开发 讯飞语音唤醒功能

    场景:进入程序后处于语音唤醒状态,当说到某个关键词的时候打开某个子界面(如:语音识别界面) 技术要点: 1. // 设置唤醒一直保持,直到调用stopListening,传入0则完成一次唤醒后,会话立 ...

  4. 【百度飞桨】手写数字识别模型部署Paddle Inference

    从完成一个简单的『手写数字识别任务』开始,快速了解飞桨框架 API 的使用方法. 模型开发 『手写数字识别』是深度学习里的 Hello World 任务,用于对 0 ~ 9 的十类数字进行分类,即输入 ...

  5. 我做的百度飞桨PaddleOCR .NET调用库

    我做的百度飞桨PaddleOCR .NET调用库 .NET Conf 2021中国我做了一次<.NET玩转计算机视觉OpenCV>的分享,其中提到了一个效果特别好的OCR识别引擎--百度飞 ...

  6. 提速1000倍,预测延迟少于1ms,百度飞桨发布基于ERNIE的语义理解开发套件

    提速1000倍,预测延迟少于1ms,百度飞桨发布基于ERNIE的语义理解开发套件 11月5日,在『WAVE Summit+』2019 深度学习开发者秋季峰会上,百度对外发布基于 ERNIE 的语义理解 ...

  7. 树莓派4B安装 百度飞桨paddlelite 做视频检测 (一、环境安装)

    前言: 当前准备重新在树莓派4B8G 上面搭载训练模型进行识别检测,训练采用了百度飞桨的PaddleX再也不用为训练部署环境各种报错发愁了,推荐大家使用. 关于在树莓派4B上面paddlelite的文 ...

  8. 百度飞桨数据处理 API 数据格式 HWC CHW 和 PIL 图像处理之间的关系

    使用百度飞桨 API 例如:Resize Normalize,处理数据的时候. Resize:如果输入的图像是 PIL 读取的图像这个数据格式是 HWC ,Resize 就需要 HWC 格式的数据. ...

  9. Ubuntu 百度飞桨和 CUDA 的安装

    Ubuntu 百度飞桨 和 CUDA 的安装 1.简介 本文主要是 Ubuntu 百度飞桨 和 CUDA 的安装 系统:Ubuntu 20.04 百度飞桨:2.2 为例 2.百度飞桨安装 访问百度飞桨 ...

  10. 【一】ERNIE:飞桨开源开发套件,入门学习,看看行业顶尖持续学习语义理解框架,如何取得世界多个实战的SOTA效果?

    ​ 参考文章: 深度剖析知识增强语义表示模型--ERNIE_财神Childe的博客-CSDN博客_ernie模型 ERNIE_ERNIE开源开发套件_飞桨 https://github.com/Pad ...

随机推荐

  1. Kubernetes入门实践(环境搭建)

    容器技术只是解决了运维部署工作中的一个很小的问题,在现实生产环境中,除了最基本的安装,还会各式各样的需求,比如服务发现.负载均衡.状态监控.健康检查.扩容缩容.应用迁移.高可用等等.这些容器之上的管理 ...

  2. 5219. 【GDOI2018模拟7.10】B

    5219. [GDOI2018模拟7.10]B 题目大意: 考试想法: 正解: 代码: 题目大意: 现在有一个字符串 s s s 当 s [ i ] s[i] s[i]为 I I I时 a n s [ ...

  3. JS 实现关键字文本搜索 高亮显示

    示例:  利用字符串的 split 方法,通过搜索的关键字分割成数组  在利用数组的 join 方法拼接成字符串 我是利用mock的省份 1 <template> 2 <div cl ...

  4. Redis分布式锁实现及使用

    文章目录 分布式锁 全局ID生成器 一人一单实现 超卖问题 一人一单 分布式锁 Redis setnx实现分布式锁 Redis在业内解决秒杀等业务场景有非常广的应用,如何设计实现一个分布式锁是解决超卖 ...

  5. Prism Sample 11-UsingDelegateCommands

    本例的知识点,全在ViewModel中,看代码: 1 public class MainWindowViewModel : BindableBase 2 { 3 private bool _isEna ...

  6. Godot 4.0 加载为占位符(InstancePlaceholder)的用法和特点

    加载为占位符的功能设计上是用来辅助选择性加载场景的.比如一个很庞大的3D世界,玩家一时之间只会处在世界一小部分区域内,同时让整个地图驻留于内存是不现实的,此时需要选择性地灵活加载地图,使用Godot的 ...

  7. 如何在 🤗 Space 上托管 Unity 游戏

    你知道吗?Hugging Face Space 可以托管自己开发的 Unity 游戏!惊不惊喜,意不意外?来了解一下吧! Hugging Face Space 是一个能够以简单的方式来构建.托管和分享 ...

  8. vant中van-dialog组件点击确认按钮禁止弹窗自动关闭

    1.在van-dialog组件中添加 before-close 属性, 2.定义该方法 newGroupBefColse(action, done) { if (action == 'confirm' ...

  9. 现代 CSS 解决方案:CSS 原生支持的三角函数

    在 CSS 中,存在许多数学函数,这些函数能够通过简单的计算操作来生成某些属性值,例如 : calc():用于计算任意长度.百分比或数值型数据,并将其作为 CSS 属性值. min() 和 max() ...

  10. 终极指南!Terraform的进阶技巧

    如果您已经对 Terraform 了如指掌,并期望自己的 IaC 技能有进一步提升的话,这篇文章很适合您!在本文中,我们将分享一些 Terraform 的高级使用技巧.从使用模块(module).工作 ...