Danny commited on
Bilingual humanize-text-model (EN T5-small + ZH Chinese T5-small)
Browse files- LICENSE +21 -0
- README.md +126 -0
- en/added_tokens.json +102 -0
- en/config.json +60 -0
- en/generation_config.json +7 -0
- en/model.safetensors +3 -0
- en/special_tokens_map.json +125 -0
- en/spiece.model +3 -0
- en/tokenizer_config.json +941 -0
- humanize.py +224 -0
- requirements.txt +4 -0
- scripts/build_dataset.py +335 -0
- scripts/evaluate.py +154 -0
- scripts/train.py +175 -0
- scripts/upload_model.py +43 -0
- src/rules.py +201 -0
- tests/__init__.py +2 -0
- tests/test_dataset.py +151 -0
- tests/test_humanize.py +114 -0
- zh/config.json +31 -0
- zh/generation_config.json +7 -0
- zh/model.safetensors +3 -0
- zh/special_tokens_map.json +7 -0
- zh/tokenizer_config.json +58 -0
- zh/vocab.txt +0 -0
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Lynote
|
| 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,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- zh
|
| 6 |
+
tags:
|
| 7 |
+
- text2text-generation
|
| 8 |
+
- text-humanizer
|
| 9 |
+
- rewriting
|
| 10 |
+
- writing-assistant
|
| 11 |
+
- bilingual
|
| 12 |
+
- responsible-ai
|
| 13 |
+
pipeline_tag: text2text-generation
|
| 14 |
+
base_model:
|
| 15 |
+
- google-t5/t5-small
|
| 16 |
+
- uer/t5-small-chinese-cluecorpussmall
|
| 17 |
+
inference: true
|
| 18 |
+
widget:
|
| 19 |
+
- text: "It is important to note that this robust solution serves as a testament to our commitment to innovation. Moreover, we leverage cutting-edge technology."
|
| 20 |
+
- text: "值得注意的是,在当今快速发展的时代,我们通过赋能团队来助力企业实现降本增效。"
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
# Humanize Text Model (Lynote)
|
| 24 |
+
|
| 25 |
+
A lightweight bilingual (English/Chinese) humanizer: two small T5-seq2seq
|
| 26 |
+
checkpoints in one repository that rewrite AI-flavored prose into more
|
| 27 |
+
natural, human-flavored prose.
|
| 28 |
+
|
| 29 |
+
- **`en/`** — fine-tuned `google-t5/t5-small` (English)
|
| 30 |
+
- **`zh/`** — fine-tuned `uer/t5-small-chinese-cluecorpussmall` (Chinese)
|
| 31 |
+
|
| 32 |
+
## What it does
|
| 33 |
+
|
| 34 |
+
- Removes high-confidence AI clichés and formulaic phrases (e.g.
|
| 35 |
+
*"it is important to note that"*, *"moreover"*, *"值得注意的是"*,
|
| 36 |
+
*"降本增效"*).
|
| 37 |
+
- Keeps already-human prose nearly untouched (identity learning).
|
| 38 |
+
- Preserves numbers, URLs, file paths, code and quoted text (protected with
|
| 39 |
+
`PROTECTED_N` placeholders during generation, restored afterwards).
|
| 40 |
+
- Routes automatically by language (CJK ratio detection).
|
| 41 |
+
|
| 42 |
+
## What it is NOT
|
| 43 |
+
|
| 44 |
+
This is a **writing-quality aid**, not a tool for evading AI detectors.
|
| 45 |
+
Detector scores are probabilistic, and no humanizer can guarantee that text
|
| 46 |
+
will be classified as human. Please use it responsibly:
|
| 47 |
+
do not use it to misrepresent authorship in academic, legal, or disciplinary
|
| 48 |
+
contexts.
|
| 49 |
+
|
| 50 |
+
## Quickstart
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
pip install transformers torch
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
```python
|
| 57 |
+
from humanize import Humanizer # the wrapper bundled in this repo
|
| 58 |
+
|
| 59 |
+
h = Humanizer() # loads this repo (en/ and zh/ sub-checkpoints)
|
| 60 |
+
print(h.humanize(
|
| 61 |
+
"It is important to note that this robust solution serves as a "
|
| 62 |
+
"testament to our commitment. Moreover, we leverage cutting-edge "
|
| 63 |
+
"technology."
|
| 64 |
+
))
|
| 65 |
+
print(h.humanize("值得注意的是,我们通过赋能团队来实现降本增效。"))
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
Raw `transformers` usage (no wrapper):
|
| 69 |
+
|
| 70 |
+
```python
|
| 71 |
+
from transformers import T5ForConditionalGeneration, T5Tokenizer
|
| 72 |
+
|
| 73 |
+
model = T5ForConditionalGeneration.from_pretrained("Danny-Lynote/humanize-text-model/en")
|
| 74 |
+
tokenizer = T5Tokenizer.from_pretrained("Danny-Lynote/humanize-text-model/en")
|
| 75 |
+
inputs = tokenizer("It is important to note that this is robust.", return_tensors="pt")
|
| 76 |
+
print(tokenizer.decode(model.generate(**inputs, max_length=128)[0], skip_special_tokens=True))
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
For Chinese use the `zh/` sub-checkpoint with `BertTokenizer`.
|
| 80 |
+
|
| 81 |
+
## Training data
|
| 82 |
+
|
| 83 |
+
The corpus is generated deterministically from the editing principles of the
|
| 84 |
+
Lynote reference projects (`humanize-text`, `humanize-text-skill`,
|
| 85 |
+
`humanizer-lite`):
|
| 86 |
+
|
| 87 |
+
1. **AI → human**: formulaic clause combinations rewritten by a conservative
|
| 88 |
+
rule engine,
|
| 89 |
+
2. **human → human (identity)**: clean prose unchanged, so the model learns
|
| 90 |
+
not to rewrite good text,
|
| 91 |
+
3. **mixed**: clean prose with one injected cliché that must be removed,
|
| 92 |
+
4. **protected spans**: examples with URLs, numbers, code and quotes.
|
| 93 |
+
|
| 94 |
+
Reproduce:
|
| 95 |
+
|
| 96 |
+
```bash
|
| 97 |
+
python scripts/build_dataset.py --out data # 14.7k pairs (en + zh)
|
| 98 |
+
python scripts/train.py --lang en --epochs 3 # -> checkpoints/humanize-text-model/en
|
| 99 |
+
python scripts/train.py --lang zh --epochs 3 # -> checkpoints/humanize-text-model/zh
|
| 100 |
+
python scripts/evaluate.py # benchmark on held-out test
|
| 101 |
+
pytest tests/ # full test suite
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
## Evaluation (held-out test, 500 AI->human + all identity/protected pairs)
|
| 105 |
+
|
| 106 |
+
| Metric | Value |
|
| 107 |
+
|---|---|
|
| 108 |
+
| Corpus BLEU vs rule reference | **99.2** |
|
| 109 |
+
| Cliché removal rate | **100.0%** (435/435) |
|
| 110 |
+
| Identity stability (clean prose, n=67) | **73.1%** |
|
| 111 |
+
| Protected-span preservation (n=253) | **96.8%** |
|
| 112 |
+
| Throughput (MPS) | ~218 chars/s |
|
| 113 |
+
|
| 114 |
+
## Limitations
|
| 115 |
+
|
| 116 |
+
- Trained on synthetic text; real-world inputs may need light post-editing.
|
| 117 |
+
- One checkpoint per language (English / Chinese); other languages are not
|
| 118 |
+
specifically trained.
|
| 119 |
+
- Long inputs are truncated to 256 tokens.
|
| 120 |
+
- It is a conservative editor: it will not add stylistic richness that is
|
| 121 |
+
absent from the source text.
|
| 122 |
+
|
| 123 |
+
## License
|
| 124 |
+
|
| 125 |
+
MIT. Base models: `google-t5/t5-small` (Apache-2.0) and
|
| 126 |
+
`uer/t5-small-chinese-cluecorpussmall` (Apache-2.0).
|
en/added_tokens.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"<extra_id_0>": 32099,
|
| 3 |
+
"<extra_id_10>": 32089,
|
| 4 |
+
"<extra_id_11>": 32088,
|
| 5 |
+
"<extra_id_12>": 32087,
|
| 6 |
+
"<extra_id_13>": 32086,
|
| 7 |
+
"<extra_id_14>": 32085,
|
| 8 |
+
"<extra_id_15>": 32084,
|
| 9 |
+
"<extra_id_16>": 32083,
|
| 10 |
+
"<extra_id_17>": 32082,
|
| 11 |
+
"<extra_id_18>": 32081,
|
| 12 |
+
"<extra_id_19>": 32080,
|
| 13 |
+
"<extra_id_1>": 32098,
|
| 14 |
+
"<extra_id_20>": 32079,
|
| 15 |
+
"<extra_id_21>": 32078,
|
| 16 |
+
"<extra_id_22>": 32077,
|
| 17 |
+
"<extra_id_23>": 32076,
|
| 18 |
+
"<extra_id_24>": 32075,
|
| 19 |
+
"<extra_id_25>": 32074,
|
| 20 |
+
"<extra_id_26>": 32073,
|
| 21 |
+
"<extra_id_27>": 32072,
|
| 22 |
+
"<extra_id_28>": 32071,
|
| 23 |
+
"<extra_id_29>": 32070,
|
| 24 |
+
"<extra_id_2>": 32097,
|
| 25 |
+
"<extra_id_30>": 32069,
|
| 26 |
+
"<extra_id_31>": 32068,
|
| 27 |
+
"<extra_id_32>": 32067,
|
| 28 |
+
"<extra_id_33>": 32066,
|
| 29 |
+
"<extra_id_34>": 32065,
|
| 30 |
+
"<extra_id_35>": 32064,
|
| 31 |
+
"<extra_id_36>": 32063,
|
| 32 |
+
"<extra_id_37>": 32062,
|
| 33 |
+
"<extra_id_38>": 32061,
|
| 34 |
+
"<extra_id_39>": 32060,
|
| 35 |
+
"<extra_id_3>": 32096,
|
| 36 |
+
"<extra_id_40>": 32059,
|
| 37 |
+
"<extra_id_41>": 32058,
|
| 38 |
+
"<extra_id_42>": 32057,
|
| 39 |
+
"<extra_id_43>": 32056,
|
| 40 |
+
"<extra_id_44>": 32055,
|
| 41 |
+
"<extra_id_45>": 32054,
|
| 42 |
+
"<extra_id_46>": 32053,
|
| 43 |
+
"<extra_id_47>": 32052,
|
| 44 |
+
"<extra_id_48>": 32051,
|
| 45 |
+
"<extra_id_49>": 32050,
|
| 46 |
+
"<extra_id_4>": 32095,
|
| 47 |
+
"<extra_id_50>": 32049,
|
| 48 |
+
"<extra_id_51>": 32048,
|
| 49 |
+
"<extra_id_52>": 32047,
|
| 50 |
+
"<extra_id_53>": 32046,
|
| 51 |
+
"<extra_id_54>": 32045,
|
| 52 |
+
"<extra_id_55>": 32044,
|
| 53 |
+
"<extra_id_56>": 32043,
|
| 54 |
+
"<extra_id_57>": 32042,
|
| 55 |
+
"<extra_id_58>": 32041,
|
| 56 |
+
"<extra_id_59>": 32040,
|
| 57 |
+
"<extra_id_5>": 32094,
|
| 58 |
+
"<extra_id_60>": 32039,
|
| 59 |
+
"<extra_id_61>": 32038,
|
| 60 |
+
"<extra_id_62>": 32037,
|
| 61 |
+
"<extra_id_63>": 32036,
|
| 62 |
+
"<extra_id_64>": 32035,
|
| 63 |
+
"<extra_id_65>": 32034,
|
| 64 |
+
"<extra_id_66>": 32033,
|
| 65 |
+
"<extra_id_67>": 32032,
|
| 66 |
+
"<extra_id_68>": 32031,
|
| 67 |
+
"<extra_id_69>": 32030,
|
| 68 |
+
"<extra_id_6>": 32093,
|
| 69 |
+
"<extra_id_70>": 32029,
|
| 70 |
+
"<extra_id_71>": 32028,
|
| 71 |
+
"<extra_id_72>": 32027,
|
| 72 |
+
"<extra_id_73>": 32026,
|
| 73 |
+
"<extra_id_74>": 32025,
|
| 74 |
+
"<extra_id_75>": 32024,
|
| 75 |
+
"<extra_id_76>": 32023,
|
| 76 |
+
"<extra_id_77>": 32022,
|
| 77 |
+
"<extra_id_78>": 32021,
|
| 78 |
+
"<extra_id_79>": 32020,
|
| 79 |
+
"<extra_id_7>": 32092,
|
| 80 |
+
"<extra_id_80>": 32019,
|
| 81 |
+
"<extra_id_81>": 32018,
|
| 82 |
+
"<extra_id_82>": 32017,
|
| 83 |
+
"<extra_id_83>": 32016,
|
| 84 |
+
"<extra_id_84>": 32015,
|
| 85 |
+
"<extra_id_85>": 32014,
|
| 86 |
+
"<extra_id_86>": 32013,
|
| 87 |
+
"<extra_id_87>": 32012,
|
| 88 |
+
"<extra_id_88>": 32011,
|
| 89 |
+
"<extra_id_89>": 32010,
|
| 90 |
+
"<extra_id_8>": 32091,
|
| 91 |
+
"<extra_id_90>": 32009,
|
| 92 |
+
"<extra_id_91>": 32008,
|
| 93 |
+
"<extra_id_92>": 32007,
|
| 94 |
+
"<extra_id_93>": 32006,
|
| 95 |
+
"<extra_id_94>": 32005,
|
| 96 |
+
"<extra_id_95>": 32004,
|
| 97 |
+
"<extra_id_96>": 32003,
|
| 98 |
+
"<extra_id_97>": 32002,
|
| 99 |
+
"<extra_id_98>": 32001,
|
| 100 |
+
"<extra_id_99>": 32000,
|
| 101 |
+
"<extra_id_9>": 32090
|
| 102 |
+
}
|
en/config.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"T5ForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"classifier_dropout": 0.0,
|
| 6 |
+
"d_ff": 2048,
|
| 7 |
+
"d_kv": 64,
|
| 8 |
+
"d_model": 512,
|
| 9 |
+
"decoder_start_token_id": 0,
|
| 10 |
+
"dense_act_fn": "relu",
|
| 11 |
+
"dropout_rate": 0.1,
|
| 12 |
+
"dtype": "float32",
|
| 13 |
+
"eos_token_id": 1,
|
| 14 |
+
"feed_forward_proj": "relu",
|
| 15 |
+
"initializer_factor": 1.0,
|
| 16 |
+
"is_encoder_decoder": true,
|
| 17 |
+
"is_gated_act": false,
|
| 18 |
+
"layer_norm_epsilon": 1e-06,
|
| 19 |
+
"model_type": "t5",
|
| 20 |
+
"n_positions": 512,
|
| 21 |
+
"num_decoder_layers": 6,
|
| 22 |
+
"num_heads": 8,
|
| 23 |
+
"num_layers": 6,
|
| 24 |
+
"output_past": true,
|
| 25 |
+
"pad_token_id": 0,
|
| 26 |
+
"relative_attention_max_distance": 128,
|
| 27 |
+
"relative_attention_num_buckets": 32,
|
| 28 |
+
"task_specific_params": {
|
| 29 |
+
"summarization": {
|
| 30 |
+
"early_stopping": true,
|
| 31 |
+
"length_penalty": 2.0,
|
| 32 |
+
"max_length": 200,
|
| 33 |
+
"min_length": 30,
|
| 34 |
+
"no_repeat_ngram_size": 3,
|
| 35 |
+
"num_beams": 4,
|
| 36 |
+
"prefix": "summarize: "
|
| 37 |
+
},
|
| 38 |
+
"translation_en_to_de": {
|
| 39 |
+
"early_stopping": true,
|
| 40 |
+
"max_length": 300,
|
| 41 |
+
"num_beams": 4,
|
| 42 |
+
"prefix": "translate English to German: "
|
| 43 |
+
},
|
| 44 |
+
"translation_en_to_fr": {
|
| 45 |
+
"early_stopping": true,
|
| 46 |
+
"max_length": 300,
|
| 47 |
+
"num_beams": 4,
|
| 48 |
+
"prefix": "translate English to French: "
|
| 49 |
+
},
|
| 50 |
+
"translation_en_to_ro": {
|
| 51 |
+
"early_stopping": true,
|
| 52 |
+
"max_length": 300,
|
| 53 |
+
"num_beams": 4,
|
| 54 |
+
"prefix": "translate English to Romanian: "
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"transformers_version": "4.57.6",
|
| 58 |
+
"use_cache": true,
|
| 59 |
+
"vocab_size": 32128
|
| 60 |
+
}
|
en/generation_config.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"decoder_start_token_id": 0,
|
| 4 |
+
"eos_token_id": 1,
|
| 5 |
+
"pad_token_id": 0,
|
| 6 |
+
"transformers_version": "4.57.6"
|
| 7 |
+
}
|
en/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:13fd26d16fd54619d6b2a3c3e5061451fa9bf5167312f9c47aa074df1c5e80e6
|
| 3 |
+
size 242041896
|
en/special_tokens_map.json
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"additional_special_tokens": [
|
| 3 |
+
"<extra_id_0>",
|
| 4 |
+
"<extra_id_1>",
|
| 5 |
+
"<extra_id_2>",
|
| 6 |
+
"<extra_id_3>",
|
| 7 |
+
"<extra_id_4>",
|
| 8 |
+
"<extra_id_5>",
|
| 9 |
+
"<extra_id_6>",
|
| 10 |
+
"<extra_id_7>",
|
| 11 |
+
"<extra_id_8>",
|
| 12 |
+
"<extra_id_9>",
|
| 13 |
+
"<extra_id_10>",
|
| 14 |
+
"<extra_id_11>",
|
| 15 |
+
"<extra_id_12>",
|
| 16 |
+
"<extra_id_13>",
|
| 17 |
+
"<extra_id_14>",
|
| 18 |
+
"<extra_id_15>",
|
| 19 |
+
"<extra_id_16>",
|
| 20 |
+
"<extra_id_17>",
|
| 21 |
+
"<extra_id_18>",
|
| 22 |
+
"<extra_id_19>",
|
| 23 |
+
"<extra_id_20>",
|
| 24 |
+
"<extra_id_21>",
|
| 25 |
+
"<extra_id_22>",
|
| 26 |
+
"<extra_id_23>",
|
| 27 |
+
"<extra_id_24>",
|
| 28 |
+
"<extra_id_25>",
|
| 29 |
+
"<extra_id_26>",
|
| 30 |
+
"<extra_id_27>",
|
| 31 |
+
"<extra_id_28>",
|
| 32 |
+
"<extra_id_29>",
|
| 33 |
+
"<extra_id_30>",
|
| 34 |
+
"<extra_id_31>",
|
| 35 |
+
"<extra_id_32>",
|
| 36 |
+
"<extra_id_33>",
|
| 37 |
+
"<extra_id_34>",
|
| 38 |
+
"<extra_id_35>",
|
| 39 |
+
"<extra_id_36>",
|
| 40 |
+
"<extra_id_37>",
|
| 41 |
+
"<extra_id_38>",
|
| 42 |
+
"<extra_id_39>",
|
| 43 |
+
"<extra_id_40>",
|
| 44 |
+
"<extra_id_41>",
|
| 45 |
+
"<extra_id_42>",
|
| 46 |
+
"<extra_id_43>",
|
| 47 |
+
"<extra_id_44>",
|
| 48 |
+
"<extra_id_45>",
|
| 49 |
+
"<extra_id_46>",
|
| 50 |
+
"<extra_id_47>",
|
| 51 |
+
"<extra_id_48>",
|
| 52 |
+
"<extra_id_49>",
|
| 53 |
+
"<extra_id_50>",
|
| 54 |
+
"<extra_id_51>",
|
| 55 |
+
"<extra_id_52>",
|
| 56 |
+
"<extra_id_53>",
|
| 57 |
+
"<extra_id_54>",
|
| 58 |
+
"<extra_id_55>",
|
| 59 |
+
"<extra_id_56>",
|
| 60 |
+
"<extra_id_57>",
|
| 61 |
+
"<extra_id_58>",
|
| 62 |
+
"<extra_id_59>",
|
| 63 |
+
"<extra_id_60>",
|
| 64 |
+
"<extra_id_61>",
|
| 65 |
+
"<extra_id_62>",
|
| 66 |
+
"<extra_id_63>",
|
| 67 |
+
"<extra_id_64>",
|
| 68 |
+
"<extra_id_65>",
|
| 69 |
+
"<extra_id_66>",
|
| 70 |
+
"<extra_id_67>",
|
| 71 |
+
"<extra_id_68>",
|
| 72 |
+
"<extra_id_69>",
|
| 73 |
+
"<extra_id_70>",
|
| 74 |
+
"<extra_id_71>",
|
| 75 |
+
"<extra_id_72>",
|
| 76 |
+
"<extra_id_73>",
|
| 77 |
+
"<extra_id_74>",
|
| 78 |
+
"<extra_id_75>",
|
| 79 |
+
"<extra_id_76>",
|
| 80 |
+
"<extra_id_77>",
|
| 81 |
+
"<extra_id_78>",
|
| 82 |
+
"<extra_id_79>",
|
| 83 |
+
"<extra_id_80>",
|
| 84 |
+
"<extra_id_81>",
|
| 85 |
+
"<extra_id_82>",
|
| 86 |
+
"<extra_id_83>",
|
| 87 |
+
"<extra_id_84>",
|
| 88 |
+
"<extra_id_85>",
|
| 89 |
+
"<extra_id_86>",
|
| 90 |
+
"<extra_id_87>",
|
| 91 |
+
"<extra_id_88>",
|
| 92 |
+
"<extra_id_89>",
|
| 93 |
+
"<extra_id_90>",
|
| 94 |
+
"<extra_id_91>",
|
| 95 |
+
"<extra_id_92>",
|
| 96 |
+
"<extra_id_93>",
|
| 97 |
+
"<extra_id_94>",
|
| 98 |
+
"<extra_id_95>",
|
| 99 |
+
"<extra_id_96>",
|
| 100 |
+
"<extra_id_97>",
|
| 101 |
+
"<extra_id_98>",
|
| 102 |
+
"<extra_id_99>"
|
| 103 |
+
],
|
| 104 |
+
"eos_token": {
|
| 105 |
+
"content": "</s>",
|
| 106 |
+
"lstrip": false,
|
| 107 |
+
"normalized": false,
|
| 108 |
+
"rstrip": false,
|
| 109 |
+
"single_word": false
|
| 110 |
+
},
|
| 111 |
+
"pad_token": {
|
| 112 |
+
"content": "<pad>",
|
| 113 |
+
"lstrip": false,
|
| 114 |
+
"normalized": false,
|
| 115 |
+
"rstrip": false,
|
| 116 |
+
"single_word": false
|
| 117 |
+
},
|
| 118 |
+
"unk_token": {
|
| 119 |
+
"content": "<unk>",
|
| 120 |
+
"lstrip": false,
|
| 121 |
+
"normalized": false,
|
| 122 |
+
"rstrip": false,
|
| 123 |
+
"single_word": false
|
| 124 |
+
}
|
| 125 |
+
}
|
en/spiece.model
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d60acb128cf7b7f2536e8f38a5b18a05535c9e14c7a355904270e15b0945ea86
|
| 3 |
+
size 791656
|
en/tokenizer_config.json
ADDED
|
@@ -0,0 +1,941 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": true,
|
| 3 |
+
"added_tokens_decoder": {
|
| 4 |
+
"0": {
|
| 5 |
+
"content": "<pad>",
|
| 6 |
+
"lstrip": false,
|
| 7 |
+
"normalized": false,
|
| 8 |
+
"rstrip": false,
|
| 9 |
+
"single_word": false,
|
| 10 |
+
"special": true
|
| 11 |
+
},
|
| 12 |
+
"1": {
|
| 13 |
+
"content": "</s>",
|
| 14 |
+
"lstrip": false,
|
| 15 |
+
"normalized": false,
|
| 16 |
+
"rstrip": false,
|
| 17 |
+
"single_word": false,
|
| 18 |
+
"special": true
|
| 19 |
+
},
|
| 20 |
+
"2": {
|
| 21 |
+
"content": "<unk>",
|
| 22 |
+
"lstrip": false,
|
| 23 |
+
"normalized": false,
|
| 24 |
+
"rstrip": false,
|
| 25 |
+
"single_word": false,
|
| 26 |
+
"special": true
|
| 27 |
+
},
|
| 28 |
+
"32000": {
|
| 29 |
+
"content": "<extra_id_99>",
|
| 30 |
+
"lstrip": false,
|
| 31 |
+
"normalized": false,
|
| 32 |
+
"rstrip": false,
|
| 33 |
+
"single_word": false,
|
| 34 |
+
"special": true
|
| 35 |
+
},
|
| 36 |
+
"32001": {
|
| 37 |
+
"content": "<extra_id_98>",
|
| 38 |
+
"lstrip": false,
|
| 39 |
+
"normalized": false,
|
| 40 |
+
"rstrip": false,
|
| 41 |
+
"single_word": false,
|
| 42 |
+
"special": true
|
| 43 |
+
},
|
| 44 |
+
"32002": {
|
| 45 |
+
"content": "<extra_id_97>",
|
| 46 |
+
"lstrip": false,
|
| 47 |
+
"normalized": false,
|
| 48 |
+
"rstrip": false,
|
| 49 |
+
"single_word": false,
|
| 50 |
+
"special": true
|
| 51 |
+
},
|
| 52 |
+
"32003": {
|
| 53 |
+
"content": "<extra_id_96>",
|
| 54 |
+
"lstrip": false,
|
| 55 |
+
"normalized": false,
|
| 56 |
+
"rstrip": false,
|
| 57 |
+
"single_word": false,
|
| 58 |
+
"special": true
|
| 59 |
+
},
|
| 60 |
+
"32004": {
|
| 61 |
+
"content": "<extra_id_95>",
|
| 62 |
+
"lstrip": false,
|
| 63 |
+
"normalized": false,
|
| 64 |
+
"rstrip": false,
|
| 65 |
+
"single_word": false,
|
| 66 |
+
"special": true
|
| 67 |
+
},
|
| 68 |
+
"32005": {
|
| 69 |
+
"content": "<extra_id_94>",
|
| 70 |
+
"lstrip": false,
|
| 71 |
+
"normalized": false,
|
| 72 |
+
"rstrip": false,
|
| 73 |
+
"single_word": false,
|
| 74 |
+
"special": true
|
| 75 |
+
},
|
| 76 |
+
"32006": {
|
| 77 |
+
"content": "<extra_id_93>",
|
| 78 |
+
"lstrip": false,
|
| 79 |
+
"normalized": false,
|
| 80 |
+
"rstrip": false,
|
| 81 |
+
"single_word": false,
|
| 82 |
+
"special": true
|
| 83 |
+
},
|
| 84 |
+
"32007": {
|
| 85 |
+
"content": "<extra_id_92>",
|
| 86 |
+
"lstrip": false,
|
| 87 |
+
"normalized": false,
|
| 88 |
+
"rstrip": false,
|
| 89 |
+
"single_word": false,
|
| 90 |
+
"special": true
|
| 91 |
+
},
|
| 92 |
+
"32008": {
|
| 93 |
+
"content": "<extra_id_91>",
|
| 94 |
+
"lstrip": false,
|
| 95 |
+
"normalized": false,
|
| 96 |
+
"rstrip": false,
|
| 97 |
+
"single_word": false,
|
| 98 |
+
"special": true
|
| 99 |
+
},
|
| 100 |
+
"32009": {
|
| 101 |
+
"content": "<extra_id_90>",
|
| 102 |
+
"lstrip": false,
|
| 103 |
+
"normalized": false,
|
| 104 |
+
"rstrip": false,
|
| 105 |
+
"single_word": false,
|
| 106 |
+
"special": true
|
| 107 |
+
},
|
| 108 |
+
"32010": {
|
| 109 |
+
"content": "<extra_id_89>",
|
| 110 |
+
"lstrip": false,
|
| 111 |
+
"normalized": false,
|
| 112 |
+
"rstrip": false,
|
| 113 |
+
"single_word": false,
|
| 114 |
+
"special": true
|
| 115 |
+
},
|
| 116 |
+
"32011": {
|
| 117 |
+
"content": "<extra_id_88>",
|
| 118 |
+
"lstrip": false,
|
| 119 |
+
"normalized": false,
|
| 120 |
+
"rstrip": false,
|
| 121 |
+
"single_word": false,
|
| 122 |
+
"special": true
|
| 123 |
+
},
|
| 124 |
+
"32012": {
|
| 125 |
+
"content": "<extra_id_87>",
|
| 126 |
+
"lstrip": false,
|
| 127 |
+
"normalized": false,
|
| 128 |
+
"rstrip": false,
|
| 129 |
+
"single_word": false,
|
| 130 |
+
"special": true
|
| 131 |
+
},
|
| 132 |
+
"32013": {
|
| 133 |
+
"content": "<extra_id_86>",
|
| 134 |
+
"lstrip": false,
|
| 135 |
+
"normalized": false,
|
| 136 |
+
"rstrip": false,
|
| 137 |
+
"single_word": false,
|
| 138 |
+
"special": true
|
| 139 |
+
},
|
| 140 |
+
"32014": {
|
| 141 |
+
"content": "<extra_id_85>",
|
| 142 |
+
"lstrip": false,
|
| 143 |
+
"normalized": false,
|
| 144 |
+
"rstrip": false,
|
| 145 |
+
"single_word": false,
|
| 146 |
+
"special": true
|
| 147 |
+
},
|
| 148 |
+
"32015": {
|
| 149 |
+
"content": "<extra_id_84>",
|
| 150 |
+
"lstrip": false,
|
| 151 |
+
"normalized": false,
|
| 152 |
+
"rstrip": false,
|
| 153 |
+
"single_word": false,
|
| 154 |
+
"special": true
|
| 155 |
+
},
|
| 156 |
+
"32016": {
|
| 157 |
+
"content": "<extra_id_83>",
|
| 158 |
+
"lstrip": false,
|
| 159 |
+
"normalized": false,
|
| 160 |
+
"rstrip": false,
|
| 161 |
+
"single_word": false,
|
| 162 |
+
"special": true
|
| 163 |
+
},
|
| 164 |
+
"32017": {
|
| 165 |
+
"content": "<extra_id_82>",
|
| 166 |
+
"lstrip": false,
|
| 167 |
+
"normalized": false,
|
| 168 |
+
"rstrip": false,
|
| 169 |
+
"single_word": false,
|
| 170 |
+
"special": true
|
| 171 |
+
},
|
| 172 |
+
"32018": {
|
| 173 |
+
"content": "<extra_id_81>",
|
| 174 |
+
"lstrip": false,
|
| 175 |
+
"normalized": false,
|
| 176 |
+
"rstrip": false,
|
| 177 |
+
"single_word": false,
|
| 178 |
+
"special": true
|
| 179 |
+
},
|
| 180 |
+
"32019": {
|
| 181 |
+
"content": "<extra_id_80>",
|
| 182 |
+
"lstrip": false,
|
| 183 |
+
"normalized": false,
|
| 184 |
+
"rstrip": false,
|
| 185 |
+
"single_word": false,
|
| 186 |
+
"special": true
|
| 187 |
+
},
|
| 188 |
+
"32020": {
|
| 189 |
+
"content": "<extra_id_79>",
|
| 190 |
+
"lstrip": false,
|
| 191 |
+
"normalized": false,
|
| 192 |
+
"rstrip": false,
|
| 193 |
+
"single_word": false,
|
| 194 |
+
"special": true
|
| 195 |
+
},
|
| 196 |
+
"32021": {
|
| 197 |
+
"content": "<extra_id_78>",
|
| 198 |
+
"lstrip": false,
|
| 199 |
+
"normalized": false,
|
| 200 |
+
"rstrip": false,
|
| 201 |
+
"single_word": false,
|
| 202 |
+
"special": true
|
| 203 |
+
},
|
| 204 |
+
"32022": {
|
| 205 |
+
"content": "<extra_id_77>",
|
| 206 |
+
"lstrip": false,
|
| 207 |
+
"normalized": false,
|
| 208 |
+
"rstrip": false,
|
| 209 |
+
"single_word": false,
|
| 210 |
+
"special": true
|
| 211 |
+
},
|
| 212 |
+
"32023": {
|
| 213 |
+
"content": "<extra_id_76>",
|
| 214 |
+
"lstrip": false,
|
| 215 |
+
"normalized": false,
|
| 216 |
+
"rstrip": false,
|
| 217 |
+
"single_word": false,
|
| 218 |
+
"special": true
|
| 219 |
+
},
|
| 220 |
+
"32024": {
|
| 221 |
+
"content": "<extra_id_75>",
|
| 222 |
+
"lstrip": false,
|
| 223 |
+
"normalized": false,
|
| 224 |
+
"rstrip": false,
|
| 225 |
+
"single_word": false,
|
| 226 |
+
"special": true
|
| 227 |
+
},
|
| 228 |
+
"32025": {
|
| 229 |
+
"content": "<extra_id_74>",
|
| 230 |
+
"lstrip": false,
|
| 231 |
+
"normalized": false,
|
| 232 |
+
"rstrip": false,
|
| 233 |
+
"single_word": false,
|
| 234 |
+
"special": true
|
| 235 |
+
},
|
| 236 |
+
"32026": {
|
| 237 |
+
"content": "<extra_id_73>",
|
| 238 |
+
"lstrip": false,
|
| 239 |
+
"normalized": false,
|
| 240 |
+
"rstrip": false,
|
| 241 |
+
"single_word": false,
|
| 242 |
+
"special": true
|
| 243 |
+
},
|
| 244 |
+
"32027": {
|
| 245 |
+
"content": "<extra_id_72>",
|
| 246 |
+
"lstrip": false,
|
| 247 |
+
"normalized": false,
|
| 248 |
+
"rstrip": false,
|
| 249 |
+
"single_word": false,
|
| 250 |
+
"special": true
|
| 251 |
+
},
|
| 252 |
+
"32028": {
|
| 253 |
+
"content": "<extra_id_71>",
|
| 254 |
+
"lstrip": false,
|
| 255 |
+
"normalized": false,
|
| 256 |
+
"rstrip": false,
|
| 257 |
+
"single_word": false,
|
| 258 |
+
"special": true
|
| 259 |
+
},
|
| 260 |
+
"32029": {
|
| 261 |
+
"content": "<extra_id_70>",
|
| 262 |
+
"lstrip": false,
|
| 263 |
+
"normalized": false,
|
| 264 |
+
"rstrip": false,
|
| 265 |
+
"single_word": false,
|
| 266 |
+
"special": true
|
| 267 |
+
},
|
| 268 |
+
"32030": {
|
| 269 |
+
"content": "<extra_id_69>",
|
| 270 |
+
"lstrip": false,
|
| 271 |
+
"normalized": false,
|
| 272 |
+
"rstrip": false,
|
| 273 |
+
"single_word": false,
|
| 274 |
+
"special": true
|
| 275 |
+
},
|
| 276 |
+
"32031": {
|
| 277 |
+
"content": "<extra_id_68>",
|
| 278 |
+
"lstrip": false,
|
| 279 |
+
"normalized": false,
|
| 280 |
+
"rstrip": false,
|
| 281 |
+
"single_word": false,
|
| 282 |
+
"special": true
|
| 283 |
+
},
|
| 284 |
+
"32032": {
|
| 285 |
+
"content": "<extra_id_67>",
|
| 286 |
+
"lstrip": false,
|
| 287 |
+
"normalized": false,
|
| 288 |
+
"rstrip": false,
|
| 289 |
+
"single_word": false,
|
| 290 |
+
"special": true
|
| 291 |
+
},
|
| 292 |
+
"32033": {
|
| 293 |
+
"content": "<extra_id_66>",
|
| 294 |
+
"lstrip": false,
|
| 295 |
+
"normalized": false,
|
| 296 |
+
"rstrip": false,
|
| 297 |
+
"single_word": false,
|
| 298 |
+
"special": true
|
| 299 |
+
},
|
| 300 |
+
"32034": {
|
| 301 |
+
"content": "<extra_id_65>",
|
| 302 |
+
"lstrip": false,
|
| 303 |
+
"normalized": false,
|
| 304 |
+
"rstrip": false,
|
| 305 |
+
"single_word": false,
|
| 306 |
+
"special": true
|
| 307 |
+
},
|
| 308 |
+
"32035": {
|
| 309 |
+
"content": "<extra_id_64>",
|
| 310 |
+
"lstrip": false,
|
| 311 |
+
"normalized": false,
|
| 312 |
+
"rstrip": false,
|
| 313 |
+
"single_word": false,
|
| 314 |
+
"special": true
|
| 315 |
+
},
|
| 316 |
+
"32036": {
|
| 317 |
+
"content": "<extra_id_63>",
|
| 318 |
+
"lstrip": false,
|
| 319 |
+
"normalized": false,
|
| 320 |
+
"rstrip": false,
|
| 321 |
+
"single_word": false,
|
| 322 |
+
"special": true
|
| 323 |
+
},
|
| 324 |
+
"32037": {
|
| 325 |
+
"content": "<extra_id_62>",
|
| 326 |
+
"lstrip": false,
|
| 327 |
+
"normalized": false,
|
| 328 |
+
"rstrip": false,
|
| 329 |
+
"single_word": false,
|
| 330 |
+
"special": true
|
| 331 |
+
},
|
| 332 |
+
"32038": {
|
| 333 |
+
"content": "<extra_id_61>",
|
| 334 |
+
"lstrip": false,
|
| 335 |
+
"normalized": false,
|
| 336 |
+
"rstrip": false,
|
| 337 |
+
"single_word": false,
|
| 338 |
+
"special": true
|
| 339 |
+
},
|
| 340 |
+
"32039": {
|
| 341 |
+
"content": "<extra_id_60>",
|
| 342 |
+
"lstrip": false,
|
| 343 |
+
"normalized": false,
|
| 344 |
+
"rstrip": false,
|
| 345 |
+
"single_word": false,
|
| 346 |
+
"special": true
|
| 347 |
+
},
|
| 348 |
+
"32040": {
|
| 349 |
+
"content": "<extra_id_59>",
|
| 350 |
+
"lstrip": false,
|
| 351 |
+
"normalized": false,
|
| 352 |
+
"rstrip": false,
|
| 353 |
+
"single_word": false,
|
| 354 |
+
"special": true
|
| 355 |
+
},
|
| 356 |
+
"32041": {
|
| 357 |
+
"content": "<extra_id_58>",
|
| 358 |
+
"lstrip": false,
|
| 359 |
+
"normalized": false,
|
| 360 |
+
"rstrip": false,
|
| 361 |
+
"single_word": false,
|
| 362 |
+
"special": true
|
| 363 |
+
},
|
| 364 |
+
"32042": {
|
| 365 |
+
"content": "<extra_id_57>",
|
| 366 |
+
"lstrip": false,
|
| 367 |
+
"normalized": false,
|
| 368 |
+
"rstrip": false,
|
| 369 |
+
"single_word": false,
|
| 370 |
+
"special": true
|
| 371 |
+
},
|
| 372 |
+
"32043": {
|
| 373 |
+
"content": "<extra_id_56>",
|
| 374 |
+
"lstrip": false,
|
| 375 |
+
"normalized": false,
|
| 376 |
+
"rstrip": false,
|
| 377 |
+
"single_word": false,
|
| 378 |
+
"special": true
|
| 379 |
+
},
|
| 380 |
+
"32044": {
|
| 381 |
+
"content": "<extra_id_55>",
|
| 382 |
+
"lstrip": false,
|
| 383 |
+
"normalized": false,
|
| 384 |
+
"rstrip": false,
|
| 385 |
+
"single_word": false,
|
| 386 |
+
"special": true
|
| 387 |
+
},
|
| 388 |
+
"32045": {
|
| 389 |
+
"content": "<extra_id_54>",
|
| 390 |
+
"lstrip": false,
|
| 391 |
+
"normalized": false,
|
| 392 |
+
"rstrip": false,
|
| 393 |
+
"single_word": false,
|
| 394 |
+
"special": true
|
| 395 |
+
},
|
| 396 |
+
"32046": {
|
| 397 |
+
"content": "<extra_id_53>",
|
| 398 |
+
"lstrip": false,
|
| 399 |
+
"normalized": false,
|
| 400 |
+
"rstrip": false,
|
| 401 |
+
"single_word": false,
|
| 402 |
+
"special": true
|
| 403 |
+
},
|
| 404 |
+
"32047": {
|
| 405 |
+
"content": "<extra_id_52>",
|
| 406 |
+
"lstrip": false,
|
| 407 |
+
"normalized": false,
|
| 408 |
+
"rstrip": false,
|
| 409 |
+
"single_word": false,
|
| 410 |
+
"special": true
|
| 411 |
+
},
|
| 412 |
+
"32048": {
|
| 413 |
+
"content": "<extra_id_51>",
|
| 414 |
+
"lstrip": false,
|
| 415 |
+
"normalized": false,
|
| 416 |
+
"rstrip": false,
|
| 417 |
+
"single_word": false,
|
| 418 |
+
"special": true
|
| 419 |
+
},
|
| 420 |
+
"32049": {
|
| 421 |
+
"content": "<extra_id_50>",
|
| 422 |
+
"lstrip": false,
|
| 423 |
+
"normalized": false,
|
| 424 |
+
"rstrip": false,
|
| 425 |
+
"single_word": false,
|
| 426 |
+
"special": true
|
| 427 |
+
},
|
| 428 |
+
"32050": {
|
| 429 |
+
"content": "<extra_id_49>",
|
| 430 |
+
"lstrip": false,
|
| 431 |
+
"normalized": false,
|
| 432 |
+
"rstrip": false,
|
| 433 |
+
"single_word": false,
|
| 434 |
+
"special": true
|
| 435 |
+
},
|
| 436 |
+
"32051": {
|
| 437 |
+
"content": "<extra_id_48>",
|
| 438 |
+
"lstrip": false,
|
| 439 |
+
"normalized": false,
|
| 440 |
+
"rstrip": false,
|
| 441 |
+
"single_word": false,
|
| 442 |
+
"special": true
|
| 443 |
+
},
|
| 444 |
+
"32052": {
|
| 445 |
+
"content": "<extra_id_47>",
|
| 446 |
+
"lstrip": false,
|
| 447 |
+
"normalized": false,
|
| 448 |
+
"rstrip": false,
|
| 449 |
+
"single_word": false,
|
| 450 |
+
"special": true
|
| 451 |
+
},
|
| 452 |
+
"32053": {
|
| 453 |
+
"content": "<extra_id_46>",
|
| 454 |
+
"lstrip": false,
|
| 455 |
+
"normalized": false,
|
| 456 |
+
"rstrip": false,
|
| 457 |
+
"single_word": false,
|
| 458 |
+
"special": true
|
| 459 |
+
},
|
| 460 |
+
"32054": {
|
| 461 |
+
"content": "<extra_id_45>",
|
| 462 |
+
"lstrip": false,
|
| 463 |
+
"normalized": false,
|
| 464 |
+
"rstrip": false,
|
| 465 |
+
"single_word": false,
|
| 466 |
+
"special": true
|
| 467 |
+
},
|
| 468 |
+
"32055": {
|
| 469 |
+
"content": "<extra_id_44>",
|
| 470 |
+
"lstrip": false,
|
| 471 |
+
"normalized": false,
|
| 472 |
+
"rstrip": false,
|
| 473 |
+
"single_word": false,
|
| 474 |
+
"special": true
|
| 475 |
+
},
|
| 476 |
+
"32056": {
|
| 477 |
+
"content": "<extra_id_43>",
|
| 478 |
+
"lstrip": false,
|
| 479 |
+
"normalized": false,
|
| 480 |
+
"rstrip": false,
|
| 481 |
+
"single_word": false,
|
| 482 |
+
"special": true
|
| 483 |
+
},
|
| 484 |
+
"32057": {
|
| 485 |
+
"content": "<extra_id_42>",
|
| 486 |
+
"lstrip": false,
|
| 487 |
+
"normalized": false,
|
| 488 |
+
"rstrip": false,
|
| 489 |
+
"single_word": false,
|
| 490 |
+
"special": true
|
| 491 |
+
},
|
| 492 |
+
"32058": {
|
| 493 |
+
"content": "<extra_id_41>",
|
| 494 |
+
"lstrip": false,
|
| 495 |
+
"normalized": false,
|
| 496 |
+
"rstrip": false,
|
| 497 |
+
"single_word": false,
|
| 498 |
+
"special": true
|
| 499 |
+
},
|
| 500 |
+
"32059": {
|
| 501 |
+
"content": "<extra_id_40>",
|
| 502 |
+
"lstrip": false,
|
| 503 |
+
"normalized": false,
|
| 504 |
+
"rstrip": false,
|
| 505 |
+
"single_word": false,
|
| 506 |
+
"special": true
|
| 507 |
+
},
|
| 508 |
+
"32060": {
|
| 509 |
+
"content": "<extra_id_39>",
|
| 510 |
+
"lstrip": false,
|
| 511 |
+
"normalized": false,
|
| 512 |
+
"rstrip": false,
|
| 513 |
+
"single_word": false,
|
| 514 |
+
"special": true
|
| 515 |
+
},
|
| 516 |
+
"32061": {
|
| 517 |
+
"content": "<extra_id_38>",
|
| 518 |
+
"lstrip": false,
|
| 519 |
+
"normalized": false,
|
| 520 |
+
"rstrip": false,
|
| 521 |
+
"single_word": false,
|
| 522 |
+
"special": true
|
| 523 |
+
},
|
| 524 |
+
"32062": {
|
| 525 |
+
"content": "<extra_id_37>",
|
| 526 |
+
"lstrip": false,
|
| 527 |
+
"normalized": false,
|
| 528 |
+
"rstrip": false,
|
| 529 |
+
"single_word": false,
|
| 530 |
+
"special": true
|
| 531 |
+
},
|
| 532 |
+
"32063": {
|
| 533 |
+
"content": "<extra_id_36>",
|
| 534 |
+
"lstrip": false,
|
| 535 |
+
"normalized": false,
|
| 536 |
+
"rstrip": false,
|
| 537 |
+
"single_word": false,
|
| 538 |
+
"special": true
|
| 539 |
+
},
|
| 540 |
+
"32064": {
|
| 541 |
+
"content": "<extra_id_35>",
|
| 542 |
+
"lstrip": false,
|
| 543 |
+
"normalized": false,
|
| 544 |
+
"rstrip": false,
|
| 545 |
+
"single_word": false,
|
| 546 |
+
"special": true
|
| 547 |
+
},
|
| 548 |
+
"32065": {
|
| 549 |
+
"content": "<extra_id_34>",
|
| 550 |
+
"lstrip": false,
|
| 551 |
+
"normalized": false,
|
| 552 |
+
"rstrip": false,
|
| 553 |
+
"single_word": false,
|
| 554 |
+
"special": true
|
| 555 |
+
},
|
| 556 |
+
"32066": {
|
| 557 |
+
"content": "<extra_id_33>",
|
| 558 |
+
"lstrip": false,
|
| 559 |
+
"normalized": false,
|
| 560 |
+
"rstrip": false,
|
| 561 |
+
"single_word": false,
|
| 562 |
+
"special": true
|
| 563 |
+
},
|
| 564 |
+
"32067": {
|
| 565 |
+
"content": "<extra_id_32>",
|
| 566 |
+
"lstrip": false,
|
| 567 |
+
"normalized": false,
|
| 568 |
+
"rstrip": false,
|
| 569 |
+
"single_word": false,
|
| 570 |
+
"special": true
|
| 571 |
+
},
|
| 572 |
+
"32068": {
|
| 573 |
+
"content": "<extra_id_31>",
|
| 574 |
+
"lstrip": false,
|
| 575 |
+
"normalized": false,
|
| 576 |
+
"rstrip": false,
|
| 577 |
+
"single_word": false,
|
| 578 |
+
"special": true
|
| 579 |
+
},
|
| 580 |
+
"32069": {
|
| 581 |
+
"content": "<extra_id_30>",
|
| 582 |
+
"lstrip": false,
|
| 583 |
+
"normalized": false,
|
| 584 |
+
"rstrip": false,
|
| 585 |
+
"single_word": false,
|
| 586 |
+
"special": true
|
| 587 |
+
},
|
| 588 |
+
"32070": {
|
| 589 |
+
"content": "<extra_id_29>",
|
| 590 |
+
"lstrip": false,
|
| 591 |
+
"normalized": false,
|
| 592 |
+
"rstrip": false,
|
| 593 |
+
"single_word": false,
|
| 594 |
+
"special": true
|
| 595 |
+
},
|
| 596 |
+
"32071": {
|
| 597 |
+
"content": "<extra_id_28>",
|
| 598 |
+
"lstrip": false,
|
| 599 |
+
"normalized": false,
|
| 600 |
+
"rstrip": false,
|
| 601 |
+
"single_word": false,
|
| 602 |
+
"special": true
|
| 603 |
+
},
|
| 604 |
+
"32072": {
|
| 605 |
+
"content": "<extra_id_27>",
|
| 606 |
+
"lstrip": false,
|
| 607 |
+
"normalized": false,
|
| 608 |
+
"rstrip": false,
|
| 609 |
+
"single_word": false,
|
| 610 |
+
"special": true
|
| 611 |
+
},
|
| 612 |
+
"32073": {
|
| 613 |
+
"content": "<extra_id_26>",
|
| 614 |
+
"lstrip": false,
|
| 615 |
+
"normalized": false,
|
| 616 |
+
"rstrip": false,
|
| 617 |
+
"single_word": false,
|
| 618 |
+
"special": true
|
| 619 |
+
},
|
| 620 |
+
"32074": {
|
| 621 |
+
"content": "<extra_id_25>",
|
| 622 |
+
"lstrip": false,
|
| 623 |
+
"normalized": false,
|
| 624 |
+
"rstrip": false,
|
| 625 |
+
"single_word": false,
|
| 626 |
+
"special": true
|
| 627 |
+
},
|
| 628 |
+
"32075": {
|
| 629 |
+
"content": "<extra_id_24>",
|
| 630 |
+
"lstrip": false,
|
| 631 |
+
"normalized": false,
|
| 632 |
+
"rstrip": false,
|
| 633 |
+
"single_word": false,
|
| 634 |
+
"special": true
|
| 635 |
+
},
|
| 636 |
+
"32076": {
|
| 637 |
+
"content": "<extra_id_23>",
|
| 638 |
+
"lstrip": false,
|
| 639 |
+
"normalized": false,
|
| 640 |
+
"rstrip": false,
|
| 641 |
+
"single_word": false,
|
| 642 |
+
"special": true
|
| 643 |
+
},
|
| 644 |
+
"32077": {
|
| 645 |
+
"content": "<extra_id_22>",
|
| 646 |
+
"lstrip": false,
|
| 647 |
+
"normalized": false,
|
| 648 |
+
"rstrip": false,
|
| 649 |
+
"single_word": false,
|
| 650 |
+
"special": true
|
| 651 |
+
},
|
| 652 |
+
"32078": {
|
| 653 |
+
"content": "<extra_id_21>",
|
| 654 |
+
"lstrip": false,
|
| 655 |
+
"normalized": false,
|
| 656 |
+
"rstrip": false,
|
| 657 |
+
"single_word": false,
|
| 658 |
+
"special": true
|
| 659 |
+
},
|
| 660 |
+
"32079": {
|
| 661 |
+
"content": "<extra_id_20>",
|
| 662 |
+
"lstrip": false,
|
| 663 |
+
"normalized": false,
|
| 664 |
+
"rstrip": false,
|
| 665 |
+
"single_word": false,
|
| 666 |
+
"special": true
|
| 667 |
+
},
|
| 668 |
+
"32080": {
|
| 669 |
+
"content": "<extra_id_19>",
|
| 670 |
+
"lstrip": false,
|
| 671 |
+
"normalized": false,
|
| 672 |
+
"rstrip": false,
|
| 673 |
+
"single_word": false,
|
| 674 |
+
"special": true
|
| 675 |
+
},
|
| 676 |
+
"32081": {
|
| 677 |
+
"content": "<extra_id_18>",
|
| 678 |
+
"lstrip": false,
|
| 679 |
+
"normalized": false,
|
| 680 |
+
"rstrip": false,
|
| 681 |
+
"single_word": false,
|
| 682 |
+
"special": true
|
| 683 |
+
},
|
| 684 |
+
"32082": {
|
| 685 |
+
"content": "<extra_id_17>",
|
| 686 |
+
"lstrip": false,
|
| 687 |
+
"normalized": false,
|
| 688 |
+
"rstrip": false,
|
| 689 |
+
"single_word": false,
|
| 690 |
+
"special": true
|
| 691 |
+
},
|
| 692 |
+
"32083": {
|
| 693 |
+
"content": "<extra_id_16>",
|
| 694 |
+
"lstrip": false,
|
| 695 |
+
"normalized": false,
|
| 696 |
+
"rstrip": false,
|
| 697 |
+
"single_word": false,
|
| 698 |
+
"special": true
|
| 699 |
+
},
|
| 700 |
+
"32084": {
|
| 701 |
+
"content": "<extra_id_15>",
|
| 702 |
+
"lstrip": false,
|
| 703 |
+
"normalized": false,
|
| 704 |
+
"rstrip": false,
|
| 705 |
+
"single_word": false,
|
| 706 |
+
"special": true
|
| 707 |
+
},
|
| 708 |
+
"32085": {
|
| 709 |
+
"content": "<extra_id_14>",
|
| 710 |
+
"lstrip": false,
|
| 711 |
+
"normalized": false,
|
| 712 |
+
"rstrip": false,
|
| 713 |
+
"single_word": false,
|
| 714 |
+
"special": true
|
| 715 |
+
},
|
| 716 |
+
"32086": {
|
| 717 |
+
"content": "<extra_id_13>",
|
| 718 |
+
"lstrip": false,
|
| 719 |
+
"normalized": false,
|
| 720 |
+
"rstrip": false,
|
| 721 |
+
"single_word": false,
|
| 722 |
+
"special": true
|
| 723 |
+
},
|
| 724 |
+
"32087": {
|
| 725 |
+
"content": "<extra_id_12>",
|
| 726 |
+
"lstrip": false,
|
| 727 |
+
"normalized": false,
|
| 728 |
+
"rstrip": false,
|
| 729 |
+
"single_word": false,
|
| 730 |
+
"special": true
|
| 731 |
+
},
|
| 732 |
+
"32088": {
|
| 733 |
+
"content": "<extra_id_11>",
|
| 734 |
+
"lstrip": false,
|
| 735 |
+
"normalized": false,
|
| 736 |
+
"rstrip": false,
|
| 737 |
+
"single_word": false,
|
| 738 |
+
"special": true
|
| 739 |
+
},
|
| 740 |
+
"32089": {
|
| 741 |
+
"content": "<extra_id_10>",
|
| 742 |
+
"lstrip": false,
|
| 743 |
+
"normalized": false,
|
| 744 |
+
"rstrip": false,
|
| 745 |
+
"single_word": false,
|
| 746 |
+
"special": true
|
| 747 |
+
},
|
| 748 |
+
"32090": {
|
| 749 |
+
"content": "<extra_id_9>",
|
| 750 |
+
"lstrip": false,
|
| 751 |
+
"normalized": false,
|
| 752 |
+
"rstrip": false,
|
| 753 |
+
"single_word": false,
|
| 754 |
+
"special": true
|
| 755 |
+
},
|
| 756 |
+
"32091": {
|
| 757 |
+
"content": "<extra_id_8>",
|
| 758 |
+
"lstrip": false,
|
| 759 |
+
"normalized": false,
|
| 760 |
+
"rstrip": false,
|
| 761 |
+
"single_word": false,
|
| 762 |
+
"special": true
|
| 763 |
+
},
|
| 764 |
+
"32092": {
|
| 765 |
+
"content": "<extra_id_7>",
|
| 766 |
+
"lstrip": false,
|
| 767 |
+
"normalized": false,
|
| 768 |
+
"rstrip": false,
|
| 769 |
+
"single_word": false,
|
| 770 |
+
"special": true
|
| 771 |
+
},
|
| 772 |
+
"32093": {
|
| 773 |
+
"content": "<extra_id_6>",
|
| 774 |
+
"lstrip": false,
|
| 775 |
+
"normalized": false,
|
| 776 |
+
"rstrip": false,
|
| 777 |
+
"single_word": false,
|
| 778 |
+
"special": true
|
| 779 |
+
},
|
| 780 |
+
"32094": {
|
| 781 |
+
"content": "<extra_id_5>",
|
| 782 |
+
"lstrip": false,
|
| 783 |
+
"normalized": false,
|
| 784 |
+
"rstrip": false,
|
| 785 |
+
"single_word": false,
|
| 786 |
+
"special": true
|
| 787 |
+
},
|
| 788 |
+
"32095": {
|
| 789 |
+
"content": "<extra_id_4>",
|
| 790 |
+
"lstrip": false,
|
| 791 |
+
"normalized": false,
|
| 792 |
+
"rstrip": false,
|
| 793 |
+
"single_word": false,
|
| 794 |
+
"special": true
|
| 795 |
+
},
|
| 796 |
+
"32096": {
|
| 797 |
+
"content": "<extra_id_3>",
|
| 798 |
+
"lstrip": false,
|
| 799 |
+
"normalized": false,
|
| 800 |
+
"rstrip": false,
|
| 801 |
+
"single_word": false,
|
| 802 |
+
"special": true
|
| 803 |
+
},
|
| 804 |
+
"32097": {
|
| 805 |
+
"content": "<extra_id_2>",
|
| 806 |
+
"lstrip": false,
|
| 807 |
+
"normalized": false,
|
| 808 |
+
"rstrip": false,
|
| 809 |
+
"single_word": false,
|
| 810 |
+
"special": true
|
| 811 |
+
},
|
| 812 |
+
"32098": {
|
| 813 |
+
"content": "<extra_id_1>",
|
| 814 |
+
"lstrip": false,
|
| 815 |
+
"normalized": false,
|
| 816 |
+
"rstrip": false,
|
| 817 |
+
"single_word": false,
|
| 818 |
+
"special": true
|
| 819 |
+
},
|
| 820 |
+
"32099": {
|
| 821 |
+
"content": "<extra_id_0>",
|
| 822 |
+
"lstrip": false,
|
| 823 |
+
"normalized": false,
|
| 824 |
+
"rstrip": false,
|
| 825 |
+
"single_word": false,
|
| 826 |
+
"special": true
|
| 827 |
+
}
|
| 828 |
+
},
|
| 829 |
+
"additional_special_tokens": [
|
| 830 |
+
"<extra_id_0>",
|
| 831 |
+
"<extra_id_1>",
|
| 832 |
+
"<extra_id_2>",
|
| 833 |
+
"<extra_id_3>",
|
| 834 |
+
"<extra_id_4>",
|
| 835 |
+
"<extra_id_5>",
|
| 836 |
+
"<extra_id_6>",
|
| 837 |
+
"<extra_id_7>",
|
| 838 |
+
"<extra_id_8>",
|
| 839 |
+
"<extra_id_9>",
|
| 840 |
+
"<extra_id_10>",
|
| 841 |
+
"<extra_id_11>",
|
| 842 |
+
"<extra_id_12>",
|
| 843 |
+
"<extra_id_13>",
|
| 844 |
+
"<extra_id_14>",
|
| 845 |
+
"<extra_id_15>",
|
| 846 |
+
"<extra_id_16>",
|
| 847 |
+
"<extra_id_17>",
|
| 848 |
+
"<extra_id_18>",
|
| 849 |
+
"<extra_id_19>",
|
| 850 |
+
"<extra_id_20>",
|
| 851 |
+
"<extra_id_21>",
|
| 852 |
+
"<extra_id_22>",
|
| 853 |
+
"<extra_id_23>",
|
| 854 |
+
"<extra_id_24>",
|
| 855 |
+
"<extra_id_25>",
|
| 856 |
+
"<extra_id_26>",
|
| 857 |
+
"<extra_id_27>",
|
| 858 |
+
"<extra_id_28>",
|
| 859 |
+
"<extra_id_29>",
|
| 860 |
+
"<extra_id_30>",
|
| 861 |
+
"<extra_id_31>",
|
| 862 |
+
"<extra_id_32>",
|
| 863 |
+
"<extra_id_33>",
|
| 864 |
+
"<extra_id_34>",
|
| 865 |
+
"<extra_id_35>",
|
| 866 |
+
"<extra_id_36>",
|
| 867 |
+
"<extra_id_37>",
|
| 868 |
+
"<extra_id_38>",
|
| 869 |
+
"<extra_id_39>",
|
| 870 |
+
"<extra_id_40>",
|
| 871 |
+
"<extra_id_41>",
|
| 872 |
+
"<extra_id_42>",
|
| 873 |
+
"<extra_id_43>",
|
| 874 |
+
"<extra_id_44>",
|
| 875 |
+
"<extra_id_45>",
|
| 876 |
+
"<extra_id_46>",
|
| 877 |
+
"<extra_id_47>",
|
| 878 |
+
"<extra_id_48>",
|
| 879 |
+
"<extra_id_49>",
|
| 880 |
+
"<extra_id_50>",
|
| 881 |
+
"<extra_id_51>",
|
| 882 |
+
"<extra_id_52>",
|
| 883 |
+
"<extra_id_53>",
|
| 884 |
+
"<extra_id_54>",
|
| 885 |
+
"<extra_id_55>",
|
| 886 |
+
"<extra_id_56>",
|
| 887 |
+
"<extra_id_57>",
|
| 888 |
+
"<extra_id_58>",
|
| 889 |
+
"<extra_id_59>",
|
| 890 |
+
"<extra_id_60>",
|
| 891 |
+
"<extra_id_61>",
|
| 892 |
+
"<extra_id_62>",
|
| 893 |
+
"<extra_id_63>",
|
| 894 |
+
"<extra_id_64>",
|
| 895 |
+
"<extra_id_65>",
|
| 896 |
+
"<extra_id_66>",
|
| 897 |
+
"<extra_id_67>",
|
| 898 |
+
"<extra_id_68>",
|
| 899 |
+
"<extra_id_69>",
|
| 900 |
+
"<extra_id_70>",
|
| 901 |
+
"<extra_id_71>",
|
| 902 |
+
"<extra_id_72>",
|
| 903 |
+
"<extra_id_73>",
|
| 904 |
+
"<extra_id_74>",
|
| 905 |
+
"<extra_id_75>",
|
| 906 |
+
"<extra_id_76>",
|
| 907 |
+
"<extra_id_77>",
|
| 908 |
+
"<extra_id_78>",
|
| 909 |
+
"<extra_id_79>",
|
| 910 |
+
"<extra_id_80>",
|
| 911 |
+
"<extra_id_81>",
|
| 912 |
+
"<extra_id_82>",
|
| 913 |
+
"<extra_id_83>",
|
| 914 |
+
"<extra_id_84>",
|
| 915 |
+
"<extra_id_85>",
|
| 916 |
+
"<extra_id_86>",
|
| 917 |
+
"<extra_id_87>",
|
| 918 |
+
"<extra_id_88>",
|
| 919 |
+
"<extra_id_89>",
|
| 920 |
+
"<extra_id_90>",
|
| 921 |
+
"<extra_id_91>",
|
| 922 |
+
"<extra_id_92>",
|
| 923 |
+
"<extra_id_93>",
|
| 924 |
+
"<extra_id_94>",
|
| 925 |
+
"<extra_id_95>",
|
| 926 |
+
"<extra_id_96>",
|
| 927 |
+
"<extra_id_97>",
|
| 928 |
+
"<extra_id_98>",
|
| 929 |
+
"<extra_id_99>"
|
| 930 |
+
],
|
| 931 |
+
"clean_up_tokenization_spaces": true,
|
| 932 |
+
"eos_token": "</s>",
|
| 933 |
+
"extra_ids": 100,
|
| 934 |
+
"extra_special_tokens": {},
|
| 935 |
+
"legacy": true,
|
| 936 |
+
"model_max_length": 512,
|
| 937 |
+
"pad_token": "<pad>",
|
| 938 |
+
"sp_model_kwargs": {},
|
| 939 |
+
"tokenizer_class": "T5Tokenizer",
|
| 940 |
+
"unk_token": "<unk>"
|
| 941 |
+
}
|
humanize.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""humanize-text-model inference wrapper (bilingual: English + Chinese).
|
| 2 |
+
|
| 3 |
+
Loads the Lynote bilingual humanizer — two small seq2seq checkpoints in one
|
| 4 |
+
repo (``en/`` T5-small, ``zh/`` Chinese T5-small) — and routes each input to
|
| 5 |
+
the right language model. URLs, numbers, paths, code and quoted strings are
|
| 6 |
+
protected with tokenizer-friendly placeholders before generation and restored
|
| 7 |
+
afterwards.
|
| 8 |
+
|
| 9 |
+
Usage:
|
| 10 |
+
from humanize import Humanizer
|
| 11 |
+
h = Humanizer() # loads "Danny-Lynote/humanize-text-model" from the Hub
|
| 12 |
+
print(h("It is important to note that this robust solution serves as a testament to our commitment."))
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import argparse
|
| 16 |
+
import os
|
| 17 |
+
import re
|
| 18 |
+
import sys
|
| 19 |
+
from typing import List, Optional
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
from transformers import BertTokenizer, T5ForConditionalGeneration, T5Tokenizer
|
| 23 |
+
|
| 24 |
+
DEFAULT_MODEL = "Danny-Lynote/humanize-text-model"
|
| 25 |
+
MAX_CHARS = 4_000
|
| 26 |
+
MAX_TOKENS = 256
|
| 27 |
+
DEVICE = "mps" if torch.backends.mps.is_available() else (
|
| 28 |
+
"cuda" if torch.cuda.is_available() else "cpu"
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
PROTECTED_PATTERN = re.compile(
|
| 32 |
+
r"(`[^`]+`|https?://[^\s\u4e00-\u9fff,。!?;:、]+|(?:[A-Za-z]:)?(?:[/\\][\w.\-]+)+|"
|
| 33 |
+
r"\b\d+(?:\.\d+)?(?:%|[A-Za-z]+)?\b|\"[^\"]*\"|'[^']*')"
|
| 34 |
+
)
|
| 35 |
+
CJK_RATIO = re.compile(r"[\u4e00-\u9fff]")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def protect(text: str, language: Optional[str] = None):
|
| 39 |
+
"""Replace protected spans with language-aware placeholders:
|
| 40 |
+
EN ``PROTECTED_n``, ZH ``【保护n】`` (both tokenizer-friendly)."""
|
| 41 |
+
protected = []
|
| 42 |
+
zh = language == "zh" or (language is None and is_chinese(text))
|
| 43 |
+
|
| 44 |
+
def replace(match):
|
| 45 |
+
token = f"【保护{len(protected)}】" if zh else f"PROTECTED_{len(protected)}"
|
| 46 |
+
protected.append(match.group(0))
|
| 47 |
+
return token
|
| 48 |
+
|
| 49 |
+
return PROTECTED_PATTERN.sub(replace, text), protected
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def restore(text: str, protected: List[str]) -> str:
|
| 53 |
+
"""Restore placeholders (either format, tolerant of inserted spaces)."""
|
| 54 |
+
for index, value in enumerate(protected):
|
| 55 |
+
text = re.sub(rf"PROTECTED_{index}\b", lambda m: value, text)
|
| 56 |
+
text = re.sub(
|
| 57 |
+
rf"【\s*保\s*护\s*{index}\s*】", lambda m: value, text
|
| 58 |
+
)
|
| 59 |
+
return text
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def is_chinese(text: str, threshold: float = 0.05) -> bool:
|
| 63 |
+
total = len(re.sub(r"\s+", "", text))
|
| 64 |
+
if total == 0:
|
| 65 |
+
return False
|
| 66 |
+
return len(CJK_RATIO.findall(text)) / total > threshold
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class Humanizer:
|
| 70 |
+
def __init__(
|
| 71 |
+
self,
|
| 72 |
+
model_id: str = DEFAULT_MODEL,
|
| 73 |
+
device: Optional[str] = None,
|
| 74 |
+
cache_dir: Optional[str] = None,
|
| 75 |
+
local_files_only: bool = False,
|
| 76 |
+
hf_token: Optional[str] = None,
|
| 77 |
+
lazy: bool = True,
|
| 78 |
+
):
|
| 79 |
+
"""model_id is a repo (or local dir) containing ``en/`` and ``zh/``
|
| 80 |
+
subdirectories with separate checkpoints. With ``lazy=True`` (default)
|
| 81 |
+
a language model is only loaded when first needed."""
|
| 82 |
+
self.model_id = model_id
|
| 83 |
+
self.device = torch.device(device or DEVICE)
|
| 84 |
+
self._models = {}
|
| 85 |
+
self._tokenizers = {}
|
| 86 |
+
self._cache_dir = cache_dir
|
| 87 |
+
self._local_only = local_files_only
|
| 88 |
+
self._token = hf_token
|
| 89 |
+
if not lazy:
|
| 90 |
+
self._get("en")
|
| 91 |
+
self._get("zh")
|
| 92 |
+
|
| 93 |
+
def _load(self, language: str, model: T5ForConditionalGeneration, tokenizer: T5Tokenizer):
|
| 94 |
+
self._models[language] = model
|
| 95 |
+
self._tokenizers[language] = tokenizer
|
| 96 |
+
|
| 97 |
+
def _get(self, language: str):
|
| 98 |
+
if language not in self._models:
|
| 99 |
+
sub = f"{self.model_id}/{language}"
|
| 100 |
+
model = T5ForConditionalGeneration.from_pretrained(
|
| 101 |
+
sub, cache_dir=self._cache_dir,
|
| 102 |
+
local_files_only=self._local_only, token=self._token,
|
| 103 |
+
)
|
| 104 |
+
tokenizer_cls = BertTokenizer if language == "zh" else T5Tokenizer
|
| 105 |
+
tokenizer = tokenizer_cls.from_pretrained(
|
| 106 |
+
sub, cache_dir=self._cache_dir,
|
| 107 |
+
local_files_only=self._local_only, token=self._token,
|
| 108 |
+
)
|
| 109 |
+
model.to(self.device).eval()
|
| 110 |
+
self._load(language, model, tokenizer)
|
| 111 |
+
return self._models[language], self._tokenizers[language]
|
| 112 |
+
|
| 113 |
+
def humanize(self, text: str, num_beams: int = 3, do_sample: bool = False) -> str:
|
| 114 |
+
text = (text or "").strip()
|
| 115 |
+
if not text:
|
| 116 |
+
raise ValueError("Input text is empty.")
|
| 117 |
+
if len(text) > MAX_CHARS:
|
| 118 |
+
raise ValueError(f"Input exceeds {MAX_CHARS:,} characters.")
|
| 119 |
+
return self.humanize_batch([text], num_beams=num_beams, do_sample=do_sample)[0]
|
| 120 |
+
|
| 121 |
+
def humanize_batch(
|
| 122 |
+
self,
|
| 123 |
+
texts: List[str],
|
| 124 |
+
num_beams: int = 3,
|
| 125 |
+
do_sample: bool = False,
|
| 126 |
+
batch_size: int = 8,
|
| 127 |
+
) -> List[str]:
|
| 128 |
+
"""Humanize a list of texts. URLs, numbers, paths, code and quotes are
|
| 129 |
+
protected per-text with placeholders and restored after generation."""
|
| 130 |
+
masked = []
|
| 131 |
+
protected_all = []
|
| 132 |
+
for t in texts:
|
| 133 |
+
masked_text, protected = protect(t)
|
| 134 |
+
masked.append(masked_text)
|
| 135 |
+
protected_all.append(protected)
|
| 136 |
+
outputs: List[str] = []
|
| 137 |
+
grouped: dict = {}
|
| 138 |
+
for i, t in enumerate(masked):
|
| 139 |
+
grouped.setdefault("zh" if is_chinese(t) else "en", []).append(i)
|
| 140 |
+
for language, indices in grouped.items():
|
| 141 |
+
model, tokenizer = self._get(language)
|
| 142 |
+
for start in range(0, len(indices), batch_size):
|
| 143 |
+
chunk_idx = indices[start : start + batch_size]
|
| 144 |
+
enc = tokenizer(
|
| 145 |
+
[masked[i] for i in chunk_idx],
|
| 146 |
+
max_length=MAX_TOKENS,
|
| 147 |
+
truncation=True,
|
| 148 |
+
padding=True,
|
| 149 |
+
return_tensors="pt",
|
| 150 |
+
)
|
| 151 |
+
if "token_type_ids" in enc:
|
| 152 |
+
enc.pop("token_type_ids")
|
| 153 |
+
enc = enc.to(self.device)
|
| 154 |
+
with torch.no_grad():
|
| 155 |
+
gen_kwargs = dict(
|
| 156 |
+
num_beams=num_beams,
|
| 157 |
+
do_sample=do_sample,
|
| 158 |
+
early_stopping=True,
|
| 159 |
+
)
|
| 160 |
+
if language == "zh":
|
| 161 |
+
# Small Chinese T5 degenerates on long sequences: cap
|
| 162 |
+
# length and block exact 4-gram repetition (EN does not
|
| 163 |
+
# need these).
|
| 164 |
+
gen_kwargs["max_length"] = 40
|
| 165 |
+
gen_kwargs["no_repeat_ngram_size"] = 4
|
| 166 |
+
else:
|
| 167 |
+
gen_kwargs["max_length"] = MAX_TOKENS
|
| 168 |
+
gen = model.generate(**enc, **gen_kwargs)
|
| 169 |
+
decoded = tokenizer.batch_decode(gen, skip_special_tokens=True)
|
| 170 |
+
for j, text in zip(chunk_idx, decoded):
|
| 171 |
+
outputs.append((j, text))
|
| 172 |
+
outputs.sort(key=lambda x: x[0])
|
| 173 |
+
|
| 174 |
+
return [
|
| 175 |
+
restore(self._polish(out), protected)
|
| 176 |
+
for out, protected in zip([o[1] for o in outputs], protected_all)
|
| 177 |
+
]
|
| 178 |
+
|
| 179 |
+
@staticmethod
|
| 180 |
+
def _polish(text: str) -> str:
|
| 181 |
+
text = re.sub(r"^\s*(?:extra\d+\s*[,,。\s]*)+", "", text)
|
| 182 |
+
text = re.sub(r"(?<=[\u4e00-\u9fff])\s+(?=[\u4e00-\u9fff,。;:!?])", "", text)
|
| 183 |
+
text = re.sub(r"(?<=[,。;:!?])\s+(?=[\u4e00-\u9fff])", "", text)
|
| 184 |
+
text = re.sub(r"[ \t]{2,}", " ", text)
|
| 185 |
+
text = re.sub(r"\s+([,.;:!?,。;:!?])", r"\1", text)
|
| 186 |
+
text = re.sub(r"[,;]\s*\.", ".", text)
|
| 187 |
+
text = re.sub(r"\.\s*[,;]", ".", text)
|
| 188 |
+
text = re.sub(r"[,,]\s*(?=[。!?.!?])", "", text)
|
| 189 |
+
text = re.sub(r"\.\s+([a-z])", lambda m: ". " + m.group(1).upper(), text)
|
| 190 |
+
return text.strip(" ,")
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def main():
|
| 194 |
+
parser = argparse.ArgumentParser(description="Bilingual text humanizer (T5-small EN + Chinese T5-small)")
|
| 195 |
+
parser.add_argument("--text", help="text to humanize")
|
| 196 |
+
parser.add_argument("--input", help="path to a text file")
|
| 197 |
+
parser.add_argument("--output", help="write result to a file")
|
| 198 |
+
parser.add_argument("--model", default=DEFAULT_MODEL, help="model id or local dir")
|
| 199 |
+
parser.add_argument("--beams", type=int, default=3)
|
| 200 |
+
parser.add_argument("--list-models", action="store_true", help="print model ids and exit")
|
| 201 |
+
args = parser.parse_args()
|
| 202 |
+
|
| 203 |
+
if args.list_models:
|
| 204 |
+
print(DEFAULT_MODEL)
|
| 205 |
+
return
|
| 206 |
+
|
| 207 |
+
if args.text and args.input:
|
| 208 |
+
parser.error("provide only one of --text / --input")
|
| 209 |
+
source = args.text or (open(args.input, encoding="utf-8").read() if args.input else None)
|
| 210 |
+
if not source:
|
| 211 |
+
parser.error("provide --text or --input")
|
| 212 |
+
|
| 213 |
+
humanizer = Humanizer(model_id=args.model)
|
| 214 |
+
result = humanizer.humanize(source, num_beams=args.beams)
|
| 215 |
+
if args.output:
|
| 216 |
+
with open(args.output, "w", encoding="utf-8") as f:
|
| 217 |
+
f.write(result)
|
| 218 |
+
print(f"written to {args.output}")
|
| 219 |
+
else:
|
| 220 |
+
print(result)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
if __name__ == "__main__":
|
| 224 |
+
main()
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
transformers>=4.46
|
| 2 |
+
torch>=2.2
|
| 3 |
+
sacrebleu>=2.4
|
| 4 |
+
pytest>=8.0
|
scripts/build_dataset.py
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Build the synthetic bilingual training corpus for the humanize model.
|
| 2 |
+
|
| 3 |
+
Methodology
|
| 4 |
+
-----------
|
| 5 |
+
Supervision comes from the conservative rule engine in ``src/rules.py``
|
| 6 |
+
(derived from the lynote-ai reference projects). We generate:
|
| 7 |
+
|
| 8 |
+
1. **AI -> human**: formulaic clause combinations rewritten by the rules,
|
| 9 |
+
2. **Human -> human (identity)**: clean prose unchanged, so the model learns
|
| 10 |
+
not to rewrite already-human text,
|
| 11 |
+
3. **Mixed**: clean prose with one injected AI cliché that must be removed,
|
| 12 |
+
4. **Protected spans**: examples containing numbers, URLs and quoted text.
|
| 13 |
+
|
| 14 |
+
Every input maps to exactly one deterministic target (one-to-one), which
|
| 15 |
+
keeps the training signal consistent. Outputs are seeded JSONL files with
|
| 16 |
+
fields ``input_text``, ``output_text``, ``lang``.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import argparse
|
| 20 |
+
import json
|
| 21 |
+
import random
|
| 22 |
+
import re
|
| 23 |
+
import sys
|
| 24 |
+
from pathlib import Path
|
| 25 |
+
|
| 26 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 27 |
+
from src.rules import EN_PHRASES, ZH_PHRASES, humanize_rules, protect
|
| 28 |
+
|
| 29 |
+
# --------------------------------------------------------------------------
|
| 30 |
+
# Seed prose (curated, human-flavored).
|
| 31 |
+
# --------------------------------------------------------------------------
|
| 32 |
+
|
| 33 |
+
EN_SEEDS = [
|
| 34 |
+
"I walked the dog this morning and the sky was grey and low.",
|
| 35 |
+
"Halfway through the park it started raining, which I hadn't expected, so we ran home.",
|
| 36 |
+
"The dog seemed to enjoy it more than me.",
|
| 37 |
+
"When we got back I made coffee and watched the rain run down the window.",
|
| 38 |
+
"Thanks for sending the report over, the numbers look good.",
|
| 39 |
+
"Could you double-check the retention section before we share it?",
|
| 40 |
+
"Let's grab coffee this week and talk through next steps.",
|
| 41 |
+
"The conference was exhausting, but a couple of the sessions were genuinely useful.",
|
| 42 |
+
"I came home with a notebook full of scribbles and a dozen open browser tabs.",
|
| 43 |
+
"The sky cleared by evening and we went for a long walk.",
|
| 44 |
+
"I've been testing the new tool for a few days and it mostly works.",
|
| 45 |
+
"The docs are clear enough, though the install steps could be shorter.",
|
| 46 |
+
"We tried the new approach on a small project and it held up fine.",
|
| 47 |
+
"My laptop died in the middle of the meeting, which was awkward.",
|
| 48 |
+
"She asked me to review her draft and I found a few typos.",
|
| 49 |
+
"The traffic was bad this morning, so I took the subway instead.",
|
| 50 |
+
"We spent the afternoon fixing bugs and ordering pizza.",
|
| 51 |
+
"I think the plan works, but we should test it with real users first.",
|
| 52 |
+
"The garden looks great after the rain.",
|
| 53 |
+
"He replied faster than I expected, which was a nice surprise.",
|
| 54 |
+
"The new hire seems sharp and asks good questions.",
|
| 55 |
+
"I read the whole chapter on the train and highlighted half of it.",
|
| 56 |
+
"We moved the deadline to Friday so nobody has to work the weekend.",
|
| 57 |
+
"The cafe near the office makes a decent flat white.",
|
| 58 |
+
"My phone died, so I missed your call, sorry about that.",
|
| 59 |
+
"She asked me to review her draft and I found a few typos. It was a nice read.",
|
| 60 |
+
"I think the plan works, but we should test it with real users first. We can start next week.",
|
| 61 |
+
]
|
| 62 |
+
|
| 63 |
+
ZH_SEEDS = [
|
| 64 |
+
"今天早上我带狗去公园,出门时天阴阴的。",
|
| 65 |
+
"走到一半忽然下起雨,我们只好跑回家,狗倒是挺高兴。",
|
| 66 |
+
"回来以后我煮了杯咖啡,坐在窗边看了会儿雨。",
|
| 67 |
+
"你要的资料我发你邮箱了,附件里有两个版本。",
|
| 68 |
+
"你看一下哪个合适,有问题随时叫我。",
|
| 69 |
+
"下午我跟老王聊了聊,他觉得可以,但预算要再压一压。",
|
| 70 |
+
"今天加班到九点,回来路上吃了碗牛肉面。",
|
| 71 |
+
"老板认得我,多给了几片牛肉。",
|
| 72 |
+
"猫蹲在门口等我,尾巴翘得老高。",
|
| 73 |
+
"周末去了趟公园,人特别多,树荫下倒是凉快。",
|
| 74 |
+
"家里的路由器坏了,折腾了一晚上也没修好。",
|
| 75 |
+
"这个月房租涨了两百块,心里有点不爽。",
|
| 76 |
+
"我在楼下便利店买了瓶水,顺便取了个快递。",
|
| 77 |
+
"下周开会的内容我还没准备完,今晚得加个班。",
|
| 78 |
+
"邻居家的狗最近晚上老叫,吵得人睡不着。",
|
| 79 |
+
"上次说的那个方案,客户反馈还不错,就是工期太紧。",
|
| 80 |
+
"我今天试着做了道红烧肉,味道还行,就是有点咸。",
|
| 81 |
+
"地铁口新开了一家奶茶店,排队的人特别多。",
|
| 82 |
+
"天气预报说明天降温,出门记得多穿点。",
|
| 83 |
+
"我把旧手机的照片都备份到电脑上了。",
|
| 84 |
+
"新来的同事挺靠谱的,交办的事情都能按时完成。",
|
| 85 |
+
"超市打折的时候我囤了不少纸巾和洗衣液。",
|
| 86 |
+
"这个项目其实不难,主要是沟通起来比较费劲。",
|
| 87 |
+
"楼下的早餐店换了老板,油条没有以前好吃了。",
|
| 88 |
+
"我在网上买了个书架,装了一个小时才装好。",
|
| 89 |
+
"妈妈打电话来说家里的桂花开了,让我回去看看。",
|
| 90 |
+
"这个周末打算去爬山,好久没运动了。",
|
| 91 |
+
"我把会议纪要整理��发到群里了,大家记得看。",
|
| 92 |
+
"打印机又卡纸了,修了半小时才弄好。",
|
| 93 |
+
"晚上的风挺大的,窗户被吹得呼呼响。",
|
| 94 |
+
"上个月的项目结项了,团队一起吃了顿饭庆祝。",
|
| 95 |
+
"小区里的银杏树黄了,特别好看。",
|
| 96 |
+
"我最近在学做菜,最拿手的是西红柿炒蛋。",
|
| 97 |
+
"公交车晚点了二十分钟,差点迟到。",
|
| 98 |
+
"这个月业绩不错,老板说要给大家发奖金。",
|
| 99 |
+
"我把书还回图书馆了,顺便借了两本新的。",
|
| 100 |
+
"朋友推荐了一家火锅店,说是味道特别好。",
|
| 101 |
+
"早上起晚了,来不及吃早饭,现在有点饿。",
|
| 102 |
+
"新买的耳机用着不错,降噪效果明显。",
|
| 103 |
+
"我把房间收拾了一遍,看着舒服多了。",
|
| 104 |
+
]
|
| 105 |
+
|
| 106 |
+
# --------------------------------------------------------------------------
|
| 107 |
+
# Formulaic clause pools. Combinations of these produce many unique inputs;
|
| 108 |
+
# the rule engine maps each one to a deterministic human-flavored target.
|
| 109 |
+
# --------------------------------------------------------------------------
|
| 110 |
+
|
| 111 |
+
EN_SUBJECTS = [
|
| 112 |
+
"artificial intelligence", "digital transformation", "cloud computing",
|
| 113 |
+
"data analytics", "the new platform", "our organization", "this initiative",
|
| 114 |
+
"remote work", "machine learning", "automation", "the latest release",
|
| 115 |
+
"the training program", "our partnership", "the revised policy",
|
| 116 |
+
]
|
| 117 |
+
|
| 118 |
+
EN_CLAUSES = [
|
| 119 |
+
"it is important to note that {s} plays a crucial role in business success",
|
| 120 |
+
"it is worth noting that leveraging {s} serves as a testament to our commitment to innovation",
|
| 121 |
+
"our comprehensive approach to {s} fosters collaboration and drives growth",
|
| 122 |
+
"the seamless integration of {s} enhances efficiency and delivers a robust solution",
|
| 123 |
+
"embracing {s} is no longer optional but essential for long-term competitiveness",
|
| 124 |
+
"{s} requires high-quality data to function effectively",
|
| 125 |
+
"our groundbreaking platform leverages cutting-edge technology to provide a seamless experience",
|
| 126 |
+
"the state-of-the-art solution plays a crucial role in improving operational efficiency",
|
| 127 |
+
"it is essential to delve into {s} in order to remain competitive",
|
| 128 |
+
"fostering a holistic approach to {s} is paramount",
|
| 129 |
+
"{s} transforms the way we work in a significant way",
|
| 130 |
+
"this game-changing innovation will revolutionize the industry",
|
| 131 |
+
]
|
| 132 |
+
|
| 133 |
+
EN_CONNECTORS = ["Moreover,", "Furthermore,", "Additionally,", "In conclusion,"]
|
| 134 |
+
|
| 135 |
+
ZH_SUBJECTS = [
|
| 136 |
+
"数字化转型", "人工智能", "这套方案", "新的平台", "我们的团队",
|
| 137 |
+
"在线办公", "自动化流程", "数据治理", "这次改革", "人才培养",
|
| 138 |
+
]
|
| 139 |
+
|
| 140 |
+
ZH_CLAUSES = [
|
| 141 |
+
"值得注意的是,{s}发挥着重要作用",
|
| 142 |
+
"在当今快速发展的时代,{s}是企业发展的重要保障",
|
| 143 |
+
"综上所述,我们通过赋能团队来助力企业实现降本增效",
|
| 144 |
+
"这一方案形成了完整的业务闭环,无缝连接各个环节",
|
| 145 |
+
"由此可见,{s}已经成为企业发展的必然趋势",
|
| 146 |
+
"总而言之,企业应当积极拥抱新技术,从而在激烈的竞争中保持优势",
|
| 147 |
+
"{s}对于企业来说至关重要",
|
| 148 |
+
"我们需要不断提升效率,持续优化流程",
|
| 149 |
+
"这一举措实现了闭环管理,进一步提升了整体效率",
|
| 150 |
+
"数字化转型为{s}提供了新的发展机遇",
|
| 151 |
+
"相关部门应该加快制定实施细则,确保各项措施落地见效",
|
| 152 |
+
"这一政策有助于优化资源配置,推动产业转型升级",
|
| 153 |
+
"我们要坚持以用户为中心,不断完善产品服务体系",
|
| 154 |
+
"项目团队克服了诸多困难,最终按期完成了交付任务",
|
| 155 |
+
"该平台整合了线上线下资源,覆盖了多个业务场景",
|
| 156 |
+
"通过数据驱动的方式,我们能够更好地洞察市场变化",
|
| 157 |
+
"本次会议明确了下一阶段的工作重点和发展方向",
|
| 158 |
+
"这套体系有效降低了运营成本,提高了响应速度",
|
| 159 |
+
"从长远来看,{s}将深刻改变行业的竞争格局",
|
| 160 |
+
"各方应加强沟通协作,共同推进相关工作的开展",
|
| 161 |
+
]
|
| 162 |
+
|
| 163 |
+
PROTECTED_SEEDS = [
|
| 164 |
+
"The license key is ABC-12345 and the server is at https://api.lynote.ai/v1/detect.",
|
| 165 |
+
"The report shows a 23.5% increase in Q3, up from 18.2% last year.",
|
| 166 |
+
"Run `pip install humanize-text==1.4.2` and check /usr/local/bin/humanize.",
|
| 167 |
+
"The budget is $12,400 for this quarter, as noted in \"Project Lynote\".",
|
| 168 |
+
"The API accepts 500 requests per minute at https://hub.example.com and returns a 99.9% success rate.",
|
| 169 |
+
"The server rebooted at 03:47 and restored 1.2 TB of data within 10 minutes.",
|
| 170 |
+
"版本号是 v2.1.3,接口地址是 https://api.lynote.ai,成本下降了 15%。",
|
| 171 |
+
"配置文件放在 /etc/lynote/config.toml,端口是 8000,超时 30 秒。",
|
| 172 |
+
"上个月的收入是 85000 元,比预期多了 12%,主要来自 https://lynote.ai。",
|
| 173 |
+
"服务器在 03:47 重启,恢复了 1.2 TB 的数据,耗时 10 分钟。",
|
| 174 |
+
]
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def _ai_en(rng: random.Random, n: int) -> list[str]:
|
| 178 |
+
out = []
|
| 179 |
+
subjects = EN_SUBJECTS
|
| 180 |
+
while len(out) < n:
|
| 181 |
+
s = rng.choice(subjects)
|
| 182 |
+
k = rng.randint(2, 4)
|
| 183 |
+
clauses = [c.format(s=s) for c in rng.sample(EN_CLAUSES, k)]
|
| 184 |
+
clauses[0] = clauses[0][0].upper() + clauses[0][1:]
|
| 185 |
+
text = clauses[0]
|
| 186 |
+
for c in clauses[1:]:
|
| 187 |
+
text += ". " + rng.choice(EN_CONNECTORS) + " " + c
|
| 188 |
+
text += "."
|
| 189 |
+
if text not in out:
|
| 190 |
+
out.append(text)
|
| 191 |
+
return out
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def _ai_zh(rng: random.Random, n: int) -> list[str]:
|
| 195 |
+
out = []
|
| 196 |
+
while len(out) < n:
|
| 197 |
+
s = rng.choice(ZH_SUBJECTS)
|
| 198 |
+
k = rng.randint(2, 4)
|
| 199 |
+
clauses = [c.format(s=s) for c in rng.sample(ZH_CLAUSES, k)]
|
| 200 |
+
text = ",".join(clauses[:-1]) + "," + clauses[-1] + "。" if k > 1 else clauses[0] + "。"
|
| 201 |
+
if text not in out:
|
| 202 |
+
out.append(text)
|
| 203 |
+
return out
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def _inject_cliche_en(seed: str) -> str:
|
| 207 |
+
phrase = random.choice(list(EN_PHRASES.keys()))
|
| 208 |
+
seed = seed.rstrip(".")
|
| 209 |
+
return f"{phrase}, {seed}."
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def _inject_cliche_zh(seed: str) -> str:
|
| 213 |
+
phrase = random.choice(list(ZH_PHRASES.keys()))
|
| 214 |
+
return f"{phrase},{seed}"
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def build(
|
| 218 |
+
seed: int = 42,
|
| 219 |
+
n_en_ai: int = 8_000,
|
| 220 |
+
n_zh_ai: int = 6_000,
|
| 221 |
+
n_mixed: int = 1_000,
|
| 222 |
+
n_protected: int = 300,
|
| 223 |
+
) -> list[dict]:
|
| 224 |
+
rng = random.Random(seed)
|
| 225 |
+
pairs: list[dict] = []
|
| 226 |
+
|
| 227 |
+
# 1. AI -> human.
|
| 228 |
+
for lang, ai_texts, _seeds in (
|
| 229 |
+
("en", _ai_en(rng, n_en_ai), EN_SEEDS),
|
| 230 |
+
("zh", _ai_zh(rng, n_zh_ai), ZH_SEEDS),
|
| 231 |
+
):
|
| 232 |
+
for ai in ai_texts:
|
| 233 |
+
human = humanize_rules(ai, lang)
|
| 234 |
+
if human == ai:
|
| 235 |
+
continue
|
| 236 |
+
pairs.append({"input_text": ai, "output_text": human, "lang": lang})
|
| 237 |
+
|
| 238 |
+
# 2. Identity pairs.
|
| 239 |
+
for lang, seeds in (("en", EN_SEEDS), ("zh", ZH_SEEDS)):
|
| 240 |
+
for s in seeds:
|
| 241 |
+
pairs.append({"input_text": s, "output_text": s, "lang": lang})
|
| 242 |
+
|
| 243 |
+
# 3. Mixed: clean prose + injected cliche -> cleaned prose.
|
| 244 |
+
for _ in range(n_mixed):
|
| 245 |
+
lang = rng.choice(["en", "zh"])
|
| 246 |
+
seed = rng.choice(EN_SEEDS if lang == "en" else ZH_SEEDS)
|
| 247 |
+
noisy = _inject_cliche_en(seed) if lang == "en" else _inject_cliche_zh(seed)
|
| 248 |
+
cleaned = humanize_rules(noisy, lang)
|
| 249 |
+
if cleaned != noisy:
|
| 250 |
+
pairs.append({"input_text": noisy, "output_text": cleaned, "lang": lang})
|
| 251 |
+
|
| 252 |
+
# 3b. Extra Chinese mixed pairs (identity/copy grounding).
|
| 253 |
+
for _ in range(n_mixed):
|
| 254 |
+
seed = rng.choice(ZH_SEEDS)
|
| 255 |
+
noisy = _inject_cliche_zh(seed)
|
| 256 |
+
cleaned = humanize_rules(noisy, "zh")
|
| 257 |
+
if cleaned != noisy:
|
| 258 |
+
pairs.append({"input_text": noisy, "output_text": cleaned, "lang": "zh"})
|
| 259 |
+
|
| 260 |
+
# 4. Protected spans (contextualized so dedup keeps variety).
|
| 261 |
+
en_prefixes = [
|
| 262 |
+
"It is important to note that ", "It is worth noting that ",
|
| 263 |
+
"Moreover, ", "Additionally, ", "In conclusion, ", "",
|
| 264 |
+
]
|
| 265 |
+
en_suffixes = [
|
| 266 |
+
" This is a robust and seamless solution.",
|
| 267 |
+
" Overall, this approach is a game-changer.",
|
| 268 |
+
" In conclusion, this matters for our work.",
|
| 269 |
+
" Furthermore, this initiative drives growth.",
|
| 270 |
+
"",
|
| 271 |
+
]
|
| 272 |
+
zh_prefixes = ["值得注意的是,", "由此可见,", "综上所述,", "在当今快速发展的时代,", ""]
|
| 273 |
+
zh_suffixes = ["这一方案发挥了重要作用。", "这就是完整的业务闭环。", "综上所述,我们需要持续优化。", ""]
|
| 274 |
+
protected_srcs = []
|
| 275 |
+
for seed in PROTECTED_SEEDS:
|
| 276 |
+
if re.search(r"[\u4e00-\u9fff]", seed):
|
| 277 |
+
protected_srcs.extend(
|
| 278 |
+
f"{p}{seed}{s}" for p in zh_prefixes for s in zh_suffixes
|
| 279 |
+
)
|
| 280 |
+
else:
|
| 281 |
+
protected_srcs.extend(
|
| 282 |
+
f"{p}{seed}{s}" for p in en_prefixes for s in en_suffixes
|
| 283 |
+
)
|
| 284 |
+
for src in protected_srcs:
|
| 285 |
+
lang = "zh" if re.search(r"[\u4e00-\u9fff]", src) else "en"
|
| 286 |
+
cleaned = humanize_rules(src, lang)
|
| 287 |
+
if cleaned != src:
|
| 288 |
+
# Raw pair (URLs/numbers visible).
|
| 289 |
+
pairs.append({"input_text": src, "output_text": cleaned, "lang": lang})
|
| 290 |
+
# Masked pair: identical placeholder spans on both sides, matching
|
| 291 |
+
# inference-time protection so the model learns to copy them.
|
| 292 |
+
masked_src, _ = protect(src, lang)
|
| 293 |
+
masked_cleaned, _ = protect(cleaned, lang)
|
| 294 |
+
pairs.append(
|
| 295 |
+
{"input_text": masked_src, "output_text": masked_cleaned, "lang": lang}
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
seen, unique = set(), []
|
| 299 |
+
for p in pairs:
|
| 300 |
+
if p["input_text"] in seen:
|
| 301 |
+
continue
|
| 302 |
+
seen.add(p["input_text"])
|
| 303 |
+
unique.append(p)
|
| 304 |
+
return unique
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def main() -> None:
|
| 308 |
+
parser = argparse.ArgumentParser()
|
| 309 |
+
parser.add_argument("--out", default="data", help="output directory")
|
| 310 |
+
parser.add_argument("--seed", type=int, default=42)
|
| 311 |
+
parser.add_argument("--val-size", type=int, default=500)
|
| 312 |
+
parser.add_argument("--test-size", type=int, default=1_000)
|
| 313 |
+
args = parser.parse_args()
|
| 314 |
+
|
| 315 |
+
out = Path(args.out)
|
| 316 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 317 |
+
pairs = build(seed=args.seed)
|
| 318 |
+
rng = random.Random(args.seed)
|
| 319 |
+
rng.shuffle(pairs)
|
| 320 |
+
|
| 321 |
+
test = pairs[: args.test_size]
|
| 322 |
+
val = pairs[args.test_size : args.test_size + args.val_size]
|
| 323 |
+
train = pairs[args.test_size + args.val_size :]
|
| 324 |
+
|
| 325 |
+
for split, items in (("train", train), ("val", val), ("test", test)):
|
| 326 |
+
with open(out / f"{split}.jsonl", "w", encoding="utf-8") as f:
|
| 327 |
+
for p in items:
|
| 328 |
+
f.write(json.dumps(p, ensure_ascii=False) + "\n")
|
| 329 |
+
print(f"{split}: {len(items)} pairs")
|
| 330 |
+
|
| 331 |
+
print("test langs:", sorted({p['lang'] for p in test}))
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
if __name__ == "__main__":
|
| 335 |
+
main()
|
scripts/evaluate.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Benchmark the humanizer model.
|
| 2 |
+
|
| 3 |
+
Measures:
|
| 4 |
+
- corpus BLEU (sacrebleu) between outputs and rule references on AI->human
|
| 5 |
+
held-out rows (meaning preservation),
|
| 6 |
+
- AI-cliché removal rate on AI-flavored inputs,
|
| 7 |
+
- identity stability on ALL clean-prose (identity) pairs in the corpus,
|
| 8 |
+
- protected-span preservation on ALL examples containing protected spans,
|
| 9 |
+
- latency (characters per second, MPS/CPU).
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import argparse
|
| 13 |
+
import json
|
| 14 |
+
import re
|
| 15 |
+
import sys
|
| 16 |
+
import time
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
|
| 19 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 20 |
+
|
| 21 |
+
from humanize import Humanizer, protect
|
| 22 |
+
|
| 23 |
+
EN_CLICHES = [
|
| 24 |
+
"it is important to note that", "it is worth noting that", "moreover",
|
| 25 |
+
"furthermore", "in conclusion", "leverage", "seamless", "robust",
|
| 26 |
+
"testament", "delve", "cutting-edge", "state-of-the-art",
|
| 27 |
+
]
|
| 28 |
+
ZH_CLICHES = [
|
| 29 |
+
"值得注意的是", "综上所述", "赋能", "降本增效", "闭环", "无缝",
|
| 30 |
+
"由此可见", "总而言之",
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def scan(data_dir: str):
|
| 35 |
+
rows = []
|
| 36 |
+
for split in ("train", "val", "test"):
|
| 37 |
+
with open(Path(data_dir) / f"{split}.jsonl", encoding="utf-8") as f:
|
| 38 |
+
for line in f:
|
| 39 |
+
rows.append(json.loads(line))
|
| 40 |
+
return rows
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def main():
|
| 44 |
+
parser = argparse.ArgumentParser()
|
| 45 |
+
parser.add_argument("--model", default="checkpoints/humanize-text-model")
|
| 46 |
+
parser.add_argument("--data", default="data")
|
| 47 |
+
parser.add_argument("--ai-limit", type=int, default=500)
|
| 48 |
+
args = parser.parse_args()
|
| 49 |
+
|
| 50 |
+
from sacrebleu import corpus_bleu
|
| 51 |
+
|
| 52 |
+
humanizer = Humanizer(model_id=args.model)
|
| 53 |
+
rows = scan(args.data)
|
| 54 |
+
|
| 55 |
+
ai_rows = [r for r in rows if r["input_text"] != r["output_text"]][: args.ai_limit]
|
| 56 |
+
identity_rows = [r for r in rows if r["input_text"] == r["output_text"]]
|
| 57 |
+
protected_rows = [
|
| 58 |
+
r for r in rows
|
| 59 |
+
if re.search(r"PROTECTED_\d+", r["input_text"]) or re.search(
|
| 60 |
+
r"https?://|PROTECTED_\d+|\d", r["input_text"]
|
| 61 |
+
)
|
| 62 |
+
]
|
| 63 |
+
protected_rows = [r for r in protected_rows if "PROTECTED_" in r["input_text"] or "https://" in r["input_text"]]
|
| 64 |
+
|
| 65 |
+
# Deduplicate by input so samples are unique.
|
| 66 |
+
def dedupe(items):
|
| 67 |
+
seen, out = set(), []
|
| 68 |
+
for r in items:
|
| 69 |
+
if r["input_text"] in seen:
|
| 70 |
+
continue
|
| 71 |
+
seen.add(r["input_text"])
|
| 72 |
+
out.append(r)
|
| 73 |
+
return out
|
| 74 |
+
|
| 75 |
+
ai_rows = dedupe(ai_rows)
|
| 76 |
+
identity_rows = dedupe(identity_rows)
|
| 77 |
+
protected_rows = dedupe(protected_rows)
|
| 78 |
+
|
| 79 |
+
print(f"AI->human rows: {len(ai_rows)} | identity rows: {len(identity_rows)} | protected rows: {len(protected_rows)}", flush=True)
|
| 80 |
+
|
| 81 |
+
# 1. AI -> human: BLEU + cliché removal.
|
| 82 |
+
refs, hyps = [], []
|
| 83 |
+
cliche_in = 0
|
| 84 |
+
cliche_removed = 0
|
| 85 |
+
t0 = time.time()
|
| 86 |
+
for n, row in enumerate(ai_rows, 1):
|
| 87 |
+
out = humanizer.humanize(row["input_text"], num_beams=3)
|
| 88 |
+
hyps.append(out)
|
| 89 |
+
refs.append(row["output_text"])
|
| 90 |
+
lower = out.lower()
|
| 91 |
+
hits_in = [c for c in (ZH_CLICHES if row["lang"] == "zh" else EN_CLICHES)
|
| 92 |
+
if c in (row["input_text"] if row["lang"] == "zh" else row["input_text"].lower())]
|
| 93 |
+
hits_out = [c for c in (ZH_CLICHES if row["lang"] == "zh" else EN_CLICHES)
|
| 94 |
+
if c in lower]
|
| 95 |
+
if hits_in:
|
| 96 |
+
cliche_in += 1
|
| 97 |
+
if not hits_out:
|
| 98 |
+
cliche_removed += 1
|
| 99 |
+
if n % 50 == 0:
|
| 100 |
+
print(f" ...ai {n}/{len(ai_rows)}", flush=True)
|
| 101 |
+
bleu = corpus_bleu(hyps, [refs]).score
|
| 102 |
+
|
| 103 |
+
# 2. Identity stability.
|
| 104 |
+
identity_ok = 0
|
| 105 |
+
for n, row in enumerate(identity_rows, 1):
|
| 106 |
+
out = humanizer.humanize(row["input_text"], num_beams=3)
|
| 107 |
+
# Allow light edits, no big drift: use char-level edit ratio.
|
| 108 |
+
ratio = sum(1 for a, b in zip(out, row["input_text"]) if a != b) / max(len(row["input_text"]), 1)
|
| 109 |
+
if ratio <= 0.15:
|
| 110 |
+
identity_ok += 1
|
| 111 |
+
if n % 25 == 0:
|
| 112 |
+
print(f" ...identity {n}/{len(identity_rows)}", flush=True)
|
| 113 |
+
|
| 114 |
+
# 3. Protected spans.
|
| 115 |
+
protected_ok = 0
|
| 116 |
+
protected_total = 0
|
| 117 |
+
failures = []
|
| 118 |
+
for n, row in enumerate(protected_rows, 1):
|
| 119 |
+
masked, spans = protect(row["input_text"])
|
| 120 |
+
out = humanizer.humanize(row["input_text"], num_beams=3)
|
| 121 |
+
for span in spans:
|
| 122 |
+
protected_total += 1
|
| 123 |
+
if span in out:
|
| 124 |
+
protected_ok += 1
|
| 125 |
+
else:
|
| 126 |
+
failures.append((row["lang"], span, out))
|
| 127 |
+
if n % 25 == 0:
|
| 128 |
+
print(f" ...protected {n}/{len(protected_rows)}", flush=True)
|
| 129 |
+
|
| 130 |
+
elapsed = time.time() - t0
|
| 131 |
+
chars = sum(len(r["input_text"]) for r in ai_rows + identity_rows + protected_rows)
|
| 132 |
+
|
| 133 |
+
print("=" * 60)
|
| 134 |
+
print(f"model: {args.model}")
|
| 135 |
+
print(f"corpus BLEU (AI->human, n={len(ai_rows)}): {bleu:.1f}")
|
| 136 |
+
print(f"cliche removal rate: {cliche_removed / max(cliche_in, 1):.1%} ({cliche_removed}/{cliche_in})")
|
| 137 |
+
print(f"identity stability (n={len(identity_rows)}): {identity_ok / max(len(identity_rows), 1):.1%} ({identity_ok}/{len(identity_rows)})")
|
| 138 |
+
print(f"protected-span preservation: {protected_ok}/{protected_total} "
|
| 139 |
+
f"({protected_ok / max(protected_total, 1):.1%})")
|
| 140 |
+
print(f"throughput: {chars / max(elapsed, 0.01):.0f} chars/s")
|
| 141 |
+
if failures:
|
| 142 |
+
print(f"\nprotected failures ({len(failures)}):")
|
| 143 |
+
for lang, span, out in failures[:10]:
|
| 144 |
+
print(f" [{lang}] lost {span!r} in {out[:80]!r}")
|
| 145 |
+
|
| 146 |
+
print("\nsample outputs:")
|
| 147 |
+
for row in ai_rows[:5]:
|
| 148 |
+
print(" IN :", row["input_text"][:100])
|
| 149 |
+
print(" OUT:", humanizer.humanize(row["input_text"], num_beams=3)[:100])
|
| 150 |
+
print()
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
if __name__ == "__main__":
|
| 154 |
+
main()
|
scripts/train.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Fine-tune T5-small as a text humanizer (one language per run).
|
| 2 |
+
|
| 3 |
+
English uses ``google-t5/t5-small``; Chinese uses a Chinese T5-small
|
| 4 |
+
(``uer/t5-small-chinese-cluecorpussmall``). Supervised pairs come from
|
| 5 |
+
``scripts/build_dataset.py`` (rule-generated), filtered to one language.
|
| 6 |
+
|
| 7 |
+
MPS notes: avoid per-step host sync; accumulate the loss tensor and sync at
|
| 8 |
+
evaluation. Save a checkpoint at the end of every epoch.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import argparse
|
| 12 |
+
import json
|
| 13 |
+
import time
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
from torch.optim import AdamW
|
| 18 |
+
from torch.utils.data import DataLoader, Dataset
|
| 19 |
+
from transformers import BertTokenizer, T5ForConditionalGeneration, T5Tokenizer
|
| 20 |
+
|
| 21 |
+
BASE_MODELS = {
|
| 22 |
+
"en": "google-t5/t5-small",
|
| 23 |
+
"zh": "uer/t5-small-chinese-cluecorpussmall",
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class PairsDataset(Dataset):
|
| 28 |
+
def __init__(self, path: Path, tokenizer: T5Tokenizer, max_len: int, lang: str):
|
| 29 |
+
self.pairs = []
|
| 30 |
+
with open(path, encoding="utf-8") as f:
|
| 31 |
+
for line in f:
|
| 32 |
+
row = json.loads(line)
|
| 33 |
+
if row["lang"] != lang:
|
| 34 |
+
continue
|
| 35 |
+
self.pairs.append((row["input_text"], row["output_text"]))
|
| 36 |
+
self.tokenizer = tokenizer
|
| 37 |
+
self.max_len = max_len
|
| 38 |
+
|
| 39 |
+
def __len__(self):
|
| 40 |
+
return len(self.pairs)
|
| 41 |
+
|
| 42 |
+
def __getitem__(self, idx):
|
| 43 |
+
src, tgt = self.pairs[idx]
|
| 44 |
+
enc = self.tokenizer(
|
| 45 |
+
src, max_length=self.max_len, padding="max_length", truncation=True
|
| 46 |
+
)
|
| 47 |
+
dec = self.tokenizer(
|
| 48 |
+
tgt, max_length=self.max_len, padding="max_length", truncation=True
|
| 49 |
+
)
|
| 50 |
+
labels = torch.tensor(dec["input_ids"])
|
| 51 |
+
labels[labels == self.tokenizer.pad_token_id] = -100
|
| 52 |
+
return {
|
| 53 |
+
"input_ids": torch.tensor(enc["input_ids"]),
|
| 54 |
+
"attention_mask": torch.tensor(enc["attention_mask"]),
|
| 55 |
+
"labels": labels,
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def decode(ids, tokenizer):
|
| 60 |
+
ids = torch.where(
|
| 61 |
+
(ids == -100) | (ids == tokenizer.pad_token_id),
|
| 62 |
+
torch.tensor(tokenizer.pad_token_id),
|
| 63 |
+
ids,
|
| 64 |
+
)
|
| 65 |
+
return tokenizer.decode(ids, skip_special_tokens=True)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def main():
|
| 69 |
+
parser = argparse.ArgumentParser()
|
| 70 |
+
parser.add_argument("--lang", choices=["en", "zh"], default="en")
|
| 71 |
+
parser.add_argument("--data", default="data")
|
| 72 |
+
parser.add_argument("--out", default=None)
|
| 73 |
+
parser.add_argument("--base-model", default=None)
|
| 74 |
+
parser.add_argument("--max-len", type=int, default=128)
|
| 75 |
+
parser.add_argument("--batch-size", type=int, default=32)
|
| 76 |
+
parser.add_argument("--lr", type=float, default=3e-4)
|
| 77 |
+
parser.add_argument("--epochs", type=int, default=3)
|
| 78 |
+
parser.add_argument("--warmup-steps", type=int, default=100)
|
| 79 |
+
parser.add_argument("--eval-every", type=int, default=200)
|
| 80 |
+
parser.add_argument("--seed", type=int, default=42)
|
| 81 |
+
args = parser.parse_args()
|
| 82 |
+
|
| 83 |
+
base_model = args.base_model or BASE_MODELS[args.lang]
|
| 84 |
+
out_dir = Path(args.out or f"checkpoints/humanize-text-model/{args.lang}")
|
| 85 |
+
|
| 86 |
+
torch.manual_seed(args.seed)
|
| 87 |
+
device = "mps" if torch.backends.mps.is_available() else (
|
| 88 |
+
"cuda" if torch.cuda.is_available() else "cpu"
|
| 89 |
+
)
|
| 90 |
+
print(f"lang={args.lang} base={base_model} device={device}", flush=True)
|
| 91 |
+
|
| 92 |
+
tokenizer = (
|
| 93 |
+
BertTokenizer.from_pretrained(base_model)
|
| 94 |
+
if args.lang == "zh"
|
| 95 |
+
else T5Tokenizer.from_pretrained(base_model)
|
| 96 |
+
)
|
| 97 |
+
if (out_dir / "pytorch_model.bin").exists() or (out_dir / "model.safetensors").exists():
|
| 98 |
+
print(f"resuming from existing checkpoint in {out_dir}", flush=True)
|
| 99 |
+
model = T5ForConditionalGeneration.from_pretrained(out_dir)
|
| 100 |
+
else:
|
| 101 |
+
model = T5ForConditionalGeneration.from_pretrained(base_model)
|
| 102 |
+
model.to(device)
|
| 103 |
+
|
| 104 |
+
train_ds = PairsDataset(Path(args.data) / "train.jsonl", tokenizer, args.max_len, args.lang)
|
| 105 |
+
val_ds = PairsDataset(Path(args.data) / "val.jsonl", tokenizer, args.max_len, args.lang)
|
| 106 |
+
val_samples = [
|
| 107 |
+
json.loads(l) for l in open(Path(args.data) / "val.jsonl", encoding="utf-8")
|
| 108 |
+
if json.loads(l)["lang"] == args.lang
|
| 109 |
+
][:3]
|
| 110 |
+
|
| 111 |
+
train_loader = DataLoader(train_ds, batch_size=args.batch_size, shuffle=True, num_workers=0)
|
| 112 |
+
optimizer = AdamW(model.parameters(), lr=args.lr)
|
| 113 |
+
total_steps = args.epochs * len(train_loader)
|
| 114 |
+
scheduler = torch.optim.lr_scheduler.LinearLR(
|
| 115 |
+
optimizer, start_factor=1 / (args.warmup_steps + 1), total_iters=args.warmup_steps
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 119 |
+
step = 0
|
| 120 |
+
t0 = time.time()
|
| 121 |
+
|
| 122 |
+
for epoch in range(1, args.epochs + 1):
|
| 123 |
+
model.train()
|
| 124 |
+
epoch_loss = torch.zeros(())
|
| 125 |
+
for batch in train_loader:
|
| 126 |
+
batch = {k: v.to(device) for k, v in batch.items()}
|
| 127 |
+
loss = model(**batch).loss
|
| 128 |
+
loss.backward()
|
| 129 |
+
optimizer.step()
|
| 130 |
+
scheduler.step()
|
| 131 |
+
optimizer.zero_grad()
|
| 132 |
+
epoch_loss = epoch_loss + loss.detach()
|
| 133 |
+
step += 1
|
| 134 |
+
|
| 135 |
+
if step % 100 == 0:
|
| 136 |
+
print(f"[hb] step {step}/{total_steps} ({time.time() - t0:.0f}s)", flush=True)
|
| 137 |
+
|
| 138 |
+
if step % args.eval_every == 0:
|
| 139 |
+
model.eval()
|
| 140 |
+
vloss = 0.0
|
| 141 |
+
with torch.no_grad():
|
| 142 |
+
for i in range(16):
|
| 143 |
+
vb = val_ds[i]
|
| 144 |
+
vb = {k: v.unsqueeze(0).to(device) for k, v in vb.items()}
|
| 145 |
+
vloss += model(**vb).loss.item()
|
| 146 |
+
avg_train = (epoch_loss / step).item()
|
| 147 |
+
model.train()
|
| 148 |
+
print(
|
| 149 |
+
f"[step {step}] epoch={epoch} train_loss={avg_train:.4f} "
|
| 150 |
+
f"val_loss={vloss / 16:.4f} ({time.time() - t0:.0f}s)",
|
| 151 |
+
flush=True,
|
| 152 |
+
)
|
| 153 |
+
sample = val_samples[0]
|
| 154 |
+
model.eval()
|
| 155 |
+
with torch.no_grad():
|
| 156 |
+
ids = model.generate(
|
| 157 |
+
input_ids=tokenizer(sample["input_text"], return_tensors="pt").input_ids.to(device),
|
| 158 |
+
max_length=args.max_len,
|
| 159 |
+
)
|
| 160 |
+
out = decode(ids[0], tokenizer)
|
| 161 |
+
model.train()
|
| 162 |
+
print(" IN :", sample["input_text"][:90])
|
| 163 |
+
print(" OUT:", out[:90], flush=True)
|
| 164 |
+
|
| 165 |
+
avg = (epoch_loss / len(train_loader)).item()
|
| 166 |
+
print(f"epoch {epoch} done: avg_loss={avg:.4f}", flush=True)
|
| 167 |
+
model.save_pretrained(out_dir)
|
| 168 |
+
tokenizer.save_pretrained(out_dir)
|
| 169 |
+
print(f"checkpoint saved to {out_dir}", flush=True)
|
| 170 |
+
|
| 171 |
+
print(f"final model saved to {out_dir}", flush=True)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
if __name__ == "__main__":
|
| 175 |
+
main()
|
scripts/upload_model.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Upload the humanize-text-model repo to Hugging Face.
|
| 2 |
+
|
| 3 |
+
Repo layout (matches Humanizer's expectations):
|
| 4 |
+
en/ zh/ humanize.py README.md LICENSE requirements.txt scripts/ tests/ src/
|
| 5 |
+
"""
|
| 6 |
+
import os
|
| 7 |
+
import shutil
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
from huggingface_hub import HfApi
|
| 11 |
+
|
| 12 |
+
TOKEN = os.environ["HF_TOKEN"]
|
| 13 |
+
REPO = "Danny-Lynote/humanize-text-model"
|
| 14 |
+
SRC = Path(__file__).resolve().parent.parent
|
| 15 |
+
STAGE = Path("/var/folders/x9/f0cp93qs3qqclss21p96pcgr0000gn/T/opencode/humanize-upload")
|
| 16 |
+
|
| 17 |
+
shutil.rmtree(STAGE, ignore_errors=True)
|
| 18 |
+
STAGE.mkdir(parents=True)
|
| 19 |
+
for name in ["humanize.py", "README.md", "LICENSE", "requirements.txt", "scripts", "tests", "src"]:
|
| 20 |
+
shutil.copytree(SRC / name, STAGE / name) if (SRC / name).is_dir() else shutil.copy2(SRC / name, STAGE / name)
|
| 21 |
+
shutil.copytree(SRC / "checkpoints/humanize-text-model/en", STAGE / "en")
|
| 22 |
+
shutil.copytree(SRC / "checkpoints/humanize-text-model/zh", STAGE / "zh")
|
| 23 |
+
(STAGE / "docs").mkdir(exist_ok=True)
|
| 24 |
+
if (SRC / "docs").exists():
|
| 25 |
+
for f in (SRC / "docs").iterdir():
|
| 26 |
+
if f.is_file():
|
| 27 |
+
shutil.copy2(f, STAGE / "docs" / f.name)
|
| 28 |
+
|
| 29 |
+
api = HfApi(token=TOKEN, endpoint="https://huggingface.co")
|
| 30 |
+
try:
|
| 31 |
+
info = api.repo_info(repo_id=REPO, repo_type="model")
|
| 32 |
+
print("repo exists, sha:", info.sha)
|
| 33 |
+
except Exception:
|
| 34 |
+
api.create_repo(repo_id=REPO, repo_type="model", private=False)
|
| 35 |
+
print("created model repo")
|
| 36 |
+
|
| 37 |
+
api.upload_folder(
|
| 38 |
+
folder_path=str(STAGE),
|
| 39 |
+
repo_id=REPO,
|
| 40 |
+
repo_type="model",
|
| 41 |
+
commit_message="Bilingual humanize-text-model (EN T5-small + ZH Chinese T5-small)",
|
| 42 |
+
)
|
| 43 |
+
print("uploaded. url: https://huggingface.co/" + REPO)
|
src/rules.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Shared rule engine for the humanize-text model.
|
| 2 |
+
|
| 3 |
+
Conservative bilingual editing rules derived from the reference projects:
|
| 4 |
+
- spaces/humanizer-lite (phrase replacements + protected spans)
|
| 5 |
+
- trust-fixes/humanizer.py (multi-method pipeline philosophy)
|
| 6 |
+
|
| 7 |
+
The rules generate the supervised (ai_text -> human_text) pairs used to
|
| 8 |
+
fine-tune the seq2seq model, and can also run as a standalone fallback.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import re
|
| 12 |
+
|
| 13 |
+
# --------------------------------------------------------------------------
|
| 14 |
+
# Protected spans: code, URLs, paths, numbers, quoted strings.
|
| 15 |
+
# --------------------------------------------------------------------------
|
| 16 |
+
|
| 17 |
+
PROTECTED_PATTERN = re.compile(
|
| 18 |
+
r"(`[^`]+`|https?://[^\s\u4e00-\u9fff,。!?;:、]+|(?:[A-Za-z]:)?(?:[/\\][\w.\-]+)+|"
|
| 19 |
+
r"\b\d+(?:\.\d+)?(?:%|[A-Za-z]+)?\b|\"[^\"]*\"|'[^']*')"
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
CJK_RE = re.compile(r"[\u4e00-\u9fff]")
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def protect(text: str, lang: str = "auto"):
|
| 27 |
+
"""Replace protected spans with language-aware placeholders:
|
| 28 |
+
EN ``PROTECTED_n``, ZH ``【保护n】`` (both tokenizer-friendly)."""
|
| 29 |
+
protected = []
|
| 30 |
+
zh = (
|
| 31 |
+
lang == "zh"
|
| 32 |
+
or (lang == "auto" and len(CJK_RE.findall(text)) > 0)
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def replace(match):
|
| 36 |
+
token = f"【保护{len(protected)}】" if zh else f"PROTECTED_{len(protected)}"
|
| 37 |
+
protected.append(match.group(0))
|
| 38 |
+
return token
|
| 39 |
+
|
| 40 |
+
return PROTECTED_PATTERN.sub(replace, text), protected
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def restore(text: str, protected):
|
| 44 |
+
"""Restore placeholders (either format, tolerant of inserted spaces)."""
|
| 45 |
+
for index, value in enumerate(protected):
|
| 46 |
+
text = re.sub(rf"PROTECTED_{index}\b", lambda m: value, text)
|
| 47 |
+
text = re.sub(
|
| 48 |
+
rf"【\s*保\s*护\s*{index}\s*】", lambda m: value, text
|
| 49 |
+
)
|
| 50 |
+
return text
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# --------------------------------------------------------------------------
|
| 54 |
+
# Formulaic (AI) phrases, English and Chinese.
|
| 55 |
+
# --------------------------------------------------------------------------
|
| 56 |
+
|
| 57 |
+
EN_PHRASES = {
|
| 58 |
+
"in today's rapidly evolving world": "today",
|
| 59 |
+
"in today's fast-paced world": "now",
|
| 60 |
+
"it is important to note that": "",
|
| 61 |
+
"it is worth noting that": "",
|
| 62 |
+
"it can be seen that": "",
|
| 63 |
+
"serves as a testament to": "shows",
|
| 64 |
+
"a testament to": "proof of",
|
| 65 |
+
"delve into": "look at",
|
| 66 |
+
"leverage": "use",
|
| 67 |
+
"leveraging": "using",
|
| 68 |
+
"seamless": "smooth",
|
| 69 |
+
"robust": "reliable",
|
| 70 |
+
"game-changer": "big improvement",
|
| 71 |
+
"groundbreaking": "new",
|
| 72 |
+
"cutting-edge": "modern",
|
| 73 |
+
"state-of-the-art": "modern",
|
| 74 |
+
"plays a crucial role in": "matters for",
|
| 75 |
+
"plays a crucial role": "matters",
|
| 76 |
+
"in the realm of": "in",
|
| 77 |
+
"in conclusion": "",
|
| 78 |
+
"moreover": "also",
|
| 79 |
+
"furthermore": "also",
|
| 80 |
+
"additionally": "also",
|
| 81 |
+
"it is essential to": "we need to",
|
| 82 |
+
"comprehensive approach": "broad approach",
|
| 83 |
+
"foster": "support",
|
| 84 |
+
"fostering": "supporting",
|
| 85 |
+
"undoubtedly": "",
|
| 86 |
+
"no longer optional but essential": "necessary",
|
| 87 |
+
"revolutionize": "change",
|
| 88 |
+
"revolutionizing": "changing",
|
| 89 |
+
"holistic": "full",
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
ZH_PHRASES = {
|
| 93 |
+
"值得注意的是": "",
|
| 94 |
+
"在当今快速发展的时代": "现在",
|
| 95 |
+
"综上所述": "",
|
| 96 |
+
"总而言之": "",
|
| 97 |
+
"由此可见": "",
|
| 98 |
+
"赋能": "帮助",
|
| 99 |
+
"助力": "帮助",
|
| 100 |
+
"降本增效": "省钱提效",
|
| 101 |
+
"闭环": "完整流程",
|
| 102 |
+
"无缝": "顺畅",
|
| 103 |
+
"发挥着重要作用": "很有用",
|
| 104 |
+
"发挥着至关重要的作用": "很重要",
|
| 105 |
+
"数字化转型": "数字化",
|
| 106 |
+
"必然趋势": "趋势",
|
| 107 |
+
"积极拥抱新技术": "用新技术",
|
| 108 |
+
"不断提升": "提高",
|
| 109 |
+
"持续优化": "改进",
|
| 110 |
+
"实现": "做到",
|
| 111 |
+
"进一步": "",
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
EN_OPENERS = ["Moreover,", "Furthermore,", "Additionally,", "In conclusion,"]
|
| 115 |
+
|
| 116 |
+
EN_EXTRA = {
|
| 117 |
+
"very": "",
|
| 118 |
+
"really": "",
|
| 119 |
+
"extremely": "",
|
| 120 |
+
"highly": "",
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
CONTRACTIONS = {
|
| 124 |
+
"it is": "it's",
|
| 125 |
+
"it will": "it'll",
|
| 126 |
+
"we are": "we're",
|
| 127 |
+
"we will": "we'll",
|
| 128 |
+
"you are": "you're",
|
| 129 |
+
"you will": "you'll",
|
| 130 |
+
"do not": "don't",
|
| 131 |
+
"does not": "doesn't",
|
| 132 |
+
"cannot": "can't",
|
| 133 |
+
"I am": "I'm",
|
| 134 |
+
"I will": "I'll",
|
| 135 |
+
"that is": "that's",
|
| 136 |
+
"there is": "there's",
|
| 137 |
+
"will not": "won't",
|
| 138 |
+
"it is not": "it isn't",
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
ZH_PARTICLES_END = ["了", "吧", "呢"]
|
| 142 |
+
ZH_PARTICLES_START = ["其实", "说实话", "我总觉得"]
|
| 143 |
+
|
| 144 |
+
# --------------------------------------------------------------------------
|
| 145 |
+
# Core rule application (single pass, deterministic).
|
| 146 |
+
# --------------------------------------------------------------------------
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def humanize_rules(text: str, lang: str = "auto") -> str:
|
| 150 |
+
"""Apply conservative editing rules to a draft. Deterministic."""
|
| 151 |
+
protected_text, protected = protect(text, lang)
|
| 152 |
+
|
| 153 |
+
if lang in ("auto", "en"):
|
| 154 |
+
low = protected_text.lower()
|
| 155 |
+
for phrase, replacement in EN_PHRASES.items():
|
| 156 |
+
protected_text = re.sub(re.escape(phrase), replacement, protected_text, flags=re.I)
|
| 157 |
+
# Only apply opener removals when they start a sentence boundary.
|
| 158 |
+
for opener in EN_OPENERS:
|
| 159 |
+
protected_text = re.sub(
|
| 160 |
+
re.escape(opener) + r"\s*", "", protected_text, flags=re.I
|
| 161 |
+
)
|
| 162 |
+
for word, replacement in EN_EXTRA.items():
|
| 163 |
+
protected_text = re.sub(r"\b" + re.escape(word) + r"\s+", "", protected_text, flags=re.I)
|
| 164 |
+
for phrase, replacement in CONTRACTIONS.items():
|
| 165 |
+
protected_text = re.sub(
|
| 166 |
+
r"\b" + re.escape(phrase) + r"\b", replacement, protected_text, flags=re.I
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
if lang in ("auto", "zh"):
|
| 170 |
+
for phrase, replacement in ZH_PHRASES.items():
|
| 171 |
+
protected_text = protected_text.replace(phrase, replacement)
|
| 172 |
+
protected_text = re.sub(r",{2,}", ",", protected_text)
|
| 173 |
+
|
| 174 |
+
protected_text = re.sub(r"[ \t]{2,}", " ", protected_text)
|
| 175 |
+
protected_text = re.sub(r"\s+([,.;:!?,。;:!?])", r"\1", protected_text)
|
| 176 |
+
protected_text = re.sub(r"[,;]\s*\.", ".", protected_text)
|
| 177 |
+
protected_text = re.sub(r"\.\s*[,;]", ".", protected_text)
|
| 178 |
+
protected_text = re.sub(r"[,,]\s*(?=[。!?.!?])", "", protected_text)
|
| 179 |
+
protected_text = re.sub(r"\.\s+([a-z])", lambda m: ". " + m.group(1).upper(), protected_text)
|
| 180 |
+
protected_text = re.sub(r"([.!?。!?])\s*\1+", r"\1", protected_text)
|
| 181 |
+
protected_text = re.sub(r"\n{3,}", "\n\n", protected_text).strip(" ,,")
|
| 182 |
+
return restore(protected_text, protected)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def zh_add_colloquial(text: str) -> str:
|
| 186 |
+
"""Add light colloquial markers (used for data augmentation only)."""
|
| 187 |
+
if re.search(r"[\u4e00-\u9fff]", text):
|
| 188 |
+
text = re.sub(r"[。!?]", lambda m: m.group(0) + "呢", text, count=1)
|
| 189 |
+
text = re.sub(r"^", "其实,", text, count=1)
|
| 190 |
+
text = text.replace("今天", "今天吧")
|
| 191 |
+
text = text.replace("我觉得", "我总觉得")
|
| 192 |
+
return text
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def en_add_colloquial(text: str) -> str:
|
| 196 |
+
"""Add light colloquial markers (used for data augmentation only)."""
|
| 197 |
+
text = re.sub(r"(^|\. )(I|we|he|she|they)\b", r"\1Honestly, \2", text, count=1)
|
| 198 |
+
text = text.replace("The ", "The ", 1)
|
| 199 |
+
if " quite " not in text:
|
| 200 |
+
text = re.sub(r"(\w+)\.$", r"\1, honestly.", text, count=1)
|
| 201 |
+
return text
|
tests/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
pytest.importorskip("src.rules")
|
tests/test_dataset.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for the synthetic dataset builder and rule engine."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import random
|
| 5 |
+
import sys
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 9 |
+
|
| 10 |
+
from src.rules import humanize_rules, protect, restore
|
| 11 |
+
|
| 12 |
+
DATA = Path(__file__).resolve().parent.parent / "data"
|
| 13 |
+
EN_PHRASES = {
|
| 14 |
+
"it is important to note that",
|
| 15 |
+
"it is worth noting that",
|
| 16 |
+
"moreover",
|
| 17 |
+
"furthermore",
|
| 18 |
+
"in conclusion",
|
| 19 |
+
"leverage",
|
| 20 |
+
"seamless",
|
| 21 |
+
"robust",
|
| 22 |
+
"delve",
|
| 23 |
+
"testament",
|
| 24 |
+
}
|
| 25 |
+
ZH_PHRASES = {
|
| 26 |
+
"值得注意的是",
|
| 27 |
+
"综上所述",
|
| 28 |
+
"赋能",
|
| 29 |
+
"降本增效",
|
| 30 |
+
"闭环",
|
| 31 |
+
"无缝",
|
| 32 |
+
"由此可见",
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _load(split: str):
|
| 37 |
+
pairs = []
|
| 38 |
+
with open(DATA / f"{split}.jsonl", encoding="utf-8") as f:
|
| 39 |
+
for line in f:
|
| 40 |
+
pairs.append(json.loads(line))
|
| 41 |
+
return pairs
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_splits_exist_and_nonempty():
|
| 45 |
+
for split in ("train", "val", "test"):
|
| 46 |
+
pairs = _load(split)
|
| 47 |
+
assert len(pairs) > 0, f"{split} is empty"
|
| 48 |
+
assert len(_load("train")) > 10_000
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def test_pairs_have_expected_fields():
|
| 52 |
+
for split in ("train", "val", "test"):
|
| 53 |
+
for p in _load(split):
|
| 54 |
+
assert set(p) == {"input_text", "output_text", "lang"}
|
| 55 |
+
assert p["lang"] in {"en", "zh"}
|
| 56 |
+
assert p["input_text"] and p["output_text"]
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def test_both_languages_present():
|
| 60 |
+
langs = {p["lang"] for p in _load("train")}
|
| 61 |
+
assert langs == {"en", "zh"}
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def test_ai_phrases_removed_in_targets():
|
| 65 |
+
counts = 0
|
| 66 |
+
checked = 0
|
| 67 |
+
for p in _load("train"):
|
| 68 |
+
if "值得注意的是" in p["input_text"] or "it is important to note that" in p["input_text"].lower():
|
| 69 |
+
checked += 1
|
| 70 |
+
target_lower = p["output_text"].lower()
|
| 71 |
+
if p["lang"] == "zh":
|
| 72 |
+
assert "值得注意的是" not in target_lower
|
| 73 |
+
assert "综上所述" not in target_lower
|
| 74 |
+
else:
|
| 75 |
+
assert "it is important to note that" not in target_lower
|
| 76 |
+
assert "it is worth noting that" not in target_lower
|
| 77 |
+
counts += 1
|
| 78 |
+
assert counts > 500
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def test_protected_spans_preserved():
|
| 82 |
+
seen = 0
|
| 83 |
+
for p in _load("train") + _load("test"):
|
| 84 |
+
src = p["input_text"]
|
| 85 |
+
if "https://" in src or "ABC-12345" in src or "v2.1.3" in src:
|
| 86 |
+
for token in ("https://api.lynote.ai", "ABC-12345", "v2.1.3"):
|
| 87 |
+
if token in src:
|
| 88 |
+
assert token in p["output_text"], f"lost {token} in {p['output_text']}"
|
| 89 |
+
seen += 1
|
| 90 |
+
assert seen > 10
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def test_identity_pairs_unchanged():
|
| 94 |
+
for p in _load("train"):
|
| 95 |
+
if p["input_text"] == p["output_text"]:
|
| 96 |
+
assert p["lang"] in {"en", "zh"}
|
| 97 |
+
return
|
| 98 |
+
raise AssertionError("no identity pairs found")
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def test_deterministic_generation():
|
| 102 |
+
"""Same seed yields the same corpus (checked on a fixed sample)."""
|
| 103 |
+
src = "In today's rapidly evolving world, it is important to note that this solution is robust. Moreover, it is seamless."
|
| 104 |
+
out1 = humanize_rules(src, "en")
|
| 105 |
+
out2 = humanize_rules(src, "en")
|
| 106 |
+
assert out1 == out2
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def test_protect_restore_roundtrip():
|
| 110 |
+
text = "Run `pip install x==1.0` on https://example.com/a/b now — the price is $12.50 and \"quoted\" text."
|
| 111 |
+
masked, protected = protect(text)
|
| 112 |
+
assert masked != text
|
| 113 |
+
assert "PROTECTED_0" in masked
|
| 114 |
+
assert restore(masked, protected) == text
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def test_rules_en():
|
| 118 |
+
src = "In today's rapidly evolving world, it is important to note that this robust solution serves as a testament to our commitment. Moreover, we leverage cutting-edge technology for a seamless experience."
|
| 119 |
+
out = humanize_rules(src, "en")
|
| 120 |
+
lower = out.lower()
|
| 121 |
+
assert "it is important to note that" not in lower
|
| 122 |
+
assert "moreover" not in lower
|
| 123 |
+
assert "leverage" not in lower
|
| 124 |
+
assert "seamless" not in lower
|
| 125 |
+
assert "robust" not in lower
|
| 126 |
+
assert "testament" not in lower
|
| 127 |
+
assert out != src
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def test_rules_zh():
|
| 131 |
+
src = "值得注意的是,在当今快速发展的时代,我们通过赋能团队来实现降本增效,形成完整闭环,无缝连接各个环节。"
|
| 132 |
+
out = humanize_rules(src, "zh")
|
| 133 |
+
assert "值得注意的是" not in out
|
| 134 |
+
assert "赋能" not in out
|
| 135 |
+
assert "降本增效" not in out
|
| 136 |
+
assert "无缝" not in out
|
| 137 |
+
assert out != src
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def test_rules_keep_human_prose_intact():
|
| 141 |
+
clean = "I walked the dog this morning and it started raining halfway through the park."
|
| 142 |
+
out = humanize_rules(clean, "en")
|
| 143 |
+
assert out == clean
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_dataset_reproducibility_from_seed():
|
| 147 |
+
random.seed(7)
|
| 148 |
+
sample1 = random.sample(_load("test"), 50)
|
| 149 |
+
random.seed(7)
|
| 150 |
+
sample2 = random.sample(_load("test"), 50)
|
| 151 |
+
assert [p["input_text"] for p in sample1] == [p["input_text"] for p in sample2]
|
tests/test_humanize.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Model inference tests. Require a trained checkpoint or the Hub model.
|
| 2 |
+
|
| 3 |
+
Set HUMANIZE_MODEL (default: checkpoints/humanize-text-model).
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
import pytest
|
| 11 |
+
|
| 12 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 13 |
+
|
| 14 |
+
from humanize import Humanizer, MAX_CHARS, protect, restore
|
| 15 |
+
|
| 16 |
+
MODEL = os.environ.get("HUMANIZE_MODEL", "checkpoints/humanize-text-model")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@pytest.fixture(scope="module")
|
| 20 |
+
def humanizer():
|
| 21 |
+
return Humanizer(model_id=MODEL)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def test_loads_and_returns_text(humanizer):
|
| 25 |
+
out = humanizer.humanize("It is important to note that this is a test.")
|
| 26 |
+
assert isinstance(out, str) and out
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_removes_ai_cliches_en(humanizer):
|
| 30 |
+
src = (
|
| 31 |
+
"In today's rapidly evolving world, it is important to note that this robust "
|
| 32 |
+
"solution serves as a testament to our commitment to innovation. Moreover, "
|
| 33 |
+
"we leverage cutting-edge technology to deliver a seamless experience."
|
| 34 |
+
)
|
| 35 |
+
out = humanizer.humanize(src)
|
| 36 |
+
lower = out.lower()
|
| 37 |
+
assert "it is important to note that" not in lower
|
| 38 |
+
assert "moreover" not in lower
|
| 39 |
+
assert "leverage" not in lower
|
| 40 |
+
assert "seamless" not in lower
|
| 41 |
+
assert "testament" not in lower
|
| 42 |
+
assert out != src
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_removes_ai_cliches_zh(humanizer):
|
| 46 |
+
src = "值得注意的是,我们通过赋能团队来助力企业实现降本增效,形成完整的业务闭环。"
|
| 47 |
+
out = humanizer.humanize(src)
|
| 48 |
+
assert "值得注意的是" not in out
|
| 49 |
+
assert "赋能" not in out
|
| 50 |
+
assert "降本增效" not in out
|
| 51 |
+
assert out != src
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def test_protects_urls_numbers_quotes(humanizer):
|
| 55 |
+
src = (
|
| 56 |
+
"It is important to note that the server at https://api.lynote.ai/v1/detect "
|
| 57 |
+
"handles 1,200 requests per second with a 99.5% uptime, as stated in "
|
| 58 |
+
'"Project Lynote". Moreover, run `pip install humanize-text==1.4.2`.'
|
| 59 |
+
)
|
| 60 |
+
out = humanizer.humanize(src)
|
| 61 |
+
for token in ("https://api.lynote.ai/v1/detect", "1,200", "99.5%", '"Project Lynote"', "humanize-text==1.4.2"):
|
| 62 |
+
assert token in out, f"protected token lost: {token}"
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def test_human_prose_stays_similar(humanizer):
|
| 66 |
+
src = "I walked the dog this morning and it started raining halfway through the park, so we ran home."
|
| 67 |
+
out = humanizer.humanize(src)
|
| 68 |
+
# Identity should be near-preserved: allow light edits but no additions.
|
| 69 |
+
assert abs(len(out.split()) - len(src.split())) <= 4
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def test_short_input_and_limits(humanizer):
|
| 73 |
+
with pytest.raises(ValueError):
|
| 74 |
+
humanizer.humanize("")
|
| 75 |
+
with pytest.raises(ValueError):
|
| 76 |
+
humanizer.humanize(" ")
|
| 77 |
+
with pytest.raises(ValueError):
|
| 78 |
+
humanizer.humanize("x" * (MAX_CHARS + 1))
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def test_batch_consistency(humanizer):
|
| 82 |
+
texts = [
|
| 83 |
+
"It is worth noting that this robust platform is a game-changer. Moreover, it is seamless.",
|
| 84 |
+
"值得注意的是,我们通过赋能团队来实现降本增效。",
|
| 85 |
+
"I walked the dog this morning and the sky was grey.",
|
| 86 |
+
]
|
| 87 |
+
batch = humanizer.humanize_batch(texts)
|
| 88 |
+
assert len(batch) == 3
|
| 89 |
+
for out, src in zip(batch, texts):
|
| 90 |
+
assert isinstance(out, str) and out
|
| 91 |
+
single = [humanizer.humanize(t) for t in texts]
|
| 92 |
+
# batch and single should agree exactly (same decode path)
|
| 93 |
+
assert batch == single
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def test_protect_restore_roundtrip():
|
| 97 |
+
text = "Run `pip install x==1.0` at https://example.com/a?b=1 — cost is $12.50 and \"Q3\" rose 4%."
|
| 98 |
+
masked, protected = protect(text)
|
| 99 |
+
assert "PROTECTED_0" in masked
|
| 100 |
+
assert restore(masked, protected) == text
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def test_is_chinese_routing():
|
| 104 |
+
from humanize import is_chinese
|
| 105 |
+
assert is_chinese("值得注意的是,这是中文测试。")
|
| 106 |
+
assert not is_chinese("It is important to note that this is English.")
|
| 107 |
+
assert not is_chinese("Mixed text with 中文 characters but mostly English words here.")
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def test_polish():
|
| 111 |
+
from humanize import Humanizer as H
|
| 112 |
+
assert H._polish("hello , world") == "hello, world"
|
| 113 |
+
assert H._polish("end., next") == "end. Next"
|
| 114 |
+
assert H._polish("a b") == "a b"
|
zh/config.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"T5ForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"classifier_dropout": 0.0,
|
| 6 |
+
"d_ff": 2048,
|
| 7 |
+
"d_kv": 64,
|
| 8 |
+
"d_model": 512,
|
| 9 |
+
"decoder_start_token_id": 101,
|
| 10 |
+
"dense_act_fn": "relu",
|
| 11 |
+
"dropout_rate": 0.1,
|
| 12 |
+
"dtype": "float32",
|
| 13 |
+
"eos_token_id": 1,
|
| 14 |
+
"feed_forward_proj": "relu",
|
| 15 |
+
"initializer_factor": 1.0,
|
| 16 |
+
"is_encoder_decoder": true,
|
| 17 |
+
"is_gated_act": false,
|
| 18 |
+
"layer_norm_epsilon": 1e-06,
|
| 19 |
+
"model_type": "t5",
|
| 20 |
+
"n_positions": 512,
|
| 21 |
+
"num_decoder_layers": 6,
|
| 22 |
+
"num_heads": 8,
|
| 23 |
+
"num_layers": 6,
|
| 24 |
+
"pad_token_id": 0,
|
| 25 |
+
"relative_attention_max_distance": 128,
|
| 26 |
+
"relative_attention_num_buckets": 32,
|
| 27 |
+
"tokenizer_class": "BertTokenizer",
|
| 28 |
+
"transformers_version": "4.57.6",
|
| 29 |
+
"use_cache": true,
|
| 30 |
+
"vocab_size": 21228
|
| 31 |
+
}
|
zh/generation_config.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"decoder_start_token_id": 101,
|
| 4 |
+
"eos_token_id": 1,
|
| 5 |
+
"pad_token_id": 0,
|
| 6 |
+
"transformers_version": "4.57.6"
|
| 7 |
+
}
|
zh/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:19c458057db296fd6abe06303a5e7bc92a9806d2d663bf43f1238abd0d021c17
|
| 3 |
+
size 219718696
|
zh/special_tokens_map.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cls_token": "[CLS]",
|
| 3 |
+
"mask_token": "[MASK]",
|
| 4 |
+
"pad_token": "[PAD]",
|
| 5 |
+
"sep_token": "[SEP]",
|
| 6 |
+
"unk_token": "[UNK]"
|
| 7 |
+
}
|
zh/tokenizer_config.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"0": {
|
| 4 |
+
"content": "[PAD]",
|
| 5 |
+
"lstrip": false,
|
| 6 |
+
"normalized": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"single_word": false,
|
| 9 |
+
"special": true
|
| 10 |
+
},
|
| 11 |
+
"100": {
|
| 12 |
+
"content": "[UNK]",
|
| 13 |
+
"lstrip": false,
|
| 14 |
+
"normalized": false,
|
| 15 |
+
"rstrip": false,
|
| 16 |
+
"single_word": false,
|
| 17 |
+
"special": true
|
| 18 |
+
},
|
| 19 |
+
"101": {
|
| 20 |
+
"content": "[CLS]",
|
| 21 |
+
"lstrip": false,
|
| 22 |
+
"normalized": false,
|
| 23 |
+
"rstrip": false,
|
| 24 |
+
"single_word": false,
|
| 25 |
+
"special": true
|
| 26 |
+
},
|
| 27 |
+
"102": {
|
| 28 |
+
"content": "[SEP]",
|
| 29 |
+
"lstrip": false,
|
| 30 |
+
"normalized": false,
|
| 31 |
+
"rstrip": false,
|
| 32 |
+
"single_word": false,
|
| 33 |
+
"special": true
|
| 34 |
+
},
|
| 35 |
+
"103": {
|
| 36 |
+
"content": "[MASK]",
|
| 37 |
+
"lstrip": false,
|
| 38 |
+
"normalized": false,
|
| 39 |
+
"rstrip": false,
|
| 40 |
+
"single_word": false,
|
| 41 |
+
"special": true
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"clean_up_tokenization_spaces": true,
|
| 45 |
+
"cls_token": "[CLS]",
|
| 46 |
+
"do_basic_tokenize": true,
|
| 47 |
+
"do_lower_case": false,
|
| 48 |
+
"extra_special_tokens": {},
|
| 49 |
+
"mask_token": "[MASK]",
|
| 50 |
+
"model_max_length": 1000000000000000019884624838656,
|
| 51 |
+
"never_split": null,
|
| 52 |
+
"pad_token": "[PAD]",
|
| 53 |
+
"sep_token": "[SEP]",
|
| 54 |
+
"strip_accents": null,
|
| 55 |
+
"tokenize_chinese_chars": true,
|
| 56 |
+
"tokenizer_class": "BertTokenizer",
|
| 57 |
+
"unk_token": "[UNK]"
|
| 58 |
+
}
|
zh/vocab.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|