LoginSignup
14
7

More than 5 years have passed since last update.

Chainerで楽曲分類をやってみた

Last updated at Posted at 2017-02-10

初投稿です。
ChainerとかCaffeとかよく触るのですが、画像処理系がほとんどなので音声処理系もやってみようと思ってやりました。
Chainerを使って、2つの曲を分類する分類器を作って、PCのマイクに音楽を入力して分類させてみます。

分類する曲

今回は以下の2つの曲を分類します。
・「Crossing Field」 (LiSA)
・「蘇生」 (Mr.Children)

(最初LiSAの曲を4つくらい録音して学習させたら、学習はできたけど推定ができなかったので、分けやすそうな曲をチョイスしました)

録音環境

実際にテスト時にマイクに音楽をかけるので、それと同じ状況で上の2曲を1回ずつ録音してデータセットを作りました。
録音にはPythonのPyAudioを使いました。(コードは以下の通り)

audio-input.py
import pyaudio
import numpy as np
import time

CHUNK = 1024
RATE = 8000
p = pyaudio.PyAudio()

stream = p.open(format=pyaudio.paInt8,
                channels=1,
                rate=RATE,
                frames_per_buffer=CHUNK,
                input=True)

while stream.is_active():
    input = stream.read(CHUNK)
    print list(np.frombuffer(input, dtype='int8'))

stream.stop_stream()
stream.close()
p.terminate()

これをターミナルで以下のように打ってエンター押した後に、マイクに向かって音楽をかけました。
僕はスマホのスピーカーを近づけて録りました。

$ Python audio-input.py > hogehoge.txt

出来上がったテキストファイルは適当に処理して、pythonで扱いやすい形にします。
これをChainerで書いた学習プログラムに投げます。

学習

モデルは以下のように定義しました。
時系列データを逐次与えていくので、けっこう有名だしLSTM使えばいいんじゃないか? という浅い発想でとりあえずLSTM入れました。

learn.py
class MyRNN(chainer.Chain):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(MyRNN, self).__init__(
            fc1=L.Linear(input_dim, hidden_dim),
            lstm2=L.LSTM(hidden_dim, hidden_dim),
            fc3=L.Linear(hidden_dim, output_dim),
        )

    def __call__(self, xs, t):
        self.lstm2.reset_state()
        for i, x in enumerate(xs):
            x = x[np.newaxis, :]
            x = Variable(cuda.to_gpu(x))
            fc1 = self.fc1(x)
            lstm2 = self.lstm2(fc1)
            fc3 = self.fc3(lstm2)

            if i == len(xs) - 1:
                t = Variable(cuda.to_gpu(t))
                loss = F.softmax_cross_entropy(fc3, t)
                accuracy = F.accuracy(fc3, t)

        return loss, accuracy

input_dim(入力層の次元)は1024です。これはaudio-input.pyのCHUNKに対応します。
hidden_dim(隠れ層の次元)は、好きな値で良いのですが今回は1024にしました。
output_dim(出力層の次元)は、2です。2曲の分類なので。

まあ、ほんとはクラスを3つにして、1つはどの曲もかかっていないというクラスにするべきかもですが、データ作るのがちょっと面倒だったので、とりあえずこんな感じで学習させました。

推論

マイクに向かって音楽を流して推論させてみたところ、そこそこ上手くいきました。ただ、「蘇生」を数十秒ほど流した後に「Crossing Field」に切り替えると、上手く推定できていなかったりして、その逆もありました。

とりあえず、曲を流してから、推論するプログラムを起動させると、AメロでもBメロでもサビでも関係なくちゃんと推定できました。

プログラムをgithubに上げてはいないのですが(コード自体もそんなに長くないです)、要望があれば対応します。

14
7
1

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
14
7