项目介绍
本项目是基于MAX78000实现的特定关键词识别。关键词识别或者说语音唤醒(keyword spotting)是在连续语流中实时检测出说话人特定片段。类似手机唤醒词”hi,sari' 以及天猫精灵的唤醒词功能。
项目设计思路
1.思路设计
“Hi siri”、“天猫精灵”、“小爱同学”,我们生活中常常会叫到这些名字,让她们来帮我们完成一些指令,这个过程就像叫某人帮你做某事的感觉。语音交互前,设备需要先被唤醒,从休眠状态进入工作状态,才能正常的处理用户的指令。
把设备从休眠状态叫醒到工作状态就叫唤醒,我们常见的有触摸唤醒(锁屏键),定时唤醒(闹钟),被动唤醒(电话)等,而语音唤醒就是——通过语音的方式将设备从休眠状态切换到工作状态。
工作状态的设备会一直处理自己收到的音频信息,把不是和自己说话的声音也当作有效信息处理,就会导致乱搭话的情况。而语音唤醒就成功的避开了这个问题,在只有用户叫名字的时候工作,其他时间休眠。
为解决此类问题,我们设计一个关键词的分类网络,如果检测到连续的关键词之后即为唤醒,如果检测到静音或者噪声认为没有唤醒。
2.搜集素材的思路
主要以第三方开源的关键词素材为主,作为主训练数据,以噪声数据,电视剧,有声书等数据作为反例数据。
这里选取了数据集:hello dongdong作为特定关键词。
3.项目实现流程
项目的数据流如下图所示,数字麦克风录取的实时语音数据,经过I2S接口保存到内部数组,先进行是否有声音的判断,如果检查到声音,进入到关键词识别,调用max78000中CNN加速器去加速CNN网络的计算。并将打印结果通过串口显示。
训练过程
在前面的章节已经完成对语音数据的收集工作,在确保语音数据质量达标的前提,就可以逐步进行添加自定义命令词数据,调整数据加载器,修改网络模型参数,训练 ,量化,评估和综合。
1 添加自定义命令词数据
首先需要为新添加的关键词标签创建一个文件夹(例如:'hello dongdong')在ai8x-training/data/KWS/raw/hellodongdong文件夹中复制所有”hello dongdong“语音样本到该文件夹中。
2 数据加载器调整
kws20项目的数据加载器脚本是ai8x-training/datasets/kws20.py文件。
添加新的标签(例如:'hello dongdong')到已排序的字典的正确位置(必须要按照标签搜字母进行排序),且具有唯一的增量值:
class_dict = {'down': 0, 'hellodongdong': 1, 'up': 2}
更新数据集中相关定义的“output”枚举。在“weight”中添加在kws20的基础上增加的词条标,最后一个类表示“未知”类别。
为了使优化器对每个类的样本数量保持平衡,可以将权重与每个标签的样本数量成反比。
具体的公式 weight[i] = (size of smallest label)/(size of label i)
{
'name': 'KWS_3', # 3 keywords
'input': (128, 128),
'output': ('up', 'down', 'hellodongdong',
'UNKNOWN'),
'weight': (1, 1, 2, 0.07),
'loader': KWS_3_get_datasets,
}
调整KWS_get_datasets函数适配3词的KWS词条列表,通过上面的“output”枚举去自动索引。
if num_classes in (3, 21):
classes = next((e for _, e in enumerate(datasets)
if len(e['output']) - 1 == num_classes))['output'][:-1]
print("classes", classes)
else:
raise ValueError(f'Unsupported num_classes {num_classes}')
创建KWS_3_get_datasets函数以返回新定义的分类类的数量。
def KWS_3_get_datasets(data, load_train=True, load_test=True):
return KWS_get_datasets(data, load_train, load_test, num_classes=3)
3 修改网络模型参数
更新ai8x-training/models/ai85net-kws20.py中初始化部分的num_classes变量默认值,根据自己的分类数进行调整。
# num_classes = n keywords + 1 unknown
def __init__(
self,
num_classes=4, # was 21
num_channels=128,
dimensions=(128, 1),
fc_inputs=7,
bias=False,
**kwargs
):
4 训练
项目分为数据预处理,模型训练,模型推理计算。
数据预处理部分,主要是清洗数据,把音频过短或者过长的数据剔除。处理之后通过一个开源的模型对数据进行标注,主要按帧标注出关键词,静音和其他,其他部分包括噪声和其他的人声。
模型训练,模型的训练使用官方提供平台ai8x-training作为训练平台,NVIDIA GPU(3070Ti) 作为硬件计算平台。
训练一开始会从所有标记样本中创建处理数据集,大概需要几个小时左右。如果不更新数据集的话,第二次会直接开始训练。
进入ai8x-training目录激活python训练环境后,执行训练命令进行训练
python train.py --epochs 100 --optimizer Adam --lr 0.001 --wd 0 --deterministic --compress policies/schedule_kws20.yaml --model ai85kws20net --dataset KWS_3 --confusion --device MAX78000 "$@"
在这个过程中如果出现问题,可以根据log信息进行定位或者再重新按照文档说明重新梳理下自己的操作流程,一般的问题都可以解决。
这里我遇到的大部分问题是增加词条时操作和修改数据加载器的过程。
补充训练中遇到问题
添加数据,遇到语料长度的问题,语料开始的长度是有2-3s,训练关键词没有效果,通过团队自己编写脚本和官方提供的:
convert_segment_wav.py
把所有语料都切到 1s 长度,训练出了效果。
训练结束后会在ai8x-training/logs/目录生成对应的训练结果相关文件,后面评估量化评估需要用到。
添加训练图过程图
以及训练结果图
量化
在ai8x-synthesis目录下,执行量化脚本如下
python ./quantize.py test-demo/kws3_dongdong_100epoch_1s/kws3_dongdong_100epoch_1s/qat_best.pth.tar test-demo/kws3_dongdong_100epoch_1s-q.pth.tar --device MAX78000 -v -c networks/kws20-hwc.yaml
在ai8x-synthesis目录下,执行生成执行的c代码的脚本如下。
python ai8xize.py --test-dir test-demo --prefix kws3_dongdong_100epoch_1s-c --checkpoint-file test-demo/kws3_dongdong_100epoch_1s-q.pth.tar --config-file networks/kws20-hwc.yaml --softmax --device MAX78000 --timer 0 --display-checkpoint --verbose
开发板部署测试
利用example/cnn/kws20_demo 工程用例,将在上一步量化阶段生成的cnn.h, cnn.h,weight.h替换kws20_demo中对应的源文件。
因为我们新添加了一个关键词hello dongdong,同时只保留:up 和 down,main.c 文件中需要做部分修改。
原始的代码:
/* Set of detected words */
const char keywords[NUM_OUTPUTS][10] = { "UP", "DOWN", "LEFT", "RIGHT", "STOP", "GO",
"YES", "NO", "ON", "OFF", "ONE", "TWO",
"THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT",
"NINE", "ZERO", "Unknown" };
修改为:
/* Set of detected words */
const char keywords[NUM_OUTPUTS][20] = { "UP", "DOWN", "hello dongdong","Unknown" };
本文是用的eclipse 工具进行代码编译的,运行。
编译。
运行。
利用serial studio 串口调试工具打印,初始化如下图。
识别为hello dongdong的关键词,如下图。
具体的项目演示效果见视频后半部分。