グーグルMagentaの採譜プログラムと自分のプログラムを比べたら互角の勝負だったという話

# 概要
ちょっと長いですが「自分の書いたプログラムとグーグルのプログラムを比べてみたら、ほぼ互角の勝負でした」という内容です

というのは私の主観です（かなりバイアスかかってます）。実際のところは、次の３つのオーディオファイルを聞いて、みなさん判断してください。

* オリジナル： [brad-1min.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.mp3)
* [Google Magenta](https://magenta.tensorflow.org/onsets-frames) の結果： [brad-1min.magenta.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.magenta.mp3)
* [WaoN](http://kichiki.github.io/waon/index.html) の結果： [brad-1min.waon_n4096_s512_cm6_r1.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.waon_n4096_s512_cm6_r1.mp3)

# はじめに

先日[ツイッターで予告した通り](https://twitter.com/ichiki_k/status/965413383448100865)、Google Magenta チームが書いた Machine Learning によるオーディオから MIDI に変換する transcriber に対して、私が昔書いた [WaoN](http://kichiki.github.io/waon/index.html) というプログラムで挑んでみよう、というはなしです。

ことの発端は、この[ツイート](https://twitter.com/fjord41/status/963165268330536960)

>New blog post about the project I've been working on for a while. Automatic piano music transcription (raw audio to MIDI) that works really well!
>
>6:37 AM - 13 Feb 2018

ここに引用されてるブログ記事「[Onsets and Frames: Dual-Objective Piano Transcription](https://magenta.tensorflow.org/onsets-frames)」を読んでみると、彼らが TensorFlow でやってることは、オーディオファイルから MIDI ファイルを生成するという処理、いわゆる採譜 (transcription) で、[WaoN](http://kichiki.github.io/waon/index.html) がやってることそのものでした。これを見て、天下のグーグルさんがどれほどのものか知るいい機会だな、と思ったわけです（この時は）。

# チャレンジその１：クラシック篇

ということで、まずは <a href="https://magenta.tensorflow.org/onsets-frames">Magenta のブログ</a>にあったショパンとモーツァルトのサンプルを WaoN で試してみました。手順は、まず上記記事からダウンロードした ground truth の mp3 ファイルを <a href="https://www.mars.org/home/rob/proj/mpeg/">madplay</a> で decode して、<a href="http://kichiki.github.io/waon/index.html">WaoN</a> に食わせてみました。 WaoN のオプションは default のままです。この WaoN が吐き出した midi file を、 <a href="http://www.fluidsynth.org/">fluidsynth</a> で wav にして <a href="http://normalize.nongnu.org/">normalize</a> をかけて、<a href="http://lame.sourceforge.net/">LAME</a> で mp3 にエンコードしました。一応、具体的な手順を、ショパンの場合で載せておきます。
<pre>% madplay -o chop25-3-ground.wav chop25-3-ground.mp3
MPEG Audio Decoder 0.15.2 (beta) - Copyright (c) 2000-2004 Robert Leslie et al.
          Title: chop25-3-ground
       Composer: Sageev Oore
         Artist: Sageev Oore
          Album: Sageev Oore's Album
           Year: 2017-10-23T09:51
609 frames decoded (0:00:15.9), -5.4 dB peak amplitude, 0 clipped samples

% waon -i chop25-3-ground.wav -o chop25-3-waon.mid
Format: Microsoft WAV format (little endian default).
Subtype: Signed 16 bit data
Endian type: Default file endian-ness.
frames     : 701568
samplerate : 44100
channels   : 2
sections   : 1
seekable   : 1
WaoN : end of file.
division = 43
WaoN : # of events = 1542
WAON_notes : n = 1542
filename : chop25-3-waon.mid

% fluidsynth -F chop25-3-waon.wav /opt/local/share/sounds/sf2/FluidR3_GM.sf2 ch\
op25-3-waon.mid
FluidSynth version 1.1.6
Copyright (C) 2000-2012 Peter Hanappe and others.
Distributed under the LGPL license.
SoundFont(R) is a registered trademark of E-mu Systems, Inc.

Rendering audio to file 'chop25-3-waon.wav'..

% normalize chop25-3-waon.wav
Computing levels...
 chop25-3-waon.wav 100% done, ETA 00:00:00 (batch 100% done, ETA 00:00:00)
Applying adjustment of 26.08dB to chop25-3-waon.wav...
 chop25-3-waon.wav 100% done, ETA 00:00:00 (batch 100% done, ETA 00:00:00)

% lame chop25-3-waon.wav
LAME 3.100 64bits (http://lame.sf.net)
Using polyphase lowpass filter, transition band: 16538 Hz - 17071 Hz
Encoding chop25-3-waon.wav to chop25-3-waon.mp3
Encoding as 44.1 kHz j-stereo MPEG-1 Layer III (11x) 128 kbps qval=3
    Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA
   558/558   (100%)|    0:00/    0:00|    0:01/    0:01|   17.819x|    0:00

   kbps        LR    MS  %     long switch short %
  128.0       37.8  62.2        98.8   0.7   0.4
Writing LAME Tag...done
ReplayGain: -7.5dB
</pre>
結果は、以下のようになりました。（比較として、上記記事にある ground truth と magenta の結果も並べておきます。）

ショパン： [chopin25-3-waon.mid](http://kichiki.github.io/diary/LOCAL/20180304-waon/chopin25-3-waon.mid)

* ground truth: [chop25-3-ground.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/chop25-3-ground.mp3)
* magenta: [chop25-3-ours.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/chop25-3-ours.mp3)
* WaoN: [chop25-3-waon.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/chop25-3-waon.mp3)

モーツァルト： [moz331-waon.mid](http://kichiki.github.io/diary/LOCAL/20180304-waon/moz331-waon.mid)

* ground truth: [moz331-ground.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/moz331-ground.mp3)
* magenta: [moz331-ours.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/moz331-ours.mp3)
* WaoN: [moz331-waon.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/moz331-waon.mp3)

善戦しているな、というのは作者のひいき目が入ってると思いますが、結果の出来は明らかに Magenta の方がいいですね。まぁ、ブログには使っているものは、たくさんある結果の中からきれいなものだろうとは思いますが。

# チャレンジその２：ジャズ篇

ここでちゃぶ台をひっくり返しますが、私はショパンとかモーツァルトが transcribe できても何も嬉しくありません。そもそも、私がどうして WaoN を書いたのかというと、譜面のない音楽の譜面が欲しい、という理由だったので、当然です（負け惜しみですが…）。ということで、ここからは私が採譜したい音楽で競争することにします。

## 素材の準備

課題となる音源は、youtube から好きなビデオをピックアップして、そのオーディオを使うことにしましょう。今回は私のお気に入りの、 Brad Mehldau がピアノソロで演奏した、Stevie Wonder の名曲「Golden Lady」を使ってみました。

* [(youtube) Brad Mehldau - Golden Lady (Stevie Wonder) Solo](https://www.youtube.com/watch?v=Lpk86Ddsp7U)

素材は、<a href="https://rg3.github.io/youtube-dl/">youtube-dl</a> と <a href="http://www.mplayerhq.hu/">mplayer</a> と <a href="http://sox.sourceforge.net/">sox</a> を使って、以下のように１分の長さの wav file を作りました。
<pre>% youtube-dl Lpk86Ddsp7U
% mplayer -vo null -ao pcm:file=brad.wav Brad\ Mehldau\ -\ Golden\ Lady\ \(Stevie\ Wonder\)\ Solo-Lpk86Ddsp7U.mkv
% sox brad.wav brad-1min.wav --show-progress trim 0 01:00
% lame brad-1min.wav</pre>

* [brad-1min.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.mp3)

聞いてみると分かる通り、ドラムなどの入ってないピアノソロで、でも使ってる音域が低音寄りで、普通に FFT でアプローチしようとすると結構タフな演奏だと思います。

## Magenta

まずは Magenta のお手並みを拝見。そのためには、まず Magenta チームの transcriber (github: <a href="https://github.com/tensorflow/magenta/tree/master/magenta/models/onsets_frames_transcription">Onsets and Frames: Dual-Objective Piano Transcription</a>) を手元に準備する必要があります。いくつか方法が書かれてますが、今回は inference だけでいいので、一番簡単な <a href="https://colab.research.google.com/notebook#fileId=/v2/external/notebooks/magenta/onsets_frames_transcription/onsets_frames_transcription.ipynb">Colab Notebook</a> を使ってみることにしました。すでに学習済みのモデルに wav file を食わせるだけです。[ノートブック](https://colab.research.google.com/notebook#fileId=/v2/external/notebooks/magenta/onsets_frames_transcription/onsets_frames_transcription.ipynb) を順次実行していき、さっき作った素材ファイルをアップすると、こんな感じに解析が完了し、結果の midi file もダウンロードできました。
<a href="http://kichiki.github.io/diary/LOCAL/20180304-waon/Screen Shot 2018-03-03 at 11.56.52 PM.png">
<img src="http://kichiki.github.io/diary/LOCAL/20180304-waon/Screen Shot 2018-03-03 at 11.56.52 PM_thumb.jpg" width="200" height="118" style="border: 2px #ccc solid;" /></a>
時間は、きちんと計ってなかったのですが、ファイルのアップロードが一番かかって、 inference は速かったです。結果の <a href="http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.magenta.mid">midi file</a> は、これまでと同様に <a href="http://www.fluidsynth.org/">fluidsynth</a> で wav にして <a href="http://normalize.nongnu.org/">normalize</a> をかけて、<a href="http://lame.sourceforge.net/">LAME</a> で mp3 にエンコードします。

* [brad-1min.magenta.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.magenta.mp3)

結果を聞いてみた感じ、低音は大きな音で（確信をもって）取っていますが、私の耳には音程がずれているように思える部分がいくつかありました。（かなり粗探しモードになってますね。）

## WaoN

さて、我が <a href="http://kichiki.github.io/waon/index.html">WaoN</a> で同じ素材を midi に変換してみましょう。最初は素直に default option で試してみます。
<pre>
% waon -i brad-1min.wav -o brad-1min.waon.mid
Format: Microsoft WAV format (little endian default).
Subtype: 32 bit float data
Endian type: Default file endian-ness.
frames     : 2880000
samplerate : 48000
channels   : 2
sections   : 1
seekable   : 1
WaoN : end of file.
division = 46
WaoN : # of events = 2882
WAON_notes : n = 2882
filename : brad-1min.waon.mid
</pre>
結果はこちら(<a href="http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.waon.mid">midi file</a>)

* [brad-1min.waon.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.waon.mp3)

聞いてみて分かるように、メインのメロディーは取れてるけど、伴奏が取れてない感じです。あと低音が弱い。ということで、ちょっと低音域を伸ばして(-n 4096)、でも時間分解能は同じくらいで(-s 512)、もうちょっと小さい音まで拾うように(-c -6)、でも雑音はできる限り落として(-r 1)、という気持ちでパラメータを調整して、再挑戦してみます。
<pre>
% waon -i brad-1min.wav -o brad-1min.waon_n4096_s512_cm6_r1.mid\
 -n 4096 -s 512 -c -6 -r 1
Format: Microsoft WAV format (little endian default).
Subtype: 32 bit float data
Endian type: Default file endian-ness.
frames     : 2880000
samplerate : 48000
channels   : 2
sections   : 1
seekable   : 1
WaoN : end of file.
division = 46
WaoN : # of events = 2374
WAON_notes : n = 2374
filename : brad-1min.waon_n4096_s512_cm6_r1.mid
</pre>
結果はこちら(<a href="http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.waon_n4096_s512_cm6_r1.mid">brad-1min.waon_n4096_s512_cm6_r1.mid</a>)

* [brad-1min.waon_n4096_s512_cm6_r1.mp3](http://kichiki.github.io/diary/LOCAL/20180304-waon/brad-1min.waon_n4096_s512_cm6_r1.mp3)

さて、少しはよくなったかな。

# 結論

この結果と Magenta の結果を聴き比べると、Magenta は１音１音確信を持ってキーを叩ききってる感じなのに対し、WaoN は繊細に弾いてるように聞こえますね。WaoN は倍音など雑音をかなり拾っているのに対し、Magenta は音の選択を、これも確信を持って行ってるように感じました。transcriber とは凡人の耳では捉えきれない和音を見つける手助けとなるプログラムである、という WaoN 作者のひいき目を最大限行使して、淡い音も可能性として切り捨てずに残している WaoN の繊細さを評価して、今回の勝負は引き分け、ということにしておきましょう（クラシックは負けたけど、ジャズは勝った、ということで）。

# さいごに

こうやって、伝統的なアルゴリズムのプログラミングと Machine Learning で対決していると、なんだか、画像処理における Computer Vision 対 Machine Learning の状況が頭をよぎります。画像処理については仕事で少し手を出してますが、私自身は、古き良き匠の技的な Computer Vision のアプローチには見切りをつけて（技能習得を諦めて、が正しいかな）、 Machine Learning の側に立っていると思っていますが、今回は、まったく逆の立場に立たされた訳で、とても面白かったです。個人的な考え方のはなしになりますが、音（特に音楽）については Machine Learning 的に「結果が得られればなんでもいい」という気分には、なかなかなり切れませんね。どうしても、古き良き匠の世界の肩を持ちたいと思ってしまいます。同じ「音」でも、音声認識とかは、全然ブラックボックスでも構わないのですが。