Neural Engineのドキュメントを読む
書籍「Core ML Survival Guide」やOSS「CoreMLHelpers」でCore ML界隈で知らない人はいないMatthijs Hollemans氏が、GitHubでNeural Engine(ニューラルエンジン)のドキュメントのリポジトリを開設した。
なぜAppleではなく氏がドキュメンテーションを行うのかというと、Core MLのポテンシャルを最大限引き出す鍵となるNeural Engineについて、Appleは開発者向けの情報はほとんど何も出してないからだ。(これについては個人的には何かしらの進展を次のWWDCで期待している...)
というわけで、氏がNeural Engineについて現状把握していることをまとめてくれたのが本リポジトリ。公開APIはない中で、プライベートフレームワークを利用してモデルの処理においてANEが使用されているか(あるいは使用されておらずCPUやGPUで処理されているか)を調べる方法や、そういった手法を用いてどういった種類のレイヤーはANEで処理されないか、といった非常に有益な情報が書かれている。以下それを読んだメモ。
なお、頻出する「ANE」はApple Neural Engineの略。Neural Engineを扱うプライベートフレームワークのプレフィックスがこれになっているらしい。(確かにCore MLを使っているとANEなんとかは目にすることが度々あった)
ANEが使用されているかを調べる方法1
**Tip:** Press the Pause button in the debugger. If there's a thread **H11ANEServicesThread** then Core ML is using the Neural Engine.
Xcodeにつないでデバッグ中にポーズボタン(⏸)を押してスレッド一覧の中に"H11ANEServicesThread"という名前のスレッドがあれば、Core MLはNeural Engineを使用していると判断できる。(前述の通りANEはApple Neural Engineの略なので)
ANEが使用されているかを調べる方法2
シンボリック・ブレークポイント(symbolic breakpoint)を使用すると、CPU, GPU, ANEのうちどのプロセッサが使用されているかを知ることができる。
たとえば`-[_ANEModel program]`にシンボリック・ブレークポイントを張ってそこで止まれば、Core MLはANEを使用している。
ちなみに氏がドキュメントの中で何度も口を酸っぱくして書いてるのは、『だからといって、モデルの処理がすべてANEで処理されているとは限らない』ということ。一部のレイヤーだけANEで処理されていて、ほかはCPUやGPUで処理されているのかもしれない、という話。
またCore MLはEspressoというプライベートフレームワークで以下の3つの「エンジン」実装を使用しているとのこと。
- ANE — `Espresso::ANERuntimeEngine`
- GPU — `Espresso::MPSEngine`
- CPU — `Espresso::BNNSEngine`
スタックトレースにこれらの名前があれば、現在どのプロセッサで処理されているかが判断できる。
また次のようにシンボリック・ブレークポイントを貼れば、CPU・GPUで処理が行われていることを発見できる。
- `Espresso::MPSEngine::context::__launch_kernel`
- `Espresso::BNNSEngine::convolution_kernel::__launch`
これでうまくいかなければ、以下のどちらかをブレークポイントに使用して、コードをステップ実行で追っていく。
- `Espresso::layer::__launch`
- `Espresso::net::__forward`
`Espresso::elementwise_kernel_cpu::__launch`のような関数で終われば、それはCPUで実行中というヒントとなる。
Espressoフレームワークのシンボルリストを取得する
Espressoフレームワークの他のシンボルも知りたければ、アプリ実行中にブレークポイントでアプリを止め、LLDBで次のコマンドを実行する。
(lldb) image list Espresso
これを実行するとEspress frameworkへのパスがprintされるので、そのパスを用いて次のようにdumpを実行する。
(lldb) image dump symtab 'put-the-path-here'
ANEがサポートしていないレイヤー
以下のタイプのレイヤーはANEでは処理されないことがわかっている。
- RNN layers such as LSTM or GRU
- dilated convolutions
- upsampling layers
- broadcastable and "ND" layers
- custom layers(・・・ANEのAPIが公開されていないので当然)
各タイプの詳細は`unsupported-layers.md`を参照のこと。(ここに書くとほとんどの内容を引用することになってしまうので。)
ちなみにすべてのレイヤータイプを試したわけではないので上記は網羅的なリストではないよとのこと。
coremltoolsでモデルを変換する際の注意点
上に挙がっている"Broadcastable layers"はANEで動作しないが、実はCore ML 2 の古いレイヤタイプと置き換えることができるらしい。そして古い方はANEで処理される。
しかしcoremltoolsでモデルを変換する際に、
minimum_ios_deployment_target='13'
と最新バージョンを指定すると、コンバータは新しい"Broadcastable layers"の方(ANEで処理されない方)を使用する傾向にあるらしい。
そういった場合にcoremltoolsを使って互換性のある古いバージョンのものと置き換えるといいよというTipsが紹介されている。
- `AddBroadcastableLayer` → `AddLayer`
- `MultiplyBroadcastableLayer` → `MultiplyLayer` or `ScaleLayer` or a linear `Activation` layer
- `ConcatND` → `Concat`
- and so on...
また`SubtractBroadcastableLayer`みたいに互換性のある古いバージョンのものがない場合はワークアラウンドとして別のレイヤーを使って同様のレイヤー機能を実装するといいよ的なことが書かれている。
その他
なんだかわからないが、重みを16ビット浮動小数点で保存したmlmodelファイルを使うと、32ビットモデルを使う場合よりも遅くなるらしい。GPUでは常に16ビットで扱うので個人的に最近は16ビットでいいやと思っていたのでこれは要注意。
最後まで読んでいただきありがとうございます!もし参考になる部分があれば、スキを押していただけると励みになります。 Twitterもフォローしていただけたら嬉しいです。 https://twitter.com/shu223/