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
梯度[k] = (f(*((xk+d,)+args)) - f0) / d[k]
ValueError:操作数无法与形状 (4,5) (4) 一起广播
您收到的错误是因为
check_gradient
只接受平面点数组。如果您使用形状为 x0
的数组 (20,)
而不是 (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])
所以看来其他地方的非平面数组也确实还没有准备好。但无论如何,实现都非常糟糕:您应该提交错误报告。
关于你的代码(需要 Vectorization - 如果你确实需要 - 做 flatten() 或 ravel()):
import numpy as np
from scipy import optimize
x = np.array(np.random.uniform(-30, 30, (4, 5)))
def sigmoid_obj(xx, gradient= False):
print(xx)
x= np.array(xx).ravel() # to vectorize further
y = 1 / (1 + np.exp(-1*x))
res= np.multiply(y, 1 - y) if gradient else y
return sum(res)
eps = np.sqrt(np.finfo(float).eps)
def num_gradient(x):
return optimize.approx_fprime(x.ravel(), sigmoid_obj, eps) # to vectorize
error = optimize.check_grad(sigmoid_obj, num_gradient, x.ravel() ) # to vectorize
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 !! var(s) used in objective_func
eps = np.sqrt(np.finfo(float).eps)
error = optimize.check_grad(sigmoid_obj, num_gradient, x0 )
print(error)
但是你的输入形状即使是
(0,0(0,0))
也会引发 ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part
。无论如何,你最好在优化之前转换你的特征数组。
附注
optimize.check_grad()
内部使用optimize.approx_fprime()
,最新的计算为一个点...或者可以通过分析计算它(使用SymPy或手动,如文档示例中所示):
将 numpy 导入为 np
从 scipy 导入优化
def func(x):
return x[0]**2 - 0.5 * x[1]**3
def grad(x):
return [2 * x[0], -1.5 * x[1]**2]
from scipy.optimize import check_grad
print(check_grad(func, grad, [1.5, -1.5]))