adobe/antialiased-cnns

my keras impl

iperov opened this issue · 18 comments

class BlurPool(KL.Layer):
    """
    https://arxiv.org/abs/1904.11486 https://github.com/adobe/antialiased-cnns
    """
    def __init__(self, filt_size=5, stride=2, **kwargs):
        self.strides = (stride,stride)
        self.filt_size = filt_size
        self.padding = ( (int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2)) ), (int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2)) ) )
        if(self.filt_size==1):
            self.a = np.array([1.,])
        elif(self.filt_size==2):
            self.a = np.array([1., 1.])
        elif(self.filt_size==3):
            self.a = np.array([1., 2., 1.])
        elif(self.filt_size==4):    
            self.a = np.array([1., 3., 3., 1.])
        elif(self.filt_size==5):    
            self.a = np.array([1., 4., 6., 4., 1.])
        elif(self.filt_size==6):    
            self.a = np.array([1., 5., 10., 10., 5., 1.])
        elif(self.filt_size==7):    
            self.a = np.array([1., 6., 15., 20., 15., 6., 1.])
        super(BlurPool, self).__init__(**kwargs)
    def compute_output_shape(self, input_shape):
        height = input_shape[1] // self.strides[0]
        width = input_shape[2] // self.strides[1] 
        channels = input_shape[3]
        return (input_shape[0], height, width, channels)
        
    def call(self, x):
        k = self.a
        k = k[:,None]*k[None,:]
        k = k / np.sum(k)
        k = np.tile (k[:,:,None,None], (1,1,K.int_shape(x)[-1],1) )                
        k = K.constant (k, dtype=K.floatx() )
        
        x = K.spatial_2d_padding(x, padding=self.padding)
        x = K.depthwise_conv2d(x, k, strides=self.strides, padding='valid')
        return x

Is this good enough and is there example code for building a model to replace Conv layer with strides=2?

@off99555 Yes, Conv (stride2)+Relu should be replaced with Conv(stride1)+Relu+BlurPool(stride2). See the readme or antialiased resnet for reference.

@iperov Great! I guess this repo will stay pytorch based for now but nice to have kears too.

@iperov
Thank you for your great work on implementing this great layer using keras. I think it is only good for 2D model. It will be great if you would like to modify it for 3D CNN. Many thanks.

I had to modify compute_output_shape in the following way to make it work with Conv2D layers preserving the behavior (e.g. in keras.applications.xception.Xception):

    height = math.ceil(input_shape[1] / self.strides[0])
    width = math.ceil(input_shape[2] / self.strides[1])

any reason you used depthwise_conv2d instead of con2d?

as original

Wow this is great!

  • Do you have working version for tensorflow2 tf.keras?
  • Is it possible to insert this layers to keras.application models?

This is my TF implementation, though not for tf 2.0

def blur_pool(inp, pad_type='reflect', filter=3, stride=2, pad_off=0):
    def pad(inp, pad_sizes, pad_type):
        return tf.pad(inp, paddings=pad_sizes, mode=pad_type)

    pad_sizes = [int(1.*(filter-1)/2), int(np.ceil(1.*(filter-1)/2)), int(1.*(filter-1)/2), int(np.ceil(1.*(filter-1)/2))]
    pad_sizes = [[0,0], [pad_sizes[0]+pad_off, pad_sizes[1]+pad_off], [pad_sizes[2]+pad_off, pad_sizes[3]+pad_off], [0,0]]

    if(filter==1):
        a = np.array([1.,])
    elif(filter==2):
        a = np.array([1., 1.])
    elif(filter==3):
        a = np.array([1., 2., 1.])
    elif(filter==4):    
        a = np.array([1., 3., 3., 1.])
    elif(filter==5):    
        a = np.array([1., 4., 6., 4., 1.])
    elif(filter==6):    
        a = np.array([1., 5., 10., 10., 5., 1.])
    elif(filter==7):    
        a = np.array([1., 6., 15., 20., 15., 6., 1.])

    filt = a[:,None]*a[None,:]
    filt = filt/np.sum(filt)
    filt = np.tile(filt[:, :, None, None], (1,1,int(np.shape(inp)[-1]), 1))
    filt = tf.constant(filt)

    if(filter==1):
        if(pad_off==0):
            return inp[:,:,::stride,::stride]    
        else:
            return pad(inp, pad_sizes, pad_type)[:,:,::stride,::stride]
    else:
        return tf.nn.depthwise_conv2d(pad(inp, pad_sizes, pad_type), tf.cast(filt, tf.float32), 
            strides=(1, stride, stride, 1), padding='VALID')

Thank you @iperov and @christegho .
I tried to create a little gist for tensorflow2 (tf.keras), however it is till not finished. If someone could help me with it that would be great!

The gist is here:
https://gist.github.com/Cospel/96b88baa589e244b6bf53d47ccfb7a4c

Curious if anyone has had any luck implementing the 1D version. There's no depthwise_conv1d, and the standard playbook to expand_dims and use depthwise_conv2d returns error "Current implementation only supports equal length strides in the row and column dimensions."

Thankyou @iperov and @christegho ! These implementations do help a lot in integration.

Great! but When I predict the U-Net model. I must fix the filt_size and stride to keep the same as the training.

Thanks @iperov for sharing!
I'm on TF 2.1 and I had to add a get_config so TF/Keras didn't freak out. Here's the method I popped in:

    # https://stackoverflow.com/questions/58678836/notimplementederror-layers-with-arguments-in-init-must-override-get-conf/58680354#58680354
    def get_config(self):

        config = super().get_config().copy()
        config.update({
            'filt_size': self.filt_size,
            'strides': self.strides[0]
        })
        return config

Hello! I'm on TF1.14 and I had an error as fllow:
AttributeError: 'mappinggroxy' object has no attribute 'update'
So, I want to konw why, what the keras version you used?

I believe I was just using tf.keras from TF 2.1, not the separate keras package.

Maybe someone knows how to change the 'compute_output_shape' function when 'input_shape' contains None for free dimensions?

it's better not to use keras or tensorflow. Migrate to pytorch and feel happy.