input_error = np.dot(output_error, self.weights.T)
weights_gradient= np.dot(self.input.T, output_error)
bias_gradient = np.sum(output_error, axis=0, keepdims=True)
l1Out = self.layer1.forward_propagation(input_data)
a1Out = self.activation1.forward_propagation(l1Out)
l2Out = self.layer2.forward_propagation(a1Out)
a2Out = self.activation2.forward_propagation(l2Out)
l3Out = self.layer3.forward_propagation(a2Out)
a3Out = self.activation3.forward_propagation(l3Out)
pred = self.layer4.forward_propagation(a3Out)
loss_gradient = self.loss_gradient(y_true, y_pred)
l4Err = self.layer4.backward_propagation(loss_gradient, learning_rate)
activate3 = self.activation3.backward_propagation(l4Err)
l3Err = self.layer3.backward_propagation(activate3, learning_rate)
activate2 = self.activation2.backward_propagation(l3Err)
l2Err = self.layer2.backward_propagation(activate2, learning_rate)
activate1 = self.activation1.backward_propagation(l2Err)
l1Err = self.layer1.backward_propagation(activate1, learning_rate)
data_pred = net.forward(data_x)
reconstruction_error = np.mean(((data_x - data_pred)**2, axis=1)
plt.figure(figsize=(8, 4))
plt.hist(reconstruction_error, bins=100, alpha=0.6, color='g')
plt.xlabel('Error')
plt.ylabel('Samples')
plt.title('Reconstruction error after model training')
plt.show()