Magentaのdocker imageを使って、バッハ風の旋律を生成する

#Magentaとは
MagentaはGoogle Brainが開発している音楽や絵画といった芸術作品を機械学習から生み出すプロジェクトです。TensorFlowをバックエンドに使用しています。
今回はMagentaのdocker imageを使い、自前で用意した学習データからモデルを生成し、そのモデルから単一の旋律を生成するまでの作業をやってみます。
私はmacbookで作業しましたが、dockerを使っているので、（理論上は）どのOSでも同じように動かせます。

それではやってみましょう。

#学習対象とする楽曲のmidiファイルを収集
今回はバッハの楽曲を学習データとするので、以下のサイトからmidiファイルをダウンロードしました。
[IMSLP/Petrucci Music Library: Free Public Domain Sheet Music](http://imslp.org/wiki/Main_Page)

また、全てをダウンロードするのは大変だったので、バッハの中で個人的に好きな以下の楽曲を今回の学習データとしました。

* Goldberg-Variationen, BWV 988
* Orchestral Suite No.1~4
* Brandenburg Concerto No.1~6

（合計68ファイル）

#docker imageの起動
```shell-session
$ docker pull tensorflow/magenta
$ docker run -it -p 6006:6006 -v /tmp/magenta:/magenta-data tensorflow/magenta
```
`-v` でマウントしているホストマシンの `/tmp/magenta` ディレクトリに学習データなどが放り込まれていきます。

##作業用ディレクトリの作成
学習の過程で幾つかの中間ファイルが生成されます。
今回は以下のようなディレクトリを用意しておきます。

```shell-session
magenta-data
├── midi
│   ├── original ← 学習用のmidiファイルを格納するディレクトリ
│   └── generated ← 生成されたmidiファイルを格納するディレクトリ
└── tmp
    └── melody_rnn
        ├── logdir
        │   └── run1 ← 学習データを格納するディレクトリ
        ├── notesequences ← 生成されたNoteSequenceを格納するディレクトリ
        └── sequens_example ← 生成されたSequenceExamplesを格納するディレクトリ
```
##学習用midiファイルを配置
今回は `magenta-data` ディレクトリがホスト側にマウントされているので、ホストマシンの`/tmp/magenta/midi/original/`ディレクトリにmidiファイルを格納しておきます

なお、以降の作業は全てコンテナ内で行います。

#学習用midiファイルからNoteSequenceの生成

```shell-session:NoteSequenceの生成
convert_dir_to_note_sequences \
    --input_dir=/magenta-data/midi/original \
    --output_file=/magenta-data/tmp/melody_rnn/notesequences/notesequences.tfrecord \
    --recursive
```
このコマンドで元のmidiファイルからNoteSequenceが生成されます。
NoteSequenceは、midiファイル内のメロディをプロトコルバッファに則った音の配列に変換して、さらにTensorFlowが扱えるフォーマットに変換したもの、くらいに考えておけば良さそうです。
[本家の説明はこちら](https://github.com/tensorflow/magenta/tree/master/magenta/models/melody_rnn#create-notesequences)

#NoteSequenceからSequenceExampleの生成

```shell-session:SequenceExampleの生成
melody_rnn_create_dataset \
--config=basic_rnn \
--input=/magenta-data/tmp/melody_rnn/notesequences/notesequences.tfrecord \
--output_dir=/magenta-data/tmp/melody_rnn/sequens_example \
--eval_ratio=0.10
```
このコマンドでNoteSequenceをSequenceExampleに変換しています。
SequenceExampleは実際の学習と評価のための材料になるものです。
パラメータの `--config=basic_rnn` で、  `basic_rnn` を指定しています。これは学習モデルを指定していて、 `basic_rnn` の他に、 `lookback_rnn` と `attention_rnn` を指定することができます。それぞれの学習モデルの概要は[こちら](https://qiita.com/marshi/items/8f36d741b297dd57faa8)のページの解説が分かりやすかったです。
また、`--eval_ratio=0.10`というパラメータは、入力データのうち、何割を学習データにして、何割を教師データにするかを指定しています。`--eval_ratio=0.10`の場合、10%を教師データとして、残りの90%を学習データとして使用する、という指定になります。
[本家の説明はこちら](https://github.com/tensorflow/magenta/tree/master/magenta/models/melody_rnn#create-sequenceexamples)

#SequenceExamplesを使ってモデルのトレーニング
```shell-session:モデルのトレーニング
melody_rnn_train \
--config=basic_rnn \
--run_dir=/magenta-data/tmp/melody_rnn/logdir/run1 \
--sequence_example_file=/magenta-data/tmp/melody_rnn/sequens_example/training_melodies.tfrecord \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--num_training_steps=2000
```
まず注目するのは`--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}"`というパラメータです。
まず`batch_size`は、一度の処理で使うメモリサイズを指定しています。デフォルトは128で、大きければ大きいほど処理は早くなりますが、大きくしすぎるとOut of Memoryのエラーが頻発します。
次に`rnn_layer_sizes`は、RNNの段数と各段のユニット数の指定です。`[64,64]`は2段のRNNで、各レイヤのユニット数は64ずつです。デフォルトでは`[128,128]`です。段数を増やすことで、より深い学習が可能ですが、それだけマシンリソースを消費します。

また、`--num_training_steps=2000`は学習ループを終了する前に取るべき更新ステップ回数を指定しています。
今回は最初`20000`を指定したのですが、トレーニング中のトレースログを見ていたら1ステップあたり1.5秒程度かかっていて、このままだと1.5秒×20000ステップで8時間以上かかる計算でした。特にmacbookなど、GPUを使えないマシンでは、少ないステップ数から検証するのが良いと思います。
ちなみに未指定の場合は、手動で終了するまで実行され続けます。
[本家の説明はこちら](https://github.com/tensorflow/magenta/tree/master/magenta/models/melody_rnn#train-and-evaluate-the-model)

#トレーニングしたモデルで作曲
```shell-session:作曲する
melody_rnn_generate \
--config=basic_rnn \
--run_dir=/magenta-data/tmp/melody_rnn/logdir/run1 \
--output_dir=/magenta-data/midi/generated \
--num_outputs=10 \
--num_steps=256 \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--primer_melody="[60, -2, 72, -2]"
```
このコマンドで注目するのは `--primer_melody="[60, -2, 72, -2]"` というパラメータです。
このパラメータは生成する旋律に対して、冒頭のフレーズを指定することができます。
今回指定している[60, -2, 72, -2]という値は、最初の音の高さは60（ド）で長さは-2（16不音符2つ分）、次の音の高さは72（最初のドの１オクターブ上のド）で長さは-2（16不音符2つ分）と指定しています。
数値の指定はmidiの仕様に則っているそうです。
また、このパラメータにmidiファイルを指定することもできます。

`--num_steps=256` は生成する旋律の長さを指定しています。1小節あたり16音×小節数の値を設定します。256の場合、256/16=16で、16小節の長さの旋律を生成します。
また `--num_outputs=10` で、生成する旋律数を10と指定しています。
[本家の説明はこちら](https://github.com/tensorflow/magenta/tree/master/magenta/models/melody_rnn#generate-melodies)

#出来上がった旋律
[その1](http://shigeshibu44.github.io/2017-12-06_223143_01.mid)
[その2](http://shigeshibu44.github.io/2017-12-06_223143_02.mid)
[その3](http://shigeshibu44.github.io/2017-12-06_223143_03.mid)
[その4](http://shigeshibu44.github.io/2017-12-06_223143_04.mid)
[その5](http://shigeshibu44.github.io/2017-12-06_223143_05.mid)
[その6](http://shigeshibu44.github.io/2017-12-06_223143_06.mid)
[その7](http://shigeshibu44.github.io/2017-12-06_223143_07.mid)
[その8](http://shigeshibu44.github.io/2017-12-06_223143_08.mid)
[その9](http://shigeshibu44.github.io/2017-12-06_223143_09.mid)
[その10](http://shigeshibu44.github.io/2017-12-06_223143_10.mid)

#感想
バッハ風かどうかは別にして、とにかく簡単でした。
全てのコマンドにパスが通っている状態のコンテナが起動しているので、やるべきことは学習データを集めて、パラメータのチューニングをするだけでした。

もっと洗練されていけば、ベースになる旋律をマシンに作らせて、和声を加えるなどのアレンジをするだけで曲を量産していく作曲家も出てくるんじゃないかなと思います。

#参考
[tensorflow/magenta](https://github.com/tensorflow/magenta)（公式）
[Magentaを使ってAI（人工知能）に作曲をさせてみる](http://johoko.blog.fc2.com/blog-entry-29.html)
[Google Magentaが新たに公開した2つの作曲ニューラルネットワーク(Lookback RNN,Attention RNN)](https://qiita.com/marshi/items/8f36d741b297dd57faa8)