I am trying to train the default Squeezenet implementation on the HAM10000 skin lesion database and my model is constantly guessing nothing but nv. If you look at the database, you will see that there are significantly more nv lesions than any other class. After my test/train split, I acquired these numbers: {0: 271, 1: 412, 2: 869, 3: 88, 4: 899, 5: 5367, 6: 119}
for my training dataset. 5
here represents nv. I have tried several different loss functions (categorical_crossentropy, categorical_hinge), several different optimizers (sgd, adam), and several different methods of implementing class_weights (total/count, 5367/count, log(5367/count), as well as manually setting the weight of 5
to be lower than 1). After all of this, I either get nonstop nv guesses, or I weight nv really low and get sub-guessing accuracy percentages. I'm running out of ideas and am wondering what else I can do. I've also considered redoing my test/train split so that the count of each class in test is uniform, but I'm afraid that would take a lot of time only to not work.
Here is the code for the model
img_input = Input(shape=input_shape)
x = Conv2D(64, (3, 3), strides=(2, 2), padding='valid', name='conv1')(img_input)
x = Activation('relu', name='relu_conv1')(x)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool1')(x)
x = fire_module(x, fire_id=2, squeeze=16, expand=64)
x = fire_module(x, fire_id=3, squeeze=16, expand=64)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool3')(x)
x = fire_module(x, fire_id=4, squeeze=32, expand=128)
x = fire_module(x, fire_id=5, squeeze=32, expand=128)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool5')(x)
x = fire_module(x, fire_id=6, squeeze=48, expand=192)
x = fire_module(x, fire_id=7, squeeze=48, expand=192)
x = fire_module(x, fire_id=8, squeeze=64, expand=256)
x = fire_module(x, fire_id=9, squeeze=64, expand=256)
x = Dropout(0.5, name='drop9')(x)
x = Conv2D(7, (1, 1), padding='valid', name='conv10')(x) #uses classes
x = Activation('softmax', name='loss')(x)
x = GlobalAveragePooling2D(data_format='channels_last')(x)
inputs = img_input
model = Model(inputs, x, name='squeezenet')
And here is the code I used for the run
np.random.seed(333)
train_data_dir = 'data/imgs/train'
validation_data_dir = 'data/imgs/test'
nb_train_samples = 8025
nb_validation_samples = 1990
epochs = 100 #todo change
batch_size = 32
img_width = 600
img_height = 450
class_weight = {0: 2.99, 1: 2.56, 2: 1.82, 3: 4.11, 4: 1.79, 5: 0.3, 6: 3.8} #this is the class weight for my most recent run, which is currently oscillating between always guessing nv and <15% accuracy
if K.image_data_format() == 'channels_first':
input_shape = (3, img_width, img_height)
else:
input_shape = (img_width, img_height, 3)
name = 'Squeezenet'
model = initModel(name, input_shape) # This gets the model from the above code snippit
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
train_datagen = ImageDataGenerator(rescale=1. / 255
,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
train_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
validation_generator = test_datagen.flow_from_directory(
validation_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
history = model.fit_generator(
train_generator,
steps_per_epoch=nb_train_samples // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=nb_validation_samples // batch_size,
class_weight=class_weight)
model.save('models/{0}_cat.h5'.format(name))
Change the order of your final layers. Change from:
x = Activation('softmax', name='loss')(x)
x = GlobalAveragePooling2D(data_format='channels_last')(x)
to
x = GlobalAveragePooling2D(data_format='channels_last')(x)
x = Activation('softmax', name='loss')(x)