Open ryotayama opened 4 years ago
↓メモ!٩( ᐛ )و
◆deezer/spleeter https://github.com/deezer/spleeter
◆iOSのSwift,Objective-CでPythonを呼び出す https://qiita.com/Hiroki_Kawakami/items/830baa5adcce5e483764
◆pieter/YoutubeDL-iOS(iOSアプリでPythonを動かしている例) https://github.com/pieter/YoutubeDL-iOS
◆Changes To Embedding Python Using Xcode 5.0 https://developer.apple.com/library/archive/technotes/tn2328/_index.html
◆他のアプリケーションへの Python の埋め込み https://docs.python.org/ja/3/extending/embedding.html
https://twitter.com/ExtractorVocal/status/1208274533720870912?s=20 VocalExtractorは概ね1GB以下のメモリーで動くようになっています。それでも大きいのですが、これはSpleeterのせいではありません。うまく実装すれば、もっと小さくできます。またPythonをiOSで動かしているのではなく、iOS上のCoreMLに変換して実装しています。横から失礼しました。
https://twitter.com/origuchi_tou/status/1208275775922638849?s=20 Vocal Extractorの作者の方から返信が来たのですが どうやらPythonをCoreMLに変換した上でSpleeterを動作させているようです
おぉ、なるほど…! 有益な情報、ありがとうございます! となると、iOS 11以上が対象になるのかな…。 CoreML自体触った事がないので、引き続き勉強してみます!🙏
https://twitter.com/ryota_yama/status/1256173450428739584?s=20 フォローありがとうございます! うぉー、Spleeter対応してるんですね! ぼくもいつかやりたいと思いつつ「CoreML……はにゃ?」というところで止まってますw めちゃくちゃ尊敬します!
フォローありがとうございます Tensorflowもよく分からないまま執念で変換しました口を開けて冷や汗をかいた笑顔 ご興味あれば聞いてください
えぇー、なんて優しいお言葉を……!号泣 もし詰まったら質問しちゃうかもしれません!
Core ML https://developer.apple.com/jp/documentation/coreml/
トレーニング済みモデルのCore MLへの変換 https://developer.apple.com/jp/documentation/coreml/converting_trained_models_to_core_ml/
TensorFlowコンバータ https://github.com/tf-coreml/tf-coreml
Problem converting savedModel https://github.com/tf-coreml/tf-coreml/issues/368
①TensorFlow Liteを使う場合 ・学習モデルを.pb形式にする ・.tflite形式に変換
②CoreMLを使う場合(他アプリでも実績あり) ・学習モデルを.pb形式にする ・.mlmodel形式に変換
以下6種類のエフェクトを追加できたら楽しそう。 (ただしリアルタイムでの変換はできず、処理がとても重たそう)
・ボーカル抽出(2stems) ・伴奏抽出(2stems) ・ドラム抽出(4stems) ・ベース抽出(4stems) ・ピアノ抽出(5stems) ・ボーカル/ドラム/ベース/ピアノ除去(5stems)
以下のコードを「Python write_graph.py」として動かすことでgraph.pbファイルが出力されることを確認✍️
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
tf.disable_eager_execution()
graph = tf.get_default_graph()
sess = tf.Session()
saver = tf.train.import_meta_graph('model.meta') saver.restore(sess, 'model')
tf.train.write_graph(sess.graph_def, '.', 'graph.pb', as_text=False)
以下のコードを「Python freeze_graph.py」として動かすことでfrozen.pbファイルが出力されることを確認✍️ (ただし、指定した"waveform"、"global_step/initial_value"しか書き出されていないため、全ての要素を指定する方法を要調査)
import tensorflow.compat.v1 as tf
from tensorflow.python.platform import gfile
from tensorflow.python.framework.graph_util import convert_variables_to_constants
tf.disable_v2_behavior()
tf.disable_eager_execution()
with tf.Session(graph=tf.Graph()) as sess: with gfile.FastGFile("graph.pb", 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) frozen_graph_def = convert_variables_to_constants(sess, graph_def, ["waveform", "global_step/initial_value"]) with tf.gfile.GFile('./frozen.pb', "wb") as f: f.write(frozen_graph_def.SerializeToString())
↓このライブラリを使う事で、.pb形式に変換できるっぽい✍️(作成された.pbファイルを.tflite形式や.mlmodel形式に変換できるか要調査)
CoreMLに変換しようと奮闘した結果、CoreMLへの変換はひとまず断念したことを報告します。 以下そのCoreMLへの変換に必要な道筋と断念した理由を書いていきます。なお、自分はTensorflow初心者なので勘違いしていたら申し訳ないです。また、spleeterがTensorflowの1.15.2を要求するので、以下Tensorflow 1.15.2環境下での話です。全体のコードはこのGoogle Colaboratoryに記しました。
まず、CoreMLに変換するには既に書かれてあるとおり、frozen graphに変換する必要が有ります。spleeterでは学習済みモデルとしてcheckpointを配布していますが、得られる情報量が多いためspleeter.utils.estimator.create_estimator
により得られるEstimator
からSavedModel
を経由してfrozen graphを作成しました。
このSavedModel
を用いて、saved_model_cli show --dir "path/to/exported" --all
から必要な出力テンソルの名前が得られるのでそれを元に、
python -m tensorflow.python.tools.freeze_graph --input_saved_model_dir "/path/to/saved_model/dir" --output_graph "frozen.pb" --output_node_names strided_slice_21,strided_slice_23 --saved_model_tags "serve"
を実行してfrozen graphを得ます。
しかし、ここでtf-coremlにこのfrozen graphを渡すと、minimum_ios_deployment_target="12"
の場合はRetval[x] does not have value
、minimum_ios_deployment_target="13"
の場合はkeras_learning_phase
に関するエラーが出てしまいます。これらのエラーは(恐らく)学習時と推論時に処理を切り替える機能がCoreMLで上手く再現できない(あるいはその機能がtf-coremlにない)ために生じると考えられるのでtensorflow.tools.graph_transforms.TransformGraph
等を用いて処理します。
こうして得られたfrozen graphをtf-coremlに渡すと処理が始まるのですが、minimum_ios_deployment_target="12"
の場合はFusedBatchNormV3
に非対応であるという旨のエラーが出てしまい、tf-coremlのこのissueより、minimum_ios_deployment_target="13"
にする必要があるそうです。しかし、minimum_ios_deployment_target="13"
とすると、RFFT
が非対応であるという旨のエラーが出ます。調べてみるとそもそもCoreMLにフーリエ変換の機能がないようでした。これを解決するには、
という案しか浮かばず、どちらも相当に大変そうだったので断念しました。 Tensorflow Liteに関してはこれから調べてみます。
Tensorflow Liteに関して調べてみた(および実行してみた)ところ、結局Tensorflow LiteでもSwitch
やRFFT
は実装されておらず、tf-coremlと同様にカスタムレイヤーを自分で実装する必要があるようです。tf-coremlの場合と違ってC++で実装してビルドしなければいけない一方で、感覚的には検索でヒットする(特に日本語記事)数はTensorflow Liteの方が多いのであとは好みといった印象です。
久しぶりに軽く調べてみたらAndroid/iOS用にライブラリ化して公開してくれてる人が居ました。 FaceOnLiveという会社っぽいですが、↓のリポジトリにライセンス書いてなくて使っていいのかちょっと疑問ですが。。 https://github.com/FaceOnLive/Spleeter-Android-iOS → Androidはaar、iOSはframeworkが入ってたんでそれロードすりゃ動くかと思いきや、 Android版をビルドしてx86エミュレータで動かしてみたら、libttvspleeter.soがないよってエラーでアプリが落ちました。 → もう少しだけ調べてみたらlibttvspleeter.soは実機向けのabiであるarm64-v8aとarmeabi-v7a用のみが用意されていたようで、x86用は存在しないだけでした。なので実機でなら実行できちゃいそうです。 → 手元のRakuten miniの実機でAndroid版の動作確認取れました。 wavファイルを渡したら5つのwavファイルを生成してくれました。 はやえもんとしては、wavへの変換機能を作れば十分使えそうです。 まずはミニマムとしてwavファイルだけ対応してますっていう風に搭載するのもありかと。
FaceOnLiveのライブラリは$6000とのこと。 ↓を読んでTFLite版を自分でビルドしてみる? https://github.com/deezer/spleeter/issues/518 →https://github.com/jinay1991/spleeter なんかここにもうめっちゃ詳しく手順書いてあるからやってみる。 https://github.com/jinay1991/spleeter/releases/tag/2.3.0 → C++版だったのでAndroid Studioでndkビルドすればnativeライブラリ作れそう? それか↓を使ってtflite用にモデル変換するべきか? https://github.com/tinoucas/spleeter-tflite-convert
tflite形式のモデルはここにアップされてた。 https://github.com/jinay1991/spleeter/releases/tag/v2.3
モデルはabiに依存していないのでandroidでもこれはそのまま使える。
wavファイル渡したらvector
libspleeter.aのためのC++ソースすべてのCmakeLists.txtをbazelのビルドルール見ながら書くのはなかなかに大変そうなので、 既存のbazelのビルドルールを改造してandroidのabi向けのビルドもしてくれるようにして各abi用のlibspleeter.aを生成するところを目標にする。
まずは各種thirdpartyのライブラリたちをandroid向けのabiでクロスコンパイルするルールを書くのを第一目標にする。
Bazelを勉強してBazelでやる or Android NDKを直に使う
bazel build -c opt --config=android_arm64 \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
--define tflite_with_xnnpack=true \
//tensorflow/lite:libtensorflowlite.so
bazel build -c opt --config=android_arm32 \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --define tflite_with_xnnpack=true \ //tensorflow/lite:libtensorflowlite.so
bazel build -c opt --config=android_x86_64 \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --define tflite_with_xnnpack=true \ //tensorflow/lite:libtensorflowlite.so
bazel build -c opt --config=android_x86_32 \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --define tflite_with_xnnpack=true \ //tensorflow/lite:libtensorflowlite.so
一応Javaで直にtfliteモデルを使う方法もトライしたけど、kboneくんの言ってた通りRFFTなんてOperationないよってエラーになった。
java.lang.IllegalArgumentException: Internal error: Failed to run on the given Interpreter: Regular TensorFlow ops are not supported by this interpreter. Make sure you apply/link the Flex delegate before inference.
Node number 39 (FlexRFFT) failed to prepare.
spleeterのC++ライブラリ部分はビルド通せた。 あとはJNI実装して動かせれば完璧。。
JNI実装して動かしたらなんとRFFTのエラー。 やっぱりダメなのは変わらんか。。
2024-03-20 17:08:14.237 28656-28656 tflite com.example.spleetertest E Attempting to use a delegate that only supports static-sized tensors with a graph that has dynamic-sized tensors.
2024-03-20 17:08:14.325 28656-28656 tflite com.example.spleetertest E Regular TensorFlow ops are not supported by this interpreter. Make sure you apply/link the Flex delegate before inference.
2024-03-20 17:08:14.325 28656-28656 tflite com.example.spleetertest E Node number 39 (FlexRFFT) failed to prepare.
tfliteモデルの可視化は以下が超お手軽だった。 https://netron.app/
Flexについて調べたらJava版なら以下で有効化できることが分かってやってみたらFlexRFFTのエラーは無事突破できた!
Interpreter.Options options = new Interpreter.Options();
FlexDelegate flexDelegate = new FlexDelegate();
options.addDelegate(flexDelegate);
というわけで結局頑張ってビルドしたC++版は使っていない…
Flex delegateについてはこの記事のIntroがとっつき易かった https://qiita.com/PINTO/items/b2af9aec3a8915457dd8
C++版でflex delegateを有効化するにはnnapi無効化しつつtflitdの依存にflex追加しつつビルド時のコマンドでdefine2つ追加でいけそう? https://qiita.com/PINTO/items/b2af9aec3a8915457dd8
5stemsの入出力情報 コピー元は5stemsのREADME.md
input | tensor_names | index |
---|---|---|
waveform | waveform | 0 |
stems | tensor_names | index |
---|---|---|
vocal | strided_slice_18 | 4 |
drums | strided_slice_38 | 1 |
bass | strided_slice_48 | 0 |
piano | strided_slice_28 | 3 |
accompaniment | strided_slice_58 | 2 |
accompaniment・・・伴奏
wavのin/out部分の自作が辛かったのでビルドが通ってるC++版を使ってみた。 動きはするが、出力されるファイルはヘッダのみで空だった。
wsl上でC++版は動かせるようにしてあるので、以下を上から確認していく。
新しく作成されたbyteバッファの順序は常にBIG_ENDIANになります。
案の定デフォルトはBIG_ENDIANだったのでとても怪しい。 ⇒LITTLE_ENDIANにしたが、まだダメ。とりあえずLITTLE_ENDIANが正しいのは確定なので修正はしたままにする。
単純なサイズミスが3か所くらいにあっただけでした!!無事動いた!
iOSにも載っけるなら結局C++ライブラリを頑張った方が良い気がする。 ただひとまずは理解のためにもJavaでしっかり音源分離動かすところまではいきたい。
https://zenn.dev/nnabeyang/articles/3c296e3d38b2f016e0f2
クロスPF頑張らないならiOS版でもちゃんとflex delegate有効にする方法はあった https://www.tensorflow.org/lite/guide/ops_select?hl=ja#ios https://qiita.com/mbotsu/items/6096663376c22c23ef37
x86_64シミュレーターでセレクト TF 演算を使用する必要がある場合は、セレクト演算フレームワークを自分で構築できます 詳細については、Bazel を使用する、および、Xcode のセクションを参照してください。
とのことなのでエミュレータは自分でライブラリのビルドから必要
Flex delegateを使うのでSelectTfOpsも必要なはず.
Podfile
pod 'TensorFlowLiteSwift'
pod 'TensorFlowLiteSelectTfOps'
先に調べた感じだとビルド時に↓でハマりそう https://github.com/tensorflow/tensorflow/issues/56255
こんな記事も見つけた 同じくハマる気がするのでメモ https://qiita.com/mbotsu/items/6096663376c22c23ef37
知らぬ間にandroidの方はtfliteがGMSサービスとして入ってたらしい。 これ使えばアプリ側でtfliteライブラリを持たなくていいからapkサイズちっさく出来るよって話。ただこれminimumSDKverとか書いてないから使えない端末がどんだけあるのか不明。 https://www.tensorflow.org/lite/android/play_services
https://www.tensorflow.org/lite/android/java#gpu_with_interpreter_apis
https://twitter.com/ryota_yama/status/1192428113491218432?s=20 さーて、噂の「spleeter」触ってみよーかな!٩( ᐛ )و
MITライセンスらしいので、ハヤえもんに組み込めたらベストだけど、きっとめちゃくちゃハードル高いんだろうなー!
音楽データからボーカル・ドラム・ベースの音を個別に抽出できる「spleeter」
https://twitter.com/origuchi_tou/status/1207065098637807617?s=20
ハヤえもん のボーカルキャンセル機能に #Spleeter を入れて欲しいところ
https://twitter.com/ryota_yama/status/1207447352904019969?s=20 これはかなりやりたい……! まずはiOSアプリでPythonを動かす方法から勉強しなきゃ。 ↓ここらへんが参考になるかも🤔 https://qiita.com/Hiroki_Kawakami/items/830baa5adcce5e483764
https://twitter.com/origuchi_tou/status/1207497939662229504?s=20 この #Spleeter をiOSで初めて実装した「VocalExtractor」というアプリがあるのですが RAM4GBのiPhone11であってもちょくちょくメモリ不足の表示が出たりするので現状のボーカルキャンセル機能と置き換えるのではなくオプションとして実装した方がいいかもしれません
情報ありがとうございます!🙏