FC2カウンター FPGAの部屋

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

TensorFlow + Kerasを使ってみた20(特徴マップ3個のMNIST用CNNをVivado HLSで実装)

TensorFlow + Kerasを使ってみた19(特徴マップ3個と2個のMNIST用CNNをテスト)”の続き。

前回は、MNIST 用のCNN で特徴マップが 3 個と 2 個の時について学習を行ったところ、3 個の場合の精度が 98.54 % で使い物になるようだった。特徴マップが 2 個の場合は、98.02 % で少し低くなっていた。今回は、MNIST 用のCNN で特徴マップが 3 個の場合をVivado HLS で実装してみよう。

まずは、畳み込み層と全結合層の重みとバイアスをKeras からC のヘッダファイルとして取得した。
そして、MNIST のデータの1000 個分をC のヘッダファイルとして出力した mnist_data.h を使用する。
これらのヘッダファイルの取得方法は、”TensorFlow + Kerasを使ってみた18(特徴マップ5個のMNIST用CNNをVivado HLSで実装)”をご覧あれ。。。

Vivado HLS 2017.4 の mnist_conv_nn5_hlss_org を作成した。
各層の演算精度が書いてある mnist_conv_nn5_hlss.h を示す。ここの値を変更すると演算精度を変更することができる。
tensorflow_keras_99_180615.png

幾度と無く mnist_conv_nn5_hlss.h のパラメータを変更して、C シミュレーションを行った結果、「特徴マップが 5 個の時」のパラメータの中で、 CONV_BIT_LENGTH を 10 から 11 ビットに変更したところ、ハードウェア化する固定小数点演算の精度が改善した。これで行くことにする。

C シミュレーションを行った。結果を示す。
tensorflow_keras_98_180615.png

1000 個のMNIST のデータのうちでハードウェアの固定小数点演算ではエラーが 15 個、ソフトウェアの浮動小数点演算ではエラーが 16 個だった。
よって、ハードウェアの固定小数点演算での精度は、98.5 % となった。
ソフトウェアの浮動小数点演算での精度は、98.4 % となった。

C コードの合成を行った。結果を示す。
tensorflow_keras_100_180615.png
tensorflow_keras_101_180615.png

Latency は min が 101701 クロックで、max は 102133 クロックだった。100 MHz 動作だと約 1.02 ms だった。fpsにすると約 979 fps となった。
リソース使用量はBRAM_18K が 34 個、DSP48E が 62 個、FF が 5284 個、LUT が 9137 個だった。

特徴マップ数が 10 個の場合と 5 個の場合と 3 個の場合のリソース使用量を表にした。
tensorflow_keras_102_180615.png

この表を見ると、特に BRAM_18K の減りが激しいようだ。BRAM_18K の特徴マップ数が 10 個の時を基準とした時の 3 個の時のパーセンテージは 26.15 % だったが、DSP48E は 35.43 % で、FF は 58.68 % 、 LUT は 55.74 % だった。

なお、あくまで C コードの合成でのリソース使用量の見積もりであることに注意してほしい。
特に、パラメータ数が多いテンプレートを使っていると C コードの合成時のレポートと実際にExport RTL した時のVivado での概算とのリソース使用量の違いが多い気がする。
  1. 2018年06月15日 05:33 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

TensorFlow + Kerasを使ってみた19(特徴マップ3個と2個のMNIST用CNNをテスト)

TensorFlow + Kerasを使ってみた18(特徴マップ5個のMNIST用CNNをVivado HLSで実装)”の続き。

特徴マップ5個のMNIST 用 CNN がうまく行ったようなので、特徴マップ 3 個と 2 個のMNIST 用CNN をKeras で学習させてみることにした。

まずは、Keras でSequentialモデルを作って学習する。以前使用していたCNN から畳み込み層の特徴マップの数を 5 個から 3 個に変更した。他のコードは変えていない。
tensorflow_keras_90_180614.png
tensorflow_keras_91_180614.png
tensorflow_keras_92_180614.png
tensorflow_keras_93_180614.png

Epoch 10 まで学習して val_acc: 0.9854 だった。やはり、特徴マップが多いものよりも精度は少し落ちている。ちなみに特徴マップが 10 個の時は、Epoch 12 で val_acc: 0.9898 、特徴マップが 5 個の時は、Epoch 10 まで学習して val_acc: 0.9864 だった。
学習も特徴マップが 10 個の時や 5 個の時よりも、3 個の時の方がより過学習になっていないようだ。


次に、Keras でSequentialモデルを作って学習する。以前使用していたCNN から畳み込み層の特徴マップの数を 5 個から 2 個に変更した。他のコードは変えていない。
tensorflow_keras_94_180614.png
tensorflow_keras_95_180614.png
tensorflow_keras_96_180614.png
tensorflow_keras_97_180614.png

Epoch 10 の時の精度が 0.9802 であまり上がらない。特徴マップ 2 個を使うという選択肢は厳しいようだ。
  1. 2018年06月14日 05:19 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

TensorFlow + Kerasを使ってみた18(特徴マップ5個のMNIST用CNNをVivado HLSで実装)

TensorFlow + Kerasを使ってみた17(特徴マップを10個から5個へ)”の続き。

前回は、今までは畳み込み層で 10 個の特徴マップを使用していたが、5 個ではどうだろうということでやってみたところ、98.64 % の精度が確保できた。今回は、特徴マップ5 個のMNIST 用 CNN を使用して、Vivado HLS で実装してみた。

まずは、畳み込み層と全結合層の重みとバイアスをKeras からC のヘッダファイルとして取得した。
TensorFlow + Kerasを使ってみた9(畳み込み層の重みをC のヘッダに変換)
TensorFlow + Kerasを使ってみた10(バイアスをC のヘッダに変換)
TensorFlow + Kerasを使ってみた11(全結合層の重みをC のヘッダに変換)"

そして、MNIST のデータの1000 個分をC のヘッダファイルとして出力した mnist_data.h を使用する。
TensorFlow + Kerasを使ってみた12(MNISTのデータの一部をC のヘッダに変換)

そうして、作成したVivado HLS 2017.4 の mnist_conv_nn5_hlss_org を作成した。
Keras で重みやバイアス、各層の出力の最大値、最小値をみたところ、畳み込み層の整数部のビット数を 1 ビット増やして、4 ビットとすることにした。総ビット数は 10 ビットで変更無しなので、小数部が 1 ビット減って 6 ビットとした。
各層の演算精度が書いてある mnist_conv_nn5_hlss.h を示す。ここの値を変更すると演算精度を変更することができる。
tensorflow_keras_89_180613.png

C シミュレーションを行った。結果を示す。
tensorflow_keras_86_180612.png

1000 個のMNIST のデータのうちでハードウェアの固定小数点演算ではエラーが 7 個、ソフトウェアの浮動小数点演算ではエラーが 9 個だった。
よって、ハードウェアの固定小数点演算での精度は、99.3 % となった。
ソフトウェアの浮動小数点演算での精度は、99.1 % となった。
だいぶ精度が良いが、10000 個のテストデータの内の最初の 1000 個のデータで評価しているので、他のところのデータの精度が悪かったのか?

C コードの合成を行った。結果を示す。
tensorflow_keras_87_180613.png
tensorflow_keras_88_180613.png

Latency は min が 130646 クロックで、max は 131366 クロックだった。100 MHz 動作だと約 1.31 ms となる。fpsにすると約 763 fps となる。
前回の特徴マップが 10 個の飽和演算なしの時の Latency の max は 204376 クロックだったので、 204376 / 131366 x 100 ≒ 155.58 % の性能ということになる。
リソース使用量はBRAM_18K が 66 個、DSP48E が 93 個、FF が 6215 個、LUT が 11144 個だった。
前回の特徴マップが 10 個の飽和演算なしの時のリソース使用量は、BRAM_18K が 130 個、DSP48E が 175 個、FF が 9005 個、LUT が 16391 個だったので、BRAM_18K については、半分以下のリソース使用量となった。
  1. 2018年06月13日 04:46 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

TensorFlow + Kerasを使ってみた17(特徴マップを10個から5個へ)

TensorFlow + Kerasを使ってみた16(C コードの合成、ビット幅の削減)”の続き。

前回は、各層の演算のビット幅をチェックしたが、ビット幅を小さくしても、思ったようにリソース使用量が少なくならなかった。今回は、IP のレイテンシとリソース使用量に効くのはなんと言っても、畳み込み層の特徴マップの数じゃないか?と思う。今までは畳み込み層で 10 個の特徴マップを使用していたが、5 個ではどうだろうということでやってみた。

まずは、Keras でSequentialモデルを作って学習する。以前使用していたCNN から畳み込み層の特徴マップの数を 10 個から 5 個に変更した。他のコードは変えていない。
tensorflow_keras_82_180612.png
tensorflow_keras_83_180612.png
tensorflow_keras_84_180612.png
tensorflow_keras_85_180612.png

Epoch 10 まで学習したが val_acc: 0.9864 なので、良さそうだ。ちなみに特徴マップが 10 個の時は、Epoch 12 で val_acc: 0.9898 だった。
演算のビット幅を落として精度が落ちるならば、特徴マップを減らして、演算のビット幅をあまり落とさないほう精度的には同じで、よりリソース使用量が小さくなるのではないか?と思っている。つまり、精度を考えた特徴マップの数と演算のビット幅の最適化を図っていきたい。
学習も特徴マップが 10 個の時よりも、5 個の時の方がより過学習になっていないようだ。
  1. 2018年06月12日 05:29 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

TensorFlow + Kerasを使ってみた16(C コードの合成、ビット幅の削減)

TensorFlow + Kerasを使ってみた15(飽和演算と通常の演算)”の続き。

前回は、畳み込み層に”Vivado HLS の任意精度固定小数点データ型の飽和演算”でテストした演算後の値を飽和演算し、また全結合層では内積の演算に飽和演算を用いる実装と、今まで通りにすべての演算に飽和演算を用いない場合について検証した。今回は、その飽和演算ありと飽和演算なしの2つの実装を合成して、リソース使用量を比較して見た後で、演算のビット幅を小さくして、C シミュレーション結果とC コードの合成結果を見ていこう。

最初に飽和演算なしの実装(mnist_conv_nn10_hlss_k_org)のC コードの合成結果を見る。
tensorflow_keras_73_180610.png
tensorflow_keras_74_180610.png

Latency は min が 202936 クロックで、 max が 204376 クロックだった。これは、100 MHz クロックを使用すると max の場合では、約 2.04 ms となった。
リソース使用量は、BRAM_18K が 130 個、DSP48E が 175 個、FF が 9005 個、LUT が 16391 個だった。

次に、飽和演算あり(mnist_conv_nn10_hlss_k)の実装のC コードの合成結果を見る。
tensorflow_keras_75_180610.png
tensorflow_keras_76_180610.png

こちらのLatency の min は 217336 クロックで max は 218776 クロックだった。100 MHz クロックだと max で 2.19 ms となった。飽和演算なしと比較すると、max 値で 217336 / 204376 ≒ 1.063 倍だった。
リソース使用量は、BRAM_18K が 130 個、DSP48E が 175 個は飽和演算なしと変化がないが、FF の 9168 個と LUT の 19109 個は飽和演算なしよりもリソース使用量が多かった。

次に演算のビット幅を縮小してみよう。
次に示す演算のビット幅でCNN の演算を行った。

#define INPUT_CHANNELS 1
#define INPUT_BIT_LENGTH 9
#define INPUT_INTEGER_LEN 1

#define CONV_BIT_LENGTH 8
#define CONV_INTEGER_LEN 3
#define CONV_CHANNELS 10
#define CONV_MID_BIT_LENGTH 13
#define CONV_MID_BIT_INTGER_LEN 3

#define AFFINE_BIT_LENGTH 11
#define AFFINE_INTEGER_LEN 5

#define OUTPUT_BIT_LENGTH 12
#define OUTPUT_INTEGER_LEN 7


演算のビット幅を 2 ビット減らしてみた。ただし整数部のビット幅はいじっていないので、飽和演算あり、飽和演算なしで同様のエラーになるはずである。
飽和演算ありと飽和演算なしの実装の C シミュレーション結果を示す。
tensorflow_keras_77_180610.png

飽和演算ありと飽和演算なしどちらも、HW_ERROR_COUNT = 23, SW_ERROR_COUNT = 15 だった。
エラー内容を示す。なおこのエラー内容も、飽和演算ありと飽和演算なしで同一だった。

id = 18, max_id_ref = 3, max_id_sw = 8
id = 115, max_id_ref = 4, max_id_sw = 9
id = 151, max_id_ref = 9, max_id_hw = 8
id = 184, max_id_ref = 8, max_id_hw = 3
id = 217, max_id_ref = 6, max_id_hw = 5
id = 247, max_id_ref = 4, max_id_sw = 2
id = 321, max_id_ref = 2, max_id_sw = 7
id = 324, max_id_ref = 0, max_id_hw = 6
id = 340, max_id_ref = 5, max_id_hw = 3
id = 340, max_id_ref = 5, max_id_sw = 3
id = 406, max_id_ref = 5, max_id_hw = 9
id = 445, max_id_ref = 6, max_id_sw = 0
id = 448, max_id_ref = 9, max_id_hw = 8
id = 449, max_id_ref = 3, max_id_sw = 5
id = 508, max_id_ref = 6, max_id_hw = 5
id = 547, max_id_ref = 2, max_id_hw = 3
id = 551, max_id_ref = 7, max_id_hw = 3
id = 582, max_id_ref = 8, max_id_hw = 2
id = 582, max_id_ref = 8, max_id_sw = 2
id = 619, max_id_ref = 1, max_id_hw = 8
id = 619, max_id_ref = 1, max_id_sw = 8
id = 625, max_id_ref = 6, max_id_hw = 4
id = 655, max_id_ref = 8, max_id_hw = 3
id = 658, max_id_ref = 7, max_id_hw = 4
id = 659, max_id_ref = 2, max_id_hw = 1
id = 659, max_id_ref = 2, max_id_sw = 1
id = 674, max_id_ref = 5, max_id_hw = 3
id = 684, max_id_ref = 7, max_id_hw = 3
id = 684, max_id_ref = 7, max_id_sw = 3
id = 685, max_id_ref = 8, max_id_hw = 2
id = 723, max_id_ref = 0, max_id_hw = 4
id = 738, max_id_ref = 2, max_id_hw = 8
id = 813, max_id_ref = 9, max_id_sw = 8
id = 846, max_id_ref = 7, max_id_hw = 9
id = 870, max_id_ref = 6, max_id_hw = 5
id = 883, max_id_ref = 3, max_id_sw = 5
id = 947, max_id_ref = 8, max_id_sw = 9
id = 965, max_id_ref = 6, max_id_sw = 0
HW_ERROR_COUNT = 23, SW_ERROR_COUNT = 15


次に、演算ビット幅を縮小したままC コードの合成を行った。
まずは、飽和演算なしからC コードの合成結果を示す。
tensorflow_keras_78_180611.png
tensorflow_keras_79_180611.png

Latency は min が 202936 クロックで、 max が 204376 クロックだった。これは、100 MHz クロックを使用すると max の場合では、約 2.04 ms となる。これはビット幅を変更する前と同じだった。
リソース使用量は、BRAM_18K が 130 個、DSP48E が 175 個、FF が 9005 個、LUT が 16391 個だった。これはビット幅を変更する前からすると、FF が約 88.2 % 、LUT が約 91.4 % に減少している。

次に飽和演算ありの C コードの合成結果を示す。
tensorflow_keras_80_180611.png
tensorflow_keras_81_180611.png

Latency の min は 217336 クロックで max は 218776 クロックだった。100 MHz クロックだと max で 2.19 ms となった。やはり、こちらもビット幅を変更する前と同一だった。
リソース使用量は、BRAM_18K が 130 個、DSP48E が 175 個は飽和演算なしと変化がないが、FF の 8107 個と LUT の 17641 個だった。これはビット幅を変更する前からすると、FF が約 88.4 % 、LUT が約 92.3 % に減少している。

ビット幅を縮小しても思ったよりリソース使用量の縮小がなかったので、以前の下に示す演算ビット幅で行こうと思う。

#define INPUT_CHANNELS 1
#define INPUT_BIT_LENGTH 9
#define INPUT_INTEGER_LEN 1

#define CONV_BIT_LENGTH 10
#define CONV_INTEGER_LEN 3
#define CONV_CHANNELS 10
#define CONV_MID_BIT_LENGTH 16
#define CONV_MID_BIT_INTGER_LEN 3

#define AFFINE_BIT_LENGTH 13
#define AFFINE_INTEGER_LEN 5

#define OUTPUT_BIT_LENGTH 12
#define OUTPUT_INTEGER_LEN 7


また、飽和演算ありと飽和演算なしだが、現在の整数ビット幅を維持できれば、飽和演算ありと飽和演算なしとエラー数も変化がないので、リソース使用量の少ない飽和演算なしで行くことにする。
  1. 2018年06月11日 05:04 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

TensorFlow + Kerasを使ってみた15(飽和演算と通常の演算)

TensorFlow + Kerasを使ってみた14(Vivado HLSで実装2)”の続き。

前回は、Keras から重みやバイアスを取り出して、Vivado HLS 2017.4 に実装したCNN をC シミュレーションしたところ、ソフトウェアのfloat 演算では精度 99 %、ハードウェアの固定小数点演算では、精度 98 % を確認することが出来た。今回は、畳み込み層に”Vivado HLS の任意精度固定小数点データ型の飽和演算”でテストした演算後の値を飽和演算し、また全結合層では内積の演算に飽和演算を用いる実装と、今まで通りにすべての演算に飽和演算を用いない場合について検証してみよう。

まずは、今まで通りに飽和演算を用いない場合について現在のパラメータで実力を確認しよう。
現在のMNIST のデータ数は 500 個を使用してる。
固定小数点のビット長のパラメータを次に示す。

#define INPUT_CHANNELS 1
#define INPUT_BIT_LENGTH 9
#define INPUT_INTEGER_LEN 1

#define CONV_BIT_LENGTH 10
#define CONV_INTEGER_LEN 3
#define CONV_CHANNELS 10

#define AFFINE_BIT_LENGTH 13
#define AFFINE_INTEGER_LEN 7

#define OUTPUT_BIT_LENGTH 12
#define OUTPUT_INTEGER_LEN 7


入力層の出力のビット長は 9 ビットで、そのうちの 1 ビットが整数のビット長である。つまり +1-0.00390625 〜 -1 までだ。
畳み込み層の出力のビット長は 10 ビットで、そのうちの 3 ビットが整数のビット長である。
全結合層の出力のビット数は 13 ビットで、そのうちの 7 ビットが整数のビット長である。
C シミュレーションの結果を示す。
tensorflow_keras_68_180609.png

500 個のMNIST データに対して、ソフトウェアのfloat 演算は 7 個、ハードウェアの固定小数点演算は 12 個のエラーが出た。
精度を計算してみよう。
ソフトウェアのfloat 演算のエラー数は 3 個なので、 (500 - 7) / 500 x 100 = 98.6 %
ハードウェアの固定小数点演算は 6 個なので、 (500 - 12) / 500 x 100 = 97.6 %
ソフトウェアのfloat 演算とハードウェアの固定小数点演算との差は 1 % となった。

さて、ここから、飽和演算をする実装に変更した mnist_conv10_hlss_k プロジェクトと今までの飽和演算をしない実装の mnist_conv10_hlss_k_org プロジェクトを比較してみよう。

飽和演算をする実装に変更した mnist_conv10_hlss_k プロジェクトの畳み込み層の飽和演算を紹介しよう。
tensorflow_keras_70_180610.png

演算結果の val を飽和演算して out_sat に代入する。 out_sat を conv_out.data[k] に代入している。

次に、mnist_conv10_hlss_k プロジェクトの飽和演算を紹介する。
tensorflow_keras_71_180610.png

飽和演算するように宣言した dot配列を使用して演算するので、飽和演算が実行できる。

さて、今までのKeras パラメータを見ると、第1層目の全結合層の値域が小さいので、整数値のビット長の 7 ビットはいらないことが分かった。そこで 5 ビットとする。そして、精度方向に 3 ビット拡張することにした。整数部を 2 ビット削除して、小数部を 3 ビット追加した。つまり、ビット長を 14 ビット、整数部のビット長を 5 ビットとした。
変更した固定小数点のビット長のパラメータを示す。

#define INPUT_CHANNELS 1
#define INPUT_BIT_LENGTH 9
#define INPUT_INTEGER_LEN 1

#define CONV_BIT_LENGTH 10
#define CONV_INTEGER_LEN 3
#define CONV_CHANNELS 10
#define CONV_MID_BIT_LENGTH 16
#define CONV_MID_BIT_INTGER_LEN 3

#define AFFINE_BIT_LENGTH 14
#define AFFINE_INTEGER_LEN 5
//#define AFFINE_BIT_LENGTH 13
//#define AFFINE_INTEGER_LEN 7

#define OUTPUT_BIT_LENGTH 12
#define OUTPUT_INTEGER_LEN 7


このパラメータでMNIST データ数を 1000 個として C シミュレーションを行った。
tensorflow_keras_69_180610.png

飽和演算をする実装に変更した mnist_conv10_hlss_k プロジェクトと今までの飽和演算をしない実装の mnist_conv10_hlss_k_org プロジェクト共に、1000 個のMNIST データに対して、ソフトウェアのfloat 演算は 15 個、ハードウェアの固定小数点演算は 15 個のエラーが出た。
という訳で、ソフトウェアのfloat 演算とハードウェアの固定小数点演算のエラー個数が同一になった。

次に、

#define AFFINE_BIT_LENGTH 13

に変更して、全結合層の演算のビット長を 13 ビットにした。 C シミュレーションの結果を示す。
tensorflow_keras_72_180610.png

飽和演算をする実装に変更した mnist_conv10_hlss_k プロジェクトと今までの飽和演算をしない実装の mnist_conv10_hlss_k_org プロジェクト共に、1000 個のMNIST データに対して、ソフトウェアのfloat 演算は 15 個、ハードウェアの固定小数点演算は 14 個のエラーが出た。
全結合層第1層目の演算のビット幅を 1 ビット減らしたのに、ハードウェアの固定小数点演算での精度が増えてしまった。良い具合に間違ったようだ。
現在のソフトウェアのfloat 演算の精度は 98.5 % だった。
  1. 2018年06月10日 05:03 |
  2. TensorFlow, Keras
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋のまとめサイトの更新(2018年6月9日)

FPGAの部屋のまとめサイトを更新しました。
2018年6月8日までの記事のエントリを追加しました。Ultra96TensorFlow, Keras のエントリを追加しました。
  1. 2018年06月09日 07:25 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0