FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

C++で記述された軽量CNN実装 mojo-cnn 試行 (1. x86上での動作試行)

f:id:msyksphinz:20180515001039j:plain
※ 画像はイメージです。

ディープラーニングと言えばTensorFlowだのKerasだのChainerだの、高級なインタフェースを持ったツールを使うケースが多いが、例えばマイコンや組み込みプロセッサなどでディープラーニングを動かしたいときは、そこまで高級な機能はいらず、C++などで簡単に記述されたCNNの実装のほうが実行しやすかったりする。

というわけで、RISC-V上で(というかFPGA上などで動いている非力なプロセッサ)でCNNを動かすことができれば面白そうだ。 「ゼロから作るディープラーニング」を見ながら位置からC++で実装してもよいけど大変そうなので、とりあえず簡単なフレームワークは無いかと探して、mojo-cnnというC++のCNN実装を見つけた。

github.com

mojo-cnn を使ってCIFAR-10を動かしたい

さっそくgit cloneして動作させてみる。環境はUbuntu 18.04 LTS上にダウンロードして試行した。 MNISTはもう飽きてしまったのでCIFAR-10を使ってみたい。ちょうとmojo-cnnにもCIFAR-10を動かすための環境がある。

単純にcloneしてビルドすると以下のようにエラーが出る。

$ git clone https://github.com/gnawice/mojo-cnn.git
$ cd mojo-cnn/example
$ make
g++ -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx test.cpp -o test
In file included from test.cpp:44:0:
../mojo/mojo.h:31:0: warning: "MOJO_AVX" redefined
 #define MOJO_AVX // turn on AVX / SSE3 / SIMD optimizations

<command-line>:0:0: note: this is the location of the previous definition
In file included from ../mojo/network.h:38:0,
                 from ../mojo/mojo.h:78,
                 from test.cpp:44:
../mojo/layer.h:41:10: fatal error: windows.h: No such file or directory
 #include <windows.h>
          ^~~~~~~~~~~
compilation terminated.
makefile:7: recipe for target 'test' failed
make: *** [test] Error 1

#include <windows.h> を除去しても以下のようなエラーで進まない。

diff --git a/mojo/layer.h b/mojo/layer.h
index 4abef77..1e2e773 100644
--- a/mojo/layer.h
+++ b/mojo/layer.h
@@ -38,7 +38,7 @@
 namespace mojo
 {

-#include <windows.h>
+// #include <windows.h>
        /*
        double PCFreq = 0.0;
        __int64 CounterStart = 0;
g++ -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx test.cpp -o test
In file included from test.cpp:44:0:
../mojo/mojo.h:31:0: warning: "MOJO_AVX" redefined
 #define MOJO_AVX // turn on AVX / SSE3 / SIMD optimizations

<command-line>:0:0: note: this is the location of the previous definition
In file included from ../mojo/mojo.h:76:0,
                 from test.cpp:44:
../mojo/core_math.h: In function ‘void mojo::dotsum_unwrapped_2x2(const float*, const float*, float*, int)’:
../mojo/core_math.h:178:2: error: ‘_mm256_zeroupper’ was not declared in this scope
  _mm256_zeroupper();
  ^~~~~~~~~~~~~~~~
../mojo/core_math.h:178:2: note: suggested alternative: ‘_mm_setzero_pd’
  _mm256_zeroupper();
...

どうもLinux環境だとAVXのIntrinsicが使えないようなので、これを使わないようにしよう。

  • AVX / SSE を使うオプションを削除する。またVGGはOpenCVを使うので除去した。
diff --git a/examples/makefile b/examples/makefile
index ee79729..70267b4 100644
--- a/examples/makefile
+++ b/examples/makefile
@@ -1,7 +1,7 @@
 CC=g++
-CFLAGS_OMP= -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx
+CFLAGS_OMP= -I../mojo/ -std=c++11 -O3

-all: test train_mnist train_cifar vgg
+all: test train_mnist train_cifar

 test: test.cpp
        $(CC) $(CFLAGS_OMP) test.cpp -o test
  • MOJO_AVX / MOJO_OMP をundefinedにする。
--- a/mojo/mojo.h
+++ b/mojo/mojo.h
@@ -28,8 +28,8 @@

 #pragma once

-#define MOJO_AVX       // turn on AVX / SSE3 / SIMD optimizations
-#define MOJO_OMP       // allow multi-threading through openmp
+// #define MOJO_AVX    // turn on AVX / SSE3 / SIMD optimizations
+// #define MOJO_OMP    // allow multi-threading through openmp
 //#define MOJO_LUTS    // use look up tables, uses more memory
 //#define MOJO_CV3     // use OpenCV 3.x utilities
 //#define MOJO_CV2     // use OpenCV 2.x utilities

これでコンパイルできるようになったのでだが、次にトレーニングデータとしてCIFAR-10のデータセットをダウンロードしてこなければならない。

$ cd ../data/
$ curl -L http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz | tar xvz

これでシミュレーションを開始しよう。

$ ./train_cifar
==  Network Configuration  ====================================================
  0 : I1 : input 32 32 3 identity
  1 : C1 : convolution 3 16 1 elu
  2 : P1 : semi_stochastic_pool 3 3
  3 : C2 : convolution 3 64 1 elu
  4 : P2 : semi_stochastic_pool 4 4
  5 : FC2 : fully_connected 10 softmax

  I1-C1, C1-P1, P1-C2
  C2-P2, P2-FC2
==  CIFAR-10  Epoch  1  =============================================== 0:00:00
  mini batch:           16
  training time:        236.644 seconds on 1 threads
  model updates:        1805 (37% of records)
  estimated accuracy:   43.39%
  testing:              20% (13sec remaining)

...

==  CIFAR-10  Epoch  129  ============================================= 7:00:58
  mini batch:           16
  training time:        175.415 seconds on 1 threads
  model updates:        1061 (26% of records)
  estimated accuracy:   68.424%
  test accuracy:        67.69% (32.31% error)
  saved model:          ../models/snapshots/tmp_129.txt

==  CIFAR-10  Epoch  130  ============================================= 7:04:10
  mini batch:           16
  training time:        175.767 seconds on 1 threads
  model updates:        1061 (26% of records)
  estimated accuracy:   68.426%
  test accuracy:        67.69% (32.31% error)
  saved model:          ../models/snapshots/tmp_130.txt

==  CIFAR-10  Epoch  131  ============================================= 7:07:23
  mini batch:           16
  training time:        175.754 seconds on 1 threads
  model updates:        1062 (26% of records)
  estimated accuracy:   68.436%
  test accuracy:        67.66% (32.34% error)
  saved model:          ../models/snapshots/tmp_131.txt

Elvis just left the building. No further improvement in training found.
Stopping..

EPOCH 131までかかってる... しかも7時間も。さすがにAVXもなし、GPUもなしというのでかなりきつい。

でもとりあえず純粋C++で動いているので、RISC-Vとかほかのプラットフォームに移植しやすそうだし改造も簡単そうだ。