Search code examples
pythonkerascntk

Keras with CNTK backend: Writing custom layers


I'm trying to write a custom layer in Keras to replicate on particular architecture proposed in a paper. The layer has no trainable weights. I believe this might be relevant, since it wouldn't be necessary to extend the class Layer.

I'm using the CNTK backend, but I'm trying to keep the code as backend-agnostic as possible, so I'm relying on the interfaces defined in keras.backend, instead of directly using CNTK.

Right now I'm just trying to get a small example to work. The example is as follows:

import numpy as np

from scipy.misc import imread
from keras import backend as K

im = imread('test.bmp')

#I'm extending a grayscale image to behave as a color image
ex_im = np.empty([im.shape[0],im.shape[1],3])

ex_im[:,:,0] = im
ex_im[:,:,1] = im
ex_im[:,:,2] = im

conv_filter = K.ones([3,3,ex_im.shape[2],ex_im.shape[2]])
x = K.conv2d(ex_im,conv_filter,padding='same')

This code, however, results in the following error:

RuntimeError: Convolution currently requires the main operand to have dynamic axes

CNTK requires the input to the convolution to have dynamic axes, otherwise it would interpret the first dimension of the input as the batch size. So I tried to make the axes dynamic with placeholders (the only way I could find of doing so):

import numpy as np

from scipy.misc import imread
from keras import backend as K

im = imread('test.bmp')

ex_im = np.empty([1,im.shape[0],im.shape[1],3])

ex_im[0,:,:,0] = im
ex_im[0,:,:,1] = im
ex_im[0,:,:,2] = im

place = K.placeholder(shape=((None,) + ex_im.shape[1:]))

conv_filter = K.ones([3,3,ex_im.shape[3],ex_im.shape[3]])
x = K.conv2d(place,conv_filter,padding='same')

The image is now an array of images, with what is basically a batch size of 1.

This works correctly. However, I can't figure out how to feed an input to the placeholder in order to test my code. eval() doesn't take any arguments, and there doesn't seem to be a way to pass the input as an argument to the evaluation.

Is there a way to do this without placeholders? Or a way to feed the inputs to the placeholder? Am I doing something fundamentally wrong and should be following another path?

I should add that I really want to avoid being locked in to a backend, so any solutions should be backend-agnostic.


Solution

  • For using custom layers, you don't define tensors, let Keras do it for you. Just create the layer, and what will be given to the layer will already be a proper tensor:

    images = np.ones((1,50,50,3))
    
    def myFunc(x):
        conv_filter = K.ones([3,3,3,3])
        return K.conv2d(x,conv_filter,padding='same')
    
    inp = Input((50,50,3))
    out = Lambda(myFunc, output_shape=(50,50,3))(inp)
    
    model = Model(inp,out)
    print(model.predict(images))