[TensorFlow / C++ / CPU] Pythonで学習 & C++で推論 (2022 Ver.). II. 保存したモデルの変換

# Pythonでネットワークを学習し、C++で推論する （2022年版）
`TenosrFlow`を用いて、**PythonとGPUでネットワークのパラメータを学習し、C++とCPUで推論を行う**ために必要な作業を紹介します。
[前回](https://qiita.com/kyaFUK/items/635ef0d31e8e7eba9ab8)は、前準備としてネットワークの入出力層の名前を取得する方法を説明しました。

# 前提条件
`TensorFlow`を用いた学習は`Python`で実行します。
モデルのパラメータやネットワークを保存した`.ckpt`ファイルはすでに作成済みであるとします。

`Python`で学習したネットワークを用いて`C++`で推論を行います。
また、学習・推論で用いるアクセラレータ等の環境は`CPU only`または`CPU+GPU`を想定しています。

ネットワークの入出力層の名前はすでに分かっている（[詳しくはこちらをご参照ください](https://qiita.com/kyaFUK/items/635ef0d31e8e7eba9ab8)）。

# 環境
**学習**：tensorflow-gpu-2.4.1（Python）
**推論**：[tensorflow_cc](https://github.com/FloopCZ/tensorflow_cc)-2.4.1（C++）

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

このページでは<font color="Red">2.</font>を解説します。
# `.ckpt`ファイルを`.pb`ファイルに変換する。
`TensorFlow`においてモデルの保存には、`Python`では`Checkpoint`ファイル (.ckpt)、C++では`protobuf`ファイル (.pb)[^2] を用います。

以下のプログラムを用いて`Python`で学習したモデルを`freeze`(モデルを再training不可能にし、パラメータを固定する)し、`C++`で読込可能な形式に変換します。



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

output_node_names = ['stack']

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')

    # Set the device for CPU
    assing_ops = tf.Graph.get_operations(sess.graph)
    for op in assing_ops: 
        op._set_device('/device:CPU:*')
    
    # Freeze the graph
    frozen_graph_def = tf.graph_util.convert_variables_to_constants(
        sess,
        sess.graph_def,
        output_node_names
        )

    # Save the frozen graph
    with open('frozen_graph.pb', 'wb') as f:
        f.write(frozen_graph_def.SerializeToString())

```
### freezingにGPUを利用しないことを明示する
```Python
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # If you want to use only CPUs
```
### [前回](https://qiita.com/kyaFUK/items/635ef0d31e8e7eba9ab8)調べた出力層の名前を明示する。

```Python
output_node_names = ['stack']
```

### モデルのネットワークの情報と学習済みパラメータを読み込む
`.ckpt`ファイルには、ネットワークの構造と学習済みパラメータが保存されています。
`.ckpt`ファイルの構成の一例を以下に示します。

```
checkpoints
├─model.ckpt-XXXX.meta
├─model.ckpt-XXXX.data-00000-of-00001
└─model.ckpt-XXXX.index
```

`model.ckpt-XXXX.meta`には、ネットワークの構造が保存されており、`model.ckpt-XXXX.data-00000-of-00001`には、学習済みパラメータが保存されています [^1]。
以下のようにモデルの情報を読み込みます。

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

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

### 推論にはCPUのみ利用することを明示する。
`GPU`を用いて学習した場合、`GPU`で並列化するための情報が記録されています。
すべての計算に`CPU`を用いることを明示します。
この部分は、**学習** (GPU) -> **推論** (CPU)の場合にのみ必要です。

```Python
    # Set the device for CPU
    assing_ops = tf.Graph.get_operations(sess.graph)
    for op in assing_ops: 
        op._set_device('/device:CPU:*')
```

### ネットワーク・パラメータの情報を固定する。
[convert_variables_to_constants()](https://www.tensorflow.org/api_docs/python/tf/compat/v1/graph_util/convert_variables_to_constants)を使って、パラメータを定数に変換します。

```
    frozen_graph_def = tf.graph_util.convert_variables_to_constants(
        sess,
        sess.graph_def,
        output_node_names
        )
```

# 次の手順
[3. `.pb`ファイルとtensorflow_ccを用いて、`C++`で`TensorFlow`の推論を実行する。](https://qiita.com/kyaFUK/items/e4dd03301e601298504f)
（追記しました。）


[^1]: `model.ckpt-XXXX.data`が複数ある場合、各データの所在を管理しているのが`model.ckpt-XXXX.index`になります。

[^2]: `.pbファイル`は、「protobuf、Protocol Buffers」の略で、学習済みモデルをスマホなどで動作させる際などに利用されます。


# 参考にしたサイト

### TensorFlowの学習・推論をC++で行う。

https://qiita.com/yukiB/items/1ea109eceda59b26cd64

https://qiita.com/rxoxixyxd/items/d821a3bc3e7fb1eed47c

https://qiita.com/rxoxixyxd/items/f2b8a0ddf09b6734d313

### .ckptファイルと.pbファイルの変換

http://workpiles.com/2016/07/tensorflow-protobuf-dump/

https://masaeng.hatenablog.com/entry/2019/08/18/224031

### .ckptファイル自体の解説
https://note.com/mlai/n/n68fb660b506d