plaidMLがいつの間にか色々素晴らしくなっていた件

#plaidMLとは
昨今DeepLearningと言えば学習に（ものによっては推論時にも）GPUを利用することがほぼ必須となってきています。しかも各種ライブラリがCUDAというGPUを扱うためのプラットフォームを前提としているため、GPUの選択肢としてNvidia製のGPU一択となっている状況でした。

機械学習界隈ではフレームワークや各種ライブラリは主となって開発している企業がどこかに関わらず、オープンという形でソースコードレベルで公開されている潮流ができており、これはとても良い事だと思うのですが、実際必要となるハードウェアが特定企業に依存した形になってしまっているのはあまり好ましくありません。特にnvidiaは最近[規約関係](https://wirelesswire.jp/2017/12/62658/)で色々とありましたしね。。。

そこで登場したうちの1つがplaidMLです。plaidMLはCUDAのようなクローズドなプラットフォームに依存せずにWindows,Mac,Linux等どの環境でどのようなGPUでも利用できるようにしよう、という発想で開発が進められているオープンソースプロジェクトです。

> plaidML
> A platform for making deep learning work everywhere.

#導入コスト

すばらしい発想のプロジェクトでも導入コストが著しく高かったら誰も使ってくれません。その点plaidMLは優秀で、KerasというTensorflowやTheanoのラッパーライブラリで書いたコードは変更をほとんどする事無く利用できてしまいます。そのため過去の学習コストや、実際のコードとしての資産も失うこと無く導入できます。

詳しい使い方は素晴らしい記事が沢山あるので調べてみてください。

#Macでの利用

実はplaidMLは去年の段階から存在は知っていて、本当に素晴らしいと思っていたのですが私自身は利用していませんでした。何故かと言うと私の基本的な開発環境がMacだからです。

実は最近のMacはnvidia製のGPUを積んでおらず、Tensorflowも最近のバージョンではMacでのGPUサポートを切ってしまうくらい、環境としては恵まれていませんでした。

plaidMLでは初期の段階からMacでのGPU利用も謳ってはいたものの、計算途中でどこかしらで突然nan(Not a number)値が出現してしまったり、微妙に動作が問題となっていて使えなかったのが実情でした。

また、Macであることとは関係ないのですが、RNN（Recurrent Neural Network)がplaidMLで対応していないことで割りとよく利用される構造のネットワークが学習できない事もあったため様子見をしていました。

昨日までは。

#最近の進化
たまたまTwitterをチェックしていたらplaidMLがここ最近のアップデート（一部まだプレバージョン）で上記の問題を一掃していたので慌てて実際に試し、しかもちゃんと動いたことに感動したので記事にしようと書いています。
どんなアップデートが来たかというと…

## Metal対応
MacではGPUを利用する際Metalという専用のフレームワークがあるのをご存知でしょうか。plaidMLがMetalを経由してGPUを利用することで、Macでの動作に最適化され、公式では最大5倍ほどの高速化が見込まれるそうです。

http://vertex.ai/blog/accelerated-deep-learning-on-macos

私的に速度はあまり問題じゃなくて（もちろん早い方が良いですが）上記したような動作上の問題、そもそも正しく動くかどうかが問題でした。そこでチュートリアルの受け売りですが、手元のMacでベンチマークを走らせてみました。

【スペック】
iMac (Retina 5K, 27-inch, 2017)
プロセッサ 3.5 GHz Intel Core i5
メモリ 8 GB 2400 MHz DDR4
GPU Radeon Pro 575 4096 MB

```plaidbench
(venv) $ plaidbench keras mobilenet
Running 1024 examples with mobilenet, batch size 1
INFO:plaidml:Opening device "metal_amd_radeon_pro_575.0"
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.6/mobilenet_1_0_224_tf.h5
17080320/17225924 [============================>.] - ETA: 0sModel loaded.
Compiling network...
Warming up ...
Main timing
Example finished, elapsed: 1.7246689796447754 (compile), 8.218460083007812 (execution), 0.008025839924812317 (execution per example)
Correctness: PASS, max_error: 1.675534622336272e-05, max_abs_error: 7.674098014831543e-07, fail_ratio: 0.0


(venv) $ PLAIDML_DEVICE_IDS="amd_radeon_pro_575_compute_engine.0" plaidbench keras mobilenet
Running 1024 examples with mobilenet, batch size 1
INFO:plaidml:Opening device "amd_radeon_pro_575_compute_engine.0"
Model loaded.
Compiling network...
Warming up ...
Main timing
Example finished, elapsed: 2.1992452144622803 (compile), 10.095165252685547 (execution), 0.00985855981707573 (execution per example)
/venv/lib/python3.6/site-packages/numpy/core/_methods.py:26: RuntimeWarning: invalid value encountered in reduce
  return umr_maximum(a, axis, None, out, keepdims)
Correctness: FAIL, max_error: nan, max_abs_error: nan, fail_ratio: 0.0


(venv) $ PLAIDML_DEVICE_IDS="llvm_preview_cpu.0" plaidbench keras mobilenet
INFO:plaidml:Opening device "llvm_preview_cpu.0"
Model loaded.
Compiling network...
Warming up ...
Main timing
Example finished, elapsed: 2.1769189834594727 (compile), 50.87789797782898 (execution), 0.04968544724397361 (execution per example)
Correctness: PASS, max_error: 2.0418785425135866e-05, max_abs_error: 1.5869736671447754e-06, fail_ratio: 0.0
```

※ 出力から余分なWarning等は省いています。

executionやexecution per exampleが実際の実行時間です。最初の実行がDefaultをMetal経由のGPUに設定しているので今回のメインです。PLAIDML_DEVICE_IDSで環境変数を指定する事で利用するデバイスを変更できるので、それらを比較しています。2つ目がMetalを使わないGPU利用で3つ目がCPUです。

2つ目はCorrectnessがFAILになっている事やerrorがnanになっている事からもわかる通り、上述したようにうまく動いていないですね…

CPUと比較するとexecutionは相当早くなっているのが分かると思います！

##RNN対応
RNN、とりあわけLSTMは割りと「とりあえず使っといたら何か精度ちょっと上がる」くらい万能なネットワークで、よく使うのですが残念ながらplaidMLでは対応していませんでした。（※この感覚は私の経験則なので信用しないでください）

それがなんと！

>Support for LSTMs & RNNs with static loop sizes, such as examples/imdb_lstm.py (from Keras)
>> Training networks with embeddings is especially slow (#96)
RNNs are only staticly sized if the input's sequence length is explicitly specified (#97)
Fixes bug related to embeddings (#92)

多少制限はあるものの、ついに対応したんです！この感動はおそらく分かる人には分かる。

#まとめ
結局、MacでMetal対応（というよりGPUでまともに動くようになった）とRNN対応したよ、すごいよ、というこれだけを報告したい記事でした。

#おまけ
plaidMLを使うことで1つだけ問題（plaidMLとは関係ない問題）がTensorboardを直に利用できなくなることです。
バックエンドがTensorflowじゃなくなるので当たり前なのですが、「直に」と書いたのには訳があって、純粋に数値をグラフ化するだけ程度の利用であれば数値を渡せさえすれば利用できるんですね。私は下記のGistを参考に利用していますので、困っている方がいたら試してみてください。

https://gist.github.com/avacariu/92db7b7f1fa1696279e9029f0237e2d4