Search code examples
pythonfunctionsegmentation-faultcntk

How to compose custom function CNTK


I have created a custom function called CustomFunc, following the explanation here: https://www.cntk.ai/pythondocs/extend.html

If I use it as suggested by the article, it works:

model = cntk.user_function(CustomFunc(prev_node))

this works fine, the model runs without any problem. My issue is that I want to use this function inside a cntk.layers.Sequential call, and inside a cntk.layers.Recurrence call. To do this, I somehow need to construct the composition of my function with another one, and then put it inside the Sequential or Recurrence call. Right now what I use some placeholder, i.e. I do is:

customFunToUse = cntk.user_function(CustomFunc(cntk.placeholder(), otherInputs))
model = cntk.layers.Sequential([cntk.layers.Dense(100), 
                                customFunToUse, 
                                cntk.layers.Recurrence(
                                customFunToUse >> cntk.layers.LSTM(100))])

But this doesn't work and raises all kinds of errors: sometimes it's a segfault, in another similar model is a

"ValueError: Cannot create an NDArrayView using a view shape '[? x 10]' that has unknown dimensions for any of its axes."

Other times instead is a

Evaluate: All nodes inside a recurrent loop must have a layout that is identical; mismatch found for nodes ...

Note also that my custom function does not alter the input dimensions: given any amount of paramters, it will return the same amount and type. The code is this:

class CustomFun(UserFunction):
    def __init__(self, *args, otherStuff, name='CustomFun'):
        super(CustomFun, self).__init__(list(args), name=name)
        self.otherStuff = otherStuff

    def forward(self, arguments, outputs=None, keep_for_backward=None, device=None, as_numpy=True):
        return None,[x/2 for x in arguments]

    def backward(self, state, root_gradients, variables=None, as_numpy=True):
        #it's not important right now, just a test...
        return root_gradient

    def infer_outputs(self):
        #shape, type and dynamic axes of inputs are not changed by this function

        outputVar = [output_variable(self.inputs[idx].shape, self.inputs[idx].dtype,
            self.inputs[idx].dynamic_axes, name='out_quantLayer') for idx in range(len(self.inputs))]
        return outputVar

    def serialize(self):
        return {'otherStuff': self.otherStuff}

    @staticmethod
    def deserialize(inputs, name, state):
        return CustomFun(inputs, otherStuff=state['otherStuff'], name=name)

Solution

  • The right way would be to write something like this def my_layer(x): @C.Function def apply(x): return cntk.user_function(CustomFunc(x)) return apply Unfortunately this seems to be causing my Python interpreter to crash. I have opened github issue 2132 on this. Will try to update this answer when the issue gets fixed.

    Update: There was a small typo that we did not catch. There's a resolution at the github issue page.