Adapt tokenization_interns1.py to transformers>=5.0.0

#7
by lvhan - opened
Files changed (1) hide show
  1. tokenization_interns1.py +146 -29
tokenization_interns1.py CHANGED
@@ -25,24 +25,27 @@ import regex as re
25
  import sentencepiece as spm
26
  from collections import OrderedDict
27
 
28
- from transformers.tokenization_utils import PreTrainedTokenizer
29
  from transformers.tokenization_utils_base import AddedToken, TextInput
30
- from transformers.models.qwen2.tokenization_qwen2 import Qwen2Tokenizer
31
  from transformers.utils import logging
 
 
 
 
 
 
32
 
33
 
34
  logger = logging.get_logger(__name__)
35
 
36
  try:
37
- from rdkit import Chem
38
- from rdkit import RDLogger
39
 
40
  RDLogger.DisableLog("rdApp.error")
41
  RDLogger.DisableLog("rdApp.*")
42
  RDKIT_AVAILABLE = True
43
  except ImportError:
44
  logger.warning_once(
45
- f"If tokenization with SMILES formula is of necessity, please 'pip install RDKit' for better tokenization quality."
46
  )
47
  RDKIT_AVAILABLE = False
48
 
@@ -60,7 +63,6 @@ PRETOKENIZE_REGEX = r"""(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p
60
  class InternS1CheckModuleMixin(ABC):
61
  """
62
  Basic auto-detection module.
63
-
64
  Note that short strings are ignored by this module.
65
  """
66
  def __init__(self, *, min_length: int):
@@ -122,7 +124,6 @@ class InternS1CheckModuleMixin(ABC):
122
  class FastaCheckModule(InternS1CheckModuleMixin):
123
  """
124
  Protein sequence auto-detection module.
125
-
126
  Automatically detects protein sequence using regex patterns.
127
  """
128
  def __init__(self, *, min_length: int = 27):
@@ -160,7 +161,6 @@ elements = [
160
  class SmilesCheckModule(InternS1CheckModuleMixin):
161
  """
162
  SMILES molecular sequence auto-detection module.
163
-
164
  Automatically detects and validates SMILES strings in text using regex patterns
165
  or chemical syntax rules. Uses RDKit for precise validation when available,
166
  otherwise falls back to rule-based validation.
@@ -343,28 +343,61 @@ class SmilesCheckModule(InternS1CheckModuleMixin):
343
  return self.check_brackets(text)
344
 
345
 
346
- class InternS1Tokenizer(Qwen2Tokenizer):
 
 
347
  """
348
- Construct an InternS1 tokenizer. Based on byte-level Byte-Pair-Encoding.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
 
 
 
350
  Same with GPT2Tokenizer, this tokenizer has been trained to treat spaces like parts of the tokens so a word will
351
  be encoded differently whether it is at the beginning of the sentence (without space) or not:
352
-
353
  ```python
354
  >>> from transformers import AutoTokenizer
355
-
356
  >>> tokenizer = AutoTokenizer.from_pretrained("InternS1Tokenizer", trust_remote_code=True)
357
  >>> tokenizer("Hello world")["input_ids"]
358
  [9707, 1879]
359
-
360
  >>> tokenizer(" Hello world")["input_ids"]
361
  [21927, 1879]
362
  ```
363
  This is expected.
364
-
365
  Include custom extension to support better domain-specific text tokenization, leveraging a separately trained tokenizer model.
366
  Users should refer to this superclass [`PreTrainedTokenizer`] for more information regarding those overloaded methods
367
-
368
  Args:
369
  vocab_file (`str`):
370
  Path to the vocabulary file.
@@ -408,6 +441,54 @@ class InternS1Tokenizer(Qwen2Tokenizer):
408
  split_special_tokens=False,
409
  **kwargs,
410
  ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  self.extra_tokenizer_start_mapping = {}
412
  self.extra_tokenizer_end_mapping = {}
413
  self._extra_special_tokens = []
@@ -460,6 +541,7 @@ class InternS1Tokenizer(Qwen2Tokenizer):
460
  pad_token=pad_token,
461
  clean_up_tokenization_spaces=clean_up_tokenization_spaces,
462
  split_special_tokens=split_special_tokens,
 
463
  **kwargs,
464
  )
465
 
@@ -497,6 +579,10 @@ class InternS1Tokenizer(Qwen2Tokenizer):
497
  """Overload method"""
498
  return self.vocab_size
499
 
 
 
 
 
500
  @property
501
  def logical_auto_tokens(self):
502
  """Tokens that won't be decoded and only for switching tokenizer"""
@@ -623,9 +709,7 @@ class InternS1Tokenizer(Qwen2Tokenizer):
623
  def tokenize(self, text: TextInput, **kwargs) -> List[str]:
624
  """
625
  Converts a string into a sequence of tokens, using the tokenizer.
626
-
627
  It will switch to domain-specific tokenizer once encountering extra/logical sp tokens.
628
-
629
  Args:
630
  text: TextInput
631
  """
@@ -633,9 +717,6 @@ class InternS1Tokenizer(Qwen2Tokenizer):
633
 
634
  text, kwargs = self.prepare_for_tokenization(text, **kwargs)
635
 
636
- if kwargs:
637
- logger.warning(f"Keyword arguments {kwargs} not recognized.")
638
-
639
  if hasattr(self, "do_lower_case") and self.do_lower_case:
640
  # convert non-special tokens to lowercase. Might be super slow as well?
641
  escaped_special_toks = [re.escape(s_tok) for s_tok in (self.all_special_tokens)]
@@ -738,7 +819,6 @@ class InternS1Tokenizer(Qwen2Tokenizer):
738
  def _add_tokens(self, new_tokens: Union[List[str], List[AddedToken]], special_tokens: bool = False) -> int:
739
  """
740
  Modified from `transformers.tokenization_utils._add_tokens`.
741
-
742
  This adaptation supports dynamic tokenizer length due to supplementary tokenizers (e.g., domain-specific or scientific text tokenizers).
743
  """
744
  added_tokens = 0
@@ -785,6 +865,7 @@ class InternS1Tokenizer(Qwen2Tokenizer):
785
  self._added_tokens_encoder[token.content] = token_index
786
  if self.verbose:
787
  logger.info(f"Adding {token} to the vocabulary")
 
788
  self._update_trie()
789
  self._update_total_vocab_size()
790
 
@@ -797,7 +878,6 @@ class InternS1Tokenizer(Qwen2Tokenizer):
797
  def _tokenize(self, text, **kwargs):
798
  """
799
  Modified from `transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer._tokenize`.
800
-
801
  This adaptation supports domain-specific tokenizers.
802
  """
803
  extra_tokenizer_stack = kwargs.pop("extra_tokenizer_stack", False)
@@ -814,6 +894,49 @@ class InternS1Tokenizer(Qwen2Tokenizer):
814
  else:
815
  return self._bpe_tokenize(text)
816
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
  def _bpe_tokenize(self, text, **kwargs):
818
  text = text.replace(
819
  "▁", " "
@@ -829,15 +952,11 @@ class InternS1Tokenizer(Qwen2Tokenizer):
829
  def convert_tokens_to_ids(self, tokens: Union[str, List[str]]) -> Union[int, List[int]]:
830
  """
831
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier.convert_tokens_to_ids`.
832
-
833
  Converts a token string (or a sequence of tokens) in a single integer id (or a sequence of ids), using the
834
  vocabulary.
835
-
836
  This adaptation supports domain-specific tokenizers.
837
-
838
  Args:
839
  tokens (`str` or `List[str]`): One or several token(s) to convert to token id(s).
840
-
841
  Returns:
842
  `int` or `List[int]`: The token id or list of token ids.
843
  """
@@ -865,7 +984,6 @@ class InternS1Tokenizer(Qwen2Tokenizer):
865
  def _convert_token_to_id_with_added_voc(self, token, **kwargs):
866
  """
867
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier._convert_token_to_id_with_added_voc`.
868
-
869
  This adaptation supports domain-specific tokenizers.
870
  """
871
  if token is None:
@@ -878,9 +996,7 @@ class InternS1Tokenizer(Qwen2Tokenizer):
878
  def _convert_token_to_id(self, token, **kwargs):
879
  """
880
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier._convert_token_to_id`.
881
-
882
  Converts a token (str) in an id using the vocab.
883
-
884
  Fall back to original tokenizer once OOV.
885
  """
886
  extra_tokenizer_stack = kwargs.pop("extra_tokenizer_stack", False)
@@ -978,3 +1094,4 @@ class InternS1Tokenizer(Qwen2Tokenizer):
978
 
979
 
980
  __all__ = ["InternS1Tokenizer"]
 
 
25
  import sentencepiece as spm
26
  from collections import OrderedDict
27
 
 
28
  from transformers.tokenization_utils_base import AddedToken, TextInput
 
29
  from transformers.utils import logging
30
+ import transformers
31
+ from packaging import version
32
+ if version.parse(transformers.__version__) >= version.parse("5.0.0"):
33
+ from transformers.tokenization_python import PreTrainedTokenizer
34
+ else:
35
+ from transformers.tokenization_utils import PreTrainedTokenizer
36
 
37
 
38
  logger = logging.get_logger(__name__)
39
 
40
  try:
41
+ from rdkit import Chem, RDLogger
 
42
 
43
  RDLogger.DisableLog("rdApp.error")
44
  RDLogger.DisableLog("rdApp.*")
45
  RDKIT_AVAILABLE = True
46
  except ImportError:
47
  logger.warning_once(
48
+ "If tokenization with SMILES formula is of necessity, please 'pip install RDKit' for better tokenization quality."
49
  )
50
  RDKIT_AVAILABLE = False
51
 
 
63
  class InternS1CheckModuleMixin(ABC):
64
  """
65
  Basic auto-detection module.
 
66
  Note that short strings are ignored by this module.
67
  """
68
  def __init__(self, *, min_length: int):
 
124
  class FastaCheckModule(InternS1CheckModuleMixin):
125
  """
126
  Protein sequence auto-detection module.
 
127
  Automatically detects protein sequence using regex patterns.
128
  """
129
  def __init__(self, *, min_length: int = 27):
 
161
  class SmilesCheckModule(InternS1CheckModuleMixin):
162
  """
163
  SMILES molecular sequence auto-detection module.
 
164
  Automatically detects and validates SMILES strings in text using regex patterns
165
  or chemical syntax rules. Uses RDKit for precise validation when available,
166
  otherwise falls back to rule-based validation.
 
343
  return self.check_brackets(text)
344
 
345
 
346
+ @lru_cache
347
+ # Copied from transformers.models.gpt2.tokenization_gpt2.bytes_to_unicode
348
+ def bytes_to_unicode():
349
  """
350
+ Returns list of utf-8 byte and a mapping to unicode strings. We specifically avoids mapping to whitespace/control
351
+ characters the bpe code barfs on.
352
+ The reversible bpe codes work on unicode strings. This means you need a large # of unicode characters in your vocab
353
+ if you want to avoid UNKs. When you're at something like a 10B token dataset you end up needing around 5K for
354
+ decent coverage. This is a significant percentage of your normal, say, 32K bpe vocab. To avoid that, we want lookup
355
+ tables between utf-8 bytes and unicode strings.
356
+ """
357
+ bs = (
358
+ list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1))
359
+ )
360
+ cs = bs[:]
361
+ n = 0
362
+ for b in range(2**8):
363
+ if b not in bs:
364
+ bs.append(b)
365
+ cs.append(2**8 + n)
366
+ n += 1
367
+ cs = [chr(n) for n in cs]
368
+ return dict(zip(bs, cs))
369
+
370
+
371
+ # Copied from transformers.models.gpt2.tokenization_gpt2.get_pairs
372
+ def get_pairs(word):
373
+ """
374
+ Return set of symbol pairs in a word.
375
+ Word is represented as tuple of symbols (symbols being variable-length strings).
376
+ """
377
+ pairs = set()
378
+ prev_char = word[0]
379
+ for char in word[1:]:
380
+ pairs.add((prev_char, char))
381
+ prev_char = char
382
+ return pairs
383
+
384
 
385
+ class InternS1Tokenizer(PreTrainedTokenizer):
386
+ """
387
+ Construct an InternS1 tokenizer. Based on byte-level Byte-Pair-Encoding.
388
  Same with GPT2Tokenizer, this tokenizer has been trained to treat spaces like parts of the tokens so a word will
389
  be encoded differently whether it is at the beginning of the sentence (without space) or not:
 
390
  ```python
391
  >>> from transformers import AutoTokenizer
 
392
  >>> tokenizer = AutoTokenizer.from_pretrained("InternS1Tokenizer", trust_remote_code=True)
393
  >>> tokenizer("Hello world")["input_ids"]
394
  [9707, 1879]
 
395
  >>> tokenizer(" Hello world")["input_ids"]
396
  [21927, 1879]
397
  ```
398
  This is expected.
 
399
  Include custom extension to support better domain-specific text tokenization, leveraging a separately trained tokenizer model.
400
  Users should refer to this superclass [`PreTrainedTokenizer`] for more information regarding those overloaded methods
 
401
  Args:
402
  vocab_file (`str`):
403
  Path to the vocabulary file.
 
441
  split_special_tokens=False,
442
  **kwargs,
443
  ):
444
+ bos_token = (
445
+ AddedToken(bos_token, lstrip=False, rstrip=False, special=True, normalized=False)
446
+ if isinstance(bos_token, str)
447
+ else bos_token
448
+ )
449
+ eos_token = (
450
+ AddedToken(eos_token, lstrip=False, rstrip=False, special=True, normalized=False)
451
+ if isinstance(eos_token, str)
452
+ else eos_token
453
+ )
454
+ unk_token = (
455
+ AddedToken(unk_token, lstrip=False, rstrip=False, special=True, normalized=False)
456
+ if isinstance(unk_token, str)
457
+ else unk_token
458
+ )
459
+ pad_token = (
460
+ AddedToken(pad_token, lstrip=False, rstrip=False, special=True, normalized=False)
461
+ if isinstance(pad_token, str)
462
+ else pad_token
463
+ )
464
+
465
+ with open(vocab_file, encoding="utf-8") as vocab_handle:
466
+ self.encoder = json.load(vocab_handle)
467
+ self.decoder = {v: k for k, v in self.encoder.items()}
468
+ self.errors = errors # how to handle errors in decoding
469
+ self.byte_encoder = bytes_to_unicode()
470
+ self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
471
+ bpe_merges = []
472
+ with open(merges_file, encoding="utf-8") as merges_handle:
473
+ for i, line in enumerate(merges_handle):
474
+ line = line.strip()
475
+ if (i == 0 and line.startswith("#version:")) or not line:
476
+ continue
477
+ bpe_merges.append(tuple(line.split()))
478
+ self.bpe_ranks = dict(zip(bpe_merges, range(len(bpe_merges))))
479
+ # NOTE: the cache can grow without bound and will get really large for long running processes
480
+ # (esp. for texts of language that do not use space between word, e.g. Chinese); technically
481
+ # not a memory leak but appears as one.
482
+ # GPT2Tokenizer has the same problem, so let's be consistent.
483
+ self.cache = {}
484
+
485
+ self.pat = re.compile(PRETOKENIZE_REGEX)
486
+
487
+ if kwargs.get("add_prefix_space", False):
488
+ logger.warning_once(
489
+ f"{self.__class__.__name} does not support `add_prefix_space`, setting it to True has no effect."
490
+ )
491
+
492
  self.extra_tokenizer_start_mapping = {}
493
  self.extra_tokenizer_end_mapping = {}
494
  self._extra_special_tokens = []
 
541
  pad_token=pad_token,
542
  clean_up_tokenization_spaces=clean_up_tokenization_spaces,
543
  split_special_tokens=split_special_tokens,
544
+ special_tokens_pattern="none",
545
  **kwargs,
546
  )
547
 
 
579
  """Overload method"""
580
  return self.vocab_size
581
 
582
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.get_vocab
583
+ def get_vocab(self):
584
+ return dict(self.encoder, **self.added_tokens_encoder)
585
+
586
  @property
587
  def logical_auto_tokens(self):
588
  """Tokens that won't be decoded and only for switching tokenizer"""
 
709
  def tokenize(self, text: TextInput, **kwargs) -> List[str]:
710
  """
711
  Converts a string into a sequence of tokens, using the tokenizer.
 
712
  It will switch to domain-specific tokenizer once encountering extra/logical sp tokens.
 
713
  Args:
714
  text: TextInput
715
  """
 
717
 
718
  text, kwargs = self.prepare_for_tokenization(text, **kwargs)
719
 
 
 
 
720
  if hasattr(self, "do_lower_case") and self.do_lower_case:
721
  # convert non-special tokens to lowercase. Might be super slow as well?
722
  escaped_special_toks = [re.escape(s_tok) for s_tok in (self.all_special_tokens)]
 
819
  def _add_tokens(self, new_tokens: Union[List[str], List[AddedToken]], special_tokens: bool = False) -> int:
820
  """
821
  Modified from `transformers.tokenization_utils._add_tokens`.
 
822
  This adaptation supports dynamic tokenizer length due to supplementary tokenizers (e.g., domain-specific or scientific text tokenizers).
823
  """
824
  added_tokens = 0
 
865
  self._added_tokens_encoder[token.content] = token_index
866
  if self.verbose:
867
  logger.info(f"Adding {token} to the vocabulary")
868
+
869
  self._update_trie()
870
  self._update_total_vocab_size()
871
 
 
878
  def _tokenize(self, text, **kwargs):
879
  """
880
  Modified from `transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer._tokenize`.
 
881
  This adaptation supports domain-specific tokenizers.
882
  """
883
  extra_tokenizer_stack = kwargs.pop("extra_tokenizer_stack", False)
 
894
  else:
895
  return self._bpe_tokenize(text)
896
 
897
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.bpe
898
+ def bpe(self, token):
899
+ if token in self.cache:
900
+ return self.cache[token]
901
+ word = tuple(token)
902
+ pairs = get_pairs(word)
903
+
904
+ if not pairs:
905
+ return token
906
+
907
+ while True:
908
+ bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf")))
909
+ if bigram not in self.bpe_ranks:
910
+ break
911
+ first, second = bigram
912
+ new_word = []
913
+ i = 0
914
+ while i < len(word):
915
+ try:
916
+ j = word.index(first, i)
917
+ except ValueError:
918
+ new_word.extend(word[i:])
919
+ break
920
+ else:
921
+ new_word.extend(word[i:j])
922
+ i = j
923
+
924
+ if word[i] == first and i < len(word) - 1 and word[i + 1] == second:
925
+ new_word.append(first + second)
926
+ i += 2
927
+ else:
928
+ new_word.append(word[i])
929
+ i += 1
930
+ new_word = tuple(new_word)
931
+ word = new_word
932
+ if len(word) == 1:
933
+ break
934
+ else:
935
+ pairs = get_pairs(word)
936
+ word = " ".join(word)
937
+ self.cache[token] = word
938
+ return word
939
+
940
  def _bpe_tokenize(self, text, **kwargs):
941
  text = text.replace(
942
  "▁", " "
 
952
  def convert_tokens_to_ids(self, tokens: Union[str, List[str]]) -> Union[int, List[int]]:
953
  """
954
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier.convert_tokens_to_ids`.
 
955
  Converts a token string (or a sequence of tokens) in a single integer id (or a sequence of ids), using the
956
  vocabulary.
 
957
  This adaptation supports domain-specific tokenizers.
 
958
  Args:
959
  tokens (`str` or `List[str]`): One or several token(s) to convert to token id(s).
 
960
  Returns:
961
  `int` or `List[int]`: The token id or list of token ids.
962
  """
 
984
  def _convert_token_to_id_with_added_voc(self, token, **kwargs):
985
  """
986
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier._convert_token_to_id_with_added_voc`.
 
987
  This adaptation supports domain-specific tokenizers.
988
  """
989
  if token is None:
 
996
  def _convert_token_to_id(self, token, **kwargs):
997
  """
998
  Modified from `transformers.tokenization_utils.PreTrainedTokenzier._convert_token_to_id`.
 
999
  Converts a token (str) in an id using the vocab.
 
1000
  Fall back to original tokenizer once OOV.
1001
  """
1002
  extra_tokenizer_stack = kwargs.pop("extra_tokenizer_stack", False)
 
1094
 
1095
 
1096
  __all__ = ["InternS1Tokenizer"]
1097
+