CNNで好きなアーティストを分類させてAIに良さを伝えたい

初めまして、AIdemy研修生のまえくら(@R25fgtSx)です。

皆さんは好きなアーティストもちろんいますよね!?

しかし、自分の好きなアーティストが全て同じ人に会ったことのある人はめちゃくちゃ少ないと思います.

そこで僕は何でも理解してくれる?コンピュータにその良さを伝えたいと思いました。伝えると言っても、楽曲データを学習させて分類させるだけですが…

ここで、僕の好きなアーティスト5組を紹介します(唐突)。

  1. AAA
    f:id:RyoMa:20180907193845j:plain
    https://avex.jp/aaa/aaak/
  2. UNISON SQUARE GARDEN
    f:id:RyoMa:20180907194015j:plain
    https://realsound.jp/2016/05/post-7509.html

  3. f:id:RyoMa:20180907194153j:plain
    https://www.johnnys-net.jp/page?id=artistTop&artist=10
  4. NEWS
    f:id:RyoMa:20180907194352j:plain
    https://kyun2-girls.com/archives/164
  5. SEKAI NO OWARI
    f:id:RyoMa:20180907194324j:plain
    https://abematimes.com/posts/2675644

これでいかに僕がイケメン好きか分かりましたね(苦笑)。

前置きが長くなりましたがいよいよ本題に入ります。

学習データの用意

まずはスマホの中にあるアーティスト5組の楽曲をPCに移します。今回はそれぞれに対して10曲ずつ用意しました。

f:id:RyoMa:20180907205543p:plain

f:id:RyoMa:20180907210156p:plain

10曲では少ない(過学習を起こすため)、曲の長さが異なるので水増しと長さを揃えるために10秒ずつにデータを分割します。ここでは、音声信号処理に良く使われるlibrosaとpydubというライブラリを用いています。


import librosa
from pydub import AudioSegment

artist = ["AAA", "UNISON_SQUARE_GARDEN", "嵐", "NEWS", "SEKAI_NO_OWARI"]

i in range(len(artist)):
    file_index = 0
    pre_time = 0
    rea_time = 10000

    for j in range(10): 
        file_dir = "./music_data/" + artist[i] + "/raw_data/raw_" + str(j) + ".wav"
        music, fs = librosa.audio.load(file_dir)
        play_time = int(librosa.samples_to_time(len(music), fs)/10)
        
        for k in range(play_time):
            audio_data = AudioSegment.from_file(file_dir, format="wav")
            split_audio = audio_data[pre_time:rea_time]
            pre_time += 10000
            rea_time += 10000
            
            split_audio.export("./music_data/" + artist[i] + "/split_data/" + str(file_index) + ".wav", format = "wav")
            
            file_index += 1

次に10秒に分割した楽曲データをメル周波数スペクトルという音声認識に良く使われる特徴量をスペクトル画像として保存します。メル周波数について少し説明をします。

まずメル尺度というものがあります。

メル尺度とは音高の知覚的尺度のことで、メル尺度の差が同じであれば人間が感じる音高の差が同じになることを意味します。

メル周波数スペクトルを求めるには、大まかに以下のような流れになります。

  1. 窓関数を使って信号処理をした後に離散フーリエ変換する。
  2. メル周波数で均等にスムージングする。

詳しくはこのページを参考にしてください。

aidiary.hatenablog.com

文字だけだと分かりづらいので可視化してみます。

普通の音声データはこんな感じです。(AAAのNo Way Backという曲の冒頭10秒)

f:id:RyoMa:20180909231451p:plain

この信号をメル周波数スペクトルに変換すると…

f:id:RyoMa:20180909231502p:plain

librosaを使ったコードは以下のように書きました。


for i in range(len(artist)):
    for j in range(split_num[i]):
        split_file_dir = "./music_data/" + artist[i] + "/split_data/" + str(j) + ".wav"
        export_dir = "./music_data/" + artist[i] + "/spectrum/" + str(j) + ".jpg"
        audio_data, sr = librosa.load(split_file_dir)
        S = librosa.feature.melspectrogram(audio_data, sr = sr, n_mels = 128)
        log_S = librosa.power_to_db(S)
        
        plt.figure(figsize=(8, 8))
        librosa.display.specshow(y=audio_data, sr=sr, hop_length = 2068)
        plt.savefig(export_dir)

次にOpenCVとnumpyを使って保存したスペクトルをRGB画像に変換して,学習データを作っていきます。ラベル作成も同時にやります。

from keras.utils.np_utils import to_categorical
improt cv2
import numpy as np

spe_dir = "./music_data/AAA/spectrum/"
X_train=[]
y_train=[]

for i in range(266):
    img = cv2.imread(spe_dir + str(i) + ".png")
    b,g,r = cv2.split(img)
    img = cv2.merge([r,g,b])
    X_train.append(img)
    y_train.append(0)

これを各データに対して行い,最後にX_trainをnumpyのリストにしy_trainは転置してOne-hot-表現に変換します.


X_train = np.array(X_train)
y_train = np.array(y_train)
y_train = y_train.T
y_train = to_categorical(y_train)

これで学習データが完成しました。テストデータも今までの流れと同じようにして作成できます。

次はいよいよ学習モデルの構築です。今回はCNNで分類を行うことにしました。

理由としてはコーネル大学が発表した”Deep convolutional networks on the pitch spiral for musical instrument recognition”という論文を少し読んだからです。なので、この記事は僕の検証記事でもあるわけですね(笑)。

[1605.06644] Deep convolutional networks on the pitch spiral for musical instrument recognition

モデル作成にはkerasを使いました。以下がモデル構築のコードになっています。


from keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D
from keras.models import Sequential, load_model
from keras.utils.np_utils import to_categorical
from keras import optimizers
from keras.layers import Dense, Dropout, Flatten, Input, BatchNormalization
from keras.models import Model, Sequential

model = Sequential()
model.add(Conv2D(input_shape=(576, 576, 3), filters=32, kernel_size=(2, 2), strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=32, kernel_size=(2,2), strides=(1,1), padding="same"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=32, kernel_size=(2,2), strides=(1,1), padding="same"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(5))
model.add(Activation('softmax'))

次はデータを学習させます。kerasに最適化のライブラリは色々用意されているのですが一番精度の良かった”Adam”にし、損失関数は”categorical_crossentropy”にしました。

ここで少し最適化について説明します。

最適化とは与えられた条件のもとで何らかの関数を最小化(もしくは最大化)することです。機械学習で言えば損失関数(ここでは交差エントロピー)は最小にしたい(過学習防止)のですが、その時の最適解を求める方法をoptimizerで指定しています。

デフォルトでは”SGD(確率的勾配降下法)”が指定されています。

詳しい数式などの説明は以下のリンクからお願いします。また、kerasで使える最適化アルゴリズム一覧(Keras Documentation)も貼っておきます。

qiita.com

最適化 – Keras Documentation


model.compile(optimizer='Adam', loss='categorical_crossentropy',metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=32, epochs=10)

f:id:RyoMa:20180909234342p:plain

学習データに対しては98%くらいの精度が出ていますね。最初の方は19%をうろうろしていたので大きな進歩です。肝心のテストデータに対しては…


score = model.evaluate(X_test, y_test, verbose=1)
print()
print("Test loss:", score[0])
print("Test accuracy:", score[1])

f:id:RyoMa:20180909235911p:plain

テストデータに対しても92%くらいになりました。

これで僕の好きなアーティストの良さが伝わったと思います。AIもイケメン好きになったかな??

これからは音楽の話はこのAIとしかしません(大嘘)。

感想として、普段は学習データ,テストデータが用意されているので前処理を行うだけですが、今回はデータセットの作成から行うことができたので苦労はしましたが自信がつきました。今後はアーティストの数を増やして分類させてみたいです。また、音声信号処理に興味が出てきたのでもう少し勉強したいと思います。

研修生の方が書いた前回の記事と組み合わせて良いサービス作れそう…

blog.aidemy.net