Neural Engine(ニューラルエンジン)は、ニューラルネットワークの推論演算に特化した機構を持つことで、GPUよりもさらに高速な処理と消費電力の低下を両立するプロセッサ。A11 Bionicは毎秒6000億回、A12 Bionicではなんと毎秒5兆回の演算が可能とされている。
いうまでもなくCore MLのポテンシャルを最大限引き出す鍵となるのがこのNeural Engineなのだが、実はNeural Engineを制御するフレームワークやAPIは存在せず1、Appleは開発者向けの情報もほとんど何も出してない。
残念ながら先のWWDC 2020でもその状況はあまり変わらなかった。
そんな中、書籍「Core ML Survival Guide」やOSS「CoreMLHelpers」でCore ML界隈で知らない人はいないMatthijs Hollemans氏が、GitHubでNeural Engine(ニューラルエンジン)のドキュメントのリポジトリを開設した。
氏がNeural Engineについて現状把握していることをまとめてくれたのが本リポジトリ。公開APIはない中で、プライベートフレームワークを利用してモデルの処理においてNeural Engineが使用されているか(あるいは使用されておらず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
orScaleLayer
or a linearActivation
layer -
ConcatND
→Concat
- and so on...
またSubtractBroadcastableLayer
みたいに互換性のある古いバージョンのものがない場合はワークアラウンドとして別のレイヤーを使って同様のレイヤー機能を実装するといいよ的なことが書かれている。
その他
なんだかわからないが、重みを16ビット浮動小数点で保存したmlmodelファイルを使うと、32ビットモデルを使う場合よりも遅くなるらしい。GPUでは常に16ビットで扱うので個人的に最近は16ビットでいいやと思っていたのでこれは要注意。
-
唯一、「Neural Engineを使わない」という指定だけはできる。Neural Engineを使うという指定はできない。 ↩