OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi

30 novimber - 1 desimber waard hâlden yn Nizjni Novgorod OpenVINO hackathon. Dielnimmers waarden frege om in prototype fan in produktoplossing te meitsjen mei de Intel OpenVINO toolkit. De organisatoaren foarstelden in list mei ûngefear ûnderwerpen dy't koe wurde liede troch by it kiezen fan in taak, mar it definitive beslút bleau by de teams. Derneist waard it gebrûk fan modellen dy't net opnommen binne yn it produkt stimulearre.

OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi

Yn dit artikel sille wy jo fertelle oer hoe't wy ús prototype fan it produkt makke hawwe, wêrmei't wy úteinlik it earste plak naam.

Mear as 10 teams diene mei oan de hackathon. It is moai dat guon fan harren út oare regio's kamen. It plak foar de hackathon wie it kompleks "Kremlinsky op Pochain", wêr't âlde foto's fan Nizhny Novgorod binnen waarden ophongen, yn in entourage! (Ik herinner jo dat op it stuit it sintrale kantoar fan Intel yn Nizhny Novgorod leit). Dielnimmers krigen 26 oeren om koade te skriuwen, en oan 'e ein moasten se har oplossing presintearje. In apart foardiel wie de oanwêzigens fan in demo-sesje om der wis fan te wêzen dat alles dat pland wie eins útfierd waard en gjin ideeën bleau yn 'e presintaasje. Merch, hapkes, iten, alles wie der ek!

Derneist levere Intel opsjoneel kamera's, Raspberry PI, Neural Compute Stick 2.

Taak seleksje

Ien fan 'e dreechste ûnderdielen fan it tarieden op in hackathon mei frije foarm is it kiezen fan in útdaging. Wy hawwe fuortendaliks besletten om mei iets te kommen dat noch net yn it produkt wie, om't de oankundiging sei dat dit tige wolkom wie.

Nei't analysearre modellen, dy't opnommen binne yn it produkt yn 'e aktuele release, komme wy ta de konklúzje dat de measte fan har ferskate problemen mei komputerfisy oplosse. Boppedat is it hiel lestich om te kommen mei in probleem op it mêd fan kompjûter fyzje dat kin net oplost wurde mei help fan OpenVINO, en sels as men kin wurde útfûn, it is dreech om te finen pre-trained modellen yn it publike domein. Wy beslute om te graven yn in oare rjochting - nei spraakferwurking en analytyk. Lit ús beskôgje in nijsgjirrige taak fan werkennen emoasjes út spraak. It moat sein wurde dat OpenVINO al in model hat dat de emoasjes fan in persoan bepaalt op basis fan har gesicht, mar:

  • Yn teory is it mooglik om in kombinearre algoritme te meitsjen dat sil wurkje op sawol lûd as byld, wat in tanimming fan krektens moat jaan.
  • Kamera's hawwe normaal in smelle kijkhoek; mear as ien kamera is nedich om in grut gebiet te dekken; lûd hat sa'n beheining net.

Litte wy it idee ûntwikkelje: lit ús it idee foar it retailsegment as basis nimme. Jo kinne klanttefredenheid mjitte by winkelkassa's. As ien fan 'e klanten ûntefreden is mei de tsjinst en begjint har toan te ferheegjen, kinne jo de behearder fuortendaliks skilje foar help.
Yn dit gefal moatte wy minsklike stimherkenning tafoegje, dit sil ús tastean om winkelmeiwurkers te ûnderskieden fan klanten en analytics foar elk yndividu te leverjen. No, dan sil it mooglik wêze om it gedrach fan 'e winkelmeiwurkers sels te analysearjen, de sfear yn it team te evaluearjen, klinkt goed!

Wy formulearje de easken foar ús oplossing:

  • Lytse grutte fan it doelapparaat
  • Echte tiid operaasje
  • Leech priis
  • Maklike skaalberens

As gefolch, wy selektearje Raspberry Pi 3 c as de doelgroep apparaat Intel NCS 2.

Hjir is it wichtich om ien wichtige funksje fan NCS op te merken - it wurket it bêste mei standert CNN-arsjitektueren, mar as jo in model moatte útfiere mei oanpaste lagen derop, ferwachtsje dan optimisaasje op leech nivo.

Der is mar ien lyts ding te dwaan: jo moatte in mikrofoan krije. In gewoane USB-mikrofoan sil dwaan, mar it sil net goed útsjen tegearre mei de RPI. Mar ek hjir leit de oplossing letterlik "dichtby." Om stim op te nimmen, beslute wy it Voice Bonnet-board te brûken út 'e kit Google AIY Voice Kit, wêrop d'r in bedrade stereomikrofoan is.

Download Raspbian fan AIY projekten repository en upload it nei in flash drive, testje dat de mikrofoan wurket mei it folgjende kommando (it sil audio 5 sekonden lang opnimme en it opslaan yn in bestân):

arecord -d 5 -r 16000 test.wav

Ik moat fuortendaliks konstatearje dat de mikrofoan tige gefoelich is en lûd goed opnimt. Om dit te reparearjen, litte wy nei alsamixer gean, selektearje Capture-apparaten en ferminderje it ynfiersinjaalnivo nei 50-60%.

OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi
Wy feroarje it lichem mei in bestân en alles past, jo kinne it sels mei in deksel slute

It tafoegjen fan in yndikator knop

Wylst wy de AIY Voice Kit útinoar nimme, betinke wy dat d'r in RGB-knop is, wêrfan de efterljochting kin wurde regele troch software. Wy sykje nei "Google AIY Led" en fine dokumintaasje: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Wêrom net brûke dizze knop te werjaan de erkende emoasje, wy hawwe mar 7 klassen, en de knop hat 8 kleuren, krekt genôch!

Wy ferbine de knop fia GPIO oan Voice Bonnet, laden de nedige biblioteken (se binne al ynstalleare yn 'e distribúsjekit fan AIY-projekten)

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

Litte wy in dict meitsje wêryn elke emoasje in oerienkommende kleur sil hawwe yn 'e foarm fan in RGB Tuple en in objekt fan' e klasse aiy.leds.Leds, wêrmei't wy de kleur bywurkje:

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

En as lêste, nei elke nije foarsizzing fan in emoasje, sille wy de kleur fan 'e knop yn oerienstimming mei har bywurkje (mei kaai).

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

OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi
Knop, brân!

Wurkje mei stim

Wy sille pyaudio brûke om de stream fan 'e mikrofoan en webrtcvad te fangen om lûd te filterjen en stim te detektearjen. Derneist sille wy in wachtrige oanmeitsje wêryn wy asynchrone stimúttreksels sille tafoegje en fuortsmite.

Om't webrtcvad in beheining hat op 'e grutte fan it levere fragmint - it moat lykweardich wêze oan 10/20/30ms, en de training fan it model foar it erkennen fan emoasjes (lykas wy letter sille leare) waard útfierd op in 48kHz dataset, sille wy capture brokken fan grutte 48000 × 20ms / 1000 × 1 (mono) = 960 bytes. Webrtcvad sil True / False weromjaan foar elk fan dizze brokken, wat oerienkomt mei de oanwêzigens of ôfwêzigens fan in stimming yn it brok.

Litte wy de folgjende logika ymplementearje:

  • Wy sille dy brokken tafoegje oan 'e list wêr't in stimming is; as der gjin stimmen is, dan sille wy de teller fan lege brokken ferheegje.
  • As de teller fan lege brokken >=30 (600 ms) is, dan sjogge wy nei de grutte fan 'e list mei opboude brokken; as it is >250, dan foegje wy it ta oan 'e wachtrige; as net, beskôgje wy dat de lingte fan it rekord is net genôch om it oan it model te fieden om de sprekker te identifisearjen.
  • As de teller fan lege brokken noch <30 is, en de grutte fan 'e list mei opboude brokken grutter is dan 300, dan sille wy it fragmint tafoegje oan' e wachtrige foar in krekter foarsizzing. (om't emoasjes mei de tiid feroarje)

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

It is tiid om te sykjen nei pre-trained modellen yn it publike domein, gean nei github, Google, mar tink derom dat wy in beheining hawwe op 'e brûkte arsjitektuer. Dit is in nochal lestich diel, om't jo de modellen moatte testen op jo ynfiergegevens, en se ek konvertearje nei it ynterne formaat fan OpenVINO - IR (Intermediate Representation). Wy besochten sawat 5-7 ferskillende oplossingen fan github, en as it model foar it herkennen fan emoasjes fuortendaliks wurke, dan moasten wy mei spraakherkenning langer wachtsje - se brûke kompleksere arsjitektuer.

Wy rjochtsje ús op it folgjende:

Folgjende sille wy prate oer it konvertearjen fan modellen, begjinnend mei teory. OpenVINO omfettet ferskate modules:

  • Iepen Model Zoo, modellen wêrfan kinne wurde brûkt en opnommen yn jo produkt
  • Model Optimzer, wêrtroch jo in model kinne konvertearje fan ferskate ramtformaten (Tensorflow, ONNX ensfh) yn it Intermediate Representation-formaat, wêrmei't wy fierder sille wurkje
  • Inference Engine kinne jo modellen útfiere yn IR-formaat op Intel-processors, Myriad-chips en Neural Compute Stick-versnellers
  • De meast effisjinte ferzje fan OpenCV (mei Inference Engine-stipe)
    Elts model yn IR opmaak wurdt beskreaun troch twa triemmen: .xml en .bin.
    Modellen wurde omboud ta IR-formaat fia Model Optimizer as folget:

    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 kinne jo selektearje de gegevens opmaak wêrmei it model sil wurkje. FP32, FP16, INT8 wurde stipe. It selektearjen fan it optimale gegevenstype kin in goede prestaasjesympuls jaan.
    --input_shape jout de diminsje fan de ynfier gegevens oan. De mooglikheid om it dynamysk te feroarjen liket oanwêzich te wêzen yn 'e C ++ API, mar wy hawwe net sa fier graven en gewoan fêststeld foar ien fan' e modellen.
    Litte wy dan besykje it al konvertearre model yn IR-formaat te laden fia de DNN-module yn OpenCV en it nei it troch te stjoeren.

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

    De lêste rigel yn dit gefal lit jo berekkeningen omliede nei de Neural Compute Stick, basisberekkeningen wurde útfierd op 'e prosessor, mar yn' t gefal fan 'e Raspberry Pi sil dit net wurkje, jo sille in stôk nedich wêze.

    Dêrnei is de logika as folget: wy ferdiele ús audio yn finsters fan in bepaalde grutte (foar ús is it 0.4 s), wy konvertearje elk fan dizze finsters yn MFCC, dy't wy dan nei it raster fiede:

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

    Litte wy dan de meast foarkommende klasse nimme foar alle finsters. In ienfâldige oplossing, mar foar in hackathon hoege jo net mei wat te abstrus te kommen, allinich as jo tiid hawwe. Wy hawwe noch in soad wurk te dwaan, dus litte wy trochgean - wy sille omgean mei stimherkenning. It is nedich om in soarte fan databank te meitsjen wêryn spektrogrammen fan foarôf opnommen stimmen wurde opslein. Om't der in bytsje tiid oer is, sille wy dit probleem sa goed mooglik oplosse.

    Wy meitsje nammentlik in skript foar it opnimmen fan in stimúttreksel (it wurket op deselde manier as hjirboppe beskreaun, allinich as it wurdt ûnderbrutsen fan it toetseboerd sil it de stim opslaan yn in bestân).

    Litte we it besykje:

    python3 voice_db/record_voice.py test.wav

    Wy opnimme de stimmen fan ferskate minsken (yn ús gefal, trije teamleden)
    Folgjende, foar elke opnommen stim útfiere wy in flugge Fourier transformaasje, krije in spektrogram en bewarje it as in numpy array (.npy):

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

    Mear details yn it bestân create_base.py
    As gefolch, as wy it haadskript útfiere, sille wy oan it begjin ynbêden krije fan dizze spektrogrammen:

    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)

    Nei ûntfangst fan de ynbêding fan it klinkende segmint, kinne wy ​​​​bepale wa't it heart troch de kosinusôfstân fan 'e passaazje nei alle stimmen yn' e databank te nimmen (hoe lytser, hoe wierskynliker) - foar de demo sette wy de drompel yn. oant 0.3):

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

    Yn 'e ein, ik soe graach opskriuwen dat de konklúzje snelheid wie fluch en makke it mooglik om te foegjen 1-2 mear modellen (foar in stekproef 7 sekonden lang duorre it 2.5 foar inference). Wy hiene gjin tiid mear om nije modellen ta te foegjen en rjochte ús op it skriuwen fan in prototype fan 'e webapplikaasje.

    Web Applikaasje

    In wichtich punt: wy nimme in router mei ús thús en set ús lokale netwurk op, it helpt om it apparaat en laptops oer it netwurk te ferbinen.

    De efterkant is in ein-oan-ein berjochtkanaal tusken de foarkant en Raspberry Pi, basearre op websockettechnology (http over tcp-protokol).

    De earste etappe is it ûntfangen fan ferwurke ynformaasje fan raspberry, dat is, foarsizzers ynpakt yn json, dy't opslein wurde yn 'e databank healwei har reis, sadat statistiken kinne wurde generearre oer de emosjonele eftergrûn fan' e brûker foar de perioade. Dit pakket wurdt dan stjoerd nei de frontend, dy't abonnemint brûkt en pakketten ûntfangt fan it websocket-einpunt. It heule backend-meganisme is boud yn 'e golang-taal; it waard keazen om't it goed geskikt is foar asynchrone taken, dy't goroutines goed behannelje.
    By tagong ta it einpunt wurdt de brûker registrearre en ynfierd yn 'e struktuer, dan wurdt syn berjocht ûntfongen. Sawol de brûker as it berjocht wurde ynfierd yn in mienskiplike hub, wêrfan berjochten al fierder ferstjoerd wurde (nei de ynskreaune front), en as de brûker de ferbining slút (raspberry of front), dan wurdt syn abonnemint annulearre en hy wurdt fuorthelle út de hub.

    OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi
    Wy wachtsje op in ferbining fan efteren

    Front-end is in webapplikaasje skreaun yn JavaScript mei de React-bibleteek om it ûntwikkelingsproses te fersnellen en te ferienfâldigjen. It doel fan dizze applikaasje is om gegevens te visualisearjen krigen mei algoritmen dy't rinne op 'e efterkant en direkt op' e Raspberry Pi. De side hat seksje-routing ymplementearre mei help fan react-router, mar de haadside fan belang is de haadside, wêr't in trochgeande stream fan gegevens yn realtime wurdt ûntfongen fan 'e tsjinner mei WebSocket-technology. Raspberry Pi detektearret in stim, bepaalt oft it heart ta in spesifike persoan út de registrearre databank, en stjoert in kâns list nei de klant. De kliïnt toant de lêste relevante gegevens, toant de avatar fan 'e persoan dy't wierskynlik yn' e mikrofoan spruts, en ek de emoasje wêrmei't hy de wurden útsprekt.

    OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi
    Thússide mei bywurke foarsizzings

    konklúzje

    It wie net mooglik om alles te foltôgjen lykas pland, wy hienen gewoan gjin tiid, dus de wichtichste hoop wie yn 'e demo, dat alles soe wurkje. Yn de presintaasje prate se oer hoe't alles wurket, hokker modellen se namen, hokker problemen se tsjinkamen. Folgjende wie it demo-diel - saakkundigen rûnen yn willekeurige folchoarder troch de keamer en benadere elk team om nei it wurkjende prototype te sjen. Se stelden ús ek fragen, elkenien antwurde har diel, se lieten it web op 'e laptop, en alles wurke echt lykas ferwachte.

    Lit my opmerke dat de totale kosten fan ús oplossing $ 150 wiene:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (jo kinne in sprekkerfergoeding nimme) ~ 15 $
    • Intel NCS 2 ~ 100 $

    Hoe ferbetterje:

    • Brûk registraasje fan 'e kliïnt - freegje om de tekst te lêzen dy't willekeurich wurdt generearre
    • Foegje in pear mear modellen ta: jo kinne geslacht en leeftyd troch stim bepale
    • Separearje tagelyk klinkende stimmen (diarisaasje)

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

    OpenVINO hackathon: stim en emoasjes werkenne op Raspberry Pi
    Wy binne wurch mar bliid

    Ta beslút wol ik de organisatoaren en dielnimmers tank sizze. Under de projekten fan oare teams fûnen wy persoanlik de oplossing foar it kontrolearjen fan frije parkearplakken. Foar ús wie it in wyld koele ûnderfining fan ûnderdompeling yn it produkt en ûntwikkeling. Ik hoopje dat mear en mear nijsgjirrige eveneminten sille wurde hâlden yn 'e regio's, ynklusyf oer AI-ûnderwerpen.

Boarne: www.habr.com

Add a comment