KNN+MFCC实现方言识别

数据集

有关方言的数据集寻找困难,而且水平参差不齐,于是自己制作:

  1. 前往学习圣地——哔哩哔哩,寻找方言配音
  2. 找到合适的配音,下载并提取音频
  3. 音频剪辑,如果不想下载软件,我推荐一个在线音频剪辑工具

音频直接剪辑下载的命名不方便,可以按照这个方法:

CTRL+A全选,点击F2,批量重命名

例如,我将文档里面的音频命名为cs-m(表示长沙方言,男性),确认后自动将名称按照cs-m + (序号) 的方式命名,注意m后面有空格,括号为半角括号:

于是数据集便以规范命名制作完毕。

MFCC特征提取

接下来需要批量导入音频文件,我试了网上的一些方法,感觉比较乱而且不符合我的需求。我的需求是:

  1. 按照循环,每次读取一个样本
  2. 对此样本进行预加重,分帧,加窗,MFCC特征矩阵的计算
  3. 将一个样本的MFCC参数按帧平均,最终提取出一个样本的13个参数(最后一个元素是短时能量,在代码中会提到)
  4. 下一次循环进行下一样本的计算,同样也是提取13个参数,放到下一行
  5. 最终将矩阵 样本数x特征 导出为excel,方便下一步训练集和测试集的制作

代码如下:

clear all;
clc;
[FileName,PathName]=uigetfile({'*.wav'},'wav文件读取','MultiSelect','on');
% 选择需要用到的wav文件,存入FileName

aveCC=zeros(30,13);
for k=1:30
    % 每次读入一个音频
    str = cell2mat(FileName(k));
    [y2,fs]=audioread(str);
    % 双音道变为单音道
    y1=y2(:,1);
    CC = mymfcc(y1,fs);
    col=size(CC,2);
    % 求平均值
    sum=zeros(1,col);
    row=size(CC,1);
    for i=1:row
        sum=sum+CC(i,:);
    end
    ave=sum/row;
    aveCC(k,:)=ave;
end

disp(aveCC);
% 写成一个xlsx
xlswrite('yy-m.xlsx',aveCC);

关于MFCC的原理,大家可以参考语音信号处理之(四)梅尔频率倒谱系数(MFCC)
写的很详细,按照原理可以很简单的写出代码。

这里是我自己的函数mymfcc,其中的2个归一化的原因和原理我比较模糊,欢迎大神指点迷津:

function mfccmat = mymfcc(x,fs)
% mfcc特征提取函数
% 输入:x为信号,fs抽样率
% 输出:mfccmat是一帧语音的MFCC系数矩阵

% 参数
n = 24; % Mel滤波器阶数
p = 12; % 倒谱阶数
nwin = 256; % 帧长

% 获得Mel滤波器组,这是voicebox中的函数
bank = melbankm(n,nwin,fs,0,0.5,'t');

% 归一化mel滤波器组系数
bank = full(bank); % 将稀疏矩阵转换全矩阵
bank = bank/max(bank(:));

% 归一化倒谱提升窗口
w = 1+0.5*p*sin(pi*(1:p)./p);
w = w/max(w);

% 预加重滤波器
x = double(x);
x = filter([1,-0.9375],1,x);

% 分帧,这也是voicebox中的函数
xframe = enframe(x,nwin);
nframe = size(xframe,1);% 总帧数

% 计算每帧的MFCC参数,留出13个位置,最后一个位置是短时能量
mfccmat = zeros(nframe,p+1);
for i = 1:nframe
    y = xframe(i,:)';
    y = y.*hamming(nwin); % 加窗
    energy = log(sum(y.^2)+eps); % 短时能量
    y = abs(fft(y));
    y = y(1:fix(nwin/2)+1); % 使y的长度和Mel滤波器组相同
    
    % Discrete cosine transform 离散余弦变换,用于逆变换取代ifft
    c = dct(log(bank*y+eps));
    c = c(2:p+1)'.*w; %2~p+1个系数,并且倒谱提升
    mfcc = [c,energy]; % 将能量加入,作为特征的一个元素
    mfccmat(i,:) = mfcc;
end

end

KNN分类器

在KNN实现鸢尾花分类中:https://blog.csdn.net/qq_45510888/article/details/106158889
已经详细介绍了KNN分类器原理和代码,这里就不赘述。

将做好的数据集按照7:3的比例分成训练集和测试集,然后使用KNN分类器导入数据,便可以进行分类。为了操作方便,进行了小修改。与上一版相比较增加的内容有:

feature=13;sort=feature+1;% 特征数和分类所在位置
k = 9;% 确定k的值,取值不同分类结果不同

% 读入训练集,分类:长沙1,河南2,南昌3,闽南4,普通话5,粤语6
train = xlsread('train.xlsx');
trainData = train(:,1:feature);% 前几列是特征
trainClass = train(:,sort);% 最后1列是分类

最终分类结果:

由于KNN最理想的情况应该是实现2分类,对于多分类(例如本文的6分类),经常会出现最大值有2个或者更多、无法确定此测试样本的最终分类的情况。作者还没想到更好的解决方法,暂且只是将第一顺序位的类别赋予结果,同样希望有相关经验的朋友交流讨论!

最后,大家在使用KNN时要规避其存在的一些不足之处:

  1. 添加某些类别的样本容量非常大,而其它类样本容量非常小,即已知的样本数量不均衡。有可能当输入一个和小容量类别同样的新样本时,该样本的K个近邻中,大容量类的样本占多数,从而导致误分类。
    解决:针对此种情况能够采用加权的方法,即和该样本距离小的近邻所相应的权值越大,将权值纳入分类的参考根据。或者尽可能制作数据集时就使各样本数量均衡。
  2. 分类时须要先计算待分类样本和全体已知样本的距离。才能求得所需的K近邻点,计算量较大,尤其是样本数量较多时。
    解决:针对这样的情况能够事先对已知样本点进行剪辑。去除对分类作用不大的样本,这一处理步骤仅适用于样本容量较大的情况,假设在原始样本数量较少时采用这样的处理。反而会添加误分类的概率。

关于KNN的改进,这里推荐一篇博客:KNN分类器

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值