2012-05-23 2 views
4

J'utilise scipy.optimize.fmin_bfgs(f, init_theta, fprime) pour minimiser f, qui a un gradient fprime. Je calcule f et fprime en une seule fonction parce que la plupart du calcul est le même, donc il n'est pas nécessaire de le faire deux fois.scipy.optimize.fmin_bfgs seule fonction calcule à la fois f et fprime

Est-il possible d'appeler fmin_bfgs() en spécifiant une seule fonction qui renvoie à la fois f et fprime?

Répondre

4

Si vous essayez de économiser sur le temps de calcul plutôt que de simplement combiner le calcul de f et f' pour la commodité du code, il semble que vous avez besoin d'un wrapper supplémentaire autour de votre fonction pour mettre en cache les valeurs, car fmin_bfgs ne semblent vous permettre de passer une telle fonction (contrairement à d'autres fonctions d'optimisation).

Voici une façon de le faire, en conservant les 10 points les plus récemment évalués dans un petit cache. (Je ne suis pas sûr que les appels à cette fonction doivent être thread-safe: probablement pas, mais si oui, vous aurez probablement besoin d'ajouter un peu de verrouillage ici, je suppose.)

def func_wrapper(f, cache_size=10): 
    evals = {} 
    last_points = collections.deque() 

    def get(pt, which): 
     s = pt.tostring() # get binary string of numpy array, to make it hashable 
     if s not in evals: 
      evals[s] = f(pt) 
      last_points.append(s) 
      if len(last_points) >= cache_size: 
       del evals[last_points.popleft()] 
     return evals[s][which] 

    return functools.partial(get, which=0), functools.partial(get, which=1) 

Si nous alors faites

>>> def f(x): 
... print "evaluating", x 
... return (x-3)**2, 2*(x-3) 

>>> f_, fprime = func_wrapper(f) 

>>> optimize.fmin_bfgs(f_, 1000, fprime) 
evaluating [ 994.93480441] 
evaluating [ 974.67402207] 
evaluating [ 893.63089268] 
evaluating [ 665.93446894] 
evaluating [ 126.99931561] 
evaluating [ 3.] 
Optimization terminated successfully. 
     Current function value: 0.000000 
     Iterations: 4 
     Function evaluations: 7 
     Gradient evaluations: 7 
array([ 3.]) 

nous pouvons voir que nous ne répétons aucune évaluation.

+0

Belle implémentation! J'ai pensé à essayer d'utiliser cette stratégie, mais je n'étais pas sûr de savoir comment faire un tableau lavable. En outre, j'espérais qu'il y avait une fonctionnalité déjà construite dans scipy.optimize pour gérer cela.Apparemment, d'autres ont demandé la fonctionnalité parce qu'elle est supposée faire partie de Scipy 0.11, qui n'est pas encore publiée. Merci de fournir ce motif de conception! – user1389890

1

Supposons que vous ayez une fonction Python f(x) qui retourne à la fois la valeur de la fonction et le gradient:

In [20]: def f(x): 
    ....:  return (x-3)**2, 2*(x-3) 

Ensuite, il suffit de passer les sorties séparément comme ceci:

In [21]: optimize.fmin_bfgs(lambda x: f(x)[0], 1000, lambda x: f(x)[1]) 
Optimization terminated successfully. 
     Current function value: 0.000000 
     Iterations: 4 
     Function evaluations: 7 
     Gradient evaluations: 7 
Out[21]: array([ 3.]) 
+0

Cela n'enregistre cependant aucun calcul, il jette juste le résultat de l'un ou de l'autre. Ce qui, je suppose, ne compte que si BFGS demande fréquemment la valeur de la fonction et le dégradé au même point: je ne connais pas l'algorithme, mais il semble que ce soit le cas. – Dougal

+0

C'est bien. 'lambda x: f (x) [0]' est un 'callable'. C'est tout ce dont 'fmin_bfgs' a besoin. Il ne se soucie pas des internes de cet appelable. –

+0

Oui: votre approche va évidemment marcher. Mais cela va répéter des calculs inutiles, ce qui semble être ce que le PO essayait d'éviter. – Dougal

0

Il ne semble pas y avoir de moyen de le faire directement. Mais scipy.optimize.minimize vous laisse faire cela. Vous pouvez passer une valeur True pour fprime au lieu d'une fonction. Ceci signale que f renvoie un tuple de la valeur de la fonction et du gradient. Vous pouvez appeler minimize avec la méthode = 'BFGS' pour obtenir l'effet souhaité.

Il est intéressant de regarder the source code for minimize. A la fois fmin_bfgs et lui appellent finalement _minimize_bfgs, ce qui prend f et fprime comme arguments de fonction séparés. Lorsque fprime est un booléen, minimize construit intelligemment fprime en tant qu'objet qui se souvient de la dernière valeur renvoyée par f, et met en cache les informations de gradient.

Questions connexes