音频特征

音频特征提取

引言:为什么需要特征提取?

原始的音频波形(Waveform)是一个非常长的一维时间序列,数据点极其密集。直接将其输入模型存在以下问题:

  • 计算量巨大:处理几十万甚至上百万个数据点非常低效。
  • 信息冗余:原始波形包含大量冗余信息和噪声。
  • 缺乏鲁棒性:微小的变化(如音量、相位)都会导致波形巨大差异。

核心目标:将原始音频转换为一种更紧凑、更稳定、信息量更丰富的表示形式,即特征,以便于模型学习。


代码示例环境准备

以下代码示例主要依赖 librosa (用于经典特征) 和 transformers (用于现代模型)。请先安装它们:

pip install librosa torch transformers soundfile

注意torchtransformers 的依赖。soundfile 用于加载音频文件。


第一部分:基础工具 - 从波形到时频谱

1. 短时傅里叶变换 (STFT) - 给时间装上频率的眼睛

  • 一句话总结:通过一个滑动的“窗口”,将一维的时域音频信号,转换为二维的“时间-频率”图像。
  • 形象类比:想象你不是一次性听完一整首歌,而是一小段一小段地听,并且分析每一小段里包含了哪些音高(频率)。
  • 核心步骤 (流水线)
    1. 分帧 (Framing):把长音频切成短的、有重叠的片段。
    2. 加窗 (Windowing):为每个片段边缘进行平滑处理,减少频谱分析的误差。
    3. 傅里叶变换 (FFT):分析每个片段包含的频率成分和能量。
    4. 拼接 (Concatenate):将所有片段的分析结果按时间顺序拼起来,形成一张图。
  • 关键权衡:时频分辨率
    • 窄窗 (Short Window)时间看得清,频率看得粗 (高时间分辨率, 低频率分辨率)。
    • 宽窗 (Long Window)频率看得清,时间看得粗 (低时间分辨率, 高频率分辨率)。

2. 语谱图 (Spectrogram) - 让声音被“看见”

语谱图就是STFT的结果的可视化。

  • 线性语谱图 (Linear Spectrogram)

    • 特点:频率轴是线性的 (例如 100Hz, 200Hz, 300Hz…)。
    • 问题:与人耳的感知方式不匹配。人耳对低频的变化更敏感。
  • 梅尔语谱图 (Mel Spectrogram)

    • 核心思想:将线性频率轴通过梅尔刻度 (Mel Scale) 进行非线性映射,使其更符合人耳的听觉特性。
    • 特点:在低频区域拉伸,在高频区域压缩。
    • 记忆点这是为卷积神经网络(CNN)准备音频数据的最常用形式,因为它模拟了我们听觉系统处理声音的方式,让模型更容易学习。

第二部分:经典特征 - 对语谱图的精炼

MFCC (梅尔频率倒谱系数)

  • 一句话总结:在梅尔语谱图的基础上,进一步压缩信息,只保留频谱的“轮廓”(包络),忽略精细的谐波结构。
  • 形象类比:如果说梅尔语谱图是一张详细的风景照,MFCC就是对这张照片的“简笔画”,只勾勒出山脉和天空的轮廓。
  • 核心步骤
    1. 获得梅尔语谱图:完成从STFT到取对数的所有步骤。
    2. 离散余弦变换 (DCT):这是MFCC最关键的一步。DCT是一种高效的压缩算法,能够将频谱能量集中在少数几个系数上。
    3. 保留低阶系数:通常只保留前12-20个系数,它们代表了频谱的宏观轮廓,而丢弃的高阶系数大多是噪声和细节。
  • 记忆点:MFCC是传统语音识别领域最经典、最成功的特征,因为它非常紧凑且能有效代表音色。

Python 示例 (librosa) - 包含含义与对齐解释

import librosa
import numpy as np

# ===================================================================
# 1. 定义核心参数 - 理解这些是理解对齐的关键
# ===================================================================
SR = 22050              # Sample Rate (采样率): 每秒的样本点数
HOP_LENGTH = 512        # Hop Length (帧移): STFT窗口每次滑动的样本点数
N_MFCC = 13             # Number of MFCCs: 要计算的MFCC系数数量

# ===================================================================
# 2. 加载音频并提取MFCC
# ===================================================================
# 使用librosa内置的音频样本,方便直接运行
y, sr_native = librosa.load(librosa.ex('trumpet'), duration=5)
# 确保音频采样率与我们定义的SR一致
if sr_native != SR:
    y = librosa.resample(y, orig_sr=sr_native, target_sr=SR)

# 提取MFCC特征
mfccs = librosa.feature.mfcc(y=y, sr=SR, n_mfcc=N_MFCC, hop_length=HOP_LENGTH)

# ===================================================================
# 3. 理解MFCC输出的数据含义
# ===================================================================
# mfccs.shape 的形式是 (n_mfcc, T)
# - n_mfcc 是我们指定的系数数量 (行)
# - T 是计算出的总时间帧数 (列)
T = mfccs.shape[1]
print(f"MFCC 矩阵形状: {mfccs.shape}")
print(f"- {mfccs.shape[0]} 行: 每个MFCC系数 (代表音色)")
print(f"- {mfccs.shape[1]} 列: 每个时间帧 (代表时间)")

# 每一列 (一个时间帧) 的含义:
# - mfccs[0, j]: 第j帧的能量/响度信息
# - mfccs[1:, j]: 第j帧的音色信息 (频谱包络的形状)

# ===================================================================
# 4. 时间对齐: 将音频帧与25fps视频帧同步
# ===================================================================
VIDEO_FPS = 25

# 关键点1: 计算每个音频帧之间的时间间隔 (秒)
time_per_audio_frame = HOP_LENGTH / SR
print(f"\n每个音频特征帧代表的时间长度: {time_per_audio_frame:.4f} 秒")

# 关键点2: 计算任意一个音频帧j的中心时间戳
# 例如,计算第100个音频帧的时间
audio_frame_index = 100
audio_time = audio_frame_index * time_per_audio_frame
print(f"音频第 {audio_frame_index} 帧的中心时间点: {audio_time:.3f} 秒")

# 关键点3: 对于给定的视频帧k,找到对应的音频帧j
# 假设我们想知道第50个视频帧,应该匹配哪个音频特征?
video_frame_index = 50
video_time = video_frame_index / VIDEO_FPS

# 公式: j = round(k * sr / (fps * hop_length))
corresponding_audio_frame = int(round(video_time / time_per_audio_frame))

print(f"\n--- 对齐示例 ---")
print(f"视频第 {video_frame_index} 帧的时间点是: {video_time:.3f} 秒")
print(f"通过计算,它最接近的音频特征是第 {corresponding_audio_frame} 帧。")

# 验证一下
corresponding_audio_time = corresponding_audio_frame * time_per_audio_frame
print(f"(验证: 音频第 {corresponding_audio_frame} 帧的时间点是 {corresponding_audio_time:.3f} 秒,两者非常接近)")

第三部分:现代特征 - 从数据中自监督学习

传统特征是基于信号处理知识“手工设计”的。现代方法则是让模型自己从海量无标签数据中“学习”出最佳特征。

1. Hubert - 学习音频的“文字”

  • 一句话总结:借鉴NLP中BERT的思想,通过“完形填空”的方式,让模型学会理解音频的上下文,从而抽取出高质量的内容特征。
  • 核心思想Masked Prediction (掩码预测)
    1. 音频离散化:先将连续的音频(如MFCC)聚类成离散的“声学单元”,好比把音频切分成一个个“单词”。
    2. 完形填空:随机遮盖掉一些“单词”,让模型去预测被遮盖的是什么。
  • 强项:对语音内容 (What was said) 的表征能力极强。

Python 示例 (transformers)

import torch
import librosa
from transformers import HubertModel, Wav2Vec2FeatureExtractor

# 1. 加载预训练模型和特征提取器
model_name = "facebook/hubert-large-ls960-ft"
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(model_name)
model = HubertModel.from_pretrained(model_name)

# 2. 加载并预处理音频
# 确保音频采样率为16kHz,与模型预训练时一致
audio_path = librosa.ex('trumpet')
y, sr = librosa.load(audio_path, sr=16000)

# 特征提取器将波形转换为模型所需的输入格式
inputs = feature_extractor(y, sampling_rate=sr, return_tensors="pt")

# 3. 模型推理
with torch.no_grad():
    outputs = model(**inputs)

# 4. 获取特征
# last_hidden_state 包含了模型最后一层输出的序列特征
# 这就是Hubert提取出的高质量音频特征
last_hidden_state = outputs.last_hidden_state

print(f'Input waveform shape: {y.shape}')
print(f'Feature extractor output shape: {inputs.input_values.shape}')
print(f'Hubert last hidden state shape: {last_hidden_state.shape}') # (批次大小, 序列长度, 隐藏层维度)

2. WavLM - 不仅懂内容,还懂“言外之意”

  • 一句话总结:Hubert的“Pro Max”版,不仅学习语音内容,还通过降噪和说话人模拟,学会了保留说话人身份等副语言信息。
  • 核心思想:在Hubert的基础上,增加了更复杂的训练任务。
    • 降噪任务:给模型听加了噪声的音频,让它预测干净的音频是什么样的。
    • 说话人混合:模拟多个人同时说话的场景,训练模型区分不同的人。
  • 强项“全能选手”。在语音内容理解的基础上,对说话人身份 (Who said it) 的识别能力非常出色,因此在声纹识别、语音分离等任务上效果拔群。

Python 示例 (transformers)

import torch
import librosa
from transformers import WavLMModel, AutoFeatureExtractor

# 1. 加载预训练模型和特征提取器
# WavLM有多种版本,Base+ 适用于多种任务
model_name = "microsoft/wavlm-base-plus"
feature_extractor = AutoFeatureExtractor.from_pretrained(model_name)
model = WavLMModel.from_pretrained(model_name)

# 2. 加载并预处理音频 (同样需要16kHz)
y, sr = librosa.load(librosa.ex('trumpet'), sr=16000)

inputs = feature_extractor(y, sampling_rate=sr, return_tensors="pt")

# 3. 模型推理
with torch.no_grad():
    outputs = model(**inputs)

# 4. 获取特征
# WavLM的输出同样可以作为通用音频特征
last_hidden_state = outputs.last_hidden_state

# WavLM的另一个强大之处在于可以提取用于声纹识别的x-vector
# xvector = outputs.xvector # (批次大小, xvector维度)

print(f'WavLM last hidden state shape: {last_hidden_state.shape}')

3. Whisper - 大力出奇迹的端到端巨人

  • 一句话总结:一个在海量(68万小时)真实互联网音频上训练出来的、极其鲁棒的语音识别和翻译模型。
  • 定位:它本身是一个端到端的应用模型,而不是一个通用的特征提取器。
  • 特点
    • 鲁棒性极强:对背景噪音、口音、方言等有超强的适应能力。
    • 多任务:集成了语音识别、翻译、语言检测等多种功能。
  • 作为特征使用:虽然是端到端模型,但其强大的编码器 (Encoder) 部分也可以被单独拿出来,作为一种非常高质量的“黑盒”特征提取器。

Python 示例 (transformers)

import torch
import librosa
from transformers import WhisperProcessor, WhisperForConditionalGeneration

# 1. 加载预训练模型和处理器
# 处理器包含了特征提取器和tokenizer
model_name = "openai/whisper-tiny"
processor = WhisperProcessor.from_pretrained(model_name)
model = WhisperForConditionalGeneration.from_pretrained(model_name)

# 2. 加载并预处理音频 (需要16kHz)
# 使用一个更适合语音识别的例子
y, sr = librosa.load(librosa.ex('libri1'), sr=16000)

inputs = processor(y, sampling_rate=sr, return_tensors="pt")

# 3. 模型推理:生成转录结果的token ID

# 4. 解码:将token ID转换为文字
transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)

print(f'Audio Transcription: {transcription[0]}')

第四部分:速查表 (Cheat Sheet)

特征/模型类别核心思想强项/主要用途记忆类比
梅尔语谱图传统/信号处理模拟人耳听觉,将线性频率映射到梅尔刻度CNN的“标配”输入,图像化音频风景照
MFCC传统/信号处理在梅尔谱基础上用DCT压缩,提取频谱包络传统语音识别,特征紧凑风景的“简笔画”
Hubert现代/自监督对离散的声学单元做“完形填空”理解语音内容 (说了什么)音频世界的BERT
WavLM现代/自监督在Hubert基础上增加降噪和说话人模拟理解内容+说话人身份 (全能)Hubert Pro Max
Whisper现代/端到端在超大规模、弱监督数据上训练的ASR系统语音识别和翻译,鲁棒性极强大力出奇迹的巨人