简体   繁体   中英

Finding gradient of an unknown function at a given point in Python

I am asked to write an implementation of the gradient descent in python with the signature gradient(f, P0, gamma, epsilon) where f is an unknown and possibly multivariate function, P0 is the starting point for the gradient descent, gamma is the constant step and epsilon the stopping criteria.

What I find tricky is how to evaluate the gradient of f at the point P0 without knowing anything on f . I know there is numpy.gradient but I don't know how to use it in the case where I don't know the dimensions of f . Also, numpy.gradient works with samples of the function, so how to choose the right samples to compute the gradient at a point without any information on the function and the point?

I'm assuming here, So how can i choose a generic set of samples each time I need to compute the gradient at a given point? means, that the dimension of the function is fixed and can be deduced from your start point.

Consider this a demo, using scipy's approx_fprime , which is an easier to use wrapper-method for numerical-differentiation and also used in scipy's optimizers when a jacobian is needed, but not given.

Of course you can't ignore the parameter epsilon, which can make a difference depending on the data.

(This code is also ignoring optimize's args-parameter which is usually a good idea; i'm using the fact that A and b are inside the scope here; surely not best-practice)

import numpy as np
from scipy.optimize import approx_fprime, minimize
np.random.seed(1)

# Synthetic data
A = np.random.random(size=(1000, 20))
noiseless_x = np.random.random(size=20)
b = A.dot(noiseless_x) + np.random.random(size=1000) * 0.01

# Loss function
def fun(x):
    return np.linalg.norm(A.dot(x) - b, 2)

# Optimize without any explicit jacobian
x0 = np.zeros(len(noiseless_x))
res = minimize(fun, x0)
print(res.message)
print(res.fun)

# Get numerical-gradient function
eps = np.sqrt(np.finfo(float).eps)
my_gradient = lambda x: approx_fprime(x, fun, eps)

# Optimize with our gradient
res = res = minimize(fun, x0, jac=my_gradient)
print(res.message)
print(res.fun)

# Eval gradient at some point
print(my_gradient(np.ones(len(noiseless_x))))

Output:

Optimization terminated successfully.
0.09272331925776327
Optimization terminated successfully.
0.09272331925776327
[15.77418041 16.43476772 15.40369129 15.79804516 15.61699104 15.52977276
 15.60408688 16.29286766 16.13469887 16.29916573 15.57258797 15.75262356
 16.3483305  15.40844536 16.8921814  15.18487358 15.95994091 15.45903492
 16.2035532  16.68831635]

Using:

# Get numerical-gradient function with a way too big eps-value
eps = 1e-3
my_gradient = lambda x: approx_fprime(x, fun, eps)

shows that eps is a critical parameter resulting in:

Desired error not necessarily achieved due to precision loss.
0.09323354898565098

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM