TensorFlowを使うだけ使う

# はじまり

某ボードを使いこなすためにTensorFlow Liteを使う必要が出てきた。
手始めにTensorFlowのサンプルをTensorFlow Liteで変換しようとしたところ、
がっつりエラーになった。知識不足何とかしたい。

注意。

`本記事はtfliteファイルを作るまでの備忘録なので、TensorFlow触った事のある人なら学びのある記事ではないです。`

# やる事

自分で作ったデータセットをtensorflowに食わせてtensorflow liteでコンバートする。
とりあえず作るモデルとしてはダック検出。手順をまとめるのが目的なので精度なんて二の次。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42445/1348c283-f35d-6431-ca0b-93d05b4e4c61.png)

<font color="dark_pink">こういう画像をダックって判断するモデルが作りたい</font>

# 準備

とりあえずpython3入れた後は必要なパッケージを入れてゆく

```bash
pip install tensorflow
pip install tensorflow_datasets
pip install matplotlib
pip install numpy
pip install h5py pyyaml
pip install pillow
```

# データセットを作ろうとして失敗する

[Adding a dataset](https://www.tensorflow.org/datasets/add_dataset)を参考にデータセットを作成する。

とりあえず大事なのは

 + tensorflow_datasets.public_api.core.GeneratorBaseBuilderを継承したクラス作る
 + _info()オーバーライドする
 + _split_generator()オーバーライドする
 + _generate_example()オーバーライドする

```python
import tensorflo_datasets.public_api as tfds

class DuckDataset(tfds.core.GeneratorBaseBuilder):

  version = tfds.core.Version('0.1.0')

  def _info(self):
    return tfds.core.DatasetInfo(
        builder=self,
        description="special greatfull duckable dataset",
        features=tfds.features.FeaturesDict({
          "image_description": tfds.features.Text(),
          "image": tfds.features.Image(),
          "label": tfds.features.ClassLabel(num_classes=5),
          }),
        supervised_keys=("image", "label"),
        urls="https://dataset-homepage.org",
        citation=r"""@article{greatfull-duckable-dataset, 
          author={miyatama},"}""",
        )

  def _split_generators(self, dl_manager):
    extract_path = "./datasets/"
    return [
        tfds.core.SplitGenerator(
          name=tfds.Split.TRAIN
          gen_kwargs={
            "image_dir_path": os.path.join(extract_path, "train"),
            "labels": os.path.join(extract_path, "train_labels.csv"),
            }
          ),
        tfds.core.SplitGenerator(
          name=tfds.Split.TEST
          gen_kwargs={
            "image_dir_path": os.path.join(extract_path, "test"),
            "labels": os.path.join(extract_path, "test_labels.csv"),
            }
          ),
        ]

  def _generate_examples(self, images_dir_path, labels):
    image_paths = sorted([os.path.join(images_dir_path, filename)
                          for filename in tf.io.gfile.listdir(images_dir_path)])

    if tf.io.gfile.exists(scenes_description_file):
      scenes_json = json.load(tf.io.gfile.GFile(scenes_description_file))
    else:
      # if annotation file does not exist, we create empty annotations
      scenes_json = {"scenes": [{"objects": []}] * len(image_paths)}

    attrs = ["image_name", "label"]
    for image_path, scene in zip(image_paths, scenes_json["scenes"]):
      objects = scene["objects"]
      fname = os.path.basename(image_path)
      record = {
          "image": image_path,
          "file_name": fname,
          "objects": [{attr: obj[attr] for attr in attrs} for obj in objects]  # pylint: disable=g-complex-comprehension
      }
      yield fname, record
```

<font color="dark_pink">何回Adding a datasetを読んでも結局gfileに食わせるものが不明だったので、何となくラベルを返すようにした。</font>

作ったクラスをどうやって使えばいいか悩んだのち、
`Follow this guide to add a dataset to TFDS.`なる文言をみて心が折れる。

その他参考

 + [datasets/flowers.py](https://github.com/tensorflow/datasets/blob/master/tensorflow_datasets/image/flowers.py)

# データセットを作成する

とりあえず画像の用意する。

 + datasets/duckディレクトリを作る
 + Googleの画像検索開く
 + `duck`と打ち込んで検索
 + 適当にスクロール
 + 右クリックしてWebページの完全保存
 + できたフォルダの`image(x)`ファイルを`image_x.jpg`に名前変える
 + 同じ感じでjojoディレクトリと画像作る

で、画像を読み込んで学習に引き渡すまでは下記の様になる。

```python
import tensorflow as tf
import numpy as np 
from PIL import Image 
import glob 

folder = ["datasets/duck", "datasets/jojo"] 
X = [] 
Y = [] 

def img(x): 
  y = Image.open(x) 
  y = y.convert("RGB") 
  y = y.resize((50, 50)) 
  return np.asarray(y)  

for index, name in enumerate(folder): 
  dir = "./" + name 
  files = glob.glob( dir + "/*.jpg") 
  for i, file in enumerate(files): 
    image = img(file) 
    X.append(image) 
    Y.append(index) 

X = np.array(X) / 255 
Y = np.array(Y) 
```


# 学習させる

ココはもうTensorFlowのサンプルのまま。最初のFlattenだけ違う感じ。

```python
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(50, 50, 3)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(2, activation=tf.nn.softmax)
])

model.compile(
  optimizer='adam',
  loss='sparse_categorical_crossentropy',
  metrics=['accuracy'])

model.fit(X, Y, epochs=5)
model.save(
  'duck_detect.h5', 
  include_optimizer=False)
```

# tensorflow liteでコンバートする

いよいよ学習結果のモデルをコンバートしてゆく。

```python
converter = tf.lite.TFLiteConverter.from_keras_model_file('duck_detect.h5')
tflite_model = converter.convert()
open('duck_detect.tflite', 'wb').write(tflite_model)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42445/e5892302-5b54-5c7d-ba63-201df3b67133.png)

<font color="dark_pink">無事tfliteファイルが出来た</font>


# ふりかえり

TensorFlowのページ分かりやすいようで分かり辛い。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Follow this guide to add a dataset to TFDS.<br>とのこと。終了。<a href="https://twitter.com/hashtag/%E5%A3%B0%E3%81%AB%E5%87%BA%E3%81%97%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%841%E8%A1%8C%E7%9B%AE?src=hash&amp;ref_src=twsrc%5Etfw">#声に出して読みたい1行目</a><a href="https://twitter.com/hashtag/TensorFlow?src=hash&amp;ref_src=twsrc%5Etfw">#TensorFlow</a></p>&mdash; みやた@代打中 (@miyata080825) <a href="https://twitter.com/miyata080825/status/1168538260680822785?ref_src=twsrc%5Etfw">September 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>