Странные результаты при тренировках с керасом - PullRequest
1 голос
/ 03 мая 2019

Я пытаюсь обучить модель unet на наборе braTS18 (медицинские данные с изображениями nifiti) с использованием кера с тензорным потоком.Однако я получаю очень странные результаты:

enter image description here

enter image description here

, как вы можете видеть, точность начинается с 96% и достигает 99% в третьей эпохе.Также потеря проверки не снижается никогда.Также обучаемая модель ничего не предсказывает.

Я разбил данные по-разному (20% обучают на 60%, или 60% обучают на 20%), но не сработали.Я думаю, что проблема может быть с моей моделью или с генератором данных.Вот коды:

unet model

def unet_model(filters=16, dropout=0.1, batch_normalize=True):

    # Build U-Net model
    inputs = Input((img_height, img_width, img_channels), name='main_input')
    s = Lambda(lambda x: x / 255) (inputs)

    c1 = Conv2D(filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c1') (s)
    c1 = Dropout(0.1) (c1)
    c1 = Conv2D(filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c1_d') (c1)
    p1 = MaxPooling2D((2, 2)) (c1)

    c2 = Conv2D(2*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c2') (p1)
    c2 = Dropout(0.1) (c2)
    c2 = Conv2D(2*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c2_d') (c2)
    p2 = MaxPooling2D((2, 2)) (c2)

    c3 = Conv2D(4*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c3') (p2)
    c3 = Dropout(0.2) (c3)
    c3 = Conv2D(4*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c3_d') (c3)
    p3 = MaxPooling2D((2, 2)) (c3)

    c4 = Conv2D(8*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c4') (p3)
    c4 = Dropout(0.2) (c4)
    c4 = Conv2D(8*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c4_d') (c4)
    p4 = MaxPooling2D(pool_size=(2, 2)) (c4)

    c5 = Conv2D(16*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c5') (p4)
    c5 = Dropout(0.3) (c5)
    c5 = Conv2D(16*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c5_d') (c5)

    u6 = Conv2DTranspose(8*filters, (2, 2), strides=(2, 2), padding='same', name = 'u6') (c5)
    u6 = concatenate([u6, c4])
    c6 = Conv2D(8*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c6') (u6)
    c6 = Dropout(0.2) (c6)
    c6 = Conv2D(8*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c6_d') (c6)

    u7 = Conv2DTranspose(4*filters, (2, 2), strides=(2, 2), padding='same', name = 'u7') (c6)
    u7 = concatenate([u7, c3])
    c7 = Conv2D(4*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c7') (u7)
    c7 = Dropout(0.2) (c7)
    c7 = Conv2D(4*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c7_d') (c7)

    u8 = Conv2DTranspose(2*filters, (2, 2), strides=(2, 2), padding='same', name = 'u8') (c7)
    u8 = concatenate([u8, c2])
    c8 = Conv2D(2*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c8') (u8)
    c8 = Dropout(0.1) (c8)
    c8 = Conv2D(2*filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c8_d') (c8)

    u9 = Conv2DTranspose(filters, (2, 2), strides=(2, 2), padding='same', name = 'u9') (c8)
    u9 = concatenate([u9, c1], axis=3)
    c9 = Conv2D(filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c9') (u9)
    c9 = Dropout(0.1) (c9)
    c9 = Conv2D(filters, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same', name = 'c9_d') (c9)

    outputs = Conv2D(1, (1, 1), activation='sigmoid', name = 'output') (c9)

    adam = optimizers.Adam(lr=lr, beta_1=beta1, decay=lr_decay, amsgrad=False)

    model = Model(inputs=[inputs], outputs=[outputs])
    model.compile(optimizer=adam, loss='binary_crossentropy', metrics=['accuracy',dice,jaccard])

    plot_model(model, to_file=os.path.join(save_dir +"model.png"))
    if os.path.exists(os.path.join(save_dir +"model.txt")):
        os.remove(os.path.join(save_dir +"model.txt"))
    with open(os.path.join(save_dir +"model.txt"),'w') as fh:
        model.summary(positions=[.3, .55, .67, 1.], print_fn=lambda x: fh.write(x + '\n'))

    model.summary()

    return model

и вот код для генератора данных:

def generate_data(X_data, Y_data, batch_size):

    samples_per_epoch = total_folders
    number_of_batches = samples_per_epoch/batch_size
    counter=0

    while True:

        X_batch = X_data[batch_size*counter:batch_size*(counter+1)]
        Y_batch = Y_data[batch_size*counter:batch_size*(counter+1)]

        counter += 1

        yield X_batch, Y_batch

        if counter >= number_of_batches:
            counter = 0
...
in the main function
...

if __name__ == "__main__":

    callbacks = [
    EarlyStopping(patience=1000, verbose=1),
    ReduceLROnPlateau(factor=0.1, patience=3, min_lr=0.00001, verbose=1),
    ModelCheckpoint(save_dir + 'model.{epoch:02d}-{val_loss:.2f}.h5', verbose=1, save_best_only=True, save_weights_only=True)
    ]

    model = unet_model(filters=16, dropout=0.05, batch_normalize=True)


    H = model.fit_generator(generate_data(X_train,Y_train,batch_size), 
                        epochs= epochs,
                        steps_per_epoch = total_folders/batch_size, 
                        validation_data=generate_data(X_test,Y_test,batch_size*2),
                        callbacks=callbacks,
                        validation_steps= total_folders/batch_size*2)

что я тут не так делаю?

1 Ответ

1 голос
/ 04 мая 2019

Я считаю, что ваша проблема заключается в вашей функции / метриках потерь. Если у большинства пациентов опухоли нет, а точность или расстояние по Жаккарту в равной степени относятся к обоим классам, ваша модель будет возвращать высокие значения точности и низкие значения индекса Жаккарда, просто говоря, что все в порядке / исправно. Вы можете проверить это, реализовав пользовательскую потерю, которая всегда возвращает метку класса для фона и сравнивает ее с текущими результатами. Чтобы решить вашу проблему, используйте что-то вроде расстояния jaccard, которое придает меньший вес фону. Обзор различных показателей, которые могут быть более подходящими, чем точность, можно найти здесь .

Кроме того, может быть, я не понимал набор данных, но не следует ли вам сегментировать различные виды опухолей и, следовательно, использовать категориальную вместо бинарной классификации?

...