MobileNet transfer learning in Keras for object localization extraction - loss computed as NaN

Viewed 1693

I'm trying to use Keras and its MobileNet implementation to do object localization (output the x/y coordinates of a few features, instead of classes) and I'm running into some likely very basic issue that I can't figure out.

My code looks like this:

# =============================
# Load MobileNet and change the top layers.
model = applications.MobileNet(weights="imagenet",
                               include_top=False,
                               input_shape=(224, 224, 3))

# Freeze all the layers except the very last 5.
for layer in model.layers[:-5]:
  layer.trainable = False

# Adding custom Layers at the end, after the last Conv2D layer.
x = model.output

x = GlobalAveragePooling2D()(x)
x = Reshape((1, 1, 1024))(x)
x = Dropout(0.5)(x)
x = Conv2D(1024, (1, 1), activation='relu', padding='same', name='conv_preds')(x)
x = Dense(1024, activation="relu")(x)

# I'd like this to output 4 variables, two pairs of x/y coordinates
x = Dense(PREDICT_SIZE, activation="sigmoid")(x)
predictions = Reshape((PREDICT_SIZE,))(x)

# =============================
# Create the new final model.
model_final = Model(input = model.input, output = predictions)

def custom_loss(y_true, y_pred):
  '''Trying to compute the Euclidian distance as a Loss Function'''
  return K.sqrt(K.sum(K.square(y_true - y_pred), axis=-1))

model_final.compile(loss = custom_loss,
                    optimizer = optimizers.adam(lr=0.0001),
                    metrics=["accuracy"])

With this model, then I load the data and try to train it.

x_train, y_train, x_val, y_val = load_data(DATASET_DIR)

# This load_data is my own implementation. It returns the images
# as tensors.
# ==> x_train[0].shape= (224, 224, 3)
#
# y_train and y_val look like this:
# ==> y_train[0]= [ 0.182  -0.0933  0.072  -0.0453]
#
# holding values in the [0, 1] interval for where the pixel
# is relative to the width/height of the image.
#
model_final.fit(x_train, y_train,
                batch_size=batch_size, epochs=5, shuffle=False,
                validation_data=(x_val, y_val))

Unfortunately, what I get when I run this model to train, I get something like this:

Train on 45 samples, validate on 5 samples
Epoch 1/5
16/45 [=========>....................] - ETA: 2s - loss: nan - acc: 0.0625
32/45 [====================>.........] - ETA: 1s - loss: nan - acc: 0.0312
45/45 [==============================] - 4s - loss: nan - acc: 0.0222 - val_loss: nan - val_acc: 0.0000e+00
Epoch 2/5
16/45 [=========>....................] - ETA: 2s - loss: nan - acc: 0.0625
32/45 [====================>.........] - ETA: 1s - loss: nan - acc: 0.0312
45/45 [==============================] - 4s - loss: nan - acc: 0.0222 - val_loss: nan - val_acc: 0.0000e+00
Epoch 3/5

I'm at a loss about why my loss value is "nan". I must be doing something wrong, and I've tried to change everything - the loss function, the shape of the output... but I can't figure out what I'm doing wrong.

Any help would be appreciated!

UPDATE: it seems like the issue is in the way I load_data.

If I create the image data like this it fails and results in loss:nan

i = pil_image.open(img_filename)
img = image.load_img(img_filename, target_size=(224, 224))

x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = keras.applications.mobilenet.preprocess_input(x)

x_train = np.append(x_train, x, axis=0)

but if I do something trivial like this, 'fit' works just fine and computes real values for loss:

x_train = np.random.random((100, 224, 224, 3))

sigh I wonder what's happening...

UPDATE #2: I figured out what the issue was

Documenting this here in case it helps anybody.

The way to properly generate the input tensors for MobileNet is this one:

test_img=[]
for i in range(len(test)):
    temp_img=image.load_img(test_path+test['filename'][i],target_size=(224,224))
    temp_img=image.img_to_array(temp_img)
    test_img.append(temp_img)

test_img=np.array(test_img) 
test_img=preprocess_input(test_img)

Notice how making it into a numpy.array and running preprocess_input happens on the whole batch of images. Doing it image by image seems to not have worked (what I was doing before).

Hope this helps somebody someday.

0 Answers
Related