OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi

Novemba 30 - Desemba 1 huko Nizhny Novgorod ilifanyika OpenVINO hackathon. Washiriki waliulizwa kuunda mfano wa suluhisho la bidhaa kwa kutumia zana ya zana ya Intel OpenVINO. Waandaaji walipendekeza orodha ya mada takriban ambayo inaweza kuongozwa na wakati wa kuchagua kazi, lakini uamuzi wa mwisho ulibaki na timu. Kwa kuongeza, matumizi ya mifano ambayo haijajumuishwa katika bidhaa ilihimizwa.

OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi

Katika makala hii tutakuambia juu ya jinsi tulivyounda mfano wetu wa bidhaa, ambayo hatimaye tulichukua nafasi ya kwanza.

Zaidi ya timu 10 zilishiriki katika hackathon. Ni vizuri kwamba baadhi yao walitoka mikoa mingine. Mahali pa hackathon hiyo ilikuwa tata ya "Kremlinsky on Pochain", ambapo picha za zamani za Nizhny Novgorod zilitundikwa ndani, kwa msafara! (Nakukumbusha kwamba kwa sasa ofisi kuu ya Intel iko katika Nizhny Novgorod). Washiriki walipewa masaa 26 kuandika kanuni, na mwisho walipaswa kuwasilisha suluhisho lao. Faida tofauti ilikuwa uwepo wa kikao cha demo ili kuhakikisha kuwa kila kitu kilichopangwa kinatekelezwa na hakibaki mawazo katika uwasilishaji. Bidhaa, vitafunio, chakula, kila kitu kilikuwa pale pia!

Kwa kuongezea, kamera za Intel kwa hiari, Raspberry PI, Fimbo ya Neural Compute 2.

Uchaguzi wa kazi

Mojawapo ya sehemu ngumu zaidi ya kujiandaa kwa hackathon isiyolipishwa ni kuchagua changamoto. Mara moja tuliamua kuja na kitu ambacho hakikuwa kwenye bidhaa, kwani tangazo lilisema kwamba hii inakaribishwa sana.

Baada ya kuchambua mifano, ambazo zimejumuishwa katika bidhaa katika kutolewa kwa sasa, tunafikia hitimisho kwamba wengi wao hutatua matatizo mbalimbali ya maono ya kompyuta. Kwa kuongezea, ni ngumu sana kupata shida katika uwanja wa maono ya kompyuta ambayo haiwezi kutatuliwa kwa kutumia OpenVINO, na hata ikiwa inaweza kuvumbuliwa, ni ngumu kupata mifano iliyofunzwa katika uwanja wa umma. Tunaamua kuchimba katika mwelekeo mwingine - kuelekea usindikaji wa hotuba na uchambuzi. Hebu fikiria kazi ya kuvutia ya kutambua hisia kutoka kwa hotuba. Inapaswa kuwa alisema kuwa OpenVINO tayari ina mfano ambao huamua hisia za mtu kulingana na uso wao, lakini:

  • Kwa nadharia, inawezekana kuunda algorithm ya pamoja ambayo itafanya kazi kwa sauti na picha, ambayo inapaswa kutoa ongezeko la usahihi.
  • Kamera kawaida huwa na pembe finyu ya kutazama; zaidi ya kamera moja inahitajika ili kufunika eneo kubwa; sauti haina kizuizi kama hicho.

Wacha tukuze wazo: wacha tuchukue wazo la sehemu ya rejareja kama msingi. Unaweza kupima kuridhika kwa mteja kwenye malipo ya duka. Ikiwa mmoja wa wateja hajaridhika na huduma na anaanza kuinua sauti zao, unaweza kupiga simu mara moja kwa msimamizi kwa usaidizi.
Katika kesi hii, tunahitaji kuongeza utambuzi wa sauti ya binadamu, hii itaturuhusu kutofautisha wafanyikazi wa duka kutoka kwa wateja na kutoa uchanganuzi kwa kila mtu. Naam, kwa kuongeza, itawezekana kuchambua tabia ya wafanyakazi wa duka wenyewe, kutathmini anga katika timu, inaonekana nzuri!

Tunaunda mahitaji ya suluhisho letu:

  • Ukubwa mdogo wa kifaa lengwa
  • Operesheni ya wakati halisi
  • Bei ya chini
  • Rahisi scalability

Kama matokeo, tunachagua Raspberry Pi 3 c kama kifaa kinacholengwa Intel NCS 2.

Hapa ni muhimu kutambua kipengele kimoja muhimu cha NCS - inafanya kazi vizuri na usanifu wa kawaida wa CNN, lakini ikiwa unahitaji kuendesha mfano na tabaka za kawaida juu yake, basi unatarajia uboreshaji wa kiwango cha chini.

Kuna jambo moja dogo tu la kufanya: unahitaji kupata kipaza sauti. Maikrofoni ya kawaida ya USB itafanya, lakini haitaonekana vizuri pamoja na RPI. Lakini hata hapa suluhisho kihalisi "iko karibu." Ili kurekodi sauti, tunaamua kutumia ubao wa Voice Bonnet kutoka kwa kit Google AIY Voice Kit, ambayo kuna maikrofoni ya stereo yenye waya.

Pakua Raspbian kutoka Hazina ya miradi ya AIY na upakie kwenye kiendeshi cha flash, jaribu kuwa kipaza sauti inafanya kazi kwa kutumia amri ifuatayo (itarekodi sauti kwa sekunde 5 na kuihifadhi kwenye faili):

arecord -d 5 -r 16000 test.wav

Mara moja ninapaswa kutambua kwamba kipaza sauti ni nyeti sana na inachukua kelele vizuri. Ili kurekebisha hili, twende kwenye alsamixer, chagua Nasa vifaa na upunguze kiwango cha mawimbi ya ingizo hadi 50-60%.

OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi
Tunarekebisha mwili na faili na kila kitu kinafaa, unaweza hata kuifunga kwa kifuniko

Inaongeza kitufe cha kiashirio

Wakati wa kutenganisha Kifaa cha Sauti cha AIY, tunakumbuka kuwa kuna kitufe cha RGB, taa ya nyuma ambayo inaweza kudhibitiwa na programu. Tunatafuta "Google AIY Led" na kupata hati: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Kwa nini usitumie kifungo hiki ili kuonyesha hisia inayotambuliwa, tuna madarasa 7 tu, na kifungo kina rangi 8, za kutosha!

Tunaunganisha kitufe kupitia GPIO kwa Sauti Bonnet, pakia maktaba zinazohitajika (tayari zimewekwa kwenye kit cha usambazaji kutoka kwa miradi ya AIY)

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

Hebu tutengeneze amri ambayo kila hisia itakuwa na rangi inayolingana katika mfumo wa RGB Tuple na kitu cha darasa aiy.leds.Leds, ambayo tutasasisha rangi:

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

Na hatimaye, baada ya kila utabiri mpya wa hisia, tutasasisha rangi ya kifungo kwa mujibu wake (kwa ufunguo).

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

OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi
Kitufe, choma!

Kufanya kazi kwa sauti

Tutatumia pyaudio kunasa mtiririko kutoka kwa maikrofoni na webrtcvad ili kuchuja kelele na kutambua sauti. Kwa kuongeza, tutaunda foleni ambayo tutaongeza na kuondoa dondoo za sauti kwa usawa.

Kwa kuwa webrtcvad ina kizuizi juu ya saizi ya kipande kilichotolewa - lazima iwe sawa na 10/20/30ms, na mafunzo ya mfano wa kutambua hisia (kama tutakavyojifunza baadaye) yalifanywa kwenye hifadhidata ya 48kHz, tutafanya. nasa vipande vya ukubwa wa 48000x20ms/1000x1( mono)=960 baiti. Webrtcvad itarejesha Kweli/Uongo kwa kila moja ya sehemu hizi, ambayo inalingana na kuwepo au kutokuwepo kwa kura katika fungu.

Wacha tutekeleze mantiki ifuatayo:

  • Tutaongeza kwenye orodha sehemu hizo ambapo kuna kura; ikiwa hakuna kura, basi tutaongeza hesabu ya vipande tupu.
  • Ikiwa kihesabu cha chunks tupu ni>=30 (600 ms), basi tunaangalia saizi ya orodha ya vipande vilivyokusanywa; ikiwa ni>250, basi tunaiongeza kwenye foleni; ikiwa sivyo, tunazingatia kwamba urefu ya rekodi haitoshi kulisha kwa mfano kutambua mzungumzaji.
  • Ikiwa counter ya chunks tupu bado ni <30, na ukubwa wa orodha ya vipande vilivyokusanywa huzidi 300, basi tutaongeza kipande kwenye foleni kwa utabiri sahihi zaidi. (kwa sababu hisia huwa zinabadilika kwa wakati)

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

Ni wakati wa kutafuta mifano iliyofunzwa mapema kwenye kikoa cha umma, nenda kwa github, Google, lakini kumbuka kuwa tuna kizuizi kwenye usanifu unaotumiwa. Hii ni sehemu ngumu sana, kwa sababu lazima ujaribu miundo kwenye data yako ya ingizo, na kwa kuongeza, ibadilishe kuwa umbizo la ndani la OpenVINO - IR (Uwakilishi wa Kati). Tulijaribu kuhusu ufumbuzi tofauti wa 5-7 kutoka kwa github, na ikiwa mfano wa kutambua hisia ulifanya kazi mara moja, basi kwa kutambua sauti tulipaswa kusubiri kwa muda mrefu - wanatumia usanifu ngumu zaidi.

Tunazingatia yafuatayo:

Ifuatayo tutazungumza juu ya kubadilisha mifano, kuanzia na nadharia. OpenVINO inajumuisha moduli kadhaa:

  • Fungua Model Zoo, miundo ambayo inaweza kutumika na kujumuishwa kwenye bidhaa yako
  • Model Optimzer, shukrani ambayo unaweza kubadilisha kielelezo kutoka kwa fomati mbalimbali za mfumo (Tensorflow, ONNX n.k) hadi umbizo la Uwakilishi wa Kati, ambalo tutafanya kazi nalo zaidi.
  • Inference Engine hukuruhusu kuendesha vielelezo katika umbizo la IR kwenye vichakataji vya Intel, chipsi Myriad na vichapuzi vya Neural Compute Stick
  • Toleo linalofaa zaidi la OpenCV (na usaidizi wa Injini ya Uelekezaji)
    Kila muundo katika umbizo la IR unafafanuliwa na faili mbili: .xml na .bin.
    Miundo inabadilishwa kuwa umbizo la IR kupitia Model Optimizer kama ifuatavyo:

    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 hukuruhusu kuchagua muundo wa data ambao mtindo utafanya kazi. FP32, FP16, INT8 zinatumika. Kuchagua aina bora ya data kunaweza kuongeza utendaji mzuri.
    --input_shape inaonyesha ukubwa wa data ya ingizo. Uwezo wa kuibadilisha kwa nguvu inaonekana kuwa iko kwenye API ya C ++, lakini hatukuchimba mbali na kuiweka kwa moja ya mifano.
    Ifuatayo, hebu tujaribu kupakia kielelezo kilichobadilishwa tayari katika umbizo la IR kupitia moduli ya DNN kwenye OpenCV na kuisambaza.

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

    Mstari wa mwisho katika kesi hii inakuwezesha kuelekeza mahesabu kwa Fimbo ya Neural Compute, mahesabu ya msingi yanafanywa kwenye processor, lakini katika kesi ya Raspberry Pi hii haitafanya kazi, utahitaji fimbo.

    Ifuatayo, mantiki ni kama ifuatavyo: tunagawanya sauti yetu katika madirisha ya ukubwa fulani (kwetu ni 0.4 s), tunabadilisha kila moja ya madirisha haya kuwa MFCC, ambayo sisi hulisha kwenye gridi ya taifa:

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

    Ifuatayo, wacha tuchukue darasa la kawaida kwa windows zote. Suluhisho rahisi, lakini kwa hackathon huna haja ya kuja na kitu kisichoeleweka sana, tu ikiwa una muda. Bado tuna kazi nyingi ya kufanya, kwa hivyo wacha tuendelee - tutashughulikia utambuzi wa sauti. Ni muhimu kuunda aina fulani ya hifadhidata ambayo spectrograms za sauti zilizorekodiwa awali zingehifadhiwa. Kwa kuwa muda umesalia, tutasuluhisha suala hili kadri tuwezavyo.

    Yaani, tunaunda hati ya kurekodi sauti ya sauti (inafanya kazi kwa njia ile ile kama ilivyoelezwa hapo juu, tu inapoingiliwa kutoka kwenye kibodi itahifadhi sauti kwenye faili).

    Tujaribu:

    python3 voice_db/record_voice.py test.wav

    Tunarekodi sauti za watu kadhaa (kwa upande wetu, washiriki watatu wa timu)
    Ifuatayo, kwa kila sauti iliyorekodiwa tunafanya ugeuzaji wa haraka zaidi wa nne, kupata spectrogramu na kuihifadhi kama safu numpy (.npy):

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

    Maelezo zaidi katika faili create_base.py
    Kama matokeo, tunapoendesha hati kuu, tutapata upachikaji kutoka kwa spectrogramu hizi mwanzoni kabisa:

    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)

    Baada ya kupokea upachikaji kutoka kwa sehemu iliyosikika, tutaweza kuamua ni ya nani kwa kuchukua umbali wa cosine kutoka kwa kifungu hadi sauti zote kwenye hifadhidata (ndogo, uwezekano zaidi) - kwa onyesho tunaweka kizingiti. kwa 0.3):

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

    Mwishowe, ningependa kutambua kuwa kasi ya uelekezaji ilikuwa haraka na ilifanya iwezekane kuongeza mifano 1-2 zaidi (kwa sampuli ya sekunde 7 ilichukua 2.5 kwa inference). Hatukuwa na muda tena wa kuongeza miundo mipya na tulilenga kuandika mfano wa programu ya wavuti.

    Programu ya wavuti

    Jambo muhimu: tunachukua router na sisi kutoka nyumbani na kuanzisha mtandao wetu wa ndani, inasaidia kuunganisha kifaa na kompyuta za mkononi kwenye mtandao.

    Upande wa nyuma ni kituo cha ujumbe kutoka mwisho hadi mwisho kati ya sehemu ya mbele na Raspberry Pi, kulingana na teknolojia ya soketi ya wavuti (itifaki ya http juu ya tcp).

    Hatua ya kwanza ni kupokea maelezo yaliyochakatwa kutoka kwa raspberry, yaani, vitabiri vilivyopakiwa katika json, ambavyo huhifadhiwa katika hifadhidata katikati ya safari yao ili takwimu ziweze kuzalishwa kuhusu usuli wa kihisia wa mtumiaji kwa kipindi hicho. Kifurushi hiki kisha hutumwa kwa sehemu ya mbele, ambayo hutumia usajili na kupokea pakiti kutoka sehemu ya mwisho ya soketi ya wavuti. Utaratibu mzima wa mazingira ya nyuma umejengwa katika lugha ya golang; ulichaguliwa kwa sababu unafaa kwa kazi zisizolingana, ambazo goroutines hushughulikia vyema.
    Wakati wa kufikia mwisho, mtumiaji amesajiliwa na kuingia kwenye muundo, basi ujumbe wake unapokelewa. Mtumiaji na ujumbe wote huingizwa kwenye kitovu cha kawaida, ambacho ujumbe tayari umetumwa zaidi (kwa sehemu ya mbele iliyosajiliwa), na ikiwa mtumiaji atafunga muunganisho (raspberry au mbele), basi usajili wake umeghairiwa na kuondolewa kutoka. kitovu.

    OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi
    Tunasubiri muunganisho kutoka nyuma

    Front-end ni programu ya wavuti iliyoandikwa kwa JavaScript kwa kutumia maktaba ya React ili kuharakisha na kurahisisha mchakato wa ukuzaji. Madhumuni ya programu hii ni kuibua data iliyopatikana kwa kutumia algoriti zinazoendeshwa kwenye upande wa nyuma na moja kwa moja kwenye Raspberry Pi. Ukurasa una uelekezaji wa sehemu unaotekelezwa kwa kutumia kipanga njia, lakini ukurasa kuu wa kuvutia ni ukurasa kuu, ambapo mtiririko unaoendelea wa data hupokelewa kwa wakati halisi kutoka kwa seva kwa kutumia teknolojia ya WebSocket. Raspberry Pi hutambua sauti, huamua ikiwa ni ya mtu mahususi kutoka kwa hifadhidata iliyosajiliwa, na kutuma orodha ya uwezekano kwa mteja. Mteja anaonyesha data muhimu ya hivi karibuni, anaonyesha avatar ya mtu ambaye uwezekano mkubwa alizungumza kwenye kipaza sauti, pamoja na hisia ambayo anatamka maneno.

    OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi
    Ukurasa wa nyumbani wenye ubashiri uliosasishwa

    Hitimisho

    Haikuwezekana kukamilisha kila kitu kama ilivyopangwa, hatukuwa na wakati, kwa hivyo tumaini kuu lilikuwa kwenye onyesho, kwamba kila kitu kitafanya kazi. Katika uwasilishaji walizungumza juu ya jinsi kila kitu kinavyofanya kazi, ni mifano gani waliyochukua, shida gani walizokutana nazo. Ifuatayo ilikuwa sehemu ya onyesho - wataalam walizunguka chumba kwa mpangilio wa nasibu na wakakaribia kila timu ili kuangalia mfano wa kufanya kazi. Walituuliza maswali pia, kila mtu alijibu sehemu yake, waliacha wavuti kwenye kompyuta ndogo, na kila kitu kilifanya kazi kama ilivyotarajiwa.

    Acha nikumbuke kuwa gharama ya jumla ya suluhisho letu ilikuwa $150:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (unaweza kuchukua ada ya kipaza sauti) ~ 15$
    • Intel NCS 2 ~ 100$

    Jinsi ya kuboresha:

    • Tumia usajili kutoka kwa mteja - uliza kusoma maandishi ambayo yametolewa bila mpangilio
    • Ongeza mifano michache zaidi: unaweza kuamua jinsia na umri kwa sauti
    • Tenganisha sauti zinazosikika kwa wakati mmoja (diaarization)

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

    OpenVINO hackathon: kutambua sauti na hisia kwenye Raspberry Pi
    Tumechoka lakini tuna furaha

    Kwa kumalizia, ningependa kusema asante kwa waandaaji na washiriki. Miongoni mwa miradi ya timu nyingine, sisi binafsi tulipenda suluhisho la kufuatilia nafasi za bure za maegesho. Kwetu sisi, ilikuwa tukio la kupendeza sana la kuzamishwa katika bidhaa na maendeleo. Natumaini kwamba matukio zaidi na zaidi ya kuvutia yatafanyika katika mikoa, ikiwa ni pamoja na mada ya AI.

Chanzo: mapenzi.com

Kuongeza maoni