【Python】Keras Tips

Kerasを学んでいく上で、備忘録として使い方を色々まとめました。
忘れそうなことがあったら、追記していきます。

<!-- ====================================================== -->
# 保存／読み込み
<!-- ====================================================== -->
[公式ドキュメント](https://keras.io/ja/getting-started/faq/#_4)

###モデル全体
この保存方法では、モデルの構造、重み、学習時の設定（loss, optimizer）、optimizerの状態をすべて保存します。
コンパイルをおこなったモデルを保存した場合、モデルをロードしたときに同時にコンパイルも行われます。

```.py
from keras.models import load_model

# 保存
model.save('model.h5')
del model  # 既存モデルの削除
# 読み込み
model = load_model('model.h5') # コンパイルも行う
```
拡張子は`.h5`または`.hdf5`です。

###アーキテクチャのみ
モデル構造のみ（重みや学習時の設定を含まない）の保存は、`JSON`または`YAML`形式として保存します。

```.py
from keras.models import model_from_json

# 保存
json_string = model.to_json()
with open('model.json', 'w') as f:
    f.write(json_string)

# 読み込み
with open('model.json') as f:
    json_string  = f.read()
model = model_from_json(json_string)
```
YMAL形式も同様。

###重みのみ
重みのみ（バイアスを含む）の場合も、`HDF5`形式として保存します。

```.py
# 保存
model.save_weights('model_weights.h5')
# 読み込み
model.load_weights('model_weights.h5')
```

また、レイヤーの名前を指定して、そのレイヤーだけ重みを読み込むこともできます。

```.py
model.load_weights('model_weights.h5', by_name=True)
```

###カスタムレイヤ
こちらについてはドキュメントを参照してください。

<!-- ====================================================== -->
# 可視化
<!-- ====================================================== -->

###モデル
[公式ドキュメント](https://keras.io/ja/visualization/)

保存して可視化する（Colabではこっちを使っています）

```.py
import IPython
from keras.utils import plot_model
# 保存
save_file = plot_model(model, to_file='model.png', show_shapes=True, expand_nested=True)
# 表示
IPython.display.Image(save_file)
```

保存しないで可視化する

```.py
from IPython.display import SVG
from keras.utils import model_to_dot

SVG(model_to_dot(model).create(prog='dot', format='svg'))
```

###学習履歴

```.py
import matplotlib.pyplot as plt

def show_history(history, mode='accuracy', save=False):
  epochs = len(history.history[f'{mode}'])
  plt.plot(range(1, epochs+1), history.history[f'{mode}'], "-")
  plt.plot(range(1, epochs+1), history.history[f'val_{mode}'], "-")
  plt.xlim(left=0)
  plt.title(f'epoch - {mode}')
  plt.ylabel(f'{mode}')
  plt.xlabel('epoch')
  plt.legend(['train', 'test'], loc='best')
  plt.grid(True)
  plt.show()

  if save:
    plt.savefig(f'epoch-{mode}.png')

# 損失
show_history(history, mode='loss')
# 精度
show_history(history, mode='accuracy')
```
***注意***
精度を指定するときは、`acc`ではなく`accuracy`で指定する。

<!-- ====================================================== -->
# コールバック（CallBack）
<!-- ====================================================== -->
[公式ドキュメント](https://keras.io/ja/callbacks/)

コールバックは、学習を行う`fit`関数にリストとして指定します。

```.py
history = model.fit(X_train, y_train,
                    batch_size=128, epochs=20,
                    callbacks = [earlyStopping, modelCheckpoint],
                    validation_split=0.2,
                    verbose=1)
```

###EarlyStopping
学習が一定以上進まなくなったときに、学習を終了するための設定です。
例えば、学習データ（検証用ではない）の`損失`が`5エポック間`で改善がないときに、学習を終了する設定は以下のようになります。

```.py
from keras.callbacks import EarlyStopping

earlyStopping = EarlyStopping(monitor='loss', patience=5)
```

###ModelCheckpoint
学習過程のモデル全体を保存しておくための設定です。
パラメーターである`save_weights_only`を指定することで（既定値はFalse）、重みのみの保存ができます。
例えば、`検証用データの精度`が`最高値を更新`したときに、モデルの`重み`を保存する設定は以下のようになります。

```.py
from keras.callbacks import ModelCheckpoint

filepath = 'weight-{epoch:02d}-{loss:.2f}-{accuracy:.2f}-{val_loss:.2f}-{val_accuracy:.2f}.hdf5'

modelCheckpoint = ModelCheckpoint(filepath , monitor='val_accuracy',
                                  save_best_only=True, save_weights_only=True)
```

<!-- ====================================================== -->
# データのかさ増し（Deta Augmentation）
<!-- ====================================================== -->
[公式ドキュメント](https://keras.io/ja/preprocessing/image/)

Deta Augmentationは、学習用のデータが少ないときにデータをかさ増しさせる技術です。
学習データが画像の場合は、回転や反転、移動をさせたデータを生成できます。これらの生成ルールは、`ImageDataGenerator`のパラメーターとして指定します。詳しくは公式ドキュメントを参照してください。

```.py
from keras.preprocessing.image import ImageDataGenerator

# 学習データの読み込み（省略）
X_train, y_train
# モデルの構築（省略）
model

# -----------------------
# Deta Augmentation
datagen = ImageDataGenerator(
    featurewise_center=False,
    samplewise_center=False,
    featurewise_std_normalization=False,
    samplewise_std_normalization=False,
    zca_whitening=False,
    rotation_range=0,
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True,
    vertical_flip=False
)

datagen.fit(X_train)
# -----------------------

# 学習データを、学習用と検証用に分ける
indices = np.arange(X_train.shape[0])
np.random.shuffle(indices)
validation_size = int(X_train.shape[0] * 0.2)
X_train, X_valid = X_train[indices[:-validation_size], :], X_train[indices[-validation_size:], :]
y_train, y_valid = y_train[indices[:-validation_size], :], y_train[indices[-validation_size:], :]

# 学習
history = model.fit(X_train, y_train,
                    batch_size=128, epochs=20,
                    validation_data=(X_valid, y_valid),
                    verbose=1)
```

***注意***
学習を行う際の`fit`には、`validation_split`というパラメーターがあります。
これは、学習データ（X_train, y_train）から検証データを分けるために使用しますが、学習データの最後のサンプルを検証データとして分離します。
そのため、上記の例では、かさ増しした学習データをシャッフルして、それを学習用と検証用に分けて`validation_data`として指定しています。

※ validation_splitの使用した例

```.py
history = model.fit(X_train, y_train,
                    batch_size=128, epochs=20,
                    validation_split=0.2,
                    verbose=1)
```

<!-- ====================================================== -->
# 転移学習／ファインチューニング
<!-- ====================================================== -->
[公式ドキュメント](https://keras.io/ja/applications/)

転移学習やファインチューニングは、既存の学習済みモデルを利用することで、限られたリソース（GPUスペック不足、充分ではない学習データ、モデル構築の手間など）でもより良い学習を行うための手法です。

転移学習とファインチューニングの違いは、特徴量抽出層の重みデータをそのまま利用するか、一部を再学習させるかの違いです。分類層に関しては、どちらも新しいタスクに対して学習させます。

***VGG16***
![VGG16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/f9ee00ee-d17a-ca53-0f0a-25d464cbffd8.png)

以下は使用例です。VGG16は、Kerasからロードすることができます。

```.py
from keras.applications.vgg16 import VGG16
from keras.optimizers import SGD

base_model = VGG16(weights='imagenet', include_top=False) # 分類層を含まないでロード

x = base_model.output
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
output = Dense(10, activation='softmax')(x)

new_model = Model(inputs=base_model.input, outputs=output)

# 転移学習
# すべての特徴量抽出層の重みを再学習させない
for layer in new_model.layers:
  layer.trainable = False

# ファインチューニング
# 15層目までは学習させない（特徴量抽出層の一部の重みを再学習させる）
for layer in new_model.layers[:15]:
    layer.trainable = False

new_model.compile(loss='categorical_crossentropy',
                  optimizer=SGD(lr=1e-3, momentum=0.9),
                  metrics=['accuracy'])
```

参考：[GPUを使ってVGG16をFine Tuningして、顔認識AIを作って見た](https://qiita.com/yottyann1221/items/20a9c8a7a02edc7cd3d1)

レイヤーのインデックスと名前の確認

```.py
for i, layer in enumerate(model.layers):
   print(i, layer.name)
```

<!-- ====================================================== -->
# おまけ
<!-- ====================================================== -->
###Colaboratoryでパッケージのバージョンを変更する

変更する場合は、ランタイムを再起動してから行います。

```.py
# インストールパッケージの確認
!pip freeze

# pythonバージョンの確認
import platform
print("python " + platform.python_version())

# Tensolflowのバージョン変更、確認
%tensorflow_version 1.x # 2系は「2.x」
import tensorflow as tf
print(tf.__version__)

# Kerasのバージョン変更、確認
!pip install keras==2.1.2
import keras
print(keras.__version__)
```

<!-- ====================================================== -->
# 参考
<!-- ====================================================== -->
***サイト***
[Kerasドキュメント](https://keras.io/ja/)
[GPUを使ってVGG16をFine Tuningして、顔認識AIを作って見た](https://qiita.com/yottyann1221/items/20a9c8a7a02edc7cd3d1)

***書籍***
[直感 Deep Learning ―Python×Kerasでアイデアを形にするレシピ](https://www.amazon.co.jp/Deep-Learning-%E2%80%95Python%C3%97Keras%E3%81%A7%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2%E3%82%92%E5%BD%A2%E3%81%AB%E3%81%99%E3%82%8B%E3%83%AC%E3%82%B7%E3%83%94-Antonio-Gulli/dp/4873118263/)
[Kerasによるディープラーニング:実践テクニック&チューニング技法](https://www.amazon.co.jp/Keras%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E5%AE%9F%E8%B7%B5%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E6%8A%80%E6%B3%95-%E9%9D%92%E9%87%8E-%E9%9B%85%E6%A8%B9/dp/4627854811/)