#๐Ÿ”’ Help with Backward Propagation of MLP

12 messages ยท Page 1 of 1 (latest)

pine gardenBOT
#

@wintry shuttle

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

wintry shuttle
#

I ran into the following error while implementing my code for backward propagation of a multilayer perceptron
Error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 126
    122         return y
    124 if __name__ == '__main__':
    125     # create an MLP
--> 126     mlp = MLP(2, [5], 1)
    128     # create some inputs (dummy data)
    129     inputs = np.array([0.1, 0.2]) 

Cell In[9], line 56, in MLP.__init__(self, num_inputs, num_hidden, num_outputs)
     54 derivatives = []
     55 for i in range(len(layers)-1): # number of derivatives are same as the weight matrices
---> 56     d = np.zeros(layers[i], layers[i+1]) 
     57     derivatives.append(d)
     58 self.derivatives = derivatives

TypeError: Cannot interpret '5' as a data type
#

Code:

import numpy as np 
# Steps: 
# save activations and derivatives 
# implement backpropagartion 
# implement gradient descent 
# implement train 
# train our network with some dummy dataset 
# make some predictions

# ........ Code below ............

# create a class 
class MLP(): 
    """A Multilayer Perceptron class.
    """
    # representation of a simple multilayer perceptron
    def __init__(self, num_inputs=3, num_hidden=[3, 5], num_outputs=2):
        """Constructor for the MLP. akes the number of inputs,
        a variable number of hidden layers, and number of outputs.
        
        Args:
            num_inputs (int): Number of inputs
            num_hidden (list): A list of ints for the hidden layers
            num_outputs (int): Number of outputs
        """
        
        self.num_inputs = num_inputs
        self.num_hidden = num_hidden
        self.num_outputs = num_outputs

        # create a generic representation of the layers
        layers = [self.num_inputs] + self.num_hidden + [self.num_outputs] # this gets a list where each item in a list represents the number of neurons in a layer

        # initiate random connection weights of the layers
        self.weights = [] 
        for i in range(len(layers)-1): # weights are in between layer, so they'll be one less than te number of layers
            w = np.random.rand(layers[i], layers[i + 1])
            self.weights.append(w)

        # alternate method for weights
        #weights = []
        #for i in range(len(layers)-1):
            #w = np.random.rand(layers[i], layers[i + 1])
            #weights.append(w)
        #self.weights = weights
#
# create data representation for activation and derivatives  
        activations = []
        for i in range(len(layers)): 
            a = np.zeros(layers[i]) # we want an amount of zeros in list equal to the number of neurons we have in each layer
            activations.append(a)
        self.activations = activations # store value in an instance variable 

        derivatives = []
        for i in range(len(layers)-1): # number of derivatives are same as the weight matrices
            d = np.zeros(layers[i], layers[i+1]) 
            derivatives.append(d)
        self.derivatives = derivatives 
def forward_propagate(self, inputs):
        """Computes forward propagation of the network based on input signals.
        
        Args:
            inputs (ndarray): Input signals
        Resturns:
            activations (ndarray): Output values
        """
        # the input layer activation is just the input itself
        activations = inputs
        self.activations[0] = inputs # save activation instance for first layer as inputs

        # iterate through the network layers  
        for i, w in enumerate(self.weights):
            # calculate the net inputs (matrix multiplication between previus activation and weight matrix)
            net_inputs = np.dot(activations, w) 
            
            # apply sigmoid activation function
            activations = self._sigmoid(net_inputs)
            self.activations [i+1] = activations # save activations as i+1 because activatins are 1 moe than the weights. to make sure you are using corresponding values in your calculation for h, you have to increase a's i value by 1. e.g w1 corresponds with a2, w2 corresponds with a3, etc

        # return output layer activation
        return activations
#
def back_propagate(self, error, verbose=False):

        # 3.  dE/dW_i = {(y - a_[i+1]) x s'(h_[i+1])} matmul {a_i}
        # 2.  s'(h_[i+1]) = s(h_[i+1]) x (1 - s(h_[i+1]))
        # 1.  s(h_[i+1])) = a_[i+1]

        # 3.  dE/dW_[i-1] = {(y - a_[i+1]) x s'(h_[i+1])} W_i s'(h_i) a_[i+1] 
        
        for i in reversed(range(len(self.derivatives))):
            # 1.
            activations = self.activations[i+1]

            # Part of 3.
            delta = error * self._sigmoid_derivative(activations) # ndarray([0.1, 0.2]) --> ndarray([[0.1, 0.2]]) - changed to 2d array with a single  row
            delta_reshaped = delta.reshape(delta.shape[0], -1).T # transposes the array 
            current_activations = self.activations[i] # ndarray([0.1, 0.2]) --> ndarray([[0.1], [0.2]]) - changed to 2d array with 2 rows
            current_activations_reshaped = current_activations.reshape(current_activations.shape[0], -1) # restructure array as shown in above comment
            self.derivatives[i] = np.dot(current_activations, delta) 
            error = np.dot(delts, self.weights[i].T) 


            if verbose:
                print("Derivatives for W{}: {}".format(i, self.derivatives(i)))
        return error


    def _sigmoid_derivative(self, x):
        return x * (1.0 - x)
        
    def _sigmoid(self, x):
        """Sigmoid activation function
        Args: 
            x (float): Value to be processed
        Returns:
            y (float): Output
        """
        y =  1.0 / (1 + np.exp(-x))
        return y
#
if __name__ == '__main__':
    # create an MLP
    mlp = MLP(2, [5], 1)
    
    # create some inputs (dummy data)
    inputs = np.array([0.1, 0.2]) 
    target = np.array([0.3])
    
    # perform forward propagation
    output = mlp.forward_propagate(inputs)

    # calculate the error
    error = target - output
    
    # back propagation
    mlp.back_propagate(error)
    
    # print the results
    print("The network input is {}".format(inputs))
    print("The network output is {}".format(output))
    print("The network error is {}".format(error))
#

Sorry about the messed up indentations for the def
They're like that because I had to break up the code to send in here

potent osprey
#

@wintry shuttle did you solve this yet?

#

because you probably want something like this

d = np.zeros([layers[i],layers[i+1]]) 
#

if your goal with that line was making a matrix of zeroes with dimensions [layer n, layer n+1]

pine gardenBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.