--- license: mit language: - zh tags: - chinese - traditional-chinese - taiwan - text-normalization - zh-tw - opencc library_name: twlat pipeline_tag: translation --- # Twinity-1 **中國大陸中文 → 臺灣正體中文的確定性轉換器。8.86M 參數,CPU 單執行緒 11,300 字/秒。** Twinity-1 不生成文字。字典編譯成 conversion lattice,界定「哪些位置可以改、可以改成什麼」; 模型只在每個歧義位點裁決「這個語境該不該改」;Viterbi 選出全域一致的編輯集合, 最後對原文做**最小 splice**——編輯範圍以外的每一個位元組原樣保留。 因此它結構上**不可能**改寫語句、增刪內容、或破壞外語片段與程式碼。 ```python import twlat twlat.convert("这个程序有bug,请在服务器上重新部署。") # '這個程式有 bug,請在伺服器上重新部署。' ``` ## 為什麼不用查表或 LLM | | 查表(OpenCC) | LLM 改寫 | **Twinity-1** | | --- | --- | --- | --- | | 語境判斷 | ✗ 無 | ✓ | ✓ | | 確定性 | ✓ | ✗ | ✓ 100% | | 只改該改的 | ✓ | ✗ 會潤飾/增刪 | ✓ 最小 splice | | 外語/程式碼保留 | ✓ | ✗ | ✓ 1,248/1,248 | | 成本 | $0 | API | $0 | | 延遲(p95) | <1 ms | 秒級 | 23 ms | 「程序」在軟體語境是**程式**、在法律語境就是**程序**;「里」在「那里」該轉**裡**、 在「公里」不能動。查表沒有語境;LLM 有語境但不確定且會多改。 ## 評測 TWBench-Neutral(1,496 題/8,438 個歧義位點,gold = 臺灣正體語料原文, 不由任何系統產生;gold 自身誤差經分層盲審量測並逐條修正): | 系統 | site accuracy | 維持 | 改動 | | --- | --- | --- | --- | | OpenCC | 0.9176 | 0.9411 | 0.8604 | | zhtw-mcp(規則層) | 0.9263 | 0.9510 | 0.8661 | | 前代 V1(6.06M) | 0.9456 | 0.9694 | 0.8877 | | 前代 V2(3.32M) | 0.9445 | 0.9565 | 0.9153 | | **Twinity-1(8.86M)** | **0.9712** | **0.9941** | **0.9153** | 對前代顯著勝出(+2.7pp/+2.6pp,paired bootstrap p < 0.001)。 與 frontier LLM 基線在 320 題子集上**無顯著差異**(0.9782 vs 0.9709,p = 0.34)—— 本模型不主張比 LLM 更準,而是在同等準確度下提供確定性、零成本與 23 ms 延遲。 盲測排序(103 題、14 位獨立評審、匿名、正反序雙輪):Twinity-1 平均名次 **1.296**, 優於 V2(1.612)與 V1(2.199)。 ## 怎麼訓練的 **Confusion-set cloze 自監督**,在 6.5 億字真實臺灣正體語料上: 每個 lattice 位點收合成單一 `[MASK]`,模型預測臺灣書寫者實際用了哪個形式。 這把無標註語料變成上億個監督事件,且消除了「相信輸入表面形式」的捷徑。 雙 pass 架構:masked pass 提供無洩漏的語境證據,clean pass(陸式汙染文本) 提供表面證據與文件級領域向量。候選一律由**與文本共用的字元 embedding** 動態編碼,沒有 per-candidate 查表參數——這是字典熱更新的前提。 架構:d=256,8 層(6 層 dilated TCN dilation 1..128 + 2 層 local attention), 預訓練 120k 步 + finetune 8k 步,RTX 4080 約 2.8 小時。 ## 操作點 模型與字典相同,只差「要多少證據才動手」(τ 是對數勝算比門檻): | preset | 語意 | site acc | | --- | --- | --- | | `accuracy` | 20:1 勝算才改,benchmark 最佳 | 0.9712 | | `balanced` | 2.7:1 即改,**預設** | 0.9668 | | `taiwanize` | 額外壓制陸式專用詞(視頻/博客/實時) | 0.9640 | | `aggressive` | 只靠字典硬過濾把關 | 0.9631 | ```python conv = twlat.Converter(preset="taiwanize") r = conv.explain("这个视频的信息量很大") for d in r.decisions: print(d) # Decision('視頻'→'影片' @[2,4) cross_strait u=2.75) ``` ## 熱更新 新增字典條目只需重新編譯 `data/dict/lattice_lexicon.json`,**模型權重不動**。 實測:訓練時完全沒見過的 50 條規則,zero-shot change accuracy **0.71** (前代架構同一量測為 0.12)。 ## 檔案 | 檔案 | 用途 | | --- | --- | | `twinity-1.pt` | 模型權重(8.86M 參數) | | `data/dict/lattice_lexicon.json` | 編譯後的字典(1,780 confusion group/3,977 詞形)——熱更新入口 | | `data/dict/char_vocab_v3.json` | 字元表(4,096),OOV 走 hash bucket | | `data/dict/rules/` | 簡繁字表、臺標變體表、必轉字表 | | `data/model/tau.json` | 部署操作點的決策門檻 | | `twlat/` | 推論程式碼(純 Python,相依:torch、numpy、regex、pyahocorasick、opencc) | ## 使用 ```bash pip install torch numpy regex pyahocorasick opencc-python-reimplemented ``` ```python from huggingface_hub import snapshot_download import os, sys path = snapshot_download("JacobLinCool/Twinity-1") os.environ["TWLAT_HOME"] = f"{path}/data" sys.path.insert(0, path) import twlat conv = twlat.Converter(ckpt=f"{path}/twinity-1.pt", device="cpu") print(conv.convert("这个程序有bug,请在服务器上部署")) ``` CPU 單執行緒最快(`torch.set_num_threads(1)`):11,300 字/秒、p95 23 ms。 執行緒開多反而慢 3–24 倍(小矩陣的執行緒同步成本壓倒運算)。 ## 已知限制 1. **陸式專用詞的漏轉**:`服務器→伺服器`、`搜索→搜尋` 等在訓練語料中缺少 change 方向訊號(網爬語料原生大量出現且被標為保留),模型高信心保留。 約佔殘餘 change 錯誤的 25%。修正路徑明確但需重訓。 2. **INT8 量化未達標**(−1.33pp),交付 FP32。 3. **台/臺 由語境決定**而非固定政策——多數語境正確,但官方機關名語域仍有殘餘錯誤。 若你的場景要求一律「臺」,請在後處理強制。 4. 訓練語料含 CC-100 網爬文本,可能帶有其偏誤。 5. 輸入超過 512 字會以滑動視窗處理(stride 384),跨窗的一致性未特別最佳化。 ## 授權與致謝 模型權重 MIT。字典衍生自 [zhtw-mcp](https://github.com/sysprog21/zhtw-mcp)(MIT) 與 [OpenCC](https://github.com/BYVoid/OpenCC)(Apache-2.0)——本專案的核心主張之一 正是「這些字典裡的語意資訊被嚴重低估」。訓練語料:維基百科 zh-tw(CC BY-SA 4.0)、 臺灣立法院法律研究資料(OGDL-1.0)、CC-100 繁體。