片段语音唤醒
实现方式
- 输入长度为T帧的语音片段,输出1个类别向量(每个类是一个唤醒词),超过阈值则唤醒;✔
- 如果只有一个命令词(唤醒任务),可以只输出一个概率值;
- 适用模型:感受野较大的模型(CNN)(因为唤醒词持续数帧)、RNN
- 输入长度为T帧的语音片段,输出T帧类别向量(每个类是一个唤醒词);计算唤醒词类别的平滑后验概率等后处理方式,超过阈值则唤醒;
- 适用模型:与上下文有关的RNN
- 输入长度为T帧的语音片段,输出T帧类别向量(每个类是是唤醒词的子词、音节、音素),后处理唤醒词组成单元的概率,超过阈值则唤醒;✔
- 适用模型:无限制(一帧语音特征可表示一个组成单元)
- 输入长度为T帧的语音片段,输出T帧类别向量(每个类是是唤醒词的子词、音节、音素、以及非唤醒词),WFST解码,搜索最优路径,最优路径是否出现唤醒词则唤醒;✔
- 适用模型:HMM模型
- 输入长度为T帧的语音片段,输出N个类别向量(每个类是是唤醒词或子词、字符),搜索最优路径,最优路径是否出现唤醒词则唤醒;
- 适用模型:transformer模型、RNN-T模型、CTC模型
[✔表示已做过实验]
基于CNN的片段语音唤醒
模型
- CNN结构、depthwise卷积、pointwise卷积、dialted卷积;
- 感受野1.84s,无padding(输入N帧,输出N-184帧);
- 残差结构,不同感受野的特征层element-wise add;
模型图
模型对比
- 对比论文“Small-Footprint Keyword Spotting with Multi-Scale Temporal Convolution”的模型结构,上述模型结果更好;
训练、推理特征输入方式
offline cmvn
1.1 语音特征文件做好offline cmvn后
1.1.1 训练时封装batch时pad_sequence补零,batch内最大长度不足1.84s的也补零至1.84s,训练时不再做cmvn;
1.1.2 训练时封装batch时pad_sequence复制第一帧,batch内最大长度不足1.84s的也复制第一帧至1.84s,训练时不再做cmvn;✔
(前期做了cmvn,训练中不用做cmvn,训练速度快)
1.2 语音特征文件没做cmvn
1.2.1 训练时封装batch时pad_sequence补零,batch内最大长度不足1.84s的也补零至1.84s
1.2.1.1 训练时根据实际长度做offline cmvn;(与1.1.1特征相同)
1.2.1.2 训练时根据pad_sequence后长度做offline cmvn;
1.2.2 训练时封装batch时pad_sequence复制第一帧,batch内最大长度不足1.84s的也复制第一帧至1.84s,训练时再做offline cmvn;
1.2.2.1 训练时根据实际长度做offline cmvn;(与1.1.2特征相同)
1.2.2.2 训练时根据pad_sequence后长度做offline cmvn;
sliding cmvn,将上述offline cmvn换成sliding cmvn
global cmvn,将上述offline cmvn换成global cmvn
- 语音特征文件没做cmvn,利用训练集统计出的全局均值方差,训练时封装batch时pad_sequence补零,再做cmvn后,pad_sequence部分值不为0,作用于网络中,但它们没有物理含义,espnet和wenet框架都是这样做的,会有问题吗?
训练目标函数
max_pooling loss 只取最大概率帧
target = filler时:$loss=\min\limits_T(1-P_{keyword})$ (min pooling)
target = keyword时:$loss=\max\limits_TP_{keyword}$ (max pooling)
取正样本某帧的最大正类概率值,让这帧概率越大越好,取负样本某帧的最小负类概率值,让这帧的概率越大越好;
more_label loss 逐输出分类(每个输出都有标签)(普通的逐帧分类)
- 扩充正负样本标签数量,取正样本输出时间轴所有帧,让所有时间帧的正类概率越大越好,取负样本输出时间轴所有帧,让所有时间帧的负类概率越小越好;
- 由于输入输出不等长,标签序列未知,要求训练数据片段内尽可能是一个分类(比如输入2s,输出16帧,16帧都要是正样本,因此2s尽可能是唤醒词,并且前后静音短)
mean_pooling loss 平均概率
- 取输出实际片段内平均概率,求bceloss
实验
最好结果:more_label loss训练得到初始模型,再用max_pooling loss训练;
输出概率层
- 单唤醒词,一个分类,用sigmoid
数据
训练集
- train_p522h_n4000h:筛选出满足唤醒词对齐区间在2.5s内正样本数据,负样本segment为2s语音。正样本101万条,负样本717万条。
训练数据数据增广
- 正样本:原始“小源小源”音频14万条,加噪、速度扰动、扩充为102万条(667小时),筛选出满足唤醒词对齐区间在2.5s内正样本数据,得到101万条,522小时;
- 负样本:原始4000小时负样本、300小时mobvoihotwords,加噪900小时(5类噪声、NoiseX_92_carcafenoise、两两混合制造重叠说话人的效果),切割为2s音频,舍弃小于2s的,的搭配717万条,4000小时;
验证集
- 训练的几个初始模型经过测试集,从测试集中选取far高、recall低作为cv;
- 正样本:0.6-6.5s,4k条,3h;
- 负样本:1.1-3.1s,15k条,11h;
推理过程
- 窗长200帧,窗移30帧的滑动窗口语音特征送入网路进行前向计算,输出16帧内,若同时满足最大概率值高于阈值1和平均概率高于阈值2,则唤醒;
- 送入整句语音,实际长度小于1.85s,复制第一帧直到总长度为1.85s,计算概率(输出一帧);实际长度在1.85s-2s之间,按实际长度计算概率,不用滑动;实际长度大于2s,进行滑动
- FLOPS = 2M(未复用)、模型参数33k
实验结果
- tdnnf[2]:chain_tdnnf8_432_48_l27r27_id_merge_all_sub4_12000_basedTDNN_smaller_phone_lm_more_epoch/graph_add_two2;
- tdnn[3]:chain_tdnn5_256_l39r27_id_merge_all_sub4_12000_basedTDNN_smaller_phone_lm_multitask/graph;
- tdnn10:keyword_lib/data/asr_config_xiaoyuan/tdnn_10/xyxyxy_2gram;
- n10:exp_1/mdtc_offline_cmvn_p382h_n4000h_max_pooling/6.pt;
C++ api
模型pt格式转为onnx格式,用onnxruntime的动态链接库进行调用;
每次读入包大小(0.32s/0.3s)的语音片段,
- 到达最后一个包并且累计语音片段长度小于1.85s,复制第一帧直到总长度为1.85s,计算概率(输出一帧);
- 到达最后一个包并且累计语音片段长度在1.85s-2s之间,按实际长度计算概率,不用滑动;
- 未到达最后一个包并且累计语音片段长度大于2s,计算概率,若唤醒,则向后滑动1s,1s内不检测,若未唤醒,则向滑动0.3s,继续判断;
RTF在原来1/3~1/2;