语音识别中的语言建模:从N-gram到神经网络重打分

发布时间:2026/10/16 13:00:52
语音识别中的语言建模:从N-gram到神经网络重打分 开篇为什么我劝你别跳过语音识别里的语言建模每次看到新人上手语音识别第一件事就是冲去训练声学模型恨不得把注意力机制、Transformer的变体都试一遍。然后就卡在了一个尴尬的问题上声学模型跑的还不错词错误率却居高不下怎么调都降不下来。直到后来有一位前辈点醒我——你根本没好好看语言建模那一章。这句话我记到现在。标题里的Chapter1-7_Speech_Recognition(Language Modeling)其实就是整套语音识别课程里专门讲语言建模的部分。如果你正在走这条路那你大概率已经意识到语音识别不只是听懂声音更是猜对文字。声音信号进来之后候选的拼音串可能有一堆怎么从中挑出最像人话的那一个这就是语言模型的工作。这篇文章我会从语言模型在语音识别中的定位讲起接着对比N-gram和神经网络语言模型的原理、实操训练流程再聊到解码器集成、工程部署的踩坑记录。适合刚学完声学模型、正准备向全链路理解迈进的开发者也适合那些做语音交互产品、想知道为什么识别结果总是不通顺的工程师参考。1. 整体设计语言模型到底在语音识别里干哪份活1.1 从拼音串到文字串的最后一公里语音识别的全链路可以粗略分成前端信号处理、声学模型、语言模型和解码器四个环节。前端负责把麦克风采集的波形转成特征向量声学模型负责把特征向量对应到音素或字符的候选序列这一步的输出往往是一堆带分数的候选路径。如果到这里就停住你会得到类似jintian tianqi zhenhao这种拼音串甚至带音调错误。怎么把它变成今天天气真好靠的就是语言模型。语言模型解决的其实是这样一个问题给定前面已经出现的词序列下一个词最可能是什么。典型公式是给定历史词 ( w_1, w_2, ..., w_{n-1} )计算 ( P(w_n | w_1, ..., w_{n-1}) )。在语音识别解码时声学模型提供听起来像什么的分数语言模型提供像不像人话的分数两者按权重融合共同决定最终的识别文本。这里有个很关键的心态变化语言模型不是配角它是和声学模型平起平坐的得分者。尤其在中文这种词边界不明显、同音字特别多的语言里语言模型的作用甚至比声学模型更能决定识别结果的可用性。比如gongsi可以是公司也可以是公式声学模型大概率给出一模一样的发音分数最后谁胜出完全看语言模型更倾向于哪个词在上下文中出现。1.2 语言模型选型之前先想清楚三件事在我实际做项目的经验里选语言模型方案前必须先回答三个问题。第一你的识别领域是开放域还是垂直域。开放域比如会议转写、视频字幕词表要够大语言模型要能处理各种口语表达。垂直域比如医疗问诊、法律文书、客服质检词表相对收敛但领域内的专有名词和习惯搭配必须覆盖到位否则识别结果在领域里完全没法看。第二你的训练数据规模有多大。如果只有几百万字的文本老老实实用统计语言模型可能更稳因为神经网络模型在数据量不足时容易出幻觉式的流畅——句子读着通顺但和声学模型对齐后反而产生更多错误。如果有上亿字甚至几十亿字的文本那就值得上Transformer语言模型它的建模能力和泛化能力会比N-gram高一个台阶。第三你的部署环境算力受限到什么程度。N-gram模型可能只要几十MB内存在低端设备上跑得飞快。大参数量的神经网络语言模型虽然效果好但推理延迟和内存占用都是实打实的成本。有些嵌入式方案甚至完全放弃神经网络语言模型只靠一个精心修剪的N-gram在设备端干活。这三点想清楚你再去看语言模型的种类就不会眼花缭乱。说白了没有绝对最好的模型只有当前场景里性价比最高的选择。1.3 从课程章节倒推工程实践重点既然标题里出现了Chapter1-7那就沿着这个章节感说说实际教学过程里最常见的误区。很多教程会把语言模型讲成一个独立的模块讲完N-gram公式再讲一下困惑度Perplexity就结束了。但工程实践根本不是这么回事语言模型必须放到解码器里看效果。我记得自己第一次在开源语音识别工具上做实验训了一个很漂亮的N-gram困惑度降到了非常好看的数字。结果一接进解码器识别效果反而比不带语言模型还差。排查了很久才发现是语言模型的剪枝太激进很多词组路径被剪掉了声学模型本来能靠上下文拉回来的候选也被强制砍掉。这给我上了很深刻的一课语言模型的评价指标不是困惑度而是最终的字错误率或词错误率。所以这篇文章后面我不会只停留在教科书式的讲解更多是把整个训练、调参、集成、部署的链路拆开揉碎给你看真实项目里每一步应该怎么做。2. 核心细节从N-gram到神经网络语言模型我建议你两条腿走路2.1 N-gram语言模型简单到极致却也依然扛打N-gram的基本假设是马尔可夫性一个词出现的概率只依赖前 ( n-1 ) 个词。二元的叫作Bigram三元的叫作Trigram在实际语音识别里用得最多的就是3-gram或4-gram因为阶数再往上涨数据稀疏问题会越来越严重。训练过程说穿了就是统计条件频次。比如训练语料里有1000次今天 天气其中800次后面跟着真好那 ( P(真好|今天, 天气) ) 就等于0.8。这个办法直观、靠谱、训练极快几十亿字的语料也能在合理时间吃完。但N-gram有两大致命伤。第一个是数据稀疏如果某个三元组在训练语料里一次都没出现过概率直接变零解码时这个路径就得被砍掉。解决办法是平滑技术常用的有Katz回退、插值、Kneser-Ney平滑。Kneser-Ney是我个人用得最多的它对尾部特征的处理非常出色能让低频但合理的词组保留下来。第二个是长距离依赖缺失我昨天去了银行把利息结算了这种句子如果银行和利息之间的距离超过N-gram的窗口模型就看不到关联了。2.2 神经网络语言模型用向量表示解决泛化问题神经网络语言模型的核心思路是给每个词学一个向量表示词的相似度由向量空间里的距离来衡量。这样即使某个Tri-gram没有出现在训练集里只要构成它的词在向量空间中与某些常见组合接近模型依然能给出合理的概率估计。这是N-gram做不到的泛化能力。从最早的前馈神经网络语言模型到RNN、LSTM再到现在主流的Transformer建模能力一直在涨。在语音识别解码时神经网络语言模型最大的问题是速度。解码器每展开一个词都要向前推理一次如果模型是几十层的Transformer那个计算量在实时解码场景里基本撑不住。所以业界的主流做法是两条线并行一条线用N-gram做解码时的主力模型另一条线训练一个强大的神经网络语言模型对N-gram解码出的N-best列表做重打分。重打分的意思是把N-gram生成的前几十条候选用神经网络语言模型重新计算句子级别得分重新排序后再选出最终结果。这个方案既保住了实时性又能吃到神经网络模型的精度红利。我自己在项目里就是用这种方法把中文会议转写的字错误率相对降了12%左右且延迟增量可以忽略不计。2.3 中文场景的语言模型必须要处理的几个细节中文和英文不一样词和词之间没有天然空格所以训练语言模型之前必须分词。分词质量直接影响N-gram的统计效果我踩过不少坑。比如专有名词被切碎深度学习被切成深和度学习那语言模型就学不到深度学习作为一个整体应该出现的概率了。行业里常用的一些分词工具对着通用语料效果不错但到了垂直领域就露馅。我的经验是先用通用工具分词再叠加一份领域自定义词典把医学、法律、金融里的术语、人名、地名强制合并。词典的维护也是个持续过程——上线后每发现一个领域专名被切错就补进去半个月下来词典会越来越完善。对于乱码和噪声文本我的建议是训练语言模型之前做一次强力的文本清洗。网络爬虫拿到的文本里有一堆广告、表情、无效字符不洗的话模型会把噪声当成正常内容学进去。常见的清洗流程包括统一全半角、过滤超长或超短行、去除非中文和非标点字符、按标点切分长句。这个步骤花费的时间不多但对最终效果的影响非常直接。3. 实操过程从语料准备到N-gram训练的完整流程3.1 语料的收集与选择逻辑语音识别里的语言模型训练语料来源很杂。公开的通用语料、爬取的新闻网页、垂直领域的文档资料、已有识别结果的反哺文本都可能作为来源。但有个原则我始终放在第一位语料的领域分布要贴近你的真实使用场景。举个例子如果做的是会议转写那语料来源就应该是会议纪要、新闻稿、演讲稿、访谈记录而不是小说或诗歌。如果手头的目标场景是智能客服那就得找大量客服对话记录、常见问题FAQ、投诉信件等。语料领域贴合得越好语言模型的收益越明显。数据量方面我给一个不严谨但实用的参考线通用领域至少准备5000万字以上的清洗后文本垂直领域如果数据实在稀缺1000万字也能做出一版能用的模型但需要配合领域词典和使用更平滑的处理方式。这不算什么严格标准纯粹是我做了多个项目之后的体感。3.2 训练一个N-gram模型的实操步骤这里我就以我常用的开源工具链为例说一遍完整过程。先准备一份已经分好词的训练文本一行一个句子词之间用空格隔开。看起来像这样今天 天气 真 好我 打算 去 公园 散步今天 出门 记得 带 雨伞然后是模型训练。用SRILM可以这样跑ngram-count -text train_seg.txt -order 3 -lm train.arpa -kndiscount -interpolate参数里-order 3指定的是3-gram-kndiscount用的是Kneser-Ney折扣平滑-interpolate表示插值。训练完后会得到一个ARPA格式的语言模型文件里面按阶数记录了各种词串的概率和对数概率回退权重。接下来必须做的步骤是剪枝。原始ARPA模型文件可能很大如果不剪枝手机端或者低配服务器直接加载都会吃力。SRILM的剪枝命令ngram -lm train.arpa -prune 1e-8 -write-lm train_pruned.arpa这个1e-8是一个经验值。阈值设得越大剪掉的n-gram越多模型越小副作用是困惑度变差。我一般会从1e-7开始往下调每剪一次就在验证集上算一下困惑度找到一个模型体积和效果都还能接受的平衡点。如果剪完以后发现特定领域的识别明显变差就说明剪枝过度了得放宽阈值。最后在验证集上算困惑度ngram -lm train_pruned.arpa -ppl dev_text.txt这里输出的perplexity是一个参考指标但就像前面说的它不能完全代表解码效果只能用来快速判断训练流程是否正确。3.3 解码器集成的权重调优语言模型训练好之后要接进解码器。大多数语音识别工具里都有两个关键参数一个是语言模型权重语言模型分数在综合得分里的权重另一个是词插入惩罚控制模型倾向多输出词还是少输出词。这两个参数是需要联调的。我的调参流程比较笨但很有效准备三五百条带标准转写的音频固定测试集不变然后网格搜索。先固定词插入惩罚为0把语言模型权重从5扫到30每档都跑一遍测试集找到字错误率最低的区域再在这个区域里固定权重扫词插入惩罚两轮下来基本能定位到较优组合。有一个我反复踩过的坑换了一个新语言模型之后原来的权重参数不能直接沿用。N-gram和神经网络重打分模型的最优权重差异很大因为我后来用的神经网络语言模型打出的分数分布和N-gram完全不是一个量级如果不重新调权重效果会非常差。每次换了模型老老实实重新做一轮参数搜索。3.4 神经网络语言模型的重打分流程如果你决定走N-best重打分的路线流程大概是这样的第一步用基础的N-gram语言模型解码输出每一条音频的前100条候选句子连同各自的声学分数和N-gram分数一起保存下来。候选条数是可以调节的候选越多重打分越有机会翻盘但后端计算量也越大我常用的是100到200条。第二步写一个批量推理脚本把候选句子逐条送进训练好的神经网络语言模型算出句子级别的对数概率。这里要注意算的是整个句子的总概率不是最后一个词的条件概率。第三步把声学分数、N-gram分数、神经网络语言模型分数按权重融合[ S_{final} S_{am} \lambda_1 S_{ngram} \lambda_2 S_{nnlm} ]这里的 ( \lambda_1 ) 和 ( \lambda_2 ) 是权重需要调。我见过一种更实用的变体调权重的时候直接用开发集做一次小的网格搜索确定最优组合。公式本身不重要重要的是融合之后重新排序取最高分数的候选作为最终输出。我之前在项目里用了一个基于Transformer的几十层语言模型做重打分输入是分词后的整句输出是句子的对数概率。整条音频的候选重打分耗时大概是音频时长的十分之一完全能接受。4. 避坑记录我在语言建模项目里踩过的实际坑4.1 语料清洗不彻底导致的风格偏移我第一次做会议转写项目时直接从网上爬了一堆新闻稿来补语料心想着数据多多益善。模型训练出来之后单独在测试集上跑一开始看指标确实不错但到了真实会议录音里识别出来的文本带着一股新闻腔很多口语化的词全都被修正成书面语了。比如发言人明明说那个啥识别结果硬是给变成了那些什么反而是错的。原因就在于新闻语料占比太高语言模型把新闻风格的概率建模成了主风格。后来我把口语语料和书面语料的比例做了调整并且专门在语料里加入了大量带口语标记的会议记录问题才缓解。这里提醒一下语言模型学的不只是词的概率还有整个语料的风格和语气语料比例一定要控制好。4.2 词表版本不一致引发的解码崩溃还有一件让我记忆犹新的事。有一版语言模型训练时用的词表和之后某次更新分词汇编时用的词表不一致了导致部分词在解码阶段查不到直接回退成了单字拼接。那一次测试集上的字错误率暴跌所有识别结果里出现了大量莫名其妙的字组。排查了两天才发现是分词工具升级之后同一个词被切成了不同的片段。从那以后我定了一条强制规范每次训练语言模型前都要记录下当前的分词器版本和自定义词典版本解码器里用的分词版本必须和训练时完全一致。如果词典有更新语言模型必须重新训练或做增量更新不能只换词典不换模型。4.3 重打分阶段的数据泄漏神经网络语言模型做重打分训练数据的划分也有讲究。如果用于训练重打分模型的文本里包含了测试集的转写内容那测试集的分数会被虚高看起来效果很好实际上线就崩。这个问题在学术论文里都经常被忽视工程上更容易踩。我的处理方式是把所有语料按时间戳或者随机方式划分确保训练、验证、测试三份数据完全不重叠。语音识别里还有一个额外的坑同样的句子可能出自多个说话人去重的时候要把完全相同的句子去掉否则仍然存在泄漏风险。4.4 模型一接进实时系统就超时N-gram的推理速度很快但如果你做了更复杂的神经网络重打分实时性就得认真考量。有一次我在服务器上测得很流畅结果接到线上发现并发一高重打分任务排队严重接口响应直接超时。后来我加了缓存机制把高频句子的重打分结果缓存起来同一个句子重复出现时直接命中缓存。对于语音识别来说实际的文本分布往往集中在一些高频句子上缓存命中率可以做到很高。如果还嫌慢就用批量矩阵推理一次把一个batch的候选句子全部输入模型而不是一条一条地feed。5. 回归本质语言建模的评估与迭代策略5.1 别被困惑度骗了前面已经提到过困惑度再展开说一点。困惑度衡量的是语言模型对验证集的预测能力数值越低代表模型越自信。但自信不代表正确尤其当语言模型和声学模型出现冲突的时候有时候语言模型太自信反而会修正掉正确的识别结果。我个人的习惯是困惑度只在训练过程中用来快速判断模型有没有正常收敛真正做决策一定以开发集上的词错误率或字错误率为准。有一位同事总结得很到位困惑度是温度计错误率是体检报告。可以在训练时每天看体温变化但最终判断身体好不好必须看完整报告。5.2 场景反馈驱动的迭代模式语言建模不是训一次就完事的。我的项目节奏是先用通用语料训一个基线模型接进解码器跑通全流程然后开始搜集真实场景的音频和转写每个迭代周期都把这些新增数据切一部分进训练语料重新走一遍清洗、分词、训练、剪枝、解码、评测的流程。这里面最容易被忽视的是标注质量。如果真实场景里的人工转写本身有大量错字那语言模型学到的是错误用法最终识别效果只会越来越差。我见过一些团队为了省成本用上一版的识别结果直接当训练语料结果错误被一个版本一个版本地继承和放大。这是最典型的负循环。5.3 让语言模型成为整个系统的主动优化器最后分享一个理念层面的经验。很多做语音识别的团队会把语言模型当成一个静态组件训完就锁定了。但在我看过比较成功的项目里语言模型其实是系统持续演进的支点。每当产品增加了新的品类、新的关键词、新的对话场景都会驱动语言模型做针对性迭代。识别的准确率可能只提升了一两个百分点但用户实际感受到的对话是不是聪明了通常有非常明显的变化。比如一个智能音箱项目初始阶段语言模型对音乐点播的支持很差我想听周杰伦的晴天经常识别成我想听周杰伦的前天。后来把大量真实点播文本灌进语言模型这类错误很快就消失了。原因不是声学模型变强了而是语言模型学会了整首歌名的搭配。最后的实操建议如果你是自己动手做语音识别的开发者我的建议是先别急着上大模型。用一份干净语料训一个3-gram接进解码器再把测实验整明白这个链路值得先完整走通。在这个基础上再慢慢加入神经网络重打分、领域语料迭代、参数调优逐步把系统做精。我个人在实际操作中最深的体会是语言建模这件事数据和工程规范远比模型结构的选择更影响最终结果。结构选型有大量成熟的方案可以借鉴但语料是否干净、分词是否一致、测试集是否科学、迭代流程是否闭环这些全是只能在真实项目里积累的东西。希望这篇文章能帮你把必经之路上的那些坑提前标出来省下几周甚至几个月的摸索时间。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询