Tensorflow / Keras: изменчивая потеря проверки - PullRequest
0 голосов
/ 06 февраля 2020

Я тренировал U- Net для сегментации небольших поражений одного класса, и постоянно получал неустойчивую потерю валидации. У меня есть около 20 тыс. Изображений, разделенных на 70/30 между обучающими и проверочными наборами, поэтому я не думаю, что проблема заключается в слишком малом количестве данных. Я несколько раз пытался перетасовать и разделять наборы без изменения волатильности, поэтому я не думаю, что набор валидации не является репрезентативным. Я пытался снизить скорость обучения без влияния на волатильность. И я попробовал несколько функций потерь (коэффициент кости, фокусное Тверски, взвешенная двойная кросс-энтропия). Я использую приличное количество дополнений, чтобы избежать переобучения. Я также проверил все свои данные (512x512 float64 с соответствующими масками 512x512 int64 - обе хранятся в виде массивов numpy), дважды проверяю диапазон значений, dtypes, et c. не шумиха ... и я даже удалил все области интереса в масках до 35 пикселей в области, которая, как я думал, может быть артефактом и портить потери.

Я использую keras ImageDataGen.flow_from_directory ... Первоначально я использовал zca_whitening и augmentation Bright_range, но я думаю, что это вызывает проблемы с flow_from_directory и потерей связи между маской и изображением ... поэтому я пропустил это.

Я пробовал генераторы проверки с использованием и без перемешивания = True. Размер партии - 8.

Вот мой код, с удовольствием добавлю больше, если это поможет:

# loss 

from keras.losses import binary_crossentropy
import keras.backend as K
import tensorflow as tf 

epsilon = 1e-5
smooth = 1

def dsc(y_true, y_pred):
    smooth = 1.
    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)
    intersection = K.sum(y_true_f * y_pred_f)
    score = (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
    return score

def dice_loss(y_true, y_pred):
    loss = 1 - dsc(y_true, y_pred)
    return loss

def bce_dice_loss(y_true, y_pred):
    loss = binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)
    return loss

def confusion(y_true, y_pred):
    smooth=1
    y_pred_pos = K.clip(y_pred, 0, 1)
    y_pred_neg = 1 - y_pred_pos
    y_pos = K.clip(y_true, 0, 1)
    y_neg = 1 - y_pos
    tp = K.sum(y_pos * y_pred_pos)
    fp = K.sum(y_neg * y_pred_pos)
    fn = K.sum(y_pos * y_pred_neg) 
    prec = (tp + smooth)/(tp+fp+smooth)
    recall = (tp+smooth)/(tp+fn+smooth)
    return prec, recall

def tp(y_true, y_pred):
    smooth = 1
    y_pred_pos = K.round(K.clip(y_pred, 0, 1))
    y_pos = K.round(K.clip(y_true, 0, 1))
    tp = (K.sum(y_pos * y_pred_pos) + smooth)/ (K.sum(y_pos) + smooth) 
    return tp 

def tn(y_true, y_pred):
    smooth = 1
    y_pred_pos = K.round(K.clip(y_pred, 0, 1))
    y_pred_neg = 1 - y_pred_pos
    y_pos = K.round(K.clip(y_true, 0, 1))
    y_neg = 1 - y_pos 
    tn = (K.sum(y_neg * y_pred_neg) + smooth) / (K.sum(y_neg) + smooth )
    return tn 

def tversky(y_true, y_pred):
    y_true_pos = K.flatten(y_true)
    y_pred_pos = K.flatten(y_pred)
    true_pos = K.sum(y_true_pos * y_pred_pos)
    false_neg = K.sum(y_true_pos * (1-y_pred_pos))
    false_pos = K.sum((1-y_true_pos)*y_pred_pos)
    alpha = 0.7
    return (true_pos + smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + smooth)

def tversky_loss(y_true, y_pred):
    return 1 - tversky(y_true,y_pred)

def focal_tversky(y_true,y_pred):
    pt_1 = tversky(y_true, y_pred)
    gamma = 0.75
    return K.pow((1-pt_1), gamma)



    model = BlockModel((len(os.listdir(os.path.join(imageroot,'train_ct','train'))), 512, 512, 1),filt_num=16,numBlocks=4)
    #model.compile(optimizer=Adam(learning_rate=0.001), loss=weighted_cross_entropy)
    #model.compile(optimizer=Adam(learning_rate=0.001), loss=dice_coef_loss)
    model.compile(optimizer=Adam(learning_rate=0.001), loss=focal_tversky)
    train_mask = os.path.join(imageroot,'train_masks')
    val_mask = os.path.join(imageroot,'val_masks')

    model.load_weights(model_weights_path) #I'm initializing with some pre-trained weights from a similar model

     data_gen_args_mask = dict(
        rotation_range=10,
        shear_range=20,
        width_shift_range=0.1, 
        height_shift_range=0.1,
        zoom_range=[0.8,1.2],
        horizontal_flip=True,
        #vertical_flip=True,
        fill_mode='nearest',
        data_format='channels_last'
        )

    data_gen_args = dict(
        **data_gen_args_mask
    )

    image_datagen_train = ImageDataGenerator(**data_gen_args)
    mask_datagen_train = ImageDataGenerator(**data_gen_args)#_mask)

    image_datagen_val = ImageDataGenerator()
    mask_datagen_val = ImageDataGenerator()

    seed = 1
    BS = 8

    steps = int(np.floor((len(os.listdir(os.path.join(train_ct,'train'))))/BS))
    print(steps)
    val_steps = int(np.floor((len(os.listdir(os.path.join(val_ct,'val'))))/BS))
    print(val_steps)

    train_image_generator = image_datagen_train.flow_from_directory(
         train_ct,
         target_size = (512, 512),
         color_mode = ("grayscale"),
         classes=None,
         class_mode=None,
         seed = seed,
         shuffle = True,
         batch_size = BS)

    train_mask_generator = mask_datagen_train.flow_from_directory(
        train_mask,
        target_size = (512, 512),
        color_mode = ("grayscale"),
        classes=None,
        class_mode=None,
        seed = seed,
        shuffle = True,
        batch_size = BS)

    val_image_generator = image_datagen_val.flow_from_directory(
        val_ct,
        target_size = (512, 512),
        color_mode = ("grayscale"),
        classes=None,
        class_mode=None,
        seed = seed,
        shuffle = True,
        batch_size = BS)

    val_mask_generator = mask_datagen_val.flow_from_directory(
        val_mask,
        target_size = (512, 512),
        color_mode = ("grayscale"),
        classes=None,
        class_mode=None,
        seed = seed,
        shuffle = True,
        batch_size = BS)

    train_generator = zip(train_image_generator, train_mask_generator)
    val_generator = zip(val_image_generator, val_mask_generator)

# make callback for checkpointing

    plot_losses = PlotLossesCallback(skip_first=0,plot_extrema=False)
    %matplotlib inline

    filepath = os.path.join(versionPath, model_version + "_saved-model-{epoch:02d}-{val_loss:.2f}.hdf5")

    if reduce:
        cb_check = [ModelCheckpoint(filepath,monitor='val_loss',
                        verbose=1,save_best_only=False,
                        save_weights_only=True,mode='auto',period=1),
                        reduce_lr,
                        plot_losses]
    else:
        cb_check = [ModelCheckpoint(filepath,monitor='val_loss',
                        verbose=1,save_best_only=False,
                        save_weights_only=True,mode='auto',period=1),
                        plot_losses]

# train model
    history = model.fit_generator(train_generator, epochs=numEp,
                    steps_per_epoch=steps,
                    validation_data=val_generator,
                    validation_steps=val_steps, 
                    verbose=1,
                    callbacks=cb_check,
                    use_multiprocessing = False
                             )

И вот как выглядит моя потеря:

enter image description here

Еще одна потенциально важная вещь: я немного подправил код flow_from_directory (добавил npy в белый список). Но потеря тренировок выглядит хорошо, поэтому, если проблема не в этом

1 Ответ

0 голосов
/ 12 февраля 2020

Два предложения:

  1. Переключиться на формат данных проверки classi c (т.е. массив numpy) вместо использования генератора - это гарантирует, что вы всегда будете использовать точно одни и те же данные проверки каждый раз. Если вы видите другую кривую валидации, то в генераторе валидации есть что-то «случайное», дающее вам разные данные в разные эпохи.

  2. Используйте фиксированный набор выборок (100 или 1000 должны хватит без всякого увеличения данных) как для обучения, так и для проверки. Если все идет хорошо, вы должны увидеть, как ваша сеть быстро переходит на этот набор данных, и ваши кривые обучения и проверки должны быть очень похожими. Если нет, отладьте вашу сеть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...