前回、SwiftF0の推論処理をC++で実装してチューナーアプリに組み込んでみたが、誤差が20cent近くあることが分かった。
チューナー用途では、20centの誤差は許容できないため、ピッチ推定のアルゴリズムは既存のものを使い、決定木の機械学習モデルの訓練データ生成にSwiftF0を使うことにした。
疑似ラベリング
SwiftF0は、チューナー用途では誤差は大きいが、半音範囲では正確に推論できるため、オクターブエラーのグランドトルゥースとして利用できる。
マイク録音したアコースティックギター、オーディオインターフェースで録音したエレキギターギターとベースの録音音声を用意して、SwiftF0でピッチ推定を行い、一定のピッチが連続する区間から、サンプリングして訓練データを作成する。
連続するピッチなしの区間からも、ピッチなしのデータとしてサンプリングを行う。
データ拡張
SwiftF0の論文に記載されていた方法を参考に、CHiME-Homeデータセットの環境音と白色ガウスノイズを混ぜたものを、SNR 30dBで加えた音声を作成した。
SwiftF0の論文では、SNR 10dBとしていたが、そこまでうるさい環境を想定していないので、軽いノイズレベルとした。
訓練データ生成
音声から訓練データを生成するプログラムをGPT-Codex-5.3で生成した。
プロンプト:
現在のピッチ検出アルゴリズムのオクターブエラー訂正の処理を機械学習の決定木のアルゴリズムに置き換えたい。 そのために、まずは、以下の特徴量として、SwiftF0をグランドトルゥースとする訓練データを作成したい。 以下の訓練データを作成するプログラムを作成してください。 音量がしきい値以上の区間で、 1. SwiftF0の音階が1秒以上連続する区間から - PitchAnalyzerと音階が一致するサンプルを1秒あたり2つ(1秒より短くても最低1つ以上) - PitchAnalyzerと音階が一致しないサンプルを1秒あたり3つ(1秒より短くても最低1つ以上) - PitchAnalyzerのオクターブエラー訂正前のpeak_frequencyと音階が一致しないサンプルを1秒あたり3つ(1秒より短くても最低1つ以上) 2. SwiftF0が信頼度が低く、かつ、PitchAnalyzerでピッチがない区間から - ピッチなしのデータを2つ 読み込んだWAVファイルに対して、ノイズを加えるデータ拡張を行うオプションを追加してください。 オプションで環境音ノイズファイルリストを受け取り、そこからランダムで選んだWAVと、白色ガウスノイズを加えて、SNR デフォルト30dB付近のノイズを加える。 一つのWAVにつき、ノイズなしとノイズありを入力データとする。 ## 入力データ - コマンドライン引数でWAVファイルを受け取る - WAVファイルは複数指定可能 ## 特徴量 (略) ## 正解データ - SwiftF0のf0 ## 訓練データの形式 - CSVファイル - 列 (略)
訓練スクリプト
決定木の学習スクリプトをGPT-Codex-5.3で生成した。
プロンプト:
上記仕様で作成された訓練データのCSVを読み込んで決定木を学習するプログラムを作成してください。 決定木の深さはデフォルト5とする(コマンドライン引数で変更可) 特徴量は以下のように加工する (略) 正解ラベルは以下のように加工する (略) 学習後訓練データに対する評価を行い結果を表示する
C++の推論コード生成
決定木から、C++の推論処理を生成するようにした。
プロンプト:
train_decision_tree.pyに、訓練した決定木を推論するC++のソースコードを出力するオプションを追加してください。 特徴量加工処理も行いようにしてください。 結果は、周波数(Hz)で出力するようにしてください。
チューナーアプリに組み込み
チューナーアプリに組み込みを行った。
プロンプト:
PitchTunerフォルダにあるC++/WinRT(WinUI3)のプロジェクトでは、ピッチ検出によるチューナーを実装している。 現在のピッチ検出は、FFTを使用したACFのピークによるf0推定とオクターブエラー訂正のアルゴリズムを実装している。 この現在のオクターブエラー訂正のアルゴリズムを廃止して、決定木を学習して生成したC++推論処理に置き換えたい。 samples/inference.cppにある推論処理に置き換えてください。 オクターブエラー訂正以外は現在の処理のままとすること。
学習
以前に精度向上のために使用した録音データも含めて、訓練データを作成し、学習したところ、決定木の深さ5で、正解率82%となった。
深さ10にすると、91%となった。
増やし過ぎても過学習になるため、深さ10で学習したモデルを使用することにした。
動作確認
チューナーアプリに組み込んで動作確認したことろ、以前は、アコースティックギターで調整すると、エレキギターのオクターブエラーが増えて、逆にエレキギターで調整するとアコースティックギターでオクターブエラーが増えるという状態で、調整に難航していたが、どちらでも安定してピッチを検出できるようになった。
しかし、アコースティックの6弦でオクターブエラーが頻発する事象が起きた。
音声データを増やしても変わらないため、訓練データの値を確認したところ、SwiftF0が誤ったオクターブエラーしたラベルを付けていた。
特定の弦の音声は正解が分かっているので、手動で誤りデータを削除して、再学習したことろ、6弦も安定するようになった。
他にもSwiftF0が誤ったラベルを付けているデータがありそうだが、一つの音声で複数音階を録音しているので、手動で訂正が難しい。
今後は、音程を一つずつファイルを分けて録音することにする。
改善したチューナーアプリ
改善版は、Microsoft Storeに反映済みである。
Windowsで無料で高精度なチューナーは他にないので、Windowsで楽器の録音をしている方にはぜひ試してみて欲しい。
まとめ
SwiftF0は疑似ラベリングによる訓練データを生成して、オクターブエラー訂正の決定木モデルを学習した。
チューナーアプリに組み込み、アコースティックギター、エレキギター、エレキベースで、安定してピッチ推定が行えるようになった。
なお、訓練データ生成、学習、推論のコードはすべてGPT-Codex-5.3を使って生成したので、コードは1行も書いていない。
生成AIが、設計意図を解釈し、学習から推論コード生成まで一貫して実装できる水準に達していることを実感した。
スマホアプリのボーカル音程モニターにもこの結果を反映するつもりである。
ギター以外の対応楽器も増やすため、VSTiで音声生成してラベル付けすることを考えている。