首页 > 解决方案 > scipy-optimize-minimize does not perform the optimization - CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL

问题描述

I am trying to minimize a function defined as follows:

utility(decision) = decision * (risk - cost)

where variables take the following form:

decision = binary array

risk = array of floats

cost = constant

I know the solution will take the form of:

decision = 1 if (risk >= threshold)

decision = 0 otherwise

Therefore, in order to minimize this function I can assume that I transform the function utility to depend only on this threshold. My direct translation to scipy is the following:

def utility(threshold,risk,cost):

     selection_list = [float(risk[i]) >= threshold for i in range(len(risk))]
     v = np.array(risk.astype(float)) - cost

     total_utility = np.dot(v, selection_list)

     return -1.0*total_utility

result = minimize(fun=utility, x0=0.2, args=(r,c),bounds=[(0,1)], options={"disp":True} )

This gives me the following result:

fun: array([-17750.44298655])  hess_inv: <1x1 LbfgsInvHessProduct with
dtype=float64>
jac: array([0.])   
message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
nfev: 2
nit: 0    status: 0   success: True
x: array([0.2])

However, I know the result is wrong because in this case it must be equal to cost. On top of that, no matter what x0 I use, it always returns it as the result. Looking at the results I observe that jacobian=0 and does not compute 1 iteration correctly.

Looking more thoroughly into the function. I plot it and observe that it is not convex on the limits of the bounds but we can clearly see the minimum at 0.1. However, no matter how much I adjust the bounds to be in the convex part only, the result is still the same.

Function plot

What could I do to minimize this function?

标签: optimizationscipyminimizationscipy-optimizescipy-optimize-minimize

解决方案


错误消息告诉您梯度在某个点太小,因此在数值上等于零。这可能是由于您在计算selection_list. 你说float(risk[i]) >= threshold,它几乎到处都有导数 0 。因此,几乎每个起始值都会为您提供收到的警告。

一种解决方案可能是对阈值操作进行一些平滑处理。float(risk[i]) >= threshold因此,您可以使用连续函数来代替:

def g(x):
    return 1./(1+np.exp(-x))

使用此函数,您可以将阈值操作表示为 g((risk[i] - threshold)/a),其中一个参数a。越大a,这个修改后的误差函数就越接近你目前所做的。在类似的a=20情况下,您可能会拥有与目前几乎相同的东西。因此,您将导出一系列解决方案,从 开始,a=1然后将该解决方案作为与 相同的问题的起始值a=2,将该解决方案作为与 的问题的起始值a=4,依此类推。在某些时候,您会注意到更改a不再更改解决方案,您就完成了。


推荐阅读