OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak

30 Kasım - 1 Aralık Nizhny Novgorod'da düzenlendi OpenVINO hackathonu. Katılımcılardan Intel OpenVINO araç setini kullanarak bir ürün çözümünün prototipini oluşturmaları istendi. Organizatörler, bir görev seçerken yönlendirilebilecek yaklaşık konuların bir listesini önerdiler, ancak nihai karar takımlara kaldı. Ayrıca üründe yer almayan modellerin kullanımı teşvik edildi.

OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak

Bu yazımızda sizlere birinci sırayı aldığımız ürünün prototipini nasıl oluşturduğumuzu anlatacağız.

Hackathon'a 10'dan fazla takım katıldı. Bazılarının başka bölgelerden gelmiş olması güzel. Hackathon'un mekanı, Nizhny Novgorod'un eski fotoğraflarının maiyetle birlikte asıldığı “Pochain'deki Kremlinsky” kompleksiydi! (Şu anda Intel'in merkez ofisinin Nijniy Novgorod'da bulunduğunu hatırlatmak isterim). Katılımcılara kod yazmaları için 26 saat süre verildi ve sonunda çözümlerini sunmaları gerekiyordu. Ayrı bir avantaj, planlanan her şeyin gerçekten uygulandığından ve sunumda fikir olarak kalmadığından emin olmak için bir demo oturumunun varlığıydı. Ürünler, atıştırmalıklar, yiyecekler, her şey de oradaydı!

Ayrıca Intel isteğe bağlı olarak kameralar, Raspberry PI ve Neural Compute Stick 2'yi de sağladı.

görev seçimi

Serbest biçimli bir hackathon'a hazırlanmanın en zor kısımlarından biri bir meydan okuma seçmektir. Duyuruda bunun son derece memnuniyetle karşılandığı söylendiğinden, hemen üründe bulunmayan bir şey bulmaya karar verdik.

analiz ettikten моделиMevcut sürümde ürüne dahil olanların çoğunun çeşitli bilgisayarlı görme sorunlarını çözdüğü sonucuna varıyoruz. Üstelik bilgisayarlı görme alanında OpenVINO kullanılarak çözülemeyen bir problemin ortaya çıkması çok zordur ve icat edilebilse bile kamuya açık alanda önceden eğitilmiş modeller bulmak zordur. Konuşma işleme ve analize doğru başka bir yöne gitmeye karar veriyoruz. Konuşmadan duyguları tanımaya yönelik ilginç bir görevi ele alalım. OpenVINO'nun zaten kişinin yüzüne göre duygularını belirleyen bir modele sahip olduğunu söylemek gerekir, ancak:

  • Teorik olarak hem ses hem de görüntü üzerinde çalışacak, doğrulukta artış sağlayacak birleşik bir algoritma oluşturmak mümkündür.
  • Kameralar genellikle dar bir görüş açısına sahiptir; geniş bir alanı kaplamak için birden fazla kamera gerekir; seste böyle bir sınırlama yoktur.

Fikri geliştirelim: Perakende segmentine yönelik fikri temel alalım. Mağaza ödemelerinde müşteri memnuniyetini ölçebilirsiniz. Müşterilerden biri hizmetten memnun değilse ve sesini yükseltmeye başlarsa, yardım için hemen yöneticiyi arayabilirsiniz.
Bu durumda insan sesi tanımayı eklememiz gerekiyor; bu, mağaza çalışanlarını müşterilerden ayırmamıza ve her bir birey için analiz sağlamamıza olanak tanıyacak. Ayrıca mağaza çalışanlarının davranışlarını bizzat analiz etmek, takımdaki atmosferi değerlendirmek mümkün olacak, kulağa hoş geliyor!

Çözümümüz için gereksinimleri formüle ediyoruz:

  • Hedef cihazın küçük boyutu
  • Gerçek zamanlı çalışma
  • Düşük fiyat
  • Kolay ölçeklenebilirlik

Sonuç olarak hedef cihaz olarak Raspberry Pi 3 c'yi seçiyoruz Intel NCS2.

Burada NCS'nin önemli bir özelliğine dikkat etmek önemlidir; standart CNN mimarileriyle en iyi şekilde çalışır, ancak üzerinde özel katmanlar bulunan bir model çalıştırmanız gerekiyorsa düşük seviyeli optimizasyon bekleyebilirsiniz.

Yapılacak küçük bir şey var: bir mikrofon almanız gerekiyor. Sıradan bir USB mikrofon iş görecektir ancak RPI ile birlikte iyi görünmeyecektir. Ancak burada bile çözüm kelimenin tam anlamıyla "yakınlarda yatıyor." Sesi kaydetmek için kitteki Ses Bonnet panosunu kullanmaya karar veriyoruz Google AIY Ses Kitiüzerinde kablolu bir stereo mikrofon bulunur.

Raspbian'ı şu adresten indirin: AIY projeleri deposu ve bunu bir flash sürücüye yükleyin, aşağıdaki komutu kullanarak mikrofonun çalışıp çalışmadığını test edin (sesi 5 saniye uzunluğunda kaydedecek ve bir dosyaya kaydedecektir):

arecord -d 5 -r 16000 test.wav

Mikrofonun çok hassas olduğunu ve gürültüyü iyi yakaladığını hemen belirtmeliyim. Bunu düzeltmek için alsamixer'a gidelim, Cihazları yakala'yı seçip giriş sinyali seviyesini %50-60'a düşürelim.

OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak
Gövdeyi bir dosyayla değiştiriyoruz ve her şey uyuyor, hatta bir kapakla kapatabilirsiniz

Gösterge düğmesi ekleme

AIY Voice Kit'i parçalarına ayırırken arka ışığı yazılımla kontrol edilebilen bir RGB butonu olduğunu hatırlıyoruz. “Google AIY Led” ifadesini arıyoruz ve belgeleri buluyoruz: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Tanınan duyguyu görüntülemek için neden bu düğmeyi kullanmıyorsunuz, yalnızca 7 sınıfımız var ve düğmenin 8 rengi var, yeter!

Düğmeyi GPIO aracılığıyla Voice Bonnet'e bağlarız, gerekli kütüphaneleri yükleriz (bunlar zaten AIY projelerinin dağıtım kitinde yüklüdür)

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

Her duygunun bir RGB Tuple biçiminde karşılık gelen bir renge ve aiy.leds.Leds sınıfından bir nesneye sahip olacağı ve bunun aracılığıyla rengi güncelleyeceğimiz bir dict oluşturalım:

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

Ve son olarak, bir duygunun her yeni tahmininden sonra, düğmenin rengini buna göre (tuşla) güncelleyeceğiz.

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

OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak
Düğme, yan!

Sesle çalışma

Mikrofondan akışı yakalamak için pyaudio'yu, gürültüyü filtrelemek ve sesi algılamak için webrtcvad'ı kullanacağız. Ayrıca asenkron olarak ses alıntılarını ekleyip çıkaracağımız bir kuyruk oluşturacağız.

Webrtcvad'in sağlanan parçanın boyutunda bir sınırlaması olduğundan - 10/20/30ms'ye eşit olmalıdır ve modelin duyguları tanımaya yönelik eğitimi (daha sonra öğreneceğimiz gibi) 48kHz'lik bir veri kümesi üzerinde gerçekleştirilmiştir. 48000×20ms/1000×1(mono)=960 bayt boyutunda parçalar yakalayın. Webrtcvad, bu parçaların her biri için Doğru/Yanlış değerini döndürecektir; bu, yığında bir oylamanın varlığına veya yokluğuna karşılık gelir.

Aşağıdaki mantığı uygulayalım:

  • Oy olan parçaları listeye ekleyeceğiz; oy yoksa boş parçaların sayacını artıracağız.
  • Boş yığınların sayacı >=30 (600 ms) ise, birikmiş yığınlar listesinin boyutuna bakarız; >250 ise kuyruğa ekleriz; değilse uzunluğu dikkate alırız. Kaydın konuşmacıyı tanımlamak için modele beslenmesi yeterli değildir.
  • Boş parçaların sayacı hala < 30 ise ve birikmiş parçalar listesinin boyutu 300'ü aşarsa, daha doğru bir tahmin için parçayı kuyruğa ekleyeceğiz. (çünkü duygular zamanla değişme eğilimindedir)

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

Kamusal alanda önceden eğitilmiş modeller aramanın, github'a, Google'a gitmenin zamanı geldi, ancak kullanılan mimari konusunda bir sınırlamamız olduğunu unutmayın. Bu oldukça zor bir kısım çünkü modelleri giriş verileriniz üzerinde test etmeniz ve ayrıca bunları OpenVINO'nun dahili formatı olan IR'ye (Ara Temsil) dönüştürmeniz gerekiyor. Github'dan yaklaşık 5-7 farklı çözüm denedik ve duyguları tanıma modeli hemen işe yaradıysa, ses tanıma için daha uzun süre beklemek zorunda kaldık - daha karmaşık mimariler kullanıyorlar.

Aşağıdakilere odaklanıyoruz:

Daha sonra teoriden başlayarak modelleri dönüştürmekten bahsedeceğiz. OpenVINO birkaç modül içerir:

  • Açık Model Hayvanat Bahçesi, kullanılabilecek ve ürününüze dahil edilebilecek modeller
  • Model Optimzer, bu sayede çeşitli çerçeve formatlarından (Tensorflow, ONNX vb.) bir modeli daha sonra üzerinde çalışacağımız Ara Gösterim formatına dönüştürebilirsiniz.
  • Inference Engine, modelleri Intel işlemciler, Myriad çipler ve Neural Compute Stick hızlandırıcılarda IR formatında çalıştırmanıza olanak tanır
  • OpenCV'nin en verimli versiyonu (Inference Engine desteğiyle)
    IR biçimindeki her model iki dosyayla tanımlanır: .xml ve .bin.
    Modeller, Model Optimizer aracılığıyla aşağıdaki şekilde IR formatına dönüştürülür:

    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 modelin çalışacağı veri formatını seçmenizi sağlar. FP32, FP16, INT8 desteklenmektedir. En uygun veri türünü seçmek iyi bir performans artışı sağlayabilir.
    --input_shape giriş verilerinin boyutunu gösterir. Bunu dinamik olarak değiştirme yeteneği C++ API'sinde mevcut gibi görünüyor, ancak o kadar derine inmedik ve bunu modellerden biri için düzelttik.
    Daha sonra IR formatında dönüştürdüğümüz modeli DNN modülü aracılığıyla OpenCV’ye yükleyip ona iletmeyi deneyelim.

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

    Bu durumda son satır, hesaplamaları Nöral Hesaplama Çubuğuna yönlendirmenize izin verir, temel hesaplamalar işlemci üzerinde gerçekleştirilir, ancak Raspberry Pi durumunda bu işe yaramaz, bir çubuğa ihtiyacınız olacaktır.

    Daha sonra mantık şu şekildedir: sesimizi belirli bir boyuttaki pencerelere böleriz (bizim için 0.4 saniyedir), bu pencerelerin her birini MFCC'ye dönüştürürüz ve ardından bunları ızgaraya besleriz:

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

    Şimdi tüm pencereler için en yaygın sınıfı ele alalım. Basit bir çözüm, ancak bir hackathon için çok karmaşık bir şey bulmanıza gerek yok, yalnızca zamanınız varsa. Hala yapacak çok işimiz var, o yüzden devam edelim; ses tanımayla ilgileneceğiz. Önceden kaydedilmiş seslerin spektrogramlarının saklanacağı bir tür veritabanı oluşturmak gereklidir. Çok az zamanımız kaldığı için bu sorunu elimizden geldiğince çözeceğiz.

    Yani, bir ses alıntısını kaydetmek için bir komut dosyası oluşturuyoruz (yukarıda açıklandığı gibi çalışır, yalnızca klavyeden kesildiğinde sesi bir dosyaya kaydeder).

    Hadi deneyelim:

    python3 voice_db/record_voice.py test.wav

    Birkaç kişinin sesini kaydediyoruz (bizim durumumuzda üç ekip üyesi)
    Daha sonra, kaydedilen her ses için hızlı bir fourier dönüşümü gerçekleştiriyoruz, bir spektrogram elde ediyoruz ve onu numpy dizisi (.npy) olarak kaydediyoruz:

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

    Dosyada daha fazla ayrıntı create_base.py
    Sonuç olarak, ana betiği çalıştırdığımızda, en başta bu spektrogramlardan yerleştirmeler alacağız:

    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)

    Seslendirilen segmentten yerleştirmeyi aldıktan sonra, veri tabanındaki tüm seslere geçişten kosinüs mesafesini alarak kime ait olduğunu belirleyebileceğiz (ne kadar küçükse, o kadar muhtemel) - demo için eşiği ayarladık 0.3'e kadar):

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

    Sonuç olarak, çıkarım hızının yüksek olduğunu ve 1-2 model daha eklemeyi mümkün kıldığını belirtmek isterim (7 saniye uzunluğundaki bir örnek için çıkarım 2.5 sürdü). Artık yeni modeller eklemeye vaktimiz kalmadı ve web uygulamasının prototipini yazmaya odaklandık.

    Web uygulaması

    Önemli bir nokta: Evden yanımıza bir yönlendirici alıp yerel ağımızı kuruyoruz, bu, cihazı ve dizüstü bilgisayarları ağ üzerinden bağlamaya yardımcı oluyor.

    Arka uç, websocket teknolojisine (http over tcp protokolü) dayalı, ön taraf ile Raspberry Pi arasında uçtan uca bir mesaj kanalıdır.

    İlk aşama, kullanıcının o döneme ait duygusal geçmişi hakkında istatistikler oluşturulabilmesi için yolculuklarının yarısında veri tabanına kaydedilen raspberry'den işlenmiş bilgileri, yani json'a paketlenmiş tahmincileri almaktır. Bu paket daha sonra aboneliği kullanan ve websocket uç noktasından paketleri alan ön uca gönderilir. Arka uç mekanizmasının tamamı golang dilinde oluşturulmuştur; goroutinlerin iyi bir şekilde yerine getirdiği asenkron görevlere çok uygun olduğu için seçilmiştir.
    Uç noktaya erişildiğinde kullanıcı kayıt altına alınarak yapıya girilir ve mesajı alınır. Hem kullanıcı hem de mesaj, mesajların zaten gönderildiği (abone olunan tarafa) ortak bir merkeze girilir ve kullanıcı bağlantıyı kapatırsa (ahududu veya ön), aboneliği iptal edilir ve kaldırılır. Merkez.

    OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak
    Arkadan bağlantı bekliyoruz

    Ön uç, geliştirme sürecini hızlandırmak ve basitleştirmek için React kütüphanesini kullanan, JavaScript ile yazılmış bir web uygulamasıdır. Bu uygulamanın amacı back-end tarafta ve doğrudan Raspberry Pi üzerinde çalışan algoritmalar kullanılarak elde edilen verilerin görselleştirilmesidir. Sayfada reaksiyon yönlendirici kullanılarak uygulanan bölümsel yönlendirme vardır, ancak ilgilenilen ana sayfa, WebSocket teknolojisi kullanılarak sunucudan gerçek zamanlı olarak sürekli bir veri akışının alındığı ana sayfadır. Raspberry Pi, bir sesi algılar, kayıtlı veri tabanından sesin belirli bir kişiye ait olup olmadığını belirler ve istemciye bir olasılık listesi gönderir. Müşteri, ilgili en son verileri görüntüler, büyük olasılıkla mikrofona konuşan kişinin avatarını ve aynı zamanda kelimeleri telaffuz ederken kullandığı duyguyu görüntüler.

    OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak
    Güncellenen tahminlerin yer aldığı ana sayfa

    Sonuç

    Her şeyi planlandığı gibi tamamlamak mümkün değildi, sadece zamanımız yoktu, bu yüzden asıl umut demodaydı, her şeyin işe yarayacağı. Sunumda her şeyin nasıl çalıştığını, hangi modelleri aldıklarını, ne gibi sorunlarla karşılaştıklarını anlattılar. Sonraki demo kısmıydı; uzmanlar odanın içinde rastgele sırayla dolaştı ve çalışan prototipe bakmak için her takıma yaklaştı. Bize de sorular sordular, herkes kendi payına düşeni yanıtladı, dizüstü bilgisayarda interneti bıraktılar ve her şey gerçekten de beklendiği gibi çalıştı.

    Çözümümüzün toplam maliyetinin 150$ olduğunu belirteyim:

    • Ahududu Pi 3 ~ 35$
    • Google AIY Voice Bonnet (yeniden konuşmacı ücreti alabilirsiniz) ~ 15$
    • Intel NCS 2 ~ 100$

    Nasıl geliştirilir:

    • İstemciden kaydı kullanın - rastgele oluşturulan metni okumayı isteyin
    • Birkaç model daha ekleyin: cinsiyeti ve yaşı sesli olarak belirleyebilirsiniz
    • Aynı anda çıkan sesleri ayırın (günlükleştirme)

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

    OpenVINO hackathonu: Raspberry Pi'de sesi ve duyguları tanımak
    Yorulduk ama mutluyuz

    Son olarak organizatörlere ve katılımcılara teşekkür etmek istiyorum. Diğer ekiplerin projeleri arasında ücretsiz park yerlerinin izlenmesi çözümünü kişisel olarak beğendik. Bizim için ürün ve geliştirme sürecine dalma konusunda son derece harika bir deneyimdi. Yapay zeka konuları da dahil olmak üzere bölgelerde giderek daha ilginç etkinliklerin düzenleneceğini umuyorum.

Kaynak: habr.com

Yorum ekle