با سیستم تشخیص گفتار آشنا شوید و مفاهیم آن را بشناسید
در هر سازمانی روزانه حجم زیادی از دادههای صوتی تولید میشوند. اگر این دادههای صوتی برای راهاندازی موتورهای هوش مصنوعی، انواع سیستم تشخیص گفتار و تجزیهوتحلیل در اختیار متخصص علم داده قرار گیرند، میتوانند اطلاعات راهبردی مهمی تولید کنند. سازمانهایی که به قدرت و اهمیت اطلاعات حاصل از دادههای صوتی آگاهاند از رونوشت مکالمات هوش مصنوعی
AI transcribed conversations برای بهبود آموزش کارکنان، ارتقای خدمات مشتریان و افزایش کیفیت تجربه آنها استفاده میکنند.
از سوی دیگر سازمانهایی هم هستند که قادر به بهرهگیری از این دادههای صوتی نیستند، زیرا (1) این دادهها را ضبط نمیکنند و یا (2) کیفیت دادههایی که ضبط کردهاند پایین است. این موانع کاربرد و قابلیتهای راهکارهای یادگیری ماشین را (که موتور هوش مصنوعی هستند) محدود میکنند. بنابراین نکتهی مهم ضبط همه دادههای ممکن با کیفیتی خوب است.
در این نوشتار قصد داریم پردازش دادههای صوتی را به صورت گام به گام توضیح دهیم که به خوانندگان در تجزیهوتحلیلهای مقدماتی کمک خواهد کرد. با این حال بهتر است قبل از شروع، با امواج صوتی و تکنیکهای مقدماتی پردازش سیگنال
Signal processing نیز آشنا شوید. برای مطالعه در مورد امواج صوتی به این لینک مراجعه کنید؛ در آن مقاله امواج صوتی را در سطح ابتدایی مورد بررسی قرار داده و تا حدی به کدکهای صوتی Audio codecs مختلف نیز پرداختهایم.
خواندن فایلهای صوتی
LIBROSA
LibROSA یک کتابخانه پایتون است که تقریباً همه ابزارهای مورد نیاز برای کار با دادههای صوتی را دارد. این کتابخانه غنی قابلیتهای فراوانی دارد که برخی از آنها عبارتاند از:
- بارگذاری و نمایش ویژگیهای یک فایل صوتی
- بازنماییهای طیفی Spectral representations
- استخراج ویژگی Feature extraction and manipulation
- تبدیلات زمان-فرکانس Time-frequency conversions
- قطعهبندی زمانی Temporal segmentation
- مدلسازی ترتیبی Sequential modeling
با توجه به اندازه بزرگ این کتابخانه در مورد همه کارکردها و خصوصیات آن صحبت نخواهیم کرد؛ بلکه فقط چند مورد را صرف آشنایی بیشتر توضیح میدهیم.
از این طریق میتوانید به سرعت کتابخانه LibROSA را نصب کنید.
pypi : pip install librosa conda : conda install -c conda-forge librosa
بارگذاری صوت در پایتون
کتابخانهی Librosa کدکهای صوتی زیادی را پشتیبانی میکند. با این حال فرمت متداول برای تجزیهوتحلیل صوتی فرمت .wav(lossless) است. بعد از اینکه libROSA را در نوتبوک jupyter خود نصب و فراخوانی کردید، میتوانید به راحتی با اجرای تابع ()librosa.load با ورودی file_path فایل صوتی را بخوانید.
تابع ()librosa.load —> دو خروجی دارد: 1. آرایهای از دامنه نوسانات An array of amplitudes و 2. نرخ نمونهبرداری Sampling rate نرخ نمونهبرداری به فرکانس نمونهبرداری اشاره دارد که هنگام ضبط صوت استفاده میشود. با مقداردهی آرگومان sr = None ، فایل صوتی با نرخ نمونهبرداری اصلی خود بارگذاری میشود. البته میتوانید نرخ نمونهبرداری را متناسب با نیاز خود مشخص کنید و libROSA میتواند نرخ نمونهبرداری سیگنال را کاهش downsampling یا افزایش upsampling دهد. به این تصویر دقت کنید:
در این تصویر، sampling_rate = 16k نشان میدهد صدا با فرکانس نمونهبرداری 16 هزار ضبط (نمونهبرداری) شده است. به عبارت دیگر هنگام ضبط این فایل صوتی 16000 دامنه در ثانیه ضبط کردیم. بنابراین اگر بخواهیم طول صوت را بدانیم، میتوانیم تعداد نمونهها (دامنهها) را بر نرخ نمونهبرداری تقسیم کنیم:
شما میتوانید این صدا را در نوتبوک Jupyter خود پخش کنید.
برنامه IPython ابزاری در اختیار ما گذاشته که به وسیله آن میتوانیم فایلهای صوتی را در نوتبوک پخش کنیم.
مصورسازی صوت
تا اینجا دامنه نوسانات و نرخ نمونهبرداری را از librosa دریافت کردیم. حالا میتوانیم به راحتی نمودار این دامنهها را برحسب زمان رسم کنیم. LibROSA تابع کمکی waveplot() را بدین منظور ارائه میدهد:
چنین شکلی بازنمایی حوزه زمانی Time-domain representation برای یک سیگنال نامیده میشود. این نمودار رسایی
loudness (دامنه نوسانات) موج صوتی را در طول زمان نشان میدهد؛ دامنه 0 نشاندهنده سکوت است. در تعریف امواج صوتی نیز دامنه نوسانات را دامنه حرکت ذرات هوا در نظر میگیریم که به خاطر تغییرات فشار ناشی از حرکت موج صوتی در اتمسفر، حرکات نوسانی از خود نشان میدهند.
این دامنهها به جز رسایی صوت، اطلاعات چندانی در اختیار ما قرار نمیدهند. برای درک بهتر سیگنال صوتی لازم است آن را به حوزه فرکانسی تبدیل کنیم. حوزه فرکانسی یک سیگنال، فرکانسهای متفاوت موجود در سیگنال را به ما نشان میدهد. تبدیل فوریه مفهومی ریاضیاتی است که یک سیگنال پیوسته Continuous signal را از حوزه زمانی به حوزه فرکانسی تبدیل میکنند.
تبدیل فوریه (FT)
سیگنال صوتی یک سیگنال پیچیده است که از چندین موج صوتی تک فرکانسی تشکیل شده است؛ این موجهای تک فرکانسی به صورت یک آشفتگی disturbance (تغییر فشار) در محیط رسانا با هم سفر میکنند. زمانی که صدا را ضبط میکنیم، در واقع برآیند دامنه نوسانات این امواج را ضبط میکنیم. این مفهوم، مفهومی ریاضیاتی است که میتواند یک سیگنال را به فرکانسهای تشکیلدهنده آن تجزیه کند. تبدیل فوریه علاوه بر به دست آوردن فرکانسهای موجود در سیگنال، مقدار هر فرکانس را نیز مشخص میکند.
تبدیل فوریه معکوس Inverse Fourier Transform دقیقاً نقطهمقابل تبدیل فوریه است؛ یعنی بازنمایی فرکانسی مربوط به یک سیگنال را به عنوان ورودی میگیرد و با انجام محاسبات ریاضیاتی سیگنال اصلی را میسازد.
در قسمت بعدی خواهید دید چطور برای تبدیل سیگنال صوتی به مؤلفههای فرکانسی آن از تبدیل فوریه استفاده کنیم.
تبدیل فوریه سریع (FFT)
تبدیل فوریه سریع یک الگوریتم ریاضیاتی است که تبدیل فوریه گسسته Discrete Fourier Transform (DFT) را برای یک دنباله خاص محاسبه میکند. تنها تفاوت بین تبدیل فوریه و تبدیل فوریه سریع این است که تبدیل فوریه روی سیگنال پیوسته کار میکند، اما FFT سیگنال گسسته را به عنوان ورودی میپذیرد. DFT یک دنباله (سیگنال گسسته) را به فرکانسهای تشکیلدهندهاش تجزیه میکند (درست مثل کاری که FT با یک سیگنال پیوسته انجام میدهد). اینجا دنبالهای از دامنه نوسانات داریم که از یک سیگنال صوتی پیوسته نمونهگیری شدهاند. الگوریتم DFT یا FFT میتواند حوزه زمانی این سیگنال گسسته را به حوزه فرکانسی Frequency-domain تبدیل کند.
استفاده از موج سینوسی ساده برای فهم بهتر FFT
برای اینکه به درک بهتری از خروجی FFT برسیم، یک موج سینوسی ساده Simple sine wave تولید میکنیم. کدی که در ادامه مشاهده میکنید یک موج سینوسی با sampling rate = 100 ، amplitude = 1 و frequency = 3 تولید میکند. مقادیر دامنه نوسانات هر 0.01 ثانیه (نرخ نمونهبرداری) محاسبه شده و در لیستی به نام y1 ذخیره میشوند. سپس برای محاسبه DFT سیگنال، مقادیر دامنههای گسسته را وارد الگوریتم FFT میکنیم.
اگر نمودار این مقادیر گسسته (y1) را رسم کنیم، به طوری که تعداد نمونهها روی محور x و مقدار دامنه نوسان روی محور y نمایش داده شود، یک موج سینوسی به دست میآوریم:
حال دنبالهای از دامنه نوساناتی داریم که در لیست y1 ذخیره شدهاند. این توالی را وارد الگوریتم FFT میکنیم که در کتابخانه spicy پیادهسازی شده است. خروجی الگوریتم FFT لیست yf است که مقادیر مختلط دامنههای نوسانات فرکانسهای سیگنال Complex-valued amplitudes of the frequencies را نشان میدهد. نیمه اول این لیست فرکانسهای مثبت Positive-frequency-terms و نیمه دوم فرکانسهای منفی Negative-frequency-terms را تولید میکند. شما میتوانید هرکدام از این نیمهها را انتخاب کنید و قدر مطلق بازنمایی فرکانسهای موجود در سیگنال را محاسبه کنید. تابعی که در این قسمت میبینید نمونهها را به عنوان ورودی گرفته و نمودار فرکانسها را رسم میکند:
در نمودار پایین، فرکانسهای رسم شده برای موج سینوسی که با استفاده از تابع fft_plot تولید شد را مشاهده میکنید. همانطور که میبینید این نمودار به وضوح مقدار تکفرکانسی که در موج سینوسی داریم که همان 3 است را را نشان میدهد. علاوه بر این دامنه مربوط به فرکانس را نیز نشان دهد که مقدارش برای موج سینوسی ما برابر با 1 بود.
برای بررسی خروجی FFT برای سیگنالی که بیش از یک فرکانس دارد، یک موج سینوسی دیگر میسازیم. این بار از sampling rate = 100 ، amplitude = 2 و frequency value = 11 استفاده میکنیم. کدی که در ادامه مشاهده میکنید سیگنال را تولید کرده و موج سینوسی موردنظر را رسم میکند:
شکل پایین موج سینوسی که از تابع بالا تولید شد را نشان میدهد. اگر نرخ نمونهبرداری را افزایش میدادیم نمودار هموارتری داشتیم. اما نرخ نمونهبرداری را روی 100 نگه داشتیم زیرا قصد داریم این سیگنال را به موج سینوسی قبلی اضافه کنیم.
بدیهی است تابع FFT یک single spike با frequency = 11 برای این موج سینوسی نشان میدهد، اما سؤال این است که خروجی این تابع برای مجموع دو سیگنال با نرخ نمونهبرداری یکسان و فرکانس و مقدار دامنه نوسان متفاوت چه خواهد بود. در کادر پایین، دنباله y3 نشاندهنده سیگنال برآیند است.
اگر سیگنال y3 را رسم کنیم، چنین شکلی خواهد داشت:
اگر این توالی (y3 ) به عنوان ورودی تابع fft_plot اعمال کنیم، نمودار فرکانسی پایین را تولید میکند. در این نمودار دو spike برای دو فرکانس حاضر در سیگنال برآیند مشاهده میکنید. پس میتوان گفت حضور یک فرکانس روی فرکانس دیگری که در سیگنال وجود دارد تأثیری نمیگذارد. علاوه بر این باید به یک نکته دیگر نیز توجه داشت: مقدار فرکانسها با موجهای سینوسیِ تولیدشده همراستا Magnitudes of the frequencies in line with sine wave هستند.
استفاده از FFT برای سیگنالهای صوتی
حال که دیدیم الگوریتم FFT چطور همه فرکانسهای موجود در یک سیگنال خاص را مشخص میکند، میخواهیم سیگنال صوتی اصلی را به عنوان ورودی تابع اعمال کنیم. بدین منظور از همان قطعه صوتی استفاده میکنیم که پیشتر با نرخ نمونهبرداری 16000 در python بارگذاری کردیم.
به نمودار فرکانسی پایین نگاه کنید. این سیگنال 3 ثانیهای از هزاران فرکانس مختلف تشکیل شده است. مقادیر این فرکانسها (>2000) بسیار کوچک است، به نحوی که احتمالاً بیشتر آنها در معرض نویز noise قرار گرفتهاند. ما اینجا فرکانسهایی را در تصویر آوردهایم که در بازهی 0 تا 8 کیلوهرتز قرار میگیرند؛ از آنجایی که سیگنال ما با نرخ نمونهبرداری 16 هزار نمونهبرداری شده و براساس قضیهی نمونهبرداری Nyquist ، تنها باید فرکانسهای (16000/2) را داشته باشد.
فرکانسهای قوی مقداری بین 0 تا 1 کیلوهرتز دارند، چون این قطعه صدا مربوط به گفتار یک فرد بود و بازهی فرکانسی غالب در گفتار معمولی انسان همین مقدار است.
بعد از دریافت فرکانسها، نوبت به اطلاعات زمانی میرسد.
طیفنگار
چرا از طیفنگار استفاده میکنیم؟
فرض کنید یک مسئله بازشناسی گفتار در دست دارید؛ برای این مسئله یک فایل صوتی دارید که در آن یک نفر یک عبارت را به زبان میآورد (برای مثال «حال شما چطوره؟»). سیستم بازشناسی باید بتواند سه کلمه موجود در این عبارت را پیشبینی کند (1.حال، 2.شما ، 3. چطوره). اگر به خاطر داشته باشید در تمرین قبلی سیگنال را به مقادیر فرکانسهای آن تجزیه کردیم، این مقادیر ویژگی سیستم بازشناسی ما به شمار میروند.
سپس الگوریتم FFT را روی سیگنال اجرا کردیم، این الگوریتم تنها مقادیر فرکانسی را به ما ارائه داد و بدین ترتیب اطلاعات زمانی مربوطه را از دست دادیم. اکنون اگر تنها از این فرکانسها به عنوان ویژگی استفاده کنیم، سیستم ما نمیتواند تشخیص دهد کدام کلمه اول میآید. بنابراین باید راه دیگری برای محاسبه ویژگیها پیدا کنیم، به نحوی که هم مقادیر فرکانسی و هم اطلاعات زمان مشاهده هرکدام از این فرکانسها را در اختیار ما قرار دهد. در این مرحله است که طیفنگارها وارد کار میشوند.
بازنمایی تصویری فرکانسها برای یک سیگنال خاص به همراه زمان را طیفنگار مینامیم. در نمودار بازنمایی طیفنگار، یک محور نشاندهنده زمان و محور دوم نشاندهنده فرکانسهاست و رنگها نیز مقدار (دامنهی) نوسانات مربوط به یک فرکانس مشاهده شده در یک زمان خاص را نمایش میدهند. تصویری که پایین مشاهده میکنید طیفنگار همان سیگنال صوتی را به نمایش گذاشته که پیشتر در موردش صحبت کردیم. هرچه رنگها روشنتر باشند، فرکانس قویتر است. همانطور که در نمودار FFT دیدیم، فرکانسهای کوچکتر (بین 0 تا 1 کیلوهرتز) قوی (روشن) هستند.
ساخت و مصورسازی طیفنگار
هدف اصلی، تجزیه سیگنال صوتی به فریمها (پنجرهها)ی کوچکتر و محاسبه DFT (یا FFT) برای هر پنجره است. بدین طریق خواهیم توانست فرکانس هر پنجره را به دست آوریم، عدد مربوط به هر پنجره نیز زمان را نشان میدهد؛ یعنی پنجره 1 اول میآید، پنجره 2 بعد از آن و … . بهتر است برای از دست ندادن فرکانسهای جدید، پنجرهها را طوری کنار هم قرار دهیم که همپوشانی overlapping داشته باشند. اندازه پنجره به مسئله شما بستگی دارد.
برای یک مسئله عادی از یک سیستم تشخیص گفتار، پیشنهاد میشود اندازه پنجره بین 20 تا 30 میلیثانیه باشد. انسان نمیتواند بیش از 1 واج در این پنجره به زبان بیاورد؛ به همین دلیل اگر پنجره را تا این حد کوچک نگه داریم میتوانیم مطمئن باشیم هیچ واجی را حین ردهبندی از دست نمیدهیم. همپوشانی فریم (پنجره)ها میتواند از 25% تا 75% باشد و بستگی به نیاز شما دارد؛ در حالت عادی برای مسائل بازشناسی گفتار این مقدار را روی 50% نگه میدارند.
ما در محاسبات طیفنگاری خود، طول پنجره را 20 میلیثانیه و مقدار همپوشانی بین پنجرهها را روی 50% تنظیم میکنیم. از آنجایی که سیگنال ما با فرکانس 16هزار نمونهگیری شده، هر پنجره دامنه خواهد داشت. با توجه به مقدار همپوشانی 50% باید () 160 دامنه پیش برویم تا به پنجره بعدی برسیم. بنابراین اندازه گام 160 خواهد بود.
به تابع طیفنگار که در شکل بعدی نشان داده شده توجه کنید؛ در خط 18 کد، یک پنجره وزندهی Weighting window (Hanning) میسازیم و قبل از اینکه آن را وارد تابع FFT کنیم (خط 20)، در دامنه ضرب میکنیم. کاربرد پنجره وزندهی این است که قبل از وارد کردن سیگنال کوچک (سیگنالی کوچک از فریمی کوچک) به الگوریتم DFT، ناپیوستگی discontinuity آن را مدیریت کنیم.
تابع پایتون برای محاسبهی ویژگیهای طیفنگار
def spectrogram(samples, sample_rate, stride_ms = 10.0, window_ms = 20.0, max_freq = None, eps = 1e-14): stride_size = int(0.001 * sample_rate * stride_ms) window_size = int(0.001 * sample_rate * window_ms) # Extract strided windows truncate_size = (len(samples) - window_size) % stride_size samples = samples[:len(samples) - truncate_size] nshape = (window_size, (len(samples) - window_size) // stride_size + 1) nstrides = (samples.strides[0], samples.strides[0] * stride_size) windows = np.lib.stride_tricks.as_strided(samples, shape = nshape, strides = nstrides) assert np.all(windows[:, 1] == samples[stride_size:(stride_size + window_size)]) # Window weighting, squared Fast Fourier Transform (fft), scaling weighting = np.hanning(window_size)[:, None] fft = np.fft.rfft(windows * weighting, axis=0) fft = np.absolute(fft) fft = fft**2 scale = np.sum(weighting**2) * sample_rate fft[1:-1, :] *= (2.0 / scale) fft[(0, -1), :] /= scale # Prepare fft frequency list freqs = float(sample_rate) / window_size * np.arange(fft.shape[0]) # Compute spectrogram feature ind = np.where(freqs <= max_freq)[0][-1] + 1 specgram = np.log(fft[:ind, :] + eps) return specgram
خروجی الگوریتم FFT لیستی از اعداد مختلط Complex numbers (size = window_size /2) است که دامنه فرکانسهای مختلف داخل هر پنجره را نشان میدهند. برای پنجرهای با اندازهی 320، لیستی شامل 160 دامنه از بازه فرکانس به دست خواهیم آورد که فرکانسهای 0 تا 8 کیلوهرتز را نشان میدهند (زیرا در این مثال نرخ نمونهبرداری ما 16هزار است).
در قدمهای بعد، قدر مطلق دامنههای مختلط محاسبه و نرمالسازی میشوند. ماتریکس دوبعدی که در انتهای این فرآیند به دست میآوریم همان طیفنگار ماست. ستونها و ردیفهای این ماتریکس شماره فریم (پنجره) و بازه فرکانس را نشان میدهند؛ مقادیر ماتریس نیز نشاندهنده قدرت فرکانسها هستند.
سیستم تشخیص گفتار با استفاده از ویژگیهای طیفنگار
تا اینجا فهمیدیم طیفنگار یک ماتریس 2بُعدی است که مقدار فرکانسهای یک سیگنال را در طول زمان نشان میدهد و با نحوه تولید آن نیز آشنا شدیم. حالا میخواهیم این طیفنگار را یک تصویر در نظر بگیریم؛ در این راستا فایل صوتی را که در دست داشتیم به تصویر زیر تبدیل میکنیم:
بدین ترتیب مسئلهای که در دست داشتیم اکنون یک مسئلهی دستهبندی تصویر Image classification است. این تصویر عبارت خواندهشده را از چپ به راست از نظر زمانی نشان میدهد. همچنین میتوانید این تصویر را عبارتی در نظر بگیرید که از چپ به راست نوشته شده است و تنها کاری که باید انجام دهید تشخیص حروف الفبا در آن است.
میتوان مدل یادگیری عمیق را با استفاده از بانک دادهای از متون انگلیسی آموزش داد و یک سیستم تشخیص گفتار ساخت. اینجا دو پایگاه داده محبوب را معرفی میکنیم که میتوانید برای این کار مورد استفاده قرار دهید:
Popular open source datasets — 1. LibriSpeech ASR corpus 2. Common Voice Massively-Multilingual Speech Corpus
مقالات پایین نیز معماریهای محبوب یادگیری عمیق را معرفی میکنند:
- Wave2Lettter (Facebook Research)
- Deep Speech, Deep Speech 2 and Deep Speech 3(Baidu Research)
- Listen, Attend and Spell (Google Brain)
- JASPER (NVIDIA)
نتیجهگیری
این مقاله به مخاطبان نحوه کار با دادههای صوتی را میآموزد و چند تکنیک تجزیهوتحلیل صدا را از سطح مقدماتی توضیح میدهد. علاوه بر این، قدم اول ساخت سیستم تشخیص گفتار را نیز ارائه میدهد. با اینکه پژوهشهای مذکور حاکی از نتایج امیدوارکننده سیستم تشخیص گفتار هستند، بسیاری از محققان معتقدند مسئله انواع سیستم تشخیص گفتار هنوز مسئلهای حلنشده به شمار میرود؛ زیرا:
- انواع سیستم تشخیص گفتار که پژوهشگران معرفی کردهاند خیلی بزرگ (پیچیده) هستند که آموزش و کاربرد آنها را با مشکل روبرو میکند.
- سیستمها تشخیص گفتار برای شناسایی همزمان چندین نفر عملکرد خوبی ندارند.
- انواع سیستم تشخیص گفتار روی اصوات با کیفیت پایین عمکلرد خوبی از خود نشان نمیدهند.
- تا حد زیادی به لهجه گوینده حساس هستند و به همین دلیل لازم است در آموزش آنها از لهجههای مختلف استفاده شود.
در حوزه پردازش گفتار فرصتها و ظرفیت زیاد برای پیشرفت و توسعه، از آمادهسازی دادهها Data preparation (خلق ویژگیهای بهتر) گرفته تا معماری مدلها (ارائهی مدلی قویتر و مقیاسپذیرتر از معماریهای یادگیری عمیق) دارد.