I am getting following error
AttributeError: 'list' object has no attribute 'dtype'
when running the following script which uses transfer learning in Keras to retrain and fine tune the last layer in the Inception V3 model. For my dataset I'm using Kaggle's Cats and Dogs: I'm still new to Keras so your help is much appreciated!
Here's the code:
import os
import sys
import glob
import argparse
import matplotlib.pyplot as plt
from keras import __version__
from keras.applications.inception_v3 import InceptionV3, preprocess_input
from keras.models import Model
from keras.layers import Dense, GlobalAveragePooling2D
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import SGD
IM_WIDTH, IM_HEIGHT = 299, 299 #fixed size for InceptionV3
EPOCHS = 3
BAT_SIZE = 32
FC_SIZE = 1024
NB_IV3_LAYERS_TO_FREEZE = 172
STEPS_PER_EPOCH = 780
def get_nb_files(directory):
"""Get number of files by searching directory recursively"""
if not os.path.exists(directory):
return 0
cnt = 0
for r, dirs, files in os.walk(directory):
for dr in dirs:
cnt += len(glob.glob(os.path.join(r, dr + "/*")))
return cnt
def setup_to_transfer_learn(model, base_model):
"""Freeze all layers and compile the model"""
for layer in base_model.layers:
layer.trainable = False
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
def add_new_last_layer(base_model, nb_classes):
"""Add last layer to the convnet
Args:
base_model: keras model excluding top
nb_classes: # of classes
Returns:
new keras model with last layer
"""
x = base_model.outputs
x = GlobalAveragePooling2D()(x)
x = Dense(FC_SIZE, activation='relu')(x) #new FC layer, random init
predictions = Dense(nb_classes, activation='softmax')(x) #new softmax layer
model = Model(inputs=base_model.inputs, outputs=predictions)
return model
def setup_to_finetune(model):
"""Freeze the bottom NB_IV3_LAYERS and retrain the remaining top layers.
note: NB_IV3_LAYERS corresponds to the top 2 inception blocks in the inceptionv3 arch
Args:
model: keras model
"""
for layer in model.layers[:NB_IV3_LAYERS_TO_FREEZE]:
layer.trainable = False
for layer in model.layers[NB_IV3_LAYERS_TO_FREEZE:]:
layer.trainable = True
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss='categorical_crossentropy', metrics=['accuracy'])
def train(args):
"""Use transfer learning and fine-tuning to train a network on a new dataset"""
# nb_train_samples = get_nb_files(args.train_dir) #RC: don't believe this is needed
nb_classes = len(glob.glob(args.train_dir + "/*"))
validation_steps = get_nb_files(args.val_dir) #RC: use the entire validation data set to evaluate error/loss
epochs = int(args.epochs)
batch_size = int(args.batch_size)
# data prep
train_datagen = ImageDataGenerator(
preprocessing_function=preprocess_input,
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(
preprocessing_function=preprocess_input,
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
# Takes the path to a directory, and generates batches of
# augmented/normalized data
train_generator = train_datagen.flow_from_directory(
args.train_dir,
# we specify the dimensions to which all images found will be resized
# see https://keras.io/preprocessing/image/
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
validation_generator = test_datagen.flow_from_directory(
args.val_dir,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
# setup model
# use the flag include_top=False to leave out the weights of the last fully
# connected layer since that is specific to the ImageNet competition, from
# which the weights were previously trained
base_model = InceptionV3(weights='imagenet', include_top=False) #include_top=False excludes final FC layer
model = add_new_last_layer(base_model, nb_classes)
# transfer learning
setup_to_transfer_learn(model, base_model)
history_tl = model.fit_generator(
train_generator,
epochs=epochs,
steps_per_epoch=STEPS_PER_EPOCH, # based on the original batch size of 32
# an epoch in training is 25,000. 25000/32 = 780
validation_data=validation_generator,
validation_steps=validation_steps,
class_weight='auto')
# fine-tuning
setup_to_finetune(model)
history_ft = model.fit_generator(
train_generator,
steps_per_epoch=STEPS_PER_EPOCH, # based on the original batch size of 32
epochs=epochs,
validation_data=validation_generator,
validation_steps=validation_steps,
class_weight='auto')
model.save(args.output_model_file)
if args.plot:
plot_training(history_ft)
def plot_training(history):
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'r.')
plt.plot(epochs, val_acc, 'r')
plt.title('Training and validation accuracy')
plt.figure()
plt.plot(epochs, loss, 'r.')
plt.plot(epochs, val_loss, 'r-')
plt.title('Training and validation loss')
plt.show()
if __name__=="__main__":
a = argparse.ArgumentParser()
a.add_argument("--train_dir")
a.add_argument("--val_dir")
a.add_argument("--epochs", default=EPOCHS)
a.add_argument("--batch_size", default=BAT_SIZE)
a.add_argument("--output_model_file", default="inceptionv3-ft.model")
a.add_argument("--plot", action="store_true")
args = a.parse_args()
if args.train_dir is None or args.val_dir is None:
a.print_help()
sys.exit(1)
if (not os.path.exists(args.train_dir)) or (not os.path.exists(args.val_dir)):
print("directories do not exist")
sys.exit(1)
train(args)`
and the error message:
Traceback (most recent call last):
File "transfer_learning_fine_tune_kerasV2.py", line 192, in <module>
train(args)
File "transfer_learning_fine_tune_kerasV2.py", line 125, in train
model = add_new_last_layer(base_model, nb_classes)
File "transfer_learning_fine_tune_kerasV2.py", line 56, in add_new_last_layer
x = GlobalAveragePooling2D()(x)
File "/Users/rchase/anaconda/lib/python3.6/site-packages/keras/engine/topology.py", line 603, in __call__
output = self.call(inputs, **kwargs)
File "/Users/rchase/anaconda/lib/python3.6/site-packages/keras/layers/pooling.py", line 536, in call
return K.mean(inputs, axis=[1, 2])
File "/Users/rchase/anaconda/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py", line 1338, in mean
if x.dtype.base_dtype == tf.bool:
AttributeError: 'list' object has no attribute 'dtype'
The problem is with the line x = base_model.outputs
. base_model.outputs
is a list containing the output tensors of base_model
, but GlobalAveragePooling2D
expects a single tensor as its input.
print(base_model.outputs)
[<tf.Tensor 'mixed10/concat:0' shape=(?, ?, ?, 2048) dtype=float32>]
print(base_model.output)
Tensor("mixed10/concat:0", shape=(?, ?, ?, 2048), dtype=float32)
You can either change the line into x = base_model.outputs[0]
or x = base_model.output
. Both should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With