Qwen3-ASR 课程 · 第 3 课

语音前端 —— 采样、波形与梅尔频谱

⏱ 约 20 分钟(一半在玩声音)· 前置:第 2 课(注意力) · 本课目标:看懂声音变成"模型食物"的全过程,亲手看到并听到频谱

前两课你知道了管道(声音 → 特征 → 编码器 → LLM)和它的心脏(注意力)。今天补上管道的第一段:声音在进入 AuT 编码器之前,到底被做成了什么样子。这节课一半时间给你玩——下面的声音实验室能合成声音、画波形和语谱图,还能播放出来。

一、采样:声波变成一串数字

麦克风每时每刻都在感受气压变化,这是连续信号。计算机存不下"连续",只能每隔固定时间记一次声压值——这就是采样。每秒记 16000 次,就叫 16000Hz 采样,得到一串数字,即波形。

每隔固定时间记一个点(紫点) 连续声波(灰线)无法存进计算机 → 数字序列
采样:连续声波(灰)→ 按固定频率记录的数字序列(紫)。Qwen3-ASR 的规定食谱是 16kHz、单声道。
为什么偏偏是 16kHz? 采样定理(奈奎斯特):采样率的一半,是能忠实记录的最高频率。语音的信息几乎都挤在 8kHz 以下(人说话的能量主要在 300–3400Hz),所以 16000Hz 正好把语音全收进来,一个不浪费。这也是为什么你给模型喂音频前,重采样到 16k 是数据准备的第一步。

奈奎斯特 violated 会怎样?下面让 3kHz 的正弦分别被 16kHz 和 4kHz 采样——看第二张图里的采样点连起来像什么:

import warnings warnings.filterwarnings("ignore") import numpy as np import matplotlib.pyplot as plt f = 3000; T = 0.002 # 3kHz 正弦,观察 2ms(6 个周期) t = np.linspace(0, T, 4000) # "连续"参考线(密采样) fig, axes = plt.subplots(2, 1, figsize=(8, 5), sharex=True) for ax, fs in zip(axes, [16000, 4000]): # 试试改成 5000? n = np.arange(0, T, 1/fs) ax.plot(t*1000, np.cos(2*np.pi*f*t), lw=1, alpha=0.6, label="3kHz sine (continuous)") ax.plot(n*1000, np.cos(2*np.pi*f*n), "o", ms=4, color="C1", label=f"samples @ {fs}Hz") if f > fs/2: # 超过奈奎斯特频率 = 采样率一半 → 混叠 ax.plot(t*1000, np.cos(2*np.pi*abs(f-fs)*t), "r--", lw=1.2, label=f"alias: samples look like {abs(f-fs)}Hz !") ax.legend(loc="upper right", fontsize=8); ax.set_ylabel("amplitude") axes[1].set_xlabel("time (ms)") plt.tight_layout(); plt.show() print("3kHz @ 16kHz:安全(奈奎斯特 8kHz 以内)") print("3kHz @ 4kHz :采样点连起来像 1kHz —— 假信号!这就是混叠")
这就是为什么要先重采样 拿 44.1kHz 的歌直接截成 16k,高于 8kHz 的成分不会消失,而是折叠成假频率污染信号——所以正规重采样要先低通滤波再降采样。现在你知道这一步不是形式主义。

先看全局:下面的六幕动画把「波形 → 切帧 → 频谱 → 语谱图 → 梅尔频谱」的完整旅程一次演完(它浓缩了本课 + 0004/0005 两课的主线,画面由真实信号计算而来,不是示意图)。点「播放全程」看一遍,再往下逐站拆解:

二、波形不够用:换个角度看声音

波形只能看出"什么时候响、什么时候轻"——节奏和音量。但它看不出"这是什么音":男声"啊"和女声"衣"的波形都是一团乱麻。而人耳分辨声音靠的是频率:声音里含有哪些"每秒振动多少次"的成分。

先玩起来,边听边看。四种声音,先各点一遍、各听一遍,再往下读:

看完发现了什么?对照一下:

声音波形里看到语谱图里看到
纯音 440Hz整齐的波浪一条水平亮线,高度正好在 440Hz——频率变成了"纵向位置"
扫频越来越密的波浪一条上升的斜线——频率随时间升高,轨迹一目了然
元音「啊」复杂但周期性的毛刺一排水平条纹(谐波),条纹亮度不一(共振峰)——这就是人声的指纹
白噪声完全随机整片糊亮——所有频率都有,谁也不突出
关键转变 波形是"时间 × 振幅",语谱图是"时间 × 频率 × 能量"。语音的内容信息几乎全在频率结构里——所以 ASR 模型吃的都是频率视角的图像。说一句话 = 语谱图上的条纹快速移动变化。

三、语谱图是怎么画出来的

原理一句话:把波形切成许多小段(帧),对每一帧问一次"此刻含有哪些频率"(用傅里叶变换/FFT),然后把每帧的答案横向排开。这叫短时傅里叶变换(STFT)。

波形 → 切成小帧 每帧做 FFT "此刻有哪些频率?" 每帧答案横着排开 = 语谱图(示意)
STFT:切帧 → 逐帧 FFT → 排开。典型配置:每帧 25 毫秒、每 10 毫秒前进一次——1 秒语音 ≈ 100 帧。第 2 课说过,AuT 编码器做注意力,就是让这些帧互相看来看去。

原理图看过了,现在亲手写一遍 STFT——除了画图,核心只有四行:切帧、加窗、逐帧 FFT、排开。改 win(窗长)再看图,体会"频率分辨率 vs 时间分辨率"的取舍:

import warnings warnings.filterwarnings("ignore") import numpy as np import matplotlib.pyplot as plt fs, dur = 8000, 1.0 # 8kHz 足够演示 t = np.arange(int(fs*dur)) / fs x = np.concatenate([np.cos(2*np.pi*500*t[:len(t)//2]), # 前半秒:500Hz np.cos(2*np.pi*900*t[len(t)//2:])]) # 后半秒:900Hz win, hop, nfft = 400, 80, 1024 # 窗长 50ms / 帧移 10ms —— 改 win 玩玩! spec = np.array([np.abs(np.fft.rfft(x[i:i+win] * np.hanning(win), nfft)) for i in range(0, len(x)-win, hop)]) plt.figure(figsize=(8, 3.5)) plt.imshow(20*np.log10(spec + 1e-6), aspect="auto", origin="lower", extent=[0, dur, 0, fs/2/1000], cmap="magma") plt.xlabel("time (s)"); plt.ylabel("frequency (kHz)") plt.title(f"Hand-written STFT (win={win}, hop={hop})") plt.tight_layout(); plt.show() print("帧数 =", spec.shape[0], "(每", hop, "样本一帧)") print("频率分辨率 =", round(fs/nfft, 1), "Hz/点 | 时间分辨率 =", round(win/fs*1000), "ms/帧") print("练习:win=1200(150ms)再跑——500 与 900 两根线更细更稳,") print(" 但切换时刻糊成一片:时间和频率,只能二选一地精细。")
blackbox 拆完了 第 3 课声音实验室里的语谱图,内核就是这四行——而且 win/hop 这两个你会真实调的参数,现在归你控制。win=1200 的实验会直观给出信号处理最著名的权衡:窗越长频率越细、时间越糊。

四、最后一步:mel 刻度(向人耳看齐)

还有一个心理学事实:人耳对低频差异敏感(500Hz 和 600Hz 听着天差地别),对高频迟钝(8500Hz 和 9500Hz 几乎没差别)。mel 刻度就是把频率轴按这个规律"压缩"——低频占更多刻度、高频挤在一起。把语谱图的纵轴换成 mel 刻度,得到的梅尔频谱就是现代 ASR 的标准输入画像(Whisper 如此,Qwen3-ASR 的具体前端参数——mel 维数、帧长帧移——我们第 5 课跑模型时从它的 processor 里亲眼看)。

0 1k 2k 3k 4k 线性频率轴:每 1kHz 等宽 —— 但人耳不这么感受 0 1k 2k 3k 4k mel 轴:低频拉开、高频压缩 —— 同一根轴,"人耳尺子"
线性轴 vs mel 轴:同样 0–4kHz,mel 轴把低频区段放宽、高频压窄,贴合人耳敏感度。

五、串回 Qwen3-ASR

今天概念在 Qwen3-ASR 里的位置
16kHz 单声道硬性输入要求;微调数据准备的第一道工序就是统一重采样
切帧 + 频率视角音频 → 帧序列;AuT 编码器里,帧与帧做自注意力(第 2 课)
梅尔频谱现代 ASR 通行前端画像;Qwen3-ASR 的确切参数第 5 课从 processor 里读
谐波/共振峰条纹元音指纹;嘈杂/口音破坏条纹结构 → 编码器要靠注意力上下文修复
checkpoint 你现在能对任意一段音频想象出它的"模型视角"了:16kHz 数字 → 一张 mel 频率条纹图 → 按帧喂给注意力。地基三课到此完成,下一课正式拆 Qwen3-ASR 本尊的架构。

检索练习

本课主读材料

去读(约 15 分钟)

Hugging Face Audio Course · Unit 1: Audio Data(右上角可切中文)

读法:Unit 1 讲的就是今天的采样、振幅、频谱,但用的是真音频 + 可运行的代码。重点看 "Frequency and pitch" 到 "Mel spectrogram" 几小节,和我们声音实验室的结论互相印证。选读:Mel Spectrogram 逐步构建。

本课新术语已入 术语表:采样定理/奈奎斯特、频率、振幅、基频/谐波、共振峰、傅里叶变换/FFT、帧、语谱图、mel 刻度。

我是你的老师,别客气 声音实验室里可以乱点乱听(比如连点"元音"和"扫频"对比条纹和斜线)。如果哪个图画得让你更糊涂了,或想听别的声音(双音?半采样?),直接说,我改给你。