iosconv-neural-networkmetal-performance-shaders

Migrating MPSCNNFullyConnected to MPSCNNConvolutionDataSource


I am migrating my neural network from the deprecated:

init(device: MTLDevice, convolutionDescriptor: MPSCNNConvolutionDescriptor, kernelWeights: UnsafePointer<Float>, biasTerms: UnsafePointer<Float>?, flags: MPSCNNConvolutionFlags)

to

init(device: MTLDevice, weights: MPSCNNConvolutionDataSource)

I have implemented a MPSCNNConvolutionDataSource which is well debugged and which works for all of my layers except one. Just for testing purposes I am calling here the datasource functions myself along with the deprecated init() of MPSCNNFullyConnected to be sure the datasource is implemented correctly. I know that this not the intended use of it, but I wanted the same data go into both MPSCNNFullyConnected() constructors. The following code runs and the NN works properly.

  /* This code runs as intended */
  let datasource = DataSource("test", 8, 8, 224, 1024, .reLU)
  _ = datasource.load()
    let layer = MPSCNNFullyConnected(device: device,
                                   convolutionDescriptor: datasource.descriptor(),
                                   kernelWeights: UnsafeMutablePointer<Float>(mutating: datasource.weights().assumingMemoryBound(to: Float.self)),
                                   biasTerms: datasource.biasTerms(),
                                   flags: .none)

When I instantiate the fully connected layer with the new init() the network fails. The following code runs but the NN does not work properly.

  /* This code does run, but the layer does NOT output the correct values */
  let datasource = DataSource("test", 8, 8, 224, 1024, .reLU)
  let layer = MPSCNNFullyConnected(device: device, weights: datasource)

Any suggestions why both calls are not identical ?


Solution

  • Finally I solved it. The difference between both calls is that you have to set the layer.offset explicitly if you use:

    init(device: MTLDevice, weights: MPSCNNConvolutionDataSource) 
    

    The deprecated call:

    init(device: MTLDevice, convolutionDescriptor: MPSCNNConvolutionDescriptor, kernelWeights: UnsafePointer<Float>, biasTerms: UnsafePointer<Float>?, flags: MPSCNNConvolutionFlags)
    

    seems to have done that implicitly.

    This code works:

    let datasource = DataSource("test", 8, 8, 224, 1024, .reLU)
    let layer = MPSCNNFullyConnected(device: device, weights: datasource)
    layer.offset = MPSOffset(x: 8/2, y: 8/2, z: 0)
    

    I guess this documented nowhere ! Thanks Apple for three days hardcore debugging.