初投稿です。
ChainerとかCaffeとかよく触るのですが、画像処理系がほとんどなので音声処理系もやってみようと思ってやりました。
Chainerを使って、2つの曲を分類する分類器を作って、PCのマイクに音楽を入力して分類させてみます。
分類する曲
今回は以下の2つの曲を分類します。
・「Crossing Field」 (LiSA)
・「蘇生」 (Mr.Children)
(最初LiSAの曲を4つくらい録音して学習させたら、学習はできたけど推定ができなかったので、分けやすそうな曲をチョイスしました)
録音環境
実際にテスト時にマイクに音楽をかけるので、それと同じ状況で上の2曲を1回ずつ録音してデータセットを作りました。
録音にはPythonのPyAudioを使いました。(コードは以下の通り)
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入れました。
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に上げてはいないのですが(コード自体もそんなに長くないです)、要望があれば対応します。