In [1]:
import re, time
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
print("Tensorflow version " + tf.__version__)
AUTOTUNE = tf.data.AUTOTUNE
Tensorflow version 2.7.0

TPU or GPU detection

TPUClusterResolver() automatically detects a connected TPU on all Gooogle's platforms: Colaboratory, AI Platform (ML Engine), Kubernetes, Kaggle, ...

In [2]:
try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for GPU or multi-GPU machines
    #strategy = tf.distribute.get_strategy() # default strategy that works on CPU and single GPU
    #strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy() # for clusters of multi-GPU machines

print("Number of accelerators: ", strategy.num_replicas_in_sync)
INFO:tensorflow:Deallocate tpu buffers before initializing tpu system.
INFO:tensorflow:Deallocate tpu buffers before initializing tpu system.
INFO:tensorflow:Initializing the TPU system: grpc://10.10.154.90:8470
INFO:tensorflow:Initializing the TPU system: grpc://10.10.154.90:8470
INFO:tensorflow:Finished initializing TPU system.
INFO:tensorflow:Finished initializing TPU system.
INFO:tensorflow:Found TPU system:
INFO:tensorflow:Found TPU system:
INFO:tensorflow:*** Num TPU Cores: 8
INFO:tensorflow:*** Num TPU Cores: 8
INFO:tensorflow:*** Num TPU Workers: 1
INFO:tensorflow:*** Num TPU Workers: 1
INFO:tensorflow:*** Num TPU Cores Per Worker: 8
INFO:tensorflow:*** Num TPU Cores Per Worker: 8
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:localhost/replica:0/task:0/device:CPU:0, CPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:localhost/replica:0/task:0/device:CPU:0, CPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:CPU:0, CPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:CPU:0, CPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:0, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:0, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:1, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:1, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:2, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:2, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:3, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:3, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:4, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:4, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:5, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:5, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:6, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:6, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:7, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:7, TPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU_SYSTEM:0, TPU_SYSTEM, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU_SYSTEM:0, TPU_SYSTEM, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:XLA_CPU:0, XLA_CPU, 0, 0)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:XLA_CPU:0, XLA_CPU, 0, 0)
Number of accelerators:  8
In [ ]:
 

Configuration

Four images sizes are available for this dataset.

In [3]:
EPOCHS = 12
IMAGE_SIZE = [331, 331]

FLOWERS_DATASETS = { # available image sizes
    192: 'gs://flowers-public/tfrecords-jpeg-192x192-2/*.tfrec',
    224: 'gs://flowers-public/tfrecords-jpeg-224x224/*.tfrec',
    331: 'gs://flowers-public/tfrecords-jpeg-331x331/*.tfrec',
    512: 'gs://flowers-public/tfrecords-jpeg-512x512/*.tfrec'
}
CLASSES = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'] # do not change, maps to the labels in the data (folder names)
assert IMAGE_SIZE[0] == IMAGE_SIZE[1], "only square images are supported"
assert IMAGE_SIZE[0] in FLOWERS_DATASETS, "this image size is not supported"


# mixed precision
# On TPU, bfloat16/float32 mixed precision is automatically used in TPU computations.
# Enabling it in Keras also stores relevant variables in bfloat16 format (memory optimization).
# On GPU, specifically V100, mixed precision must be enabled for hardware TensorCores to be used.
# XLA compilation must be enabled for this to work. (On TPU, XLA compilation is the default)
MIXED_PRECISION = False
if MIXED_PRECISION:
    if tpu: 
        policy = tf.keras.mixed_precision.Policy('mixed_bfloat16')
    else: #
        policy = tf.keras.mixed_precision.Policy('mixed_float16')
        tf.config.optimizer.set_jit(True) # XLA compilation
    tf.keras.mixed_precision.set_global_policy(policy)
    print('Mixed precision enabled')

# batch and learning rate settings
if strategy.num_replicas_in_sync == 8: # TPU or 8xGPU
    BATCH_SIZE = 16 * strategy.num_replicas_in_sync
    VALIDATION_BATCH_SIZE = 16 * strategy.num_replicas_in_sync
    start_lr = 0.00001
    min_lr = 0.00001
    max_lr = 0.00005 * strategy.num_replicas_in_sync
    rampup_epochs = 5
    sustain_epochs = 0
    exp_decay = .8
elif strategy.num_replicas_in_sync == 1: # single GPU
    BATCH_SIZE = 16
    VALIDATION_BATCH_SIZE = 16
    start_lr = 0.00001
    min_lr = 0.00001
    max_lr = 0.0002
    rampup_epochs = 5
    sustain_epochs = 0
    exp_decay = .8
else: # TPU pod
    BATCH_SIZE = 8 * strategy.num_replicas_in_sync
    VALIDATION_BATCH_SIZE = 8 * strategy.num_replicas_in_sync
    start_lr = 0.00001
    min_lr = 0.00001
    max_lr = 0.00002 * strategy.num_replicas_in_sync
    rampup_epochs = 7
    sustain_epochs = 0
    exp_decay = .8

def lrfn(epoch):
    def lr(epoch, start_lr, min_lr, max_lr, rampup_epochs, sustain_epochs, exp_decay):
        if epoch < rampup_epochs:
            lr = (max_lr - start_lr)/rampup_epochs * epoch + start_lr
        elif epoch < rampup_epochs + sustain_epochs:
            lr = max_lr
        else:
            lr = (max_lr - min_lr) * exp_decay**(epoch-rampup_epochs-sustain_epochs) + min_lr
        return lr
    return lr(epoch, start_lr, min_lr, max_lr, rampup_epochs, sustain_epochs, exp_decay)
    
lr_callback = tf.keras.callbacks.LearningRateScheduler(lambda epoch: lrfn(epoch), verbose=True)

rng = [i for i in range(EPOCHS)]
y = [lrfn(x) for x in rng]
plt.plot(rng, [lrfn(x) for x in rng])
print(y[0], y[-1])
1e-05 0.00011223616000000004

Display utilities

In [4]:
#@title display utilities [RUN ME]

def dataset_to_numpy_util(dataset, N):
  dataset = dataset.unbatch().batch(N)
  for images, labels in dataset:
    numpy_images = images.numpy()
    numpy_labels = labels.numpy()
    break;  
  return numpy_images, numpy_labels

def title_from_label_and_target(label, correct_label):
  label = np.argmax(label, axis=-1)  # one-hot to class number
  correct_label = np.argmax(correct_label, axis=-1) # one-hot to class number
  correct = (label == correct_label)
  return "{} [{}{}{}]".format(CLASSES[label], str(correct), ', shoud be ' if not correct else '',
                              CLASSES[correct_label] if not correct else ''), correct

def display_one_flower(image, title, subplot, red=False):
    plt.subplot(subplot)
    plt.axis('off')
    plt.imshow(image)
    plt.title(title, fontsize=16, color='red' if red else 'black')
    return subplot+1
  
def display_9_images_from_dataset(dataset):
  subplot=331
  plt.figure(figsize=(13,13))
  images, labels = dataset_to_numpy_util(dataset, 9)
  for i, image in enumerate(images):
    title = CLASSES[np.argmax(labels[i], axis=-1)]
    subplot = display_one_flower(image, title, subplot)
    if i >= 8:
      break;
              
  #plt.tight_layout() # bug in tight layout in this version of matplotlib
  plt.subplots_adjust(wspace=0.1, hspace=0.1)
  plt.show()
  
def display_9_images_with_predictions(images, predictions, labels):
  subplot=331
  plt.figure(figsize=(13,13))
  for i, image in enumerate(images):
    title, correct = title_from_label_and_target(predictions[i], labels[i])
    subplot = display_one_flower(image, title, subplot, not correct)
    if i >= 8:
      break;
              
  #plt.tight_layout() # bug in tight layout in this version of matplotlib
  plt.subplots_adjust(wspace=0.1, hspace=0.1)
  plt.show()
  
def display_training_curves(training, validation, title, subplot):
  if subplot%10==1: # set up the subplots on the first call
    plt.subplots(figsize=(10,10), facecolor='#F0F0F0')
    #plt.tight_layout() # bug in tight layout in this version of matplotlib
  ax = plt.subplot(subplot)
  ax.set_facecolor('#F8F8F8')
  ax.plot(training)
  ax.plot(validation)
  ax.set_title('model '+ title)
  ax.set_ylabel(title)
  #ax.set_ylim(0.28,1.05)
  ax.set_xlabel('epoch')
  ax.legend(['train', 'valid.'])

Read images and labels from TFRecords

In [5]:
def count_data_items(filenames):
    # the number of data items is written in the name of the .tfrec files, i.e. flowers00-230.tfrec = 230 data items
    n = [int(re.compile(r"-([0-9]*)\.").search(filename).group(1)) for filename in filenames]
    return np.sum(n)

gcs_pattern = FLOWERS_DATASETS[IMAGE_SIZE[0]]
validation_split = 0.19
filenames = tf.io.gfile.glob(gcs_pattern)
split = len(filenames) - int(len(filenames) * validation_split)
TRAIN_FILENAMES = filenames[:split]
VALID_FILENAMES = filenames[split:]
TRAIN_STEPS = count_data_items(TRAIN_FILENAMES) // BATCH_SIZE
print("TRAINING IMAGES: ", count_data_items(TRAIN_FILENAMES), ", STEPS PER EPOCH: ", TRAIN_STEPS)
print("VALIDATION IMAGES: ", count_data_items(VALID_FILENAMES))
        
def read_tfrecord(example):
    features = {
        "image": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring
        "class": tf.io.FixedLenFeature([], tf.int64),  # shape [] means scalar
        "one_hot_class": tf.io.VarLenFeature(tf.float32),
    }
    example = tf.io.parse_single_example(example, features)
    image = tf.io.decode_jpeg(example['image'], channels=3)
    image = tf.cast(image, tf.float32) / 255.0  # convert image to floats in [0, 1] range
    class_label = tf.cast(example['class'], tf.int32)
    one_hot_class = tf.sparse.to_dense(example['one_hot_class'])
    one_hot_class = tf.reshape(one_hot_class, [5])
    return image, one_hot_class
    
def force_image_sizes(dataset, image_size):
    # explicit size will be needed for TPU
    reshape_images = lambda image, label: (tf.reshape(image, [*image_size, 3]), label)
    dataset = dataset.map(reshape_images, num_parallel_calls=AUTOTUNE)
    return dataset

def load_dataset(filenames):
    # read from TFRecords. For optimal performance, use "interleave(tf.data.TFRecordDataset, ...)"
    # to read from multiple TFRecord files at once and set the option experimental_deterministic = False
    # to allow order-altering optimizations.

    opt = tf.data.Options()
    opt.experimental_deterministic = False

    dataset = tf.data.Dataset.from_tensor_slices(filenames)
    dataset = dataset.with_options(opt)
    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE) # automatically interleaves reads from multiple files
    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)
    dataset = force_image_sizes(dataset, IMAGE_SIZE)
    return dataset

def data_augment(image, one_hot_class):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_saturation(image, 0, 2)
    return image, one_hot_class
    
# For experts: fine adjustments of tf.data.Dataset distribution behavior:

# Replicating a datset with state (even random number generator state) does not replicate the
# state and changes the behavior of the dataset. If the state is just the RNG state, it usually
# does not matter but this behavior can be adjusted with tf.data.experimental.ExternalStatePolicy:
#  WARN = 0   (this is the default in Tensorflow outside of Keras)
#  IGNORE = 1 (this is the default in Keras)
#  FAIL = 2

# On TPU pods, the dataset API attempts to shard the dataset across individual TPUs at the file
# level so that TPUs only load the data they will actually train on. This requires more data files 
# than TPUs in the pod. (ex: TPU v3-32 pod = 4 TPUs => dataset must have at least 4 files)
# An error will occur if there are not enough data files. File-level sharding can be disabled:
#  opt = tf.data.Options()
#  opt.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.DATA
#  dataset = dataset.with_options(opt)
   

def get_training_dataset():
    dataset = load_dataset(TRAIN_FILENAMES)
    dataset = dataset.map(data_augment, num_parallel_calls=AUTOTUNE)
    dataset = dataset.repeat()
    dataset = dataset.shuffle(2048)
    dataset = dataset.batch(BATCH_SIZE)
    dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch while training (autotune prefetch buffer size)
    return dataset

def get_validation_dataset():
    dataset = load_dataset(VALID_FILENAMES)
    dataset = dataset.batch(VALIDATION_BATCH_SIZE)
    dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch while training (autotune prefetch buffer size)
    
    # needed for TPU 32-core pod: the test dataset has only 3 files but there are 4 TPUs. FILE sharding policy must be disabled.
    opt = tf.data.Options()
    opt.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.DATA
    dataset = dataset.with_options(opt)
    
    return dataset
TRAINING IMAGES:  2990 , STEPS PER EPOCH:  23
VALIDATION IMAGES:  680

training and validation datasets

In [6]:
training_dataset = get_training_dataset()
validation_dataset = get_validation_dataset()
In [7]:
display_9_images_from_dataset(validation_dataset)

Model

In [8]:
def create_model():
    #pretrained_model = tf.keras.applications.MobileNetV2(input_shape=[*IMAGE_SIZE, 3], include_top=False)
    pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3], include_top=False)
    #pretrained_model = tf.keras.applications.VGG16(weights='imagenet', include_top=False ,input_shape=[*IMAGE_SIZE, 3])
    #pretrained_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, input_shape=[*IMAGE_SIZE, 3])
    #pretrained_model = tf.keras.applications.MobileNet(weights='imagenet', include_top=False, input_shape=[*IMAGE_SIZE, 3])
    pretrained_model.trainable = True

    model = tf.keras.Sequential([
        pretrained_model,
        tf.keras.layers.GlobalAveragePooling2D(),
        #tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(5, activation='softmax', dtype=tf.float32) # the float32 is needed on softmax layer when using mixed precision
    ])

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

    return model
    
In [9]:
with strategy.scope(): # creating the model in the TPUStrategy scope places the model on the TPU
    model = create_model()
model.summary()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/xception/xception_weights_tf_dim_ordering_tf_kernels_notop.h5
83689472/83683744 [==============================] - 1s 0us/step
83697664/83683744 [==============================] - 1s 0us/step
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 xception (Functional)       (None, 11, 11, 2048)      20861480  
                                                                 
 global_average_pooling2d (G  (None, 2048)             0         
 lobalAveragePooling2D)                                          
                                                                 
 dense (Dense)               (None, 5)                 10245     
                                                                 
=================================================================
Total params: 20,871,725
Trainable params: 20,817,197
Non-trainable params: 54,528
_________________________________________________________________

Training

In [10]:
start_time = time.time()
history = model.fit(training_dataset, validation_data=validation_dataset,
                    steps_per_epoch=TRAIN_STEPS, epochs=EPOCHS, callbacks=[lr_callback])

final_accuracy = history.history["val_accuracy"][-5:]
print("FINAL ACCURACY MEAN-5: ", np.mean(final_accuracy))
print("TRAINING TIME: ", time.time() - start_time, " sec")
Epoch 00001: LearningRateScheduler setting learning rate to 1e-05.
Epoch 1/12
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/engine/training.py:2970: StrategyBase.unwrap (from tensorflow.python.distribute.distribute_lib) is deprecated and will be removed in a future version.
Instructions for updating:
use `experimental_local_results` instead.
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/engine/training.py:2970: StrategyBase.unwrap (from tensorflow.python.distribute.distribute_lib) is deprecated and will be removed in a future version.
Instructions for updating:
use `experimental_local_results` instead.
23/23 [==============================] - 97s 1s/step - loss: 1.5257 - accuracy: 0.3291 - val_loss: 1.4283 - val_accuracy: 0.4691 - lr: 1.0000e-05

Epoch 00002: LearningRateScheduler setting learning rate to 8.8e-05.
Epoch 2/12
23/23 [==============================] - 9s 374ms/step - loss: 0.8011 - accuracy: 0.8020 - val_loss: 0.3530 - val_accuracy: 0.8912 - lr: 8.8000e-05

Epoch 00003: LearningRateScheduler setting learning rate to 0.000166.
Epoch 3/12
23/23 [==============================] - 8s 372ms/step - loss: 0.2633 - accuracy: 0.9154 - val_loss: 0.1539 - val_accuracy: 0.9471 - lr: 1.6600e-04

Epoch 00004: LearningRateScheduler setting learning rate to 0.000244.
Epoch 4/12
23/23 [==============================] - 8s 371ms/step - loss: 0.1249 - accuracy: 0.9609 - val_loss: 0.1479 - val_accuracy: 0.9456 - lr: 2.4400e-04

Epoch 00005: LearningRateScheduler setting learning rate to 0.000322.
Epoch 5/12
23/23 [==============================] - 9s 375ms/step - loss: 0.0682 - accuracy: 0.9840 - val_loss: 0.1266 - val_accuracy: 0.9662 - lr: 3.2200e-04

Epoch 00006: LearningRateScheduler setting learning rate to 0.0004.
Epoch 6/12
23/23 [==============================] - 9s 373ms/step - loss: 0.0549 - accuracy: 0.9844 - val_loss: 0.1633 - val_accuracy: 0.9603 - lr: 4.0000e-04

Epoch 00007: LearningRateScheduler setting learning rate to 0.000322.
Epoch 7/12
23/23 [==============================] - 9s 377ms/step - loss: 0.0454 - accuracy: 0.9874 - val_loss: 0.1681 - val_accuracy: 0.9574 - lr: 3.2200e-04

Epoch 00008: LearningRateScheduler setting learning rate to 0.0002596000000000001.
Epoch 8/12
23/23 [==============================] - 9s 378ms/step - loss: 0.0294 - accuracy: 0.9905 - val_loss: 0.1316 - val_accuracy: 0.9662 - lr: 2.5960e-04

Epoch 00009: LearningRateScheduler setting learning rate to 0.00020968000000000004.
Epoch 9/12
23/23 [==============================] - 9s 380ms/step - loss: 0.0254 - accuracy: 0.9946 - val_loss: 0.1587 - val_accuracy: 0.9574 - lr: 2.0968e-04

Epoch 00010: LearningRateScheduler setting learning rate to 0.00016974400000000002.
Epoch 10/12
23/23 [==============================] - 9s 394ms/step - loss: 0.0137 - accuracy: 0.9963 - val_loss: 0.1442 - val_accuracy: 0.9676 - lr: 1.6974e-04

Epoch 00011: LearningRateScheduler setting learning rate to 0.00013779520000000003.
Epoch 11/12
23/23 [==============================] - 9s 377ms/step - loss: 0.0147 - accuracy: 0.9966 - val_loss: 0.1335 - val_accuracy: 0.9647 - lr: 1.3780e-04

Epoch 00012: LearningRateScheduler setting learning rate to 0.00011223616000000004.
Epoch 12/12
23/23 [==============================] - 9s 378ms/step - loss: 0.0086 - accuracy: 0.9990 - val_loss: 0.1246 - val_accuracy: 0.9632 - lr: 1.1224e-04
FINAL ACCURACY MEAN-5:  0.9638235449790955
TRAINING TIME:  195.0656020641327  sec
In [11]:
print(history.history.keys())
display_training_curves(history.history['accuracy'][1:], history.history['val_accuracy'][1:], 'accuracy', 211)
display_training_curves(history.history['loss'][1:], history.history['val_loss'][1:], 'loss', 212)
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy', 'lr'])

Predictions

In [12]:
# a couple of images to test predictions too
some_flowers, some_labels = dataset_to_numpy_util(validation_dataset, 160)
In [13]:
# randomize the input so that you can execute multiple times to change results
permutation = np.random.permutation(8*20)
some_flowers, some_labels = (some_flowers[permutation], some_labels[permutation])

predictions = model.predict(some_flowers, batch_size=16)
evaluations = model.evaluate(some_flowers, some_labels, batch_size=16)
  
print(np.array(CLASSES)[np.argmax(predictions, axis=-1)].tolist())
print('[val_loss, val_acc]', evaluations)

display_9_images_with_predictions(some_flowers, predictions, some_labels)
10/10 [==============================] - 7s 27ms/step - loss: 0.1087 - accuracy: 0.9563
['tulips', 'tulips', 'sunflowers', 'sunflowers', 'roses', 'roses', 'tulips', 'dandelion', 'sunflowers', 'daisy', 'roses', 'tulips', 'dandelion', 'daisy', 'roses', 'daisy', 'tulips', 'tulips', 'daisy', 'tulips', 'tulips', 'tulips', 'sunflowers', 'roses', 'sunflowers', 'daisy', 'daisy', 'dandelion', 'dandelion', 'dandelion', 'sunflowers', 'daisy', 'roses', 'sunflowers', 'dandelion', 'tulips', 'tulips', 'roses', 'tulips', 'dandelion', 'daisy', 'dandelion', 'dandelion', 'sunflowers', 'tulips', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'daisy', 'sunflowers', 'daisy', 'roses', 'tulips', 'tulips', 'roses', 'sunflowers', 'sunflowers', 'daisy', 'roses', 'sunflowers', 'tulips', 'tulips', 'daisy', 'daisy', 'dandelion', 'roses', 'roses', 'tulips', 'sunflowers', 'dandelion', 'daisy', 'sunflowers', 'dandelion', 'dandelion', 'sunflowers', 'sunflowers', 'sunflowers', 'dandelion', 'tulips', 'tulips', 'sunflowers', 'tulips', 'tulips', 'daisy', 'tulips', 'roses', 'roses', 'daisy', 'roses', 'daisy', 'dandelion', 'daisy', 'sunflowers', 'dandelion', 'tulips', 'roses', 'daisy', 'sunflowers', 'sunflowers', 'tulips', 'daisy', 'daisy', 'dandelion', 'daisy', 'tulips', 'roses', 'tulips', 'daisy', 'dandelion', 'dandelion', 'dandelion', 'tulips', 'roses', 'roses', 'roses', 'dandelion', 'tulips', 'daisy', 'dandelion', 'roses', 'roses', 'roses', 'dandelion', 'sunflowers', 'tulips', 'dandelion', 'dandelion', 'tulips', 'roses', 'roses', 'sunflowers', 'dandelion', 'roses', 'dandelion', 'daisy', 'daisy', 'dandelion', 'roses', 'roses', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'sunflowers', 'tulips', 'roses', 'tulips', 'dandelion', 'roses', 'dandelion', 'roses', 'roses', 'sunflowers', 'dandelion', 'dandelion', 'dandelion', 'tulips', 'tulips']
[val_loss, val_acc] [0.10869703441858292, 0.956250011920929]

Save the model

In [14]:
model.save('model.h5')
/usr/local/lib/python3.7/dist-packages/keras/engine/functional.py:1410: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
  layer_config = serialize_layer_fn(layer)

Reload the model

In [15]:
reload_model = tf.keras.models.load_model('model.h5')

predictions = reload_model.predict(some_flowers, batch_size=16)
evaluations = reload_model.evaluate(some_flowers, some_labels, batch_size=16)
print(np.array(CLASSES)[np.argmax(predictions, axis=-1)].tolist())
print('[val_loss, val_acc]', evaluations)
display_9_images_with_predictions(some_flowers, predictions, some_labels)
10/10 [==============================] - 7s 619ms/step - loss: 0.1120 - accuracy: 0.9563
['tulips', 'tulips', 'sunflowers', 'sunflowers', 'roses', 'roses', 'tulips', 'dandelion', 'sunflowers', 'daisy', 'roses', 'tulips', 'dandelion', 'daisy', 'roses', 'daisy', 'tulips', 'tulips', 'daisy', 'tulips', 'tulips', 'tulips', 'sunflowers', 'roses', 'sunflowers', 'daisy', 'daisy', 'dandelion', 'dandelion', 'dandelion', 'sunflowers', 'daisy', 'roses', 'sunflowers', 'dandelion', 'tulips', 'tulips', 'roses', 'tulips', 'dandelion', 'daisy', 'dandelion', 'dandelion', 'sunflowers', 'tulips', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'daisy', 'sunflowers', 'daisy', 'roses', 'tulips', 'tulips', 'roses', 'sunflowers', 'sunflowers', 'daisy', 'roses', 'sunflowers', 'tulips', 'tulips', 'daisy', 'daisy', 'dandelion', 'roses', 'roses', 'tulips', 'sunflowers', 'dandelion', 'daisy', 'sunflowers', 'dandelion', 'dandelion', 'sunflowers', 'sunflowers', 'sunflowers', 'dandelion', 'tulips', 'tulips', 'sunflowers', 'tulips', 'tulips', 'daisy', 'tulips', 'roses', 'roses', 'daisy', 'roses', 'daisy', 'dandelion', 'daisy', 'sunflowers', 'dandelion', 'tulips', 'roses', 'daisy', 'sunflowers', 'sunflowers', 'tulips', 'daisy', 'daisy', 'dandelion', 'daisy', 'tulips', 'roses', 'tulips', 'daisy', 'dandelion', 'dandelion', 'dandelion', 'tulips', 'roses', 'roses', 'roses', 'dandelion', 'tulips', 'daisy', 'dandelion', 'roses', 'roses', 'roses', 'dandelion', 'sunflowers', 'tulips', 'dandelion', 'dandelion', 'tulips', 'roses', 'roses', 'sunflowers', 'dandelion', 'roses', 'dandelion', 'daisy', 'daisy', 'dandelion', 'roses', 'roses', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'dandelion', 'sunflowers', 'tulips', 'roses', 'tulips', 'dandelion', 'roses', 'dandelion', 'roses', 'roses', 'sunflowers', 'dandelion', 'dandelion', 'dandelion', 'tulips', 'tulips']
[val_loss, val_acc] [0.11197922378778458, 0.956250011920929]
In [ ]: