What does a program have to get past before it can tell classical, jazz and rock apart?
Give a program a photo of a cat and it receives a neat grid of colours. Give it the sound of a passing car and it receives a long list of numbers arriving one after another over time. Both jobs are called “classification”, but sound needs one extra step: someone first has to turn that list of numbers into evidence that can be compared, or the program has nothing to compare. That step is called audio signal processing, and it is what these 23 lessons are about. This lesson covers no specific algorithm. It settles three things: what task we want the program to do, the order the 23 lessons follow, and what background you need.
导读: 给程序一张猫的照片,它拿到的是一块排得整整齐齐的颜色网格;给程序一段汽车驶过的声音,它拿到的是一长串按时间冒出来的数字。两件事都叫「分类」,但声音这一边多了一道工序——得先有人把那串数字整理成可以互相比较的证据,程序才有东西可比。这道工序就叫音频信号处理,也就是这 23 课要做的事。这一课不讲任何一种具体算法,只把三件事定下来:我们要让程序完成什么任务、这 23 课按什么顺序走、你需要什么基础。
给程序一张猫的照片,和给它一段汽车的声音
先看照片这一边。给程序看很多张已经标好「猫」的照片,再给它一张没见过的,让它判断里面是不是猫。这类程序下面统称分类程序:它从带答案的例子里找出差别,再把新东西归到某一类。
照片好办在哪里?一张照片本身就是一个整整齐齐的数字网格。 宽多少个点、高多少个点、每个点什么颜色,都是现成的数。两张照片只要缩放到同样的宽高,就能一个位置对一个位置地比。
再看声音这一边。一段汽车驶过的声音,麦克风是这样记的:每隔极短的一瞬量一次空气被推挤的强弱,记下一个数,一秒钟量上万次。所以程序拿到的不是网格,是一长串有先后顺序的数。
麻烦就在这串数上:
- 两段录音长度不同,一段 3 秒一段 30 秒,数的个数就差十倍,没法一个位置对一个位置地比。
- 两段录音可能每秒量的次数不同,同样是 1 秒,一个 44100 个数,一个 22050 个数。
- 一串数里,哪些反映的是这段声音本身,哪些只是录的时候音量开大了、麦克风离得近,光看数字看不出来。
- 而且单独一个数几乎什么也说明不了——「第 8137 个数是 0.03」,这句话没有任何用。
图 1:照片一进来就是排好的网格;录音一进来是一长串随时间排列的数。中间那一步「把录音整理成可比较的证据」,就是这 23 课的全部内容。
把这串数读懂、切开、算成可以互相比较的证据——这件事叫音频信号处理。它不是机器学习的一部分,它是机器学习开始之前的那一步。
把声音整理好之后,程序能做哪些事
这一步做好了,能往上接的任务比想象中多:
- 判断这是什么声音:警报、车辆、玻璃破碎、机器出故障。
- 听懂人说了什么:把语音转成文字;也可以反过来,核对说话的是不是本人。
- 把录音修干净:削掉不想要的杂声,或者把音质偏低的录音补得清楚一些。
- 分析音乐:找出用了什么乐器、节拍在哪里、整首曲子是什么情绪、结构怎么分段。
这些任务表面上差得很远,起点却是同一个:先从录音里找出和任务有关的证据。 证据没找对,后面换个多大的分类程序都补不回来。
这门课挑「古典、爵士、摇滚三选一」当贯穿始终的任务,有两个原因:结果一看就懂,不需要专业知识判断对错;而且后面每学会一种计算,都能立刻回到同一个问题上检验——这个数字真的帮得上区分这三种音乐吗?
这 23 课怎样从录音走到可用特征
完整的 23 讲路线图只放在课程总纲里,这里不再复制一遍。总纲回答“整套课按什么顺序学”,第 01 讲只回答“为什么要先处理录音”。
现在只需要记住两层结构:
- 第 01—06 讲是共同基础。 先认识声音和数字录音,再学会把整段录音切成可重复计算的短片段。
- 第 07—23 讲提供几类不同证据。 有的方法沿时间观察强弱变化,有的方法拆出不同高低的成分;拆出的结果还可以按听觉重新整理,或者压成少量统计数字。实际任务按需选择或组合,并不是把一段录音连续加工 23 次。
课程的边界也在这里说清楚:这 23 课到「为分类程序准备好可信的输入」为止。 至于怎么训练那个分类程序(业内把这样一个从数据里学出判断规则的程序叫模型)、怎么调它的结构、怎么把它上线,都不在范围内。把边界划出来,是为了让每一课都服务于同一个能验收的结果。
每个概念都讲两遍:一遍讲原理,一遍写代码
这门课不把「原理」和「编程」拆成两条互不相干的线。每遇到一个新概念,都会走同一组动作:
- 先说清它要解决什么问题;
- 用图看清输入是什么、输出是什么、中间变了什么;
- 用一小段代码算出真实结果,不是示意;
- 改一个参数或换一段输入,看结果为什么跟着变;
- 把能复用的完整版本留在课程代码里。
正文里只保留看懂实验必需的那几行代码。每一课对应的完整脚本放在课程代码目录,可以直接运行;脚本比正文更全,还带上参数扫描、画图和边界情况的检查。
这门课用哪些工具
全程用 Python。数组计算交给 NumPy,音频读取和一批音频专用函数交给 librosa(它读写文件时实际调用的是 SoundFile),画图交给 Matplotlib,个别信号处理函数来自 SciPy。
这门课不需要 API Key,也不需要联网,所有计算都在你自己的机器上跑完。装什么、怎么装、用的什么版本,在课程总纲里写了一次,后面每一课都不再重复。
学完之后你应该会做什么
23 课结束时,目标不是背下一串函数名,而是能够:
- 说清声音怎样被记录成数字,以及「每秒记录多少次」这类参数分别改变了什么;
- 看懂随时间起伏的那条线、声音成分的分布图,以及同时表示时间和成分的二维图,各自说明了什么;
- 自己写出课程里的核心计算,并且能检查单位对不对、坐标轴方向对不对、数组形状对不对;
- 按任务挑特征,并说清这个特征保留了什么、丢掉了什么;
- 把一堆音乐片段整理成一张能直接交给分类程序的表。
看到公式不用紧张
后面会出现一些公式,尤其是讲到把声音拆成不同成分那几课。每一个公式都会先用一句话说清它在干什么,再逐项解释每个符号代表什么,最后用代码算一遍给你看结果。
看不懂公式不影响往下走。你可以先跑脚本、看输出、看图,等有了直觉再回头看公式——很多时候公式只是把你已经看懂的那件事写紧凑了而已。
这门课写给谁
这套课程面向这几类人:做机器学习的工程师、计算机专业的学生、软件工程师、音乐科技从业者,以及对技术有兴趣的音乐人。
需要的基础只有一条:会写一点 Python。 能看懂变量、函数调用、循环和列表就够了。
不需要任何音频、声学或者信号处理的基础。本课程不会预设你已经知道录音软件里那条线为什么上下抖,也不会预设你知道“一秒振动多少次”怎样决定听起来偏高还是偏低;这两件事从第 02 讲开始解释。如果 Python 也还不熟,可以先直接运行完整脚本,对照输出再逐行读正文。
实验:先确认三段素材程序都能读到
这一课的实验不分析任何曲线,也不计算声音由哪些成分组成。它只完成项目真正的第一步:把任务、文件和标签定下来。
三段主素材和它们的标签:
COURSE_AUDIO = [
("debussy.wav", "古典"),
("duke.wav", "爵士"),
("redhot.wav", "摇滚"),
]
我们约定:将来交给分类程序的是一段 1 秒的音乐,程序输出「古典、爵士、摇滚」中的一个标签。这里每段素材已经标好的类别,就是判断程序答得对不对时用的标准答案。
装好总纲里的环境之后,运行:
python lessons/lesson01_course_map.py
脚本只读文件本身的信息,不改每秒记录多少次,也不合并声道。声道指的是一个音频文件里同时存了几路声音:单声道存一路,立体声存左右两路。
def inspect_course_audio():
rows = []
folder = audio_dir()
for filename, label in COURSE_AUDIO:
info = sf.info(os.path.join(folder, filename))
rows.append({
"file": filename,
"label": label,
"duration_s": info.duration,
"sample_rate_hz": info.samplerate,
"channels": info.channels,
})
return rows
实测输出:
[正文] 三段主素材
debussy.wav | 古典
30.00 秒 | 22050 Hz | 1 声道
duke.wav | 爵士
30.00 秒 | 22050 Hz | 1 声道
redhot.wav | 摇滚
30.00 秒 | 22050 Hz | 1 声道
三个文件都读到了,而且三段都是 30 秒、每秒 22050 个数、单声道。所以呢? 所以开头那一类最烦人的麻烦被排除了:不会有某个文件打不开,也不会出现「一段每秒 44100 个数、另一段 22050 个数」这种连基本格式都对不齐的情况。三段素材有了共同的起点。
但要说清楚,这不代表三段音乐已经可以直接比了,更不代表「每秒记录次数相同」就能区分风格。它只是把格式这一层的问题排除掉。每秒记录多少次、几个声道分别意味着什么,第 04 课会正式讲。
脚本还会写出一张三行的小表 dataset_manifest.csv,把文件名、标签和原始信息固定下来。后面每一课都读这张表,就不会出现正文写一套名字、代码用另一套名字的情况。
除这三段主素材外,完整脚本还会列出七段对照声音:噪声、钢琴、萨克斯、音阶、颤音、小提琴和人声。它们后面讲音高、音色、颤音和噪声时会用到,但不会混进三分类的标签表。
这一课只定问题,不抢后面的答案
到这里,这门课要完成什么、按什么顺序走、需要什么基础,三件事都定下来了,共同的素材也确认过了。
这一课没有解释那条线为什么上下摆动,没有讲「频率」怎么算,也没有比较任何两种表示声音的方式。把这些提前塞进导论,同一个概念会在后面重复讲第二遍,第一次接触声音处理的人反而会失去顺序。
下一课从最开头讲起:声音是怎么产生的、怎么传到麦克风的,以及录音软件里那条抖动的线到底记下了什么。