kevinwatt commited on
Commit
4aba6fb
·
verified ·
1 Parent(s): d384064

Release frozen D512 ONNX weights under MIT

Browse files

Includes the cleaned ONNX model, vocabulary, inference manifest, MIT license, and model card with CPU inference examples.

Files changed (5) hide show
  1. LICENSE +21 -0
  2. README.md +128 -0
  3. manifest.json +28 -0
  4. model.onnx +3 -0
  5. vocab.json +0 -0
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Funmula Corp Limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - zh
5
+ library_name: onnxruntime
6
+ pipeline_tag: token-classification
7
+ tags:
8
+ - onnx
9
+ - punctuation-restoration
10
+ - input-method
11
+ - traditional-chinese
12
+ ---
13
+
14
+ # 靈犀注音(0c)D512 補標點模型(雙向 encoder)
15
+
16
+ 為沒有標點的繁體中文片段補上標點。只插入標點、不改動原字,段尾不補。
17
+ 標籤:, 。 ? ! 、 ;,另有一類「不插入」。
18
+
19
+ - 架構:8 層、hidden 512、8 heads、FFN 768 的雙向 Transformer encoder,從零預訓練的 D512 骨幹改為雙向注意力後微調
20
+ - 窗口 128 字、步長 64
21
+ - 格式:ONNX(opset 18),CPU 推論
22
+ - 字表:27857 字(`vocab.json`)
23
+
24
+ ## 檔案
25
+
26
+ | 檔案 | 說明 |
27
+ |---|---|
28
+ | `model.onnx` | 標點分類模型,SHA-256 `4db3ed40df777347d9c455da01285b01de192d4c210c5f7cd9f0646c727880d8` |
29
+ | `vocab.json` | 特殊 token 與字表,SHA-256 `556dffaffbf4c05211a846dcee55ef735cea68c5754f9bffcc6f9588124172b3` |
30
+ | `manifest.json` | 標籤、門檻、窗口與檔案雜湊 |
31
+
32
+ ## 評測
33
+
34
+ 匯出核對用的 1171 段開發資料(訓練期間看過,不是未曝光測試集),門檻 0.63:
35
+ precision 0.897、recall 0.755、F1 0.820。ONNX 與 PyTorch 插入結果逐段相同。
36
+
37
+ ## 安裝
38
+
39
+ 需要 Python 3.9 以上,只用 CPU。
40
+
41
+ ```sh
42
+ pip install onnxruntime numpy huggingface_hub
43
+ ```
44
+
45
+ 第一次執行會從 Hugging Face 下載模型(約 130 MB),之後使用本機快取。
46
+
47
+ ## 使用
48
+
49
+ 下面是完整程式,存成 `punctuate.py` 就能執行。門檻 0.63 是 precision 約 0.9 的操作點;調低會補更多標點,但錯補也會變多。
50
+
51
+ ```python
52
+ import json
53
+ import re
54
+
55
+ import numpy as np
56
+ import onnxruntime as ort
57
+ from huggingface_hub import snapshot_download
58
+
59
+ path = snapshot_download("Funmula/0c-punct-d512")
60
+ manifest = json.load(open(f"{path}/manifest.json", encoding="utf-8"))
61
+ vocab = json.load(open(f"{path}/vocab.json", encoding="utf-8"))
62
+ CHARS = {c: i + 8 for i, c in enumerate(vocab["characters"])} # 0–7 是特殊 token,4 = UNK
63
+ LABELS = manifest["labels"] # 第 0 類 = 不插入
64
+ THRESHOLD, WINDOW, STRIDE = manifest["threshold"], manifest["window"], manifest["stride"]
65
+ session = ort.InferenceSession(f"{path}/model.onnx", providers=["CPUExecutionProvider"])
66
+
67
+ URL = re.compile(r"(?:https?://|www\.)[^\s,。!?、;「」『』()【】]+", re.I)
68
+ ENUMERATORS = set("一二三四五六七八九十壹貳參肆伍陸柒捌玖拾甲乙丙丁戊己庚辛壬癸")
69
+
70
+
71
+ def is_han(c):
72
+ return "㐀" <= c <= "鿿" or "\U00020000" <= c <= "\U000323af"
73
+
74
+
75
+ def allowed(text):
76
+ # 第 i 個字之後可插標點:該字是漢字且不在網址內,緊接的字是漢字或空白,下一個可見字元是漢字或英數字。
77
+ protected = {i for m in URL.finditer(text) for i in range(m.start(), m.end())}
78
+ out = []
79
+ for i, c in enumerate(text):
80
+ nxt = text[i + 1:i + 2]
81
+ visible = text[i + 1:].lstrip()[:1]
82
+ out.append(is_han(c) and i not in protected
83
+ and (not nxt or is_han(nxt) or nxt.isspace())
84
+ and (not visible or is_han(visible) or visible.isascii() and visible.isalnum()))
85
+ return out
86
+
87
+
88
+ def window_probs(text):
89
+ ids = np.array([[1, 7] + [CHARS.get(c, 4) for c in text]], dtype=np.int64) # [BOS][任務] 原文
90
+ return session.run(["probs"], {"ids": ids})[0][2:] # 第 i 列 = 第 i 個字之後各標籤的機率
91
+
92
+
93
+ def probabilities(text):
94
+ # 超過一個窗口時滑動,每個位置取離窗口邊緣最遠的那一窗。
95
+ n = len(text)
96
+ starts = [0] if n <= WINDOW else list(range(0, n - WINDOW, STRIDE)) + [n - WINDOW]
97
+ out, best = [None] * n, [-1] * n
98
+ for s in starts:
99
+ probs = window_probs(text[s:s + WINDOW])
100
+ for j in range(len(probs)):
101
+ margin = min(j, len(probs) - 1 - j) if n > WINDOW else 0
102
+ if margin > best[s + j]:
103
+ best[s + j], out[s + j] = margin, probs[j]
104
+ return np.stack(out)
105
+
106
+
107
+ def punctuate(text):
108
+ # 只插入標點、不改原字;段尾不補。
109
+ if not text:
110
+ return text
111
+ ok, probs = allowed(text), probabilities(text)
112
+ out = []
113
+ for i, c in enumerate(text):
114
+ out.append(c)
115
+ if i == len(text) - 1 or not ok[i]:
116
+ continue
117
+ k = int(probs[i, 1:].argmax()) + 1
118
+ if i == 0 and LABELS[k] == "、" and c in ENUMERATORS: # 開頭單個數字後不補頓號
119
+ continue
120
+ if probs[i, k] >= THRESHOLD:
121
+ out.append(LABELS[k])
122
+ return "".join(out)
123
+
124
+
125
+ print(punctuate("好的我知道了明天見")) # 好的我知道了,明天見
126
+ ```
127
+
128
+ 輸入法內每次最多送 256 字;上面的程式本身沒有長度限制。
manifest.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "zuin-punct-runtime-v1",
3
+ "labels": [
4
+ "",
5
+ ",",
6
+ "。",
7
+ "?",
8
+ "!",
9
+ "、",
10
+ ";"
11
+ ],
12
+ "threshold": 0.63,
13
+ "window": 128,
14
+ "stride": 64,
15
+ "input": "bidirectional",
16
+ "end_of_text": "never_inserted",
17
+ "max_text_runes": 256,
18
+ "files": {
19
+ "model.onnx": "4db3ed40df777347d9c455da01285b01de192d4c210c5f7cd9f0646c727880d8",
20
+ "vocab.json": "556dffaffbf4c05211a846dcee55ef735cea68c5754f9bffcc6f9588124172b3"
21
+ },
22
+ "spec": {
23
+ "hidden": 512,
24
+ "layers": 8,
25
+ "heads": 8,
26
+ "intermediate": 768
27
+ }
28
+ }
model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4db3ed40df777347d9c455da01285b01de192d4c210c5f7cd9f0646c727880d8
3
+ size 130584075
vocab.json ADDED
The diff for this file is too large to render. See raw diff