Release frozen D512 ONNX weights under MIT
Browse filesIncludes the cleaned ONNX model, vocabulary, inference manifest, MIT license, and model card with CPU inference examples.
- LICENSE +21 -0
- README.md +128 -0
- manifest.json +28 -0
- model.onnx +3 -0
- 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
|
|
|