音頻數(shù)據(jù)建模全流程代碼示例:通過(guò)講話(huà)人的聲音進(jìn)行年齡預(yù)測(cè)(1)
來(lái)源:DeepHub IMBA
大多數(shù)人都熟悉如何在圖像、文本或表格數(shù)據(jù)上運(yùn)行數(shù)據(jù)科學(xué)項(xiàng)目。但處理音頻數(shù)據(jù)的樣例非常的少見(jiàn)。在本文中,將介紹如何在機(jī)器學(xué)習(xí)的幫助下準(zhǔn)備、探索和分析音頻數(shù)據(jù)。簡(jiǎn)而言之:與其他的形式(例如文本或圖像)類(lèi)似我們需要將音頻數(shù)據(jù)轉(zhuǎn)換為機(jī)器可識(shí)別的格式。
音頻數(shù)據(jù)的有趣之處在于您可以將其視為多種不同的模式:
可以提取高級(jí)特征并分析表格數(shù)據(jù)等數(shù)據(jù)。
可以計(jì)算頻率圖并分析圖像數(shù)據(jù)等數(shù)據(jù)。
可以使用時(shí)間敏感模型并分析時(shí)間序列數(shù)據(jù)等數(shù)據(jù)。
可以使用語(yǔ)音到文本模型并像文本數(shù)據(jù)一樣分析數(shù)據(jù)。
在本文中,我們將介紹前三種方法。首先看看音頻數(shù)據(jù)的實(shí)際樣子。
音頻數(shù)據(jù)的格式
雖然有多個(gè) Python 庫(kù)可以處理音頻數(shù)據(jù),但我們推薦使用 librosa。讓我們加載一個(gè) MP3 文件并繪制它的內(nèi)容。
# Import librosaimport librosa
# Loads mp3 file with a specific sampling rate, here 16kHzy, sr = librosa.load("c4_sample-1.mp3", sr=16_000)
# Plot the signal stored in 'y'from matplotlib import pyplot as pltimport librosa.display
plt.figure(figsize=(12, 3))plt.title("Audio signal as waveform")librosa.display.waveplot(y, sr=sr);
這里看到的是句子的波形表示。
1. 波形 - 信號(hào)的時(shí)域表示
之前稱(chēng)它為時(shí)間序列數(shù)據(jù),但現(xiàn)在我們稱(chēng)它為波形? 當(dāng)只看這個(gè)音頻文件的一小部分時(shí),這一點(diǎn)變得更加清晰。下圖顯示了與上面相同的內(nèi)容,但這次只有 62.5 毫秒。
我們看到的是一個(gè)時(shí)間信號(hào),它以不同的頻率和幅度在值 0 附近振蕩。該信號(hào)表示氣壓隨時(shí)間的變化,或揚(yáng)聲器膜(或耳膜)的物理位移 . 這就是為什么這種對(duì)音頻數(shù)據(jù)的描述也稱(chēng)為波形的原因。
頻率是該信號(hào)振蕩的速度。低頻例如 60 Hz 可能是低音吉他的聲音,而鳥(niǎo)兒的歌聲可能是 8000 Hz 的更高頻率。我們?nèi)祟?lèi)語(yǔ)言通常介于兩者之間。
要知道這個(gè)信號(hào)在單位時(shí)間內(nèi)從連續(xù)信號(hào)中提取并組成離散信號(hào)的采樣個(gè)數(shù),我們使用赫茲(Hz)來(lái)表示每秒的采樣個(gè)數(shù)。16'000 或 16k Hz表示美標(biāo)采集了16000次。我們?cè)谏蠄D中可以看到的 1'000 個(gè)時(shí)間點(diǎn)代表了 62.5 毫秒(1000/16000 = 0.0625)的音頻信號(hào)。
2. 傅里葉變換——信號(hào)的頻域表示
雖然之前的可視化可以告訴我們什么時(shí)候發(fā)生了(即 2 秒左右似乎有很多波形信號(hào)),但它不能真正告訴我們它發(fā)生的頻率。因?yàn)椴ㄐ蜗蛭覀冿@示了有關(guān)時(shí)間的信息,所以該信號(hào)也被稱(chēng)為信號(hào)的時(shí)域表示。
可以使用快速傅立葉變換,反轉(zhuǎn)這個(gè)問(wèn)題并獲得關(guān)于存在哪些頻率的信息,同時(shí)丟棄掉關(guān)于時(shí)間的信息。在這種情況下,信號(hào)表示被稱(chēng)為信號(hào)的頻域表示。
讓我們看看之前的句子在頻域中的表現(xiàn)。
import scipyimport numpy as np
# Applies fast fourier transformation to the signal and takes absolute valuesy_freq = np.abs(scipy.fftpack.fft(y))
# Establishes all possible frequency# (dependent on the sampling rate and the length of the signal)f = np.linspace(0, sr, len(y_freq))
# Plot audio signal as frequency information.plt.figure(figsize=(12, 3))plt.semilogx(f[: len(f) // 2], y_freq[: len(f) // 2])plt.xlabel("Frequency (Hz)")plt.show();
可以在此處看到大部分信號(hào)在 ~100 到 ~1000 Hz 之間(即 102 到 103 之間)。另外,似乎還有一些從 1'000 到 10'000 Hz 的內(nèi)容。
3. 頻譜圖
我們并不總是需要決定時(shí)域或頻域。使用頻譜圖同時(shí)表示這兩個(gè)領(lǐng)域中的信息,同時(shí)將它們的大部差別保持在最低限度。有多種方法可以創(chuàng)建頻譜圖,但在本文中將介紹常見(jiàn)的三種。
3a 短時(shí)傅里葉變換 (STFT)
這是之前的快速傅立葉變換的小型改編版本,即短時(shí)傅立葉變換 (STFT), 這種方式是以滑動(dòng)窗口的方式計(jì)算多個(gè)小時(shí)間窗口(因此稱(chēng)為“短時(shí)傅立葉”)的 FFT。
import librosa.display
# Compute short-time Fourier Transformx_stft = np.abs(librosa.stft(y))
# Apply logarithmic dB-scale to spectrogram and set maximum to 0 dBx_stft = librosa.amplitude_to_db(x_stft, ref=np.max)
# Plot STFT spectrogramplt.figure(figsize=(12, 4))librosa.display.specshow(x_stft, sr=sr, x_axis="time", y_axis="log")plt.colorbar(format="%+2.0f dB")plt.show();
與所有頻譜圖一樣,顏色代表在給定時(shí)間點(diǎn)給定頻率的量(響度/音量)。+0dB 是最響亮的,-80dB 接近靜音。在水平 x 軸上我們可以看到時(shí)間,而在垂直 y 軸上我們可以看到不同的頻率。
3b 梅爾譜圖
作為 STFT 的替代方案,還可以計(jì)算基于 mel 標(biāo)度的梅爾頻譜圖。這個(gè)尺度解釋了我們?nèi)祟?lèi)感知聲音音高的方式。計(jì)算 mel 標(biāo)度,以便人類(lèi)將由 mel 標(biāo)度中的 delta 隔開(kāi)的兩對(duì)頻率感知為具有相同的感知差異。
梅爾譜圖的計(jì)算與 STFT 非常相似,主要區(qū)別在于 y 軸使用不同的刻度。
# Compute the mel spectrogramx_mel = librosa.feature.melspectrogram(y=y, sr=sr)
# Apply logarithmic dB-scale to spectrogram and set maximum to 0 dBx_mel = librosa.power_to_db(x_mel, ref=np.max)
# Plot mel spectrogramplt.figure(figsize=(12, 4))librosa.display.specshow(x_mel, sr=sr, x_axis="time", y_axis="mel")plt.colorbar(format="%+2.0f dB")plt.show();
與 STFT 的區(qū)別可能不太明顯,但如果仔細(xì)觀察,就會(huì)發(fā)現(xiàn)在 STFT 圖中,從 0 到 512 Hz 的頻率在 y 軸上占用的空間比在 mel 圖中要大得多。
3c 梅爾頻率倒譜系數(shù) (MFCC)
梅爾頻率倒譜系數(shù) (MFCC) 是上面梅爾頻譜圖的替代表示。MFCC 相對(duì)于 梅爾譜圖的優(yōu)勢(shì)在于特征數(shù)量相當(dāng)少(即獨(dú)特的水平線(xiàn)標(biāo)度),通常約為 20。
由于梅爾頻譜圖更接近我們?nèi)祟?lèi)感知音高的方式,并且 MFCC 只有少數(shù)幾個(gè)分量特征,所以大多數(shù)機(jī)器學(xué)習(xí)從業(yè)者更喜歡 使用MFCC 以“圖像方式”表示音頻數(shù)據(jù)。但是對(duì)于某些問(wèn)題,STFT、mel 或波形表示可能會(huì)更好。
讓我們繼續(xù)計(jì)算 MFCC 并繪制它們。
# Extract 'n_mfcc' numbers of MFCCs components (here 20)x_mfccs = librosa.feature.mfcc(y, sr=sr, n_mfcc=20)
# Plot MFCCsplt.figure(figsize=(12, 4))librosa.display.specshow(x_mfccs, sr=sr, x_axis="time")plt.colorbar()plt.show();
數(shù)據(jù)清洗
現(xiàn)在我們更好地理解了音頻數(shù)據(jù)的樣子,讓我們可視化更多示例。
在這四個(gè)示例中,我們可以收集到有關(guān)此音頻數(shù)據(jù)集的更多問(wèn)題:
大多數(shù)錄音在錄音的開(kāi)頭和結(jié)尾都有一段較長(zhǎng)的靜默期(示例 1 和示例 2)。這是我們?cè)凇靶藜簟睍r(shí)應(yīng)該注意的事情。
在某些情況下,由于按下和釋放錄制按鈕,這些靜音期會(huì)被“點(diǎn)擊”中斷(參見(jiàn)示例 2)。
一些錄音沒(méi)有這樣的靜音階段,即一條直線(xiàn)(示例 3 和 4)。
在收聽(tīng)這些錄音時(shí),有大量背景噪音。
為了更好地理解這在頻域中是如何表示的,讓我們看一下相應(yīng)的 STFT 頻譜圖。
當(dāng)聽(tīng)錄音時(shí),可以觀察到樣本 3 具有覆蓋多個(gè)頻率的不同背景噪聲,而樣本 4 中的背景噪聲相當(dāng)恒定。這也是我們?cè)谏蠄D中看到的。樣本 3 在整個(gè)過(guò)程中都非常嘈雜,而樣本 4 僅在幾個(gè)頻率上(即粗水平線(xiàn))有噪聲。我們不會(huì)詳細(xì)討論如何消除這種噪音,因?yàn)檫@超出了本文的范圍。
但是讓我們研究一下如何消除此類(lèi)噪音并修剪音頻樣本的“捷徑”。雖然使用自定義過(guò)濾函數(shù)的更手動(dòng)的方法可能是從音頻數(shù)據(jù)中去除噪聲的最佳方法,但在我們的例子中,將推薦使用實(shí)用的 python 包 noisereduce。
import noisereduce as nrfrom scipy.io import wavfile
# Loop through all four samplesfor i in range(4):
# Load audio file fname = "c4_sample-%d.mp3" % (i + 1) y, sr = librosa.load(fname, sr=16_000)
# Remove noise from audio sample reduced_noise = nr.reduce_noise(y=y, sr=sr, stationary=False)
# Save output in a wav file as mp3 cannot be saved to directly wavfile.write(fname.replace(".mp3", ".wav"), sr, reduced_noise)
聆聽(tīng)創(chuàng)建的 wav 文件,可以聽(tīng)到噪音幾乎完全消失了。雖然我們還引入了更多的代碼,但總的來(lái)說(shuō)我們的去噪方法利大于弊。
對(duì)于修剪步驟,可以使用 librosa 的 .effects.trim() 函數(shù)。每個(gè)數(shù)據(jù)集可能需要一個(gè)不同的 top_db 參數(shù)來(lái)進(jìn)行修剪,所以最好進(jìn)行測(cè)試,看看哪個(gè)參數(shù)值好用。在這個(gè)的例子中,它是 top_db=20。
# Loop through all four samplesfor i in range(4):
# Load audio file fname = "c4_sample-%d.wav" % (i + 1) y, sr = librosa.load(fname, sr=16_000)
# Trim signal y_trim, _ = librosa.effects.trim(y, top_db=20)
# Overwrite previous wav file wavfile.write(fname.replace(".mp3", ".wav"), sr, y_trim)
現(xiàn)在讓我們?cè)倏匆幌虑謇砗蟮臄?shù)據(jù)。
看樣子好多了。
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。