You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨说话人音频相似度检测异常:MFCC+DTW方法问题排查与优化

语音相似度检测问题

我有两段由不同说话人录制的、包含同一词汇的音频文件,需要验证它们是否相似,但不想采用语音转文本的方式(因为部分音频不含有效词汇)。

我对音频预处理后提取了MFCCs(梅尔频率倒谱系数)向量,并应用了DTW(动态时间规整)算法。对比同一音频时得到0相似度分数(符合预期),但对比不同说话人的同词汇音频时却得到高分(判定为不相似),这显然不符合需求。请问我的方法存在什么问题?能否提供解决思路?


现有实现代码

from pydub import AudioSegment, silence
from scipy.signal import lfilter
import librosa
import numpy as np
from sklearn.preprocessing import StandardScaler
from dtw import dtw

# 音频静音切割预处理
audio_file = AudioSegment.from_wav('C://Users//10Rs6//Desktop//testapb.wav')
min_silence_len = 100  # 毫秒
silence_thresh = -25  # dBFS
non_silent_segments = silence.split_on_silence(audio_file, 
                                                min_silence_len=min_silence_len, 
                                                silence_thresh=silence_thresh)
trimmed_audio = AudioSegment.empty()
for segment in non_silent_segments:
    trimmed_audio += segment
trimmed_audio.export('C://Users//10Rs6//Desktop//trimmed_audio5.wav', format='wav')

# 预加重函数
def preemphasis(signal, alpha=0.97):
    return lfilter([1, -alpha], [1], signal)

# 假设resampled_audio_test和resampled_audio_ref是已重采样的音频数组
pre_emphasised_test = preemphasis(resampled_audio_test)
pre_emphasised_ref = preemphasis(resampled_audio_ref)
normalized_test = librosa.util.normalize(pre_emphasised_test)
normalized_ref = librosa.util.normalize(pre_emphasised_ref)

# 提取MFCC并错误地做了时间维度均值
mfccsT = librosa.feature.mfcc(y=pre_emphasised_test, sr=41100, n_mfcc=13)
mfccsT = np.mean(mfccsT.T, axis=0)  # 这里丢失了时间序列信息

mfccsR = librosa.feature.mfcc(y=pre_emphasised_ref, sr=41100, n_mfcc=13)
mfccsR = np.mean(mfccsR.T, axis=0)  # 同样丢失时间序列

# 分别标准化两个MFCC(错误)
mfccsT_2d = np.reshape(mfccsT, (mfccsT.shape[0], -1))
scaler = StandardScaler()
scaler.fit(mfccsT_2d)
normalized_mfccsT_2d = scaler.transform(mfccsT_2d)
normalized_mfccsT = np.reshape(normalized_mfccsT_2d, mfccsT.shape)

mfccsR_2d = np.reshape(mfccsR, (mfccsR.shape[0], -1))
scaler = StandardScaler()
scaler.fit(mfccsR_2d)
normalized_mfccsR_2d = scaler.transform(mfccsR_2d)
normalized_mfccsR = np.reshape(normalized_mfccsR_2d, mfccsR.shape)

# DTW计算距离
normalized_mfccsT = normalized_mfccsT.reshape(-1, 1)
normalized_mfccsR = normalized_mfccsR.reshape(-1, 1)
l2_norm = lambda x, y: (x - y) ** 2
dist, cost_matrix, acc_cost_matrix, path = dtw(normalized_mfccsT, normalized_mfccsR, dist=l2_norm)
print(dist)

问题分析

你的核心问题出在MFCC特征的处理和标准化流程上,完全浪费了DTW的时序对齐能力:

  1. 丢失语音时序信息:对MFCC的时间维度取均值,把原本的二维时序特征(13个MFCC系数×时间步)压缩成了一维静态向量。DTW的作用是对齐不同长度的时间序列,现在变成了两个固定向量的距离计算,完全忽略了同一词汇发音的时序变化(比如辅音到元音的过渡节奏),不同说话人的同词汇发音时序差异被抹平,自然无法正确判断相似度。
  2. 独立标准化导致特征分布不一致:分别对两个音频的MFCC做StandardScaler,相当于把两个特征拉到了不同的分布空间,此时计算的距离没有意义,无法反映真实的特征相似性。
  3. 额外隐患:代码中resampled_audio_test和resampled_audio_ref的采样率是否统一到了41100?如果不一致,MFCC的频率分辨率会有差异,也会干扰结果判断。

解决方案

1. 保留MFCC的时序特征

去掉对MFCC时间维度的均值计算,保留原始的二维MFCC矩阵,让DTW能发挥时序对齐的作用:

# 正确提取MFCC:保留时间序列
mfccsT = librosa.feature.mfcc(y=pre_emphasised_test, sr=41100, n_mfcc=13)
mfccsR = librosa.feature.mfcc(y=pre_emphasised_ref, sr=41100, n_mfcc=13)
# 转置成 (时间步, n_mfcc),符合DTW的输入习惯
mfccsT = mfccsT.T
mfccsR = mfccsR.T

2. 统一标准化流程

用同一个Scaler拟合所有数据(或其中一个作为基准),再对两个MFCC做转换,保证特征分布一致:

# 合并两个MFCC的时间步数据来拟合scaler
combined_mfccs = np.vstack([mfccsT, mfccsR])
scaler = StandardScaler()
scaler.fit(combined_mfccs)
# 分别转换两个MFCC
normalized_mfccsT = scaler.transform(mfccsT)
normalized_mfccsR = scaler.transform(mfccsR)

3. 优化DTW计算

使用更适合语音特征的距离度量(比如余弦距离),或者添加窗口约束避免过度对齐:

# 使用余弦距离作为DTW的元素距离
from scipy.spatial.distance import cosine
dist, cost_matrix, acc_cost_matrix, path = dtw(normalized_mfccsT, normalized_mfccsR, dist=cosine)
# 可选:添加窗口约束(只允许在±50%时间步内对齐,避免无效匹配)
# dist, cost_matrix, acc_cost_matrix, path = dtw(normalized_mfccsT, normalized_mfccsR, dist=cosine, window_type="sakoechiba", window_size=min(len(mfccsT), len(mfccsR))//2)

4. 增强特征维度(可选)

添加MFCC的一阶、二阶差分,捕捉语音的动态变化趋势,提升相似度判断的准确性:

def extract_mfcc_features(y, sr):
    mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
    mfcc_delta = librosa.feature.delta(mfcc)
    mfcc_delta2 = librosa.feature.delta(mfcc, order=2)
    # 合并特征并转置为(时间步, 特征数)
    combined = np.vstack([mfcc, mfcc_delta, mfcc_delta2]).T
    return combined

mfccsT = extract_mfcc_features(pre_emphasised_test, 41100)
mfccsR = extract_mfcc_features(pre_emphasised_ref, 41100)

5. 统一预处理流程

确保两个音频的采样率、静音切割参数完全一致,避免预处理环节引入不必要的差异。


内容的提问来源于stack exchange,提问作者jey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:44:53