悩める作曲家のために、RNN(リカレントニューラルネットワーク)を利用して、曲のコード進行を生成するウェブサービス Deep Chordを作りました。本記事ではDeep Chordの概要を紹介します。
Deep ChordのRNNの学習・推定にはChainerを、ウェブ部分にはDjangoを利用しています。
Deep Chordの概要
コード進行って?
例えばピアノの鍵盤で「ド・ミ・ソ」とか「レ・ファ・ラ」とか、複数の音を同時に弾くと、それぞれ異なる響きになりますが、これを和音(コード)と呼びます。この和音が様々に遷移していくことで曲が出来上がるのですが、この和音の遷移こそがコード進行です。
コード進行には、多くの曲で利用される王道進行というものがあります。例えばJ-POPでは、F(ファ・ラ・ド) → G(ソ・シ・レ) → Em(ミ・ソ・シ) → Am(ラ・ド・ミ)、というコード進行が多用されていて、Ⅳ→Ⅴ→Ⅲ→Ⅵと呼ばれています。
その他にも、パッヘルベルのカノンで使われるコード進行や、90年代の曲で流行ったコード進行など、最近の曲でも多用される進行があります。
コード進行を学習させる
コード進行によく使われるパターンがあるということは、ニューラルネットに学習させればコード進行を推定できるんじゃないかということで、約15,000曲のコード進行のデータをRNNに投入して学習をさせました。
実は数年前に、ニューラルネットではなくN-Gramを使ったアプローチでコード進行を推定するウェブアプリを作ったのですが、その時は納得のいくコード進行には全然ならず、早々にサイトを閉じてしまいました。今回はその時のリベンジの意味も込めて、ディープラーニングに手を出してみました。
とはいえ、ディープラーニングはあまり詳しくなかったので、Chainerのサンプルプログラムの文章から言語を学習するモデルを参考に、ニューラルネットの生成と学習を行いました。
ニューラルネットの学習
言語のモデルでは単語毎にIDが振られ、ある文(単語のリスト)に後続する単語を推定しています。コード進行も同様で、あるコード進行に対して次にどんなコードが続きそうかを学習させます。
語彙のサイズ(コードの全種類)は約370で、言語モデルに比べると遥かに小さいですね。学習したコードには、CやAm、B7-5といったコードだけでなく、Bm/Eのようなスラッシュコードも含まれています。
RNNのパラメータはあまり考えず、ざっくり決めてしまいました。ここは今後の課題。。。
学習用のデータは以下の様なフォーマットで、1行に1曲ずつコードがカンマ区切りで並んでいます。
C,G,Am,Em/G, ...
A,D,E,A,F#m,D, ...
言語モデルと比べて語彙数が遥かに小さく、ニューラルネットのサイズも小さく定義したため、学習はCore i7のCPUで数日間で終わりました。
コード進行の推定
Deep Chordでは、フォームに入力されたコード進行をニューラルネットに食わせて、推定されたコードの中から次のコードを確率的に選択しています。よって、同一の初期コードを入力しても、生成されるコード進行は毎回変わります。
ニューラルネットがうまく学習されていれば、定番のコード進行っぽいコードが生成されるという仮説のもと、いくつかのコードから進行を生成してみました。
例えば、最初のコードにC (カノン進行)と入力した場合、以下の様なコード進行が生成されました。
- 1回目: C FM9 Gm B7-9 A#7sus4 Gm EbmM7 Gb7sus4
- 2回目: C Gm Am7-5 DbM7 Ab6 C/D Gb7+5 Ab7
- 3回目: C FM9 Asus4 G#aug G#7-9 E69 G#m7 Cm6
うーん、どれも複雑な響き。現代音楽っぽいですね。CからFM9にいきやすいあたりは、ポップス的なコード進行を学習できているのかもしれません。
初期コードをもう少し長めに入力してみます。今度はF G Em (王道進行)と入れてみました。
- 1回目: F G Em A7-5 G6 Eb7sus4 G#m7 G7sus4
- 2回目: F G Em Gm G#m7 Dbm7 E69 Dbm7
- 3回目: F G Em G#aug GmM7 G#6 Gb7+5 DbM7
最初が王道進行なだけに、外れた音がするとなんかドキッとしますね。
今後の課題
少し試していただくとわかると思いますが、全然自然なコード進行が生成されません。もっといい感じのフレーズが生まれると思ったんだけど。。。
とはいえ、ここを改善すればいいかなーという勘所はあります。
曲の調や音階を利用
ハ長調とかイ短調とかって聞いたことがあるかもしれませんが、今回はそれら、曲の調(キー)の情報を使わずにニューラルネットの学習を行いました。すると、曲の途中で転調があった場合に、そこの部分のコード進行はいわば不連続な形で学習されてしまいます。
本来、それぞれのコードは調の中で役割(トニックやドミナントなど)を持っており、転調すると同じ役割をするコードも変わります。
学習の前処理として、コードの調の中での相対的な位置(役割)を求めておくと、異なる調の曲からも同様に学習が出来るのではないかと考えられます。また、短調と長調の曲を分けるというのも良い方法かもしれません。言語モデルでいうところのWord2Vecによる前処理と、学習用データのカテゴリの絞り込みのようなところでしょうか。
さいごに
より自然なコードを生成し、(自分の)曲作りのサポートが出来るよう、今後もDeep Chordのアップデートを頑張っていこうと思います。ご意見やアドバイスがありましたら、是非コメントいただけると嬉しいです。
2019/07/08 追記
次のコードを確率的に選択する部分に重大なバグを発見したので修正してみたところ、生成されるコードのランダム感が減り、よりJ-POPらしいコード進行が生成されるようになりました(学習データの大半がJ-POPなのです)。以前試されたことがある方もぜひもう一度遊んでみてください!
ちなみにバグを発見した経緯は、Deep ChordをPython2からPython3に移行する際に文法を直していたら、Python初心者(3年前の僕のことです)が書いたとしか思えない香ばしいコードを見つけ、リファクタリングしたつもりが、まさかのアルゴリズム自体のバグを修正してしまった、といった感じです。いやー、個人開発のプロダクトでもテストを書くのは大切ですね。
ついでにHTTPSにも対応させて、今どきのウェブアプリっぽくしてみました。フロントエンドがバリバリのBootstrapなのが気に入らないけど、それはまたそのうち修正します〜。