30 nĂ«ntor â 1 dhjetor nĂ« Nizhni Novgorod u mbajt. PjesĂ«marrĂ«sve iu propozuar tĂ« krijonin njĂ« prototip tĂ« zgjidhjes produktore duke pĂ«rdorur kit-in e Intel OpenVINO. OrganizatorĂ«t ofruan njĂ« listĂ« temash orientuese, mbi tĂ« cilat mund tĂ« bazoheshin pĂ«r tĂ« zgjedhur problemin, por zgjidhja pĂ«rfundimtare mbetej nĂ« duar tĂ« grupeve. PĂ«rveç kĂ«saj, u inkurajua pĂ«rdorimi i modeleve qĂ« nuk pĂ«rfshihen nĂ« produkt.

Në këtë artikull do të flasim për atë se si krijuam prototipin tonë të produktit, me të cilin përfundimisht fituam vendin e parë.
NĂ« hakaton morĂ«n pjesĂ« mĂ« shumĂ« se 10 grupe. E kĂ«ndshme qĂ« disa prej tyre erdhĂ«n nga rajone tĂ« tjera. Vendi i mbajtjes sĂ« hakatonit ishte seç i veçantĂ« âKremlevskiy na Poçaineâ, ku brenda ishin tĂ« varura fotografi antike tĂ« Nizhni Novgorod-it, njĂ« ambient i veçantĂ«! (po e pĂ«rkujtoj se aktualisht zyra qendrore e kompanisĂ« Intel ndodhet pikĂ«risht nĂ« Nizhni Novgorod). PjesĂ«marrĂ«sit kishin 26 orĂ« pĂ«r tĂ« shkruar kodin, dhe nĂ« fund duhej tĂ« prezantonin zgjidhjen e tyre. NjĂ« pĂ«rfitim shtesĂ« ishte prezenca e njĂ« sesioni demo, pĂ«r tĂ« siguruar qĂ« gjithçka ishte realizuar ashtu siç ishte menduar, dhe nuk kishte mbetur thjesht ide nĂ« prezantim. Merchandise, snacks, ushqim, gjithçka ishte gjithashtu!
Përveç kësaj, kompania Intel sipas dëshirës ofronte kamera, Raspberry PI, Neural Compute Stick 2.
Zgjedhja e detyrës
Një nga pjesët më të vështira të përgatitjes për një hakaton me temë të lirë është zgjedhja e problemit. Menjëherë vendosëm të krijonim diçka që nuk ekziston ende në produkt, pasi në njoftim ishte thënë se kjo inkurajohet.
Pas analizĂ«s , qĂ« pĂ«rfshihen nĂ« produktin nĂ« kĂ«tĂ« version, arrijmĂ« nĂ« pĂ«rfundimin se shumica e tyre zgjidhin probleme tĂ« ndryshme tĂ« vizionit kompjuterik. NĂ« tĂ« vĂ«rtetĂ«, Ă«shtĂ« shumĂ« e vĂ«shtirĂ« tĂ« mendosh pĂ«r njĂ« problem nĂ« fushĂ«n e vizionit kompjuterik, qĂ« nuk mund tĂ« zgjidhet duke pĂ«rdorur OpenVINO, dhe nĂ«se diçka e tillĂ« mund tĂ« mendohesh, Ă«shtĂ« e vĂ«shtirĂ« tĂ« gjesh modele tĂ« paratuara nĂ« qasje tĂ« hapur. Vendosim tĂ« hedhim njĂ« vĂ«shtrim nĂ« njĂ« drejtim tjetĂ«r â drejt pĂ«rpunimit dhe analizĂ«s sĂ« zĂ«rit. Po shqyrtojmĂ« njĂ« problem interesant nĂ« njohjen e emocioneve pĂ«rmes zĂ«rit. Duhet thĂ«nĂ« se nĂ« OpenVINO ka njĂ« model qĂ« pĂ«rcakton emocionet e njeriut pĂ«rmes fytyrĂ«s, por:
- Teoria thotë se mund të krijohet një algoritëm i kombinuar, i cili do të funksionojë si me zë, ashtu edhe me imazh, gjë që duhet të sigurojë një rritje në saktësi.
- Kamerat zakonisht kanë një kënd të ngushtë shikimi, për të mbuluar një zonë më të madhe nevojitet më shumë se një kamerë; tingulli nuk ka një kufizim të tillë.
Po zhvillojmĂ« idenĂ«: tĂ« marrim si bazĂ« idenĂ« pĂ«r segmentin e shitjeve me pakicĂ«. Mund tĂ« pĂ«rcaktojmĂ« kĂ«naqĂ«sinĂ« e klientit nĂ« kasat e dyshaneve. NĂ«se ndonjĂ« nga blerĂ«sit Ă«shtĂ« i pakĂ«naqur me shĂ«rbimin dhe fillon tĂ« ngrejĂ« zĂ«rin â mund ta thĂ«rrasim menjĂ«herĂ« administratorin pĂ«r ndihmĂ«.
Në këtë rast, duhet të shtojmë njohjen e personave përmes zërit, kjo do të na lejojë të dallojmë punonjësit e dyqanit nga blerësit, të lëshojmë analitika për çdo individ. Po kështu, do të mund të analizojmë sjelljen e vetë punonjësve të dyqanit, të vlerësojmë atmosferën në grup, tingëllon mjaft mirë!
Formulojmë kërkesat për zgjidhjen tonë:
- Madhësi e vogël e pajisjes së synuar
- Funksionim në kohë reale
- Ămim i ulĂ«t
- Lehtësi për t'u shkallëzuar
Në përfundim, si pajisje e synuar zgjidhim Raspberry Pi 3 me .
KĂ«tu Ă«shtĂ« e rĂ«ndĂ«sishme tĂ« theksojmĂ« njĂ« karakteristikĂ« tĂ« rĂ«ndĂ«sishme tĂ« NCS â ai punon mĂ« sĂ« miri me arkitekturĂ« standarde CNN, nĂ«se do tĂ« nevojitet tĂ« ekzekutohet njĂ« model me shtresa tĂ« pĂ«rshtatshme, atĂ«herĂ« prisni ̶t̶a̶n̶c̶e̶v̶ ̶s̶ ̶b̶u̶b̶n̶o̶m̶ optimizim tĂ« ulĂ«t.
PunĂ«t e vogla: duhet tĂ« sigurojmĂ« njĂ« mikrofon. NjĂ« mikrofon USB i zakonshĂ«m do tĂ« ishte i mjaftueshĂ«m, megjithatĂ« nuk do tĂ« duket mirĂ« me RPI. Por edhe kĂ«tu zgjidhja Ă«shtĂ« dosido ânĂ« krahâ. PĂ«r regjistrimin e zĂ«rit vendosim tĂ« pĂ«rdorim platinĂ« Voice Bonnet nga seti , nĂ« tĂ« cilĂ«n ka njĂ« mikrofon stereo tĂ« impiantuar.
Shkarkojmë Raspbian nga dhe e shkarkojmë në një flash-drive, testojmë nëse mikrofonit i punon me komandën e mëposhtme (ajo do të regjistrojë audio me gjatësi 5 sekonda dhe do ta ruajë në një skedar):
arecord -d 5 -r 16000 test.wavTë theksoj se mikrofonin është shumë i ndjeshëm dhe kap mirë zhurmat. Për ta rregulluar këtë, do të hyjmë në alsamixer, do të zgjedhim pajisjet e kapjes dhe do ta ulim nivelin e zërit të hyrës në 50-60%.

E përmirësojmë trupin me një dosje dhe çdo gjë hyn, madje mund ta mbyllim me kapak
Shtojmë një buton-indikator
GjatĂ« çmontimit tĂ« AIY Voice Kit nĂ« pjesĂ«, kujtojmĂ« se aty ka njĂ« buton RGB, dritĂ«n e tĂ« cilit mund ta menaxhojmĂ« nĂ« mĂ«nyrĂ« programore. KĂ«rkojmĂ« âGoogle AIY Ledâ dhe gjejmĂ« dokumentacionin:
Pse të mos e përdorim këtë buton për të shfaqur emocionin e njohur, ne kemi vetëm 7 klasa, ndërsa butoni ka 8 ngjyrat, pikërisht mjafton!
Lidhim butonin përmes GPIO me Voice Bonnet, ngarko bibliotekat përkatëse (ato tashmë janë instaluar në distribucionin e AIY projects)
from aiy.leds import Leds, Color
from aiy.leds import RgbLedsTë krijojmë një dict, ku çdo emocion do të përfaqësohet nga një ngjyrë në formën e një RGB Tuple dhe një objekt të klasës aiy.leds.Leds, përmes të cilit do të përditësojmë ngjyrën:
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()
Dhe, përfundimisht, pas çdo parashikimi të ri të emocionit do të përditësojmë ngjyrën e butonit në përputhje me të (sipër çelësit).
leds.update(Leds.rgb_on(led_dict.get(classes[prediction])))
Buton, ndiz!
Punojmë me zë
Do të përdorim pyaudio për kapjen e fluxit nga mikrofonin dhe webrtcvad për filtrimin e zhurmës dhe për zbuluar zërin. Përveç kësaj, do të krijojmë një radhë, ku do të shtojmë dhe të marrim asinkronisht pjesë me zë.
Duke qenĂ« se webrtcvad ka njĂ« kufizim nĂ« madhĂ«sinĂ« e fragmentit tĂ« dĂ«rguar â ai duhet tĂ« jetĂ« 10/20/30ms, dhe trajnimi i modelit pĂ«r njohjen e emocioneve (si do ta mĂ«sojmĂ« mĂ« tej) Ă«shtĂ« kryer nĂ« njĂ« dataset prej 48kHz, do tĂ« kapim copa madhĂ«sie 48000Ă20ms/1000Ă1(mono)=960 byte. Webrtcvad do tĂ« kthejĂ« True/False pĂ«r secilĂ«n nga kĂ«to copa, qĂ« i korrespondon pranisĂ« ose mungesĂ«s sĂ« zĂ«rit nĂ« copĂ«.
Do të zbatojmë logjikën në vijim:
- Do të shtojmë në listë ato copa ku ka zë, nëse nuk ka zë, do të inkrementojmë numëruesin e copave të zbrazëta.
- Nëse numëruesi i copave të zbrazëta >=30 (600 ms), atëherë shikojmë madhësinë e listës së copave të akumuluara, nëse është >250, do ta shtojmë në radhë, nëse jo, e konsiderojmë se gjerësia e regjistrimit është e pafavorshme për ta dërguar modelit për identifikimin e folësit.
- Nëse numëruesi i copave të zbrazëta ende < 30, dhe madhësia e listës së copave të akumuluara kalon 300, atëherë do ta shtojmë fragmetin në radhë për një parashikim më të saktë. (sepse me kalimin e kohës emocionet kanë prirjen të ndryshojnë)
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 = []Ka ardhur koha për të kërkuar modele të paratashikuara në hapësirën e hapur, shkojmë në github, guglojmë, por kujtojmë se kemi një kufizim mbi arkitekturën e përdorur. Kjo është një pjesë mjaft e komplikuar, sepse duhen testuar modelet në të dhënat tona hyrëse, dhe për më tepër, konvertuar në formatin e brendshëm OpenVINO - IR (Përfaqësimi Ndërmjetësor). Ne provuam rreth 5-7 zgjidhje të ndryshme nga github, dhe nëse modeli për njohjen e emocioneve funksionoi menjëherë, me njohjen përmes zërit na duhet të punojmë më shumë - aty përdoren arkitektura më të ndërlikuara.
Shoqërohemi me të mëposhtmet:
- Emocionet nga zĂ«ri â
Ajo funksionon sipas parimit tĂ« mĂ«poshtĂ«m: audio ndahet nĂ« pjesĂ« tĂ« caktuara, pĂ«r çdo njĂ« nga kĂ«to pjesĂ« ne identifikojmĂ« dhe mĂ« pas i japim ato si hyrje nĂ« CNN - Njohja pĂ«rmes zĂ«rit â
Këtu në vend të MFCC punojmë me spektrogramin, pas FFT i japim sinjalin në CNN, ku në daljen e saj marrim një përfaqësim vektorik të zërit.
Më pas do flasim për konvertimin e modeleve, të fillojmë me teorinë. OpenVINO përfshin disa module:
- Open Model Zoo, modelet nga të cilat mund të përdoren dhe përfshihen në produktin tuaj
- Model Optimzer, falë të cilit mund të konvertojmë modelin nga formate të ndryshme të frameworkeve (Tensorflow, ONNX etj.) në formatin e Përfaqësimit Ndërmjetësor, me të cilin më pas do të punojmë
- Engine e Inferencës lejon ekzekutimin e modeleve në formatin IR në procesorët Intel, çipat Myriad dhe akceleratorët Neural Compute Stick
- Versioni më efektiv i OpenCV (me mbështetje për Engine e Inferencës)
Ădo model nĂ« formatin IR pĂ«rshkruhet nga dy skedarĂ«: .xml dhe .bin.
Modelet konvertohen në formatin IR përmes Model Optimizer në mënyrën e mëposhtme: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_typelejon të zgjidhni formatin e të dhënave me të cilin do të punojë modeli. Mbështeten FP32, FP16, INT8. Zgjedhja e tipit optimal të të dhënave mund të japë një rritje të performancës.
--input_shapetregon dimensionin e të dhënave hyrëse. Mundësia për ta ndryshuar dinamikisht duket se ekziston në API-në C++, por ne nuk shkuam kaq thellë dhe për një nga modelet thjesht e kemi ngritur atë.
Më pas do të përpiqemi të ngarkojmë modelin e konvertuar tashmë në formatin IR përmes modulit DNN në OpenCV dhe ta bëjmë forward për të.import cv2 as cv emotionsNet = cv.dnn.readNet('emotions_model.bin', 'emotions_model.xml') emotionsNet.setPreferableTarget(cv.dnn.DNN_TARGET_MYRIAD)Rreshti i fundit në këtë rast lejon të ridrejtoni llogaritjet në Neural Compute Stick, normalisht llogaritjet kryhen në procesor, por në rastin e Raspberry Pi kjo nuk do funksiononte, do të nevojitej një stick.
MĂ« pas logjika Ă«shtĂ« si vijon: do tĂ« ndajmĂ« audio-n tonĂ« nĂ« dritare me pĂ«rmasĂ« tĂ« caktuar (nĂ« rastin tonĂ« Ă«shtĂ« 0.4s), secilĂ«n nga kĂ«to dritare do ta transformojmĂ« nĂ« MFCC, tĂ« cilat mĂ« pas do tâi jepnim rrjetit:
emotionsNet.setInput(MFCC_from_window) result = emotionsNet.forward()Pas kësaj do të marrim klasën më të zakonshme për të gjitha dritaret. Një zgjidhje e thjeshtë, por për hackathon nuk nevojitet të shpikim diçka shumë të ndërlikuar, vetëm nëse ka kohë. Ne kemi ende shumë punë, prandaj vazhdojmë - merremi me njohjen e zërit. Duhet të bëjmë një bazë të dhënash, në të cilën do të ruhen spektrat e zërave të regjistruar më parë. Duke pasur parasysh që koha është e kufizuar, po e zgjidhim këtë çështje si mundemi.
Konkretisht, krijojmë një skript për regjistrimin e një fragmenti të zërit (punon ashtu siç është përshkruar më sipër, vetëm se kur ndërpritet me tastierë do të ruajë zërin në një skedar).
Provo:
python3 voice_db/record_voice.py test.wavRegjistrojmë zërat e disa njerëzve (në rastin tonë, tre anëtarë të ekipit)
Më pas për çdo zë të regjistruar kryejmë transformimin e shpejtë të Fourier, marrim spektrin dhe e ruajmë në formën e një array numpy (.npy):for file in glob.glob("voice_db/*.wav"): spec = get_fft_spectrum(file) np.save(file[:-4] + '.npy', spec)Më shumë në skedarin
create_base.py
NĂ« fund, kur ekzekutojmĂ« skriptin kryesor do tĂ« marrim nĂ« fillim embed-dimet nga kĂ«to spektra: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)Pas marrjes sĂ« embedimit nga segmenti i dĂ«gjuar, mund tĂ« pĂ«rcaktojmĂ« se kujt i pĂ«rket, duke marrĂ« distancĂ«n kosinike nga segmenti deri te tĂ« gjitha zĂ«rat nĂ« bazĂ« (sa mĂ« e vogĂ«l, aq mĂ« e mundshme) â pĂ«r demo ne vendosĂ«m pragun 0.3):
dist_list = cdist(emb, enroll_embs, metric="cosine") distances = pd.DataFrame(dist_list, columns = df.speaker)Në fund, do të theksoj se shpejtësia e inferencës ishte e shpejtë dhe lejonte shtimin e 1-2 modeleve të tjera (në një mostër me gjatësi 7 sekonda, inferenca merrte 2.5). Ne nuk kishim më kohë të shtonim modele të reja dhe u përqendruam në shkrimin e prototipit të aplikacionit web.
Aplikacioni web
Pika e rëndësishme: marrim me vete një router nga shtëpia dhe konfiguroni lokalitetin tonë, ndihmon për të lidhur pajisjen dhe laptopët në rrjet.
Backend-i përbën një kanal transmetimi mes frontend-it dhe Raspberry Pi, i bazuar në teknologjinë websocket (http mbi protokollin tcp).
Hapi i parë është marrja e informacionit të procesuar nga Raspberry, pra parashikimet e paketuar në json, që në mes të rrugës së tyre ruhen në bazën e të dhënave, për të formuar statistikë mbi gjendjen emocionale të përdoruesit për një periudhë. Më pas, kjo paketë dërgohet në frontend, i cili përdor abonimin dhe marrjen e pakove nga pika e fundit të websockets. E gjithë mekanika e backend-it është ndërtuar në gjuhën golang, zgjedhja e saj ishte për shkak se ajo i përshtatet mirë detyrave asinkrone, për të cilat goroutine janë shumë efikase.
Kur përdoruesi akseson pikën e fundit, ai regjistrohet dhe shtohet në strukturë, më pas ndodh marrja e mesazhit të tij. Si përdoruesi, ashtu edhe mesazhi, regjistrohen në hub-in e përbashkët, nga ku mesazhet dërgohen më tej (në frontend-in e abonuar), dhe nëse përdoruesi mbyll lidhjen (Raspberry ose frontend), atëherë abonimi i tij anullohet dhe ai është zhdukur nga hub-i.
Pritim lidhjen me backend-inFront-end përfaqëson një aplikacion web të shkruar në JavaScript me përdorimin e bibliotekës React për të përshpejtuar dhe thjeshtuar procesin e zhvillimit. Qëllimi i këtij aplikacioni është të vizualizojë të dhënat e marra me anë të algoritmeve të ekzekutuar në anën e back-end dhe drejtpërdrejt në Raspberry Pi. Në faqe ka routing për seksionet, realizuar me ndihmën e react-router, por interesi kryesor përfaqësohet nga faqja kryesore, ku në kohë reale arrin një rrjedhë të vazhdueshme të dhënash nga serveri përmes teknologjisë WebSocket. Raspberry Pi detekton zë, përcakton përkatësinë ndaj një personi të caktuar nga baza e të dhënave të regjistruara dhe dërgon një listë probability për klientin. Klienti shfaq të dhënat më të fundit, paraqet avatarin e personit që ka folur me probabilitetin më të lartë në mikrofon, si dhe emocionin me të cilin ai shpreh fjalët.

Faqja kryesore me parashikime që rifreskohenPërfundimi
Nuk arritĂ«m tĂ« pĂ«rfundojmĂ« gjithçka siç kishim planifikuar, thjesht nuk kemi pasur kohĂ«, kĂ«shtu qĂ« shpresa e vetme ishte te demo, qĂ« gjithçka do tĂ« funksiononte. NĂ« prezantim folĂ«m pĂ«r mĂ«nyrĂ«n se si funksionon gjithçka, cilat modele morĂ«m, me çfarĂ« probleme pĂ«rballĂ« u dhamĂ«. MĂ« pas ishte pjesa e demon â ekspertĂ«t shĂ«titĂ«n nĂ« sallĂ« nĂ« rend tĂ« rastĂ«sishĂ«m dhe iu afruan çdo ekipi pĂ«r tĂ« parĂ« prototipin nĂ« funksion. BĂ«nĂ« pyetje edhe ne, çdo njeri u pĂ«rgjigj nĂ« pjesĂ«n e tij, nĂ« laptop e linçuam web-in, dhe gjithçka me tĂ« vĂ«rtetĂ« funksionoi siç pritej.
Dua të theksoj se kostoja totale e zgjidhjes sonë është 150$:
- Raspberry Pi 3 ~ 35$
- Google AIY Voice Bonnet (mund të merrni një pllakë respeaker) ~ 15$
- Intel NCS 2 ~ 100$
Si të përmirësojmë:
- Përdorni regjistrimin nga klienti - kërkoni të lexoni tekstin që e gjenerojmë rastësisht
- Shtoni edhe disa modele të tjera: me zë mund të përcaktojmë gjininë dhe moshën
- Të ndahen zërat që dëgjohen njëkohësisht (diarizimi)
Repo:

Të lodhur, por të lumtur neNë përfundim, dëshiroj të falënderoj organizatorët dhe pjesëmarrësit. Nga projektet e ekipeve të tjera, na pëlqeu zgjidhja për monitorimin e vendeve të lira për parkim. Për ne, kjo ishte një përvojë jashtëzakonisht e shkëlqyer dhe depërtuese në produkt dhe zhvillim. Shpresoj që në rajone të organizohen gjithnjë e më shumë aktivitete interesante, përfshirë edhe ato të tematikës AI.
Burimi: habr.com



