機械学習なんもしらないけどピアノ採譜AIを作りたかった
どうもこんにちは、だだっこぱんだです。
「今年の最も大きなチャレンジ」 ということで、今回は夏休みにちょくちょくやっていたピアノ採譜AIを作るという挑戦について書いていきたいと思います。
ちなみに結論から言うと、一部うまくいきませんでした。けれどもまだまだ諦め切れずにはいます。もし機械学習に強い方で何か気がついたことや、アドバイス等ありましたら、ぜひ教えていただけると嬉しいです。
👇やろうと思ったきっかけになったツイート
既存の技術
まずは既存の技術を調べていきます。
これについては主にFくんに調べてもらいました。ありがとう😊
このスクラップと同じような内容になってしまいますが、再度まとめていきます。
Onsets and Frames
Googleから出ているピアノ採譜AI。Onsets(各音符の開始位置)とFrames(音符が存在するすべてのフレーム)を別タスクに分けて検出することで精度が大幅に上がったとのこと。
hFT-Transformer
音を周波数(音の高さ)軸と時間軸にtransformerを重ねることで正確に識別できるようになったとのこと。
MT3
T5の転移学習でmidi-like tokenを予測させているとのこと。
Transkun
セミマルコフCRFモデルを使っているらしいとのこと。よくわからんかったけど名前好き。
bytedance/piano_transcription
ペダル推論もできる。
なんか三角関数を使ってるらしい。
ConcertCreator
wav入力ができて、おそらくではあるが左右分離されたペダル推論つきのmidiをAIで生成している。
比較
mt3, hFT-Transformer, ConcertCreatorを比較しました。
ここで比較していないものもいくつかありますが基本的にhFT-Transformerや、mt3と同じ感じでした。
問題点
主に以下の点が自分の感じた問題点です。
- ペダルが推論されていない
- ノートが必要以上に伸びている
ペダルが推論されていない点についてはそのまんまです。
「ノートが必要以上に伸びている」というのは、人間が演奏する際にそんなに押し続けないやろ!ってくらいにノートが伸びていることです。
ノートが伸びている例
ノートが伸びていない例
いざ演奏しよう!と思う際にみてわかりやすいのは下だと思います。
今回はこの2点の問題を解決するのをゴールにしていきます。
既存の技術を見ていく
とりあえずまずは、既存のものが一体何をしているのかを、初心者なりに理解しようと思いました。
まず見てみたのがhFT-Transformerです。
hFT-Transformer
自分は、AI関連でコードの理解をしたい時は、コードを読んでリライト(0から書き直す)する、ということをしています。ということで、hFT-Transformerを見ながら自分なりの書き方でリライトしてみました。
実際にリライトしたものは以下になります
このhFT-Transformerがやっていることを自分が理解できた範囲でまとめると以下のようになります。
- wavをlog-melspectrogramに変換
- それをモデルに入力 (たぶんtransformerってやつをやってるんだと思ってる、Attentionとかあったし。しらんけど。)
- モデルから、
onsets
,offsets
,mpe
,velocity
の4つの情報が出力される - それらをmidiに変換
といった流れです。
onsets
, offsets
, mpe
, velocity
はそれぞれ、ノートの開始、終了、存在している時間、強さを表しています。全て[T, 88]
の形で出力されます。
データセットの処理
まず、ピアノ採譜AIでのデータセットは基本的にmaestro-v3.0.0を使っています。
約200時間のMIDIとその音源がペアになったデータセットです。
MIDIはおそらくwav収録に使用した電子ピアノのMIDI出力をそのまま使ったものでした。
hFT-Transformerでは、このデータセットを以下のように処理していきます。
まず、MIDIはonsets
, offsets
, mpe
, velocity
に変換されます。
この際に、offsetsをペダルが踏まれている分だけ伸ばす処理がデフォルトで入っています......!
ということでここで1つの問題が解決しました。ノートの伸びを解決するためには、offsetsをそのままのデータでラベル付けすればいいだけです。
これは他のピアノ採譜AIでも同じ処理をされていました。ペダルを推論しない代わりにノートを伸ばして入力音声に近づけていたのかと思われます。(bytedanceのピアノ採譜AIはペダル推論できるのにノートを伸ばす処理をしていました。謎いですね。)
ペダル推論
さて、残すはペダル推論だけです。
まずは、hFT-Transformerにペダル推論機能を実装していく方針で進めました。
というかここについては僕じゃなくてFくんが色々実装考えてくれました。ありがとう😊
👇Fくんが書いてくれたコード
これを参考に、自分のリライトしたリポジトリにペダル推論機能を実装していきました。
方針その1
始めに、ノートのラベルを onset
, offset
, mpe
, onpedal
, offpedal
, mpe_pedal
, velocity
に変更しました。
1つ1つのノートにそのタイミングでペダルが踏まれていたかどうかの情報を持たせた、という感じです。
これで学習した結果は、以下のとおりです
ものすごい数のペダルイベントが推論されてしまいました。
推論後の後処理が良くなかったのか、方針自体が良くなかったのかなんとも言えないですが一旦別の方法を考えることにしました。
方針その2
今度は、ペダルとノートを別ものとして捉えて学習するようにしました。
ノートのラベルは onset
, offset
, mpe
, velocity
に戻し、ペダルのラベルは onpedal
, offpedal
, mpe_pedal
としました。
しかし結果は、ノートすら推論されないという結果になりました。
方針その3
方針その2では、ノートとペダルに同じnn.Module
を使用していたので、それらを全て分けるように実装し直しました。
そうすると結果は少しだけ変わりました。
ノートの推論は完璧な形に
しかし、このまましばらく学習を進めていくと。。。
なぜかノートが推論されなくなってしまいました。
方針その4
なんならもうペダルとノートの学習自体分けてしまった方が良いのでは!!
ということでモデルごと分けて学習することにしました。
結果はかなり惜しいところまで行きました。
冒頭はすこしおかしいところがあったのですが後半の推論がかなり良かったです。
しっかり「小節の頭でペダルを踏み直す」、という動作が推論できていたのでかなりテンション上がりました。
しかし、そのまま学習を進めていくと、結果があまり変わらなくなりました。なんなら少し悪化しているくらいになってしまったので、この方針も失敗と判断しました。
諦め
hFT-Transformerにペダル推論機能を追加するのはなんかきつそうと思ったのでやめました。
ByteDanceのピアノ採譜AI
次に、ByteDanceのピアノ採譜AIを見ていくことにしました。
大まかな流れはhFT-Transformerと同じで
- wavをlog-melspectrogramに変換
- それをモデルに入力
- モデルから、
onset
,offset
,reg_onset
,reg_offset
,frame
,velocity
の6つの情報が出力される - それらをmidiに変換
といった流れです。
頭にreg_
がついているのはおそらく、三角関数関係?のものかなと思っています。
一応理解するためにコードのリライトはしましたが、なんかわからんところがおおかったです。
解決?
これについてはもうペダル推論機能はあります。あとはノートの伸びを解決するだけです。
hFT-Transformerの時も説明しましたが、ノートの伸びの原因はoffsetsをペダルが踏まれている分だけ伸ばしていることです。
ということはこれをオフにして学習すればいいだけじゃん!!と思い、学習を進めようとしました。
処理速度
いざ、学習をさせようとしたら、なんかすごくおそいです。
GPU使用率が一瞬100%になってすぐ0%に落ちるという挙動をしていました。
調べて行ったところ、get_regression
という関数がものすごく時間を使っていることがわかりました。
この関数は冒頭の説明で述べた、三角関数の計算をしている部分なのではと思っています。
先に計算してしまおう
学習にはRunpodのGPUサーバーを借りていたので時間をかけるとその分お金がかかります。そのためGPUの使用率はなるべくあげたいです。
そこで、get_regression
の計算を先にしてしまい結果を保存して、学習時にはそれを読み込むだけに使用と思いました。
事前処理にget_regression
の処理を追加して、いざ家のPCで計算を行おうとしたのですが、終わるのにかかる時間がなんと約70時間...
vultrでベアメタルのつよつよCPUサーバーを借りて、計算しても7時間。。。
諦め
流石に時間かかりすぎるのと、bytedanceのコードは若干よくわからないところが多かったので、一旦諦めました。
Onsets and Frames
最後に、Onsets and Framesを見ていきます。
これのpyrorch実装のコードがすんごく見やすくてありがたすぎました。
これもリライトしました。
処理の流れはこれもhFT-Transformerとだいたい同じで
- wavをlog-melspectrogramに変換
- それをモデルに入力
- モデルから、
onset
,offset
,frame
,velocity
の4つの情報が出力される - それらをmidiに変換
といった感じでした。
ペダル推論
Onsets and Framesにはペダル推論機能がないので、自分で実装していかないといけません。
これについてはhFT-Transformerで行った方針その4と同じく、ノートとペダルを別々に学習することにしました。
結果、ノートの推論は普通にできたのですが、ペダルの推論はやはりできなかったです。
関係あるのかわからないですが、lossがすごい動きをしていました。
特にframeのlossがすごい変な動きをしていたので、これが原因かもしれないと思いました。
冒頭で述べた通り、Onsets and Framesのframeの部分は音符の長さです。ペダルには長さがなく、ただのon/offだけなので、frameはそもそも要らなかったのかも?と思いました。
この考え方が正しいのかわかりませんが。。。
とりあえずframeを削除して学習を進めてみました。
結果、lossの動きは安定しましたが、値がものすごく小さくなりました。
推論もペダルは全く推論されない状態でした。
諦め
ここまでで約1ヶ月経ちました。そろそろ疲れてきたので一旦やめて、この記事にまとめることにしました。
まとめ
基礎的な知識がなく、なんとなくでやってきましたが、やっぱり機械学習は難しいなと言うことがわかりました。コードのを見ていても何をやってるのかよくわからない関数だったりクラスがいっぱいなのでまずはこのあたりをしっかり知っていく必要があるなと感じました。
しかし、少しでもペダル推論が綺麗に動いたときはものすごく感動しました。ゴールには達しませんでしたが、まさかここまで作れるとは思っていなかったので始めた時の想定以上の結果が出せたと思います。
たすけて
ピアノ採譜AI(というかピアノのペダル推論AI)についてはまだまだ諦めてないのでゆっくりと勉強しながら進められたらと思っています。
冒頭でも書いた通り、もし機械学習つよつよな方でお手伝いしてくださる方がいたらすごく嬉しいです!!!!!!!!!
Discussion
とても面白い記事で勉強になりました。これだけ手を動かしているの…流石です。
何かしら意見を求めていそうなので読んで感じたことを書いてみます。私は音声分野やこのタスクについては全くの門外漢なのと、コードも読んでいないのでトンチンカンなことも書いてますでしょうし、アイディアの参考程度にしてくれたらと思います。
hFT-Transformerの学習について
方針その3について
最初はノートを検出できていたけどそのあと検出されなかったとのことですが、ノート推論とペダル推論の loss の計算部分でそれぞれ重み付けなどされてるといいかも、と思いました。この場合だとペダル推論のほうの重み計算の割合が大きくて、そっちに引っ張られて後半学習が崩壊してるのかな?など思ったりしました。
方針その4について
こちら結構うまくいったとのことですがペダル推論のほうがうまく行かなかったとのこと。
これは完全に所感なんですが、ペダルが押されているか押されていないか、という2値分類のタスクなら波形から割とかんたんに推論できそうなタスク?な気もするので、何かしら設定やモデルの問題なのかなと思いました。(実際にやったことはないのでただの所感です)
この場合だと spectrogram から切り出す窓の長さはペダル推論はノート推論よりも長いのかなと思いました。なんとなくノート推論は比較的短い窓で推論でき、ペダル推論はもう少し長い窓で推論しそうな気がした、というだけです。
ByteDance のモデルについて
既にペダルの推論もできてますし、これがうまく学習できたら一番ハッピーな気がしますね…
Onsets and Frames について
これ学習時のデータがどうなってるかわからないのですが、ペダルの on/off をそのまま学習してるのでしょうか?このモデルが何かしらノートが押されたタイミングと押されているフレームに分けて学習しているっぽいので、ペダルも押されたタイミングと押されているフレームに分けるような前処理をして学習するべきなのかな、と思いました(既にそうしているかも。)
以上読んで思った所感でした。既に色々手を動かされておりタスク難易度の肌感もすごくあると思いますので参考程度に...。
とても面白い取り組みだと思うので、頑張ってください!
コメントありがとうございます🙏
すごくありがたいです!!!
hFT-Transformerの学習について
方針その3について
これは
みたいなイメージであっていますでしょうか?
方針その4について
すいません!この辺り自分のspectrogramへの理解が0に等しいのであんまり理解できなかったです。
まずはそこから勉強するべきだと感じたので勉強します!
ByteDance のモデルについて
ある程度計算資源にお金かければなんとかなるのですが、それだとなんか面白くないのであえてスルーしたところもあります😓
Onsets and Frames について
frameを使わない処理をする前はそのような処理でした。pedalのon/offをnoteのon/offと捉えて、
onset
,offset
,frame
のラベル付けをしました。いま記事見直すと、onsets and frameの説明だけやたら雑ですね、、、
後ほど追記します。
そして今いただいたコメントを読みつつ返信を書いていたら、記事内の
というのはやっぱり間違っているような気がしてきました。
この辺りもう一度見直して再度実験していきたいですね。
方針その3について
そうです!
方針その4について
私もそこらへんあまり詳しくないですが、spectrogram は音声を各フレームで周波数分解して(ここらへんはフーリエ変換とかででてきます!)それが時間方向に並んだものが入力になってそうで、その時間幅をどれくらいとるか?みたいなパラメータですね。(多分この添付画像だとM+1+Mに相当してそう) 例えばどのノートがなってるかどうかは割と1秒とか切り出しても推定できそうですが、リバーブがかかってるかどうかは1秒とかの短い時間より5秒とかのほうが判定しやすいと思ったりしました。
なんか間違ってたらすみません。
めちゃくちゃ理解しました!!!わかりやすく説明いただきありがとうございます...!!
この辺りのパラメータの調整は全くやってなかったので試したいですね。
ピアノ採譜AIについて、この論文は結構面白いですよ。ぜひ見てください。
論文のURL:https://arxiv.org/pdf/2208.14339
作者たちはそのコードをアプロードした:https://github.com/WX-Wei/HPPNet
一般的に言えば「Transformer」が有効的であるが、この論文は「LSTM」を活用して、効率的だと思います。