對音樂進行分類
目錄 |
準備數據
這次,我們的任務是對一批音樂進行分類。 音樂的訓練數據從這裡可以下載到。這個數據里包含了blue,classical 等十個音樂類別,每個音樂類別有一百首樣本。每個樣本都是一首歌的前30秒。不過這個數據集是.au格式的,我們需要先轉換成比較易於python處理的格式:wma。從這裡下載sox的windows安裝版本。安裝完之後,我們在windows的cmd窗口裡,可以採取寫命令的方式利用sox批量轉音樂文件的格式。
一個比較傻瓜的方式是
1.先把cmd命令移動到genres下的某個文件夾,例如jazz(使用"cd /."回c盤根目錄,使用"e:"跳轉到e盤,然後繼續用cd前往要去的文件夾);
2.然後「mkdir converted」來新建一個converted文件夾;
3.接著使用如下命令批量裝換jazz中的.au文件到converted文件夾中:
for %x in (*.au) do C:\sox-14-3-2\sox.exe %x E:\wulingfei\music_classification\genres\jazz\converted\%x.wav
在本練習中,我們只使用到jazz,classical,country, pop, rock, metal六個類型,所以只要在這六個文件夾下分別重複以上三個步驟就可以了。
音頻文件的頻譜圖
我們可以先把一個wma文件讀入python,然後繪製它的頻譜圖(spectrogram)來看看是什麼樣的。
from scipy.io import wavfile from matplotlib.pyplot import specgram import matplotlib.pyplot as plt sample_rate, X = wavfile.read("E:\wulingfei\music_classification\genres\jazz\converted\jazz.00000.au.wav") print sample_rate, X.shape specgram(X, Fs=sample_rate, xextent=(0,30)) plt.xlabel("time") plt.ylabel("frequency")
上圖就是一個jazz音樂樣本的頻譜圖。當然,我們也可以把每一種的音樂都抽一些出來列印頻譜圖以便比較,如下圖:
從肉眼就可以看出一些區分,金屬音樂的能量在各個頻率上都比較強,爵士則是分布很不均勻的。
def plotSpec(g,n): sample_rate, X = wavfile.read("D:/genres/"+g+"/converted/"+g+"."+n+".au.wav") specgram(X, Fs=sample_rate, xextent=(0,30)) plt.title(g+"_"+n[-1]) figure(num=None, figsize=(18, 9), dpi=80, facecolor='w', edgecolor='k') plt.subplot(6,3,1);plotSpec("classical","00001");plt.subplot(6,3,2);plotSpec("classical","00002") plt.subplot(6,3,3);plotSpec("classical","00003");plt.subplot(6,3,4);plotSpec("jazz","00001") plt.subplot(6,3,5);plotSpec("jazz","00002");plt.subplot(6,3,6);plotSpec("jazz","00003") plt.subplot(6,3,7);plotSpec("country","00001");plt.subplot(6,3,8);plotSpec("country","00002") plt.subplot(6,3,9);plotSpec("country","00003");plt.subplot(6,3,10);plotSpec("pop","00001") plt.subplot(6,3,11);plotSpec("pop","00002");plt.subplot(6,3,12);plotSpec("pop","00003") plt.subplot(6,3,13);plotSpec("rock","00001");plt.subplot(6,3,14);plotSpec("rock","00002") plt.subplot(6,3,15);plotSpec("rock","00003");plt.subplot(6,3,16);plotSpec("metal","00001") plt.subplot(6,3,17);plotSpec("metal","00002");plt.subplot(6,3,18);plotSpec("metal","00003") plt.tight_layout(pad=0.4, w_pad=0, h_pad=1.0)
什麼是快速(離散)傅里葉變換(FFT)?
FFT是一種數據處理技巧,它可以把time domain上的數據,例如一個音頻,拆成一堆基準頻率,然後投射到frequency domain上。 為了理解FFT,我們可以先生成三個音頻文件。在cmd環境下輸入
C:\sox-14-3-2\sox.exe --null -r 22050 sine_a.wav synth 0.2 sine 400 C:\sox-14-3-2\sox.exe --null -r 22050 sine_b.wav synth 0.2 sine 3000 C:\sox-14-3-2\sox.exe --combine mix --volume 1 sine_b.wav --volume 0.5 sine_a.wav sine_mix.wav
生成三個音頻文件。如果我們播放的話,會發現sine_a聲音比較低,sine_b聲音比較高,而sine_mix則混合了兩者。
figure(num=None, figsize=(12, 8), dpi=80, facecolor='w', edgecolor='k') plt.subplot(3,2,1) sample_rate, a = wavfile.read("E:/wulingfei/music_classification/sine_a.wav") specgram(a, Fs=sample_rate, xextent=(0,30)) plt.xlabel("time") plt.ylabel("frequency") plt.title("400 HZ sine wave") plt.subplot(3,2,2) fft_a = abs(scipy.fft(a)) specgram(fft_a) plt.xlabel("frequency") plt.ylabel("amplitude") plt.title("FFT of 400 HZ sine wave") plt.subplot(3,2,3) sample_rate, b = wavfile.read("E:/wulingfei/music_classification/sine_b.wav") specgram(b, Fs=sample_rate, xextent=(0,30)) plt.xlabel("time") plt.ylabel("frequency") plt.title("3000 HZ sine wave") plt.subplot(3,2,4) fft_b = abs(scipy.fft(b)) specgram(fft_b) plt.xlabel("frequency") plt.ylabel("amplitude") plt.title("FFT of 3000 HZ sine wave") plt.subplot(3,2,5) sample_rate, c = wavfile.read("E:/wulingfei/music_classification/sine_mix.wav") specgram(c, Fs=sample_rate, xextent=(0,30)) plt.xlabel("time") plt.ylabel("frequency") plt.title("Mixed sine wave") plt.subplot(3,2,6) fft_c = abs(scipy.fft(c)) specgram(fft_c) plt.xlabel("frequency") plt.ylabel("amplitude") plt.title("FFT of mixed sine wave") plt.tight_layout(pad=0.4, w_pad=0, h_pad=1.0)
figure(num=None, figsize=(9, 6), dpi=80, facecolor='w', edgecolor='k') sample_rate, X = wavfile.read("E:/wulingfei/music_classification/genres/jazz/converted/jazz.00000.au.wav") plt.subplot(2,1,1) specgram(X, Fs=sample_rate, xextent=(0,30)) plt.xlabel("time") plt.ylabel("frequency") plt.subplot(2,1,2) fft_X = abs(scipy.fft(X)) specgram(fft_X) plt.xlabel("frequency") plt.ylabel("amplitude") plt.tight_layout(pad=0.4, w_pad=0, h_pad=1.0)
使用頻域強度特徵來判別音樂
轉換完數據後,我們就可以使用頻域的特徵來判別音樂數據了。這裡我們考慮最簡單的分類器,例如KNN方法。
#---------prepare train data-------------------- genre_list = ["classical", "jazz", "country", "pop", "rock", "metal"] def getdata(g,n): ad="E:/wulingfei/music_classification/genres/"+g+"/converted/"+g+"."+"0000"+str(n)+".au.wav" sample_rate, X = wavfile.read(ad) fft_X = list(abs(scipy.fft(X)[:1000])) f=fft_X+[genre_list.index(g)] return f data=[] for g in genre_list: for n in range(10): data.append(getdata(g,n)) data=array(data) #-----------prepare test data------------------- sample_rate, test = wavfile.read("E:/wulingfei/music_classification/genres/metal/converted/metal.00080.au.wav") testdata=abs(scipy.fft(test))[:1000] #---------classify----------------------- def distance(p0, p1): return np.sum( (p0-p1)**2) def nn_classify(training_set, training_labels, new_example): dists = np.array([distance(t, new_example) for t in training_set]) da=zip(dists,training_labels) da=array(sorted(da, key=lambda x:x[0])) votes = da[:3,1] return votes nn_classify(data[:,:-1], data[:,-1], testdata)