paint-brush
Het hosten van uw eigen AI met tweerichtingsspraakchat is eenvoudiger dan u denkt!door@herahavenai
Nieuwe geschiedenis

Het hosten van uw eigen AI met tweerichtingsspraakchat is eenvoudiger dan u denkt!

door HeraHaven AI10m2025/01/08
Read on Terminal Reader

Te lang; Lezen

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.
featured image - Het hosten van uw eigen AI met tweerichtingsspraakchat is eenvoudiger dan u denkt!
HeraHaven AI HackerNoon profile picture

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:

  • Python : versie 3.9 of hoger.
  • PyTorch : Voor het uitvoeren van de modellen.
  • Transformatoren : Biedt toegang tot het Qwen-model.
  • Versnellen : vereist in sommige omgevingen.
  • FFmpeg & pydub : voor audioverwerking.
  • FastAPI : Om de webserver te maken.
  • Uvicorn : ASGI-server om FastAPI uit te voeren.
  • Schors : Voor tekst-naar-spraaksynthese.
  • Multipart & Scipy : Voor het bewerken van audio.


FFmpeg kan worden geïnstalleerd via apt install ffmpeg op Linux of brew install ffmpeg op MacOS.


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 mps om PyTorch op Metal uit te voeren, maar de PyTorch Metal-implementatie is niet volledig.

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 Runpod of Vast draait, moet u de HuggingFace-start- en Bark-mappen instellen op uw volumeopslag door export HF_HOME=/workspace/hf & export XDG_CACHE_HOME=/workspace/bark uit te voeren voordat u de modellen downloadt.


 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 alle Qwen-modellen bekijken op HuggingFace om te controleren of u hun nieuwste model gebruikt.


Voor een productieomgeving kunt u het beste een snelle inferentie-engine zoals vLLM gebruiken voor een veel hogere doorvoer.

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 kunt u een gekwantiseerde versie van het Qwen-model gebruiken om geheugen te besparen.

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 /voice & /text eindpunt.

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 model.generate -functie.

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 curl als volgt gebruiken om onze server te pingen:


 # 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:

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)