NeuromorphicProcessorProject / snn_toolbox

Toolbox for converting analog to spiking neural networks (ANN to SNN), and running them in a spiking neuron simulator.
MIT License
362 stars 105 forks source link

Accuracy loss through dense layers for brian2 backend with new version (0.3.1) #47

Closed wilkieolin closed 5 years ago

wilkieolin commented 5 years ago

I see that the build_dense function has been rewritten to better incorporate flatten layers. However, something about how the function has changed is leading to a drop in accuracy when I'm executing the example MNIST network.

When I replace the function with its old version, activation/SR correlation is much improved and accuracy is restored. I can't quite figure out why this is the case since it seems like the two functions should be doing the same thing when no flatten layer is involved.

New Brian2 build_dense function:

def build_dense(self, layer, weights=None):

        if layer.activation == 'softmax':
            raise warnings.warn("Activation 'softmax' not implemented. Using "
                                "'relu' activation instead.", RuntimeWarning)

        _weights, biases = layer.get_weights()
        if weights is None:
            weights = _weights

        set_biases(biases)

        delay = self.config.getfloat('cell', 'delay')
        connections = []
        if len(self.flatten_shapes) == 1:
            print("Swapping data_format of Flatten layer.")
            flatten_name, shape = self.flatten_shapes.pop()
            if self.data_format == 'channels_last':
                y_in, x_in, f_in = shape
            else:
                f_in, y_in, x_in = shape
            for i in range(weights.shape[0]):  # Input neurons
                # Sweep across channel axis of feature map. Assumes that each
                # consecutive input neuron lies in a different channel. This is
                # the case for channels_last, but not for channels_first.
                f = i % f_in
                # Sweep across height of feature map. Increase y by one if all
                # rows along the channel axis were seen.
                y = i // (f_in * x_in)
                # Sweep across width of feature map.
                x = (i // f_in) % x_in
                new_i = f * x_in * y_in + x_in * y + x
                for j in range(weights.shape[1]):  # Output neurons
                    connections.append((new_i, j, weights[i, j], delay))
        elif len(self.flatten_shapes) > 1:
            raise RuntimeWarning("Not all Flatten layers have been consumed.")
        else:
            for i in range(weights.shape[0]):
                for j in range(weights.shape[1]):
                    connections.append((i, j, weights[i, j], delay))

        connections = np.array(connections)

        self.connections[-1].connect(i=connections[:, 0].astype('int64'),
                                     j=connections[:, 1].astype('int64'))

        self.connections[-1].w = connections[:, 2]

The old function:

def build_dense(self, layer, input_weight=None):

        if layer.activation == 'softmax':
            raise warnings.warn("Activation 'softmax' not implemented. Using "
                                "'relu' activation instead.", RuntimeWarning)

        weights, biases = layer.get_weights()
        self.set_biases(biases)
        self.connections[-1].connect(True)
        if input_weight is not None:
            self.connections[-1].w = input_weight.flatten()
        else:
            self.connections[-1].w = weights.flatten()
        print("Lenght of weights:{}".format(len(self.connections[-1].w)))

New build_dense: new_dense new_dense_srdist

Old build_dense: old_dense old_dense_corr

rbodo commented 5 years ago

This fix for the flatten layer was introduced because of a change in Keras. Please update your Keras version (I'm using 2.3.1) - the accuracy / correlation should then be OK.