TensorFlow, Kerasで転移学習・ファインチューニング(画像分類の例)
TensorFlowとKerasを利用して学習済みモデルを元に転移学習(Transfer Learning)・ファインチューニング(Fine Tuning)を行う方法をサンプルコードとともに説明する。
- 転移学習・ファインチューニングとは
- MobileNetの学習済みモデルをCIFAR10データセットに適用
- データの読み込み
- モデルの実装
- 追加した全結合層のみを学習
- 学習済みモデルの一部を再学習(ファインチューニング)
- 転移学習・ファインチューニングにおいて考慮する項目
- ローカルの画像を扱う例
- データのダウンロード
- データの準備:
ImageDataGenerator
- モデルの実装と学習
以下のサンプルコードのTensorFlowのバージョンは2.1.0
。TensorFlowに統合されたKerasを使う。
import tensorflow as tf
print(tf.__version__)
# 2.1.0
スタンドアローンのKerasを使う場合、import keras
で別途Kerasをインポートして、コード中のtf.keras
の部分をkeras
に置き換えれば動くかもしれないが、保証はできない。
TensorFlow, Kerasについての基礎は以下の記事を参照。
転移学習・ファインチューニングとは
転移学習とファインチューニングについては様々な説明がされているようだが、ここでは書籍『直感 Deep Learning』の記述を引用する。
転移学習(transfer learning)はディープラーニングにおいてとても強力な、さまざまな分野で適用可能な手法です。転移学習のコンセプトはとても簡単な例で説明することができます。あなたが新しい言語(たとえばスペイン語)を学びたいと思ったとき、すでに学んでいる言語、たとえば英語の知識は役に立つでしょう。このように、すでに学習されたモデルを他の新しいタスクに適用することが転移学習の基本的な考え方です。
現在画像処理に携わる研究者たちは、このシンプルな考えを元に、モデルとゼロから訓練するほどデータを用意できない新しいタスクに対して事前学習済みのCNNを利用しています。このとき、ファインチューニング(fine-tuning)と呼ばれる、事前学習済みのモデルの一部を変更して再学習する手法がよく用いられます。 直感 Deep Learning P99-100
転移学習(Transfer Learning)は「すでに学習されたモデルを他の新しいタスクに適用する手法(あるいは考え方)全般」を指し、その中の一つにファインチューニング(Fine Tuning)と呼ばれる「事前学習済みのモデルの一部を変更して再学習する手法」がある…という理解だが、厳密な定義を求めているわけではないので、ここでは深追いしない。
参考になりそうなものをほかにもいくつか引用しておく。
Transfer learning (TL) is a research problem in machine learning (ML) that focuses on storing knowledge gained while solving one problem and applying it to a different but related problem. Transfer learning - Wikipedia
Fine-Tuning: Unfreeze a few of the top layers of a frozen model base and jointly train both the newly-added classifier layers and the last layers of the base model. This allows us to "fine-tune" the higher-order feature representations in the base model in order to make them more relevant for the specific task. Transfer learning with a pretrained ConvNet | TensorFlow Core
The three major Transfer Learning scenarios look as follows:
- ConvNet as fixed feature extractor. ...
- Fine-tuning the ConvNet. ...
- Pretrained models. ...
CS231n Convolutional Neural Networks for Visual Recognition
MobileNetの学習済みモデルをCIFAR10データセットに適用
転移学習・ファインチューニングの具体例として、ここでは、MobileNetV2のImageNetで学習済みのモデルをCIFAR10のデータセットに適用する。
学習済みのモデルとしてMobileNetV2を選んだのは、特に深い理由があるわけではなく以下の公式のチュートリアルに合わせただけ。VGG16やResNetなどそのほかのモデルで使い方は同じ。
上記公式チュートリアルでは犬と猫の2クラス分類を行っているが、ここではKerasのDatasets
に含まれているCIFAR10のデータを使う。MobileNetV2のImageNetで学習したモデル(= 1000クラス分類)をCIFAR10の10クラス分類に適用する、という処理となる。
サンプルコード全体は以下。
ローカルの犬猫画像で2クラス分類を行う例は後述する。
データの読み込み
CIFAR10のデータセットはtf.keras.datasets.cifar10.load_data()
で読み込める。初回実行時に~/.keras/models/
にデータがダウンロードされる。
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
print(type(x_train))
# <class 'numpy.ndarray'>
print(x_train.shape, y_train.shape)
# (50000, 32, 32, 3) (50000, 1)
print(x_test.shape, y_test.shape)
# (10000, 32, 32, 3) (10000, 1)
32 x 32
のRGBカラー画像が訓練用50000枚、テスト用10000枚。正解ラベルは0
から9
の整数。
モデルの実装
ここでは、引数input_tensor
を指定して、モデルの入力層側にリサイズと前処理を行うレイヤーを組み込む。モデルに組み込まず、外部で処理してからモデルに入力する方式でも構わない。
さらに、出力層側を元のモデルのものから10クラス分類用の全結合層に変更する。include_top=False
としてSequential APIで全結合層を追加する。Functional APIの例は後述。
inputs = tf.keras.Input(shape=(None, None, 3))
x = tf.keras.layers.Lambda(lambda img: tf.image.resize(img, (160, 160)))(inputs)
x = tf.keras.layers.Lambda(tf.keras.applications.mobilenet_v2.preprocess_input)(x)
base_model = tf.keras.applications.mobilenet_v2.MobileNetV2(
weights='imagenet', input_tensor=x, input_shape=(160, 160, 3),
include_top=False, pooling='avg'
)
model = tf.keras.Sequential([
base_model,
tf.keras.layers.Dense(10, activation='softmax')
])
model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# mobilenetv2_1.00_160 (Model) (None, 1280) 2257984
# _________________________________________________________________
# dense (Dense) (None, 10) 12810
# =================================================================
# Total params: 2,270,794
# Trainable params: 2,236,682
# Non-trainable params: 34,112
# _________________________________________________________________
summary()
の出力でも分かるように、Sequential APIの場合はベースモデル(MobileNetV2の学習済みモデル)が一つのレイヤーとして扱われる。入れ子になっているようなイメージ。
print(len(model.layers))
# 2
print(model.layers[0].name)
# mobilenetv2_1.00_160
print(len(model.layers[0].layers))
# 158
引数input_tensor
, include_top
による入力層・出力層の変更や学習済みモデルの前処理などについての詳細は以下の記事を参照。
上の記事の最後に書いたように、TensorFlow2.1.0
ではVGG16やVGG19などのmode='caffe'
のpreprocess_input
を使ったLambda
レイヤーを含むモデルでfit()
やpredict()
を実行するとエラーが発生するので注意。
なお、MobileNetV2では入力画像サイズが224 x 224
, 192 x 192
, 160 x 160
, 128 x 128
, 96 x 96
の場合の学習済み重みデータが提供されている。
input_tensor
を指定した場合もinput_shape
を省略せず指定しないと所望の重みデータが使われない模様(バージョン、環境によって違うかもしれない)。
上の例では160 x 160
にリサイズしているが、これは適当に決めたもので特別な理由があるわけではない。
追加した全結合層のみを学習
まずは追加した全結合層のみを学習する。
ベースモデルのtrainable
属性をFalse
とし、Freeze(凍結)する。ベースモデルの各レイヤーの重みが更新されなくなる(= 学習されなくなる)。
base_model.trainable = False
model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# mobilenetv2_1.00_160 (Model) (None, 1280) 2257984
# _________________________________________________________________
# dense (Dense) (None, 10) 12810
# =================================================================
# Total params: 2,270,794
# Trainable params: 12,810
# Non-trainable params: 2,257,984
# _________________________________________________________________
summary()
の出力結果のTrainable params
(学習されるパラメータの数)が追加した全結合層の分のみになっていることに注目。
なお、summary()
の出力結果には反映されているが、実際に設定を有効にするにはcompile()
する必要があるので注意。compile()
のあとでtrainable
を変更した場合、再度compile()
しなければならない。
trainable
属性についての詳細は以下の記事を参照。
オプティマイザー、損失関数、評価関数を設定してcompile()
。学習率learning_rate
は上記の公式チュートリアルの値を使った。
model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.0001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
追加した全結合層はランダムな重みで初期化されているだけなので、当然、学習前のこの時点ではまったく分類できない。参考までにevaluate()
で評価してみると、正解率は10%前後となる。10クラス分類なので適当に予測して偶然当たっているだけの正解率。
print(model.evaluate(x_test, y_test, verbose=0))
# [2.9224756198883055, 0.1132]
fit()
で学習しevaluate()
で評価する。CPU環境だとかなり時間がかかるので注意。
model.fit(x_train, y_train, epochs=6, validation_split=0.2, batch_size=256)
# Train on 40000 samples, validate on 10000 samples
# Epoch 1/6
# 40000/40000 [==============================] - 23s 571us/sample - loss: 1.9849 - accuracy: 0.3234 - val_loss: 1.5291 - val_accuracy: 0.4970
# Epoch 2/6
# 40000/40000 [==============================] - 21s 537us/sample - loss: 1.2436 - accuracy: 0.6140 - val_loss: 1.0953 - val_accuracy: 0.6405
# Epoch 3/6
# 40000/40000 [==============================] - 22s 540us/sample - loss: 0.9540 - accuracy: 0.6974 - val_loss: 0.9669 - val_accuracy: 0.6762
# Epoch 4/6
# 40000/40000 [==============================] - 21s 534us/sample - loss: 0.8236 - accuracy: 0.7321 - val_loss: 0.8732 - val_accuracy: 0.7070
# Epoch 5/6
# 40000/40000 [==============================] - 22s 541us/sample - loss: 0.7538 - accuracy: 0.7530 - val_loss: 0.8641 - val_accuracy: 0.7090
# Epoch 6/6
# 40000/40000 [==============================] - 22s 546us/sample - loss: 0.7110 - accuracy: 0.7629 - val_loss: 0.8390 - val_accuracy: 0.7204
#
# <tensorflow.python.keras.callbacks.History at 0x7f79f9f37630>
print(model.evaluate(x_test, y_test, verbose=0))
# [0.8526914182662964, 0.7186]
学習済みモデルの一部を再学習(ファインチューニング)
学習済みのベースモデルの一部を再学習する。
MobileNetV2はblock_1_xxx
からblock_16_xxx
まで16のブロックに分かれているが、ここではblock_12_xxx
以降を再学習することにする。
ブロック12の最初のレイヤーであるblock_12_expand
のインデックス(何層目か)を取得する。
layer_names = [l.name for l in base_model.layers]
idx = layer_names.index('block_12_expand')
print(idx)
# 110
ベースモデルのtrainable
属性をTrue
とし、全体をUnfreeze(解凍)してから、ブロック11までのレイヤー(block_12_expand
の一つ前までのレイヤー)のtrainable
をFalse
としFreeze(凍結)する。
base_model.trainable = True
for layer in base_model.layers[:idx]:
layer.trainable = False
スライス[start:stop]
はstop
を含まないことに注意。
なお、ベースモデルのtrainable
をTrue
としないと、その内部のレイヤーのtrainable
をTrue
としてもUnfreeze(解凍)されず学習されないので注意。この例のようにベースモデルが一つのレイヤーとして扱われている場合、ベースモデルのtrainable
がFalse
だと、内部のレイヤーのtrainable
がTrue
であっても学習対象とならない。
上述のように、trainable
を変更した後は再度compile()
する必要がある。ここで、上記の公式チュートリアルを参考に学習率learning_rate
を初回の学習時より小さくしている。
model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.00001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# mobilenetv2_1.00_160 (Model) (None, 1280) 2257984
# _________________________________________________________________
# dense (Dense) (None, 10) 12810
# =================================================================
# Total params: 2,270,794
# Trainable params: 1,812,426
# Non-trainable params: 458,368
# _________________________________________________________________
この状態で学習すると正解率がさらに改善することが確認できる。
model.fit(x_train, y_train, epochs=6, validation_split=0.2, batch_size=256)
# Train on 40000 samples, validate on 10000 samples
# Epoch 1/6
# 40000/40000 [==============================] - 29s 714us/sample - loss: 0.6117 - accuracy: 0.7946 - val_loss: 0.7145 - val_accuracy: 0.7577
# Epoch 2/6
# 40000/40000 [==============================] - 26s 651us/sample - loss: 0.4992 - accuracy: 0.8292 - val_loss: 0.6788 - val_accuracy: 0.7719
# Epoch 3/6
# 40000/40000 [==============================] - 26s 656us/sample - loss: 0.4307 - accuracy: 0.8522 - val_loss: 0.6632 - val_accuracy: 0.7744
# Epoch 4/6
# 40000/40000 [==============================] - 26s 651us/sample - loss: 0.3784 - accuracy: 0.8713 - val_loss: 0.6444 - val_accuracy: 0.7792
# Epoch 5/6
# 40000/40000 [==============================] - 26s 650us/sample - loss: 0.3377 - accuracy: 0.8857 - val_loss: 0.6478 - val_accuracy: 0.7790
# Epoch 6/6
# 40000/40000 [==============================] - 27s 671us/sample - loss: 0.3038 - accuracy: 0.8981 - val_loss: 0.6257 - val_accuracy: 0.7865
#
# <tensorflow.python.keras.callbacks.History at 0x7f79f9dbcf98>
print(model.evaluate(x_test, y_test, verbose=0))
# [0.6538689835548401, 0.7845]
転移学習・ファインチューニングにおいて考慮する項目
上の例では転移学習・ファインチューニングの流れを説明することを目的として正解率(精度)は特に気にしていないが、実際に正解率を改善するには様々なことを考慮する必要がある。
再び『直感 Deep Learning 』から引用する。
Kerasに組み込まれた事前学習済みのモデルを使用して転移学習を行えば、ゼロから学習するよりも多くの時間を節約することができます。もちろん、実際に良い精度を出すには再学習させるレイヤーの数や学習のエポック数など、調整すべきパラメータが多くあります。 直感 Deep Learning P102
例えば、上の例では再学習させるレイヤーを適当に決めたが、これが最善とは限らない。
一般的に、畳み込み層を重ねるモデルの場合、層が深くなるにつれて抽象的な特徴が抽出され、出力層に近づくと分類するクラス(物体)に対応した重みを学習していく。中間層で抽出される抽象的な特徴はどのようなクラスに分類するとしても共通であるから後半のレイヤーのみを再学習すればよい、というのが画像分類におけるファインチューニングの考え方である。
どのレイヤーまでが共通する特徴を抽出する部分で、どのレイヤーからが分類するクラスに依存する部分というのははっきり分けられるものではなく、いくつかのパターンを試してみるというのが現実的だろう。
また、上の例では最後に全結合層Dense
を一層のみ追加したが、隠れ層(中間層)を加える構成も考えられる。
当然ながら、通常のモデルの学習と同様に、学習率やバッチサイズ、エポック数などのハイパーパラメータも調整しなければいけない。
ローカルの画像を扱う例
次に、ローカルの画像を扱うサンプルコードを示す。
画像をすべて読み込んで形状が(サンプル数, 縦, 横, チャンネル数)
のnumpy.ndarray
とすれば上の例と同じように扱えるが、画像が大量でメモリに乗り切らないような場合は、ローカルのディレクトリから画像ファイルを逐次読み込む必要がある。
ここではtf.keras.preprocessing.image.ImageDataGenerator
を用いる。
サンプルコード全体は以下。
データのダウンロード
例として、以下の公式チュートリアルで紹介されている犬と猫の画像データを使用する。あくまでもお試し用なので枚数は多くない。
tf.keras.utils.get_file()
でZIPファイルをダウンロードし展開する。この例ではデフォルトの~/.keras/datasets/
以下にダウンロード、展開される。
path_to_zip = tf.keras.utils.get_file(
fname='cats_and_dogs_filtered.zip',
origin='https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip',
extract=True
)
以下のようなディレクトリ構造で、犬と猫の画像に分けられ、さらにtrain
とvalidation
に振り分けられている。
cats_and_dogs_filtered
|__ train
|______ cats: [cat.0.jpg, cat.1.jpg, cat.2.jpg ....]
|______ dogs: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]
|__ validation
|______ cats: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ....]
|______ dogs: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]
上記の公式チュートリアルではディレクトリ名の通り、訓練データと検証(validation)データとして使っているが、以下のサンプルコードではtrain
を訓練データと検証データ、validation
をテストデータとして使う。
tf.keras.utils.get_file()
はダウンロード先のディレクトリのパスを返す。そこからtrain
, validation
の各ディレクトリへのパス文字列を生成する。
path_to_dir = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')
train_dir = os.path.join(path_to_dir, 'train')
test_dir = os.path.join(path_to_dir, 'validation')
データの準備: ImageDataGenerator
まずImageDataGenerator
のインスタンスを生成する。
引数preprocessing_function
に前処理を行う関数、ここではMobileNetV2の前処理関数preprocess_input
を指定する。
訓練データの方は訓練用と検証用に分割するため引数validation_split
を指定する。
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
preprocessing_function=tf.keras.applications.mobilenet_v2.preprocess_input,
validation_split=0.2
)
test_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
preprocessing_function=tf.keras.applications.mobilenet_v2.preprocess_input
)
Data Augmentation(画像の水増し)を行う場合はImageDataGenerator()
のその他の引数を設定するが、今回は行わない。なお、validation_split
を設定した場合、訓練用と検証用の両方に対してData Augmentationが行われるので注意。
- python - Can flow_from_directory get train and validation data from the same directory in Keras? - Stack Overflow
- Split train data into training and validation when using ImageDataGenerator and model.fit_generator · Issue #5862 · keras-team/keras
訓練用、検証用、テスト用の各ジェネレータイテレータをflow_from_directory()
メソッドで生成する。
訓練用と検証用は引数subset
をそれぞれ'training'
, 'validation'
とする。また、引数target_size
に画像のサイズを設定するとリサイズされる。
batch_size = 64
height = 160
width = 160
train_generator = train_datagen.flow_from_directory(
batch_size=batch_size,
directory=train_dir,
target_size=(height, width),
class_mode='binary',
subset='training'
)
# Found 1600 images belonging to 2 classes.
valid_generator = train_datagen.flow_from_directory(
batch_size=batch_size,
directory=train_dir,
target_size=(height, width),
class_mode='binary',
subset='validation'
)
# Found 400 images belonging to 2 classes.
test_generator = test_datagen.flow_from_directory(
batch_size=batch_size,
directory=test_dir,
target_size=(height, width),
class_mode='binary'
)
# Found 1000 images belonging to 2 classes.
モデルの実装と学習
上の例と同じくMobileNetV2の学習済みモデルをベースモデルとして使う。リサイズを含む前処理はImageDataGenerator
の設定で行っているため、ここではinput_shape
を設定するのみ。
base_model = tf.keras.applications.mobilenet_v2.MobileNetV2(
weights='imagenet', input_shape=(height, width, 3),
include_top=False, pooling='avg'
)
x = base_model.output
x = tf.keras.layers.Dense(1, activation='sigmoid')(x)
model = tf.keras.Model(inputs=base_model.input, outputs=x)
上の例のようにSequential APIでモデルを生成してもいいが、参考までにここではFunctional APIを用いる。
Sequential APIではベースモデルが一つのレイヤーとして扱われるが、Functional APIの場合はそのような入れ子の形にはならない。
print(len(model.layers))
# 157
ここではmodel.summary()
の結果は省略するが、サンプルコード全体のリンク先で参照されたい。
入れ子構造ではないが、ベースモデルbase_model
の各レイヤーと新たに構築したmodel
の各レイヤーは同じオブジェクトを指している。ベースモデルbase_model
のtrainable
を変更すると、その中の各レイヤーのtrainable
も変更されるため、上のSequential APIでの例と同じく一括で設定可能。
print(model.layers[0] is base_model.layers[0])
# True
print(base_model.layers[0].trainable)
# True
print(model.layers[0].trainable)
# True
base_model.trainable = False
print(base_model.layers[0].trainable)
# False
print(model.layers[0].trainable)
# False
compile()
でオプティマイザー、損失関数、評価関数を設定。
model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.0001),
loss='binary_crossentropy',
metrics=['accuracy'])
新たに追加した全結合層の学習前にevaluate()
でモデルを評価すると、正解率は50%程度。2クラス分類なので、まったく分類できていないことが確認できる。
print(model.evaluate(test_generator, verbose=0))
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# [0.7827205918729305, 0.473]
なお、以前のバージョンではジェネレータを用いる場合はevaluate_generator()
メソッドを使っていたが、新しいバージョンではevaluate()
にジェネレータを指定できるようになった。fit()
とfit_generator()
、predict()
とpredict_generator()
についても同様。
fit()
で学習。ImageDataGenerator
は無限にイテレーションするので引数steps_per_epoch
およびvalidation_steps
を明示的に設定する。
model.fit(
train_generator,
steps_per_epoch=train_generator.n // batch_size,
validation_data=valid_generator,
validation_steps=valid_generator.n // batch_size,
epochs=6
)
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# Train for 25 steps, validate for 6 steps
# Epoch 1/6
# 25/25 [==============================] - 4s 164ms/step - loss: 0.7280 - accuracy: 0.5475 - val_loss: 0.6718 - val_accuracy: 0.6120
# Epoch 2/6
# 25/25 [==============================] - 2s 85ms/step - loss: 0.6413 - accuracy: 0.6306 - val_loss: 0.5767 - val_accuracy: 0.7292
# Epoch 3/6
# 25/25 [==============================] - 2s 84ms/step - loss: 0.5740 - accuracy: 0.7044 - val_loss: 0.4978 - val_accuracy: 0.7917
# Epoch 4/6
# 25/25 [==============================] - 2s 85ms/step - loss: 0.5162 - accuracy: 0.7600 - val_loss: 0.4349 - val_accuracy: 0.8385
# Epoch 5/6
# 25/25 [==============================] - 2s 87ms/step - loss: 0.4672 - accuracy: 0.8012 - val_loss: 0.3834 - val_accuracy: 0.8620
# Epoch 6/6
# 25/25 [==============================] - 2s 86ms/step - loss: 0.4255 - accuracy: 0.8350 - val_loss: 0.3423 - val_accuracy: 0.8750
#
# <tensorflow.python.keras.callbacks.History at 0x7fd8cbfa9240>
print(model.evaluate(test_generator, verbose=0))
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# [0.3087761905044317, 0.912]
ファインチューニングのために、ベースモデルの後半のレイヤーのtrainable
をTrue
とする。trainable
を変更したあとは忘れずにcompile()
。
idx = [l.name for l in base_model.layers].index('block_12_expand')
for layer in base_model.layers[idx:]:
layer.trainable = True
model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.00001),
loss='binary_crossentropy',
metrics=['accuracy'])
ベースモデルbase_model
のtrainable
はFalse
のままだが、ベースモデルが一つのレイヤーとして扱われていないので、ベースモデルのtrainable
の値によらず内部のレイヤーのtrainable
の値が反映される。
なお、ベースモデルのtrainable
を変更するとその中のレイヤーのtrainable
も一括で変更されるので、上のSequential APIの例のように、ベースモデルのtrainable
をTrue
にしてから前半のレイヤーのtrainable
をFalse
にしても同じ結果になる。
再学習を行う。
model.fit(
train_generator,
steps_per_epoch=train_generator.n // batch_size,
validation_data=valid_generator,
validation_steps=valid_generator.n // batch_size,
epochs=6
)
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# Train for 25 steps, validate for 6 steps
# Epoch 1/6
# 25/25 [==============================] - 5s 208ms/step - loss: 0.3144 - accuracy: 0.8944 - val_loss: 0.2101 - val_accuracy: 0.9375
# Epoch 2/6
# 25/25 [==============================] - 2s 86ms/step - loss: 0.2024 - accuracy: 0.9538 - val_loss: 0.1632 - val_accuracy: 0.9453
# Epoch 3/6
# 25/25 [==============================] - 2s 87ms/step - loss: 0.1433 - accuracy: 0.9762 - val_loss: 0.1352 - val_accuracy: 0.9505
# Epoch 4/6
# 25/25 [==============================] - 2s 87ms/step - loss: 0.1029 - accuracy: 0.9869 - val_loss: 0.1181 - val_accuracy: 0.9609
# Epoch 5/6
# 25/25 [==============================] - 2s 92ms/step - loss: 0.0744 - accuracy: 0.9944 - val_loss: 0.1072 - val_accuracy: 0.9661
# Epoch 6/6
# 25/25 [==============================] - 2s 87ms/step - loss: 0.0540 - accuracy: 0.9975 - val_loss: 0.1002 - val_accuracy: 0.9688
#
# <tensorflow.python.keras.callbacks.History at 0x7fd8cbe39588>
print(model.evaluate(test_generator, verbose=0))
# WARNING:tensorflow:sample_weight modes were coerced from
# ...
# to
# ['...']
# [0.06722519337199628, 0.978]
正解率の改善が確認できる。