2019年我只对setk贡献了一些边边角角的东西,没有大的方面的改动。当初写的时候,也多是出于我自身实践上的一些需求,才将常见的操作以脚本的形式固定。但是随着技术的进步,工具链的完善和自身认识的加深,以前的某些需求,在我有了更加高效和便捷的实现方式之后,便没有继续在setk中进行集成。本篇文章主要记录一下过去一年的更新和随之产生的一些想法。
先结合这个工具集合主要包含的部分说一下2019年的更新。1)数据IO和可视化,2)数据预处理,3)数据仿真,4)特征提取,5)评估与后处理。
数据IO和可视化
数据IO当初为了兼容Kaldi(特征和recipe),规范和格式基本都跟随它的风格,同时在此之上拓展了一些numpy,matlab,audio的IO支持。Kaldi的IO部分从我另一个repo kaldi-python-io中修改而来,19年主要更新有二,其一是对vector和matrix两种对象IO进行了统一,不加区分,这样接口使用的时候便捷很多,其二是优化了对compress数据格式的读取,效率上要远超之前。audio的IO采用soundfile替代原来的scipy,主要原因是可以支持chunk-level的读写,在某些情况下可以节省IO时间。可视化上,改进了一下TF矩阵,音频频谱的可视化,控制了一下输出的dpi和默认的字体。
数据预处理
数据预处理部分包含的是一些信号层面的预处理算法,波束形成,声源定位,盲源分离,去混响这些,通常一个脚本对应一个具体的功能。这个方向是我后面想打算强化的,主要原因如下。一是我对前端这块,纯算法和理论一直比较感兴趣,传统的算法虽然效果不如现在的DL模型,但是计算量小,而且无监督,可以拿到即时的反馈,里面有很多概念和原理,即使在DL大行其道的今天,依然能够看到它们的影子。二是不论在产品还是研究上,往往需要一些信号的pipeline去做预处理,或者搭建基线等,显然是需要一个规范易用的工具去做这些事情,减轻这方面的复现工作。2019年增加了基于最大似然和srp-phat的定位脚本,优化了cacgmm/cgmm的盲源分离脚本,同时增加在基于mask的自适应波束形成中增加了一些新的特性和选项,固定波束目前只提供了延迟相加和超指向两种。后面的话,像一些自动增益控制(AGC),噪声抑制(NS),回声消除(AEC),包括GSC结构的波束形成框架,其他的一些定位算法等等,我也是希望可以添加到这一模块中去。
数据仿真
数据仿真指的是包括rir生成在内的,加噪加混响,产生近/远场训练数据相关的过程。目前我自己形成的一个比较稳定的规范(流程)是将这个过程划分成三步进行。第一步,产生rir和噪声数据,准备原始干净数据。rir仿真的脚本之前已经提供,19年增加了使用gpuRIR的选项,这样可以极大的提升rir仿真的效率。噪声数据一般使用开源的或者自己实录的。第二步,使用脚本产生针对单/多通道,近/远场的数据配置文件,格式统一定义,可以支持增强,分离,识别等多个任务。这么做的目的有多个考虑,一是不用产生实际的数据,避免了磁盘空间的浪费,尤其在数据量很大的情况下,二是可以在训练阶段配合对应的数据加载器,进行在线的数据合成,三是同时固定了训练集的数据,不同模型的对比结果更加客观,四是从数据的角度来看,便于调试和重新配置,五是对于一些完全没有经验进行数据仿真的人来说,按照这个pipeline,只需要配置一些原始数据,噪声,rir,就可以进行数据仿真了。第三步是可选的,如果需要的话,可以用另一个脚本从配置文件生成数据本身,用于数据检查,以及一些不用在线数据加载器的情况下,进行模型训练。目前二三步对应的脚本还没有merge到分支中去,我本人是比较推荐这种逻辑的,也是我实践了很久之后认可的方式。
特征提取
特征提取这块是变动比较小的一个地方,因为最近一两年来,我基本已经很少用离线特征提取这一方式准备特征了。之前的话,为了兼容Kaldi,在这方面留有一些脚本,包括产生方向特征(只支持线阵和PCA的导向向量估计),IPD特征,谱/mel-filter bank特征,mask的标签等等。如今全部换成在线的特征提取流程,自己也有Pytorch对应常用的声学特征实现,包括单/多通道,增强/识别任务在内的。这样做的好处依旧是可以简化实验的pipeline,基本配置一下数据就可以进行训练,而且免去对磁盘空间的占用。所以后期可能会继续补充,优化一些脚本,不做大用,只是为了拿到一些即时的结果,分析一下数据等等。
评估与后处理
最后一个部分,评估和后处理。评估这块基本也没有什么新增的内容,由于我一般很少算增强的那些pesq,stoi等指标,目前主要是WER,SNR这些,用于快速评估实验结果。后处理主要包括TF-masking和TF-mapping两种方法的时域音频合成,由于目前多是端到端的结构,前者偶尔会用到,而后者就基本很少用到了。
简单小结一下。在此之前,主要是2018年,setk的编码主要是围绕我个人的需求在里面,因为当时手上的任务很杂,前端后端都要考虑,所以想做的是针对speech这些任务本身,打通前后端的处理流程,后端则主要要对接Kaldi。从去年年初开始,因为在实践中慢慢的产生了一些自己的思考,加上对前端一些算法理解的加深,增加了一部分新的功能,实现上更加高效和全面一些,如上所述。对于2020年,我觉得又迎来了一个新的阶段,已经毕业的我,计划加一些我自己感兴趣的模块和算法进去(主要是前端这块的pipeline),看后面的工作强度和时间安排吧,考虑有时候自己比较佛系,进度上可能会比较慢……。
此外,毕业前还不紧不慢的搭了一个E2E-ASR的repo,围绕着个人的习惯和想法,设计了一些易用的特性,做了点单多通道的实验。 这其实是我早在研一就想做的事(但是当年各方面可能达不到要求),结果被拖到了毕业前才差不多开始。晚做的也有一些好处,就是对任务的理解比较深刻的话,从头实现起来效率挺高,自己在遇到问题之后,也可以明显发现定位,解决的速度快了不少。很多时候这样一种感觉,就是看到一种做法,或者产生一种想法,按照自己的思路实现之后,拿到了一些效果,或者产生一些预期的现象,内心才会产生认可,是比较美妙的一件事情,反之,则会一直留有顾虑,想起来有些心虚。虽然可能有些质疑说,费了很大劲写那么多code,跑出来的结果可能不如其他开源的code,白白浪费不少时间。在我看来,我在意的是这个从无到有的过程,而不是要比结果(硬要调的话,未必会差很多),否则因噎废食,会错过很多美味的。