SDSoCで作ったニューラルネットワークの回路がZynqberryで動いた!
ZYNQを搭載したRaspberry PiライクなFPGAボード「Zynqberry」で、SDSoCプラットフォームを作り、ニューラルネットワークの計算をハードウェアでアクセラレートするデザインを動かすことができました。
私もいよいよSDSoCデビューです。
![]()
使用したニューラルネットワークの構造と学習済みモデルは、オライリーのDeepLearning本のものを使用しています。したがって、隠れ層は2層で、ニューロンの数は50個と100個です。
MNISTの実行結果はというと、認識率が93%程度。これはオライリー本のコードをPythonで実行した場合と同じです。
動作速度は、ZYNQ上のARMで実行するのと比べて約2~3倍にアクセラレートできています。初めて書いたSDSoCのコードですが、まずまずでした。
作られたSDSoCの回路はこんな感じになりました。
multsumという関数をハードウェア化し、それを3回呼び出すようにしています。
#pragma SDS data zero_copy(input[0:row])
#pragma SDS data zero_copy(output[0:col])
#pragma SDS data zero_copy(w[0:row*col])
#pragma SDS data zero_copy(b[0:row])
#pragma SDS data access_pattern(input:SEQUENTIAL)
#pragma SDS data access_pattern(output:SEQUENTIAL)
#pragma SDS data access_pattern(w:SEQUENTIAL)
#pragma SDS data access_pattern(B:SEQUENTIAL)
void multsum(int *input,int *output, int *w, int *b, int row, int col)
{
for(int i = 0; i < row; i++) {
int tmp = 0;
for(int j=0;j < col;j++) {
#pragma HLS PIPELINE II=1
tmp += input[j] * *w++;
}
tmp += b[i] * 256;
output[i] = sigmoid(tmp / 65536.) * 256;
}
}
void hw_predict(int *p, int *y1, int *y2, int *y3,
int *w1_8b, int *b1_8b,
int *w2_8b, int *b2_8b,
int *w3_8b, int *b3_8b,
int w1r, int w1c, int w2r, int w2c, int w3r, int w3c) {
multsum(p, y1 ,w1_8b, b1_8b, w1r, w1c);
multsum(y1,y2 ,w2_8b, b2_8b, w2r, w2c);
multsum(y2,y3 ,w3_8b, b3_8b, w3r, w3c);
}
ちょっとワーキング用の配列が多いのと、シグモイド関数を実行するときに浮動小数点になっているのが気になりますが、それでも何も考えずに書いて、動いてしまいました。
![]()
パフォーマンスを見てみると、毎回の積和計算が整数化して6クロックで行えているので、自分でHDL書けばもっと最適化できるのにな・・と思えます。
また、シグモイド関数の中身は1/(1+exp(-x))をdouble型でベタに実行していますが、実行時間はというと・・
100クロック以上使っています。
浮動小数点の除算や、exp、加算などを愚直に行っているようでした。これならソフト、というかNEONでやったほうが速いかもしれませんね。
でも、sigmoid関数の計算はニューロン1個に対して1回なので、それほどパフォーマンスに影響はしないのではないかと思われます。
![]()
リソース使用量は76%。ほとんどがシグモイド関数だと思われます。
結果はというと、
- オライリー本の第3章のMNIST認識多層パーセプトロンのプログラムをCで書き直してSDSoCに移植
- ZYNQのARMより2~3倍高速
- 何も考えなくても、浮動小数点のシグモイド関数ができてしまった!
私もついにSDSoCデビューできました。
![]()
こちらのスタータキットを使って試せるよう、デザインのアップロードを準備中です。
https://www.trenz.jp/product/TE0726-STARTER-KIT
お楽しみに
| 固定リンク







コメント