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.

Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi

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ì !

Inoltre, Intel hà furnitu opzionalmente camere, Raspberry PI, Neural Compute Stick 2.

Scelta di u travagliu

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%.

Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
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:

led_dict = {'neutral': (255, 255, 255), 'happy': (0, 255, 0), 'sad': (0, 255, 255), 'angry': (255, 0, 0), 'fearful': (0, 0, 0), 'disgusted':  (255, 0, 255), 'surprised':  (255, 255, 0)} 
leds = Leds()

È finarmenti, dopu ogni nova prediczione di una emozione, aghjurnà u culore di u buttone in cunfurmità cù questu (per chjave).

leds.update(Leds.rgb_on(led_dict.get(classes[prediction])))

Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
Buttonu, brusgià !

U travagliu cù a voce

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:

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:

    python /opt/intel/openvino/deployment_tools/model_optimizer/mo_tf.py --input_model speaker.hdf5.pb --data_type=FP16 --input_shape [1,512,1000,1]

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

            dist_list = cdist(emb, enroll_embs, metric="cosine")
            distances = pd.DataFrame(dist_list, columns = df.speaker)

    À 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.

    Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
    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.

    Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
    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)

    Repository: https://github.com/vladimirwest/OpenEMO

    Hackathon OpenVINO: ricunnosce a voce è l'emozioni nantu à Raspberry Pi
    Semu stanchi ma felici

    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.

Source: www.habr.com

Add a comment