PiaoYang commited on
Commit
e99d347
1 Parent(s): c25a8e0

Upload tokenizer

Browse files
ice_text.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5e974d9a69c242ce014c88c2b26089270f6198f3c0b700a887666cd3e816f17e
3
+ size 2706249
special_tokens_map.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<sop>",
3
+ "eos_token": "<eop>",
4
+ "mask_token": "[MASK]",
5
+ "pad_token": "<pad>",
6
+ "unk_token": "<unk>"
7
+ }
tokenization_chatglm.py ADDED
@@ -0,0 +1,444 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tokenization classes for ChatGLM."""
2
+ from typing import List, Optional, Union
3
+ import os
4
+
5
+ from transformers.tokenization_utils import PreTrainedTokenizer
6
+ from transformers.utils import logging, PaddingStrategy
7
+ from transformers.tokenization_utils_base import EncodedInput, BatchEncoding
8
+ from typing import Dict
9
+ import sentencepiece as spm
10
+ import numpy as np
11
+
12
+ logger = logging.get_logger(__name__)
13
+
14
+ PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = {
15
+ "THUDM/chatglm-6b": 2048,
16
+ }
17
+
18
+
19
+ class TextTokenizer:
20
+ def __init__(self, model_path):
21
+ self.sp = spm.SentencePieceProcessor()
22
+ self.sp.Load(model_path)
23
+ self.num_tokens = self.sp.vocab_size()
24
+
25
+ def encode(self, text):
26
+ return self.sp.EncodeAsIds(text)
27
+
28
+ def decode(self, ids: List[int]):
29
+ return self.sp.DecodeIds(ids)
30
+
31
+ def tokenize(self, text):
32
+ return self.sp.EncodeAsPieces(text)
33
+
34
+ def convert_tokens_to_string(self, tokens):
35
+ return self.sp.DecodePieces(tokens)
36
+
37
+ def convert_tokens_to_ids(self, tokens):
38
+ return [self.sp.PieceToId(token) for token in tokens]
39
+
40
+ def convert_token_to_id(self, token):
41
+ return self.sp.PieceToId(token)
42
+
43
+ def convert_id_to_token(self, idx):
44
+ return self.sp.IdToPiece(idx)
45
+
46
+ def __len__(self):
47
+ return self.num_tokens
48
+
49
+
50
+ class SPTokenizer:
51
+ def __init__(
52
+ self,
53
+ vocab_file,
54
+ num_image_tokens=20000,
55
+ max_blank_length=80,
56
+ byte_fallback=True,
57
+ ):
58
+ assert vocab_file is not None
59
+ self.vocab_file = vocab_file
60
+ self.num_image_tokens = num_image_tokens
61
+ self.special_tokens = ["[MASK]", "[gMASK]", "[sMASK]", "<unused_0>", "<sop>", "<eop>", "<ENC>", "<dBLOCK>"]
62
+ self.max_blank_length = max_blank_length
63
+ self.byte_fallback = byte_fallback
64
+ self.text_tokenizer = TextTokenizer(vocab_file)
65
+
66
+ def _get_text_tokenizer(self):
67
+ return self.text_tokenizer
68
+
69
+ @staticmethod
70
+ def get_blank_token(length: int):
71
+ assert length >= 2
72
+ return f"<|blank_{length}|>"
73
+
74
+ @staticmethod
75
+ def get_tab_token():
76
+ return f"<|tab|>"
77
+
78
+ @property
79
+ def num_text_tokens(self):
80
+ return self.text_tokenizer.num_tokens
81
+
82
+ @property
83
+ def num_tokens(self):
84
+ return self.num_image_tokens + self.num_text_tokens
85
+
86
+ @staticmethod
87
+ def _encode_whitespaces(text: str, max_len: int = 80):
88
+ text = text.replace("\t", SPTokenizer.get_tab_token())
89
+ for i in range(max_len, 1, -1):
90
+ text = text.replace(" " * i, SPTokenizer.get_blank_token(i))
91
+ return text
92
+
93
+ def _preprocess(self, text: str, linebreak=True, whitespaces=True):
94
+ if linebreak:
95
+ text = text.replace("\n", "<n>")
96
+ if whitespaces:
97
+ text = self._encode_whitespaces(text, max_len=self.max_blank_length)
98
+ return text
99
+
100
+ def encode(
101
+ self, text: str, linebreak=True, whitespaces=True, add_dummy_prefix=True
102
+ ) -> List[int]:
103
+ """
104
+ @param text: Text to encode.
105
+ @param linebreak: Whether to encode newline (\n) in text.
106
+ @param whitespaces: Whether to encode multiple whitespaces or tab in text, useful for source code encoding.
107
+ @param special_tokens: Whether to encode special token ([MASK], [gMASK], etc.) in text.
108
+ @param add_dummy_prefix: Whether to add dummy blank space in the beginning.
109
+ """
110
+ text = self._preprocess(text, linebreak, whitespaces)
111
+ if not add_dummy_prefix:
112
+ text = "<n>" + text
113
+ tmp = self._get_text_tokenizer().encode(text)
114
+ tokens = [x + self.num_image_tokens for x in tmp]
115
+ return tokens if add_dummy_prefix else tokens[2:]
116
+
117
+ def postprocess(self, text):
118
+ text = text.replace("<n>", "\n")
119
+ text = text.replace(SPTokenizer.get_tab_token(), "\t")
120
+ for i in range(2, self.max_blank_length + 1):
121
+ text = text.replace(self.get_blank_token(i), " " * i)
122
+ return text
123
+
124
+ def decode(self, text_ids: List[int]) -> str:
125
+ ids = [int(_id) - self.num_image_tokens for _id in text_ids]
126
+ ids = [_id for _id in ids if _id >= 0]
127
+ text = self._get_text_tokenizer().decode(ids)
128
+ text = self.postprocess(text)
129
+ return text
130
+
131
+ def decode_tokens(self, tokens: List[str]) -> str:
132
+ text = self._get_text_tokenizer().convert_tokens_to_string(tokens)
133
+ text = self.postprocess(text)
134
+ return text
135
+
136
+ def tokenize(
137
+ self, text: str, linebreak=True, whitespaces=True, add_dummy_prefix=True
138
+ ) -> List[str]:
139
+ """
140
+ @param text: Text to encode.
141
+ @param linebreak: Whether to encode newline (\n) in text.
142
+ @param whitespaces: Whether to encode multiple whitespaces or tab in text, useful for source code encoding.
143
+ @param special_tokens: Whether to encode special token ([MASK], [gMASK], etc.) in text.
144
+ @param add_dummy_prefix: Whether to add dummy blank space in the beginning.
145
+ """
146
+ text = self._preprocess(text, linebreak, whitespaces)
147
+ if not add_dummy_prefix:
148
+ text = "<n>" + text
149
+ tokens = self._get_text_tokenizer().tokenize(text)
150
+ return tokens if add_dummy_prefix else tokens[2:]
151
+
152
+ def __getitem__(self, x: Union[int, str]):
153
+ if isinstance(x, int):
154
+ if x < self.num_image_tokens:
155
+ return "<image_{}>".format(x)
156
+ else:
157
+ return self.text_tokenizer.convert_id_to_token(x - self.num_image_tokens)
158
+ elif isinstance(x, str):
159
+ if x.startswith("<image_") and x.endswith(">") and x[7:-1].isdigit():
160
+ return int(x[7:-1])
161
+ else:
162
+ return self.text_tokenizer.convert_token_to_id(x) + self.num_image_tokens
163
+ else:
164
+ raise ValueError("The key should be str or int.")
165
+
166
+
167
+ class ChatGLMTokenizer(PreTrainedTokenizer):
168
+ """
169
+ Construct a ChatGLM tokenizer. Based on byte-level Byte-Pair-Encoding.
170
+
171
+ Args:
172
+ vocab_file (`str`):
173
+ Path to the vocabulary file.
174
+ """
175
+
176
+ vocab_files_names = {"vocab_file": "ice_text.model"}
177
+ max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES
178
+ model_input_names = ["input_ids", "attention_mask", "position_ids"]
179
+
180
+ def __init__(
181
+ self,
182
+ vocab_file,
183
+ do_lower_case=False,
184
+ remove_space=False,
185
+ bos_token='<sop>',
186
+ eos_token='<eop>',
187
+ end_token='</s>',
188
+ mask_token='[MASK]',
189
+ gmask_token='[gMASK]',
190
+ padding_side="left",
191
+ pad_token="<pad>",
192
+ unk_token="<unk>",
193
+ num_image_tokens=20000,
194
+ **kwargs
195
+ ) -> None:
196
+ # Move the initialization of special tokens before the parent class initialization
197
+ self.sp_tokenizer = SPTokenizer(vocab_file, num_image_tokens=num_image_tokens)
198
+
199
+ super().__init__(
200
+ do_lower_case=do_lower_case,
201
+ remove_space=remove_space,
202
+ padding_side=padding_side,
203
+ bos_token=bos_token,
204
+ eos_token=eos_token,
205
+ end_token=end_token,
206
+ mask_token=mask_token,
207
+ gmask_token=gmask_token,
208
+ pad_token=pad_token,
209
+ unk_token=unk_token,
210
+ num_image_tokens=num_image_tokens,
211
+ **kwargs
212
+ )
213
+
214
+ self.do_lower_case = do_lower_case
215
+ self.remove_space = remove_space
216
+ self.vocab_file = vocab_file
217
+
218
+ self.bos_token = bos_token
219
+ self.eos_token = eos_token
220
+ self.end_token = end_token
221
+ self.mask_token = mask_token
222
+ self.gmask_token = gmask_token
223
+
224
+ """ Initialisation """
225
+
226
+ @property
227
+ def gmask_token_id(self) -> Optional[int]:
228
+ if self.gmask_token is None:
229
+ return None
230
+ return self.convert_tokens_to_ids(self.gmask_token)
231
+
232
+ @property
233
+ def end_token_id(self) -> Optional[int]:
234
+ """
235
+ `Optional[int]`: Id of the end of context token in the vocabulary. Returns `None` if the token has not been
236
+ set.
237
+ """
238
+ if self.end_token is None:
239
+ return None
240
+ return self.convert_tokens_to_ids(self.end_token)
241
+
242
+ @property
243
+ def vocab_size(self):
244
+ """ Returns vocab size """
245
+ return self.sp_tokenizer.num_tokens
246
+
247
+ def get_vocab(self):
248
+ """ Returns vocab as a dict """
249
+ vocab = {self._convert_id_to_token(i): i for i in range(self.vocab_size)}
250
+ vocab.update(self.added_tokens_encoder)
251
+ return vocab
252
+
253
+ def preprocess_text(self, inputs):
254
+ if self.remove_space:
255
+ outputs = " ".join(inputs.strip().split())
256
+ else:
257
+ outputs = inputs
258
+
259
+ if self.do_lower_case:
260
+ outputs = outputs.lower()
261
+
262
+ return outputs
263
+
264
+ def _tokenize(self, text, **kwargs):
265
+ """ Returns a tokenized string. """
266
+ text = self.preprocess_text(text)
267
+
268
+ seq = self.sp_tokenizer.tokenize(text)
269
+
270
+ return seq
271
+
272
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
273
+ return self.sp_tokenizer.decode_tokens(tokens)
274
+
275
+ def _decode(
276
+ self,
277
+ token_ids: Union[int, List[int]],
278
+ **kwargs
279
+ ) -> str:
280
+ if isinstance(token_ids, int):
281
+ token_ids = [token_ids]
282
+ if len(token_ids) == 0:
283
+ return ""
284
+ if self.pad_token_id in token_ids: # remove pad
285
+ token_ids = list(filter((self.pad_token_id).__ne__, token_ids))
286
+ return super()._decode(token_ids, **kwargs)
287
+
288
+ def _convert_token_to_id(self, token):
289
+ """ Converts a token (str) in an id using the vocab. """
290
+ return self.sp_tokenizer[token]
291
+
292
+ def _convert_id_to_token(self, index):
293
+ """Converts an index (integer) in a token (str) using the vocab."""
294
+ return self.sp_tokenizer[index]
295
+
296
+ def save_vocabulary(self, save_directory, filename_prefix=None):
297
+ """
298
+ Save the vocabulary and special tokens file to a directory.
299
+
300
+ Args:
301
+ save_directory (`str`):
302
+ The directory in which to save the vocabulary.
303
+ filename_prefix (`str`, *optional*):
304
+ An optional prefix to add to the named of the saved files.
305
+
306
+ Returns:
307
+ `Tuple(str)`: Paths to the files saved.
308
+ """
309
+ if os.path.isdir(save_directory):
310
+ vocab_file = os.path.join(
311
+ save_directory, self.vocab_files_names["vocab_file"]
312
+ )
313
+ else:
314
+ vocab_file = save_directory
315
+
316
+ with open(self.vocab_file, 'rb') as fin:
317
+ proto_str = fin.read()
318
+
319
+ with open(vocab_file, "wb") as writer:
320
+ writer.write(proto_str)
321
+
322
+ return (vocab_file,)
323
+
324
+ def build_inputs_with_special_tokens(
325
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
326
+ ) -> List[int]:
327
+ """
328
+ Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and
329
+ adding special tokens. A BERT sequence has the following format:
330
+
331
+ - single sequence: `[CLS] X [SEP]`
332
+ - pair of sequences: `[CLS] A [SEP] B [SEP]`
333
+
334
+ Args:
335
+ token_ids_0 (`List[int]`):
336
+ List of IDs to which the special tokens will be added.
337
+ token_ids_1 (`List[int]`, *optional*):
338
+ Optional second list of IDs for sequence pairs.
339
+
340
+ Returns:
341
+ `List[int]`: List of [input IDs](../glossary#input-ids) with the appropriate special tokens.
342
+ """
343
+ gmask_id = self.sp_tokenizer[self.gmask_token]
344
+ eos_id = self.sp_tokenizer[self.eos_token]
345
+ token_ids_0 = token_ids_0 + [gmask_id, self.sp_tokenizer[self.bos_token]]
346
+ if token_ids_1 is not None:
347
+ token_ids_0 = token_ids_0 + token_ids_1 + [eos_id]
348
+ return token_ids_0
349
+
350
+ def _pad(
351
+ self,
352
+ encoded_inputs: Union[Dict[str, EncodedInput], BatchEncoding],
353
+ max_length: Optional[int] = None,
354
+ padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,
355
+ pad_to_multiple_of: Optional[int] = None,
356
+ return_attention_mask: Optional[bool] = None,
357
+ ) -> dict:
358
+ """
359
+ Pad encoded inputs (on left/right and up to predefined length or max length in the batch)
360
+
361
+ Args:
362
+ encoded_inputs:
363
+ Dictionary of tokenized inputs (`List[int]`) or batch of tokenized inputs (`List[List[int]]`).
364
+ max_length: maximum length of the returned list and optionally padding length (see below).
365
+ Will truncate by taking into account the special tokens.
366
+ padding_strategy: PaddingStrategy to use for padding.
367
+
368
+ - PaddingStrategy.LONGEST Pad to the longest sequence in the batch
369
+ - PaddingStrategy.MAX_LENGTH: Pad to the max length (default)
370
+ - PaddingStrategy.DO_NOT_PAD: Do not pad
371
+ The tokenizer padding sides are defined in self.padding_side:
372
+
373
+ - 'left': pads on the left of the sequences
374
+ - 'right': pads on the right of the sequences
375
+ pad_to_multiple_of: (optional) Integer if set will pad the sequence to a multiple of the provided value.
376
+ This is especially useful to enable the use of Tensor Core on NVIDIA hardware with compute capability
377
+ `>= 7.5` (Volta).
378
+ return_attention_mask:
379
+ (optional) Set to False to avoid returning attention mask (default: set to model specifics)
380
+ """
381
+ # Load from model defaults
382
+ bos_token_id = self.sp_tokenizer[self.bos_token]
383
+ mask_token_id = self.sp_tokenizer[self.mask_token]
384
+ gmask_token_id = self.sp_tokenizer[self.gmask_token]
385
+ assert self.padding_side == "left"
386
+
387
+ required_input = encoded_inputs[self.model_input_names[0]]
388
+ seq_length = len(required_input)
389
+
390
+ if padding_strategy == PaddingStrategy.LONGEST:
391
+ max_length = len(required_input)
392
+
393
+ if max_length is not None and pad_to_multiple_of is not None and (max_length % pad_to_multiple_of != 0):
394
+ max_length = ((max_length // pad_to_multiple_of) + 1) * pad_to_multiple_of
395
+
396
+ needs_to_be_padded = padding_strategy != PaddingStrategy.DO_NOT_PAD and len(required_input) != max_length
397
+
398
+ # Initialize attention mask if not present.
399
+ if max_length is not None:
400
+ if "attention_mask" not in encoded_inputs:
401
+ if bos_token_id in required_input:
402
+ context_length = required_input.index(bos_token_id)
403
+ else:
404
+ context_length = seq_length
405
+ attention_mask = np.ones((1, seq_length, seq_length))
406
+ attention_mask = np.tril(attention_mask)
407
+ attention_mask[:, :, :context_length] = 1
408
+ attention_mask = np.bool_(attention_mask < 0.5)
409
+ encoded_inputs["attention_mask"] = attention_mask
410
+
411
+ if "position_ids" not in encoded_inputs:
412
+ if bos_token_id in required_input:
413
+ context_length = required_input.index(bos_token_id)
414
+ else:
415
+ context_length = seq_length
416
+ position_ids = np.arange(seq_length, dtype=np.int64)
417
+ mask_token = mask_token_id if mask_token_id in required_input else gmask_token_id
418
+ if mask_token in required_input:
419
+ mask_position = required_input.index(mask_token)
420
+ position_ids[context_length:] = mask_position
421
+ block_position_ids = np.concatenate(
422
+ [np.zeros(context_length, dtype=np.int64),
423
+ np.arange(1, seq_length - context_length + 1, dtype=np.int64)])
424
+ encoded_inputs["position_ids"] = np.stack([position_ids, block_position_ids], axis=0)
425
+
426
+ if needs_to_be_padded:
427
+ difference = max_length - len(required_input)
428
+
429
+ if "attention_mask" in encoded_inputs:
430
+ encoded_inputs["attention_mask"] = np.pad(encoded_inputs["attention_mask"],
431
+ pad_width=[(0, 0), (difference, 0), (difference, 0)],
432
+ mode='constant', constant_values=True)
433
+ if "token_type_ids" in encoded_inputs:
434
+ encoded_inputs["token_type_ids"] = [self.pad_token_type_id] * difference + encoded_inputs[
435
+ "token_type_ids"
436
+ ]
437
+ if "special_tokens_mask" in encoded_inputs:
438
+ encoded_inputs["special_tokens_mask"] = [1] * difference + encoded_inputs["special_tokens_mask"]
439
+ if "position_ids" in encoded_inputs:
440
+ encoded_inputs["position_ids"] = np.pad(encoded_inputs["position_ids"],
441
+ pad_width=[(0, 0), (difference, 0)])
442
+ encoded_inputs[self.model_input_names[0]] = [self.pad_token_id] * difference + required_input
443
+
444
+ return encoded_inputs
tokenizer_config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoTokenizer": [
4
+ "tokenization_chatglm.ChatGLMTokenizer",
5
+ null
6
+ ]
7
+ },
8
+ "bos_token": "<sop>",
9
+ "clean_up_tokenization_spaces": true,
10
+ "do_lower_case": false,
11
+ "end_token": "</s>",
12
+ "eos_token": "<eop>",
13
+ "gmask_token": "[gMASK]",
14
+ "mask_token": "[MASK]",
15
+ "model_max_length": 2048,
16
+ "num_image_tokens": 0,
17
+ "pad_token": "<pad>",
18
+ "padding_side": "left",
19
+ "remove_space": false,
20
+ "tokenizer_class": "ChatGLMTokenizer",
21
+ "unk_token": "<unk>"
22
+ }