Some checks failed
Self-hosted runner (nightly-past-ci-caller) / Get number (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.11 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.10 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.9 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.8 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.7 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.6 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.5 (push) Has been cancelled
Self-hosted runner (benchmark) / Benchmark (aws-g5-4xlarge-cache) (push) Has been cancelled
Build documentation / build (push) Has been cancelled
Build documentation / build_other_lang (push) Has been cancelled
CodeQL Security Analysis / CodeQL Analysis (push) Has been cancelled
New model PR merged notification / Notify new model (push) Has been cancelled
PR CI / pr-ci (push) Has been cancelled
Slow tests on important models (on Push - A10) / Get all modified files (push) Has been cancelled
Secret Leaks / trufflehog (push) Has been cancelled
Update Transformers metadata / build_and_package (push) Has been cancelled
Slow tests on important models (on Push - A10) / Model CI (push) Has been cancelled
Check Tiny Models / Check tiny models (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Model CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Pipeline CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Example CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / DeepSpeed CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Trainer/FSDP CI (push) Has been cancelled
Nvidia CI - Flash Attn / Setup (push) Has been cancelled
Nvidia CI - Flash Attn / Model CI (push) Has been cancelled
Nvidia CI / Setup (push) Has been cancelled
Nvidia CI / Model CI (push) Has been cancelled
Nvidia CI / Torch pipeline CI (push) Has been cancelled
Nvidia CI / Example CI (push) Has been cancelled
Nvidia CI / Trainer/FSDP CI (push) Has been cancelled
Nvidia CI / DeepSpeed CI (push) Has been cancelled
Nvidia CI / Quantization CI (push) Has been cancelled
Nvidia CI / Kernels CI (push) Has been cancelled
Doctests / Setup (push) Has been cancelled
Doctests / Call doctest jobs (push) Has been cancelled
Doctests / Send results to webhook (push) Has been cancelled
Extras Smoke Test / Get supported Python versions (push) Has been cancelled
Extras Smoke Test / Test extras on Python ${{ matrix.python-version }} (push) Has been cancelled
Extras Smoke Test / Check Slack token availability (push) Has been cancelled
Extras Smoke Test / Notify failures to Slack (push) Has been cancelled
Self-hosted runner (AMD scheduled CI caller) / Trigger Scheduled AMD CI (push) Has been cancelled
Stale Bot / Close Stale Issues (push) Has been cancelled
247 lines
9.9 KiB
Python
247 lines
9.9 KiB
Python
# Copyright 2024 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.
|
|
"""Testing suite for the IBM Granite Speech model."""
|
|
|
|
import unittest
|
|
|
|
import pytest
|
|
|
|
from transformers import (
|
|
AutoProcessor,
|
|
GraniteConfig,
|
|
GraniteSpeechConfig,
|
|
GraniteSpeechEncoderConfig,
|
|
GraniteSpeechForConditionalGeneration,
|
|
GraniteSpeechModel,
|
|
)
|
|
from transformers.testing_utils import (
|
|
cleanup,
|
|
require_torch,
|
|
slow,
|
|
torch_device,
|
|
)
|
|
from transformers.utils import (
|
|
is_datasets_available,
|
|
is_peft_available,
|
|
is_torch_available,
|
|
)
|
|
|
|
from ...alm_tester import ALMModelTest, ALMModelTester
|
|
from ...test_modeling_common import floats_tensor
|
|
|
|
|
|
if is_torch_available():
|
|
import torch
|
|
|
|
if is_datasets_available():
|
|
from datasets import load_dataset
|
|
|
|
|
|
class GraniteSpeechModelTester(ALMModelTester):
|
|
config_class = GraniteSpeechConfig
|
|
base_model_class = GraniteSpeechModel
|
|
conditional_generation_class = GraniteSpeechForConditionalGeneration
|
|
text_config_class = GraniteConfig
|
|
audio_config_class = GraniteSpeechEncoderConfig
|
|
audio_config_key = "encoder_config"
|
|
|
|
def __init__(self, parent, **kwargs):
|
|
kwargs["projector_config"] = {
|
|
"model_type": "blip_2_qformer",
|
|
"hidden_size": 32,
|
|
"num_hidden_layers": 2,
|
|
"num_attention_heads": 4,
|
|
"intermediate_size": 256,
|
|
"encoder_hidden_size": 32,
|
|
}
|
|
|
|
super().__init__(parent, **kwargs)
|
|
|
|
def create_audio_features(self):
|
|
# GraniteSpeech expects [B, seq_len, features] (time-first), unlike the standard [B, features, seq_len]
|
|
return floats_tensor([self.batch_size, self.feat_seq_length, self.num_mel_bins])
|
|
|
|
def get_audio_embeds_mask(self, audio_mask):
|
|
# Projector: ceil(feat_seq_length / window_size) * (window_size // downsample_rate) tokens per sample.
|
|
import math
|
|
|
|
config = self.get_config()
|
|
nblocks = math.ceil(self.feat_seq_length / config.window_size)
|
|
num_audio_tokens = nblocks * (config.window_size // config.downsample_rate)
|
|
return torch.ones([self.batch_size, num_audio_tokens], dtype=torch.long).to(torch_device)
|
|
|
|
def create_attention_mask(self, input_ids):
|
|
return torch.ones(input_ids.shape, dtype=torch.long).to(torch_device)
|
|
|
|
def create_and_check_granite_speech_model_fp16_forward(self, config, input_ids, input_features, attention_mask):
|
|
model = GraniteSpeechForConditionalGeneration(config=config)
|
|
model.to(torch_device)
|
|
model.half()
|
|
model.eval()
|
|
logits = model(
|
|
input_ids=input_ids,
|
|
attention_mask=attention_mask,
|
|
input_features=input_features,
|
|
return_dict=True,
|
|
)["logits"]
|
|
self.parent.assertFalse(torch.isnan(logits).any().item())
|
|
|
|
def create_and_check_granite_speech_model_fp16_autocast_forward(
|
|
self,
|
|
config,
|
|
input_ids,
|
|
input_features,
|
|
attention_mask,
|
|
):
|
|
config.dtype = torch.float16
|
|
model = GraniteSpeechForConditionalGeneration(config=config)
|
|
model.to(torch_device)
|
|
model.eval()
|
|
with torch.autocast(device_type="cuda", dtype=torch.float16):
|
|
logits = model(
|
|
input_ids=input_ids,
|
|
attention_mask=attention_mask,
|
|
input_features=input_features.to(torch.bfloat16),
|
|
return_dict=True,
|
|
)["logits"]
|
|
self.parent.assertFalse(torch.isnan(logits).any().item())
|
|
|
|
|
|
@require_torch
|
|
class GraniteSpeechForConditionalGenerationModelTest(ALMModelTest, unittest.TestCase):
|
|
"""
|
|
Model tester for `GraniteSpeechForConditionalGeneration`.
|
|
"""
|
|
|
|
model_tester_class = GraniteSpeechModelTester
|
|
pipeline_model_mapping = {"any-to-any": GraniteSpeechForConditionalGeneration} if is_torch_available() else {}
|
|
|
|
@unittest.skip(
|
|
reason="This test does not apply to GraniteSpeech since inputs_embeds corresponding to audio tokens are replaced when input features are provided."
|
|
)
|
|
def test_inputs_embeds_matches_input_ids(self):
|
|
pass
|
|
|
|
def test_inputs_embeds(self):
|
|
# Overwrite inputs_embeds tests because we need to delete "input_features" for the audio model
|
|
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
|
|
|
for model_class in self.all_model_classes:
|
|
model = model_class(config)
|
|
model.to(torch_device)
|
|
model.eval()
|
|
|
|
inputs = self._prepare_for_class(inputs_dict, model_class)
|
|
|
|
input_ids = inputs["input_ids"]
|
|
del inputs["input_ids"]
|
|
del inputs["input_features"]
|
|
|
|
wte = model.get_input_embeddings()
|
|
inputs["inputs_embeds"] = wte(input_ids)
|
|
|
|
with torch.no_grad():
|
|
model(**inputs)
|
|
|
|
@pytest.mark.generate
|
|
@slow
|
|
@unittest.skip(reason="Granite Speech doesn't support SDPA for all backbones")
|
|
def test_eager_matches_sdpa_generate(self):
|
|
pass
|
|
|
|
|
|
class GraniteSpeechForConditionalGenerationIntegrationTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.model_path = "ibm-granite/granite-speech-3.3-2b"
|
|
self.processor = AutoProcessor.from_pretrained(self.model_path)
|
|
self.prompt = self._get_prompt(self.processor.tokenizer)
|
|
|
|
def tearDown(self):
|
|
cleanup(torch_device, gc_collect=True)
|
|
|
|
def _get_prompt(self, tokenizer):
|
|
chat = [
|
|
{
|
|
"role": "system",
|
|
"content": "Knowledge Cutoff Date: April 2024.\nToday's Date: December 19, 2024.\nYou are Granite, developed by IBM. You are a helpful AI assistant",
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": "<|audio|>can you transcribe the speech into a written format?",
|
|
},
|
|
]
|
|
return tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)
|
|
|
|
def _load_datasamples(self, num_samples):
|
|
ds = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
|
|
# automatic decoding with librispeech
|
|
speech_samples = ds.sort("id")[:num_samples]["audio"]
|
|
|
|
return [x["array"] for x in speech_samples]
|
|
|
|
@slow
|
|
@pytest.mark.skipif(not is_peft_available(), reason="Outputs diverge without lora")
|
|
def test_small_model_integration_test_single(self):
|
|
model = GraniteSpeechForConditionalGeneration.from_pretrained(self.model_path).to(torch_device)
|
|
input_speech = self._load_datasamples(1)
|
|
|
|
# Verify feature sizes; note that the feature mask refers to the size of
|
|
# features that are masked into the LLM, not the output of the processor,
|
|
# which is why we inspect the mask instead of the `num_features` tensor.
|
|
inputs = self.processor(self.prompt, input_speech, return_tensors="pt").to(torch_device)
|
|
|
|
num_computed_features = self.processor.audio_processor._get_num_audio_features(
|
|
[speech_arr.shape[-1] for speech_arr in input_speech],
|
|
)[0]
|
|
num_actual_features = torch.sum(inputs["input_features_mask"]).item()
|
|
assert num_actual_features == num_computed_features
|
|
|
|
# verify generation
|
|
output = model.generate(**inputs, max_new_tokens=32)
|
|
EXPECTED_DECODED_TEXT = "systemKnowledge Cutoff Date: April 2024.\nToday's Date: December 19, 2024.\nYou are Granite, developed by IBM. You are a helpful AI assistant\nusercan you transcribe the speech into a written format?\nassistantmister quilter is the apostle of the middle classes and we are glad to welcome his gospel" # fmt: skip
|
|
|
|
self.assertEqual(
|
|
self.processor.tokenizer.decode(output[0], skip_special_tokens=True),
|
|
EXPECTED_DECODED_TEXT,
|
|
)
|
|
|
|
@slow
|
|
@pytest.mark.skipif(not is_peft_available(), reason="Outputs diverge without lora")
|
|
def test_small_model_integration_test_batch(self):
|
|
model = GraniteSpeechForConditionalGeneration.from_pretrained(self.model_path).to(torch_device)
|
|
input_speech = self._load_datasamples(2)
|
|
prompts = [self.prompt, self.prompt]
|
|
|
|
# Verify feature sizes & padding
|
|
inputs = self.processor(prompts, input_speech, return_tensors="pt").to(model.device)
|
|
num_computed_features = self.processor.audio_processor._get_num_audio_features(
|
|
[speech_arr.shape[-1] for speech_arr in input_speech],
|
|
)
|
|
num_actual_features = torch.sum(inputs["input_features_mask"], dim=-1)
|
|
for e_feats, a_feats in zip(num_computed_features, num_actual_features):
|
|
assert e_feats == a_feats.item()
|
|
|
|
# verify generation
|
|
output = model.generate(**inputs, max_new_tokens=32)
|
|
|
|
EXPECTED_DECODED_TEXT = [
|
|
"systemKnowledge Cutoff Date: April 2024.\nToday's Date: December 19, 2024.\nYou are Granite, developed by IBM. You are a helpful AI assistant\nusercan you transcribe the speech into a written format?\nassistantmister quilter is the apostle of the middle classes and we are glad to welcome his gospel",
|
|
"systemKnowledge Cutoff Date: April 2024.\nToday's Date: December 19, 2024.\nYou are Granite, developed by IBM. You are a helpful AI assistant\nusercan you transcribe the speech into a written format?\nassistantnor is mister quilter's manner less interesting than his matter"
|
|
] # fmt: skip
|
|
|
|
self.assertEqual(
|
|
self.processor.tokenizer.batch_decode(output, skip_special_tokens=True),
|
|
EXPECTED_DECODED_TEXT,
|
|
)
|