OpenVINO hackathon: herken stem en emosies op Raspberry Pi

30 November - 1 Desember in Nizhny Novgorod gehou OpenVINO hackathon. Deelnemers is gevra om 'n prototipe van 'n produkoplossing te skep deur die Intel OpenVINO-gereedskapstel te gebruik. Die organiseerders het 'n lys van benaderde onderwerpe voorgestel wat gelei kan word deur wanneer 'n taak gekies word, maar die finale besluit het by die spanne gebly. Daarbenewens is die gebruik van modelle wat nie by die produk ingesluit is nie, aangemoedig.

OpenVINO hackathon: herken stem en emosies op Raspberry Pi

In hierdie artikel sal ons jou vertel hoe ons ons prototipe van die produk geskep het, waarmee ons uiteindelik die eerste plek behaal het.

Meer as 10 spanne het aan die hackathon deelgeneem. Dis lekker dat sommige van hulle uit ander streke gekom het. Die plek vir die hackathon was die "Kremlinsky on Pochain"-kompleks, waar ou foto's van Nizhny Novgorod binne gehang is, in 'n gevolg! (Ek herinner u daaraan dat die sentrale kantoor van Intel tans in Nizhny Novgorod geleë is). Deelnemers is 26 uur gegee om kode te skryf, en aan die einde moes hulle hul oplossing aanbied. 'n Afsonderlike voordeel was die teenwoordigheid van 'n demonstrasiesessie om seker te maak dat alles wat beplan is, werklik geïmplementeer is en nie idees in die aanbieding gebly het nie. Handelsware, eetgoed, kos, alles was ook daar!

Daarbenewens het Intel opsioneel kameras, Raspberry PI, Neural Compute Stick 2 verskaf.

Taakkeuse

Een van die moeilikste dele van die voorbereiding vir 'n vrye-vorm hackathon is om 'n uitdaging te kies. Ons het dadelik besluit om met iets vorendag te kom wat nog nie in die produk was nie, aangesien die aankondiging gesê het dat dit baie welkom is.

Na ontleed model, wat by die produk in die huidige weergawe ingesluit is, kom ons tot die gevolgtrekking dat die meeste van hulle verskeie rekenaarvisieprobleme oplos. Boonop is dit baie moeilik om met 'n probleem op die gebied van rekenaarvisie vorendag te kom wat nie met OpenVINO opgelos kan word nie, en selfs al kan een uitgevind word, is dit moeilik om vooraf opgeleide modelle in die publieke domein te vind. Ons besluit om in 'n ander rigting te delf - na spraakverwerking en analise. Kom ons kyk na 'n interessante taak om emosies uit spraak te herken. Dit moet gesê word dat OpenVINO reeds 'n model het wat 'n persoon se emosies bepaal op grond van hul gesig, maar:

  • In teorie is dit moontlik om 'n gekombineerde algoritme te skep wat op beide klank en beeld sal werk, wat 'n toename in akkuraatheid behoort te gee.
  • Kameras het gewoonlik 'n nou kykhoek; meer as een kamera word benodig om 'n groot area te dek; klank het nie so 'n beperking nie.

Kom ons ontwikkel die idee: kom ons neem die idee vir die kleinhandelsegment as basis. U kan kliëntetevredenheid by winkelafhandelings meet. As een van die kliënte ontevrede is met die diens en hul toon begin verhef, kan jy dadelik die administrateur vir hulp bel.
In hierdie geval moet ons menslike stemherkenning byvoeg, dit sal ons toelaat om winkelwerknemers van kliënte te onderskei en ontledings vir elke individu te verskaf. Wel, boonop sal dit moontlik wees om die gedrag van die winkelwerknemers self te ontleed, die atmosfeer in die span te evalueer, klink goed!

Ons formuleer die vereistes vir ons oplossing:

  • Klein grootte van die teiken toestel
  • Intydse werking
  • Lae prys
  • Maklike skaalbaarheid

As gevolg hiervan, kies ons Raspberry Pi 3 c as die teiken toestel Intel NCS 2.

Hier is dit belangrik om op een belangrike kenmerk van NCS te let - dit werk die beste met standaard CNN-argitekture, maar as jy 'n model met pasgemaakte lae daarop moet laat loop, verwag dan laevlak-optimering.

Daar is net een klein ding om te doen: jy moet 'n mikrofoon kry. 'n Gewone USB-mikrofoon sal doen, maar dit sal nie goed lyk saam met die RPI nie. Maar selfs hier lê die oplossing letterlik “naby”. Om stem op te neem, besluit ons om die Voice Bonnet-bord uit die kit te gebruik Google AIY Voice Kit, waarop daar 'n bedrade stereomikrofoon is.

Laai Raspbian af vanaf AIY-projekte-bewaarplek en laai dit op na 'n flash drive, toets dat die mikrofoon werk deur die volgende opdrag te gebruik (dit sal oudio 5 sekondes lank opneem en dit in 'n lêer stoor):

arecord -d 5 -r 16000 test.wav

Ek moet dadelik daarop let dat die mikrofoon baie sensitief is en geraas goed optel. Om dit reg te stel, kom ons gaan na alsamixer, kies Vasvangtoestelle en verminder die insetseinvlak tot 50-60%.

OpenVINO hackathon: herken stem en emosies op Raspberry Pi
Ons verander die liggaam met 'n lêer en alles pas, jy kan dit selfs met 'n deksel toemaak

Voeg 'n aanwyserknoppie by

Terwyl ons die AIY Voice Kit uitmekaar haal, onthou ons dat daar 'n RGB-knoppie is, waarvan die agtergrond deur sagteware beheer kan word. Ons soek na "Google AIY Led" en vind dokumentasie: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Hoekom nie hierdie knoppie gebruik om die erkende emosie te vertoon nie, ons het net 7 klasse, en die knoppie het 8 kleure, net genoeg!

Ons koppel die knoppie via GPIO aan Voice Bonnet, laai die nodige biblioteke (hulle is reeds geïnstalleer in die verspreidingskit van AIY-projekte)

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

Kom ons skep 'n diktaat waarin elke emosie 'n ooreenstemmende kleur sal hê in die vorm van 'n RGB Tuple en 'n voorwerp van die klas aiy.leds.Leds, waardeur ons die kleur sal opdateer:

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 uiteindelik, na elke nuwe voorspelling van 'n emosie, sal ons die kleur van die knoppie in ooreenstemming daarmee opdateer (per sleutel).

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

OpenVINO hackathon: herken stem en emosies op Raspberry Pi
Knoppie, brand!

Werk met stem

Ons sal pyaudio gebruik om die stroom van die mikrofoon en webrtcvad vas te vang om geraas te filter en stem op te spoor. Daarbenewens sal ons 'n tou skep waarby ons asinchronies stemuittreksels sal byvoeg en verwyder.

Aangesien webrtcvad 'n beperking het op die grootte van die verskafde fragment - dit moet gelyk wees aan 10/20/30ms, en die opleiding van die model vir die herkenning van emosies (soos ons later sal leer) is uitgevoer op 'n 48kHz datastel, sal ons vang stukke van grootte 48000×20ms/1000×1(mono)=960 grepe vas. Webrtcvad sal vir elkeen van hierdie stukke Waar/Onwaar terugstuur, wat ooreenstem met die teenwoordigheid of afwesigheid van 'n stem in die stuk.

Kom ons implementeer die volgende logika:

  • Ons sal daardie stukke waar daar gestem word by die lys voeg; as daar nie gestem is nie, sal ons die teller van leë stukke verhoog.
  • As die teller van leë stukke >=30 (600 ms) is, dan kyk ons ​​na die grootte van die lys opgehoopte stukke; as dit >250 is, dan voeg ons dit by die tou; indien nie, beskou ons dat die lengte van die rekord is nie genoeg om dit aan die model te voer om die spreker te identifiseer nie.
  • As die teller van leë stukke steeds < 30 is, en die grootte van die lys opgehoopte stukke oorskry 300, dan sal ons die fragment by die tou voeg vir 'n meer akkurate voorspelling. (omdat emosies geneig is om met verloop van tyd te verander)

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

Dit is tyd om te soek na vooraf-opgeleide modelle in die publieke domein, gaan na github, Google, maar onthou dat ons 'n beperking het op die argitektuur wat gebruik word. Dit is 'n taamlik moeilike deel, want jy moet die modelle op jou insetdata toets, en dit boonop omskakel na OpenVINO se interne formaat - IR (Intermediate Representation). Ons het ongeveer 5-7 verskillende oplossings van github probeer, en as die model vir die herkenning van emosies dadelik gewerk het, dan moes ons met stemherkenning langer wag - hulle gebruik meer komplekse argitekture.

Ons fokus op die volgende:

Volgende sal ons praat oor die omskakeling van modelle, begin met teorie. OpenVINO bevat verskeie modules:

  • Oop Model Zoo, modelle waarvandaan gebruik kan word en by jou produk ingesluit kan word
  • Model Optimzer, waardeur u 'n model van verskeie raamwerkformate (Tensorflow, ONNX, ens.) kan omskep in die Intermediêre verteenwoordiging-formaat, waarmee ons verder sal werk
  • Inference Engine laat jou toe om modelle in IR-formaat op Intel-verwerkers, Myriad-skyfies en Neural Compute Stick-versnellers te laat loop
  • Die mees doeltreffende weergawe van OpenCV (met Inference Engine-ondersteuning)
    Elke model in IR-formaat word beskryf deur twee lêers: .xml en .bin.
    Modelle word soos volg omgeskakel na IR-formaat via Model Optimizer:

    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 laat jou toe om die dataformaat te kies waarmee die model sal werk. FP32, FP16, INT8 word ondersteun. Die keuse van die optimale datatipe kan 'n goeie prestasie-hupstoot gee.
    --input_shape dui die dimensie van die invoerdata aan. Die vermoë om dit dinamies te verander blyk teenwoordig te wees in die C++ API, maar ons het nie so ver gegrawe nie en dit eenvoudig vir een van die modelle reggemaak.
    Kom ons probeer dan om die reeds omgeskakelde model in IR-formaat via die DNN-module in OpenCV te laai en dit daarna aan te stuur.

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

    Die laaste reël in hierdie geval laat jou toe om berekeninge na die Neural Compute Stick te herlei, basiese berekeninge word op die verwerker uitgevoer, maar in die geval van die Raspberry Pi sal dit nie werk nie, jy sal 'n stok nodig hê.

    Vervolgens is die logika soos volg: ons verdeel ons klank in vensters van 'n sekere grootte (vir ons is dit 0.4 s), ons skakel elkeen van hierdie vensters om na MFCC, wat ons dan na die rooster voer:

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

    Kom ons neem dan die mees algemene klas vir alle vensters. 'n Eenvoudige oplossing, maar vir 'n hackathon hoef jy nie met iets te abstruus vorendag te kom nie, net as jy tyd het. Ons het nog baie werk om te doen, so kom ons gaan aan – ons sal met stemherkenning te doen kry. Dit is nodig om 'n soort databasis te maak waarin spektrogramme van vooraf opgeneemde stemme gestoor sal word. Aangesien daar min tyd oor is, sal ons hierdie probleem so goed moontlik oplos.

    Ons skep naamlik 'n skrip om 'n stemuittreksel op te neem (dit werk op dieselfde manier as hierbo beskryf, slegs wanneer dit van die sleutelbord af onderbreek word, sal dit die stem in 'n lêer stoor).

    Kom ons probeer:

    python3 voice_db/record_voice.py test.wav

    Ons neem die stemme van verskeie mense op (in ons geval, drie spanlede)
    Volgende, vir elke opgeneemde stem voer ons 'n vinnige fourier-transformasie uit, verkry 'n spektrogram en stoor dit as 'n numpy-skikking (.npy):

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

    Meer besonderhede in die lêer create_base.py
    As gevolg hiervan, wanneer ons die hoofskrif uitvoer, sal ons heel aan die begin inbeddings van hierdie spektrogramme kry:

    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)

    Nadat ons die inbedding van die klanksegment ontvang het, sal ons kan bepaal aan wie dit behoort deur die kosinusafstand van die gang na al die stemme in die databasis te neem (hoe kleiner, hoe meer waarskynlik) - vir die demo stel ons die drempel tot 0.3):

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

    Op die ou end wil ek daarop let dat die afleidingspoed vinnig was en dit moontlik gemaak het om nog 1-2 modelle by te voeg (vir 'n monster van 7 sekondes lank het dit 2.5 geneem vir afleiding). Ons het nie meer tyd gehad om nuwe modelle by te voeg nie en het daarop gefokus om 'n prototipe van die webtoepassing te skryf.

    Web Aansoek

    'n Belangrike punt: ons neem 'n router van die huis af saam en stel ons plaaslike netwerk op, dit help om die toestel en skootrekenaars oor die netwerk te koppel.

    Die agterkant is 'n end-tot-end boodskapkanaal tussen die voorkant en Raspberry Pi, gebaseer op websocket-tegnologie (http over tcp-protokol).

    Die eerste fase is om verwerkte inligting van framboos te ontvang, dit wil sê voorspellers verpak in json, wat halfpad deur hul reis in die databasis gestoor word sodat statistieke oor die gebruiker se emosionele agtergrond vir die tydperk gegenereer kan word. Hierdie pakkie word dan na die frontend gestuur, wat intekening gebruik en pakkies vanaf die websocket-eindpunt ontvang. Die hele backend-meganisme is in die golang-taal gebou; dit is gekies omdat dit goed geskik is vir asinchroniese take, wat goroutines goed hanteer.
    By toegang tot die eindpunt word die gebruiker geregistreer en in die struktuur ingevoer, dan word sy boodskap ontvang. Beide die gebruiker en die boodskap word in 'n gemeenskaplike spilpunt ingevoer, vanwaar boodskappe reeds verder gestuur word (na die ingetekende front), en as die gebruiker die verbinding (framboos of voorkant) sluit, dan word sy intekening gekanselleer en hy word verwyder van die spilpunt.

    OpenVINO hackathon: herken stem en emosies op Raspberry Pi
    Ons wag vir 'n verbinding van agter af

    Front-end is 'n webtoepassing wat in JavaScript geskryf is deur die React-biblioteek te gebruik om die ontwikkelingsproses te bespoedig en te vereenvoudig. Die doel van hierdie toepassing is om data te visualiseer wat verkry is met algoritmes wat aan die agterkant en direk op die Raspberry Pi loop. Die bladsy het deursnee-roetering geïmplementeer met behulp van react-router, maar die hoofbladsy van belang is die hoofbladsy, waar 'n deurlopende stroom data intyds vanaf die bediener ontvang word deur WebSocket-tegnologie te gebruik. Raspberry Pi bespeur 'n stem, bepaal of dit aan 'n spesifieke persoon behoort vanaf die geregistreerde databasis, en stuur 'n waarskynlikheidslys aan die kliënt. Die kliënt vertoon die nuutste relevante data, vertoon die avatar van die persoon wat heel waarskynlik in die mikrofoon gepraat het, asook die emosie waarmee hy die woorde uitspreek.

    OpenVINO hackathon: herken stem en emosies op Raspberry Pi
    Tuisblad met opgedateerde voorspellings

    Gevolgtrekking

    Dit was nie moontlik om alles te voltooi soos beplan nie, ons het eenvoudig nie tyd gehad nie, so die hoofhoop was in die demo, dat alles sou werk. In die aanbieding het hulle gepraat oor hoe alles werk, watter modelle hulle geneem het, watter probleme hulle ondervind het. Volgende was die demo-deel - kundiges het in willekeurige volgorde deur die kamer geloop en elke span genader om na die werkende prototipe te kyk. Hulle het ons ook vrae gevra, almal het hul deel geantwoord, hulle het die web op die skootrekenaar gelos, en alles het regtig gewerk soos verwag.

    Laat ek daarop let dat die totale koste van ons oplossing $150 was:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (jy kan 'n respeakerfooi neem) ~ 15$
    • Intel NCS 2 ~ 100$

    Hoe om te verbeter:

    • Gebruik registrasie van die kliënt - vra om die teks wat lukraak gegenereer word te lees
    • Voeg nog 'n paar modelle by: jy kan geslag en ouderdom deur stem bepaal
    • Skei stemme wat gelyktydig klink (diarisering)

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

    OpenVINO hackathon: herken stem en emosies op Raspberry Pi
    Moeg maar gelukkig is ons

    Ten slotte wil ek baie dankie sê aan die organiseerders en deelnemers. Onder die projekte van ander spanne het ons persoonlik gehou van die oplossing vir die monitering van gratis parkeerplekke. Vir ons was dit 'n baie cool ervaring van onderdompeling in die produk en ontwikkeling. Ek hoop dat meer en meer interessante geleenthede in die streke gehou sal word, insluitend oor KI-onderwerpe.

Bron: will.com

Voeg 'n opmerking