Я пытаюсь реализовать U-Net с Keras с бэкэндом Tensorflow для задачи сегментации изображения. У меня есть изображения размером (128,96) в качестве входных данных для сети вместе с изображениями маски размера (12288,6), поскольку они сплющены. У меня есть 6 различных классов (0-5), которые дают вторую часть формы изображений маски. Они были закодированы в горячие метки с помощью функции to_categorical (). На данный момент я использую только одно входное изображение, а также то же самое, что и данные проверки и проверки.

Я хотел бы, чтобы U-Net выполнял сегментацию изображения, где класс 0 соответствует фону. Когда я сейчас тренирую свою сеть U-Net только в течение нескольких эпох (1–10), результирующее предсказанное изображение маски, кажется, просто дает случайные классы для каждого пикселя. Когда я тренирую сеть дольше (более 50 эпох), все пиксели классифицируются как фон. Поскольку я тренируюсь и тестирую с использованием одного и того же изображения, я нахожу это очень странным, так как я ускорял работу сети. Как я могу решить эту проблему? Может ли быть что-то не так с тем, как я передаю в сеть изображения маски и реальные изображения?

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

Ниже приведен код моей сети. Он основан на U-Net, взятом из этого репозитория. Мне удалось подготовить его для случая с двумя классами с хорошими результатами, но я не знаю, как расширить его на большее количество классов сейчас.

def get_unet(self):

    inputs = Input((128, 96,1))
    #Input shape=(?,128,96,1)

    conv1 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
      kernel_initializer = 'he_normal', input_shape=(None,128,96,6))(inputs)
    #Conv1 shape=(?,128,96,64)
    conv1 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
          kernel_initializer = 'he_normal')(conv1)
    #Conv1 shape=(?,128,96,64)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
    #pool1 shape=(?,64,48,64)


    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(pool1)
    #Conv2 shape=(?,64,48,128)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv2)
    #Conv2 shape=(?,64,48,128)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
    #Pool2 shape=(?,32,24,128)

    conv5 = Conv2D(256, (3,3), activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(pool2)
    conv5 = Conv2D(256, (3,3), activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv5)

    up8 = Conv2D(128, 2, activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv5))
    merge8 = concatenate([conv2,up8], axis = 3)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv8)


    up9 = Conv2D(64, (2,2), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
    merge9 = concatenate([conv1,up9], axis = 3)
    conv9 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(merge9)
    conv9 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(conv9)
    conv9 = Conv2D(6, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(conv9)

    conv10 = Conv2D(6, (1,1), activation = 'sigmoid')(conv9)
    conv10 = Reshape((128*96,6))(conv10)

    model = Model(input = inputs, output = conv10)
    model.compile(optimizer = Adam(lr = 1e-5), loss = 'binary_crossentropy',
          metrics = ['accuracy'])

    return model

Кто-нибудь может указать, что не так с моей моделью?

1
Jasmin 29 Авг 2017 в 15:40

3 ответа

Лучший ответ

По моему опыту, также с U-net для сегментации. Это имеет тенденцию делать это:

  • Перейти к полностью черному или полностью белому
  • Через много времени, когда потеря кажется замороженной, она находит свой путь.

Я также использую метод "тренируй только одно изображение", чтобы найти эту конвергенцию, затем добавлю другие изображения.

Но мне приходилось пробовать много раз, и единственный раз, когда он работал довольно быстро, это когда я использовал:

  • окончательная активация = сигмовидная
  • потеря = 'двоичная_кросентропия'

Но я нигде не использовал "relu" ... возможно, это немного влияет на скорость сходимости ...? Думая о «relu», который имеет только 0 или положительные результаты, в этой функции есть большая область, которая не имеет градиента. Может быть, наличие большого количества активаций "relu" создает много "плоских" областей без градиентов? (Надо подумать, чтобы подтвердить это)

Попробуйте несколько раз (и имейте терпение ждать много-много эпох) с различными инициализациями веса.

Существует вероятность того, что ваша скорость обучения слишком велика.


О to_categorical(): вы пытались нанести / напечатать свои маски? Они действительно похожи на то, что вы ожидаете от них?

1
Daniel Möller 31 Авг 2017 в 16:26

Спасибо @Daniel, ваши предложения помогли мне в конце концов заставить работать Unet. Мне удалось получить результаты, которые не просто классифицировали все изображение как фон при работе более 500 эпох. Кроме того, вместо использования kernel_initializer='he_normal', kernel_initializer='zeros' или kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.07) работали для меня. Я использовал функцию активации 'sigmoid' и loss='binary_crossentropy'. Я сохранил активацию 'relu' для всех скрытых сверточных слоев. Я заметил, что моя сеть иногда застревает в локальном минимуме, где потери больше не улучшаются, поэтому мне нужно перезапустить.

2
Jasmin 31 Авг 2017 в 09:30

Я не вижу ваш слой предсказания, который, насколько я знаю, должен быть плотным слоем, а не сверточным слоем. Может быть, это твоя проблема.

1
Florian 29 Авг 2017 в 12:57