193 KiB
193 KiB
In [1]:
import numpy as np
n = 100 #100 datapoints
M = 5 #size of each minibatch
m = int(n/M) #number of minibatches
n_epochs = 10 #number of epochs
j = 0
for epoch in range(1,n_epochs+1):
for i in range(m):
k = np.random.randint(m) #Pick the k-th minibatch at random
#Compute the gradient using the data in minibatch Bk
#Compute new suggestion for
j += 1In [2]:
import numpy as np
def step_length(t,t0,t1):
return t0/(t+t1)
n = 100 #100 datapoints
M = 5 #size of each minibatch
m = int(n/M) #number of minibatches
n_epochs = 500 #number of epochs
t0 = 1.0
t1 = 10
gamma_j = t0/t1
j = 0
for epoch in range(1,n_epochs+1):
for i in range(m):
k = np.random.randint(m) #Pick the k-th minibatch at random
#Compute the gradient using the data in minibatch Bk
#Compute new suggestion for beta
t = epoch*m+i
gamma_j = step_length(t,t0,t1)
j += 1
print("gamma_j after %d epochs: %g" % (n_epochs,gamma_j))gamma_j after 500 epochs: 9.97108e-05
In [3]:
%matplotlib inline
# Importing various packages
from math import exp, sqrt
from random import random, seed
import numpy as np
import matplotlib.pyplot as plt
n = 100
x = 2*np.random.rand(n,1)
y = 4+3*x+np.random.randn(n,1)
X = np.c_[np.ones((n,1)), x]
XT_X = X.T @ X
theta_linreg = np.linalg.inv(X.T @ X) @ (X.T @ y)
print("Own inversion")
print(theta_linreg)
# Hessian matrix
H = (2.0/n)* XT_X
EigValues, EigVectors = np.linalg.eig(H)
print(f"Eigenvalues of Hessian Matrix:{EigValues}")
theta = np.random.randn(2,1)
eta = 1.0/np.max(EigValues)
Niterations = 1000
for iter in range(Niterations):
gradients = 2.0/n*X.T @ ((X @ theta)-y)
theta -= eta*gradients
print("theta from own gd")
print(theta)
xnew = np.array([[0],[2]])
Xnew = np.c_[np.ones((2,1)), xnew]
ypredict = Xnew.dot(theta)
ypredict2 = Xnew.dot(theta_linreg)
n_epochs = 50
M = 5 #size of each minibatch
m = int(n/M) #number of minibatches
t0, t1 = 5, 50
def learning_schedule(t):
return t0/(t+t1)
theta = np.random.randn(2,1)
for epoch in range(n_epochs):
# Can you figure out a better way of setting up the contributions to each batch?
for i in range(m):
random_index = M*np.random.randint(m)
xi = X[random_index:random_index+M]
yi = y[random_index:random_index+M]
gradients = (2.0/M)* xi.T @ ((xi @ theta)-yi)
eta = learning_schedule(epoch*m+i)
theta = theta - eta*gradients
print("theta from own sdg")
print(theta)
plt.plot(xnew, ypredict, "r-")
plt.plot(xnew, ypredict2, "b-")
plt.plot(x, y ,'ro')
plt.axis([0,2.0,0, 15.0])
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
plt.title(r'Random numbers ')
plt.show()Own inversion [[4.03011012] [2.91071448]] Eigenvalues of Hessian Matrix:[0.33793867 4.1217953 ] theta from own gd [[4.03011012] [2.91071448]] theta from own sdg [[4.06899505] [2.91970243]]
In [4]:
import autograd.numpy as np
# To do elementwise differentiation:
from autograd import elementwise_grad as egrad
# To plot:
import matplotlib.pyplot as plt
def f(x):
return np.sin(2*np.pi*x + x**2)
def f_grad_analytic(x):
return np.cos(2*np.pi*x + x**2)*(2*np.pi + 2*x)
# Do the comparison:
x = np.linspace(0,1,1000)
f_grad = egrad(f)
computed = f_grad(x)
analytic = f_grad_analytic(x)
plt.title('Derivative computed from Autograd compared with the analytical derivative')
plt.plot(x,computed,label='autograd')
plt.plot(x,analytic,label='analytic')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
print("The max absolute difference is: %g"%(np.max(np.abs(computed - analytic))))The max absolute difference is: 1.77636e-15
In [5]:
import autograd.numpy as np
from autograd import grad
def f1(x):
return x**3 + 1
f1_grad = grad(f1)
# Remember to send in float as argument to the computed gradient from Autograd!
a = 1.0
# See the evaluated gradient at a using autograd:
print("The gradient of f1 evaluated at a = %g using autograd is: %g"%(a,f1_grad(a)))
# Compare with the analytical derivative, that is f1'(x) = 3*x**2
grad_analytical = 3*a**2
print("The gradient of f1 evaluated at a = %g by finding the analytic expression is: %g"%(a,grad_analytical))The gradient of f1 evaluated at a = 1 using autograd is: 3 The gradient of f1 evaluated at a = 1 by finding the analytic expression is: 3
In [6]:
import autograd.numpy as np
from autograd import grad
def f2(x1,x2):
return 3*x1**3 + x2*(x1 - 5) + 1
# By sending the argument 0, Autograd will compute the derivative w.r.t the first variable, in this case x1
f2_grad_x1 = grad(f2,0)
# ... and differentiate w.r.t x2 by sending 1 as an additional arugment to grad
f2_grad_x2 = grad(f2,1)
x1 = 1.0
x2 = 3.0
print("Evaluating at x1 = %g, x2 = %g"%(x1,x2))
print("-"*30)
# Compare with the analytical derivatives:
# Derivative of f2 w.r.t x1 is: 9*x1**2 + x2:
f2_grad_x1_analytical = 9*x1**2 + x2
# Derivative of f2 w.r.t x2 is: x1 - 5:
f2_grad_x2_analytical = x1 - 5
# See the evaluated derivations:
print("The derivative of f2 w.r.t x1: %g"%( f2_grad_x1(x1,x2) ))
print("The analytical derivative of f2 w.r.t x1: %g"%( f2_grad_x1(x1,x2) ))
print()
print("The derivative of f2 w.r.t x2: %g"%( f2_grad_x2(x1,x2) ))
print("The analytical derivative of f2 w.r.t x2: %g"%( f2_grad_x2(x1,x2) ))Evaluating at x1 = 1, x2 = 3 ------------------------------ The derivative of f2 w.r.t x1: 12 The analytical derivative of f2 w.r.t x1: 12 The derivative of f2 w.r.t x2: -4 The analytical derivative of f2 w.r.t x2: -4
In [7]:
import autograd.numpy as np
from autograd import grad
def f3(x): # Assumes x is an array of length 5 or higher
return 2*x[0] + 3*x[1] + 5*x[2] + 7*x[3] + 11*x[4]**2
f3_grad = grad(f3)
x = np.linspace(0,4,5)
# Print the computed gradient:
print("The computed gradient of f3 is: ", f3_grad(x))
# The analytical gradient is: (2, 3, 5, 7, 22*x[4])
f3_grad_analytical = np.array([2, 3, 5, 7, 22*x[4]])
# Print the analytical gradient:
print("The analytical gradient of f3 is: ", f3_grad_analytical)The computed gradient of f3 is: [ 2. 3. 5. 7. 88.] The analytical gradient of f3 is: [ 2. 3. 5. 7. 88.]
In [8]:
import autograd.numpy as np
from autograd import grad
def f4(x):
return np.sqrt(1+x**2) + np.exp(x) + np.sin(2*np.pi*x)
f4_grad = grad(f4)
x = 2.7
# Print the computed derivative:
print("The computed derivative of f4 at x = %g is: %g"%(x,f4_grad(x)))
# The analytical derivative is: x/sqrt(1 + x**2) + exp(x) + cos(2*pi*x)*2*pi
f4_grad_analytical = x/np.sqrt(1 + x**2) + np.exp(x) + np.cos(2*np.pi*x)*2*np.pi
# Print the analytical gradient:
print("The analytical gradient of f4 at x = %g is: %g"%(x,f4_grad_analytical))The computed derivative of f4 at x = 2.7 is: 13.8759 The analytical gradient of f4 at x = 2.7 is: 13.8759
In [9]:
import autograd.numpy as np
from autograd import grad
def f5(x):
if x >= 0:
return x**2
else:
return -3*x + 1
f5_grad = grad(f5)
x = 2.7
# Print the computed derivative:
print("The computed derivative of f5 at x = %g is: %g"%(x,f5_grad(x)))The computed derivative of f5 at x = 2.7 is: 5.4
In [10]:
import autograd.numpy as np
from autograd import grad
def f6_for(x):
val = 0
for i in range(10):
val = val + x**i
return val
def f6_while(x):
val = 0
i = 0
while i < 10:
val = val + x**i
i = i + 1
return val
f6_for_grad = grad(f6_for)
f6_while_grad = grad(f6_while)
x = 0.5
# Print the computed derivaties of f6_for and f6_while
print("The computed derivative of f6_for at x = %g is: %g"%(x,f6_for_grad(x)))
print("The computed derivative of f6_while at x = %g is: %g"%(x,f6_while_grad(x)))The computed derivative of f6_for at x = 0.5 is: 3.95703 The computed derivative of f6_while at x = 0.5 is: 3.95703
In [11]:
import autograd.numpy as np
from autograd import grad
# Both of the functions are implementation of the sum: sum(x**i) for i = 0, ..., 9
# The analytical derivative is: sum(i*x**(i-1))
f6_grad_analytical = 0
for i in range(10):
f6_grad_analytical += i*x**(i-1)
print("The analytical derivative of f6 at x = %g is: %g"%(x,f6_grad_analytical))The analytical derivative of f6 at x = 0.5 is: 3.95703
In [12]:
import autograd.numpy as np
from autograd import grad
def f7(n): # Assume that n is an integer
if n == 1 or n == 0:
return 1
else:
return n*f7(n-1)
f7_grad = grad(f7)
n = 2.0
print("The computed derivative of f7 at n = %d is: %g"%(n,f7_grad(n)))
# The function f7 is an implementation of the factorial of n.
# By using the product rule, one can find that the derivative is:
f7_grad_analytical = 0
for i in range(int(n)-1):
tmp = 1
for k in range(int(n)-1):
if k != i:
tmp *= (n - k)
f7_grad_analytical += tmp
print("The analytical derivative of f7 at n = %d is: %g"%(n,f7_grad_analytical))The computed derivative of f7 at n = 2 is: 1 The analytical derivative of f7 at n = 2 is: 1
In [13]:
import autograd.numpy as np
from autograd import grad
def f8(x): # Assume x is an array
x[2] = 3
return x*2
f8_grad = grad(f8)
x = 8.4
print("The derivative of f8 is:",f8_grad(x))[0;31m---------------------------------------------------------------------------[0m [0;31mTypeError[0m Traceback (most recent call last) Input [0;32mIn [13][0m, in [0;36m<cell line: 11>[0;34m()[0m [1;32m 7[0m f8_grad [38;5;241m=[39m grad(f8) [1;32m 9[0m x [38;5;241m=[39m [38;5;241m8.4[39m [0;32m---> 11[0m [38;5;28mprint[39m([38;5;124m"[39m[38;5;124mThe derivative of f8 is:[39m[38;5;124m"[39m,[43mf8_grad[49m[43m([49m[43mx[49m[43m)[49m) File [0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/autograd/wrap_util.py:20[0m, in [0;36munary_to_nary.<locals>.nary_operator.<locals>.nary_f[0;34m(*args, **kwargs)[0m [1;32m 18[0m [38;5;28;01melse[39;00m: [1;32m 19[0m x [38;5;241m=[39m [38;5;28mtuple[39m(args[i] [38;5;28;01mfor[39;00m i [38;5;129;01min[39;00m argnum) [0;32m---> 20[0m [38;5;28;01mreturn[39;00m [43munary_operator[49m[43m([49m[43munary_f[49m[43m,[49m[43m [49m[43mx[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[43mnary_op_args[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mnary_op_kwargs[49m[43m)[49m File [0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/autograd/differential_operators.py:25[0m, in [0;36mgrad[0;34m(fun, x)[0m [1;32m 18[0m [38;5;129m@unary_to_nary[39m [1;32m 19[0m [38;5;28;01mdef[39;00m [38;5;21mgrad[39m(fun, x): [1;32m 20[0m [38;5;250m [39m[38;5;124;03m"""[39;00m [1;32m 21[0m [38;5;124;03m Returns a function which computes the gradient of `fun` with respect to[39;00m [1;32m 22[0m [38;5;124;03m positional argument number `argnum`. The returned function takes the same[39;00m [1;32m 23[0m [38;5;124;03m arguments as `fun`, but returns the gradient instead. The function `fun`[39;00m [1;32m 24[0m [38;5;124;03m should be scalar-valued. The gradient has the same type as the argument."""[39;00m [0;32m---> 25[0m vjp, ans [38;5;241m=[39m [43m_make_vjp[49m[43m([49m[43mfun[49m[43m,[49m[43m [49m[43mx[49m[43m)[49m [1;32m 26[0m [38;5;28;01mif[39;00m [38;5;129;01mnot[39;00m vspace(ans)[38;5;241m.[39msize [38;5;241m==[39m [38;5;241m1[39m: [1;32m 27[0m [38;5;28;01mraise[39;00m [38;5;167;01mTypeError[39;00m([38;5;124m"[39m[38;5;124mGrad only applies to real scalar-output functions. [39m[38;5;124m"[39m [1;32m 28[0m [38;5;124m"[39m[38;5;124mTry jacobian, elementwise_grad or holomorphic_grad.[39m[38;5;124m"[39m) File [0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/autograd/core.py:10[0m, in [0;36mmake_vjp[0;34m(fun, x)[0m [1;32m 8[0m [38;5;28;01mdef[39;00m [38;5;21mmake_vjp[39m(fun, x): [1;32m 9[0m start_node [38;5;241m=[39m VJPNode[38;5;241m.[39mnew_root() [0;32m---> 10[0m end_value, end_node [38;5;241m=[39m [43mtrace[49m[43m([49m[43mstart_node[49m[43m,[49m[43m [49m[43mfun[49m[43m,[49m[43m [49m[43mx[49m[43m)[49m [1;32m 11[0m [38;5;28;01mif[39;00m end_node [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m: [1;32m 12[0m [38;5;28;01mdef[39;00m [38;5;21mvjp[39m(g): [38;5;28;01mreturn[39;00m vspace(x)[38;5;241m.[39mzeros() File [0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/autograd/tracer.py:10[0m, in [0;36mtrace[0;34m(start_node, fun, x)[0m [1;32m 8[0m [38;5;28;01mwith[39;00m trace_stack[38;5;241m.[39mnew_trace() [38;5;28;01mas[39;00m t: [1;32m 9[0m start_box [38;5;241m=[39m new_box(x, t, start_node) [0;32m---> 10[0m end_box [38;5;241m=[39m [43mfun[49m[43m([49m[43mstart_box[49m[43m)[49m [1;32m 11[0m [38;5;28;01mif[39;00m isbox(end_box) [38;5;129;01mand[39;00m end_box[38;5;241m.[39m_trace [38;5;241m==[39m start_box[38;5;241m.[39m_trace: [1;32m 12[0m [38;5;28;01mreturn[39;00m end_box[38;5;241m.[39m_value, end_box[38;5;241m.[39m_node File [0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/autograd/wrap_util.py:15[0m, in [0;36munary_to_nary.<locals>.nary_operator.<locals>.nary_f.<locals>.unary_f[0;34m(x)[0m [1;32m 13[0m [38;5;28;01melse[39;00m: [1;32m 14[0m subargs [38;5;241m=[39m subvals(args, [38;5;28mzip[39m(argnum, x)) [0;32m---> 15[0m [38;5;28;01mreturn[39;00m [43mfun[49m[43m([49m[38;5;241;43m*[39;49m[43msubargs[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m Input [0;32mIn [13][0m, in [0;36mf8[0;34m(x)[0m [1;32m 3[0m [38;5;28;01mdef[39;00m [38;5;21mf8[39m(x): [38;5;66;03m# Assume x is an array[39;00m [0;32m----> 4[0m x[[38;5;241m2[39m] [38;5;241m=[39m [38;5;241m3[39m [1;32m 5[0m [38;5;28;01mreturn[39;00m x[38;5;241m*[39m[38;5;241m2[39m [0;31mTypeError[0m: 'ArrayBox' object does not support item assignment
In [14]:
import autograd.numpy as np
from autograd import grad
def f9(a): # Assume a is an array with 2 elements
b = np.array([1.0,2.0])
return a.dot(b)
f9_grad = grad(f9)
x = np.array([1.0,0.0])
print("The derivative of f9 is:",f9_grad(x))In [15]:
import autograd.numpy as np
from autograd import grad
def f9_alternative(x): # Assume a is an array with 2 elements
b = np.array([1.0,2.0])
return np.dot(x,b) # The same as x_1*b_1 + x_2*b_2
f9_alternative_grad = grad(f9_alternative)
x = np.array([3.0,0.0])
print("The gradient of f9 is:",f9_alternative_grad(x))
# The analytical gradient of the dot product of vectors x and b with two elements (x_1,x_2) and (b_1, b_2) respectively
# w.r.t x is (b_1, b_2).In [16]:
a += b
a -= b
a*= b
a /=bIn [17]:
# Using Autograd to calculate gradients for OLS
from random import random, seed
import numpy as np
import autograd.numpy as np
import matplotlib.pyplot as plt
from autograd import grad
def CostOLS(beta):
return (1.0/n)*np.sum((y-X @ beta)**2)
n = 100
x = 2*np.random.rand(n,1)
y = 4+3*x+np.random.randn(n,1)
X = np.c_[np.ones((n,1)), x]
XT_X = X.T @ X
theta_linreg = np.linalg.pinv(XT_X) @ (X.T @ y)
print("Own inversion")
print(theta_linreg)
# Hessian matrix
H = (2.0/n)* XT_X
EigValues, EigVectors = np.linalg.eig(H)
print(f"Eigenvalues of Hessian Matrix:{EigValues}")
theta = np.random.randn(2,1)
eta = 1.0/np.max(EigValues)
Niterations = 1000
# define the gradient
training_gradient = grad(CostOLS)
for iter in range(Niterations):
gradients = training_gradient(theta)
theta -= eta*gradients
print("theta from own gd")
print(theta)
xnew = np.array([[0],[2]])
Xnew = np.c_[np.ones((2,1)), xnew]
ypredict = Xnew.dot(theta)
ypredict2 = Xnew.dot(theta_linreg)
plt.plot(xnew, ypredict, "r-")
plt.plot(xnew, ypredict2, "b-")
plt.plot(x, y ,'ro')
plt.axis([0,2.0,0, 15.0])
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
plt.title(r'Random numbers ')
plt.show()In [18]:
# Using Autograd to calculate gradients for OLS
from random import random, seed
import numpy as np
import autograd.numpy as np
import matplotlib.pyplot as plt
from autograd import grad
def CostOLS(beta):
return (1.0/n)*np.sum((y-X @ beta)**2)
n = 100
x = 2*np.random.rand(n,1)
y = 4+3*x#+np.random.randn(n,1)
X = np.c_[np.ones((n,1)), x]
XT_X = X.T @ X
theta_linreg = np.linalg.pinv(XT_X) @ (X.T @ y)
print("Own inversion")
print(theta_linreg)
# Hessian matrix
H = (2.0/n)* XT_X
EigValues, EigVectors = np.linalg.eig(H)
print(f"Eigenvalues of Hessian Matrix:{EigValues}")
theta = np.random.randn(2,1)
eta = 1.0/np.max(EigValues)
Niterations = 30
# define the gradient
training_gradient = grad(CostOLS)
for iter in range(Niterations):
gradients = training_gradient(theta)
theta -= eta*gradients
print(iter,gradients[0],gradients[1])
print("theta from own gd")
print(theta)
# Now improve with momentum gradient descent
change = 0.0
delta_momentum = 0.3
for iter in range(Niterations):
# calculate gradient
gradients = training_gradient(theta)
# calculate update
new_change = eta*gradients+delta_momentum*change
# take a step
theta -= new_change
# save the change
change = new_change
print(iter,gradients[0],gradients[1])
print("theta from own gd wth momentum")
print(theta)Warning:
Output truncated. This notebook contains too many cells to display efficiently.