基于MATLAB的有限语音识别

前段时间参与了一个小组项目,其中涉及到了MATLAB进行“上”、“下”、“左”、“右”的语音识别。查了一些资料、进行些许调试算是把问题解决了。

目录

一、识别原理

1.MFCC算法

2.SVM算法

3.ECOC(纠错输出码)

二、代码原理

1.训练集的收集

2.模型的训练

3.收集待分类音频样本并进行分类

4.音频的向量化处理

三、其它问题


一、识别原理

对于语音识别,首先确定基本原理是利用MFCC算法提取声音特征,然后将特征转化成的向量进行分类,根据训练特点可以考虑采用基于SVM算法的纠错输出码(Error-Correcting Output Codes, ECOC)算法。

1.MFCC算法

MFCC(Mel Frequency Cepstral Coefficients)是一种用于特征提取的算法,其通过傅立叶变换、梅尔滤波和DCT等,从时域转换到梅尔频率倒谱空间,提取声音的形状特征。

1. 傅立叶变换(FFT):对音频信号进行快速傅立叶变换,从时域转换到频域。

2. 梅尔滤波器组(Mel Filterbank):将频域信号通过一个梅尔滤波器组进行过滤。梅尔滤波器组模拟人耳对频率的感知,低频分辨率高,高频分辨率低。

3. 对梅尔滤波器组输出取对数:为了弱化高能成分的影响。

4. 离散余弦变换(DCT):将对数梅尔频率能量谱转换到梅尔频率倒谱空间。DCT可以将相关性信息压缩到少数系数中。

5. 取前N个系数。这些系数即为MFCC特征。

2.SVM算法

SVM(支持向量机)算法的基本思想和工作原理是将数据映射到高维空间,在这个空间中找到最大间隔超平面进行分类,其基本原理是:

1. 将原始输入空间中的数据映射到一个高维特征空间,使得原来不线性可分的两个类在这个新空间中可以线性分开。这种映射是通过核函数来实现的。

2. 在这个新空间中找到一个超平面,使得这个超平面可以将两个类隔开,且离这两个类最近的样本点与超平面的距离最大。这两个离超平面最近的样本点称为支持向量。

3. 找到的这个最大间隔超平面就是SVM的决策面。新样本点将根据它与这个超平面之间的位置关系来判断属于哪一类。

3.ECOC(纠错输出码

SVM在解决本问题中存在一个不可忽视的问题,那就是SVM算法是一种二分类的算法,然而实际上本问题的语音指令识别要求识别“上”、“下”、“左”、“右”四种指令。此时,纠错输出码(Error-Correcting Output Codes, ECOC)将作为一个很好的解决思路,ECOC的工作原理如下:

- ECOC将问题分解成多个二分类子问题。对于K类分类问题,ECOC的二值编码矩阵,其中每一行对应一个类,每一列对应一个二分类器。

- 二值编码矩阵中的每个元素都取0或1,用来表示该类是否属于该二分类器正例或反例集合。

三分类的一个例子

- 然后根据编码矩阵训练出L个二分类器。在预测阶段,每个样本被每个二分类器判断,获得一个长为L的二值代码向量。

- 最后将该样本的代码向量与每一行编码进行汉明距离计算,预测属于距离最小的那一行对应的类。

二、代码原理

在完成原理设计的确认后,就开始完成代码的编写。代码总体流程设计如下:

1.训练集的收集

getVoice.m用于实现基于能量特征的语音关键词触发录音功能,录音片段会自动命名并保存到文件,可以用于后续语音识别模型训练,函数流程如下:

1. 定义了关键词列表和能量阈值。

2. 初始化麦克风录音设备,设置采样参数。

3. 输入需要录制的每个类别的样本数量。

4. 开始循环录制每个类别的语音:

   - 实时从麦克风读取音频数据。

   - 计算音频能量特征,判断是否超过阈值。

   - 如果超过阈值,则将该段音频保存到文件。

   - 循环录制,直到录制数量达到设定值。

function getVoice()
% 该函数用于录入音频
% 音频格式满足voiceTrain

% 关键词
keywords={'up','down','left','right'};
% 定义触发阈值,根据实际情况调整
thresholds = 0.0008;
% 定义声音频率
fs = 44100;
% 定义采样参数SamplesPerFrame
SPF = 1024*32;
% 定义文件位置
filePath = strcat('./voice',num2str(fs),'/');

% 获取麦克风设备信息
% devices = audiodevinfo;

% 创建麦克风输入对象
reader = audioDeviceReader('Device',"Default",'SamplesPerFrame',SPF);
% setup(reader);

% 输入每个类别训练数量
numVoice = input('请输入每个类别训练数量');
% while(~isnumerictype(numVoice))
%     numVoice = input('请输入每个类别训练数量');
% end

% 定义文件号
n = 0;
% 开始语音录制
disp('开始实时语音识别...');
for k = 1:size(keywords,2)
    kw = cell2mat(keywords(k)); % 定义当前类别

    disp(strcat('请说:',kw));
    firstAudio = reader();
    % 当前类别的理论最大文件序号
    knumVoice = numVoice;
    while(n < knumVoice)
        % 待提取的二倍长度原始音频
        rawAudio = [firstAudio;reader()];
        % 后半段音频将作为下次的前半段
        firstAudio = rawAudio(SPF+1:end,:);
        % 音频的振幅最高点作为中点
        [~,maxPoint] = max(rawAudio);
        % 将rawAudio振幅最高点的两侧作为判断的音频
        if(maxPoint - SPF/2 >= 1 && maxPoint + SPF/2 - 1 <= size(rawAudio,1))
            audio = rawAudio(maxPoint-SPF/2:maxPoint+SPF/2-1,:);
        else % 可以认为音频不完整
            continue;
        end
        % 计算音频能量特征
        energy = sum(abs(audio).^2) / length(audio); % 计算音频能量特征
        if(energy > thresholds) % 能量超过阈值
            % 完整文件路径
            fullPath = strcat(filePath,kw,int2str(n),'.wav');
            while(exist(fullPath,'file') == 2) % 文件已存在
                % 设置文件序号的偏移
                knumVoice = knumVoice + 1;
                n = n + 1;
                fullPath = strcat(filePath,kw,int2str(n),'.wav');
            end
            audiowrite(fullPath,audio,fs);
            disp(strcat('获得数据',kw,int2str(n)));
            n = n +1;
        end
    end
    n = 0;
end

% release(reader);
disp('识别结束,请自行清理不良音频并注意文件命名');
end
2.模型的训练

voiceTrain.m用于实现基于SVM的语音分类模型训练,函数流程如下:

1. 定义关键词列表和声音文件路径。

2. 初始化训练数据矩阵和标签向量。

3. 开始循环每个类别:

   - 按文件名顺序读取该类别下所有语音文件。

   - 对读取的音频数据进行预处理,提取特征。

   - 将特征数据添加到训练数据矩阵,同时将类别标签添加到标签向量。

4. 循环读取完一个类别后,标签值加1,表示下一个类别。

5. 循环读取完所有类别后,训练SVM多分类模型。

6. 将训练好的SVM模型保存到文件。

function voiceTrain()
% 该函数用于训练SVM模型
% 声音的录取应遵循相应格式

% 关键词
keywords={'up','down','left','right'};
% 声音频率
fs = 44100;
% 定义文件位置
filePath = strcat('./voice',num2str(fs),'/');

% 训练数据,每行代表一个样本,每列代表一个特征
trainData = [];
% 训练标签,是一个列向量
trainLable = [];
% 训练类
classTag = 1;
for kw = keywords
    k = 0; % 该类别下声音的序号
    fileName = strcat(filePath,cell2mat(kw),num2str(k),'.wav'); % 文件名
    while(exist(fileName,"file") == 2) % 直到文件不存在
        tempData = dataProcess(audioread(fileName),fs); % 读取并处理音频
        trainData = [trainData;tempData]; % 添加训练数据
        trainLable = [trainLable;classTag]; % 添加训练标签
        k = k + 1;
        fileName = strcat(filePath,cell2mat(kw),num2str(k),'.wav'); %下一个文件名
    end
    disp(strcat(kw,num2str(k)));
    classTag = classTag + 1;
end
% 训练并保存SVM模型
voiceModel = fitcecoc(trainData, trainLable);
save('voiceModel.mat',"voiceModel",'-mat');

end
3.收集待分类音频样本并进行分类

voiceClassifier用于实现基于训练好的SVM模型进行语音实时识别的功能,代码流程如下:

1. 定义必要参数,如关键词、采样率等。

2. 初始化麦克风录音设备。

3. 设置能量阈值,加载已训练好的SVM模型。

4. 开始实时循环录音和识别:

   - 从麦克风读取音频数据。

   - 判断音频能量是否超过阈值。

   - 如果超过,则提取语音片段,进行预处理得到测试特征。

   - 将测试特征输入训练好的SVM模型进行分类。

   - 输出分类结果。

function voiceClassifier()
% 定义声音频率
fs = 44100;
% 定义采样参数SamplesPerFrame
SPF = 1024*32;

% 关键词
keywords={'up','down','left','right'};
% 获取麦克风设备信息
% devices = audiodevinfo;

% 创建麦克风输入对象
reader = audioDeviceReader('Device',"Default",'SamplesPerFrame',SPF);
setup(reader);

% 定义触发阈值
thresholds = 0.0008; % 根据实际情况调整阈值
% 加载训练好的SVM模型
voiceModel = load('voiceModel.mat');
voiceModel = voiceModel.voiceModel;

% 开始实时语音识别
disp('开始实时语音识别...');
firstAudio = reader(); % 第一段音频
while true
    % 待提取的二倍长度原始音频
    rawAudio = [firstAudio;reader()];
    % 后半段音频将作为下次的前半段
    firstAudio = rawAudio(SPF+1:end,:);
    % 音频的振幅最高点作为中点
    [~,maxPoint] = max(rawAudio);
    % 将rawAudio振幅最高点的两侧作为判断的音频
    if(maxPoint - SPF/2 >= 1 && maxPoint + SPF/2 - 1 <= size(rawAudio,1))
        audio = rawAudio(maxPoint-SPF/2:maxPoint+SPF/2-1,:);
    else
        continue;
    end
    % 计算音频能量特征
    energy = sum(abs(audio).^2) / length(audio);
    if(energy > thresholds) % 能量超过阈值
%         plot(audio);
%         drawnow;
        % 音频的预处理
        testData = dataProcess(audio,fs);
        % 使用训练好的模型对测试数据进行分类
        [predictedLabels,scores] = predict(voiceModel, testData);
        % 输出测试结果
        if(scores(predictedLabels)>-0.3)
            disp(keywords(predictedLabels));
            disp(num2str(scores));
        end
    end
end

end
4.音频的向量化处理

dataProcess.m用于实现语音信号的预处理以及提取MFCC特征,其同时被voiceTrain和voiceClassifier调用,代码流程如下:

1. 对原始语音信号进行低通滤波,滤除低频噪声。

2. 再进行高通滤波,滤除高频噪声。

3. 调用mfcc函数计算语音信号的MFCC特征。

4. 将提取的MFCC特征矩阵转化为行向量,作为分类模型的输入特征。

function result = dataProcess(voiceData,fs)
% 输入一个声音信号,以行向量形式返回处理结果

% voiceData(abs(voiceData)<0.005)=0;

% MFCC参数个数
num_ceps_coeffs = 40;
% 设置截止频率
fcutLow = 300; 
fcutHigh = 50;

% 调用wavfilter函数对声音信号进行滤波
result = wavfilter(voiceData);
% 调用mfcc函数计算MFCC特征
result = mfcc(result,fs,'NumCoeffs', num_ceps_coeffs);
% 将特征矩阵转换为行向量
result = result(:).';

    function out = wavfilter(signal)
        % out = signal;
        % return;
        % signal = medfilt1(signal);

        % 低通滤波函数
        nyq = fs/2; % 奈奎斯特频率
        wc = 1000/nyq; % 归一化截止频率
        b = fir1(fcutLow,wc); % 设计低通滤波器
        signal = filter(b,1,signal); % 对信号进行滤波

        % 高通滤波器函数
        order = 4;
        [b, a] = butter(order, fcutHigh/(fs/2), 'high'); % 设计
        out = filter(b, a, signal); % 对信号进行滤波
    end
end
三、其它问题

在声音提取的过程中,出现声音采集不完整的情况,这是收集声音采用定时采集定长音频导致的。对此,采取了对相邻两段取有效区间的办法,即每次识别会先在获得的音频前加入前一段区间的音频,然后取振幅最高点为中点,取出音频区间作为判断依据。

采取这一方案后,音频的训练集和测试集样本质量得到较大的改善,识别准确度有了显著提升。

  • 21
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值