2013-07-02 5 views
7

Come posso rendere scipy fmin_cg utilizzare una funzione che restituisce cost e gradient come una tupla? Il problema di avere f per il costo e fprime per il gradiente, è che potrebbe essere necessario eseguire un'operazione due volte (molto costosa) mediante la quale vengono calcolati i valori grad e cost. Inoltre, condividere le variabili tra di loro potrebbe essere problematico.Come restituire il costo, grad come tupla per la funzione fmin_cg di scipy

In Matlab tuttavia, fmin_cg funziona con una funzione che restituisce il costo e il gradiente come tupla. Non vedo perché lo fmin_cg di Scipy non sia in grado di fornire tale comodità.

Grazie in anticipo ...

risposta

6

È possibile utilizzare scipy.optimize.minimize con jac=True. Se questo non è un'opzione per qualche motivo, allora si può guardare 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 

Questa classe avvolge una funzione che restituisce valore della funzione e di pendenza, mantenendo una cache di un elemento e controlla che per vedere se si sa già il suo risultato. Uso:

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

La cosa strana di questo codice è che esso presuppone f viene sempre chiamato prima fprime (ma non tutti i f chiamata è seguito da un fprime chiamata). Non sono sicuro che lo standard scipy.optimize lo garantisca, ma il codice può essere facilmente adattato per non dare quell'assunzione, comunque. Versione robusta di quanto sopra (non testata ):

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 Bel modo di caching ritorno dell'ultima chiamata! Non sarebbe troppo difficile superare quell'ultima limitazione potenziale ('f' chiamato prima di' fprime'), giusto? – Jaime

+0

@Jaime: no, basta ripetere il trucco usato in 'derivata'. Vedi risposta aggiornata. –

+0

Waw, questa è una soluzione incredibile, ho appena testato il mio codice con qualcosa come 'minimizza (fun = self._cost_grad, x0 = initial_theta, method = 'Newton-CG', options = {'maxiter': 20, 'disp ': True}, jac = True, args = (X, n_features, n_samples)) ', e ho ottenuto risultati fantastici. Il parametro 'fun' si aspetta una funzione che ritorna (cost, grad) come tupla, e' method' può essere semplicemente cambiato per eseguire 'l_bfgs',' bfgs', 'cg' o qualsiasi ottimizzatore disponibile in scipy. Grazie mille! Sono sorpreso che questa risposta non sia prevalente. – Curious

Problemi correlati