[TensorFlow / C++ / CPU] Pythonで学習 & C++で推論 (2022 Ver.). I. Layer Nameの取得

# Pythonでネットワークを学習し、C++で推論する （2022年版）
`TensorFlow`を用いてDeep Learningを行う場合、`Python`を使うのが一般的になってきました。`Google Colab`なども普及して手軽に`GPU`も使えるようになってきています。

一方で、`Python`や`GPU`を用いた場合、学習は高速化できても、**推論はボトルネックとなる**ことがしばしばあります。

そこで、**PythonとGPUでネットワークのパラメータを学習し、C++とCPUで推論を行う**のが高速化への第一歩だと思います。

ここでは、ネットに落ちている色々な情報を統合し、2022年現在、再現性・汎用性の高い手法に絞って紹介したいと思います。



# 前提条件
`TensorFlow`を用いて`Python`で学習したモデルはすでに用意できているとします。
また、モデルのパラメータやネットワークを保存した`.ckpt`ファイルはすでに作成済みであるとします。
このモデルを用いて`C++`で推論を実行します。
学習・推論で用いるアクセラレータ等の環境は`CPU only`または`CPU+GPU`を想定しています。

# 環境
Python側：tensorflow-gpu-2.4.1
C++側：[tensorflow_cc](https://github.com/FloopCZ/tensorflow_cc)-2.4.1

# C++でTensorFlowの推論を実行する手順
<font color="Red">1. **学習済みモデルの入力層・出力層の名前を取得する。**</font>
[2. `.ckpt`ファイルを`.pb`ファイルに変換する。](https://qiita.com/kyaFUK/items/a4cb330b77bc3a41ac3c)
[3. `.pb`ファイルとtensorflow_ccを用いて、`C++`で`TensorFlow`の推論を実行する。](https://qiita.com/kyaFUK/items/e4dd03301e601298504f)

このページでは<font color="Red">1.</font>を解説します。

# 学習済みモデルの入力層・出力層の名前を取得する。
あとの工程で必要になるので、まずは学習済みモデルの入力層・出力層の名前を調べます。
以下のプログラムを使用します。

```Python:FindLayers.py
import tensorflow.compat.v1 as tf
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # If you want to use only CPUs

with tf.Session() as sess:
    # Restore the graph
    saver = tf.train.import_meta_graph("./checkpoints/model.ckpt-XXXX.meta")

    # Load weights
    saver.restore(sess, './checkpoints/model.ckpt-XXXX')

    # Show Layers' Names
    ########################################
    print("Start")
    graph = sess.graph

    with graph.as_default():

        for op in graph.get_operations():
            print(op.type, op.name, op.outputs)  # type, name, shape
    print("Finish")
    ########################################
```

### ネットワークの構造と学習済みパラメータの読み込み
ネットワークの構造は、`model.ckpt-XXXX.meta`に保存されています。
また、学習済みパラメータの情報は`model.ckpt-XXXX`に保存されています。
`XXXX`に、は学習回数などの数値が記されています。

```Python
    # Restore the graph
    saver = tf.train.import_meta_graph("./checkpoints/model.ckpt-XXXX.meta")

    # Load weights
    saver.restore(sess, './checkpoints/model.ckpt-XXXX')
```

### 各Layerの情報の出力
[get_operations()](https://www.tensorflow.org/api_docs/python/tf/Graph#get_operations)を使って、各Layerの`type` （型）, `name` （名前）, `shape` （次元）を取得します。

```Python
    # Show Layers' Names
    ########################################
    print("Start")
    graph = sess.graph

    with graph.as_default():

        for op in graph.get_operations():
            print(op.type, op.name, op.outputs)  # type, name, shape
    print("Finish")
    ########################################
```


出力結果が以下になります。

```:output
Start
Placeholder Placeholder [<tf.Tensor 'Placeholder:0' shape=(4, 20, 16, 16, 16, 8) dtype=float32>]
Placeholder Placeholder_1 [<tf.Tensor 'Placeholder_1:0' shape=(4, 18, 16, 16, 16, 8) dtype=float32>]
Placeholder Placeholder_2 [<tf.Tensor 'Placeholder_2:0' shape=() dtype=float32>]
Const XXXXXXX/strided_slice/stack [<tf.Tensor 'XXXXXXX/strided_slice/stack:0' shape=(2,) dtype=int32>]
Const XXXXXXX/strided_slice/stack_1 [<tf.Tensor 'XXXXXXX/strided_slice/stack_1:0' shape=(2,) dtype=int32>]
Const XXXXXXX/strided_slice/stack_2 [<tf.Tensor 'XXXXXXX/strided_slice/stack_2:0' shape=(2,) dtype=int32>]
StridedSlice XXXXXXX/strided_slice [<tf.Tensor 'XXXXXXX/strided_slice:0' shape=(4, 16, 16, 16, 8) dtype=float32>]
Const XXXXXXX/zeros/shape_as_tensor [<tf.Tensor 'XXXXXXX/zeros/shape_as_tensor:0' shape=(5,) dtype=int32>]
Const XXXXXXX/zeros/Const [<tf.Tensor 'XXXXXXX/zeros/Const:0' shape=() dtype=float32>]
Fill XXXXXXX/zeros [<tf.Tensor 'XXXXXXX/zeros:0' shape=(4, 16, 16, 16, 32) dtype=float32>]
Const XXXXXXX/zeros_1/shape_as_tensor [<tf.Tensor 'XXXXXXX/zeros_1/shape_as_tensor:0' shape=(5,) dtype=int32>]

(中略)

Finish
```
出力結果の`dtype`や`shape`の値をヒントにして、入力層・出力層に相当するLayerに見当をつけます。
outputは数千~数万行に及ぶ場合もあるので、`python FindLayer.py |& tee layer.log`などと実行して、ログを保存してから名前を調べると良いかもしれません。

**Layerの名前を調べるのは非常に根気のいる作業です。**

# 次の手順
[**2. .ckptファイルを.pbファイルに変換する。**](https://qiita.com/kyaFUK/items/a4cb330b77bc3a41ac3c)
(追記しました。)

# 参考にしたサイト
### Pythonで学習したネットワークをC++で実行する
https://memo.saitodev.com/home/tensorflow/use_graph_in_cxx/

### ニューラルネットワークの情報のロード
https://qiita.com/cfiken/items/bcdd7eb945c5c3b2bb5f

https://www.tensorflow.org/api_docs/python/tf/Graph#get_operations