做本地中文语音输入时,识别之后通常还需要一步“整理”:去掉“嗯”“那个”、合并重复表达、 修正标点,必要时把口语改成书面语。常见做法是识别用一个 ASR 模型,整理再挂一个通用小语言模型。 在消费级电脑上,这意味着要同时加载两个模型。
我在开源项目 Saymore(Windows 本地中文语音输入)里换了个思路:Qwen3-ASR-1.7B 的解码器 本身就是一个 Qwen3 语言模型,那能不能直接在它上面训练一个整理 LoRA,让识别和整理共用同一个基座? 这篇记录这个方案的几个关键决定,以及训练中踩到的一个数据坑。
一、为什么复用 ASR 基座
显存和进程都只算一份:ASR 和整理都挂在同一个 llama.cpp 服务上,整理只是多加载一个 LoRA 适配器。 4GB 显存的显卡,甚至纯 CPU,都能同时承载两项任务。
领域天然对齐:解码器本来就在处理 ASR 输出的中文口语,整理任务的输入分布和它的原任务很接近。
部署简单:用户只需下载约 1.5GB 的 ASR 模型,整理 LoRA 随安装包分发,体积很小。
代价是 1.7B 的容量有限,不能指望它做复杂改写。所以任务定义要收紧:只做整理,不做扩写、不回答问题、 不补充原文没有的信息。
二、一个 LoRA,四种风格
最初我为“轻度整理”和“深度整理”各训了一个 LoRA,后来合并成一个适配器,用系统提示词切换风格:
风格 目标
轻度 去口头禅和重复,改错字和标点,尽量保留原话
深度 口语改写为书面表达
邮件 整理为称呼、正文、结尾三段式
00 后 换成轻松的网络聊天语气
合并的收益是只维护一份权重,推理时也不必在多个适配器之间切换。实测合并后原有两种风格没有明显退化, 这说明对于 1.7B 这种规模,“多风格共享一个 LoRA + 提示词路由”是可行的。
训练数据约 3200 条(四种风格大致均衡),流程是:程序化生成或收集口述文本 → 用更大的语言模型按各风格的 提示词标注 → 清洗(去空、去截断、去跨集重复)→ 抽样人工检查。评测集单独冻结,永不混入训练集。
三、量化要和训练对齐
生产环境跑的是 GGUF 格式。我在训练阶段使用 QLoRA 的 4bit NF4 量化,部署时解码器选用 IQ4_NL (同属 4bit NormalFloat 码本)。这样训练时模型“看到”的权重和部署时基本一致, 避免 LoRA 在 fp16 基座上训练、却挂到量化基座上推理带来的效果下滑。
四、踩坑:模型“爱分段”,病根在数据
给轻度、深度、00 后三种风格加上自然分段后,冒烟测试发现一个系统性问题: 单一话题的长内容被切成三四段。比如一段一百多字的故障排查描述,本该是一整段, 输出却被拆得七零八落。
第一反应是模型容量不够,但查数据后发现问题出在标注环节。分段标签由一个“分段编辑器”自动补充—— 它只在原标签中插入空行,不改任何字。统计它的输出:
三种风格的分段率都在 90% 以上;
100 字以上的样本 96% 被切段,200 字以上 99%。
也就是说,训练数据在教模型“长了就切”。根因是编辑器的提示词偏向“让长文更好读”,而且关闭了推理。
修复只改了编辑器:
打开推理,提示词改为“默认不分段”:只有两件以上明显不同、彼此独立的事才切; 单一话题无论多长都保持一整段,步骤、因果链、时间顺序都算同一话题;
保留安全网:编辑后的文本去掉空白必须和输入逐字相同,否则丢弃、回退为不分段的原标签。
改完先用一批人工设计、正反例均衡的标定集验证(104 条,55% 应切): 召回 95%,特异度 100%(零误切),准确率 97%。确认口径可靠后再全量重跑。
重跑后的分段率:轻度 92% → 16%,深度 93% → 26%,00 后 91% → 22%;邮件保持三段式不变。 重新训练后,冒烟测试中六条长单话题样本全部保持整段,过切问题消失。
这个案例的经验是:小模型的“坏习惯”往往是数据分布的直接投影。在怀疑容量之前, 先统计一下标签本身的分布,很多问题在数据侧就能定位。
五、效果与速度
在普通 CPU 上,一整段口述的整理约 5 秒;4GB 显存、支持 Vulkan 的显卡约 2 秒。 识别本身 CPU 约 1–2 秒一句,GPU 约 0.5 秒。整个流程离线完成,录音和文字不离开本机。
目前的已知不足:00 后风格在多事件长吐槽上偶尔欠切;1.7B 做书面化改写时, 个别递进关系会被改成并列。正式邮件类输出仍建议人工过目后再发送。
六、小结
ASR 解码器本身就是可微调的语言模型,复用它做后处理能在端侧省下一整份模型的资源;
多风格共享一个 LoRA、用提示词切换,在小模型上可行;
训练与部署的量化方式要对齐;
模型行为异常时,先查标签分布。
Saymore 的程序代码和整理 LoRA 权重以 MIT 协议开源(目前仅 Windows 客户端): https://github.com/frankzch/Saymore