21 KiB
21 KiB
In [3]:
import autograd.numpy as np # We need to use this numpy wrapper to make automatic differentiation work later
from autograd import grad, elementwise_grad
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
# Defining some activation functions
def ReLU(z):
return np.where(z > 0, z, 0)
# Derivative of the ReLU function
def ReLU_der(z):
return np.where(z > 0, 1, 0)
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def mse(predict, target):
return np.mean((predict - target) ** 2)In [41]:
def feed_forward_one_layer(W, b, x):
z = ...
a = ...
return a
def cost_one_layer(W, b, x, target):
predict = feed_forward_one_layer(W, b, x)
return mse(predict, target)
x = np.random.rand(2)
target = np.random.rand(3)
W = ...
b = ...In [ ]:
autograd_one_layer = grad(cost_one_layer, [0, 1])
W_g, b_g = autograd_one_layer(W, b, x, target)
print(W_g, b_g)In [ ]:
z = W @ x + b
a = sigmoid(z)
predict = a
def mse_der(predict, target):
return ...
print(mse_der(predict, target))
cost_autograd = grad(mse, 0)
print(cost_autograd(predict, target))In [ ]:
def sigmoid_der(z):
return ...
print(sigmoid_der(z))
sigmoid_autograd = elementwise_grad(sigmoid, 0)
print(sigmoid_autograd(z))In [54]:
dC_da = ...
dC_dz = ...In [ ]:
dC_da = ...
dC_dz = ...
dC_dW = ...
dC_db = ...
print(dC_dW, dC_db)In [ ]:
W_g, b_g = autograd_one_layer(W, b, x, target)
print(W_g, b_g)In [59]:
x = np.random.rand(2)
target = np.random.rand(4)
W1 = np.random.rand(3, 2)
b1 = np.random.rand(3)
W2 = np.random.rand(4, 3)
b2 = np.random.rand(4)
layers = [(W1, b1), (W2, b2)]In [60]:
z1 = W1 @ x + b1
a1 = sigmoid(z1)
z2 = W2 @ a1 + b2
a2 = sigmoid(z2)In [61]:
dC_da2 = ...
dC_dz2 = ...
dC_dW2 = ...
dC_db2 = ...In [ ]:
In [63]:
dC_da1 = ...
dC_dz1 = ...
dC_dW1 = ...
dC_db1 = ...In [ ]:
print(dC_dW1, dC_db1)
print(dC_dW2, dC_db2)In [67]:
def feed_forward_two_layers(layers, x):
W1, b1 = layers[0]
z1 = W1 @ x + b1
a1 = sigmoid(z1)
W2, b2 = layers[1]
z2 = W2 @ a1 + b2
a2 = sigmoid(z2)
return a2In [ ]:
def cost_two_layers(layers, x, target):
predict = feed_forward_two_layers(layers, x)
return mse(predict, target)
grad_two_layers = grad(cost_two_layers, 0)
grad_two_layers(layers, x, target)In [4]:
def create_layers(network_input_size, layer_output_sizes):
layers = []
i_size = network_input_size
for layer_output_size in layer_output_sizes:
W = np.random.randn(layer_output_size, i_size)
b = np.random.randn(layer_output_size)
layers.append((W, b))
i_size = layer_output_size
return layers
def feed_forward(input, layers, activation_funcs):
a = input
for (W, b), activation_func in zip(layers, activation_funcs):
z = W @ a + b
a = activation_func(z)
return a
def cost(layers, input, activation_funcs, target):
predict = feed_forward(input, layers, activation_funcs)
return mse(predict, target)In [5]:
def feed_forward_saver(input, layers, activation_funcs):
layer_inputs = []
zs = []
a = input
for (W, b), activation_func in zip(layers, activation_funcs):
layer_inputs.append(a)
z = W @ a + b
a = activation_func(z)
zs.append(z)
return layer_inputs, zs, aIn [ ]:
def backpropagation(
input, layers, activation_funcs, target, activation_ders, cost_der=mse_der
):
layer_inputs, zs, predict = feed_forward_saver(input, layers, activation_funcs)
layer_grads = [() for layer in layers]
# We loop over the layers, from the last to the first
for i in reversed(range(len(layers))):
layer_input, z, activation_der = layer_inputs[i], zs[i], activation_ders[i]
if i == len(layers) - 1:
# For last layer we use cost derivative as dC_da(L) can be computed directly
dC_da = ...
else:
# For other layers we build on previous z derivative, as dC_da(i) = dC_dz(i+1) * dz(i+1)_da(i)
(W, b) = layers[i + 1]
dC_da = ...
dC_dz = ...
dC_dW = ...
dC_db = ...
layer_grads[i] = (dC_dW, dC_db)
return layer_gradsIn [ ]:
network_input_size = 2
layer_output_sizes = [3, 4]
activation_funcs = [sigmoid, ReLU]
activation_ders = [sigmoid_der, ReLU_der]
layers = create_layers(network_input_size, layer_output_sizes)
x = np.random.rand(network_input_size)
target = np.random.rand(4)In [ ]:
layer_grads = backpropagation(x, layers, activation_funcs, target, activation_ders)
print(layer_grads)In [ ]:
cost_grad = grad(cost, 0)
cost_grad(layers, x, [sigmoid, ReLU], target)In [ ]:
class NeuralNetwork:
def __init__(
self,
network_input_size,
layer_output_sizes,
activation_funcs,
activation_ders,
cost_fun,
cost_der,
):
pass
def predict(self, inputs):
# Simple feed forward pass
pass
def cost(self, inputs, targets):
pass
def _feed_forward_saver(self, inputs):
pass
def compute_gradient(self, inputs, targets):
pass
def update_weights(self, layer_grads):
pass
# These last two methods are not needed in the project, but they can be nice to have! The first one has a layers parameter so that you can use autograd on it
def autograd_compliant_predict(self, layers, inputs):
pass
def autograd_gradient(self, inputs, targets):
pass