Upload BERTc-165M-CSC
Browse files- BERTc-Tokenizer.pt +3 -0
- README.md +20 -2
- tokenizer.py +8 -4
BERTc-Tokenizer.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ecc3774ba1afc0225c22147e4ff719acfa1aa9df8616befcb6c12f730ac6e07
|
| 3 |
+
size 249668
|
README.md
CHANGED
|
@@ -34,17 +34,35 @@ library_name: pytorch
|
|
| 34 |
from csc_model import BERTcForCSC
|
| 35 |
|
| 36 |
model = BERTcForCSC.from_pretrained(".")
|
| 37 |
-
print(model.correct("
|
|
|
|
| 38 |
```
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
## 阈值
|
| 41 |
|
| 42 |
`correct(..., threshold=0.7)`:纠错置信度低于阈值就保留原字。调低提召回、
|
| 43 |
调高提精确率。0.7 是与 MacBERT4CSC 对齐的默认值,报告的指标都基于它。
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
## Tokenizer
|
| 46 |
|
| 47 |
-
字级 SentencePiece,词表 12536(pad=12531,mask=12535)。**必须用 `dict="no"`
|
| 48 |
加载**(字模式,不挂分词词典)——挂了词典编码结果会跟训练时不一致,而且不报错。
|
| 49 |
|
| 50 |
```bash
|
|
|
|
| 34 |
from csc_model import BERTcForCSC
|
| 35 |
|
| 36 |
model = BERTcForCSC.from_pretrained(".")
|
| 37 |
+
print(model.correct("他平时喜欢锻练身体")) # 他平时喜欢锻炼身体
|
| 38 |
+
print(model.correct(["句子一", "句子二"])) # 也接列表
|
| 39 |
```
|
| 40 |
|
| 41 |
+
## 评测口径
|
| 42 |
+
|
| 43 |
+
指标在 **SIGHAN-15 官方 707 条**上测(`shibing624/pycorrector` 里
|
| 44 |
+
`pycorrector/data/sighan2015_test.tsv` 那一版)。注意 CTCDataset 里还有个
|
| 45 |
+
1100 条的 `sighan15_test.jsonl`,不是同一个东西。
|
| 46 |
+
|
| 47 |
+
判定是**整句**级:整句完全一致才算对,改对一半不给分。
|
| 48 |
+
|
| 49 |
## 阈值
|
| 50 |
|
| 51 |
`correct(..., threshold=0.7)`:纠错置信度低于阈值就保留原字。调低提召回、
|
| 52 |
调高提精确率。0.7 是与 MacBERT4CSC 对齐的默认值,报告的指标都基于它。
|
| 53 |
|
| 54 |
+
## 一个反直觉的行为
|
| 55 |
+
|
| 56 |
+
`correct()` **只用纠错头,不用检测头**。检测头是训练时的辅助信号,推理不参与。
|
| 57 |
+
|
| 58 |
+
所以会出现"模型知道这里有错、但选不出正确的字"的情况。比如「我今天很稿兴」,
|
| 59 |
+
`稿` 位置的检测分是 0.98,但纠错头的 top-1 仍是 `稿` 本身(0.22),
|
| 60 |
+
`高` 只排第 4(0.11)—— 这种时候**调低阈值没有任何用**,阈值只能否决改动,
|
| 61 |
+
不能凭空造出改动。
|
| 62 |
+
|
| 63 |
## Tokenizer
|
| 64 |
|
| 65 |
+
字级 SentencePiece,`BERTc-Tokenizer.pt`,词表 12536(pad=12531,mask=12535)。**必须用 `dict="no"`
|
| 66 |
加载**(字模式,不挂分词词典)——挂了词典编码结果会跟训练时不一致,而且不报错。
|
| 67 |
|
| 68 |
```bash
|
tokenizer.py
CHANGED
|
@@ -3,6 +3,9 @@
|
|
| 3 |
这份代码会**随模型一起发到 HF**,所以只能依赖 piece_tokenizer 本身,
|
| 4 |
不能 import 仓库里的任何东西。
|
| 5 |
|
|
|
|
|
|
|
|
|
|
| 6 |
装 tokenizer:
|
| 7 |
pip install git+https://github.com/Ismantic/PieceTokenizer
|
| 8 |
"""
|
|
@@ -18,16 +21,17 @@ class PieceCharTokenizer:
|
|
| 18 |
训练时不一致,而且不会报错。
|
| 19 |
"""
|
| 20 |
|
|
|
|
|
|
|
| 21 |
def __init__(self, model_dir="."):
|
| 22 |
model_dir = Path(model_dir)
|
| 23 |
self._tok = _pt.Tokenizer()
|
| 24 |
-
self._tok.load(str(model_dir /
|
| 25 |
|
| 26 |
self.pad_token_id = self._tok.piece_to_id("<pad>")
|
| 27 |
self.unk_token_id = 0
|
| 28 |
-
|
| 29 |
-
self.mask_token_id =
|
| 30 |
-
if mask_path.exists() else self._tok.vocab_size())
|
| 31 |
self.vocab_size = self._tok.vocab_size() + 1
|
| 32 |
self._cache = {}
|
| 33 |
|
|
|
|
| 3 |
这份代码会**随模型一起发到 HF**,所以只能依赖 piece_tokenizer 本身,
|
| 4 |
不能 import 仓库里的任何东西。
|
| 5 |
|
| 6 |
+
词表文件 BERTc-Tokenizer.pt 与 PieceTokenizer 仓库 save/ 下的那份逐字节相同 ——
|
| 7 |
+
同名是为了让来源一目了然。
|
| 8 |
+
|
| 9 |
装 tokenizer:
|
| 10 |
pip install git+https://github.com/Ismantic/PieceTokenizer
|
| 11 |
"""
|
|
|
|
| 21 |
训练时不一致,而且不会报错。
|
| 22 |
"""
|
| 23 |
|
| 24 |
+
MODEL_NAME = "BERTc-Tokenizer.pt"
|
| 25 |
+
|
| 26 |
def __init__(self, model_dir="."):
|
| 27 |
model_dir = Path(model_dir)
|
| 28 |
self._tok = _pt.Tokenizer()
|
| 29 |
+
self._tok.load(str(model_dir / self.MODEL_NAME), dict="no")
|
| 30 |
|
| 31 |
self.pad_token_id = self._tok.piece_to_id("<pad>")
|
| 32 |
self.unk_token_id = 0
|
| 33 |
+
# [MASK] 追加在 piece 词表之后,id 就等于词表大小 —— 不需要单独存一个文件
|
| 34 |
+
self.mask_token_id = self._tok.vocab_size()
|
|
|
|
| 35 |
self.vocab_size = self._tok.vocab_size() + 1
|
| 36 |
self._cache = {}
|
| 37 |
|