Macでも爆速で機械学習ができるかもしれないPlaidMLを試す．その1

# まえがき
いよいよ書きたかった機械学習ネタに踏み込んでいこうと思います．とはいえ，まだまだ機械学習に対して未知なことが多く，学ばなくてはいけないことが数多くあるので温かい目で見守っていただけたらと思います．指摘等がありましたらコメントお願いします．

# この記事の内容
- （この記事を書き始めた理由）
- PlaidMLとは何か
- PlaidMLのセットアップ
- PlaidMLのベンチマーク

この3つを簡単にまとめられたらなと思っています．

## この記事を書き始めた理由
大学１年生の時にKerasを軽く触れ，２年生で[OpenVINO](https://www.intel.co.jp/content/www/jp/ja/internet-of-things/solution-briefs/openvino-toolkit-product-brief.html)がどんなものなのかを教えてもらい，現在[PyTorch](https://pytorch.org/)を用いて機械学習とは何か・一体どのように構成していくのかについて教えてもらっています．しかし，Macで機械学習等を行っていてボトルネックとなるのは**GPU**でした．現在CatalinaはNvidia製のGPUをサポートしておらずCUDAを使うことができません．OpenVINOにおいてはIntel製CPUで,内蔵GPUを用いるので爆速さは期待できず，PyTorchはAMD製のGPUをMac上でサポートしていません．[AWSのクラウドGPU](https://aws.amazon.com/jp/?nc2=h_lg)や[Google Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)を使用するのもひとつの手かもしれませんが，**ローカル環境だけで完結させたい**という思いが強くありました．なのでそれを実現するために自分がやったことをここにまとめながら学んでいこうと思い書いています．

# 本題
## PlaidMLとは何か
PlaidMLの[github page](https://github.com/plaidml/plaidml)を見てみると
> PlaidML is an advanced and portable tensor compiler for enabling deep learning on laptops, embedded devices, or other devices where the available computing hardware is not well supported or the available software stack contains unpalatable license restrictions.

> PlaidML sits underneath common machine learning frameworks, enabling users to access any hardware supported by PlaidML. PlaidML supports Keras, ONNX, and nGraph.

> As a component within the nGraph Compiler stack, PlaidML further extends the capabilities of specialized deep-learning hardware (especially GPUs,) and makes it both easier and faster to access or make use of subgraph-level optimizations that would otherwise be bounded by the compute limitations of the device.

> As a component under Keras, PlaidML can accelerate training workloads with customized or automatically-generated Tile code. It works especially well on GPUs, and it doesn't require use of CUDA/cuDNN on Nvidia hardware, while achieving comparable performance.

> PlaidML works on all major operating systems: Linux, macOS, and Windows.

> If you are using a hardware target not supported by PlaidML by default, such as Clover, check out the instructions at building PlaidML to build a custom configuration to support your hardware.

すみません．翻訳は自信がないのでGoogle翻訳にでもかけてみてください笑
僕の理解としては**AMD製のGPUを使って機械学習・深層学習を可能にしてくれる**って感じです．

## PlaidMLのセットアップ
### 使用環境
まずは僕の使用環境を公開しておきます．

- MacBook Pro (16-inch, 2019)

| | |
|:-----------|------------:|:------------:|
| プロセッサ | 2.3GHz 8コアIntel Core i9 (i9-9880H) |
| メモリ | 16GB 2667MHz DDR4 |
| 内蔵グラフィックス | Intel UHD Graphics 630 1536MB |
| 独立グラフィックス | AMD Radeon Pro 5500M 8GB |

- Python
    - [Python 3.8.2](https://www.python.org/downloads/release/python-382/)
    - [Pyenv](https://github.com/pyenv/pyenv) + [Pipenv](https://github.com/pypa/pipenv)

こんな感じです．

### 環境の作成
#### 1. Homebrewの導入
まずは[Homebrew](https://brew.sh/index_ja)を入れます．下記のコードをTerminalに打ち込んでください．

```Terminal
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```

#### 2. pyenv と pipenvのインストール
下記のコードを1行ずつTerminalで実行してください．

```Terminal
$ brew install pyenv
$ brew install pipenv
```

#### 3. PlaidMLを使う環境の作成
下記のコードを1行ずつTerminalで実行してください．
ディレクトリ名も任意です．

```Terminal
$ cd
$ mkdir hoge_plaidml
$ cd hoge_plaidml
```

#### 4. pyenvでpythonのバージョンを管理
下記のコードを1行ずつTerminalで実行してください．
今回は自分の環境通りに記述をしていくので適宜バージョンを変えて実行してください

```Terminal
$ pyenv install 3.8.2
$ pyenv local 3.8.2
$ ls .*
```
#### 5. pipenvで仮想環境を構築
下記のコードを1行ずつTerminalで実行してください．
ここからは出力も表示しておきます．

```Terminal
$ pipenv --python 3.8.2

Creating a virtualenv for this project…
Pipfile: /Users/hoge/Playground/hoge_plaidml/Pipfile
Using /Users/hoge/.pyenv/versions/3.8.2/bin/python3.8 (3.8.2) to create virtualenv…
⠦ Creating virtual environment...created virtual environment CPython3.8.2.final.0-64 in 300ms
  creator CPython3Posix(dest=/Users/hoge/.local/share/virtualenvs/hoge_plaidml-hoge, clear=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/hoge/Library/Application Support/virtualenv)
    added seed packages: pip==20.2.3, setuptools==50.3.0, wheel==0.35.1
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator

✔ Successfully created virtual environment! 
Virtualenv location: /Users/hoge/.local/share/virtualenvs/hoge_plaidml-hoge
Creating a Pipfile for this project…

$ pipenv shell

Launching subshell in virtual environment…
 . /Users/hoge/.local/share/virtualenvs/hoge_plaidml-hoge/bin/activate
```
ここまでで仮想環境をビルドし，起動した状態になってます．

#### 6. PlaidMLの導入
下記のコードを1行ずつTerminalで実行してください．

```Terminal
$ pipenv install plaidml-keras

pipenv install plaidml-keras
Installing plaidml-keras…
Adding plaidml-keras to Pipfile's [packages]…
✔ Installation Succeeded 
Pipfile.lock (db4242) out of date, updating to (a88167)…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Building requirements...
Resolving dependencies...
✔ Success! 
Updated Pipfile.lock (a88167)!
Installing dependencies from Pipfile.lock (a88167)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
```
jupyter lab or notebookも入れておきたいので下記を参考にしていただければ幸いです．
[pyenv + pipenv環境でjupyter lab / notebookを使う](https://qiita.com/gabe-ds/items/fc9eb903a2a21ecbb86a)

#### 7. PlaidMLの設定

```Terminal
$ plaidml-setup

PlaidML Setup (0.7.0)

Thanks for using PlaidML!

The feedback we have received from our users indicates an ever-increasing need
for performance, programmability, and portability. During the past few months,
we have been restructuring PlaidML to address those needs.  To make all the
changes we need to make while supporting our current user base, all development
of PlaidML has moved to a branch — plaidml-v1. We will continue to maintain and
support the master branch of PlaidML and the stable 0.7.0 release.

Read more here: https://github.com/plaidml/plaidml 

Some Notes:
  * Bugs and other issues: https://github.com/plaidml/plaidml/issues
  * Questions: https://stackoverflow.com/questions/tagged/plaidml
  * Say hello: https://groups.google.com/forum/#!forum/plaidml-dev
  * PlaidML is licensed under the Apache License 2.0
 

Default Config Devices:
   llvm_cpu.0 : CPU (via LLVM)
   metal_intel(r)_uhd_graphics_630.0 : Intel(R) UHD Graphics 630 (Metal)
   metal_amd_radeon_pro_5500m.0 : AMD Radeon Pro 5500M (Metal)

Experimental Config Devices:
   llvm_cpu.0 : CPU (via LLVM)
   opencl_amd_radeon_pro_5500m_compute_engine.0 : AMD AMD Radeon Pro 5500M Compute Engine (OpenCL)
   opencl_intel_uhd_graphics_630.0 : Intel Inc. Intel(R) UHD Graphics 630 (OpenCL)
   metal_intel(r)_uhd_graphics_630.0 : Intel(R) UHD Graphics 630 (Metal)
   metal_amd_radeon_pro_5500m.0 : AMD Radeon Pro 5500M (Metal)

Using experimental devices can cause poor performance, crashes, and other nastiness.

Enable experimental device support? (y,n)[n]:
```

**y**を入力して進みます．

```Terminal
Multiple devices detected (You can override by setting PLAIDML_DEVICE_IDS).
Please choose a default device:

   1 : llvm_cpu.0
   2 : opencl_amd_radeon_pro_5500m_compute_engine.0
   3 : opencl_intel_uhd_graphics_630.0
   4 : metal_intel(r)_uhd_graphics_630.0
   5 : metal_amd_radeon_pro_5500m.0

Default device? (1,2,3,4,5)[1]:
```

使いたいデバイスを選びます．

```Terminal
Selected device:
    metal_amd_radeon_pro_5500m.0

Almost done. Multiplying some matrices...
Tile code:
  function (B[X,Z], C[Z,Y]) -> (A) { A[x,y : X,Y] = +(B[x,z] * C[z,y]); }
Whew. That worked.

Save settings to /Users/gabe/.plaidml? (y,n)[y]:
```
僕は一旦5を選んだので2行目に**metal_amd_radeon_pro_5500m.0**が表示されています．
**Whew. That worked.**とあるのでなんか計算うまくいってるみたいです．
今回も**y**を押してやりましょう．

```Terminal
Success!
```

やったね！成功です！！ここまででようやくセットアップが終りました．おつかれ．

## PlaidMLのベンチマーク
CPUが速いのか，OpenCLが速いのか，Metalが速いのか気になる方はご参考に．

### ベンチマークの下準備

```Terminal
$ pipenv install plaidbench

Installing plaidbench…
Adding plaidbench to Pipfile's [packages]…
✔ Installation Succeeded 
Pipfile.lock (a88167) out of date, updating to (7ac60f)…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Building requirements...
Resolving dependencies...
✔ Success! 
Updated Pipfile.lock (7ac60f)!
Installing dependencies from Pipfile.lock (7ac60f)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
```

### 実行
ここからは**7. PlaidMLの設定**で選んだデバイスが使われます．
5つもデバイスがあるので一気に結果を表示しておきます．

```Terminal
$ plaidbench keras mobilenet

---
1 : llvm_cpu.0
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "llvm_cpu.0"
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.6/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 14s 1us/step
Compiling network... Warming up... Running...
Example finished, elapsed: 2.695s (compile), 84.637s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            82.65 ms                  80.31 ms / 12.45 fps
Correctness: PASS, max_error: 1.7640328223933466e-05, max_abs_error: 7.040798664093018e-07, fail_ratio: 0.0

---
2 : opencl_amd_radeon_pro_5500m_compute_engine.0
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "opencl_amd_radeon_pro_5500m_compute_engine.0"
Compiling network... Warming up... Running...
Example finished, elapsed: 3.966s (compile), 10.173s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            9.93 ms                   0.00 ms / 486825.29 fps
Correctness: PASS, max_error: 2.0418785425135866e-05, max_abs_error: 1.0728836059570312e-06, fail_ratio: 0.0

---
3 : opencl_intel_uhd_graphics_630.0
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "opencl_intel_uhd_graphics_630.0"
Compiling network... Warming up... Running...
Example finished, elapsed: 7.235s (compile), 22.982s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            22.44 ms                  15.99 ms / 62.55 fps
Correctness: PASS, max_error: 7.947917765704915e-06, max_abs_error: 1.0728836059570312e-06, fail_ratio: 0.0

---
4 : metal_intel(r)_uhd_graphics_630.0
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "metal_intel(r)_uhd_graphics_630.0"
Compiling network... Warming up... Running...
Example finished, elapsed: 3.840s (compile), 23.871s (execution)

-----------------------------------------------------------------------------------------
Network Name         Inference Latency         Time / FPS          
-----------------------------------------------------------------------------------------
mobilenet            23.31 ms                  0.00 ms / 1000000000.00 fps
Correctness: PASS, max_error: 6.440454399125883e-06, max_abs_error: 5.811452865600586e-07, fail_ratio: 0.0

---
5 : metal_amd_radeon_pro_5500m.0
Running 1024 examples with mobilenet, batch size 1, on backend plaid
INFO:plaidml:Opening device "metal_amd_radeon_pro_5500m.0"
Compiling network... Warming up... Running...
Example finished, elapsed: 1.821s (compile), 14.672s (execution)

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

こんな感じになりました．
数値的にはOpenCLとMetalを使用したamd_radeon_pro_5500mがCPUに比べて速そうですね．

#まとめ
独立GPUを搭載していないMacではCPUか内蔵GPUのスコアをみてください．
eGPUでのベンチマークも気になるので大学で試す機会があればいいなぁ．追記していきたいです．
続編の記事ではガンガンPlaidMLを使っていけたらいいなと思います．
**最後までご覧いただきありがとうございました！**