OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s

30. november – 1. detsember peeti Nižni Novgorodis OpenVINO häkaton. Osalejatel paluti luua tootelahenduse prototüüp, kasutades Intel OpenVINO tööriistakomplekti. Korraldajad pakkusid välja nimekirja ligikaudsetest teemadest, millest võiks ülesande valikul juhinduda, kuid lõplik otsus jäi meeskondadele. Lisaks julgustati kasutama mudeleid, mida toode ei sisalda.

OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s

Selles artiklis räägime teile sellest, kuidas lõime oma toote prototüübi, millega saime lõpuks esikoha.

Häkatonil osales üle 10 meeskonna. Tore, et osa neist tuli teistest piirkondadest. Häkatoni toimumispaigaks oli kompleks “Kremlinski Pochainil”, mille sisse riputati saatjaskonnas iidsed fotod Nižni Novgorodist! (Tuletan meelde, et praegu asub Inteli keskkontor Nižni Novgorodis). Osalejatele anti koodi kirjutamiseks aega 26 tundi ning lõpus tuli oma lahendus esitleda. Eraldi eeliseks oli demosessiooni olemasolu, et veenduda, et kõik planeeritud sai ka reaalselt ellu viidud ega jääks esitlusse ideedeks. Kaup, snäkid, toit, kõik oli ka olemas!

Lisaks tarnis Intel valikuliselt kaameraid, Raspberry PI-d, Neural Compute Stick 2.

Ülesande valik

Vabas vormis hackathoniks valmistumise üks keerulisemaid osi on väljakutse valimine. Otsustasime kohe välja mõelda midagi, mida tootes veel ei olnud, sest teadaandes oli öeldud, et see on väga teretulnud.

Olles analüüsinud mudelid, mis on praeguses väljaandes tootes sisalduvad, jõuame järeldusele, et enamik neist lahendab erinevaid arvuti nägemisprobleeme. Veelgi enam, arvutinägemise valdkonnas on väga raske välja mõelda probleemi, mida OpenVINO abil ei saaks lahendada, ja isegi kui see on võimalik leiutada, on raske avalikkusest leida eelkoolitatud mudeleid. Otsustame kaevata teises suunas – kõnetöötluse ja analüütika poole. Vaatleme huvitavat ülesannet kõnest emotsioonide äratundmiseks. Peab ütlema, et OpenVINO-l on juba mudel, mis määrab inimese emotsioonid näo järgi, kuid:

  • Teoreetiliselt on võimalik luua kombineeritud algoritm, mis töötab nii helil kui pildil, mis peaks suurendama täpsust.
  • Kaameratel on tavaliselt kitsas vaatenurk, suure ala katmiseks on vaja rohkem kui ühte kaamerat, helil sellist piirangut pole.

Arendame ideed: võtame aluseks jaemüügisegmendi idee. Klientide rahulolu saate mõõta kaupluste kassas. Kui üks klientidest pole teenusega rahul ja hakkab oma tooni tõstma, võite koheselt administraatorilt abi kutsuda.
Sel juhul peame lisama inimese hääletuvastuse, see võimaldab meil eristada poe töötajaid klientidest ja pakkuda analüütikat iga inimese kohta. Noh, lisaks on võimalik analüüsida poetöötajate endi käitumist, hinnata meeskonnas valitsevat õhkkonda, kõlab hästi!

Sõnastame oma lahendusele nõuded:

  • Sihtseadme väike suurus
  • Reaalajas toimimine
  • Madal hind
  • Lihtne skaleeritavus

Selle tulemusena valime sihtseadmeks Raspberry Pi 3 c Intel NCS 2.

Siin on oluline märkida üks oluline NCS-i omadus - see töötab kõige paremini standardsete CNN-arhitektuuridega, kuid kui teil on vaja käivitada kohandatud kihtidega mudelit, siis oodake madalat optimeerimist.

Teha on vaid üks väike asi: pead hankima mikrofoni. Tavaline USB-mikrofon sobib küll, kuid see ei näe koos RPI-ga hea välja. Kuid isegi siin on lahendus sõna otseses mõttes "lähedal". Hääle salvestamiseks otsustame kasutada komplekti kuuluvat Voice Bonnet tahvlit Google AIY Voice Kit, millel on juhtmega stereomikrofon.

Laadige Raspbian alla saidilt AIY projektide hoidla ja laadige see mälupulgale, testige, kas mikrofon töötab, kasutades järgmist käsku (salvestab heli 5 sekundit ja salvestab selle faili):

arecord -d 5 -r 16000 test.wav

Pean kohe märkima, et mikrofon on väga tundlik ja võtab hästi müra. Selle parandamiseks läheme alsamixeri, valime Capture devices ja vähendame sisendsignaali taset 50-60% peale.

OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s
Korpust modifitseerime viiliga ja kõik sobib, saab kasvõi kaanega kinni panna

Indikaatornupu lisamine

AIY Voice Kit’i lahti võtmisel meenub, et seal on RGB nupp, mille taustvalgustust saab tarkvaraliselt 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 ainult 7 klassi ja nupul on 8 värvi, täpselt piisavalt!

Ühendame nupu GPIO kaudu Voice Bonnetiga, laadime vajalikud teegid (need on juba AIY projektide jaotuskomplekti installitud)

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

Loome diktaadi, milles igale emotsioonile on vastav värv RGB Tuple ja klassi aiy.leds.Leds objektina, mille kaudu värskendame värvi:

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 uut emotsiooni ennustust, värskendame nupu värvi vastavalt sellele (võtme järgi).

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

OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s
Nupp, põleta!

Töö häälega

Kasutame mikrofoni voo jäädvustamiseks pyaudiot ja müra filtreerimiseks ja hääle tuvastamiseks webrtcvadi. Lisaks loome järjekorra, kuhu asünkroonselt lisame ja eemaldame häälekatkendeid.

Kuna webrtcvadil on tarnitava fragmendi suurusele piirang - see peab olema võrdne 10/20/30 ms ja emotsioonide äratundmise mudeli koolitus (nagu hiljem teada saame) viidi läbi 48kHz andmekogumil, jäädvustage tükke suurusega 48000 × 20 ms/1000 × 1 (mono) = 960 baiti. Webrtcvad tagastab iga nende tükkide puhul väärtuse Tõene/Väär, mis vastab hääle olemasolule või puudumisele tükis.

Rakendame järgmist loogikat:

  • Lisame nimekirja need jupid, kus on hääl, kui häält pole, siis suurendame tühjade juppide loendurit.
  • Kui tühjade tükkide loendur on >=30 (600 ms), siis vaatame kogunenud tükkide loendi suurust, kui see on >250, siis lisame selle järjekorda, kui ei, siis arvestame pikkusega. plaadist ei piisa, et see kõlari tuvastamiseks mudelile ette anda.
  • Kui tühjade tükkide loendur on endiselt < 30 ja kogutud tükkide loendi suurus ületab 300, siis lisame fragmendi täpsema prognoosi saamiseks järjekorda. (sest emotsioonid kipuvad aja jooksul muutuma)

 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 aeg otsida üldkasutatavaid eelkoolitatud mudeleid, avada github, Google, kuid pidage meeles, et kasutataval arhitektuuril on piirangud. See on üsna keeruline osa, kuna peate mudeleid oma sisendandmete peal testima ja lisaks konverteerima need OpenVINO sisevormingusse - IR (Intermediate Representation). Githubist proovisime umbes 5-7 erinevat lahendust ja kui emotsioonide äratundmise mudel töötas kohe, siis hääletuvastusega tuli kauem oodata - need kasutavad keerulisemaid arhitektuure.

Keskendume järgmisele:

Järgmisena räägime mudelite teisendamisest, alustades teooriast. OpenVINO sisaldab mitmeid mooduleid:

  • Avage Model Zoo, mille mudeleid saate kasutada ja oma tootesse lisada
  • Model Optimzer, tänu millele saate teisendada mudeli erinevatest raamistiku formaatidest (Tensorflow, ONNX jne) Intermediate Representation formaadiks, millega me edasi töötame
  • Inference Engine võimaldab teil käitada IR-vormingus mudeleid Inteli protsessoritel, Myriad kiipidel ja Neural Compute Stick kiirenditel
  • OpenCV kõige tõhusam versioon (koos järeldusmootori toega)
    Iga IR-vormingus mudelit kirjeldatakse kahe failiga: .xml ja .bin.
    Mudelid teisendatakse IR-vormingusse Model Optimizeri kaudu 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 võimaldab valida andmevormingu, millega mudel töötab. Toetatud on FP32, FP16, INT8. Optimaalse andmetüübi valimine võib jõudlust hästi suurendada.
    --input_shape näitab sisendandmete mõõdet. Võimalus seda dünaamiliselt muuta näib olevat C++ API-s olemas, kuid me ei kaevanud nii kaugele ja lihtsalt parandasime selle ühe mudeli jaoks.
    Järgmiseks proovime juba konverteeritud mudelit IR-vormingus DNN-mooduli kaudu OpenCV-sse laadida ja sellele edasi saata.

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

    Viimane rida võimaldab sel juhul suunata arvutused Neural Compute Stickile, põhilised arvutused tehakse protsessori peal, kuid Raspberry Pi puhul see ei toimi, vaja läheb pulka.

    Järgmisena on loogika järgmine: jagame oma heli teatud suurusega akendeks (meie jaoks on see 0.4 s), teisendame kõik need aknad MFCC-ks, mille seejärel võrgustikku suuname:

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

    Järgmisena võtame kõigi akende jaoks kõige tavalisema klassi. Lihtne lahendus, kuid häkatoni jaoks ei pea te midagi liiga abstraktset välja mõtlema, ainult siis, kui teil on aega. Meil on veel palju tööd teha, nii et lähme edasi – tegeleme hääletuvastusega. Vaja on luua mingisugune andmebaas, kuhu salvestataks eelsalvestatud häälte spektrogrammid. Kuna aega on jäänud vähe, lahendame selle probleemi nii hästi kui suudame.

    Nimelt loome häälekatkendi salvestamiseks skripti (toimib samamoodi nagu eelpool kirjeldatud, ainult klaviatuurilt katkestamisel salvestab hääle faili).

    Proovime:

    python3 voice_db/record_voice.py test.wav

    Salvestame mitme inimese häält (meie puhul kolme meeskonnaliikme)
    Seejärel teostame iga salvestatud hääle jaoks kiire Fourier-teisenduse, saame spektrogrammi ja salvestame selle numpy massiivina (.npy):

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

    Täpsem info failis create_base.py
    Selle tulemusena saame põhiskripti käivitamisel kohe alguses nende spektrogrammide manuseid:

    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 helistatud segmendist manustamise saamist saame määrata, kellele see kuulub, võttes koosinuse kauguse lõigust kõigi andmebaasis olevate häälteni (mida väiksem, seda tõenäolisem) - demo jaoks määrame läve kuni 0.3):

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

    Lõpetuseks tahaksin märkida, et järelduskiirus oli kiire ja võimaldas lisada veel 1-2 mudelit (7 sekundi pikkuse näidise jaoks kulus järelduste tegemiseks 2.5). Meil ei olnud enam aega uute mudelite lisamiseks ja keskendusime veebirakenduse prototüübi kirjutamisele.

    Veebirakendus

    Oluline punkt: võtame kodust kaasa ruuteri ja paneme paika oma kohtvõrgu, see aitab üle võrgu seadme ja sülearvutid ühendada.

    Taustaprogramm on esiosa ja Raspberry Pi vaheline otsast lõpuni sõnumikanal, mis põhineb veebisocket-tehnoloogial (http tcp-protokolli kaudu).

    Esimeses etapis saadakse vaarikast töödeldud info ehk json-i pakitud ennustajad, mis poole teekonna pealt andmebaasi salvestatakse, et saaks genereerida statistikat kasutaja emotsionaalse tausta kohta perioodi kohta. See pakett saadetakse seejärel kasutajaliidesele, mis kasutab tellimust ja võtab vastu pakette veebipesa lõpp-punktist. Kogu taustamehhanism on üles ehitatud golangi keeles, see valiti seetõttu, et see sobib hästi asünkroonsete ülesannete jaoks, millega gorutiinid hästi hakkama saavad.
    Lõpp-punktile juurdepääsul registreeritakse kasutaja ja sisestatakse struktuuri, seejärel võetakse vastu tema sõnum. Nii kasutaja kui ka sõnum sisestatakse ühisesse jaoturisse, kust saadetakse sõnumeid juba edasi (tellitud rindele) ja kui kasutaja sulgeb ühenduse (vaarikas või esikülg), siis tema tellimus tühistatakse ja ta eemaldatakse keskus.

    OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s
    Ootame ühendust tagantpoolt

    Esiosa on JavaScriptis kirjutatud veebirakendus, mis kasutab arendusprotsessi kiirendamiseks ja lihtsustamiseks Reacti teeki. Selle rakenduse eesmärk on visualiseerida andmeid, mis on saadud taustaküljel ja otse Raspberry Pi-l töötavate algoritmide abil. Lehel on react-ruuteri abil realiseeritud sektsioonmarsruutimine, kuid huvipakkuv põhileht on põhileht, kus serverist võetakse WebSocket tehnoloogia abil reaalajas vastu pidev andmevoog. Raspberry Pi tuvastab hääle, teeb registreeritud andmebaasist kindlaks, kas see kuulub konkreetsele isikule ja saadab kliendile tõenäosusloendi. Klient kuvab viimased asjakohased andmed, kuvab suure tõenäosusega mikrofoni rääkinud inimese avatari, samuti emotsiooni, millega ta sõnu hääldab.

    OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s
    Uuendatud ennustustega koduleht

    Järeldus

    Kõike ei olnud võimalik plaanipäraselt valmis teha, meil lihtsalt polnud aega, nii et põhiline lootus oli demos, et kõik läheb korda. Ettekandes räägiti, kuidas kõik toimib, milliseid mudeleid võtsid, milliste probleemidega kokku puutusid. Järgmine oli demoosa – eksperdid kõndisid ruumis juhuslikus järjekorras ja lähenesid igale meeskonnale, et vaadata töötavat prototüüpi. Nad esitasid meile ka küsimusi, igaüks vastas oma osa, nad jätsid veebi sülearvutisse ja kõik toimis ootuspäraselt.

    Lubage mul märkida, et meie lahenduse kogumaksumus oli 150 dollarit:

    • Raspberry Pi 3 ~ 35 dollarit
    • Google AIY Voice Bonnet (võite võtta kõneleja tasu) ~ 15 $
    • Intel NCS 2 ~ 100 $

    Kuidas parandada:

    • Kasutage kliendi registreerimist – paluge lugeda juhuslikult genereeritud tekst
    • Lisage veel mõned mudelid: saate hääle abil määrata soo ja vanuse
    • Eraldi samaaegselt kõlavad hääled (diariseerimine)

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

    OpenVINO häkaton: hääle ja emotsioonide tuvastamine Raspberry Pi-s
    Oleme väsinud, kuid õnnelikud

    Kokkuvõtteks tahan tänada korraldajaid ja osalejaid. Teiste meeskondade projektidest meeldis meile isiklikult vabade parkimiskohtade jälgimise lahendus. Meie jaoks oli see metsikult lahe kogemus tootesse ja arendusse sukeldumisest. Loodan, et piirkondades toimub järjest rohkem huvitavaid üritusi, sealhulgas tehisintellekti teemadel.

Allikas: www.habr.com

Lisa kommentaar