Chaoses-Ib / IbPinyinLib

一个高性能 Rust 拼音匹配库,支持 C、AHK2
MIT License
28 stars 0 forks source link

关于多音字处理,能否增加自定义词典或其他方式? #5

Closed tyoul29 closed 1 week ago

tyoul29 commented 2 months ago

在将一系列字典的 key 拼音首字母匹配,以供检索时,时常出现一些违反常识、却能匹配的案例,这带来两大困扰:一是常用多音字增加了很多无意义的匹配,二是多音字偏僻的或几乎废用的音也匹配经常让人摸不着头脑,如尿的 sui 这个音。 这带来很大困扰。 我一种简单笨拙的处理是:

  1. 用户自己为每种匹配方式维护一个 json 列表,如 dict/firstchar.json: [ ["mnx", "泌尿系"], ["cq", "重庆"], ["q", "奇 其 期"] ]
  2. 检测词典文件,如无则按默认多音字方式处理;如有将字符串按列表顺序遍历替换后再处理,手动过滤多音字。

这种方式不够智能、维护麻烦,唯一的优点是手动个人可控,也有其他更好的处理方式,我看到其他项目有对于这方面的处理,但实现原理不大知晓。

Chaoses-Ib commented 2 months ago

如果能找到一个多音字频率表的话会比较好,可以自定义只匹配频率在多少以上的多音字。

目前的数据源是多个字典混合的,找不到频率表的话也可以试试去掉几个含有不常用音数据源:https://github.com/Chaoses-Ib/pinyin-data

tyoul29 commented 2 months ago

我的意思是现在是字粒度的,不能实现词粒度,调频或删拼音只是以单字处理,其结果不一定能让人满意。 对于这种情况,有的库处理结果是:https://github.com/hotoo/pinyin

console.log(pinyin("中心", {
  heteronym: true,              // 启用多音字模式
}));                            // [ [ 'zhōng', 'zhòng' ], [ 'xīn' ] ]

console.log(pinyin("中心", {
  heteronym: true,              // 启用多音字模式
  segment: true,                // 启用分词,以解决多音字问题。默认不开启,使用 true 开启使用 Intl.Segmenter 分词库。
}));                            // [ [ 'zhōng' ], [ 'xīn' ] ]

console.log(pinyin("中心", {
  segment: "@node-rs/jieba",    // 指定分词库,可以是 "Intl.Segmenter", "nodejieba"、"segmentit"、"@node-rs/jieba"。
}));                            // [ [ 'zhōng' ], [ 'xīn' ] ]

有的可能用了人工智能的处理,根据上下文预测,但这些实现可能都有点复杂,且对于一些自定义的汉语缩写,处理起来就更为麻烦了,我看到像 jieba 那种为用户提供了 自定义字典,你 PinyinData 里面好像都不涉及到词,且编译后好像只剩下 dll 文件。

我意思是用户可以指定一个词典多音字目录或词典字典,方便随时修改,如:

pinyin_dict := {
            "PINYIN_NOTATION_ASCII_FIRST_LETTER" : "dict/PINYIN_NOTATION_ASCII_FIRST_LETTER.json",
            "PINYIN_NOTATION_ASCII" : "dict/PINYIN_NOTATION_ASCII.json"
}

词典内为列表,字符长的优先替换为拼音,预处理后避免掉多音字的问题,不过字典量大也会带来性能问题,预处理也可以由用户先处理,只是在想预处理这一步由 rust 实现性能要更高些。

[
    ["MNX", "泌尿系"],

    ["XT", "系统"],
    ["SZ", "生长"],

    ["A", "艾 阿"],
    ["B", "般 吡 扁 β"],
    ["C", "查 侧 颤"],
    ["D", "单 丁"],
    ["E", "恶"],
    ["H", "合 红 坏"],
    ["L", "乐 率"],
    ["J", "解 家 见 咀 夹 降 颈"],
    ["K", "溃 卡 咳 咯 咖"],
    ["M", "泌"],
    ["N", "尿 疟"],

    ["1", "Ⅰ"],
    ["2", "Ⅱ"], 
    ["3", "Ⅲ"],
    ["4", "Ⅳ"],
    ["5", "Ⅴ"],
    ["6", "Ⅵ"],
    ["7", "Ⅶ"],
    ["8", "Ⅷ"],
    ["9", "Ⅸ"],
]

如果未设置的话,就按默认的方式处理。 但这样也存在问题,拼音首字母转换,字符串长度不变,但转换全拼、双拼后,计算匹配位置可能比较复杂。 只是提供一种思路讨论。

Chaoses-Ib commented 2 months ago

对中文分词就必然会涉及到歧义,这种机械预处理的方式误识别率太高,比如如果词典中有 生长: sz那个学生长长的鼻子 中的 生长 就可能被错误识别。要实现比较好的效果,需要引入机器学习模型对句子完整分词。目前已经有这样的 Rust 分词库了:https://github.com/messense/jieba-rs ,不过分词的结果不含拼音。

tyoul29 commented 2 months ago

https://github.com/hotoo/pinyin 这个项目好像是结合了分词解决多音字。 完全消除歧义基本是不可能的,机器学习可能对一些自定义缩写也没办法,只是用到转换拼音的场景,可能大部分都是用来简化搜索,这种情况能消除大部分歧义也够用了,自定义词典只是在不影响原本程序包含全部的情况,将部分分离给用户,用来微调一下。不过确实会带来很多的复杂和不确定性,或者提供一个返回所有拼音的方式,给用户自己处理。