前言
伴随着实习结束期限的到达,我回顾了一下在平安科技AI音乐技术组(以下简称AI-Music)这四个月的实习,总结一些得与失。
我是今年七月被同学推荐到AI-Music组的,简历直接递交到了J老师,面试的时候也是J老师,也是我后来的mentor,工作主要是和他汇报。J老师面试时粗略地问了我在学校做的和音乐信息检索方向有关的科研,我会的编程语言。我那个时候刚刚结束一个用CNN做音乐表征学习的论文,后来这篇文章投到2020ICASSP去了,来复旦这边后我一直主要用Python和Keras,这些是我的基本情况。J老师在面试的时候也介绍了一下他们AI-Music组的两个方向:Music generation和一个Score following项目,前者是基于MIDI的,后者是基于音频的。并且询问了我想做哪个项目,由于我在学校做的都是基于音频的,我想这个熟悉一点就选择做Score following。
Score following其实就是音符跟踪,理想的Score following算法是:在用户弹奏乐器时(比如钢琴),算法将APP检测到音频中的音符检测出来,然后在和APP前端展示的乐谱上给出反馈:用户弹奏了C4,算法检测到弹的C4音准对了则应该在乐谱上过渡到下一个音符。这种算法主要用在主打音乐教育的APP上,这一类APP想用算法来评价用户在乐器学习时的演奏乐器情况,从音准,节奏等方面给出评价,用户借助算法评价能够改进自己的乐器练习。乐器学习伴随着大量的重复练习,且需要在正确的专业老师指导下。这类APP的终极就是通过算法来取代专业的老师,让用户能够借助这一类App自己练习乐器。
我是7.29日正式开始实习,实习主要跨越7.29-8.31,10.20-11.18,11-18-1.18日总计四个月。接下来我将按照这三个阶段来总结。
7.29-8.31日
我入职的时候,D同学已经开始这个Score following的项目了,他已经提前做了一些算法调研,然后已经选定了要实现的这个算法。是一篇2016年台湾清华大学的icassp论文,但是我们主要参考作者的博士论文,里面的算法细节描述得更加详细。来之前我也做了一些Score following算法的调研。说实话,这是一个我从来没有接触过的项目,仓促之间,我们决定开始复现这篇论文里面的算法。我们一开始选用python来实现这个算法,原因有几个:
- 是这篇论文使用了一个预训练的CNN来判断乐符的Onset
- Python能够最快复现这个算法, 它有丰富的第三方库,Madmom(一个针对MIR任务的音频处理库,我们需要用到Onset判断),Librosa(提供基本的信号处理的音频处理库,我们需要CQT转换), MIDO(处理midi的库,我们要用midi来提供真实的Onset时间,pitch信息)。
复现算法的本身并没有给我和D同学带来什么挑战,算法的细节我放在这里,感兴趣可以连接。只是在复现的过程随着我对这个算法的理解程度加深,我逐渐的发现这个算法的前提条件太多了,它可能无法解决我们想要解决的问题。简单的说,它假设弹奏者①弹奏的速度是基本稳定的,不会忽快忽慢;②弹奏是连续的,演奏的绝大部分的音符之间的位置关系和乐谱里面的音符位置关系是一致的。实际上这两种情况是很理想的,针对①实际情况弹奏者的速度是会忽快忽慢的,熟悉的就弹奏的快,不熟悉的就会弹奏的很慢;针对②,漏弹一个音且将后一个的音符都提前弹奏到当前的时间点,这就会使得演奏的音符之间的位置关系和乐谱里面的位置关系就不一致。这两个会导致这个算法根本不可能满足要求。这在算法调研的时候就应该要能够想到
从我现在写这篇总结的时间点来看,其实这个算法无非就是将音频里面的音符和乐谱里面的音符都表示出来,然后用皮尔逊相关系数构造一个相似性矩阵,然后用DTW算法在这个矩阵里找一条匹配路径。offline算法基本上都是这个模式,基于全局的信息(一般是什么矩阵,如距离矩阵,相似性矩阵),在矩阵里找到一条匹配路径,可是我们的需求是一个online的score following算法,没有全局信息,只有当前音符信息和之前的音符信息,这个算法主要是基于一个局部速度估计构造一个线性函数,使用这个线性函数来匹配当前音频的音符对应乐谱中的拿一个音符。到8月底,我们这个复现的算法只能跟踪正确的弹奏,如果弹奏错误,漏弹中间的某一个音符,停顿都会使算法crash. 问题来了,如果都是正确的弹奏,那完全不需要评价了。这一个月的投入,得到这么一个用Python写的只能在IDE里面跑的玩具demo,没有实际应用价值,我其实是不能够接受的。导致这样的局面,我反思了以下几个原因:
- 首先我没有将需求转换为一个很好的算法问题,即我们到底是要做一个offline的算法,还是一个online的算法。导致我花了一些时间在offline模型上(比如谷歌的onset&frames模型,虽然这是最近两年一个很好的转录模型)。2-8定律告诉我们,一个优秀的算法工程师,应该花费80%的时间在①问题定义阶段将一个需求转化为一个定义清楚的算法问题,有时可能还要分解成好几个小问题。②算法调研阶段,一定要全面的调研当前已有的算法,认真的评估每一个算法是否能够解决当前的问题,一旦盲目地开始复现,投入的时间不仅很多,而且还不一定能得到理想的算法。现在很多论文的算法仅仅在论文里面是有用的,和现实情况是严重脱节的。只将20%的时间花费在实现上。
- 不要迷恋fancy的模型,一定要考虑实际的需求。比如说Onset&frames模型,哇,deep learning!很fancy!很cool!放出来的demo效果也非常不错!但是它能够部署到手机端吗?不能!它首先是一个offline模型,其次这个模型太庞大了,部署在移动端不现实。在这一个月中,我们没有考虑过这个算法能否应用到IOS,Android端,它依赖多个python的三方库。python实现的算法,该如何和前段后端对接呢?这一方面欠考虑,毕竟我不是有很多经验的工程师,不太了解这些问题,这些该由公司更加资深的工程师来评估。
10.21-11.18
9月1号-10.20日我就返回学校继续完善那篇投2020ICASSP的论文。到10.21日重新回到AI-Music组。这段时间我不知道怎么了,脑子一热开始研究Music generation了,这是组里最重要的项目。在回来之前我也看了一点音乐生成的论文,看了一下Google Magenta的几篇博客以及Music transformer那篇论文。Music transformer的衍生自transformer,后者是最开始用在机器翻译任务上,然后又被用于文本生成任务上。
我先花了一周时间阅读了transformer和music transformer两篇文章。并且根据tensorflow的transformer教程实现了一遍transformer的基本代码。music transformer的笔记我放在这里。music transformer是一个端到端的音乐生成模型,在我开始实现和训练music transformer之前,我吸取了8月的教训,花了两天的时间测试google magenta提供的music transformer demo。我想先测试一下这个模型的生成能力是怎样的,于是我找了不同primer分别输入这个demo,最终得到了一份测试报告。
我没有花费太多时间在将MIDI预处理成sequence模型需要的输入向量,我也没有花费太多时间在music transformer模型的实现上(我觉得我也复现不出来,模型对于我来说有点复杂)。我在github上找到了一个repo满足我的要求,修改了一个bug,为repo提了一个issure。MIDI文件我则是使用和Maestro数据集一样的midi文件,repo里提供下载脚本。我训练了几次,保存了几个预训练模型,并且使用巴赫的一些钢琴奏鸣曲尝试进行生成,然而我并没有得到和原repo的开源者一样的生成效果,但是模型最终的收敛loss和原repo给出的结果是差不多的。后续我没有花费更多时间去寻找原因,因为此时我已经对音乐生成的这项工作的意义产生了怀疑,我也对这种端到端的生成模型感到失望。
我个人认为音乐生成在现在仍然是一个challenge,对于平安科技AI-music去做这方面的研究一直是持怀疑态度的:①音乐本身就是一个非常complex,emotional的载体,对于目前的生成模型来说,显然音乐是一种它hold不住的研究对象。②平安科技如果真的想做,应该招募更多非常聪明的人才才可能。就我个人而言,我在这将近一个月的投入是0产出的,很大的原因来源于我自身,不应该去碰这种题的。这提醒着我,应该更加谨慎的选择项目,能够对项目的难度进行事前评估。
11.18-1.18
最后二个月,我的收获是颇多的。Z同学的加入,在这之前,我们在复旦已经合作过半年多的时间。J老师交给我们一个小提琴陪练App算法开发任务。我们需要根据业务需求,实现一个完整的算法SDK,并且考虑到跨平台(IOS,Android),需要使用C++开发。此时我并没有使用过C++开发,C语言基础也早就荒废了,但是我还是乐意接受这个任务,和Z同学一起合作完成整个SDK开发,开发语言不是问题,我大约花了10-12天的时间翻了C++ primer 第五版300页左右,熟悉了C++的基本语法以及一些OOP特性,即上手开发。
J老师给了我们一些可以参考的代码,其中就有Yin算法这个音高检测算法,除此以外没有封装成接口,我们的任务基本上要从0️⃣开始需要开发完整的算法,并且还要针对小提琴的特性,可能要对单音双音进行音高检测。一开始我们其实并不了解所要开发算法系统的所有功能,我们从最基本的功能开始实现,一步一步迭代加入新的功能,也就是敏捷开发。但是我们大致知道,我们首先应该解决两个问题:①单音音高检测,也就是要能根据音频检测出用户演奏的音符;②相应的评价算法:检测用户的演奏音符是否准确,演奏节奏是否正确。
单音音高检测:Yin算法能够解决,并且计算速度非常快,这是一个经典的音高检测算法。————。我们主要需要实现一个缓存区来存储前端监听到的音频流,为了增加Yin算法鲁棒性,我们需要在缓冲区上设置一个重叠的窗口,每次只用Yin算法计算这个窗口里的音频的频率,这个频率当做窗口起点时间戳的频率值。这个频率值可能需要保存在一个数据结构里,以便评价算法评价整个音符的音高准确与否时需要用到;也可能需要马上返回给前端(调音模式即需要,判断当前弦是否调音准确),这里我实践了C++函数指针,学习函数指针时觉得这个很抽象,显然前端后端联系就是函数指针应用场景。
音高,节奏评价算法:音高评价就是评价每一个音符是否演奏准确,比如说如果中央C4被演奏成了C 4#的话就是错误的;节奏评价就是一个音符的被演奏的时间是不是和乐谱当中的时间一致。如果要设计音高评价,应该将每一个音符的音高检测值和参考的音高值进行对比。设计节奏评价,应该检测出音符的Onset时间,去比较这个Onset和真实的乐谱时间是否一致。所以在音高检测时,音高检测值被保存下来就尤为的重要,有音高检测值,可以轻而易举的找到Onset的时间。
很快我们就实现了一个完整的单音音高检测,音高评价,节奏评价算法。除此以外,我们将这几个算法都分装成简洁的接口,用C语言编写的API可以供OC,Swift项目调用。
双音音高检测算法:由于小提琴不仅仅能够演奏出单音,还能有双音演奏。这意味着Yin算法不能再用作音高检测算法。兼顾实时和准确的算法,需要将音频信号转换到频域。常数Q变换能够将音频从时域转换到基于十二平均律的CQT谱,我和Z同学对CQT的了解只限于librosa里面的CQT变换,于是我们使用Garage band制作了几个双音音频,借助librosa里面的cqt变换(一个八度设置12个bin, 一个bin代表一个半音)以及librosa.display里面的可视化函数。我们发现一个双音在频谱上的显示会有六个明显的半音有能量(幅度),比如说如果演奏了A4(midi pitch为42)和C4(midi pitch为49),CQT谱上一般会有两条比较亮,分别是(41,42,43)和(48,49,50)。如果我们能从CQT谱上检测到最亮的两个半音和前端给的真实pitch能对应上,就可以判断音高的是否准确。一个小插曲是,我们误将garage band上的C4 当成了我们理解的中央C4,实际上garage band的C4是比中央C4高一个八度C5。库乐队之所以使用这种向下偏移一个八度的是为了尽可能兼容多的乐器的音域。确定这种方法可行之后,我们需要使用C++实现一个CQT变换算法即可。还需要考虑的一个问题是,由于我们有真实的音高可以利用,所以我们在CQT谱上只关注存在真实音高的八度范围,那么如何在这个八度范围(若干个音高排列,可以是12个或者14个)中尽可能合理的地挑选出可能的两个目标音高值,一旦挑错了,那么会直接影响这个音符。
双音音高,节奏检测算法:双音音高评价算法基本上可以沿用单音的逻辑,可能更加简便,检测出来的音高是半音为单位,如果和真实的音高对不上就可以认为是错误的。
除此以外,我们努力确保我们提供的接口不包含C++特性,只包含C的特性,因为swift不能和包含C++特性的头文件一起编译。我和Z同学一起设计了算法的系统架构,调用逻辑,检测,评价算法;音频缓冲区,音高检测模式(实时的反馈用户每一个演奏音符的得分),跟音模式(用户弹奏正确一个音符才会过掉),调音模式(用于调音)调用逻辑的设计则由Z同学担纲。到我离开的前几天,我们一共提交了15000行代码,包括我们自己写的1600行最终在项目中使用的代码,Yin算法和CQT算法(来自vamp插件,QMUL开发的)10000行左右,其余的就是我们的测试代码和开发时所产生的过程代码。整个项目的代码难点在于①音高检测,评价算法,节奏检测,评价算法。②音频存储缓冲区设计,音高检测模式,跟音模式逻辑设计。③CQT算法实现(这个项目中使用开源代码)
说一些其它事:在AI-Music组的实习的四个月,我有三个月花在score following上,一个月花在music generation上,这个项目在我走之前基本上完成了我们预先设定的目标,并且我们也下载了demo在自己手机上玩玩;在这里我有时间调研算法,设计算法,编程,协同编程,交叉code review,组里的氛围也很好(自由宽松,基本上没有kpi压力),同时这里有很多擅长不同领域(作曲,NLP,统计系等)同学;我很高兴和Z同学一起合作完成这个项目,她很聪明,经常讨论的时候我跟不上她的思路,我经常自嘲:“脑子像生锈了一样”,同时她良好的音乐背景是我们能够顺利推进这个项目的一个重要条件,MIR是一个计算机和音乐的交叉学科。最后,我叮嘱Z同学,一定要让我们的这个App上线啊!!
我在这里学到了非常多在学校永远学不到知识!至此,我的第一份实习就画上了句号。