Add Qwen2-Audio (#32137)
* add qwen2audio * Update check_repo.py * fix style * fix test * fix style * add model size * Qwen2AudioEncoderModel->Qwen2AudioEncoder; add copy info * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * switch the attention_mask and the feature_attention_mask * add to PRIVATE_MODELS in check_repo.py; add to MODEL_NAMES_TO_IGNORE in check_table.py * fix initialization * update chat_template * fix consistency issue after copy * add docstrings to _merge_input_ids_with_audio_features * add copied from to prepare_inputs_for_generation * add more details to docs * rm comment * add init_std * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * Update src/transformers/models/qwen2_audio/modeling_qwen2_audio.py Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> * update * Update docs/source/en/model_doc/qwen2_audio.md Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com> * update tests * rm ignore_index * update processor * rm ffmpeg_read * Update tests/models/qwen2_audio/test_modeling_qwen2_audio.py Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com> * Update docs/source/en/model_doc/qwen2_audio.md Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com> * Update docs/source/en/model_doc/qwen2_audio.md Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com> * Update docs/source/en/model_doc/qwen2_audio.md Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com> * update * typo * [run_slow] qwen2_audio * [run_slow] qwen2_audio * [run_slow] qwen2_audio * fix quality * [run_slow] qwen2_audio * [run_slow] qwen2_audio * [run_slow] qwen2_audio * add official model --------- Co-authored-by: Yoach Lacombe <52246514+ylacombe@users.noreply.github.com> Co-authored-by: amyeroberts <22614925+amyeroberts@users.noreply.github.com>
This commit is contained in:
@@ -506,6 +506,8 @@
|
||||
title: QDQBert
|
||||
- local: model_doc/qwen2
|
||||
title: Qwen2
|
||||
- local: model_doc/qwen2_audio
|
||||
title: Qwen2Audio
|
||||
- local: model_doc/qwen2_moe
|
||||
title: Qwen2MoE
|
||||
- local: model_doc/rag
|
||||
|
||||
@@ -256,6 +256,7 @@ Flax), PyTorch, and/or TensorFlow.
|
||||
| [PVTv2](model_doc/pvt_v2) | ✅ | ❌ | ❌ |
|
||||
| [QDQBert](model_doc/qdqbert) | ✅ | ❌ | ❌ |
|
||||
| [Qwen2](model_doc/qwen2) | ✅ | ❌ | ❌ |
|
||||
| [Qwen2Audio](model_doc/qwen2_audio) | ✅ | ❌ | ❌ |
|
||||
| [Qwen2MoE](model_doc/qwen2_moe) | ✅ | ❌ | ❌ |
|
||||
| [RAG](model_doc/rag) | ✅ | ✅ | ❌ |
|
||||
| [REALM](model_doc/realm) | ✅ | ❌ | ❌ |
|
||||
|
||||
198
docs/source/en/model_doc/qwen2_audio.md
Normal file
198
docs/source/en/model_doc/qwen2_audio.md
Normal file
@@ -0,0 +1,198 @@
|
||||
<!--Copyright 2024 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Qwen2Audio
|
||||
|
||||
## Overview
|
||||
|
||||
The Qwen2-Audio is the new model series of large audio-language models from the Qwen team. Qwen2-Audio is capable of accepting various audio signal inputs and performing audio analysis or direct textual responses with regard to speech instructions. We introduce two distinct audio interaction modes:
|
||||
|
||||
* voice chat: users can freely engage in voice interactions with Qwen2-Audio without text input
|
||||
* audio analysis: users could provide audio and text instructions for analysis during the interaction
|
||||
|
||||
It was proposed in [Qwen2-Audio Technical Report](https://arxiv.org/abs/2407.10759) by Yunfei Chu, Jin Xu, Qian Yang, Haojie Wei, Xipin Wei, Zhifang Guo, Yichong Leng, Yuanjun Lv, Jinzheng He, Junyang Lin, Chang Zhou, Jingren Zhou.
|
||||
|
||||
The abstract from the paper is the following:
|
||||
|
||||
*We introduce the latest progress of Qwen-Audio, a large-scale audio-language model called Qwen2-Audio, which is capable of accepting various audio signal inputs and performing audio analysis or direct textual responses with regard to speech instructions. In contrast to complex hierarchical tags, we have simplified the pre-training process by utilizing natural language prompts for different data and tasks, and have further expanded the data volume. We have boosted the instruction-following capability of Qwen2-Audio and implemented two distinct audio interaction modes for voice chat and audio analysis. In the voice chat mode, users can freely engage in voice interactions with Qwen2-Audio without text input. In the audio analysis mode, users could provide audio and text instructions for analysis during the interaction. Note that we do not use any system prompts to switch between voice chat and audio analysis modes. Qwen2-Audio is capable of intelligently comprehending the content within audio and following voice commands to respond appropriately. For instance, in an audio segment that simultaneously contains sounds, multi-speaker conversations, and a voice command, Qwen2-Audio can directly understand the command and provide an interpretation and response to the audio. Additionally, DPO has optimized the model's performance in terms of factuality and adherence to desired behavior. According to the evaluation results from AIR-Bench, Qwen2-Audio outperformed previous SOTAs, such as Gemini-1.5-pro, in tests focused on audio-centric instruction-following capabilities. Qwen2-Audio is open-sourced with the aim of fostering the advancement of the multi-modal language community. *
|
||||
|
||||
|
||||
## Usage tips
|
||||
|
||||
`Qwen2-Audio-7B` and `Qwen2-Audio-7B-Instruct` can be found on the [Huggingface Hub](https://huggingface.co/Qwen)
|
||||
|
||||
In the following, we demonstrate how to use `Qwen2-Audio-7B-Instruct` for the inference, supporting both voice chat and audio analysis modes. Note that we have used the ChatML format for dialog, in this demo we show how to leverage `apply_chat_template` for this purpose.
|
||||
|
||||
### Voice Chat Inference
|
||||
In the voice chat mode, users can freely engage in voice interactions with Qwen2-Audio without text input:
|
||||
```python
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
import librosa
|
||||
from transformers import Qwen2AudioForConditionalGeneration, AutoProcessor
|
||||
|
||||
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct", device_map="auto")
|
||||
|
||||
conversation = [
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/guess_age_gender.wav"},
|
||||
]},
|
||||
{"role": "assistant", "content": "Yes, the speaker is female and in her twenties."},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/translate_to_chinese.wav"},
|
||||
]},
|
||||
]
|
||||
text = processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
|
||||
audios = []
|
||||
for message in conversation:
|
||||
if isinstance(message["content"], list):
|
||||
for ele in message["content"]:
|
||||
if ele["type"] == "audio":
|
||||
audios.append(librosa.load(
|
||||
BytesIO(urlopen(ele['audio_url']).read()),
|
||||
sr=processor.feature_extractor.sampling_rate)[0]
|
||||
)
|
||||
|
||||
inputs = processor(text=text, audios=audios, return_tensors="pt", padding=True)
|
||||
inputs.input_ids = inputs.input_ids.to("cuda")
|
||||
|
||||
generate_ids = model.generate(**inputs, max_length=256)
|
||||
generate_ids = generate_ids[:, inputs.input_ids.size(1):]
|
||||
|
||||
response = processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
|
||||
```
|
||||
|
||||
### Audio Analysis Inference
|
||||
In the audio analysis, users could provide both audio and text instructions for analysis:
|
||||
```python
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
import librosa
|
||||
from transformers import Qwen2AudioForConditionalGeneration, AutoProcessor
|
||||
|
||||
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct", device_map="auto")
|
||||
|
||||
conversation = [
|
||||
{'role': 'system', 'content': 'You are a helpful assistant.'},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3"},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
]},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{"role": "user", "content": [
|
||||
{"type": "text", "text": "What can you do when you hear that?"},
|
||||
]},
|
||||
{"role": "assistant", "content": "Stay alert and cautious, and check if anyone is hurt or if there is any damage to property."},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/1272-128104-0000.flac"},
|
||||
{"type": "text", "text": "What does the person say?"},
|
||||
]},
|
||||
]
|
||||
text = processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
|
||||
audios = []
|
||||
for message in conversation:
|
||||
if isinstance(message["content"], list):
|
||||
for ele in message["content"]:
|
||||
if ele["type"] == "audio":
|
||||
audios.append(
|
||||
librosa.load(
|
||||
BytesIO(urlopen(ele['audio_url']).read()),
|
||||
sr=processor.feature_extractor.sampling_rate)[0]
|
||||
)
|
||||
|
||||
inputs = processor(text=text, audios=audios, return_tensors="pt", padding=True)
|
||||
inputs.input_ids = inputs.input_ids.to("cuda")
|
||||
|
||||
generate_ids = model.generate(**inputs, max_length=256)
|
||||
generate_ids = generate_ids[:, inputs.input_ids.size(1):]
|
||||
|
||||
response = processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
|
||||
```
|
||||
|
||||
### Batch Inference
|
||||
We also support batch inference:
|
||||
```python
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
import librosa
|
||||
from transformers import Qwen2AudioForConditionalGeneration, AutoProcessor
|
||||
|
||||
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct", device_map="auto")
|
||||
|
||||
conversation1 = [
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3"},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
]},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/f2641_0_throatclearing.wav"},
|
||||
{"type": "text", "text": "What can you hear?"},
|
||||
]}
|
||||
]
|
||||
|
||||
conversation2 = [
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/1272-128104-0000.flac"},
|
||||
{"type": "text", "text": "What does the person say?"},
|
||||
]},
|
||||
]
|
||||
|
||||
conversations = [conversation1, conversation2]
|
||||
|
||||
text = [processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False) for conversation in conversations]
|
||||
|
||||
audios = []
|
||||
for conversation in conversations:
|
||||
for message in conversation:
|
||||
if isinstance(message["content"], list):
|
||||
for ele in message["content"]:
|
||||
if ele["type"] == "audio":
|
||||
audios.append(
|
||||
librosa.load(
|
||||
BytesIO(urlopen(ele['audio_url']).read()),
|
||||
sr=processor.feature_extractor.sampling_rate)[0]
|
||||
)
|
||||
|
||||
inputs = processor(text=text, audios=audios, return_tensors="pt", padding=True)
|
||||
inputs['input_ids'] = inputs['input_ids'].to("cuda")
|
||||
inputs.input_ids = inputs.input_ids.to("cuda")
|
||||
|
||||
generate_ids = model.generate(**inputs, max_length=256)
|
||||
generate_ids = generate_ids[:, inputs.input_ids.size(1):]
|
||||
|
||||
response = processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)
|
||||
```
|
||||
|
||||
## Qwen2AudioConfig
|
||||
|
||||
[[autodoc]] Qwen2AudioConfig
|
||||
|
||||
## Qwen2AudioConfig
|
||||
|
||||
[[autodoc]] Qwen2AudioEncoderConfig
|
||||
|
||||
## Qwen2AudioProcessor
|
||||
|
||||
[[autodoc]] Qwen2AudioProcessor
|
||||
|
||||
## Qwen2AudioForConditionalGeneration
|
||||
|
||||
[[autodoc]] Qwen2AudioForConditionalGeneration
|
||||
- forward
|
||||
@@ -77,6 +77,7 @@ FlashAttention-2 is currently supported for the following architectures:
|
||||
* [StableLm](https://huggingface.co/docs/transformers/model_doc/stablelm#transformers.StableLmModel)
|
||||
* [Starcoder2](https://huggingface.co/docs/transformers/model_doc/starcoder2#transformers.Starcoder2Model)
|
||||
* [Qwen2](https://huggingface.co/docs/transformers/model_doc/qwen2#transformers.Qwen2Model)
|
||||
* [Qwen2Audio](https://huggingface.co/docs/transformers/model_doc/qwen2_audio#transformers.Qwen2AudioEncoder)
|
||||
* [Qwen2MoE](https://huggingface.co/docs/transformers/model_doc/qwen2_moe#transformers.Qwen2MoeModel)
|
||||
* [Whisper](https://huggingface.co/docs/transformers/model_doc/whisper#transformers.WhisperModel)
|
||||
* [Wav2Vec2](https://huggingface.co/docs/transformers/model_doc/wav2vec2#transformers.Wav2Vec2Model)
|
||||
@@ -227,6 +228,7 @@ For now, Transformers supports SDPA inference and training for the following arc
|
||||
* [StableLm](https://huggingface.co/docs/transformers/model_doc/stablelm#transformers.StableLmModel)
|
||||
* [Starcoder2](https://huggingface.co/docs/transformers/model_doc/starcoder2#transformers.Starcoder2Model)
|
||||
* [Qwen2](https://huggingface.co/docs/transformers/model_doc/qwen2#transformers.Qwen2Model)
|
||||
* [Qwen2Audio](https://huggingface.co/docs/transformers/model_doc/qwen2_audio#transformers.Qwen2AudioEncoder)
|
||||
* [Qwen2MoE](https://huggingface.co/docs/transformers/model_doc/qwen2_moe#transformers.Qwen2MoeModel)
|
||||
* [Musicgen](https://huggingface.co/docs/transformers/model_doc/musicgen#transformers.MusicgenModel)
|
||||
* [MusicGen Melody](https://huggingface.co/docs/transformers/model_doc/musicgen_melody#transformers.MusicgenMelodyModel)
|
||||
|
||||
@@ -655,6 +655,11 @@ _import_structure = {
|
||||
"Qwen2Config",
|
||||
"Qwen2Tokenizer",
|
||||
],
|
||||
"models.qwen2_audio": [
|
||||
"Qwen2AudioConfig",
|
||||
"Qwen2AudioEncoderConfig",
|
||||
"Qwen2AudioProcessor",
|
||||
],
|
||||
"models.qwen2_moe": ["Qwen2MoeConfig"],
|
||||
"models.rag": ["RagConfig", "RagRetriever", "RagTokenizer"],
|
||||
"models.recurrent_gemma": ["RecurrentGemmaConfig"],
|
||||
@@ -2980,6 +2985,13 @@ else:
|
||||
"Qwen2PreTrainedModel",
|
||||
]
|
||||
)
|
||||
_import_structure["models.qwen2_audio"].extend(
|
||||
[
|
||||
"Qwen2AudioEncoder",
|
||||
"Qwen2AudioForConditionalGeneration",
|
||||
"Qwen2AudioPreTrainedModel",
|
||||
]
|
||||
)
|
||||
_import_structure["models.qwen2_moe"].extend(
|
||||
[
|
||||
"Qwen2MoeForCausalLM",
|
||||
@@ -5378,6 +5390,11 @@ if TYPE_CHECKING:
|
||||
from .models.pvt import PvtConfig
|
||||
from .models.pvt_v2 import PvtV2Config
|
||||
from .models.qwen2 import Qwen2Config, Qwen2Tokenizer
|
||||
from .models.qwen2_audio import (
|
||||
Qwen2AudioConfig,
|
||||
Qwen2AudioEncoderConfig,
|
||||
Qwen2AudioProcessor,
|
||||
)
|
||||
from .models.qwen2_moe import Qwen2MoeConfig
|
||||
from .models.rag import RagConfig, RagRetriever, RagTokenizer
|
||||
from .models.recurrent_gemma import RecurrentGemmaConfig
|
||||
@@ -7390,6 +7407,11 @@ if TYPE_CHECKING:
|
||||
Qwen2Model,
|
||||
Qwen2PreTrainedModel,
|
||||
)
|
||||
from .models.qwen2_audio import (
|
||||
Qwen2AudioEncoder,
|
||||
Qwen2AudioForConditionalGeneration,
|
||||
Qwen2AudioPreTrainedModel,
|
||||
)
|
||||
from .models.qwen2_moe import (
|
||||
Qwen2MoeForCausalLM,
|
||||
Qwen2MoeForSequenceClassification,
|
||||
|
||||
@@ -189,6 +189,7 @@ from . import (
|
||||
pvt,
|
||||
pvt_v2,
|
||||
qwen2,
|
||||
qwen2_audio,
|
||||
qwen2_moe,
|
||||
rag,
|
||||
recurrent_gemma,
|
||||
|
||||
5
src/transformers/models/auto/configuration_auto.py
Executable file → Normal file
5
src/transformers/models/auto/configuration_auto.py
Executable file → Normal file
@@ -208,6 +208,8 @@ CONFIG_MAPPING_NAMES = OrderedDict(
|
||||
("pvt_v2", "PvtV2Config"),
|
||||
("qdqbert", "QDQBertConfig"),
|
||||
("qwen2", "Qwen2Config"),
|
||||
("qwen2_audio", "Qwen2AudioConfig"),
|
||||
("qwen2_audio_encoder", "Qwen2AudioEncoderConfig"),
|
||||
("qwen2_moe", "Qwen2MoeConfig"),
|
||||
("rag", "RagConfig"),
|
||||
("realm", "RealmConfig"),
|
||||
@@ -504,6 +506,8 @@ MODEL_NAMES_MAPPING = OrderedDict(
|
||||
("pvt_v2", "PVTv2"),
|
||||
("qdqbert", "QDQBert"),
|
||||
("qwen2", "Qwen2"),
|
||||
("qwen2_audio", "Qwen2Audio"),
|
||||
("qwen2_audio_encoder", "Qwen2AudioEncoder"),
|
||||
("qwen2_moe", "Qwen2MoE"),
|
||||
("rag", "RAG"),
|
||||
("realm", "REALM"),
|
||||
@@ -642,6 +646,7 @@ SPECIAL_MODEL_TYPE_TO_MODULE_NAME = OrderedDict(
|
||||
("maskformer-swin", "maskformer"),
|
||||
("xclip", "x_clip"),
|
||||
("clip_vision_model", "clip"),
|
||||
("qwen2_audio_encoder", "qwen2_audio"),
|
||||
("siglip_vision_model", "siglip"),
|
||||
("chinese_clip_vision_model", "chinese_clip"),
|
||||
("rt_detr_resnet", "rt_detr"),
|
||||
|
||||
3
src/transformers/models/auto/modeling_auto.py
Executable file → Normal file
3
src/transformers/models/auto/modeling_auto.py
Executable file → Normal file
@@ -196,6 +196,7 @@ MODEL_MAPPING_NAMES = OrderedDict(
|
||||
("pvt_v2", "PvtV2Model"),
|
||||
("qdqbert", "QDQBertModel"),
|
||||
("qwen2", "Qwen2Model"),
|
||||
("qwen2_audio_encoder", "Qwen2AudioEncoder"),
|
||||
("qwen2_moe", "Qwen2MoeModel"),
|
||||
("recurrent_gemma", "RecurrentGemmaModel"),
|
||||
("reformer", "ReformerModel"),
|
||||
@@ -323,6 +324,7 @@ MODEL_FOR_PRETRAINING_MAPPING_NAMES = OrderedDict(
|
||||
("nllb-moe", "NllbMoeForConditionalGeneration"),
|
||||
("openai-gpt", "OpenAIGPTLMHeadModel"),
|
||||
("paligemma", "PaliGemmaForConditionalGeneration"),
|
||||
("qwen2_audio", "Qwen2AudioForConditionalGeneration"),
|
||||
("retribert", "RetriBertModel"),
|
||||
("roberta", "RobertaForMaskedLM"),
|
||||
("roberta-prelayernorm", "RobertaPreLayerNormForMaskedLM"),
|
||||
@@ -829,6 +831,7 @@ MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES = OrderedDict(
|
||||
("pegasus_x", "PegasusXForConditionalGeneration"),
|
||||
("plbart", "PLBartForConditionalGeneration"),
|
||||
("prophetnet", "ProphetNetForConditionalGeneration"),
|
||||
("qwen2_audio", "Qwen2AudioForConditionalGeneration"),
|
||||
("seamless_m4t", "SeamlessM4TForTextToText"),
|
||||
("seamless_m4t_v2", "SeamlessM4Tv2ForTextToText"),
|
||||
("switch_transformers", "SwitchTransformersForConditionalGeneration"),
|
||||
|
||||
@@ -82,6 +82,7 @@ PROCESSOR_MAPPING_NAMES = OrderedDict(
|
||||
("paligemma", "PaliGemmaProcessor"),
|
||||
("pix2struct", "Pix2StructProcessor"),
|
||||
("pop2piano", "Pop2PianoProcessor"),
|
||||
("qwen2_audio", "Qwen2AudioProcessor"),
|
||||
("sam", "SamProcessor"),
|
||||
("seamless_m4t", "SeamlessM4TProcessor"),
|
||||
("sew", "Wav2Vec2Processor"),
|
||||
|
||||
@@ -392,6 +392,7 @@ else:
|
||||
"Qwen2TokenizerFast" if is_tokenizers_available() else None,
|
||||
),
|
||||
),
|
||||
("qwen2_audio", ("Qwen2Tokenizer", "Qwen2TokenizerFast" if is_tokenizers_available() else None)),
|
||||
(
|
||||
"qwen2_moe",
|
||||
(
|
||||
|
||||
57
src/transformers/models/qwen2_audio/__init__.py
Normal file
57
src/transformers/models/qwen2_audio/__init__.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# Copyright 2024 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.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ...utils import OptionalDependencyNotAvailable, _LazyModule, is_torch_available
|
||||
|
||||
|
||||
_import_structure = {
|
||||
"configuration_qwen2_audio": ["Qwen2AudioConfig", "Qwen2AudioEncoderConfig"],
|
||||
"processing_qwen2_audio": ["Qwen2AudioProcessor"],
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
if not is_torch_available():
|
||||
raise OptionalDependencyNotAvailable()
|
||||
except OptionalDependencyNotAvailable:
|
||||
pass
|
||||
else:
|
||||
_import_structure["modeling_qwen2_audio"] = [
|
||||
"Qwen2AudioForConditionalGeneration",
|
||||
"Qwen2AudioPreTrainedModel",
|
||||
"Qwen2AudioEncoder",
|
||||
]
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .configuration_qwen2_audio import Qwen2AudioConfig, Qwen2AudioEncoderConfig
|
||||
from .processing_qwen2_audio import Qwen2AudioProcessor
|
||||
|
||||
try:
|
||||
if not is_torch_available():
|
||||
raise OptionalDependencyNotAvailable()
|
||||
except OptionalDependencyNotAvailable:
|
||||
pass
|
||||
else:
|
||||
from .modeling_qwen2_audio import (
|
||||
Qwen2AudioEncoder,
|
||||
Qwen2AudioForConditionalGeneration,
|
||||
Qwen2AudioPreTrainedModel,
|
||||
)
|
||||
|
||||
else:
|
||||
import sys
|
||||
|
||||
sys.modules[__name__] = _LazyModule(__name__, globals()["__file__"], _import_structure)
|
||||
199
src/transformers/models/qwen2_audio/configuration_qwen2_audio.py
Normal file
199
src/transformers/models/qwen2_audio/configuration_qwen2_audio.py
Normal file
@@ -0,0 +1,199 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2024 Microsoft Research & University of Wisconsin-Madison 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.
|
||||
"""Qwen2Audio model configuration"""
|
||||
|
||||
from ...configuration_utils import PretrainedConfig
|
||||
from ...utils import logging
|
||||
from ..auto import CONFIG_MAPPING
|
||||
|
||||
|
||||
logger = logging.get_logger(__name__)
|
||||
|
||||
|
||||
class Qwen2AudioEncoderConfig(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a [`Qwen2AudioEncoder`]. It is used to instantiate a
|
||||
Qwen2-Audio audio encoder according to the specified arguments, defining the model architecture. Instantiating a
|
||||
configuration with the defaults will yield a similar configuration to that of the audio encoder of the Qwen2-Audio
|
||||
architecture.
|
||||
|
||||
e.g. [Qwen/Qwen2-Audio-7B](https://huggingface.co/Qwen/Qwen2-Audio-7B)
|
||||
|
||||
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
||||
documentation from [`PretrainedConfig`] for more information.
|
||||
|
||||
Args:
|
||||
num_mel_bins (`int`, *optional*, defaults to 128):
|
||||
Number of mel features used per input features. Should correspond to the value used in the
|
||||
`Qwen2AudioProcessor` class.
|
||||
encoder_layers (`int`, *optional*, defaults to 32):
|
||||
Number of encoder layers.
|
||||
encoder_attention_heads (`int`, *optional*, defaults to 20):
|
||||
Number of attention heads for each attention layer in the Transformer encoder.
|
||||
encoder_ffn_dim (`int`, *optional*, defaults to 5120):
|
||||
Dimensionality of the "intermediate" (often named feed-forward) layer in encoder.
|
||||
encoder_layerdrop (`float`, *optional*, defaults to 0.0):
|
||||
The LayerDrop probability for the encoder. See the [LayerDrop paper](see https://arxiv.org/abs/1909.11556)
|
||||
for more details.
|
||||
d_model (`int`, *optional*, defaults to 1280):
|
||||
Dimensionality of the layers.
|
||||
dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout probability for all fully connected layers in the embeddings, encoder, and pooler.
|
||||
attention_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for the attention probabilities.
|
||||
activation_function (`str`, *optional*, defaults to `"gelu"`):
|
||||
The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
activation_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for activations inside the fully connected layer.
|
||||
scale_embedding (`bool`, *optional*, defaults to `False`):
|
||||
Scale embeddings by diving by sqrt(d_model).
|
||||
init_std (`float`, *optional*, defaults to 0.02):
|
||||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
||||
max_source_positions (`int`, *optional*, defaults to 1500):
|
||||
The maximum sequence length of log-mel filter-bank features that this model might ever be used with.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
>>> from transformers import Qwen2AudioEncoderConfig, Qwen2AudioEncoder
|
||||
|
||||
>>> # Initializing a Qwen2AudioEncoderConfig
|
||||
>>> configuration = Qwen2AudioEncoderConfig()
|
||||
|
||||
>>> # Initializing a Qwen2AudioEncoder (with random weights)
|
||||
>>> model = Qwen2AudioEncoder(configuration)
|
||||
|
||||
>>> # Accessing the model configuration
|
||||
>>> configuration = model.config
|
||||
```"""
|
||||
|
||||
model_type = "qwen2_audio_encoder"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_mel_bins=128,
|
||||
encoder_layers=32,
|
||||
encoder_attention_heads=20,
|
||||
encoder_ffn_dim=5120,
|
||||
encoder_layerdrop=0.0,
|
||||
d_model=1280,
|
||||
dropout=0.0,
|
||||
attention_dropout=0.0,
|
||||
activation_function="gelu",
|
||||
activation_dropout=0.0,
|
||||
scale_embedding=False,
|
||||
init_std=0.02,
|
||||
max_source_positions=1500,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.num_mel_bins = num_mel_bins
|
||||
self.d_model = d_model
|
||||
self.encoder_layers = encoder_layers
|
||||
self.encoder_attention_heads = encoder_attention_heads
|
||||
self.encoder_ffn_dim = encoder_ffn_dim
|
||||
self.dropout = dropout
|
||||
self.attention_dropout = attention_dropout
|
||||
self.activation_function = activation_function
|
||||
self.activation_dropout = activation_dropout
|
||||
self.encoder_layerdrop = encoder_layerdrop
|
||||
self.num_hidden_layers = encoder_layers
|
||||
self.init_std = init_std
|
||||
self.scale_embedding = scale_embedding # scale factor will be sqrt(d_model) if True
|
||||
self.max_source_positions = max_source_positions
|
||||
|
||||
|
||||
class Qwen2AudioConfig(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a [`Qwen2AudioForConditionalGeneration`]. It is used to instantiate an
|
||||
Qwen2-Audio model according to the specified arguments, defining the model architecture. Instantiating a configuration
|
||||
with the defaults will yield a similar configuration to that of the Qwen2-Audio.
|
||||
|
||||
e.g. [Qwen/Qwen2-Audio-7B](https://huggingface.co/Qwen/Qwen2-Audio-7B)
|
||||
|
||||
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
||||
documentation from [`PretrainedConfig`] for more information.
|
||||
|
||||
Args:
|
||||
audio_config (`Union[AutoConfig, dict]`, *optional*, defaults to `CLIPVisionConfig`):
|
||||
The config object or dictionary of the audio backbone.
|
||||
text_config (`Union[AutoConfig, dict]`, *optional*, defaults to `LlamaConfig`):
|
||||
The config object or dictionary of the text backbone.
|
||||
audio_token_index (`int`, *optional*, defaults to 151646):
|
||||
The image token index to encode the image prompt.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
>>> from transformers import Qwen2AudioForConditionalGeneration, Qwen2AudioConfig, Qwen2AudioEncoderConfig, Qwen2Config
|
||||
|
||||
>>> # Initializing a Qwen2AudioEncoder config
|
||||
>>> audio_config = Qwen2AudioEncoderConfig()
|
||||
|
||||
>>> # Initializing a Qwen2 config
|
||||
>>> text_config = Qwen2Config()
|
||||
|
||||
>>> # Initializing a Qwen2Audio configuration
|
||||
>>> configuration = Qwen2AudioConfig(audio_config, text_config)
|
||||
|
||||
>>> # Initializing a model from the qwen2-audio style configuration
|
||||
>>> model = Qwen2AudioForConditionalGeneration(configuration)
|
||||
|
||||
>>> # Accessing the model configuration
|
||||
>>> configuration = model.config
|
||||
```"""
|
||||
|
||||
model_type = "qwen2_audio"
|
||||
is_composition = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
audio_config=None,
|
||||
text_config=None,
|
||||
audio_token_index=151646,
|
||||
**kwargs,
|
||||
):
|
||||
self.audio_token_index = audio_token_index
|
||||
|
||||
if isinstance(audio_config, dict):
|
||||
audio_config["model_type"] = (
|
||||
audio_config["model_type"] if "model_type" in audio_config else "qwen2_audio_encoder"
|
||||
)
|
||||
audio_config = CONFIG_MAPPING[audio_config["model_type"]](**audio_config)
|
||||
elif audio_config is None:
|
||||
audio_config = CONFIG_MAPPING["qwen2_audio_encoder"](
|
||||
d_model=1280,
|
||||
encoder_attention_heads=20,
|
||||
encoder_ffn_dim=5120,
|
||||
encoder_layerdrop=0.0,
|
||||
encoder_layers=32,
|
||||
num_mel_bins=128,
|
||||
max_source_positions=1500,
|
||||
scale_embedding=False,
|
||||
activation_function="gelu",
|
||||
)
|
||||
|
||||
self.audio_config = audio_config
|
||||
|
||||
if isinstance(text_config, dict):
|
||||
text_config["model_type"] = text_config["model_type"] if "model_type" in text_config else "qwen2"
|
||||
text_config = CONFIG_MAPPING[text_config["model_type"]](**text_config)
|
||||
elif text_config is None:
|
||||
text_config = CONFIG_MAPPING["qwen2"]()
|
||||
|
||||
self.text_config = text_config
|
||||
|
||||
super().__init__(**kwargs)
|
||||
1378
src/transformers/models/qwen2_audio/modeling_qwen2_audio.py
Normal file
1378
src/transformers/models/qwen2_audio/modeling_qwen2_audio.py
Normal file
File diff suppressed because it is too large
Load Diff
177
src/transformers/models/qwen2_audio/processing_qwen2_audio.py
Normal file
177
src/transformers/models/qwen2_audio/processing_qwen2_audio.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2024 The HuggingFace Inc. team.
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Processor class for Qwen2Audio.
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ...feature_extraction_utils import BatchFeature
|
||||
from ...processing_utils import ProcessorMixin
|
||||
from ...tokenization_utils_base import PaddingStrategy, PreTokenizedInput, TextInput
|
||||
|
||||
|
||||
class Qwen2AudioProcessor(ProcessorMixin):
|
||||
r"""
|
||||
Constructs a Qwen2Audio processor which wraps a Qwen2Audio feature extractor and a Qwen2Audio tokenizer into a single processor.
|
||||
|
||||
[`Qwen2AudioProcessor`] offers all the functionalities of [`WhisperFeatureExtractor`] and [`Qwen2TokenizerFast`]. See the
|
||||
[`~Qwen2AudioProcessor.__call__`] and [`~Qwen2AudioProcessor.decode`] for more information.
|
||||
|
||||
Args:
|
||||
feature_extractor ([`WhisperFeatureExtractor`], *optional*):
|
||||
The feature extractor is a required input.
|
||||
tokenizer ([`Qwen2TokenizerFast`], *optional*):
|
||||
The tokenizer is a required input.
|
||||
chat_template (`Optional[str]`, *optional*):
|
||||
The Jinja template to use for formatting the conversation. If not provided, the default chat template
|
||||
is used.
|
||||
"""
|
||||
|
||||
attributes = ["feature_extractor", "tokenizer"]
|
||||
feature_extractor_class = "WhisperFeatureExtractor"
|
||||
tokenizer_class = "AutoTokenizer"
|
||||
|
||||
def __init__(self, feature_extractor=None, tokenizer=None, chat_template=None):
|
||||
if chat_template is None:
|
||||
chat_template = self.default_chat_template
|
||||
super().__init__(feature_extractor, tokenizer, chat_template=chat_template)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
text: Union[TextInput, PreTokenizedInput, List[TextInput], List[PreTokenizedInput]] = None,
|
||||
audios: Union[np.ndarray, List[np.ndarray]] = None,
|
||||
padding: Union[bool, str, PaddingStrategy] = False,
|
||||
sampling_rate: Optional[int] = None,
|
||||
**kwargs,
|
||||
) -> BatchFeature:
|
||||
"""
|
||||
Main method to prepare for the model one or several sequences(s) and audio(s). This method forwards the `text`
|
||||
and `kwargs` arguments to Qwen2TokenizerFast's [`~Qwen2TokenizerFast.__call__`] if `text` is not `None` to encode
|
||||
the text. To prepare the audio(s), this method forwards the `audios` and `kwrags` arguments to
|
||||
WhisperFeatureExtractor's [`~WhisperFeatureExtractor.__call__`] if `audios` is not `None`. Please refer to the doctsring
|
||||
of the above two methods for more information.
|
||||
|
||||
Args:
|
||||
text (`str`, `List[str]`, `List[List[str]]`):
|
||||
The sequence or batch of sequences to be encoded. Each sequence can be a string or a list of strings
|
||||
(pretokenized string). If the sequences are provided as list of strings (pretokenized), you must set
|
||||
`is_split_into_words=True` (to lift the ambiguity with a batch of sequences).
|
||||
audios (`np.ndarray`, `List[np.ndarray]`):
|
||||
The audio or batch of audios to be prepared. Each audio can be a NumPy array.
|
||||
padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*, defaults to `False`):
|
||||
Select a strategy to pad the returned sequences (according to the model's padding side and padding
|
||||
index) among:
|
||||
- `True` or `'longest'`: Pad to the longest sequence in the batch (or no padding if only a single
|
||||
sequence if provided).
|
||||
- `'max_length'`: Pad to a maximum length specified with the argument `max_length` or to the maximum
|
||||
acceptable input length for the model if that argument is not provided.
|
||||
- `False` or `'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of different
|
||||
lengths).
|
||||
sampling_rate (`int`, defaults to 16000):
|
||||
The sampling rate at which the audio files should be digitalized expressed in hertz (Hz).
|
||||
"""
|
||||
|
||||
if text is None:
|
||||
raise ValueError("You need to specify either a `text` input to process.")
|
||||
inputs = self.tokenizer(text, padding=padding, **kwargs)
|
||||
|
||||
if audios is not None:
|
||||
audio_inputs = self.feature_extractor(
|
||||
audios, sampling_rate=sampling_rate, return_attention_mask=True, padding="max_length", **kwargs
|
||||
)
|
||||
audio_inputs["feature_attention_mask"] = audio_inputs.pop(
|
||||
"attention_mask"
|
||||
) # rename attention_mask to prevent conflicts later on
|
||||
inputs.update(audio_inputs)
|
||||
|
||||
return BatchFeature(data={**inputs})
|
||||
|
||||
def batch_decode(self, *args, **kwargs):
|
||||
"""
|
||||
This method forwards all its arguments to Qwen2TokenizerFast's [`~PreTrainedTokenizer.batch_decode`]. Please
|
||||
refer to the docstring of this method for more information.
|
||||
"""
|
||||
return self.tokenizer.batch_decode(*args, **kwargs)
|
||||
|
||||
def decode(self, *args, **kwargs):
|
||||
"""
|
||||
This method forwards all its arguments to Qwen2TokenizerFast's [`~PreTrainedTokenizer.decode`]. Please refer to
|
||||
the docstring of this method for more information.
|
||||
"""
|
||||
return self.tokenizer.decode(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def model_input_names(self):
|
||||
tokenizer_input_names = self.tokenizer.model_input_names
|
||||
feature_extractor_input_names = self.feature_extractor.model_input_names
|
||||
return list(dict.fromkeys(tokenizer_input_names + feature_extractor_input_names + ["feature_attention_mask"]))
|
||||
|
||||
@property
|
||||
def default_chat_template(self):
|
||||
"""
|
||||
This default vicuna template formats inputs in the form of a chat history. For each message in the chat history:
|
||||
* the template will output the role of the speaker followed by the content of the message.
|
||||
* content is a list of strings and audios.
|
||||
* If the content element is an audio, the template will output a sequence of <|AUDIO|> tokens
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{'role': 'system', 'content': 'You are a helpful assistant.'},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3"},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
]},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{"role": "user", "content": [
|
||||
{"type": "audio", "audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/f2641_0_throatclearing.wav"},
|
||||
{"type": "text", "text": "How about this one?"},
|
||||
]},
|
||||
]
|
||||
|
||||
result = template.render(messages=messages, add_generation_prompt=True)
|
||||
```
|
||||
"""
|
||||
# fmt: off
|
||||
return (
|
||||
"{% set audio_count = namespace(value=0) %}"
|
||||
"{% for message in messages %}"
|
||||
"{% if loop.first and message['role'] != 'system' %}"
|
||||
"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
|
||||
"{% endif %}"
|
||||
"<|im_start|>{{ message['role'] }}\n"
|
||||
"{% if message['content'] is string %}"
|
||||
"{{ message['content'] }}<|im_end|>\n"
|
||||
"{% else %}"
|
||||
"{% for content in message['content'] %}"
|
||||
"{% if 'audio' in content or 'audio_url' in content %}"
|
||||
"{% set audio_count.value = audio_count.value + 1 %}"
|
||||
"Audio {{ audio_count.value }}: <|audio_bos|><|AUDIO|><|audio_eos|>\n"
|
||||
"{% elif 'text' in content %}"
|
||||
"{{ content['text'] }}"
|
||||
"{% endif %}"
|
||||
"{% endfor %}"
|
||||
"<|im_end|>\n"
|
||||
"{% endif %}"
|
||||
"{% endfor %}"
|
||||
"{% if add_generation_prompt %}"
|
||||
"<|im_start|>assistant\n"
|
||||
"{% endif %}"
|
||||
)
|
||||
# fmt: on
|
||||
@@ -7231,6 +7231,27 @@ class Qwen2PreTrainedModel(metaclass=DummyObject):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class Qwen2AudioEncoder(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class Qwen2AudioForConditionalGeneration(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class Qwen2AudioPreTrainedModel(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class Qwen2MoeForCausalLM(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
|
||||
0
tests/models/qwen2_audio/__init__.py
Normal file
0
tests/models/qwen2_audio/__init__.py
Normal file
379
tests/models/qwen2_audio/test_modeling_qwen2_audio.py
Normal file
379
tests/models/qwen2_audio/test_modeling_qwen2_audio.py
Normal file
@@ -0,0 +1,379 @@
|
||||
# coding=utf-8
|
||||
# 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 PyTorch Qwen2Audio model."""
|
||||
|
||||
import gc
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
|
||||
import librosa
|
||||
|
||||
from transformers import (
|
||||
AutoProcessor,
|
||||
Qwen2AudioConfig,
|
||||
Qwen2AudioForConditionalGeneration,
|
||||
is_torch_available,
|
||||
)
|
||||
from transformers.testing_utils import (
|
||||
require_torch,
|
||||
slow,
|
||||
torch_device,
|
||||
)
|
||||
|
||||
from ...test_configuration_common import ConfigTester
|
||||
from ...test_modeling_common import ModelTesterMixin, floats_tensor, ids_tensor
|
||||
|
||||
|
||||
if is_torch_available():
|
||||
import torch
|
||||
else:
|
||||
is_torch_greater_or_equal_than_2_0 = False
|
||||
|
||||
|
||||
class Qwen2AudioModelTester:
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
ignore_index=-100,
|
||||
audio_token_index=0,
|
||||
seq_length=7,
|
||||
feat_seq_length=60,
|
||||
text_config={
|
||||
"model_type": "qwen2",
|
||||
"intermediate_size": 36,
|
||||
"initializer_range": 0.02,
|
||||
"hidden_size": 32,
|
||||
"max_position_embeddings": 52,
|
||||
"num_hidden_layers": 2,
|
||||
"num_attention_heads": 4,
|
||||
"num_key_value_heads": 2,
|
||||
"use_labels": True,
|
||||
"use_mrope": False,
|
||||
"vocab_size": 99,
|
||||
},
|
||||
is_training=True,
|
||||
audio_config={
|
||||
"model_type": "qwen2_audio_encoder",
|
||||
"d_model": 16,
|
||||
"encoder_attention_heads": 4,
|
||||
"encoder_ffn_dim": 16,
|
||||
"encoder_layers": 2,
|
||||
"num_mel_bins": 80,
|
||||
"max_source_positions": 30,
|
||||
"initializer_range": 0.02,
|
||||
},
|
||||
):
|
||||
self.parent = parent
|
||||
self.ignore_index = ignore_index
|
||||
self.audio_token_index = audio_token_index
|
||||
self.text_config = text_config
|
||||
self.audio_config = audio_config
|
||||
self.seq_length = seq_length
|
||||
self.feat_seq_length = feat_seq_length
|
||||
|
||||
self.num_hidden_layers = text_config["num_hidden_layers"]
|
||||
self.vocab_size = text_config["vocab_size"]
|
||||
self.hidden_size = text_config["hidden_size"]
|
||||
self.num_attention_heads = text_config["num_attention_heads"]
|
||||
self.is_training = is_training
|
||||
|
||||
self.batch_size = 3
|
||||
self.encoder_seq_length = audio_config["max_source_positions"] // 2 + seq_length - 1
|
||||
|
||||
def get_config(self):
|
||||
return Qwen2AudioConfig(
|
||||
text_config=self.text_config,
|
||||
audio_config=self.audio_config,
|
||||
ignore_index=self.ignore_index,
|
||||
audio_token_index=self.audio_token_index,
|
||||
)
|
||||
|
||||
def prepare_config_and_inputs(self):
|
||||
input_features_values = floats_tensor(
|
||||
[
|
||||
self.batch_size,
|
||||
self.audio_config["num_mel_bins"],
|
||||
self.feat_seq_length,
|
||||
]
|
||||
)
|
||||
config = self.get_config()
|
||||
feature_attention_mask = torch.ones([self.batch_size, self.feat_seq_length], dtype=torch.long).to(torch_device)
|
||||
return config, input_features_values, feature_attention_mask
|
||||
|
||||
def prepare_config_and_inputs_for_common(self):
|
||||
config_and_inputs = self.prepare_config_and_inputs()
|
||||
config, input_features_values, feature_attention_mask = config_and_inputs
|
||||
input_ids = ids_tensor([self.batch_size, self.seq_length], config.text_config.vocab_size - 1) + 1
|
||||
attention_mask = torch.ones(input_ids.shape, dtype=torch.long).to(torch_device)
|
||||
attention_mask[:, :1] = 0
|
||||
# we are giving 3 audios let's make sure we pass in 3 audios tokens
|
||||
input_ids[:, 1] = config.audio_token_index
|
||||
inputs_dict = {
|
||||
"input_features": input_features_values,
|
||||
"feature_attention_mask": feature_attention_mask,
|
||||
"input_ids": input_ids,
|
||||
"attention_mask": attention_mask,
|
||||
}
|
||||
return config, inputs_dict
|
||||
|
||||
def create_and_check_qwen2audio_model_fp16_forward(self, config, input_ids, pixel_values, attention_mask):
|
||||
model = Qwen2AudioForConditionalGeneration(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,
|
||||
pixel_values=pixel_values.to(torch.bfloat16),
|
||||
return_dict=True,
|
||||
)["logits"]
|
||||
self.parent.assertFalse(torch.isnan(logits).any().item())
|
||||
|
||||
|
||||
@require_torch
|
||||
class Qwen2AudioForConditionalGenerationModelTest(ModelTesterMixin, unittest.TestCase):
|
||||
"""
|
||||
Model tester for `Qwen2AudioForConditionalGeneration`.
|
||||
"""
|
||||
|
||||
all_model_classes = (Qwen2AudioForConditionalGeneration,) if is_torch_available() else ()
|
||||
test_pruning = False
|
||||
test_head_masking = False
|
||||
|
||||
def setUp(self):
|
||||
self.model_tester = Qwen2AudioModelTester(self)
|
||||
self.config_tester = ConfigTester(self, config_class=Qwen2AudioConfig, has_text_modality=False)
|
||||
|
||||
@unittest.skip(reason="Compile not yet supported because in Qwen2Audio models")
|
||||
def test_sdpa_can_compile_dynamic(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="Compile not yet supported because in Qwen2Audio models")
|
||||
def test_sdpa_can_dispatch_on_flash(self):
|
||||
pass
|
||||
|
||||
|
||||
@require_torch
|
||||
class Qwen2AudioForConditionalGenerationIntegrationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.processor = AutoProcessor.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
|
||||
def tearDown(self):
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
@slow
|
||||
def test_small_model_integration_test_single(self):
|
||||
# Let' s make sure we test the preprocessing to replace what is used
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
|
||||
url = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3"
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "audio", "audio_url": url},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
raw_audio, _ = librosa.load(BytesIO(urlopen(url).read()), sr=self.processor.feature_extractor.sampling_rate)
|
||||
|
||||
formatted_prompt = self.processor.apply_chat_template(messages, add_generation_prompt=True)
|
||||
|
||||
inputs = self.processor(text=formatted_prompt, audios=[raw_audio], return_tensors="pt", padding=True)
|
||||
|
||||
output = model.generate(**inputs, max_new_tokens=32)
|
||||
|
||||
EXPECTED_INPUT_IDS = torch.tensor(
|
||||
[
|
||||
[
|
||||
151644,
|
||||
8948,
|
||||
198,
|
||||
2610,
|
||||
525,
|
||||
264,
|
||||
10950,
|
||||
17847,
|
||||
13,
|
||||
151645,
|
||||
198,
|
||||
151644,
|
||||
872,
|
||||
198,
|
||||
14755,
|
||||
220,
|
||||
16,
|
||||
25,
|
||||
220,
|
||||
151647,
|
||||
151646,
|
||||
151648,
|
||||
198,
|
||||
3838,
|
||||
594,
|
||||
429,
|
||||
5112,
|
||||
30,
|
||||
151645,
|
||||
198,
|
||||
151644,
|
||||
77091,
|
||||
198,
|
||||
]
|
||||
]
|
||||
)
|
||||
self.assertTrue(torch.equal(inputs["input_ids"], EXPECTED_INPUT_IDS))
|
||||
|
||||
EXPECTED_DECODED_TEXT = "<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\nAudio 1: <|audio_bos|><|AUDIO|><|audio_eos|>\nWhat's that sound?<|im_end|>\n<|im_start|>assistant\nIt is the sound of glass breaking.<|im_end|>"
|
||||
|
||||
self.assertEqual(
|
||||
self.processor.decode(output[0], skip_special_tokens=False),
|
||||
EXPECTED_DECODED_TEXT,
|
||||
)
|
||||
|
||||
@slow
|
||||
def test_small_model_integration_test_batch(self):
|
||||
# Let' s make sure we test the preprocessing to replace what is used
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
|
||||
conversation1 = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3",
|
||||
},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
],
|
||||
},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/f2641_0_throatclearing.wav",
|
||||
},
|
||||
{"type": "text", "text": "What can you hear?"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
conversation2 = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/1272-128104-0000.flac",
|
||||
},
|
||||
{"type": "text", "text": "What does the person say?"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
conversations = [conversation1, conversation2]
|
||||
|
||||
text = [
|
||||
self.processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
|
||||
for conversation in conversations
|
||||
]
|
||||
|
||||
audios = []
|
||||
for conversation in conversations:
|
||||
for message in conversation:
|
||||
if isinstance(message["content"], list):
|
||||
for ele in message["content"]:
|
||||
if ele["type"] == "audio":
|
||||
audios.append(
|
||||
librosa.load(
|
||||
BytesIO(urlopen(ele["audio_url"]).read()),
|
||||
sr=self.processor.feature_extractor.sampling_rate,
|
||||
)[0]
|
||||
)
|
||||
|
||||
inputs = self.processor(text=text, audios=audios, return_tensors="pt", padding=True)
|
||||
|
||||
output = model.generate(**inputs, max_new_tokens=32)
|
||||
|
||||
EXPECTED_DECODED_TEXT = [
|
||||
"system\nYou are a helpful assistant.\nuser\nAudio 1: \nWhat's that sound?\nassistant\nIt is the sound of glass shattering.\nuser\nAudio 2: \nWhat can you hear?\nassistant\ncough and throat clearing.",
|
||||
"system\nYou are a helpful assistant.\nuser\nAudio 1: \nWhat does the person say?\nassistant\nThe original content of this audio is: 'Mister Quiller is the apostle of the middle classes and we are glad to welcome his gospel.'",
|
||||
]
|
||||
self.assertEqual(
|
||||
self.processor.batch_decode(output, skip_special_tokens=True),
|
||||
EXPECTED_DECODED_TEXT,
|
||||
)
|
||||
|
||||
@slow
|
||||
def test_small_model_integration_test_multiturn(self):
|
||||
# Let' s make sure we test the preprocessing to replace what is used
|
||||
model = Qwen2AudioForConditionalGeneration.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3",
|
||||
},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
],
|
||||
},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/f2641_0_throatclearing.wav",
|
||||
},
|
||||
{"type": "text", "text": "How about this one?"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
formatted_prompt = self.processor.apply_chat_template(messages, add_generation_prompt=True)
|
||||
|
||||
audios = []
|
||||
for message in messages:
|
||||
if isinstance(message["content"], list):
|
||||
for ele in message["content"]:
|
||||
if ele["type"] == "audio":
|
||||
audios.append(
|
||||
librosa.load(
|
||||
BytesIO(urlopen(ele["audio_url"]).read()),
|
||||
sr=self.processor.feature_extractor.sampling_rate,
|
||||
)[0]
|
||||
)
|
||||
|
||||
inputs = self.processor(text=formatted_prompt, audios=audios, return_tensors="pt", padding=True)
|
||||
|
||||
output = model.generate(**inputs, max_new_tokens=32, top_k=1)
|
||||
|
||||
EXPECTED_DECODED_TEXT = [
|
||||
"system\nYou are a helpful assistant.\nuser\nAudio 1: \nWhat's that sound?\nassistant\nIt is the sound of glass shattering.\nuser\nAudio 2: \nHow about this one?\nassistant\nThroat clearing.",
|
||||
]
|
||||
self.assertEqual(
|
||||
self.processor.batch_decode(output, skip_special_tokens=True),
|
||||
EXPECTED_DECODED_TEXT,
|
||||
)
|
||||
114
tests/models/qwen2_audio/test_processor_qwen2_audio.py
Normal file
114
tests/models/qwen2_audio/test_processor_qwen2_audio.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# Copyright 2024 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 tempfile
|
||||
import unittest
|
||||
|
||||
from transformers import AutoProcessor, AutoTokenizer, Qwen2AudioProcessor, WhisperFeatureExtractor
|
||||
from transformers.testing_utils import require_torch, require_torchaudio
|
||||
|
||||
|
||||
@require_torch
|
||||
@require_torchaudio
|
||||
class Qwen2AudioProcessorTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.checkpoint = "Qwen/Qwen2-Audio-7B-Instruct"
|
||||
self.tmpdirname = tempfile.mkdtemp()
|
||||
|
||||
def test_can_load_various_tokenizers(self):
|
||||
processor = Qwen2AudioProcessor.from_pretrained(self.checkpoint)
|
||||
tokenizer = AutoTokenizer.from_pretrained(self.checkpoint)
|
||||
self.assertEqual(processor.tokenizer.__class__, tokenizer.__class__)
|
||||
|
||||
def test_save_load_pretrained_default(self):
|
||||
tokenizer = AutoTokenizer.from_pretrained(self.checkpoint)
|
||||
processor = Qwen2AudioProcessor.from_pretrained(self.checkpoint)
|
||||
feature_extractor = processor.feature_extractor
|
||||
|
||||
processor = Qwen2AudioProcessor(tokenizer=tokenizer, feature_extractor=feature_extractor)
|
||||
|
||||
processor.save_pretrained(self.tmpdirname)
|
||||
processor = Qwen2AudioProcessor.from_pretrained(self.tmpdirname)
|
||||
|
||||
self.assertEqual(processor.tokenizer.get_vocab(), tokenizer.get_vocab())
|
||||
self.assertEqual(processor.feature_extractor.to_json_string(), feature_extractor.to_json_string())
|
||||
self.assertIsInstance(processor.feature_extractor, WhisperFeatureExtractor)
|
||||
|
||||
def test_tokenizer_integration(self):
|
||||
slow_tokenizer = AutoTokenizer.from_pretrained(self.checkpoint, use_fast=False)
|
||||
fast_tokenizer = AutoTokenizer.from_pretrained(self.checkpoint, from_slow=True, legacy=False)
|
||||
|
||||
prompt = "<|im_start|>system\nAnswer the questions.<|im_end|><|im_start|>user\n<|audio_bos|><|AUDIO|><|audio_eos|>\nWhat is it in this audio?<|im_end|><|im_start|>assistant\n"
|
||||
EXPECTED_OUTPUT = [
|
||||
"<|im_start|>",
|
||||
"system",
|
||||
"Ċ",
|
||||
"Answer",
|
||||
"Ġthe",
|
||||
"Ġquestions",
|
||||
".",
|
||||
"<|im_end|>",
|
||||
"<|im_start|>",
|
||||
"user",
|
||||
"Ċ",
|
||||
"<|audio_bos|>",
|
||||
"<|AUDIO|>",
|
||||
"<|audio_eos|>",
|
||||
"Ċ",
|
||||
"What",
|
||||
"Ġis",
|
||||
"Ġit",
|
||||
"Ġin",
|
||||
"Ġthis",
|
||||
"Ġaudio",
|
||||
"?",
|
||||
"<|im_end|>",
|
||||
"<|im_start|>",
|
||||
"assistant",
|
||||
"Ċ",
|
||||
]
|
||||
print(slow_tokenizer.tokenize(prompt))
|
||||
self.assertEqual(slow_tokenizer.tokenize(prompt), EXPECTED_OUTPUT)
|
||||
self.assertEqual(fast_tokenizer.tokenize(prompt), EXPECTED_OUTPUT)
|
||||
|
||||
def test_chat_template(self):
|
||||
processor = AutoProcessor.from_pretrained(self.checkpoint)
|
||||
expected_prompt = "<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\nAudio 1: <|audio_bos|><|AUDIO|><|audio_eos|>\nWhat's that sound?<|im_end|>\n<|im_start|>assistant\nIt is the sound of glass shattering.<|im_end|>\n<|im_start|>user\nAudio 2: <|audio_bos|><|AUDIO|><|audio_eos|>\nHow about this one?<|im_end|>\n<|im_start|>assistant\n"
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/glass-breaking-151256.mp3",
|
||||
},
|
||||
{"type": "text", "text": "What's that sound?"},
|
||||
],
|
||||
},
|
||||
{"role": "assistant", "content": "It is the sound of glass shattering."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "audio",
|
||||
"audio_url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/f2641_0_throatclearing.wav",
|
||||
},
|
||||
{"type": "text", "text": "How about this one?"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
formatted_prompt = processor.apply_chat_template(messages, add_generation_prompt=True)
|
||||
self.assertEqual(expected_prompt, formatted_prompt)
|
||||
@@ -69,6 +69,7 @@ PRIVATE_MODELS = [
|
||||
"MT5Stack",
|
||||
"UMT5Stack",
|
||||
"Pop2PianoStack",
|
||||
"Qwen2AudioEncoder",
|
||||
"SwitchTransformersStack",
|
||||
"TFDPRSpanPredictor",
|
||||
"MaskFormerSwinModel",
|
||||
|
||||
@@ -173,7 +173,7 @@ MODEL_NAMES_WITH_SAME_CONFIG = {
|
||||
"XLS-R": "Wav2Vec2",
|
||||
"XLSR-Wav2Vec2": "Wav2Vec2",
|
||||
}
|
||||
MODEL_NAMES_TO_IGNORE = ["CLIPVisionModel", "SiglipVisionModel", "ChineseCLIPVisionModel"]
|
||||
MODEL_NAMES_TO_IGNORE = ["CLIPVisionModel", "SiglipVisionModel", "ChineseCLIPVisionModel", "Qwen2AudioEncoder"]
|
||||
|
||||
|
||||
def get_model_table_from_auto_modules() -> str:
|
||||
|
||||
Reference in New Issue
Block a user