前回、テキストで音楽を表現する方法 を調べた。今回は実際に char-rnn を利用して音楽を生成してみた。
まずは結果から
バッハのインベンションの No.1 - No.15 の全 15 曲を学習データにして、 char-rnn を利用して学習させた。結果がこちら。
無調音楽に近いような感じ。
実装
以下のサイトの記事を真似しました。
参考にした記事では、karpathy さんの char-rnn をそのままつかっていたけれども、 理解を深めるために Tensorflow で char-rnn を実装した。 といっても、Udacity で学んだことを応用しただけ。
その他、
- テキスト形式としては、**kern 記譜法を採用。
- 音楽を扱うライブラリは music21 を利用。
今回の Jupyter Notebook はコチラ。いつものようにコードを抜粋しながら説明。
KernScore からデータ取得
KernScores には、大量のクラシック音楽の **kern 記譜法でかかれた楽譜が置いてある。 まずは、バッハのインベンションの楽譜を取得した。
from urllib.request import urlopen
for i in range(1,15+1):
filename = "inven{0:02d}.krn".format(i)
file = urlopen("https://kern.humdrum.org/cgi-bin/ksdata?l=osu/classical/bach/inventions&file=%s&f=kern"%filename)
with open("kernscore/bach/"+filename,'wb') as output:
output.write(file.read())
データを char-rnn に食わせるために整形
データを見てみると余分なものがあることに気づく。これはいらない。
- metadata
- コメント
- 小節記号
!!!COM: Bach, Johann Sebastian
!!!OPR: Inventio
!!!OTL: Inventio 1
!!!XEN: Two-part Inventions [for keyboard]
!!!ONM: No. 1
!!!ONB: In C major.
!!!SCT: BWV 772
!!!YEC: Copyright 1994, David Huron
!!!YEM: Rights to all derivative electronic formats reserved.
!! Performance duration: 1:29; performance tempo: 59.3 quarter-notes per minute.
!! (Andras Schiff, piano, "Two-Part Inventions BWV 772a-786; Three-part
!! Inventions BWV 787-801" Decca 411 974-2)
**kern **kern
*staff2 *staff1
*clefF4 *clefG2
*k[] *k[]
*C: *C:
*M4/4 *M4/4
*MM59.3 *MM59.3
=1- =1-
2r 16r
. 16c
. 16d
. 16e
. 16f
. 16d
. 16e
. 16c
16r 8g
16C .
16D 8cc
16E .
16F 8bM
16D .
16E 8cc
16C .
...
. 16b
=22 =22
1CC; 1C; 1e; 1g; 1cc;
==|! ==|!
*- *-
!!!CDT: 1685/3//-1750/7/28
!!!OCY: Deutschland
!!!AGN: polyphony
!!!AST: baroque
!!!AMT: simple quadruple
!!!SCA: Bach-Werke-Verzeichnis
!!!YOR: Bach Gesellschaft
!!!AIN: cemb; clavi
!!!EEV: 1.0
!!!RDT: 1986 November
!!!YER: 1995 May 22
!!!YEN: Canada
!!!EFL: 1/15
!!!EED: David Huron
これらを除去して、インベンション 15 曲を一つのファイルに出力する。
import glob
REP="@\n"
def trim_metadata(output_path, glob_path):
comp_txt = open(output_path,"w")
ll = glob.glob(glob_path)
for song in ll:
lines = open(song,"r").readlines()
out = []
found_first = False
for l in lines:
if l.startswith("="):
## new measure, replace the measure with the @ sign, not part of humdrum
out.append(REP)
found_first = True
continue
if not found_first:
## keep going until we find the end of the header and metadata
continue
if l.startswith("!"):
## ignore comments
continue
out.append(l)
comp_txt.writelines(out)
comp_txt.close()
output_path = "composer/bach.txt"
glob_path = "kernscore/bach/*.krn"
trim_metadata(output_path, glob_path)
char-rnn で音楽生成
ここからのコードは、Udacity で与えられたコードをほぼそのまま使った。 ハイパーパラメータは、以下のとおりに設定した。これで、学習時間が 30 分くらい。 曲の完成度からみると、これではすくないような気もする。
batch_size = 10
num_steps = 10
lstm_size = 512
num_layers = 2
learning_rate = 0.001
keep_prob = 0.5
出力データのフォーマットを整える
出力データが 結構ぐちゃぐちゃに吐き出される。
- 右手左手の 2 行だけでよいのに、3 行目がある。
- 右手パートしかない
- 左手パートしかない
- 終端マークが途中で現れる。
これらを解消するために、**kern 記法に沿ったフォーマットで吐き出されるようにした。
ついでに、メタデータと小節番号の bar も追加。
r = []
r.append("**kern\t**kern\n")
r.append("*staff2\t*staff1\n")
r.append("*clefF4\t*clefG2\n")
r.append("*k[]\t*k[]\n")
r.append("*C:\t*C:\n")
r.append("*M4/4\t*M4/4\n")
r.append("*MM80\t*MM80\n")
bar = 1
for line in samp.splitlines():
sp = line.split('\t')
if sp[0] == '@':
r.append("={bar}\t={bar}\n".format(bar=bar))
bar += 1
else:
ln = len(sp)
if ln == 1 and sp[0] != "":
r.append(sp[0])
r.append('\t')
r.append('.')
r.append('\n')
elif ln == 1 and sp[0] == "":
r.append(".")
r.append('\t')
r.append('.')
r.append('\n')
elif sp[0] == "*-" or sp[1] == "*-":
continue
else:
r.append(sp[0])
r.append('\t')
r.append(sp[1])
r.append('\n')
r.append("==|!\t==|!\n")
r.append("*-\t*-\n")
open("results/bach2ai.krn","w").writelines(r)
これで、楽譜が完成!
midi 吐き出し
あとは、これを midi に吐き出して再生するだけだ。
from music21 import *
m1 = converter.parse("results/bach2ai.krn")
m1.write('midi', fp='midi/bach2ai.mid')
Jupyter Notebook 上でも再生できる。
m1.show("midi")
おわりに
今回は、あまり満足のいく作品ができなかった。改善点としては、
- char ベース見ていくのではなくて、音符単位(8a, 7b など)で音を見る
- 左手、右手を分けて音を見ていく
- 学習をもっと実施する、ハイパーパラメータの調整
などが考えられる。とはいえ、音楽をディープラーニングで生成できた。 ディープラーニングを学ぶ動機のひとつが音楽生成だったので、達成できて嬉しい。
音楽に関係する取り組みは、char-rnn ベース以外にもあるみたいだ。 今後は、これらを理解していくことにしよう。
- Composing Music With Recurrent Neural Networks · hexahedria … LSTM でクラシック音楽生成
- Magenta .. 言わずと知れた、Google のプロジェクト
- Tech Circle #23 Next Music Production by Google Magenta … 音楽生成の歴史やら magenta のことがまとまっている資料
- DeepBach: a Steerable Model for Bach chorales generation … Bach のメロディーにコーラスを自動生成
- deepjazz: deep learning for jazz … ハッカソンで生まれた Jazz 生成
- jiexunsee/rudimentary-ai-composer … わずか 100 行の LSTM での音楽生成。