同一批数据训练,误触发差 5 倍:唤醒词模型的训练随机性

2026 年 9 月 · 误触发 · 训练方差 · 多候选模型

同一个唤醒词、同一份负样本、同一套训练配方,唯一不同的是随机种子——一次重训得到 2.74 次/小时的误触发,另一次得到 14.20。5 倍的差距,只来自初始化和数据顺序。这篇记录我们的生产数据,以及对应的解法:现在每轮重训产出 5 个候选模型,让用户自己挑。

先上数据

我们的误触发优化是一个自动闭环:拿刚训好的模型去扫公开语料,把它打高分的非关键词片段挖出来当困难负样本,再全量重训(这套机制的细节见之前那篇误触发排查文章)。以前一轮重训只产出一个模型;现在每轮重训用 5 个不同的种子各训一个候选,所有候选在同一个评估集上测两个指标——每小时误触发和召回变化。

下面是 7 条生产链路的真实数据,覆盖 5 种语言,每行一个关键词,候选按误触发升序排列(关键词做了匿名,都是真实用户的词):

语言R0(次/时)5 个候选(次/时,升序)最好/最差
en104.44.46 / 4.69 / 5.84 / 6.10 / 10.862.4×
en49.53.05 / 3.68 / 5.21 / 7.44 / 9.523.1×
en44.64.83 / 5.35 / 5.39 / 6.02 / 8.481.8×
zh74.53.51 / 10.08 / 11.02 / 14.41 / 16.854.8×
ja330.95.12 / 7.35 / 9.90 / 11.64 / 13.042.5×
fr222.74.51 / 7.25 / 9.02 / 10.75 / 15.213.4×
de265.32.74 / 3.89 / 4.46 / 4.79 / 14.205.2×

每一轮重训都比 R0 基线降了 90% 以上——这是机制本身的功劳。但候选之间的差距稳定在 2~5 倍,五种语言无一例外。只训一个模型,等于闭眼抽签。

方差从哪来

端侧唤醒词模型很小,我们的只有 3 万参数左右。小模型、小参数量、大量彼此不同的极小值点——种子决定你落进哪一个。在我们的场景里,三件事把它放大:

两条走不通的路

挑幸运种子。种子 A 在关键词甲上跑出 2.7,换个关键词乙再用它——没有保证。种子到结果的映射在不同数据集、不同关键词之间不稳定,"好种子"不可迁移。我们验证过。

模型集成。5 个模型的分数取平均,方差基本消掉。但这类模型要跑在 ESP32-S3 和 Android 上,预算是一部设备上的一枚 100KB 模型、几毫秒推理。集成把内存和算力乘 5,这个设备类别付不起。

我们上线的方案

一轮重训产出 5 个候选,全部在同一评估集上测完,控制台把 5 个候选连同双指标(误触发/小时、召回变化)一起列出来,用户挑一个下载。

"为什么不让服务器直接选最好的"——这个问题的诚实答案才是有意思的部分。服务器确实排了序,但依据是公开语料。我们的评估集是通用语音,用户家不是。哪个候选最好,取决于部署环境里有什么声音、用户怎么权衡误触发和召回。上面表格里就有现成的例子:法语关键词有个候选误触发 7.25,看着不错,直到你注意到它的召回相对 R0 掉了 7 个点——对一台偶尔用一次的设备,这个交换可以接受;对一个全天候待命的唤醒词,不行。这是用户的决策,不是服务器的决策。

原则:集中评估所有候选,但把最终选择权放在真实分布所在的地方——用户的环境里。

自己训练模型的话,四条实操建议

  1. 不要相信单次训练的误触发数字。对比两个配方之前,先用不同种子重训一遍——种子解释的差异可能比你的改动还大。
  2. 候选在固定评估集上测,误触发旁边永远放上召回。只赢误触发的候选,可能在悄悄变聋。
  3. 部署端跑不动集成的话,离线训 N 个候选选一个——这是穷人的集成,花的是训练时间,不是运行时开销。
  4. 盯尾部不只是均值:德语那行最差候选(14.20)是最好的 5 倍。配方合理之后,"选"比"调"更值钱。

局限

5 个候选共享同一份数据——这样降的是种子方差,不是数据方差。评估语料仍是公开语音,20~30 小时的留出集对个位数触发率的分辨率也比较粗。每轮重训 5 倍的训练算力是真实成本;我们付这个成本,是因为 100KB 的模型训起来不贵,而用户对一个乱触发的设备的耐心很贵。

训练一个唤醒词,看看你的候选模型
查看开源推理引擎和演示模型 →
ONNX / TFLite · Android · ESP32 · Web · Python · Home Assistant