書いてから教えてもらいましたが、ラズパイに乗っているARM Cortex-A53にはFP16対応のNEONは搭載されていません。ですので、すべての演算でFP32への変換が入り遅くなります。@hiratch に感謝。
コキュートスを使って、なんとかボードで意味のあるNNを動かそうと試行錯誤しています。
FP16とは
FP16とは半精度浮動小数点数のことです。正確にはこの辺を見てもらうとして
要するに通常の浮動小数点数が32bitなので、それを半分の精度の16bitで実装するフォーマットです。Deep Learningにおいては、メモリアクセスがボトルネックになる事があり、その場合データ量が半分になるので速度の向上が見込めます。またARM NEONを使う時でも、64bitレジスタの中に倍のデータが格納でき計算が早くなることが期待できます。
欠点としは、当然精度が下がることと、FP16をサポートしていないプロセッサ(x86など)ではデータやソースが共通にできない等があります。
ちなみにFP16のデータを作るにはnumpyを使えば一発です。
環境
ボード
Raspberry Pi 3 MODEL B
Cソース
今回テストに使ったソースはこれ。コキュートスで生成しています。
https://gist.github.com/natsutan/b2379ca023036f27aaeb756e7d6cccb6
全体はtiny-yoloを動かしています。
https://github.com/natsutan/cocytus/tree/master/example/tiny-yolo/c
これの重みデータや途中の変数をFP16に変換しています。
コンパイルオプション
cmakeの中でこのように設定しました。
add_definitions("-pg -Wall -g -Iinc -mfloat-abi=hard -ffast-math -mfp16-format=ieee -Ofast -mcpu=cortex-a53 -mfpu=neon-fp16 -save-temps=cwd")
結果
残念なことに浮動小数点数バージョンが21秒なのに対し、FP16バージョンでは48秒と遅くなってしまいました。
せっかく-save-tempsオプションを使っているので、アセンブラの出力を見てみましょう。
https://gist.github.com/natsutan/95f889c1bab3627b2975598fcc57641b
出したコードはよくわかりませんが、floatからfp16へ変換している箇所がいくつか見つかります。
.LVL26: .loc 1 59 0 discriminator 1 ble .L18 ldr r3, [sp, #36] .loc 1 59 0 is_stmt 0 mov r7, #0 ldr r2, [sp, #52] mov lr, r7 vcvtb.f32.f16 s1, s1 vcvtb.f32.f16 s2, s2 add r2, r3, r2 vcvtb.f32.f16 s3, s3 str r2, [sp, #16] vcvtb.f32.f16 s4, s4 mov r2, r3 vcvtb.f32.f16 s9, s9 vcvtb.f32.f16 s5, s5 vcvtb.f32.f16 s6, s6 vcvtb.f32.f16 s7, s7 vcvtb.f32.f16 s8, s8
Cソースの浮動小数点数はすべてFP16にしているので、コンパイル時に不要なfp16->float(32bit)->fp16の変換が入っていると思われます。
NEONを直に書くしかないですね。