2012-08-29 8 views
13

Mam algorytm wizji komputerowej Chcę nastroić za pomocą scipy.optimize.minimize. Teraz chcę tylko dostroić dwa parametry, ale liczba parametrów może w końcu wzrosnąć, więc chciałbym użyć techniki, która może wykonywać wielowymiarowe wyszukiwania gradientowe. Implementacja Neldera-Meada w SciPy wydawała się pasować.Integer step size in scipy optimize minimize

Mam ustawiony kod, ale wygląda na to, że funkcja minimalizacji naprawdę chce używać wartości zmiennoprzecinkowych o rozmiarze kroku mniejszym niż jeden. Bieżący zestaw parametrów to liczby całkowite i jeden ma wielkość kroku jeden i drugi ma wielkość kroku równą dwóm (tj. wartość musi być nieparzysta, jeśli nie jest to coś, co próbuję zoptymalizować, przekonwertuję ją na nieparzystą liczbę). Z grubsza jeden parametr to rozmiar okna w pikselach, a drugim parametrem jest próg (wartość od 0-255).

Za to, co jest warte, używam nowej wersji scipy z repozytorium git. Czy ktokolwiek wie, jak powiedzieć scipie, aby używał określonego rozmiaru kroku dla każdego parametru? Czy istnieje sposób, w jaki mogę przetasować własną funkcję gradientu? Czy istnieje flaga scipy, która może mi pomóc? Mam świadomość, że można to zrobić za pomocą prostego przeciągnięcia parametru, ale ostatecznie chciałbym zastosować ten kod do znacznie większych zestawów parametrów.

Sam kod jest martwy prosta:

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 

To co moje wyjście wygląda. Jak widać, powtarzamy wiele przebiegów i nie osiągamy żadnego minimum w minimalizacji.

*+++++++++++++++++++++++++++++++++++++++++ 
[ 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* 
+2

Według Dokumenty metoda scipy w Neldera-Mead wykorzystuje algorytm programowania liniowego Simplex. Polega na użyciu nie zintegrowanych punktów/stopni w celu optymalizacji funkcji.Nie jestem zaznajomiony z SciPy w ogóle, więc może istnieć opcja konfiguracji, aby zrobić to, co chcesz. Możesz również zajrzeć do programowania liczb całkowitych (http://en.wikipedia.org/wiki/Integer_programming), ponieważ brzmi to jak to, co próbujesz osiągnąć. –

+0

@EricG właściwie myślę, że to tylko mieszanka nazw, "Simplex" Neldera-Meada działa z geometryczną strukturą Simplex. Nie ma nic wspólnego z algorytmem Simplex z programowania liniowego, a to jest nieliniowa optymalizacja. – seberg

+1

Z powodu takich problemów, dostrajanie parametrów dla algorytmów ML jest zwykle wykonywane tylko za pomocą wyszukiwania w sieci (często na siatce logarytmicznej, ale dla parametrów, które nie wydają się konieczne). Możesz wykonać przeszukiwanie z grubej siatki, aby najpierw znaleźć dobry region, a następnie dokładniejsze wyszukiwanie siatki we wspomnianym regionie. – Dougal

Odpowiedz

5

Zakładając, że funkcja minimalizacji jest dowolnie złożona (nieliniowa), jest to ogólnie bardzo trudny problem. Nie można zagwarantować, że zostanie rozwiązany optymalnie, chyba że spróbujesz każdej możliwej opcji. Robię , a nie wiem, czy są jakieś nieliniowe optymalizatory z ograniczoną liczbą całkowitą (nieco wątpię) i zakładam, że wiesz, że Nelder-Mead powinien działać dobrze, jeśli jest funkcją ciągłą.

Edycja: Biorąc pod uwagę komentarz z @Dougal, po prostu dodam tutaj: Najpierw skonfiguruj zgrubne + dokładne wyszukiwanie siatki, jeśli będziesz wtedy chciał spróbować, jeśli Twoja Nelder-Mead działa (i zbiegają się szybciej), poniższe punkty mogą pomóc ...

Ale może kilka punktów, które pomogą:

  1. Biorąc pod uwagę jak cała całkowitą ograniczeniem jest bardzo trudne, być może będzie to opcja zrobić jakąś prostą interpolację, aby pomóc optymalizator. Powinien nadal zbiegać się do rozwiązania integer. Oczywiście wymaga to obliczenia dodatkowych punktów, ale może rozwiązać wiele innych problemów. (nawet w programowaniu liniowym całkowitym wspólnym dla rozwiązania systemu AFAIK jako pierwszego)
  2. Nelder-Mead zaczyna się od N + 1 punktów, są one twardo połączone w scipy (przynajmniej starsze wersje) z (1+0.05) * x0[j] (dla j we wszystkich wymiarach, chyba że x0[j] jest 0), które zobaczysz w swoich pierwszych krokach oceny. Może te mogą być dostarczone w nowszych wersjach, w przeciwnym razie możesz po prostu zmienić/skopiować kod scipy (to jest czysty python) i ustawić go na coś bardziej sensownego. Lub jeśli uważasz, że jest to prostsze, skaluj wszystkie zmienne wejściowe w dół tak, aby (1 + 0,05) * x0 miało rozsądny rozmiar.
  3. Może powinieneś przechowywać w pamięci podręcznej wszystkie oceny funkcji, ponieważ jeśli użyjesz Nelder-Mead, zgaduję, że zawsze możesz uruchomić analizę duplikatów (przynajmniej na końcu).
  4. Musisz sprawdzić, jak bardzo prawdopodobne jest, że Nelder-Mead zmniejszy się do jednej wartości i zrezygnuje, ponieważ zawsze znajdzie taki sam wynik.
  5. Generalnie musisz sprawdzić, czy twoja funkcja jest w ogóle dobrze zachowana ... Ta optymalizacja jest skazana na zagładę, jeśli funkcja nie zmienia się płynnie w przestrzeni parametrów, a nawet wtedy może łatwo przejść do lokalnych minimów, jeśli powinieneś mieć te . (odkąd zbuforowałeś wszystkie oceny - patrz 2. - możesz przynajmniej je wykreślić i spojrzeć na krajobraz błędu bez potrzeby wykonywania dodatkowych wykrętów)
1

Przyciągaj swoje pływaki x, y (inaczej wygrywa, próg) do siatki całkowitej wewnątrz swojej funkcji, na przykład:

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

Następnie Nelder-Mead zobaczy wartości funkcji tylko na starcie, a powinny dać niemal całkowitą x, y.

(Jeśli chcesz dbać aby umieścić swój kod gdzieś, szukam przypadków testowych dla Nelder-Mead z ponownym uruchomieniu.)

2

Niestety scipy wbudowanej narzędzi optymalizacyjnych nie pozwalają łatwo dla tego. Ale nigdy się nie bój; brzmi to jak problem wypukły, więc powinieneś być w stanie znaleźć unikalne optimum, nawet jeśli nie będzie matematycznie ładne.

Dwie opcje, które zaimplementowałem dla różnych problemów, to tworzenie niestandardowego algorytmu gradientowego i zastosowanie bisekcji w serii jednoznacznych problemów. Jeśli robisz sprawdzanie krzyżowe w twoim dostrajaniu, twoja funkcja strat niestety nie będzie płynna (z powodu szumu z krzyżowej walidacji na różnych zestawach danych), ale będzie ogólnie wypukła.

Aby wprowadzić pochylenie gradientowe numerycznie (bez posiadania analitycznej metody oceny gradientu), wybierz punkt testowy i drugi punkt, który jest delta z dala od punktu testowego we wszystkich wymiarach. Ewaluacja funkcji straty w tych dwóch punktach może pozwolić na obliczenie liczbowe lokalnego subgradienta. Ważne jest, aby delta był wystarczająco duży, aby wykraczał poza lokalne minimalne wartości utworzone przez krzyżyk sprawdzania poprawności.

Wolniejszą, ale potencjalnie bardziej niezawodną alternatywą jest implementacja bisekcji dla każdego testowanego parametru. Jeśli wiesz, że problem ze wspólną wypukłością w twoich dwóch parametrach (lub parametrów n), możesz oddzielić to na jednoosobowe problemy optymalizacji i napisać algorytm bisekcji rekursywnie podążający za optymalnymi parametrami. Może to pomóc w radzeniu sobie z niektórymi rodzajami quasiconvexity (na przykład, jeśli funkcja utraty przyjmuje wartość szumu tła dla części swojej domeny i jest wypukła w innym regionie), ale wymaga dobrego odgadnięcia, co do granic początkowej iteracji.

Jeśli po prostu przystawki żądanych x wartości do siatki całkowitej bez mocowania xtol mapować do tego gridsize, istnieje ryzyko posiadające zażądania Solver dwóch punktów w komórce siatki, przyjmując taką samą wartość wyjściową, i stwierdzić, że jest minimum.

Niełatwo odpowiedzieć, niestety.