ひとり開発 Advent Calendar 2019 の16日目の記事です。
昨年に続いて参戦させていただいています。 (→ 昨年書いた記事1・記事2)
記事の概要
- ディープラーニングを活用して、ファミコンの音楽の自動生成にチャレンジしました。
- 曲をタイミングセクションとメロディ・ハーモニーセクションの2つに分解して、生成するアプローチを考えました。
- うまくいきません。助けてください。
できたもの
ディープラーニングでファミコンの音楽を生成する実験です。https://t.co/3q8fZtIYtE
— tanikawa (@diatonic_codes) December 16, 2019
なぜ作ろうと思ったか
Google Magenta や AWS DeepComposer、OpenAI MuseNet などの登場で、「音楽生成」はディープラーニングにおける応用例の1つとして注目されつつあるように思います。私も、作曲ツールなど音楽系ツールの個人開発を行っている身として、一度 Google や Amazon に立ち向かうべく音楽生成にチャレンジしてみたいと思いました。
いきなり複雑な音楽を扱うのは難しいので、シンプルなパート構成の音楽を探していたところ、ファミコン音源くらいであればうまくいくのではないかと思い、今回はファミコンの音楽の生成を試しました。
先行研究
まずは方向性を検討するにあたって、すでに研究事例が存在しないか調査したところ、ピンポイントでファミコン音楽の生成を行っている論文が見つかりました。
Chris, et al.: "LakhNES: Improving multi-instrumental music generation with cross-domain pre-training" (2019) [ArXiv] [GitHub] [生成された曲の例]
この論文のモデルは Transformer-XL 1 をベースとしたもので、単純に MIDI イベントを1ステップずつ生成していくアプローチのようです。実装と学習済みモデルが公開されているのが非常にありがたいです。
もうこれを使えばいいのではと思いましたが、実際に自分の手元の環境で動かしてみたところ、残念なことに著者らが公開しているクオリティの曲の再現ができませんでした(オリジナルはうまく生成できた例をチェリーピックしているのでしょうか?)。
LakhNES 追試(音楽プレイヤーのページに飛びます)
また、自分で1から LSTM ベースや Transformer ベースのモデルを実装したり、いろいろとデータセットを変えてみたりもしましたが、やはり曲として聞けるほどのクオリティにはなりませんでした。うまくいかない原因の1つとしては、そもそも計算資源とデータ資源が不足していることが考えられます。とはいえ、GPU とデータセットのせいにして終わりでは元も子もないので、アーキテクチャでなんとかできる部分はないか考えてみることにしました。
アプローチ
音楽は リズム・メロディ・ハーモニー の3要素から成り立っているとよく言われます。上記の手法では、この3要素を1つのモデルでまとめて処理しているわけですが、この処理部をタイミング(リズム)セクション 2 とメロディ・ハーモニーセクションの2つに分けることで、個々のタスクの難易度を下げて成功確率を高められるのではないかと考えました。これを踏まえて構築したモデルの模式図は次の通りです。
タイミングセクションは、どのタイミングで(bar: 小節 と tick: 小節内のティック)どのチャンネルのイベントを発生させるか(channel: 矩形波1 or 矩形波2 or 三角波)を決めます。図の通り、例えば既存曲のタイミング情報だけを引っこ抜いてくることもできますし、テンプレートパターンをある程度用意しておいてそこからランダムに選択するなどのアプローチが可能です(冒頭の動画は後者の方法を用いています)。
メロディ・ハーモニーセクションでは、タイミングセクションから受け取った入力をもとに、各タイミングにおける音の高さ(ノートナンバー)を1ステップずつ決めていきます。
input 1 に scale というものがいきなり出てきていますが、これは音階情報(どの音階を使ってよいか)を指定するものです。今回は時間がなく、手動で音階の進行パターンを作ってしまいましたが、コード進行の推論 を応用することで、自動で作ることも可能だと思います。
また、input 2 の prev note number は、1つ前のステップで出力された MIDI ノートナンバーです。output の方の note number は、そのステップにおけるノートナンバーの予測です(どちらも0〜127の値を出力します。ただし例外的に note off を 0 に割り当てています)。
Generator の後ろに Self-Attention を挟んでいますが、これは過去のタイムステップにおける Generator の出力を参照する形式をとっています。
結果
学習データは、先行研究 LakhNES と同様のものを今回のモデルのインプットに合わせて加工して使いました。学習の結果は冒頭の動画の通りです。全然ダメです…。1曲目は割と良い感じに聞こえる気もしますが、全体的に LakhNES より良くなるどころか、ひどくなっているのではないかと思うレベルです。
ちなみに、実装初期は Self-Attention を導入していなかったのですが、その後 Self-Attention を導入することにより、メロディのリフレインなど、フレーズの構成が若干曲っぽくなりました。Attention は重要みたいです。
ただ一方で、過剰に同じフレーズが繰り返されることによって、曲の展開に欠けたり、コード進行を無視したり、すぐにオクターブユニゾンしだしたり、などといった点が目立つようになったので、展開とリフレインの兼ね合いをうまくコントロールすることが課題です。
おまけ
ファミコンでもっとも有名であろうマリオの地上 BGM のタイミング情報を拝借して、メロディ・ハーモニーセクションだけ再構築するとどんな曲ができるか確かめてみました。scale の情報として、D メジャースケール G→A→D→Bm の循環進行を与えています 3。
マリオ乗っ取り曲(音楽プレイヤーのページに飛びます)
名曲を台無しにしてしまいました。あらためてメロディとハーモニーの重要性を痛感します。
まじめに考察すると、曲の最後の方でスケールアウトする音が少し出てきますが、これは Exposure Bias の影響だと思います。1音スケールアウトすると、そこからずるずるとスケールアウトが顕著になってきます(ほかの生成結果でも同様の傾向が見られました)。scale による制御を無視した生成を行っているので厄介です。
おわりに
いろいろと試行錯誤していますが、今のところまったく Google や Amazon に太刀打ちできません。
一応、最終的な目標としては DADABOTS のように YouTube で24時間365日配信できるレベルまでいきたいです。これからもがんばります。
…なんだか個人開発っぽくない話になってしまいました、すみません…。田舎暮らしなので周りに AI 系の研究者やエンジニアがほとんどおらず、毎日孤独に研究しています。
-
Dai, et al.: "Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context" (2019) ↩
-
「リズム」と書くとドラムのリズムと混同しそうなので「タイミング」に置き換えます。 ↩
-
原曲と同じコード進行を与えてもいいですが、絶対に劣化コピーしかできないので、大胆に変化を加えています。 ↩