Python以外でもDeep Learningしたい！

# はじめに 
この記事は、[NTTテクノクロス Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ntt-tx) の23日目です。

こんにちは、NTTテクノクロスの広瀬と申します。業務では高精細VR配信エンジン[^1]や機械学習による画像認識AIエンジンの研究開発に取り組んでいます。

今回はちょっと技術や開発から離れて、Deep Learningを組み込んだアプリ開発現場で、速度を求めたりGUIが欲しかったりと**アプリ開発はPython以外で開発したい**時はどうするの？といった疑問を解決する入口部分に触れていきたいと思います。

入門書を読み終わってアプリを作ろう！という方の、次の勉強ステージ選択の手助けになれれば幸いです。


## 我々はPythonに縛られている

世の中にはTensorFlowやPyTorch/ChainerといったDeep Learningを扱うフレームワークは数多く存在するのですが、開発にあたっては**Python**を前提としています。
そして、このフレームワーク達は高機能であるためにモデルの定義から、学習/推論フェーズをまでをサポートしてくれるので、以下のような推論フェーズもPythonで実装する構成になりがちです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/0a1515c3-596a-68a9-1b86-7b3a5fd4e8f7.png" alt="推論ソースの流用">

第三者に提供するアプリケーションを考えるにあたって、

1. C++で実装する高速に処理を実行するCUI
2. React等モダンな言語で作成したSPA[^2]と連携するWebAPI(両方開発するので言語を揃えたい)
3. Windows上で動作する.Net Frameworkで作成するGUI
4. Android/iOS上で動作するモバイルアプリ

といった用に、**Python以外の言語で書きたい!**といった想いはあるのではないでしょうか。

### 目指すはこんな構成
推論フェーズで使用する学習結果は、フレームワーク依存からの脱却を目指すためにも各フレームワークが出力する独自フォーマットからの変換作業が必要となります。[^3]


![推論フェーズでは非Python](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350454/328dd1c4-eb88-83c0-b880-07e303f309d2.png)



## 早速モデルを変換してみる

早速、TensorFlow2.0, Pytorch, Chainer色々な言語で学習された結果を、汎用的なフォーマットに変換していきます。
Deep Learning界隈の学習結果の重みとモデルを取り扱う汎用フォーマットとしては、下の2つが代表なものとして挙げられます。

1. **ONNX[^4]**
  - AWSやFacebook、Microsoft等が普及を進めるモデルの汎用シリアライゼーションフォーマット。
  - 最近はMathworks等も開発に参加し、Matlabでの利用も期待されています。
2. **SavedModel**
  - TensorFlowで利用される汎用フォーマット。
  - サーバ機能を提供するTensorFlow Servingや、モバイル向けのTensorFlow Liteのようなフレームワークもこのフォーマットに変換して利用されます。

今回は、ONNXを手厚くサポートするPytorchと、SavedModelを経由して多言語のフレームワークを展開するTensorFlowでVGG16を読み込んで、シリアライズしてみます。

---
### PyTorch→ ONNX

```python:pytorch_export_onnx.py
import torch
from torchvision import models

# サンプルモデルの用意
vgg = models.vgg16(pretrained=True)
input = torch.autograd.Variable(torch.randn(1, 3, 224, 224), requires_grad=True)
out = vgg(input)

# ONNX形式で出力
torch.onnx._export(vgg, input, "./vgg.onnx", export_params=True)
```


### TensorFlow/Keras → SavedModel

```python:tensorflow_export_savedmodel.py
import tensorflow as tf
from tensorflow.keras.applications.vgg16 import VGG16

# サンプルモデルの用意
vgg = VGG16(weights='imagenet', include_top=True)

# SavedModel形式で出力(事前に./vggディレクトリ作成しておく)
tf.saved_model.save(vgg, "./vgg")
```

SavedModelはファイル単体ではなく、指定されたディレクトリ下にファイルセットが出力されます。
使用時には、ディレクトリを指定します。

```
vgg ← 出力先に指定したディレクトリ
│  saved_model.pb  ← モデルの情報をProtocol Buffersで記述したもの
│
├─assets
└─variables  ← 変数情報。
        variables.data-00000-of-00002
        variables.data-00001-of-00002
        variables.index
```


## Pythonを使わず推論する

デプロイ先言語での実装もソースを載せ始めると長くなりすぎてしまうので、今回は入り口編ということで紹介に留めさせてもらいます。
先にも紹介しましたが、TensorFlowやMicrosoftがデプロイ先言語としてTensorFlow XX、ONNX Runtimeを公開してくれているので、様々な言語に対応できます。

|クラウド|モバイル/IoT|ブラウザ|デスクトップ|その他|
|:--:|:--:|:--:|:--:|:--:|
|TensorFlow Serving|TensorFlow Lite|TensorFlow.js|-|TensorFlow C++ API等|
|ONNX Runtime Server(β)| - |ONNX.js|ONNX RunTime C# API|ONNX RunTime等|


SavedModelはSwift for TensorFlowの開発も進んでいたり、ONNXに変換する様なツール[^5]も公開されています。
ONNXもJava APIの公開やMicrosoftならではの特徴として、.Net Frameworkを使ったデスクトップアプリ開発に対応しているなども魅力的です。

C++erにはPFNがリリースしているMenoh[^6]などもあります。


### おまけ：アプリなんていらないツワモノへ

TensorFlowにはSavedModelを読み込んでCLIで推論を実行させるsaved_model_cliが存在します。
入力を.npyのフォーマットで受け付けて、出力を標準出力(+α ファイル出力)してくれるアプリケーションです。
あまり使い勝手の良さを感じていませんが、サーバレスなどには向いてるかもしれません。

```bash:inference_cli.sh
# SavedModelの中身を表示
$saved_model_cli show --dir ./vgg --tag_set serve --signature_def serving_default
> The given SavedModel SignatureDef contains the following input(s):
>   inputs['input_1'] tensor_info:
>       dtype: DT_FLOAT
>       shape: (-1, 224, 224, 3)
>       name: serving_default_input_1:0
> The given SavedModel SignatureDef contains the following output(s):
>   outputs['predictions'] tensor_info:
>       dtype: DT_FLOAT
>       shape: (-1, 1000)
>       name: StatefulPartitionedCall:0
> Method name is: tensorflow/serving/predict
# 推論を実行して結果を保存
$ saved_model_cli run --dir ./vgg --tag_set serve --signature_def serving_default --inputs input_1=./sample.npy --outdir ./
> Output predictions is saved to ./predictions.npy
```

# 終わりに
学習が終わったDeep LearningモデルをPython以外で使うには大きく2ステップ。

1. 汎用シリアライゼーションフォーマットに変換する
2. それぞれの言語で書かれたフレームワークを探して読み込む

が必要です。というお話でした。


Pythonを推論環境で使用することがバッドノウハウというわけではなく、Reactで画面作って処理はWebAPIで行う事もよくあります。
苦手なPythonでなんとかWebAPIやGUIを作るよりは、慣れた言語やクラウドサービスを使ったほうがセキュアなものになるはずですよね。

この記事がきっかけで、得意な言語でDeep Learningできる！と閃いた方がどしどしアプリを作って公開する手助けになればなと思います。
**フレームワークと言語に縛られることなく**好きな言語を選択して、楽しくコードを書いていきましょう！

残り僅かとなりましたが、引き続き[NTTテクノクロス アドベントカレンダー 2019 24日目](https://qiita.com/advent-calendar/2019/ntt-tx)をお楽しみください。


[^1]: 普段は[高精細VR配信エンジン](https://www.ntt-tx.co.jp/products/panocho/?utm_source=qiita&utm_medium=ac2019&utm_campaign=23) 等、映像/画像処理エンジンの開発をしています。
[^2]: Single Page Applicationの略。画面遷移を伴わない単一Webページで構成されるアプリケーション。
[^3]: Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、クリエイティブ・コモンズ表示 3.0 ライセンスに記載された条件に従って使用しています
[^4]: Open Neural Network eXchangeの略でオニキスと呼びます。
[^5]: tf2onnx - https://github.com/onnx/tensorflow-onnx
[^6]: Menoh - https://github.com/pfnet-research/menoh