
开题先说个实际场景上个月接了个需求要把客服录音里的四川话、东北话、粤语自动分出来再转成文字归档。乍一听就是标准语音识别流程结果用现成的ASR模型一测普通话语料上95%的准确率放到方言音频上直接跌破70%东北话稍微好点粤语和闽南语几乎是灾难现场。后来我把整个流程从数据到模型重新捋了一遍换成基于深度学习的方言识别方案效果才有了质的提升。这篇就完整记录我这次方言识别模型训练的全过程包括为什么通用模型会失效、语料怎么收集和清洗、模型怎么选型、训练时有哪些坑、以及最后落地部署时的评测和优化思路。内容覆盖从0到1搭建方言识别系统的完整链路适合做语音相关项目、需要处理方言数据、或者想入门语音深度学习的读者参考。1. 为什么通用ASR模型一遇到方言就失灵问题定位1.1 模型训练数据的“普通话偏见”先理清一个事实开源社区和工业界的主流ASR模型训练数据主体是标准普通话。以常见的几大开源数据集为例普通话时长占比普遍在90%以上就算加了部分方言口音的普通话也只是带口音不等于方言本身。这意味着模型学到的发音映射关系本质上是一套“普通话声韵调系统”。当输入变成四川话的入声、粤语的声调系统、吴语的浊音阻塞音时模型的特征提取层完全没有对应的映射知识输出自然就开始胡编。这不是模型不够强而是训练分布和推理分布偏差太大。1.2 方言识别任务的两种定义别一开始就搞混很多人把“方言识别”笼统当一个任务实际拆开是两个完全不同的方向方言口音分类Dialect Classification判断一段音频说的是哪种方言属于多分类任务输出是一个类别标签。方言语音识别Dialect ASR把方言音频转写成文字属于序列到序列任务输出是一段文本。这两个任务的难度完全不在一个量级。口音分类在特征层面就能解决一大部分而方言ASR需要处理发音、词汇、语法三重差异。我这篇以方言ASR为主线展开但第3章的模型选型会把两种任务都覆盖到因为实际落地时往往是先分类、再识别做成一个串联流程。1.3 方言差异的三个层次声韵调、词汇、语法理解方言为什么难得从语言学层面看差异在哪里声韵调系统差异粤语有9个声调普通话只有4个吴语保留全浊音声母四川话里平翘舌不分z/c/s和zh/ch/sh混用。这些差异直接改变声学特征的空间分布。词汇差异方言里有大量普通话不存在的词比如四川话的“啥子”、粤语的“猴赛雷”、东北话的“整”这些词在普通话词表里根本没有对应项OOV问题非常严重。语法差异方言的语序和句式也有区别比如粤语说“我走先”普通话是“我先走”这种句法层面的差异是声学模型解决不了的需要语言模型配合。2. 语料收集和标注决定模型上限的第一道关卡2.1 开源方言数据集有哪些怎么选方言数据集的公开资源比普通话少很多但也不是完全没有。我这次调研和试用的几个数据集语言/方言规模参考适用场景THCHS-30普通话带部分口音30小时预训练、基线测试AISHELL-1普通话170小时预训练、基线测试KeSpeech9种方言北京话、东北话、四川话、广东话等几百小时级别多方言ASR任务Common Voice多语种含中文各地方言子集按需提取方言口音分类MAGICDATA方言版多方言朗读语音按需选取方言识别与分类KeSpeech这类多方言数据集是目前比较适合做方言ASR的公开资源但仍有一个问题各方言时长不均衡粤语和四川话的样本明显多于其他方言。如果直接用原始分布训练模型会有严重的类别偏向。我的处理方式是用对数均衡采样logarithmic oversampling做数据重采样让稀有方言的样本权重提高控制每个batch里各方言的比例不要差距太大。2.2 自采数据的标注比想象中更折腾公开数据集不够用的时候自采数据是绕不开的路。但自采数据有几个之前没预料到的问题口音一致性同一个县城不同乡镇的“四川话”发音差异比我以为的大很多。我最初找5个人录了各50条四川话合在一起训练后模型分辨不清楚四川话和重庆话后来细听才发现有一个人实际上是重庆口音标签打错了。这提醒我方言标签必须做事前口音筛选不能自己报了籍贯就直接打标。设备差异手机录音、麦克风录音、电话转接线录音三种设备录出来的信噪比差异巨大。如果混在一起不处理模型会学到“设备特征”而不是“方言特征”换场景就失效。我后来统一做了降噪和响度归一化才缓解了这个问题。标注成本每条音频要确定“说了什么字”和“是什么方言”两个信息。转写部分我最初尝试找外包做但外包人员的普通话转写习惯会导致标注文本和实际发音不一致。后来摸索出相对可行的流程先让标注者用普通话拼音汉字混合转写再由一个懂当地方言的人校对一遍汉字用方言用字拿不准的轻声用拼音标注。2.3 数据清洗和预处理这些细节直接影响收敛速度采集到原始音频后预处理我按以下流程走了一遍每一步都有明确目的采样率统一。所有音频重采样到16kHz这是后端特征提取和预训练模型的统一要求。静音切除。用webrtcvad切掉首尾静音减少无用计算也能避免模型把静音段学习成信号特征。幅度归一化。把音频峰值归一化到-1到1之间防止不同录音设备导致音量差异过大。降噪处理。加了简单的谱减法对电话录音有效果对嘈杂环境录音改善有限但总体利大于弊。长短截断。音频长度统一截断或补齐到10秒以内。方言语音有时候夹杂笑场、停顿这种片段最好直接切掉而不是保留。2.4 数据增强SpecAugment是性价比最高的方案数据增强里我实测下来最有效的是SpecAugment也就是在特征图上做时间掩蔽和频率掩蔽。具体参数我用了10条频率掩蔽带、最大掩蔽宽度参数设为20时间掩蔽最大宽度参数设为40。这个操作强制模型不能在某个固定频率或时间片段上过拟合提升了泛化性。噪声增强我也试过加入随机环境噪声可以把准确率提升1-2个点但前提是噪声音量要控制好。我的经验是SNR在10dB以上才有正面效果低于这个值会污染原本的语音特征。家用环境录的音频加城市交通噪声效果不错但加餐厅嘈杂人声容易把模型带偏。3. 特征与模型选型从Fbank到预训练模型的取舍3.1 输入特征MFCC、Fbank还是原始波形特征选择是第一步。三个方案的对比MFCC压缩了信息保留了倒谱系数适合传统GMM/HMM时代但深度模型下特征信息量偏低。FbankFilter Bank保留更多原始声学细节是当前端到端ASR主流选择我在主实验里用的就是80维Fbank。原始波形理论上可以在网络中自动学习特征但需要足够深的模型和数据量方言场景数据有限实际效果并不理想。还有一个点很容易被忽略特征做了均值方差归一化CMVN后模型训练的稳定性能好很多。我在实践时对每个音频做全局CMVN而不是按数据集做统计这个做法在数据量小的时候更稳。3.2 经典结构CNNRNNCTC仍然是强基线模型结构选型上我对比了几种方案。先做一个强基线ResNet18提取声学时频特征后面接两层BiGRU建模时序关系最后接CTC损失做音素/字级别的对齐。这个结构在普通话ASR里已经是老面孔但在方言场景依然能打。基线模型的训练细节输入80维Fbank25ms窗长10ms帧移网络ResNet18 BiGRU(隐藏层256) Linear(词汇表大小)解码CTC贪心解码优化器Adam初始学习率1e-3warmup 4000步Batch size16梯度累积4步等效batch size 64总训练轮数25轮留10%数据做验证这个基线在KeSpeech子集我用的是四川话、东北话、粤语三类各200小时混合数据上字错误率CER能到30%左右。不算好但作为从0到1的起点足够提供对比参照。3.3 从Conformer到预训练模型真实收益有多大基线之后我把模型升级到Conformer结构这是当前语音识别里相对成熟的backbone。Conformer把卷积的局部建模能力和Transformer的全局依赖能力结合在长序列上的表现比纯RNN好很多。Conformer在同一个数据集上的CER从30%降到了22%左右提升明显。配置文件里的关键参数编码器层数12、注意力头数4、卷积核大小31、前馈层维度512。训练轮数25轮参数量约46M单卡A100约两天跑完。接下来试了预训练模型路线。用wav2vec2.0的代表性中文预训练模型做特征提取后面接一个轻量分类头做口音分类时效果很好几步微调就能到95%以上的分类准确率。但做ASR转写时wav2vec2.0的词汇表相对普通话为主方言词汇OOV严重CER反而不如Conformer直训。Whisper的表现也测了虽然其在多语言上做了预训练但中文方言转写时输出偏向普通话表达方言词汇被强行转成近音普通话用字用于内容归档勉强能用用于方言原文保留就差点意思。3.4 我的最终选型分类用预训练微调转写用Conformer外部语言模型根据实测我最终采用的方案是串并联结构口音分类用wav2vec2.0中文预训练模型微调一个全连接分类头训练10轮准确率95.3%。方言转写用ConformerCTC/Attention联合训练字错误率22%。提升手段在Conformer解码时额外加了N-gram语言模型做外部重打分。语言模型用方言转写文本训练语言模型权重0.6CER又降了2-3个点。方案口音分类准确率方言转写CER训练资源单卡A100ResNetBiGRUCTC86.2%30.1%约4小时ConformerCTC92.4%22.4%约2天wav2vec2.0微调分类95.3%-约3小时Whisper微调转写91.0%25.7%约1天Conformer外部语言模型92.4%19.8%约2天语言模型训练4. 训练过程中的参数调试与避坑实录4.1 学习率设置Poly调度比Cosine更稳训练这类模型学习率策略非常关键。我一开始用Cosine Annealing效果不如预期训练后期loss有时会突然跳动换成Poly策略后稳定不少。Poly路径下学习率从1e-3按幂函数下降幂指数0.7配合1000-4000步warmup。warmup的重要性不能低估因为Conformer里的LayerNorm和自注意力模块在不稳定输入下很容易震荡warmup相当于给模型一个逐步适应的缓冲期我在初期没有加warmup时前2000步loss直接攀升到初始值的一倍加了warmup才正常往下走。一个心法如果loss前几百步降不下来优先检查学习率和warmup而不是换模型结构。4.2 梯度累积和有效Batch SizeGPU显存不够时梯度累积是常用操作。但有一个细节要注意梯度累积4步时BN层和LayerNorm的统计量更新逻辑不一致。BN在累积模式下无法跨batch归一化而Conformer用的是LayerNorm不受这个问题影响这也是我选Conformer而非纯CNN的额外原因。梯度累积时学习率要按照累积后的等效batch size来调。等效batch size从16翻到64学习率如果不从8e-4升到1e-3收敛会偏慢。这个在第一次实验里明显感觉到调整后loss下降曲线顺滑很多。4.3 标签错误导致模型困惑一个耗时一下午的排查方言转写有个隐蔽问题标注文本里的错字会导致模型在特定位置反复出错。有一次训练时我发现模型总是把四川话的“啥子”识别成“沙子”单独抽这些句子的音频出来听标注文本确实是对的“啥子”但声学特征和普通话“沙子”高度接近。后来检查发现这些音频的采集人本身是外省口音四川方言说得不标准被标注员误标成标准四川语料。这件事给我一个教训方言数据质量的关键不在字面标注是否准确而在于发音人的口音是否纯正。在自采数据时必须要有一个懂方言的人把关发音人筛选否则模型学到的是“不标准口音”输出到“标准文字”的映射和目标任务完全相反。4.4 训练资源与时间成本估算很多刚开始接触深度学习的同学会担心算力问题。我在这个项目里实测消耗口音分类wav2vec2.0微调400小时语料V100单卡3小时。Conformer直训400小时语料A100单卡48小时或V100单卡5天。语言模型训练纯CPU2小时。推理Orin NX设备上Conformer小模型实时率0.3完全可跑。如果条件有限建议先用小数据集50-100小时跑通流程确认模型合理后再上全量数据避免一次性开三天训练结果发现前面参数设定有问题。4.5 一个容易忽略的Checkpoint策略我习惯每2个epoch存一次checkpoint并保存验证集CER最低的best模型。有时候训练后期的模型反而会过拟合到普通话腔调方言字词识别率下降所以不能只看最终epoch的结果。我在实验里发现第18轮的一个中间检查点比第25轮final在方言词上CER低4个百分点果断回退使用。条件允许的话建议每轮或每两轮都保存最后做一次验证集整体评估再选最终模型。5. 评测指标体系与错误分析不能只看整体准确率5.1 按方言分组的字错误率比平均CER更关键模型训练完第一件要做的事就是按方言类别拆开评测。我在KeSpeech子集上训练测试了6种方言各自的CER方言CERConformer语言模型四川话16.2%东北话14.8%粤语21.3%闽南语28.6%吴语上海话27.1%长沙话23.9%平均19.8%东北话最接近普通话CER最低粤语因为声调系统复杂和词汇差异大明显更难闽南语和吴语训练数据偏少加上本身发音系统差异更大CER最高。只看平均CER 19.8%会以为系统“还可以”但按类别拆开后发现问题依旧严重。如果业务目标是粤语转写这个效果并不达标。所以评测一定不能只给一个平均分必须拆细。另外口音分类单独测了一套指标6分类的混淆矩阵。四川话和重庆话互相混淆最严重粤语和闽南语偶有混淆东北话和普通话腔之间也有少量误判。分类任务如果业务上允许可以把辨识度低的方言合并成一类比如四川话和重庆话合并成“西南官话”准确率会明显提升。5.2 错误模式分类发音、词汇、切分三类问题我把错误样本人工抽了200条逐一分析错误原因归类后发现三类问题发音近似混淆约45%模型把方言发音映射到了近音普通话词。比如粤语“唔该”转成“无盖”闽南语“拍谢”转成“怕腻”。方言词汇OOV约35%方言特有词汇不在模型的字表里输出被迫拆字或者用普通话音译语义基本丢失。切分错误约20%多说话人、背景音干扰导致模型把两个句子的边界搞混转写结果会出现跨句拼接。针对发音近似混淆我试着在训练时做字级别的混淆增强把拼音混淆程度高的词做随机替换。这个操作有一定效果方言字词错的频率降低了。方言词汇OOV是更根源的问题单纯扩充字表作用有限更好的路径是对方言文本语料做领域自适应语言模型把高频方言词提前拉进解码空间。我在语言模型训练语料里加入了方言词典和短视频平台的方言字幕文本词汇覆盖率提高了不少。切分错误建议单独用VAD模型做分段不要让ASR模型自己场裁判断句子边界。我后来在预处理阶段用Silero VAD先切句再逐句送入识别模型跨句拼接问题基本消失。5.3 推理速度与模型压缩真机部署时必须考虑的训练完看效果是一回事真机落地又是另一回事。我把模型部署到Orin NX设备上初始Conformer模型实时率只能到0.5对实时性要求高的场景不够。压缩路径我做了三个方向的尝试量化FP32转FP16推理加速30%CER基本无变化。INT8量化后模型体积降到原来的1/4实时率到0.3但CER上升了2个百分点还能接受。蒸馏用大Conformer蒸馏到小Conformer编码器层数12降到6CER上升3.5个点但推理速度翻倍。剪枝对前馈层做了稀疏化结合Pytorch的torch.prune实际收益不如量化明显还容易掉点。最终部署方案是6层Conformer INT8量化 FP16语言模型实时率0.25CER约23%比原始大模型高3个点左右但满足设备端实时识别需求。5.4 流式和非流式的取舍这点的选择取决于是否需要做到边说边出字。我做的是录音归档场景延迟不敏感直接用非流式。如果场景是实时字幕或电话客服需要流式推理模型结构要换成Streaming Conformer或Chunk-Attention这会牺牲2-3个点的准确率属于业务场景的成本权衡。6. 把方言识别模型接进实际业务部署架构和优化思路模型训好后整个推理链路用了一个相对简单的流程音频输入节点取到录音文件后先VAD切段再走口音分类模块算出一个概率分布再交给对应方言的ASR解码分支。因为不同方言的发音模型差异大我在解码阶段实际给每种方言单独配了一套解码图和语言模型字错误率比一个通用解码器又降了1-2个点。串并联的好处是分类模块如果判定置信度低于阈值比如0.9可以退回普通话通用识别流程避免方言识别模型在不确定样本上强行输出。这个回退机制在真实噪音环境下帮助很大。如果要快速验证可以用现成的Kaldi或ESPnet框架但它们对自定义数据格式有要求需要花时间学习。如果是切图或者只想跑通Demo建议直接用ESPnet的官方recipe改数据集路径省去很多工程麻烦。我自己用的是PyTorch原生实现灵活度高但工程量确实大不少。最后说一个经验方言识别项目里最容易拖垮进度的不是模型部分而是数据。所以我建议刚开始做的时候哪怕只有50个小时的语音数据也要先把整个训练、评测、部署链路全部走通再慢慢堆数据。数据量上去了模型结构稍微改一改效果就能稳步提升。如果一上来就追求最好的模型结构而忽略了数据质量后面只会越做越难受。