OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi

30. nóvember - 1. desember í Nizhny Novgorod var haldinn OpenVINO hackathon. Þátttakendur voru beðnir um að búa til frumgerð af vörulausn með því að nota Intel OpenVINO verkfærakistuna. Skipuleggjendur lögðu til lista yfir áætluð efni sem hægt væri að hafa að leiðarljósi við val á verkefni, en endanleg ákvörðun var eftir hjá teymunum. Auk þess var hvatt til notkunar á gerðum sem ekki eru innifalin í vörunni.

OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi

Í þessari grein munum við segja þér frá því hvernig við bjuggum til frumgerð okkar af vörunni, sem við náðum að lokum fyrsta sæti með.

Meira en 10 lið tóku þátt í hakkaþoninu. Það er gaman að sumir þeirra komu frá öðrum svæðum. Vettvangurinn fyrir hackathonið var „Kremlinsky on Pochain“ flókið, þar sem fornar ljósmyndir af Nizhny Novgorod voru hengdar inni, í fylgdarliði! (Ég minni á að í augnablikinu er aðalskrifstofa Intel staðsett í Nizhny Novgorod). Þátttakendur fengu 26 klukkustundir til að skrifa kóða og í lokin áttu þeir að kynna lausn sína. Sérstakur kostur var tilvist kynningarlotu til að ganga úr skugga um að allt sem fyrirhugað var væri í raun útfært og væri ekki áfram hugmyndir í kynningunni. Varningur, snarl, matur, allt var líka!

Að auki útvegaði Intel valfrjálst myndavélar, Raspberry PI, Neural Compute Stick 2.

Verkefnaval

Einn af erfiðustu hlutunum við undirbúning fyrir frjálst form hackathon er að velja áskorun. Við ákváðum strax að koma með eitthvað sem var ekki enn í vörunni, þar sem í tilkynningunni stóð að þetta væri mjög kærkomið.

Búin að greina módel, sem eru innifalin í vörunni í núverandi útgáfu, komumst við að þeirri niðurstöðu að flestir þeirra leysi ýmis tölvusjónvandamál. Þar að auki er mjög erfitt að koma upp vandamáli á sviði tölvusjónar sem ekki er hægt að leysa með OpenVINO, og jafnvel þótt hægt sé að finna það upp, er erfitt að finna fyrirfram þjálfuð módel á almenningi. Við ákveðum að grafa í aðra átt - í átt að talvinnslu og greiningu. Við skulum íhuga áhugavert verkefni að þekkja tilfinningar frá tali. Það verður að segjast að OpenVINO er ​​nú þegar með líkan sem ákvarðar tilfinningar einstaklings út frá andliti þeirra, en:

  • Fræðilega séð er hægt að búa til samsett reiknirit sem virkar bæði á hljóð og mynd, sem ætti að gefa aukna nákvæmni.
  • Myndavélar hafa venjulega þröngt sjónarhorn; fleiri en eina myndavél þarf til að ná yfir stórt svæði; hljóð hefur ekki slíka takmörkun.

Þróum hugmyndina: tökum hugmyndina að smásöluhlutanum til grundvallar. Þú getur mælt ánægju viðskiptavina við afgreiðslur verslana. Ef einn viðskiptavinurinn er óánægður með þjónustuna og byrjar að hækka tóninn, geturðu strax hringt í stjórnanda til að fá aðstoð.
Í þessu tilviki þurfum við að bæta við mannlegri raddgreiningu, þetta gerir okkur kleift að greina verslunarstarfsmenn frá viðskiptavinum og veita greiningar fyrir hvern einstakling. Jæja, auk þess verður hægt að greina hegðun starfsmanna verslunarinnar sjálfra, meta andrúmsloftið í teyminu, hljómar vel!

Við mótum kröfurnar fyrir lausnina okkar:

  • Lítil stærð marktækisins
  • Rauntíma rekstur
  • Lágt verð
  • Auðvelt stigstærð

Fyrir vikið veljum við Raspberry Pi 3 c sem miða tækið Intel NCS 2.

Hér er mikilvægt að hafa í huga einn mikilvægan eiginleika NCS - hann virkar best með venjulegum CNN arkitektúr, en ef þú þarft að keyra líkan með sérsniðnum lögum á því, þá búist við hagræðingu á lágu stigi.

Það er bara eitt lítið sem þarf að gera: þú þarft að fá þér hljóðnema. Venjulegur USB hljóðnemi dugar, en hann mun ekki líta vel út ásamt RPI. En jafnvel hér er lausnin bókstaflega „í nálægð“. Til að taka upp rödd ákveðum við að nota Voice Bonnet borðið úr settinu Google AIY raddsett, sem er með snúru steríó hljóðnema.

Sækja Raspbian frá AIY verkefnageymsla og hladdu því upp á flash-drif, prófaðu að hljóðneminn virki með því að nota eftirfarandi skipun (hann tekur upp hljóð í 5 sekúndur og vistar það í skrá):

arecord -d 5 -r 16000 test.wav

Ég skal strax taka það fram að hljóðneminn er mjög næmur og tekur vel upp hávaða. Til að laga þetta skulum við fara í alsamixer, velja Capture devices og minnka inntaksmerkjastigið í 50-60%.

OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi
Við breytum líkamanum með skrá og allt passar, þú getur jafnvel lokað honum með loki

Bætir við vísirhnappi

Þegar við tökum AIY raddsettið í sundur munum við að það er RGB hnappur, sem hægt er að stjórna baklýsingu með hugbúnaði. Við leitum að „Google AIY Led“ og finnum skjöl: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Af hverju ekki að nota þennan hnapp til að sýna viðurkenndar tilfinningar, við höfum aðeins 7 flokka og hnappurinn hefur 8 liti, bara nóg!

Við tengjum hnappinn í gegnum GPIO við Voice Bonnet, hleðum nauðsynlegum bókasöfnum (þau eru þegar uppsett í dreifingarsettinu frá AIY verkefnum)

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

Við skulum búa til dict þar sem hver tilfinning mun hafa samsvarandi lit í formi RGB Tuple og hlut í bekknum aiy.leds.Leds, þar sem við munum uppfæra litinn:

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

Og að lokum, eftir hverja nýja spá um tilfinningu, munum við uppfæra lit hnappsins í samræmi við það (með lykli).

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

OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi
Hnappur, brenndu!

Að vinna með rödd

Við munum nota pyaudio til að fanga strauminn úr hljóðnemanum og webrtcvad til að sía hávaða og greina rödd. Að auki munum við búa til biðröð sem við munum ósamstillt bæta við og fjarlægja raddbrot.

Þar sem webrtcvad hefur takmörkun á stærð hlutans sem fylgir - það verður að vera jafnt og 10/20/30ms, og þjálfun líkansins til að þekkja tilfinningar (eins og við munum læra síðar) var framkvæmd á 48kHz gagnasafni, munum við handtaka klumpur af stærð 48000×20ms/1000×1(mónó)=960 bæti. Webrtcvad mun skila True/False fyrir hvern þessara klumpa, sem samsvarar tilvist eða fjarveru atkvæða í klumpnum.

Við skulum innleiða eftirfarandi rökfræði:

  • Við munum bæta við listann þeim klumpum þar sem kosið er; ef það er ekkert atkvæði munum við hækka teljarann ​​með tómum klumpum.
  • Ef teljari tómra bita er >=30 (600 ms), þá lítum við á stærð lista yfir uppsafnaða bita; ef hann er >250, þá bætum við honum við röðina; ef ekki, teljum við að lengdin plötunnar er ekki nóg til að fæða það til líkansins til að bera kennsl á hátalarann.
  • Ef teljari tómra bita er enn < 30 og stærð lista yfir uppsafnaða bita fer yfir 300, þá munum við bæta brotinu við röðina til að fá nákvæmari spá. (vegna þess að tilfinningar hafa tilhneigingu til að breytast með tímanum)

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

Það er kominn tími til að leita að forþjálfuðum módelum á almenningi, farðu á github, Google, en mundu að við höfum takmörkun á arkitektúrnum sem notuð er. Þetta er frekar erfiður hluti, vegna þess að þú verður að prófa líkanin á inntaksgögnunum þínum og að auki breyta þeim í innra snið OpenVINO - IR (Intermediate Representation). Við reyndum um 5-7 mismunandi lausnir frá github, og ef líkanið til að þekkja tilfinningar virkaði strax, þá þurftum við að bíða lengur með raddgreiningu - þeir nota flóknari arkitektúr.

Við leggjum áherslu á eftirfarandi:

Næst munum við tala um að umbreyta líkönum, byrjað á kenningum. OpenVINO inniheldur nokkrar einingar:

  • Open Model Zoo, módel sem hægt er að nota og fylgja með í vörunni þinni
  • Model Optimzer, þökk sé því sem þú getur umbreytt líkani úr ýmsum rammasniðum (Tensorflow, ONNX osfrv.) í Intermediate Representation sniðið, sem við munum vinna frekar með
  • Inference Engine gerir þér kleift að keyra gerðir á IR sniði á Intel örgjörvum, Myriad flísum og Neural Compute Stick hröðlum
  • Skilvirkasta útgáfan af OpenCV (með Inference Engine stuðningi)
    Hverri gerð á IR-sniði er lýst með tveimur skrám: .xml og .bin.
    Líkönum er breytt í IR snið með Model Optimizer sem hér segir:

    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 gerir þér kleift að velja gagnasniðið sem líkanið mun vinna með. FP32, FP16, INT8 eru studdir. Að velja ákjósanlegasta gagnategundina getur gefið góða frammistöðuuppörvun.
    --input_shape gefur til kynna stærð inntaksgagnanna. Getan til að breyta því á virkan hátt virðist vera til staðar í C++ API, en við pældum ekki svo langt og laguðum það einfaldlega fyrir eina af gerðunum.
    Næst skulum við reyna að hlaða þegar breyttu líkaninu á IR sniði í gegnum DNN eininguna í OpenCV og senda það til hennar.

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

    Síðasta línan í þessu tilfelli gerir þér kleift að beina útreikningum á Neural Compute Stick, grunnútreikningar eru gerðir á örgjörvanum, en í tilfelli Raspberry Pi mun þetta ekki virka, þú þarft staf.

    Næst er rökfræðin sem hér segir: við skiptum hljóðinu okkar í glugga af ákveðinni stærð (fyrir okkur er það 0.4 s), við umbreytum hverjum þessara glugga í MFCC, sem við sendum síðan á ristina:

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

    Næst skulum við taka algengasta flokkinn fyrir alla glugga. Einföld lausn, en fyrir hackathon þarftu ekki að koma með eitthvað of gróft, aðeins ef þú hefur tíma. Við eigum enn mikið verk fyrir höndum, svo við skulum halda áfram - við munum fást við raddgreiningu. Nauðsynlegt er að búa til einhvers konar gagnagrunn þar sem litróf af foruppteknum röddum yrðu geymd. Þar sem það er lítill tími eftir munum við leysa þetta mál eins og við getum.

    Við búum nefnilega til handrit til að taka upp raddbrot (það virkar á sama hátt og lýst er hér að ofan, aðeins þegar það er truflað frá lyklaborðinu mun það vista röddina í skrá).

    Reynum:

    python3 voice_db/record_voice.py test.wav

    Við tökum upp raddir nokkurra manna (í okkar tilviki, þrír liðsmenn)
    Næst, fyrir hverja hljóðritaða rödd, framkvæmum við hraðvirka fourier umbreytingu, fáum litróf og vistum það sem numpy array (.npy):

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

    Nánari upplýsingar í skránni create_base.py
    Þar af leiðandi, þegar við keyrum aðalhandritið, munum við fá innfellingar frá þessum litrófsritum strax í upphafi:

    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)

    Eftir að hafa fengið innfellinguna frá hljóðhlutanum, munum við geta ákvarðað hverjum hann tilheyrir með því að taka kósínusfjarlægð frá leiðinni til allra raddanna í gagnagrunninum (því minni, því líklegra) - fyrir kynninguna setjum við þröskuldinn til 0.3):

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

    Í lokin vil ég taka fram að ályktunarhraðinn var mikill og gerði það mögulegt að bæta við 1-2 gerðum í viðbót (fyrir sýnishorn sem var 7 sekúndur tók það 2.5 fyrir ályktun). Við höfðum ekki lengur tíma til að bæta við nýjum gerðum og lögðum áherslu á að skrifa frumgerð af vefforritinu.

    Vefforrit

    Mikilvægur punktur: við tökum með okkur bein að heiman og setjum upp staðarnetið okkar, það hjálpar til við að tengja tækið og fartölvur yfir netið.

    Bakendinn er enda-til-enda skilaboðarás milli framhliðarinnar og Raspberry Pi, byggt á vefsocket tækni (http over tcp protocol).

    Fyrsta stigið er að taka á móti unnum upplýsingum frá hindberjum, það er að segja spár sem eru pakkaðir í json, sem eru vistaðir í gagnagrunninum á miðri leið á leiðinni svo hægt sé að búa til tölfræði um tilfinningalegan bakgrunn notandans fyrir tímabilið. Þessi pakki er síðan sendur til framenda, sem notar áskrift og tekur á móti pakka frá endapunkti vefsocketsins. Allt bakendakerfið er byggt á golang tungumálinu; það var valið vegna þess að það hentar vel fyrir ósamstillt verkefni, sem goroutines höndla vel.
    Við aðgang að endapunkti er notandinn skráður og færður inn í skipulagið, þá berast skilaboð hans. Bæði notandinn og skilaboðin eru færð inn í sameiginlegan miðstöð, þaðan sem skilaboð eru þegar send lengra (til áskrifenda) og ef notandinn lokar tengingunni (hindberjum eða framan), þá er áskrift hans sagt upp og hann fjarlægður frá miðstöðin.

    OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi
    Við erum að bíða eftir tengingu að aftan

    Front-end er vefforrit skrifað í JavaScript með því að nota React bókasafnið til að flýta fyrir og einfalda þróunarferlið. Tilgangur þessa forrits er að sjá gögn sem fengin eru með reikniritum sem keyra á bakhliðinni og beint á Raspberry Pi. Síðan er með hlutaleiðingu útfærð með react-router, en aðalsíðan sem vekur áhuga er aðalsíðan, þar sem stöðugur straumur gagna er móttekinn í rauntíma frá þjóninum með WebSocket tækni. Raspberry Pi skynjar rödd, ákvarðar hvort hún tilheyri tilteknum einstaklingi úr skráða gagnagrunninum og sendir líkindalista til viðskiptavinarins. Viðskiptavinurinn sýnir nýjustu viðeigandi gögn, sýnir avatar þess sem líklega talaði í hljóðnemann, sem og tilfinninguna sem hann segir orðin með.

    OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi
    Heimasíða með uppfærðum spám

    Ályktun

    Það var ekki hægt að klára allt eins og áætlað var, við höfðum einfaldlega ekki tíma, svo helsta vonin var í demoinu, að allt myndi virka. Í kynningunni ræddu þau hvernig allt virkar, hvaða líkön þau tóku, hvaða vandamál þau lentu í. Næst var kynningarhlutinn - sérfræðingar gengu um herbergið í tilviljunarkenndri röð og nálguðust hvert lið til að skoða frumgerðina sem virkar. Þeir spurðu okkur spurninga líka, allir svöruðu sínu, þeir skildu vefinn eftir á fartölvunni og allt virkaði í raun eins og til var ætlast.

    Leyfðu mér að hafa í huga að heildarkostnaður við lausnina okkar var $150:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (þú getur tekið hátalaragjald) ~ 15$
    • Intel NCS 2 ~ 100 $

    Hvernig á að bæta:

    • Notaðu skráningu frá viðskiptavininum - biddu um að lesa textann sem er búinn til af handahófi
    • Bættu við nokkrum módelum í viðbót: þú getur ákvarðað kyn og aldur með rödd
    • Aðskilja raddir sem hljóma samtímis (dæling)

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

    OpenVINO hackathon: þekkja rödd og tilfinningar á Raspberry Pi
    Við erum þreytt en ánægð

    Að endingu vil ég þakka skipuleggjendum og þátttakendum. Meðal verkefna annarra teyma líkaði okkur persónulega vel við lausnina til að fylgjast með ókeypis bílastæðum. Fyrir okkur var þetta ofboðslega flott upplifun að dýfa okkur í vöruna og þróunina. Ég vona að fleiri og fleiri áhugaverðir viðburðir verði haldnir á svæðinu, þar á meðal um gervigreindarefni.

Heimild: www.habr.com

Bæta við athugasemd