Kotonia
ログイン今すぐ始める

Kotonia Articles

能自动识别中日英的输入法,切得越碎越吃亏

为了不切换输入法就能打日语、英语和中文,我给输入法加上了自动语言识别。用着用着发现,多打一段再转换反而改得更少。按键相同、只改切分方式来测:日语的修改次数减半,中文降到四分之一。

作者 4分钟阅读
#输入法#fcitx5#日语输入#中文#多语言

我每天混着写日语、英语和中文,最磨人的其实是切换输入法。 忘了按 Ctrl+Space,打出 nihao 却得到「にはお」,删掉,切换,再打一遍。 一天要来几十次。

那不如让输入法自己从按键序列里猜语言。于是我给 fcitx5 + hazkey(AzooKey 引擎 + 神经网络假名汉字转换 Zenzai)加上了英语和中文的自动识别。 不用切换键,kyouhaiitenkidesune(今日はいい天気ですね)、meeting、nihao 都在同一个状态下直接打。

用了一阵,我发现一件怪事:多打一长段再转换,需要改的次数明显更少。 这和一般输入法的用法正好相反,所以我实际测了一下。

一般的输入法,切碎了反而轻松

日语输入法用久了的人,大多习惯一个文节(短语)一转、确定,或者几个文节一转。 整句一次性转换(连文节转换)早就有了,但长句一旦转错,改文节边界、重选候选词都很麻烦。 勤确定,转错时损失就小。这是很合理的习惯。

可是在自动识别语言的输入法里,这个前提不成立了。原因有两个。

  1. 输入越短,语言识别越容易错。 nihao(你好)、wo(我)、ma(吗)都能顺理成章地读成日语罗马字。 我做了一个只看按键序列猜语言的分类器,按长度测:3 个字母时准确率 89%,8 个以上 99%。 短输入里根本没有足够的信息来判断。
  2. 转换模型看的是整段输入。 Zenzai 把整段输入当作上下文。 「かれは」该转成「彼は」(他)还是「枯葉」(枯叶),不看后面接什么就定不下来。

做了什么

  • 英语识别:字符 1–5gram 的对数似然比(英语 vs 日语罗马字)。像英语就把原样的字母放在第一候选。 长输入里夹着英语单词时,用动态规划把它切成英语段和日语段。 例如 kinouhagithubnosetteiwokaeta → 「昨日はgithubの設定を変えた」(昨天改了 github 的设置)。
  • 中文识别:在英语识别之后,用同样形式的 n-gram 判断是拼音还是日语罗马字。 像中文就交给 libime 转换。它和 fcitx5 自带拼音输入法用的是同一个引擎,单独跑一个进程(每个短语 2.8ms)。
  • 上下文:光标前的文字里有假名就偏向日语,没有就偏向最后一次确定的语言。 需要后者,是因为 Discord 这类应用不会把前后文传给输入法。

日语罗马字和拼音重叠得很厉害:46% 的日语片段也能拆成合法的拼音音节。 所以「能不能读成拼音」几乎帮不上忙,真正起作用的只有长度和上下文。

怎么测的

用 Tatoeba 的句子,按键序列完全相同,只改切分方式:

  • 每个文节转换、确定(不传上下文,模拟不传前后文的应用)
  • 每个文节转换、确定(把已确定的文字作为下一次转换的上下文)
  • 每两个文节 / 每四个文节
  • 整句一次

每段都用第一候选确定,和标准答案比较字符错误率(CER)和整句完全正确的比例。 另外还测了「边改边打」:每一段如果第一候选不对、而列表里有正确答案,就选它。 第一候选不对的次数记为「修改次数」。为了不让学习功能影响结果,每句都重启一次服务器。

数据三组,各 150 句:

  • 纯日语
  • 日语夹英语(把「名词 + 助词」中的名词换成 github、meeting 等)
  • 中文(用 jieba 分词)

结果

纯日语(平均 5.2 个文节)

一次转换的长度错误率整句正确修改次数/句
逐文节·无上下文11.9%31%1.21
逐文节·有上下文6.8%57%0.60
每两个文节4.1%64%0.42
每四个文节3.8%66%0.37
整句3.6%68%0.32

即使是几乎不涉及语言识别的纯日语,也是越长越好。改善分两步。

  • 从无上下文到有上下文,修改次数减半。这是左侧上下文的作用。
  • 从逐文节到整句,又减半。这是右侧上下文,也就是后面接什么的作用。

左侧上下文可以传给输入法,右侧上下文只存在于打字的人脑子里。 典型的错误是「かれは」→「枯葉」:逐文节确定,就得在看到后面的「シャーウッドの森を」(舍伍德森林)之前做决定。

日语夹英语(平均 5.5 个文节)

一次转换的长度错误率整句正确修改次数/句
逐文节·无上下文11.5%31%1.17
逐文节·有上下文7.3%50%0.68
每两个文节6.3%55%0.50
每四个文节5.9%56%0.45
整句5.9%59%0.41

趋势相同,到四个文节左右就到顶了。

中文(平均 7.2 个词)

一次转换的长度错误率整句正确修改次数/句
逐词·无上下文66%12%1.85
逐词·有上下文85%12%1.84
每两个词34%42%0.86
每四个词23%55%0.62
整句14%57%0.43

这里差别最明显。一个词一个词地打,根本不会被当成中文。

你快要走了。          → に快要走了。
你和我有一些共同点。   → にheをyouyixiegongtongdian。

ni、he 只有两个字母,读成日语罗马字反而更自然。 传上下文甚至更糟(66% → 85%)。第一个 ni 变成「に」之后,它就成了「前面是日语」的上下文, 把后面的拼音也拉向日语。错的结果成为下一次判断的依据,错误一路传下去。

整句打的话,七个词的拼音几乎不可能被认成别的语言。

打字方式反过来了

在一般的输入法里,切碎是保险,转错时损失小。 在自动识别语言的输入法里,切碎本身就是出错的原因。

  • 纯日语:整句所需的修改次数约为逐文节的一半(0.60 → 0.32)
  • 中文:约为四分之一(1.84 → 0.43)
  • 最差的是在不传上下文的应用里逐文节打(日语 1.21,中文 1.85)

长输入不再是「错了很麻烦」的东西,而是「让它转对的材料」。 实际用下来,我也自然变成了一直打到标点再按空格。

测量才发现的 bug

这次测量与其说是为了写文章,不如说更像是一次实现的体检。

  • 句中有逗号的中文句子全都转乱了。 libime 读不了带标点的输入, nihao,wojiaoqingshui. 变成了一串日语假名。中文句子里有 15% 句中带逗号。 改成按标点切开分别转换后,整句的错误率从 39% 降到 14%。上面的中文表格是修复后的数字。
  • 较长的输入里,youtube 会变成「ようつべ」(日语里 YouTube 的俗称)。 逐文节打时能保留英语, 两个文节以上一起转换就输给了 Zenzai 的转换。尚未修复。

一句一句手动试的时候,这两个都没发现。 逗号的 bug 正好打在「越长越好」这个结论上。要是不测就写文章, 我会把中文的结果误读成「长了也没好多少」。

这些数字的局限

  • 错误率包含写法差异(ご飯/ごはん、行った/いった)。各种切分方式都按同样条件计算,不影响比较。
  • 「修改次数」假设的是理想的用户:列表里有正确答案就一定会选。
  • Tatoeba 的句子较短(约五个文节)。更长的段落会到顶还是反而变差,还没测。
  • 中文按 jieba 的词切分,可能和人实际停顿的地方不同。
  • 只测了第一候选是否正确,没测转换耗时。Zenzai 在 CPU 上每句约 200ms,整句转换比打字时的实时候选更重。

结语

「多打一段更好」本来只是用着用着隐约感觉到的。 一测才知道,纯日语也有明显差距,而中文的差距大到不改打字方式就没法用。 输入法好不好用,取决于引擎和用户「在哪里切」的习惯, 引擎变了,最合适的习惯也会跟着变。

Kotonia 将语音 AI、AI 聊天、图像生成和团队协作整合到一个 AI 工作区中。

试用 Kotonia