【備忘録】KerasのImageData Generator

# はじめに

KerasのImageData Generatorの理解を深めたい。

Iteratorの動きに関しては、下記のポストをご参照ください。

https://qiita.com/kotai2003/items/c10294edcc31c7542281

# やりたいこと

学習の時、model.fit()を使わず、for文でやりたい。そちらのほうがいろいろ処理の自由度が増すので。

# Image Data Generatorの挙動の基礎

```python
# 01.Buildin
import os, time, math, random, pickle

# 02.2nd source
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Base path
base_path = 'G:\\マイドライブ\\datasets\\mvtec_anomaly_detection\\bottle\\test'

# Generator
train_datagen = ImageDataGenerator(
    featurewise_center=False,  # データセット全体で，入力の平均を0にします
    samplewise_center=False,  # 各サンプルの平均を0にします
    featurewise_std_normalization=False,  # 入力をデータセットの標準偏差で正規化します
    samplewise_std_normalization=False,  # 各入力をその標準偏差で正規化します
    zca_whitening=False,  # ZCA白色化を適用します
    zca_epsilon=1e-06,  # ZCA白色化のイプシロン．デフォルトは1e-6
    rotation_range=0.0,  # 画像をランダムに回転する回転範囲
    width_shift_range=0.0,  # 浮動小数点数（横幅に対する割合）．ランダムに水平シフトする範囲
    height_shift_range=0.0,  # 浮動小数点数（縦幅に対する割合）．ランダムに垂直シフトする範囲．
    brightness_range=None,  #
    shear_range=0.0,  # 浮動小数点数．シアー強度（反時計回りのシアー角度）
    zoom_range=0.0,
    # 浮動小数点数または[lower，upper]．ランダムにズームする範囲．浮動小数点数が与えられた場合，[lower, upper] = [1-zoom_range, 1+zoom_range]です．
    channel_shift_range=0.0,  # 浮動小数点数．ランダムにチャンネルをシフトする範囲．
    fill_mode='nearest',  #: {"constant", "nearest", "reflect", "wrap"}
    cval=0.0,  # 浮動小数点数または整数．fill_mode = "constant"のときに境界周辺で利用される値．
    horizontal_flip=False,  # 水平方向に入力をランダムに反転します．
    vertical_flip=False,  # 垂直方向に入力をランダムに反転します．
    rescale=1. / 255,  # 画素値のリスケーリング係数．デフォルトはNone．Noneか0ならば，適用しない．それ以外であれば，(他の変換を行う前に) 与えられた値をデータに積算する．
    preprocessing_function=None,
    # 各入力に適用される関数です．この関数は他の変更が行われる前に実行されます．この関数は3次元のNumpyテンソルを引数にとり，同じshapeのテンソルを出力するように定義する必要があります．
    data_format=None,  # {"channels_first", "channels_last"}のどちらか
    validation_split=0.0  # 浮動小数点数．検証のために予約しておく画像の割合
)

# Directoryか画像を読み込む

train_generator = train_datagen.flow_from_directory(
    directory=base_path,#ディレクトリへのパス．クラスごとに1つのサブディレクトリを含み，サブディレクトリはPNGかJPGかBMPかPPMかTIF形式の画像を含まなければいけません．
    target_size=(256, 256),#整数のタプル(height, width)．
    color_mode='rgb',#"grayscale"か"rbg"の一方．
    classes=None,#クラスサブディレクトリのリスト．（例えば，['dogs', 'cats']）
    class_mode='categorical',# "categorical"か"binary"か"sparse"か"input"か"None""categorical"は2次元のone-hotにエンコード化されたラベル，"binary"は1次元の2値ラベル，"sparse"は1次元の整数ラベル，"input"は入力画像と同じ画像になります（主にオートエンコーダで用いられます）．Noneであれば，ラベルを返しません（ジェネレーターは画像のバッチのみ生成するため，model.predict_generator()やmodel.evaluate_generator()などを使う際に有用）．class_modeがNoneの場合，正常に動作させるためにはdirectoryのサブディレクトリにデータが存在する必要があることに注意してください．
    batch_size=32,#データのバッチのサイズ
    shuffle=True,#データをシャッフルするかどうか
    seed=None,#シャッフルや変換のためのオプションの乱数シード
    save_to_dir=None,# Noneまたは文字列（デフォルト: None）．生成された拡張画像を保存するディレクトリを指定できます
    save_prefix='',#文字列．画像を保存する際にファイル名に付けるプリフィックス
    save_format='png',#"png"または"jpeg"
    follow_links=False,#
    subset=None,#
    interpolation='nearest'#

)


#Check
for data_batch, labels_batch in train_generator:

    print('Data Batch shape:', data_batch.shape)
    print('Labels Batch shape:', labels_batch.shape)

```

無限ループで、いつになってもデータを渡してくれる。

```python:結果

Found 83 images belonging to 4 classes.
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (19, 256, 256, 3)
Labels Batch shape: (19, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (19, 256, 256, 3)
Labels Batch shape: (19, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
Data Batch shape: (19, 256, 256, 3)
Labels Batch shape: (19, 4)
Data Batch shape: (32, 256, 256, 3)
Labels Batch shape: (32, 4)
...

```

# Image Data Generatorを利用し、データを渡すプログラム
動きはわかったので、10 epochsだけ回るプログラムを作成します。

```python
# 01.Buildin
import os, time, math, random, pickle

# 02.2nd source
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Variables
batch_size = 16
epochs_threshold = 10

# Base path
base_path = 'G:\\マイドライブ\\datasets\\mvtec_anomaly_detection\\bottle\\test'



# Generator
train_datagen = ImageDataGenerator(

    rotation_range=1,  # 画像をランダムに回転する回転範囲
    width_shift_range=0.1,  # 浮動小数点数（横幅に対する割合）．ランダムに水平シフトする範囲
    height_shift_range=0.1,  # 浮動小数点数（縦幅に対する割合）．ランダムに垂直シフトする範囲．
    rescale=1. / 255,  # 画素値のリスケーリング係数．デフォルトはNone．Noneか0ならば，適用しない．それ以外であれば，(他の変換を行う前に) 与えられた値をデータに積算する．

)

# Directoryか画像を読み込む


train_generator = train_datagen.flow_from_directory(
    directory=base_path,#ディレクトリへのパス．クラスごとに1つのサブディレクトリを含み，サブディレクトリはPNGかJPGかBMPかPPMかTIF形式の画像を含まなければいけません．
    target_size=(256, 256),#整数のタプル(height, width)．
    color_mode='rgb',#"grayscale"か"rbg"の一方．
    classes=None,#クラスサブディレクトリのリスト．（例えば，['dogs', 'cats']）
    class_mode='categorical',
    # "categorical"か"binary"か"sparse"か"input"か"None",
    # "categorical"は2次元のone-hotにエンコード化されたラベル，
    # "binary"は1次元の2値ラベル，
    # "sparse"は1次元の整数ラベル，
    # "input"は入力画像と同じ画像になります（主にオートエンコーダで用いられます）．
    # Noneであれば，ラベルを返しません（ジェネレーターは画像のバッチのみ生成するため，model.predict_generator()やmodel.evaluate_generator()などを使う際に有用）．class_modeがNoneの場合，正常に動作させるためにはdirectoryのサブディレクトリにデータが存在する必要があることに注意してください．
    batch_size=batch_size,#データのバッチのサイズ
    shuffle=False,#データをシャッフルするかどうか

)

#全体のデータ

count_files = 0

for current_dir, sub_dirs, files_list in os.walk(base_path):
    print(current_dir)
    # print(sub_dirs)
    print(files_list)

    count_files = count_files + len(files_list)

print(count_files)

#Calculate the batch number per each epoch

steps_per_epochs = math.ceil(count_files/batch_size)
print('steps_per_epochs:', steps_per_epochs)

now_epoch = 0

for i, (data_batch, labels_batch) in enumerate(train_generator):

    #epochsのカウント
    if i % steps_per_epochs == 0:
        now_epoch = now_epoch + 1
        print('epochs:',now_epoch)

    # 判断
    if now_epoch > epochs_threshold:
        break

    print('Data Batch shape:', data_batch.shape)
    print('Labels Batch shape:', labels_batch.shape)


```

```python:結果

epochs: 10
Data Batch shape: (16, 256, 256, 3)
Labels Batch shape: (16, 4)
Data Batch shape: (16, 256, 256, 3)
Labels Batch shape: (16, 4)
Data Batch shape: (16, 256, 256, 3)
Labels Batch shape: (16, 4)
Data Batch shape: (16, 256, 256, 3)
Labels Batch shape: (16, 4)
Data Batch shape: (16, 256, 256, 3)
Labels Batch shape: (16, 4)
Data Batch shape: (3, 256, 256, 3)
Labels Batch shape: (3, 4)
epochs: 11

プロセスは終了コード 0 で終了しました

```

# 注意点

generatorのclass_modeの引数は、様々は使い方ができることを忘れないようにしましょう。

```python 
 class_mode='categorical',
    # "categorical"か"binary"か"sparse"か"input"か"None",
    # "categorical"は2次元のone-hotにエンコード化されたラベル，
    # "binary"は1次元の2値ラベル，
    # "sparse"は1次元の整数ラベル，
    # "input"は入力画像と同じ画像になります（主にオートエンコーダで用いられます）．
    # Noneであれば，ラベルを返しません（ジェネレーターは画像のバッチのみ生成するため，model.predict_generator()やmodel.evaluate_generator()などを使う際に有用）．class_modeがNoneの場合，正常に動作させるためにはdirectoryのサブディレクトリにデータが存在する必要があることに注意してください．
```