【作っておぼえるMIR】Transformerで自動ピアノ採譜

音楽の音響信号を楽譜に変換する自動採譜手法、いわゆる「**自動耳コピ**」。音楽情報処理の永遠のテーマの一つです。

自動採譜モデルは長らく、**音楽記号を等間隔な時間フレーム単位（およそ数十ms単位）のラベル列に変換する**ことで定式化されていました。例えばピアノ採譜の場合、時間軸をフレーム単位に分割した2次元行列、いわゆる「ピアノロール」の形式に変換し、DNNに推定させています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/41c93c33-7e3e-15b7-1551-fb4b8f59bbe3.png)
これは音響特徴ベクトルと時間単位を揃え、音楽記号のタイミングを正確にあらわすための戦略です。これまで色んなタスクでそこそこ上手くいっていたのですが、音楽記号を細かく刻むことで色んな問題も生んでいました。

どうにか、音楽記号本来の構造を保ったまま、自動採譜モデルを作れないか...この課題に挑んだのが、Google Magentaプロジェクトから提案されたSequence-to-sequenceピアノ採譜モデル、およびそれを拡張したMT3モデルです。

https://magenta.tensorflow.org/transcription-with-transformers

今回はSequence-to-sequenceモデルを使い、自動ピアノ採譜アルゴリズムを作る実験をしてみます。

## Jupyter Notebook実装例

https://github.com/Xiao-Ming/TsuKuObo-MIR/blob/master/notebooks/Seq2Seq-Piano-Transcription.ipynb

## Sequence-to-sequence構造

方法はずばり、**Sequence-to-sequenceな変換ができるTransformerを使って、音楽音響信号のスペクトログラムを直接楽譜記号列に変換する！** 力技です。

スペクトログラムを時系列として扱えるのは自明です。
一方、楽譜を時系列で表すのはちょっと自明ではありません。音符は等間隔・直線的に並んでいるわけでは無く、色んなタイミングや持続長を持ちうるし、複数の音符が同時刻に重なることも多いです。

研究者らは、MIDIメッセージから着想を得た、ノートオン・ノートオフイベントで音符のタイミングを制御する**MIDI-likeトークン**を利用しました。
元々は音楽パフォーマンスをモデル化するために考案されたトークンです。

https://arxiv.org/abs/1808.03715

このトークン文法を使えば楽譜を1次元のトークン系列で表現できるので、Seq2Seq変換を実装することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/c4c8659e-2e75-7054-f62a-190fc8447cf1.png)


ただし、Transformerは（主にSelf-Attention機構のせいで）あまり長い系列を扱えないという弱点があるため、１曲分のスペクトログラムとトークン列を丸ごと与えるのは厳しいです。
また、音楽採譜の局所性を考慮しても、長すぎる系列ペアを与えて学習・推論の難易度を無駄に上げる必要はありません。
なので学習・推論の際は、２秒間の音声・楽譜の**セグメント**を切り出して、学習・推論に使います。推論時は、２秒のセグメントごとにトークン列を推定してから繋ぐ、という処理をしています。セグメントを跨ぐ音符に対応する処理が必要になりますが、そのためのトークン文法も定義されています。



## 楽譜のトークン化

楽譜をトークン列に変換するために、トークンとトークン列の文法を定義します。

### トークン定義
以下の種類のトークンを定義します。

- **Note**: 音符の音高をあらわすトークン。MIDIノートナンバーに対応した128個の値をとる。
- **Time**: セグメント先頭からの時間を表すトークン。10ms刻みで、205個の値をとる。
- **On/Off**: ノートオン・オフの状態をあらわすトークン。2つの値をとる。
- **End Tie Section**: トークン列の"tie"セクション（後述）の終了をあらわすトークン。
- **EOS**：トークン列の終わりをあらわすトークン。

以上、計337個の値をとるトークンを定義しました。これらのトークンからなる系列で、ピアノの楽譜を表します。

次にトークン系列の構造を定義します。
トークン系列は"tie"セクションと"events"セクションからなっており、**End Tie Section**トークンによって区切られています。"tie"セクションが空の場合、系列は**End Tie Section**トークンから始まるとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/9c8b5698-4443-eaee-8ef5-f8a6221091d3.png)

## tieセクション

tie（タイ）とは、同じ高さの音符を繋ぐ、あの弧線を指す用語です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/6fef6a1b-eafd-4dda-99f0-d4f365164e17.png)
音が途切れていないことをあらわす記号ですね。

トークン列におけるtieセクションはNoteトークンのみで構成されており、「**前のセグメントで終止していない音符**」を示します。
tieセクションに含まれる音符は、本セグメント、あるいは更に先のセグメントで終止することになります。
前のセグメントで終止していないのに、tieセクションに含まれていない音符がある場合、それらの音符はこのセグメントの開始地点で終止させます。

### eventsセクション

Note、Time、On/Offトークンからなり、EOSトークンで終了する、本セグメントの音符配置をあらわす部分です。
基本的に、**Time、On/Off、Noteトークン**の順に並べることで一つの**音符イベント**を表します。これら3つの値の組み合わせで、本セグメントにおける音符の開始・終止イベントの位置を指定します。
次の音符イベントが前と同時な場合や、同じタイプ（開始or終止）である場合、TimeトークンやOn/Offトークンは省略します。

一例を下に示します。


上の例のように、すべての音符が本セグメント内で終止するとは限りません。このような音符は次のセグメントのtieセクションを通して引き継いでもらうことになります。


## 学習

トークン列を定義出来たら、ピアノ音セグメントのMel-Spectrogramと、対応するトークン列の対を使って、Sequence-to-sequenceの変換を学習させます。

元の論文では、Googleラボが考案した「T5」というTransformerをSequence-to-sequenceモデルとして採用しています。Notebookでは、Hugging FaceのTransformersライブラリのT5実装を利用します。forward関数一行でlossまで計算してくれたり、トークン推論も一行でできる親切設計。なのでニューラルネットに関するコードは非常に少なく済んでいます。

## 評価

mir_evalライブラリに実装されている、自動採譜評価関数を使います。

大まかなルールは、「**推定された音符と正解音符のノートオン・ノートオフのタイミングのズレが一定範囲内に収まっている場合、推定結果は正しいと見なす**」です。
具体的には、「ノートオンのタイミングのズレが±50ms以内、ノートオフのタイミングのズレが50ms以内あるいは音符の長さの20%以内」と定められています。ノートオフのタイミングはノートオンよりも判断が難しいので、判定基準はノートオンの基準より若干緩くしています。

上述のルールに従って、ノートオフを考慮した・考慮しない場合のF値をそれぞれ計算し、自動採譜の正確性の評価基準とします。

## 実験結果
Maestroデータセットのテストデータスコアはこうなりました。
（小規模データで回した、Jupyter Notebookに記録された結果とは異なります）

| Test metric | Score |
|:-:|:-:|
| Average_Overlap_Ratio  | 89.17 % |
| Average_Overlap_Ratio_no_offset | 67.94 %  |
| F-measure   | 58.46 % |
| F-measure_no_offset   | 94.28 %  |
| Precision   | 59.91 %  |
| Precision_no_offset   |  96.57 % |
| Recall   | 57.15 %  |
| Recall_no_offset   | 92.21 %  |
| Offset_F-measure   | 78.84 %  |
| Offset_Precision   | 80.42 %  |
| Offset_Recall   | 76.65 %  |
| Onset_F-measure   |  95.35 % |
| Onset_Precision   |  97.68 % |
| Onset_Recall   | 93.23 %  |

ノートオンのみを考慮した指標は90%以上の高いスコアが並んでいますが、ノートオフ認識の精度が今一つでした。元論文の実験結果ではオフセット認識の精度も良かったみたいなので、まだ改善の余地はありそうです。

## 応用：ポピュラー音楽のピアノカバー生成

上記のseq2seq手法を応用したものとして、2022年に公開され、ICASSP2023に論文採択された「**Pop2Piano**」が挙げられます。ポピュラー音楽を入力し、ピアノカバーを生成するモデルです。

https://github.com/sweetcocoa/pop2piano

ポピュラー音楽とピアノカバーのペアを沢山用意すれば、seq2seqモデルを使ってピアノカバー生成を学習させることができます。この学習データを作成する方法が、Pop2Piano論文の主なテーマの一つになっています。

DTWによる時間同期・拍位置推定・音源分離・メロディー推定を組み合わせた前処理を行っているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/c860e992-a673-70da-34d0-096c155db6fb.png)
さらにPop2Pianoは、トークンの定義やモデル構造にいくつか工夫を加えています。
* timeトークンの単位が0.1秒から8分音符（半拍）に変更
* 入力スペクトログラムの長さが一定長（２秒）から４拍に変更
* アレンジスタイルの条件付けを追加

オリジナルのMIDI-likeトークンが考慮していなかった**拍の情報**に基づいた構造になっています。