гитхаб: 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 для принадлежности к внутренней части клетки, в результате чего получается бинарное изображение с черным для границ ячейки и белым. для других областей.

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

Подход :

Идея состоит в том, чтобы иметь архитектуру нейронной сети, состоящую из:

  1. Слои пониженной дискретизации: которые будут отвечать за извлечение признаков из изображения. Эти слои представляют собой обычные сверточные слои, а также слои максимального объединения для понижения дискретизации изображения. Думайте об этом как о проецировании изображения в пространство более низкого измерения.
  2. Слои повышающей дискретизации: эти слои преобразуют свои входные данные в представления более высокого измерения. Для изображений это можно сделать с помощью транспонированной свертки, которую можно рассматривать как обратный способ выполнения операции свертки (ну, не совсем так). Разницу между нормальной сверткой и транспонированной сверткой можно хорошо понять по картинкам ниже от 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% на тестовом наборе.

Результаты :

Входное изображение Сегментированное изображение

Анимация тренировочного процесса (обратите внимание, как исчезают органеллы):

Входное изображение Сегментированное изображение по мере продвижения эпох

Использованная литература :

  1. Вызов 2D-сегментации ISBI 2012
  2. ВНЦ; Кардона и др., Краудсорсинг создания алгоритмов сегментации изображений для коннектомики 2010
  3. Венсан Дюмулен, Франческо Визин