2017-11-19 4 views
2

Gibt es eine Möglichkeit, mit scipy.minimize pro Kosteneinheit auf die Kostenfunktion zuzugreifen, ohne den Rückruf zu verwenden und die Kostenfunktion erneut auszuführen?scipy.minimize - Kostenfunktion vs Iteration erhalten?

options.disp scheint dazu gedacht zu sein, verursacht aber nur, dass der Optimierer die Beendigungsnachricht druckt.

Es wäre in Ordnung, es auf stdout auszudrucken und contextlib.redirect_stdout mit io.StringIO zu verwenden, um es zu sammeln und die Daten danach zu analysieren, aber ich finde keinen Weg, auf die Kostenfunktion bei jeder Iteration effizient zuzugreifen.

Antwort

1

ich mit stdlib Funktionen eine Art Hack herausgefunden, verwendet es eine "tiefe" von sys.stdout umleiten. Beachten Sie, dass dies nicht mit jupyter funktioniert, da IPython sys.stdout entführt, wodurch das Attribut .fileno entfernt wird.

Es kann möglich sein, Jupyter auf diese Weise mit tempfile.SpooledTemporaryFile zu patchen, wodurch dieses Problem behoben wird. Ich weiß es nicht.

Ich glaube, da dies OS-Level-Dateideskriptoren verwendet, ist es auch nicht threadsafe.

import os 
import sys 
import tempfile 

class forcefully_redirect_stdout(object): 
    ''' Forces stdout to be redirected, for both python code and C/C++/Fortran 
     or other linked libraries. Useful for scraping values from e.g. the 
     disp option for scipy.optimize.minimize. 
    ''' 
    def __init__(self, to=None): 
     ''' Creates a new forcefully_redirect_stdout context manager. 

     Args: 
      to (`None` or `str`): what to redirect to. If type(to) is None, 
       internally uses a tempfile.SpooledTemporaryFile and returns a UTF-8 
       string containing the captured output. If type(to) is str, opens a 
       file at that path and pipes output into it, erasing prior contents. 

     Returns: 
      `str` if type(to) is None, else returns `None`. 

     ''' 

     # initialize where we will redirect to and a file descriptor for python 
     # stdout -- sys.stdout is used by python, while os.fd(1) is used by 
     # C/C++/Fortran/etc 
     self.to = to 
     self.fd = sys.stdout.fileno() 
     if self.to is None: 
      self.to = tempfile.SpooledTemporaryFile(mode='w+b') 
     else: 
      self.to = open(to, 'w+b') 

     self.old_stdout = os.fdopen(os.dup(self.fd), 'w') 
     self.captured = '' 

    def __enter__(self): 
     self._redirect_stdout(to=self.to) 
     return self 

    def __exit__(self, *args): 
     self._redirect_stdout(to=self.old_stdout) 
     self.to.seek(0) 
     self.captured = self.to.read().decode('utf-8') 
     self.to.close() 

    def _redirect_stdout(self, to): 
     sys.stdout.close() # implicit flush() 
     os.dup2(to.fileno(), self.fd) # fd writes to 'to' file 
     sys.stdout = os.fdopen(self.fd, 'w') # Python writes to fd 

if __name__ == '__main__': 
    import re 
    from scipy.optimize import minimize 

    def foo(x): 
     return 1/(x+0.001)**2 + x 

    with forcefully_redirect_stdout() as txt: 
     result = minimize(foo, [100], method='L-BFGS-B', options={'disp': True}) 

    print('this appears before `disp` output') 
    print('here''s the output from disp:') 
    print(txt.captured) 
    lines_with_cost_function_values = \ 
     re.findall(r'At iterate\s*\d\s*f=\s*-*?\d*.\d*D[+-]\d*', txt.captured) 

    fortran_values = [s.split()[-1] for s in lines_with_cost_function_values] 
    # fortran uses "D" to denote double and "raw" exp notation, 
    # fortran value 3.0000000D+02 is equivalent to 
    # python value 3.0000000E+02 with double precision 
    python_vals = [float(s.replace('D', 'E')) for s in fortran_values] 
    print(python_vals) 
2

Die Methode least_squares tut das mit Parameter . Es ist jedoch kein Allzweck-Minimierer, dessen Zweck es ist, die Summe der Quadrate der gegebenen Funktionen zu minimieren. Beispiel:

least_squares(lambda x: [x[0]*x[1]-6, x[0]+x[1]-5], [0, 0], verbose=2) 

Für andere Methoden, wie minimize, gibt es keine solche Option. Anstatt Callback zu verwenden und die Kostenfunktion neu zu bewerten, können Sie der Funktion selbst einige Protokollfunktionen hinzufügen. Zum Beispiel, hier fun anhängt die berechneten Werte für die globale Variable cost_values:

def fun(x): 
    c = x[0]**2 - 2*x[0] + x[1]**4 
    cost_values.append(c) 
    return c 

cost_values = [] 
minimize(fun, [3, 2]) 
print(cost_values) 

In diesem Beispiel gibt es 4 ähnliche Funktionswerte für jeden Iterationsschritt, wie der Minimierungsalgorithmus um sieht, die Berechnung der ungefähren Jacobi und/oder hessischen . Also, print(cost_values[::4]) wäre der Weg, um einen Wert der Kostenfunktion pro Schritt zu erhalten.

Aber es sind nicht immer 4 Werte pro Schritt (hängt von der Dimension und der verwendeten Methode ab). Daher ist es besser, eine Rückruffunktion zu verwenden, um die Kosten nach jedem Schritt zu protokollieren. Die aktuellen Kosten sollten in einer globalen Variablen gespeichert werden, sodass sie nicht neu berechnet werden muss.

def fun(x): 
    global current_cost 
    current_cost = x[0]**2 - 2*x[0] + x[1]**4 
    return current_cost 

def log_cost(x): 
    cost_values.append(current_cost) 

cost_values = [] 
minimize(fun, [3, 2], callback=log_cost) 
print(cost_values) 

Dieser druckt

[3.5058199763814986, -0.2358850818406083, -0.56104822688320077, -0.88774448831043995, -0.96018358963745964, -0.98750765702936738, -0.99588975368993771, -0.99867208501468863, -0.99956795994852465, -0.99985981414137615, -0.99995446605426996, -0.99998521591611178, -0.99999519917089297, -0.99999844105574265, -0.99999949379700426, -0.99999983560485239, -0.99999994662329761, -0.99999998266175671] 
+0

Das ist schlau! – MaxU

+0

Diese Antwort ist sehr clever! Gibt es einen Weg, dies ohne Schließungen zu tun und sich damit zu beschäftigen, immer einen "n-ten" Wert von der Liste zu nehmen? Ich stelle mir vor, dass in einer komplizierten Kostenfunktion ein Schritt gemacht werden kann, der keine Neuberechnung des Jac/Hess entlang einiger Dimensionen erfordert, so dass die "Überspring" -Länge selbst eine Funktion der Iteration sein wird. –

+0

@ 6'whitemale - siehe die Antwort, die ich gepostet habe, gibt es eine Lösung, solange Sie es nicht brauchen, um in IPython zu arbeiten. –

Verwandte Themen