
Molti LLM, in particolare quelli open source, si sono generalmente limitati all'elaborazione di testo o, occasionalmente, di testo con immagini (grandi modelli multimodali o LMM). Ma cosa succede se vuoi comunicare con il tuo LLM usando la tua voce? Grazie al progresso delle potenti tecnologie open source di sintesi vocale negli ultimi anni, questo diventa realizzabile.
Entreremo nell'integrazione di Llama 3 con un modello di sintesi vocale, il tutto all'interno di un'interfaccia user-friendly. Questa fusione consente la comunicazione (quasi) in tempo reale con un LLM attraverso la voce. La nostra esplorazione prevede la selezione di Llama 3 8B come LLM, utilizzando il modello di sintesi vocale di Whisper e le funzionalità di NiceGUI, un framework che utilizza FastAPI sul backend e Vue3 sul frontend, interconnesso con socket.io.
Dopo aver letto questo post, sarai in grado di aumentare un LLM con una nuova modalità audio. Ciò ti consentirà di creare un flusso di lavoro end-to-end completo e un'interfaccia utente che ti consenta di utilizzare la tua voce per comandare e richiedere un LLM invece di digitare. Questa funzionalità può rivelarsi particolarmente utile per le applicazioni mobili, dove la digitazione su una tastiera potrebbe non essere così intuitiva come sui desktop. Inoltre, l'integrazione di questa funzionalità può migliorare l'accessibilità della tua app LLM, rendendola più inclusiva per le persone con disabilità.
Ecco gli strumenti e le tecnologie con cui questo progetto ti aiuterà a familiarizzare:
- Lama 3 LLM
- Sussurra STT
- Bella GUI
- (Alcuni) Javascript di base e Vue3
- L'API di replica
In questo progetto integriamo vari componenti per consentire l'interazione vocale con LLM (Large Language Models). In primo luogo, i LLM fungono da nucleo del nostro sistema, elaborando input e generando output basati su un'ampia conoscenza della lingua. Successivamente, Whisper, il modello di sintesi vocale da noi scelto, converte l'input parlato in testo, consentendo una comunicazione fluida con gli LLM. Il nostro frontend, basato su Vue3, incorpora componenti personalizzati all'interno del framework NiceGUI, fornendo un'interfaccia utente intuitiva per l'interazione. Sul backend, il codice personalizzato combinato con FastAPI costituisce la base della funzionalità dell'app. Infine, Replicate.com fornisce l'infrastruttura di hosting per i modelli ML, garantendo accesso affidabile e scalabilità. Insieme, questi componenti convergono per creare un'app di base per l'interazione vocale (quasi) in tempo reale con gli LLM.
NiceGUI non ha ancora un componente di registrazione audio, quindi ne ho contribuito uno al loro set di esempi: https://github.com/zauberzeug/nicegui/tree/main/examples/audio_recorder che riutilizzerò qui.
Per creare tale componente, dobbiamo solo definire un file .vue che definisca ciò che vogliamo:
<template>
<div>
<button class="record-button" @mousedown="startRecording" @mouseup="stopRecording">Hold to speak</button>
</div>
</template>
Qui, fondamentalmente, creiamo un elemento pulsante in cui, quando si fa clic, chiamerà un metodo startRecording
e non appena il mouse sarà alzato chiamerà stopRecording
.
Per questo, definiamo questi metodi principali:
methods: {
async requestMicrophonePermission() {
try {
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
console.error('Error accessing microphone:', error);
}
},
async startRecording() {
try {
if (!this.stream) {
await this.requestMicrophonePermission();
}
this.audioChunks = ();
this.mediaRecorder = new MediaRecorder(this.stream);
this.mediaRecorder.addEventListener('dataavailable', event => {
if (event.data.size > 0) {
this.audioChunks.push(event.data);
}
});
this.mediaRecorder.start();
this.isRecording = true;
} catch (error) {
console.error('Error accessing microphone:', error);
}
},
stopRecording() {
if (this.isRecording) {
this.mediaRecorder.addEventListener('stop', () => {
this.isRecording = false;
this.saveBlob();
// this.playRecordedAudio();
});
this.mediaRecorder.stop();
}
}
Questo codice definisce tre metodi: requestMicrophonePermission
, startRecording
E stopRecording
. IL requestMicrophonePermission
Il metodo tenta in modo asincrono di accedere al microfono dell'utente utilizzando navigator.mediaDevices.getUserMedia
gestire eventuali errori che potrebbero verificarsi. IL startRecording
Il metodo, anch'esso asincrono, inizializza la registrazione configurando un registratore multimediale con il flusso microfonico ottenuto, mentre il stopRecording
Il metodo interrompe il processo di registrazione e salva l'audio registrato.
Una volta terminata la registrazione, questo codice emetterà anche un evento denominato 'audio_ready'
insieme a dati audio codificati base64. All'interno del metodo, un nuovo FileReader
l'oggetto viene creato. Dopo aver caricato il file, il onload
viene attivato l'evento, estraendo i dati base64 dal risultato del file caricato. Infine, questi dati base64 vengono emessi come parte del file 'audio_ready'
evento utilizzando $emit()
funzione con il tasto 'audioBlobBase64'
contenente i dati base64.
emitBlob() {
const reader = new FileReader();
reader.onload = () => {
const base64Data = reader.result.split(',')(1); // Extracting base64 data from the result
this.$emit('audio_ready', { audioBlobBase64: base64Data });
};
}
Questo evento verrà ricevuto dal backend insieme ai dati base64.
Il backend sarà fondamentalmente il collante che lega l'input dell'utente con i modelli ML ospitati in Replicate.
Utilizzeremo due modelli principali per il nostro progetto:
openai/whisper
: questo modello da sequenza a sequenza di Transformer è dedicato alle attività di sintesi vocale, abili nella conversione dell'audio in testo. Addestrato su varie attività di elaborazione vocale, come riconoscimento vocale multilingue, traduzione vocale, identificazione della lingua parlata e rilevamento dell'attività vocale.meta/meta-llama-3-8b-instruct
: La famiglia Llama 3, inclusa questa variante da 8 miliardi di parametri, è una famiglia LLM creata da Meta. Questi modelli di testo generativi preaddestrati e ottimizzati per le istruzioni sono specificamente ottimizzati per i casi d'uso dei dialoghi.
Per il primo definiamo una semplice funzione che prende come input l'audio base64 e chiama l'api replicata:
def transcribe_audio(base64_audio):
audio_bytes = base64.b64decode(base64_audio)
prediction = replicate.run(
f"{MODEL_STT}:{VERSION}", input={"audio": io.BytesIO(audio_bytes), **ARGS}
)
text = "\n".join(segment("text") for segment in prediction.get("segments", ()))
return text
Che può essere utilizzato facilmente come:
with open("audio.ogx", "rb") as f:
content = f.read()_base64_audio = base64.b64encode(content).decode("utf-8")
_prediction = transcribe_audio(_base64_audio)
pprint.pprint(_prediction)
Quindi, per il secondo componente, definiamo una funzione simile:
def call_llm(prompt):
prediction = replicate.stream(MODEL_LLM, input={"prompt": prompt, **ARGS})
output_text = ""
for event in prediction:
output_text += str(event)
return output_text
Ciò interrogherà LLM e trasmetterà le risposte da esso token per token nel file output_text
Successivamente, definiamo il flusso di lavoro completo nel seguente metodo asincrono:
async def run_workflow(self, audio_data):
self.prompt = "Transcribing audio..."
self.response_html = ""
self.audio_byte64 = audio_data.args("audioBlobBase64")
self.prompt = await run.io_bound(
callback=transcribe_audio, base64_audio=self.audio_byte64
)
self.response_html = "Calling LLM..."
self.response = await run.io_bound(callback=call_llm, prompt=self.prompt)
self.response_html = self.response.replace("\n", "</br>")
ui.notify("Result Ready!")
Una volta che i dati audio sono pronti, prima trascriviamo l'audio, quindi, una volta fatto ciò, chiamiamo LLM e visualizziamo la sua risposta. Le variabili self.prompt
E self.response_html
sono legati ad altri componenti NiceGUI che vengono aggiornati automaticamente. Se vuoi saperne di più su come funziona, puoi consultare un tutorial precedente che ho scritto:
Il risultato completo del flusso di lavoro è simile al seguente:
Piuttosto pulito!
Ciò che richiede più tempo qui è la trascrizione audio. L'endpoint è sempre attivo durante la replica quando lo controllo, ma questa versione è la v3 di grandi dimensioni che non è quella più veloce. I file audio sono anche molto più pesanti da spostare rispetto al testo semplice, quindi questo contribuisce alla piccola latenza.
Appunti:
- Dovrai impostare REPLICATE_API_TOKEN prima di eseguire questo codice. Puoi ottenerlo registrandoti su replicate.com. Ho potuto eseguire questi esperimenti utilizzando il livello gratuito.
- A volte la trascrizione subisce un leggero ritardo e viene restituita dopo un breve periodo di “coda”.
- Il codice è a: https://github.com/CVxTz/LLM-Voice Il punto di ingresso è main.py.
In sintesi, l'integrazione di modelli open source come Whisper e Llama 3 ha notevolmente semplificato l'interazione vocale con gli LLM, rendendola altamente accessibile e facile da usare. Questa combinazione è particolarmente comoda per gli utenti che preferiscono non digitare, offrendo un'esperienza fluida. Questa però è solo la prima parte del progetto; ci saranno ulteriori miglioramenti in arrivo. I prossimi passi includono l'abilitazione della comunicazione vocale bidirezionale, la possibilità di utilizzare modelli locali per una maggiore privacy, il miglioramento del design complessivo per un'interfaccia più raffinata, l'implementazione di conversazioni multi-turno per interazioni più naturali, lo sviluppo di un'applicazione desktop per una più ampia accessibilità, e ottimizzazione della latenza per l'elaborazione da voce a testo in tempo reale. Con questi miglioramenti, l'obiettivo è quello di migliorare l'esperienza dell'interazione vocale con gli LLM, rendendola più facile da usare per coloro, come me, a cui non piace molto digitare.
Fammi sapere su quali miglioramenti pensi che dovrei lavorare prima.
Fonte: towardsdatascience.com