OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi

30. novembris - 1. decembris Ņižņijnovgorodā notika OpenVINO hakatons. Dalībniekiem tika lūgts izveidot produkta risinājuma prototipu, izmantojot Intel OpenVINO rīku komplektu. Organizatori piedāvāja sarakstu ar aptuvenām tēmām, pēc kurām varētu vadīties, izvēloties uzdevumu, taču gala lēmums palika komandām. Turklāt tika mudināts izmantot modeļus, kas nav iekļauti izstrādājumā.

OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi

Šajā rakstā mēs jums pastāstīsim par to, kā mēs izveidojām mūsu produkta prototipu, ar kuru mēs galu galā ieņēmām pirmo vietu.

Hakatonā piedalÄ«jās vairāk nekā 10 komandas. PatÄ«kami, ka daži no viņiem nākuÅ”i no citiem novadiem. Hakatona norises vieta bija komplekss ā€œKremlsky on Pochainā€, kurā iekŔā svÄ«tā bija izkārtas senās Ņižņijnovgorodas fotogrāfijas! (Atgādinu, ka Å”obrÄ«d Intel centrālais birojs atrodas Ņižņijnovgorodā). DalÄ«bniekiem tika dotas 26 stundas koda rakstÄ«Å”anai, un beigās viņiem bija jāprezentē savs risinājums. AtseviŔķa priekÅ”rocÄ«ba bija demo sesijas klātbÅ«tne, lai pārliecinātos, ka viss iecerētais ir reāli Ä«stenots un nepaliek idejas prezentācijā. Preces, uzkodas, ēdiens, viss arÄ« bija!

Turklāt Intel pēc izvēles nodroÅ”ināja kameras, Raspberry PI, Neural Compute Stick 2.

Uzdevuma atlase

Viena no grūtākajām daļām, gatavojoties brīvas formas hakatonam, ir izaicinājuma izvēle. Mēs nekavējoties nolēmām nākt klajā ar kaut ko tādu, kas vēl nebija produktā, jo paziņojumā bija teikts, ka tas ir ļoti apsveicami.

Izanalizējot modeļi, kas ir iekļauti produktā paÅ”reizējā laidienā, nonākam pie secinājuma, ka lielākā daļa no tiem atrisina dažādas datorredzes problēmas. Turklāt ir ļoti grÅ«ti nākt klajā ar problēmu datorredzes jomā, kuru nevar atrisināt, izmantojot OpenVINO, un pat ja to var izgudrot, ir grÅ«ti atrast iepriekÅ” apmācÄ«tus modeļus publiskajā telpā. Mēs nolemjam rakt citā virzienā - runas apstrādes un analÄ«tikas virzienā. ApskatÄ«sim interesantu uzdevumu atpazÄ«t emocijas no runas. Jāteic, ka OpenVINO jau ir modelis, kas nosaka cilvēka emocijas pēc sejas, taču:

  • Teorētiski ir iespējams izveidot kombinētu algoritmu, kas darbosies gan ar skaņu, gan attēlu, kam vajadzētu dot precizitātes pieaugumu.
  • Kamerām parasti ir Å”aurs skata leņķis; ir nepiecieÅ”ama vairāk nekā viena kamera, lai aptvertu lielu laukumu; skaņai nav Ŕādu ierobežojumu.

Attīstīsim ideju: par pamatu ņemsim ideju par mazumtirdzniecības segmentu. Jūs varat izmērīt klientu apmierinātību pie veikala kasēm. Ja kāds no klientiem ir neapmierināts ar pakalpojumu un sāk paaugstināt tonusu, nekavējoties var saukt pēc palīdzības administratoram.
Å ajā gadÄ«jumā mums ir jāpievieno cilvēka balss atpazÄ«Å”ana, kas ļaus mums atŔķirt veikala darbiniekus no klientiem un nodroÅ”ināt analÄ«zi katram indivÄ«dam. Nu, turklāt bÅ«s iespējams analizēt paÅ”u veikala darbinieku uzvedÄ«bu, novērtēt atmosfēru kolektÄ«vā, izklausās labi!

Mēs formulējam prasības savam risinājumam:

  • MērÄ·a ierÄ«ces mazs izmērs
  • DarbÄ«ba reāllaikā
  • Zema cena
  • Viegla mērogojamÄ«ba

Rezultātā kā mērÄ·a ierÄ«ci mēs izvēlamies Raspberry Pi 3 c Intel NCS 2.

Å eit ir svarÄ«gi atzÄ«mēt vienu svarÄ«gu NCS iezÄ«mi - tā vislabāk darbojas ar standarta CNN arhitektÅ«rām, taču, ja jums ir nepiecieÅ”ams palaist modeli ar pielāgotiem slāņiem, sagaidiet zema lÄ«meņa optimizāciju.

Ir tikai viena maza lieta, kas jādara: jums ir jāiegādājas mikrofons. Parasts USB mikrofons derēs, taču tas neizskatÄ«sies labi kopā ar RPI. Bet pat Å”eit risinājums burtiski ā€œatrodas blakusā€. Lai ierakstÄ«tu balsi, mēs nolemjam izmantot komplektā iekļauto Voice Bonnet paneli Google AIY balss komplekts, uz kura ir vadu stereo mikrofons.

Lejupielādējiet Raspbian no AIY projektu krātuve un augÅ”upielādējiet to zibatmiņas diskā, pārbaudiet, vai mikrofons darbojas, izmantojot Ŕādu komandu (tas ierakstÄ«s audio 5 sekundes un saglabās to failā):

arecord -d 5 -r 16000 test.wav

Uzreiz jāatzÄ«mē, ka mikrofons ir ļoti jutÄ«gs un labi uztver troksni. Lai to labotu, dodieties uz alsamixer, atlasiet UztverÅ”anas ierÄ«ces un samaziniet ievades signāla lÄ«meni lÄ«dz 50-60%.

OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi
Pārveidojam korpusu ar vīli un viss der, var pat aizvērt ar vāku

Indikatora pogas pievienoŔana

Izjaucot AIY Voice Kit, atceramies, ka ir RGB poga, kuras fona apgaismojumu var vadÄ«t ar programmatÅ«ru. Mēs meklējam ā€œGoogle AIY Ledā€ un atrodam dokumentāciju: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Kāpēc neizmantot Å”o pogu, lai parādÄ«tu atpazÄ«to emociju, mums ir tikai 7 klases, un pogai ir 8 krāsas, tikai pietiekami!

Mēs savienojam pogu caur GPIO ar Voice Bonnet, ielādējam nepiecieÅ”amās bibliotēkas (tās jau ir instalētas izplatÄ«Å”anas komplektā no AIY projektiem)

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

Izveidosim diktu, kurā katrai emocijai bÅ«s atbilstoÅ”a krāsa RGB Tuple formā un aiy.leds.Leds klases objekts, caur kuru mēs atjaunināsim krāsu:

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

Un visbeidzot, pēc katras jaunas emociju prognozes, mēs atjaunināsim pogas krāsu atbilstoÅ”i tai (pēc atslēgas).

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

OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi
Poga, sadedzini!

Darbs ar balsi

Mēs izmantosim pyaudio, lai tvertu straumi no mikrofona, un webrtcvad, lai filtrētu troksni un noteiktu balsi. Turklāt mēs izveidosim rindu, kurai asinhroni pievienosim un noņemsim balss fragmentus.

Tā kā Webrtcvad ir ierobežots piegādātā fragmenta izmērs - tam jābÅ«t vienādam ar 10/20/30 ms, un emociju atpazÄ«Å”anas modeļa apmācÄ«ba (kā mēs uzzināsim vēlāk) tika veikta uz 48kHz datu kopas, mēs tveriet gabalus ar izmēru 48000 Ɨ 20 ms/1000 Ɨ 1 (mono) = 960 baiti. Webrtcvad atgriezÄ«s True/False katram no Å”iem gabaliem, kas atbilst balsojuma esamÄ«bai vai neesamÄ«bai daļā.

Ieviesīsim Ŕādu loģiku:

  • Mēs pievienosim sarakstam tos gabalus, kur ir balsojums, ja balsoÅ”anas nav, tad palielināsim tukÅ”o gabalu skaitÄ«tāju.
  • Ja tukÅ”o gabalu skaitÄ«tājs ir >=30 (600 ms), tad skatāmies uzkrāto gabalu saraksta lielumu; ja tas ir >250, tad pievienojam rindai; ja nē, uzskatām, ka garums ar ierakstu nepietiek, lai to ievadÄ«tu modelim, lai identificētu runātāju.
  • Ja tukÅ”o gabalu skaitÄ«tājs joprojām ir < 30 un uzkrāto gabalu saraksta lielums pārsniedz 300, fragmentu pievienosim rindai precÄ«zākai prognozei. (jo emocijas laika gaitā mēdz mainÄ«ties)

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

Ir pienācis laiks meklēt iepriekÅ” apmācÄ«tus modeļus publiskajā domēnā, dodieties uz github, Google, taču atcerieties, ka mums ir ierobežojumi attiecÄ«bā uz izmantoto arhitektÅ«ru. Å Ä« ir diezgan sarežģīta daļa, jo jums ir jāpārbauda modeļi uz jÅ«su ievades datiem un papildus jāpārveido tie OpenVINO iekŔējā formātā - IR (Intermediate Representation). Izmēģinājām kādus 5-7 dažādus risinājumus no github, un, ja emociju atpazÄ«Å”anas modelis nostrādāja uzreiz, tad ar balss atpazÄ«Å”anu bija jāgaida ilgāk - viņi izmanto sarežģītāku arhitektÅ«ru.

Mēs koncentrējamies uz sekojoÅ”o:

Tālāk mēs runāsim par modeļu konvertÄ“Å”anu, sākot ar teoriju. OpenVINO ietver vairākus moduļus:

  • Atveriet Modeļu zoodārzu, kuru modeļus varētu izmantot un iekļaut jÅ«su produktā
  • Model Optimzer, pateicoties kuram jÅ«s varat pārveidot modeli no dažādiem ietvara formātiem (Tensorflow, ONNX utt.) Intermediate Representation formātā, ar kuru mēs strādāsim tālāk
  • Inference Engine ļauj palaist modeļus IR formātā Intel procesoros, Myriad mikroshēmās un Neural Compute Stick paātrinātājos
  • VisefektÄ«vākā OpenCV versija (ar Inference Engine atbalstu)
    Katrs modelis IR formātā ir aprakstīts ar diviem failiem: .xml un .bin.
    Modeļi tiek pārveidoti IR formātā, izmantojot modeļa optimizētāju:

    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 ļauj izvēlēties datu formātu, ar kādu modelis darbosies. Tiek atbalstīti FP32, FP16, INT8. Optimālā datu veida izvēle var dot labu veiktspējas palielinājumu.
    --input_shape norāda ievades datu izmēru. Å Ä·iet, ka C++ API ir iespēja to dinamiski mainÄ«t, taču mēs tik tālu nemeklējām un vienkārÅ”i labojām to vienam no modeļiem.
    Tālāk mēģināsim ielādēt jau pārveidoto modeli IR formātā caur DNN moduli OpenCV un pārsūtīt uz to.

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

    Pēdējā rindiņa Å”ajā gadÄ«jumā ļauj novirzÄ«t aprēķinus uz Neural Compute Stick, pamata aprēķini tiek veikti procesoram, bet Raspberry Pi gadÄ«jumā tas nedarbosies, jums bÅ«s nepiecieÅ”ams stick.

    Tālāk loÄ£ika ir Ŕāda: mēs sadalām savu audio noteikta izmēra logos (mums tas ir 0.4 s), mēs pārvērÅ”am katru no Å”iem logiem par MFCC, ko pēc tam ievadām režģī:

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

    Tālāk ņemsim visizplatÄ«tāko klasi visiem logiem. VienkārÅ”s risinājums, taču hakatonam nav jāizdomā kaut kas pārāk abstrakts, tikai tad, ja ir laiks. Mums vēl daudz jāstrādā, tāpēc ejam tālāk ā€“ tiksim galā ar balss atpazÄ«Å”anu. Vajag izveidot kaut kādu datu bāzi, kurā glabātos iepriekÅ” ierakstÄ«to balsu spektrogrammas. Tā kā laika ir palicis maz, mēs Å”o problēmu atrisināsim, cik vien spēsim.

    Proti, veidojam skriptu balss fragmenta ierakstīŔanai (tas darbojas tāpat kā iepriekŔ aprakstīts, tikai pārtraucot no tastatūras saglabās balsi failā).

    Pamēģināsim:

    python3 voice_db/record_voice.py test.wav

    Mēs ierakstām vairāku cilvēku (mūsu gadījumā trīs komandas locekļu) balsis
    Pēc tam katrai ierakstītajai balsij veicam ātro Furjē transformāciju, iegūstam spektrogrammu un saglabājam to kā numpy masīvu (.npy):

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

    Sīkāka informācija failā create_base.py
    Rezultātā, palaižot galveno skriptu, mēs paŔā sākumā iegÅ«sim iegulÅ”anu no Ŕīm spektrogrammām:

    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ēc iegulÅ”anas saņemÅ”anas no apskaņotā segmenta, mēs varēsim noteikt, kam tas pieder, ņemot kosinusa attālumu no fragmenta lÄ«dz visām balsÄ«m datubāzē (jo mazāka, jo lielāka iespēja) - demonstrācijai mēs uzstādām slieksni lÄ«dz 0.3):

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

    Nobeigumā vēlos atzÄ«mēt, ka secinājuma ātrums bija ātrs un ļāva pievienot vēl 1-2 modeļus (7 sekunžu garam paraugam bija nepiecieÅ”ami 2.5 secinājumi). Mums vairs nebija laika pievienot jaunus modeļus un koncentrējāmies uz tÄ«mekļa lietojumprogrammas prototipa rakstÄ«Å”anu.

    Tīmekļa lietojumprogramma

    SvarÄ«gs punkts: mēs paņemam lÄ«dzi marÅ”rutētāju no mājām un izveidojam savu lokālo tÄ«klu, tas palÄ«dz savienot ierÄ«ci un klēpjdatorus tÄ«klā.

    Aizmugursistēma ir tieÅ”s ziņojumu kanāls starp priekÅ”pusi un Raspberry Pi, kura pamatā ir tÄ«mekļa ligzdas tehnoloÄ£ija (http, izmantojot tcp protokolu).

    Pirmais posms ir apstrādātas informācijas saņemÅ”ana no Raspberry, tas ir, json iesaiņoti prognozētāji, kas tiek saglabāti datu bāzē ceļojuma pusceļā, lai varētu Ä£enerēt statistiku par lietotāja emocionālo fonu attiecÄ«gajā periodā. Pēc tam Ŕī pakete tiek nosÅ«tÄ«ta uz priekÅ”galu, kas izmanto abonementu un saņem paketes no tÄ«mekļa ligzdas galapunkta. Viss aizmugursistēmas mehānisms ir veidots golang valodā; tas tika izvēlēts, jo tas ir labi piemērots asinhroniem uzdevumiem, ar kuriem goroutines labi tiek galā.
    Piekļūstot galapunktam, lietotājs tiek reÄ£istrēts un ievadÄ«ts struktÅ«rā, pēc tam tiek saņemts viņa ziņojums. Gan lietotājs, gan ziņojums tiek ievadÄ«ti kopējā centrmezglā, no kura ziņojumi jau tiek sÅ«tÄ«ti tālāk (uz abonēto fronti), un, ja lietotājs noslēdz savienojumu (aveņu vai priekÅ”puse), tad viņa abonements tiek atcelts un viņŔ tiek noņemts no centrs.

    OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi
    Mēs gaidām savienojumu no aizmugures

    PriekÅ”gals ir tÄ«mekļa lietojumprogramma, kas rakstÄ«ta JavaScript valodā, izmantojot React bibliotēku, lai paātrinātu un vienkārÅ”otu izstrādes procesu. Å Ä«s lietojumprogrammas mērÄ·is ir vizualizēt datus, kas iegÅ«ti, izmantojot algoritmus, kas darbojas aizmugures pusē un tieÅ”i Raspberry Pi. Lapā ir ieviesta sekciju marÅ”rutÄ“Å”ana, izmantojot react-router, bet galvenā interesējoŔā lapa ir galvenā lapa, kurā no servera tiek saņemta nepārtraukta datu plÅ«sma reāllaikā, izmantojot WebSocket tehnoloÄ£iju. Raspberry Pi nosaka balsi, no reÄ£istrētās datu bāzes nosaka, vai tā pieder konkrētai personai, un nosÅ«ta klientam varbÅ«tÄ«bu sarakstu. Klients parāda jaunākos attiecÄ«gos datus, parāda tās personas iemiesojumu, kura, visticamāk, runāja mikrofonā, kā arÄ« emocijas, ar kurām viņŔ izrunā vārdus.

    OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi
    Mājas lapa ar atjauninātām prognozēm

    Secinājums

    Nebija iespējams visu paveikt, kā plānots, mums vienkārÅ”i nebija laika, tāpēc galvenā cerÄ«ba bija demonstrācijā, ka viss izdosies. Prezentācijā viņi runāja par to, kā viss darbojas, kādus modeļus paņēma, ar kādām problēmām saskārās. Tālāk sekoja demonstrācijas daļa ā€“ eksperti nejauŔā secÄ«bā staigāja pa telpu un piegāja pie katras komandas, lai apskatÄ«tu strādājoÅ”o prototipu. Viņi arÄ« uzdeva mums jautājumus, katrs atbildēja uz savu daļu, viņi atstāja tÄ«mekli klēpjdatorā, un viss patieŔām darbojās, kā paredzēts.

    Ļaujiet man atzīmēt, ka mūsu risinājuma kopējās izmaksas bija USD 150:

    • Raspberry Pi 3 ~ 35 USD
    • Google AIY Voice Bonnet (varat iekasēt maksu par runātāju) ~ 15 USD
    • Intel NCS 2 ~ 100 USD

    Kā uzlabot:

    • Izmantojiet klienta reÄ£istrāciju - lÅ«dziet izlasÄ«t tekstu, kas tiek Ä£enerēts nejauÅ”i
    • Pievienojiet vēl dažus modeļus: pēc balss varat noteikt dzimumu un vecumu
    • AtseviŔķas vienlaicÄ«gi skanoÅ”as balsis (diarizācija)

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

    OpenVINO hakatons: balss un emociju atpazÄ«Å”ana vietnē Raspberry Pi
    Mēs esam noguruÅ”i, bet laimÄ«gi

    Nobeigumā vēlos pateikt paldies organizatoriem un dalÄ«bniekiem. No citu komandu projektiem mums personÄ«gi patika bezmaksas stāvvietu uzraudzÄ«bas risinājums. Mums tā bija ļoti forÅ”a pieredze, iedziļinoties produktā un attÄ«stÄ«bā. Ceru, ka reÄ£ionos notiks arvien vairāk interesantu pasākumu, arÄ« par AI tēmām.

Avots: www.habr.com

Pievieno komentāru