гитхаб: https://github.com/sarangzambare/segmentation
Tensorflow 2.0 здесь, сессия ушла. Этот проект использует tensorflow 2.0 для обучения модели сверточной сегментации, состоящей из слоев пониженной и повышающей дискретизации.
Входное изображение Сегментированное изображение, анимированное по эпохам.
Семантическая сегментация – это процесс присвоения каждому пикселю данного изображения определенного класса для лучшего понимания изображения или для дальнейшей обработки.
Проблема
Эта задача является частью вызова 2D-сегментации ISBI 2012. Мы имеем дело с набором из 30 последовательных изображений (512 × 512 пикселей) из серийного набора данных трансмиссионной электронной микроскопии (ssTEM) вентрального нервного шнура личинки первого возраста дрозофилы (VNC; Cardona et al., 2010). Цель состоит в том, чтобы присвоить каждому пикселю либо 0 для принадлежности к границе между нейронами, либо 1 для принадлежности к внутренней части клетки, в результате чего получается бинарное изображение с черным для границ ячейки и белым. для других областей.
Обнаружение границ является сложной задачей, поскольку многие границы выглядят нечеткими и неоднозначными. Кроме того, должны быть обнаружены только границы между нейритами, а границы внутриклеточных органелл, таких как митохондрии и синаптические везикулы, следует игнорировать.
Подход :
Идея состоит в том, чтобы иметь архитектуру нейронной сети, состоящую из:
- Слои пониженной дискретизации: которые будут отвечать за извлечение признаков из изображения. Эти слои представляют собой обычные сверточные слои, а также слои максимального объединения для понижения дискретизации изображения. Думайте об этом как о проецировании изображения в пространство более низкого измерения.
- Слои повышающей дискретизации: эти слои преобразуют свои входные данные в представления более высокого измерения. Для изображений это можно сделать с помощью транспонированной свертки, которую можно рассматривать как обратный способ выполнения операции свертки (ну, не совсем так). Разницу между нормальной сверткой и транспонированной сверткой можно хорошо понять по картинкам ниже от Vincent Dumoulin, Francesco Visin:
Анимации свертки
Синие карты — это входные данные, а голубые — выходные
Без заполнения, без шаговПроизвольное дополнение, без шаговОтступы, шаги
Транспонированные анимации свертки (повышение дискретизации)
Синие карты — это входные данные, а голубые — выходные
Без отступов, без шагов, транспонированные Произвольные отступы, без шагов, транспонированныеЗаполнение, шаги, транспонированные
Основная идея здесь состоит в том, чтобы спроектировать dnn, который кодирует признаки, используя слои пониженной дискретизации, а затем генерирует сегментированные изображения, используя слои повышающей дискретизации.
Это контролируемый процесс, и для каждого входного изображения метка представляет собой изображение в градациях серого, состоящее из черных границ ячеек и белых внутренних частей ячейки.
Модель:
Наряду с серией слоев пониженной и повышенной дискретизации модель также имеет пропускные соединения. Пропускные соединения — это соединения между активациями двух слоев, которые не являются последовательными. Такие межуровневые связи помогают в тех случаях, когда может быть необходимо «запомнить» извлеченные функции на каждом уровне, чтобы перекомпоновать изображение из пространства более низкого измерения. Эти соединения никоим образом не являются ограничительными, потому что вес, придаваемый таким соединениям, выучивается, и если архитектура не требует таких соединений, тогда веса будут просто выучены равными нулю.
Ниже приводится краткое описание используемой модели:
def segmentator_model(input_shape):
x_inputs = Input(input_shape) x = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x_inputs) x = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x_skip_4 = x
x = MaxPooling2D(pool_size=(2, 2))(x) x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x_skip_3 = x
x = MaxPooling2D(pool_size=(2, 2))(x) x = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x_skip_2 = x
x = MaxPooling2D(pool_size=(2, 2))(x) x = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Dropout(0.5)(x) x_skip_1 = x
x = MaxPooling2D(pool_size=(2, 2))(x) x = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Dropout(0.5)(x)
x = Conv2DTranspose(filters=512,kernel_size=2,activation='relu',padding='same',strides=2,kernel_initializer='he_normal')(x) x = concatenate([x_skip_1,x], axis = 3) x = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)
x = Conv2DTranspose(filters=256,kernel_size=2,activation='relu',padding='same',strides=2,kernel_initializer='he_normal')(x) x = concatenate([x_skip_2,x], axis = 3) x = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)
x = Conv2DTranspose(filters=256,kernel_size=2,activation='relu',padding='same',strides=2,kernel_initializer='he_normal')(x) x = concatenate([x_skip_3,x], axis = 3) x = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)
x = Conv2DTranspose(filters=128,kernel_size=2,activation='relu',padding='same',strides=2,kernel_initializer='he_normal')(x) x = concatenate([x_skip_4,x], axis = 3) x = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x) x = Conv2D(1, 1, activation = 'sigmoid', padding = 'same', kernel_initializer = 'he_normal')(x)
return tf.keras.Model(inputs=x_inputs,outputs=x)
Model: "model" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) [(None, 256, 256, 1) 0 __________________________________________________________________________________________________ conv2d (Conv2D) (None, 256, 256, 64) 640 input_1[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, 256, 256, 64) 36928 conv2d[0][0] __________________________________________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 128, 128, 64) 0 conv2d_1[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, 128, 128, 128 73856 max_pooling2d[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, 128, 128, 128 147584 conv2d_2[0][0] __________________________________________________________________________________________________ max_pooling2d_1 (MaxPooling2D) (None, 64, 64, 128) 0 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, 64, 64, 256) 295168 max_pooling2d_1[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, 64, 64, 256) 590080 conv2d_4[0][0] __________________________________________________________________________________________________ max_pooling2d_2 (MaxPooling2D) (None, 32, 32, 256) 0 conv2d_5[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, 32, 32, 512) 1180160 max_pooling2d_2[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, 32, 32, 512) 2359808 conv2d_6[0][0] __________________________________________________________________________________________________ dropout (Dropout) (None, 32, 32, 512) 0 conv2d_7[0][0] __________________________________________________________________________________________________ max_pooling2d_3 (MaxPooling2D) (None, 16, 16, 512) 0 dropout[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, 16, 16, 1024) 4719616 max_pooling2d_3[0][0] __________________________________________________________________________________________________ conv2d_9 (Conv2D) (None, 16, 16, 1024) 9438208 conv2d_8[0][0] __________________________________________________________________________________________________ dropout_1 (Dropout) (None, 16, 16, 1024) 0 conv2d_9[0][0] __________________________________________________________________________________________________ conv2d_transpose (Conv2DTranspo (None, 32, 32, 512) 2097664 dropout_1[0][0] __________________________________________________________________________________________________ concatenate (Concatenate) (None, 32, 32, 1024) 0 dropout[0][0] conv2d_transpose[0][0] __________________________________________________________________________________________________ conv2d_10 (Conv2D) (None, 32, 32, 512) 2097664 concatenate[0][0] __________________________________________________________________________________________________ conv2d_11 (Conv2D) (None, 32, 32, 512) 1049088 conv2d_10[0][0] __________________________________________________________________________________________________ conv2d_transpose_1 (Conv2DTrans (None, 64, 64, 256) 524544 conv2d_11[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, 64, 64, 512) 0 conv2d_5[0][0] conv2d_transpose_1[0][0] __________________________________________________________________________________________________ conv2d_12 (Conv2D) (None, 64, 64, 256) 524544 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_13 (Conv2D) (None, 64, 64, 256) 262400 conv2d_12[0][0] __________________________________________________________________________________________________ conv2d_transpose_2 (Conv2DTrans (None, 128, 128, 256 262400 conv2d_13[0][0] __________________________________________________________________________________________________ concatenate_2 (Concatenate) (None, 128, 128, 384 0 conv2d_3[0][0] conv2d_transpose_2[0][0] __________________________________________________________________________________________________ conv2d_14 (Conv2D) (None, 128, 128, 128 196736 concatenate_2[0][0] __________________________________________________________________________________________________ conv2d_15 (Conv2D) (None, 128, 128, 128 65664 conv2d_14[0][0] __________________________________________________________________________________________________ conv2d_transpose_3 (Conv2DTrans (None, 256, 256, 128 65664 conv2d_15[0][0] __________________________________________________________________________________________________ concatenate_3 (Concatenate) (None, 256, 256, 192 0 conv2d_1[0][0] conv2d_transpose_3[0][0] __________________________________________________________________________________________________ conv2d_16 (Conv2D) (None, 256, 256, 64) 49216 concatenate_3[0][0] __________________________________________________________________________________________________ conv2d_17 (Conv2D) (None, 256, 256, 64) 16448 conv2d_16[0][0] __________________________________________________________________________________________________ conv2d_18 (Conv2D) (None, 256, 256, 64) 16448 conv2d_17[0][0] __________________________________________________________________________________________________ conv2d_19 (Conv2D) (None, 256, 256, 1) 65 conv2d_18[0][0] ================================================================================================== Total params: 26,070,593 Trainable params: 26,070,593 Non-trainable params: 0 __________________________________________________________________________________________________
Обратите внимание, что все «конкатенированные» слои являются пропускными соединениями.
Обучение :
Набор данных состоит всего из 30 обучающих изображений. Этого недостаточно для обучения глубокой нейронной сети. Следовательно, мы увеличиваем данные, используя такие преобразования, как зеркальное отображение, сдвиг, вращение и т. д. Обо всех этих преобразованиях прекрасно заботится встроенная функция keras под названием «ImageDataGenerator». Эта функция дает нам объект генератора Python, из которого мы можем бесконечно извлекать преобразованные изображения. Об этом процессе заботится модуль data_pre.py.
Прежде чем изображения будут загружены в сеть, их размер изменяется до (256 256), чтобы уменьшить вычислительные затраты. Ниже приведен список параметров, используемых для обучения:
- Шагов за эпоху = 1000
- Размер партии = 2 изображения на шаг
- Количество эпох = 10
- Потеря: бинарная кросс-энтропия
- Оптимизатор: Адам
- Скорость обучения: 1e-4 (без затухания)
Обучение 10 эпох с вышеописанной конфигурацией заняло около 30 часов на процессоре Intel Core i5 с частотой 1,4 ГГц.
Достигнутая точность составила: 96% на тренировочном наборе, 92% на тестовом наборе.
Результаты :
Входное изображение Сегментированное изображение
Анимация тренировочного процесса (обратите внимание, как исчезают органеллы):
Входное изображение Сегментированное изображение по мере продвижения эпох