Q:

Neural network breaks if hidden layers are increased

I am trying to make my neural network as dynamic as possible. For example, I want to be able to increase hidden_layers number. It works so far with one hidden layer, but if I increase it to two, it instantly breaks. There is a problem when the backpropagation is reaching the layer between Inputs <- 1st_hidden. Here is my code. I am trying to solve XOR problem by providing this to my NN:
inputs  = np.array([[1, 0], [0, 1], [1, 1], [0, 0]]) 
targets = np.array([[1], [1], [0], [0]])

Can anyone help me? I can not seem to solve it.   
nn = NeuralNetwork(input_nodes = 2, hidden_layers = 1, hidden_nodes = 3, output_nodes = 1)

I initialize my neural network:
class NeuralNetwork:
def __init__(self, input_nodes, hidden_layers, hidden_nodes, output_nodes):

    self.input_nodes   = input_nodes
    self.hidden_layers = hidden_layers
    self.hidden_nodes  = hidden_nodes
    self.output_nodes  = output_nodes
    self.layers_count  = self.hidden_layers + 1

    self.weights       = []
    self.bias          = []

    min_value = -2
    max_value =  2

    # Initialize weights and bias matrices and place them in a list
    # Matrix shape within lists
    # [(2,2), (2,2), (1,2)]

    for i in range(self.layers_count):
        if i == 0:
            self.weights.append(np.random.uniform(min_value, max_value, (self.hidden_nodes, self.input_nodes)))
            self.bias.append(np.random.uniform(min_value, max_value, (self.hidden_nodes,)))
        elif i == self.hidden_layers:
            self.weights.append(np.random.uniform(min_value, max_value, (self.output_nodes, self.hidden_nodes)))
            self.bias.append(np.random.uniform(min_value, max_value, (self.output_nodes,)))
        else:
            self.weights.append(np.random.uniform(min_value, max_value, (self.hidden_nodes, self.hidden_nodes)))
            self.bias.append(np.random.uniform(min_value, max_value, (self.hidden_nodes,)))

    self.learning_rate = 0.1

def train(self, input_array, targets):

    inputs = np.transpose(input_array)

    applied_activation = nn.feedforward(inputs)

    output_errors = np.subtract(targets, applied_activation[-1])

    # Backpropogation
    for i in reversed(range(self.layers_count)):
        delta    = np.ndarray((self.input_nodes,))
        gradient = np.ndarray((self.hidden_layers,))

        if i == self.hidden_layers:
            errors = output_errors
            delta, gradient = calculate_weights_deltas(applied_activation[i], output_errors, applied_activation[i-1])
        elif i == 0:     
            # IT BREAKS HERE
            errors          = calculate_errors(self.weights[i+1], output_errors)
            delta, gradient = calculate_weights_deltas(applied_activation[i], errors, inputs)
        else:
            errors          = calculate_errors(self.weights[i+1], output_errors)
            delta, gradient = calculate_weights_deltas(applied_activation[i], errors, applied_activation[i-1])

        delta, gradient = calculate_weights_deltas(applied_activation[i], errors, applied_activation[i-1])
        # Adjust weights by deltas
        self.weights[i] = np.add(self.weights[i], delta)
        # Adjust the bias by its deltas 
        self.bias[i] = np.add(self.bias[i], gradient)

The error I get is the following:
            elif i == 0:       
                errors          = calculate_errors(self.weights[i+1], output_errors)

            ValueError: shapes (3,3) and (1,) not aligned: 3 (dim 1) != 1 (dim 0)

A:

I found my error. My early calculations only involved one hidden layer, however, I did not include the derivatives of previous weights when calculating the hidden_error. This means that in the scenario of a neural network of:
Input -> H1 -> H2 -> Output

to calculate the backpropagation of the last step (Input <- H1), I did not calculate derivatives of weights_hidden_output (H2 -> Output) and weights_hidden_to_hidden (H1 -> H2).

