OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l

30. november – 1. detsember toimus Nizhni Novgorodis OpenVINO hackathon. Osalejad pidid looma toote lahenduse prototĂŒĂŒbi, kasutades Intel OpenVINO tööriistu. Korraldajad pakkusid vĂ€lja nimekirja nĂ€idisteemadest, millele ĂŒlesande valimisel Ă”nnestuda, kuid lĂ”pliku otsuse langetamine jĂ€i meeskondade kĂ€tte. Samuti julgustati kasutama mudeleid, mis ei kuulu tootesse.

OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l

Artiklis rÀÀgime, kuidas me oma toote prototĂŒĂŒbi lĂ”ime, millega lĂ”puks esikoht saavutati.

Hackathonil osales ĂŒle 10 meeskonna. On tore, et mĂ”ned neist tulid teistest piirkondadest. Hackathoni toimumispaik oli valitud kompleks „Kreml Počainas”, kus olid seintel vanad fotod Nizhni Novgorodist, luues erilise Ă”hkkonna! (meenutan, et praegu asub ettevĂ”tte Intel keskasutus just Nizhni Novgorodis). Koodi kirjutamiseks anti osalejatele 26 tundi, lĂ”pus tuli esitada oma lahendus. Erakordne pluss oli demoesitluse olemasolu, et veenduda, et kĂ”ik, mis oli planeeritud, on tĂ”eliselt teostatud, mitte ainult ideed esitlustes. Kaupade, suupistete ja toidu osas oli samuti kĂ”ik olemas!

Lisaks pakkus ettevÔte Intel soovi korral kaamerad, Raspberry PI ja Neural Compute Stick 2.

Ülesande valik

Üks keerukamaid osi hackathoni ettevalmistamisel avatud temaatikaga on ĂŒlesande valik. Otsustasime kohe vĂ€lja mĂ”elda midagi, mida tootest veel ei leidu, kuna kuulutuses öeldi, et seda teretatakse.

AnalĂŒĂŒsides mudeleid, mis on toodud tootes praeguses vĂ€ljaandes, jĂ”uame jĂ€reldusele, et enamik neist lahendab erinevaid arvutinĂ€gemise ĂŒlesandeid. TĂ”si, on vĂ€ga keeruline vĂ€lja mĂ”elda arvutinĂ€gemise valdkonda kuuluvat ĂŒlesannet, mida ei saa lahendada OpenVINO abil, ja kui selline vĂ”iks isegi olla, on raske leida avalikkusele kergesti ligipÀÀsetavaid eelĂ”ppinud mudeleid. SeetĂ”ttu mĂ”tleme uurida ka teisest suunast — hÀÀlprotsessimise ja analĂŒĂŒsi suunas. Kaaluveame huvitavat ĂŒlesannet emotsioonide tuvastamisel hÀÀle kaudu. Tuleb öelda, et OpenVINO-s on juba mudel, mis mÀÀrab inimese emotsioone nĂ€o jĂ€rgi, kuid:

  • Teoreetiliselt on vĂ”imalik luua kombineeritud algoritm, mis töötab nii heli kui ka pildi jĂ€rgi, mis peaks tooma tĂ€psuse suurenemist.
  • Kaameratel on tavaliselt kitsas vaatenurk, et katta suuremat ala, mistĂ”ttu on vajalik rohkem kui ĂŒks kaamera; helil ei ole selliseid piiranguid.

Arendame ideed: vÔtame aluseks idee jaeturu segmendi jaoks. Saame mÀÀrata ostja rahulolu kaupluse kassade juures. Kui mÔni ostjatest on teenindusega rahulolematu ja hakkab tooni tÔstma, saab kohe kutsuda administraatori appi.
Sel juhul on vajalik lisada hÀÀletuvastus, mis vĂ”imaldab meil eristada poe töötajaid ostjatest ning anda analĂŒĂŒsi igale indiviidile. Lisaks saab analĂŒĂŒsida poe töötajate kĂ€itumist ja hinnata kollektiivi atmosfÀÀri, kĂ”lab hĂ€sti!

MÀÀrame nÔuded meie lahendusele:

  • Sihtseadmest vĂ€ike suurus
  • Töö reaalses ajas
  • Madal hind
  • Lihtne skaleeritavus

KokkuvÔttes valime sihtseadmeks Raspberry Pi 3 koos Intel NCS 2.

Siin on oluline mÀrkida, et NCS töötab kÔige paremini standardsete CNN arhitektuuridega; kui soovite kÀivitada sellel mudelit kohandatud kihtidega, oodake madalamat optimeerimist.

Tuleb leida mikrofon. Sobib ka tavaline USB-mikrofon, kuigi see ei nĂ€e RPI kĂ”rval hea vĂ€lja. Kuid ka siin on lahendus sĂ”na otseses mĂ”ttes „kĂ€eulatuses“. HÀÀle salvestamiseks otsustame kasutada Voice Bonnet plaati komplektist Google AIY Voice Kit, millel on solderdatud stereomikrofon.

Laadime Raspbiani alla AIY projektide hoidlast ja salvestame selle mÀlupulgale, testime, kas mikrofon töötab jÀrgmise kÀsuga (see salvestab 5 sekundi pikkuse heli ja salvestab faili):

arecord -d 5 -r 16000 test.wav

TĂ€htis on mĂ€rkida, et mikrofon on vĂ€ga tundlik ja pĂŒĂŒab hĂ€sti ĂŒles hÀÀlest. Selle parandamiseks siseneme alsamixerisse, valime Capture seadmed ja vĂ€hendame sisendsignaali taset 50-60%-ni.

OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l
Viimistleme korpuse viiliga ja kÔik mahub sinna, isegi kaane saab kinni panna.

Lisame indikaatornupu.

AIY Voice Kit'i lahti vÔttes meenutame, et seal on RGB-nupp, mille valgustust saab programmiliselt juhtida. Otsime "Google AIY Led" ja leiame dokumentatsiooni: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Miks mitte kasutada seda nuppu tuvastatud emotsiooni kuvamiseks, meil on kokku 7 klassi ja nupul on 8 vÀrvi, mis on just piisavalt!

Ühendame nupu GPIO kaudu Voice Bonnet'iga, laadime vajalikud teegid (need on juba AIY projektide jaotisesse installitud).

from aiy.leds import Leds, Color
from aiy.leds import RgbLeds

Loome dict'i, kus iga emotsioon vastab vÀrvile RGB Tuple'ina ja aiy.leds.Leds klassi objektile, mille kaudu vÀrvi uuendame:

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

Ja lÔpuks, pÀrast iga uue emotsioonide ennustuse tegemist uuendame nuppude vÀrvi vastavalt sellele (vÔtme jÀrgi).

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

OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l
Nupp, pÔle!

Töötame hÀÀlega

Kasutame pyaudio helivoogude salvestamiseks mikrofonilt ja webrtcvad mĂŒravaba filtriks ning hÀÀle tuvastamiseks. Lisaks loome jĂ€rjekorra, kuhu lisame ja sealt eemaldame helilĂ”ike asĂŒnkroonselt.

Kuna webrtcvad-il on piirang suuruse kohta, peab sisestatud fragment olema 10/20/30 ms, ja emotsioonide tuvastamise mudelit Ă”petati (nagu me hiljem saame teada) 48 kHz andmestikul, siis salvestame fragmente suurusega 48000 × 20 ms / 1000 × 1 (mono) = 960 baiti. Webrtcvad tagastab True/False iga sellise fragmendi kohta, mis vastab hÀÀle olemasolule vĂ”i puudumisele fragmendis.

Rakendame jÀrgmise loogika:

  • Lisame listi need fragmendid, kus on hÀÀl; kui hÀÀlt ei ole, siis suurendame tĂŒhjade fragmentide loendurit.
  • Kui tĂŒhi chunkide loendur on >=30 (600 ms), vaatame kogunenud chunkide lehe suurust; kui see on >250, siis lisame jĂ€rjekorda, kui ei, arvame, et salvestuse pikkus pole piisav, et esitada see mudelile kĂ”neleja tuvastamiseks.
  • Kui tĂŒhi chunkide loendur on endiselt < 30 ja kogunenud chunkide lehe suurus ĂŒletab 300, siis lisame lĂ”igu jĂ€rjekorda tĂ€psema prognoosi jaoks. (Sest aja jooksul emotsioonid muutuvad.)

 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 = []

On saanud aeg otsida avatud juurdepÀÀsuga eeltreenitud mudeleid, lĂ€heme githubi, guugeldame, kuid peame meeles, et meil on arhitektuuri osas piirang. See on ĂŒsna keeruline osa, kuna tuleb mudeleid testida oma sisendandmetega ning lisaks konverteerida need OpenVINO sisemiseks vorminguks — IR (Intermediate Representation). Proovisime umbes 5-7 erinevat lahendust githubis, ja kui emotsioonide tuvastamise mudel töötas kohe, siis hÀÀltuvastamisega pidime kauem vaeva nĂ€gema — seal kasutatakse keerukamaid arhitektuure.

Peatume jÀrgmistel:

JÀrgmisena rÀÀgime mudelite konverteerimisest, alustame teooriast. OpenVINO sisaldab mitmeid mooduleid:

  • Open Model Zoo, mille mudeleid saab kasutada ja oma toodetesse integreerida.
  • Model Optimizer, mis vĂ”imaldab konverteerida mudeleid erinevate raamistike formaatidest (Tensorflow, ONNX jms) Intermediate Representation formaati, millega me edasi töötame.
  • Inference Engine vĂ”imaldab kĂ€ivitada mudeleid IR formaadis Intel protsessoritel, Myriad kiipidel ja Neural Compute Stick kiirenditel.
  • KĂ”ige tĂ”husam OpenCV versioon (Inference Engine'i toega).
    Iga IR formaadis mudel on kirjeldatud kahe failiga: .xml ja .bin.
    Mudeleid konverteeritakse IR formaati Model Optimizer abil jÀrgmiselt:
    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 valib andmeformaadi, millega mudel töötleb. Toetatakse FP32, FP16, INT8. Optimaalse andmeformaadi valik vÔib anda mÀrkimisvÀÀrse jÔudluse suurenemise.
    --input_shape nĂ€itab sisendi mÔÔtmeid. C++ API-s on tĂ”enĂ€oliselt olemas vĂ”imalus seda dĂŒnaamiliselt muuta, kuid me ei sĂŒvenenud sellesse nii sĂŒgavale ja fikseerisime selle ĂŒhe mudeli puhul lihtsalt.
    SeejÀrel proovime laadida juba konverteeritud mudelit IR formaadis OpenCV DNN mooduli kaudu ja teha sellele forward.

    import cv2 as cv
    emotionsNet = cv.dnn.readNet('emotions_model.bin',
                              'emotions_model.xml')
    emotionsNet.setPreferableTarget(cv.dnn.DNN_TARGET_MYRIAD)

    Antud juhul suudab viimane rida suunata arvutused Neural Compute Stickile, kuna algselt viiakse arvutused lÀbi protsessoril, kuid Raspberry Pi puhul see ei toimi, seetÔttu on vajalik stick.

    SeejĂ€rel kĂ€ib loogika jĂ€rgmiselt: jagame oma audio kindla suurusega akendeks (meie puhul on see 0,4 s), igaĂŒhe neist akendest teisendame MFCC-ks, mida seejĂ€rel edastame vĂ”rku:

    emotionsNet.setInput(MFCC_from_window)
    result = emotionsNet.forward()

    VĂ”tame seejĂ€rel kĂ”igi akende jaoks kĂ”ige sagedamini esineva klassi. Lihtne lahendus, kuid hakatoni jaoks ei ole vaja midagi liiga keerulist vĂ€lja mĂ”elda, vĂ€lja arvatud juhul, kui aega on. Meil on veel palju tööd, seega liigume edasi — mĂ”istame hÀÀltuvastust. Peame looma mingi andmebaasi, kuhu salvestatakse eelnevalt salvestatud hÀÀlte spektraalid. Kuna aega on jÀÀnud vĂ€he, lahendame selle kĂŒsimuse nii nagu oskame.

    Nimelt loome skripti, et salvestada hÀÀlest lÔiku (see töötab sama moodi nagu eelnevalt kirjeldatud, kuid katkemise korral klaviatuurilt salvestab see hÀÀle faili).

    Proovime.:

    python3 voice_db/record_voice.py test.wav

    Salvestame mitme inimese hÀÀli (meie puhul kolme meeskonnaliikme hÀÀli)
    SeejĂ€rel teeme iga salvestatud hÀÀle jaoks kiire fourier' analĂŒĂŒsi, saame spektraaldiagrammi ja salvestame numpy massiivina (.npy):

    for file in glob.glob("voice_db/*.wav"):
            spec = get_fft_spectrum(file)
            np.save(file[:-4] + '.npy', spec)

    Lisainfot failis create_base.py
    Tulemuseks, kui kÀivitame pÔhiskripti, saame alguses need spektrite embeddid:

    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)

    PĂ€rast embeedi saamist kĂ”lavatest katkestustest saame mÀÀrata, kellele see kuulub, vĂ”tme cosinus kauguse katkestusest kĂ”igi hÀÀltelt andmebaasis (mida madalam, seda tĂ”enĂ€olisem) — demo jaoks oleme seadnud lĂ€ve 0.3):

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

    LĂ”puks mĂ€rgin, et infereerimise kiirus oli kiire ja lubas lisada veel 1-2 mudelit (7-sekundilise nĂ€idise infereerimiseks kulus 2.5). Uute mudelite lisamine jĂ€i meil juba hiljaks ja keskendusime veebirakenduse prototĂŒĂŒbi kirjutamisele.

    Veebirakendus

    Oluline punkt: vĂ”tame kodust kaasa ruuteri ja seadistame oma kohalikku vĂ”rku, mis aitab seadmeid ja sĂŒlearvuteid omavahel ĂŒhenduda.

    Backend on pidev sĂ”numite kanali ĂŒhendus frondi ja Raspberry Pi vahel, mis pĂ”hineb websocket-tehnoloogial (http ĂŒle tcp protokolli).

    Esimene etapp on töödeldud teabe saamine Raspberry Pi'lt, see tĂ€hendab, et JSON-formaadis ennustused salvestatakse andmebaasi nende teekonna keskel, et saaksime luua statistikat kasutaja emotsionaalse tausta kohta ajaperioodil. Edasi saadetakse see pakett frondi, mis kasutab tellimist ja pakettide vastuvĂ”tmist websocketsi lĂ”pp-punktist. Kogu backend mehhanism on ĂŒles ehitatud Go keeles, kuna see sobib hĂ€sti asĂŒnkroonsete ĂŒlesannete jaoks, millega gorangiinid hĂ€sti toime tulevad.
    LĂ”pp-punkti juurde pÀÀsemisel registreeritakse kasutaja ja lisatakse struktuuri, seejĂ€rel toimub tema sĂ”numi saamine. N nii kasutaja kui sĂ”num lisatakse ĂŒhiselt hub-i, kust sĂ”numid saadetakse edasi (registreeritud frontile), ja kui kasutaja ĂŒhenduse lĂ”petab (raspberry vĂ”i front), siis tema tellimus tĂŒhistatakse ja ta eemaldatakse hub-ist.

    OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l
    Ootame ĂŒhendust tagaplaaniga

    Front-end on web-rakendus, mis on kirjutatud JavaScriptis, kasutades raamatukogu React, et kiirendada ja lihtsustada arendusprotsessi. Selle rakenduse eesmĂ€rk on andmete visualiseerimine, mis on saadud back-end'i pool töötavate algoritmide ja Raspberry Pi kaudu. Lehel on jaotiste vahel suunamine, mis on rakendatud react-router'i abil, kuid peamine huvi keskendub avalehele, kus reaalajas voolab pidev andmevoog serverist WebSocketi tehnoloogia kaudu. Raspberry Pi tuvastab hÀÀlt, mÀÀrab, kellele see kuulub registreeritud andmebaasis, ja saadab kliendile tĂ”enĂ€osuste loendi. Klient kuvab viimased asjakohased andmed, kuvab inimese avatar, kellel on suurim tĂ”enĂ€osus mikrofoniga rÀÀkida, samuti emotsiooni, millega ta sĂ”nu ĂŒtleb.

    OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l
    Avaleht uuenevate ennustustega

    KokkuvÔte

    Kahjuks ei Ă”nnestunud meil kĂ”ike plaanitud lĂ”pule viia, ajaliselt lihtsalt ei jĂ”udnud, seega oli peamine lootus demoon, et kĂ”ik töötab. Esitlusel rÀÀgiti, kuidas kĂ”ik on korraldatud, milliseid mudeleid kasutati ja millega silmitsi seisti. JĂ€rgnes demooni osa — eksperdid liikusid saalis vabalt ringi ja lĂ€henesid igale meeskonnale, et vaadata töötavat prototĂŒĂŒpi. Nad kĂŒsisid kĂŒsimusi ka meilt, igaĂŒks vastas oma osa kohta, sĂŒlearvutites oli veeb ja kĂ”ik tĂ”epoolest töötas, nagu oodati.

    TÔstan esile, et meie lahenduse kogukulu oli 150$:

    • Raspberry Pi 3 ~ 35$
    • Google AIY Voice Bonnet (vĂ”ite vĂ”tta respeakeri plaadi) ~ 15$
    • Intel NCS 2 ~ 100$

    Kuidas parandada:

    • Kasuta kliendi registreerimist — kĂŒsi, et lugeda teksti, mida genereerime juhuslikult
    • Lisada veel mĂ”ned mudelid: hÀÀle jĂ€rgi saab mÀÀrata soo ja vanuse
    • Erinevate hÀÀlte eristamine, kui need kĂ”lavad samal ajal (diarisatsioon)

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

    OpenVINO hackathon: tuvastame hÀÀlt ja emotsioone Raspberry Pi-l
    VÀsinud, kuid Ônnelikud oleme

    LĂ”petuseks tahaksime tĂ€nada korraldajate ja osalejate eest. Teiste tiimide projektidest meeldis meile isiklikult lahendus parkimiskohtade jĂ€lgimiseks. See oli meile ÀÀrmiselt Ă€ge kogemus toote ja arenduse maailmas. Loodan, et piirkondades toimub ĂŒha rohkem huvitavaid ĂŒritusi, sealhulgas AI-teemalisi.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster