PyTorch, ONNX, Caffe, OpenVINO (NCHW) のモデルをTensorflow / TensorflowLite (NHWC) へお手軽に変換する

<a href="https://github.com/PINTO0309/openvino2tensorflow"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/PINTO0309/openvino2tensorflow.png" width="460px"></a><a href="https://github.com/PINTO0309/PINTO_model_zoo"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/PINTO0309/PINTO_model_zoo.png" width="460px"></a>

**日本語**　**[English](https://qiita.com/PINTO/items/ed06e03eb5c007c2e102)**

# 1. はじめに
いつも左中間を狙うようなプチニッチなふざけた記事ばかりを量産しています。 この記事の手順を実施すると、 最終的に PyTorch製 高精度Semantic Segmentation の **`U^2-Net`** を TensorFlow Lite へ変換することができます。 下図のような感じです。
![ezgif.com-gif-maker (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/a6fd7ebb-1724-c091-cd33-036704dd6d40.gif)

TensorFlow めちゃくちゃ扱いにくいです。 日々公開される最新のとても面白いモデルは軒並みPyTorch実装ですし、なんでTensorFlowで実装してくれないんだ！！ と、常日頃思っています。 論文のベンチマークの結果から精度やパフォーマンスが出ることがあらかじめ分かっているモデルにも関わらず、いちいちTensorFlowで再実装してトレーニングし直すのはかなり面倒くさいですし不毛です。 また、みなさんが 「TensorFlowは扱いにくい」 と感じてみえるポイントと私が扱いにくいと感じているポイントはズレているかもしれませんが、概ね下記のようなことがネックになっているのではないか、と考えます。

1. 他のフレームワークが軒並み **`NCHW形式`** なのに対し、TensorFlowだけ **`NHWC形式`**
2. **`NHWC形式`** がデフォルトのせいで他のフレームワークへの転用をしにくい
3. 同じ処理・同じ名前・異なるインタフェースのオペレーションがあちこちに散在していて超カオス
4. 最新のおもしろ実装が比較的少ない (少なく感じる)
5. 構文が難しい

でも、良いところもちゃんとあると思います。私が考える良いところは下記です。

1. デバイス向けにゴリゴリにモデルチューニングして爆速体験ができる
2. TensorFlow.js, TensorRT, TF-TRT, TensorFlow Lite, MediaPipe など、実行環境への最適化の適応幅が広い
3. ちゃんと最適化すればモデルの構造がとても美しい

はい、 3. は完全に私の趣味の話ですね。 この記事では TensorFlow のメリットを活かしつつ、デメリットを打ち消すためのノウハウを主に共有したいと思います。 トレーニングは好きなフレームワークで実施し、推論はゴリゴリにチューニングした状態で実行する。 そんなことを目指していきます。

なお、前回書いたモデル変換に関する泥臭いノウハウ集その１はコチラ **[\[Tensorflow Lite\] Various Neural Network Model quantization methods for Tensorflow Lite (Weight Quantization, Integer Quantization, Full Integer Quantization, Float16 Quantization, EdgeTPU). As of May 05, 2020.](https://qiita.com/PINTO/items/008c54536fca690e0572)** の記事をご覧ください。 タイトルは英語ですが中身は日本語です。 また、必要ないとは思いますが英語版も用意してあります。　**[\[English ver.\] \[Tensorflow Lite\] Various Neural Network Model quantization methods for Tensorflow Lite (Weight Quantization, Integer Quantization, Full Integer Quantization, Float16 Quantization, EdgeTPU). As of May 05, 2020.]()**

# 2. モデル変換用の様々なツール
フレームワーク間でモデルを変換し、面白いモデルを好きなフレームワーク上で実行できると嬉しいですよね。 現在世に出回っているモデル変換用のツールは下記のようなものがあります。 ONNX最狂説。 今回は、ONNXはPyTorchのモデルをフリーズするための一時的な中継フレームワークとして使用するのみです。 ちなみに、私が作成した雑な変換ツール (**[openvino2tensorflow](https://github.com/PINTO0309/openvino2tensorflow.git)**) と下記の主要ツール群との大きな違いは、 **`NCHW形式`** を **`NHWC形式`** にストレートに変換しつつ **`量子化`** まで実施できるところです。

- **`onnx-tensorflow`** ONNXをTensorFlowへ変換するツール
    - GitHub **https://github.com/onnx/onnx-tensorflow**
    - PyPI **https://pypi.org/project/onnx-tf/**
- **`tensorflow-onnx`** TensorflowをONNXへ変換するツール
    - GitHub **https://github.com/onnx/tensorflow-onnx**
    - PyPI **https://pypi.org/project/tf2onnx/**
- **`onnx2keras`** ONNXをKerasへ変換するツール
    - GitHub **https://github.com/nerox8664/onnx2keras**
    - PyPI **https://pypi.org/project/onnx2keras/**
- **`tflite2onnx`** TensorFlowLiteをONNXへ変換するツール
    - HomePage **https://jackwish.net/tflite2onnx/**
    - GitHub **https://github.com/jackwish/tflite2onnx**
    - PyPI **https://pypi.org/project/tflite2onnx/**
- **`sklearn-onnx`** sklearnをONNXへ変換するツール
    - GitHub **https://github.com/onnx/sklearn-onnx**
- **`onnx-mlir`** ONNXをMLIRへ変換するツール
    - GitHub **https://github.com/onnx/onnx-mlir**
- **`keras-onnx`** KerasをONNXへ変換するツール
    - GitHub **https://github.com/onnx/keras-onnx**
- **`onnx-tensorrt`** ONNXをTensorRTへ変換するツール
    - GitHub **https://github.com/onnx/onnx-tensorrt**
- **`onnx-coreml`** ONNXをCoreMLへ変換するツール
    - GitHub **https://github.com/onnx/onnx-coreml**
- **`onnx-simplifier`** ONNXモデル構造の最適化を行うツール
    - GitHub **https://github.com/daquexian/onnx-simplifier**
    - PyPI **https://pypi.org/project/onnx-simplifier/**
- **`OpenVINO Deep Learning Deployment Toolkit (DLDT) - Model Optimizer`** TensorFlow,ONNX,MXNet,CaffeをOpenVINO IR形式への変換やその他の各種便利ツールキット
    - HomePage **https://docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_linux.html**
    - GitHub **https://github.com/openvinotoolkit/openvino**
- **`coremltools`** 各種フレームワークからCoreMLモデルへ変換するツール
    - GitHub **https://github.com/apple/coremltools**
    - PyPI **https://pypi.org/project/coremltools/**
- **`MMdnn`** 各種フレームワーク間でモデルを変換するツール
    - GitHub **https://github.com/microsoft/MMdnn**
    - PyPI **https://pypi.org/project/mmdnn/**
- **`torch2tflite`** PyTorch -> ONNX -> TF2/TFLite
    - GitHub **https://github.com/omerferhatt/torch2tflite**
- **`openvino2tensorflow`** OpenVINO IRモデルをTensorFlowへ変換するツール onnx-tensorflowを使用すると大量のゴミTransposeが外挿されるため、キレイにNCHWのモデルをNHWCへ変換するためのツールとして作成
    - GitHub **https://github.com/PINTO0309/openvino2tensorflow**
    - PyPI **https://pypi.org/project/openvino2tensorflow/**
- **`tflite2tensorflow`** tfliteをTensorFlowへ逆変換するツール 100種類以上のOPに対応
    - GitHub **https://github.com/PINTO0309/tflite2tensorflow**
    - PyPI **https://pypi.org/project/tflite2tensorflow/**
- **`PINTO_model_zoo`** PyTorch(ONNX),Caffe,TensorFlow,TensorflowLite,CoreML,TF-TRT,TFJSのモデルコレクション、モデル変換用スクリプトを大量にコミットしてあります
    - GitHub **https://github.com/PINTO0309/PINTO_model_zoo**

# 3. NCHW形式のモデルからNHWC形式のモデルへ変換するツラさ
実際にやってみると分かると思うのですが、前節で挙げた私が作成したツール以外のツール群は、**`NCHW形式`** を **`NHWC形式`** へうまく変換することができません。 できたとしてもゴミのようなTransposeレイヤーが大量に埋め込まれることが多いです。 **`NHWC形式`** から **`NCHW形式`** の方向への変換は概ね対応されています。 NCHW形式をNHWC形式へ綺麗に変換しようとした場合、モデルに記録された重み情報をNumpy配列として抽出し、全てに対してTranspose処理で転置を施す必要がありとても手間が多いです。 私も実際に手作業で何個もモデルをコンバージョンしましたが、とにかくミスが重なります。 巨大なモデル あるいは 構造が複雑なモデル(例えば分岐が異様に多いモデル) を手作業で変換するのは誰でもできる作業ですが、人間ですのでどうしてもミスが発生します。 とにかくツラい。 異常にツラい。

「やったー！ ２日かけてようやく変換出来たー！」
　↓
「。。。。なんや、コレ。。。ワイ、ゴミ錬成してもうた。。。」

の繰り返しが関の山です。 心が病みます。 **`PyTorch製 EfficientDet-D0`** の手作業による **`TensorFlow Lite`** 変換にチャレンジして成功しましたが、モデルの特性上あまりにも離合が多くて吐きそうでした。

NCHW to NHWC変換に関して、実は YoloV4 の論文著者の **[Alexeyさん](https://github.com/AlexeyAB)** と私の間で直接議論を交わしていた経緯があります。 今の所、３７回もキャッチボールをしている長命なissueですが、どういう経緯があったか興味がある方は一度覗かれてみると面白いかもしれません。
**[GitHub issue: Is there an easy way to convert ONNX or PB from (NCHW) to (NHWC)?](https://github.com/PINTO0309/PINTO_model_zoo/issues/15)**
[![Screenshot 2020-11-12 09:18:11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/670cc06e-b9f8-9c35-7628-532253053710.png)](https://github.com/PINTO0309/PINTO_model_zoo/issues/15)

# 4. あえてOpenVINOを経由して変換する理由
この記事では **`PyTorch`** -> **`ONNX`** -> **`OpenVINO`** -> **`TensorFlow / Tensorflow Lite`** の流れでモデルを最適化しながらNCHW to NHWC変換を行います。 ONNXやその他のNCHW形式のフォーマットからTensorFlowのNHWC形式のフォーマットへは一気に変換しません。 端的に言うと、OpenVINO の Model Optimizer がかなり優秀なのであえてワンクッションだけOpenVINOへの変換をワークフローに組み込むということを行います。

OpenVINOへの変換をワンクッション経由することに関して私なりに考えたメリットは下記の５点です。

1. OpenVINO の Model Optimizer が変換過程のモデルを勝手に最適化してくれる
2. OpenVINO自体が推論専用フレームワークとして推論とフレームワーク間コンバージョンの役目に特化しているため、共通フォーマットとしては洗練されている
3. PyTorch(ONNX), TensorFlow, Caffe, MXNet などの豊富なフレームワークの変換に対応している
4. 全てのオペレーションの情報とオペレーション間の接続情報が人間が読解可能な簡単なXMLファイルに出力されるため、学習後のモデルの構造をエディタを使用して後から簡単に書き換えることができる
5. OpenCVに取り込まれている

5.に関してはメリットとは言いにくいですが、まぁ、そんなところです。

ちなみに、ロシア語の記事ですが **`ニューロンをコーヒーメーカーに詰め込む方法`** という内容の記事のコメント欄で **`openvino2tensorflow`** の有用性を Alexeyさん が解説して紹介してくれています。
**[Как запихать нейронку в кофеварку - ニューロンをコーヒーメーカーに詰め込む方法](https://habr.com/ru/company/recognitor/blog/524980/)**

# 5. モデル構造の美しさ
ディープラーニングモデルを生成するうえで重要な要素は、
　1. サイズ
　2. 精度
　3. 構造の美しさ
です。 ウソです。 ごめんなさい。 美しさを判断要素にあげているのはたぶん私だけだと思います。 私はモデルをコレクションしていますので、作業を続けているうちにいつの間にか **`美しさ`** を追究要素として加えていることに気づきました。 では、 **`美しさ`** とはどのようなことを指すか。 下図を見ていただくとなんとなく分かって頂けるかもしれません。 見た目だけ、の話ですが、TensorFlow Liteの形式へ変換すると、アクティベーション関数やBatchNormarizationが Convolution にマージされて元のONNXモデルの３分の２ほどのサイズにキレイにまとまっています。

ONNXからTensorFlow Liteへの変換テストに使用させて頂いたモデルは、**`Digital- Standard Co., Ltd.`** さんの3D骨格検出モデル **`ThreeDPoseUnityBarracuda`** のONNXモデルです。 趣味や研究で利用する場合はフリーとして公開いただいていますが、商用利用する場合は **[制約事項](https://github.com/digital-standard/ThreeDPoseUnityBarracuda#commercial-use)** がありますので、LICENSE条文をよく読んでご利用ください。
**[https://digital-standard.com/threedpose/models/Resnet34_3inputs_448x448_20200609.onnx](https://github.com/digital-standard/ThreeDPoseUnityBarracuda#download-and-put-files)**  

|ONNX|OpenVINO|TFLite|
|:--:|:--:|:--:|
|![Resnet34_3inputs_448x448_20200609 onnx_](https://user-images.githubusercontent.com/33194443/96398683-62683680-1207-11eb-928d-e4cb6c8cc188.png)|![Resnet34_3inputs_448x448_20200609 xml](https://user-images.githubusercontent.com/33194443/96153010-23f12400-0f48-11eb-8186-4bbad73b517a.png)|![model_float32 tflite](https://user-images.githubusercontent.com/33194443/96153019-26ec1480-0f48-11eb-96be-0c405ee2cbf7.png)|

# 6. 変換手順
**`PyTorch`** -> **`ONNX`** -> **`OpenVINO`** -> **`TensorFlow / Tensorflow Lite`** の流れでモデルを変換します。 OSS上でいただいたアドバイスなどをもとに独自のワークフローとして組み入れた手順をご紹介します。
## 6-1. TensorFlow と OpenVINO のインストール
TensorFlow と OpenVINO をインストールします。 TensorFlow は **`v2.3.1`** 以降、 OpenVINO は **`2021.1`** 以降を導入してください。  

```console:TensorFlowのインストール
$ sudo pip3 install tensorflow==2.3.1 --upgrade
```

OpenVINO はOSによって導入の手順が異なります。 下記に記載の手順に従ってインストールしてください。 なお、この手順でサポートする OpenVINO のバージョンは **`2021.1`** 以降のものです。 

- **Linux - 2021.1+**
**https://docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_linux.html**

- **Windows - 2021.1+**
**https://docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_windows.html**


## 6-2. openvino2tensorflow のインストール
OpenVINO IR のモデルを TensorFlow の saved_model や .pbファイル、.tfliteファイル、.h5ファイル へ自動変換することができる私の独自ツール **`openvino2tensorflow`** をインストールします。 下記のコマンドを実行するだけで最新のパッケージをインストールすることができます。 ほぼ毎日のようにバグフィックスと対応レイヤーの増加を行っていますので、作業開始時には毎回実行いただくことをおすすめします。

```console:openvino2tensorflowのインストール
$ sudo pip3 install openvino2tensorflow --upgrade
```
【参考】GitHub: **https://github.com/PINTO0309/openvino2tensorflow**

## 6-3. PyTorch のインストール
下記のホームページから、ご利用の環境に適したバージョンのインストーラを選択してください。 私の場合は下記の通りの指定を行いました。 注意点はご利用中の CUDA のバージョンを適切に選択すること、ぐらいです。

**https://pytorch.org/**
![Screenshot 2020-11-13 00:25:38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/b80760d9-3cdf-cd99-145c-0789990afb37.png)

```console:PyTorchのインストール
$ sudo pip3 install torch==1.7.0+cu101 \
  torchvision==0.8.1+cu101 torchaudio==0.7.0 \
  -f https://download.pytorch.org/whl/torch_stable.html
```

## 6-4. ONNX Runtime のインストール

```console:onnxruntimeのインストール
$ sudo pip3 install onnxruntime --upgrade
```

【参考】GitHub **https://github.com/microsoft/onnxruntime**

## 6-5. ONNX Simplifier のインストール
ONNXのモデルを出力したことがある方は感じたことがあるかもしれませんが、ONNXのモデル構造はかなり冗長です。 例えば下図の構造が、
![simple_reshape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/ffb3da03-a900-8303-2b65-6cde8502b519.png)
ONNX変換するとこうなります。 うげげげ。。。 美しくない。。。
![complicated_reshape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/319554ae-54b9-e375-192e-9d3d50d4e531.png)
ココで導入する **`ONNX Simplifier`** にONNXモデルを投入すると、モデル全体の重みのサイズの最適化と構造の最適化を同時に実施してくれます。下図のとおりです。 最適化具合が一目瞭然ですね。
![comparison.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/ecad07b3-20a5-a2b3-3baf-2d320f442d5f.png)

```console:ONNX_Simplifierのインストール
$ sudo pip3 install onnx-simplifier --upgrade
```
【参考】GitHub: **https://github.com/daquexian/onnx-simplifier**

## 6-6. PyTorch -> ONNX 変換
では、ようやく本題のモデル変換の実作業に入っていきます。 この記事ではPyTorchをONNX変換する際の特殊手順をご紹介します。 この手順では、比較的シンプルで高精度なモデル **`U^2-Net (ユースクエアネット)`** を例に変換手順を残したいと思います。 なお TorchScript については触れませんので、気になる方はコチラの記事 **[TorchScriptを使用してPyTorchのモデルを保存する - Qiita - hirune924さん](https://qiita.com/hirune924/items/8f6908e29a4e05048207)** がとても参考になります。
### 6-6-1. 変換手順に使用するサンプルリポジトリのClone
高精度Semantic Segmentationのモデル **`U^2-Net`** のPyTorch実装のリポジトリをCloneします。 U^2-NetによるSemantic Segmentationの実行イメージは下図のとおりです。 驚くほど精細でカッコイイですね。
![u2netqual.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/12359c62-f784-a5e4-107a-4549525cce9b.png)

```console:U^2-NetのClone
$ git clone https://github.com/NathanUA/U-2-Net.git
$ cd U-2-Net
```

### 6-6-2. OpenVINO のmodel_downloaderのバックエンドモジュール pytorch_to_onnx.py を使用してonnxを生成
OpenVINO の付属ツール **`model_downloader`** は各種モデルをダウンロードすると同時に OpenVINO IR へ自動変換してくれるモジュールをバックエンドでコールしてONNXへ変換してくれます。 どのようなものかを調べていくと分かるのですが、単なるPythonスクリプトで提供されていることが分かります。 この特殊手順は **`model_downloader`** がコールしている **`pytorch_to_onnx.py`** を使用してストレートにPyTorchのモデルをONNXへ変換してしまいます。 メリットは超特殊なPyTorchモデルを除き、ほとんどの場合PyTorchプログラムに変更を加えなくてもコマンド一発でお手軽に.pthをONNXに変換できることです。
![Screenshot 2020-11-12 22:16:18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/6f86db42-4cd2-b765-df2b-226449d9242d.png)
では、実際にサンプルのPyTorchモデル **`U^2-Net`** を変換してみます。 モデル変換用スクリプト **`pytorch_to_onnx.py`** のパラメータは下記のとおりです。

|No.|Parameter|意味|
|:--:|:--|:--|
|1|import-module|モデル構造が記録された.pyファイル名の.pyを除いた部分を指定します。 フォルダ階層の何段か下にモデルファイルがある場合は **`フォルダ名1.フォルダ名2.u2net`** というように、**`"."`** 区切りでフォルダ名を指定します。 例：カレント階層の u2net.py の場合は**`u2net`**と指定|
|2|model-name|No.1 の.pyファイルに記載されているモデルのCLASS名を指定します。 例：**`class U2NETP(nn.Module):`** の **`U2NETP`** の部分|
|3|input-shape|モデルへの入力解像度を 空白無しのカンマ区切りのNCHW形式 で指定します。|
|4|weights|PyTorchの重みが記録された.pthファイルまでの 相対パス あるいは 絶対パス を指定します。|
|5|output-file|エクスポート後のONNXファイル名を指定します。|
|6|input-names|モデルの入力変数名を指定します。大抵の場合、 No.1 の forward関数に指定されている引数名で問題ありません。複数の変数を指定する場合は空白無しのカンマ区切りで指定します。 例：**`"x,y,z"`**|
|7|output-names|モデルの出力変数名を指定します。大抵の場合、 No.1 の forward関数のreturn文に指定されている変数名で問題ありません。複数の変数を指定する場合は空白無しのカンマ区切りで指定します。 例：**`"out1,out2,out3,out4"`**|
Cloneしたリポジトリフォルダの直下で下記のようにコマンドを実行します。

```console:pytorch_to_onnx.pyによる.pthファイルのONNX変換
$ python3 ${INTEL_OPENVINO_DIR}/deployment_tools/tools/model_downloader/pytorch_to_onnx.py \
  --import-module model.u2net \
  --model-name U2NETP \
  --input-shape 1,3,320,320 \
  --weights saved_models/u2netp/u2netp.pth \
  --output-file u2netp_320x320.onnx \
  --input-names "x" \
  --output-names "F.sigmoid(d0),F.sigmoid(d1),F.sigmoid(d2),F.sigmoid(d3),F.sigmoid(d4),F.sigmoid(d5),F.sigmoid(d6)"
```
下図のように **`ONNX check passed successfully.`** と表示されれば成功です。
![Screenshot 2020-11-12 23:45:15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/90227c08-5939-e087-a265-d6e980cf3a1f.png)
**`u2netp_320x320.onnx`** がちゃんと生成されていますね。
![Screenshot 2020-11-12 23:46:27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/c166fe21-4531-b53e-ff05-ad15fe487a5b.png)

## 6-7. ONNXモデルの最適化
先ほど生成したONNXファイルをパラメータに指定して下記のコマンドを実行するだけです。

```console:ONNXモデルの最適化実行
$ python3 -m onnxsim u2netp_320x320.onnx u2netp_320x320_opt.onnx
```
めちゃくちゃ最適化されました。 複雑なモデルであればあるほど効果が高くなります。

|最適化前 u2netp_320x320.onnx|最適化後 u2netp_320x320_opt.onnx|
|:--:|:--:|
|![u2netp_320x320.onnx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/fdb92c97-d9d3-7442-66d2-60790c3de5f5.png)|![u2netp_320x320_opt.onnx (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/5dffb834-d574-03b9-3425-77073982db6a.png)|
【参考】GitHub **https://github.com/daquexian/onnx-simplifier**

## 6-8. ONNX -> OpenVINO IR 変換
では、先ほど最適化して生成した **`u2netp_320x320_opt.onnx`** を入力として、OpenVINO のコンバーターを使用して IR形式へ変換します。 下記のコマンドを実行します。 Caffeのモデルを変換する場合はココから先の手順を実施するだけでよいです。

```console:OpenVINO_IR形式への変換
$ python3 ${INTEL_OPENVINO_DIR}/deployment_tools/model_optimizer/mo.py \
  --input_model u2netp_320x320_opt.onnx \
  --input_shape [1,3,320,320] \
  --output_dir openvino/320x320/FP32 \
  --data_type FP32
```
**`.bin`** **`.mapping`** **`.xml`** の３種類のファイルが生成されました。
![Screenshot 2020-11-13 21:52:37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/1f10617b-2466-5a69-e0bf-0910aa727103.png)
**`model_optimizer`** へ指定可能なパラメータは下記をご覧ください。

【参考】 **https://docs.openvinotoolkit.org/latest/openvino_docs_MO_DG_prepare_model_convert_model_Convert_Model_From_ONNX.html**

【参考】 **https://docs.openvinotoolkit.org/latest/openvino_docs_MO_DG_prepare_model_convert_model_Converting_Model_General.html**

## 6-9. OpenVINO IR -> TensorFlow / TensorFlow Lite 変換
いよいよ最後の手順です。 **`openvino2tensorflow`** という私お手製の雑ツールを使用して、 NCHW形式のIRモデルからNHWC形式の TensorFlow / TensorFlow Lite のsaved_modelや.pb、.h5、.tflite を生成します。 下記のコマンドを実行します。

|No.|Parameter|意味|
|:--:|:--|:--|
|1|model_path|IRモデルのxmlファイルまでの相対パスあるいは絶対パスを指定します。 xmlファイルとbinファイルは同じフォルダに存在している必要があります。|
|2|model_output_path|NHWC形式へ変換したあとのモデルファイルの出力先パスを相対パスあるいは絶対パスで指定します。|
|3|output_saved_model|True 又は False、saved_model形式で出力する場合は **`True`** を指定します。|
|4|output_h5|True 又は False、.h5 形式で出力する場合は **`True`** を指定します。|
|5|output_weight_and_json|True 又は False、重みとJSONを出力する場合は **`True`** を指定します。|
|6|output_pb|True 又は False、.pb 形式で出力する場合は **`True`** を指定します。|
|7|output_no_quant_float32_tflite|True 又は False、.tfliteのFloat32精度で出力する場合は **`True`** を指定します。|
|8|output_weight_quant_tflite|True 又は False、.tfliteの重みを量子化して出力する場合は **`True`** を指定します。|
|9|output_float16_quant_tflite|True 又は False、tfliteの重みをFloat16量子化して出力する場合は **`True`** を指定します。|
|10|replace_swish_and_hardswish|True 又は False、活性化関数のSwishとHard-Swishを入れ替える場合は **`True`** を指定します。 EfficientDetのパフォーマンス検証用です。|
|11|debug|デバッグモードを有効にします。デバッグプリントで変換途中の特定のレイヤーの構成情報を出力します。|
|12|debug_layer_number|デバッグプリントで形状を確認したいレイヤーの番号を指定します。 **`--debug`** が指定されているときのみ有効です。|

```console:OpenVINO_IR形式(NCHW)からTensorFlow形式(NHWC)のモデルを生成
$ openvino2tensorflow \
  --model_path openvino/320x320/FP32/u2netp_320x320.xml \
  --model_output_path saved_model_320x320 \
  --output_saved_model \
  --output_h5 \
  --output_pb \
  --output_no_quant_float32_tflite \
  --output_weight_quant_tflite \
  --output_float16_quant_tflite
```
PyTorch (NCHW) -> ONNX (NCHW) -> OpenVINO (NCHW) -> TensorFlow Lite (NHWC) の変換が終わりました。 どうでしょうか、簡単でしたでしょうか？ このツールで saved_model 形式で出力しておくと、 TFJS や TF-TRT や CoreML への変換がOSSのツール一発でできるようになります。 そのあたりの追加の手順はコチラ **[GitHub - PINTO0309/PINTO_model_zoo](https://github.com/PINTO0309/PINTO_model_zoo)** にサンプルスクリプトを大量にコミットしてありますので、気になる方はご参考までにどうぞ。 2020.11.14時点では、 71種類のモデルを変換してコミットしてあります。

|TFLiteのモデル構造　model_float32.tflite|
|:--:|
|![u2netp_320x320_float32.tflite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194769/aba09bda-8725-0ffd-c01e-cb65c4158ef6.png)|


【参考】 **https://github.com/PINTO0309/openvino2tensorflow**

# 7. おわりに
私は別に TensorFlow の信者ではありません。 にも関わらずここまでして何故 TensorFlow のモデルへの変換にこだわっているかというと、 TensorFlow はサポートされている実行環境が豊富だから、という点に尽きます。 PyTorch は面白いモデルが豊富なこともありワクワクが凄いので嫌いではないのですが、 ONNX へエクスポートができないトリッキーなモデルがはびこっていて今の所はあまり好きにはなれていません。 というか、TorchScriptを使えばいいじゃん、というご指摘があると思いますが、フレームワーク自身の標準機能で用意されたモデル出力機能にも関わらずエラーが頻発するのが理解不能で耐えられません。（今の所は、です。） より洗練されていけば使いやすくなってとっつきやすくなるのかな、と考えています。


# 8. 参考記事
1. **https://github.com/digital-standard/ThreeDPoseUnityBarracuda**
2. **[TorchScriptを使用してPyTorchのモデルを保存する - Qiita - hirune924さん](https://qiita.com/hirune924/items/8f6908e29a4e05048207)**
3. **[モデル構造の可視化ツール Netron](https://netron.app/)**
4. **https://github.com/PINTO0309/PINTO_model_zoo**