PowerMachine commited on
Commit
e75027a
·
verified ·
1 Parent(s): 2c27897

Delete cnn_bigru/tokenizer/bbpe_tokenizer.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. cnn_bigru/tokenizer/bbpe_tokenizer.py +0 -173
cnn_bigru/tokenizer/bbpe_tokenizer.py DELETED
@@ -1,173 +0,0 @@
1
- """bbpe_tokenizer.py — BBPE (Byte-Level BPE) Tokenizer simplificado.
2
-
3
- Inspirado em PowerMachine/BiGRU_T_version/src/bigru_t/tokenizer/bbpe_tokenizer.py,
4
- mas simplificado para o projeto CNN-BiGRU. Usa a biblioteca `tokenizers` da
5
- HuggingFace com modelo BPE byte-level.
6
-
7
- Cobertura universal UTF-8 (qualquer string é tokenizável sem <unk>).
8
- """
9
- from __future__ import annotations
10
-
11
- import json
12
- import logging
13
- import os
14
- from pathlib import Path
15
- from typing import List, Optional, Sequence
16
-
17
- from tokenizers import Tokenizer
18
- from tokenizers.models import BPE
19
- from tokenizers.pre_tokenizers import ByteLevel
20
- from tokenizers.processors import ByteLevel as ByteLevelProcessor
21
- from tokenizers.decoders import ByteLevel as ByteLevelDecoder
22
- from tokenizers.trainers import BpeTrainer
23
-
24
- logger = logging.getLogger(__name__)
25
-
26
- BOS_TOKEN = "<s>"
27
- PAD_TOKEN = "<pad>"
28
- EOS_TOKEN = "</s>"
29
- UNK_TOKEN = "<unk>"
30
-
31
- SPECIAL_TOKENS = [BOS_TOKEN, PAD_TOKEN, EOS_TOKEN, UNK_TOKEN]
32
- BOS_ID = 0
33
- PAD_ID = 1
34
- EOS_ID = 2
35
- UNK_ID = 3
36
-
37
-
38
- class BBPETokenizer:
39
- """Wrapper sobre Tokenizer (HuggingFace) com API conveniente.
40
-
41
- Garante: dec(enc(s)) = s para todo s UTF-8.
42
- """
43
-
44
- def __init__(self, tokenizer: Optional[Tokenizer] = None):
45
- self._tok = tokenizer
46
-
47
- @property
48
- def tokenizer(self) -> Tokenizer:
49
- if self._tok is None:
50
- raise RuntimeError("Tokenizer não inicializado. Use train_from_* ou load.")
51
- return self._tok
52
-
53
- @property
54
- def vocab_size(self) -> int:
55
- return self.tokenizer.get_vocab_size()
56
-
57
- @property
58
- def bos_id(self) -> int:
59
- return self.tokenizer.token_to_id(BOS_TOKEN) or BOS_ID
60
-
61
- @property
62
- def pad_id(self) -> int:
63
- return self.tokenizer.token_to_id(PAD_TOKEN) or PAD_ID
64
-
65
- @property
66
- def eos_id(self) -> int:
67
- return self.tokenizer.token_to_id(EOS_TOKEN) or EOS_ID
68
-
69
- @property
70
- def unk_id(self) -> int:
71
- return self.tokenizer.token_to_id(UNK_TOKEN) or UNK_ID
72
-
73
- def encode(self, text: str, add_special: bool = True) -> List[int]:
74
- if add_special:
75
- text = f"{BOS_TOKEN}{text}{EOS_TOKEN}"
76
- return self.tokenizer.encode(text).ids
77
-
78
- def encode_batch(self, texts: Sequence[str], add_special: bool = True) -> List[List[int]]:
79
- if add_special:
80
- texts = [f"{BOS_TOKEN}{t}{EOS_TOKEN}" for t in texts]
81
- return [e.ids for e in self.tokenizer.encode_batch(list(texts))]
82
-
83
- def decode(self, ids: List[int], skip_special: bool = True) -> str:
84
- if skip_special:
85
- ids = [i for i in ids if i not in (self.bos_id, self.pad_id, self.eos_id, self.unk_id)]
86
- return self.tokenizer.decode(ids)
87
-
88
- def save(self, path: str) -> None:
89
- Path(path).parent.mkdir(parents=True, exist_ok=True)
90
- self.tokenizer.save(path)
91
- logger.info("Tokenizer salvo em %s", path)
92
-
93
- @classmethod
94
- def load(cls, path: str) -> "BBPETokenizer":
95
- tok = Tokenizer.from_file(path)
96
- return cls(tok)
97
-
98
- @classmethod
99
- def train_from_files(
100
- cls,
101
- files: Sequence[str],
102
- vocab_size: int = 8000,
103
- min_frequency: int = 2,
104
- ) -> "BBPETokenizer":
105
- # Desativa paralelismo de tokenizers para evitar problemas de GIL no shutdown
106
- os.environ["TOKENIZERS_PARALLELISM"] = "false"
107
- tokenizer = Tokenizer(BPE(unk_token=UNK_TOKEN))
108
- tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=False)
109
- trainer = BpeTrainer(
110
- vocab_size=vocab_size,
111
- min_frequency=min_frequency,
112
- special_tokens=SPECIAL_TOKENS,
113
- show_progress=False,
114
- )
115
- valid_files = [str(Path(p).resolve()) for p in files if Path(p).exists()]
116
- if not valid_files:
117
- raise FileNotFoundError(f"Nenhum arquivo válido em: {files}")
118
- tokenizer.train(valid_files, trainer)
119
- tokenizer.post_processor = ByteLevelProcessor(trim_offsets=False)
120
- # Decoder byte-level: reverte a codificação byte-level para UTF-8
121
- tokenizer.decoder = ByteLevelDecoder()
122
- logger.info("BBPE treinado com vocab_size=%d", tokenizer.get_vocab_size())
123
- return cls(tokenizer)
124
-
125
- @classmethod
126
- def train_from_texts(
127
- cls,
128
- texts: Sequence[str],
129
- vocab_size: int = 8000,
130
- min_frequency: int = 1,
131
- ) -> "BBPETokenizer":
132
- """Treina a partir de uma lista de textos em memória."""
133
- # Salva temporariamente
134
- import tempfile
135
- with tempfile.NamedTemporaryFile(
136
- mode="w", suffix=".txt", delete=False, encoding="utf-8"
137
- ) as f:
138
- for line in texts:
139
- line = line.strip()
140
- if line:
141
- f.write(line + "\n")
142
- tmp_path = f.name
143
- try:
144
- return cls.train_from_files([tmp_path], vocab_size=vocab_size,
145
- min_frequency=min_frequency)
146
- finally:
147
- try:
148
- os.unlink(tmp_path)
149
- except OSError:
150
- pass
151
-
152
- def validate_roundtrip(self, test_texts: Sequence[str]) -> float:
153
- """Valida que dec(enc(s)) == s. Retorna fração de sucessos."""
154
- if not test_texts:
155
- return 1.0
156
- ok = 0
157
- for s in test_texts:
158
- try:
159
- ids = self.encode(s, add_special=False)
160
- decoded = self.decode(ids, skip_special=False)
161
- if decoded == s:
162
- ok += 1
163
- except Exception:
164
- pass
165
- return ok / len(test_texts)
166
-
167
-
168
- __all__ = [
169
- "BBPETokenizer",
170
- "BOS_TOKEN", "PAD_TOKEN", "EOS_TOKEN", "UNK_TOKEN",
171
- "BOS_ID", "PAD_ID", "EOS_ID", "UNK_ID",
172
- "SPECIAL_TOKENS",
173
- ]