Tensorflowのobject detection APIのチュートリアルを試す（windows）

オブジェクト検出とやらをTensorflowでやってみたい　→　APIがある！試してみる
エラーに苦しむもなんとか動かせたのでその記録

# 環境
Windows10
Tensorflow-gpu 1.8.0
Gforce GTX 1080
Anaconda3.5.1.0（Python3.6）
※オフラインで使用
※Tensorflowの環境は構築済

# tensorflow/modelsを入手
- 以下のURLからzipファイルをダウンロード
https://github.com/tensorflow/models
- zipファイルを適当な場所で解凍（models-masterのフォルダできる）

# チュートリアルにチャレンジ
- 以下のURLを参考にしながらチュートリアルにチャレンジ（これだとGoogle Cloudで学習させてますが、今回はローカルで）
https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_pets.md


### データの入手
- まず以下のURLからチュートリアルに使うペットの画像とラベルを入手（URLに接続するとダウンロードされる）
http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz
http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz 

- ダウンロードできたら解凍して適当な場所に置く（下のようなフォルダの構造になりますとチュートリアルに書いているが、完全無視）

```
- images.tar.gz
- annotations.tar.gz
+ images/
+ annotations/
+ object_detection/
... other files and directories
```

### Tfrecord作成
- ダウンロードしたimageとannotationファイルからTfrecordをつくる
チュートリアルの説明通りにコマンドプロンプトで以下をたたくと

```
# From tensorflow/models/research/
python object_detection/dataset_tools/create_pet_tf_record.py \
    --label_map_path=object_detection/data/pet_label_map.pbtxt \
    --data_dir=`pwd` \
    --output_dir=`pwd`
```
```No module named object_detection```のエラー

- これは適当なところにmodels-masterのフォルダを置いていてパス通してないのが原因
- パスを通せばいいのでmodels-master/research/object_detection/dataset_toolsのフォルダにあるcreate_pet_tf_record.pyを開き以下のように変更（スクリプトの上のほうだけ抜粋）

```python
import sys # 追加
sys.path.append('C:/~~/models-master/research') # パス追加

import hashlib
import io
import logging
import os
import random
import re

import contextlib2
from lxml import etree
import numpy as np
import PIL.Image
import tensorflow as tf

from object_detection.dataset_tools import tf_record_creation_util
from object_detection.utils import dataset_util
from object_detection.utils import label_map_util

flags = tf.app.flags
                              # ↓ 追加
flags.DEFINE_string('data_dir', r'C:/~~/tensorflow', 'Root directory to raw pet dataset.') # imagesとannotationのフォルダがある場所を指定
                                 # ↓ 追加
flags.DEFINE_string('output_dir', r'C:/~~/tensorflow', 'Path to directory to output TFRecords.') # tfrecordを保存したい場所を指定
                                      # ↓ 絶対パスに変更 
flags.DEFINE_string('label_map_path', r'C:/~~/models-master/research/object_detection/data/pet_label_map.pbtxt',
                    'Path to label map proto')
# ↑各分類のidと名前の対応をpbtxtというファイルにしておくようだ
flags.DEFINE_boolean('faces_only', True, 'If True, generates bounding boxes '
                     'for pet faces.  Otherwise generates bounding boxes (as '
                     'well as segmentations for full pet bodies).  Note that '
                     'in the latter case, the resulting files are much larger.')
flags.DEFINE_string('mask_type', 'png', 'How to represent instance '
                    'segmentation masks. Options are "png" or "numerical".')
flags.DEFINE_integer('num_shards', 10, 'Number of TFRecord shards')

FLAGS = flags.FLAGS
```
- これで動かしてみると今度は```cannot import name 'string_int_label_map_pb2'```というエラー
- C:\~~\models-master\research\object_detection\protos を確認すると.protoファイルはあるがpyファイルがない
- Githubのissuesによるとprotoファイルをコンパイルする必要があるらしい（https://github.com/tensorflow/models/issues/1595）

### protoファイルをコンパイル
- 以下のページからprotoc-3.6.1-win.zipをダウンロード
https://github.com/protocolbuffers/protobuf/releases
- zipを解凍し、"C:\~~\protoc-3.6.1-win32\bin"を環境変数に追加
- コマンドプロンプトを立ち上げ、C:\~~\models-master\researchに移動
- ```protoc ./object_detection\protos\*.proto --python_out=.```を実行するもエラー
- 仕方ないので以下を実行（ファイル一個ずつ指定）

``` 
protoc --python_out=. .\object_detection\protos\anchor_generator.proto .\object_detection\protos\argmax_matcher.proto .\object_detection\protos\bipartite_matcher.proto .\object_detection\protos\box_coder.proto .\object_detection\protos\box_predictor.proto .\object_detection\protos\eval.proto .\object_detection\protos\faster_rcnn.proto .\object_detection\protos\faster_rcnn_box_coder.proto .\object_detection\protos\grid_anchor_generator.proto .\object_detection\protos\hyperparams.proto .\object_detection\protos\image_resizer.proto .\object_detection\protos\input_reader.proto .\object_detection\protos\losses.proto .\object_detection\protos\matcher.proto .\object_detection\protos\mean_stddev_box_coder.proto .\object_detection\protos\model.proto .\object_detection\protos\optimizer.proto .\object_detection\protos\pipeline.proto .\object_detection\protos\post_processing.proto .\object_detection\protos\preprocessor.proto .\object_detection\protos\region_similarity_calculator.proto .\object_detection\protos\square_box_coder.proto .\object_detection\protos\ssd.proto .\object_detection\protos\ssd_anchor_generator.proto .\object_detection\protos\string_int_label_map.proto .\object_detection\protos\train.proto .\object_detection\protos\keypoint_box_coder.proto .\object_detection\protos\multiscale_anchor_generator.proto .\object_detection\protos\graph_rewriter.proto
```

- C:\~~\models-master\research\object_detection\protos を確認するとpyファイルができてる！

### 今度こそTfrecord作成
- create_pet_tf_record.pyを実行するとtfrecordファイルが完成！
- trainファイルとvalファイル10個ずつできた（これで合っているのかは謎）

### 学習済モデルをダウンロード
- 以下のURLのfaster_rcnn_resnet101_cocoをダウンロード
https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md

### configファイル作成
- configファイルとやらを作らねばならないらしい（これでパラメータとか入力ファイルとか指定するようだ）
- C:\~~\models-master\research\object_detection\samples\configs にあるfaster_rcnn_resnet101_pets.configをspyderで開いて（開ければなんでも）変更（下のほうだけ抜粋）

```
train_config: {
  batch_size: 1
  optimizer {
    momentum_optimizer: {
      learning_rate: {
        manual_step_learning_rate {
          initial_learning_rate: 0.0003
          schedule {
            step: 900000
            learning_rate: .00003
          }
          schedule {
            step: 1200000
            learning_rate: .000003
          }
        }
      }
      momentum_optimizer_value: 0.9
    }
    use_moving_average: false
  }
  gradient_clipping_by_norm: 10.0
  fine_tune_checkpoint: "C:/~~/faster_rcnn_resnet101_coco_2018_01_28/model.ckpt"　← 絶対パスで指定
  from_detection_checkpoint: true
  load_all_detection_checkpoint_vars: true
  # Note: The below line limits the training process to 200K steps, which we
  # empirically found to be sufficient enough to train the pets dataset. This
  # effectively bypasses the learning rate schedule (the learning rate will
  # never decay). Remove the below line to train indefinitely.
  num_steps: 200000
  data_augmentation_options {
    random_horizontal_flip {
    }
  }
}

train_input_reader: {
  tf_record_input_reader {
    input_path: "C:/~~/pet_faces_train.record-00000-of-00010" ← 絶対パスに変更
  }
  label_map_path: "C:/~~/object_detection/data/pet_label_map.pbtxt" ← 絶対パスに変更
}

eval_config: {
  metrics_set: "coco_detection_metrics"
  num_examples: 1101
}

eval_input_reader: {
  tf_record_input_reader {
    input_path: "C:/~~/pet_faces_val.record-00000-of-00010" ← 絶対パスに変更
  }
  label_map_path: "C:/~~/object_detection/data/pet_label_map.pbtxt" ← 絶対パスに変更
  shuffle: false
  num_readers: 1
}
```
### pycocotoolsをインストール
- pypi（
https://pypi.org/project/pycocotools/
）からpycocotools-2.0.0.tar.gzを入手してコマンドプロンプトで```pip install pycocotools-2.0.0.tar.gz```を実行すると```Failed building wheel for pycocotools```というエラー
- Github（
https://github.com/cocodataset/cocoapi
）でzipをダウンロードし、展開（cocoapi-masterのフォルダできる）、コマンドプロンプトでcocoapi-master/PythonAPIに移動し、```python setup.py install```を行うと入った

### 学習をはじめ・・・たい
- C:/Users/user/Downloads/models-master/research/object_detection/train.py
を開いてパスをいれるところなんかを変更

```python
# No module named 'ほにゃらら'が出る場合パスを追加
import sys
sys.path.append(r'C:\~~\models-master\research\slim') # No module named 'deployment'と'No module named 'net'に対応
sys.path.append(r'C:\~~\models-master\research') # No module named 'object_detection'に対応

import functools
import json
import os
import tensorflow as tf

from object_detection import trainer
from object_detection.builders import dataset_builder
from object_detection.builders import graph_rewriter_builder
from object_detection.builders import model_builder
from object_detection.utils import config_util

tf.logging.set_verbosity(tf.logging.INFO)

flags = tf.app.flags
flags.DEFINE_string('master', '', 'Name of the TensorFlow master to use.')
flags.DEFINE_integer('task', 0, 'task id')
flags.DEFINE_integer('num_clones', 1, 'Number of clones to deploy per worker.')
flags.DEFINE_boolean('clone_on_cpu', False,
                     'Force clones to be deployed on CPU.  Note that even if '
                     'set to False (allowing ops to run on gpu), some ops may '
                     'still be run on the CPU if they have no GPU kernel.')
flags.DEFINE_integer('worker_replicas', 1, 'Number of worker+trainer '
                     'replicas.')
flags.DEFINE_integer('ps_tasks', 0,
                     'Number of parameter server tasks. If None, does not use '
                     'a parameter server.')
# 学習を行うディレクトリを指定（チェックポイントがここに保存される）
flags.DEFINE_string('train_dir', r'C:/~~/models-master/research/object_detection',
                    'Directory to save the checkpoints and training summaries.')
# 絶対パスで.configファイルを指定
flags.DEFINE_string('pipeline_config_path', r'C:/~~/models-master/research/object_detection/samples/configs/faster_rcnn_resnet101_pets.config',
                    'Path to a pipeline_pb2.TrainEvalPipelineConfig config '
                    'file. If provided, other configs are ignored')
```


- この状態でtrain.pyを動かしてみると今度は```Tried to convert 't' to a tensor and failed. Error: Argument must be a dense tensor: range(0, 3) - got shape [3], but wanted [].```という謎のエラー
    - ぐぐってみるとGithubのissue3752で発見
https://github.com/tensorflow/models/issues/3752
    - python3との互換性の問題のよう
https://github.com/tensorflow/models/issues/3752#issuecomment-376452211
    - 以下のページにリンクされていたので、書いてある通りに修正してみる
https://github.com/tensorflow/models/issues/3705#issuecomment-375563179

- models-master/research/object_detection/utils/learning_schedules.pyを開く
- 167-169行目の部分（以下）を

```python
  rate_index = tf.reduce_max(tf.where(tf.greater_equal(global_step, boundaries),
                                      range(num_boundaries),
                                      [0] * num_boundaries))
```
下のように修正（rangeの部分をlist()で囲っただけ）

```python
  rate_index = tf.reduce_max(tf.where(tf.greater_equal(global_step, boundaries),
                                      list(range(num_boundaries)),
                                      [0] * num_boundaries))
```

### 今度こそ学習開始
- train.pyを動かすと学習が開始！


※これを投稿する直前にGithubでtensorflow/modelsを確認したところ、models-master/research/object_detection/train.pyのファイルがなくなっているーー！
※ちょっと古いファイルにはtrain.pyあり

# まとめ
とりあえず学習を動かすところまではできた！
次は自分のデータでやってみたい