Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi

30 November - 1 Disember di Nizhny Novgorod telah diadakan Hackathon OpenVINO. Peserta diminta untuk mencipta prototaip penyelesaian produk menggunakan kit alat Intel OpenVINO. Penganjur mencadangkan senarai topik anggaran yang boleh dibimbing semasa memilih tugas, tetapi keputusan muktamad kekal dengan pasukan. Di samping itu, penggunaan model yang tidak termasuk dalam produk adalah digalakkan.

Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi

Dalam artikel ini kami akan memberitahu anda tentang cara kami mencipta prototaip produk kami, yang akhirnya kami mengambil tempat pertama.

Lebih daripada 10 pasukan menyertai hackathon itu. Seronoknya ada yang datang dari daerah lain. Tempat untuk hackathon adalah kompleks "Kremlinsky on Pochain", di mana gambar-gambar kuno Nizhny Novgorod digantung di dalam, dalam rombongan! (Saya mengingatkan anda bahawa pada masa ini pejabat pusat Intel terletak di Nizhny Novgorod). Peserta diberi masa 26 jam untuk menulis kod, dan pada akhirnya mereka perlu membentangkan penyelesaian mereka. Kelebihan berasingan ialah kehadiran sesi demo untuk memastikan semua yang dirancang benar-benar dilaksanakan dan tidak kekal sebagai idea dalam pembentangan. Barang dagangan, makanan ringan, makanan, semuanya ada di sana juga!

Selain itu, Intel menyediakan kamera secara pilihan, Raspberry PI, Neural Compute Stick 2.

Pemilihan tugas

Salah satu bahagian paling sukar untuk menyediakan hackathon bentuk bebas ialah memilih cabaran. Kami segera memutuskan untuk menghasilkan sesuatu yang belum ada dalam produk, kerana pengumuman itu mengatakan bahawa ini sangat dialu-alukan.

Setelah menganalisis model, yang termasuk dalam produk dalam keluaran semasa, kami sampai pada kesimpulan bahawa kebanyakan mereka menyelesaikan pelbagai masalah penglihatan komputer. Lebih-lebih lagi, adalah sangat sukar untuk menimbulkan masalah dalam bidang penglihatan komputer yang tidak dapat diselesaikan menggunakan OpenVINO, dan walaupun seseorang itu boleh dicipta, sukar untuk mencari model yang telah dilatih dalam domain awam. Kami memutuskan untuk menggali ke arah lain - ke arah pemprosesan pertuturan dan analisis. Mari kita pertimbangkan tugas yang menarik untuk mengenali emosi daripada ucapan. Harus dikatakan bahawa OpenVINO sudah mempunyai model yang menentukan emosi seseorang berdasarkan wajah mereka, tetapi:

  • Secara teori, adalah mungkin untuk mencipta algoritma gabungan yang akan berfungsi pada kedua-dua bunyi dan imej, yang sepatutnya memberikan peningkatan ketepatan.
  • Kamera biasanya mempunyai sudut tontonan yang sempit; lebih daripada satu kamera diperlukan untuk menutup kawasan yang luas; bunyi tidak mempunyai had sedemikian.

Mari kita kembangkan idea: mari kita ambil idea untuk segmen runcit sebagai asas. Anda boleh mengukur kepuasan pelanggan semasa membuat pembayaran di kedai. Jika salah seorang pelanggan tidak berpuas hati dengan perkhidmatan dan mula meningkatkan nada mereka, anda boleh segera menghubungi pentadbir untuk mendapatkan bantuan.
Dalam kes ini, kami perlu menambah pengecaman suara manusia, ini akan membolehkan kami membezakan pekerja kedai daripada pelanggan dan menyediakan analisis untuk setiap individu. Nah, sebagai tambahan, adalah mungkin untuk menganalisis tingkah laku pekerja kedai itu sendiri, menilai suasana dalam pasukan, terdengar bagus!

Kami merumuskan keperluan untuk penyelesaian kami:

  • Saiz kecil peranti sasaran
  • Operasi masa nyata
  • Harga rendah
  • Skala mudah

Akibatnya, kami memilih Raspberry Pi 3 c sebagai peranti sasaran Intel NCS 2.

Di sini adalah penting untuk mengambil perhatian satu ciri penting NCS - ia berfungsi paling baik dengan seni bina CNN standard, tetapi jika anda perlu menjalankan model dengan lapisan tersuai padanya, maka jangkakan pengoptimuman peringkat rendah.

Hanya ada satu perkara kecil yang perlu dilakukan: anda perlu mendapatkan mikrofon. Mikrofon USB biasa akan berfungsi, tetapi ia tidak akan kelihatan baik bersama-sama dengan RPI. Tetapi di sini penyelesaiannya secara literal "terletak berdekatan." Untuk merakam suara, kami memutuskan untuk menggunakan papan Bonet Suara daripada kit Kit Suara Google AIY, di mana terdapat mikrofon stereo berwayar.

Muat turun Raspbian dari AIY projek repositori dan muat naiknya ke pemacu kilat, uji bahawa mikrofon berfungsi menggunakan arahan berikut (ia akan merakam audio selama 5 saat dan menyimpannya ke fail):

arecord -d 5 -r 16000 test.wav

Saya harus segera ambil perhatian bahawa mikrofon adalah sangat sensitif dan menangkap bunyi dengan baik. Untuk membetulkannya, mari pergi ke alsamixer, pilih Tangkap peranti dan kurangkan tahap isyarat input kepada 50-60%.

Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi
Kami mengubah suai badan dengan fail dan semuanya sesuai, malah anda boleh menutupnya dengan penutup

Menambah butang penunjuk

Semasa mengasingkan Kit Suara AIY, kami ingat bahawa terdapat butang RGB, lampu latarnya boleh dikawal oleh perisian. Kami mencari "Google AIY Led" dan mencari dokumentasi: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Mengapa tidak menggunakan butang ini untuk memaparkan emosi yang diiktiraf, kami hanya mempunyai 7 kelas, dan butang itu mempunyai 8 warna, cukup!

Kami menyambungkan butang melalui GPIO ke Voice Bonnet, memuatkan perpustakaan yang diperlukan (ia telah dipasang dalam kit pengedaran dari projek AIY)

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

Mari kita buat dict di mana setiap emosi akan mempunyai warna yang sepadan dalam bentuk RGB Tuple dan objek kelas aiy.leds.Leds, yang melaluinya kami akan mengemas kini warna:

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

Dan akhirnya, selepas setiap ramalan baru sesuatu emosi, kami akan mengemas kini warna butang mengikutnya (mengikut kekunci).

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

Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi
Butang, bakar!

Bekerja dengan suara

Kami akan menggunakan pyaudio untuk menangkap strim daripada mikrofon dan webrtcvad untuk menapis bunyi dan mengesan suara. Di samping itu, kami akan membuat baris gilir yang kami akan menambah dan mengalih keluar petikan suara secara tidak segerak.

Memandangkan webrtcvad mempunyai had pada saiz serpihan yang dibekalkan - ia mestilah sama dengan 10/20/30ms, dan latihan model untuk mengenali emosi (seperti yang akan kita pelajari kemudian) telah dijalankan pada set data 48kHz, kita akan tangkap ketulan bersaiz 48000Γ—20ms/1000Γ—1(mono)=960 bait. Webrtcvad akan mengembalikan Betul/Salah untuk setiap bahagian ini, yang sepadan dengan kehadiran atau ketiadaan undian dalam bahagian tersebut.

Mari kita laksanakan logik berikut:

  • Kami akan menambah pada senarai bahagian yang terdapat undian; jika tiada undian, maka kami akan menambah bilangan bahagian kosong.
  • Jika pembilang ketulan kosong ialah >=30 (600 ms), maka kita melihat pada saiz senarai ketulan terkumpul; jika >250, maka kita menambahnya pada baris gilir; jika tidak, kita menganggap bahawa panjang daripada rekod tidak mencukupi untuk menyuapkannya kepada model untuk mengenal pasti pembesar suara.
  • Jika pembilang ketulan kosong masih < 30, dan saiz senarai ketulan terkumpul melebihi 300, maka kami akan menambah serpihan pada baris gilir untuk ramalan yang lebih tepat. (kerana emosi cenderung berubah dari semasa ke semasa)

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

Sudah tiba masanya untuk mencari model terlatih dalam domain awam, pergi ke github, Google, tetapi ingat bahawa kami mempunyai had pada seni bina yang digunakan. Ini adalah bahagian yang agak sukar, kerana anda perlu menguji model pada data input anda, dan sebagai tambahan, tukarkannya kepada format dalaman OpenVINO - IR (Perwakilan Pertengahan). Kami mencuba kira-kira 5-7 penyelesaian berbeza daripada github, dan jika model untuk mengenali emosi berfungsi serta-merta, maka dengan pengecaman suara kami terpaksa menunggu lebih lama - mereka menggunakan seni bina yang lebih kompleks.

Kami memberi tumpuan kepada perkara berikut:

Seterusnya kita akan bercakap tentang menukar model, bermula dengan teori. OpenVINO termasuk beberapa modul:

  • Buka Zoo Model, model yang boleh digunakan dan disertakan dalam produk anda
  • Pengoptimum Model, terima kasih kepada anda yang boleh menukar model daripada pelbagai format rangka kerja (Tensorflow, ONNX dll) ke dalam format Perwakilan Pertengahan, yang dengannya kami akan bekerja lebih lanjut
  • Enjin Inferens membolehkan anda menjalankan model dalam format IR pada pemproses Intel, cip Myriad dan pemecut Neural Compute Stick
  • Versi OpenCV yang paling cekap (dengan sokongan Enjin Inferens)
    Setiap model dalam format IR diterangkan oleh dua fail: .xml dan .bin.
    Model ditukar kepada format IR melalui Pengoptimum Model seperti berikut:

    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 membolehkan anda memilih format data yang model akan berfungsi. FP32, FP16, INT8 disokong. Memilih jenis data yang optimum boleh memberikan peningkatan prestasi yang baik.
    --input_shape menunjukkan dimensi data input. Keupayaan untuk mengubahnya secara dinamik nampaknya terdapat dalam API C++, tetapi kami tidak menggali sejauh itu dan hanya membetulkannya untuk salah satu model.
    Seterusnya, mari cuba memuatkan model yang telah ditukar dalam format IR melalui modul DNN ke dalam OpenCV dan majukannya kepadanya.

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

    Baris terakhir dalam kes ini membolehkan anda mengubah hala pengiraan ke Neural Compute Stick, pengiraan asas dilakukan pada pemproses, tetapi dalam kes Raspberry Pi ini tidak akan berfungsi, anda memerlukan kayu.

    Seterusnya, logiknya adalah seperti berikut: kami membahagikan audio kami ke dalam tetingkap dengan saiz tertentu (bagi kami ialah 0.4 s), kami menukar setiap tetingkap ini menjadi MFCC, yang kemudian kami suapkan ke grid:

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

    Seterusnya, mari kita ambil kelas yang paling biasa untuk semua tingkap. Penyelesaian yang mudah, tetapi untuk hackathon anda tidak perlu membuat sesuatu yang terlalu kabur, hanya jika anda mempunyai masa. Kami masih mempunyai banyak kerja, jadi mari teruskan - kami akan menangani pengecaman suara. Adalah perlu untuk membuat beberapa jenis pangkalan data di mana spektrogram suara pra-rakam akan disimpan. Memandangkan masa yang tinggal sedikit, kami akan menyelesaikan masalah ini sebaik mungkin.

    Iaitu, kami mencipta skrip untuk merakam petikan suara (ia berfungsi dengan cara yang sama seperti yang diterangkan di atas, hanya apabila diganggu dari papan kekunci ia akan menyimpan suara ke fail).

    Mari kita cuba:

    python3 voice_db/record_voice.py test.wav

    Kami merakam suara beberapa orang (dalam kes kami, tiga ahli pasukan)
    Seterusnya, untuk setiap suara yang dirakam kami melakukan transformasi fourier pantas, dapatkan spektrogram dan simpannya sebagai tatasusunan numpy (.npy):

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

    Butiran lanjut dalam fail create_base.py
    Akibatnya, apabila kita menjalankan skrip utama, kita akan mendapat pembenaman daripada spektrogram ini pada mulanya:

    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)

    Selepas menerima pembenaman daripada segmen yang dibunyikan, kami akan dapat menentukan miliknya dengan mengambil jarak kosinus dari laluan ke semua suara dalam pangkalan data (semakin kecil, lebih berkemungkinan) - untuk demo kami menetapkan ambang kepada 0.3):

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

    Pada akhirnya, saya ingin ambil perhatian bahawa kelajuan inferens adalah pantas dan memungkinkan untuk menambah 1-2 lagi model (untuk sampel selama 7 saat mengambil masa 2.5 untuk inferens). Kami tidak lagi mempunyai masa untuk menambah model baharu dan menumpukan pada menulis prototaip aplikasi web.

    aplikasi sesawang

    Perkara penting: kami membawa penghala bersama kami dari rumah dan menyediakan rangkaian tempatan kami, ia membantu untuk menyambungkan peranti dan komputer riba melalui rangkaian.

    Bahagian belakang ialah saluran mesej hujung ke hujung antara hadapan dan Raspberry Pi, berdasarkan teknologi websocket (http over tcp protocol).

    Peringkat pertama ialah menerima maklumat yang diproses daripada raspberi, iaitu peramal yang dibungkus dalam json, yang disimpan dalam pangkalan data separuh perjalanan mereka supaya statistik boleh dijana tentang latar belakang emosi pengguna untuk tempoh tersebut. Paket ini kemudiannya dihantar ke bahagian hadapan, yang menggunakan langganan dan menerima paket dari titik akhir soket web. Seluruh mekanisme bahagian belakang dibina dalam bahasa golang; ia dipilih kerana ia sangat sesuai untuk tugas tak segerak, yang dikendalikan dengan baik oleh goroutine.
    Apabila mengakses titik akhir, pengguna didaftarkan dan dimasukkan ke dalam struktur, kemudian mesejnya diterima. Kedua-dua pengguna dan mesej dimasukkan ke dalam hab biasa, dari mana mesej telah dihantar lebih jauh (ke hadapan yang dilanggan), dan jika pengguna menutup sambungan (raspberi atau hadapan), maka langganannya dibatalkan dan dia dikeluarkan daripada hab.

    Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi
    Kami sedang menunggu sambungan dari belakang

    Front-end ialah aplikasi web yang ditulis dalam JavaScript menggunakan perpustakaan React untuk mempercepat dan memudahkan proses pembangunan. Tujuan aplikasi ini adalah untuk menggambarkan data yang diperoleh menggunakan algoritma yang berjalan di bahagian belakang dan terus pada Raspberry Pi. Halaman ini mempunyai penghalaan keratan yang dilaksanakan menggunakan penghala tindak balas, tetapi halaman utama yang diminati ialah halaman utama, di mana aliran data berterusan diterima dalam masa nyata daripada pelayan menggunakan teknologi WebSocket. Raspberry Pi mengesan suara, menentukan sama ada ia milik orang tertentu daripada pangkalan data berdaftar dan menghantar senarai kebarangkalian kepada pelanggan. Pelanggan memaparkan data terkini yang berkaitan, memaparkan avatar orang yang kemungkinan besar bercakap ke dalam mikrofon, serta emosi yang dia menyebut perkataan itu.

    Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi
    Halaman utama dengan ramalan yang dikemas kini

    Kesimpulan

    Tidak mungkin untuk menyelesaikan segala-galanya seperti yang dirancang, kami hanya tidak mempunyai masa, jadi harapan utama adalah dalam demo, bahawa semuanya akan berfungsi. Dalam pembentangan mereka bercakap tentang bagaimana semuanya berfungsi, model apa yang mereka ambil, masalah apa yang mereka hadapi. Seterusnya ialah bahagian demo - pakar berjalan di sekeliling bilik secara rawak dan mendekati setiap pasukan untuk melihat prototaip yang berfungsi. Mereka bertanya soalan kepada kami juga, semua orang menjawab bahagian mereka, mereka meninggalkan web pada komputer riba, dan semuanya benar-benar berfungsi seperti yang diharapkan.

    Biar saya ambil perhatian bahawa jumlah kos penyelesaian kami ialah $150:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (anda boleh mengambil bayaran pembesar suara) ~ 15$
    • Intel NCS 2 ~ 100$

    Bagaimana untuk menambah baik:

    • Gunakan pendaftaran daripada pelanggan - minta untuk membaca teks yang dijana secara rawak
    • Tambah beberapa lagi model: anda boleh menentukan jantina dan umur melalui suara
    • Pisahkan suara yang berbunyi serentak (diarisasi)

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

    Hackathon OpenVINO: mengenali suara dan emosi pada Raspberry Pi
    Penat tapi kami gembira

    Kesimpulannya, saya ingin mengucapkan terima kasih kepada pihak penganjur dan peserta. Antara projek pasukan lain, kami secara peribadi menyukai penyelesaian untuk memantau tempat letak kereta percuma. Bagi kami, ia adalah pengalaman yang hebat untuk menyelami produk dan pembangunan. Saya berharap lebih banyak acara menarik akan diadakan di rantau ini, termasuk topik AI.

Sumber: www.habr.com

Tambah komen