びぼーろぐ

備忘録としての勉強のログです。淡々と学んだことをログって行くので、雑な記事が多いです。

Automated flow for compressing convolution neural networks for efficient edge-computation with FPGA

arxiv.org
2017
LeapMindさんの論文です。

一言で言うと

学習済みTensorFLowモデルの自動フローをbinarized CNNのFPGA SoC実装に移植する。このフローは、パラメータとアクティベーション(おそらく、中間レイヤーに対する入力値)の量子化と、(binary CNNに向けたFPGAアクセラレータの自動生成による)C組み込みのネットワークとモデルを生成する。 binarized YOLOv2をこの自動フローで実装し、低コストで低パワーなCyclone-V FPGAで動作させた。それを動かしたら、CPUやモバイルCPUと比べてモデルサイズや推論スピードで非常に良い性能が得られた。さらに、学習済みモデルからFPGA合成(高位合成)をする全体の自動フローは1時間以内に完了させることができる。

感想

チャネルの順番をDを最後にする(Depth is first updated)と計算の速度だったり、メモリ効率だったりが変わるなんて衝撃だった。この論文が初?

結果

f:id:taku-buntu:20190112222817p:plain

f:id:taku-buntu:20190112222758p:plain

ざっく理論(もうざっくりじゃない)

手順

  1. full precisionモデルを 重みパラ=1 bit、アクティベーション=2 bitにして、オリジナルデータセットで再学習。
  2. 完全に再学習された量子化モデルであるTensorFlow protocol buffer formatが得られる。
  3. モデルはパースされ、関連性のあるグラフ変換が適用される。
  4. 量子化ネットワークのための組み込みCコードが生成され、FPGAアクセラレータの高位合成(HLS)実装が量子化ネットワーク用にカスタマイズされる。これは、適切なレベルの並列化とローカルメモリ使用率を選択するための、モデルの必要メモリと計算の複雑さを考慮して自動スクリプトを使って生成される。

f:id:taku-buntu:20190112175159p:plain
学習済みTensorFlow実装CNNモデルのFPGAへの実装フロー

モデルパース

いろいろな深層学習フレームワークの完全に学習されたモデルは、それぞれのbinary protocol buffer fileにシリアライズされる(.h5ファイルとか)。そのファイルは計算グラフやパラメータを保持している。
量子化アクティベーションと重みパラメータの自動的かつ透明性のある管理をするのがこの論文のキモな部分。量子化がサブグラフ(これは枝刈りされて、オリジナルグラフとビットごとに対応したもの、将来的に置換される)に使用されるなら、推論はすごく効率的になる。(←うまい日本語ができない...こんな感じかな)

下図は2つの連続した畳み込みがそれらの間で線形演算を持つときの例をサブグラフで示している。この場合、カーネル量子化サブグラフは削除することができる。これらのサブグラフは32ビットfloatの実数定数テンソルを持っている。しかし、Binary CNNはで学習は実行でき、その各層の重みは効率的に保存(pack)される。これは、1つの4ビットワードで32個の重みを保持することができるということ。

f:id:taku-buntu:20190112181300p:plain
連続した畳み込み間のサブグラフ。しきい値ユニットに置換される。

アクセラレーションに基づくFPGA

FPGAアクセラレータの並列化はターゲットFPGAの計算リソースやRAM block、メモリ帯域幅に大きく依存する。

設計準備

ここでは、以下の点を仮定する

  1. 特徴マップの数は8の倍数個
  2. 入力の特徴マップの数は16の倍数個
  3. オンチップメモリは制限されてる
アクセラレータ生成

以下の3つのステップ

  1. 基本的なbuilding blockをカスタマイズ
    i.e. 入力やカーネルに対する要素あたりのビットに従うPE
  2. PE行列の形にPENをカスタマイズ。PEの数は16からmin( depth _ { i })数個ある。( depth _ { i }は任意の層の入力次元の深さ)
  3. 自動的に他の関連性のあるパラメータと制御コードを計算

ここで、

  • PE:32ビット(1ワード)で保持されているカーネル要素積 。 1ビットカーネルなら、一つのPEで並列に32カーネル要素を計算できる。
  • PEN:並列に処理されるカーネル積。これはPEの同じ入力要素と異なるカーネル要素を並列に処理できる。なので、カーネル間の並列処理を利用してインプットを再利用できる。

f:id:taku-buntu:20190112211753p:plain
並列要素処理を持つFPGA SoCプラットフォームブロックダイアグラム

データ順序の最適化

CNNの入力、出力とカーネルは3次元データ配列で可視化される。典型的に(D, W, H)か(D, H, W)だが、今回は(H, W, D)か(W, H, D)を使う(Depth次元が最初に更新される)。Depth first順序は下記のちょっとしたメリットがある。

メモリ帯域の最適化

FPGAからおふチップDRAMへのメモリ帯域はボトルネックになりがち。これはバースト転送(Burst transfers)を使えば緩和することができる。けど、バーストサイズは連続的にアクセスされるメモリアドレスに制限される。上の提案順序はバーストサイズを最大化し、かつ効率的にメモリ帯域が改善する。

W-barとD-bar

W-barでは、入力サイズ( I _ { h }, I _ { d })、カーネルサイズ( K _ { h }, K _ { d })で、 I _ { w } K _ { w })の異なる幅を持つ。

D-barだと \operatorname { Ih } \times I w K h \times K wは違うは I _ { d } K _ { d }の異なる深さを持つ。

この図よくわからn...普通の畳み込みっぽく見えるんだけど

f:id:taku-buntu:20190112214428p:plain
入力とカーネルの要素ごとの重なり

外部と内部メモリアクセス
  • 外部メモリからの入力:D-barだとメモリジャンプが少ない。W-barだとKwの要素しかオーバーラップしないので、Kw×Kh×Kdのカーネル全体でKh×Kdの入力のメモリアドレスジャンプがある。一方、D-barだとKd×Kwの要素がオーバーラップするので、Khだけしかジャンプしない。結果、D-barだとメモリの連続性が向上してバースト性能が向上する。

f:id:taku-buntu:20190112220011p:plain
データ順序によるメモリアクセスの違い

  • ローカルメモリビットパッキングとPE:D-barは分かりやすくて、効率的にRAMブロック上で量子化のためのbit-packing実装ができ、複数のローカルメモリアクセスとマスキング演算が不要になる。D-barだとcourse-grainアクセスが可能になる。

f:id:taku-buntu:20190112221230p:plain
提案データ順序でのローカルRAMブロック上のbit-packing実装の効率的最適化

  • カーネル間の並列化:出力がローカルメモリに保存されてたら、D-barはクリーンなメモリ書き込みが可能になる。

英語

..., followed by...・・・その後に...