使用Scipy检查梯度
我想用 scipy.optimize.check_grad
来检查我实现的 sigmoid 函数 的梯度;这是我的 Python 函数:
def sigmoid(x, gradient=False):
y = 1 / (1 + numpy.exp(-x))
return numpy.multiply(y, 1 - y) if gradient else y
这里是传给 check_grad
的参数和调用:
x0 = numpy.random.uniform(-30, 30, (4, 5))
func = sigmoid
grad = lambda x: sigmoid(x, gradient=True)
error = scipy.optimize.check_grad(func, grad, x0)
我遇到了下面的错误。这个形状不匹配是指操作 xk+d
。你知道可能是什么原因吗?
文件 "scipy\optimize\optimize.py",第 597 行,在 approx_fprime
grad[k] = (f(*((xk+d,)+args)) - f0) / d[k]
ValueError: 操作数无法一起广播,形状为 (4,5) (4)
2 个回答
关于你的代码(需要向量化 - 如果需要的话,可以使用flatten()或ravel()):
import numpy as np
from scipy import optimize
def sigmoid_obj(x, gradient=False):
y = 1 / (1 + np.exp(-1*x))
res= np.multiply(y, 1 - y) if gradient else y
return sum(res) # must return scalar !
def num_gradient(x):
return optimize.approx_fprime(x, sigmoid_obj, eps)
x = np.array(np.random.uniform(-30, 30, (4, 5))) # shape(4,5)
##x0= [0,] # guess !! var(s) used in objective_func
eps = np.sqrt(np.finfo(float).eps)
error = optimize.check_grad(sigmoid_obj, num_gradient, x.flatten() )
print(error)
但通常情况下,objective_func
是用来处理一个 x_point
到y_target
的投影,从x0=guess
开始优化。在你代码的最简单版本中:
import numpy as np
from scipy import optimize
def sigmoid_obj(x, gradient=False):
y = 1 / (1 + np.exp(-1*x))
res= np.multiply(y, 1 - y) if gradient else y
return res
def num_gradient(x):
return optimize.approx_fprime(x, sigmoid_obj, eps)
x = np.array(np.random.uniform(-30, 30, (4, 5)))
x0= [0,] # guess !! init_var(s) used in objective_func
eps = np.sqrt(np.finfo(float).eps)
error = optimize.check_grad(sigmoid_obj, num_gradient, x0 )
print(error)
? 如果你的初始猜测只假设为一个x,那么guess=x0
必须是[0,]
附注:optimize.check_grad()
内部使用optimize.approx_fprime()
,后者是通过一个点投影到y来计算的[就像是局部的]... 或者可以通过解析的方法计算(使用SymPy或者手动,像文档中的例子那样):
def func(x):
return x[0]**2 - 0.5 * x[1]**3
def grad(x):
return [2 * x[0], -1.5 * x[1]**2] # here can be numerical_gradient=optimize.approx_fprime(...)
from scipy.optimize import check_grad
print(check_grad(func, grad, x0= [1.5, -1.5]))
你遇到的错误是因为 check_gradient
只接受一维的数组。也就是说,你应该用一个形状为 (20,)
的数组 x0
,而不是 (4, 5)
的数组。不过,它还是不行!
这是我安装的 approx_fprime
的实现代码(scipy.__version__ = '0.9.0'
):
def approx_fprime(xk,f,epsilon,*args):
f0 = f(*((xk,)+args))
grad = numpy.zeros((len(xk),), float)
ei = numpy.zeros((len(xk),), float)
for k in range(len(xk)):
ei[k] = epsilon
grad[k] = (f(*((xk+ei,)+args)) - f0)/epsilon
ei[k] = 0.0
return grad
我看了好几遍,真不敢相信这样的糟糕代码会出现在 scipy 里,我觉得我一定漏掉了什么……但我担心这就是错的。如果你把它换成:
def approx_fprime(xk,f,epsilon,*args):
return (f(*((xk + epsilon,) + args)) - f(*((xk,) + args))) / epsilon
现在对我来说可以用了。用 x0.shape = (20,)
我得到了:
In [2]: error
Out[2]: 1.746097524556073e-08
而用 x0.shape = (4, 5)
:
In [4]: error
Out[4]:
array([ 1.03560895e-08, 1.45994321e-08, 8.54143390e-09,
1.09225833e-08, 9.85988655e-09])
所以看来在其他地方也确实不支持非一维数组。不过无论如何,这个实现都是有问题的:你应该提交一个错误报告。