Мы используем изображения глаз в оттенках серого и основную правду радужной оболочки глаза, чтобы обучить CNN сегментации радужной оболочки. Наш набор данных содержит около 2000 изображений с соответствующими масками, оба имеют размер 224x224.
Мы использовали архитектуру UNET, как показано в коде ниже. Чтобы убедиться, что наша сеть работает, мы пытаемся приспособить сеть к 30 изображениям нашего набора данных.
Мы использовали функцию потери игральных костей (mean_iou было около 0,80), но при тестировании на изображениях поездов результаты были плохими. Он показал намного больше белых пикселей, чем основную правду. Мы попробовали несколько оптимизаторов (Adam, SGD, RMsprop) без существенных различий.
Мы удалили функцию активации в двух последних слоях Conv2D, что улучшило mean_iou и потерю костей, но у нас все еще была та же проблема пятен белых пикселей (ошибочно предсказывает наличие белых пикселей), но это было меньше, чем раньше.
Затем мы использовали функцию потерь Тверского для устранения ложных срабатываний (пробуя несколько значений альфа и бета). Результаты улучшились но при тестировании обучающих изображений сетевые прогнозы не были точными.
Мы также добавили обратный вызов, чтобы уменьшить скорость обучения, когда потеря не меняется, что немного улучшило результаты.
При выполнении предыдущих запусков мы пытались бежать по 50 эпох за раз, и мы бы достигли точки, когда потери больше не уменьшаются примерно на (0,1) и mean_iou = 0,9. Независимо от того, сколько эпох, ни лучше, ни только колебания. Скорость обучения была низкой (0,00001), и обратный вызов уменьшил бы ее до 1x10 ^ -8, но все равно больше не уменьшал бы потери.
Если кто-то имеет опыт в этом или может дать нам какое-либо представление о том, как преодолеть эта проблема, помощь будет оценена.
def conv2d_block(input_tensor, n_filters, kernel_size=3, batchnorm=True):
# first layer
x = Conv2D(filters=n_filters, kernel_size=(kernel_size, kernel_size), kernel_initializer="he_normal",
padding="same")(input_tensor)
if batchnorm:
x = BatchNormalization()(x)
x = Activation("relu")(x)
# second layer
x = Conv2D(filters=n_filters, kernel_size=(kernel_size, kernel_size), kernel_initializer="he_normal",
padding="same")(x)
if batchnorm:
x = BatchNormalization()(x)
x = Activation("relu")(x)
return x
def get_unet(input_img, n_filters=16, dropout=0.5, batchnorm=False):
# contracting path
c1 = conv2d_block(input_img, n_filters=n_filters*1, kernel_size=3, batchnorm=batchnorm)
p1 = MaxPooling2D((2, 2)) (c1)
p1 = Dropout(dropout*0.5)(p1)
c2 = conv2d_block(p1, n_filters=n_filters*2, kernel_size=3, batchnorm=batchnorm)
p2 = MaxPooling2D((2, 2)) (c2)
p2 = Dropout(dropout)(p2)
c3 = conv2d_block(p2, n_filters=n_filters*4, kernel_size=3, batchnorm=batchnorm)
p3 = MaxPooling2D((2, 2)) (c3)
p3 = Dropout(dropout)(p3)
c4 = conv2d_block(p3, n_filters=n_filters*8, kernel_size=3, batchnorm=batchnorm)
p4 = MaxPooling2D(pool_size=(2, 2)) (c4)
p4 = Dropout(dropout)(p4)
c5 = conv2d_block(p4, n_filters=n_filters*16, kernel_size=3, batchnorm=batchnorm)
# expansive path
u6 = Conv2DTranspose(n_filters*8, (3, 3), strides=(2, 2), padding='same') (c5)
u6 = concatenate([u6, c4])
u6 = Dropout(dropout)(u6)
c6 = conv2d_block(u6, n_filters=n_filters*8, kernel_size=3, batchnorm=batchnorm)
u7 = Conv2DTranspose(n_filters*4, (3, 3), strides=(2, 2), padding='same') (c6)
u7 = concatenate([u7, c3])
u7 = Dropout(dropout)(u7)
c7 = conv2d_block(u7, n_filters=n_filters*4, kernel_size=3, batchnorm=batchnorm)
u8 = Conv2DTranspose(n_filters*2, (3, 3), strides=(2, 2), padding='same') (c7)
u8 = concatenate([u8, c2])
u8 = Dropout(dropout)(u8)
c8 = conv2d_block(u8, n_filters=n_filters*2, kernel_size=3, batchnorm=False)
u9 = Conv2DTranspose(n_filters*1, (3, 3), strides=(2, 2), padding='same') (c8)
u9 = concatenate([u9, c1], axis=3)
u9 = Dropout(dropout)(u9)
c9 = conv2d_block(u9, n_filters=n_filters*1, kernel_size=3, batchnorm=False)
outputs = Conv2D(1, (1, 1), activation='sigmoid') (c9)
model = Model(inputs=[input_img], outputs=[outputs])
return model