對音樂進行分類

出自集智百科
跳轉到: 導覽搜尋

目錄

準備數據

這次,我們的任務是對一批音樂進行分類。 音樂的訓練數據從這裡可以下載到。這個數據里包含了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")

Music classification 1.png

上圖就是一個jazz音樂樣本的頻譜圖。當然,我們也可以把每一種的音樂都抽一些出來打印頻譜圖以便比較,如下圖: Music classification 2.png

從肉眼就可以看出一些區分,金屬音樂的能量在各個頻率上都比較強,爵士則是分布很不均勻的。

    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則混合了兩者。

Music classification 3.png

    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)

本文一開始的示例jazz數據經過FFT變形是這樣的: Music classification 4.png

    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)
個人工具
名字空間
動作
導覽
工具箱