ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи

30. новембар – 1. децембар одржан у Нижњем Новгороду ОпенВИНО хацкатхон. Учесници су замољени да направе прототип решења производа користећи Интел ОпенВИНО алат. Организатори су предложили листу оквирних тема којима би се могли руководити при избору задатка, али је коначна одлука остала на тимовима. Поред тога, подстицана је употреба модела који нису укључени у производ.

ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи

У овом чланку ћемо вам рећи како смо креирали наш прототип производа, са којим смо на крају заузели прво место.

На хакатону је учествовало више од 10 тимова. Лепо је што су неки од њих дошли из других крајева. Место одржавања хакатона био је комплекс „Кремљински на Почејну“, где су у пратњи биле окачене древне фотографије Нижњег Новгорода! (Подсећам да се у овом тренутку централна канцеларија Интела налази у Нижњем Новгороду). Учесницима је дато 26 сати да напишу код, а на крају су морали да представе своје решење. Посебна предност је било присуство демо сесије како би се уверило да је све планирано заиста спроведено и да није остало идеја у презентацији. Роба, грицкалице, храна, све је било ту!

Поред тога, Интел је опционо обезбедио камере, Распберри ПИ, Неурал Цомпуте Стицк 2.

Избор задатака

Један од најтежих делова припреме за хакатон слободне форме је одабир изазова. Одмах смо одлучили да смислимо нешто што још није било у производу, пошто је у најави писало да је то добродошло.

Након анализе модели, који су укључени у производ у тренутном издању, долазимо до закључка да већина њих решава различите проблеме компјутерског вида. Штавише, веома је тешко доћи до проблема у области компјутерског вида који се не може решити коришћењем ОпенВИНО-а, а чак и ако се може измислити, тешко је пронаћи унапред обучене моделе у јавном домену. Одлучујемо да копамо у другом правцу – ка обради говора и аналитици. Хајде да размотримо занимљив задатак препознавања емоција из говора. Мора се рећи да ОпенВИНО већ има модел који одређује емоције особе на основу њеног лица, али:

  • У теорији, могуће је направити комбиновани алгоритам који ће радити и на звуку и на слици, што би требало да даје повећање тачности.
  • Камере обично имају узак угао гледања; потребно је више од једне камере за покривање велике површине; звук нема такво ограничење.

Хајде да развијемо идеју: узмимо идеју за малопродајни сегмент као основу. Задовољство купаца можете мерити на касама у продавницама. Ако је неко од купаца незадовољан услугом и почне да диже тон, можете одмах позвати администратора у помоћ.
У овом случају, морамо да додамо препознавање људског гласа, то ће нам омогућити да разликујемо запослене у продавници од купаца и пружимо аналитику за сваког појединца. Па, поред тога, биће могуће анализирати понашање самих запослених у продавници, проценити атмосферу у тиму, звучи добро!

Формулишемо захтеве за наше решење:

  • Мала величина циљног уређаја
  • Рад у реалном времену
  • Ниска цена
  • Лака скалабилност

Као резултат, бирамо Распберри Пи 3 ц као циљни уређај Интел НЦС 2.

Овде је важно напоменути једну важну карактеристику НЦС-а - најбоље ради са стандардним ЦНН архитектурама, али ако треба да покренете модел са прилагођеним слојевима на њему, очекујте оптимизацију ниског нивоа.

Постоји само једна мала ствар: потребно је да набавите микрофон. Обичан УСБ микрофон ће послужити, али неће изгледати добро заједно са РПИ. Али чак и овде решење буквално „лежи у близини“. За снимање гласа одлучујемо да користимо плочу Воице Боннет из комплета Гоогле АИИ гласовни комплет, на коме се налази жичани стерео микрофон.

Преузмите Распбиан са Репозиторијум АИИ пројеката и отпремите га на флеш диск, тестирајте да микрофон ради помоћу следеће команде (он ће снимати звук у трајању од 5 секунди и сачувати га у датотеку):

arecord -d 5 -r 16000 test.wav

Одмах треба да приметим да је микрофон веома осетљив и добро хвата буку. Да ово поправимо, идемо на алсамикер, изаберите Цаптуре девицес и смањите ниво улазног сигнала на 50-60%.

ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи
Тело модификујемо турпијом и све стане, можете га чак и затворити поклопцем

Додавање дугмета индикатора

Док растављамо АИИ Воице Кит, сећамо се да постоји РГБ дугме, чије позадинско осветљење се може контролисати софтвером. Тражимо „Гоогле АИИ Лед“ и налазимо документацију: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Зашто не користите ово дугме за приказ препознате емоције, имамо само 7 часова, а дугме има 8 боја, сасвим довољно!

Повезујемо дугме преко ГПИО-а на Воице Боннет, учитавамо потребне библиотеке (оне су већ инсталиране у дистрибутивном комплету из АИИ пројеката)

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

Хајде да направимо дицт у коме ће свака емоција имати одговарајућу боју у облику РГБ Тупле и објекат класе аии.ледс.Ледс, преко којег ћемо ажурирати боју:

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

И коначно, након сваког новог предвиђања емоције, ажурираћемо боју дугмета у складу са њом (по кључу).

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

ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи
Дугме, спали!

Рад са гласом

Користићемо пиаудио да ухватимо ток са микрофона и вебртцвад да филтрирамо шум и детектујемо глас. Поред тога, направићемо ред у који ћемо асинхроно додавати и уклањати гласовне изводе.

Пошто вебртцвад има ограничење на величину достављеног фрагмента – оно мора бити једнако 10/20/30мс, а обука модела за препознавање емоција (како ћемо касније сазнати) је спроведена на скупу података од 48кХз, ми ћемо ухвати комаде величине 48000×20мс/1000×1( моно)=960 бајтова. Вебртцвад ће вратити Тачно/Нетачно за сваки од ових делова, што одговара присуству или одсуству гласања у делу.

Хајде да применимо следећу логику:

  • Додаћемо на листу оне делове где се гласа; ако нема гласања, онда ћемо повећати бројач празних делова.
  • Ако је бројач празних делова >=30 (600 мс), онда гледамо величину листе акумулираних делова; ако је >250, онда га додајемо у ред; ако није, сматрамо да је дужина записа није довољно да га пренесе моделу да идентификује говорника.
  • Ако је бројач празних комада и даље < 30, а величина листе акумулираних комада прелази 300, онда ћемо фрагмент додати у ред за прецизније предвиђање. (јер емоције имају тенденцију да се мењају током времена)

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

Време је да потражите унапред обучене моделе у јавном домену, идите на гитхуб, Гоогле, али запамтите да имамо ограничење у вези са архитектуром која се користи. Ово је прилично тежак део, јер морате да тестирате моделе на вашим улазним подацима, и поред тога да их конвертујете у ОпенВИНО интерни формат - ИР (Интермедиате Репресентатион). Пробали смо око 5-7 различитих решења са гитхуб-а, и ако је модел за препознавање емоција прорадио одмах, онда смо са препознавањем гласа морали дуже да чекамо – користе сложеније архитектуре.

Фокусирамо се на следеће:

  • Емоције из гласа - https://github.com/alexmuhr/Voice_Emotion
    Ради по следећем принципу: аудио се сече на пасусе одређене величине, за сваки од ових пасуса бирамо МФЦЦ а затим их доставити као улазне податке ЦНН-у
  • Препознавање гласа - https://github.com/linhdvu14/vggvox-speaker-identification
    Овде уместо МФЦЦ-а радимо са спектрограмом, након ФФТ-а шаљемо сигнал у ЦНН, где на излазу добијамо векторску репрезентацију гласа.

Затим ћемо говорити о претварању модела, почевши од теорије. ОпенВИНО укључује неколико модула:

  • Отворите модел Зоо, модели из којих би се могли користити и укључити у ваш производ
  • Модел Оптимзер, захваљујући којем можете конвертовати модел из различитих формата оквира (Тенсорфлов, ОННКС итд.) у формат Интермедиате Репресентатион, са којим ћемо даље радити
  • Инференце Енгине вам омогућава да покренете моделе у ИР формату на Интел процесорима, Мириад чиповима и Неурал Цомпуте Стицк акцелераторима
  • Најефикаснија верзија ОпенЦВ-а (са подршком за Инференце Енгине)
    Сваки модел у ИР формату је описан са две датотеке: .кмл и .бин.
    Модели се конвертују у ИР формат преко Оптимизатора модела на следећи начин:

    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 омогућава вам да изаберете формат података са којим ће модел радити. Подржани су ФП32, ФП16, ИНТ8. Избор оптималног типа података може дати добар подстицај перформансама.
    --input_shape означава димензију улазних података. Чини се да је могућност динамичке промене присутна у Ц++ АПИ-ју, али нисмо копали толико далеко и једноставно смо је поправили за један од модела.
    Затим, покушајмо да учитамо већ конвертовани модел у ИР формату преко ДНН модула у ОпенЦВ и проследимо га на њега.

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

    Последњи ред у овом случају вам омогућава да преусмерите прорачуне на Неурал Цомпуте Стицк, основни прорачуни се обављају на процесору, али у случају Распберри Пи ово неће радити, биће вам потребан штап.

    Даље, логика је следећа: делимо наш аудио на прозоре одређене величине (за нас је то 0.4 с), сваки од ових прозора конвертујемо у МФЦЦ, који затим убацујемо у мрежу:

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

    Затим, узмимо најчешћу класу за све прозоре. Једноставно решење, али за хакатон не морате да смишљате нешто превише неразумно, само ако имате времена. Имамо још доста посла, па идемо даље – бавићемо се препознавањем гласа. Потребно је направити неку врсту базе података у којој би се чували спектрограми унапред снимљених гласова. Пошто је остало мало времена, решићемо овај проблем најбоље што можемо.

    Наиме, креирамо скрипту за снимање извода гласа (ради на исти начин као што је горе описано, само када се прекине са тастатуре, сачуваће глас у датотеку).

    Хајде да покушамо:

    python3 voice_db/record_voice.py test.wav

    Снимамо гласове неколико људи (у нашем случају три члана тима)
    Затим, за сваки снимљени глас изводимо брзу Фуријеову трансформацију, добијамо спектрограм и чувамо га као нумпи низ (.нпи):

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

    Више детаља у фајлу create_base.py
    Као резултат тога, када покренемо главну скрипту, добићемо уграђивање из ових спектрограма на самом почетку:

    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)

    Након што добијемо уградњу из озвученог сегмента, моћи ћемо да утврдимо коме припада узимајући косинусну удаљеност од пролаза до свих гласова у бази (што је мањи, то је вероватнији) - за демо смо поставили праг до 0.3):

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

    На крају, желео бих да приметим да је брзина закључивања била велика и омогућила је додавање још 1-2 модела (за узорак од 7 секунди било је потребно 2.5 за закључивање). Више нисмо имали времена за додавање нових модела и фокусирали смо се на писање прототипа веб апликације.

    веб апликација

    Важна тачка: узимамо рутер са собом од куће и постављамо нашу локалну мрежу, помаже у повезивању уређаја и лаптопа преко мреже.

    Позадински део је канал за поруке од краја до краја између предњег и Распберри Пи-ја, заснован на технологији вебсоцкет-а (хттп преко тцп протокола).

    Прва фаза је примање обрађених информација од малине, односно предиктора упакованих у јсон, који се чувају у бази података на пола пута како би се могла генерисати статистика о емоционалној позадини корисника за период. Овај пакет се затим шаље на фронтенд, који користи претплату и прима пакете са крајње тачке вебсоцкета. Цео позадински механизам је изграђен на језику голанг; изабран је зато што је веома погодан за асинхроне задатке, са којима се горутине добро баве.
    Приликом приступа крајњој тачки, корисник се региструје и уноси у структуру, затим се прима његова порука. И корисник и порука се уносе у заједничко чвориште, из којег се поруке већ шаљу даље (на претплаћени фронт), а ако корисник затвори везу (малина или фронт), онда му се претплата отказује и уклања се са средиште.

    ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи
    Чекамо везу с леђа

    Фронт-енд је веб апликација написана у ЈаваСцрипт-у користећи Реацт библиотеку да убрза и поједностави процес развоја. Сврха ове апликације је да визуализује податке добијене коришћењем алгоритама који раде на позадинској страни и директно на Распберри Пи. Страница има секционо рутирање имплементирано помоћу реацт-роутера, али главна страница од интереса је главна страница, где се континуирани ток података прима у реалном времену са сервера користећи ВебСоцкет технологију. Распберри Пи детектује глас, утврђује да ли припада одређеној особи из регистроване базе података и шаље листу вероватноће клијенту. Клијент приказује најновије релевантне податке, приказује аватар особе која је највероватније проговорила у микрофон, као и емоцију са којом изговара речи.

    ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи
    Почетна страница са ажурираним предвиђањима

    Закључак

    Није било могуће све завршити како је планирано, једноставно нисмо имали времена, тако да је главна нада била у демо-у, да ће све функционисати. У презентацији су говорили о томе како све функционише, које моделе су узели, са каквим проблемима су наишли. Следећи је био демо део – стручњаци су ходали по хали насумично и пришли сваком тиму да погледају радни прототип. Постављали су нам питања, свако је одговарао на свој део, оставили су веб на лаптопу и све је заиста функционисало како се очекивало.

    Дозволите ми да приметим да је укупна цена нашег решења била 150 долара:

    • Распберри Пи 3 ~ 35 долара
    • Гоогле АИИ Воице Боннет (можете узети накнаду за звучник) ~ 15 $
    • Интел НЦС 2 ~ 100$

    Како да се побољша:

    • Користите регистрацију од клијента - затражите да прочитате текст који је генерисан насумично
    • Додајте још неколико модела: можете одредити пол и године по гласу
    • Одвојите гласове који истовремено звуче (дијаризација)

    Репозиторијум: https://github.com/vladimirwest/OpenEMO

    ОпенВИНО хакатон: препознавање гласа и емоција на Распберри Пи
    Уморни али срећни смо

    На крају, желим да се захвалим организаторима и учесницима. Од пројеката других тимова, нама се лично допало решење за праћење слободних паркинг места. За нас је то било дивно кул искуство урањања у производ и развој. Надам се да ће се у регионима одржавати све више занимљивих догађаја, укључујући и теме АИ.

Извор: ввв.хабр.цом

Додај коментар