Подтвердить что ты не робот

Integer размер шага в scipy оптимизирует минимизацию

У меня есть алгоритм компьютерного зрения, который я хочу настроить с помощью scipy.optimize.minimize. Прямо сейчас я хочу только настроить два параметра, но количество параметров может в конечном итоге расти, поэтому я хотел бы использовать технику, которая может выполнять поиски градиентов высокого уровня. Реализация Nelder-Mead в SciPy показалась хорошей подгонкой.

Я получил весь код, но кажется, что функция минимизации действительно хочет использовать значения с плавающей запятой с размером шага, который меньше единицы. Текущий набор параметров - это целые числа и один размер шага один а второй имеет размер шага два (то есть значение должно быть нечетным, если это не то, что я пытаюсь оптимизировать, преобразует его в нечетное число). Примерно один параметр - размер окна в пикселях, а другой параметр - порог (значение от 0 до 255).

Для чего я использую новую сборку scipy из репозитория git. Кто-нибудь знает, как сказать scipy использовать определенный размер шага для каждого параметра? Есть ли способ катить мою функцию градиента? Есть ли scipy флаг, который мог бы помочь мне? Я знаю, что это можно сделать с помощью простой развертки параметров, но в конечном итоге я бы хотел применить этот код к гораздо большим наборам параметров.

Сам код мертв просто:

import numpy as np
from scipy.optimize import minimize
from ScannerUtil import straightenImg 
import bson

def doSingleIteration(parameters):
    # do some machine vision magic
    # return the difference between my value and the truth value

parameters = np.array([11,10])
res = minimize( doSingleIteration, parameters, method='Nelder-Mead',options={'xtol': 1e-2, 'disp': True,'ftol':1.0,}) #not sure if these params do anything
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
print res

Вот как выглядит мой вывод. Как вы можете видеть, мы повторяем много прогонов и не получаем нигде в минимизации.

*+++++++++++++++++++++++++++++++++++++++++
[ 11.  10.]  <-- Output from scipy minimize
{'block_size': 11, 'degree': 10} <-- input to my algorithm rounded and made int
+++++++++++++++++++++++++++++++++++++++++
120  <-- output of the function I am trying to minimize
+++++++++++++++++++++++++++++++++++++++++
[ 11.55  10.  ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.   10.5]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.55   9.5 ]
{'block_size': 11, 'degree': 9}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.1375  10.25  ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.275  10.   ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.    10.25]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.275   9.75 ]
{'block_size': 11, 'degree': 9}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
~~~
SNIP 
~~~
+++++++++++++++++++++++++++++++++++++++++
[ 11.         10.0078125]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
Optimization terminated successfully.
         Current function value: 120.000000
         Iterations: 7
         Function evaluations: 27
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  status: 0
    nfev: 27
 success: True
     fun: 120.0
       x: array([ 11.,  10.])
 message: 'Optimization terminated successfully.'
     nit: 7*
4b9b3361

Ответ 1

Предполагая, что функция минимизации сколь угодно сложна (нелинейна), в общем случае это очень сложная проблема. Невозможно гарантировать оптимальное решение, если вы не попробуете все возможные варианты. Я не знаю, есть ли целочисленный нелинейный оптимизатор (несколько сомневаюсь в нем), и я предполагаю, что вы знаете, что Nelder-Mead должен работать нормально, если это была непрерывная функция.

Изменить: учитывая комментарий от @Dougal, я просто добавлю здесь: сначала создайте грубый + мелкий сетчатый поиск, если вам захочется попробовать, если ваш Nelder-Mead работает (и сходится быстрее), приведенные ниже пункты могут помочь...

Но, может быть, некоторые моменты, которые помогают:

  • Учитывая, что ограничение целого целого является очень сложным, возможно, было бы возможно сделать некоторую простую интерполяцию, чтобы помочь оптимизатору. Он все равно должен сходиться к целочисленному решению. Конечно, это требует вычисления дополнительных очков, но это может решить многие другие проблемы. (даже в линейном целочисленном программировании его общее для решения системы без ограничений в первой AFAIK)
  • Nelder-Mead начинается с N + 1 очков, они сложны в scipy (по крайней мере, более старые версии) до (1+0.05) * x0[j] (для j во всех измерениях, если x0[j] равно 0), которые вы увидите в ваших первых этапах оценки. Возможно, они могут поставляться в более новых версиях, иначе вы могли бы просто изменить/скопировать scipy-код (это чистый python) и установить его в нечто более разумное. Или, если вы считаете, что это проще, масштабируйте все входные переменные так, чтобы (1 + 0,05) * x0 имел разумный размер.
  • Возможно, вам следует кэшировать все оценки функций, так как если вы используете Nelder-Mead, я бы предположил, что вы всегда можете столкнуться с оценкой дубликата (по крайней мере, в конце).
  • Вам нужно проверить, насколько вероятно, что Nelder-Mead просто сжимается до одного значения и отказывается от него, потому что он всегда находит тот же результат.
  • Обычно вам нужно проверить, хорошо ли вообще работает ваша функция... Эта оптимизация обречена, если функция не меняется сглаженно над пространством параметров, и даже тогда она может легко запускаться в локальные минимумы, если вы должны иметь эти, (так как вы кэшировали все оценки - см. 2. - вы могли бы по крайней мере очертить их и посмотреть на пейзаж ошибок, не требуя дополнительных решений).

Ответ 2

К сожалению, встроенные инструменты оптимизации Scipy не легко позволяют это сделать. Но никогда не бойтесь; это похоже на то, что у вас выпуклая проблема, и поэтому вы должны иметь возможность найти уникальный оптимум, даже если он не будет математически красивым.

Два варианта, которые я реализовал для различных задач, - это создание пользовательского алгоритма спуска градиента и использование бисекции по ряду одномерных задач. Если вы выполняете кросс-валидацию в своей настройке, ваша функция потерь, к сожалению, не будет гладкой (из-за шума от перекрестной проверки на разных наборах данных), но будет в целом выпуклой.

Чтобы реализовать градиентный спуск численно (без аналитического метода для оценки градиента), выберите тестовую точку и вторую точку, находящуюся delta, от вашей тестовой точки во всех измерениях. Оценка вашей функции потерь в этих двух точках может позволить вам численно вычислить локальный субградиент. Важно, чтобы delta был достаточно большим, чтобы он выходил за пределы локальных минимумов, создаваемых шумом перекрестной проверки.

Более медленная, но потенциально более надежная альтернатива заключается в реализации бисекции для каждого параметра, который вы тестируете. Если вы знаете, что проблема совместно выпуклых по вашим двум параметрам (или n параметрам), вы можете разделить это на n одномерных задач оптимизации и написать алгоритм деления пополам, который рекурсивно набирает оптимальные параметры. Это может помочь справиться с некоторыми типами квазивыпуклости (например, если ваша функция потерь принимает значение фонового шума для части своего домена и является выпуклой в другой области), но требует хорошего предположения относительно границ начальной итерации.

Если вы просто привязываете запрошенные значения x к целочисленной сетке без фиксации xtol для сопоставления с этим грид-кодом, вы рискуете, что решатель запросит две точки в ячейке сетки, получив одинаковое выходное значение и сделав вывод, что это как минимум.

Несложный ответ, к сожалению.

Ответ 3

Привяжите свои поплавки x, y (a.k.a. winsize, threshold) к целочисленной сетке внутри вашей функции, например:

def func( x, y ):
    x = round( x )
    y = round( (y - 1) / 2 ) * 2 + 1  # 1 3 5 ...
    ...

Затем Nelder-Mead будет видеть значения функции только в сетке и должен дать вам почти целое число x, y.

(Если вы хотите разместить свой код где-нибудь, я ищу тестовые примеры для Nelder-Mead с перезагрузками.)

Ответ 4

Теперь метод минимизации Nelder-Mead позволяет указать начальные точки вершины симплексов, поэтому вы можете установить точки симплекса далеко друг от друга, и симплекс будет вращаться вокруг и находить минимум и сходиться, когда размер симплекса падает ниже 1.

https://docs.scipy.org/doc/scipy/reference/optimize.minimize-neldermead.html#optimize-minimize-neldermead