简体   繁体   中英

Keras CNN prediction output is only predicting within a range of numbers

I am trying to predict 3D medical brain images based on their assessment score using CNN, however the accuracy scores I have received were within a range of numbers (ex: there are 7 possible test scores: 1, 1.5, 2, 2.5, 3, 4, 5 and the output only gives predictions within 1-1.5 range)

I have resized, normalized, and separated the images into train (66 imgs), test (22 imgs), and validation (22 imgs) sets. Because there are so few images, I have added a custom 3D image augmentation (from github) so the total number of images increased to 10x what I originally had.

I have tried changing most, if not all, the parameters (batch size, optimizer, learning rate, simpler/complex nn's, activation, loss, etc) within my network to no avail. I have also looked online for similar problems in hope that someone has had the same issue and solved it.

Here is an example image that I am using:

图片说明

the size of this image is (96, 96, 96) and a strip of its array value is (after normalizing them):

[0.54124768 0.59549533 0.61464823 0.59833751 0.50441322 0.33578409
 0.40528049 0.4359369  0.39544678 0.32074109 0.20008253 0.28538722
 0.27870766 0.37098099 0.13504691 0.2372147  0.4171059  0.56398624
 0.38187722 0.71896363 0.44387385 0.41523819 0.31195372 0.10586056
 0.12634818 0.13454185 0.57811427 0.6465261  0.61814963 0.61493715]

After preprocessing steps, I fed this into my CNN model:

batch_size = 3

model = Sequential()

model.add(Conv3D(32, [3, 3, 3], padding='same', activation='relu',
                 input_shape=input_size))
model.add(Conv3D(32, [3, 3, 3], padding='same', activation='relu'))
model.add(MaxPooling3D(pool_size=(2, 2, 2), padding='same'))

model.add(Conv3D(64, [3, 3, 3], padding='same', activation='relu'))
model.add(Conv3D(64, [3, 3, 3], padding='same', activation='relu'))
model.add(MaxPooling3D(pool_size=(2, 2, 2), padding='same'))
model.add(Dropout(0.5))

model.add(Conv3D(128, [3, 3, 3], padding='same', activation='relu'))
model.add(Conv3D(128, [3, 3, 3], padding='same', activation='relu'))
model.add(Conv3D(128, [3, 3, 3], padding='same', activation='relu'))
model.add(MaxPooling3D(pool_size=(2, 2, 2), padding='same'))

model.add(Conv3D(256, [3, 3, 3], padding='same', activation='relu'))
model.add(Conv3D(256, [3, 3, 3], padding='same', activation='relu'))
model.add(Conv3D(256, [3, 3, 3], padding='same', activation='relu'))
model.add(MaxPooling3D(pool_size=(2, 2, 2), padding='same'))
model.add(Dropout(0.5))

model.add(Flatten())
model.add(Dense(1024, activation='relu'))
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='linear'))

opt = optimizers.Adam(lr=1e-6)
model.compile(loss='mean_squared_logarithmic_error', optimizer=opt, metrics=['accuracy'])

train_datagen = customImageDataGenerator(shear_range=0.2,
                                         zoom_range=0.2,
                                         horizontal_flip=True)

val_datagen = customImageDataGenerator()

test_datagen = customImageDataGenerator()



training_set = train_datagen.flow(x_train, y_train, batch_size=batch_size, shuffle=True)

validation_set = val_datagen.flow(x_val, y_val, batch_size=batch_size, shuffle=False)

testing_set = test_datagen.flow(x_test, y_test, batch_size=batch_size, shuffle=False)



earlystop = EarlyStopping(monitor='val_loss', patience=30)

history = model.fit(
                    training_set,
                    steps_per_epoch = len(x_train)//batch_size,
                    epochs = 50,
                    #callbacks = [earlystop],
                    validation_data = validation_set,
                    validation_steps = len(x_val)//batch_size
                    )

I created a custom accuracy check to visualize the outputs which are:

Predicted score: [1.8405123] True score: 3.0
Predicted score: [1.4033222] True score: 3.0
Predicted score: [1.4690828] True score: 1.0
Predicted score: [1.5127727] True score: 3.0
Predicted score: [1.6159409] True score: 1.0
Predicted score: [1.4333361] True score: 1.5
Predicted score: [1.7470968] True score: 3.0
Predicted score: [1.2196972] True score: 1.5
Predicted score: [1.5940914] True score: 4.0
Predicted score: [1.4052064] True score: 1.0
Predicted score: [1.5127727] True score: 1.0
Predicted score: [1.4584785] True score: 1.0
Predicted score: [1.7860543] True score: 3.0
Predicted score: [1.4752649] True score: 2.5
Predicted score: [1.8568267] True score: 1.0
Predicted score: [1.4793051] True score: 3.0
Predicted score: [1.395096] True score: 2.5
Predicted score: [1.6011616] True score: 4.0
Predicted score: [1.9094267] True score: 1.0
Predicted score: [1.6322718] True score: 1.0
Predicted score: [1.7284409] True score: 4.0
Predicted score: [1.5262214] True score: 1.5
Out: 0.09090909090909091

As you can see, the predicted values fall within the 1-2 range even though there are test scores at the 2.5, 3, 4, and 5 range.

print(y_pred.min(), y_pred.max())
1.2196972 1.9094267

Finally, here are my graphs:

在此处输入图像描述 在此处输入图像描述

As you can see, the loss decreases beautifully, but the accuracy freezes midway and I am not sure what the cause may be.

Sorry for the long post, but I would appreciate any answers, thank you!

There are several things that may help with the accuracy of your model.

  1. Add BatchNomalization after each Conv3D layer, it improves the convergence.
  2. Try scaling scores into a range [0, 1]. This is usually not a problem when you have enough data, but may become a problem if your dataset is small. This will also require a softmax activation in your last layer.
  3. Dropout of 0.5 may be a bit extreme. Try adding a smaller dropout after each Conv3D. It can be conveniently combined with proposition 1) using the following wrapper:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Layer, Conv3D, BatchNormalization, Dropout, InputLayer


class ConvBlock(Layer):
  def __init__(self, filters=32, kernel_size = [3, 3, 3], 
               padding='same', activation='relu', dropout=0.1):
    super(ConvBlock, self).__init__()
    self.conv = Conv3D(filters=filters, 
                       kernel_size=kernel_size, 
                       activation=activation, 
                       padding=padding)
    self.norm = BatchNormalization()
    self.dropout = Dropout(dropout)

  def call(self, input, training=False):
    x = self.conv(input)
    x = self.norm(x)
    x = self.dropout(x, training=training)
    return x
  
  
model = Sequential()

model.add(InputLayer((96, 96, 96, 1)))
model.add(ConvBlock())

model.summary()

Hope this helps to improve it at least a little bit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM