OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen

30. November - Dezember 1 zu Nizhny Novgorod ofgehalen OpenVINO Hackathon. D'Participanten goufen opgefuerdert e Prototyp vun enger Produktléisung mam Intel OpenVINO Toolkit ze kreéieren. D'Organisateuren hunn eng Lëscht vun ongeféieren Themen proposéiert, déi bei der Auswiel vun enger Aufgab guidéiert kënne ginn, awer déi definitiv Entscheedung blouf bei den Équipen. Zousätzlech gouf d'Benotzung vu Modeller encouragéiert, déi net am Produkt abegraff sinn.

OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen

An dësem Artikel wäerte mir Iech soen wéi mir eise Prototyp vum Produkt erstallt hunn, mat deem mir schlussendlech éischt Plaz geholl hunn.

Méi wéi 10 Equippen hunn um Hackathon deelgeholl. Et ass schéin, datt e puer vun hinnen aus anere Regioune koumen. D'Plaz fir den Hackathon war de Komplex "Kremlinsky op Pochain", wou antike Fotoe vun Nizhny Novgorod dobannen opgehang goufen, an engem Entourage! (Ech erënneren Iech datt am Moment den Zentralbüro vun Intel zu Nizhny Novgorod läit). D'Participanten kruten 26 Stonnen Zäit fir Code ze schreiwen, an um Enn hu se hir Léisung ze presentéieren. E separate Virdeel war d'Präsenz vun enger Demo Sessioun fir sécherzestellen datt alles wat geplangt ass tatsächlech ëmgesat gouf an net Iddien an der Presentatioun bleift. Merch, Snacks, Iessen, alles war och do!

Zousätzlech huet Intel optional Kameraen zur Verfügung gestallt, Raspberry PI, Neural Compute Stick 2.

Aufgab Auswiel

Ee vun de schwieregsten Deeler vun der Virbereedung fir e gratis-Form Hackathon ass eng Erausfuerderung ze wielen. Mir hunn direkt decidéiert mat eppes ze kommen wat nach net am Produkt war, well d'Ukënnegung sot datt dëst ganz wëllkomm ass.

Analyséiert ze hunn Modellen, déi am Produkt an der aktueller Verëffentlechung abegraff sinn, komme mir zur Conclusioun datt déi meescht vun hinnen verschidde Computervisiounsproblemer léisen. Ausserdeem ass et ganz schwéier mat engem Problem am Beräich vun der Computervisioun ze kommen, deen net mat OpenVINO geléist ka ginn, an och wann een erfonnt ka ginn, ass et schwéier pre-trainéiert Modeller am Domaine public ze fannen. Mir décidéieren an eng aner Richtung ze graven - Richtung Ried Veraarbechtung an Analyse. Loosst eis eng interessant Aufgab betruecht fir Emotiounen aus Ried ze erkennen. Et muss gesot ginn datt OpenVINO schonn e Modell huet deen d'Emotiounen vun enger Persoun baséiert op hirem Gesiicht bestëmmt, awer:

  • An Theorie ass et méiglech e kombinéierten Algorithmus ze kreéieren deen op Toun a Bild funktionnéiert, wat eng Erhéijung vun der Genauegkeet sollt ginn.
  • Kameraen hunn normalerweis e schmuele Siichtwénkel; méi wéi eng Kamera ass erfuerderlech fir e grousst Gebitt ze decken; Toun huet net sou eng Begrenzung.

Loosst eis d'Iddi entwéckelen: loosst eis d'Iddi fir de Retail Segment als Basis huelen. Dir kënnt d'Zefriddenheet vun de Clienten an de Buttekskäschte moossen. Wann ee vun de Clienten net zefridden mam Service ass a fänkt un hiren Toun z'erhéijen, kënnt Dir direkt den Administrateur fir Hëllef ruffen.
An dësem Fall musse mir d'mënschlech Stëmmerkennung addéieren, dëst erlaabt eis Buttek Mataarbechter vu Clienten z'ënnerscheeden an Analyse fir all Eenzelen ze bidden. Gutt, zousätzlech wäert et méiglech sinn d'Behuele vun de Buttek Mataarbechter selwer ze analyséieren, d'Atmosphär an der Équipe evaluéieren, kléngt gutt!

Mir formuléieren d'Ufuerderunge fir eis Léisung:

  • Kleng Gréisst vun der Zil- Apparat
  • Echtzäit Operatioun
  • Méi niddreg Präisser
  • Einfach Skalierbarkeet

Als Resultat, wielt mir Raspberry Pi 3 c als Zil- Apparat Intel NCS 2.

Hei ass et wichteg eng wichteg Feature vun NCS ze notéieren - et funktionnéiert am Beschten mat Standard CNN Architekturen, awer wann Dir e Modell mat personaliséierte Schichten muss lafen, erwaart Iech e Low-Level Optimiséierung.

Et gëtt just eng kleng Saach ze maachen: Dir musst e Mikro kréien. E normale USB Mikrofon wäert et maachen, awer et wäert net gutt zesumme mam RPI kucken. Awer och hei ass d'Léisung wuertwiertlech "an der Géigend." Fir Stëmm opzehuelen, decidéieren mir de Voice Bonnet Board aus dem Kit ze benotzen Google AIY Voice Kit, op deem et e verkabelte Stereomikrofon ass.

Download Raspbian aus AIY Projeten Repository an lued et op e Flash Drive erop, test datt de Mikrofon funktionnéiert mat dem folgenden Kommando (et wäert Audio 5 Sekonnen laang ophuelen an et an eng Datei späicheren):

arecord -d 5 -r 16000 test.wav

Ech sollt direkt feststellen datt de Mikro ganz sensibel ass a Geräischer gutt ophëlt. Fir dëst ze fixéieren, loosst eis op alsamixer goen, wielt Capture Geräter a reduzéieren den Input Signalniveau op 50-60%.

OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen
Mir änneren de Kierper mat enger Datei an alles passt, Dir kënnt et souguer mat engem Deckel zoumaachen

Dobäizemaachen en Indikator Knäppchen

Wärend den AIY Voice Kit auserneen hëlt, erënnere mir eis datt et e RGB-Knäppchen ass, deen d'Backlight vun der Software kontrolléiert ka ginn. Mir sichen no "Google AIY Led" a fanne Dokumentatioun: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Firwat benotzen dëse Knäppchen net déi unerkannt Emotioun ze weisen, mir hunn nëmmen 7 Klassen, an de Knäppchen huet 8 Faarwen, just genuch!

Mir verbannen de Knäppchen iwwer GPIO op Voice Bonnet, lued déi néideg Bibliothéiken (si si schonn am Verdeelungskit vun AIY Projeten installéiert)

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

Loosst eis en Dikt erstellen an deem all Emotioun eng entspriechend Faarf a Form vun engem RGB Tuple an en Objet vun der Klass aiy.leds.Leds wäert hunn, duerch déi mir d'Faarf aktualiséieren:

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

A schliisslech, no all neier Prognose vun enger Emotioun, wäerte mir d'Faarf vum Knäppchen aktualiséieren entspriechend et (mam Schlëssel).

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

OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen
Knapp, verbrennt!

Schafft mat Stëmm

Mir benotze pyaudio fir de Stream aus dem Mikrofon a webrtcvad z'erfaassen fir Geräischer ze filteren an d'Stëmm z'entdecken. Zousätzlech wäerte mir eng Schlaang erstellen, op déi mir asynchron Stëmmauszüge addéieren an ewechhuelen.

Zënter Webrtcvad huet eng Begrenzung vun der Gréisst vum geliwwerte Fragment - et muss gläich 10/20/30ms sinn, an d'Formatioun vum Modell fir Emotiounen z'erkennen (wéi mir méi spéit léieren) gouf op engem 48kHz Dataset duerchgefouert, wäerte mir erfaassen Stécker vu Gréisst 48000 × 20ms / 1000 × 1 (Mono) = 960 Bytes. Webrtcvad wäert True / False fir all eenzel vun dësen Stécker zréckginn, wat mat der Präsenz oder der Fehlen vun engem Vote am Stéck entsprécht.

Loosst eis déi folgend Logik implementéieren:

  • Mir wäerten déi Stécker op d'Lëscht setzen, wou et gestëmmt gëtt; wann et kee Vote gëtt, da wäerte mir de Konter vun eidel Stécker erhéijen.
  • Wann de Konter vun eidel Stécker >=30 (600 ms) ass, da kucke mir d'Gréisst vun der Lëscht vun de akkumuléierte Stécker; wann et >250 ass, da fügen mir et an d'Schlaang; wann net, betruechte mir datt d'Längt vum Rekord ass net genuch fir et dem Modell ze fidderen fir de Spriecher z'identifizéieren.
  • Wann de Konter vun eidel Stécker nach ëmmer <30 ass, an d'Gréisst vun der Lëscht vun de akkumuléierte Stécker méi wéi 300 ass, da addéiere mer de Fragment an d'Schlaang fir eng méi genee Prognose. (well Emotiounen tendéieren mat der Zäit ze änneren)

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

Et ass Zäit fir pre-trainéiert Modeller am Domaine public ze sichen, gitt op github, Google, awer erënnert datt mir eng Begrenzung vun der benotzter Architektur hunn. Dëst ass e relativ schwéieren Deel, well Dir musst d'Modeller op Ären Inputdaten testen, an zousätzlech konvertéieren se an dat internt Format vum OpenVINO - IR (Intermediate Representation). Mir hunn ongeféier 5-7 verschidde Léisunge vu Github probéiert, a wann de Modell fir Emotiounen ze erkennen huet direkt geschafft, dann hu mir mat Stëmmerkennung méi laang misse waarden - si benotze méi komplex Architekturen.

Mir konzentréieren eis op déi folgend:

Als nächst wäerte mir iwwer d'Konvertéierung vun Modeller schwätzen, ugefaange mat der Theorie. OpenVINO enthält verschidde Moduler:

  • Open Model Zoo, Modeller aus deenen an Ärem Produkt benotzt an abegraff kënne ginn
  • Model Optimzer, dank deem Dir e Modell aus verschiddene Frameworkformater (Tensorflow, ONNX etc) an d'Intermediate Representation Format konvertéieren, mat deem mir weider schaffen
  • Inference Engine erlaabt Iech Modeller am IR Format op Intel Prozessoren, Myriad Chips an Neural Compute Stick Beschleuniger ze lafen
  • Déi effizientst Versioun vum OpenCV (mat Inference Engine Support)
    All Modell am IR Format gëtt vun zwee Fichieren beschriwwen: .xml an .bin.
    Modeller ginn an IR Format iwwer Model Optimizer ëmgewandelt wéi follegt:

    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 erlaabt Iech den Dateformat ze wielen mat deem de Modell funktionnéiert. FP32, FP16, INT8 ginn ënnerstëtzt. Wiel vun der optimaler Datetyp kann e gudde Performance Boost ginn.
    --input_shape weist d'Dimensioun vun den Inputdaten un. D'Kapazitéit fir dynamesch z'änneren schéngt an der C ++ API präsent ze sinn, awer mir hunn net sou wäit gegruewen an et einfach fir ee vun de Modeller fixéiert.
    Nächst, loosst eis probéieren de scho konvertéierte Modell am IR Format iwwer den DNN Modul an OpenCV ze lueden an et weider ze schécken.

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

    Déi lescht Zeil an dësem Fall erlaabt Iech d'Berechnungen op den Neural Compute Stick ze redirectéieren, Basisberechnunge ginn um Prozessor ausgefouert, awer am Fall vun der Raspberry Pi funktionnéiert dat net, Dir braucht e Stick.

    Als nächst ass d'Logik wéi follegt: mir deelen eisen Audio a Fënstere vun enger gewësser Gréisst (fir eis ass et 0.4 s), mir konvertéieren all dës Fënsteren an MFCC, déi mir dann an d'Gitter fidderen:

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

    Als nächst, loosst eis déi allgemeng Klass fir all Fënsteren huelen. Eng einfach Léisung, awer fir en Hackathon musst Dir net mat eppes ze abstrus kommen, nëmmen wann Dir Zäit hutt. Mir hunn nach vill Aarbecht ze maachen, also loosst eis weidergoen - mir wäerte mat Stëmmerkennung beschäftegen. Et ass néideg eng Zort Datebank ze maachen, an där Spektrogramme vu viropgeholle Stëmme gespäichert ginn. Well et wéineg Zäit bleift, wäerte mir dëst Thema sou gutt wéi méiglech léisen.

    Mir kreéieren nämlech e Skript fir e Stëmmauszuch opzehuelen (et funktionnéiert op déiselwecht Manéier wéi uewen beschriwwen, nëmmen wann se vun der Tastatur ënnerbrach gëtt, späichert et d'Stëmm an eng Datei).

    Loosst eis probéieren:

    python3 voice_db/record_voice.py test.wav

    Mir notéieren d'Stëmme vu verschiddene Leit (an eisem Fall, dräi Teammemberen)
    Als nächst maache mir fir all opgeholl Stëmm e schnelle Fourier-Transform, kréien e Spektrogramm a späicheren et als numpy-Array (.npy):

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

    Méi Detailer am Dossier create_base.py
    Als Resultat, wa mir den Haaptskript lafen, kréie mir Embeddings vun dëse Spektrogrammen am Ufank:

    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)

    Nom Empfang vun der Embedding vum geklongen Segment, kënne mir bestëmmen, wiem et gehéiert, andeems Dir d'Cosinus-Distanz vum Passage op all d'Stëmmen an der Datebank hëlt (wat méi kleng, wat méi wahrscheinlech) - fir d'Demo setzen mir d'Schwell. bis 0.3):

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

    Zum Schluss wëll ech feststellen datt d'Inferenzgeschwindegkeet séier war an et méiglech gemaach huet 1-2 méi Modeller ze addéieren (fir eng Probe 7 Sekonnen laang huet et 2.5 fir Inferenz gedauert). Mir haten net méi Zäit fir nei Modeller derbäi ze ginn a konzentréieren eis op e Prototyp vun der Webapplikatioun ze schreiwen.

    Web Applikatioun

    E wichtege Punkt: Mir huelen e Router vun doheem mat eis an setzen eise lokalen Netzwierk op, et hëlleft den Apparat an de Laptops iwwer dem Netz ze verbannen.

    De Backend ass en Enn-zu-Enn Message Kanal tëscht der Front a Raspberry Pi, baséiert op Websocket Technologie (http iwwer tcp Protokoll).

    Déi éischt Stuf ass d'Veraarbechtte Informatioun vu Himbeer ze kréien, dat heescht, Prädiktoren déi am json gepackt sinn, déi an der Datebank hallef duerch hir Rees gespäichert ginn, sou datt Statistiken iwwer den emotionalen Hannergrond vum Benotzer fir d'Period generéiert kënne ginn. Dëse Paket gëtt dann un de Frontend geschéckt, deen Abonnement benotzt a Pakete vum Websocket Endpunkt kritt. De ganze Backend Mechanismus ass an der Golang Sprooch gebaut; Et gouf gewielt well et gutt ass fir asynchron Aufgaben, déi d'Goroutine gutt handhaben.
    Beim Zougang zum Endpunkt gëtt de Benotzer registréiert an an d'Struktur aginn, da gëtt säi Message kritt. Souwuel de Benotzer wéi d'Botschaft ginn an e gemeinsame Hub aginn, vun deem d'Messagen scho weider geschéckt ginn (op d'abonnéiert Front), a wann de Benotzer d'Verbindung zoumaacht (Hambierbéier oder Front), da gëtt säin Abonnement annuléiert an hie gëtt aus den Hub.

    OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen
    Mir waarden op eng Verbindung vum Réck

    Front-End ass eng Webapplikatioun geschriwwen a JavaScript mat der React Bibliothéik fir den Entwécklungsprozess ze beschleunegen an ze vereinfachen. Den Zweck vun dëser Applikatioun ass d'Daten ze visualiséieren, déi mat Algorithmen op der Récksäit an direkt op der Raspberry Pi lafen. D'Säit huet Sektiounsrouting implementéiert mam React-Router, awer d'Haaptsäit vun Interesse ass d'Haaptsäit, wou e kontinuéierleche Stroum vun Daten an Echtzäit vum Server mat WebSocket Technologie kritt gëtt. Raspberry Pi erkennt eng Stëmm, bestëmmt ob et zu enger spezifescher Persoun aus der registréierter Datebank gehéiert, a schéckt eng Wahrscheinlechkeetslëscht un de Client. De Client weist déi lescht relevant Donnéeën, weist den Avatar vun der Persoun déi héchstwahrscheinlech an de Mikro geschwat huet, wéi och d'Emotioun mat där hien d'Wierder aussprécht.

    OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen
    Homepage mat aktualiséierten Prognosen

    Konklusioun

    Et war net méiglech alles wéi geplangt ofzeschléissen, mir haten einfach keng Zäit, sou datt d'Haapthoffnung an der Demo war, datt alles funktionnéiert. An der Presentatioun hu si geschwat wéi alles funktionéiert, wéi eng Modeller si geholl hunn, wéi eng Problemer si stoungen. Als nächst war den Demo-Deel - Experten sinn an zoufälleger Uerdnung ronderëm de Raum gaang an sinn all Team ukomm fir den funktionnéierende Prototyp ze kucken. Si hunn eis och Froen gestallt, jiddereen huet hiren Deel geäntwert, si hunn de Web um Laptop verlooss, an alles huet wierklech geschafft wéi erwaart.

    Loosst mech notéieren datt d'Gesamtkäschte vun eiser Léisung $ 150 waren:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (Dir kënnt e Spriechergebühr huelen) ~ 15 $
    • Intel NCS 2 ~ 100 $

    Wéi verbesseren:

    • Benotzt Umeldung vum Client - frot den Text ze liesen deen zoufälleg generéiert gëtt
    • Füügt e puer méi Modeller: Dir kënnt Geschlecht an Alter duerch Stëmm bestëmmen
    • Separat gläichzäiteg klingende Stëmmen (Diariséierung)

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

    OpenVINO Hackathon: Stëmm an Emotiounen op Raspberry Pi erkennen
    Mir sinn midd awer glécklech

    Ofschléissend wëll ech den Organisateuren an d'Participanten Merci soen. Ënnert de Projete vun aneren Teams hu mir perséinlech d'Léisung fir d'Iwwerwaachung vun gratis Parkplazen gefall. Fir eis war et eng wilde cool Erfahrung vun Taucht am Produkt an Entwécklung. Ech hoffen, datt ëmmer méi interessant Evenementer an de Regiounen ofgehale ginn, och iwwer AI Themen.

Source: will.com

Setzt e Commentaire