LoginSignup
30
34

More than 5 years have passed since last update.

Kerasを使って音楽をジャンル分けしてみた

Posted at

概要

  • FolkとElectronicのジャンルで各500曲を用意
  • メル周波数ケプストラム係数 (MFCC) を使って学習モデルを作成
  • Kerasを使って学習・判定
  • 交差検証を行い、二値分類で精度は約85%程度

データの準備

フリーで音楽データを利用可能なFMA (Free Music Archive) というサービスがあります。
このFMAからさらに、8ジャンル x 1000曲を抽出したfma_smallというデータセットがあります。

このfma_smallから、違いが分かりやすいと思われる2ジャンル FolkElectronic の楽曲をそれぞれ500曲選択し実験に使いました。

1曲の長さは全て30秒となっています。

モデルの作成

モデルにはメル周波数ケプストラム係数 (MFCC) を用います。
MFCCについてはこちらが詳しく書かれています。

MFCCへの変換にはlibrosaを用いました。
特徴的なFolkとElectronicの曲のMFCCを可視化すると下図のようになります。

スクリーンショット 2018-01-03 23.16.22.png

スクリーンショット 2018-01-03 23.16.42.png

なんとなく違いがあるように思えます。

このままモデルとしても今回の実験ではうまくいかなかったため、縦軸の音の高さ毎に平均を取り1次元の配列にし、それをモデルとしました。
なので、今回のモデルには時間軸の要素はないことになります。

具体的には以下のプログラムでモデルを作成しました。


import os
import librosa
import numpy as np

def load(dir_path, label):
    n_mfcc = 20
    genre_x = np.zeros((0, n_mfcc))
    genre_y = np.zeros((0, 1), dtype='int')

    files = os.listdir(dir_path)
    for i, file in enumerate(files):
        file_path = dir_path + file
        y, sr = librosa.load(file_path)
        mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=n_mfcc)
        mean = np.mean(mfcc, axis = 1)
        genre_x = np.vstack((genre_x, mean))
        genre_y = np.vstack((genre_y, label))

        print(f'{i+1}/{len(files)} loaded: {file_path}')

    return genre_x, genre_y

if __name__ == '__main__':
    folk_x, folk_y = load('Folkのディレクトリパス', 0)
    electronic_x, electronic_y = load('Electronicのディレクトリパス', 1)

    X = np.r_[folk_x, electronic_x]
    Y = np.r_[folk_y, electronic_y]

    np.save('x.npy', X)
    np.save('y.npy', Y)

結果をx.npyy.npyとしてそれぞれファイル保存しています。

学習と評価

前節で作成したモデルを用いて交差検証を行います。


import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout
from sklearn.model_selection import train_test_split

x = np.load('x.npy')
y = np.load('y.npy')

x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=0.8)

model = Sequential()
model.add(Dense(256, activation='relu', input_shape=(20,)))
model.add(Dropout(0.25))
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=500, batch_size=128)

score = model.evaluate(x_test, y_test, batch_size=128)
print(f'loss: {score[0]}, accuracy: {score[1]}')

librosaのMFCC変換時の係数の次元がデフォルトで20だったので、入力の次元も20となっています。

結果

学習結果

Epoch 1/500
800/800 [==============================] - 0s 358us/step - loss: 5.3203 - acc: 0.5800
Epoch 2/500
800/800 [==============================] - 0s 35us/step - loss: 3.2174 - acc: 0.6875

...

Epoch 499/500
800/800 [==============================] - 0s 29us/step - loss: 0.0370 - acc: 0.9900
Epoch 500/500
800/800 [==============================] - 0s 30us/step - loss: 0.0067 - acc: 0.9975

学習がうまくいったことが分かります。

評価結果

200/200 [==============================] - 0s 106us/step
loss: 0.8637916135787964, accuracy: 0.8850000071525573

何回か試してみましたが、今回の実験では概ね85%前後となりました。

まとめ

FMAの音楽データを使ってジャンル分類をしました。

結果は二値分類で85%となりましたが、MFCC変換の調整や学習のさせ方次第で、もっと精度が出るような気がします。

今回は2ジャンルでの実験だったので、もっとジャンルを増やしてやってみたいですね。

30
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
34