scipy-optimize-minimal не выполняет оптимизацию - КОНВЕРГЕНЦИЯ: NORM_OF_PROJECTED_GRADIENT _ <= _ PGTOL - PullRequest
0 голосов
/ 17 марта 2020

Я пытаюсь минимизировать функцию, определенную следующим образом:

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

, где переменные принимают следующую форму:

решение = двоичный массив

риск = массив чисел с плавающей запятой

стоимость = константа

Я знаю, что решение будет иметь вид:

решение = 1 если (риск > = порог)

решение = 0 в противном случае

Поэтому, чтобы свести к минимуму эту функцию, я могу предположить, что я преобразую утилиту функции в зависит только от этого порога. Мой прямой перевод на scipy следующий:

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} )

Это дает мне следующий результат:

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])

Однако я знаю, что результат неверный, потому что в этом случае он должен быть равен до стоимость . Кроме того, независимо от того, какой x0 я использую, он всегда возвращает его как результат. Глядя на результаты, я замечаю, что jacobian = 0 и неправильно вычисляет 1 итерацию.

Более подробное изучение функции. Я строю это и наблюдаю, что оно не выпукло в пределах границ, но мы можем ясно видеть минимум в 0.1. Однако независимо от того, насколько я настроил границы, чтобы они были только в выпуклой части, результат остается тем же.

Function plot

Что я мог сделать минимизировать эту функцию?

1 Ответ

0 голосов
/ 17 марта 2020

Сообщение об ошибке говорит вам, что в какой-то момент градиент был слишком мал и, следовательно, численно такой же, как ноль. Это, вероятно, связано с порогом, который вы делаете, когда вычисляете свой selection_list. Там вы говорите float(risk[i]) >= threshold, который имеет производную 0 почти везде. Следовательно, почти каждое начальное значение будет давать вам предупреждение, которое вы получите.

Решением может быть применение некоторого сглаживания к операции порогового значения. Поэтому вместо float(risk[i]) >= threshold вы должны использовать непрерывную функцию:

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

С помощью этой функции вы можете express операцию порогового значения как g((risk[i] - threshold)/a), что является параметром a. Чем больше a, тем ближе эта измененная функция ошибок к тому, что вы делаете до сих пор. Что-то вроде a=20 или около того, вы, вероятно, будете иметь почти то же самое, что и сейчас. Поэтому вы должны получить последовательность решений, где вы начинаете с a=1, а затем принимаете это решение в качестве начального значения для той же проблемы с a=2, принимаете это решение в качестве начального значения для проблемы с a=4 и скоро. В какой-то момент вы заметите, что изменение a больше не меняет решение, и все готово.

...