☰
MFCC与GMM语音识别基线:从特征提取到说话人识别全流程解析
2026/10/18 23:09:27 网站建设 项目流程

简介:这份基于MFCC与GMM的语音识别Matlab工程,面向语音信号处理初学者和需要动手实践的科研人员,解决从特征提取到声学建模再到识别的完整流程落地问题。项目共28个文件,以25个m脚本为主,辅以2个mat模型数据文件和1个txt说明文档,压缩包仅1.44MB,轻量易用。核心代码涵盖wave2mfcc、MFCC特征比较、GMM参数估计与分段检测等模块,并附带speakerGmm.mat与speakerData.mat,便于直接验证。已有321人学习下载,适合用Matlab快速复现基于MFCC的GMM语音识别。通过阅读说明文档与运行主脚本,可掌握预加重、分帧、梅尔滤波、EM训练及识别匹配的完整链路,同时理解epdByVol端点检测、gmm_estimate等关键函数的实现思路,是一份兼顾原理与工程的入门参考资料。

1. 从MFCC到GMM:一套能跑的语音识别基线,到底拆出了什么

很多人拿到“基于MFCC的GMM的语音识别”这类项目时,第一反应是“这年头谁还不用深度学习”,但真到落地阶段你会发现,一个不依赖GPU、Matlab原生能跑起来的经典基线,往往比花一周调参的神经网络更实用。这套代码的核心链路很清晰:epdbyvol做端点检测,wave2mfcc提MFCC特征,gmm_estimate训练GMM,MFCC_feature_compare做识别判决,四段串起来就是一套完整的mfcc gmm语音识别流程。适合想搞懂语音识别原理、要做说话人识别小实验、或者需要快速在嵌入式环境验证特征的从业者。拆完这份包,你能拿到的不是炫技代码,而是一套每一步都看得懂、能改、能跑通的参考实现。

2. 特征提取链路:wave2mfcc到底做了什么,参数怎么选不翻车

2.1 从波形到MFCC:预加重、分帧、加窗、FFT、Mel滤波、DCT一锅端

MFCC的出发点很朴素:人耳对频率的感知不是线性的,低频分辨率高,高频分辨率低,所以先按Mel刻度设计一组三角滤波器,把FFT得到的线性频谱压缩成符合听觉感知的频谱包络,再做一次倒谱变换,得到一组低维、相关性低的系数。听起来是标准流程,但代码里最容易出乱子的地方恰恰是顺序和尺寸。我一般会把wave2mfcc的骨架写成下面这样,正好对应项目里的wave2mfcc.m:

% ========== wave2mfcc: 一维语音信号 -> MFCC特征矩阵 ========== % 输入: signal为一维浮点波形, p为mfccParamSet返回的参数结构体 % 输出: mfccs为帧数 × p.numCep 矩阵,第t行是第t帧的MFCC向量 function mfccs = wave2mfcc(signal, p) % 1. 预加重: 一阶高通,弥补声门激励造成的高频能量衰减 signal = filter([1 -p.preEmph], 1, signal); % 2. 分帧: buffer2内部处理帧移和末尾不足一帧的丢弃 frames = buffer2(signal(:), p.frameLen, p.frameShift); % 3. 帧内去直流: 每帧减去本帧均值,避免直流分量污染FFT低频 frames = frameZeroMean(frames); % 4. 加Hamming窗: 减少帧边缘截断带来的频谱泄漏 win = hamming(p.frameLen, 'periodic'); framesWin = frames .* repmat(win, 1, size(frames, 2)); % 5. FFT幅度谱平方得到功率谱,只用单边 spec = abs(fft(framesWin, p.fftLen)); powerSpec = spec(1:end/2+1, :).^2; % 6. 三角Mel滤波器组压缩频谱 melFb = getTriFilterParam(p.numFilter, p.numFftBin, p.fMin, p.fMax, p.fs); melEnergy = melFb * powerSpec; % 7. 取对数 + DCT到倒谱域,再乘倒谱提升窗 logMel = log(melEnergy + eps); % eps防止log(0) mfccs = dct(logMel')'; % 标准DCT mfccs = mfccs(1:p.numCep, :); mfccs = mfccs .* repmat(1 + 0.5*p.lifter*sin(pi*(1:p.numCep)/p.lifter)', 1, size(mfccs, 2)); end

这套顺序里有三处值得细说。

第一,预加重系数0.97是个很稳的经验值,它把频谱整体抬高6dB/octave,不做这一步,训练出的GMM很容易被低频能量主导,识别对辅音和摩擦音特别吃亏。第二,帧内去直流这一步经常被忽略,实际录音总有基线漂移,麦克风偏置会让FFT的0频分量一直很大,不去这个均值,后面Mel滤波器组的低频输出会被污染。第三,日志里取log(melEnergy + eps)而不是log(melEnergy),是因为静音段能量可能精确为0,直接取log会产生-Inf,这个-Inf一旦混进GMM训练,EM的均值方差直接NaN。

2.2 分帧参数:帧长、帧移、fftLen怎么配是一套组合拳

项目里的frame2sampleIndex.m和buffer2.m配合完成了分帧。frame2sampleIndex的核心逻辑是:第i帧的起始采样点是(i-1)*frameShift + 1,结束是start + frameLen - 1。这个映射看起来简单,但它决定了你后续做特征对齐、手工标注、端点检测回溯时,能不能精确定位到原始波形的某个位置。

常见的参数组合是:8kHz采样率下,帧长256点(32ms),帧移80点(10ms),FFT点数512。16kHz采样率下,帧长400点或512点(25~32ms),帧移160点(10ms)。参数表格化的意义在于,训练和识别必须共用同一份配置,否则训练时用的是32ms帧,识别时换了25ms帧,GMM打分的分布直接错位。

参数8kHz电话语音推荐16kHz宽频推荐说明
frameLen256400~512帧长过短频率分辨率差,过长违背短时平稳假设
frameShift80160帧移10ms是语音识别的事实标准
preEmph0.970.97高通预加重系数,改到0.9以上区别不大
numFilter2424~40Mel滤波器组数量,24是MFCC常用起点
numCep1313~20说话人识别常用13维,再加delta特征时用39维
fMax3400fs/2 * 0.85电话信道只有300~3400Hz带宽,设高会引入噪声

mfccParamSet.m把这堆参数集中成一个结构体,这个设计非常关键。很多人在语音识别上栽跟头不是因为算法,而是训练和识别各写了一套参数,最后靠玄学调参。把参数集中管理,至少能保证链路参数一致这个最基本要求。

2.3 端点检测epdByVol:先剪掉静音,再谈特征

端点检测是整个链路的第一个“黑匣子”。项目里的epdByVol.m走的是短时能量路线:先分帧,再算每帧的能量,能量高于某个阈值的帧判为语音帧。代码逻辑大致如下:

% ========== epdByVol: 基于短时能量 + 双门限的端点检测 ========== % 输入: signal波形, p为epdParamSet参数 % 输出: segs为[起始帧 结束帧; ...],每一行是一个语音段 function segs = epdByVol(signal, p) % 逐帧计算短时能量 vols = frame2volume(signal, p.frameLen, p.frameShift); % 双门限: 高阈值确认语音,低阈值扩展边界 hiTh = p.hiRatio * max(vols); loTh = p.loRatio * max(vols); isSpeech = vols > loTh; % segmentFind把离散的语音帧粘连成连续段,并做最短时长过滤 segs = segmentFind(isSpeech, hiTh, vols, p.minSpeechFrames); end

这里我一般会把hiRatio设成0.2~0.3,loRatio设成0.05~0.1。高阈值负责确认“这里一定有语音”,低阈值负责把语音段的边界向外扩,避免把字首字尾的弱音切掉。segmentFind的作用是粘连:把连续超过minSpeechFrames(比如3帧)的语音帧拼成一个完整段,太短的孤立高能量帧直接丢弃,那些往往是开关噪声或点击声。

端点检测做得粗,后面GMM学到的就不是语音分布而是“录音环境分布”。训练模型时混入大量静音帧,识别时必然在静音段给出莫名的高分,这个坑我在后面专门展开。

3. GMM训练与EM迭代:gmm_estimate在估计什么,K值怎么定

3.1 为什么GMM适合做语音识别里的声学模型

GMM的直觉理解是这样的:一个说话人的发音特征在特征空间里不是紧成一团的,同一个音素会因语速、音调、上下文不同呈现多峰分布。单个高斯模型只有一个峰,描述不了这种复杂形态,GMM用K个加权高斯成分叠加去逼近真实分布,K就是峰的个数取KaTeX parse error: undefined control sequence: \,此处不展开。项目里gmm_estimate.m负责估计每个高斯成分的权重、均值、协方差,这组参数就是说话人的声学指纹。

K值的选择直接影响模型容量。K设太小,模型表达力不够,不同说话人的分布混在一起分不开;K设太大,参数爆炸,训练数据不够时每个成分只拿到几十帧,协方差矩阵估计不准,反而过拟合。我的经验:纯做孤立词识别,每个词一个GMM时,K取8~16就够;做说话人识别,每人一个GMM,K取16~32是性价比最高的区间;数据量很大的时候可以推到64。判据很简单,训练完看测试集识别率,K翻倍之后识别率提升小于1%,就说明K到头了,再大只是在拟合噪声。

3.2 EM算法:E步算后验,M步更新参数,循环到收敛

GMM没有解析解,只能靠EM迭代逼近。E步用当前参数计算每一帧特征属于每个高斯成分的后验概率γ,M步按γ加权重新估计均值和协方差。工程上最怕的是概率乘积下溢:当K=32、特征维度13维时,单帧对单个高斯的似然值可能是10^-20这个量级,直接乘会变成0,因此所有计算都要在log域做。项目里lmultigauss.m这个名字直译就是“log多变量高斯”,它的职责正是把E步的似然计算搬到log域:

% ========== lmultigauss: 单帧特征对单个GMM的log似然 ========== % x是当前帧特征向量(1×D), w/mu/sigma是GMM参数 % 输出logLik为标量log P(x | GMM) function logLik = lmultigauss(x, w, mu, sigma) K = length(w); D = numel(x); logLik = -Inf; for k = 1:K diffK = x - mu(k, :); % 用Cholesky分解算行列式,比det()数值稳定得多 L = chol(sigma(:, :, k), 'lower'); logDet = 2 * sum(log(diag(L))); % 马氏距离平方,注意用L求解线性方程而非直接求逆 quad = sum((L \ diffK') .^ 2)'; logN = -0.5 * (D * log(2 * pi) + logDet + quad); % log-sum-exp合并,避免直接加exp()导致下溢 if logLik == -Inf logLik = log(w(k)) + logN; else logLik = logsumexp(logLik, log(w(k)) + logN); end end end

注意这里的quad用的是L \ diffK'而不是diffK * inv(sigma) * diffK',原因是直接求逆在协方差近奇异时会得到极不稳定的马氏距离,而Cholesky分解求解线性方程组是业界通用做法。logsumexp在手写时会写成maxVal + log(exp(a - maxVal) + exp(b - maxVal))这套evaluate式,很多Matlab初学者把log(w(k)) + logN误写成w(k) * exp(logN),等于把精度丢回线性域再取对数,白做了log域搬移。

有了逐帧的log似然,EM的E步和M步就能落地了。gmm_estimate.m的内部循环可以做这样的抽象:

% ========== gmm_estimate: EM估计GMM参数(w, mu, sigma) ========== % X: T×D训练特征矩阵, K: 高斯成分数, itLim: 最大迭代次数 function [w, mu, sigma, logLHist] = gmm_estimate(X, K, itLim) [T, D] = size(X); % ---- 初始化: 绝不建议随机初始化 ---- % 常见做法是k-means聚类,用聚类中心当初始均值 idx = kmeans(X, K, 'Replicates', 3, 'MaxIter', 80); for k = 1:K mu(k, :) = mean(X(idx == k, :), 1); covK = cov(X(idx == k, :)); % 协方差加正则项,防奇异矩阵,1e-6到1e-4是常见量级 sigma(:, :, k) = covK + 1e-5 * eye(D); w(k) = sum(idx == k) / T; end % ---- EM迭代 ---- logLHist = zeros(1, itLim); for it = 1:itLim % E步: 逐帧算log似然,再归一化为后验 gamma = zeros(T, K); logLik = zeros(T, K); for t = 1:T for k = 1:K logLik(t, k) = lmultigauss(X(t, :), w(k), mu(k, :), sigma(:, :, k)); end % log-sum-exp归一化得到后验概率 maxLL = max(logLik(t, :)); logNorm = maxLL + log(sum(exp(logLik(t, :) - maxLL))); gamma(t, :) = exp(logLik(t, :) - logNorm); end totalLogL = sum(max(logLik, [], 2) + log(sum(exp(logLik - max(logLik, [], 2)), 2))); logLHist(it) = totalLogL / T; % 平均log似然,跨数据集可比 % M步: 按后验加权更新参数 Nk = sum(gamma, 1); w = Nk / T; for k = 1:K mu(k, :) = (gamma(:, k)' * X) / Nk(k); diffX = X - mu(k, :); sigma(:, :, k) = (diffX .* gamma(:, k))' * diffX / Nk(k); sigma(:, :, k) = sigma(:, :, k) + 1e-5 * eye(D); % 每轮都加正则 end % 收敛判据: 平均log似然增量小于1e-3就停 if it > 1 && abs(logLHist(it) - logLHist(it - 1)) < 1e-3 break; end end end

这段逻辑里有三个容易被忽视的细节。

第一是初始化,用k-means而不是随机选帧当初始均值。随机初始化在K较大时极易陷入局部最优,表现是训练过程中log似然曲线先升后降或长期横盘,k-means初始化基本能一次收敛。第二是协方差正则要加在每次M步之后,而不是只在初始化时加一次。EM每次更新sigma后,某些成分因为后验权重Nk很小,协方差会退化成半正定矩阵,不加正则下一轮E步的chol()直接报错。第三是收敛判据用平均log似然的增量,而非累计log似然。累计值随帧数线性增长,不同说话人训练集大小不同时没法比较收敛程度。

3.3 训练时的log似然曲线怎么看

把logLHist画出来,正常情况是一条上升后走平的曲线,前5~10次迭代涨得很快,后面逐渐平稳。如果你看到曲线锯齿状上下跳,优先检查是不是有帧的特征向量里出现了NaN或者Inf,多半是前面log(melEnergy + eps)里的eps被改成了0,或者是某个特征维度在整个训练集上方差为0。还有一个临床经验:如果K取得太大,某些成分的后验概率一直趋近于0,对应的高斯成分在M步里拖着不更新,log似然曲线会在中途出现一个平台期,拉起后继续上升。这不是bug,是EM在重新分配成分,耐心等它走完即可。

4. 识别判决:MFCC_feature_compare怎么打分,阈值怎么定

4.1 从speakerData到speakerGmm的工程组织

项目里的speakerData.mat存的是原始说话人数据,speakerGmm.mat存的是训练好的GMM参数。这组命名暴露了一个好的工程习惯:数据和模型分离。训练流程应当遵循三步:先从speakerData.mat里按说话人标签切出各自的语音段,再对每个人跑wave2mfcc提取特征,最后调用gmm_estimate训练出这个人对应的GMM,汇总存入speakerGmm.mat。

实际操作中,说话人标签的组织方式通常是mat文件里一个结构体数组:speakerData{1}.wave、speakerData{1}.label,或者用文件夹命名区分说话人。GMM训练完,speakerGmm.mat里应该保存gmmSet结构体,每个元素包含w、mu、sigma三个字段,外加一个spkName字段记录身份。识别时只加载模型文件,不碰原始数据,这是工程上最基本的数据流边界。

4.2 逐帧打分:为什么除法比求和关键

识别阶段的核心函数是MFCC_feature_compare.m,它的任务是拿一段测试语音的MFCC特征序列,对每一个说话人的GMM计算得分,得分最高的就是识别结果。逐帧打分的代码骨架如下:

% ========== MFCC_feature_compare: 测试特征对多个GMM打分 ========== % mfccTest: 测试语音的MFCC特征矩阵(帧数×D) % gmmSet: speakerGmm.mat里加载的结构体数组 % 输出spkId是判定的说话人编号,scorePerGmm是每个GMM的平均log似然 function [spkId, scorePerGmm] = MFCC_feature_compare(mfccTest, gmmSet) nSpk = numel(gmmSet); nFrames = size(mfccTest, 1); scorePerGmm = zeros(nSpk, 1); for spk = 1:nSpk frameLL = zeros(nFrames, 1); for t = 1:nFrames frameLL(t) = lmultigauss(mfccTest(t, :), gmmSet(spk).w, ... gmmSet(spk).mu, gmmSet(spk).sigma); end % 关键: 用mean而不用sum,消除时长带来的不公平优势 scorePerGmm(spk) = mean(frameLL); end [~, spkId] = max(scorePerGmm); end

这里mean(frameLL)就是全篇最容易踩坑的地方。如果你用sum(frameLL),一段2秒的录音天然比0.5秒的录音多积累4倍的log似然,哪怕它们来自同一个人同一个GMM,长录音也必然“看起来更像”目标说话人。实际工程里测试语音时长不可能一致,用均值就是给所有说话人的分数做时长归一化。项目在说话人识别场景下这么设计是对的,但如果做的是孤立词识别,同类别下所有训练样本长度接近,这时sum和mean差别不大,依然推荐用mean,因为新样本大概率不在训练时的时长范围内。

打分逻辑说完,还有一个容易忽略的问题:特征的维度和顺序必须和训练时完全一致。训练时用了13维MFCC,识别时如果静音帧没剔掉、占了一个帧位,lmultigauss里的D = numel(x)会变成14或12,Matlab不会报错但矩阵运算尺寸不匹配会让你排查半天。这在第5章里再展开。

4.3 阈值与拒识:识别不准不代表不能判

多说话人识别遇到“这个人不在模型库里”的情况很常见。GMM对所有已知说话人打分,分数最高的那个即使完全不匹配,也会被当作结果输出。所以要加一个拒识阈值:maxScore - threshold > 0才接受识别结果,否则判定为“未知说话人”。这个阈值怎么定?留一段已知说话人的录音反复测,记录每个正确匹配的最低分和每个错误匹配的最高分,阈值取两者之间中点再略偏宽松一侧。你可以把它理解成给识别器画一条安全线:线内我认,线外我拒绝。阈值越低,误识率越高;阈值越高,拒识率越高,这个折中完全看业务容忍哪种错。

5. 避坑与排查:训练翻车、分数不平、端点检测不干净的五个典型问题

5.1 训练不收敛,log似然曲线锯齿状跳

现象:gmm_estimate跑完,logLHist画出来不是平滑上升,而是每3~5次迭代上下剧烈摆动,甚至出现NaN。

原因:常见于K值偏大、k-means初始化没有生效,或者用随机初始化;另一个高频原因是MFCC特征里有Inf/NaN混入,通常是log(0)、DCT后出现极大值、数据文件里录了一段纯静音。

解决:第一,先检查特征矩阵any(isnan(X(:)))和any(isinf(X(:))),有就定位到具体帧删除;第二,把初始化改成k-means聚类,Replicates设成3以上让Matlab多次尝试取最优;第三,协方差正则系数从1e-5往上调一个量级看曲线是否稳定。按这个顺序排查,90%的训练发散问题都能在15分钟内定位。

5.2 lmultigauss返回NaN或Inf,识别程序直接崩

现象:训练一切正常,到识别阶段,调用lmultigauss时返回NaN,scorePerGmm里全是NaN,max函数选出个莫名其妙的下标。

原因:马氏距离计算时协方差矩阵奇异,或者特征向量里含NaN。协方差奇异多半是某个说话人的训练数据太少,某几个高斯成分的后验权重趋近0,M步更新出的协方差行列式为负。

解决:两条腿走路。一是给所有sigma加正则项并设置下限,每次M步之后sigma = sigma + 1e-4 * eye(D)强制可逆;二是在lmultigauss里对diffK分量加一个极小值保护,比如diffK = abs(diffK) < 1e-10 ? 0 : diffK。前者治本,后者是保命用的兜底逻辑。

5.3 端点检测把静音当语音,GMM学了个噪声分布

现象:训练集中混入了大量静音帧,模型在测试时对“背景噪声段”给出极高似然,识别正确率不升反降。

原因:epdByVol里loRatio设得太低,比如0.01,环境底噪的能量都超过了阈值;或者录音本身信噪比很低,能量检测法天然失效。

解决:核对epdByVol处理后的语音段时长,正常语音段的时长占比应该在20%~60%之间。同时加一个最短帧长的限制,比如minSpeechFrames=5,低于5帧的片段大概率是瞬时噪声,直接丢弃。另外,如果录音环境噪声稳定,可以在epdParamSet里把高阈值和低阈值改成基于绝对能量而不是相对最大能量,避免“录音越响阈值越高”的自适应陷阱。

5.4 某几个说话人永远得分高,识别结果偏向他们

现象:训练数据里有5个人,识别测试时其中2个人轮流霸榜,即使测试语音是第3个人的。

原因:GMM打分对特征的绝对幅度敏感。嗓门大的人录音能量高,MFCC整体幅值就大,那么log似然里的log(2π)项固定而马氏距离变小,分数天然偏高。这是最常见的“特征幅值绑架模型”问题。

解决:在特征层面做倒谱均值减除(CMS),把每一维MFCC减去整段语音该维的均值,消除说话人响度差异和信道偏移。这个操作在代码里就是mfccs = mfccs - mean(mfccs, 1),三行代码,效果立竿见影。另一个辅助方案是训练时对每人录音做RMS归一化,让所有说话人语音段的平均能量一致。

5.5 训练集上识别率95%,换话筒测试直接掉到60%

现象:在同一个录音环境、同一个麦克风下测试一切正常,换一个会议室或者换个耳机麦克风,识别率断崖式下跌。

原因:GMM学到的特征分布里混入了麦克风的信道响应和房间混响。麦克风频响差异会让MFCC低频段整体偏移,房间混响则扩散了共振峰位置,GMM没见过这种分布自然无法正确匹配。

解决:数据层面做轻量级数据增强,对训练语音做随机音量抖动和少量高斯噪声叠加,让GMM对信道变化不那么敏感;特征层面,CMS能消除一部分线性信道偏差,但对非线性失真无用。最稳妥的方案是测试环境采集少量录音做模型自适应,用EM再做几步迭代把已有模型参数向新环境微调,这比重新训练成本低一个量级。

6. 验证与进阶:分文件交叉验证和阈值校准,让识别结果可信

整套模型训练完,最后要做的事是验证,不是直接拿测试集跑一遍看个准确率就完事。我每次都会写一个留一文件交叉验证的脚本:每名说话人录4条语音,训练时用其中3条,留1条当验证,4轮轮换,最终准确率取4轮平均。这个脚本的骨架极简单,但能暴露很多问题,比如某个说话人某条录音质量特别差,单独留出来测试时准确率掉得离谱,那你就要考虑是不是该把这条录音清洗掉。

% ========== 留一文件交叉验证: 评估GMM识别泛化能力 ========== % dataPerSpk为结构体数组,每个元素含多条语音 load('speakerData.mat'); nSpk = numel(spkData); accSum = 0; nFold = 4; for fold = 1:nFold gmmSet = struct([]); for spk = 1:nSpk idxTr = setdiff(1:nFold, fold); % 留出fold当作验证 featsTr = []; for f = idxTr mf = wave2mfcc(spkData(spk).wave{f}, mfccParamSet()); featsTr = [featsTr; mf]; end [w, mu, sigma] = gmm_estimate(featsTr, 16, 50); gmmSet(spk).w = w; gmmSet(spk).mu = mu; gmmSet(spk).sigma = sigma; gmmSet(spk).spkName = spkData(spk).name; end % 用留下的fold做测试 for spk = 1:nSpk mfTest = wave2mfcc(spkData(spk).wave{fold}, mfccParamSet()); [id, ~] = MFCC_feature_compare(mfTest, gmmSet); if strcmp(gmmSet(id).spkName, spkData(spk).name) accSum = accSum + 1; end end end fprintf('平均识别率: %.1f%%\n', accSum / (nSpk * nFold) * 100);

这段脚本里有个细节值得注意:训练特征拼接时只做了简单纵向拼接,没有做帧级打乱。GMM训练并不要求帧顺序,所以打不打乱对结果影响很小,这个设计反而保留了“每条录音完整语义”的一致性。交叉验证跑完,如果单轮准确率波动超过15%,基本能断定某条录音有问题,要么端点检测没剪干净,要么录音里有大段噪声。

阈值校准是另一个收尾步骤。拿验证时的所有得分画一个箱线图,正确匹配的分数和不匹配的分数如果分布重叠严重,说明特征区分度不够,这时调阈值只是亡羊补牢,真正要回头加delta特征或者换特征选择。如果两个分布分界明显,就在分布中点偏零的方向设阈值,形成拒识安全边界。

这套流程跑下来,我对模型是否可信心里有数了。从那以后,我每次做语音识别Demo,都强制自己先过一遍留一文件交叉验证,再把阈值画出来,绝不直接拿一个测试集给结果。坑踩得多了,你会明白语音识别里最值钱的不是模型结构,而是验证流程和特征质量这两件基础功。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询