Created
March 28, 2018 14:00
-
-
Save glederrey/5db2be697d2784bd9a521bae7cda00dc to your computer and use it in GitHub Desktop.
Central Finite Differences for computing Gradient and Hessian
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import numpy as np | |
def gradient(x, func, eps=1e-6): | |
""" | |
Central finite differences for computing gradient | |
INPUT: | |
x: vector of parameters with shape (n, 1) | |
func: function to compute the gradient on. Take x as a parameter. | |
eps: parameter for the central differences | |
OUTPUT: | |
grad: vector with the values of the gradient with shape (n, 1) | |
""" | |
# Size the problem, i.e. nbr of parameters | |
n = len(x) | |
# Prepare the vector for the gradient | |
grad = np.zeros(n) | |
# Prepare the array to add epsilon to. | |
dx = np.zeros(n) | |
# Go through all parameters | |
for i in range(len(x)): | |
# Add epsilon to variate a parameter | |
dx[i] += eps | |
# Central finite differences | |
grad[i] = -(func(x+dx) - func(x-dx))/(2*eps) | |
# Set back to 0 | |
dx[i] = 0 | |
return grad | |
def hessian(x, func, eps=1e-6): | |
""" | |
Central finite differences for computing the hessian | |
INPUT: | |
x: vector of parameters with shape (n, 1) | |
func: function to compute the gradient on. Take x as a parameter. | |
eps: parameter for the central differences | |
OUTPUT: | |
grad: vector with the values of the gradient with shape (n, 1) | |
""" | |
# Size the problem, i.e. nbr of parameters | |
n = len(x) | |
# Prepare the vector for the gradient | |
hess = np.zeros((n,n)) | |
# Prepare the array to add epsilon to. | |
dx = np.zeros(n) | |
# Go through all parameters | |
for i in range(n): | |
# Add epsilon to variate a parameter | |
dx[i] += eps | |
# Compute the gradient with forward and backward difference | |
grad_plus = gradient(x+dx, func, eps) | |
grad_minus = gradient(x-dx, func, eps) | |
# Central finite difference | |
hess[i,:] = -(grad_plus - grad_minus)/(2*eps) | |
# Set back to 0 | |
dx[i] = 0 | |
return hess |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
import numpy as np | |
import numdifftools as nd | |
from cfd_grad_hess import gradient, hessian | |
# Create a function to test our implementation | |
func = lambda x: 3*x[0]**2 + 2*x[1]**3 + x[0]*x[1] + x[2] | |
# Parameter | |
x = [1,1,1] | |
print('Function value at [{}]: {}'.format(', '.join(map(str, x)), func(x))) | |
# Compute the gradient | |
start = time.time() | |
grad = gradient(x, func, eps=1e-4) | |
stop = time.time() | |
print('Gradient is equal to ({}) and has been computed in {:.2f}µs.'.format(', '.join(map(str, grad)), 1e6*(stop-start))) | |
# Compute the hessian | |
start = time.time() | |
hess = hessian(x, func, eps=1e-4) | |
stop = time.time() | |
print('Hessian is equal to ({}) and has been computed in {:.2f}µs.'.format(', '.join(map(str, hess)), 1e6*(stop-start))) | |
# Compare our implementation with numdifftools | |
start = time.time() | |
nd_hess = nd.Hessian(func) | |
nd_hess_vals = nd_hess(x) | |
stop = time.time() | |
print('Hessian from numdifftools is equal to ({}) and has been computed in {:.2f}µs.'.format(', '.join(map(str, nd_hess_vals)), 1e6*(stop-start))) | |
print('Norm of the difference between the two hessians: {:.2E}'.format(np.linalg.norm(hess - nd_hess_vals))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
After struggling to find a good finite difference implementation of a Hessian (and
numdifftools
being way too slow), I decided to share my own implementation.You can use the file
test.py
in order to test the implementation and compare it to the Hessian computed fromnumdifftools
. It's possible that you may have to change the parametereps
to get a better result. However, the smaller does not always mean the better result.