MacBook Pro でOpenPose その2

[MacBook Pro でOpenPose](https://qiita.com/kenkino/items/fccb40bc09827f2af5b9)でMacBook ProでもOpenPoseが動作することがわかったのですが「GPUで動かせないかな？」と思い調べていたところ[PlaidML](https://www.intel.ai/plaidml/)というものをあるということで、下記を読んでみたところ、自分でもなんとかできそうかなということで試してみました。

- [plaidMLがいつの間にか色々素晴らしくなっていた件](https://qiita.com/Mco7777/items/7b33414bc113584493e9) 
- [非NVIDIAなGPUでディープラーニング可能なPlaidMLをMacで試してみた。](https://yaritakunai.hatenablog.com/entry/plaidml-test)


# 環境

- MacBook Pro 15in 2018 2.2 GHz Core i7 32GB Memory
- Mojave 10.14.6
- python 3.7.5
- homebrew でpythonとvirtualenv、wgetをインストール

# 設定手順

## 準備

準備としては、Pythonの仮想環境を設定します。前回の環境を使っても良かったのですが、何か競合等がある場合を考えて作り直しました。

```
仮想環境を作成
$ virtualenv mppose-plaidml

仮想環境へ入る
$ source mppose-plaidml/bin/activate
```

## PlaidML設定、動作確認

仮想環境へ入った後、PlaidMLパッケージのインストールと使用するデバイスの設定を行います。自分のMacBook ProにはRadeon Pro 555xが入っていたのでそちらを選択しました。

```
PlaidMLとベンチマークのパッケージをインストール
$ pip install plaidml-keras plaidbench

PlaidMLで使用するデバイスを設定
$ plaidml-setup
※デバイスの設定以外は y で
・・・
< 中略 >
・・・
デバイスの設定箇所では一覧に表示されたデバイスの番号を選択してEnter
Multiple devices detected (You can override by setting PLAIDML_DEVICE_IDS).
Please choose a default device:

   1 : llvm_cpu.0
   2 : opencl_intel_uhd_graphics_630.0
   3 : opencl_cpu.0
   4 : opencl_amd_radeon_pro_555x_compute_engine.0
   5 : metal_intel(r)_uhd_graphics_630.0
   6 : metal_amd_radeon_pro_555x.0

Default device? (1,2,3,4,5,6)[1]:6
・・・
< 中略 >
・・・
```
パッケージのインストールとデバイスの設定が終わったら、動作確認を行います

```
ベンチマークで動作を確認
$ plaidbench keras mobilenet
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "metal_amd_radeon_pro_555x.0" ※この表示が出ていれば選択したデバイスで動作している
Compiling network... Warming up... Running...
Example finished, elapsed: 0.545s (compile), 14.425s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            14.09 ms                  0.00 ms / 1000000000.00 fps
Correctness: PASS, max_error: 1.675534622336272e-05, max_abs_error: 7.674098014831543e-07, fail_ratio: 0.0
```

動作していることは分かったのですが「CPUを使った場合と比べて早いのか遅いのか」が分からずどうすればいいのかなと思ったのですが、コマンドの前に``PLAIDML_DEVICE_IDS="デバイス名"``の記述をすればそのデバイスで動作するという情報があり、試してみました。

```
CPUを実行デバイスに設定してベンチマーク
$ PLAIDML_DEVICE_IDS="opencl_cpu.0" plaidbench keras mobilenet
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "opencl_cpu.0"
Compiling network... Warming up... Running...
Example finished, elapsed: 3.034s (compile), 141.360s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            138.05 ms                 134.30 ms / 7.45 fps
Correctness: FAIL, max_error: 0.005546755623072386, max_abs_error: 0.0003522038459777832, fail_ratio: 0.076
```
結果としてはRadeon Pro 555xでCPUの10倍ほどのパフォーマンスが出るようなので、効果は出ているのかと思います。

あと、アクティビティモニターの``ウィンドウ　-> GPUの履歴``を選択するとGPUデバイス一覧と動作状況がリアルタイムで参照できるので、そちらを使って動作状況を見るという方法もあります。

## OpenPose設定

では、OpenPoseを設定していきます。手順としては大方は前回と同じですが、PlaidMLを使うために手順を一部変える必要があります。

```
OpenPoseを動作させるためのパッケージをインストールします。
$ pip install ipython configobj pillow opencv-python

Kerasバックエンド版のOpenPoseをGithubよりクローンします。
$ git clone https://github.com/michalfaber/keras_Realtime_Multi-Person_Pose_Estimation.git

ディレクトリへ移動します。
$ cd keras_Realtime_Multi-Person_Pose_Estimation/

リポジトリ内のmodelディレクトリに移動してcaffeとkerasの学習モデルをダウンロードします。
$ cd model
$ sh get_caffe_model.sh 
$ sh get_keras_model.sh 

モデル処理のモジュールにPlaidMLを使用するための修正を加えます。
$ vi cmu_model.py

※ ソースの文頭に下記の2行を追加するだけでいいです。 
import plaidml.keras
plaidml.keras.install_backend()

空のinit.pyを作成します。(私の環境では、これを作成しないと後のpythonスクリプトを動作させる際にmodelディレクトリ配下のpythonモジュールが読みこめず、エラーになりました)
$ touch __init.py__

リポジトリのトップへ戻り、caffeのnumpy学習モデルレイヤをダンプします。
$ cd ../
$ docker run -v `pwd`:/workspace -it bvlc/caffe:cpu python dump_caffe_layers.py

caffeの学習モデルレイヤをkeras用に変換します。
$ python caffe_to_keras.py 
```
以上で設定は完了です。

# 動作は成功、しかし・・・・

ということで、前回と同じくサンプル写真とカメラで動作確認をしてみたのですが・・・

```
サンプル写真の姿勢検出をしてみる。
$ python demo_image.py --image sample_images/ski.jpg 

カメラを起動して姿勢検出をしてみる。
$ python demo_camera.py
```
カメラの姿勢検出で、大体processing time 0.21sec程度という結果となり、CPUでの動作結果（processing time 0.07sec程度）よりもパフォーマンスが落ちてしまいました。
「どうしてだろう？」と調べてみたところ、PlaidMLのissueにこんなやりとりが上がっていました。
[Plaidml backend slower than TF backend #530](https://github.com/plaidml/plaidml/issues/530?fbclid=IwAR3Q8x1yFA54KmtwOWckIN_-7WvH0mrCejgb0uvNmXd6dggt3RdQlSP2XcA)

考えられる可能性としては「現在のPlaidMLが対応している計算モデルが少ない」ということなのかもしれません、今回使用したOpenPoseの計算モデル（CMU?）には対応できていないということなのかと思います。

通常であればここで諦めて「GPUを使った環境を用意する」のが正当かと思います。
今回は「現状自宅にある環境で、どこまでできるか？」というところなので、一応ここまでできたので、あとは

- PlaidMLのバージョンアップ後を期待する（1年以上更新が止まっているようなので期待薄?）
- PlaidMLが対応している計算モデルで動くように作り変えてみる

といったところで、色々考えつつやっていこうかと思います。