2013-07-02 9 views
7

Jak mogę dokonać scipy's fmin_cg użyć jednej funkcji, która zwraca cost i gradient jako krotki? Problem z posiadaniem f dla kosztu i fprime dla gradientu jest taki, że być może będę musiał wykonać operację dwukrotnie (bardzo kosztowną), przez którą obliczane są grad i cost. Również dzielenie zmiennych między nimi może być kłopotliwe.Jak zwrócić koszt, grad jako krotka dla funkcji scipy fmin_cg

W programie Matlab jednak fmin_cg działa z jedną funkcją, która zwraca koszt i gradient jako krotkę. Nie rozumiem, dlaczego scipy's fmin_cg nie zapewnia takiej wygody.

góry dzięki ...

Odpowiedz

6

Można użyć scipy.optimize.minimize z jac=True. Jeśli nie jest to opcja dla jakiegoś powodu, to można spojrzeć na how it handles this situation:

class MemoizeJac(object): 
    """ Decorator that caches the value gradient of function each time it 
    is called. """ 
    def __init__(self, fun): 
     self.fun = fun 
     self.jac = None 
     self.x = None 

    def __call__(self, x, *args): 
     self.x = numpy.asarray(x).copy() 
     fg = self.fun(x, *args) 
     self.jac = fg[1] 
     return fg[0] 

    def derivative(self, x, *args): 
     if self.jac is not None and numpy.alltrue(x == self.x): 
      return self.jac 
     else: 
      self(x, *args) 
      return self.jac 

Ta klasa jest zawijany funkcję, która zwraca wartość funkcji i gradientu, zachowując pamięć jednego elementu i sprawdza, że ​​aby sprawdzić, czy już wie jego wynik. Zastosowanie:

fmemo = MemoizeJac(f, fprime) 
xopt = fmin_cg(fmemo, x0, fmemo.derivative) 

Najdziwniejsze o tym kodzie jest to, że zakłada f zawsze jest wywoływana przed fprime (ale nie każdy f wywołanie następuje przez fprime rozmowy). Nie jestem pewien, czy rzeczywiście to gwarantuje scipy.optimize, ale kod można łatwo zaadaptować, aby nie przyjąć takiego założenia. Wytrzymała wersja powyższego (niesprawdzone):

class MemoizeJac(object): 
    def __init__(self, fun): 
     self.fun = fun 
     self.value, self.jac = None, None 
     self.x = None 

    def _compute(self, x, *args): 
     self.x = numpy.asarray(x).copy() 
     self.value, self.jac = self.fun(x, *args) 

    def __call__(self, x, *args): 
     if self.value is not None and numpy.alltrue(x == self.x): 
      return self.value 
     else: 
      self._compute(x, *args) 
      return self.value 

    def derivative(self, x, *args): 
     if self.jac is not None and numpy.alltrue(x == self.x): 
      return self.jac 
     else: 
      self._compute(x, *args) 
      return self.jac 
+1

+1 miły sposób buforowania powrót ostatniego połączenia głównej! Nie byłoby zbyt trudno pokonać ostatnie potencjalne ograniczenie ("f" wywołane przed 'fprime'), prawda? – Jaime

+0

@Jime: nie, po prostu powtórz lewę użytą w 'derivative'. Zobacz zaktualizowaną odpowiedź. –

+0

Waw, to takie niesamowite rozwiązanie, właśnie przetestowałem mój kod z czymś takim jak 'minimize (fun = self._cost_grad, x0 = initial_theta, method = 'Newton-CG', options = {'maxiter': 20, 'disp ': True}, jac = True, args = (X, n_features, n_samples)) ', i mam niesamowite rezultaty. Parametr 'fun' oczekuje funkcji zwracającej (cost, grad) jako krotki, a' method' można po prostu zmienić, wykonując 'l_bfgs',' bfgs', 'cg' lub dowolny optymalizator w scipy. Stukrotne dzięki! Jestem zaskoczony, że ta odpowiedź nie jest powszechna. – Curious

Powiązane problemy