chinese-ime-lm README

The project's README from github.com/metasequoiaime/chinese-ime-lm. As one markdown file: README.md.

chinese-ime-lm

CI CodeQL License Stars

面向中文输入法的语言模型工程:语料、统计模型、神经模型,以及判断它们好坏的评测集。

仓库里有两条并行的路线,服务同一件事——在拼音解码给出的若干读法之间做出选择:

路线 位置
n-gram 语料清洗 → KenLM 统计 → Viterbi 解码 corpus/clean/、ngram/
神经模型 语料获取 → 字级 Transformer → 候选重排 corpus/fetch.py、neural/、reference/

两者用同一套评测集(eval/)衡量,所以可以直接比较。评测集本身比任何一个模型都更值得先看:它把"某个改动好不好"从意见变成数字,并且把两个会悄悄毁掉测量的陷阱固化成了用例(详见 docs/measurements.md)。

与其自己训,不如直接用开源小模型? 在同一套候选池上量过四个 Qwen:观测上没有一个超过本项目 25.5 MB 的模型,0.6B 与 0.8B 打平 4.5 MB 的那个,而体积差 140 到 380 倍、单次决策慢一个数量级。但排序质量上的名次差距全部落在噪声里——52 条上两两分歧只有 2 到 6 条,McNemar 没有一对显著,要检出 1 到 2pp 需要一千到近万条用例。选自训权重的理由是尺寸和延迟这两项确定性测量,不是名次。同一次测量还发现整句集里有 12 条「他/她」在拼音上不可判的用例,本就不该收。全部记在 docs/measurements.md。

当前状态:神经模型两条都在用,n-gram 一条从未出过这个仓库。 权重以 Apache-2.0 发布在 Hugging Face —— pinyin-ime-reranker-4M(4.5 MB,按键路径用这个)和 pinyin-ime-reranker-25M(25.5 MB,精度到顶),同一批文件也在 model-v1 release 里,MSIME 的 host-api 通过资源锁装载它们:小的跑按键路径,大的在输入停顿时接手。

n-gram 一侧从未有产物进入过任何产品,而神经一侧按音节直接解码(reference/examples/decode.rs)的表现仍然比它本想改进的引擎还差——重排有用,直接解码没到。采用之前先把话说在前面。

目录

路径 内容
corpus/ 语料:fetch.py 获取可再分发语料,clean/ 清洗自备语料,pinyin/ 生成单字拼音表
ngram/ n-gram 路线:KenLM + Viterbi 解码
neural/ 神经路线:字级 Transformer 的训练、导出、评测
reference/ Rust 推理参考实现,无 unsafe,除 serde 外无依赖(Apache-2.0)
eval/ 25,119 条词级用例 + 60 条整句用例
docs/format.md 模型文件格式规范,用任何语言实现加载器只需要这一篇
docs/measurements.md 实测结论:模型在哪里有用、在哪里有害,两个测量陷阱,以及与开源通用模型的对比
tests/ 语料清洗的回归测试

许可按目录区分

仓库整体为 GPL-3.0(见 LICENSE)。

reference/ 下的 Rust 代码著作权单一,另以 Apache-2.0 授权(见 reference/LICENSE),因为大量开源输入法是 MIT/Apache/BSD,链不了 GPL 代码——第三方可以只取那个目录。

格式规范、评测集和发布的权重都不受代码许可约束。发布的权重为 Apache-2.0,理由和 reference/ 一样:链不了 GPL 的输入法同样用不了 GPL 的权重,而一个没人能采用的公共资源不成其为公共资源。该许可写在权重文件自己的 __metadata__ 里,所以它不会与权重分离。

权重另有语料的署名义务,见 NOTICE;那份义务来自语料而非代码,两者不可相互替代。

语料来源与许可

本仓库只分发处理脚本。 语料和训练好的模型都不入库:data/、model/、kenlm_bin/ 下除 .gitkeep 外全部被 gitignore,没有任何内容被跟踪。语料由你自己准备。

这一点要紧,因为本项目最初依据的语料不能自由再授权,而你下载它的那个仓库上的许可并不能解决这个问题:

维基百科下游的任何人都无法把维基的文本重新授权为 MIT,所以聚合仓库的 MIT 徽章只应理解为覆盖其脚本与打包工作。

这在实践中意味着什么。 从语料算出来的统计量(n-gram 计数、概率)通常不等于语料本身,但"统计量"和"衍生作品"之间的界线并不清晰——一个能复现源句子的模型,比一张二元频率表更接近衍生作品。所以:

corpus/fetch.py 正是第一条路线的实现:它只获取许可清晰、可再分发的语料(C4 为 ODC-BY,LCCC 为 MIT),发布的神经模型权重只用这些训练。详见 NOTICE。

本仓库的 LICENSE(GPL-3.0)只适用于这里的脚本。它不涉及、也无法授予你喂给这些脚本的语料的任何权利。

获取语料

两种方式,按你的用途选:

自己准备。 语料来源见上节那个聚合仓库,解压后放进 data/ 目录,再用 corpus/clean/ 清洗。

让 corpus/fetch.py 下载。 它只取许可清晰的来源,流式下载并直接规范化,原始压缩包不落盘:

python corpus/fetch.py c4   --out data/c4.txt --max-chars 1_000_000_000
python corpus/fetch.py lccc --out data/lccc.txt --split large

语料清洗(自备语料)

对于句子中的非中文字符,进行过滤,只要是非中文的字符,一律将其扩充成连通块,然后,将这个连通块当作是一个分隔符,将句子切分成小句子。最后,预处理好的数据就都是一个个由纯粹的中文组成的单独的连通块。

python .\corpus\clean\extract_connected_hanzi_components.py
python .\corpus\clean\extract_wiki_connected_hanzi_components.py
python .\corpus\clean\split_all_cleaned_txt_using_space.py
python .\corpus\clean\split_wiki_txt_using_space.py

注意,这里是比较耗时的,至少需要半个小时左右,在我的 11 代 intel 处理器上。

分词

对上面预处理好的数据进行分词。由于使用的是字级 n-gram,分词只需要把字一个一个分开即可。

统计 n-gram

分别计算一元数据和二元数据。

这里的处理就都交给 kenlm 来做,kenlm 的编译和使用参见我的笔记。需要准备的东西是这种格式的文本语料:

我 爱 北 京
我 爱 中 国
我 爱 上 海
北 京 是 中 国 首 都

需要将 kenlm 项目编译好的 Release 版本的二进制文件复制到 kenlm_bin 目录下。然后,将生成的 model.arpa 和 model.binary 文件放到相应的 model\all 和 model\only_wiki 目录下。

cmd /c ".\kenlm_bin\lmplz.exe -o 3 < .\data\output\all_cleaned_only_wiki_zh_spaced.txt > model.arpa"
.\kenlm_bin\build_binary.exe model.arpa model.binary
mkdir model\only_wiki
mv model.arpa model\only_wiki
mv model.binary model\only_wiki
cmd /c ".\kenlm_bin\lmplz.exe -o 3 < .\data\output\all_cleaned_spaced.txt > model.arpa"
.\kenlm_bin\build_binary.exe model.arpa model.binary
mkdir model\all
mv model.arpa model\all
mv model.binary model\all

神经模型

字级 Transformer,在拼音解码拼装出的候选之间重排。它不是普遍有益的——实测见 docs/measurements.md,结论是:词典对整个键的精确命中已经携带语料词频, 覆盖它们在任何权重下都掉分;而解码器拼装的候选不带词频证据,模型的价值只在那里。

pip install -r neural/requirements.txt

python corpus/fetch.py c4   --out data/c4.txt --max-chars 1_000_000_000
python corpus/fetch.py lccc --out data/lccc.txt --split large

python neural/train.py --corpus data/c4.txt data/lccc.txt --out runs/desktop --preset desktop
python neural/export.py --run runs/desktop --out dist/model.safetensors --precision int8

corpus/fetch.py 与 corpus/clean/ 的关系:后者处理你自己准备好的语料,前者负责获取许可清晰、可再分发的语料。两者的中文规范化是同一个算法——保留汉字连通块,其余一律当分隔符。

推理见 reference/,格式见 docs/format.md。

构建与测试

n-gram 路线

Windows 上用 PowerShell 7.0+:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
pip install https://github.com/kpu/kenlm/archive/master.zip

需要 Python 3.12。

跑解码之前先生成拼音到单字的候选表:

python .\corpus\pinyin\make_single_pinyin_table.py

然后就可以运行 Viterbi 解码:

python .\ngram\viterbi.py

语料清洗的回归测试:

python -m unittest discover -s tests -v

神经路线

pip install -r neural/requirements.txt

cd reference
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --check

参考实现除 serde 外无依赖,测试里手工构造模型文件并加载,因此它同时校验 docs/format.md 与实现是否一致。

参考资料

Star History

Star History Chart

chinese-ime-lm (metasequoiaime/chinese-ime-lm)

nlp
Searching Database…