Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
Nuvembre 30 - dicembre 1 in Nizhny Novgorod hè stata Hackathon OpenVINO. I participanti sò stati dumandati à creà un prototipu di una suluzione di produttu cù u toolkit Intel OpenVINO. L'urganizatori prupunenu una lista di temi apprussimativi chì puderanu esse guidati da quandu sceglie un compitu, ma a decisione finale hè stata cù e squadre. Inoltre, l'usu di mudelli chì ùn sò micca inclusi in u pruduttu hè statu incuraghjitu.
In questu articulu vi dicu cumu avemu creatu u nostru prototipu di u pruduttu, cù quale avemu eventualmente pigliatu u primu postu.
Più di 10 squadre anu participatu à l'hackathon. Hè bellu chì alcuni di elli venenu da altre regioni. U locu per l'hackathon era u cumplessu "Kremlinsky on Pochain", induve antichi ritratti di Nizhny Novgorod eranu appiccicati in un entourage! (Vi ricordu chì in u mumentu l'uffiziu cintrali di Intel hè situatu in Nizhny Novgorod). I participanti anu datu 26 ore per scrive u codice, è à a fine avianu a prisentà a so suluzione. Un vantaghju separatu era a prisenza di una sessione demo per assicurà chì tuttu ciò chì pianificatu hè statu implementatu veramente è ùn resta micca idee in a presentazione. Merch, snacks, food, tuttu era ancu quì !
Una di e parte più difficili di a preparazione per un hackathon in forma libera hè di sceglie una sfida. Avemu subitu decisu di vene cun qualcosa chì ùn era micca ancu in u pruduttu, postu chì l'annunziu hà dettu chì questu era assai benvenutu.
Dopu avè analizatu mudelli, chì sò inclusi in u pruduttu in a versione attuale, ghjunghjemu à a cunclusione chì a maiò parte di elli risolve diversi prublemi di visione di l'informatica. Inoltre, hè assai difficiuli di vene cun un prublema in u campu di a visione di l'informatica chì ùn pò micca esse risolta cù OpenVINO, è ancu s'ellu si pò inventà, hè difficiule di truvà mudelli pre-furmati in u duminiu publicu. Decidimu di scavà in un'altra direzzione - versu l'elaborazione di a parolla è l'analisi. Cunsideremu un travagliu interessante di ricunnosce l'emozioni da a parolla. Ci vole à dì chì OpenVINO hà digià un mudellu chì determina l'emozioni di una persona nantu à a so faccia, ma:
In teoria, hè pussibule di creà un algoritmu cumminatu chì travaglià nantu à u sonu è l'imaghjini, chì deve dà un aumentu di precisione.
E camere sò generalmente un angolo di vista strettu; più di una camera hè necessaria per copre una grande zona; u sonu ùn hà micca una limitazione cusì.
Sviluppemu l'idea: pigliemu l'idea per u segmentu di vendita cum'è una basa. Pudete misurà a satisfaczione di i clienti in i cassi di a tenda. Se unu di i clienti ùn hè micca cuntentu cù u serviziu è cumencia à elevà u so tonu, pudete immediatamente chjamà l'amministratore per aiutu.
In questu casu, avemu bisognu di aghjunghje a ricunniscenza di a voce umana, questu ci permetterà di distingue l'impiegati di a tenda da i clienti è furnisce analitiche per ogni individuu. Ebbè, in più, serà pussibule analizà u cumpurtamentu di l'impiegati di a tenda stessi, evaluà l'atmosfera in a squadra, sona bè!
Formulemu i requisiti per a nostra suluzione:
Small size di u dispusitivu di destinazione
Operazione in tempu reale
Prezzo bassu
Scalabilità faciule
In u risultatu, selezziunate Raspberry Pi 3 c cum'è u dispositivu di destinazione Intel NCS 2.
Quì hè impurtante di nutà una caratteristica impurtante di NCS - funziona megliu cù l'architetture standard CNN, ma se avete bisognu di eseguisce un mudellu cù strati persunalizati nantu à questu, allora aspettate l'ottimisazione di livellu bassu.
Ci hè solu una piccula cosa da fà: avete bisognu di un microfonu. Un microfonu USB regulare farà, ma ùn pare micca bè cù l'RPI. Ma ancu quì a suluzione literalmente "si trova vicinu". Per arregistrà a voce, decidemu d'utilizà a tavola Voice Bonnet da u kit Google AIY Voice Kit, nantu à quale ci hè un microfonu stereo cablatu.
Scaricate Raspbian da Repositoriu di prughjetti AIY è caricate in una unità flash, verificate chì u micrufonu funziona cù u cumandimu seguitu (registrarà l'audio 5 seconde di longu è salvà in un schedariu):
arecord -d 5 -r 16000 test.wav
Aghju da nutà immediatamente chì u microfonu hè assai sensitivu è coglie bè u sonu. Per riparà questu, andemu à alsamixer, selezziunà i dispusitivi Capture è riduce u nivellu di signale di input à 50-60%.
Mudificàmu u corpu cù un schedariu è tuttu si adatta, pudete ancu chjude cù una tapa
Aghjunghjendu un buttone indicatore
Mentre pigliate u Kit di voce AIY, ricurdemu chì ci hè un buttone RGB, a retroilluminazione di quale pò esse cuntrullata da u software. Cerchemu "Google AIY Led" è truvamu documentazione: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Perchè ùn aduprà stu buttone per vede l'emozione ricunnisciuta, avemu solu 7 classi, è u buttone hà 8 culori, basta!
Cunnetteremu u buttone via GPIO à Voice Bonnet, caricate e librerie necessarie (sò digià stallatu in u kit di distribuzione da i prughjetti AIY)
from aiy.leds import Leds, Color
from aiy.leds import RgbLeds
Creemu un dittu in quale ogni emozione avarà un culore currispundente in a forma di un Tuple RGB è un ughjettu di a classa aiy.leds.Leds, attraversu quale aghjurnà u culore:
Useremu pyaudio per catturà u flussu da u microfonu è webrtcvad per filtrà u rumore è detectà a voce. Inoltre, creeremu una fila à a quale aghjunghje è sguassate asincronamente estratti di voce.
Siccomu webrtcvad hà una limitazione di a dimensione di u frammentu furnitu - deve esse uguali à 10/20/30ms, è a furmazione di u mudellu per a ricunniscenza di l'emozioni (cum'è avemu da amparà dopu) hè stata realizata nantu à un dataset 48kHz, avemu da esse. catturà pezzi di taglia 48000×20ms/1000×1(mono)=960 bytes. Webrtcvad restituverà True / False per ognuna di sti chunks, chì currisponde à a prisenza o l'absenza di un votu in u chunk.
Implementemu a seguente logica:
Aggiungheremu à a lista quelli pezzi induve ci hè un votu; se ùn ci hè votu, allora aumenteremu u contatore di pezzi vacanti.
Se u contatore di pezzi vioti hè > = 30 (600 ms), allora fighjemu a dimensione di a lista di pezzi accumulati; s'ellu hè > 250, allora l'aghjunghje à a fila; se no, cunsideremu chì a lunghezza di u record ùn hè micca abbastanza per alimentallu à u mudellu per identificà u parlante.
Se u contatore di pezzi vioti hè sempre < 30, è a dimensione di a lista di pezzi accumulati supera 300, allora aghjunghjemu u fragmentu à a fila per una predizione più precisa. (perchè l'emozioni tendenu à cambià cù u tempu)
def to_queue(frames):
d = np.frombuffer(b''.join(frames), dtype=np.int16)
return d
framesQueue = queue.Queue()
def framesThreadBody():
CHUNK = 960
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 48000
p = pyaudio.PyAudio()
vad = webrtcvad.Vad()
vad.set_mode(2)
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
false_counter = 0
audio_frame = []
while process:
data = stream.read(CHUNK)
if not vad.is_speech(data, RATE):
false_counter += 1
if false_counter >= 30:
if len(audio_frame) > 250:
framesQueue.put(to_queue(audio_frame,timestamp_start))
audio_frame = []
false_counter = 0
if vad.is_speech(data, RATE):
false_counter = 0
audio_frame.append(data)
if len(audio_frame) > 300:
framesQueue.put(to_queue(audio_frame,timestamp_start))
audio_frame = []
Hè u tempu di circà mudelli pre-furmati in u duminiu publicu, andate à github, Google, ma ricordate chì avemu una limitazione di l'architettura utilizata. Questa hè una parte piuttostu difficiule, perchè avete da pruvà i mudelli nantu à i vostri dati di input, è in più, cunvertisce in u formatu internu di OpenVINO - IR (Rappresentazione Intermedia). Pruvamu circa 5-7 suluzioni diffirenti da github, è se u mudellu per a ricunniscenza di l'emozioni hà travagliatu immediatamente, allora cù u ricunniscenza di voce avemu avutu aspittà più longu - usanu architetture più cumplesse.
Fighjemu nantu à i seguenti:
Emozioni da a voce - https://github.com/alexmuhr/Voice_Emotion
Funziona secondu u principiu seguente: l'audio hè tagliatu in passaghji di una certa dimensione, per ognuna di sti passaghji selezziunate MFCC è poi sottumettenu cum'è input à CNN
ricunniscenza di voce - https://github.com/linhdvu14/vggvox-speaker-identification
Quì, invece di MFCC, avemu travagliatu cù un spettrogramma, dopu à FFT avemu alimentatu u signale à CNN, induve à l'output avemu una rapprisentazione vettoriale di a voce.
Dopu avemu da parlà di cunvertisce mudelli, cuminciendu cù a teoria. OpenVINO include parechji moduli:
Open Model Zoo, mudelli chì ponu esse utilizati è inclusi in u vostru pruduttu
Model Optimzer, grazia à quale pudete cunvertisce un mudellu da diversi formati di framework (Tensorflow, ONNX etc.) in u formatu di Rappresentazione Intermedia, cù quale avemu da travaglià più.
Inference Engine vi permette di eseguisce mudelli in formatu IR nantu à i processori Intel, Myriad chips è Neural Compute Stick acceleratori.
A versione più efficiente di OpenCV (cù supportu Inference Engine)
Ogni mudellu in u furmatu IR hè discrittu da dui schedari: .xml è .bin.
I mudelli sò cunvertiti in furmatu IR via Model Optimizer cum'è seguente:
--data_type permette di selezziunà u furmatu di dati cù quale u mudellu hà da travaglià. FP32, FP16, INT8 sò supportati. Sceglie u tipu di dati ottimali pò dà un bonu impulsu di rendiment. --input_shape indica a dimensione di i dati di input. A capacità di cambià in modu dinamicu pare esse presente in l'API C++, ma ùn avemu micca scavatu cusì luntanu è solu solu solu per unu di i mudelli.
In seguitu, pruvemu di carricà u mudellu digià cunvertitu in formatu IR via u modulu DNN in OpenCV è invià à ellu.
import cv2 as cv
emotionsNet = cv.dnn.readNet('emotions_model.bin',
'emotions_model.xml')
emotionsNet.setPreferableTarget(cv.dnn.DNN_TARGET_MYRIAD)
L'ultima linea in questu casu permette di redirige i calculi à u Neural Compute Stick, i calculi basi sò realizati nantu à u processatore, ma in u casu di u Raspberry Pi questu ùn hà micca travagliatu, avete bisognu di un bastone.
In seguitu, a logica hè a siguenti: dividemu u nostru audio in Windows di una certa dimensione (per noi hè 0.4 s), cunvertemu ognuna di sti finestri in MFCC, chì poi alimentamu à a griglia:
emotionsNet.setInput(MFCC_from_window)
result = emotionsNet.forward()
In seguitu, pigliemu a classa più cumuna per tutti i finestri. Una suluzione simplice, ma per un hackathon ùn avete micca bisognu di cullà cù qualcosa troppu astrusu, solu s'ellu avete tempu. Avemu sempre assai travagliu da fà, allora andemu avanti - avemu da trattà cun ricunniscenza di voce. Hè necessariu di fà qualchì tipu di basa di dati in quale spettrogrammi di voci pre-arregistrati seranu guardati. Siccomu ci hè pocu tempu, avemu da risolve stu prublemu u megliu pudemu.
Vale à dì, creamu un script per arregistrà un extracte di voce (funziona in u listessu modu cum'è descrittu sopra, solu quandu hè interrotta da u teclatu, salverà a voce in un schedariu).
Pruvemu:
python3 voice_db/record_voice.py test.wav
Registramu e voci di parechje persone (in u nostru casu, trè membri di a squadra)
In seguitu, per ogni voce registrata facemu una trasformazione fourier veloce, uttene un spettrogramma è salvemu cum'è un array numpy (.npy):
for file in glob.glob("voice_db/*.wav"):
spec = get_fft_spectrum(file)
np.save(file[:-4] + '.npy', spec)
Più dettagli in u schedariu create_base.py
In u risultatu, quandu eseguimu u script principale, riceveremu embeddings da questi spettrogrammi à u principiu:
for file in glob.glob("voice_db/*.npy"):
spec = np.load(file)
spec = spec.astype('float32')
spec_reshaped = spec.reshape(1, 1, spec.shape[0], spec.shape[1])
srNet.setInput(spec_reshaped)
pred = srNet.forward()
emb = np.squeeze(pred)
Dopu avè ricivutu l'incrustazione da u segmentu sonatu, seremu capace di determinà à quale appartene piglià a distanza di cosenu da u passaghju à tutte e voci in a basa di dati (u più chjucu, u più prubabile) - per a demo avemu stabilitu u sogliu. à 0.3):
À a fine, vogliu nutà chì a velocità di inferenza hè stata rapida è hà permessu di aghjunghje 1-2 mudelli più (per una mostra di 7 seconde, hà pigliatu 2.5 per inferenza). Ùn avemu più tempu per aghjunghje novi mudelli è cuncintrati à scrive un prototipu di l'applicazione web.
Applicazione Web
Un puntu impurtante: pigliamu un router cun noi da a casa è cunfigurà a nostra reta lucale, aiuta à cunnette u dispusitivu è i laptops nantu à a reta.
U backend hè un canale di messagiu end-to-end trà u fronte è Raspberry Pi, basatu annantu à a tecnulugia websocket (protokollu http over tcp).
A prima tappa hè di riceve infurmazioni processate da raspberry, vale à dì, predittori imballati in json, chì sò salvati in a basa di dati a mità di u so viaghju in modu chì e statistiche ponu esse generate nantu à u fondu emozionale di l'utilizatore per u periodu. Stu pacchettu hè dopu mandatu à u frontend, chì usa l'abbonamentu è riceve pacchetti da u websocket endpoint. Tuttu u mecanismu di backend hè custruitu in a lingua golang; hè statu sceltu perchè hè bè adattatu per i travaglii asincroni, chì i goroutines gestiscenu bè.
Quandu accede à l'endpoint, l'utilizatore hè registratu è intrutu in a struttura, dopu u so messagiu hè ricevutu. Sia l'utilizatore è u messagiu sò inseriti in un centru cumune, da quale i missaghji sò digià mandati più (à u fronte abbonatu), è se l'utilizatore chjude a cunnessione (raspberry o front), allora u so abbunamentu hè annullatu è hè eliminatu da u hub.
Aspittemu una cunnessione da u spinu
Front-end hè una applicazione web scritta in JavaScript utilizendu a biblioteca React per accelerà è simplificà u prucessu di sviluppu. U scopu di sta applicazione hè di visualizà e dati ottenuti cù l'algoritmi chì funzionanu in u back-end è direttamente nantu à u Raspberry Pi. A pagina hà un routing seccionale implementatu cù react-router, ma a pagina principale di interessu hè a pagina principale, induve un flussu cuntinuu di dati hè ricevutu in tempu reale da u servitore utilizendu a tecnulugia WebSocket. Raspberry Pi detecta una voce, determina s'ellu appartene à una persona specifica da a basa di dati registrata, è manda una lista di probabilità à u cliente. U cliente mostra l'ultimi dati pertinenti, mostra l'avatar di a persona chì probabilmente hà parlatu in u microfonu, è ancu l'emozione cù quale pronuncia e parolle.
Pagina di casa cù predizioni aghjurnati
cunchiusioni
Ùn era micca pussibule di compie tuttu cum'è previstu, simpricimenti ùn avemu micca tempu, cusì a speranza principale era in a demo, chì tuttu funziona. In a presentazione anu parlatu di cumu tuttu funziona, chì mudelli anu pigliatu, chì prublemi anu scontru. Dopu era a parte demo - l'esperti caminavanu in a stanza in ordine aleatoriu è si avvicinavanu à ogni squadra per fighjà u prototipu di travagliu. Ci anu dumandatu ancu dumande, ognunu hà rispostu a so parte, anu lasciatu u web nantu à u laptop, è tuttu hà veramente travagliatu cum'è previstu.
Lasciami nutà chì u costu tutale di a nostra suluzione era $ 150:
Raspberry Pi 3 ~ $ 35
Google AIY Voice Bonnet (pudete piglià una tassa di respeaker) ~ 15 $
Intel NCS 2 ~ 100 $
Cumu migliurà:
Utilizà a registrazione da u cliente - dumandate à leghje u testu chì hè generatu aleatoriu
Aghjunghjite uni pochi di mudelli più: pudete stabilisce u sessu è l'età per voce
Separate voci chì sonanu simultaneamente (diarizazione)
In cunclusioni, vogliu ringrazià l'urganizatori è i participanti. Trà i prughjetti di l'altri squadre, ci hè piaciutu personalmente a suluzione per u monitoraghju di i parcheghji gratuiti. Per noi, era una sperienza fantastica di immersione in u pruduttu è u sviluppu. Spergu chì avvenimenti più è più interessanti seranu tenuti in e regioni, ancu nantu à temi di AI.