OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi

30 November - 1 Desember diadakan di Nizhny Novgorod Peretasan OpenVINO. Peserta diminta membuat prototipe solusi produk menggunakan toolkit Intel OpenVINO. Penyelenggara mengusulkan daftar perkiraan topik yang dapat dijadikan pedoman saat memilih tugas, tetapi keputusan akhir tetap ada pada tim. Selain itu, penggunaan model yang tidak disertakan dalam produk dianjurkan.

OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi

Pada artikel ini kami akan memberi tahu Anda tentang bagaimana kami membuat prototipe produk kami, yang pada akhirnya kami menempati posisi pertama.

Lebih dari 10 tim berpartisipasi dalam hackathon ini. Untung saja ada yang datang dari daerah lain. Tempat hackathon tersebut adalah kompleks “Kremlinsky on Pochain”, di mana foto-foto kuno Nizhny Novgorod digantung di dalamnya, bersama rombongan! (Saya ingatkan Anda bahwa saat ini kantor pusat Intel berlokasi di Nizhny Novgorod). Peserta diberikan waktu 26 jam untuk menulis kode, dan pada akhirnya mereka harus mempresentasikan solusinya. Keunggulan tersendiri adalah hadirnya sesi demo untuk memastikan semua yang direncanakan benar-benar terlaksana dan tidak tinggal ide dalam presentasi. Merchandise, snack, makanan, semuanya juga ada di sana!

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

Pemilihan tugas

Salah satu bagian tersulit dalam mempersiapkan hackathon bentuk bebas adalah memilih tantangan. Kami segera memutuskan untuk menghadirkan sesuatu yang belum ada dalam produk, karena pengumuman tersebut menyatakan bahwa hal ini sangat disambut baik.

Setelah dianalisis model, yang disertakan dalam produk dalam rilis saat ini, kami sampai pada kesimpulan bahwa sebagian besar dari produk tersebut memecahkan berbagai masalah penglihatan komputer. Selain itu, sangat sulit untuk menemukan masalah di bidang visi komputer yang tidak dapat diselesaikan dengan menggunakan OpenVINO, dan bahkan jika masalah tersebut dapat ditemukan, sulit untuk menemukan model terlatih di domain publik. Kami memutuskan untuk menggali arah lain - menuju pemrosesan ucapan dan analisis. Mari kita pertimbangkan tugas menarik untuk mengenali emosi dari ucapan. Harus dikatakan bahwa OpenVINO sudah memiliki model yang menentukan emosi seseorang berdasarkan wajahnya, tetapi:

  • Secara teori, dimungkinkan untuk membuat algoritma gabungan yang akan bekerja pada suara dan gambar, yang akan memberikan peningkatan akurasi.
  • Kamera biasanya memiliki sudut pandang yang sempit; diperlukan lebih dari satu kamera untuk mencakup area yang luas; suara tidak memiliki batasan seperti itu.

Mari kita kembangkan idenya: mari kita jadikan ide untuk segmen retail sebagai dasar. Anda dapat mengukur kepuasan pelanggan di kasir toko. Jika salah satu pelanggan tidak puas dengan layanan dan mulai meninggikan nada suaranya, Anda dapat segera menghubungi administrator untuk meminta bantuan.
Dalam hal ini, kita perlu menambahkan pengenalan suara manusia, ini akan memungkinkan kita membedakan karyawan toko dari pelanggan dan menyediakan analisis untuk setiap individu. Selain itu, dimungkinkan untuk menganalisis perilaku karyawan toko itu sendiri, mengevaluasi suasana dalam tim, kedengarannya bagus!

Kami merumuskan persyaratan untuk solusi kami:

  • Ukuran kecil dari perangkat target
  • Operasi waktu nyata
  • Harga rendah
  • Skalabilitas yang mudah

Hasilnya, kami memilih Raspberry Pi 3 c sebagai perangkat target Intel NCS 2.

Di sini penting untuk mencatat satu fitur penting NCS - ini berfungsi paling baik dengan arsitektur CNN standar, tetapi jika Anda perlu menjalankan model dengan lapisan khusus di dalamnya, harapkan pengoptimalan tingkat rendah.

Hanya ada satu hal kecil yang harus dilakukan: Anda perlu mendapatkan mikrofon. Mikrofon USB biasa bisa digunakan, tetapi tidak akan terlihat bagus jika dipadukan dengan RPI. Namun bahkan di sini solusinya “terletak dekat”. Untuk merekam suara, kami memutuskan untuk menggunakan papan Voice Bonnet dari kit Perangkat Suara Google AIY, yang di dalamnya terdapat mikrofon stereo berkabel.

Unduh Raspbian dari Repositori proyek AIY dan unggah ke flash drive, uji apakah mikrofon berfungsi menggunakan perintah berikut (ini akan merekam audio berdurasi 5 detik dan menyimpannya ke file):

arecord -d 5 -r 16000 test.wav

Saya harus segera mencatat bahwa mikrofon sangat sensitif dan menangkap kebisingan dengan baik. Untuk mengatasinya, buka alsamixer, pilih Capture devices dan kurangi level sinyal input menjadi 50-60%.

OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi
Kami memodifikasi badan dengan file dan semuanya pas, Anda bahkan dapat menutupnya dengan penutup

Menambahkan tombol indikator

Saat membongkar AIY Voice Kit, kami ingat bahwa ada tombol RGB, yang lampu latarnya dapat dikontrol oleh perangkat lunak. Kami mencari “Google AIY Led” dan menemukan dokumentasi: https://aiyprojects.readthedocs.io/en/latest/aiy.leds.html
Mengapa tidak menggunakan tombol ini untuk menampilkan emosi yang dikenali, kami hanya memiliki 7 kelas, dan tombol tersebut memiliki 8 warna, cukup!

Kami menghubungkan tombol melalui GPIO ke Voice Bonnet, memuat perpustakaan yang diperlukan (sudah diinstal di kit distribusi dari proyek AIY)

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

Mari kita buat dict di mana setiap emosi akan memiliki warna yang sesuai dalam bentuk Tuple RGB dan objek kelas aiy.leds.Leds, yang melaluinya kita akan memperbarui warnanya:

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 terakhir, setelah setiap prediksi emosi baru, kami akan memperbarui warna tombol sesuai dengannya (berdasarkan tombol).

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

OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi
Tombol, bakar!

Bekerja dengan suara

Kami akan menggunakan pyaudio untuk menangkap aliran dari mikrofon dan webrtcvad untuk menyaring kebisingan dan mendeteksi suara. Selain itu, kami akan membuat antrian yang akan kami tambahkan dan hapus kutipan suara secara asinkron.

Karena webrtcvad memiliki batasan pada ukuran fragmen yang disediakan - itu harus sama dengan 10/20/30ms, dan pelatihan model untuk mengenali emosi (seperti yang akan kita pelajari nanti) dilakukan pada kumpulan data 48kHz, kami akan menangkap potongan berukuran 48000×20ms/1000×1( mono)=960 byte. Webrtcvad akan mengembalikan Benar/Salah untuk masing-masing potongan ini, yang sesuai dengan ada atau tidaknya suara dalam potongan tersebut.

Mari kita terapkan logika berikut:

  • Kami akan menambahkan ke daftar potongan-potongan di mana ada suara; jika tidak ada suara, maka kami akan menambah penghitung potongan-potongan kosong.
  • Jika penghitung bongkahan kosong >=30 (600 ms), maka kita lihat ukuran daftar bongkahan yang terakumulasi; jika >250, maka kita tambahkan ke antrian; jika tidak, kita anggap panjangnya rekaman tidak cukup untuk dimasukkan ke model guna mengidentifikasi pembicara.
  • Jika penghitung bongkahan kosong masih < 30, dan ukuran daftar bongkahan yang terakumulasi melebihi 300, maka kami akan menambahkan fragmen ke antrian untuk prediksi yang lebih akurat. (karena emosi cenderung berubah seiring berjalannya waktu)

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

Saatnya mencari model terlatih di domain publik, buka github, Google, tapi ingat bahwa kami memiliki batasan pada arsitektur yang digunakan. Ini adalah bagian yang agak sulit, karena Anda harus menguji model pada data masukan Anda, dan sebagai tambahan, mengonversikannya ke format internal OpenVINO - IR (Representasi Menengah). Kami mencoba sekitar 5-7 solusi berbeda dari github, dan jika model pengenalan emosi langsung berfungsi, maka dengan pengenalan suara kami harus menunggu lebih lama - mereka menggunakan arsitektur yang lebih kompleks.

Kami fokus pada hal berikut:

Selanjutnya kita akan membahas tentang konversi model, dimulai dengan teori. OpenVINO mencakup beberapa modul:

  • Open Model Zoo, model yang dapat digunakan dan disertakan dalam produk Anda
  • Model Optimzer, berkat itu Anda dapat mengonversi model dari berbagai format kerangka kerja (Tensorflow, ONNX, dll) ke dalam format Representasi Menengah, yang akan kami kerjakan lebih lanjut
  • Mesin Inferensi memungkinkan Anda menjalankan model dalam format IR pada prosesor Intel, chip Myriad, dan akselerator Neural Compute Stick
  • Versi OpenCV paling efisien (dengan dukungan Mesin Inferensi)
    Setiap model dalam format IR dijelaskan oleh dua file: .xml dan .bin.
    Model dikonversi ke format IR melalui Model Optimizer sebagai 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 memungkinkan Anda memilih format data yang akan digunakan model tersebut. FP32, FP16, INT8 didukung. Memilih tipe data yang optimal dapat memberikan peningkatan kinerja yang baik.
    --input_shape menunjukkan dimensi data masukan. Kemampuan untuk mengubahnya secara dinamis tampaknya ada di C++ API, namun kami tidak menggali sejauh itu dan hanya memperbaikinya untuk salah satu model.
    Selanjutnya, mari kita coba memuat model yang sudah dikonversi dalam format IR melalui modul DNN ke OpenCV dan meneruskannya ke sana.

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

    Baris terakhir dalam hal ini memungkinkan Anda untuk mengarahkan perhitungan ke Neural Compute Stick, perhitungan dasar dilakukan pada prosesor, tetapi dalam kasus Raspberry Pi ini tidak akan berfungsi, Anda memerlukan tongkat.

    Selanjutnya, logikanya adalah sebagai berikut: kami membagi audio kami menjadi jendela dengan ukuran tertentu (bagi kami 0.4 detik), kami mengubah masing-masing jendela ini menjadi MFCC, yang kemudian kami masukkan ke grid:

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

    Selanjutnya, mari kita ambil kelas yang paling umum untuk semua windows. Solusi sederhana, tetapi untuk hackathon Anda tidak perlu memikirkan sesuatu yang terlalu rumit, hanya jika Anda punya waktu. Masih banyak pekerjaan yang harus kita lakukan, jadi mari kita lanjutkan - kita akan menangani pengenalan suara. Penting untuk membuat semacam database di mana spektogram suara yang direkam sebelumnya akan disimpan. Karena waktu tersisa sedikit, kami akan menyelesaikan masalah ini sebaik mungkin.

    Yaitu, kami membuat skrip untuk merekam kutipan suara (cara kerjanya sama seperti dijelaskan di atas, hanya jika disela dari keyboard, suara akan disimpan ke file).

    Mari mencoba:

    python3 voice_db/record_voice.py test.wav

    Kami merekam suara beberapa orang (dalam kasus kami, tiga anggota tim)
    Selanjutnya, untuk setiap rekaman suara kita melakukan transformasi fourier cepat, mendapatkan spektogram dan menyimpannya sebagai array numpy (.npy):

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

    Lebih detailnya ada di file create_base.py
    Hasilnya, saat kita menjalankan skrip utama, kita akan mendapatkan penyematan dari spektogram berikut di awal:

    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)

    Setelah menerima penyematan dari segmen yang dibunyikan, kita akan dapat menentukan milik siapa dengan mengambil jarak kosinus dari bagian tersebut ke semua suara di database (semakin kecil, semakin besar kemungkinannya) - untuk demo kita menetapkan ambang batas hingga 0.3):

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

    Pada akhirnya, saya ingin mencatat bahwa kecepatan inferensinya cepat dan memungkinkan penambahan 1-2 model lagi (untuk sampel yang berdurasi 7 detik, dibutuhkan 2.5 untuk inferensi). Kami tidak lagi mempunyai waktu untuk menambah model baru dan fokus pada penulisan prototipe aplikasi web.

    aplikasi web

    Poin penting: kami membawa router dari rumah dan mengatur jaringan lokal kami, ini membantu menghubungkan perangkat dan laptop melalui jaringan.

    Backend adalah saluran pesan ujung ke ujung antara bagian depan dan Raspberry Pi, berdasarkan teknologi websocket (http melalui protokol tcp).

    Tahap pertama adalah menerima informasi yang diproses dari raspberry, yaitu prediktor yang dikemas dalam json, yang disimpan dalam database di tengah perjalanannya sehingga statistik dapat dihasilkan tentang latar belakang emosional pengguna pada periode tersebut. Paket ini kemudian dikirim ke frontend, yang menggunakan langganan dan menerima paket dari titik akhir websocket. Seluruh mekanisme backend dibangun dalam bahasa golang; mekanisme ini dipilih karena cocok untuk tugas-tugas asinkron, yang dapat ditangani dengan baik oleh goroutine.
    Saat mengakses titik akhir, pengguna terdaftar dan dimasukkan ke dalam struktur, kemudian pesannya diterima. Baik pengguna maupun pesan dimasukkan ke dalam hub umum, dari mana pesan sudah dikirim lebih jauh (ke bagian depan berlangganan), dan jika pengguna menutup koneksi (raspberry atau depan), maka langganannya dibatalkan dan dia dihapus dari pusatnya.

    OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi
    Kami menunggu koneksi dari belakang

    Front-end adalah aplikasi web yang ditulis dalam JavaScript menggunakan perpustakaan React untuk mempercepat dan menyederhanakan proses pengembangan. Tujuan dari aplikasi ini adalah untuk memvisualisasikan data yang diperoleh dengan menggunakan algoritma yang berjalan di sisi back-end dan langsung di Raspberry Pi. Halaman tersebut memiliki perutean bagian yang diimplementasikan menggunakan router reaksi, tetapi halaman utama yang menarik adalah halaman utama, tempat aliran data berkelanjutan diterima secara real time dari server menggunakan teknologi WebSocket. Raspberry Pi mendeteksi suara, menentukan apakah suara itu milik orang tertentu dari database terdaftar, dan mengirimkan daftar kemungkinan ke klien. Klien menampilkan data terbaru yang relevan, menampilkan avatar orang yang kemungkinan besar berbicara melalui mikrofon, serta emosi yang dia ucapkan saat mengucapkan kata-kata tersebut.

    OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi
    Halaman beranda dengan prediksi terbaru

    Kesimpulan

    Tidak mungkin menyelesaikan semuanya sesuai rencana, kami tidak punya waktu, jadi harapan utama ada pada demo, bahwa semuanya akan berhasil. Dalam presentasinya mereka bercerita tentang bagaimana segala sesuatunya berjalan, model apa yang mereka ambil, permasalahan apa saja yang mereka temui. Berikutnya adalah bagian demo - para ahli berjalan mengelilingi ruangan secara acak dan mendekati setiap tim untuk melihat prototipe yang berfungsi. Mereka juga mengajukan pertanyaan kepada kami, semua orang menjawab bagian mereka, mereka meninggalkan web di laptop, dan semuanya benar-benar berfungsi seperti yang diharapkan.

    Izinkan saya mencatat bahwa total biaya solusi kami adalah $150:

    • Raspberry Pi 3 ~ $35
    • Google AIY Voice Bonnet (Anda dapat mengenakan biaya respeaker) ~$15
    • Intel NCS 2 ~$100

    Cara meningkatkan:

    • Gunakan registrasi dari klien - minta untuk membaca teks yang dihasilkan secara acak
    • Tambahkan beberapa model lagi: Anda dapat menentukan jenis kelamin dan usia melalui suara
    • Pisahkan suara yang terdengar bersamaan (diarisasi)

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

    OpenVINO hackathon: mengenali suara dan emosi di Raspberry Pi
    Lelah tapi kami bahagia

    Akhir kata saya ucapkan terima kasih kepada pihak penyelenggara dan peserta. Di antara proyek tim lain, kami secara pribadi menyukai solusi untuk memantau tempat parkir gratis. Bagi kami, ini adalah pengalaman yang sangat keren dalam mendalami produk dan pengembangan. Saya berharap semakin banyak acara menarik yang diadakan di daerah, termasuk yang bertemakan AI.

Sumber: www.habr.com

Tambah komentar