一、项目整体背景
该项目属于基于高斯混合模型(GMM)的说话人识别(含性别识别) 领域,是语音信号处理和生物特征识别的典型应用。说话人识别的核心目标是通过语音信号的声学特征区分不同说话人(或识别性别),广泛应用于身份验证、语音助手、安防监控等场景。
传统说话人识别的关键挑战在于:
- 原始语音时域信号冗余度高、噪声敏感,无法直接建模;
- 不同说话人的语音特征服从复杂的概率分布,需选择合适的模型拟合;
- 需兼顾特征的静态表征(如音色)和动态变化(如语调)。
二、整体解决方案
- 特征层:提取梅尔频率倒谱系数(MFCC)作为静态特征,计算其差分(Delta)特征捕捉动态变化,组合成40维特征向量,并通过归一化消除信道/环境差异;
- 模型层:使用高斯混合模型(GMM)拟合每个说话人/性别的特征概率分布(GMM适合处理语音特征的多峰分布特性);
- 识别层:对测试音频提取相同特征,计算特征在各GMM模型下的对数似然值,取似然值最大的模型对应说话人/性别作为识别结果。
三、各文件详细解析(背景+解决方案+详细注释)
1. speakerfeatures.py — 语音特征提取核心模块
(1)文件背景
语音信号的原始时域数据无法直接用于建模,需转换为能表征声学特性的特征。MFCC(梅尔频率倒谱系数)是语音识别的经典特征,能模拟人耳对频率的非线性感知;Delta(差分)特征则补充特征的动态变化信息(如语速、语调),两者结合可提升特征的区分性。该文件实现了MFCC+Delta特征的提取、归一化,最终输出40维特征向量。
(2)解决的核心问题
- 原始音频时域信号冗余、噪声敏感,转换为频域特征(MFCC)降低维度并保留核心信息;
- 静态MFCC无法反映特征的动态变化,通过Delta特征捕捉时间维度的特征变化;
- 不同音频的特征尺度差异大,通过归一化(CMS)消除信道/环境干扰。
(3)详细注释代码
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 14 19:26:59 2015
@author: Abhijeet Kumar
@code : 实现音频的MFCC+Delta特征提取,输出40维特征向量
@Note : 20维MFCC(19个MFCC系数 + 1帧对数能量) + 20维Delta特征 = 40维特征
"""
import numpy as np
from sklearn import preprocessing
import python_speech_features as mfcc # 专业的语音特征提取库
def calculate_delta(array):
"""
计算特征向量矩阵的Delta(差分)特征,捕捉特征的动态变化
Delta特征反映了MFCC特征在时间维度的变化趋势,提升特征区分性
参数:
array: 20维MFCC特征矩阵,shape=(帧数, 20)
返回:
deltas: 20维Delta特征矩阵,shape=(帧数, 20)
"""
rows, cols = array.shape # rows=帧数,cols=MFCC特征维度(20)
deltas = np.zeros((rows, 20)) # 初始化Delta特征矩阵
N = 2 # 计算Delta的邻域窗口大小(前后各2帧)
for i in range(rows): # 遍历每一帧的MFCC特征
index = [] # 存储邻域帧的索引(后帧,前帧)
j = 1
while j <= N:
# 处理边界情况:若i-j<0则取第0帧,若i+j>最后一帧则取最后一帧
if i - j < 0:
first = 0
else:
first = i - j
if i + j > rows - 1:
second = rows - 1
else:
second = i + j
index.append((second, first)) # 存储(后j帧,前j帧)的索引
j += 1
# 计算Delta特征:加权平均邻域帧的差值,权重为2(j=2时)和1(j=1时),分母10是归一化系数
deltas[i] = (array[index[0][0]] - array[index[0][1]] + (2 * (array[index[1][0]] - array[index[1][1]]))) / 10
return deltas
def extract_features(audio, rate):
"""
提取音频的40维特征向量:20维MFCC(含能量)+ 20维Delta特征
步骤:1.提取MFCC;2.归一化(CMS);3.计算Delta;4.拼接特征
参数:
audio: 原始音频信号的时域数据(一维数组)
rate: 音频的采样率(如16000Hz)
返回:
combined: 40维特征矩阵,shape=(帧数, 40)
"""
# 提取MFCC特征:
# - rate: 采样率
# - 0.025: 帧长(25ms,语音处理标准值)
# - 0.01: 帧移(10ms,语音处理标准值)
# - 20: MFCC特征维度
# - appendEnergy=True: 最后一维添加帧的对数能量(替代第0阶MFCC)
mfcc_feat = mfcc.mfcc(audio, rate, 0.025, 0.01, 20, appendEnergy=True)
# 归一化(CMS,倒谱均值减):消除信道/环境差异,提升特征鲁棒性
mfcc_feat = preprocessing.scale(mfcc_feat)
# 计算Delta特征(动态特征)
delta = calculate_delta(mfcc_feat)
# 拼接MFCC和Delta特征,得到40维特征向量
combined = np.hstack((mfcc_feat, delta))
return combined
# 主函数:测试/说明调用方式
if __name__ == "__main__":
print("主函数说明:调用extract_features(audio, signal_rate)传入音频和采样率即可提取特征")
2. train_models.py — GMM模型训练模块
(1)文件背景
说话人的语音特征服从多峰高斯分布,高斯混合模型(GMM)能精准拟合这种分布。该文件读取训练集音频路径,为每个说话人提取5个音频的特征,训练16分量的GMM模型,并将模型保存到指定路径,供后续测试使用。
(2)解决的核心问题
- 如何批量处理多个说话人的训练音频,聚合同一说话人的特征;
- 如何选择GMM超参数(分量数、迭代次数等)保证拟合效果;
- 如何将训练好的模型持久化,供测试阶段加载使用。
(3)详细注释代码
import pickle # 模型持久化(保存/加载)
import numpy as np
from scipy.io.wavfile import read # 读取WAV音频文件
from sklearn.mixture import GaussianMixture # 高斯混合模型
from speakerfeatures import extract_features # 导入自定义的特征提取函数
import warnings
# 忽略警告(如GMM收敛警告,不影响模型效果)
warnings.filterwarnings("ignore")
# 训练音频数据路径(存放各说话人的训练音频)
source = "./development_set/"
# 训练好的GMM模型保存路径
dest = "./speaker_models/"
# 训练集音频路径列表文件(每行是一个音频文件路径,格式如:speaker1-1.wav)
train_file = "development_set_enroll.txt"
# 打开训练文件,读取音频路径
file_paths = open(train_file, 'r')
count = 1 # 计数器:统计当前说话人的音频数量(每个说话人取5个音频)
# 初始化特征数组:存储单个说话人的所有音频特征
features = np.asarray(())
# 遍历每个训练音频路径
for path in file_paths:
path = path.strip() # 去除路径两端的空格/换行符
print(f"正在处理音频:{path}")
# 读取音频文件:返回采样率(sr)和音频时域数据(audio)
sr, audio = read(source + path)
# 提取40维MFCC+Delta特征
vector = extract_features(audio, sr)
# 聚合当前说话人的特征:
# - 若特征数组为空,直接赋值
# - 否则垂直拼接(按帧维度)
if features.size == 0:
features = vector
else:
features = np.vstack((features, vector))
# 每个说话人使用5个音频训练模型(计数器到5时训练并保存模型)
if count == 5:
# 初始化GMM模型:
# - n_components=16: 高斯分量数(经验值,平衡拟合效果和计算量)
# - max_iter=200: 最大迭代次数(保证模型收敛)
# - covariance_type='diag': 对角协方差矩阵(计算高效,适合语音特征)
# - n_init=3: 多次初始化取最优结果,避免局部最优
gmm = GaussianMixture(n_components=16, max_iter=200, covariance_type='diag', n_init=3)
# 用当前说话人的特征训练GMM模型
gmm.fit(features)
# 生成模型文件名:从音频路径中提取说话人名称(如speaker1-1.wav → speaker1.gmm)
picklefile = path.split("-")[0] + ".gmm"
# 保存训练好的GMM模型(二进制写入)
pickle.dump(gmm, open(dest + picklefile, 'wb'))
# 打印训练完成信息:说话人、特征维度
print(f'+ 说话人 {picklefile} 建模完成,特征数据量 = {features.shape}')
# 重置特征数组,准备下一个说话人的特征聚合
features = np.asarray(())
count = 0 # 重置计数器
count = count + 1 # 计数器递增
3. test_gender.py — 说话人/性别识别测试模块
(1)文件背景
测试阶段需加载训练好的GMM模型,对测试音频提取特征后,计算特征在每个模型下的对数似然值(似然值越高,说明特征属于该模型的概率越大),最终选择似然值最大的模型作为识别结果。
(2)解决的核心问题
- 如何批量加载所有训练好的GMM模型,并映射到对应的说话人/性别;
- 如何计算测试特征在各模型下的对数似然值,量化匹配程度;
- 如何根据似然值确定最终识别结果,并输出。
(3)详细注释代码
import os # 文件路径处理
import pickle # 加载训练好的GMM模型
import numpy as np
from scipy.io.wavfile import read # 读取测试音频
from speakerfeatures import extract_features # 导入特征提取函数
import warnings
import time # 延迟输出(提升可读性)
# 忽略警告
warnings.filterwarnings("ignore")
# 测试音频数据路径
source = "./development_set/"
# 训练好的GMM模型路径
modelpath = "./speaker_models/"
# 测试集音频路径列表文件
test_file = "development_set_test.txt"
# 打开测试文件,读取测试音频路径
file_paths = open(test_file, 'r')
# 获取所有GMM模型文件路径(过滤.gmm后缀)
gmm_files = [os.path.join(modelpath, fname) for fname in
os.listdir(modelpath) if fname.endswith('.gmm')]
# 加载所有训练好的GMM模型(列表形式)
models = [pickle.load(open(fname, 'rb')) for fname in gmm_files]
# 提取每个模型对应的说话人/性别名称(从文件名中解析:speaker1.gmm → speaker1)
speakers = [fname.split("/")[-1].split(".gmm")[0] for fname
in gmm_files]
# 遍历每个测试音频,进行识别
for path in file_paths:
path = path.strip() # 去除路径两端的空格/换行符
print(f"\n正在测试音频:{path}")
# 读取测试音频的采样率和时域数据
sr, audio = read(source + path)
# 提取测试音频的40维MFCC+Delta特征
vector = extract_features(audio, sr)
# 初始化对数似然值数组:长度=模型数量(说话人/性别数量)
log_likelihood = np.zeros(len(models))
# 遍历每个GMM模型,计算测试特征的对数似然值
for i in range(len(models)):
gmm = models[i] # 获取当前模型
# 计算特征在模型下的对数似然值(score返回每帧的对数概率)
scores = np.array(gmm.score(vector))
# 求和得到整个音频的总对数似然值(量化匹配程度)
log_likelihood[i] = scores.sum()
# 找到对数似然值最大的模型索引(似然值越大,匹配度越高)
winner = np.argmax(log_likelihood)
# 输出识别结果:最大似然值对应的说话人/性别
print(f"\t识别结果 - {speakers[winner]}")
# 延迟1秒(避免输出过快,提升可读性)
time.sleep(1.0)
四、关键补充说明
-
数据文件格式要求:
development_set_enroll.txt:每行是训练音频路径(如male1-1.wav、female2-3.wav),每个说话人/性别需包含5个音频;development_set_test.txt:每行是测试音频路径,格式与训练文件一致;development_set/:存放所有训练/测试WAV音频;speaker_models/:自动生成,存放训练好的.gmm模型文件。
-
模型超参数调整:
- GMM的
n_components(分量数)可根据数据量调整(如8/16/32),分量数越多拟合效果越好,但计算量越大; - 帧长(0.025s)、帧移(0.01s)是语音处理的标准值,无需轻易修改;
- MFCC维度(20)可调整为13(更常用),需同步修改Delta特征的维度。
- GMM的
-
扩展方向:
- 加入噪声鲁棒性处理(如预加重、端点检测);
- 改用i-vector/PLDA模型提升识别精度;
- 增加性别分类的专用标签,实现纯性别识别(而非通用说话人识别)。


被折叠的 条评论
为什么被折叠?



