LLM-lərin səs imkanları ilə inteqrasiyası fərdiləşdirilmiş müştərilərlə qarşılıqlı əlaqədə yeni imkanlar yaratmışdır.
Bu bələdçi Python, Transformers, Qwen2-Audio-7B-Instruct və Bark istifadə edərək ikitərəfli səsli qarşılıqlı əlaqəni dəstəkləyən yerli LLM serverinin qurulmasında sizə yol göstərəcək.
Başlamazdan əvvəl aşağıdakıları quraşdıracaqsınız:
FFmpeg Linux-da apt install ffmpeg
vasitəsilə quraşdırıla bilər və ya MacOS-da brew install ffmpeg
.
Siz pip istifadə edərək Python asılılıqlarını quraşdıra bilərsiniz: pip install torch transformers accelerate pydub fastapi uvicorn bark python-multipart scipy
Əvvəlcə Python mühitimizi quraq və PyTorch cihazımızı seçək:
import torch device = 'cuda' if torch.cuda.is_available() else 'cpu'
Bu kod CUDA-uyğun (Nvidia) GPU-nun mövcud olub olmadığını yoxlayır və cihazı müvafiq olaraq təyin edir.
Əgər belə bir GPU yoxdursa, PyTorch daha yavaş olan CPU-da işləyəcək.
Daha yeni Apple Silicon cihazları üçün cihaz PyTorch-u Metal üzərində işlətmək üçün
mps
-ə də təyin edilə bilər, lakin PyTorch Metal tətbiqi hərtərəfli deyil.
Açıq mənbəli LLM-lərin əksəriyyəti yalnız mətn daxiletmə və mətn çıxışını dəstəkləyir. Bununla belə, səslə səs sistemi yaratmaq istədiyimiz üçün bu, (1) nitqi LLM-ə daxil edilməzdən əvvəl mətnə çevirmək və (2) LLM çıxışını geri çevirmək üçün daha iki modeldən istifadə etməyimizi tələb edəcək. nitqinə.
Qwen Audio kimi multimodal LLM-dən istifadə etməklə biz nitq daxiletməsini mətn cavabında emal etmək üçün bir modeldən qurtula bilərik və sonra yalnız LLM çıxışını yenidən nitqə çevirən ikinci modeldən istifadə etməliyik.
Bu multimodal yanaşma təkcə emal vaxtı və (V) RAM istehlakı baxımından daha səmərəli deyil, həm də adətən daha yaxşı nəticələr verir, çünki giriş səsi heç bir sürtünmə olmadan birbaşa LLM-ə göndərilir.
Runpod və ya Vast kimi bulud GPU hostunda işləyirsinizsə, yükləmədən əvvəl
export HF_HOME=/workspace/hf
vəexport XDG_CACHE_HOME=/workspace/bark
işlətməklə HuggingFace ev və Bark qovluqlarını həcm yaddaşınıza qurmaq istəyə bilərsiniz. modellər.
from transformers import AutoProcessor, Qwen2AudioForConditionalGeneration model_name = "Qwen/Qwen2-Audio-7B-Instruct" processor = AutoProcessor.from_pretrained(model_name) model = Qwen2AudioForConditionalGeneration.from_pretrained(model_name, device_map="auto").to(device)
Hesablama tələblərimizi azaltmaq üçün burada Qwen Audio model seriyasının kiçik 7B variantından istifadə etməyi seçdik. Bununla belə, siz bu məqaləni oxuduğunuz zaman Qwen daha güclü və daha böyük audio modelləri buraxmış ola bilər. Ən son modelindən istifadə etdiyinizi iki dəfə yoxlamaq üçün HuggingFace-də bütün Qwen modellərinə baxa bilərsiniz.
İstehsal mühiti üçün daha yüksək ötürmə qabiliyyəti üçün vLLM kimi sürətli nəticə çıxarma mühərrikindən istifadə etmək istəyə bilərsiniz.
Bark bir çox dilləri və səs effektlərini dəstəkləyən ən müasir açıq mənbəli mətndən nitqə AI modelidir.
from bark import SAMPLE_RATE, generate_audio, preload_models preload_models()
Barkdan başqa, siz digər açıq mənbəli və ya mülkiyyətli mətndən nitqə modellərdən də istifadə edə bilərsiniz. Nəzərə alın ki, mülkiyyətli olanlar daha performanslı ola bilsələr də, daha yüksək qiymətə gəlirlər. TTS arenası müasir müqayisə aparır .
Hər iki Qwen Audio 7B və Bark yaddaşa yükləndikdə, təxmini (V) RAM istifadəsi 24 GB-dır, ona görə də aparatınızın bunu dəstəklədiyinə əmin olun. Əks halda, yaddaşa qənaət etmək üçün Qwen modelinin kvantlaşdırılmış versiyasından istifadə edə bilərsiniz .
Daxil olan audio və ya mətn daxiletmələrini idarə etmək və audio cavabları qaytarmaq üçün iki marşrutu olan FastAPI server yaradacağıq.
from fastapi import FastAPI, UploadFile, Form from fastapi.responses import StreamingResponse import uvicorn app = FastAPI() @app.post("/voice") async def voice_interaction(file: UploadFile): # TODO return @app.post("/text") async def text_interaction(text: str = Form(...)): # TODO return if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)
Bu server /voice
və /text
son nöqtəsində POST sorğuları vasitəsilə audio faylları qəbul edir.
Daxil olan audionu emal etmək və onu Qwen modeli üçün hazırlamaq üçün ffmpeg-dən istifadə edəcəyik.
from pydub import AudioSegment from io import BytesIO import numpy as np def audiosegment_to_float32_array(audio_segment: AudioSegment, target_rate: int = 16000) -> np.ndarray: audio_segment = audio_segment.set_frame_rate(target_rate).set_channels(1) samples = np.array(audio_segment.get_array_of_samples(), dtype=np.int16) samples = samples.astype(np.float32) / 32768.0 return samples def load_audio_as_array(audio_bytes: bytes) -> np.ndarray: audio_segment = AudioSegment.from_file(BytesIO(audio_bytes)) float_array = audiosegment_to_float32_array(audio_segment, target_rate=16000) return float_array
İşlənmiş audio ilə biz Qwen modelindən istifadə edərək mətn cavabı yarada bilərik. Bu, həm mətn, həm də audio daxiletmələri idarə etməli olacaq.
Önprosessor daxiletməmizi modelin söhbət şablonuna çevirəcək (Qwen işində ChatML).
def generate_response(conversation): 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": audio_array = load_audio_as_array(ele["audio_url"]) audios.append(audio_array) if audios: inputs = processor( text=text, audios=audios, return_tensors="pt", padding=True ).to(device) else: inputs = processor( text=text, return_tensors="pt", padding=True ).to(device) 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] return response
model.generate
funksiyasındakı temperatur kimi nəsil parametrləri ilə oynamaqdan çekinmeyin.
Nəhayət, yaradılan mətn cavabını yenidən nitqə çevirəcəyik.
from scipy.io.wavfile import write as write_wav def text_to_speech(text): audio_array = generate_audio(text) output_buffer = BytesIO() write_wav(output_buffer, SAMPLE_RATE, audio_array) output_buffer.seek(0) return output_buffer
Audio və ya mətn daxiletməsini emal etmək, cavab yaratmaq və sintez edilmiş nitqi WAV faylı kimi qaytarmaq üçün son nöqtələri yeniləyin.
@app.post("/voice") async def voice_interaction(file: UploadFile): audio_bytes = await file.read() conversation = [ { "role": "user", "content": [ { "type": "audio", "audio_url": audio_bytes } ] } ] response_text = generate_response(conversation) audio_output = text_to_speech(response_text) return StreamingResponse(audio_output, media_type="audio/wav") @app.post("/text") async def text_interaction(text: str = Form(...)): conversation = [ {"role": "user", "content": [{"type": "text", "text": text}]} ] response_text = generate_response(conversation) audio_output = text_to_speech(response_text) return StreamingResponse(audio_output, media_type="audio/wav")
Siz köməkçinin cavablarına daha çox nəzarət etmək üçün söhbətlərə sistem mesajı əlavə etməyi də seçə bilərsiniz.
Serverimizi aşağıdakı kimi ping etmək üçün curl
istifadə edə bilərik:
# Audio input curl -X POST http://localhost:8000/voice --output output.wav -F "[email protected]" # Text input curl -X POST http://localhost:8000/text --output output.wav -H "Content-Type: application/x-www-form-urlencoded" -d "text=Hey"
Bu addımları yerinə yetirməklə siz ən müasir modellərdən istifadə edərək ikitərəfli səsli qarşılıqlı əlaqə qura bilən sadə yerli server qurmuşsunuz. Bu quraşdırma daha mürəkkəb səslə işləyən proqramların qurulması üçün əsas ola bilər.
Süni intellektlə işləyən dil modellərindən pul qazanmağın yollarını araşdırırsınızsa, bu potensial tətbiqləri nəzərdən keçirin:
import torch from fastapi import FastAPI, UploadFile, Form from fastapi.responses import StreamingResponse import uvicorn from transformers import AutoProcessor, Qwen2AudioForConditionalGeneration from bark import SAMPLE_RATE, generate_audio, preload_models from scipy.io.wavfile import write as write_wav from pydub import AudioSegment from io import BytesIO import numpy as np device = 'cuda' if torch.cuda.is_available() else 'cpu' model_name = "Qwen/Qwen2-Audio-7B-Instruct" processor = AutoProcessor.from_pretrained(model_name) model = Qwen2AudioForConditionalGeneration.from_pretrained(model_name, device_map="auto").to(device) preload_models() app = FastAPI() def audiosegment_to_float32_array(audio_segment: AudioSegment, target_rate: int = 16000) -> np.ndarray: audio_segment = audio_segment.set_frame_rate(target_rate).set_channels(1) samples = np.array(audio_segment.get_array_of_samples(), dtype=np.int16) samples = samples.astype(np.float32) / 32768.0 return samples def load_audio_as_array(audio_bytes: bytes) -> np.ndarray: audio_segment = AudioSegment.from_file(BytesIO(audio_bytes)) float_array = audiosegment_to_float32_array(audio_segment, target_rate=16000) return float_array def generate_response(conversation): 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": audio_array = load_audio_as_array(ele["audio_url"]) audios.append(audio_array) if audios: inputs = processor( text=text, audios=audios, return_tensors="pt", padding=True ).to(device) else: inputs = processor( text=text, return_tensors="pt", padding=True ).to(device) 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] return response def text_to_speech(text): audio_array = generate_audio(text) output_buffer = BytesIO() write_wav(output_buffer, SAMPLE_RATE, audio_array) output_buffer.seek(0) return output_buffer @app.post("/voice") async def voice_interaction(file: UploadFile): audio_bytes = await file.read() conversation = [ { "role": "user", "content": [ { "type": "audio", "audio_url": audio_bytes } ] } ] response_text = generate_response(conversation) audio_output = text_to_speech(response_text) return StreamingResponse(audio_output, media_type="audio/wav") @app.post("/text") async def text_interaction(text: str = Form(...)): conversation = [ {"role": "user", "content": [{"type": "text", "text": text}]} ] response_text = generate_response(conversation) audio_output = text_to_speech(response_text) return StreamingResponse(audio_output, media_type="audio/wav") if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)