TensorFlow-Slimを逆にたどっていきながら理解する【評価編】

[学習](https://qiita.com/mine820/items/42dfe5ed7249e239406c)が終わったら、その結果で評価を行う必要があります。
Slimでは評価を行う関数が用意されているので、そこからたどっていきます。

# 前提情報
TensorFlow-Slimは「tensorflow/contrib/slim」にあります。
そこで、すでに

```python
import tensorflow as tf
import tensorflow.contrib.slim as slim
```
が行われているものとします。

# 評価
評価を行う関数は「slim.evaluation.evaluation_loop()」です。
※「slim.evaluation.evaluate_once()」というのもありますが、こちらは今回は省略

session.run()とか呼ばずに、この関数を呼ぶとすぐに学習を始めます。
学習データの読み込みも、この関数が呼ばれたときに実行されます。
（この関数内でsession.run()が呼ばれているようなイメージです）

こんな書き方をします。

```python
slim.evaluation.evaluation_loop(
    '',
    checkpoint_dir,
    log_dir,
    num_evals=num_batches,
    eval_op=list(names_to_updates.values()),
    summary_op=tf.summary.merge(summary_ops),
    eval_interval_secs=eval_interval_secs)
```

引数の意味を調べてみます。

| 変数名 | 意味 | 初期値 |
|---|---|---|
| master | TensorFlowマスターのBNSアドレス |  |
| checkpoint_dir | チェックポイントが格納されているディレクトリ |  |
| logdir | TensorFlowサマリーが書き込まれるディレクトリ |  |
| num_evals | 「eval_op」を実行する回数 | None |
| initial_op | 評価の開始時に実行される処理 | None |
| initial_op_feed_dict | 「initial_op」を実行する際に使用するフィード辞書 | None |
| init_fn | 「init_op」の後に実行されるオプションの呼び出し可能関数<br/>引数として１つだけ、初期化されたセッションが必要です | None |
| eval_op | 「num_evals」回実行される処理 | None |
| eval_op_feed_dict | 「eval_op」を実行する際に使用するフィード辞書 | None |
| final_op | 全ての「eval_op」の実行の後に実行する処理<br/>「final_op」の値が返されます | None |
| final_op_feed_dict | 「final_op」を実行する際に使用するフィード辞書 | None |
| summary_op | TensorFlow-Slimメトリック操作を実行した後に評価するサマリー処理<br/>デフォルトでは、tf.summary.merge_all()が設定されています | _USE_DEFAULT |
| summary_op_feed_dict | 「summary_op」を実行する際に使うオプションのフィード辞書 | None |
| variables_to_restore | 評価中にリストアするTensorFlow変数のリスト<br/>引数が 「None」のままであれば、slim.variables.GetVariablesToRestore()が使用されます | None |
| eval_interval_secs | 評価間の最小秒数 | 60 |
| max_number_of_evaluations | 評価の反復の最大回数<br/>値が「None」の場合、評価は無期限に続きます | None |
| session_config | 「Session」を設定するために使われる「 tf.ConfigProto」のインスタンス<br/>「None」の場合、デフォルトが使用されます | None |
| timeout | チェックポイント間で待機する最大時間<br/「None」の場合、プロセスは無期限に待機します | None |
| timeout_fn | タイムアウト後に呼び出すオプションの関数<br/>この関数がTrueを返すと、新しいチェックポイントが生成されず、イテレータが終了することを意味します<br/>関数は引数なしで呼び出されます | None |
| hooks | 繰り返し評価中に渡す追加の 「SessionRunHook」オブジェクトのリスト | None |

このうち、「master」「checkpoint_dir」「logdir」は必須になります。
※「master」は「''」がよい
なお、「eval_op」がないと意味がありませんので、これは設定します。さらに「summary_op」もあった方がよいので、これらをまとめて説明します。

## evel_op
評価を行う処理になります。
例えば平均二乗誤差で評価を行う場合は、以下の関数を使用します。

```python
mae_value_op, mae_update_op = slim.metrics.streaming_mean_absolute_error(predictions, labels)
```
この時の「mae_update_op」を渡します。

通常、評価は1つの指標だけでなく、いくつかの指標を見ます。
そこで、以下のようにまとめます。

```python
value_ops, update_ops = slim.metrics.aggregate_metrics(
    slim.metrics.streaming_mean_absolute_error(predictions, labels),
    slim.metrics.streaming_mean_squared_error(predictions, labels))
```
ラベルを付ける場合は、以下のようになります。

```python
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
    "eval/mean_absolute_error": slim.metrics.streaming_mean_absolute_error(predictions, labels),
    "eval/mean_squared_error": slim.metrics.streaming_mean_squared_error(predictions, labels),
})
```
※この場合は「names_to_updates.value()」を設定する

評価の処理は、「accuracy」や「precision」も求めることが出来ます。

```python
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
    'accuracy': slim.metrics.accuracy(predictions, labels),
    'precision': slim.metrics.precision(predictions, labels),
    'recall': slim.metrics.recall(mean_relative_errors, 0.3),
})
```

### 「predictions」と「labels」
比較評価を行いますので、同じ型である必要があります。
また、評価の種類によっては、型の指定があります。

【例】「accuracy」の場合は、bool／integer／stringのみとなります
【例】「precision」「recall」の場合は、強制的にboolにキャストされます

## summary_op
評価処理を行った結果を出力する処理になります。
前述の「value_ops」や「names_to_values」を利用します。
※ここでは「names_to_values」の方を使用します

```python
summary_ops = []
for metric_name, metric_value in names_to_values.items():
  op = tf.summary.scalar(metric_name, metric_value)
  op = tf.Print(op, [metric_value], metric_name)
  summary_ops.append(op)
```

評価処理の結果を順番に出力します。
まずprotocol buffer形式で、サマリーにラベル名と値を出力します。
次に、同様にラベル名と値を標準出力に表示します。

# おまけ
TensorFlow-Slimの[GitHub](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/slim)に、詳しい情報が載っていますが、残念ながらPython2ベースのため、ところどころPython3に変換してあげる必要があります。