Open titu1994 opened 7 years ago
Script used to create images (extracted from your script) :
import cv2
import numpy as np
import random
import copy
import keras.backend as K
from keras.layers import Activation, BatchNormalization, Conv2D, Dropout, concatenate
from keras.layers import Dense, AveragePooling2D, GlobalAveragePooling2D, MaxPooling2D
from keras.layers import UpSampling2D, Conv2DTranspose, Reshape
from keras.regularizers import l2
"""
Code for the DenseNet implementation based on:
https://github.com/titu1994/DenseNet/
[1] [Densely Connected Convolutional Networks](https://arxiv.org/pdf/1608.06993v3.pdf)
[2] [The One Hundred Layers Tiramisu: Fully Convolutional DenseNets for Semantic Segmentation](https://arxiv.org/pdf/1611.09326v2.pdf)
"""
def ConvBlock(x, n_filters, bottleneck=False, p=0, decay=1e-4):
if bottleneck:
x = BatchNormalization(gamma_regularizer=l2(decay), beta_regularizer=l2(decay))(x)
x = Activation('relu')(x)
x = Conv2D(n_filters * 4, 1, padding='same', use_bias=False,
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(x)
x = BatchNormalization(gamma_regularizer=l2(decay), beta_regularizer=l2(decay))(x)
x = Activation('relu')(x)
x = Conv2D(n_filters, 3, padding='same', use_bias=False,
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(x)
if p > 0:
x = Dropout(p)(x)
return x
def DenseBlock(x, n_layers, n_filters, growth_rate, p=0, decay=1e-4,
bottleneck=False, grow_nb_filters=True, return_concat_list=False):
x_list = [x]
for i in range(n_layers):
cb = ConvBlock(x, growth_rate, bottleneck, p, decay)
x_list.append(cb)
x = concatenate(x_list)
if grow_nb_filters:
n_filters += growth_rate
if return_concat_list:
return x, n_filters, x_list
else:
return x, n_filters
def TransitionBlock(x, n_filters, compression=1, p=0, decay=1e-4, kernel_sz=3):
x = BatchNormalization(gamma_regularizer=l2(decay), beta_regularizer=l2(decay))(x)
x = Activation('relu')(x)
x = Conv2D(int(n_filters * compression), kernel_sz, padding='same', use_bias=False,
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(x)
if p > 0:
x = Dropout(p)(x)
x = AveragePooling2D(pool_size=(2, 2), strides=(2, 2))(x)
return x
def TransitionUpBlock(x, n_filters, decay=1e-4, up_type='upsampling'):
if up_type == 'upsampling':
x = UpSampling2D()(x)
else:
x = Conv2DTranspose(n_filters, 3, padding='same', activation='relu',
strides=(2, 2), kernel_initializer='he_uniform')(x)
return x
def get_DenseNet(n_classes, img_input, depth=40, n_dense_blocks=3, growth_rate=12,
n_filters=-1, n_layers_per_block=-1, bottleneck=False, compression=1,
p=0, decay=1e-4, large_input=False, include_top=True, activation='softmax'):
assert (depth - 4) % 3 == 0, 'Depth must be 3 N + 4'
if compression != 0.0:
assert compression <= 1.0 and compression > 0.0, 'compression range needs to be between 0.0 and 1.0'
# layers in each dense block
if type(n_layers_per_block) is list or type(n_layers_per_block) is tuple:
nb_layers = list(n_layers_per_block) # Convert tuple to list
assert len(nb_layers) == (n_dense_blocks + 1), 'If list, nb_layer is used as provided. ' \
'Note that list size must be (nb_dense_block + 1)'
final_nb_layer = nb_layers[-1]
nb_layers = nb_layers[:-1]
else:
if n_layers_per_block == -1:
count = int((depth - 4) / 3)
nb_layers = [count for _ in range(n_dense_blocks)]
final_nb_layer = count
else:
final_nb_layer = n_layers_per_block
nb_layers = [n_layers_per_block] * n_dense_blocks
if bottleneck:
nb_layers = [int(layer // 2) for layer in nb_layers]
# compute initial nb_filter if -1, else accept users initial nb_filter
if n_filters <= 0:
n_filters = 2 * growth_rate
# Initial convolution
if large_input:
x = Conv2D(n_filters, 7, strides=(2, 2), padding='same', use_bias=False, name='initial_conv2D',
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(img_input)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='same')(x)
else:
x = Conv2D(n_filters, 3, padding='same', use_bias=False, name='initial_conv2D',
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(img_input)
# Add dense blocks
for block_idx in range(n_dense_blocks - 1):
x, n_filters = DenseBlock(x, nb_layers[block_idx], n_filters, growth_rate,
p=p, decay=decay, bottleneck=bottleneck)
# add transition_block
x = TransitionBlock(x, n_filters, compression=compression, p=p, decay=decay)
n_filters = int(n_filters * compression)
# The last dense_block does not have a transition_block
x, n_filters = DenseBlock(x, final_nb_layer, n_filters, growth_rate,
p=p, decay=decay, bottleneck=bottleneck)
# Final BN-RELU-POOL
x = BatchNormalization(gamma_regularizer=l2(decay), beta_regularizer=l2(decay))(x)
x = Activation('relu')(x)
x = GlobalAveragePooling2D()(x)
if include_top:
x = Dense(n_classes, activation=activation, W_regularizer=l2(decay), b_regularizer=l2(decay))(x)
return x
def get_FCN_DenseNet(img_input, n_classes=1, n_dense_blocks=5, growth_rate=16,
n_layers_per_block=4, compression=1, p=0, decay=1e-4,
input_shape=(224, 224, 3), init_conv_filters=48, include_top=True,
activation='softmax', upsampling_conv=128, upsampling_type='upsampling',
verbose=0):
upsampling_type = upsampling_type.lower()
if activation not in ['softmax', 'sigmoid']:
raise ValueError('activation must be either "softmax" or "sigmoid"')
if activation == 'sigmoid' and n_classes != 1:
raise ValueError('sigmoid activation can only be used when classes = 1')
if compression != 0.0:
assert compression <= 1.0 and compression > 0.0, 'compression range needs to be between 0.0 and 1.0'
# check if upsampling_conv has minimum number of filters
# minimum is set to 12, as at least 3 color channels are needed for correct upsampling
assert upsampling_conv > 12 and upsampling_conv % 4 == 0, 'Parameter `upsampling_conv` number of channels must ' \
'be a positive number divisible by 4 and greater ' \
'than 12'
# layers in each dense block
if type(n_layers_per_block) is list or type(n_layers_per_block) is tuple:
nb_layers = list(n_layers_per_block) # Convert tuple to list
assert len(nb_layers) == (n_dense_blocks + 1), 'If list, nb_layer is used as provided. ' \
'Note that list size must be (nb_dense_block + 1)'
bottleneck_nb_layers = nb_layers[-1]
rev_layers = nb_layers[::-1]
nb_layers.extend(rev_layers[1:])
else:
bottleneck_nb_layers = n_layers_per_block
nb_layers = [n_layers_per_block] * (2 * n_dense_blocks + 1)
# Initial convolution
x = Conv2D(init_conv_filters, 3, padding='same', use_bias=False, name='initial_conv2D',
kernel_initializer='he_uniform', kernel_regularizer=l2(decay))(img_input)
n_filters = init_conv_filters
skip_list = []
# Add dense blocks and transition down blocks
for block_idx in range(n_dense_blocks):
if verbose > 0:
print ("\nAdding Dense Block {}".format(block_idx))
print ("Layers: {}".format(nb_layers[block_idx]))
print ("Filters: {}".format(n_filters))
print ("Growth Rate: {}".format(growth_rate))
x, n_filters = DenseBlock(x, nb_layers[block_idx], n_filters, growth_rate,
p=p, decay=decay, bottleneck=False)
# store skip connections
skip_list.append(x)
# add transition_block
if verbose > 0:
print ("\nAdding TD Block {}".format(block_idx))
print ("Filters: {}".format(n_filters))
print ("Compression Rate: {}".format(compression))
x = TransitionBlock(x, n_filters, compression=compression, p=p, decay=decay, kernel_sz=1)
n_filters = int(n_filters * compression)
# The last dense block does not have a transition down block
# the block below returns the concatenated feature map without the concatenation of the input
if verbose > 0:
print ("\nAdding Bottleneck Block")
print ("Layers: {}".format(bottleneck_nb_layers))
print ("Filters: {}".format(n_filters))
print ("Growth Rate: {}".format(growth_rate))
_, n_filters, concat_list = DenseBlock(x, bottleneck_nb_layers, n_filters, growth_rate,
p=p, decay=decay, bottleneck=False,
return_concat_list=True)
# Reverse the skip list
skip_list = skip_list[::-1]
# Add dense blocks and transition up blocks
for block_idx in range(n_dense_blocks):
n_filters_keep = growth_rate * nb_layers[n_dense_blocks + block_idx]
# Upsamplig block upsamples only the feature maps
l = concatenate(concat_list[1:])
if verbose > 0:
print ("\nAdding TU Block {}".format(block_idx))
print ("Filters: {}".format(n_filters_keep))
t = TransitionUpBlock(l, n_filters=n_filters_keep, up_type=upsampling_type)
# concatenate skip connection with transition block
x = concatenate([t, skip_list[block_idx]])
# To prevent feature map size to grow in upsampling dense blocks
# we set grow_nb_filters to False.
if verbose > 0:
print ("\nAdding Dense Block {}".format(block_idx))
print ("Layers: {}".format(nb_layers[n_dense_blocks + block_idx + 1]))
print ("Filters: {}".format(growth_rate))
print ("Growth Rate: {}".format(growth_rate))
DB_up, n_filters, concat_list = DenseBlock(x, nb_layers[n_dense_blocks + block_idx + 1],
n_filters=growth_rate, growth_rate=growth_rate,
p=p, decay=decay, bottleneck=False,
return_concat_list=True, grow_nb_filters=False)
if include_top:
x = Conv2D(n_classes, 1, padding='same', activation='linear', use_bias=False,
kernel_regularizer=l2(decay))(DB_up)
rows, cols, channels = input_shape
x = Reshape((rows * cols, n_classes))(x)
x = Activation(activation)(x)
x = Reshape((rows, cols, n_classes))(x)
return x
if __name__ == '__main__':
from keras.models import Model
from keras.layers import Input
input_shape = (224, 224, 3)
img_input = Input(shape=input_shape)
from keras.utils.vis_utils import plot_model
model = get_FCN_DenseNet(img_input)
model = Model(img_input, model)
model.summary()
plot_model(model, 'external_test _with_correction.png', show_shapes=False)
Hi, you sent a PR to me a few months back about corrections to the DenseBlock.
I wanted to tell you that your current implementation (with those corrections) does not actually have any dense connectivity at all.
This is due to your change in the DenseBlock. It should concatenate the x_list, not x and cb. If you concatenate x and cb, you are basically creating a U-Net and not a DenseNetFCN.
The image below is from your script - visualized using Keras. http://imgur.com/a/vJAUa
This is the image with the correction from
concatenate([x, cb])
toconcatenate(x_list)
. - https://imgur.com/a/U3NDD