【作っておぼえるMIR】TCNで音楽ビート・ダウンビート位置自動推定

DJアプリや自動耳コピソフトにとって、音楽のビート・ダウンビート（＝拍・小節線）の位置を正確に特定する技術は欠かせません。
ほかの様々な分野と同様、ビート・ダウンビート位置推定もDNNベースの手法が成果を上げています。今回は実際にDNNを使った推定モデルを作ってみます。

# Jupyter notebook実装例
https://github.com/Xiao-Ming/TsuKuObo-MIR/blob/master/notebooks/Tempo-Beat-Downbeat-Estimation.ipynb

# 主な参考資料
* 【ISMIR2021チュートリアル】Tempo, Beat, and Downbeat Estimation　https://tempobeatdownbeat.github.io/tutorial/intro.html
* 【TCN】EP MatthewDavies and Sebastian Böck. Temporal convolutional networks for musical audio beat tracking. In 2019 27th European Signal Processing Conference (EUSIPCO), 1–5. IEEE, 2019.
* 【状態空間モデル】Krebs, Florian, Sebastian Böck, and Gerhard Widmer. "An Efficient State-Space Model for Joint Tempo and Meter Tracking." ISMIR. 2015.

# 処理の流れ
一般的なビート推定アルゴリズムは、音響特徴抽出⇒ビート確率推定⇒後処理　という手順を経てビート位置を求めます（ダウンビートも大体同じ）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/71cd8ad6-b831-bbc8-a9e9-6213c3187253.png)
1. 音響特徴抽出：音楽の音響信号を特徴ベクトル系列に変換します。音楽的な構造を強調したり、時間軸の次元数を削減するための処理ですが、何もしない（End-to-endなモデル）という手も一応あります。
1. ビート確率推定：各地点のビート（ダウンビート）である確率を求めます。一般的にDNN時系列モデルに任せる部分です。
1. 後処理：2で求まった確率値の系列から、具体的なビート位置を決める処理です。一般的に、拍・小節の音楽的な性質（拍の等間隔性など）に基づいた確率モデルを利用します。

# 音響特徴抽出
音楽のリズム的な特徴を強調するために、Chroma・MFCC・オンセット特徴を計算したり、特徴の隣接フレーム差分を計算するなどのテクニックが使われてきましたが、DNNベースの手法では特に手の込んだ特徴エンジニアリングをせず、log-scaleやmel-scaleなスペクトログラムを使っても十分機能するようです。

特徴量の時間分解能がそのまま推定結果の分解能になるので、フレーム間隔は短めに設定した方がいいでしょう。100fps（10ms間隔）に設定することが多いです。

# Temporal Convolutional Network (TCN)
時系列予測を行うために設計された畳み込み系のネットワークです。Wavenetのように、Dilated Convolutionを重ねることで受容野を広げ、長いコンテキストを学習できるような設計になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/637e28f5-4d2f-151f-bb5c-e96fa75c41d8.png)
これなら音声波形を直接入力するend-to-endモデルも作れそうな感じがしますが（実際やってみた人はいます[^1]）、ここではスペクトログラムに変換してから入力します。

2次元データである入力スペクトログラムは、まず畳み込み層とMax-poolingを数回経て周波数軸を圧縮してから、1次元データとしてWavenetライクなTCNに入力します。TCNは、入力スペクトログラムと同じ長さの確率値系列を出力します。

[^1]:Christian J Steinmetz and Joshua D Reiss. Wavebeat: end-to-end beat and downbeat tracking in the time domain. arXiv preprint arXiv:2110.01436, 2021.

```mermaid
graph TD;
    classDef White fill:#ffffff,stroke:#ffffff
    spec[スペクトログラム]:::White
    conv(畳み込み + Max Pooling)
    prob[確率値系列]:::White
    tcn(TCN)
    spec-->conv;
    conv-->tcn
    tcn-->prob
```
# 教師あり学習
TCNは楽曲音声のスペクトログラムを受け取り、フレームごとのビート（ダウンビート）確率を出力します。この出力に対する正解ラベルは、ビート位置に対応するフレームが1.0、それ以外が0.0になるような系列です。
出力とラベル列の $binary\\_cross\\_entropy$ を最小化することが学習目標になります。

正解ラベルを作るとき、**Label widening**というトリックがよく使われます。正解のビート時点にだけ、ピンポイントで1.0を立てるのではなく、その時点の隣接フレームにも0.5くらいの値を与えて、正解を若干「ぼやけ」させる手法です。
これにより、学習がより収束しやすくなると言われています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/043d5972-f011-e59e-584a-d9060e145a83.png)
**マルチタスク学習**、すなわちビート・ダウンビート位置・グローバルテンポを１つのネットワークで推定するよう学習する手法も、このタスクに置いては有効みたいです[^2]。

[^2]:Sebastian Böck and Matthew EP Davies. Deconstruct, analyse, reconstruct: how to improve tempo, beat, and downbeat estimation. Proc. of ISMIR (International Society for Music Information Retrieval). Montreal, Canada, pages 574–582, 2020.

```mermaid
graph TD;
    classDef White fill:#ffffff,stroke:#ffffff
    spec[スペクトログラム]
    conv(畳み込み + Max Pooling)
    avg(Average Pooling)
    beatprob[ビート確率]
    dbeatprob[ダウンビート確率]
    tempo[グローバルテンポ]
    tcn(TCN)
    spec:::White-->conv;
    conv-->tcn;
    tcn-->beatprob:::White;
    tcn-->dbeatprob:::White;
    tcn--中間特徴-->avg;
    avg-->tempo:::White
```
Notebookの実装例では、（有効性は検証してませんが）これらのテクニックを適用しています。

# 状態空間モデル(SSM)による後処理
ニューラルネットが出力した確率はこんな感じになります。赤い点線が正解のビート・ダウンビート位置です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/e472f34f-8060-9d72-0b48-1982139d3585.png)
大半の正解位置にちゃんとピークが立っていますが、シンコペーションのせいかピークが立っていない（低い）所があったり、正解と違う位置にもピークが沢山立っています。単純にピークだけで判断すると間違いやすいので、拍・小節自身の性質も加味して、ビート位置を最終決定するのが後処理過程です。

拍・小節の性質といえば、要は**等間隔性**です。ビートの位置は、たいていの場合一定の間隔を保っています。一方で、微妙に間隔が前後したり、ズレが積み重なっていつのまにかテンポが変化することもあります（生演奏レコーディングなど）。意図的にテンポを上げ下げすることも、もちろんあります。

絶対的なルールはありませんが、明らかに確率的な偏りはありますよね。
このような確率過程を表現する**状態空間モデル**（**SSM**）を用いて、ビート位置を決める方法を紹介します。

まず、以下のように配置された、離散的状態のセットを考えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/f2dfe83a-fb55-2f93-65c9-dec383b8a4e8.png)
この図では、
* 1つの点が1つの状態で、あるテンポにおける、一拍内の1つの位相を表しています。
* 黄色の点（ゼロ位相の状態）はビート位置、灰色の点は非ビート位置を表します。
* テンポが速いほど、1拍に含まれるフレーム数が少なくなり、位相の間隔が大きくなるため、鐘のような形になります。


そして状態遷移は以下のルールに従い、１フレームごとに１回遷移します。
* 位相が最大（=いちばん右）でない状態にいる場合、次の位相に100%の確率で遷移する（**赤矢印**）。
* 位相が最大な状態にいる場合、高確率で同テンポのゼロ位相状態に遷移する。低確率で、近いテンポのゼロ位相状態に遷移する（**黄矢印**）。離れたテンポほど、遷移確率が低くなる。
![ssm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/a6834945-78db-a05c-480e-f9e66ea68289.png)


DNNによって推定された、とある時点のビート確率を$p$とすると、ゼロ位相状態の確率=$p$、その他の状態の確率=$1-p$　とみなすことができます。
状態確率と遷移確率の積（対数確率の和）を最大化する状態列は、動的計画法（Viterbi推定）で求めることができます。
その状態列におけるゼロ位相状態の位置が、ビート位置になります。

ダウンビート位置も、1拍ごとに遷移するSSMを作れば、同じ方法で求めることができます。たとえば、2拍子～6拍子の小節に対応できるSSMは、以下の図のようになります。
![ssm_downbeat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/b40c2e03-6f52-55b4-5d15-e3f540fab834.png)
実際にSSMを使って推定すると、正しいビート・ダウンビート位置を見つけることができます（緑の点線）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128515/d052edfe-bd34-6bde-6341-fbeeff5cf29a.png)