- assets/lexicon/domain_words.tsv:日志里选过 ≥ 2 次、词库与短语层都没有、公开语料里出现过的词;次数用语料次数,不到 20 的按 20,多音字拼音给全 - bigram --phrases 可重复给:领域词当 token 统计会吸走成分词的二元证据(词库 77 次 → 词 + 库 路径没了,整句出 助词哭),走合成路整句得分与原路径一样 - 回放 词 8064 → 8098、整句 2977 → 2983,整句评测集 37.6% 持平(字准确率 76.8 → 76.9);同步 dict.tsv 与领域词库 - 记录:docs/notes/domain-words.md、QINGJIAN.md 4d
6.3 KiB
短语层(2026-09-12)
起因
常数扫描(constant-sweep.md)之后看没命中的构成:词级 1208 条没命中里 742 条是 5 键以上的短语当词选,
日志里上屏过、词库没有的汉字串 2271 个,选过两次以上的 449 个,最常见的是 我的(57 次)/ 好的 / 都是 / 更好 / 我觉得 / 而不是。
它们不是「新词」,是常用词表(liuxilu 5.6 万条)按词典收词原则不收的组合;mine 挖语料那步的虚词规则又把含 的 / 不 / 是 / 有 的全滤掉了。
所以缺的不是数据新旧,是「人会整块打出来」的短语层。
挖法:dict-convert phrases
- 语料按词库分词,两遍扫:第一遍数相邻两词,第二遍数两段二元都够频的相邻三词。
- 次数按语料文件分开数,总次数与对话语料(LCCC)里的次数都要 ≥ 2000:维基里的模板句(一个市镇、面积为、人口密度)次数再高也不是人会打的。
- 不用点互信息:虚词组合的 PMI 天然低,而它们正是要收的。只用边界规则(
phrases/rules.rs): 不以 的了着得地吗吧呢啊 开头,不以 不没很太也都又再才还把被和与及或而且但 与数词结尾;代词 + 了 / 这 / 那 是碎片; 单字 + 代词只认 给让帮等找叫送陪问那… 这些(给我 / 那我 成,这你 / 天你 不成);次数刚过线的再套「单字 + 的 只认代词」「是 / 在 + 单字不要」, 够常见的(≥ 5 倍下限)不套——好的 / 真的 / 是啥 是人天天打的,多的 / 岁的 / 是新 不是。 - 笑声串不算相邻:你哈哈哈 切成 你 / 哈哈 / 哈,一个 哈 token 挨着另一个 哈 时含它的窗口不计,否则 你 + 哈哈 会灌成高频短语。 孤立的 哈 照常,所以 你哈(谢谢你哈,LCCC 三千多次)仍在——它是真的。
- 读音由成分词拼出(同一个词多个读音取词频最高的),不用再标多音字;
--extra-words文件加了第三列拼音,lexicon给了就不按字猜。 - 结果 5398 条(二词 4300 多、三词 1100 左右),人工抽样精度约九成,覆盖日志 449 个缺词里的 188 个(语料里 ≥ 20 次的 299 个里 170 个)。 剩下的是 词库 / 对齐 / 后端 这类语料里少的领域词,与 是不 / 看一 这类日志里接受的碎片。
- 词库已并入短语后重跑要
--refresh assets/lexicon/phrases.tsv:不摘掉上次的短语,我的 就是一个词,挖不出 我 + 的(踩过:只挖出 34 条)。
进语言模型的三种接法(都在同一份冻结日志 + 整句评测集上比)
| 接法 | 整句评测集首选 | 回放 词 | 回放 整句 |
|---|---|---|---|
| 改前 | 37.6% | 8032 | 3045 |
| A 短语当 token 重训 bigram | 38.4% | 8092 | 2878 |
| B 语言模型不动、短语只进词库 | 37.6% | 8067 | 2929 |
| C 分词摘掉短语、统计完按成分合成计数(采用) | 37.6% | 8064 | 2978 |
A 的整句评测涨了 0.8 个点(短语 token 相当于给了三元上下文),但回放掉 5 个点,看明细两个机制: ① 二十 压过 而是——短语当 token 后语料里的「而是」都归了 token,「而 + 是」的二元证据没了,而是 的一元又比 二十 低; ② 不对 / 不在 / 等会 / 放到 这类整句恰好是一个词的,以前是 Viterbi 路径 不 + 对 当整句候选排最前,现在整段是一个词就不出整句候选, 按词级排序放,部队 这种维基高频词压上来。B 没有①,但②仍在:短语对语言模型是未知词,兜底扣 4 nat。
C 是 bigram --phrases:分词时把短语摘掉(二元证据保住),统计完给每条短语合成
一元 = c(a,b)、前接 c(v,短语) = c(v,a)·c(a,b)/c(a)、后接 c(短语,w) = c(a,b)·c(b,w)/c(b),
于是 P(短语|v) = P(a|v)·P(b|a)、P(w|短语) = P(w|b),短语在词图与词级排序里的得分与原来走两个词的路径一样,只是多了个能整块选的词。
合成行 186 万条另加、不占真实二元行的 300 万名额(占了会把低频真实行挤掉,整句评测掉 0.4);lm.qj 29 MB → 44 MB,加载仍在 100 ms 内。
C 的回放整句 −67 是什么
新增没命中 75 条、修好 30 条。75 条里:
- 一半以上是尺子偏差:旧引擎没有 那就是 / 你可以 / 我现在 / 然后就是 这些短语,用户当时只能选前缀 那就 / 你可 / 我先 再接着打, 日志记的是前缀;现在短语排第一、前缀第二,反而对。沃德 压 我的 也是回放早期从日志学了 沃德。
- 十来条真翻转:猜对 / 才对、不听 / 不停、没法 / 没发、说道 / 说到、要得 / 要的、在哪 / 在那、回事 / 会是。 这是聊天语料的先验与这个用户的习惯不一致,两边都是合法短语;个人学习选一次就翻回来。
- 比较好 / 比较高(
bijiaogao):短语的一元是它真实的出现概率,比原来两步路径的乘积略高(差约 1 nat),刚好让邻键纠错 g → h 赢了。 整句评测集里没有敲错,所以那把尺子看不到这一类;量小,先不管。
品牌词
assets/lexicon/brand.tsv:青简 210,既是词频也写进语言模型一元(句首二元按八分之一算,与 请柬 的句首占比一致)。
只写词频没用:词级排序用语言模型概率,语料里没有的词兜底扣 4 nat,青简 排不进前三;句首二元给整个 210 又压过了整句 请见。
目标是 qingjian 下 请见(整句)/ 青简 / 请柬 的顺序,用户选一次个人学习就把它顶到第一。
复现
cargo run --release -p qingjian-dict-convert -- phrases data/corpus/*.txt # 重跑加 --refresh assets/lexicon/phrases.tsv
cp data/generated/phrases.tsv assets/lexicon/phrases.tsv
cargo run --release -p qingjian-dict-convert -- lexicon --pinyin data/generated/pinyin-llm.jsonl --frequency data/generated/lm-unigram.tsv \
--extra-words assets/lexicon/mined_words.tsv --extra-words assets/lexicon/phrases.tsv --extra-words assets/lexicon/brand.tsv
cargo run --release -p qingjian-dict-convert -- bigram --phrases assets/lexicon/phrases.tsv --phrases assets/lexicon/domain_words.tsv --brand assets/lexicon/brand.tsv data/corpus/*.txt
再 pack dict / pack lm(bundle.sh 会做),然后 --eval-text data/eval/sentences.tsv 与 --replay 冻结日志各跑一遍与改前比。