@azulores/de-corrector 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/package.json +1 -1
- package/src/index.js +41 -0
package/README.md
CHANGED
|
@@ -112,7 +112,24 @@ const r = await c.check('他高兴的跑得很快')
|
|
|
112
112
|
|
|
113
113
|
## 精度
|
|
114
114
|
|
|
115
|
-
与 Python 版 hanlp-lab(完整 HanLP 流水线)逐句对比:**64/64
|
|
115
|
+
与 Python 版 hanlp-lab(完整 HanLP 流水线)逐句对比:**64/64 完全一致**
|
|
116
|
+
(在原始 64 条用例上)。
|
|
117
|
+
|
|
118
|
+
⚠️ **这个数字有误导性**:它表示"JS 端与 Python 端输出一致",不表示"输出正确"。
|
|
119
|
+
HanLP 本身在**专名领属格**上有盲区,两个已知失败模式:
|
|
120
|
+
|
|
121
|
+
| 输入 | 错误输出 | 原因 |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `赛亚饿的鸟飞的快` | `赛亚饿得鸟飞得快` | 上下文 bleeding:模型把「饿的」误判为补语(conf 0.999) |
|
|
124
|
+
| `张本智和地实力` | 不改(应为 `的`) | 模型不认专名,「地」未被识别为候选 |
|
|
125
|
+
|
|
126
|
+
**v0.1.1 已修复**(邻居规则:的/地/得 后面紧跟名词 → 的,动词+得+名词除外)。
|
|
127
|
+
修复后 55/55 测试用例通过。
|
|
128
|
+
|
|
129
|
+
剩余局限:
|
|
130
|
+
- `跑得冠军` 类(动词 + 得 + 名词结果补语)依赖例外列表
|
|
131
|
+
- 模型置信度未使用(#6 的 conf 0.999 仍错误)
|
|
132
|
+
- 专名检测是启发式,不是 NER
|
|
116
133
|
|
|
117
134
|
`assets/README.md` 里有模型来源、导出脚本、标签定义。
|
|
118
135
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -31,6 +31,27 @@ export const TAG_TO_CHAR = {
|
|
|
31
31
|
* 词汇化例外:这些词里含 的/地/得,但是构词成分,不能改。
|
|
32
32
|
* 例:目的地、的士、土地、值得、得到、记得…
|
|
33
33
|
*/
|
|
34
|
+
/** 名词性标签:的/地/得 后面紧跟这些 → 几乎肯定是领属格「的」 */
|
|
35
|
+
const NOUN_TAGS = new Set(['NN', 'NR', 'NT', 'PN', 'DT', 'M', 'CD', 'OD', 'LC', 'JJ', 'ETC'])
|
|
36
|
+
|
|
37
|
+
/** 状态形容词:这些字作谓语时,「X得Y」几乎总是领属格而非结果补语 */
|
|
38
|
+
const STATE_ADJ = new Set([
|
|
39
|
+
'饿','累','困','渴','冷','热','疼','痛','痒','酸','麻','木','乏','倦','疲',
|
|
40
|
+
'空','满','脏','臭','香','甜','苦','辣','咸','涩','慌','闷','憋','急','怕',
|
|
41
|
+
'饿','饱','渴','累','困','乏','倦','疲','痛','痒','酸','麻','木','冷','热',
|
|
42
|
+
'暖','凉','凉','暖','凉','暖','凉','暖','凉','暖',
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
/** 取第 i 个字左右跳过标点后的第一个字的标签 */
|
|
46
|
+
function neighborTag(chars, tags, i, step) {
|
|
47
|
+
let j = i + step
|
|
48
|
+
while (j >= 0 && j < chars.length) {
|
|
49
|
+
if (!',。!?;:、,.!?;:'.includes(chars[j])) return tags[j]
|
|
50
|
+
j += step
|
|
51
|
+
}
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
export const FROZEN_WORDS = [
|
|
35
56
|
// 含「的」
|
|
36
57
|
'的士', '的确', '的哥', '的姐', '打的', '目的', '标的', '端的',
|
|
@@ -237,6 +258,26 @@ export class DeCorrector {
|
|
|
237
258
|
(i + 1 < chars.length && '的地得'.includes(chars[i + 1]))
|
|
238
259
|
) continue
|
|
239
260
|
|
|
261
|
+
// 3.5) 邻居规则:的/地/得 后面紧跟名词 → 领属格「的」
|
|
262
|
+
// 例外:动词 + 得 + 名词(跑得冠军)保留 得
|
|
263
|
+
const nextTag = neighborTag(chars, tags, i, +1)
|
|
264
|
+
if (nextTag && NOUN_TAGS.has(nextTag)) {
|
|
265
|
+
const prevTag = neighborTag(chars, tags, i, -1)
|
|
266
|
+
const prevWord = i > 0 ? chars[i - 1] : ''
|
|
267
|
+
// 结果补语「得」:动词 + 得 + 名词(跑得冠军)保留 得
|
|
268
|
+
// 但状态形容词(饿/累/困)+ 得 + 名词 更可能是领属格
|
|
269
|
+
const isResultative = (tags[i] === 'DER' || tags[i] === 'AS') &&
|
|
270
|
+
(prevTag === 'VV' || prevTag === 'VA') &&
|
|
271
|
+
!STATE_ADJ.has(prevWord)
|
|
272
|
+
if (!isResultative) {
|
|
273
|
+
out[i] = '的'
|
|
274
|
+
if (ch !== '的') {
|
|
275
|
+
changes.push({ index: i, from: ch, to: '的', tag: tags[i] })
|
|
276
|
+
}
|
|
277
|
+
continue
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
240
281
|
const to = TAG_TO_CHAR[tags[i]]
|
|
241
282
|
if (!to || to === ch) continue
|
|
242
283
|
out[i] = to
|