Edited at

DeepLearningで楽曲特徴量を抽出し、タグを予測する

More than 1 year has passed since last update.


モチベーション

従来の音楽推薦技術は協調フィルタリングを用いることが多かったようですが、協調フィルタリングではマイナーな曲、新曲など「ユーザーからの評価が集まらない」作品をうまく扱えないという欠点があります。

音楽推薦技術のもう一つのアプローチ、「楽曲特徴量を抽出して推薦に活かす」という方法ならば、前述の問題を回避することができそうです。

そこで'End-to-end learning for music audio'1という論文を使って勉強しようとしたところソースコードが見つからなかったので、自力で再現して「DeepLearningで楽曲特徴量を抽出し、タグを予測する」タスクを行うことにしました。(なお、一部作業を変更し、完全な再現ではないことをご了承くださいませ。)

音楽をPython, DeepLearningで処理する記事があまり見つからなかったので、何かの参考になれば幸いです。


データセットの入手

MagnaTagATune Dataset2を使います。1曲につき29秒の楽曲が25863曲、188個のタグとセットになっています。

# MP3データの入手

$ wget http://mi.soi.city.ac.uk/datasets/magnatagatune/mp3.zip.001
$ wget http://mi.soi.city.ac.uk/datasets/magnatagatune/mp3.zip.002
$ wget http://mi.soi.city.ac.uk/datasets/magnatagatune/mp3.zip.003

# 分割zipファイルを統合、解凍する
$ cat mp3.zip* > ~/music.zip
$ unzip music.zip

# タグデータの入手
$ wget http://mi.soi.city.ac.uk/datasets/magnatagatune/annotations_final.csv


MP3をNumpyで扱えるようにする


pydubのインストール

通常音声認識やMIR(music information retrieval)で使われる音声特徴量は、RAWデータに特徴量抽出を施した後のメル周波数ケプストラムなどです。しかしこの論文では、RAWデータをそのまま音声特徴量として用いています。画像と同様、生のデータをDeepLearningにぶっ込んで自動で特徴量抽出させようって魂胆なわけです。

MP3をRAWに変換するために、pydubというパッケージを使いました。libavかffmpeg(音声のエンコード、デコードをしてくれるらしい)も必要です。詳しくは公式のGithub

$ pip install pydub

# macの場合
$ brew install libav --with-libvorbis --with-sdl --with-theora

# linuxの場合
$ apt-get install libav-tools libavcodec-extra-53

また、私のubuntu環境では公式の方法が効かなかったので、こちらの記事を参考にしました。


ファイルのインポートとndarrayへの変換

mp3ファイルのパスを引数にndarrayを生成する以下の関数を定義しておきましょう。

import numpy as np

from pydub import AudioSegment

def mp3_to_array(file):

# MP3からRAWへの変換
song = AudioSegment.from_mp3(file)

# RAWからbytestring型への変換
song_data = song._data

# bytestringからNumpy配列への変換
song_arr = np.fromstring(song_data, np.int16)

return song_arr


データセットの準備


楽曲タグ(y)の準備

先ほどダウンロードしたタグデータを読み込みましょう。

また、以下の2点にご留意ください。


  • タグをよく使われる50個に限定していること

  • メモリに乗り切らないのでサンプルを3000個に限定していること

import pandas as pd

tags_df = pd.read_csv('annotations_final.csv', delim_whitespace=True)
tags_df = tags_df.sample(frac=1)
tags_df = tags_df[:3000]

top50_tags = tags_df.iloc[:, 1:189].sum().sort_values(ascending=False).index[:50].tolist()
y = tags_df[top50_tags].values


RAWデータ(X)の準備


  • tags_dfにmp3ファイルへのパスが含まれているので利用します。

  • Xは[samples(曲数), features, channel(今回は1)]にreshapeしておきます。

  • RAWデータは16kHzなので、1秒あたり16000個、約30秒で465984個のfeaturesを持ちます

  • 元論文では音源を3秒に区切って訓練させていましたが、面倒なので30秒のままぶっ込みます。

files = tags_df.mp3_path.values

X = np.array([ mp3_to_array(file) for file in files ])
X = X.reshape(X.shape[0], X.shape[1], 1)


訓練データ、テストデータの準備

from sklearn.model_selection import train_test_split

random_state = 42

train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2, random_state=random_state)


学習&テスト(7/9 改正)


モデル構築

Kerasを使ってみました。

元の論文とは違ってxの次元が465984次元と長いので、少しだけ層を深く積みます。


import keras
from keras.models import Model
from keras.layers import Dense, Flatten, Input
from keras.layers import Conv1D, MaxPooling1D

features = train_X.shape[1]

x_inputs = Input(shape=(features, 1), name='x_inputs') # (特徴量数, チャネル数)
x = Conv1D(128, 256, strides=256,
padding='valid', activation='relu') (x_inputs)
x = Conv1D(32, 8, activation='relu') (x) # (チャネル数, フィルタの長さ )
x = MaxPooling1D(4) (x) # (フィルタの長さ)
x = Conv1D(32, 8, activation='relu') (x)
x = MaxPooling1D(4) (x)
x = Conv1D(32, 8, activation='relu') (x)
x = MaxPooling1D(4) (x)
x = Conv1D(32, 8, activation='relu') (x)
x = MaxPooling1D(4) (x)
x = Flatten() (x)
x = Dense(100, activation='relu') (x) #(ユニット数)
x_outputs = Dense(50, activation='sigmoid', name='x_outputs') (x)

model = Model(inputs=x_inputs, outputs=x_outputs)
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_X1, train_y, batch_size=600, epochs=50)


計算グラフ可視化

''' pngへ出力 ''' 

from keras.utils.visualize_util import plot
plot(model, to_file="music_only.png", show_shapes=True)

''' interactiveに可視化 '''
from IPython.display import SVG
from keras.utils.visualize_util import model_to_dot
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))

music_only.png


テスト

元の論文ではAUC: 0.87 ほどでしたが、今回の実験では0.66しか出ていません。

サンプルサイズを5分の1以下にしているので低く出ていまいますが、生の(かつ30秒のままの)音声データをそのまま食べさせてある程度予測が出来たのでよしとしましょう。

from sklearn.metrics import roc_auc_score

pred_y_x1 = model.predict(test_X1, batch_size=50)
print(roc_auc_score(test_y, pred_y_x1)) # => 0.668582599155


まとめ


  • 音声ファイルをndarrayに変換することができた。

  • 特徴量抽出なしのRAWファイルをぶっ込んでタグを予測することが出来た

  • もう少しで研究用環境が整うので、サンプル増やして試したいです。





  1. Sander Dieleman and Benjamin Schrauwen. End-to-end learning for music audio. In Acoustics, Speech and Signal Processing (ICASSP), 2014 IEEE International Conference on, pages 6964–6968. IEEE, 2014. 



  2. Edith Law, Kris West, Michael Mandel, Mert Bay and J. Stephen Downie (2009). Evaluation of algorithms using games: the case of music annotation. In Proceedings of the 10th International Conference on Music Information Retrieval (ISMIR)