De integratie van LLM's met spraakfuncties heeft nieuwe mogelijkheden gecreëerd voor gepersonaliseerde klantinteracties. Deze handleiding begeleidt u bij het opzetten van een lokale LLM-server die tweerichtingsspraakinteracties ondersteunt met behulp van Python, Transformers, Qwen2-Audio-7B-Instruct en Bark. Vereisten Voordat we beginnen, moet u het volgende geïnstalleerd hebben: : versie 3.9 of hoger. Python : Voor het uitvoeren van de modellen. PyTorch : Biedt toegang tot het Qwen-model. Transformatoren : vereist in sommige omgevingen. Versnellen : voor audioverwerking. FFmpeg & pydub : Om de webserver te maken. FastAPI : ASGI-server om FastAPI uit te voeren. Uvicorn : Voor tekst-naar-spraaksynthese. Schors : Voor het bewerken van audio. Multipart & Scipy FFmpeg kan worden geïnstalleerd via op Linux of op MacOS. apt install ffmpeg brew install ffmpeg Je kunt de Python-afhankelijkheden installeren met behulp van pip: pip install torch transformers accelerate pydub fastapi uvicorn bark python-multipart scipy Stap 1: De omgeving instellen Laten we eerst onze Python-omgeving instellen en ons PyTorch-apparaat kiezen: import torch device = 'cuda' if torch.cuda.is_available() else 'cpu' Deze code controleert of er een CUDA-compatibele (Nvidia) GPU beschikbaar is en stelt het apparaat dienovereenkomstig in. Als een dergelijke GPU niet beschikbaar is, zal PyTorch op een CPU draaien die veel langzamer is. Voor nieuwere Apple Silicon-apparaten kan het apparaat ook worden ingesteld op om PyTorch op Metal uit te voeren, maar de PyTorch Metal-implementatie is niet volledig. mps Stap 2: Het model laden De meeste open-source LLM's ondersteunen alleen tekstinvoer en tekstuitvoer. Omdat we echter een voice-in-voice-out-systeem willen maken, zouden we hiervoor nog twee modellen moeten gebruiken om (1) de spraak om te zetten in tekst voordat deze in onze LLM wordt ingevoerd en (2) de LLM-uitvoer terug om te zetten in spraak. Door een multimodale LLM zoals Qwen Audio te gebruiken, kunnen we met één model spraakinvoer verwerken tot een tekstantwoord, en hoeven we vervolgens slechts een tweede model te gebruiken om de LLM-uitvoer terug te converteren naar spraak. Deze multimodale aanpak is niet alleen efficiënter in termen van verwerkingstijd en (V)RAM-verbruik, maar levert doorgaans ook betere resultaten op, omdat de invoeraudio rechtstreeks en zonder enige wrijving naar de LLM wordt verzonden. Als u op een cloud-GPU-host zoals of draait, moet u de HuggingFace-start- en Bark-mappen instellen op uw volumeopslag door & uit te voeren voordat u de modellen downloadt. Runpod Vast export HF_HOME=/workspace/hf export XDG_CACHE_HOME=/workspace/bark 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) We hebben ervoor gekozen om de kleine 7B-variant van de Qwen Audio-modelserie te gebruiken om onze rekenvereisten te verminderen. Qwen heeft echter mogelijk sterkere en grotere audiomodellen uitgebracht tegen de tijd dat u dit artikel leest. U kunt om te controleren of u hun nieuwste model gebruikt. alle Qwen-modellen bekijken op HuggingFace Voor een productieomgeving kunt u het beste een snelle inferentie-engine zoals gebruiken voor een veel hogere doorvoer. vLLM Stap 3: Het Bark-model laden Bark is een geavanceerd open-source tekst-naar-spraak AI-model dat meerdere talen en geluidseffecten ondersteunt. from bark import SAMPLE_RATE, generate_audio, preload_models preload_models() Naast Bark kunt u ook andere open-source of propriëtaire tekst-naar-spraakmodellen gebruiken. Houd er rekening mee dat de propriëtaire modellen weliswaar beter presteren, maar wel veel duurder zijn. . De TTS-arena houdt een up-to-date vergelijking bij Met zowel Qwen Audio 7B & Bark geladen in het geheugen, is het geschatte (V)RAM-gebruik 24 GB, dus zorg ervoor dat uw hardware dit ondersteunt. Anders om geheugen te besparen. kunt u een gekwantiseerde versie van het Qwen-model gebruiken Stap 4: De FastAPI-server instellen We maken een FastAPI-server met twee routes om binnenkomende audio- of tekstinvoer te verwerken en audioreacties te retourneren. 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) Deze server accepteert audiobestanden via POST-verzoeken op het & eindpunt. /voice /text Stap 5: Verwerken van audio-invoer We gebruiken ffmpeg om de binnenkomende audio te verwerken en voor te bereiden op het Qwen-model. 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 Stap 6: Tekstuele respons genereren met Qwen Met de verwerkte audio kunnen we een tekstuele respons genereren met behulp van het Qwen-model. Dit moet zowel tekst- als audio-inputs verwerken. De preprocessor zet onze invoer om in de chatsjabloon van het model (in het geval van Qwen is dat 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 Experimenteer gerust met de generatieparameters, zoals de temperatuur in de -functie. model.generate Stap 7: Tekst naar spraak converteren met Bark Ten slotte zetten we het gegenereerde tekstantwoord terug om in spraak. 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 Stap 8: Alles integreren in de API's Werk de eindpunten bij om de audio- of tekstinvoer te verwerken, een reactie te genereren en de gesynthetiseerde spraak als een WAV-bestand te retourneren. @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") U kunt er ook voor kiezen om een systeembericht aan de gesprekken toe te voegen, zodat u meer controle krijgt over de reacties van de assistent. Stap 9: Dingen uitproberen We kunnen als volgt gebruiken om onze server te pingen: curl # 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" Conclusie Door deze stappen te volgen, hebt u een eenvoudige lokale server opgezet die tweerichtingsspraakinteracties mogelijk maakt met behulp van state-of-the-art modellen. Deze opstelling kan dienen als basis voor het bouwen van complexere spraakgestuurde applicaties. Toepassingen Als u manieren onderzoekt om AI-gestuurde taalmodellen te gelde te maken, kunt u de volgende mogelijke toepassingen overwegen: Chatbots (bijv. , ); Character AI NSFW AI Chat Telefonische agenten (bijv. , ) Synthflow Bland Automatisering van klantenondersteuning (bijv. , ) Zendesk Forethought Juridische assistenten ( , ) Harvey AI Leya AI Volledige code 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)