Raspberrypi3でMovidius Neural Compute Stickのサンプルまでを動かしてみた


# 概要

AI流行っていますね。そんな中、IntelがUSBで動くDeep Learningスティック、[Movidius Neural Compute Stick](https://www.movidius.com/)の販売を開始しました(以下、Movidius NCS)。このスティックは、Caffeのネットワークモデルの計算だけスティックで高速に実行するというものです。今年度の[CVPR2017](http://cvpr2017.thecvf.com/)で先行販売されたので、早速購入してきました。ちなみに余談ですが、僕が先頭に並んでいたので、世界で最も最初にこの製品を手にしたというのが、ちょっとした自慢です。

追記：Movidiusは製品名ではなく開発している会社名で、製品名はNeural Compute Stickなんですね。ただ、NCSだとよくわからないので、Movidius NCSと記載することにします。

![スクリーンショット 2017-07-31 2.52.29.png](https://qiita-image-store.s3.amazonaws.com/0/146529/322f7bfd-4713-736e-cdae-a595bf7458c0.png "スクリーンショット 2017-07-31 2.52.29.png")


本記事では、取り敢えずMovidius NCSをRaspberrypi3で動かすまでの手順について紹介します。本当はRealTimePoseMachineとかで姿勢推定までやりたかったですが、時間が足りなかったので近々実施します。実はこの記事の前に、できればMacで動かしたいという気持ちでVirtualBoxで試したり色々動かなかったという作業を永遠とやっていたので、もし動いたら教えて欲しいです・・・！（未確認ですがVMWareでは動くっぽいという[報告](https://ncsforum.movidius.com/discussion/123/ncs-sdk-virtual-machine-support)があるみたいです。）

![スクリーンショット 2017-07-31 2.51.18.png](https://qiita-image-store.s3.amazonaws.com/0/146529/f262bb7f-100c-f475-38eb-dfc375dd4724.png "スクリーンショット 2017-07-31 2.51.18.png")


# Raspberrypi3へのUbuntuインストール

ラズパイで遊ぶといえば、OSは大抵Rasbianですが、Movidius NCSはUbuntuのみ対応ですので、Ubuntuをインストールします。今回はラズパイ３をターゲットにしていますが、仕様的にはZEROや２でも大丈夫に見えます。こちらも時間があったら調査したい気持ちは少しあります。

まずは、OSのインストールの手順。OSを[ここ](https://wiki.ubuntu.com/ARM/RaspberryPi)からダウンロードしてきます。なお、この作業はMac上で行うことを前提としておきます。

```
$ wget http://www.finnie.org/software/raspberrypi/ubuntu-rpi3/ubuntu-16.04-preinstalled-server-armhf+raspi3.img.xz
```

まず、OSを入れるmicroSDを用意ます。PCにmicroSDをセットし、

```
diskutil list
```

などでディレクトリを確認し、

```
diskutil umountDisk /dev/disk2
```

でアンマウントしておきましょう。
次に、先ほどダウンロードしたOSイメージを解凍してmicroSDに焼き込みます。Macの場合は、xzを扱うためにツールをインストールしておきます。

```
$ brew install xz
$ xzcat ubuntu-16.04-preinstalled-server-armhf+rasPi 3.img.xz | sudo dd of=/dev/disk2
```

１〜２時間ほど待つと焼き終わると思います。ここでの作業は、これで終了ですのでmicroSDを抜いてラズパイ側にセットしましょう。

# Raspberrypi3の準備

大した作業ではないですが、リモートで作業できたほうが良いですので、簡単なセットアップをします。

```
sudo apt-get update
sudo apt-get install avahi-daemon
```

以降、別マシンから```ssh ubuntu@ubuntu.local```でログインできるようになります。

# Movidius NCSのセットアップ

ここからが、Movidiusの NCS準備です。と言っても、一箇所を除き[公式動画](https://www.youtube.com/watch?v=ZtgBTk7HuJU)をそのまま動かすだけですが・・・。作業はラズパイにsshで入って、適当な場所で行います。僕はホーム直下でそのままやっちゃっています。

まずは、SDKをダウンロードおよび解凍します。

```
$ wget https://ncs-forum-uploads.s3.amazonaws.com/ncsdk/MvNC_SDK_01_07_07/MvNC_SDK_1.07.07.tgz
$ tar xvf MvNC_SDK_1.07.07.tgz
$ tar xvf MvNC_API-1.07.07.tgz
$ tar xvf MvNC_Toolkit-1.07.06.tgz
```

続いてインストール作業を行います。まずは、caffe環境をラズパイに入れます。これが結構長く、数時間待ちですので、気長に待ちます。

```
cd bin
./setup.sh
```

ここで、環境変数の再読込みなど色々あるので、一旦ログアウトし再度ログインします。その上で依存関係のチェックを行います。以下のコマンドで何も出なければOKです。

```
cd bin
make check
```

次に、諸々のモデルなどのインストールです。

```
cd ncapi
./setup.sh
```

すると、下記エラーが僕の環境では出ました。

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/mvnc/mvncapi.py", line 7, in <module>
    f = CDLL("./libmvnc.so")
  File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: ./libmvnc.so: cannot open shared object file: No such file or directory
```

これは、ラズパイ用にライブラリが正常にインストールされていないと起こるようです。ラズパイ用のライブラリのインストールは下記で行います。

```
$ cd ncapi/redist/pi_jessie
$ sudo dpkg -i *
```

再度、モデルの構築を行いますが、続きのみで良いので、

```
$ cd ncapi/tools
$ ./convert_models.sh
```

で実行します。今度はエラーが消えたと思います。ただし、最後に

```
[Error 28] Caffe Error: MemoryError. Potential Cause: Available RAM not sufficient for Network to be loaded into Caffe
```

と出ます。ラズパイだとAlexNetは大きすぎるっぽいですね（モデル構築についてはデフォルトのコマンドでは速度が出ないので、本記事最後の追記も確認ください）。

# Movidius NCSの実行

さて、ここからはマニュアル通りに少しずつ確認していきます。最初は上記でcompileしてできたであろうgraphファイルをチェックします。

```
$ cd bin
$ python3 mvNCProfile.pyc ./data/lenet8.prototxt -w ./data/lenet8.caffemodel
```

出力は下記のようになります。

```
ubuntu@ubuntu:~/bin$ python3 mvNCProfile.pyc ./data/lenet8.prototxt -w ./data/lenet8.caffemodel
mvNCProfile v02.00, Copyright @ Movidius Ltd 2016

USB: Transferring Data...
Time to Execute :  7.54  ms
USB: Myriad Execution Finished
Time to Execute :  6.58  ms
USB: Myriad Execution Finished
USB: Myriad Connection Closing.
USB: Myriad Connection Closed.
Network Summary

Detailed Per Layer Profile
Layer      Name                                 MFLOPs    Bandwidth MB/s        time(ms)
========================================================================================
0          conv1                                 5.530            258.44            1.20
1          pool1                                 0.014            304.65            0.09
2          conv2                                 4.301            252.09            0.91
3          pool2                                 0.004            160.46            0.04
4          ip1                                   0.002           2083.80            0.41
5          ip2                                   0.001            481.20            0.02
6          softmax                               0.000              0.61            0.03
----------------------------------------------------------------------------------------
           Total inference time                                                     2.70
----------------------------------------------------------------------------------------

Generating Profile Report 'output_report.html'...
```

これは、ネットワークを可視化するもので、```output_report.html```に結果が出力されます。以下に途中までの図を記載します。

![スクリーンショット 2017-07-31 2.44.09.png](https://qiita-image-store.s3.amazonaws.com/0/146529/9e6782b7-a2db-07aa-d316-4a5164ec945c.png "スクリーンショット 2017-07-31 2.44.09.png")


次に、ネットワークの動作チェックをします。

```
$ python3 ./mvNCCheck.pyc ./data/lenet8.prototxt -w ./data/lenet8.caffemodel -S 255 -M ./data/imagenet_mean.py
```

結果は下記となります。

```
ubuntu@ubuntu:~/bin$ python3 ./mvNCCheck.pyc ./data/lenet8.prototxt -w ./data/lenet8.caffemodel -S 255 -M ./data/imagenet_mean.py
mvNCCheck v02.00, Copyright @ Movidius Ltd 2016

USB: Transferring Data...
USB: Myriad Execution Finished
USB: Myriad Connection Closing.
USB: Myriad Connection Closed.
Result:  (1, 10)
1) 8 0.48267
2) 2 0.34961
3) 4 0.09375
4) 6 0.029266
5) 3 0.015182
Expected:  (10,)
1) 8 0.48413
2) 2 0.3479
3) 4 0.09436
4) 6 0.029251
5) 3 0.015228
------------------------------------------------------------
 Obtained values
------------------------------------------------------------
 Obtained Min Pixel Accuracy: 0.3530004993081093% (max allowed=2%), Pass
 Obtained Average Pixel Accuracy: 0.08027215953916311% (max allowed=1%), Pass
 Obtained Percentage of wrong values: 0.0% (max allowed=0%), Pass
 Obtained Pixel-wise L2 error: 0.15237264986375257% (max allowed=1%), Pass
 Obtained Global Sum Difference: 0.0038862228393554688
------------------------------------------------------------
```

さて、ここまで来たら、後は簡単な認識をやってみましょう。まずはプログラムをビルドします（2017/08時点でのサンプルにはバグがあり、本記事最後の追記も併せて確認ください）。

```
$ cd ncapi/c_examples/
$ make
```

そしたら、compileして作成したgraphを実行してみましょう。

```
./ncs-fullcheck -c1 ../networks/GoogLeNet ../images/cat.jpg
```

![cat.jpg](https://qiita-image-store.s3.amazonaws.com/0/146529/96f60196-e025-6f5e-e5fc-d7123c8f5985.jpeg "cat.jpg")



結果は下記となります。

```
ubuntu@ubuntu:~/ncapi/c_examples$ ./ncs-fullcheck -c1 ../networks/GoogLeNet ../images/cat.jpg
OpenDevice 1.5 succeeded
Graph allocated
Persian cat (46.17%) Egyptian cat (17.80%) tabby, tabby cat (7.54%) lynx, catamount (7.19%) Arctic fox, white fox, Alopex lagopus (5.78%)
Inference time: 569.193970 ms, total time 582.029392 ms
Deallocate graph, rc=0
Device closed, rc=0
```

速度は0.5秒とMovidius NCSが使えているか微妙に見える結果ですね、ラズパイにしては速いのだろうか。そして本当は（多分）tiger catかtabby catあたり(すみません、前回Egyptian catと書いていましたが間違いでした、猫わからん・・・)が１位に来るはずですが、うまくはいってない模様です。近々確認してみます。

# まとめ

今回の記事では、一先ずラズパイでMovidius NCSが動くところまでを実施しました。精度・速度とともに未検証なので、まずは次回はその検証を行います。実用上好きなCaffeモデルを扱う所が重要ですが、それも次々回以降に試していこうと思います。その他ラズパイに載せたカメラで認識する、Dockerで動かすなど興味は付きないので、試した分だけこちらに書いていこうと思います。

# [2017/08/10 追記]

上記で述べたように、精度と速度を検証しました。結論として、Movidius NCSのバグやサンプルの実行方法によって、大分改善される事がわかりました。

## 精度の改善

コメントでも報告いただいておりますが、```ncs-fullcheck```についてはサンプルのバグが[報告](https://ncsforum.movidius.com/discussion/143/ordering-of-rgb-is-wrong-in-ncs-fullcheck-c)されているようです。具体的にはRGBとBGRが逆とのことです。これを修正すると精度が改善するようです。報告にあるように下記のように書き換えることで修正できます。

```
149,154c149,151
<               float r = imgfp32[3*i+0];
<               float g = imgfp32[3*i+1];
<               float b = imgfp32[3*i+2];
<               imgfp32[3*i+0] = (b-mean[0])*std[0];
<               imgfp32[3*i+1] = (g-mean[1])*std[1];
<               imgfp32[3*i+2] = (r-mean[2])*std[2];
---
>               imgfp32[3*i] = (imgfp32[3*i]-mean[0])*std[0];
>               imgfp32[3*i+1] = (imgfp32[3*i+1]-mean[1])*std[1];
>               imgfp32[3*i+2] = (imgfp32[3*i+2]-mean[2])*std[2];
```

## 速度の改善

サンプル中で、```convert_model.sh```でネットワークをコンパイルするのですが、その際にデフォルトでは何故かSHAVEコアを1個しか使わない設定になっているようです。これを12個使うようにオプションで指定することで速度が大幅に改善できます。コンパイルは下記のようにすることで実行できます。

```
cd data/GoogLeNet
python3 ../../bin/mvNCCompile.pyc ./deploy.prototxt -s 12 -w ./bvlc_googlenet.caffemodel -o graph
```

これは既に[記事](http://www.cnx-software.com/2017/08/09/movidius-neural-compute-stick-shown-to-boost-performance-by-about-3-times-on-raspberry-pi-3-board/?utm_campaign=shareaholic&utm_medium=twitter&utm_source=socialnetwork)として報告されております。素晴らしい！

## 実験結果

GoogLeNetの実験の結果は下記のようになりました。速度が約100msと、nshaveが1の時と比べて5倍以上速くなっていますね。認識結果も問題ないように思います。

```
OpenDevice 1.5 succeeded
Graph allocated
tiger cat (44.21%) tabby, tabby cat (25.59%) Egyptian cat (21.39%) lynx, catamount (2.53%) Persian cat (1.04%)
Inference time: 108.022110 ms, total time 121.338250 ms
Deallocate graph, rc=0
Device closed, rc=0
```