Trigger Word Detection

trigger word detection چیست؟

Trigger word کلمه ای است که با گفتن آن شروع به صحبت با voice assistant می کنیم. در حقیقت voice assistant ها در حال شنیدن صدا هستند و زمانی که ما یک trigger word خاص را می گوییم فعال می شوند.
به عنوان مثال با گفتن “?Hey Siri, what’s the weather like today”، ابزار siri متعلق به شرکت apple سعی در پیدا کردن وضعیت هوای امروز را می کند. در حقیقت ما در اینجا به نحوه ی trigger شدن اینگونه assistant ها می پردازیم.

برخی از voice assistant ها و trigger word های مربوط به آن ها را در زیر می بینیم:

قبل از شروع نگاهی به کتابخانه های مورد نیاز می اندازیم:

Pydub

Pydub کتابخانه کار با صوت پایتون است. در زیر برخی اعمال پایه ای که می توان با این کتابخانه انجام داد را می بینیم:

Open a file

یک فایل صوتی را میتوان لود کرد(با فرمت های مختلف) در بالا فایل صوتی با فرمت wav را لود کردیم.

Slice audio

کتابخانه pydub با واحد میلی ثانیه کار می کند.

Make the beginning louder and the end quieter

با – یا + می توان volume صدا را کم یا زیاد کرد(بر حسب db).

(Concatenate audio (add one file to the end of another

ادغام دو بخش صوت(که در اینجا 10 ثانیه اول و 5 ثانیه آخر را ادغام کردیم).

AudioSegments are immutable

می توان صوت را برعکس کرد.

Repeat

با * میتوان تعداد تکرار را مشخص کرد.

(Save the results (again whatever ffmpeg supports

می توان خروجی را با هر فرمتی که ffmpeg پشتیبانی می کند، ذخیره کرد.

 

Trigger Word Detection

کد ها برگرفته از نوت بوک مربوط به کورس sequence models سایت coursera می باشد که از این آدرس قابل دسترس است.

همچنین ویدیو های مربوط به این آموزش را میتوانید از آدرس های 1 و 2 مشاهده کنید.

کتابخانه های لازم را import می کنیم.

( )Ipython.display.Audio تابع jupyter notebook برای نمایش فایل صوتی است.

Dataset

دیتاست شامل 3 پوشه است:
• پوشه صوت های trigger شدن که در اینجا کلمه activate است.
• پوشه صوت های negative برای negative sampling که شامل کلمات غیر trigger است.
• پوشه صوت های پس زمینه(background)

صوت های لازم را خودمان می سازیم، یعنی مثلا یک background را برداشته و روی آن کلمات trigger و negative را سوار می کنیم. (نمونه آن را در فایل صوتی “audio_examples/example_train.wav” می بینیم)

From audio recordings to spectrograms

ضبط صدا دقیقا چیست؟
فرض کنید میکروفون شما فرکانس نمونه برداری 44100 هرتز دارد(یعنی در هر ثانیه 44100 نمونه بر میدارد)، اگر 10 ثانیه صحبت کنید 441000 نمونه برداشته می شود.(بر اساس فشار هوای وارد شده بر میکروفون)
در این نوع representation دیتا ما تشخیص کلمه trigger سخت است و ما آن را به spectrogram تبدیل می کنیم.

با این تابع spectrogram صوت sample ما را بدست می آورد(محور x به ما time-step و محور y به ما فرکانس را نشان می دهد)
خروجی آن چنین است:

 

فرکانس ها را بازه بندی کردیم(مثلا در اینجا سبز نشانه شدت زیاد فرکانس و آبی نشانه شدت کم فرکانس است).
مقدار time-step به مقدار hyper parameter های spectrogram وابسته است. در اینجا 10 ثانیه طول صوت داشتیم و time-step ما مقدار𝑇𝑥=5511 دارد.(یک mapping بین time مورد نظر و time-step وجود دارد مثلا برای بدست آوردن ثانیه 5 ام میتوان 𝑇𝑥 را نصف کرد)

خروجی

قبل از تبدیل به spectrogram به اندازه 441000 نمونه داشتیم و پس از تبدیل dimension ما به (5511, 101) تغییر کرد. چون 5511 مقدار time-step ماست و در هر کدام 101 مقدار فرکانس داریم.

پس برای یک صوت 10 ثانیه ای مقادیر زیر را داریم:

  • 441000 تعداد نمونه هایی که میکروفون ذخیره می کند.
  • 5511=𝑇𝑥 خروجی spectrogram و تعداد time-step هایی است که به ورودی شبکه می دهیم.
  • n_freq تعداد فرکانسی است که در هر time-step است.
  • 1375=𝑇𝑦 خروجی شبکه

Generating a single training example

مراحل ساخت داده training:

  1. یک صوت background ده ثانیه ای به صورت رندوم برمیداریم.

  2. به صورت رندوم 0-4 صوت activate برداشته و به جاهای مختلف بدون همپوشانی اضافه(overlay) می کنیم.

  3. به صورت رندوم 0-2 صوت negative برداشته و به جاهای مختلف بدون همپوشانی اضافه(overlay) می کنیم.

تابع ()load_raw_audio از توابع فایل td_utils.py، فایل های Wav درون فولدر activates, negatives و background را می خواند و در با فرمت datastructure خود pydub در لیست هایی به همین نام ها append می کند.

در این صوت 10 ثانیه ای لیبل خروجی را صفر میگذاریم، فقط در هر جایی کلمه activate تمام می شود 50 time-step خروجی را 1 میکنیم.

Helper Functions

این تابع یک time segment رندوم برای ما جدا می کند. به این ترتیب که ما به عنوان ورودی طول segment خود را برحسب میلی ثانیه وارد می کنیم. خروجی یک tuple است که شامل نقطه شروع و پایان segment است.(نقطه ی شروع طوری انتخاب می شود که اگر با طول segment آرگمان جمع شود از 10 ثانیه طول کلی بیشتر نشود)

 

این تابع وضعیت همپوشانی segment time را با لیستی از segment time های قبلی چک می کند. در صورتی که همپوشانی داشته باشد، True برمیگرداند.(یادآوری: segment time یک tuple از نقطه ی شروع و پایان یک segment است)

 

این تابع سه ورودی یک background(صوت 10 ثانیه ای)، یک audio_clip(یک صوت activate/negative) و time segment های قبلی را می گیرد و audio_clip را روی background قرار می دهد. به این شکل که ابتدا طول صوت audio_clip را می گیرد و در جایی از background که امکان قرار دادن آن وجود داشته باشد را پیدا کرده و روی آن overlay می کند. Time segment مربوط به این audio_clip را به time segment اضافه میکند(که اگر صوت جدیدی را بخواهیم overlay کنیم مشکلی پیش نیاید)

 

بعد هر بار کلمه activate ما 50 لیبل خروجی را یک بگذاریم. این تابع با توجه به نقطه ی پایان segment در فاصله 10 ثانیه نقطه ی متناظر با آن را در طول بردار خروجی(1375) پیدا می کند و 50 تای بعد آن 1 میگذارد(شرط if برای این است که اگر مثلا نقطه انتهایی در بردار خروجی 1370 بود، تا 1374 را یک کند)

یک نمونه از plot اجرای این تابع می بینیم:

به کمک این تابع training set خود را می سازیم.این تابع یک صوت background، یک لیست از صوت های activate و یک لیست از صوت های negative می گیرد. صدای background را کم کرده و لیست 1375 تایی خروجی می سازد.به صورت رندوم بین 0-4 صوت activate و همچنین 0-2 صوت negative را انتخاب کرده و بر روی background به کمک helper function های ذکر شده overlay می کند. در صورت overlay کردن activate ها 50 لیبل خروجی باید یک شود.(تابع match_target_amplitude نیز برای استاندارد سازی volume صوت نهایی بکار میرود)پس خروجی نیز spectrogram صوت ورودی و لیبل های منتاظر است.

Full training set

برای ایجاد training set می توان تابع بالا را به میزان دلخواه ران کرد. در اینجا ما از یک دیتا preprocess شده استفاده میکنیم.
ورودی ما دارای shape روبروست: (101, 5511, 26)
یعنی ما 26 تا صوت داریم.

Development set

برای development set بهتر است دیتا را synthesize نکنیم(یعنی مثل روشی که برای درست کردن training set استفاده شد، کلمات را روی background سوار نکنیم). در اینجا این دیتاست جمع آوری شده واقعا صدای ضبط شده ی 10 ثانیه ای است که لیبل زده شده است. دلیل آن هم این است که به test set و دیتای واقعی نزدیک تر است.

shape آن (101, 5511, 25) است یعنی 25 صوت داریم.

Model

ساختار و summary شبکه بصورت زیر است:

در ابتدا به ورودی شبکه که 5511 تاست(یعنی خروجی spectrogram) یک کانولوشن 1 بعدی میزنیم با پارامتر های filter_size = 15 و stride = 4 که تعداد فیلتر ها 196 تا است.
طبق فرمول، بعد خروجی کانولوشن اینگونه حساب میشود:

(input-filter_size)/stride+1 = (5511-16)/4+1 = 1375

سپس دیتا را نرمال سازی می کنیم(در تمام لایه ها این روال را داریم) سپس به تابع ReLU پاس می دهیم و پس از آن به تابع dropout با مقدار 0.8 پاس می دهیم(در تمام لایه ها این روال را داریم).
در مرحله بعد به (128 تایی)GRU حالت stack شده پاس می دهیم(با پارامتر return_sequences=True).
در آخر نیز دیتا را به یک لایه dense و activation سیگموید میدهیم.
لازم به ذکر است که لایه dense بصورت TimeDitributed است که برای هر time step یک dense جدید تعریف نشود بلکه کلا یک dense داریم که پارمتر ها share شده اند(طول این لایه 129 تا است).

مدل را می سازیم.

Fit the model

می توان مدل را train کرد ولی در انجا ما از مدل pre-trained استفاده می کنیم.

مدل را با AdamOptimizer کامپایل کرده و یک epoch میزنیم.

Test the model

روی developent set به Accuray 95 درصد میرسیم.

Making Predictions

برای prediction از دو تابع زیر استفاده می کنیم:

این تابع یک فایل صوتی می گیرد و spectrogram آن را رسم می کند و پس از تغییر محور ها به  (shape (5511, 101 , تغییر کرده و dimension آن را expand میکنیم(چون بصورت minibatch به شبکه می دهیم) و آن را به عنوان ورودی شبکه میدهیم.
خروجی تابع predict بصورت (1, 1375, 1) است(اولی بدلیل minibatch و آخری بدلیل لایه سیگموید است).
در آخر مقادیر predict شده را رسم می کند.

این تابع یک صدای بوق را load می کند و درجایی که predict کرده که صدای activate را شنیده، این صدای بوق را overlay می کند.
این تابع دو هایپرپارامتر threshold و consecutive_timestep دارد که اولی آستانه ای برای خروجی سیگموند برای قبول کردن شرط activate بودن است و دومی به منظور این است که چندبار پشت هم صدای بوق را overlay نکنیم(که پس از overlay کردن آن را صفر می کنیم).

Test on dev examples

برای تست کردن فایل 2.wav را از پوشه development set بر میداریم.

در این فایل دوبار صورت activate شنیده شده است که دو بار peak داریم.