Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

597 lines
24 KiB
Python

# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
# Copyright 2020 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import shutil
import unittest
from typing import Any, Dict, List, Tuple
from paddlenlp.transformers.skep.tokenizer import (
BasicTokenizer,
BpeEncoder,
SkepTokenizer,
WordpieceTokenizer,
)
from ...testing_utils import get_tests_dir, slow
from ..test_tokenizer_common import TokenizerTesterMixin, filter_non_english
def _class_name_func(cls, num: int, params_dict: Dict[str, Any]):
suffix = "UseBPE" if params_dict["use_bpe_encoder"] else "NotUseBPE"
return f"{cls.__name__}{suffix}"
def _read_tokens_from_file(file: str) -> List[str]:
with open(file, "r", encoding="utf-8") as f:
tokens = [token.strip() for token in f.readlines()]
return tokens
class SkepBpeEncoderTest(unittest.TestCase):
def setUp(self):
self.vocab_file = get_tests_dir("fixtures/bpe.en/vocab.json")
self.merges_file = get_tests_dir("fixtures/bpe.en/merges.txt")
self.encoder = BpeEncoder(encoder_json_file=self.vocab_file, vocab_bpe_file=self.merges_file)
def test_tokenizer(self):
text = " lower newer"
bpe_tokens = ["\u0120low", "er", "\u0120", "n", "e", "w", "er"]
tokens = self.encoder._tokenize(text)
self.assertListEqual(tokens, bpe_tokens)
decoded_text = self.encoder.convert_tokens_to_string(tokens)
self.assertEqual(text, decoded_text)
def test_tokenizer_encode_decode(self):
text = " lower newer"
bpe_tokens = ["\u0120low", "er", "\u0120", "n", "e", "w", "er"]
token_ids = self.encoder.encode(text)
tokens = [self.encoder.decoder[token_id] for token_id in token_ids]
self.assertListEqual(tokens, bpe_tokens)
decoded_text = self.encoder.decode(token_ids)
self.assertEqual(text, decoded_text)
def test_unk_word(self):
text = " lower newer a"
with self.assertRaises(KeyError):
self.encoder.encode(text)
# can tokenize correct
tokens = self.encoder._tokenize(text)
# recognize the `a` as the <unk-token>
token_ids = [self.encoder._convert_token_to_id(token) for token in tokens]
decoded_tokens = [self.encoder._convert_id_to_token(token_id) for token_id in token_ids]
self.assertIn(self.encoder.unk_token, decoded_tokens)
class SkepBPETokenizationTest(TokenizerTesterMixin, unittest.TestCase):
tokenizer_class = SkepTokenizer
space_between_special_tokens = True
from_pretrained_filter = filter_non_english
test_seq2seq = True
use_bpe_encoder = True
def setUp(self):
super().setUp()
vocab = [
"l",
"o",
"w",
"e",
"r",
"s",
"t",
"i",
"d",
"n",
"\u0120",
"\u0120l",
"\u0120n",
"\u0120lo",
"\u0120low",
"er",
"\u0120lowest",
"\u0120newer",
"\u0120wider",
"<unk>",
"<|endoftext|>",
]
# save vocab file
self.vocab_file = os.path.join(self.tmpdirname, "vocab.txt")
with open(self.vocab_file, "w", encoding="utf-8") as f:
# f.write('\n'.join(vocab))
f.write("\n".join(vocab + ["[PAD]", "[CLS]", "[SEP]", "[MASK]"]))
# save bpe related files
self.bpe_json_file = os.path.join(self.tmpdirname, "encoder.json")
self.bpe_vocab_file = os.path.join(self.tmpdirname, "merges.txt")
shutil.copyfile(get_tests_dir("fixtures/bpe.en/vocab.json"), self.bpe_json_file)
shutil.copyfile(get_tests_dir("fixtures/bpe.en/merges.txt"), self.bpe_vocab_file)
def get_tokenizer(self, **kwargs):
tokenizer = self.tokenizer_class.from_pretrained(
self.tmpdirname,
bpe_vocab_file=self.bpe_vocab_file,
bpe_json_file=self.bpe_json_file,
use_bpe_encoder=self.use_bpe_encoder,
unk_token="<unk>",
**kwargs,
)
return tokenizer
def get_input_output_texts(self, tokenizer):
input_text = " lower"
output_text = "\u0120lower"
return input_text, output_text
def get_clean_sequence(self, tokenizer, with_prefix_space=False, max_length=20, min_length=5) -> Tuple[str, list]:
toks = [
(i, tokenizer.decode([i], clean_up_tokenization_spaces=False))
for i in range(len(tokenizer.bpe_tokenizer.encoder))
]
toks = list(
filter(
lambda t: [t[0]]
== tokenizer.encode(t[1], return_token_type_ids=None, add_special_tokens=False)["input_ids"],
toks,
)
)
if max_length is not None and len(toks) > max_length:
toks = toks[:max_length]
if min_length is not None and len(toks) < min_length and len(toks) > 0:
while len(toks) < min_length:
toks = toks + toks
# toks_str = [t[1] for t in toks]
toks_ids = [t[0] for t in toks]
# Ensure consistency
output_txt = tokenizer.decode(toks_ids, clean_up_tokenization_spaces=False)
if " " not in output_txt and len(toks_ids) > 1:
output_txt = (
tokenizer.decode([toks_ids[0]], clean_up_tokenization_spaces=False)
+ " "
+ tokenizer.decode(toks_ids[1:], clean_up_tokenization_spaces=False)
)
if with_prefix_space:
output_txt = " " + output_txt
output_ids = tokenizer.encode(output_txt, return_token_type_ids=None, add_special_tokens=False)["input_ids"]
return output_txt, output_ids
def test_full_tokenizer(self):
tokenizer = self.get_tokenizer()
text = " lower newer"
bpe_tokens = ["\u0120low", "er", "\u0120", "n", "e", "w", "er"]
# test tokenize
tokens = tokenizer.tokenize(text)
self.assertListEqual(tokens, bpe_tokens)
# test encode
token_ids = tokenizer.encode(text)["input_ids"]
# test decode
decode_text = tokenizer.decode(token_ids, skip_special_tokens=True, spaces_between_special_tokens=False)
self.assertEqual(text, decode_text)
self.assertListEqual(tokenizer.convert_tokens_to_ids(tokens), [14, 15, 10, 9, 3, 2, 15])
def test_internal_consistency(self):
tokenizers = self.get_tokenizers()
for tokenizer in tokenizers:
with self.subTest(f"{tokenizer.__class__.__name__}"):
input_text, output_text = self.get_input_output_texts(tokenizer)
tokens = tokenizer.tokenize(input_text)
ids = tokenizer.convert_tokens_to_ids(tokens)
tokens_2 = tokenizer.convert_ids_to_tokens(ids)
ids = tokenizer.convert_tokens_to_ids(tokens)
ids_2 = tokenizer.encode(input_text, return_token_type_ids=None, add_special_tokens=False)["input_ids"]
self.assertListEqual(ids, ids_2)
tokens_2 = tokenizer.convert_ids_to_tokens(ids)
self.assertNotEqual(len(tokens_2), 0)
text_2 = tokenizer.decode(ids)
self.assertIsInstance(text_2, str)
def test_chinese(self):
tokenizer = BasicTokenizer()
self.assertListEqual(tokenizer.tokenize("ah\u535A\u63A8zz"), ["ah", "\u535A", "\u63A8", "zz"])
def test_clean_text(self):
tokenizer = self.get_tokenizer()
# Example taken from the issue https://github.com/huggingface/tokenizers/issues/340
self.assertListEqual(
[tokenizer.tokenize(t) for t in ["Test", "\xad", "test"]],
[["T", "e", "s", "t"], ["Â", "Ń"], ["t", "e", "s", "t"]],
)
def test_sequence_builders(self):
tokenizer = self.tokenizer_class.from_pretrained("skep_ernie_1.0_large_ch")
text = tokenizer.encode("sequence builders", return_token_type_ids=None, add_special_tokens=False)["input_ids"]
text_2 = tokenizer.encode("multi-sequence build", return_token_type_ids=None, add_special_tokens=False)[
"input_ids"
]
encoded_sentence = tokenizer.build_inputs_with_special_tokens(text)
encoded_pair = tokenizer.build_inputs_with_special_tokens(text, text_2)
assert encoded_sentence == [tokenizer.cls_token_id] + text + [tokenizer.sep_token_id]
assert encoded_pair == [tokenizer.cls_token_id] + text + [tokenizer.sep_token_id] + text_2 + [
tokenizer.sep_token_id
]
def test_pretokenized_inputs(self):
# Test when inputs are pretokenized
tokenizers = self.get_tokenizers(do_lower_case=False) # , add_prefix_space=True)
for tokenizer in tokenizers:
with self.subTest(f"{tokenizer.__class__.__name__}"):
if hasattr(tokenizer, "add_prefix_space") and not tokenizer.add_prefix_space:
continue
# Prepare a sequence from our tokenizer vocabulary
sequence, ids = self.get_clean_sequence(tokenizer, with_prefix_space=True, max_length=20)
# Test encode for pretokenized inputs
output_sequence = tokenizer.encode(sequence, return_token_type_ids=None, add_special_tokens=False)[
"input_ids"
]
self.assertEqual(ids, output_sequence)
def test_conversion_reversible(self):
self.skipTest("bpe vocab not supported cls_token, bos_token")
def test_offsets_mapping(self):
self.skipTest("using basic-tokenizer or word-piece tokenzier to do this test, so to skpt")
def test_special_tokens_mask_input_pairs(self):
tokenizers = self.get_tokenizers(do_lower_case=False)
for tokenizer in tokenizers:
with self.subTest(f"{tokenizer.__class__.__name__}"):
sequence_0 = " lower"
sequence_1 = "newer"
encoded_sequence = tokenizer.encode(sequence_0, return_token_type_ids=None, add_special_tokens=False)[
"input_ids"
]
encoded_sequence += tokenizer.encode(sequence_1, return_token_type_ids=None, add_special_tokens=False)[
"input_ids"
]
encoded_sequence_dict = tokenizer.encode(
sequence_0,
sequence_1,
add_special_tokens=True,
return_special_tokens_mask=True,
# add_prefix_space=False,
)
encoded_sequence_w_special = encoded_sequence_dict["input_ids"]
special_tokens_mask = encoded_sequence_dict["special_tokens_mask"]
self.assertEqual(len(special_tokens_mask), len(encoded_sequence_w_special))
filtered_sequence = [
(x if not special_tokens_mask[i] else None) for i, x in enumerate(encoded_sequence_w_special)
]
filtered_sequence = [x for x in filtered_sequence if x is not None]
self.assertEqual(encoded_sequence, filtered_sequence)
class SkepWordPieceTokenizationTest(TokenizerTesterMixin, unittest.TestCase):
tokenizer_class = SkepTokenizer
space_between_special_tokens = True
from_pretrained_filter = filter_non_english
test_seq2seq = True
use_bpe_encoder = False
from_pretrained_kwargs = {"do_lower_case": False}
def setUp(self):
super().setUp()
vocab_tokens = [
"[UNK]",
"[CLS]",
"[SEP]",
"[PAD]",
"[MASK]",
"want",
"##want",
"##ed",
"wa",
"un",
"runn",
"##ing",
",",
"low",
"lowest",
]
self.vocab_file = os.path.join(self.tmpdirname, "vocab.txt")
with open(self.vocab_file, "w", encoding="utf-8") as vocab_writer:
vocab_writer.write("\n".join(vocab_tokens))
def test_basic_tokenizer_lower(self):
tokenizer = BasicTokenizer(do_lower_case=True)
self.assertListEqual(
tokenizer.tokenize(" \tHeLLo!how \n Are yoU? "), ["hello", "!", "how", "are", "you", "?"]
)
self.assertListEqual(tokenizer.tokenize("H\u00E9llo"), ["hello"])
def test_basic_tokenizer_lower_strip_accents_false(self):
tokenizer = BasicTokenizer(do_lower_case=True, strip_accents=False)
self.assertListEqual(
tokenizer.tokenize(" \tHäLLo!how \n Are yoU? "), ["hällo", "!", "how", "are", "you", "?"]
)
self.assertListEqual(tokenizer.tokenize("H\u00E9llo"), ["h\u00E9llo"])
def test_basic_tokenizer_lower_strip_accents_true(self):
tokenizer = BasicTokenizer(do_lower_case=True)
self.assertListEqual(
tokenizer.tokenize(" \tHäLLo!how \n Are yoU? "), ["hallo", "!", "how", "are", "you", "?"]
)
self.assertListEqual(tokenizer.tokenize("H\u00E9llo"), ["hello"])
def test_basic_tokenizer_lower_strip_accents_default(self):
tokenizer = BasicTokenizer(do_lower_case=True)
self.assertListEqual(
tokenizer.tokenize(" \tHäLLo!how \n Are yoU? "), ["hallo", "!", "how", "are", "you", "?"]
)
self.assertListEqual(tokenizer.tokenize("H\u00E9llo"), ["hello"])
def test_basic_tokenizer_no_lower(self):
tokenizer = BasicTokenizer(do_lower_case=False)
self.assertListEqual(
tokenizer.tokenize(" \tHeLLo!how \n Are yoU? "), ["HeLLo", "!", "how", "Are", "yoU", "?"]
)
def test_basic_tokenizer_no_lower_strip_accents_false(self):
tokenizer = BasicTokenizer(do_lower_case=False, strip_accents=False)
self.assertListEqual(
tokenizer.tokenize(" \tHäLLo!how \n Are yoU? "), ["HäLLo", "!", "how", "Are", "yoU", "?"]
)
def test_basic_tokenizer_no_lower_strip_accents_true(self):
tokenizer = BasicTokenizer(do_lower_case=False, strip_accents=True)
self.assertListEqual(
tokenizer.tokenize(" \tHäLLo!how \n Are yoU? "), ["HaLLo", "!", "how", "Are", "yoU", "?"]
)
def test_basic_tokenizer_respects_never_split_tokens(self):
tokenizer = BasicTokenizer(do_lower_case=False, never_split=["[UNK]"])
self.assertListEqual(
tokenizer.tokenize(" \tHeLLo!how \n Are yoU? [UNK]"), ["HeLLo", "!", "how", "Are", "yoU", "?", "[UNK]"]
)
def test_wordpiece_tokenizer(self):
vocab_tokens = ["[UNK]", "[CLS]", "[SEP]", "want", "##want", "##ed", "wa", "un", "runn", "##ing"]
vocab = {}
for (i, token) in enumerate(vocab_tokens):
vocab[token] = i
tokenizer = WordpieceTokenizer(vocab=vocab, unk_token="[UNK]")
self.assertListEqual(tokenizer.tokenize(""), [])
self.assertListEqual(tokenizer.tokenize("unwanted running"), ["un", "##want", "##ed", "runn", "##ing"])
self.assertListEqual(tokenizer.tokenize("unwantedX running"), ["[UNK]", "runn", "##ing"])
class SkepChineseTokenizationTest(TokenizerTesterMixin, unittest.TestCase):
tokenizer_class = SkepTokenizer
space_between_special_tokens = False
from_pretrained_filter = filter_non_english
test_seq2seq = True
use_bpe_encoder = False
only_english_character = False
def setUp(self):
super().setUp()
self.vocab_file = os.path.join(self.tmpdirname, "vocab.txt")
shutil.copyfile(get_tests_dir("fixtures/vocab.zh.txt"), self.vocab_file)
self.bpe_vocab_file = None
self.bpe_json_file = None
def get_tokenizer(self, **kwargs):
return self.tokenizer_class.from_pretrained(
self.tmpdirname,
vocab_file=self.vocab_file,
bpe_vocab_file=self.bpe_vocab_file,
bpe_json_file=self.bpe_json_file,
use_bpe_encoder=self.use_bpe_encoder,
**kwargs,
)
def get_input_output_texts(self, tokenizer):
input_text = "飞\u6868深度学习框架"
output_text = "飞 桨 深 度 学 习 框 架"
return input_text, output_text
def test_full_tokenizer(self):
tokenizer = self.tokenizer_class(self.vocab_file)
tokens = tokenizer.tokenize("飞\u6868深度学习框架")
self.assertListEqual(tokens, list("飞桨深度学习框架"))
self.assertListEqual(tokenizer.convert_tokens_to_ids(tokens), [11, 12, 13, 10, 14, 15, 16, 17])
def test_chinese(self):
tokenizer = BasicTokenizer()
self.assertListEqual(tokenizer.tokenize("飞\u535A\u63A8桨"), ["飞", "\u535A", "\u63A8", "桨"])
def test_basic_tokenizer_no_lower(self):
tokenizer = BasicTokenizer(do_lower_case=False)
tokens = tokenizer.tokenize(" \t飞!桨 \n 深度学 习 ")
self.assertListEqual(tokens, ["飞", "!", "桨", "深", "度", "学", "习"])
def test_basic_tokenizer_respects_never_split_tokens(self):
tokenizer = BasicTokenizer(do_lower_case=False, never_split=["[UNK]"])
tokens = tokenizer.tokenize(" \t飞!桨 \n 深度学 习 [UNK]")
self.assertListEqual(tokens, ["飞", "!", "桨", "深", "度", "学", "习", "[UNK]"])
def test_offsets_mapping(self):
for tokenizer, pretrained_name, kwargs in self.tokenizers_list:
with self.subTest(f"{tokenizer.__class__.__name__} ({pretrained_name})"):
tokenizer = self.tokenizer_class.from_pretrained(pretrained_name, **kwargs)
text = "这世界很美"
pair = "我们需要共同守护"
# No pair
tokens_with_offsets = tokenizer.encode(
text, return_special_tokens_mask=True, return_offsets_mapping=True, add_special_tokens=True
)
added_tokens = tokenizer.num_special_tokens_to_add(False)
offsets = tokens_with_offsets["offset_mapping"]
# Assert there is the same number of tokens and offsets
self.assertEqual(len(offsets), len(tokens_with_offsets["input_ids"]))
# Assert there is online added_tokens special_tokens
self.assertEqual(sum(tokens_with_offsets["special_tokens_mask"]), added_tokens)
# Pairs
tokens_with_offsets = tokenizer.encode(
text, pair, return_special_tokens_mask=True, return_offsets_mapping=True, add_special_tokens=True
)
added_tokens = tokenizer.num_special_tokens_to_add(True)
offsets = tokens_with_offsets["offset_mapping"]
# Assert there is the same number of tokens and offsets
self.assertEqual(len(offsets), len(tokens_with_offsets["input_ids"]))
# Assert there is online added_tokens special_tokens
self.assertEqual(sum(tokens_with_offsets["special_tokens_mask"]), added_tokens)
def test_clean_text(self):
tokenizer = self.get_tokenizer()
# Example taken from the issue https://github.com/huggingface/tokenizers/issues/340
self.assertListEqual([tokenizer.tokenize(t) for t in ["鲲", "\xad", "鹏"]], [["[UNK]"], [], ["[UNK]"]])
@slow
def test_sequence_builders(self):
tokenizer = self.tokenizer_class.from_pretrained("skep_ernie_1.0_large_ch")
text = tokenizer.encode("sequence builders", return_token_type_ids=None, add_special_tokens=False)["input_ids"]
text_2 = tokenizer.encode("multi-sequence build", return_token_type_ids=None, add_special_tokens=False)[
"input_ids"
]
encoded_sentence = tokenizer.build_inputs_with_special_tokens(text)
encoded_pair = tokenizer.build_inputs_with_special_tokens(text, text_2)
assert encoded_sentence == [tokenizer.cls_token_id] + text + [tokenizer.sep_token_id]
assert encoded_pair == [tokenizer.cls_token_id] + text + [tokenizer.sep_token_id] + text_2 + [
tokenizer.sep_token_id
]
@slow
def test_offsets_with_special_characters(self):
for tokenizer, pretrained_name, kwargs in self.tokenizers_list:
with self.subTest(f"{tokenizer.__class__.__name__} ({pretrained_name})"):
tokenizer = self.tokenizer_class.from_pretrained(pretrained_name, **kwargs)
sentence = f"北京的首都 {tokenizer.mask_token} 是北京"
tokens = tokenizer.encode(
sentence,
return_attention_mask=False,
return_token_type_ids=False,
return_offsets_mapping=True,
add_special_tokens=True,
spaces_between_special_tokens=self.space_between_special_tokens,
)
expected_results = [
((0, 0), tokenizer.cls_token),
((0, 1), "北"),
((1, 2), "京"),
((2, 3), "的"),
((3, 4), "首"),
((4, 5), "都"),
((6, 12), "[MASK]"),
((13, 14), "是"),
((14, 15), "北"),
((15, 16), "京"),
((0, 0), tokenizer.sep_token),
]
self.assertEqual(
[e[1] for e in expected_results], tokenizer.convert_ids_to_tokens(tokens["input_ids"])
)
self.assertEqual([e[0] for e in expected_results], tokens["offset_mapping"])
def test_change_tokenize_chinese_chars(self):
list_of_commun_chinese_char = ["的", "人", "有"]
text_with_chinese_char = "".join(list_of_commun_chinese_char)
for tokenizer, pretrained_name, kwargs in self.tokenizers_list:
with self.subTest(f"{tokenizer.__class__.__name__} ({pretrained_name})"):
kwargs["tokenize_chinese_chars"] = True
tokenizer = self.tokenizer_class.from_pretrained(pretrained_name, **kwargs)
ids_without_spe_char_p = tokenizer.encode(
text_with_chinese_char, return_token_type_ids=None, add_special_tokens=False
)["input_ids"]
tokens_without_spe_char_p = tokenizer.convert_ids_to_tokens(ids_without_spe_char_p)
# it is expected that each Chinese character is not preceded by "##"
self.assertListEqual(tokens_without_spe_char_p, list_of_commun_chinese_char)
# not yet supported in bert tokenizer
"""
kwargs["tokenize_chinese_chars"] = False
tokenizer = self.tokenizer_class.from_pretrained(pretrained_name, **kwargs)
ids_without_spe_char_p = tokenizer.encode(text_with_chinese_char, return_token_type_ids=None,add_special_tokens=False)["input_ids"]
tokens_without_spe_char_p = tokenizer.convert_ids_to_tokens(ids_without_spe_char_p)
# it is expected that only the first Chinese character is not preceded by "##".
expected_tokens = [
f"##{token}" if idx != 0 else token for idx, token in enumerate(list_of_commun_chinese_char)
]
self.assertListEqual(tokens_without_spe_char_p, expected_tokens)
"""
def test_pretrained_model_lists(self):
self.skipTest("`max_model_input_sizes` not found, so skip this test")