1774 lines
202 KiB
Python
1774 lines
202 KiB
Python
# Copyright 2025 Mistral AI and The HuggingFace Inc. 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 gc
|
|
import tempfile
|
|
import unittest
|
|
|
|
import numpy as np
|
|
import torch
|
|
|
|
from transformers.image_utils import load_image
|
|
from transformers.models.auto.tokenization_auto import AutoTokenizer
|
|
from transformers.testing_utils import require_mistral_common
|
|
from transformers.tokenization_mistral_common import MistralCommonTokenizer
|
|
from transformers.tokenization_utils_base import BatchEncoding, TruncationStrategy
|
|
from transformers.utils import PaddingStrategy, is_mistral_common_available
|
|
|
|
|
|
if is_mistral_common_available():
|
|
import mistral_common.tokens.tokenizers
|
|
from mistral_common.exceptions import InvalidMessageStructureException
|
|
from mistral_common.protocol.instruct.request import ChatCompletionRequest
|
|
from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
|
|
from mistral_common.tokens.tokenizers.utils import list_local_hf_repo_files
|
|
|
|
# To avoid unnecessary `requests.get` calls which give us `Error: Too Many Requests for url` on CircleCI
|
|
mistral_common.tokens.tokenizers.image.download_image = load_image
|
|
|
|
|
|
from .test_processing_common import url_to_local_path
|
|
|
|
|
|
IMG_URL = url_to_local_path(
|
|
"https://huggingface.co/datasets/raushan-testing-hf/images_test/resolve/main/picsum_237_200x300.jpg"
|
|
)
|
|
# Required by `mistral_common.tokens.tokenizers.image.image_from_chunk` to correctly use local file
|
|
IMG_URL = f"file://{IMG_URL}" if not IMG_URL.startswith("http") else IMG_URL
|
|
|
|
IMG_BASE_64 = """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"""
|
|
|
|
AUDIO_NAMESPACE = "hf-internal-testing"
|
|
AUDIO_REPO_NAME = "dummy-audio-samples"
|
|
AUDIO_FILENAME = "bcn_weather.mp3"
|
|
AUDIO_URL = url_to_local_path(
|
|
f"https://huggingface.co/datasets/{AUDIO_NAMESPACE}/{AUDIO_REPO_NAME}/resolve/main/{AUDIO_FILENAME}"
|
|
)
|
|
AUDIO_BASE_64 = """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TCB8BGPktKQFPDJZlktQqRstAgz6NqpMQU1FMy4xMDCqqqqqqqqqqqqqWFCaoAAc01MABG2hIGLlHOR52rTFi2EhbxyKm69DJ64iu/hVcrfoU5Vq//sUxPIAQ0AdHyMNImhXgeQkYIwFm9MFVaxuWqCxV1U4PjpMQU1FMy4xMDCqqqqqqqqqqqqqqqqquqoZgAAOAALlEkLBT2IspyoS6/AD5qiuztVbuyV3B4ORBNAKBSAW//sUxPEAQsg/GqMAaihdhmOgYAzEWAm9+dsbVBwrPddgrB9MQU1FMy4xMDBVVVVVVVVVVVW66higABNEC4CyBgmGwYpkDQddSESlQ+1tYQcbUsUHTfCNipnEhCWsBNxc//sUxPUAQ0wxHyEkYmhuCaRwkYj1WCdT0aTC1xor4lmUnG1MQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVWIQhIt1AACgIkrKkgTGlZ0FyEHUKLBIY85enatdC0VMSEM//sUxPaAQ6AhI4ClgGhtB6QwNIwUlU41oUS90QgtNr9VHapMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqKUDQTbAAAboJeoBgpuYgItvRxTJQOur/478LbdGkBU+mMedD//sUxPQAQxQ9ISSEYihtA+OkZIxNw4FjEIKy19by9SI2evtMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVhRBQCwQZLhQfVQMKD4P2JQR8bZAKP/a3+PVtBQXPDoeSQtyITI//sUxPOAQ2whHQYAyChbBOQkwKBE4oNgv2TAcrbu/V//i6pMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppkalgACAJwo6zIGlWiDKB//sUxPYAw/hhHSMEaKhgB+QgYI2EhgOlH0zrG6iaFIQVuDlMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxPSAQ2AhHyMkwChnhmOgZIwlVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxPaAQ+QnHySwZmBmiGOgwI1dVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxPIAQ4wvIYQEZuBMAyQgkIxEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxPMAQ1AdIYGkYmhdhCPkkwDVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxPQAQwQXGQSlIkhsBCQklIwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUxOkBwzwXHyGYwCAQACNUAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"""
|
|
|
|
|
|
@require_mistral_common
|
|
class TestMistralCommonTokenizer(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.repo_id = "hf-internal-testing/namespace-mistralai-repo_name-Mistral-Small-3.1-24B-Instruct-2503"
|
|
# determine if we already have this downloaded
|
|
cls.local_files_only = len(list_local_hf_repo_files(cls.repo_id, revision=None)) > 0
|
|
|
|
cls.tokenizer: MistralCommonTokenizer = AutoTokenizer.from_pretrained(
|
|
cls.repo_id,
|
|
tokenizer_type="mistral",
|
|
local_files_only=cls.local_files_only,
|
|
# This is a hack as `list_local_hf_repo_files` from `mistral_common` has a bug
|
|
# TODO: Discuss with `mistral-common` maintainers: after a fix being done there, remove this `revision` hack
|
|
revision=None,
|
|
)
|
|
cls.ref_tokenizer: MistralTokenizer = MistralTokenizer.from_hf_hub(
|
|
cls.repo_id, local_files_only=cls.local_files_only
|
|
)
|
|
# cls.tokenizer_audio: MistralCommonTokenizer = AutoTokenizer.from_pretrained(
|
|
# "hf-internal-testing/namesspace-mistralai-repo_name-Voxtral-Mini-3B-2507"
|
|
# )
|
|
repo_id = "mistralai/Voxtral-Mini-3B-2507"
|
|
local_files_only = len(list_local_hf_repo_files(repo_id, revision=None)) > 0
|
|
|
|
cls.tokenizer_audio: MistralCommonTokenizer = AutoTokenizer.from_pretrained(
|
|
repo_id,
|
|
local_files_only=local_files_only,
|
|
revision=None,
|
|
)
|
|
cls.ref_tokenizer_audio: MistralCommonTokenizer = MistralTokenizer.from_hf_hub(
|
|
repo_id, local_files_only=local_files_only
|
|
)
|
|
|
|
cls.fixture_conversations = [
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
],
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the temperature in Paris?"},
|
|
],
|
|
]
|
|
cls.tokenized_fixture_conversations = [
|
|
cls.ref_tokenizer.encode_chat_completion(ChatCompletionRequest.from_openai(conversation))
|
|
for conversation in cls.fixture_conversations
|
|
]
|
|
|
|
cls.ref_special_ids = {t["rank"] for t in cls.ref_tokenizer.instruct_tokenizer.tokenizer._all_special_tokens}
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
del cls.tokenizer
|
|
del cls.ref_tokenizer
|
|
del cls.tokenizer_audio
|
|
del cls.ref_tokenizer_audio
|
|
del cls.fixture_conversations
|
|
del cls.tokenized_fixture_conversations
|
|
del cls.ref_special_ids
|
|
gc.collect()
|
|
|
|
def _ref_piece_to_id(self, piece: str) -> int:
|
|
pieces = self.ref_tokenizer.instruct_tokenizer.tokenizer._model.encode(
|
|
piece, allowed_special="all", disallowed_special=set()
|
|
)
|
|
assert len(pieces) == 1, f"Expected to decode 1 token, got {len(pieces)}"
|
|
return pieces[0]
|
|
|
|
def test_vocab_size(self):
|
|
self.assertEqual(self.tokenizer.vocab_size, self.ref_tokenizer.instruct_tokenizer.tokenizer.n_words)
|
|
|
|
def test_save_pretrained(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
self.tokenizer.save_pretrained(tmp_dir)
|
|
loaded_tokenizer = MistralCommonTokenizer.from_pretrained(tmp_dir)
|
|
|
|
self.assertIsNotNone(loaded_tokenizer)
|
|
self.assertEqual(self.tokenizer.get_vocab(), loaded_tokenizer.get_vocab())
|
|
self.assertEqual(
|
|
self.tokenizer.tokenizer.instruct_tokenizer.tokenizer.version,
|
|
loaded_tokenizer.tokenizer.instruct_tokenizer.tokenizer.version,
|
|
)
|
|
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.save_pretrained`."
|
|
):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
self.tokenizer.save_pretrained(tmp_dir, unk_args="")
|
|
|
|
def test_encode(self):
|
|
string = "Hello, world!"
|
|
|
|
# Test 1:
|
|
# encode with add_special_tokens
|
|
expected_with_special = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string, bos=True, eos=True)
|
|
tokens_with_special = self.tokenizer.encode(string, add_special_tokens=True)
|
|
self.assertEqual(tokens_with_special, expected_with_special)
|
|
|
|
# Test 2:
|
|
# encode without add_special_tokens
|
|
expected_without_special = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string, bos=False, eos=False)
|
|
tokens_without_special = self.tokenizer.encode(string, add_special_tokens=False)
|
|
self.assertEqual(tokens_without_special, expected_without_special)
|
|
|
|
# Test 3:
|
|
# encode with return_tensors
|
|
tokens_with_return_tensors = self.tokenizer.encode(string, add_special_tokens=False, return_tensors="pt")
|
|
self.assertIsInstance(tokens_with_return_tensors, torch.Tensor)
|
|
self.assertEqual(tokens_with_return_tensors.tolist()[0], expected_without_special)
|
|
|
|
# Test 4:
|
|
# encode with max_length
|
|
tokens_with_max_length = self.tokenizer.encode(string, add_special_tokens=False, max_length=3)
|
|
self.assertEqual(tokens_with_max_length, expected_without_special[:3])
|
|
|
|
# Test 5:
|
|
# encode with padding
|
|
tokens_with_padding = self.tokenizer.encode(
|
|
string, add_special_tokens=False, padding=True, pad_to_multiple_of=6
|
|
)
|
|
expected_padding = [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (
|
|
6 - len(expected_without_special) % 6
|
|
) + expected_without_special
|
|
self.assertEqual(tokens_with_padding, expected_padding)
|
|
|
|
for padding in [
|
|
False,
|
|
True,
|
|
"longest",
|
|
"max_length",
|
|
"do_not_pad",
|
|
PaddingStrategy.LONGEST,
|
|
PaddingStrategy.MAX_LENGTH,
|
|
PaddingStrategy.DO_NOT_PAD,
|
|
]:
|
|
tokens_with_padding = self.tokenizer.encode(string, add_special_tokens=False, padding=padding)
|
|
self.assertEqual(tokens_with_padding, expected_without_special)
|
|
|
|
# For truncation, we use a longer string
|
|
string_long = (
|
|
"Hello world! It is a beautiful day today. The sun is shining brightly and the birds are singing."
|
|
)
|
|
expected_long = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string_long, bos=False, eos=False)
|
|
|
|
# Test 6:
|
|
# encode with truncation
|
|
tokens_with_truncation = self.tokenizer.encode(
|
|
string_long, add_special_tokens=False, truncation=True, max_length=12
|
|
)
|
|
self.assertEqual(tokens_with_truncation, expected_long[:12])
|
|
|
|
# Test 7:
|
|
# encode with padding and truncation
|
|
tokens_with_padding_and_truncation = self.tokenizer.encode(
|
|
string_long, add_special_tokens=False, padding=True, pad_to_multiple_of=12, truncation=True, max_length=36
|
|
)
|
|
expected_long_padding = [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (
|
|
12 - len(expected_long) % 12
|
|
) + expected_long
|
|
self.assertEqual(tokens_with_padding_and_truncation, expected_long_padding)
|
|
|
|
# Test encode with unsupported kwargs
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.encode`."
|
|
):
|
|
self.tokenizer.encode("Hello, world!", add_special_tokens=True, unk_args="")
|
|
|
|
def test_decode(self):
|
|
string = "Hello, world!"
|
|
string_with_space = "Hello, world !"
|
|
|
|
tokens_ids = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string, bos=True, eos=True)
|
|
tokens_ids_with_space = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(
|
|
string_with_space, bos=True, eos=True
|
|
)
|
|
|
|
# Test 1:
|
|
# decode with and without skip_special_tokens
|
|
self.assertEqual(self.tokenizer.decode(tokens_ids, skip_special_tokens=True), string)
|
|
self.assertEqual(self.tokenizer.decode(tokens_ids, skip_special_tokens=False), "<s>" + string + "</s>")
|
|
self.assertEqual(self.tokenizer.decode(tokens_ids_with_space, skip_special_tokens=True), string_with_space)
|
|
|
|
# Test 2:
|
|
# decode with clean_up_tokenization_spaces
|
|
self.assertEqual(
|
|
self.tokenizer.decode(tokens_ids_with_space, skip_special_tokens=True, clean_up_tokenization_spaces=True),
|
|
"Hello, world!",
|
|
)
|
|
|
|
# Test 3:
|
|
# decode with unsupported kwargs
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.decode`."
|
|
):
|
|
self.tokenizer.decode(tokens_ids, skip_special_tokens=False, unk_args="")
|
|
|
|
def test_batch_decode(self):
|
|
string = "Hello, world!"
|
|
string_with_space = "Hello, world !"
|
|
|
|
batch_tokens_ids = [
|
|
self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string, bos=True, eos=True),
|
|
self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string_with_space, bos=True, eos=True),
|
|
]
|
|
|
|
# Test 1:
|
|
# batch_decode with and without skip_special_tokens
|
|
self.assertEqual(
|
|
self.tokenizer.batch_decode(batch_tokens_ids, skip_special_tokens=True),
|
|
[string, string_with_space],
|
|
)
|
|
self.assertEqual(
|
|
self.tokenizer.batch_decode(batch_tokens_ids, skip_special_tokens=False),
|
|
["<s>" + string + "</s>", "<s>" + string_with_space + "</s>"],
|
|
)
|
|
self.assertEqual(
|
|
self.tokenizer.batch_decode(batch_tokens_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True),
|
|
["Hello, world!", "Hello, world!"],
|
|
)
|
|
|
|
# Test 2:
|
|
# batch_decode with unsupported kwargs
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.batch_decode`."
|
|
):
|
|
self.tokenizer.batch_decode(batch_tokens_ids, skip_special_tokens=False, unk_args="")
|
|
|
|
def test_convert_ids_to_tokens(self):
|
|
# Test 1:
|
|
# with skip_special_tokens=False
|
|
ids = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode("Hello world!", bos=True, eos=True)
|
|
expected_tokens = [self.ref_tokenizer.instruct_tokenizer.tokenizer.id_to_piece(id) for id in ids]
|
|
|
|
tokens = self.tokenizer.convert_ids_to_tokens(ids, skip_special_tokens=False)
|
|
self.assertEqual(tokens, expected_tokens)
|
|
|
|
token = self.tokenizer.convert_ids_to_tokens(ids[0], skip_special_tokens=False)
|
|
self.assertEqual(token, expected_tokens[0])
|
|
|
|
# Test 2:
|
|
# with skip_special_tokens=True
|
|
expected_tokens = expected_tokens[1:-1]
|
|
tokens = self.tokenizer.convert_ids_to_tokens(ids, skip_special_tokens=True)
|
|
self.assertEqual(tokens, expected_tokens)
|
|
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.convert_ids_to_tokens(ids[0], skip_special_tokens=True)
|
|
token = self.tokenizer.convert_ids_to_tokens(ids[1], skip_special_tokens=True)
|
|
self.assertEqual(token, expected_tokens[0])
|
|
|
|
def test_convert_tokens_to_ids(self):
|
|
tokens = ["Hello", "world", "!"]
|
|
expected_ids = [self._ref_piece_to_id(token) for token in tokens]
|
|
# Test 1:
|
|
# list of tokens
|
|
ids = self.tokenizer.convert_tokens_to_ids(tokens)
|
|
self.assertEqual(ids, expected_ids)
|
|
|
|
# Test 2:
|
|
# single token
|
|
id = self.tokenizer.convert_tokens_to_ids(tokens[0])
|
|
self.assertEqual(id, expected_ids[0])
|
|
self.assertEqual(id, self.tokenizer.convert_tokens_to_ids(tokens[0]))
|
|
|
|
def test_tokenize(self):
|
|
string = "Hello world!"
|
|
expected_tokens = [
|
|
self.ref_tokenizer.instruct_tokenizer.tokenizer.id_to_piece(id)
|
|
for id in self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(string, bos=False, eos=False)
|
|
]
|
|
tokens = self.tokenizer.tokenize(string)
|
|
self.assertEqual(tokens, expected_tokens)
|
|
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [add_special_tokens] are not supported by `MistralCommonTokenizer.tokenize`."
|
|
):
|
|
self.tokenizer.tokenize(string, add_special_tokens=True)
|
|
|
|
def test_get_special_tokens_mask(self):
|
|
# Test 1:
|
|
# with skip_special_tokens=False
|
|
ids = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode("Hello world!", bos=True, eos=True)
|
|
expected_mask = [1 if id in self.ref_special_ids else 0 for id in ids]
|
|
|
|
mask = self.tokenizer.get_special_tokens_mask(ids)
|
|
self.assertEqual(mask, expected_mask)
|
|
|
|
# Test 2:
|
|
# already_has_special_tokens=True should raise an error
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.get_special_tokens_mask(ids, already_has_special_tokens=True)
|
|
|
|
# Test 3:
|
|
# token_ids_1 not None should raise an error
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.get_special_tokens_mask(ids, token_ids_1=ids)
|
|
|
|
def test_pad_batch_encoding_input(self):
|
|
# Test 1:
|
|
# padding and default values
|
|
|
|
def get_batch_encoding():
|
|
return self.tokenizer("Hello world!", return_special_tokens_mask=True)
|
|
|
|
batch_encoding = get_batch_encoding()
|
|
|
|
for padding in [
|
|
False,
|
|
True,
|
|
"longest",
|
|
"max_length",
|
|
"do_not_pad",
|
|
PaddingStrategy.LONGEST,
|
|
PaddingStrategy.MAX_LENGTH,
|
|
PaddingStrategy.DO_NOT_PAD,
|
|
]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding)
|
|
self.assertEqual(padded_batch_encoding, batch_encoding)
|
|
|
|
# Test 2:
|
|
# padding_strategy="max_length" or PaddingStrategy.MAX_LENGTH and max_length
|
|
for padding in ["max_length", PaddingStrategy.MAX_LENGTH]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding, max_length=12)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (12 - len(batch_encoding["input_ids"]))
|
|
+ batch_encoding["input_ids"],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[0] * (12 - len(batch_encoding["input_ids"])) + batch_encoding["attention_mask"],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[1] * (12 - len(batch_encoding["input_ids"])) + batch_encoding["special_tokens_mask"],
|
|
)
|
|
|
|
# Test 3:
|
|
# padding_strategy=True or "longest" or PaddingStrategy.LONGEST or "max_length" or PaddingStrategy.MAX_LENGTH and pad_to_multiple_of 16
|
|
for padding in [True, "longest", PaddingStrategy.LONGEST]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding, pad_to_multiple_of=16)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (16 - len(batch_encoding["input_ids"]))
|
|
+ batch_encoding["input_ids"],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[0] * (16 - len(batch_encoding["input_ids"])) + batch_encoding["attention_mask"],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[1] * (16 - len(batch_encoding["input_ids"])) + batch_encoding["special_tokens_mask"],
|
|
)
|
|
|
|
# Test 4:
|
|
# padding_side="right"
|
|
right_tokenizer = MistralCommonTokenizer.from_pretrained(
|
|
self.repo_id,
|
|
local_files_only=self.local_files_only,
|
|
padding_side="right",
|
|
revision=None,
|
|
)
|
|
right_paddings = [
|
|
right_tokenizer.pad(get_batch_encoding(), padding="max_length", max_length=12),
|
|
self.tokenizer.pad(get_batch_encoding(), padding="max_length", max_length=12, padding_side="right"),
|
|
]
|
|
for padded_batch_encoding in right_paddings:
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
batch_encoding["input_ids"]
|
|
+ [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (12 - len(batch_encoding["input_ids"])),
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
batch_encoding["attention_mask"] + [0] * (12 - len(batch_encoding["input_ids"])),
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
batch_encoding["special_tokens_mask"] + [1] * (12 - len(batch_encoding["input_ids"])),
|
|
)
|
|
|
|
# Test 5:
|
|
# return_attention_mask=False
|
|
padded_batch_encoding = self.tokenizer.pad(
|
|
get_batch_encoding(), padding="max_length", max_length=12, return_attention_mask=False
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (12 - len(batch_encoding["input_ids"]))
|
|
+ batch_encoding["input_ids"],
|
|
)
|
|
self.assertEqual(padded_batch_encoding["attention_mask"], batch_encoding["attention_mask"])
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[1] * (12 - len(batch_encoding["input_ids"])) + batch_encoding["special_tokens_mask"],
|
|
)
|
|
|
|
# Test 6:
|
|
# return_tensors="pt" or "np"
|
|
for return_tensors in ["pt", "np"]:
|
|
padded_batch_encoding = self.tokenizer.pad(
|
|
get_batch_encoding(), padding="max_length", max_length=12, return_tensors=return_tensors
|
|
)
|
|
self.assertEqual(padded_batch_encoding["input_ids"].shape, torch.Size((12,)))
|
|
self.assertEqual(padded_batch_encoding["attention_mask"].shape, torch.Size((12,)))
|
|
self.assertEqual(padded_batch_encoding["special_tokens_mask"].shape, torch.Size((12,)))
|
|
|
|
def test_list_batch_encoding_input(self):
|
|
def get_batch_encoding():
|
|
return self.tokenizer(["Hello world!", "Hello world! Longer sentence."], return_special_tokens_mask=True)
|
|
|
|
# Test 1:
|
|
# padding=True or "longest" or PaddingStrategy.LONGEST
|
|
batch_encoding = get_batch_encoding()
|
|
for padding in [
|
|
True,
|
|
"longest",
|
|
PaddingStrategy.LONGEST,
|
|
]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (len(batch_encoding["input_ids"][1]) - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["input_ids"][0],
|
|
batch_encoding["input_ids"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[
|
|
[0] * (len(batch_encoding["input_ids"][1]) - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["attention_mask"][0],
|
|
batch_encoding["attention_mask"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[
|
|
[1] * (len(batch_encoding["input_ids"][1]) - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["special_tokens_mask"][0],
|
|
batch_encoding["special_tokens_mask"][1],
|
|
],
|
|
)
|
|
|
|
# Test 2:
|
|
# padding_strategy="max_length" or PaddingStrategy.MAX_LENGTH and max_length
|
|
for padding in ["max_length", PaddingStrategy.MAX_LENGTH]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding, max_length=12)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (12 - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["input_ids"][0],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (12 - len(batch_encoding["input_ids"][1]))
|
|
+ batch_encoding["input_ids"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[
|
|
[0] * (12 - len(batch_encoding["input_ids"][0])) + batch_encoding["attention_mask"][0],
|
|
[0] * (12 - len(batch_encoding["input_ids"][1])) + batch_encoding["attention_mask"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[
|
|
[1] * (12 - len(batch_encoding["input_ids"][0])) + batch_encoding["special_tokens_mask"][0],
|
|
[1] * (12 - len(batch_encoding["input_ids"][1])) + batch_encoding["special_tokens_mask"][1],
|
|
],
|
|
)
|
|
|
|
# Test 3:
|
|
# padding_strategy=True or "longest" or PaddingStrategy.LONGEST or "max_length" or PaddingStrategy.MAX_LENGTH and pad_to_multiple_of 16
|
|
for padding in [True, "longest", PaddingStrategy.LONGEST]:
|
|
padded_batch_encoding = self.tokenizer.pad(get_batch_encoding(), padding=padding, pad_to_multiple_of=16)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (16 - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["input_ids"][0],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (16 - len(batch_encoding["input_ids"][1]))
|
|
+ batch_encoding["input_ids"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[
|
|
[0] * (16 - len(batch_encoding["input_ids"][0])) + batch_encoding["attention_mask"][0],
|
|
[0] * (16 - len(batch_encoding["input_ids"][1])) + batch_encoding["attention_mask"][1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[
|
|
[1] * (16 - len(batch_encoding["input_ids"][0])) + batch_encoding["special_tokens_mask"][0],
|
|
[1] * (16 - len(batch_encoding["input_ids"][1])) + batch_encoding["special_tokens_mask"][1],
|
|
],
|
|
)
|
|
|
|
# Test 4:
|
|
# padding_side="right"
|
|
right_tokenizer = MistralCommonTokenizer.from_pretrained(
|
|
self.repo_id,
|
|
local_files_only=self.local_files_only,
|
|
padding_side="right",
|
|
revision=None,
|
|
)
|
|
right_paddings = [
|
|
right_tokenizer.pad(get_batch_encoding(), padding="max_length", max_length=12),
|
|
self.tokenizer.pad(get_batch_encoding(), padding="max_length", max_length=12, padding_side="right"),
|
|
]
|
|
for padded_batch_encoding in right_paddings:
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[
|
|
batch_encoding["input_ids"][0]
|
|
+ [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (12 - len(batch_encoding["input_ids"][0])),
|
|
batch_encoding["input_ids"][1]
|
|
+ [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
* (12 - len(batch_encoding["input_ids"][1])),
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["attention_mask"],
|
|
[
|
|
batch_encoding["attention_mask"][0] + [0] * (12 - len(batch_encoding["input_ids"][0])),
|
|
batch_encoding["attention_mask"][1] + [0] * (12 - len(batch_encoding["input_ids"][1])),
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[
|
|
batch_encoding["special_tokens_mask"][0] + [1] * (12 - len(batch_encoding["input_ids"][0])),
|
|
batch_encoding["special_tokens_mask"][1] + [1] * (12 - len(batch_encoding["input_ids"][1])),
|
|
],
|
|
)
|
|
|
|
# Test 5:
|
|
# return_attention_mask=False
|
|
padded_batch_encoding = self.tokenizer.pad(
|
|
get_batch_encoding(), padding="max_length", max_length=12, return_attention_mask=False
|
|
)
|
|
self.assertEqual(
|
|
padded_batch_encoding["input_ids"],
|
|
[
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (12 - len(batch_encoding["input_ids"][0]))
|
|
+ batch_encoding["input_ids"][0],
|
|
[self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id] * (12 - len(batch_encoding["input_ids"][1]))
|
|
+ batch_encoding["input_ids"][1],
|
|
],
|
|
)
|
|
self.assertEqual(padded_batch_encoding["attention_mask"], batch_encoding["attention_mask"])
|
|
self.assertEqual(
|
|
padded_batch_encoding["special_tokens_mask"],
|
|
[
|
|
[1] * (12 - len(batch_encoding["input_ids"][0])) + batch_encoding["special_tokens_mask"][0],
|
|
[1] * (12 - len(batch_encoding["input_ids"][1])) + batch_encoding["special_tokens_mask"][1],
|
|
],
|
|
)
|
|
|
|
# Test 6:
|
|
# return_tensors="pt" or "np"
|
|
for return_tensors in ["pt", "np"]:
|
|
padded_batch_encoding = self.tokenizer.pad(
|
|
get_batch_encoding(), padding="max_length", max_length=12, return_tensors=return_tensors
|
|
)
|
|
self.assertEqual(padded_batch_encoding["input_ids"].shape, torch.Size((2, 12)))
|
|
self.assertEqual(padded_batch_encoding["attention_mask"].shape, torch.Size((2, 12)))
|
|
self.assertEqual(padded_batch_encoding["special_tokens_mask"].shape, torch.Size((2, 12)))
|
|
|
|
def test_truncate_sequences(self):
|
|
# Test 1:
|
|
# truncation_strategy="longest_first" or TruncationStrategy.LONGEST_FIRST
|
|
text = "Hello world!"
|
|
ids = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(text, bos=True, eos=True)
|
|
for truncation in ["longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
for num_tokens_to_remove in [0, 2]:
|
|
tokens, none, overflowing_tokens = self.tokenizer.truncate_sequences(
|
|
ids, truncation_strategy=truncation, num_tokens_to_remove=num_tokens_to_remove
|
|
)
|
|
self.assertEqual(tokens, ids[:-num_tokens_to_remove] if num_tokens_to_remove > 0 else ids)
|
|
self.assertIsNone(none)
|
|
self.assertEqual(overflowing_tokens, ids[-num_tokens_to_remove:] if num_tokens_to_remove > 0 else [])
|
|
|
|
# Test 2:
|
|
# truncation_strategy="only_first" or "only_second" or TruncationStrategy.ONLY_FIRST or TruncationStrategy.ONLY_SECOND
|
|
# Should raise a ValueError
|
|
for truncation in ["only_first", "only_second", TruncationStrategy.ONLY_FIRST, TruncationStrategy.ONLY_SECOND]:
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.truncate_sequences(ids, truncation_strategy=truncation, num_tokens_to_remove=1)
|
|
|
|
# Test 3:
|
|
# truncation_strategy="do_not_truncate" or TruncationStrategy.DO_NOT_TRUNCATE
|
|
for truncation in ["do_not_truncate", TruncationStrategy.DO_NOT_TRUNCATE]:
|
|
tokens, none, overflowing_tokens = self.tokenizer.truncate_sequences(
|
|
ids, truncation_strategy=truncation, num_tokens_to_remove=1
|
|
)
|
|
self.assertEqual(tokens, ids)
|
|
self.assertIsNone(none)
|
|
self.assertEqual(overflowing_tokens, [])
|
|
|
|
# Test 4:
|
|
# pair_ids is not None
|
|
# Should raise a ValueError
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.truncate_sequences(
|
|
ids, pair_ids=ids, truncation_strategy="longest_first", num_tokens_to_remove=1
|
|
)
|
|
|
|
# Test 5:
|
|
# stride
|
|
for stride in [0, 2]:
|
|
tokens, none, overflowing_tokens = self.tokenizer.truncate_sequences(
|
|
ids, truncation_strategy="longest_first", num_tokens_to_remove=2, stride=stride
|
|
)
|
|
self.assertEqual(tokens, ids[:-2])
|
|
self.assertIsNone(none)
|
|
self.assertEqual(overflowing_tokens, ids[-2 - stride :])
|
|
|
|
# Test 6:
|
|
# truncation_side="left"
|
|
left_tokenizer = MistralCommonTokenizer.from_pretrained(
|
|
self.repo_id,
|
|
local_files_only=self.local_files_only,
|
|
truncation_side="left",
|
|
revision=None,
|
|
)
|
|
tokens, none, overflowing_tokens = left_tokenizer.truncate_sequences(
|
|
ids, truncation_strategy="longest_first", num_tokens_to_remove=2
|
|
)
|
|
self.assertEqual(tokens, ids[2:])
|
|
self.assertIsNone(none)
|
|
self.assertEqual(overflowing_tokens, ids[:2])
|
|
|
|
def test_apply_chat_template_basic(self):
|
|
conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the capital of France?"},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(ChatCompletionRequest.from_openai(conversation))
|
|
|
|
# Test 1:
|
|
# with tokenize
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=False),
|
|
expected_tokenized.text,
|
|
)
|
|
|
|
# Test 2:
|
|
# without tokenize
|
|
self.assertEqual(self.tokenizer.apply_chat_template(conversation, tokenize=True), expected_tokenized.tokens)
|
|
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.apply_chat_template`."
|
|
):
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=True, unk_args="")
|
|
|
|
def test_apply_chat_template_continue_final_message(self):
|
|
conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the capital of France?"},
|
|
{"role": "assistant", "content": "Paris"},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(conversation, continue_final_message=True)
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=False, continue_final_message=True),
|
|
expected_tokenized.text,
|
|
)
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=True, continue_final_message=True),
|
|
expected_tokenized.tokens,
|
|
)
|
|
|
|
with self.assertRaises(InvalidMessageStructureException):
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=False, continue_final_message=False)
|
|
|
|
def test_apply_chat_template_with_tools(self):
|
|
conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the temperature in Paris?"},
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": "azerty123",
|
|
"function": {
|
|
"name": "get_current_weather",
|
|
"arguments": {"location": "Paris", "format": "text", "unit": "celsius"},
|
|
},
|
|
}
|
|
],
|
|
},
|
|
{"role": "tool", "name": "get_current_weather", "content": "22", "tool_call_id": "azerty123"},
|
|
]
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "get_current_weather",
|
|
"description": "Get the current weather in a given location",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "The city and state, e.g. San Francisco, CA",
|
|
"required": ["location"],
|
|
},
|
|
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
|
"format": {
|
|
"type": "string",
|
|
"enum": ["text", "json"],
|
|
"description": "The format of the response",
|
|
"required": ["format"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(conversation, tools)
|
|
)
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tools=tools, tokenize=False),
|
|
expected_tokenized.text,
|
|
)
|
|
|
|
def test_apply_chat_template_with_image(self):
|
|
ref_conversation = conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": IMG_URL},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(ref_conversation)
|
|
)
|
|
image_contents = [
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": IMG_URL},
|
|
},
|
|
{
|
|
"type": "image",
|
|
"url": IMG_URL,
|
|
},
|
|
{"type": "image", "base64": IMG_BASE_64},
|
|
]
|
|
for image_content in image_contents:
|
|
conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [{"type": "text", "text": "What is this?"}, image_content],
|
|
},
|
|
]
|
|
|
|
output = self.tokenizer.apply_chat_template(conversation, tokenize=True)
|
|
self.assertEqual(output, expected_tokenized.tokens)
|
|
|
|
output_dict = self.tokenizer.apply_chat_template(conversation, tokenize=True, return_dict=True)
|
|
self.assertEqual(output_dict["input_ids"], expected_tokenized.tokens)
|
|
self.assertEqual(len(output_dict["pixel_values"]), len(expected_tokenized.images))
|
|
for o, e in zip(output_dict["pixel_values"], expected_tokenized.images):
|
|
self.assertTrue(np.allclose(o, e))
|
|
|
|
output_dict = self.tokenizer.apply_chat_template(
|
|
conversation, tokenize=True, return_dict=True, return_tensors="pt"
|
|
)
|
|
self.assertEqual(output_dict["input_ids"].tolist()[0], expected_tokenized.tokens)
|
|
self.assertTrue(torch.allclose(output_dict["pixel_values"], torch.tensor(expected_tokenized.images)))
|
|
|
|
def test_apply_chat_template_with_audio(self):
|
|
ref_conversation = conversation = [
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "input_audio",
|
|
"input_audio": {
|
|
"data": AUDIO_BASE_64,
|
|
"format": "wav",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer_audio.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(ref_conversation)
|
|
)
|
|
audio_contents = [
|
|
{
|
|
"type": "audio",
|
|
"url": AUDIO_URL,
|
|
},
|
|
{
|
|
"type": "audio",
|
|
"path": AUDIO_URL,
|
|
},
|
|
{"type": "audio", "base64": AUDIO_BASE_64},
|
|
]
|
|
for audio_content in audio_contents:
|
|
conversation = [
|
|
{
|
|
"role": "user",
|
|
"content": [{"type": "text", "text": "What is this?"}, audio_content],
|
|
},
|
|
]
|
|
|
|
output = self.tokenizer_audio.apply_chat_template(conversation, tokenize=True)
|
|
self.assertEqual(output, expected_tokenized.tokens)
|
|
|
|
output_dict = self.tokenizer_audio.apply_chat_template(conversation, tokenize=True, return_dict=True)
|
|
self.assertEqual(output_dict["input_ids"], expected_tokenized.tokens)
|
|
self.assertEqual(len(output_dict["audio"]), len(expected_tokenized.audios))
|
|
for o, e in zip(output_dict["audio"], expected_tokenized.audios):
|
|
audio_array = e.audio_array
|
|
self.assertTrue(np.allclose(o, audio_array))
|
|
|
|
with self.assertRaises(NotImplementedError):
|
|
output_dict = self.tokenizer_audio.apply_chat_template(
|
|
conversation, tokenize=True, return_dict=True, return_tensors="pt"
|
|
)
|
|
|
|
def test_appsly_chat_template_with_truncation(self):
|
|
conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the capital of France?"},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(ChatCompletionRequest.from_openai(conversation))
|
|
|
|
# Test 1:
|
|
# with truncation
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=True, truncation=True, max_length=20),
|
|
expected_tokenized.tokens[:20],
|
|
)
|
|
|
|
# Test 2:
|
|
# without truncation
|
|
self.assertEqual(
|
|
self.tokenizer.apply_chat_template(conversation, tokenize=True, truncation=False, max_length=20),
|
|
expected_tokenized.tokens,
|
|
)
|
|
|
|
# Test 3:
|
|
# assert truncation is boolean
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.apply_chat_template(
|
|
conversation, tokenize=True, truncation=TruncationStrategy.LONGEST_FIRST, max_length=20
|
|
)
|
|
|
|
def test_batch_apply_chat_template(self):
|
|
conversations = [
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": IMG_URL},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you?"},
|
|
{"role": "user", "content": "What is the temperature in Paris?"},
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": "azerty123",
|
|
"function": {
|
|
"name": "get_current_weather",
|
|
"arguments": {"location": "Paris", "format": "text", "unit": "celsius"},
|
|
},
|
|
}
|
|
],
|
|
},
|
|
{"role": "tool", "name": "get_current_weather", "content": "22", "tool_call_id": "azerty123"},
|
|
],
|
|
]
|
|
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "get_current_weather",
|
|
"description": "Get the current weather in a given location",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "The city and state, e.g. San Francisco, CA",
|
|
"required": ["location"],
|
|
},
|
|
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
|
"format": {
|
|
"type": "string",
|
|
"enum": ["text", "json"],
|
|
"description": "The format of the response",
|
|
"required": ["format"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
]
|
|
|
|
expected_tokenized = [
|
|
self.ref_tokenizer.encode_chat_completion(ChatCompletionRequest.from_openai(conversation, tools=tools))
|
|
for conversation in conversations
|
|
]
|
|
|
|
text_outputs = self.tokenizer.apply_chat_template(conversations, tools=tools, tokenize=False)
|
|
token_outputs = self.tokenizer.apply_chat_template(conversations, tools=tools, tokenize=True)
|
|
|
|
self.assertEqual(len(text_outputs), len(token_outputs))
|
|
self.assertEqual(len(text_outputs), len(expected_tokenized))
|
|
for text, token, expected in zip(text_outputs, token_outputs, expected_tokenized):
|
|
self.assertEqual(text, expected.text)
|
|
self.assertEqual(token, expected.tokens)
|
|
|
|
with self.assertRaises(
|
|
ValueError,
|
|
msg="Kwargs [unk_args] are not supported by `MistralCommonTokenizer.batch_apply_chat_template`.",
|
|
):
|
|
self.tokenizer.apply_chat_template(conversations, tools=tools, tokenize=True, unk_args="")
|
|
|
|
def test_batch_apply_images(self):
|
|
conversations = [
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": IMG_URL},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "image",
|
|
"url": IMG_URL,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{"type": "image", "base64": IMG_BASE_64},
|
|
],
|
|
},
|
|
],
|
|
]
|
|
|
|
ref_conversation = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What is this?"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": IMG_URL},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
expected_tokenized = self.ref_tokenizer.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(ref_conversation)
|
|
)
|
|
|
|
output = self.tokenizer.apply_chat_template(conversations, tokenize=True)
|
|
self.assertEqual(output, [expected_tokenized.tokens] * 3)
|
|
|
|
output = self.tokenizer.apply_chat_template(conversations, tokenize=True, return_dict=True)
|
|
self.assertEqual(output["input_ids"], [expected_tokenized.tokens] * 3)
|
|
self.assertEqual(len(output["pixel_values"]), len(expected_tokenized.images) * 3)
|
|
for o, e in zip(output["pixel_values"], [expected_tokenized.images] * 3):
|
|
self.assertTrue(np.allclose(o, e))
|
|
|
|
output = self.tokenizer.apply_chat_template(
|
|
conversations, tokenize=True, return_dict=True, return_tensors="pt"
|
|
)
|
|
self.assertEqual(output["input_ids"].tolist(), [expected_tokenized.tokens] * 3)
|
|
self.assertEqual(output["input_ids"].shape[0], len(expected_tokenized.images) * 3)
|
|
self.assertTrue(torch.allclose(output["pixel_values"], torch.tensor([expected_tokenized.images] * 3)))
|
|
|
|
output = self.tokenizer.apply_chat_template(
|
|
conversations, tokenize=True, return_dict=True, return_tensors="np"
|
|
)
|
|
self.assertEqual(output["input_ids"].tolist(), [expected_tokenized.tokens] * 3)
|
|
self.assertTrue(np.allclose(output["pixel_values"], np.array([expected_tokenized.images] * 3)))
|
|
|
|
def test_batch_apply_chat_template_with_continue_final_message(self):
|
|
conversations = [
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can "},
|
|
],
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
{"role": "assistant", "content": "Hello! How can I help you? Ou préférez vous "},
|
|
],
|
|
]
|
|
|
|
# Test 1:
|
|
# with continue_final_message
|
|
expected_tokenized = [
|
|
self.ref_tokenizer.encode_chat_completion(
|
|
ChatCompletionRequest.from_openai(conversation, continue_final_message=True)
|
|
)
|
|
for conversation in conversations
|
|
]
|
|
|
|
token_outputs = self.tokenizer.apply_chat_template(conversations, tokenize=True, continue_final_message=True)
|
|
|
|
for output, expected in zip(token_outputs, expected_tokenized):
|
|
self.assertEqual(output, expected.tokens)
|
|
|
|
# Test 2:
|
|
# without continue_final_message
|
|
with self.assertRaises(InvalidMessageStructureException):
|
|
self.tokenizer.apply_chat_template(
|
|
conversations,
|
|
tokenize=False,
|
|
continue_final_message=False,
|
|
)
|
|
|
|
# Test 3:
|
|
# with continue_final_message and last role is not assistant
|
|
with self.assertRaises(InvalidMessageStructureException):
|
|
self.tokenizer.apply_chat_template(
|
|
conversation=[
|
|
[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hi!"},
|
|
]
|
|
],
|
|
tokenize=True,
|
|
continue_final_message=True,
|
|
)
|
|
|
|
def test_batch_apply_chat_template_with_truncation(
|
|
self,
|
|
):
|
|
# Test 1:
|
|
# with truncation
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, truncation=True, max_length=20
|
|
)
|
|
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(output, expected.tokens[:20])
|
|
|
|
# Test 2:
|
|
# without truncation
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, truncation=False, max_length=20
|
|
)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(output, expected.tokens)
|
|
|
|
# Test 3:
|
|
# assert truncation is boolean
|
|
with self.assertRaises(ValueError):
|
|
self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, truncation=TruncationStrategy.LONGEST_FIRST, max_length=20
|
|
)
|
|
|
|
def test_batch_apply_chat_template_with_padding(
|
|
self,
|
|
):
|
|
for padding in [True, "max_length", PaddingStrategy.LONGEST, PaddingStrategy.MAX_LENGTH]:
|
|
if padding == PaddingStrategy.MAX_LENGTH:
|
|
# No padding if no max length is provided
|
|
token_outputs = self.tokenizer.apply_chat_template(self.fixture_conversations, padding=padding)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(output, expected.tokens)
|
|
|
|
max_length = 20 if padding == PaddingStrategy.MAX_LENGTH else None
|
|
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, padding=padding, max_length=max_length
|
|
)
|
|
|
|
if padding != PaddingStrategy.MAX_LENGTH:
|
|
longest = max(len(tokenized.tokens) for tokenized in self.tokenized_fixture_conversations)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(
|
|
output,
|
|
[self.tokenizer.pad_token_id] * (longest - len(expected.tokens)) + expected.tokens,
|
|
)
|
|
else:
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
if len(expected.tokens) < max_length:
|
|
self.assertEqual(
|
|
output,
|
|
[self.tokenizer.pad_token_id] * (20 - len(expected.tokens)) + expected.tokens,
|
|
)
|
|
else:
|
|
self.assertEqual(output, expected.tokens)
|
|
|
|
for padding in [False, "do_not_pad", PaddingStrategy.DO_NOT_PAD]:
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, padding=padding
|
|
)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(output, expected.tokens)
|
|
|
|
def test_batch_apply_chat_template_with_padding_and_truncation(
|
|
self,
|
|
):
|
|
max_length = 20
|
|
for padding in [True, "max_length", PaddingStrategy.LONGEST, PaddingStrategy.MAX_LENGTH]:
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, truncation=True, padding=padding, max_length=max_length
|
|
)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(
|
|
output, [self.tokenizer.pad_token_id] * (20 - len(expected.tokens)) + expected.tokens[:20]
|
|
)
|
|
for padding in [False, "do_not_pad", PaddingStrategy.DO_NOT_PAD]:
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, truncation=True, padding=padding, max_length=max_length
|
|
)
|
|
self.assertEqual(len(token_outputs), len(self.tokenized_fixture_conversations))
|
|
for output, expected in zip(token_outputs, self.tokenized_fixture_conversations):
|
|
self.assertEqual(output, expected.tokens[:20])
|
|
|
|
def test_batch_apply_chat_template_return_tensors(self):
|
|
# Test 1:
|
|
# with tokenize
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=True, return_tensors="pt", padding=True
|
|
)
|
|
self.assertIsInstance(token_outputs, torch.Tensor)
|
|
self.assertEqual(
|
|
token_outputs.shape,
|
|
(len(self.fixture_conversations), max(len(t.tokens) for t in self.tokenized_fixture_conversations)),
|
|
)
|
|
|
|
# Test 2:
|
|
# without tokenize, should ignore return_tensors
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=False, return_tensors="pt", padding=True
|
|
)
|
|
self.assertEqual(token_outputs, [t.text for t in self.tokenized_fixture_conversations])
|
|
|
|
def test_batch_apply_chat_template_return_dict(self):
|
|
# Test 1:
|
|
# with tokenize
|
|
token_outputs = self.tokenizer.apply_chat_template(self.fixture_conversations, tokenize=True, return_dict=True)
|
|
self.assertIn("input_ids", token_outputs)
|
|
self.assertIn("attention_mask", token_outputs)
|
|
self.assertEqual(token_outputs["input_ids"], [t.tokens for t in self.tokenized_fixture_conversations])
|
|
self.assertEqual(
|
|
token_outputs["attention_mask"], [[1] * len(t.tokens) for t in self.tokenized_fixture_conversations]
|
|
)
|
|
|
|
# Test 2:
|
|
# without tokenize, should ignore return_dict
|
|
token_outputs = self.tokenizer.apply_chat_template(
|
|
self.fixture_conversations, tokenize=False, return_dict=True
|
|
)
|
|
self.assertNotIsInstance(token_outputs, dict)
|
|
self.assertEqual(token_outputs, [t.text for t in self.tokenized_fixture_conversations])
|
|
|
|
def test_call(self):
|
|
# Test 1:
|
|
# default case
|
|
text = "Hello world!"
|
|
expected_tokens = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(text, bos=True, eos=True)
|
|
tokens = self.tokenizer(text)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens))
|
|
|
|
# Test 2:
|
|
# return_attention_mask=False
|
|
tokens = self.tokenizer(text, return_attention_mask=False)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertNotIn("attention_mask", tokens)
|
|
|
|
# Test 3:
|
|
# return_tensors="pt"
|
|
tokens = self.tokenizer(text, return_tensors="pt")
|
|
self.assertIsInstance(tokens["input_ids"], torch.Tensor)
|
|
self.assertTrue(torch.equal(tokens["input_ids"], torch.Tensor(expected_tokens).unsqueeze(0)))
|
|
self.assertIsInstance(tokens["attention_mask"], torch.Tensor)
|
|
self.assertTrue(torch.equal(tokens["attention_mask"], torch.ones(1, len(expected_tokens))))
|
|
|
|
# Test 4:
|
|
# return_special_tokens_mask=True
|
|
tokens = self.tokenizer(text, return_special_tokens_mask=True)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens))
|
|
self.assertEqual(tokens["special_tokens_mask"], [1] + [0] * (len(expected_tokens) - 2) + [1])
|
|
|
|
# Test 5:
|
|
# add_special_tokens=False
|
|
expected_tokens = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(text, bos=False, eos=False)
|
|
tokens = self.tokenizer(text, add_special_tokens=False, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens))
|
|
self.assertEqual(tokens["special_tokens_mask"], [0] * len(expected_tokens))
|
|
|
|
with self.assertRaises(
|
|
ValueError, msg="Kwargs [wrong_kwarg] are not supported by `MistralCommonTokenizer.__call__`."
|
|
):
|
|
self.tokenizer(text, wrong_kwarg=True)
|
|
|
|
with self.assertRaises(
|
|
ValueError,
|
|
msg="`text_pair`, `text_target` and `text_pair_target` are not supported by `MistralCommonTokenizer`.",
|
|
):
|
|
self.tokenizer(text, text_pair="Hello world!")
|
|
with self.assertRaises(
|
|
ValueError,
|
|
msg="`text_pair`, `text_target` and `text_pair_target` are not supported by `MistralCommonTokenizer`.",
|
|
):
|
|
self.tokenizer(text, text_target="Hello world!")
|
|
with self.assertRaises(
|
|
ValueError,
|
|
msg="`text_pair`, `text_target` and `text_pair_target` are not supported by `MistralCommonTokenizer`.",
|
|
):
|
|
self.tokenizer(text, text_pair_target="Hello world!")
|
|
|
|
def test_call_with_truncation(self):
|
|
# Test 1:
|
|
# truncation=True or "longest_first" or TruncationStrategy.LONGEST_FIRST
|
|
text = "Hello world!" * 10
|
|
expected_tokens = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(text, bos=True, eos=True)
|
|
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
tokens = self.tokenizer(text, truncation=True, max_length=10, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens[:10])
|
|
self.assertEqual(tokens["attention_mask"], [1] * 10)
|
|
self.assertEqual(tokens["special_tokens_mask"], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
|
|
# Test 2:
|
|
# truncation=False
|
|
for truncation in [False, "do_not_truncate", TruncationStrategy.DO_NOT_TRUNCATE]:
|
|
tokens = self.tokenizer(text, truncation=truncation, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens))
|
|
self.assertEqual(tokens["special_tokens_mask"], [1] + [0] * (len(expected_tokens) - 2) + [1])
|
|
|
|
# Test 3:
|
|
# truncation=True or "longest_first" or TruncationStrategy.LONGEST_FIRST with return_overflowing_tokens=True and stride
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
for stride in [0, 2]:
|
|
tokens = self.tokenizer(
|
|
text,
|
|
truncation=truncation,
|
|
max_length=10,
|
|
return_overflowing_tokens=True,
|
|
return_special_tokens_mask=True,
|
|
stride=stride,
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens[:10])
|
|
self.assertEqual(tokens["attention_mask"], [1] * 10)
|
|
self.assertEqual(tokens["special_tokens_mask"], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
self.assertEqual(tokens["overflowing_tokens"], expected_tokens[10 - stride :])
|
|
self.assertEqual(tokens["num_truncated_tokens"], len(expected_tokens) - 10)
|
|
|
|
# Test 4:
|
|
# truncation="only_first" or TruncationStrategy.ONLY_FIRST or "only_second" or TruncationStrategy.ONLY_SECOND
|
|
# should raise an error
|
|
for truncation in ["only_first", TruncationStrategy.ONLY_FIRST, "only_second", TruncationStrategy.ONLY_SECOND]:
|
|
with self.assertRaises(
|
|
ValueError,
|
|
msg="Truncation strategy `only_first` and `only_second` are not supported by `MistralCommonTokenizer`.",
|
|
):
|
|
self.tokenizer(text, truncation=truncation)
|
|
|
|
def test_call_with_padding(self):
|
|
text = "Hello world!"
|
|
expected_tokens = self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(text, bos=True, eos=True)
|
|
|
|
# Test 1:
|
|
# padding=False or padding=True or "do_not_pad" or PaddingStrategy.DO_NOT_PAD or padding="longest" or PaddingStrategy.LONGEST
|
|
for padding in [False, True, "do_not_pad", PaddingStrategy.DO_NOT_PAD, "longest", PaddingStrategy.LONGEST]:
|
|
tokens = self.tokenizer(text, padding=padding, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens))
|
|
self.assertEqual(tokens["special_tokens_mask"], [1] + [0] * (len(expected_tokens) - 2) + [1])
|
|
|
|
# Test 2:
|
|
# padding="max_length" or PaddingStrategy.MAX_LENGTH
|
|
for padding in ["max_length", PaddingStrategy.MAX_LENGTH]:
|
|
tokens = self.tokenizer(text, padding=padding, max_length=20, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = 20 - len(expected_tokens)
|
|
self.assertEqual(tokens["input_ids"], num_padding * [self.tokenizer.pad_token_id] + expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], num_padding * [0] + [1] * len(expected_tokens))
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"], num_padding * [1] + [1] + [0] * (len(expected_tokens) - 2) + [1]
|
|
)
|
|
|
|
# Test 3:
|
|
# pad_to_multiple_of
|
|
tokens = self.tokenizer(
|
|
text, padding=True, max_length=20, pad_to_multiple_of=16, return_special_tokens_mask=True
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = 16 - len(expected_tokens)
|
|
self.assertEqual(tokens["input_ids"], num_padding * [self.tokenizer.pad_token_id] + expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], num_padding * [0] + [1] * len(expected_tokens))
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"], num_padding * [1] + [1] + [0] * (len(expected_tokens) - 2) + [1]
|
|
)
|
|
|
|
# Test 4:
|
|
# padding="max_length" and padding_side="right"
|
|
tokens = self.tokenizer(
|
|
text, padding="max_length", max_length=20, padding_side="right", return_special_tokens_mask=True
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = 20 - len(expected_tokens)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens + num_padding * [self.tokenizer.pad_token_id])
|
|
self.assertEqual(tokens["attention_mask"], [1] * len(expected_tokens) + num_padding * [0])
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"], [1] + [0] * (len(expected_tokens) - 2) + [1] + num_padding * [1]
|
|
)
|
|
|
|
def test_batch_call(self):
|
|
# Test 1:
|
|
# default case
|
|
text = ["Hello world!", "Hello world! Longer"]
|
|
expected_tokens = [self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(t, bos=True, eos=True) for t in text]
|
|
tokens = self.tokenizer(text)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [[1] * len(t) for t in expected_tokens])
|
|
|
|
# Test 2:
|
|
# return_attention_mask=False
|
|
tokens = self.tokenizer(text, return_attention_mask=False)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertNotIn("attention_mask", tokens)
|
|
|
|
# Test 3:
|
|
# return_tensors="pt"
|
|
tokens = self.tokenizer(text, return_tensors="pt", padding="longest", return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens["input_ids"], torch.Tensor)
|
|
self.assertEqual(tokens["input_ids"].shape, torch.Size([2, len(expected_tokens[1])]))
|
|
self.assertTrue(
|
|
torch.equal(
|
|
tokens["input_ids"][0],
|
|
torch.Tensor(
|
|
(len(expected_tokens[1]) - len(expected_tokens[0]))
|
|
* [self.ref_tokenizer.instruct_tokenizer.tokenizer.pad_id]
|
|
+ expected_tokens[0]
|
|
),
|
|
)
|
|
)
|
|
self.assertIsInstance(tokens["attention_mask"], torch.Tensor)
|
|
self.assertEqual(tokens["attention_mask"].shape, torch.Size([2, len(expected_tokens[1])]))
|
|
self.assertTrue(
|
|
torch.equal(
|
|
tokens["attention_mask"][0],
|
|
torch.Tensor(
|
|
[0] * (len(expected_tokens[1]) - len(expected_tokens[0])) + [1] * len(expected_tokens[0])
|
|
),
|
|
)
|
|
)
|
|
self.assertTrue(torch.equal(tokens["attention_mask"][1], torch.Tensor([1] * len(expected_tokens[1]))))
|
|
self.assertIsInstance(tokens["special_tokens_mask"], torch.Tensor)
|
|
self.assertEqual(tokens["special_tokens_mask"].shape, torch.Size([2, len(expected_tokens[1])]))
|
|
self.assertTrue(
|
|
torch.equal(
|
|
tokens["special_tokens_mask"][0],
|
|
torch.Tensor(
|
|
(len(expected_tokens[1]) - len(expected_tokens[0])) * [1]
|
|
+ [1]
|
|
+ [0] * (len(expected_tokens[0]) - 2)
|
|
+ [1]
|
|
),
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
torch.equal(
|
|
tokens["special_tokens_mask"][1], torch.Tensor([1] + [0] * (len(expected_tokens[1]) - 2) + [1])
|
|
)
|
|
)
|
|
|
|
# Test 4:
|
|
# add_special_tokens=False
|
|
expected_tokens = [
|
|
self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(t, bos=False, eos=False) for t in text
|
|
]
|
|
tokens = self.tokenizer(text, add_special_tokens=False, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [[1] * len(t) for t in expected_tokens])
|
|
self.assertEqual(tokens["special_tokens_mask"], [[0] * len(t) for t in expected_tokens])
|
|
|
|
def test_batch_call_with_truncation(self):
|
|
# Test 1:
|
|
# truncation=True
|
|
text = ["Hello world!", "Hello world! Longer" * 10]
|
|
expected_tokens = [self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(t, bos=True, eos=True) for t in text]
|
|
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
tokens = self.tokenizer(text, truncation=True, max_length=10, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], [expected_tokens[0][:10], expected_tokens[1][:10]])
|
|
self.assertEqual(tokens["attention_mask"], [[1] * min(len(t), 10) for t in expected_tokens])
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[[1 if id in self.ref_special_ids else 0 for id in ids[:10]] for ids in expected_tokens],
|
|
)
|
|
|
|
# Test 2:
|
|
# truncation=False
|
|
for truncation in [False, "do_not_truncate", TruncationStrategy.DO_NOT_TRUNCATE]:
|
|
tokens = self.tokenizer(text, truncation=truncation, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [[1] * len(t) for t in expected_tokens])
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[[1] + [0] * (len(t) - 2) + [1] for t in expected_tokens],
|
|
)
|
|
|
|
# Test 3:
|
|
# truncation=True or "longest_first" or TruncationStrategy.LONGEST_FIRST with return_overflowing_tokens=True and stride
|
|
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
for stride in [0, 2]:
|
|
tokens = self.tokenizer(
|
|
text,
|
|
truncation=truncation,
|
|
max_length=10,
|
|
return_overflowing_tokens=True,
|
|
return_special_tokens_mask=True,
|
|
stride=stride,
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], [expected_tokens[0][:10], expected_tokens[1][:10]])
|
|
self.assertEqual(tokens["attention_mask"], [[1] * min(len(t), 10) for t in expected_tokens])
|
|
self.assertEqual(
|
|
tokens["overflowing_tokens"],
|
|
[expected_tokens[0][10 - stride :], expected_tokens[1][10 - stride :]],
|
|
)
|
|
self.assertEqual(
|
|
tokens["num_truncated_tokens"], [len(expected_tokens[0]) - 10, len(expected_tokens[1]) - 10]
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[[1 if id in self.ref_special_ids else 0 for id in ids[:10]] for ids in expected_tokens],
|
|
)
|
|
|
|
def test_batch_call_with_padding(self):
|
|
# Test 1:
|
|
# padding=False or padding=True or "do_not_pad" or PaddingStrategy.DO_NOT_PAD or padding="longest" or PaddingStrategy.LONGEST
|
|
text = ["Hello world!", "Hello world! Longer"]
|
|
expected_tokens = [self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(t, bos=True, eos=True) for t in text]
|
|
for padding in [False, "do_not_pad", PaddingStrategy.DO_NOT_PAD]:
|
|
tokens = self.tokenizer(text, padding=padding, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(tokens["input_ids"], expected_tokens)
|
|
self.assertEqual(tokens["attention_mask"], [[1] * len(t) for t in expected_tokens])
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[[1] + [0] * (len(t) - 2) + [1] for t in expected_tokens],
|
|
)
|
|
|
|
# Test 2:
|
|
# padding="max_length" or PaddingStrategy.MAX_LENGTH
|
|
for padding in ["max_length", PaddingStrategy.MAX_LENGTH]:
|
|
tokens = self.tokenizer(text, padding=padding, max_length=20, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = [20 - len(t) for t in expected_tokens]
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[
|
|
num_padding[0] * [self.tokenizer.pad_token_id] + expected_tokens[0],
|
|
num_padding[1] * [self.tokenizer.pad_token_id] + expected_tokens[1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[
|
|
num_padding[0] * [0] + [1] * len(expected_tokens[0]),
|
|
num_padding[1] * [0] + [1] * len(expected_tokens[1]),
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
num_padding[0] * [1] + [1] + [0] * (len(expected_tokens[0]) - 2) + [1],
|
|
num_padding[1] * [1] + [1] + [0] * (len(expected_tokens[1]) - 2) + [1],
|
|
],
|
|
)
|
|
|
|
# Test 3:
|
|
# padding=True or "longest" or PaddingStrategy.LONGEST
|
|
for padding in [True, "longest", PaddingStrategy.LONGEST]:
|
|
tokens = self.tokenizer(text, padding=padding, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = [len(expected_tokens[1]) - len(t) for t in expected_tokens]
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[
|
|
num_padding[0] * [self.tokenizer.pad_token_id] + expected_tokens[0],
|
|
num_padding[1] * [self.tokenizer.pad_token_id] + expected_tokens[1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[
|
|
num_padding[0] * [0] + [1] * len(expected_tokens[0]),
|
|
num_padding[1] * [0] + [1] * len(expected_tokens[1]),
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
num_padding[0] * [1] + [1] + [0] * (len(expected_tokens[0]) - 2) + [1],
|
|
num_padding[1] * [1] + [1] + [0] * (len(expected_tokens[1]) - 2) + [1],
|
|
],
|
|
)
|
|
|
|
# Test 4:
|
|
# pad_to_multiple_of
|
|
tokens = self.tokenizer(
|
|
text, padding=True, max_length=32, pad_to_multiple_of=16, return_special_tokens_mask=True
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = [16 - len(t) for t in expected_tokens]
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[
|
|
num_padding[0] * [self.tokenizer.pad_token_id] + expected_tokens[0],
|
|
num_padding[1] * [self.tokenizer.pad_token_id] + expected_tokens[1],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[
|
|
num_padding[0] * [0] + [1] * len(expected_tokens[0]),
|
|
num_padding[1] * [0] + [1] * len(expected_tokens[1]),
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
num_padding[0] * [1] + [1] + [0] * (len(expected_tokens[0]) - 2) + [1],
|
|
num_padding[1] * [1] + [1] + [0] * (len(expected_tokens[1]) - 2) + [1],
|
|
],
|
|
)
|
|
|
|
# Test 5:
|
|
# padding="max_length" or PaddingStrategy.MAX_LENGTH and padding_side="right"
|
|
for padding in ["max_length", PaddingStrategy.MAX_LENGTH]:
|
|
tokens = self.tokenizer(
|
|
text, padding=padding, max_length=20, padding_side="right", return_special_tokens_mask=True
|
|
)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = [20 - len(t) for t in expected_tokens]
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[
|
|
expected_tokens[0] + num_padding[0] * [self.tokenizer.pad_token_id],
|
|
expected_tokens[1] + num_padding[1] * [self.tokenizer.pad_token_id],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[
|
|
[1] * len(expected_tokens[0]) + num_padding[0] * [0],
|
|
[1] * len(expected_tokens[1]) + num_padding[1] * [0],
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
[1] + [0] * (len(expected_tokens[0]) - 2) + [1] + num_padding[0] * [1],
|
|
[1] + [0] * (len(expected_tokens[1]) - 2) + [1] + num_padding[1] * [1],
|
|
],
|
|
)
|
|
|
|
def test_batch_call_with_padding_and_truncation(self):
|
|
# Test 1:
|
|
# padding=True or "longest" or PaddingStrategy.LONGEST or "max_length" or PaddingStragy.MAX_LENGTH
|
|
# and truncation=True or "longest_first" or TruncationStrategy.LONGEST_FIRST
|
|
# and max_length
|
|
text = ["Hello world!", "Hello world! Longer" * 10]
|
|
expected_tokens = [self.ref_tokenizer.instruct_tokenizer.tokenizer.encode(t, bos=True, eos=True) for t in text]
|
|
for padding in [True, "longest", PaddingStrategy.LONGEST, "max_length", PaddingStrategy.MAX_LENGTH]:
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
tokens = self.tokenizer(
|
|
text, padding=padding, truncation=truncation, max_length=10, return_special_tokens_mask=True
|
|
)
|
|
num_padding = [max(0, 10 - len(t)) for t in expected_tokens]
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[num_padding[i] * [self.tokenizer.pad_token_id] + t[:10] for i, t in enumerate(expected_tokens)],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[num_padding[i] * [0] + [1] * min(len(t), 10) for i, t in enumerate(expected_tokens)],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
num_padding[i] * [1] + [1 if id in self.ref_special_ids else 0 for id in ids[:10]]
|
|
for i, ids in enumerate(expected_tokens)
|
|
],
|
|
)
|
|
|
|
# Test 2:
|
|
# padding=True or "longest" or PaddingStrategy.LONGEST and truncation=True or "longest_first" or TruncationStrategy.LONGEST_FIRST
|
|
# and no max_length
|
|
for padding in ["longest", PaddingStrategy.LONGEST]:
|
|
for truncation in [True, "longest_first", TruncationStrategy.LONGEST_FIRST]:
|
|
tokens = self.tokenizer(text, padding=padding, truncation=truncation, return_special_tokens_mask=True)
|
|
self.assertIsInstance(tokens, BatchEncoding)
|
|
num_padding = [max(len(t) for t in expected_tokens) - len(t) for t in expected_tokens]
|
|
self.assertEqual(
|
|
tokens["input_ids"],
|
|
[num_padding[i] * [self.tokenizer.pad_token_id] + t for i, t in enumerate(expected_tokens)],
|
|
)
|
|
self.assertEqual(
|
|
tokens["attention_mask"],
|
|
[num_padding[i] * [0] + [1] * len(t) for i, t in enumerate(expected_tokens)],
|
|
)
|
|
self.assertEqual(
|
|
tokens["special_tokens_mask"],
|
|
[
|
|
num_padding[i] * [1] + [1 if id in self.ref_special_ids else 0 for id in ids]
|
|
for i, ids in enumerate(expected_tokens)
|
|
],
|
|
)
|