TensorFlow製音楽生成プロジェクト「Magenta」でMIDIファイルを学習させ、曲を生成する。

# はじめに

この記事では、Magentaの開発環境を準備した上で、手元のMIDIファイルを学習し、曲を作成するということを行います。
[magentaのBasic RNNのREADME](https://github.com/tensorflow/magenta/tree/master/magenta/models/basic_rnn)の通りですので、そちらを読んでいただいてもよろしいかと思います。

用語等、不明な点もあるため、ご了承いただければと思います。
開発環境の準備はこちらの記事もご参考ください。

[TensorFlow製音楽生成プロジェクト「Magenta」を開発環境の設定から曲生成までやってみる。](http://qiita.com/tackey/items/c9d5045743539e25e9c2)



# データセットを作る

手持ちにMIDIファイルを学習する前に、まずはTensorFlowで扱えるように、NoteSequencesという形式に変換する必要があります。

下記のスクリプトを実行します。
不正な形式のMIDIファイルを読み込んだ際にエラーを吐くことがあります。
解析できないMIDIファイルはスキップされるので安心して大丈夫なようです。


````

MIDI_DIRECTORY=<folder containing MIDI files. can have child folders.>

# TFRecord file that will contain NoteSequence protocol buffers.
SEQUENCES_TFRECORD=/tmp/notesequences.tfrecord

bazel run //magenta/scripts:convert_midi_dir_to_note_sequences -- \
--midi_dir=$MIDI_DIRECTORY \
--output_file=$SEQUENCES_TFRECORD \
--recursive


```

`convert_midi_dir_to_note_sequences.py`と言うのを実行しているようですね。

下記のようなメッセージが表示されれば完了です。

```
..............
INFO: Found 1 target...
Target //magenta/scripts:convert_midi_dir_to_note_sequences up-to-date:
  bazel-bin/magenta/scripts/convert_midi_dir_to_note_sequences
INFO: Elapsed time: 7.312s, Critical Path: 0.63s

(中略)

INFO:tensorflow:Could not parse 0 MIDI files.
INFO:tensorflow:Wrote 6 NoteSequence protos to '/tmp/notesequences.tfrecord'
```


# 学習する

## メロディーを抽出する
`basic_rnn_create_dataset.py`を実行します。

これは上記でMIDIファイルを元に作成した`NoteSequence data`からメロディーを抽出するスクリプトです。
アウトプットは`SequenceExampleprotos`を含んだ`tfrecord`ファイルとして書き出されます。

`basic_rnn`モデルのTensorFlowの読み込みは、`SequenceExample protos`を直接モデルに読み込むことができます。

この例では、評価データセットを2つ目の`tfrecord`ファイルとして作ります。
ですが、`evaloutput`と`evalratio`フラグを削除することで省くことができます。

下記のようなメッセージが出ればOKです。


```
INFO: Found 1 target...
Target //magenta/models/basic_rnn:basic_rnn_create_dataset up-to-date:
  bazel-bin/magenta/models/basic_rnn/basic_rnn_create_dataset
INFO: Elapsed time: 0.573s, Critical Path: 0.01s

INFO: Running command line: bazel-bin/magenta/models/basic_rnn/basic_rnn_create_dataset '--input=/tmp/notesequences.tfrecord' '--output_dir=/tmp/basic_rnn/sequence_examples' '--eval_ratio=0.10'
INFO:tensorflow:

Completed.

INFO:tensorflow:Processed 6 inputs total. Produced 31 outputs.
INFO:tensorflow:DAGPipeline_RandomPartition_training_melodies_count: 29
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_melodies_discarded_too_long: 0
INFO:tensorflow:DAGPipeline_RandomPartition_eval_melodies_count: 2
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_melody_lengths_in_bars:
  [0,1): 10
  [1,3): 6
  [3,6): 17
  [6,7): 2
  [7,8): 1
  [8,10): 4
  [10,20): 21
  [20,30): 5
  [30,40): 6
  [50,100): 4
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_melodies_discarded_too_short: 35
INFO:tensorflow:DAGPipeline_Quantizer_sequences_discarded_because_multiple_time_signatures: 5
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_melodies_truncated: 0
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_polyphonic_tracks_discarded: 43
INFO:tensorflow:DAGPipeline_MonophonicMelodyExtractor_melodies_discarded_too_few_pitches: 10
```

完了すると
`/tmp/basic_rnn/sequence_examples/`に
`training_melodies.tfrecord`と`eval_melodies.tfrecord`の2つのファイルができます。
`training_melodies.tfrecord`は訓練用、`eval_melodies.tfrecord`は評価用のようです。





## トレーニングのスクリプトを実行する

Magentaの開発者の方がスクリプトを作ってくださっています。


```
$EXPERIMENT_DIR=/tmp/basic_rnn/logdir
```

作業フォルダを指定します。


```
$HYPERPARAMETER_STRING=`{"rnn_layer_sizes":[50]}’
```

ハイパーパラメータの指定をします。
このように指定することで、LSTMモデルのサイズを指定することができるようです。
LSTMについては下記が参考になると思います(大作)。

[わかるLSTM ～ 最近の動向と共に](http://qiita.com/t_Signull/items/21b82be280b46f467d1b)

> LSTM(Long short-term memory)は、RNN(Recurrent Neural Network)の拡張として1995年に登場した、時系列データ(sequential data)に対するモデル、あるいは構造(architecture)の1種です。

>　(中略)

> LSTMのその最も大きな特長は、従来のRNNでは学習できなかった長期依存(long-term dependencies)を学習可能であるところにあります。

```
$NUM_TRAINING_STEPS=20000
```

訓練回数を指定します。Macだととても時間がかかるので、数百とかに減らすといいと思います。


```
$TRAIN_DATA=/tmp/basic_rnn/sequence_examples/training_melodies.tfrecord
```

上記で生成した訓練データのパスを記載します。

```
$EVAL_DATA=/tmp/basic_rnn/sequence_examples/eval_melodies.tfrecord
```

オプションとして、訓練データと並行して検証データも並行してはしらせることができるようです。

設定ができたら、スクリプトを実行します。

```
./run_basic_rnn_train.sh $EXPERIMENT_DIR $HYPERPARAMETER_STRING $NUM_TRAINING_STEPS $TRAIN_DATA [$EVAL_DATA]
```


下記のメッセージが出ます。

````
INFO: Found 1 target...
Target //magenta/models/basic_rnn:basic_rnn_train up-to-date:
  bazel-bin/magenta/models/basic_rnn/basic_rnn_train
INFO: Elapsed time: 0.544s, Critical Path: 0.00s
INFO:tensorflow:hparams = {'rnn_layer_sizes': [50], 'decay_rate': 0.85, 'dropout_keep_prob': 0.5, 'batch_size': 128, 'decay_steps': 1000, 'clip_norm': 5, 'initial_learning_rate': 0.01, 'skip_first_n_losses': 0}
Starting TensorBoard 28 on port 6006
(You can navigate to http://10.200.3.144:6006)
INFO:tensorflow:Train dir: /tmp/melody_rnn/logdir/run1/train
INFO:tensorflow:Starting training loop...
INFO:tensorflow:global_step/sec: 0

(中略)

INFO:tensorflow:Global Step: 100 - Learning Rate: 0.01000 - Loss: 1.680 - Perplexity: 13.678 - Accuracy: 85.811
INFO:tensorflow:Training complete.
```


# 曲を生成する。

上記の訓練が完了すると、作業ディレクトリ`/tmp/basic_rnn/logdir/run1/train/` の配下に、下記のようなファイルができると思います。

```
checkpoint					model.ckpt-31
events.out.tfevents.1475664203.HTMac.local	model.ckpt-31.meta
graph.pbtxt					model.ckpt-39
model.ckpt-15					model.ckpt-39.meta
model.ckpt-15.meta				model.ckpt-47
model.ckpt-23					model.ckpt-47.meta
model.ckpt-23.meta
```

checkpointというのはTensorFlowにおけるファイルの保存やロードに用いるもののようです。
Basic_RNNではこのうち、最後のcheckpoint(上記だとmodel.ckpt-47)を用いて曲を生成してくれるようです。


また、曲を生成するには、モデルにはじまりのメロディを与える必要があります。
曲生成はそのメロディを元に、次の音をどんどん予測して行って曲を延長していく、ということをするからです。準備するメロディはモノラルの(同タイミングに複数の音が鳴らない)メロディである必要があります。

任意に作成してもいいですし、サンプルとして
`/magenta/models/shared/primer.mid`も準備されています。

ここではこのサンプルを用います。

```
PRIMER_PATH=<absolute path of your primer MIDI file>

bazel run //magenta/models/basic_rnn:basic_rnn_generate -- \
--run_dir=/tmp/basic_rnn/logdir/run1 \
--hparams='{"rnn_layer_sizes":[50]}' \
--output_dir=/tmp/basic_rnn/generated \
--num_outputs=10 \
--num_steps=128 \
--primer_midi=$PRIMER_PATH
```

最小の３小節はprimer.midを元に固定されています。

[サンプル1](https://clyp.it/iwkk1xg3)
[サンプル2](https://clyp.it/jebdi2ny)

サカナクションさんを学習させましたが、雰囲気を感じることができました。


# 最後に

DeepLearningの知識、TensorFlowの知識、英語読解力の不足が原因で、ちょっと読み解けてない部分があります。
引き続き勉強します。。。

ありがとうございました。