#長く続く構造を捉える難しさ
先月2016年6月上旬にディープラーニングによって美術や音楽を生成することを目的としたプロジェクトMagentaは、第1弾としてBasic RNNを公開しました。この時点で、機械学習で作曲することに対する問題点がありました。このBasic RNNで生成された曲は、部分部分で聞くと、キーはバラバラでないし、一応音楽っぽいものにはなりましたが、コード進行はよくわからないし、曲の展開も変だし、メロディのつながりも微妙でした。
実は、長く続く構造(音楽でいうコード進行や曲の展開)は機械学習でとらえることはなかなか難しいのです。一方で人間はたやすくとらえています。今回新しく公開したLookback RNNとAttention RNNはBasic RNNよりこの長く続く構造を学習し生成しようと試みたものです。
#Basic RNNの仕組み
Basic RNNでは入出力にOne-hot Vectorを使用していました。これは、一つの要素だけ1であり、他は0になっているベクトルのことです。ここでは、メロディをOne-hot Vectorの時系列として捉えます。それぞれの時点でのOne-hot Vectorは、それぞれの時点でどの音を鳴らすか、音を止めるか、何もしないか、という状態(これをイベントと呼んでいます)を表しています。そして、One-hot Vectorの時系列生成モデルをRNNで作り、曲を作っていきます。
Basic RNNで使うOne-hot Vectorには
①それぞれの音程が鳴ることを示すの"note-on"イベント
②鳴っている音程が終わることを示す"note-off"イベント
③音程を新しく鳴らすことも終わらすこともしない"no"イベント
この3つの情報が入っています。
#Lookback RNNの仕組み
Lookback RNNはBasic RNNで使う入出力にさらに情報を追加していきます。このうち一番重要なのはOne-hot Vectorに追加する新しい情報です。
④1小節前のイベントを繰り返すかどうか
⑤2小節前のイベントを繰り返すかどうか
よくある音楽は、1小節前の音や2小節前の音を繰り返すことが多く、これをモデルの入出力として明示しておくことで、繰り返しのフレーズを生成しやすくなります。
その他に、Lookback RNNでは入力としてOne-hot Vectorのほかに3つの情報を追加します。
・1小節前と2小節前のイベント
・直近のイベントがその1小節前や2小節前のイベントの繰り返しであるかどうか
・何拍目であるか
情報を追加した後は、Basic RNNと同じようにLSTMによって、One-hot Vectorの時系列生成モデルを作っていきます。
#Attention RNNの仕組み
Attention RNNは2014年ごろから使われ始めた手法です。TensorFlowのチュートリアルなんかでも紹介されています。とくに言語モデルでよく使われています。日本語だとここで詳しい説明がされています。Attentionはencoder-decoderモデルでよく利用されていますが、ここではencoder-decoderの構造を持たないモデルに対して適用されています。
Attentionは簡単に言えば過去のRNNの出力を毎回のステップで参照することです。過去の直近n回分のRNNの出力を、あるニューラルネットワーク(階層モデル)によりn次元ベクトルに変換します。
u_{i}^{t} = {\boldsymbol v}^{T}\text{tanh}(W_{1}^{\prime}{\boldsymbol h}_{i} + W_{2}^{\prime}{\boldsymbol d}_{t})\\
a_{i}^{t} = \text{softmax}(u_{i}^{t})\\
{\boldsymbol d}_{t}^{\prime} = \sum_{i=1}^{T_{A}} a_{i}^{t}{\boldsymbol h}_{i}
ここで、$t$は現在の時間を表し、$i$は過去n階分のそれぞれ時間を表します($i=t-n,\cdots,t-1$)。${\boldsymbol v},W_{1}^{\prime},W_{2}^{\prime}$は学習するパラメーターです。また$h_{i}$は過去iのときのモデルの出力で、$d_{t}$は現在tのモデルの出力です。この階層モデルにより、新たな出力$d_{t}^{\prime}$が求まります。結局これは過去のRNNの出力の線形結合となっており、その重みは1番目と2番目の式であらわされる、3層ニューラルネットワークのような階層モデルによって、決まります。この重みの$a_i^t$が何を表すかというと、過去のどのタイミングの出力にどの程度Attention(注意)を向けるか、ということを示しています。
$d_t^{\prime}$は、$v,W_{1}^{\prime},W_{2}^{\prime}$がうまく学習していれば、Attention(注意)すべきタイミングの情報をうまく抽出しているはずです。この手法を使うことによって、モデルに過去の情報を蓄えることをせずに、やや長い曲の構造をとらえることができます。
ここで作った$d_t^{\prime}$は結局、次の式であらわされるベクトルに変換され、これが出力として扱われます。
W_{out1}{\boldsymbol d}_t^{\prime}+W_{out2}{\boldsymbol d}_t+{\boldsymbol b}_{out}
要は、$d_t^{\prime}$と$d_t$を結合させた後に、ニューラルネットワークの線形層(Linear layer)を通します。これによって、現在のRNNの出力と過去の出力を統合した情報を一緒にしています。
Magentaが公開しているAttention RNNで作った曲はここから聴くことができます。
#動かす環境
実際に動かしていきますが、環境について説明します。
私はUbuntu環境でやっています。
Windowsに対応していないTensorflowが必要なので、MacかLinuxが必要です。
Tensorflowとビルドツールであるbazelをインストールします。
#動かしてみましょう-Lookback RNN
これらのモデルはすでに動かせる形でgithubに公開されています。まずMagentaのgithubからクローンでソースをとってきたりpullしたりします。なお、これは2016年7月26日現在のもので、動かし方が今後変わっていくかもしれませんので、気づいたらお知らせください。
学習データのmidiファイルを適当な場所に置いておきます。MIDIデータがないときは、http://www.midiworld.com/files/142/ からとってくると良いでしょう。上手くいくMIDIファイルと上手くいかないMIDIファイルがあるみたいなので、エラーが起きたらMIDIファイルを変えていろいろ試してみてください。今回は"tmp/midi"に置きます。
まず、MIDIファイルをTensorflowが使うtfrecordフォーマットに変換し、訓練データと評価データに分けます。
MIDI_DIRECTORY=/tmp/midi
SEQUENCES_TFRECORD=/tmp/notesequences.tfrecord
bazel run //magenta/scripts:convert_midi_dir_to_note_sequences -- \
--midi_dir=$MIDI_DIRECTORY \
--output_file=$SEQUENCES_TFRECORD \
--recursive
bazel run //magenta/models/lookback_rnn:lookback_rnn_create_dataset -- \
--input=/tmp/notesequences.tfrecord \
--output_dir=/tmp/lookback_rnn/sequence_examples \
--eval_ratio=0.10
訓練データを使って、RNNを学習していきます。訓練ステップ20000は長いかもしれないので、適当なところで中断してみても良いかもしれません。
bazel build //magenta/models/lookback_rnn:lookback_rnn_train
./bazel-bin/magenta/models/lookback_rnn/lookback_rnn_train \
--run_dir=/tmp/lookback_rnn/logdir/run1 \
--sequence_example_file=/tmp/lookback_rnn/sequence_examples/training_melodies.tfrecord \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--num_training_steps=20000
訓練を終えたら、作曲をさせます。つまり、MIDIファイルを生成します。RNNの生成モデルは、過去のメロディをもとに今の音を決めていくようなモデルなので、初めのメロディがなければいけません。primer_melodyははじめのメロディを表します。下の"[60, -2, 60, -2, 67, -2, 67, -2]"は"[ド, ー, ド, ー, ソ, ー, ソ, ー]"を表します。キラキラ星のメロディです。
premer_melodyの代わりに初めのメロディのMIDIファイルを指定することができて、"--primer_melody="の代わりに"--primer_midi=<ファイルパス>"で指定してください。
bazel run //magenta/models/lookback_rnn:lookback_rnn_generate -- \
--run_dir=/tmp/lookback_rnn/logdir/run1 \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--output_dir=/tmp/lookback_rnn/generated \
--num_outputs=10 \
--num_steps=128 \
--primer_melody="[60, -2, 60, -2, 67, -2, 67, -2]"
これで、"/tmp/lookback_rnn/generated"にMIDIファイルができていると思います。
#動かしてみましょう-Attention RNN
Attention RNNもLockback RNNとほとんど同じコマンドで動かせます。
MIDIファイルから訓練データを生成し、モデルを学習させます。
MIDI_DIRECTORY=/tmp/midi
SEQUENCES_TFRECORD=/tmp/notesequences.tfrecord
bazel run //magenta/scripts:convert_midi_dir_to_note_sequences -- \
--midi_dir=$MIDI_DIRECTORY \
--output_file=$SEQUENCES_TFRECORD \
--recursive
bazel run //magenta/models/attention_rnn:attention_rnn_create_dataset -- \
--input=/tmp/notesequences.tfrecord \
--output_dir=/tmp/attention_rnn/sequence_examples \
--eval_ratio=0.10
bazel build //magenta/models/attention_rnn:attention_rnn_train
./bazel-bin/magenta/models/attention_rnn/attention_rnn_train \
--run_dir=/tmp/attention_rnn/logdir/run1 \
--sequence_example_file=/tmp/attention_rnn/sequence_examples/training_melodies.tfrecord \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--num_training_steps=20000
学習したモデルに作曲をさせます。MIDIファイルを生成します。
bazel run //magenta/models/attention_rnn:attention_rnn_generate -- \
--run_dir=/tmp/attention_rnn/logdir/run1 \
--hparams="{'batch_size':64,'rnn_layer_sizes':[64,64]}" \
--output_dir=/tmp/attention_rnn/generated \
--num_outputs=10 \
--num_steps=128 \
--primer_melody="[60, -2, 60, -2, 67, -2, 67, -2]"
"/tmp/attention_rnn/generated"にMIDIファイルができていると思います。
#まとめ
これらのモデルではBasic RNNに比べると、数小節にまたがるようなメロディの構造、特に繰り返しなんかは捉えていました。一方、曲全体のAメロ→Bメロ→サビみたいな構造はなかなか難しいです。また、コード進行も捉えられていないと思います。こういったところには、機械学習などの統計的な手法ではなく、ルールベースのAIを使わなければならないのではないかという意見もあると思います。
ただ、僕はもうちょっとニューラルネットワークを頑張れば、いずれできるのではないかと思っています。つまり、曲の長い構造と局所的なメロディの両方をとらえるような、統計的なモデルを作ることは可能だと思います。
また、このモデルではモノフォニック(単音)のメロディしか作れませんが、これも今後に期待できるとおもっています。
とにかく、今後も注目していきたいと思います。