From fcd8b6b4c06d29547bff0844dd0670ab2cd5945e Mon Sep 17 00:00:00 2001 From: gty1829 <3132548401@qq.com> Date: Wed, 6 Aug 2025 16:07:43 +0800 Subject: [PATCH 1/3] add asr module --- .../SpeechTranscription/audio/test.wav | Bin 0 -> 101388 bytes .../pipeline_speechtranscription.jsonl | 2 + .../generate/SpeechTranscription/__init__.py | 0 .../SpeechTranscription/speech_transcriptor | 165 ++++++++++++++++++ dataflow/operators/generate/__init__.py | 3 + dataflow/serving/LocalModelLALMServing.py | 125 +++++++++++++ .../speechtranscription_pipeline.py | 32 ++++ pyproject.toml | 1 + 8 files changed, 328 insertions(+) create mode 100644 dataflow/example/SpeechTranscription/audio/test.wav create mode 100644 dataflow/example/SpeechTranscription/pipeline_speechtranscription.jsonl create mode 100644 dataflow/operators/generate/SpeechTranscription/__init__.py create mode 100644 dataflow/operators/generate/SpeechTranscription/speech_transcriptor create mode 100644 dataflow/serving/LocalModelLALMServing.py create mode 100644 dataflow/statics/pipelines/gpu_pipelines/speechtranscription_pipeline.py diff --git a/dataflow/example/SpeechTranscription/audio/test.wav b/dataflow/example/SpeechTranscription/audio/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..4439a1e5fbd4bb0d3f307d8edf2b9b1abb7a1429 GIT binary patch literal 101388 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 L;8AAw_}c#g^B(=F literal 0 HcmV?d00001 diff --git a/dataflow/example/SpeechTranscription/pipeline_speechtranscription.jsonl b/dataflow/example/SpeechTranscription/pipeline_speechtranscription.jsonl new file mode 100644 index 00000000..87f7674f --- /dev/null +++ b/dataflow/example/SpeechTranscription/pipeline_speechtranscription.jsonl @@ -0,0 +1,2 @@ +{"raw_content": "../example_data/SpeechTranscription/audio/test.wav"} +{"raw_content": "https://raw.githubusercontent.com/FireRedTeam/FireRedASR/main/examples/wav/IT0011W0001.wav"} \ No newline at end of file diff --git a/dataflow/operators/generate/SpeechTranscription/__init__.py b/dataflow/operators/generate/SpeechTranscription/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dataflow/operators/generate/SpeechTranscription/speech_transcriptor b/dataflow/operators/generate/SpeechTranscription/speech_transcriptor new file mode 100644 index 00000000..13a774fd --- /dev/null +++ b/dataflow/operators/generate/SpeechTranscription/speech_transcriptor @@ -0,0 +1,165 @@ +from dataflow.utils.registry import OPERATOR_REGISTRY +from dataflow import get_logger + +from dataflow.utils.storage import DataFlowStorage +from dataflow.core import OperatorABC +from dataflow.core import LLMServingABC + +import os +import math +import warnings +import base64 +from io import BytesIO +from typing import List, Optional, Union, Dict, Tuple + +import librosa +import numpy as np +import requests + +# 不重采样 +DEFAULT_SR = None + +def _read_audio_remote(path: str, sr: Optional[int] = DEFAULT_SR) -> Tuple[np.ndarray, int]: + url = path + resp = requests.get(url, stream=True) + + audio_bytes = BytesIO(resp.content) + y, sr = librosa.load(audio_bytes, sr=sr) + return y, sr + +def _read_audio_local(path: str, sr: Optional[int] = DEFAULT_SR) -> Tuple[np.ndarray, int]: + return librosa.load(path, sr=sr, mono=True) + +def _read_audio_bytes(data: bytes, sr: Optional[int] = DEFAULT_SR) -> Tuple[np.ndarray, int]: + return librosa.load(BytesIO(data), sr=sr, mono=True) + +def _read_audio_base64(b64: str, sr: Optional[int] = DEFAULT_SR) -> Tuple[np.ndarray, int]: + header, b64data = b64.split(",", 1) + data = base64.b64decode(b64data) + return _read_audio_bytes(data, sr=sr) + +def process_audio_info( + conversations: List[dict] | List[List[dict]], # 这个conversation对应的是vllm中的messages列表(对应的是conversation_to_message函数的message) + sampling_rate: Optional[int] +) -> Tuple[ + Optional[List[np.ndarray]], + Optional[List[int]], + Optional[List[str]] +]: + """ + 类似于 vision 的 process_vision_info,从 message 列表中提取音频输入。 + 支持三种格式输入: + - 本地或 http(s) URL 路径(通过 librosa 接口处理) + - base64 编码 (data:audio/…;base64,…) + - 直接传入 bytes 对象 + 返回二元组: + - audio_arrays: 解码后的 waveform (List[np.ndarray]) + - sample_rates: 采样率列表 (List[int]) + """ + if isinstance(conversations, list) and conversations and isinstance(conversations[0], dict): + # 单条 conversaion + conversations = [conversations] # conversations被统一为List[List[dict]] + + audio_arrays = [] + sampling_rates = [] + + for conv in conversations: + for msg in conv: + if not isinstance(msg.get("content"), list): + continue + for ele in msg["content"]: + if ele.get("type") != "audio": + continue + aud = ele.get("audio") + if isinstance(aud, str): + if aud.startswith("data:audio") and "base64," in aud: + arr, sr = _read_audio_base64(aud, sr=sampling_rate) + audio_arrays.append(arr) + sampling_rates.append(sr) + elif aud.startswith("http://") or aud.startswith("https://"): + # 使用 librosa 支持远程路径 + arr, sr = _read_audio_remote(aud, sr=sampling_rate) + audio_arrays.append(arr) + sampling_rates.append(sr) + else: + # 本地路径 + arr, sr = _read_audio_local(aud, sr=sampling_rate) + audio_arrays.append(arr) + sampling_rates.append(sr) + elif isinstance(aud, (bytes, bytearray)): + arr, sr = _read_audio_bytes(bytes(aud), sr=sampling_rate) + audio_arrays.append(arr) + sampling_rates.append(sr) + else: + raise ValueError(f"Unsupported audio type: {type(aud)}") + + if not audio_arrays: + return None, None + return audio_arrays, sampling_rates + +@OPERATOR_REGISTRY.register() +class SpeechTranscriptor(OperatorABC): + def __init__( + self, + llm_serving: LLMServingABC, + system_prompt: str = "You are a helpful assistant", + ): + self.logger = get_logger() + self.llm_serving = llm_serving + self.system_prompt = system_prompt + + def run(self, storage: DataFlowStorage, input_key: str = "raw_content", output_key: str = "generated_content"): + self.input_key, self.output_key = input_key, output_key + self.logger.info("Running Speech Transcriptor...") + + dataframe = storage.read('dataframe') + self.logger.info(f"Loading, number of rows: {len(dataframe)}") + + conversations = [] + for index, row in dataframe.iterrows(): + path_or_url = row.get(self.input_key, '') + conversation = [ + { + "role": "system", + "content": self.system_prompt + }, + { + "role": "user", + "content": [ + { + "type": "audio", + "audio": path_or_url + }, + { + "type": "text", + "text": "请把语音转录为中文文本" + } + ] + } + ] + conversations.append(conversation) + + user_inputs = [self.llm_serving.processor.apply_chat_template( + conversation, + tokenize=False, + add_generation_prompt=True, + add_audio_id = True + ) for conversation in conversations] + print(user_inputs) + + + audio_arrays, sampling_rates = process_audio_info(conversations=conversations, sampling_rate=16000) + audio_inputs = [(audio_array, sampling_rate) for audio_array, sampling_rate in zip(audio_arrays, sampling_rates)] + + transcriptions = self.llm_serving.generate_from_input( + user_inputs=user_inputs, + audio_inputs=audio_inputs, + system_prompt=self.system_prompt + ) + + dataframe[self.output_key] = transcriptions + output_file = storage.write(dataframe) + self.logger.info(f"Saving to {output_file}") + self.logger.info("Speech Transcriptor done") + + return output_key diff --git a/dataflow/operators/generate/__init__.py b/dataflow/operators/generate/__init__.py index c2e94f16..70506d1c 100644 --- a/dataflow/operators/generate/__init__.py +++ b/dataflow/operators/generate/__init__.py @@ -49,6 +49,9 @@ #VQA from .VQA.PromptedVQAGenerator import PromptedVQAGenerator + + # SpeechTranscription + from .SpeechTranscription.speech_transcriptor import SpeechTranscriptor else: import sys from dataflow.utils.registry import LazyLoader, generate_import_structure_from_type_checking diff --git a/dataflow/serving/LocalModelLALMServing.py b/dataflow/serving/LocalModelLALMServing.py new file mode 100644 index 00000000..ac5ba04c --- /dev/null +++ b/dataflow/serving/LocalModelLALMServing.py @@ -0,0 +1,125 @@ +import os +import torch +from dataflow import get_logger +from huggingface_hub import snapshot_download +from dataflow.core import LLMServingABC +from transformers import AutoProcessor +from typing import Optional, Union, List, Dict, Any + +class LocalModelLALMServing_vllm(LLMServingABC): + ''' + A class for generating text using vllm, with model from huggingface or local directory + ''' + def __init__(self, + hf_model_name_or_path: str = None, + hf_cache_dir: str = None, + hf_local_dir: str = None, + vllm_tensor_parallel_size: int = 1, + vllm_temperature: float = 0.7, + vllm_top_p: float = 0.9, + vllm_max_tokens: int = 1024, + vllm_top_k: int = 40, + vllm_repetition_penalty: float = 1.0, + vllm_seed: int = 42, + vllm_max_model_len: int = None, + vllm_gpu_memory_utilization: float=0.9, + ): + + self.load_model( + hf_model_name_or_path=hf_model_name_or_path, + hf_cache_dir=hf_cache_dir, + hf_local_dir=hf_local_dir, + vllm_tensor_parallel_size=vllm_tensor_parallel_size, + vllm_temperature=vllm_temperature, + vllm_top_p=vllm_top_p, + vllm_max_tokens=vllm_max_tokens, + vllm_top_k=vllm_top_k, + vllm_repetition_penalty=vllm_repetition_penalty, + vllm_seed=vllm_seed, + vllm_max_model_len=vllm_max_model_len, + vllm_gpu_memory_utilization=vllm_gpu_memory_utilization, + ) + + def load_model(self, + hf_model_name_or_path: str = None, + hf_cache_dir: str = None, + hf_local_dir: str = None, + vllm_tensor_parallel_size: int = 1, + vllm_temperature: float = 0.7, + vllm_top_p: float = 0.9, + vllm_max_tokens: int = 1024, + vllm_top_k: int = 40, + vllm_repetition_penalty: float = 1.0, + vllm_seed: int = 42, + vllm_max_model_len: int = None, + vllm_gpu_memory_utilization: float=0.9, + ): + self.logger = get_logger() + if hf_model_name_or_path is None: + raise ValueError("hf_model_name_or_path is required") + elif os.path.exists(hf_model_name_or_path): + self.logger.info(f"Using local model path: {hf_model_name_or_path}") + self.real_model_path = hf_model_name_or_path + else: + self.logger.info(f"Downloading model from HuggingFace: {hf_model_name_or_path}") + self.real_model_path = snapshot_download( + repo_id=hf_model_name_or_path, + cache_dir=hf_cache_dir, + local_dir=hf_local_dir, + ) + # get the model name from the real_model_path + self.model_name = os.path.basename(self.real_model_path) + self.processor = AutoProcessor.from_pretrained(self.real_model_path, cache_dir=hf_cache_dir) + print(f"Model name: {self.model_name}") + + + # Import vLLM and set up the environment for multiprocessing + # vLLM requires the multiprocessing method to be set to spawn + try: + from vllm import LLM,SamplingParams + except: + raise ImportError("please install vllm first like 'pip install open-dataflow[vllm]'") + # Set the environment variable for vllm to use spawn method for multiprocessing + # See https://docs.vllm.ai/en/v0.7.1/design/multiprocessing.html + os.environ['VLLM_WORKER_MULTIPROC_METHOD'] = "spawn" + + self.sampling_params = SamplingParams( + temperature=vllm_temperature, + top_p=vllm_top_p, + max_tokens=vllm_max_tokens, + top_k=vllm_top_k, + repetition_penalty=vllm_repetition_penalty, + seed=vllm_seed + ) + + self.llm = LLM( + model=self.real_model_path, + tensor_parallel_size=vllm_tensor_parallel_size, + max_model_len=vllm_max_model_len, + gpu_memory_utilization=vllm_gpu_memory_utilization, + ) + self.logger.success(f"Model loaded from {self.real_model_path} by vLLM backend") + + def generate_from_input(self, + user_inputs: list[str], + audio_inputs: list, + system_prompt: str = "You are a helpful assistant", + ) -> list[str]: + + + full_prompts = [] + for user_input, audio_input in zip(user_inputs, audio_inputs): + full_prompts.append({ + 'prompt': user_input, + 'multi_modal_data': {'audio': audio_input} + }) + + responses = self.llm.generate(full_prompts, self.sampling_params) + return [output.outputs[0].text for output in responses] + + def cleanup(self): + del self.llm + import gc; + gc.collect() + torch.cuda.empty_cache() + \ No newline at end of file diff --git a/dataflow/statics/pipelines/gpu_pipelines/speechtranscription_pipeline.py b/dataflow/statics/pipelines/gpu_pipelines/speechtranscription_pipeline.py new file mode 100644 index 00000000..9d577bcb --- /dev/null +++ b/dataflow/statics/pipelines/gpu_pipelines/speechtranscription_pipeline.py @@ -0,0 +1,32 @@ +from dataflow.operators.generate.SpeechTranscription.speech_transcriptor import SpeechTranscriptor +from dataflow.serving import LocalModelLALMServing_vllm +from dataflow.utils.storage import FileStorage + +class SpeechTranscription_GPUPipeline(): + def __init__(self): + self.storage = FileStorage( + first_entry_file_name="../example_data/SpeechTranscription/pipeline_speechtranscription.jsonl", + cache_path="./cache", + file_name_prefix="dataflow_cache_step", + cache_type="jsonl", + ) + + self.llm_serving = LocalModelLALMServing_vllm( + hf_model_name_or_path='/data0/gty/models/Qwen2-Audio-7B-Instruct', + vllm_tensor_parallel_size=4, + vllm_max_tokens=8192, + ) + self.speech_transcriptor = SpeechTranscriptor( + llm_serving = self.llm_serving, + system_prompt="你是一个专业的翻译员,你需要将语音转录为文本。" + ) + + def forward(self): + self.speech_transcriptor.run( + storage=self.storage.step(), + input_key="raw_content" + ) + +if __name__ == "__main__": + pipeline = SpeechTranscription_GPUPipeline() + pipeline.forward() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d96cd5d2..98805e65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,3 +73,4 @@ agent = [ "uvicorn", "sseclient-py", ] +audio = ['librosa', 'soundfile'] \ No newline at end of file From ce36d584b3372a4031000643ce9abefbfea16b04 Mon Sep 17 00:00:00 2001 From: gty1829 <3132548401@qq.com> Date: Wed, 6 Aug 2025 16:11:10 +0800 Subject: [PATCH 2/3] add serving __init__.py --- dataflow/serving/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dataflow/serving/__init__.py b/dataflow/serving/__init__.py index c7c56c2b..87d486a7 100644 --- a/dataflow/serving/__init__.py +++ b/dataflow/serving/__init__.py @@ -3,11 +3,13 @@ from .LocalModelLLMServing import LocalModelLLMServing_sglang from .GoogleAPIServing import PerspectiveAPIServing from .LiteLLMServing import LiteLLMServing +from .LocalModelLALMServing import LocalModelLALMServing_vllm __all__ = [ "APILLMServing_request", "LocalModelLLMServing_vllm", "LocalModelLLMServing_sglang", "PerspectiveAPIServing", - "LiteLLMServing" + "LiteLLMServing", + "LocalModelLALMServing_vllm" ] \ No newline at end of file From 513c6cbc54e7eba83ae61d0519098007ffa13b5a Mon Sep 17 00:00:00 2001 From: gty1829 <3132548401@qq.com> Date: Wed, 6 Aug 2025 17:10:37 +0800 Subject: [PATCH 3/3] change file name --- .../{speech_transcriptor => speech_transcriptor.py} | 0 requirements.txt | 4 ++++ 2 files changed, 4 insertions(+) rename dataflow/operators/generate/SpeechTranscription/{speech_transcriptor => speech_transcriptor.py} (100%) diff --git a/dataflow/operators/generate/SpeechTranscription/speech_transcriptor b/dataflow/operators/generate/SpeechTranscription/speech_transcriptor.py similarity index 100% rename from dataflow/operators/generate/SpeechTranscription/speech_transcriptor rename to dataflow/operators/generate/SpeechTranscription/speech_transcriptor.py diff --git a/requirements.txt b/requirements.txt index e9d8af2b..4a0fae3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -63,3 +63,7 @@ requests termcolor uvicorn sseclient-py + +# speech +librosa +soundfile \ No newline at end of file