TensorFlowで簡単に画像分類


#はじめに

久しぶりにTensorFlow(Keras)を触ったら忘れていることが多かったので、簡単にメモとして残しておきます。

##学習の流れ

TensorFlow(以下TF)では、
① データセットの準備
② モデルの準備
③ `model.compile` でoptimizer, loss, metricsの指定
④ callbacksの指定
⑤ `model.fit`で学習開始
⑥ `model.evaluate` でテストの評価
⑦ 画像の予想確率だけ得るときは `model.predict`

のような流れです。
これは比較的簡単な書き方です。もう少し細かいところまで記述したい場合は、以前に記事を書きましたので参考にしてみてください。

[TensorFlow2で機械学習をはじめたいと思ってる方へ](https://qiita.com/tatsuya11bbs/items/7d7a2c920730ae0c592a)




##① データセットの準備

ディレクトリに画像を保存しておいてそこから読み込むには、`flow_from_directory()`を用います。
その前に`ImageDataGenerator`を使用し、augmentationの中身を記述します。


```.py
train_datagen = ImageDataGenerator(
            rescale=1./255,
            zoom_range=0.2,
            horizontal_flip=True,
            rotation_range=20,
            width_shift_range=0.2,
            height_shift_range=0.2,
            )

val_datagen = ImageDataGenerator(rescale=1./255)


train_generator = train_datagen.flow_from_directory(
            TRAIN_DIR,
            target_size=(img_size, img_size),
            batch_size=batch_size,
            classes=classes,
            class_mode='categorical')


val_generator = val_datagen.flow_from_directory(
            VAL_DIR,
            target_size=(img_size,img_size),
            batch_size=batch_size,
            classes=classes,
            class_mode='categorical')
```

*クラスラベルについては、`train_generator.class_indices`のようにすると取得可能。クラスの対応表が出力できます。基本的にはアルファベット順。


以下のように使用できる augmentationは多いので各自タスクに合わせて決めてください。
詳しく知りたい人は、[公式ドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/ImageDataGenerator)で確認してください。

```.py
tf.keras.preprocessing.image.ImageDataGenerator(
    featurewise_center=False, samplewise_center=False,
    featurewise_std_normalization=False, samplewise_std_normalization=False,
    zca_whitening=False, zca_epsilon=1e-06, rotation_range=0, width_shift_range=0.0,
    height_shift_range=0.0, brightness_range=None, shear_range=0.0, zoom_range=0.0,
    channel_shift_range=0.0, fill_mode='nearest', cval=0.0,
    horizontal_flip=False, vertical_flip=False, rescale=None,
    preprocessing_function=None, data_format=None, validation_split=0.0, dtype=None
)

```


`flow_from_directory`では、ディレクトリのパスを指定して、オーギュメントされた画像がバッチサイズごとに生成されます。

```.py

flow_from_directory(
    directory, target_size=(256, 256), color_mode='rgb', classes=None,
    class_mode='categorical', batch_size=32, shuffle=True, seed=None,
    save_to_dir=None, save_prefix='', save_format='png',
    follow_links=False, subset=None, interpolation='nearest'
)
```

`class_mode` : categorical, binary, sparse, input, None が選択可能。デフォは,categorical. 
`shuffle` : Flaseならアルファベット順
`save_to_dir` : None or str(default:None). ディレクトリを指定することによって augmented data を保存してくれる。可視化に役に立つ。
`save_prefix` : str. 保存された画像のファイル名に使う。(save_to_dirが設定されている場合有効)
`save_format` : png or jpeg (Default:'png')
`interpolation` : nearest(デフォルト), bilinear, bicubic


##② モデルの準備

今回は例として、MobileNetv2を使います。`tf.keras.applications`に他の学習済みモデルもあります。

```.py
IMG_SHAPE = (img_size, img_size, channels)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                            include_top=False,
                                            weights='imagenet')

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(n_classes, activation='softmax')
model = tf.keras.Sequential([
            base_model,
            global_average_layer,
            prediction_layer
            ])
```

##③ model.compile で optimizer, loss, metricsの指定

modelに対して学習の詳細を設定します。


```.py
model.compile(optimizer=optimizers.SGD(lr=0.0001, momentum=0.99, decay=0, nesterov=True),
            loss='categorical_crossentropy',
            metrics=['accuracy'])
```



```.py
compile(
    optimizer='rmsprop', loss=None, metrics=None, loss_weights=None,
    weighted_metrics=None, run_eagerly=None, steps_per_execution=None, **kwargs
)
```

`optimizer` : string or optimizer instance (default: rmsprop)
画像系のタスクだと個人的には、`SGD`かな？

ex) SGD, RMSprop, Adam, Adadelta,...

```.py
sgd = tf.keras.optimizers.SGD(
    learning_rate=0.01, momentum=0.0, nesterov=False, name='SGD', **kwargs
)
```
*この`sgd`を model.compileのoptimizerに設定する

`loss` : string, object function or `tf.keras.losses.Loss` instance
`metrics` : trainingとtestで使用するmetricsのリスト
`loss_weights` : ロスに重み付けする





##④ callbacksの指定

CSVLogger, History, ProgbarLogger, TensorBoard, EarlyStopping, ReduceLROnPlateau　など設定できます。
`model.fit`で学習する際に渡すことができます。詳しくは[公式ドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks)をご確認ください。

例をいくつか挙げておきます。

```.py
tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', min_delta=0, patience=0, verbose=0,
    mode='auto', baseline=None, restore_best_weights=False
)
```

```.py
tf.keras.callbacks.ModelCheckpoint(
    filepath, monitor='val_loss', verbose=0, save_best_only=False,
    save_weights_only=False, mode='auto', save_freq='epoch',
    options=None, **kwargs
)
```

`filepath` : string or PathLike, モデルを保存するパス。指定できる変数は、epoch, loss, acc, val_loss, val_accです。
ex) filepath = '{val_loss:.2f}-{val_acc:.2f}.hdf5'
`monitor` : 何を基準にモデルを保存するか。accuracy, val_accuracy, loss, val_loss
*もし metrics name が分からなかったら、 `history = model.fit()` で `history.history`を確認。
`mode` : {auto, min, max}
`save_best_only` : もし、filepathが{epoch}のようなフォーマットを含んでいなかったら、filepathはオーバーライドされる
`save_weights_only` : modelの重みのみ保存
`save_freq` : epoch, or integer.


```.py
tf.keras.callbacks.LearningRateScheduler(
    schedule, verbose=0
)
```

```.py
tf.keras.callbacks.ReduceLROnPlateau(
    monitor='val_loss', factor=0.1, patience=10, verbose=0,
    mode='auto', min_delta=0.0001, cooldown=0, min_lr=0, **kwargs
)
```



```.py
tf.keras.callbacks.RemoteMonitor(
    root='http://localhost:9000', path='/publish/epoch/end/',
    field='data', headers=None, send_as_json=False
)
```

```.py
tf.keras.callbacks.CSVLogger(
    filename, separator=',', append=False
)
```


##⑤ model.fitで学習開始


例

```.py
history = model.fit(
        train_generator,
        steps_per_epoch=steps_per_epoch,
        validation_data=val_generator,
        validation_steps=validation_steps,
        epochs=CONFIG.epochs,
        shuffle=True,
        callbacks=[cp_callback])
```


```.py
fit(
    x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None,
    validation_split=0.0, validation_data=None, shuffle=True, class_weight=None,
    sample_weight=None, initial_epoch=0, steps_per_epoch=None,
    validation_steps=None, validation_batch_size=None, validation_freq=1,
    max_queue_size=10, workers=1, use_multiprocessing=False
)
```
`validation_split` : [0~1]. training dataの一部をa validation data とする。x, yの後ろのデータが用いられる(シャッフルする前)。
`validation_data` : validation data. validation_splitがある場合はオーバーライドされる
`class_weight` : データが少ないクラスのlossに重きを置く方法。辞書型で渡す。例) {0:0.66, 1:1.33}
`steps_per_epoch` : Integer or None. training data // batch_size で求められる。
`validation_steps` : Integer or None. validation data // batch_size で求められる。







##⑥ model.evaluate でテストの評価

```.py

test_loss, test_acc = model.evaluate(test_generator, steps=test_steps)
```

```.py
evaluate(
    x=None, y=None, batch_size=None, verbose=1, sample_weight=None, steps=None,
    callbacks=None, max_queue_size=10, workers=1, use_multiprocessing=False,
    return_dict=False
)
```

`x` : 入力データ。 Numpy array, tensor, tf.data.dataset, (inputs, targets) or (inputs, targets, sample_weights)
`y` : ターゲットデータ。
`batch_size` : Integer or None. 
`verbose` :  0 or 1. Verbosity mode. 0 = silent, 1 = progress bar.
`sample_weight` : 
`steps` : Integer or None
`callbacks` : list of `keras.callbacks.Callback` instances
`max_queue_size` : Integer.
`workers` : Integer. 
`use_multiprocessing` : boolean
`return_dict` : もしTrueなら、metric results をdictで返す



##⑦ 画像の予想確率だけ得るときは model.predict

```.py
predict(
    x, batch_size=None, verbose=0, steps=None, callbacks=None, max_queue_size=10,
    workers=1, use_multiprocessing=False
)
```

##+α

####<<モデルを保存した際に作成されるファイルについて>>

**checkpoint** : 1ファイルのみ作成。どのファイルが最新か確認可能。学習の際に必要なのでテストの際はなくても大丈夫。保存したデータから再度学習を始める場合に必要。

**XXXXX.data-0000-of-00001** : 変数名をテンソル値としてマッピングした独自のフォーマット。


**XXXXX.index** : このファイルはバイナリファイル。複数のステップでデータを保存した際に同名の「.data-0000-of-00001」ファイルが、どのステップのどのデータであるか一意に定まる。

#終わりに
細かい設定まで今回目を通すことができて良い機会になりました。

#参考文献

- [TensorFlow](https://www.tensorflow.org/api_docs/python/tf)